Symfony - Concepts avancés

Dans ce chapitre, nous allons découvrir quelques concepts avancés du framework Symfony.

Cache HTTP

La mise en cache dans une application Web améliore les performances. Par exemple, des produits chauds dans une application Web de panier d'achat peuvent être mis en cache pendant une durée limitée, de sorte qu'ils puissent être présentés au client d'une manière rapide sans toucher la base de données. Voici quelques composants de base de Cache.

Élément de cache

L'élément de cache est une unité unique d'informations stockée sous forme de paire clé / valeur. lekey devrait être une chaîne et valuepeut être n'importe quel objet PHP. Les objets PHP sont stockés sous forme de chaîne par sérialisation et reconvertis en objets lors de la lecture des éléments.

Adaptateur de cache

L'adaptateur de cache est le mécanisme réel pour stocker l'élément dans un magasin. Le magasin peut être une mémoire, un système de fichiers, une base de données, un redis, etc.AdapterInterfacegrâce auquel un adaptateur peut stocker un élément de cache dans un magasin principal. De nombreux adaptateurs de cache intégrés sont disponibles. Peu d'entre eux sont les suivants -

  • Adaptateur Array Cache - Les éléments du cache sont stockés dans un tableau PHP.

  • Adaptateur de cache du système de fichiers - Les éléments du cache sont stockés dans des fichiers.

  • Adaptateur de cache de fichiers PHP - Les éléments du cache sont stockés sous forme de fichiers php.

  • Adaptateur de cache APCu - Les éléments du cache sont stockés dans la mémoire partagée à l'aide de l'extension PHP APCu.

  • Adaptateur de cache Redis - Les éléments de cache sont stockés sur le serveur Redis.

  • Adaptateur de cache PDO et Doctrine DBAL - Les éléments du cache sont stockés dans la base de données.

  • Adaptateur de cache de chaîne - Combine plusieurs adaptateurs de cache à des fins de réplication.

  • Adaptateur de cache proxy - Les éléments de cache sont stockés à l'aide d'un adaptateur tiers, qui implémente CacheItemPoolInterface.

Pool de cache

Cache Pool est un référentiel logique d'éléments de cache. Les pools de cache sont implémentés par des adaptateurs de cache.

Application simple

Créons une application simple pour comprendre le concept de cache.

Step 1 - Créer une nouvelle application, cache-example.

cd /path/to/app 
mkdir cache-example 
cd cache-example

Step 2 - Installez le composant de cache.

composer require symfony/cache

Step 3 - Créez un adaptateur de système de fichiers.

require __DIR__ . '/vendor/autoload.php';  
use Symfony\Component\Cache\Adapter\FilesystemAdapter;  
$cache = new FilesystemAdapter();

Step 4 - Créez un élément de cache en utilisant getItem et setméthode d'adaptateur. getItem récupère l'élément de cache à l'aide de sa clé. si la clé n'est pas persistante, elle crée un nouvel élément. La méthode set stocke les données réelles.

$usercache = $cache->getitem('item.users'); 
$usercache->set(['jon', 'peter']); 
$cache->save($usercache);

Step 5 - Accédez à l'élément de cache en utilisant getItem, isHit et getméthode. isHit informe la disponibilité de l'élément de cache et la méthode get fournit les données réelles.

$userCache = $cache->getItem('item.users'); 
if(!$userCache->isHit()) { 
   echo "item.users is not available"; 
} else { 
   $users = $userCache->get(); 
   var_dump($users); 
}

Step 6 - Supprimez l'élément de cache en utilisant deleteItem méthode.

$cache->deleteItem('item.users');

La liste complète des codes est la suivante.

<?php  
   require __DIR__ . '/vendor/autoload.php'; 
   use Symfony\Component\Cache\Adapter\FilesystemAdapter;  

   $cache = new FilesystemAdapter();  
   $usercache = $cache->getitem('item.users'); 
   $usercache->set(['jon', 'peter']); 
   $cache->save($usercache);  
   $userCache = $cache->getItem('item.users'); 
   
   if(!$userCache->isHit()) { 
      echo "item.users is not available"; 
   } else { 
      $users = $userCache->get(); 
      var_dump($users); 
   }  
   $cache->deleteItem('item.users');  
?>

Résultat

array(2) { 
   [0]=> 
   string(3) "jon" 
   [1]=> 
   string(5) "peter" 
}

Déboguer

Le débogage est l'une des activités les plus fréquentes lors du développement d'une application. Symfony fournit un composant séparé pour faciliter le processus de débogage. Nous pouvons activer les outils de débogage Symfony en appelant simplement leenable méthode de la classe Debug.

use Symfony\Component\Debug\Debug  
Debug::enable()

Symfony propose deux classes, ErrorHandler et ExceptionHandlerà des fins de débogage. Alors que ErrorHandler détecte les erreurs PHP et les convertit en exceptions, ErrorException ou FatalErrorException, ExceptionHandler intercepte les exceptions PHP non interceptées et les convertit en une réponse PHP utile. ErrorHandler et ExceptionHandler sont désactivés par défaut. Nous pouvons l'activer en utilisant la méthode register.

use Symfony\Component\Debug\ErrorHandler; 
use Symfony\Component\Debug\ExceptionHandler;  
ErrorHandler::register(); 
ExceptionHandler::register();

Dans une application Web Symfony, le debug environmentest fourni par DebugBundle. Enregistrez le bundle dans AppKernelregisterBundles méthode pour l'activer.

if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
   $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); 
}

Profiler

Le développement d'une application nécessite un outil de profilage de classe mondiale. L'outil de profilage collecte toutes les informations d'exécution d'une application telles que le temps d'exécution, le temps d'exécution des modules individuels, le temps nécessaire à une activité de base de données, l'utilisation de la mémoire, etc. Une application Web a besoin de beaucoup plus d'informations telles que l'heure de la demande, temps nécessaire pour créer une réponse, etc. en plus des paramètres ci-dessus.

Symfony active toutes ces informations dans une application Web par défaut. Symfony fournit un ensemble distinct pour le profilage Web appeléWebProfilerBundle. Le bundle de profileur Web peut être activé dans une application Web en enregistrant le bundle dans la méthode registerBundles d'AppKernel.

if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
   $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 
}

Le composant de profil Web peut être configuré sous web_profile section du fichier de configuration de l'application, app/config/config.xml

web_profiler: 
   toolbar:      false 
   position:     bottom

L'application Symfony affiche les données profilées au bas de la page sous la forme d'une section distincte.

Symfony fournit également un moyen simple d'ajouter des détails personnalisés sur la page dans les données de profil en utilisant DataCollectorInterface interfaceet modèle de brindille. En bref, Symfony permet à un développeur Web de créer une application de classe mondiale en fournissant un excellent cadre de profilage avec une relative facilité.

Sécurité

Comme indiqué précédemment, Symfony fournit un cadre de sécurité robuste grâce à son composant de sécurité. Le composant de sécurité est divisé en quatre sous-composants comme suit.

  • symfony / security-core - Fonctionnalité de sécurité de base.
  • symfony / security-http - Fonction de sécurité intégrée dans le protocole HTTP.
  • symfony / security-csrf - Protection contre la falsification de requêtes intersites dans une application Web.
  • symfony / security-acl - Cadre de sécurité basé sur une liste de contrôle d'accès avancé.

Authentification et autorisation simples

Apprenons le concept d'authentification et d'autorisation à l'aide d'une simple application de démonstration.

Step 1 - Créer une nouvelle application Web securitydemo en utilisant la commande suivante.

symfony new securitydemo

Step 2- Activez la fonction de sécurité dans l'application à l'aide du fichier de configuration de sécurité. La configuration relative à la sécurité est placée dans un fichier séparé,security.yml. La configuration par défaut est la suivante.

security: 
   providers: 
      in_memory: 
         memory: ~ 
   firewalls: 
      dev: 
         pattern: ^/(_(profiler|wdt)|css|images|js)/ 
         security: false  
   main: 
      anonymous: ~ 
      #http_basic: ~ 
      #form_login: ~

La configuration par défaut active le fournisseur de sécurité basé sur la mémoire et l'accès anonyme à toutes les pages. La section pare-feu exclut les fichiers correspondant au modèle,^/(_(profiler|wdt)|css|images|js)/du cadre de sécurité. Le modèle par défaut comprend des feuilles de style, des images et des scripts Java (ainsi que des outils de développement tels que le profiler).

Step 3 - Activez le système d'authentification de sécurité basé sur HTTP en ajoutant l'option http_basic dans la section principale comme suit.

security: 
   # ...  
   firewalls: 
      # ...  
      main: 
         anonymous: ~ 
         http_basic: ~ 
         #form_login: ~

Step 4- Ajoutez des utilisateurs dans la section du fournisseur de mémoire. Ajoutez également des rôles pour les utilisateurs.

security: 
   providers: 
      in_memory: 
         memory: 
            users: 
               myuser: 
                  password: user 
                  roles: 'ROLE_USER' 
                     myadmin: 
                        password: admin 
                        roles: 'ROLE_ADMIN'

Nous avons ajouté deux utilisateurs, l' utilisateur dans le rôle ROLE_USER et l' administrateur dans le rôle ROLE_ADMIN.

Step 5- Ajoutez l'encodeur pour obtenir les détails complets de l'utilisateur actuellement connecté. Le but de l'encodeur est d'obtenir des détails complets sur l'objet utilisateur actuel à partir de la requête Web.

security: 
   # ... 
   encoders: 
      Symfony\Component\Security\Core\User\User: bcrypt 
      # ...

Symfony fournit une interface, UserInterface pour obtenir les détails de l'utilisateur tels que le nom d'utilisateur, les rôles, le mot de passe, etc. Nous devons implémenter l'interface selon nos besoins et la configurer dans la section encodeur.

Par exemple, considérons que les détails de l'utilisateur sont dans la base de données. Ensuite, nous devons créer une nouvelle classe User et implémenter les méthodes UserInterface pour obtenir les détails de l'utilisateur de la base de données. Une fois les données disponibles, le système de sécurité les utilise pour autoriser / refuser l'utilisateur. Symfony fournit une implémentation utilisateur par défaut pour le fournisseur de mémoire. L'algorithme est utilisé pour déchiffrer le mot de passe de l'utilisateur.

Step 6 - Cryptez le mot de passe utilisateur en utilisant bcryptalgorithme et placez-le dans le fichier de configuration. Depuis que nous avons utilisébcryptalgorithme, l'objet utilisateur tente de déchiffrer le mot de passe spécifié dans le fichier de configuration, puis essaie de faire correspondre le mot de passe entré par l'utilisateur. L'application console Symfony fournit une commande simple pour crypter le mot de passe.

php bin/console security:encode-password admin 
Symfony Password Encoder Utility 
================================  
------------------ -----------------------------------
Key   Value  
------------------ ------------------------------------
Encoder used       Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder         
Encoded password   
$2y$12$0Hy6/.MNxWdFcCRDdstHU.hT5j3Mg1tqBunMLIUYkz6..IucpaPNO    
------------------ ------------------------------------   
! [NOTE] Bcrypt encoder used: the encoder generated its own built-in salt.
[OK] Password encoding succeeded

Step 7 - Utilisez la commande pour générer le mot de passe crypté et le mettre à jour dans le fichier de configuration.

# To get started with security, check out the documentation: 
# http://symfony.com/doc/current/security.html 
   security:  
      # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded 
      providers: 
         in_memory: 
            memory: 
               users: 
                  user: 
                     password: $2y$13$WsGWNufreEnVK1InBXL2cO/U7WftvfNvH
                     Vb/IJBH6JiYoDwVN4zoi  
                     roles: 'ROLE_USER' 
                     admin: 
                        password: $2y$13$jQNdIeoNV1BKVbpnBuhKRuOL01NeMK
                        F7nEqEi/Mqlzgts0njK3toy  
                        roles: 'ROLE_ADMIN' 
                         
         encoders: 
            Symfony\Component\Security\Core\User\User: bcrypt  
         firewalls: 
            # disables authentication for assets and the profiler, 
            # adapt it according to your needs 
         dev: 
            pattern: ^/(_(profiler|wdt)|css|images|js)/
         security: false  
         main: 
            anonymous: ~ 
            # activate different ways to authenticate  
            # http://symfony.com/doc/current/security.html#a-co
            nfiguring-howyour-users-will-authenticate 
            http_basic: ~  
            # http://symfony.com/doc/current/cookbook/security/
            form_login_setup.html 
            #form_login: ~

Step 8- Maintenant, appliquez la sécurité à une section de l'application. Par exemple, limitez la section d'administration aux utilisateurs du rôle ROLE_ADMIN.

security: 
   # ... 
      firewalls: 
         # ... 
      default: 
         # ...  
      access_control: 
         # require ROLE_ADMIN for /admin* 
         - { path: ^/admin, roles: 'ROLE_ADMIN' }

Step 9 - Ajoutez une page d'administration dans DefaultController comme suit.

/** 
   * @Route("/admin") 
*/ 
public function adminLandingAction() { 
   return new Response('<html><body>This is admin section.</body></html>'); 
}

Step 10- Enfin, accédez à la page d'administration pour vérifier la configuration de la sécurité dans un navigateur. Le navigateur demandera le nom d'utilisateur et le mot de passe et n'autorisera que les utilisateurs configurés.

Résultat

Flux de travail

Le flux de travail est un concept avancé utilisé dans de nombreuses applications d'entreprise. Dans une application de commerce électronique, le processus de livraison du produit est un flux de travail. Le produit est d'abord facturé (création de commande), acheté en magasin et emballé (emballage / prêt à expédier), et expédié à l'utilisateur. En cas de problème, le produit revient de l'utilisateur et la commande est annulée. L'ordre du flux d'action est très important. Par exemple, nous ne pouvons pas livrer un produit sans facturation.

Le composant Symfony fournit une manière orientée objet de définir et de gérer un flux de travail. Chaque étape d'un processus est appeléeplace et l'action requise pour se déplacer d'un endroit à un autre est appelée transition. La collection de lieux et la transition pour créer un flux de travail s'appelle unWorkflow definition.

Comprenons le concept de workflow en créant une application simple pour la gestion des congés.

Step 1 - Créer une nouvelle application, workflow-example.

cd /path/to/dev 
mkdir workflow-example 

cd workflow-example 
composer require symfony/workflow

Step 2 - Créer une nouvelle classe, Leave ayant applied_by, leave_on et status les attributs.

class Leave { 
   public $applied_by; 
   public $leave_on;  
   public $status; 
}

Ici, appliqué_by fait référence aux employés qui souhaitent un congé. congé_on fait référence à la date du congé. le statut fait référence au statut du congé.

Step 3 - La gestion des congés a quatre places, appliquée, en cours de processus et approuvée / rejetée.

use Symfony\Component\Workflow\DefinitionBuilder; 
use Symfony\Component\Workflow\Transition; 
use Symfony\Component\Workflow\Workflow; 
use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; 
use Symfony\Component\Workflow\Registry; 
use Symfony\Component\Workflow\Dumper\GraphvizDumper;

$builder = new DefinitionBuilder(); 
$builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']);

Ici, nous avons créé une nouvelle définition en utilisant DefinitionBuilder et ajout de lieux en utilisant addPlaces méthode.

Step 4 - Définissez les actions nécessaires pour passer d'un endroit à un autre.

$builder->addTransition(new Transition('to_process', 'applied', 'in_process')); 
$builder->addTransition(new Transition('approve', 'in_process', 'approved')); 
$builder->addTransition(new Transition('reject', 'in_process', 'rejected'));

Ici, nous avons trois transitions, to_process, approve et reject. to_process transition accepte la demande de congé et déplace le lieu de appliqué à in_process. approuver la transition approuve la demande de congé et déplace le lieu vers approuvé. De même, la transition de rejet rejette la demande de congé et déplace le lieu sur rejeté. Nous avons créé toutes les transitions en utilisant la méthode addTransition.

Step 5 - Construisez la définition en utilisant la méthode de construction.

$definition = $builder->build();

Step 6 - Facultativement, la définition peut être sauvegardée au format graphviz dot, qui peut être convertie en fichier image à des fins de référence.

$dumper = new GraphvizDumper(); 
echo $dumper->dump($definition);

Step 7 - Créez un magasin de marquage, qui est utilisé pour stocker les lieux / statuts actuels de l'objet.

$marking = new SingleStateMarkingStore('status');

Ici, nous avons utilisé SingleStateMarkingStoreclasse pour créer la marque et il marque l'état actuel dans la propriété status de l'objet. Dans notre exemple, l'objet est l'objet Leave.

Step 8 - Créez le workflow à l'aide de la définition et du marquage.

$leaveWorkflow =    new Workflow($definition, $marking);

Ici, nous avons utilisé Workflow classe pour créer le workflow.

Step 9 - Ajoutez le flux de travail dans le registre du framework de flux de travail en utilisant Registry classe.

$registry = new Registry(); 
$registry->add($leaveWorkflow, Leave::class);

Step 10 - Enfin, utilisez le workflow pour savoir si une transition donnée est appliquée en utilisant can méthode et si oui, applyla transition à l'aide de la méthode apply. Lorsqu'une transition est appliquée, l'état de l'objet se déplace d'un endroit à un autre.

$workflow = $registry->get($leave); 
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n"; 

$workflow->apply($leave, 'to_process'); 
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
echo $leave->status . "\r\n"; 

$workflow->apply($leave, 'approve'); 
echo $leave->status . "\r\n";

Le codage complet est le suivant -

<?php  
   require __DIR__ . '/vendor/autoload.php';  

   use Symfony\Component\Workflow\DefinitionBuilder; 
   use Symfony\Component\Workflow\Transition; 
   use Symfony\Component\Workflow\Workflow; 
   use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; 
   use Symfony\Component\Workflow\Registry; 
   use Symfony\Component\Workflow\Dumper\GraphvizDumper;

   class Leave { 
      public $applied_by; 
      public $leave_on;  
      public $status; 
   }  
   $builder = new DefinitionBuilder(); 
   $builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']); 
   $builder->addTransition(new Transition('to_process', 'applied', 'in_process')); 
   $builder->addTransition(new Transition('approve', 'in_process', 'approved')); 
   $builder->addTransition(new Transition('reject', 'in_process', 'rejected')); 
   $definition = $builder->build();  

   // $dumper = new GraphvizDumper(); 
   // echo $dumper->dump($definition);  

   $marking = new SingleStateMarkingStore('status'); 
   $leaveWorkflow = new Workflow($definition, $marking);  
   $registry = new Registry(); 
   $registry->add($leaveWorkflow, Leave::class);  

   $leave = new Leave(); 
   $leave->applied_by = "Jon"; 
   $leave->leave_on = "1998-12-12"; 
   $leave->status = 'applied';  

   $workflow = $registry->get($leave); 
   echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
   echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n"; 
   
   $workflow->apply($leave, 'to_process');  
   echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
   echo $leave->status . "\r\n"; 
   
   $workflow->apply($leave, 'approve'); 
   echo $leave->status . "\r\n";  
?>

Résultat

Can we approve the leave now?  
Can we approve the start process now? 1 
Can we approve the leave now? 1 
in_process 
approved