The Components Book Version:. generated on September, 0
The Components Book (.) This work is licensed under the Attribution-Share Alike.0 Unported license (http://creativecommons.org/ licenses/by-sa/.0/). You are free to share (to copy, distribute and transmit the work), and to remix (to adapt the work) under the following conditions: Attribution: You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). Share Alike: If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license. For any reuse or distribution, you must make clear to others the license terms of this work. The information in this book is distributed on an as is basis, without warranty. Although every precaution has been taken in the preparation of this work, neither the author(s) nor SensioLabs shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in this work. If you find typos or errors, feel free to report them by creating a ticket on the Symfony ticketing system (http://github.com/symfony/symfony-docs/issues). Based on tickets and users feedback, this book is continuously updated.
Contents at a Glance Comment installer et utiliser les composants de Symfony... Le Composant ClassLoader... Le Composant Config...0 Chargement des ressources... Mécanisme de cache basé sur les ressources... Définir et traiter les valeurs de configuration... Le Composant Console... Utiliser les commandes de console, les raccourcis et les commandes préconstruites... Modifier la commande par défault... Comment construire une application qui est une commande unique... Utilisez les événements... Helper Dialog... Formatter Helper... Progress Helper... Table Helper... Le Composant CssSelector... Le Composant Debug... Débugger un Class Loader... Le Composant d'injection de Dépendance («Dependency Injection» en anglais)...0 Types d'injection... Introduction aux paramètres... Travailler avec les définitions du conteneur... Compiler le Conteneur... Travailler avec des Services Taggés... Utiliser une «Factory» pour créer des services...0 Configurer des services avec un configurateur de service... Gérer les dépendances communes avec des services parents... Configuration Avancée du Conteneur... 00 Les services instanciés à la demande («lazy services» en anglais)... 0 Processus de construction du Conteneur... 0 Le Composant DomCrawler... 0 Le Composant répartiteur d'évènement... L'objet Évènement Générique... Le Répartiteur d'évènement du Container Aware... Le composant ExpressionLanguage... 0 Syntaxe des expressions... generated on September, 0 Contents at a Glance iii
Étendre ExpressionLanguage... Cacher les expressions en utilisant les parsers de cache... Le Composant «Filesystem» («système de fichiers» en français)... Le Composant Finder... Le composant Formulaire... Créer un Type Guesser personnalisé... Le composant HttpFoundation... Gestion de Session... Configurer les Sessions et les gestionnaires de sauvegarde... Tester avec les sessions... Intégration avec les session "Legacy"... Proxies de confiance... Le composant HttpKernel... Le Composant Locale... Le Composant «Process»... Le Composant PropertyAccess... Le Composant de Routage... Comment faire correspondre une route en se basant sur l'hôte... 0 Le composant sécurité... Le Pare-Feu et le Contexte de Sécurité... Authentification... Autorisation... Le Composant Serializer... Le Composant Templating... Le Composant YAML... Le Format YAML... iv Contents at a Glance Contents at a Glance
Chapter Comment installer et utiliser les composants de Symfony Si vous commencez un nouveau projet (ou si vous en avez déjà un) qui doit utiliser un ou plusieurs composants, la manière la plus facile pour intégrer cela est Composer. Composer est suffisamment intelligent pour télécharger le ou les composants dont vous avez besoin et prendre en charge l'autoloading, vous n'avez plus qu'à commencer à utiliser tout de suite ces librairies. Cet article va vous guider à traver la documentation : Le Composant Finder. Cela pourra être utilisé pour tout autre composant. Utiliser le composant Finder. Si vous créez un nouveau projet, créez le avec un répertoire vide.. Créé un fichier appelé composer.json et coller ce qui suit: Listing - "require": "symfony/finder": "..*" Si vous avez déjà un fichier composer.json, ajoutez juste les lignes ci-dessus. Vous aurez peut-être besoin de modifié la version (exp... ou..*). Vous pouvez rechercher les noms et versions des composants à packagist.org.. Installez composer si il n'est pas déjà présent sur votre système:. Téléchargez les librairies tierces et générez fichier vendor/autoload.php:. http://getcomposer.org. https://packagist.org/. http://getcomposer.org/download/ generated on September, 0 Chapter : Comment installer et utiliser les composants de Symfony
Listing - $ php composer.phar install. Ecrivez votre code: Une fois que Composer a téléchargé le ou les composants, tout ce dont vous avez besoin est inclut dans le fichier vendor/autoload.php qui a été généré par Composer. Ce fichier prends en compte l'autoloading pour toutes les librairies, donc vous pouvez directement les utiliser: Listing - 0 // Fichier: src/script.php // mettez jour ce chemin le répertoire "vendor/" en relation la position // de ce fichier require_once '../vendor/autoload.php'; use Symfony\Component\Finder\Finder; $finder = new Finder(); $finder->in('../data/'); //... Si vous voulez utiliser tous les composants Symfony, au lieu de les ajouter tous un par un: Listing - "require": "symfony/finder": "..*", "symfony/dom-crawler": "..*", "symfony/css-selector": "..*" Vous pouvez faire ceci: Listing - "require": "symfony/symfony": "..*" Cela inclue les librairies Bundle et Bridge, que vous n'avez pas encore besoin. Et maintenant? Le composant est installé et autochargé, lisez la documentation du composant pour en savoir sur comment l'utiliser. Et maintenant, amusez vous bien! generated on September, 0 Chapter : Comment installer et utiliser les composants de Symfony
Chapter Le Composant ClassLoader Le Composant ClassLoader charge les classes de votre projet automatiquement si elles suivent quelques conventions standards de PHP. Chaque fois que vous utilisez une classe non-définie, PHP utilise le mécanisme de chargement automatique («Autoloading» en anglais) afin qu'il s'occupe de charger un fichier définissant la classe. Symfony fournit un «autoloader universel», qui est capable de charger des classes depuis des fichiers qui implémentent l'une des conventions suivantes : Les standards d'intéropérabilité technique pour les espaces de nom de PHP. et les noms de classe ; La convention de nommage PEAR pour les classes. Si vos classes et les bibliothèques tierces que vous utilisez pour votre projet suivent ces standards, l'«autoloader» de Symfony est l'unique «autoloader» dont vous aurez besoin. Installation Vous pouvez installer le composant de plusieurs manières : Utilisez le dépôt officiel Git (https://github.com/symfony/classloader ) ; Installez le via Composer (symfony/class-loader sur Packagist ). Utilisation New in version.: La méthode useincludepath a été ajoutée dans Symfony... http://symfony.com/psr0. http://pear.php.net/manual/en/standards.php. https://github.com/symfony/classloader. https://packagist.org/packages/symfony/class-loader generated on September, 0 Chapter : Le Composant ClassLoader
Enregistrer l'«autoloader» UniversalClassLoader est très facile: Listing - 0 require_once '/path/to/src/symfony/component/classloader/universalclassloader.php'; use Symfony\Component\ClassLoader\UniversalClassLoader; $loader = new UniversalClassLoader(); // enregistrez les espaces de noms et préfixes ici (voir ci-dessous) // vous pouvez rechercher dans l'«include_path» en dernier recours. $loader->useincludepath(true); $loader->register(); Pour des gains de performance mineurs, les chemins vers les classes peuvent être cachés en mémoire en utilisant APC en enregistrant la classe ApcUniversalClassLoader : Listing - require_once '/path/to/src/symfony/component/classloader/universalclassloader.php'; require_once '/path/to/src/symfony/component/classloader/apcuniversalclassloader.php'; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; $loader = new ApcUniversalClassLoader('apc.prefix.'); $loader->register(); L'«autoloader» est utile uniquement si vous ajoutez des bibliothèques à charger automatiquement. L'«autoloader» est automatiquement enregistré dans une application Symfony (voir app/ autoload.php). Si les classes à charger automatiquement utilisent les espaces de noms, utilisez la méthode registernamespace() ou registernamespaces() : Listing - $loader->registernamespace('symfony', DIR.'/vendor/symfony/symfony/src'); $loader->registernamespaces(array( 'Symfony' => DIR.'/../vendor/symfony/symfony/src', 'Monolog' => DIR.'/../vendor/monolog/monolog/src', )); $loader->register(); Pour les classes qui suivent la convention de nommage PEAR, utilisez la méthode registerprefix() ou registerprefixes() 0 : Listing - $loader->registerprefix('twig_', DIR.'/vendor/twig/twig/lib');. http://api.symfony.com/./symfony/component/classloader/universalclassloader.html. http://api.symfony.com/./symfony/component/classloader/apcuniversalclassloader.html. http://api.symfony.com/./symfony/component/classloader/universalclassloader.html#method_registernamespace. http://api.symfony.com/./symfony/component/classloader/universalclassloader.html#method_registernamespaces. http://api.symfony.com/./symfony/component/classloader/universalclassloader.html#method_registerprefix 0. http://api.symfony.com/./symfony/component/classloader/universalclassloader.html#method_registerprefixes generated on September, 0 Chapter : Le Composant ClassLoader
$loader->registerprefixes(array( 'Swift_' => DIR.'/vendor/swiftmailer/swiftmailer/lib/classes', 'Twig_' => DIR.'/vendor/twig/twig/lib', )); $loader->register(); Certaines bibliothèques requièrent que la racine de leur chemin soit définie dans le «include path» PHP (set_include_path()). Les classes provenant d'un sous-espace de nom ou d'une sous-hiérarchie de classes PEAR peuvent être recherchées dans une liste de chemins afin de faciliter la séparation de sous-ensembles de bibliothèques pour les grands projets: Listing - $loader->registernamespaces(array( 'Doctrine\\Common' => DIR.'/vendor/doctrine/common/lib', 'Doctrine\\DBAL\\Migrations' => DIR.'/vendor/doctrine/migrations/lib', 'Doctrine\\DBAL' => DIR.'/vendor/doctrine/dbal/lib', 'Doctrine' => DIR.'/vendor/doctrine/orm/lib', )); $loader->register(); Dans cet exemple, si vous essayez d'utiliser une classe de l'espace de noms Doctrine\Common ou de l'un de ses enfants, l'«autoloader» va d'abord rechercher la classe dans le répertoire doctrine-common, et il va ensuite rechercher dans le répertoire Doctrine (le dernier configuré) s'il ne la trouve pas, avant d'abandonner. L'ordre des enregistrements est significatif dans ce cas. generated on September, 0 Chapter : Le Composant ClassLoader
Chapter Le Composant Config Introduction Le Composant Config fournit plusieurs classes pour vous aider à trouver, charger, combiner, préremplir et valider des valeurs de configuration de n'importe quel type, quelle que soit leur source (Yaml, XML, fichiers INI, ou par exemple une base de données). Installation Vous pouvez installer le composant de deux manières différentes : Utilisez le dépôt Git officiel (https://github.com/symfony/config ) ; Installez le via Composer (symfony/config à Packagist ). Sections Chargement des ressources Mécanisme de cache basé sur les ressources Définir et traiter les valeurs de configuration. https://github.com/symfony/config. https://packagist.org/packages/symfony/config generated on September, 0 Chapter : Le Composant Config 0
Chapter Chargement des ressources Localisation des ressources Le chargement de la configuration démarre normalement par une recherche des ressources - dans la plupart des cas : des fichiers. Cela peut être effectué à l'aide de la classe FileLocator : Listing - use Symfony\Component\Config\FileLocator; $configdirectories = array( DIR.'/app/config'); $locator = new FileLocator($configDirectories); $yamluserfiles = $locator->locate('users.yml', null, false); Le localisateur reçoit une collection de localisations où il doit rechercher les fichiers. Le premier argument de locate() est le nom du fichier à rechercher. Le second argument peut être le chemin courant, et lorsqu'il est fourni, le localisateur doit rechercher en premier dans ce répertoire. Le troisième argument indique si oui ou non le localisateur doit retourner le premier fichier qu'il trouve, ou bien un tableau contenant tous les fichiers trouvés. Chargeurs de ressource Pour chaque type de ressource (Yaml, XML, annotations, etc.), un chargeur doit être défini. Chaque chargeur doit implémenter LoaderInterface ou étendre la classe abstraite FileLoader, qui permet d'importer d'autres ressources de manière récursive: Listing - use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Yaml\Yaml;. http://api.symfony.com/./symfony/component/config/filelocator.html. http://api.symfony.com/./symfony/component/config/loader/loaderinterface.html. http://api.symfony.com/./symfony/component/config/loader/fileloader.html generated on September, 0 Chapter : Chargement des ressources
0 0 class YamlUserLoader extends FileLoader public function load($resource, $type = null) $configvalues = Yaml::parse($resource); //... gérer les valeurs de configuration // importer peut-être d'autres ressources : // $this->import('extra_users.yml'); public function supports($resource, $type = null) return is_string($resource) && 'yml' === pathinfo( $resource, PATHINFO_EXTENSION ); Trouver le bon chargeur La classe LoaderResolver reçoit en tant que premier argument de son constructeur une collection de chargeurs. Quand une ressource (par exemple un fichier XML) doit être chargée, le «LoaderResolver» va itérer sur cette collection de chargeurs et retourner le chargeur qui supporte ce type de ressource en particulier. La classe DelegatingLoader utilise la classe LoaderResolver. Lorsqu'on lui demande de charger une ressource, elle délègue cette question à la classe LoaderResolver. Dans le cas où le résolveur trouve un chargeur approprié, ce dernier va être utilisé pour charger la ressource: Listing - 0 use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; $loaderresolver = new LoaderResolver(array(new YamlUserLoader($locator))); $delegatingloader = new DelegatingLoader($loaderResolver); $delegatingloader->load( DIR.'/users.yml'); /* Le YamlUserLoader va être utilisé pour charger cette ressource puisqu'il supporte les fichiers ayant une extension «yml» */. http://api.symfony.com/./symfony/component/config/loader/loaderresolver.html. http://api.symfony.com/./symfony/component/config/loader/delegatingloader.html. http://api.symfony.com/./symfony/component/config/loader/loaderresolver.html. http://api.symfony.com/./symfony/component/config/loader/loaderresolver.html generated on September, 0 Chapter : Chargement des ressources
Chapter Mécanisme de cache basé sur les ressources Lorsque toutes les ressources de configuration sont chargées, vous pourriez vouloir traiter les valeurs de configuration et les combiner toutes en un seul fichier. Ce fichier agit en tant que cache. Son contenu n'a pas besoin d'être regénéré chaque fois que l'application est exécutée mais seulement quand les ressources de configuration sont modifiées. Par exemple, le composant de Routage de Symfony vous permet de charger toutes les routes, et d'afficher une correspondance d'url ou une URL générée basé sur ces routes. Dans ce cas, lorsqu'une des ressources est modifiée (et que vous travaillez dans un environnement de développement), le fichier généré devrait être invalidé ou regénéré. Cela peut être effectué en utilisant la classe ConfigCache. L'exemple ci-dessous vous montre comment collecter des ressources, puis générer du code basé sur les ressources qui ont été chargées, et écrire ce code dans le cache. Le cache reçoit aussi la collection de ressources qui ont été utilisées pour générer le code. En regardant le timestamp de la date de dernière modification de ces ressources, le cache peut dire s'il contient toujours la dernière version ou si son contenu devrait être regénéré: Listing - 0 use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; $cachepath = DIR.'/cache/appUserMatcher.php'; // le second argument indique si vous voulez utiliser le mode debug ou non $usermatchercache = new ConfigCache($cachePath, true); if (!$usermatchercache->isfresh()) // remplissez cela avec un tableau contenant les chemins des fichiers // «users.yml» $yamluserfiles =...; $resources = array(); foreach ($yamluserfiles as $yamluserfile) // Lisez l'article précédent «Chargement des ressources» // pour voir d'où provient $delegatingloader. http://api.symfony.com/./symfony/component/config/configcache.html generated on September, 0 Chapter : Mécanisme de cache basé sur les ressources
0 0 $delegatingloader->load($yamluserfile); $resources[] = new FileResource($yamlUserFile); // le code pour le «UserMatcher» est généré quelque part d'autre $code =...; $usermatchercache->write($code, $resources); // vous pourriez vouloir inclure le code caché : require $cachepath; En mode débuggage, un fichier.meta sera créé dans le même répertoire que le fichier de cache lui-même. Ce fichier.meta contient les ressources sérialisées, dont les timestamps sont utilisées pour déterminer si le cache contient toujours la dernière version. Lorsque vous n'êtes pas en mode débuggage, le cache est considéré comme contenant la dernière version dès qu'il existe, et donc, aucun fichier.meta ne sera généré. generated on September, 0 Chapter : Mécanisme de cache basé sur les ressources
Chapter Définir et traiter les valeurs de configuration Valider les valeurs de configuration Après avoir chargé les valeurs de configuration depuis toute sorte de ressources, les valeurs et leur structure peuvent être validées en utilisant la partie «Definition» du Composant Config. On s'attend généralement à ce que les valeurs de configuration possèdent une certaine hiérarchie. Aussi, les valeurs devraient être d'un certain type, restreintes en nombre ou faire partie d'un ensemble de valeurs donné. Par exemple, la configuration suivante (en YAML) montre une hiérarchie claire et quelques règles de validation qui devraient lui être appliquées (par exemple : «la valeur de auto_connect doit être une valeur booléenne») : Listing - 0 auto_connect: true default_connection: mysql connections: mysql: host: localhost driver: mysql username: user password: pass sqlite: host: localhost driver: sqlite memory: true username: user password: pass Lorsque vous chargez plusieurs fichiers de configuration, il devrait être possible de fusionner et de surcharger quelques valeurs. D'autres valeurs ne devraient pas être fusionnées et rester comme elles étaient lorsque vous les avez rencontrées pour la première fois. Aussi, certaines clés sont seulement disponibles quand une autre clé possède une valeur spécifique (dans l'exemple de configuration ci-dessus : la clé memory n'a de sens que si le driver est sqlite). generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
Définir une hiérarchie des valeurs de configuration en utilisant le «TreeBuilder» Toutes les règles concernant les valeurs de configuration peuvent être définies en utilisant le TreeBuilder («constructeur d'arbre» en français). Une instance de TreeBuilder devrait être retournée depuis une classe Configuration personnalisée qui implémente ConfigurationInterface : Listing - 0 namespace Acme\DatabaseConfiguration; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; class DatabaseConfiguration implements ConfigurationInterface public function getconfigtreebuilder() $treebuilder = new TreeBuilder(); $rootnode = $treebuilder->root('database'); //... ajoute des définitions de noeud à la racine de l'arbre return $treebuilder; Ajouter des définitions de noeuds à l'arbre Noeuds variable Un arbre contient des définitions de noeuds qui peuvent être décrites d'une manière sémantique. Cela signifie qu'en utilisant l'indentation et une notation fluide, il est possible de refléter la structure réelle des valeurs de configuration: Listing - 0 $rootnode ->children() ->booleannode('auto_connect') ->defaulttrue() ->end() ->scalarnode('default_connection') ->defaultvalue('default') ->end() ->end() ; Le noeud racine lui-même est un tableau de noeuds, et possède des enfants, comme le noeud booléen auto_connect et le noeud scalaire default_connection. En général : après avoir défini un noeud, un appel à la méthode end() vous fait remonter d'un niveau dans la hiérarchie.. http://api.symfony.com/./symfony/component/config/definition/builder/treebuilder.html. http://api.symfony.com/./symfony/component/config/definition/builder/treebuilder.html. http://api.symfony.com/./symfony/component/config/definition/configurationinterface.html generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
Types de noeud Il est possible de valider le type d'une valeur fournie en utilisant la définition de noeud appropriée. Les types de noeud disponibles sont : scalar boolean array enum integer (new in.) float (new in.) variable (pas de validation) et sont créés avec node($name, $type) ou leurs méthodes raccourcies associées xxxxnode($name). Contraintes de noeud numérique New in version.: Les noeuds numériques (float et integer) ont été ajoutés dans la version. Les noeuds numériques (float et integer) fournissent deux contraintes supplémentaires min() et max() qui vous permettent de valider leur valeur: Listing - 0 $rootnode ->children() ->integernode('positive_value') ->min(0) ->end() ->floatnode('big_value') ->max(e) ->end() ->integernode('value_inside_a_range') ->min(-0)->max(0) ->end() ->end() ; Noeuds tableau Il est possible d'ajouter un niveau plus profond à la hiérarchie en ajoutant un noeud tableau. Le noeud tableau lui-même peut avoir un ensemble prédéfini de noeuds variable: Listing - 0 $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver')->end() ->scalarnode('host')->end() ->scalarnode('username')->end() ->scalarnode('password')->end() ->end() ->end() ->end() ; Ou vous pouvez définir un prototype pour chaque noeud à l'intérieur d'un noeud tableau:. http://api.symfony.com/./symfony/component/config/definition/builder.html#method_min. http://api.symfony.com/./symfony/component/config/definition/builder.html#method_max generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
Listing - 0 $rootnode ->children() ->arraynode('connections') ->prototype('array') ->children() ->scalarnode('driver')->end() ->scalarnode('host')->end() ->scalarnode('username')->end() ->scalarnode('password')->end() ->end() ->end() ->end() ->end() ; Un prototype peut être utilisé pour ajouter une définition qui peut être répétée un grand nombre de fois à l'intérieur du noeud courant. Selon la définition du prototype dans l'exemple ci-dessus, il est possible d'avoir plusieurs tableaux de connexion (contenant un driver, host, etc.). Options de noeud tableau Avant de définir les enfants d'un noeud tableau, vous pouvez fournir des options comme : useattributeaskey() Fournit le nom d'un noeud enfant, dont la valeur devrait être utilisée en tant que clé dans le tableau résultant requiresatleastoneelement() Il devrait y avoir au moins un élément dans le tableau (fonctionne seulement quand isrequired est aussi appelé). adddefaultsifnotset() Si plusieurs noeuds enfants ont des valeurs par défaut, utilisez les si des les valeurs n'ont pas été explicitement fournies. Un exemple de cela: Listing - 0 $rootnode ->children() ->arraynode('parameters') ->isrequired() ->requiresatleastoneelement() ->useattributeaskey('name') ->prototype('array') ->children() ->scalarnode('value')->isrequired()->end() ->end() ->end() ->end() ->end() ; En YAML, la configuration ressemblerait à ceci: Listing - database: parameters: param: value: paramval generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
En XML, chaque noeud parameters doit avoir un attribut name ( suivi de value), qui devrait être supprimé et utilisé comme une clé pour cet élément dans le tableau final. L'option useattributeaskey est utile pour normaliser comment les tableaux sont spécifiés dans chaque format comme le XML et le YAML Valeurs par défaut et valeurs requises Pour tous les types de noeud, il est possible de définir des valeurs par défaut et des valeurs de remplacement dans le cas où un noeud possède une certaine valeur : defaultvalue() Définit une valeur par défaut isrequired() Doit être défini (mais peut être vide) cannotbeempty() Ne peut pas contenir de valeur vide default*() (null, true, false), raccourci pour defaultvalue() treat*like() (null, true, false), fournit une valeur de remplacement dans le cas où la valeur est *. Listing - 0 0 0 $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver') ->isrequired() ->cannotbeempty() ->end() ->scalarnode('host') ->defaultvalue('localhost') ->end() ->scalarnode('username')->end() ->scalarnode('password')->end() ->booleannode('memory') ->defaultfalse() ->end() ->end() ->end() ->arraynode('settings') ->adddefaultsifnotset() ->children() ->scalarnode('name') ->isrequired() ->cannotbeempty() ->defaultvalue('value') ->end() ->end() ->end() ->end() ; generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
Sections facultatives New in version.: Les méthodes canbeenabled et canbedisabled sont une nouveauté de Symfony. Si vous avez des sections entières qui sont facultatives et qui peuvent être activées ou désactivées, vous pouvez profiter des avantages des méthodes raccourci canbeenabled() et canbedisabled() : Listing -0 0 $arraynode ->canbeenabled() ; // est équivalent à $arraynode ->treatfalselike(array('enabled' => false)) ->treattruelike(array('enabled' => true)) ->treatnulllike(array('enabled' => true)) ->children() ->booleannode('enabled') ->defaultfalse() ; La méthode canbedisabled est quasiment identique à ceci près que la section sera activée par défaut. Options de fusion Des options supplémentaires concernant le processus de fusion peuvent être fournies. Pour les tableaux : performnodeepmerging() Lorsque la valeur est aussi définie dans un second tableau de configuration, n'essaye pas de fusionner un tableau, mais le surcharge entièrement Pour tout les noeuds : cannotbeoverwritten() Ne laisse pas les autres tableaux de configuration surcharger une valeur existante pour ce noeud Ajouter des sections Si vous devez valider une configuration complexe, alors l'arbre peut devenir très long et vous voudrez surement le découper en plusieurs sections. Vous pouvez faire cela en définissant une section dans un noeud séparé et en ajoutant ce noeud à l'arbre principal avec append(): Listing - 0 public function getconfigtreebuilder() $treebuilder = new TreeBuilder(); $rootnode = $treebuilder->root('database'); $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver'). http://api.symfony.com/./symfony/component/config/definition/builder/arraynodedefinition.html#method_canbeenabled. http://api.symfony.com/./symfony/component/config/definition/builder/arraynodedefinition.html#method_canbedisabled generated on September, 0 Chapter : Définir et traiter les valeurs de configuration 0
0 0 0 ; ->isrequired() ->cannotbeempty() ->end() ->scalarnode('host') ->defaultvalue('localhost') ->end() ->scalarnode('username')->end() ->scalarnode('password')->end() ->booleannode('memory') ->defaultfalse() ->end() ->end() ->append($this->addparametersnode()) ->end() ->end() return $treebuilder; public function addparametersnode() $builder = new TreeBuilder(); $node = $builder->root('parameters'); $node ->isrequired() ->requiresatleastoneelement() ->useattributeaskey('name') ->prototype('array') ->children() ->scalarnode('value')->isrequired()->end() ->end() ->end() ; return $node; C'est aussi très utile pour vous aider à ne pas vous répeter si vous avez des sections de configuration qui sont identiques en plusieurs endroits. Normalisation Lorsque les fichiers de configuration sont traités, ils sont d'abord normalisés. Ensuite, ils sont fusionnés puis l'arbre est utilisé pour valider le tableau qui a été généré. La normalisation consiste à supprimer certaines des différences issues des différents formats de configuration, principalement des différences entre Yaml et XML. Typiquement, le séparateur de clés utilisé en Yaml est _ et - en XML. Par exemple, auto_connect en Yaml deviendrait auto-connect en XML. La normalisation les transforme tout les deux en auto_connect. La clé cible ne sera pas modifié si elle est mélangée comme ceci foo-bar_moo ou si elle existe déjà. generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
Une autre différence en Yaml et Xml est la manière dont les tableaux de valeurs sont représentés. En Yaml, cela ressemble à : Listing - twig: extensions: ['twig.extension.foo', 'twig.extension.bar'] et en XML à : Listing - <twig:config> <twig:extension>twig.extension.foo</twig:extension> <twig:extension>twig.extension.bar</twig:extension> </twig:config> Cette différence peut être supprimée à la normalisation en pluralisant la clé utilisée en XML. Vous pouvez indiquer que vous voulez qu'une clé soit pluralisée de cette manière avec fixxmlconfig(): Listing - $rootnode ->fixxmlconfig('extension') ->children() ->arraynode('extensions') ->prototype('scalar')->end() ->end() ->end() ; S'il s'agit d'un pluriel irrégulier, vous pouvez le spécifier comme second argument: Listing - $rootnode ->fixxmlconfig('child', 'children') ->children() ->arraynode('children') ->end() ; En parallèle, fixxmlconfig garantit que chaque élément xml sera toujours intégré dans un tableau. Vous pouvez avoir : Listing - <connection>default</connection> <connection>extra</connection> et parfois seulement : Listing - <connection>default</connection> Par défaut, connection sera un tableau dans le premier cas et une chaine de caractères dans le second cas, ce qui rendrait la validation difficile. Vous pouvez vous assurer qu'il s'agisse toujours d'un tableau avec fixxmlconfig. Si nécessaire, vous pouvez contrôler encore plus le processus de normalisation. Par exemple, vous pouvez autoriser qu'une chaine de caractères soit définie et utilisée comme clé particulière, ou que plusieurs clés soit définies explicitement. Pour cela, si tout est facultatif dans votre config sauf l'id: Listing - connection: name: my_mysql_connection host: localhost generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
driver: mysql username: user password: pass vous pouvez également autoriser ce qui suit : Listing - connection: my_mysql_connection en changeant la valeur d'une chaine de caractère en tableau associatif avec name comme clé: Listing -0 0 $rootnode ->children() ->arraynode('connection') ->beforenormalization() ->ifstring() ->then(function($v) return array('name'=> $v); ) ->end() ->children() ->scalarnode('name')->isrequired() //... ->end() ->end() ->end() ; Règles de validation Des règles de validation plus avancées peuvent être fournies en utilisant le ExprBuilder («constructeur d'expression» en français). Ce constructeur implémente une interface fluide pour une structure de contrôle bien connue. Le constructeur est utilisé pour ajouter des règles de validation avancées aux définitions de noeud, comme: Listing - 0 $rootnode ->children() ->arraynode('connection') ->children() ->scalarnode('driver') ->isrequired() ->validate() ->ifnotinarray(array('mysql', 'sqlite', 'mssql')) ->theninvalid('invalid database driver "%s"') ->end() ->end() ->end() ->end() ->end() ; Une règle de validation a toujours une partie «if». Vous pouvez spécifier cette partie grâce aux manières suivantes : iftrue(). http://api.symfony.com/./symfony/component/config/definition/builder/exprbuilder.html generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
ifstring() ifnull() ifarray() ifinarray() ifnotinarray() always() Une règle de validation requiert aussi une partie «then» : then() thenemptyarray() theninvalid() thenunset() Généralement, «then» est une closure. Sa valeur de retour sera utilisée en tant que nouvelle valeur pour le noeud, à la place de la valeur originale de ce dernier. Traiter les valeurs de configuration La classe Processor utilise l'arbre comme s'il était construit en utilisant le TreeBuilder 0 pour traiter plusieurs tableaux de valeurs de configuration qui devraient être fusionnés. Si une valeur quelconque n'est pas du type attendu, est obligatoire et pas encore définie, ou n'a pas pu être validée d'une façon ou d'une autre, une exception sera lancée. Sinon, le résultat est un tableau contenant les valeurs de configuration: Listing - 0 use Symfony\Component\Yaml\Yaml; use Symfony\Component\Config\Definition\Processor; use Acme\DatabaseConfiguration; $config = Yaml::parse( DIR.'/src/Matthias/config/config.yml'); $config = Yaml::parse( DIR.'/src/Matthias/config/config_extra.yml'); $configs = array($config, $config); $processor = new Processor(); $configuration = new DatabaseConfiguration; $processedconfiguration = $processor->processconfiguration( $configuration, $configs) ;. http://api.symfony.com/./symfony/component/config/definition/processor.html 0. http://api.symfony.com/./symfony/component/config/definition/builder/treebuilder.html generated on September, 0 Chapter : Définir et traiter les valeurs de configuration
Chapter Le Composant Console Le composant Console facilite la création d'interfaces en ligne de commandes, belles et testables. Le composant Console vous permet de créer des commandes de ligne de commandes. Vos commandes de console peuvent être utilisées pour n'importe quelle tâche récurrente, comme des tâches cron, des imports, ou d'autres processus à exécuter par lots. Installation Vous pouvez installer le composant de deux manières différentes : Utilisez le dépôt Git officiel (https://github.com/symfony/console ) ; Installez le via Composer (symfony/console à Packagist ). Windows ne supporte pas les couleurs ANSI par défaut, donc le composant Console le détecte et désactive les couleurs pour lequelles Windows n'apporte pas de support. Cependant, si Windows n'est pas configuré avec un driver ANSI et que les commandes appellent d'autres scripts qui envoient des séquences de couleurs ANSI, ils seront affichées en caractères bruts. Pour activer les couleurs ANSI dans Windows, installez ANSICON. Créer une Commande basique Pour faire une commande de console qui vous accueille depuis la ligne de commande, créez un fichier GreetCommand.php et ajoutez-lui ce qui suit: Listing -. https://github.com/symfony/console. https://packagist.org/packages/symfony/console. https://github.com/adoxa/ansicon generated on September, 0 Chapter : Le Composant Console
0 0 0 0 namespace Acme\DemoBundle\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class GreetCommand extends Command protected function configure() $this ->setname('demo:greet') ->setdescription('saluez quelqu\'un') ->addargument( 'name', InputArgument::OPTIONAL, 'Qui voulez-vous saluez?' ) ->addoption( 'yell', null, InputOption::VALUE_NONE, 'Si défini, la réponse est affichée en majuscules' ) ; protected function execute(inputinterface $input, OutputInterface $output) $name = $input->getargument('name'); if ($name) $text = 'Salut, '.$name; else $text = 'Salut'; if ($input->getoption('yell')) $text = strtoupper($text); $output->writeln($text); Vous devez aussi créer le fichier à exécuter en ligne de commandes qui crée une Application et lui ajoute les commandes: Listing - #!/usr/bin/env php <?php // app/console use Acme\DemoBundle\Command\GreetCommand; use Symfony\Component\Console\Application; $application = new Application(); generated on September, 0 Chapter : Le Composant Console
0 $application->add(new GreetCommand); $application->run(); Testez la nouvelle commande de console en exécutant ce qui suit : Listing - $ app/console demo:greet Fabien Cela devrait afficher le texte suivant sur votre ligne de commandes : Listing - Salut, Fabien Vous pouvez aussi utiliser l'option --yell pour tout afficher en majuscules : Listing - $ app/console demo:greet Fabien --yell Cela affiche: Listing - SALUT, FABIEN Ajouter de la couleur à l'affichage A chaque fois que vous affichez du texte, vous pouvez entourer le texte avec des balises afin d'ajouter de la couleur à l'affichage. Par exemple: Listing - 0 // texte vert $output->writeln('<info>foo</info>'); // texte jaune $output->writeln('<comment>foo</comment>'); // texte noir sur fond cyan $output->writeln('<question>foo</question>'); // texte blanc sur fond rouge $output->writeln('<error>foo</error>'); Il est possible de définir vos propres styles en utilisant la classe OutputFormatterStyle : Listing - $style = new OutputFormatterStyle('red', 'yellow', array('bold', 'blink')); $output->getformatter()->setstyle('fire', $style); $output->writeln('<fire>foo</fire>'); Les couleurs d'écriture et de fond disponibles sont : black («noir»), red («rouge»), green («vert»), yellow («jaune»), blue («bleu»), magenta («magenta»), cyan («cyan») et white («blanc»). Et les options disponibles sont : bold («gras»), underscore («souligné»), blink («clignotant»), reverse («inversé») et conceal («masqué»). Vous pouvez aussi définir ces couleurs et options directement dans les balises: Listing -. http://api.symfony.com/./symfony/component/console/formatter/outputformatterstyle.html generated on September, 0 Chapter : Le Composant Console
// texte vert $output->writeln('<fg=green>foo</fg=green>'); // texte noir sur fond cyan $output->writeln('<fg=black;bg=cyan>foo</fg=black;bg=cyan>'); // texte gras sur fond jaune $output->writeln('<bg=yellow;options=bold>foo</bg=yellow;options=bold>'); Niveau de verbosité New in version.: Les constantes VERBOSITY_VERY_VERBOSE et VERBOSITY_DEBUG ont été introduites avec la version.. La console a niveaux de verbosité. Ils sont définis dans la classe OutputInterface : Mode OutputInterface::VERBOSITY_QUIET OutputInterface::VERBOSITY_NORMAL OutputInterface::VERBOSITY_VERBOSE OutputInterface::VERBOSITY_VERY_VERBOSE OutputInterface::VERBOSITY_DEBUG Valeur N'affiche pas les messages Le niveau par défaut Augmente la verbosité des messages Les messages d'information non essentiels Les messages de débuggage Vous pouvez spécifier le niveau de verbosité silencieuse avec l'option --quiet ou -q. L'option --verbose ou -v est utilisé quand vous voulez augmenter le niveau de verbosité. La stacktrace de l'exception complète est affichée si le niveau VERBOSITY_VERBOSE ou supérieur est utilisé. Il est possible d'afficher un message dans une commande pour un niveau spécifique de verbosité. Par exemple: Listing -0 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getverbosity()) $output->writeln(...); Quand le niveau quiet est utilisé, tous les affichages sont supprimés et la méthode Symfony\Component\Console\Output::write retourne sans aucun affichage. Utiliser des arguments de commande La partie la plus intéressante des commandes sont les arguments et options que vous pouvez rendre disponibles. Les arguments sont les chaînes de caractères - séparées par des espaces - qui viennent après le nom de la commande lui-même. Ils sont ordonnés, et peuvent être optionnels ou obligatoires. Par exemple, ajoutez un argument optionnel last_name à la commande et faites en sorte que l'argument name soit obligatoire:. http://api.symfony.com/./symfony/component/console/output/outputinterface.html. http://api.symfony.com/./symfony/component/console/output.html#method_write generated on September, 0 Chapter : Le Composant Console
Listing - 0 $this //... ->addargument( 'name', InputArgument::REQUIRED, 'Qui voulez-vous saluer?' ) ->addargument( 'last_name', InputArgument::OPTIONAL, 'Votre nom de famille?' ); Vous avez maintenant accès à l'argument last_name depuis votre commande: Listing - if ($lastname = $input->getargument('last_name')) $text.= ' '.$lastname; La commande peut maintenant être utilisée de l'une des façons suivantes : Listing - $ app/console demo:greet Fabien $ app/console demo:greet Fabien Potencier Il est aussi possible de passer une liste de valeurs en argument (imaginez que vous vouliez saluer tous vos amis). Pour effectuer ceci, vous devez le spécifier à la fin de la liste d'arguments: Listing - $this //... ->addargument( 'names', InputArgument::IS_ARRAY, 'Qui voulez-vous saluez (séparer les noms par des espaces)?' ); Pour l'utiliser, spécifiez combien de noms vous voulez: Listing - $ app/console demo:greet Fabien Ryan Bernhard Vous accédez à l'argument names comme un tableau: Listing - if ($names = $input->getargument('names')) $text.= ' '.implode(', ', $names); Il y a différents arguments que vous pouvez utiliser.: Mode InputArgument::REQUIRED InputArgument::OPTIONAL InputArgument::IS_ARRAY Valeur L'argument est requis L'argument est optionnel et peut être omis L'argument peut contenir une infinité d'arguments et doit être utilisé à la fin de la liste des arguments generated on September, 0 Chapter : Le Composant Console
Vous pouvez combiner IS_ARRAY avec REQUIRED et OPTIONAL comme ceci: Listing - $this //... ->addargument( 'names', InputArgument::IS_ARRAY InputArgument::REQUIRED, 'Qui voulez vous saluez (séparer les noms par des espaces)?' ); Utiliser des options de commande Contrairement aux arguments, les options ne sont pas ordonnées (ce qui signifie que vous pouvez les spécifier dans n'importe quel ordre) et sont spécifiées avec deux tirets (par exemple : --yell - vous pouvez aussi déclarer un raccourci d'une lettre que vous pouvez appeler avec un unique tiret comme -y). Les options sont toujours optionnelles, et peuvent être déclarées de manière à accepter une valeur (par exemple : dir=src) ou simplement en tant qu'indicateur booléen sans valeur (par exemple : yell). Il est aussi possible de faire qu'une option accepte optionnellement une valeur (qui ferait que -- yell ou yell=loud fonctionnerait). Les options peuvent être configurées pour accepter un tableau de valeurs. Par exemple, ajoutez une nouvelle option à la commande qui peut être utilisée pour spécifier combien de fois le message devrait être affiché: Listing - $this //... ->addoption( 'iterations', null, InputOption::VALUE_REQUIRED, 'Combien de fois voulez vous afficher le message?', ); Ensuite, utilisez cette commande pour afficher le message plusieurs fois : Listing - for ($i = 0; $i < $input->getoption('iterations'); $i++) $output->writeln($text); Maintenant, lorsque vous exécutez la tâche, vous pouvez spécifier de manière facultative une option -- iterations : Listing -0 $ app/console demo:greet Fabien $ app/console demo:greet Fabien --iterations= Le premier exemple va afficher le résultat une seule fois, puisque iterations est vide et que par défaut il vaut (le dernier argument de addoption). Le second exemple va afficher le résultat cinq fois. Rappelez-vous bien que ces options ne tiennent pas compte de leur ordre. Donc, n'importe laquelle des deux commandes suivantes va fonctionner : generated on September, 0 Chapter : Le Composant Console 0
Listing - $ app/console demo:greet Fabien --iterations= --yell $ app/console demo:greet Fabien --yell --iterations= Il y a variantes d'options que vous pouvez utiliser : Option Valeur InputOption::VALUE_IS_ARRAY Cette option accepte de multiples valeurs (par exemple : -- dir=/foo --dir=/bar) InputOption::VALUE_NONE InputOption::VALUE_REQUIRED InputOption::VALUE_OPTIONAL N'accepte pas de valeur en entrée pour cette option (par exemple : --yell) Cette valeur est requise (par exemple : --iterations=), l'option elle-même reste optionnelle Cette option peut ou non avoir une valeur (par exemple : yell ou yell=loud) Vous pouvez combiner VALUE_IS_ARRAY avec VALUE_REQUIRED ou VALUE_OPTIONAL de la manière suivante Listing - $this //... ->addoption( 'iterations', null, InputOption::VALUE_REQUIRED InputOption::VALUE_IS_ARRAY, 'Combien de fois voulez vous afficher le message?', ); Helpers pour la Console Le composant de console contient également un ensemble de «helpers» qui sont de petits outils qui peuvent vous aider dans différentes tâches: Helper Dialog: demande d'informations à l'utilisateur de manière interactive Formatter Helper: personnalisation de la coloration de sortie Progress Helper: affichage d'une barre de progression Table Helper: affichage de données tabulaires comme un tableau Tester les commandes Symfony fournit plusieurs outils pour vous aider à tester vos commandes. La plus utile est la classe CommandTester. Elle utilise des classes «d'entrée et de sortie» spécifiques permettant de faciliter les tests sans avoir de console réelle: Listing - use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester;. http://api.symfony.com/./symfony/component/console/tester/commandtester.html generated on September, 0 Chapter : Le Composant Console
0 0 use Acme\DemoBundle\Command\GreetCommand; class ListCommandTest extends \PHPUnit_Framework_TestCase public function testexecute() $application = new Application(); $application->add(new GreetCommand()); $command = $application->find('demo:greet'); $commandtester = new CommandTester($command); $commandtester->execute(array('command' => $command->getname())); $this->assertregexp('/.../', $commandtester->getdisplay()); //... La méthode getdisplay() retourne ce qui aurait été affiché avec l'appel normal de la commande via la console. Vous pouvez tester les arguments et options envoyés à la commande, en les passant dans un tableau à la méthode execute() Listing - 0 0 use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Acme\DemoBundle\Command\GreetCommand; class ListCommandTest extends \PHPUnit_Framework_TestCase //... public function testnameisoutput() $application = new Application(); $application->add(new GreetCommand()); $command = $application->find('demo:greet'); $commandtester = new CommandTester($command); $commandtester->execute( array('command' => $command->getname(), 'name' => 'Fabien') ); $this->assertregexp('/fabien/', $commandtester->getdisplay()); Vous pouvez aussi tester une application console entière en utilisant ApplicationTester 0.. http://api.symfony.com/./symfony/component/console/tester/commandtester.html#method_getdisplay. http://api.symfony.com/./symfony/component/console/tester/commandtester.html#method_execute 0. http://api.symfony.com/./symfony/component/console/tester/applicationtester.html generated on September, 0 Chapter : Le Composant Console
Appeler une commande existante Si une commande dépend d'une autre ayant été exécutée avant elle, plutôt que de demander à l'utilisateur de se rappeler de l'ordre d'exécution, vous pouvez l'appeler directement vous-même. Cela est aussi utile si vous souhaitez créer une commande «méta» qui exécute juste un ensemble de commandes (par exemple, toutes les commandes qui ont besoin d'être exécutées lorsque le code du projet a été modifié sur les serveurs de production : effacer le cache, générer les proxys Doctrine, préparer les fichiers Assetic,...). Appeler une commande depuis une autre est très simple: Listing - 0 protected function execute(inputinterface $input, OutputInterface $output) $command = $this->getapplication()->find('demo:greet'); $arguments = array( 'command' => 'demo:greet', 'name' => 'Fabien', '--yell' => true, ); $input = new ArrayInput($arguments); $returncode = $command->run($input, $output); //... D'abord, vous trouvez, grâce à la méthode find(), la commande que vous voulez exécuter en passant le nom de cette dernière. Ensuite, vous devez créer une nouvelle classe ArrayInput avec les arguments et options que vous souhaitez passer à la commande. Éventuellement, vous pouvez appelez la méthode run() qui va exécuter la commande et retourner le code retourné par la commande (retourne la valeur de la méthode execute() de la commande). La plupart du temps, appeler une commande depuis du code qui n'est pas exécuté depuis la ligne de commandes n'est pas une bonne idée pour plusieurs raisons. Mais le plus important, c'est que vous compreniez qu'il faut voir une commande comme un contrôleur ; elle devrait utiliser le modèle pour faire quelque chose et afficher le retour à l'utilisateur. Donc, plutôt que d'appeler une commande depuis le Web, revoyez votre code et déplacez la logique dans une nouvelle classe. En savoir plus! Utiliser les commandes de console, les raccourcis et les commandes préconstruites Comment construire une application qui est une commande unique. http://api.symfony.com/./symfony/component/console/application.html#method_find. http://api.symfony.com/./symfony/component/console/input/arrayinput.html generated on September, 0 Chapter : Le Composant Console
Chapter Utiliser les commandes de console, les raccourcis et les commandes préconstruites En plus des options que vous spécifiez dans vos commandes, il existe des options ainsi que que des commandes préconstruites pour le composant Console. Ces exemples supposent que vous avez ajouté un fichier app/console pour exécuter l'interface de commande CLI: Listing - #!/usr/bin/env php # app/console <?php use Symfony\Component\Console\Application; $application = new Application(); //... $application->run(); Commandes préconstruites Il existe une commande list qui affiche la liste des options standard et des commandes enregistrées : Listing - $ php app/console list Vous pouvez également avoir le même affichage sans exécuter aucune commande Listing - $ php app/console La commande d'aide liste les informations de la commande spécifiées. Par exemple, pour avoir une aide sur la commande list, exécutez : generated on September, 0 Chapter : Utiliser les commandes de console, les raccourcis et les commandes préconstruites
Listing - $ php app/console help list Exécuter help sans spécifier aucune commande listera les options globales : Listing - $ php app/console help Options globales Vous pouvez obtenir de l'aide sur n'importe quelle commande grâce à l'option --help. Pour obtenir de l'aide sur la commande list : Listing - $ php app/console list --help $ php app/console list -h Vous pouvez supprimer l'affichage avec : Listing - $ php app/console list --quiet $ php app/console list -q Vous pouvez obtenir des messages plus verbeux (si c'est supporté par la commande) avec : Listing - $ php app/console list --verbose $ php app/console list -v Le niveau de verbosité peut optionnellement prendre une valeur entre (par défaut) et pour afficher plus ou moins d'informations: $ php app/console list --verbose= $ php app/console list -vv $ php app/console list --verbose= $ php app/console list -vvv Si vous définissez des arguments facultatifs pour donner un nom et une version à votre application: Listing - $application = new Application('Acme Console Application', '.'); alors vous pouvez utiliser : Listing -0 $ php app/console list --version $ php app/console list -V pour obtenir l'affichage de ces informations : Listing - Acme Console Application version. Si vous ne spécifiez pas les arguments, alors cela affichera juste : Listing - console tool Vous pouvez forcer la coloration ANSI de l'affichage avec : generated on September, 0 Chapter : Utiliser les commandes de console, les raccourcis et les commandes préconstruites
Listing - $ php app/console list --ansi ou la désactiver : Listing - $ php app/console list --no-ansi Vous pouvez supprimer les questions interactives de la commande que vous exécutez avec : Listing - $ php app/console list --no-interaction $ php app/console list -n Syntaxe raccourcie Vous n'avez pas besoin de taper les noms de commande en entier. Vous pouvez vous contenter de taper le nom raccourci non ambigu pour exécuter une commande. En conséquence, s'il y a des commandes non conflictuelles, vous pouvez exécuter help comme ceci : Listing - $ php app/console h Si vous avez des commandes qui utilisent : pour les espaces de noms, alors il vous suffit juste de taper le texte raccourci non ambigu de chaque partie. Si vous avez créé la commande demo:greet comme expliqué dans Le Composant Console, alors vous pouvez l'exécuter avec : Listing - $ php app/console d:g Fabien Si vous tapez un raccourci de commande qui est ambigu (c-a-d si plusieurs commandes correspondent), alors aucune commande ne sera exécutée et cela affichera une liste de suggestion des commandes qu'il est possible de choisir. generated on September, 0 Chapter : Utiliser les commandes de console, les raccourcis et les commandes préconstruites
Chapter Modifier la commande par défault New in version.: La méthode setdefaultcommand() a été ajouté dans le version.. app/console lancera toujours ListCommand lorsqu'aucune commande n'est passée. Pour changer la commande par défaut vous n'avez simplement qu'à passer le nom de la commande que vous souhaitez lancer par défaut dans la méthode setdefaultcommand Listing - 0 namespace Acme\Console\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class HelloWorldCommand extends Command protected function configure() $this->setname('hello:world') ->setdescription('outputs \'Hello World\''); protected function execute(inputinterface $input, OutputInterface $output) $output->writeln('hello World'); Exécuter l'application et changer la commande par défaut Listing - // application.php use Acme\Console\Command\HelloWorldCommand; use Symfony\Component\Console\Application; $command = new HelloWorldCommand();. http://api.symfony.com/./symfony/component/console/application.html#method_setdefaultcommand generated on September, 0 Chapter : Modifier la commande par défault
0 $application = new Application(); $application->add($command); $application->setdefaultcommand($command->getname()); $application->run(); Testez la nouvelle commande console par défaut en lançant la commande suivante : Listing - $ php application.php Cela affichera ce qui suit dans votre ligne de commande : Listing - Hello Fabien Cette fonctionnalité a une limitation : il est impossible de l'utiliser avec des arguments. Apprenez-en plus! Comment construire une application qui est une commande unique generated on September, 0 Chapter : Modifier la commande par défault
Chapter 0 Comment construire une application qui est une commande unique Lorsque vous construisez un outil de ligne de commande, vous n'avez peut être pas besoin de fournir plusieurs commandes. Dans ce cas, devoir passer plusieurs noms de commande à chaque fois peut être fastidieux. Heureusement, il est possible de supprimer cette nécessité en étendant l'application: Listing 0-0 0 namespace Acme\Tool; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\InputInterface; class MyApplication extends Application /** * Récupère le nom de la commande saisie. * * @param InputInterface $input L'interface de saisie * * @return string Le nom de la commande */ protected function getcommandname(inputinterface $input) // Retourne le nom de votre commande. return 'my_command'; /** * Récupère les commandes par défaut qui sont toujours disponibles. * * @return array Un tableau d'instances de commandes par défaut */ protected function getdefaultcommands() // Conserve les commandes par défaut du noyau pour avoir la // commande HelpCommand en utilisant l'option --help generated on September, 0 Chapter 0: Comment construire une application qui est une commande unique
0 0 $defaultcommands = parent::getdefaultcommands(); $defaultcommands[] = new MyCommand(); return $defaultcommands; /** * Surchargé afin que l'application accepte que le premier argument ne * soit pas le nom. */ public function getdefinition() $inputdefinition = parent::getdefinition(); // efface le premier argument, qui est le nom de la commande $inputdefinition->setarguments(); return $inputdefinition; Lorsque vous appelez votre script de console, la commande MyCommand sera maintenant toujours utilisée, sans avoir à saisir son nom. Vous pouvez aussi simplifier l'éxécution de l'application: Listing 0- #!/usr/bin/env php <?php // command.php use Acme\Tool\MyApplication; $application = new MyApplication(); $application->run(); generated on September, 0 Chapter 0: Comment construire une application qui est une commande unique 0
Chapter Utilisez les événements New in version.: Les évenements de Console ont été ajouté à Symfony.. La classe d'application du composant Console vous permet en option de vous brancher sur le cycle de vie de l'application. Au lieu de réinventer la roue, il utilise le composant EventDispatcher de Symfony pour faire le boulot: Listing - use Symfony\Component\Console\Application; use Symfony\Component\EventDispatcher\EventDispatcher; $application = new Application(); $application->setdispatcher($dispatcher); $application->run(); L'événement ConsoleEvents::COMMAND Utilisation courante: Faire quelque chose avant que n'importe quelle commande soit lancé( comme logger ce que la commande est train d' exécuter), ou afficher certaines choses sur l'événement en cours d'exécution. Avant l'exécution de toute commande, l'événement ConsoleEvents::COMMAND est envoyé. Les écouteurs reçoivent un événement de classe ConsoleCommandEvent : Listing - use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\ConsoleEvents; $dispatcher->addlistener(consoleevents::command, function (ConsoleCommandEvent $event) // récupérer l'instance de la saisie $input = $event->getinput(); // récupérer la sortie $output = $event->getoutput();. http://api.symfony.com/./symfony/component/console/event/consolecommandevent.html generated on September, 0 Chapter : Utilisez les événements
0 // récupérer la commande exécutée $command = $event->getcommand(); // Ecrire à propos de la commande $output->writeln(sprintf('avant le début de l\'exécution de la commande <info>%s</info>', $command->getname())); ); // récupérer l'application $application = $command->getapplication(); L'événement ConsoleEvents::TERMINATE Utilisation courante: Réaliser certains nettoyages après que la commande soit exécutée. Après que la commande soit terminée, l'événement ConsoleEvents::TERMINATE est envoyé. Il peut être utiliser pour faire certaines actions à exécuter pour toutes les commandes ou pour nettoyer ce qui a été initié avec l'écouteur ConsoleEvents::COMMAND ( comme envoyer des logs, fermer une connection à la base de données, envoyer des emails...). Un écouteur peut aussi modifier le code de sortie. Les écouteurs reçoivent un événement de classe ConsoleTerminateEvent Listing - 0 use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\ConsoleEvents; $dispatcher->addlistener(consoleevents::terminate, function (ConsoleTerminateEvent $event) // récupérer la sortie $output = $event->getoutput(); // récupérer la commande exécutée $command = $event->getcommand(); // afficher des informations $output->writeln(sprintf('après l\'exécution de la commande <info>%s</info>', $command->getname())); ); // Changer le code de sortie $event->setexitcode(); Cet événement est aussi envoyé quand une exception est lancée par une commande. Il est envoyé juste avant l'événement ConsoleEvents::EXCEPTION. Le code de sortie reçu dans ce cas est le code d'exception. L'événement ConsoleEvents::EXCEPTION Utilisation courante: Récupérer les exceptions lever lors de l'exécution de la commande.. http://api.symfony.com/./symfony/component/console/event/consoleterminateevent.html generated on September, 0 Chapter : Utilisez les événements
Quand une exception est levée par une commande, l'événement ConsoleEvents::EXCEPTION est envoyé. Un écouteur peut couvrir ou changer l'exception ou changer certaines choses avant que l'exception soit lever par l'application. Les écouteurs reçoivent un événement de classe ConsoleForExceptionEvent Listing - 0 use Symfony\Component\Console\Event\ConsoleForExceptionEvent; use Symfony\Component\Console\ConsoleEvents; $dispatcher->addlistener(consoleevents::exception, function (ConsoleForExceptionEvent $event) $output = $event->getoutput(); $command = $event->getcommand(); $output->writeln(sprintf('oops, L\'exception levée par la commande en cours <info>%s</info>', $command->getname())); // Récupérer le code de sortie (Le code de l'exception ou de sortie défini par l'événement ConsoleEvents::TERMINATE) $exitcode = $event->getexitcode(); // change le type d'exception $event->setexception(new \LogicException('Caught exception', $exitcode, $event->getexception())); );. http://api.symfony.com/./symfony/component/console/event/consoleforexceptionevent.html generated on September, 0 Chapter : Utilisez les événements
Chapter Helper Dialog La classe DialogHelper fournit des fonctions qui demandent des informations à l'utilisateur. Elle est inclue dans les helpers par défaut, que vous pouvez récupérer en appelant gethelperset() : Listing - $dialog = $this->gethelperset()->get('dialog'); Toutes les méthodes du Helper Dialog ont une OutputInterface comme premier argument, la question comme second argument et la valeur par défaut comme dernier argument. Demander une confirmation à l'utilisateur Supposons que vous voulez confirmer une action avant de l'exécuter. AJouter le code suivant à votre commande: Listing - //... if (!$dialog->askconfirmation( $output, '<question>continuer avec cette action?</question>', false )) return; Dans ce cas, la question «Continuer avec cette action?» sera posée à l'utilisateur, et elle retournera true si l'utilisateur répond y, ou false dans les autres cas. Le troisième argument de askconfirmation est la valeur par défaut à retourner si l'utilisateur ne saisit rien.. http://api.symfony.com/./symfony/component/console/helper/dialoghelper.html. http://api.symfony.com/./symfony/component/console/command/command.html#method_gethelperset. http://api.symfony.com/./symfony/component/console/output/outputinterface.html generated on September, 0 Chapter : Helper Dialog
Demander des informations à l'utilisateur Vous pouvez également poser une question dont la réponse serait plus qu'un simple oui/non. Par exemple, si vous voulez connaitre le nom d'un bundle, vous pouvez ajouter le code suivant à votre commande: Listing - //... $bundle = $dialog->ask( $output, 'Veuillez entrer le nom du bundle', 'AcmeDemoBundle' ); La question «Veuillez entrer le nom du bundle» sera posée à l'utilisateur. Il pourra taper un nom qui sera retourné à la méthode ask. S'il n'entre aucun nom, la valeur par défaut (AcmeDemoBundle dans ce cas) sera retournée. Autocompletion New in version.: L'autocomplétion pour les questions a été ajoutée dans Symfony.. Vous pouvez aussi spécifier un tableau de réponses possibles pour une question donnée. La réponse de l'utilisateur sera auto-complétée à mesure qu'il tape: Listing - $dialog = $this->gethelperset()->get('dialog'); $bundlenames = array('acmedemobundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); $name = $dialog->ask( $output, 'Please enter the name of a bundle', 'FooBundle', $bundlenames ); Cacher la réponse de l'utilisateur New in version.: La méthode askhiddenresponse a été ajoutée dans Symfony.. Vous pouvez également poser une question et cacher la réponse. Ceci est particulièrement pratique pour les mots de passe: Listing - $dialog = $this->gethelperset()->get('dialog'); $password = $dialog->askhiddenresponse( $output, 'Quel le mot de passe de la base de données?', false ); Lorsque vous demandez une réponse cachée, Symfony utilisera soit un binaire, soit il changera le mode stty, soit il utilisera autre chose pour cacher la réponse. Si aucun n'est disponible, il se rabattra sur une question classique à moins que vous n'ayez passé false comme troisième argument, comme dans l'exemple ci-dessus. Dans ce cas, une RuntimeException sera levée. generated on September, 0 Chapter : Helper Dialog
Poser une question et valider la réponse Vous pouvez même valider la réponse. Par exemple, dans le dernier exemple, vous avez demandé le nom d'un bundle. En suivant les conventions de nommage de Symfony, ce nom doit avoir Bundle comme suffixe. Vous pouvez valider cela en utilisant la méthode askandvalidate() : Listing - 0 //... $bundle = $dialog->askandvalidate( $output, 'Veuillez entrer le nom du bundle', function ($answer) if ('Bundle'!== substr($answer, -)) throw new \RunTimeException( 'Le nom du bundle doit avoir \'Bundle\' comme suffixe.' ); return $answer;, false, 'AcmeDemoBundle' ); Cette méthode a nouveaux arguments, sa signature complète est: Listing - askandvalidate( OutputInterface $output, string array $question, callback $validator, integer $attempts = false, string $default = null ) $validator est un callback qui prend en charge la validation. Il devrait lever une exception si quelque chose se passe mal. Le message de l'exception sera affiché dans la console, donc c'est une bonne pratique d'y mettre des informations utiles. Vous pouvez définir le nombre maximum de demandes dans l'argument $attempts. Si vous atteignez ce nombre, la valeur par défaut donnée en dernier argument sera choisie. Utiliser false revient à définir un nombre d'essais illimité. La demande sera faite à l'utilisateur jusqu'à ce qu'il propose une réponse valide. Cacher la réponse de l'utilisateur New in version.: La méthode askhiddenresponseandvalidate a été ajoutée dans Symfony.. Vous pouvez poser une question et valider une réponse cachée: Listing - $dialog = $this->gethelperset()->get('dialog'); $validator = function ($value) if (trim($value) == '') throw new \Exception('Le mot de passe ne peut pas être vide'); return $value; ;. http://api.symfony.com/./symfony/component/console/helper/dialoghelper.html#method_askandvalidate generated on September, 0 Chapter : Helper Dialog
0 $password = $dialog->askhiddenresponseandvalidate( $output, 'Veuillez entrer le nom du widget', $validator, 0, false ); Si vous voulez permettre qu'une réponse soit visible si elle ne peut pas être cachée pour une raison quelconque, passez true comme cinquième argument. Laisser l'utilisateur choisir parmi une liste de réponse New in version.: La méthode select() a été ajouté depuis Symfony.. Si vous déterminez une liste de réponse dans laquelle l'utilisateur peut choisir, vous pouvez utiliser la méthode ask citée précédemment, pour être sùr de la réponse de l'utilisateur, la méthode askandvalidate.les deux ont le même désavantage. Vous devez vous occuper de la gestion des valeurs incorrectes. Au lieu de cela, vous pouvez utiliser la méthode select(), qui permet de restreindre la saisie à la liste prédéfinie: Listing - 0 $dialog = $app->gethelperset()->get('dialog'); $colors = array('rouge', 'bleu', 'jaune'); $color = $dialog->select( $output, 'Svp choisissez une couleur (par défaut rouge)', $colors, 0 ); $output->writeln('vous venez de sélectionner : '.$colors[$color]); //... Utilisez la variable $color L'option par défaut qui est utilisée, est fournie par le quatrième argument. Si cet argument est à null, Cela signifie qu'il n'y a pas de valeur par défaut. Si l'utilisateur propose une valeur incorrecte, un message d'erreur est affiché et il lui est demandé de faire à nouveau une proposition, jusqu'à ce que l'utilisateur entre une valeur correcte ou que le nombre d'essais soit atteint ( que vous pouvez définir dans le cinquième argument). Le nombre d'essais est par défaut à false, ce qui signifie qu'il n'y a pas de limite d'essais. Vous pouvez définir votre message d'erreur dans le sixième argument New in version.: le support de Multiselect a été ajouté à Symfony.. Multiple Choices Certaines fois, de multiple réponses pourraient être valides. Le DialogHelper permet cette fonctionnalité en utilisant des valeurs séparées par des virgules. Cette possibilité est désactivée par défaut. Pour l'activer, définissez le septième argument à true: Listing -0. http://api.symfony.com/./symfony/component/console/helper/dialoghelper.html#method_select. http://api.symfony.com/./symfony/component/console/helper/dialoghelper.html#method_select generated on September, 0 Chapter : Helper Dialog
0 //... $selected = $dialog->select( $output, 'Sélectionnez votre couleur favorite (par défaut à rouge)', $colors, 0, false, 'La valeur "%s" est incorrecte', true // active l'option multiselect ); $selectedcolors = array_map(function($c) use ($colors) return $colors[$c];, $selected) $output->writeln('vous venez de choisir: '. implode(', ', $selectedcolors)); Maintenant, quand les utilisateurs saisissent,, Le résultat obtenu est : Vous venez de choisir: bleu, jaune. Tester une commande nécessitant une entrée Si vous écrivez un test unitaire pour une commande qui nécessite la saisie dans la ligne de commande, vous aurez besoin de surcharger le HelperSet utilisé par la commande: Listing - 0 0 use Symfony\Component\Console\Helper\DialogHelper; use Symfony\Component\Console\Helper\HelperSet; //... public function testexecute() //... $commandtester = new CommandTester($command); $dialog = $command->gethelper('dialog'); $dialog->setinputstream($this->getinputstream("test\n")); // Equivalent à l'entrée par l'utilisateur de "Test" et appuie sur ENTER // Si vous avez besoin d'une confirmation, "yes\n" fonctionne. $commandtester->execute(array('command' => $command->getname())); // $this->assertregexp('/.../', $commandtester->getdisplay()); protected function getinputstream($input) $stream = fopen('php://memory', 'r+', false); fputs($stream, $input); rewind($stream); return $stream; En définissant le inputstream du DialogHelper, vous imitez ce que fait la console en interne avec tous les utilisateurs qui entrent des données via la ligne de commande. De cette façon, vous pouvez tester toute interaction de l'utilisateur (même complexes) en passant les bonnes valeurs. generated on September, 0 Chapter : Helper Dialog
Chapter Formatter Helper Le Formatter Helper fournit des fonctions pour formater la sortie avec les couleurs. Vous pouvez faire des choses plus avancées avec cette aide Ajouter de la couleur à l'affichage. La méthode Command::getHelperSet : Listing - $formatter = $this->gethelperset()->get('formatter'); Les méthodes retournent une chaîne, ce que vous pouvez rendre directement avec OutputInterface::writeln. Ecrire un message avec une section Symfony propose un style défini lors de l'affichage d'un message qui appartient à une certaine «section». Elle affiche la section en couleur et avec des crochets autour d'elle et le message réelle à sa droite. La couleur en moins, cela pourrait ressembler à ça: Listing - [UneSection] Un message en rapport avec cette section Pour reproduire ce style, vous pouvez utilisez la méthode formatsection() : Listing - $formattedline = $formatter->formatsection( 'Unesection', 'Un message en rapport avec cette section' ); $output->writeln($formattedline);. http://api.symfony.com/./symfony/component/console/helper/formatterhelper`fait partie des helpers par défaut, que vous pouvez obtenir en appelant la méthode :method:`symfony/component/console/command/command::gethelperset.html. http://api.symfony.com/./symfony/component/console/output/outputinterface.html#writeln(). http://api.symfony.com/./symfony/component/console/helper/formatterhelper.html#formatsection() generated on September, 0 Chapter : Formatter Helper
Ecrire un message dans un bloc Certaines fois, vous souhaiteriez afficher un bloc de texte avec une couleur de fond. Symfony l'utilise pour afficher des messages d'erreur. Si vous écrivez vos messages d'erreur sur plus d'une ligne, vous pouvez remarquerez que le fond est aussi long que chaque ligne. Pour générer un bloc utilisez la méthode formatblock() : Listing - $errormessages = array('erreur!', 'Quelque chose ne vas pas'); $formattedblock = $formatter->formatblock($errormessages, 'error'); $output->writeln($formattedblock); Comme vous pouvez le voir, on peux passer un tableau de messages à la méthode formatblock() pour créer la sortie désirée. Si vous passez true en eme paramètre, le bloc est formaté avec plus de style (une ligne vide au dessus et en dessous des messages, ainsi que deux espaces à gauche et à droite). Dans le cas précédent, vous utilisiez le style prédéfini error, mais vous pouvez créer votre propre style, regardez Ajouter de la couleur à l'affichage.. http://api.symfony.com/./symfony/component/console/helper/formatterhelper.html#formatblock(). http://api.symfony.com/./symfony/component/console/helper/formatterhelper.html#formatblock() generated on September, 0 Chapter : Formatter Helper 0
Chapter Progress Helper New in version.: Le Helper progress a été ajouté à Symfony.. New in version.: La méthode setcurrent a été ajouté à Symfony.. Quand vous exécutez une commande avec une longue execution, il peut être utile d'afficher la progression de celle-ci: Pour afficher les détails de la progression, utilisez la classe ProgressHelper, passez lui le nombre total d'unités, et avancer la progression au fur et à mesure que la commande s'execute: Listing - 0 $progress = $this->gethelperset()->get('progress'); $progress->start($output, 0); $i = 0; while ($i++ < 0) //... Le code à exécuter // avance la barre de progression d'une unité $progress->advance(); $progress->finish(); Vous pouvez aussi définir la progression en cours en appelant la méthode setcurrent().. http://api.symfony.com/./symfony/component/console/helper/progresshelper.html. http://api.symfony.com/./symfony/component/console/helper/progresshelper.html#setcurrent() generated on September, 0 Chapter : Progress Helper
L'apparence de la barre de progression peut aussi être personnalisée, avec de nombreux niveaux de verbosité. Chacun d'eux affiche différents éléments possibles - comme le pourcentage d'achèvement, une barre de progression mobile ou l'information en cours/total (ex. 0/0): Listing - $progress->setformat(progresshelper::format_quiet); $progress->setformat(progresshelper::format_normal); $progress->setformat(progresshelper::format_verbose); $progress->setformat(progresshelper::format_quiet_nomax); // la valeur par défaut $progress->setformat(progresshelper::format_normal_nomax); $progress->setformat(progresshelper::format_verbose_nomax); En plus, vous pouvez contrôler les différents caractères et la largeur utilisés pour la barre de progression: Listing - // la dernière partie de la barre $progress->setbarcharacter('<comment>=</comment>'); // la partie en cours de la barre $progress->setemptybarcharacter(' '); $progress->setprogresscharacter(' '); $progress->setbarwidth(0); Pour voir toutes les options possibles, regarder la documentation de l'api à la classe ProgressHelper. Pour des questions de performances, faites attention si vous définissez un grand nombre total d'étapes. Par exemple, si vous itérez à travers un grand nombre d'éléments, envisager de définir la fréquence de rafraîchissement à une valeur plus haute en appelant la méthode setredrawfrequency(), afin de ne mettre à jour qu'à certaines iterations: Listing - 0 $progress->start($output, 0000); // mis à jour que tous les 00 itérations $progress->setredrawfrequency(00); $i = 0; while ($i++ < 0000) //... Le code à exécuter $progress->advance();. http://api.symfony.com/./symfony/component/console/helper/progresshelper.html. http://api.symfony.com/./symfony/component/console/helper/progresshelper.html#setredrawfrequency() generated on September, 0 Chapter : Progress Helper
Chapter Table Helper New in version.: Le Helper table a été ajouté à Symfony.. Quand vous développez une application console, il peut être utile d'afficher des tableaux de données: Pour afficher un tableau, utilisez la classe TableHelper, définissez les entêtes, les lignes et affichez: Listing - 0 $table = $app->gethelperset()->get('table'); $table ->setheaders(array('isbn-', 'TITRE', 'AUTEUR')) ->setrows(array( array('-00', 'La divine comédie', 'Dante Alighieri'), array('-00', 'Le conte de deux cités', 'Charles Dickens'), array('-', 'Le seigneur des anneaux', 'J. R. R. Tolkien'), array('-00', 'Les dix petits nègres', 'Agatha Christie'), )) ; $table->render($output); L'aspect du tableau peut aussi être modifié. Il y a deux façons de personnaliser le rendu du tableau: utilisé des layouts nommés ou en personnalisant les options de rendu. Personnaliser l'aspect d'un tableau en utilisant des layouts nommés Le Helper Table embarque deux layouts déjà configurés:. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html generated on September, 0 Chapter : Table Helper
TableHelper::LAYOUT_DEFAULT TableHelper::LAYOUT_BORDERLESS Le Layout peut être défini avec la méthode setlayout(). Personnaliser l'aspect d'un tableau en utilisant les options de rendu Vous pouvez contrôler le rendu du tableau en définissant avec des valeurs personnalisées les options de rendu: setpaddingchar() sethorizontalborderchar() setverticalborderchar() setvrossingchar() setvellheaderformat() setvellrowformat() setborderformat() setpadtype() 0. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setlayout. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setpaddingchar. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_sethorizontalborderchar. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setverticalborderchar. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setvrossingchar. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setvellheaderformat. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setvellrowformat. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setborderformat 0. http://api.symfony.com/./symfony/component/console/helper/tablehelper.html#method_setpadtype generated on September, 0 Chapter : Table Helper
Chapter Le Composant CssSelector Le Composant CssSelector convertit des sélecteurs CSS en expressions XPath. Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt Git officiel (https://github.com/symfony/cssselector ) ; Installez le via Composer (symfony/css-selector sur Packagist ). Utilisation Pourquoi utiliser des sélecteurs CSS? Lorsque vous parcourez un document HTML ou XML, la méthode la plus puissante est de loin d'utiliser XPath. Les expressions XPath sont incroyablement flexibles, ce qui fait qu'il y a presque toujours une expression XPath qui va trouver l'élément dont vous avez besoin. Malheureusement, ces expressions peuvent aussi devenir très compliquées, et la courbe d'apprentissage est raide. Même les opérations communes (comme trouver un élément ayant une classe particulière) peuvent requérir des expressions longues et difficile à manier. Beaucoup de développeurs -- en particulier les développeurs web -- sont plus à l'aise avec l'utilisation de sélecteurs CSS pour trouver des éléments. En plus de fonctionner dans les feuilles de styles, les sélecteurs CSS sont aussi utilisés en Javascript avec la fonction queryselectorall et dans des bibliothèques Javascript populaires telles jquery, Prototype et MooTools. Les sélecteurs CSS sont moins puissants que XPath, mais sont beaucoup plus facile à écrire, lire et comprendre. Comme ils sont moins puissants, quasiment tous les sélecteurs CSS peuvent être convertis. https://github.com/symfony/cssselector. https://packagist.org/packages/symfony/css-selector generated on September, 0 Chapter : Le Composant CssSelector
en un équivalent XPath. Cette expression XPath peut dès lors être utilisée avec d'autres fonctions et classes qui utilisent XPath pour trouver des éléments dans un document. Le composant CssSelector Le seul objectif de ce composant est de convertir des sélecteurs CSS en leurs équivalents XPath: Listing - use Symfony\Component\CssSelector\CssSelector; print CssSelector::toXPath('div.item > h > a'); Cela vous affiche ce qui suit : Listing - descendant-or-self::div[contains(concat(' ',normalize-space(@class), ' '), ' item ')]/h/a Vous pouvez utiliser cette expression avec, par exemple, DOMXPath ou avec SimpleXMLElement afin de trouver des éléments dans un document. La méthode Crawler::filter() utilise le composant CssSelector pour trouver des éléments en se basant sur une chaîne de caractères représentant un sélecteur CSS. Lisez Le Composant DomCrawler pour plus de détails. Limitations du composant CssSelector Seulement certains sélecteurs CSS peuvent être convertis en un équivalent XPath. Il y a plusieurs sélecteurs CSS qui n'ont de sens que dans le contexte d'un navigateur web. sélecteurs basés sur le statut d'un lien : :link, :visited, :target sélecteurs basés sur l'action d'un utilisateur : :hover, :focus, :active sélecteurs basés sur l'état de l'interface utilisateur : :enabled, :disabled, :indeterminate (cependant, :checked et :unchecked sont disponibles) Les pseudo-éléments (:before, :after, :first-line, :first-letter) ne sont pas supportés car ils sélectionnent des portions de texte plutôt que des éléments. Plusieurs pseudo-classes ne sont pas encore supportées : :lang(language) root *:first-of-type, *:last-of-type, *:nth-of-type, *:nth-last-of-type, *:only-oftype. (Ceux-ci fonctionnent avec un nom d'élément (par exemple : li:first-of-type) mais pas avec *.. http://php.net/manual/en/class.domxpath.php. http://php.net/manual/en/class.simplexmlelement.php. http://api.symfony.com/./symfony/component/domcrawler/crawler.html#filter() generated on September, 0 Chapter : Le Composant CssSelector
Chapter Le Composant Debug Le composant Debug founit des outils pour faciliter le debugging du code PHP. New in version.: Le Composant Debug est nouveau en Symfony.. Précédemment, les classes étaient situées dans le composant HttpKernel. Installation Vous pouvez installer le composant de deux manières différentes : Utilisez le dépôt Git officiel (https://github.com/symfony/debug ); Installez le via Composer (symfony/debug sur Packagist ). Utilisation Le composant Debug fournit quelques outils pour vous aider à débugger du code PHP. Activer ces outils est aussi simple que de faire Listing - use Symfony\Component\Debug\Debug; Debug::enable(); La méthode enable() enregistre un error handler (gestionnaire d'erreur), un exception handler (gestionnaire d'exception) et une classe loader spéciale. Lisez les sections suivantes pour plus d'informations sur les différents outils disponibles.. https://github.com/symfony/debug. https://packagist.org/packages/symfony/debug. http://api.symfony.com/./symfony/component/debug/debug.html#method_enable generated on September, 0 Chapter : Le Composant Debug
Vous ne devriez jamais activer les outils de debug en environnement de production car ils divulgueraient des informations sensibles à l'utilisateur. Activer l'error Handler La classe ErrorHandler attrape les erreurs PHP et les convertit en exceptions (de la classe ErrorException ou FatalErrorException pour les fatal erreurs PHP) Listing - use Symfony\Component\Debug\ErrorHandler; ErrorHandler::register(); Activer l'exception Handler La classe ExceptionHandler attrape les exceptions PHP non rattrapées et les convertit en jolie réponses PHP. C'est utile en mode debug pour remplacer la sortie PHP/XDebug par défaut, par quelque chose de plus joli et plus utile Listing - use Symfony\Component\Debug\ExceptionHandler; ExceptionHandler::register(); Si le composant HttpFoundation est disponible, le handler (gestionnaire) utilise un objet Response de Symfony; le cas échéant, il retourne une réponse PHP standard.. http://api.symfony.com/./symfony/component/debug/errorhandler.html. http://php.net/manual/en/class.errorexception.php. http://api.symfony.com/./symfony/component/debug/exception/fatalerrorexception.html. http://api.symfony.com/./symfony/component/debug/exceptionhandler.html generated on September, 0 Chapter : Le Composant Debug
Chapter Débugger un Class Loader New in version.: Le DebugClassLoader du composant Debug est nouveau dans Symfony.. Précédemment, cette classe était située dans le composant Class Loader. La classe DebugClassLoader tente de jeter des exceptions plus explicites lorsqu'une classe n'est pas retrouvée par les autoladers enregistrés. Tous les autoloaders qui implémentent la méthode findfile() sont remplacés avec un wrapper DebugClassLoader. L'utilisation de DebugClassLoader est aussi facile que d'appeler la méthode statique enable() Listing - use Symfony\Component\ClassLoader\DebugClassLoader; DebugClassLoader::enable();. http://api.symfony.com/./symfony/component/debug/debugclassloader.html. http://api.symfony.com/./symfony/component/debug/debugclassloader.html#method_enable generated on September, 0 Chapter : Débugger un Class Loader
Chapter Le Composant d'injection de Dépendance («Dependency Injection» en anglais) Le composant d'injection de Dépendance vous permet de standardiser et de centraliser la manière dont les objets sont construits dans votre application. Pour une introduction sur l'injection de Dépendance et des conteneurs de service, lisez le chapitre Service Container. Installation Vous pouvez installer le composant de deux manières différentes : Utilisez le dépôt Git officiel (https://github.com/symfony/dependencyinjection ) ; Installez le via Composer (symfony/dependency-injection sur Packagist ). Utilisation Basique Vous pourriez avoir une classe toute simple comme par exemple Mailer, que l'on peut voir ci-dessous, que vous voulez rendre disponible en tant que service: Listing - class Mailer private $transport; public function construct(). https://github.com/symfony/dependencyinjection. https://packagist.org/packages/symfony/dependency-injection generated on September, 0 Chapter : Le Composant d'injection de Dépendance («Dependency Injection» en anglais) 0
0 $this->transport = 'sendmail'; //... Vous pouvez enregistrer cette dernière dans le conteneur en tant que service: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->register('mailer', 'Mailer'); Une amélioration que l'on pourrait apporter à la classe afin de la rendre plus flexible serait de permettre au conteneur de définir la propriété transport utilisée. Si vous changez la classe afin que la propriété soit passée au constructeur, cela nous donne: Listing - 0 class Mailer private $transport; public function construct($transport) $this->transport = $transport; //... Ensuite, vous pouvez définir votre choix de transport dans le conteneur: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container ->register('mailer', 'Mailer') ->addargument('sendmail'); Cette classe est maintenant beaucoup plus flexible car vous avez séparé le choix du transport - qui est maintenant du ressort du conteneur - de l'implémentation de la classe. Le mode de transport d'email que vous avez choisi pourrait être quelque chose que d'autres services ont besoin de connaître. Vous pouvez éviter d'avoir à le changer à différents endroits en en faisant un paramètre dans le conteneur et en y faisant référence par la suite lorsque vous définissez l'argument du constructeur du service Mailer: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->setparameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addargument('%mailer.transport%'); generated on September, 0 Chapter : Le Composant d'injection de Dépendance («Dependency Injection» en anglais)
Maintenant que le service mailer est dans le conteneur, vous pouvez l'injecter comme une dépendance dans d'autres classes. Si vous avez une classe NewsletterManager comme ceci: Listing - 0 class NewsletterManager private $mailer; public function construct(\mailer $mailer) $this->mailer = $mailer; //... Alors vous pouvez aussi l'enregistrer en tant que service et lui passer le service mailer: Listing - 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; $container = new ContainerBuilder(); $container->setparameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addargument('%mailer.transport%'); $container ->register('newsletter_manager', 'NewsletterManager') ->addargument(new Reference('mailer')); Si le NewsletterManager n'avait pas toujours besoin du Mailer et que l'injection était optionnelle, alors vous pourriez utiliser une injection par mutateur à la place: Listing - 0 class NewsletterManager private $mailer; public function setmailer(\mailer $mailer) $this->mailer = $mailer; //... Vous pouvez maintenant choisir de ne pas injecter un Mailer dans le NewsletterManager. Mais si vous le désirez, alors le conteneur peut appeler la méthode du mutateur: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; $container = new ContainerBuilder(); $container->setparameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') generated on September, 0 Chapter : Le Composant d'injection de Dépendance («Dependency Injection» en anglais)
0 ->addargument('%mailer.transport%'); $container ->register('newsletter_manager', 'NewsletterManager') ->addmethodcall('setmailer', array(new Reference('mailer'))); Vous pourriez alors récupérer votre service newsletter_manager depuis le conteneur comme cela: Listing -0 use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); //... $newslettermanager = $container->get('newsletter_manager'); Eviter que votre code devienne dépendant du Conteneur Tandis que vous pouvez récupérer directement des services depuis le conteneur, il est plus judicieux de minimiser cela. Par exemple, dans le NewsletterManager, vous avez injecté le service mailer plutôt que de le demander depuis le conteneur. Vous pourriez avoir injecté le conteneur et ensuite récupéré depuis ce dernier le service mailer mais cela voudrait dire que ce service serait lié à ce conteneur en particulier rendant ainsi difficile la réutilisation de cette classe quelque part d'autre. Vous allez devoir récupérer un service depuis le conteneur à un moment ou à un autre mais cela devrait être limité autant que possible au point d'entrée de votre application. Initialiser le Conteneur avec des fichiers de configuration Tout comme vous avez initialisé vos services en utilisant PHP ci-dessus, vous pouvez aussi utiliser des fichiers de configuration. Cela vous permet d'écrire les définitions de services au format XML ou YAML au lieu de le faire en PHP comme montré dans les exemples ci-dessus. Dans le cadre de grosses applications, il est important d'organiser les définitions de services en les plaçant dans un ou plusieurs fichiers. Pour faire cela, vous devez installer le composant «Config». Chargement d'un fichier de configuration XML: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; $container = new ContainerBuilder(); $loader = new XmlFileLoader($container, new FileLocator( DIR )); $loader->load('services.xml'); Chargement d'un fichier de configuration YAML Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; $container = new ContainerBuilder(); generated on September, 0 Chapter : Le Composant d'injection de Dépendance («Dependency Injection» en anglais)
$loader = new YamlFileLoader($container, new FileLocator( DIR )); $loader->load('services.yml'); Si vous voulez charger des fichiers de configuration alors vous aurez également besoin d'installer Le composant YAML. Si vous souhaitez utiliser PHP pour créer des services, vous pouvez le déplacer dans un fichier de configuration séparé comme ceci: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; $container = new ContainerBuilder(); $loader = new PhpFileLoader($container, new FileLocator( DIR )); $loader->load('services.php'); Les services newsletter_manager et mailer peuvent aussi être initialisés en utilisant des fichiers de configuration : Listing - 0 parameters: #... mailer.transport: sendmail services: mailer: class: Mailer arguments: ["%mailer.transport%"] newsletter_manager: class: NewsletterManager calls: - [setmailer, ["@mailer"]] generated on September, 0 Chapter : Le Composant d'injection de Dépendance («Dependency Injection» en anglais)
Chapter 0 Types d'injection Rendre les dépendances d'une classe explicites et exiger qu'elles soient injectées dans cette dernière est une bonne manière de rendre une classe plus réutilisable, testable, et découplée des autres. Il y a plusieurs manières d'injecter des dépendances. Chaque type d'injection a ses propres avantages et inconvénients à prendre en considération, ainsi que différentes façons de fonctionner lorsque vous les utilisez avec le conteneur de service. Injection via le Constructeur La manière la plus commune d'injecter des dépendances est via le constructeur de la classe. Pour effectuer cela, vous avez besoin d'ajouter un argument à la signature du constructeur afin d'accepter la dépendance: Listing 0-0 class NewsletterManager protected $mailer; public function construct(\mailer $mailer) $this->mailer = $mailer; //... Vous pouvez spécifier quel service vous souhaiteriez injecter dans cette classe via la configuration du conteneur de service : Listing 0- services: my_mailer: #... newsletter_manager: class: NewsletterManager arguments: ["@my_mailer"] generated on September, 0 Chapter 0: Types d'injection
Le fait de requérir l'injection d'un certain type d'objet signifie que vous pouvez être sûr qu'une dépendance appropriée a été injectée. Grâce à cela, vous allez recevoir immédiatement une erreur claire si une dépendance inappropriée est injectée. En forçant le type grâce à une interface plutôt que via une classe, vous pouvez rendre le choix de la dépendance plus flexible. Et en supposant que vous utilisez uniquement des méthodes définies dans l'interface, vous pouvez tirer parti de cette flexibilité tout en continuant d'utiliser l'objet de manière sécurisée. Utiliser l'injection via le constructeur propose plusieurs avantages : Si la dépendance est une condition requise et que la classe ne peut pas fonctionner sans elle, alors l'injecter via le constructeur permet de s'assurer que la dépendance sera présente lorsque la classe sera utilisée puisque la classe ne peut pas être construite sans elle. Le constructeur est appelé seulement une fois lorsque l'objet est créé, donc vous pouvez être sûr que la dépendance ne changera pas pendant la durée de vie de l'objet. Ces avantages signifient que l'injection via constructeur n'est pas envisageable pour travailler avec des dépendances optionnelles. Ce type d'injection est aussi plus difficile à utiliser avec les hiérarchies de classe : si une classe utilise l'injection via le constructeur, alors l'étendre et surcharger le constructeur devient problématique. Injection via Mutateur Un autre type possible d'injection dans une classe se fait par l'ajout d'une méthode mutateur qui accepte la dépendance: Listing 0-0 class NewsletterManager protected $mailer; public function setmailer(\mailer $mailer) $this->mailer = $mailer; //... Listing 0- services: my_mailer: #... newsletter_manager: class: NewsletterManager calls: - [setmailer, ["@my_mailer"]] Cette fois les avantages sont : L'injection par mutateur fonctionne bien avec les dépendances optionnelles. Si vous n'avez pas besoin de la dépendance, alors n'appelez pas le mutateur, tout simplement ; Vous pouvez appeler le mutateur plusieurs fois. Cela est particulièrement utile si la méthode ajoute la dépendance dans une collection. Vous pouvez ainsi avoir un nombre variable de dépendances. Les inconvénients d'une injection par mutateur sont : generated on September, 0 Chapter 0: Types d'injection
Le mutateur peut encore être appelé après la construction donc vous ne pouvez pas être sûr que la dépendance n'ait pas été remplacée pendant la durée de vie de l'objet (excepté si vous ajoutez une vérification explicite dans la méthode mutateur qui contrôle s'il n'a pas déjà été appelé) ; Vous ne pouvez pas être sûr que le mutateur sera appelé et vous devez ajouter des contrôles qui vérifient que toute dépendance requise est injectée. Injection via une Propriété Une autre possibilité est de simplement définir des champs publics dans la classe: Listing 0- class NewsletterManager public $mailer; //... Listing 0- services: my_mailer: #... newsletter_manager: class: NewsletterManager properties: mailer: "@my_mailer" Utiliser l'injection via une propriété n'apporte presque que des inconvénients, cette méthode est similaire à l'injection par mutateur mais avec d'autres problèmes importants en plus : Vous ne pouvez pas du tout contrôler quand la dépendance est définie, elle peut être changée à n'importe quel moment pendant la durée de vie de l'objet ; Vous ne pouvez pas utiliser la détection de type donc vous ne pouvez pas être sûr du type de la dépendance injectée excepté si vous écrivez dans le code de la classe un test qui vérifie l'objet instancié avant de l'utiliser. Mais, il est utile de savoir que ceci peut être effectué avec le conteneur de service, spécialement si vous travaillez avec du code qui n'est pas sous votre contrôle, comme avec une bibliothèque tierce, qui utilise des propriétés publiques pour ses dépendances. generated on September, 0 Chapter 0: Types d'injection
Chapter Introduction aux paramètres Vous pouvez définir des paramètres dans le conteneur de services qui peuvent être directement utilisé ou en partie dans les définitions des services. Cela peut aider à séparer les valeurs que vous changez régulièrement. Récupérer et définir les paramètres du Conteneur Travailler avec les paramètres du conteneur est très facile si vous utilisez les méthodes d'accès du conteneur pour les paramètres. Vous pouvez contrôler qu'un paramètre a été défini dans le conteneur avec: Listing - $container->hasparameter('mailer.transport'); Vous pouvez récupérer des paramètres définis dans le conteneur avec: Listing - $container->getparameter('mailer.transport'); et définir un paramètre dans le conteneur grâce à: Listing - $container->setparameter('mailer.transport', 'sendmail'); Vous ne pouvez définir un paramètre qu'avant que le conteneur soit compilé. Pour en apprendre plus sur la compilation du conteneur, lisez Compiler le Conteneur. Paramètres dans le fichier de configuration Vous pouvez aussi utiliser la section parameters du fichier de configuration pour définir des paramètres: Listing - generated on September, 0 Chapter : Introduction aux paramètres
parameters: mailer.transport: sendmail Vous pouvez récupérer les valeurs des paramètres directement dans le conteneur, vous pouvez aussi les utiliser dans les fichiers de configuration. Vous pouvez les référencer en les entourant de signe pourcentage, exemple %mailer.transport%. Vous pouvez les utiliser pour injecter ces valeurs dans vos services. Cela permet de configurer différentes versions de services entre applications ou plusieurs services basés sur une même classe, mais configurer différemment dans une seule application. Vous pouvez injecter le choix du transport du courrier directement dans la classe Mailer. En créant un paramètre, cela facilite les changements de ce paramètre plutôt que de les intégrer dans la définition du service. Listing - parameters: mailer.transport: sendmail services: mailer: class: Mailer arguments: ['%mailer.transport%'] Les valeurs entre les tags parameter dans votre configuration XML ne sont pas "trimmée" (les espaces, les retours à la ligne, etc. ne sont pas retirées). Cela signifie que la configuration ci-dessous aura comme valeur \n sendmail\n: Listing - <parameter key="mailer.transport"> sendmail </parameter> Dans certains cas (pour les constantes ou les noms de classes), il se peut que cela génère des erreurs. Pour éviter cela, vous devez toujours écrire la définition de vos paramètres sur une ligne : Listing - <parameter key="mailer.transport">sendmail</parameter> Si vous l'utilisez ailleurs aussi, vous n'aurez besoin que de changer la valeur du paramètre à un seul endroit. Vous pouvez également utiliser les paramètres dans la définition du service, par exemple, en ajoutant le paramètre dans la classe du service: Listing - parameters: mailer.transport: sendmail mailer.class: Mailer services: mailer: class: '%mailer.class%' arguments: ['%mailer.transport%'] generated on September, 0 Chapter : Introduction aux paramètres
Le signe pourcentage dans un paramètre ou un argument, faisant parti de la chaîne de caractère, doit être échappé avec un autre signe pourcentage: Listing - arguments: ['http://symfony.com/?foo=%%s&bar=%%d'] Tableau de paramètres Les paramètres ne sont pas nécessairement de simple chaîne de caractères, ils peuvent être aussi des tableaux. Pour le format XML, vous aurez besoin d'utiliser l'attribut type="collection" pour tous les paramètres qui sont des tableaux. Listing -0 0 # app/config/config.yml parameters: my_mailer.gateways: - mail - mail - mail my_multilang.language_fallback: en: - en - fr fr: - fr - en Des constantes en paramètres Le conteneur supporte aussi la définition des constantes PHP comme paramètres. Pour tirer parti de cette fonctionnalité, il faut mapper le nom de la constant à une clé de paramètre, et définissez son type comme constant. Listing - 0 <?xml version=".0" encoding="utf-"?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w.org/00/xmlschema-instance"> <parameters> <parameter key="global.constant.value" type="constant">global_constant</parameter> <parameter key="my_class.constant.value" type="constant">my_class::constant_name</parameter> </parameters> </container> generated on September, 0 Chapter : Introduction aux paramètres 0
Cela ne fonctionne pas la configuration Yaml. Si vous utilisez Yaml, vous pouvez importer un fichier XML pour tirer parti de cette fonctionnalité: Listing - # app/config/config.yml imports: - resource: parameters.xml Mots clés PHP en XML Par défaut, true, false et null en XML sont transformés en mots clés PHP (respectivement true, false et null) : Listing - <parameters> <parameter key="mailer.send_all_in_once">false</parameters> </parameters> <!-- après parsing $container->getparameter('mailer.send_all_in_once'); // retourne false --> Pour ne pas avoir cette transformation, utilisez le type string : Listing - <parameters> <parameter key="mailer.some_parameter" type="string">true</parameter> </parameters> <!-- après parsing $container->getparameter('mailer.some_parameter'); // retourne "true" --> Ceci n'est pas disponible si vous le faites en YAML ou en PHP, parce que ces langages supportent déjà les mots clés PHP. Syntaxe pour la référence de service Vous pouvez bien évidemment référencer vos services, la syntaxe sera un peu différente pour chacun des formats. Il vous est possible de configurer leur comportement si le service référencé n'existe pas. Par défaut, une exception sera levée quand un service référencé est inexistant. YAML Commencez la chaine de caractère avec @ ou @? pour référencer un service en YAML. @mailer référence le service mailer. Si le service n'existe pas, une exception sera levée; @?mailer références le service mailer. Si le service n'existe pas, il sera ignoré; generated on September, 0 Chapter : Introduction aux paramètres
Utilisez @@ pour échaper le symbole @ en YAML. @@mailer sera converti en la chaîne de caractères "@mailer" au lieu de référencer le service mailer. XML En XML, utilisez le type service. Le comportement peut être spécifié si le service n'existe pas en utilisant l'argument on-invalid. Par défaut, une exception est levée. Les valeurs valides pour l'argument oninvalid sont null (utilisez null au lieu d'un service manquant) ou ignored (très similaire à null, sauf dans le cas d'un appel de méthode, celui-ci est supprimé). PHP En PHP, vous pouvez utiliser la classe Reference pour référencer un service. Le comportement dinvalidité est configuré en utilisant le second argument et constantes du constructeur de l'interface ContainerInterface.. http://api.symfony.com/./symfony/component/dependencyinjection/reference.html. http://api.symfony.com/./symfony/component/dependencyinjection/containerinterface.html generated on September, 0 Chapter : Introduction aux paramètres
Chapter Travailler avec les définitions du conteneur Récupérer et définir les définitions de service Il existe aussi des méthodes utiles pour travailler avec les définitions de service. Pour savoir s'il y a une définition pour un certain ID de service: Listing - $container->hasdefinition($serviceid); Cela est utile si vous voulez faire quelque chose uniquement si une définition particulière existe. Vous pouvez récupérer une définition avec: Listing - $container->getdefinition($serviceid); ou: Listing - $container->finddefinition($serviceid); qui contrairement à getdefinition() résoud aussi les alias donc si un argument $serviceid est un alias, vous allez récupérer la définition sous-jacente. Les définitions de service elles-mêmes sont des objets donc si vous récupérez une définition avec ces méthodes et effectuez des changements sur cette dernière, ils seront répercutés sur le conteneur. Cependant, si vous créez une nouvelle définition alors vous pouvez l'ajouter au conteneur en utilisant: Listing - $container->setdefinition($id, $definition); generated on September, 0 Chapter : Travailler avec les définitions du conteneur
Travailler avec une définition Créer une nouvelle définition Si vous devez créer une nouvelle définition plutôt que d'en manipuler une récupérée depuis le conteneur, alors la classe de définition est Definition. Classe La classe de définition est la classe de l'objet retourné lorsque le service est demandé depuis le conteneur. Pour savoir quelle classe est définie pour une définition: Listing - $definition->getclass(); et pour définir une classe différente: Listing - $definition->setclass($class); // nom de classe entièrement qualifié en tant que chaîne de caractères Arguments du constructeur Pour récupérer un tableau contenant les arguments du constructeur pour une définition, vous pouvez utiliser: Listing - $definition->getarguments(); ou pour récupérer un seul argument via sa position: Listing - $definition->getargument($index); // par exemple : $definition->getarguments(0) pour le premier argument Vous pouvez ajouter un argument à la fin du tableau d'arguments en utilisant: Listing - $definition->addargument($argument); L'argument peut être une chaîne de caractères, un tableau, un paramètre de service en utilisant %nom_de_paramètre% ou un ID de service en utilisant: Listing -0 use Symfony\Component\DependencyInjection\Reference; //... $definition->addargument(new Reference('service_id')); De façon similaire, vous pouvez remplacer un argument déjà défini via son index en utilisant: Listing - $definition->replaceargument($index, $argument); Vous pouvez aussi remplacer tous les arguments (ou en définir quelques-uns s'il n'y en a pas) par un tableau d'arguments:. http://api.symfony.com/./symfony/component/dependencyinjection/definition.html generated on September, 0 Chapter : Travailler avec les définitions du conteneur
Listing - $definition->replacearguments($arguments); Appels de méthode Si le service avec lequel vous travaillez utilise l'injection par mutateur («setter» en anglais), alors vous pouvez aussi manipuler n'importe quels appels de méthode dans les définitions. Vous pouvez récupérer un tableau de tous les appels de méthode avec: Listing - $definition->getmethodcalls(); Ajoutez un appel de méthode avec: Listing - $definition->addmethodcall($method, $arguments); Où $method est le nom de la méthode et $arguments est un tableau d'arguments à utiliser lors de l'appel de la méthode. Les arguments peuvent être des chaînes de caractères, des tableaux, des paramètres ou des IDs de service tout comme pour les arguments du constructeur. Vous pouvez aussi remplacer n'importe quel appel de méthode par un tableau de nouveaux appels grâce à la méthode: Listing - $definition->setmethodcalls($methodcalls); Il y a plein d'exemples d'utilisation des définitions dans les blocs de code PHP d'exemples de configuration dans les pages Utiliser une «Factory» pour créer des services et Gérer les dépendances communes avec des services parents. Les méthodes présentées qui permettent de modifier les définitions des services peuvent seulement être utilisées avant la compilation du conteneur, une fois que le conteneur est compilé, vous ne pouvez plus manipuler les définitions des services. Pour apprendre plus sur la compilation du conteneur, lisez Compiler le Conteneur. generated on September, 0 Chapter : Travailler avec les définitions du conteneur
Chapter Compiler le Conteneur Le conteneur de service peut être compilé pour plusieurs raisons. Ces raisons incluent les vérifications de tout les problèmes potentiels comme par exemple les références circulaires, ainsi que le fait de rendre le conteneur plus efficace en résolvant les paramètres et en supprimant les services qui ne sont pas utilisés. Le conteneur est compilé en exécutant: Listing - $container->compile(); La méthode «compile» utilise des Passes de Compilation pour la compilation. Le composant d'injection de Dépendance est fourni avec plusieurs passes qui sont automatiquement enregistrées pour la compilation. Par exemple, la classe CheckDefinitionValidityPass vérifie les problèmes potentiels qu'il pourrait y avoir avec les définitions qui ont été déclarées dans le conteneur. Après cette passe et d'autres qui se chargent de vérifier la validité du conteneur, des passes de compilation supplémentaires sont utilisées pour optimiser la configuration avant qu'elle soit mise en cache. Par exemple, les services privés et les services abstraits sont supprimés, et les alias sont résolus. Gérer la configuration avec les extensions Tout comme le chargement de la configuration directement dans le conteneur qui est expliqué dans Le Composant d'injection de Dépendance («Dependency Injection» en anglais), vous pouvez gérer le chargement de la configuration en enregistrant des extensions dans le conteneur. La première étape dans le processus de compilation est de charger la configuration depuis n'importe quelle classe d'extension enregistrée dans le conteneur. Contrairement au chargement direct, les extensions ne sont traitées que lorsque le conteneur est compilé. Si votre application est modulaire, alors les extensions permettent à chaque module d'enregistrer et de gérer leur propre configuration de service. Les extensions doivent implémenter ExtensionInterface et peuvent être enregistrées dans le conteneur avec: Listing - $container->registerextension($extension);. http://api.symfony.com/./symfony/component/dependencyinjection/compiler/checkdefinitionvaliditypass.html. http://api.symfony.com/./symfony/component/dependencyinjection/extension/extensioninterface.html generated on September, 0 Chapter : Compiler le Conteneur
Le principal travail de l'extension est accompli dans la méthode load. Dans la méthode de chargement, vous pouvez charger la configuration depuis un ou plusieurs fichiers de configuration tout comme vous pouvez manipuler les définitions du conteneur en utilisant les méthodes montrées dans Travailler avec les définitions du conteneur. La méthode load reçoit en paramètre un conteneur neuf à configurer, qui est ensuite fusionné dans le conteneur dans lequel il est enregistré. Cela vous permet d'avoir plusieurs extensions qui gèrent les définitions de conteneur indépendamment. Les extensions ne s'ajoutent pas à la configuration des conteneurs au moment de l'ajout, mais sont traitées lorsque la méthode compile du conteneur est appelée. Une extension très simple peut charger des fichiers de configuration dans le conteneur: Listing - 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\Config\FileLocator; class AcmeDemoExtension implements ExtensionInterface public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader( $container, new FileLocator( DIR.'/../Resources/config') ); $loader->load('services.xml'); //... Cela n'apporte pas grand chose comparé au fait de charger le fichier directement dans le conteneur global qui est construit. Cela permet juste aux fichiers d'être séparés entre les modules/bundles. Être en mesure de travailler sur la configuration d'un module à partir de fichiers de configuration en dehors du module/bundle est nécessaire pour faire une application complexe et configurable. Cela peut être fait en spécifiant les parties des fichiers de configuration qui sont chargés directement dans le conteneur comme appartement à une extension particulière. Ces parties de la configuration ne seront pas prises en charge directement par le conteneur mais par l'extension concernée. L'Extension doit définir une méthode getalias pour implémenter l'interface: Listing - 0 //... class AcmeDemoExtension implements ExtensionInterface //... public function getalias() return 'acme_demo'; Pour les fichiers de configuration YAML, spécifier l'alias de l'extension comme une clé signifiera que les valeurs seront passées à la méthode load de l'extension : Listing - generated on September, 0 Chapter : Compiler le Conteneur
#... acme_demo: foo: foovalue bar: barvalue Si ce fichier est chargé dans la configuration, alors ses valeurs ne sont traitées que lorsque le conteneur sera compilé et les Extensions chargées: Listing - 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; $container = new ContainerBuilder(); $container->registerextension(new AcmeDemoExtension); $loader = new YamlFileLoader($container, new FileLocator( DIR )); $loader->load('config.yml'); //... $container->compile(); Quand vous chargez un fichier de configuration qui utilise un alias d'extension comme clé, l'extension doit être déjà enregistrée par le constructeur du conteneur ou une exception sera levée. Les valeurs de ces parties de fichiers de configuration sont passées dans le premier argument de la méthode load de l'extension: Listing - public function load(array $configs, ContainerBuilder $container) $foo = $configs[0]['foo']; //foovalue $bar = $configs[0]['bar']; //barvalue L'argument $configs est un tableau qui contient chaque fichier de configuration qui est chargé dans le conteneur. Nous avons chargé qu'un seul fichier dans l'exemple ci-dessus mais il s'agit tout de même d'un tableau. Le tableau ressemble à ceci: Listing - array( array( 'foo' => 'foovalue', 'bar' => 'barvalue', ) ) Alors que vous pouvez gérer manuellement la fusion des différents fichiers, il est cependant préférable d'utiliser the Config Component pour fusionner et valider les valeurs de la configuration. Au cours du processus, vous pouvez accéder aux valeurs de configuration de cette manière: Listing - use Symfony\Component\Config\Definition\Processor; //... public function load(array $configs, ContainerBuilder $container) generated on September, 0 Chapter : Compiler le Conteneur
0 $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processconfiguration($configuration, $configs); $foo = $config['foo']; //foovalue $bar = $config['bar']; //barvalue //... Il existe deux autres méthodes que vous devez implémenter. L'une pour retourner l'espace de nom XML afin que les parties concernées d'un fichier de configuration XML soient passées à l'extension. L'autre pour spécifier la base du chemin vers les fichiers XSD pour valider la configuration XML: Listing -0 public function getxsdvalidationbasepath() return DIR.'/../Resources/config/'; public function getnamespace() return 'http://www.example.com/symfony/schema/'; La validation XSD est facultative. Retourner false depuis la méthode getxsdvalidationbasepath la désactivera. La version XML de la configuration ressemblerait maintenant à ceci : Listing - 0 <?xml version=".0"?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w.org/00/xmlschema-instance" xmlns:acme_demo="http://www.example.com/symfony/schema/" xsi:schemalocation="http://www.example.com/symfony/schema/ http://www.example.com/ symfony/schema/hello-.0.xsd"> <acme_demo:config> <acme_demo:foo>foovalue</acme_hello:foo> <acme_demo:bar>barvalue</acme_demo:bar> </acme_demo:config> </container> Dans le framework full stack Symfony, il existe une classe Extension de base qui implémente ces méthodes ainsi que des raccourcis de méthodes pour traiter la configuration. Lisez Comment exposer une configuration sémantique pour un Bundle pour plus de détails. La valeur de configuration traitée peut maintenant être ajoutée aux paramètres du conteneur comme si elle était listée dans la section parameters du fichier de configuration, mais avec l'avantage supplémentaire de partager plusieurs fichiers ainsi que la validation de la configuration: Listing - generated on September, 0 Chapter : Compiler le Conteneur
0 public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processconfiguration($configuration, $configs); $container->setparameter('acme_demo.foo', $config['foo']); //... Des pré-requis de configuration plus complexes peuvent être pris en charge dans les classes Extension. Par exemple, vous pouvez choisir de charger un fichier de configuration de service principal, mais aussi d'en charger un secondaire seulement si un paramètre spécifique est défini: Listing - 0 public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processconfiguration($configuration, $configs); $loader = new XmlFileLoader( $container, new FileLocator( DIR.'/../Resources/config') ); $loader->load('services.xml'); if ($config['advanced']) $loader->load('advanced.xml'); Juste enregistrer une extension dans le conteneur n'est pas suffisant pour pour qu'elle soit traitée avec les autres extensions quand le conteneur est compilé. Chargé le fichier de configuration comme ci-dessus en utilisant l'alias de l'extension comme une clé vous assure que l'extension est chargée. Le constructeur du conteneur peut aussi les charger avec sa méthode loadfromextension() method: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $extension = new AcmeDemoExtension(); $container->registerextension($extension); $container->loadfromextension($extension->getalias()); $container->compile(); Si vous devez manipuler la configuration chargée par une extension, alors vous ne pouvez pas le faire depuis une autre extension qui utilise un conteneur neuf. Vous devez plutôt utiliser une passe de compilateur qui fonctionne avec l'ensemble du conteneur après que les extensions ont été traitées.. http://api.symfony.com/./symfony/component/dependencyinjection/containerbuilder.html#loadfromextension() generated on September, 0 Chapter : Compiler le Conteneur 0
Préfixer une configuration en passant par une extension Une extension peut préfixer la configuration de n'importe quel bundle avant que la méthode load() soit appelée en implémentant la classe PrependExtensionInterface : Listing - 0 use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; //... class AcmeDemoExtension implements ExtensionInterface, PrependExtensionInterface //... public function prepend() //... $container->prependextensionconfig($name, $config); //... Pour plus de détails, lisez Comment exposer une configuration sémantique pour un Bundle, qui est spécifique au Framework Symfony, mais contient beaucoup détail sur cette fonctionnalité. Créer une Passe de Compilateur Vous pouvez aussi créer et enregistrer vos propres passes de compilateur dans le conteneur. Pour créer une passe de compilateur, vous devez implémenter l'interface CompilerPassInterface. La passe de compilateur vous donne l'opportunité de manipuler les définitions de service qui ont été compilées. Cela peut être très puissant, mais ce n'est pas non plus quelque chose dont vous aurez besoin tous les jours. La passe de compilateur doit avoir la méthode process qui est passée au conteneur qui doit être compilé: Listing - 0 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; class CustomCompilerPass implements CompilerPassInterface public function process(containerbuilder $container) //... Les paramètres et définitions du conteneur peuvent être manipulés en utilisant les méthodes décrites dans la documentation que vous trouverez ici Travailler avec les définitions du conteneur. Une chose courante à faire dans une passe de compilateur est de rechercher tous les services qui ont un certain tag afin de les traiter d'une certaine manière ou d'injecter chacun d'entre eux dans un autre service de façon dynamique.. http://api.symfony.com/./symfony/component/dependencyinjection/extension/prependextensioninterface.html. http://api.symfony.com/./symfony/component/dependencyinjection/compiler/compilerpassinterface.html generated on September, 0 Chapter : Compiler le Conteneur
Enregistrer une Passe de Compilateur Vous devez enregistrer votre passe personnalisée dans votre conteneur. Sa méthode «process» sera alors appelée lorsque le conteneur aura été compilé: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->addcompilerpass(new CustomCompilerPass); Les passes de compilateur sont enregistrées différemment si vous utilisez le framework full stack. Lisez Comment travailler avec les Passes de Compilation dans les Bundles pour plus de détails. Contrôler l'ordre des Passes Les passes de compilateur par défaut sont groupées en des passes d'optimisation et des passes de suppression. Les passes d'optimisation sont exécutées en premier et incluent des tâches comme résoudre les références dans les définitions. Les passes de suppression exécutent des tâches telles que la suppression des alias privés et des services inutilisés. Vous pouvez choisir dans quel ordre de passage vous souhaitez que vos passes personnalisées soient exécutées. Par défaut, elles vont être exécutées avant les passes d'optimisation. Vous pouvez utiliser les constantes suivantes en tant que second argument quand vous enregistrez une passe dans le conteneur pour contrôler où elle sera placée dans l'ordre de passage : PassConfig::TYPE_BEFORE_OPTIMIZATION PassConfig::TYPE_OPTIMIZE PassConfig::TYPE_BEFORE_REMOVING PassConfig::TYPE_REMOVE PassConfig::TYPE_AFTER_REMOVING Par exemple, pour exécuter votre passe personnalisée après que les passes de suppression par défaut ont été exécutées, vous pouvez faire comme cela: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; $container = new ContainerBuilder(); $container->addcompilerpass( new CustomCompilerPass, PassConfig::TYPE_AFTER_REMOVING ); «Dumper» la Configuration pour plus de Performance Utiliser des fichiers de configuration pour gérer le conteneur de services peut être beaucoup plus facile à comprendre que d'utiliser PHP une fois que vous avez de nombreux services. Néanmoins, cette facilité a un prix quand on commence à parler de performance car les fichiers de configuration ont besoin d'être traités et ensuite la configuration en PHP a besoin d'être assemblée à partir de ces derniers. Le processus de compilation rend le conteneur plus efficace mais il prend du temps à être exécuté. Cependant, vous generated on September, 0 Chapter : Compiler le Conteneur
pouvez avoir le meilleur des deux mondes en utilisant des fichiers de configuration que vous «dumpez» et dont vous cachez la configuration résultante. Le PhpDumper facilite le «dump» du conteneur compilé: Listing - 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; $file = DIR.'/cache/container.php'; if (file_exists($file)) require_once $file; $container = new ProjectServiceContainer(); else $container = new ContainerBuilder(); //... $container->compile(); $dumper = new PhpDumper($container); file_put_contents($file, $dumper->dump()); ProjectServiceContainer est le nom par défaut donné à la classe du conteneur «dumpé», mais vous pouvez changer cela avec l'option class lorsque vous la «dumpez»: Listing -0 0 //... $file = DIR.'/cache/container.php'; if (file_exists($file)) require_once $file; $container = new MyCachedContainer(); else $container = new ContainerBuilder(); //... $container->compile(); $dumper = new PhpDumper($container); file_put_contents( $file, $dumper->dump(array('class' => 'MyCachedContainer')) ); Vous allez maintenant profiter de la rapidité du conteneur PHP configuré tout en conservant la facilité d'utilisation des fichiers de configuration. De plus, dumper le conteneur de cette manière optimise encore la manière dont les services sont créés par le conteneur. Dans l'exemple ci-dessus, vous devrez supprimer le fichier du conteneur mis en cache chaque fois que vous effectuerez des changements. Ajouter un contrôle sur une variable qui détermine si vous êtes en mode débuggage vous permet de conserver la rapidité du conteneur mis en cache en production mais aussi d'avoir une configuration toujours à jour lorsque vous êtes en train de développer votre application: Listing - //... // basé sur une information provenant de votre projet $isdebug =...; $file = DIR.'/cache/container.php'; if (!$isdebug && file_exists($file)) generated on September, 0 Chapter : Compiler le Conteneur
0 0 require_once $file; $container = new MyCachedContainer(); else $container = new ContainerBuilder(); //... $container->compile(); if (!$isdebug) $dumper = new PhpDumper($container); file_put_contents( $file, $dumper->dump(array('class' => 'MyCachedContainer')) ); Cela pourrait être encore amélioré en recompilant seulement le conteneur en mode debug lorsque des changements ont été fait dans sa configuration plutôt qu'à chaque requête. Ceci peut être fait en cachant les fichiers utilisés pour configurer le conteneur de la manière décrite dans «Mécanisme de cache basé sur les ressources» dans la documentation du composant Config. Vous n'avez pas besoin de vous soucier des fichiers à mettre en cache car le constructeur du conteneur garde une trace de toute les ressources utilisées pour le configurer, pas seulement les fichiers de configuration mais également les classes d'extension et les passes de compilateur. Cela signifie que tout changement dans l'un de ces fichiers invalidera le cache et déclenchera la régénération du conteneur. Vous avez juste besoin de demander ces ressources au conteneur et les utiliser comme metadonnées pour le cache: Listing - 0 0 //... // basé sur quelque chose dans votre projet $isdebug =...; $file = DIR.'/cache/container.php'; $containerconfigcache = new ConfigCache($file, $isdebug); if (!$containerconfigcache->isfresh()) $containerbuilder = new ContainerBuilder(); //... $containerbuilder->compile(); $dumper = new PhpDumper($containerBuilder); $containerconfigcache->write( $dumper->dump(array('class' => 'MyCachedContainer')), $containerbuilder->getresources() ); require_once $file; $container = new MyCachedContainer(); Maintenant, le conteneur récupéré dans le cache est utilisé indépendamment du fait que le mode debug est activé ou non. La différence est que le ConfigCache est définit comme le debug mode (la valeur du mode debug lui est passé comme second argument dans son constructeur). Lorsque le cache n'est pas en mode debug, le conteneur mis en cache sera toujours utilisé s'il existe. En mode debug, un fichier de métadonnées est écrit avec le timestamp de tout les fichiers de ressource. Ceci sont ensuite vérifiés pour voir si les fichiers ont changé, et si c'est le cas, le cache sera considéré comme périmé. generated on September, 0 Chapter : Compiler le Conteneur
Dans le framework full stack, le compilateur et le cache du conteneur s'en occupent pour vous. generated on September, 0 Chapter : Compiler le Conteneur
Chapter Travailler avec des Services Taggés Les tags sont des chaînes de caractères génériques (accompagnées de quelques options) qui peuvent être appliquées à n'importe quel service. Les tags en eux-mêmes n'altèrent en rien la fonctionnalité de vos services. Mais si vous le décidez, vous pouvez demander à un constructeur de conteneur de vous donner la liste de tous les services étant taggés avec un tag spécifique. Cela est utile dans les passes de compilation où vous pouvez trouver ces services et les utiliser ou les modifier d'une manière spécifique. Par exemple, si vous utilisez Swift Mailer, vous pourriez imaginer que vous souhaitez implémenter une «chaîne de transport», qui est une collection de classes implémentant \Swift_Transport. En utilisant la chaîne, vous allez vouloir que Swift Mailer essaye plusieurs manières de transporter le message jusqu'à ce que l'une d'entre elle fonctionne. Pour commencer, définissez la classe TransportChain: Listing - 0 class TransportChain private $transports; public function construct() $this->transports = array(); public function addtransport(\swift_transport $this->transports[] = $transport; $transport) Puis, définissez la chaîne en tant que service : Listing - parameters: acme_mailer.transport_chain.class: TransportChain services: acme_mailer.transport_chain: class: "%acme_mailer.transport_chain.class%" generated on September, 0 Chapter : Travailler avec des Services Taggés
Définir des services avec un tag personnalisé Maintenant, vous voulez peut être que plusieurs classes \Swift_Transport soient instanciées et ajoutées à la chaîne automatiquement en utilisant la méthode addtransport(). Par exemple, vous pouvez ajouter les transports suivants en tant que services : Listing - 0 services: acme_mailer.transport.smtp: class: \Swift_SmtpTransport arguments: - "%mailer_host%" tags: - name: acme_mailer.transport acme_mailer.transport.sendmail: class: \Swift_SendmailTransport tags: - name: acme_mailer.transport Notez qu'un tag nommé acme_mailer.transport a été attribué à chacun. C'est le tag personnalisé que vous allez utiliser dans votre passe de compilateur. La passe de compilateur est ce qui donne un sens à ce tag. Créer une CompilerPass («Passe de Compilateur» en français) Votre passe de compilateur peut maintenant interroger le conteneur pour n'importe quel service ayant le tag personnalisé: Listing - 0 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; class TransportCompilerPass implements CompilerPassInterface public function process(containerbuilder $container) if (!$container->hasdefinition('acme_mailer.transport_chain')) return; $definition = $container->getdefinition( 'acme_mailer.transport_chain' ); $taggedservices = $container->findtaggedserviceids( 'acme_mailer.transport' ); foreach ($taggedservices as $id => $attributes) $definition->addmethodcall( 'addtransport', array(new Reference($id)) ); generated on September, 0 Chapter : Travailler avec des Services Taggés
La méthode process() vérifie l'existence du service acme_mailer.transport_chain, puis recherche tous les services taggés avec acme_mailer.transport. Elle ajoute un appel à addtransport() à la définition du service acme_mailer.transport_chain pour chaque service acme_mailer.transport qu'elle trouve. Le premier argument de chacun de ces appels sera le service de transport d'email lui-même. Enregistrer la passe dans le Conteneur Vous avez aussi besoin d'enregistrer la passe dans le conteneur ; elle sera ensuite exécutée lorsque le conteneur sera compilé: Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; $container = new ContainerBuilder(); $container->addcompilerpass(new TransportCompilerPass); Les passes de compilateur sont enregistrées différemment si vous utilisez le framework full stack. Lisez Comment travailler avec les Passes de Compilation dans les Bundles pour plus de détails. Ajouter des attributs additionnels aux tags Quelquefois, vous avez besoin d'informations additionnelles à propos de chaque service qui est taggé avec votre tag. Par exemple, vous pourriez vouloir ajouter un alias pour chaque TransportChain. Pour commencer, changez la classe TransportChain: Listing - 0 0 class TransportChain private $transports; public function construct() $this->transports = array(); public function addtransport(\swift_transport $transport, $alias) $this->transports[$alias] = $transport; public function gettransport($alias) if (array_key_exists($alias, $this->transports)) return $this->transports[$alias]; return; Comme vous pouvez le voir, lorsque addtransport est appelée, elle ne prend pas que l'objet Swift_Transport, mais aussi un alias sous forme de chaîne de caractères pour ce transport. Donc, comment pouvez-vous autoriser chaque transport taggé à fournir aussi un alias? generated on September, 0 Chapter : Travailler avec des Services Taggés
Pour répondre à cette question, changez la déclaration du service comme suit : Listing - 0 services: acme_mailer.transport.smtp: class: \Swift_SmtpTransport arguments: - "%mailer_host%" tags: - name: acme_mailer.transport, alias: foo acme_mailer.transport.sendmail: class: \Swift_SendmailTransport tags: - name: acme_mailer.transport, alias: bar Notez que vous avez ajouté une clé générique alias au tag. Pour utiliser cette dernière, mettez à jour votre compilateur: Listing - 0 0 use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; class TransportCompilerPass implements CompilerPassInterface public function process(containerbuilder $container) if (!$container->hasdefinition('acme_mailer.transport_chain')) return; $definition = $container->getdefinition( 'acme_mailer.transport_chain' ); $taggedservices = $container->findtaggedserviceids( 'acme_mailer.transport' ); foreach ($taggedservices as $id => $tagattributes) foreach ($tagattributes as $attributes) $definition->addmethodcall( 'addtransport', array(new Reference($id), $attributes["alias"]) ); Ici, la partie délicate est la variable $attributes. Comme vous pouvez utiliser le même tag plusieurs fois avec le même service (par exemple : vous pourriez, en théorie, tagger le même service cinq fois avec le tag acme_mailer.transport), $attributes est un tableau contenant l'information du tag pour chaque tag de ce service. generated on September, 0 Chapter : Travailler avec des Services Taggés
Chapter Utiliser une «Factory» pour créer des services Le Conteneur de Service de Symfony fournit une manière puissante de contrôler la création d'objets, vous permettant de spécifier les arguments passés au constructeur ainsi que d'appeler des méthodes et de définir des paramètres. Parfois, cependant, ceci ne vous fournira pas tout ce dont vous avez besoin pour construire vos objets. Dans cette situation, vous pouvez utiliser une «factory» pour créer l'objet et informer le conteneur de service d'appeler une méthode de la «factory» plutôt que d'instancier l'objet directement. Supposons que vous ayez une «factory» qui configure et retourne un nouvel objet NewsletterManager: Listing - 0 class NewsletterFactory public function get() $newslettermanager = new NewsletterManager(); //... return $newslettermanager; Pour rendre l'objet NewsletterManager disponible en tant que service, vous pouvez configurer le conteneur de service afin qu'il utilise la classe «factory» NewsletterFactory : Listing - parameters: #... newsletter_manager.class: NewsletterManager newsletter_factory.class: NewsletterFactory services: newsletter_manager: class: "%newsletter_manager.class%" factory_class: "%newsletter_factory.class%" factory_method: get generated on September, 0 Chapter : Utiliser une «Factory» pour créer des services 0
Lorsque vous spécifiez la classe à utiliser pour la «factory» (via factory_class) la méthode sera appelée de manière statique. Si la «factory» elle-même doit être instanciée et la méthode de l'objet résultant appelée (comme dans cet exemple), configurez la «factory» elle-même comme un service : Listing - 0 parameters: #... newsletter_manager.class: NewsletterManager newsletter_factory.class: NewsletterFactory services: newsletter_factory: class: "%newsletter_factory.class%" newsletter_manager: class: "%newsletter_manager.class%" factory_service: newsletter_factory factory_method: get Le service «factory» est spécifié par son ID de nom et non pas par une référence au service luimême. Donc vous ne devez pas utiliser la syntaxe «@». Passer des Arguments à la Méthode de la «Factory» Si vous devez passer des arguments à la méthode de la «factory», vous pouvez utiliser l'option arguments du conteneur de service. Par exemple, supposons que la méthode get de l'exemple précédent prenne le service templating en tant qu'argument : Listing - 0 parameters: #... newsletter_manager.class: NewsletterManager newsletter_factory.class: NewsletterFactory services: newsletter_factory: class: "%newsletter_factory.class%" newsletter_manager: class: "%newsletter_manager.class%" factory_service: newsletter_factory factory_method: get arguments: - "@templating" generated on September, 0 Chapter : Utiliser une «Factory» pour créer des services
Chapter Configurer des services avec un configurateur de service Le configurateur de service est une fonctionnalité du containeur d'injection de dépendances qui permet d'utiliser une fonction de rappel (callable) pour configurer un service après son instanciation. Vous pouvez spécifier une méthode d'un autre service, une fonction PHP ou une méthode statique d'une classe. L'instance du service est passé à une fonction de rappel permettant au configurateur d'effectuer ce qui est nécessaire à la configuration du service après sa création. Le configurateur de service peut être utilisé, par exemple, quand vous avez un service qui nécessite une configuration complexe basée sur des paramètres de configuration provenant de différentes sources/ services. En utilisant un configurateur externe, vous pouvez maintenir l'implementation du service proprement et le garder découplée des autres objets qui fournissent la configuration. Un autre cas d'utilisation intéressant, c'est lorsque vous avez plusieurs objets qui partagent une configuration commune ou qui doivent être configurés de manière similaire à l'exécution. Par exemple, supposons que vous ayez une application où vous envoyez différents types d'e-mails aux utilisateurs. Les e-mails sont transmis à travers différents formateurs qui pourraient être activée ou non en fonction de certains paramètres dynamiques de l'application. Vous commencez la définition d'une classe NewsletterManager comme ceci: Listing - 0 class NewsletterManager implements EmailFormatterAwareInterface protected $mailer; protected $enabledformatters; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function setenabledformatters(array $enabledformatters) $this->enabledformatters = $enabledformatters; generated on September, 0 Chapter : Configurer des services avec un configurateur de service
//... et ensuite, une classe GreetingCardManager: Listing - 0 class GreetingCardManager implements EmailFormatterAwareInterface protected $mailer; protected $enabledformatters; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function setenabledformatters(array $enabledformatters) $this->enabledformatters = $enabledformatters; //... Comme mentionné précédemment, l'objectif est de définir les formateurs lors de l'exécution en fonction des paramètres de l'application. Pour ce faire, vous avez également une classe EmailFormatterManager qui est responsable du chargement et de la validation du formatage permis par l'application: Listing - 0 0 class EmailFormatterManager protected $enabledformatters; public function loadformatters() // code pour configurer comment les formateurs sont utilisés $enabledformatters = array(...); //... $this->enabledformatters = $enabledformatters; public function getenabledformatters() return $this->enabledformatters; //... Si votre objectif est d'éviter d'avoir à coupler NewsletterManager et GreetingCardManager avec EmailFormatterManager, alors vous voudrez peut-être créer une classe configurateur pour configurer ces instances: Listing - class EmailConfigurator private $formattermanager; generated on September, 0 Chapter : Configurer des services avec un configurateur de service
0 public function construct(emailformattermanager $formattermanager) $this->formattermanager = $formattermanager; public function configure(emailformatterawareinterface $emailmanager) $emailmanager->setenabledformatters( $this->formattermanager->getenabledformatters() ); //... Le travail de EmailConfigurator consiste à injecter les filtres activés dans NewsletterManager et GreetingCardManager parce qu'ils ne sont pas conscients de l'endroit d'où proviennent les filtres. En revanche, la classe EmailFormatterManager connaît les formateurs activés et la façon de les charger, en gardant la seule responsabilité. La configuration d'un configurateur de service La configuration pour les classes ci-dessus pourraient ressembler à ceci: Listing - 0 0 services: my_mailer: #... email_formatter_manager: class: EmailFormatterManager #... email_configurator: class: EmailConfigurator arguments: ["@email_formatter_manager"] #... newsletter_manager: class: NewsletterManager calls: - [setmailer, ["@my_mailer"]] configurator: ["@email_configurator", configure] greeting_card_manager: class: GreetingCardManager calls: - [setmailer, ["@my_mailer"]] configurator: ["@email_configurator", configure] generated on September, 0 Chapter : Configurer des services avec un configurateur de service
Chapter Gérer les dépendances communes avec des services parents Au fur et à mesure que vous ajoutez de la fonctionnalité à votre application, vous pourriez commencer à avoir des classes liées partageant les mêmes dépendances. Par exemple, vous pourriez avoir un gestionnaire de lettres d'information («Newsletter Manager» en anglais) qui utilise l'injection via des mutateurs pour définir ses dépendances: Listing - 0 class NewsletterManager protected $mailer; protected $emailformatter; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function setemailformatter(emailformatter $emailformatter) $this->emailformatter = $emailformatter; //... et aussi une classe «Greeting Card» («carte de voeux» en français) qui partage les mêmes dépendances: Listing - class GreetingCardManager protected $mailer; protected $emailformatter; public function setmailer(mailer $mailer) generated on September, 0 Chapter : Gérer les dépendances communes avec des services parents
0 $this->mailer = $mailer; public function setemailformatter(emailformatter $emailformatter) $this->emailformatter = $emailformatter; //... La configuration des services pour ces classes ressemble à quelque chose comme ça : Listing - 0 0 parameters: #... newsletter_manager.class: NewsletterManager greeting_card_manager.class: GreetingCardManager services: my_mailer: #... my_email_formatter: #... newsletter_manager: class: "%newsletter_manager.class%" calls: - [setmailer, ["@my_mailer"]] - [setemailformatter, ["@my_email_formatter"]] greeting_card_manager: class: "%greeting_card_manager.class%" calls: - [setmailer, ["@my_mailer"]] - [setemailformatter, ["@my_email_formatter"]] Il y a beaucoup de répétitions dans chacune des classes et dans la configuration. Cela signifie que si vous changiez, par exemple, le Mailer des classes de EmailFormatter injecté via le constructeur, vous devriez mettre à jour la configuration à deux endroits. De même, si vous deviez effectuer des changements dans les méthodes de mutation, vous devriez faire cela dans les deux classes. La manière usuelle de gérer les méthodes communes de ces classes liées serait de les extraire dans une classe parente: Listing - 0 abstract class MailManager protected $mailer; protected $emailformatter; public function setmailer(mailer $mailer) $this->mailer = $mailer; public function setemailformatter(emailformatter $emailformatter) $this->emailformatter = $emailformatter; //... generated on September, 0 Chapter : Gérer les dépendances communes avec des services parents
Les classes NewsletterManager et GreetingCardManager peuvent alors étendre cette classe parente: Listing - class NewsletterManager extends MailManager //... et: Listing - class GreetingCardManager extends MailManager //... De façon similaire, le conteneur de service de Symfony supporte aussi l'extension de services via la configuration qui vous permet de réduire le nombre de répétitions en spécifiant un parent pour un service. Listing - 0 0 parameters: #... newsletter_manager.class: NewsletterManager greeting_card_manager.class: GreetingCardManager services: my_mailer: #... my_email_formatter: #... mail_manager: abstract: true calls: - [setmailer, ["@my_mailer"]] - [setemailformatter, ["@my_email_formatter"]] newsletter_manager: class: "%newsletter_manager.class%" parent: mail_manager greeting_card_manager: class: "%greeting_card_manager.class%" parent: mail_manager Dans ce contexte, avoir un service parent implique que les arguments et appels de méthode du service parent devrait être utilisés pour les services enfants. Spécifiquement, les méthodes mutateurs définies pour le service parent seront appelées lorsque les services enfants seront instanciés. Si vous supprimez la clé de configuration parent, les services seront toujours instanciés et étendront toujours la classe MailManager. La différence est que le fait d'omettre la clé de configuration parent signifiera que les appels définis sur le service mail_manager ne seront pas exécutés quand les services enfants seront instanciés. Les attributs scope, abstract et tags sont toujours pris à partir du service enfant. generated on September, 0 Chapter : Gérer les dépendances communes avec des services parents
La classe parente est abstraite comme elle ne devrait pas être directement instanciée. La définir comme abstraite dans le fichier de configuration comme cela a été fait ci-dessus signifiera qu'elle ne peut être utilisée uniquement en tant que service parent et ne peut pas être utilisée directement en tant que service à injecter et sera supprimée au moment de la compilation. Pour que les dépendances parents puissent être résolues, le ContainerBuilder doit d'abord être compilé. Lisez Compiler le Conteneur pour plus de détails. Surcharger des dépendances parentes Il se peut qu'à un moment ou à un autre vous souhaitiez surcharger quelle classe est passée en tant que dépendance d'un seul de vos services enfants. Heureusement, en ajoutant la configuration d'appel de méthode pour le service enfant, les dépendances définies par la classe parente seront surchargées. Donc si vous aviez besoin de passer une dépendance différente uniquement à la classe NewsletterManager, la configuration ressemblerait à quelque chose comme ça : Listing - 0 0 parameters: #... newsletter_manager.class: NewsletterManager greeting_card_manager.class: GreetingCardManager services: my_mailer: #... my_alternative_mailer: #... my_email_formatter: #... mail_manager: abstract: true calls: - [setmailer, ["@my_mailer"]] - [setemailformatter, ["@my_email_formatter"]] newsletter_manager: class: "%newsletter_manager.class%" parent: mail_manager calls: - [setmailer, ["@my_alternative_mailer"]] greeting_card_manager: class: "%greeting_card_manager.class%" parent: mail_manager La classe GreetingCardManager va recevoir les mêmes dépendances qu'avant, mais la classe NewsletterManager quant à elle va avoir le service my_alternative_mailer à la place du service my_mailer. Collections de dépendances Veuillez noter que la méthode de mutation surchargée dans l'exemple précédent est en fait appelée deux fois - une fois par la définition du parent et une fois par la définition de l'enfant. Dans l'exemple generated on September, 0 Chapter : Gérer les dépendances communes avec des services parents
précédent, cela fonctionnait bien, puisque le second appel à setmailer remplaçait l'objet mailer défini par le premier appel. Dans certains cas, cependant, cela peut être problématique. Par exemple, si l'appel de la méthode surchargée implique l'ajout de quelque chose à une collection, alors deux objets seront ajoutés à cette collection. Ce qui suit montre un tel cas, avec une classe parente qui ressemble à cela: Listing - 0 abstract class MailManager protected $filters; public function setfilter($filter) $this->filters[] = $filter; //... Si vous aviez la configuration suivante : Listing -0 0 parameters: #... newsletter_manager.class: NewsletterManager services: my_filter: #... another_filter: #... mail_manager: abstract: true calls: - [setfilter, ["@my_filter"]] newsletter_manager: class: "%newsletter_manager.class%" parent: mail_manager calls: - [setfilter, ["@another_filter"]] Dans cet exemple, la méthode setfilter du service newsletter_manager sera appelée deux fois, donnant comme résultat le tableau $filters contenant les deux objets my_filter et another_filter. C'est parfait si vous voulez simplement ajouter des filtres additionnels aux sous-classes. Si vous souhaitez remplacer les filtres passés à la sous-classe, supprimer le paramètre parent de la configuration va éviter que la classe de base appelle setfilter. Dans les exemples présentées, il y a une relation identique entre les services parent et enfants ainsi les classes sous-jacentes parente et enfants. Vous ne devriez pas faire ainsi cependant, vous pouvez extraire les parties communes des définitions de services similaires du service parent sans hériter de la classe parente. generated on September, 0 Chapter : Gérer les dépendances communes avec des services parents
Chapter Configuration Avancée du Conteneur Marquer les services comme publics / privés Lorsque vous définissez des services, vous allez généralement vouloir avoir accès à ces définitions depuis le code de votre application. Ces services sont dits public. Par exemple, le service doctrine enregistré dans le conteneur lorsque vous utilisez le DoctrineBundle est un service public auquel vous pouvez accéder via: Listing - $doctrine = $container->get('doctrine'); Cependant, il y a des cas où vous ne souhaitez pas qu'un service soit public. Cela arrive souvent quand un service est défini seulement pour être utilisé comme argument d'un autre service. Si vous utilisez un service privé comme argument d'un seul autre service, cela résultera en une instanciation «instantanée» (par exemple : new PrivateFooBar()) à l'intérieur de cet autre service, le rendant publiquement indisponible à l'exécution. Pour faire simple : un service est privé quand vous ne voulez pas y accéder directement depuis votre code. Voici un exemple : Listing - services: foo: class: Example\Foo public: false Maintenant que le service est privé, vous ne pouvez pas l'appeler: Listing - $container->get('foo'); Cependant, si un service a été marqué comme privé, vous pouvez toujours créer un alias de ce dernier (voir ci-dessous) pour y accéder (via l'alias). generated on September, 0 Chapter : Configuration Avancée du Conteneur 00
Les services sont par défaut publics. Les services synthétiques Les services synthétiques sont des services injectés au conteneur de services au lieu d'être créé par le conteneur. Par exemple, si vous utilisez le composant HttpKernel avec le composant DependencyInjection, et bien le service request est injecté dans la méthode ContainerAwareHttpKernel::handle() lorsqu'on entre dans le scope de la request. La classe n'existe pas lorsqu'il n'y a pas de request, donc la request ne peut être incluse à la configuration du conteneur de services. Aussi, le service devrait être différent pour chaque sous-requête dans l'application. Pour créer un service synthétique, mettez synthetic à true : Listing - services: request: synthetic: true Comme vous pouvez le voir, seule l'option synthetic est déclarée. toutes les autres options sont uniquement utilisée pour configurer pour la création d'un service par le conteneur de services. Comme le service n'est pas créé par le conteneur, ces options sont ommises. Désormais, vous pouvez injecter la classe en utilisant Container::set : Listing - //... $container->set('request', new MyRequest(...)); Créer un alias Parfois, vous pourriez vouloir utiliser des raccourcis pour accéder à certains de vos services. Vous pouvez faire cela en créant des alias pour ces derniers ; de plus, vous pouvez même créer des alias pour les services non publics. Listing - services: foo: class: Example\Foo bar: alias: foo Cela signifie que lorsque vous utilisez le conteneur directement, vous pouvez accéder au service foo en demandant le service bar comme cela: Listing - $container->get('bar'); // Retourne le service foo. http://api.symfony.com/./symfony/component/httpkernel/dependencyinjection/containerawarehttpkernel.html#method_handle. http://api.symfony.com/./symfony/component/dependencyinjection/container.html#method_set generated on September, 0 Chapter : Configuration Avancée du Conteneur 0
Requérir des fichiers Il pourrait y avoir des cas où vous aurez besoin d'inclure un autre fichier juste avant que le service luimême soit chargé. Pour faire cela, vous pouvez utiliser la directive file. Listing - services: foo: class: Example\Foo\Bar file: "%kernel.root_dir%/src/path/to/file/foo.php" Notez que Symfony va appeler en interne la fonction PHP require_once, ce qui veut dire que votre fichier va être inclus seulement une fois par requête. generated on September, 0 Chapter : Configuration Avancée du Conteneur 0
Chapter Les services instanciés à la demande («lazy services» en anglais) New in version.: Les services lazy (instancés à la demande) ont été ajoutés depuis Symfony.. Pourquoi les services lazy? Dans certains cas, vous souhaiteriez injecter un service qui est vraiment lourd à instancier mais il n'est pas toujours utilisé dans l'objet. Par exemple, imaginez que vous ayez un NewsletterManager et que vous injectiez le service mailer. Seules quelques méthodes de votre NewsletterManager utilisent le service mailer, mais même quand vous n'en avez pas besoin, le service mailer est toujours instancié à la construction de votre NewsletterManager. Configurer les services comme lazy est une réponse à ce problème. Avec le service lazy, un "proxy" du service mailer est injecté. Il ressemble et agit comme le service mailer, à l'exception près que le service mailer n'est pas instancié, et ne le sera qu'au moment où vous interagirez avec le proxy. Installation Tout d'abord, pour utiliser l'instantiation d'un service lazy, vous aurez besoin d'installer ProxyManager bridge : Listing - $ php composer.phar require symfony/proxy-manager-bridge:..* Si vous utilisez le framework full-stack, le paquet n'est pas inclus et vous aurez besoin de l'ajouter à votre composer.json et l'installer (ce que fait la commande ci-dessus).. https://github.com/symfony/symfony/tree/master/src/symfony/bridge/proxymanager generated on September, 0 Chapter : Les services instanciés à la demande («lazy services» en anglais) 0
Configuration Vous pouvez configurer un service comme lazy en modifiant sa définition: Listing - services: foo: class: Acme\Foo lazy: true Vous pouvez récupérer le service depuis le conteneur: Listing - $service = $container->get('foo'); A ce moment, l'argument $service récupéré doit être un proxy virtuel et a la même signature que la classe représentant le service. Vous pouvez aussi injecter ce service normalement dans d'autres services. L'objet qui est réellement injecté est le service proxy. Pour vérifier si votre proxy fonctionne, vous pouvez simplement vérifier l'interface de l'objet reçu. Listing - var_dump(class_implements($service)); Si la classe implémente l'interface ProxyManager\Proxy\LazyLoadingInterface vos services instanciés à la demande (lazy) fonctionnent. Si vous n'installez pas le ProxyManager bridge, le conteneur passera outre le drapeau lazy et instanciera normalement le service. Le proxy est initialisé et le service demandé sera instancié au moment où vous interagirez avec l'objet. Ressources additionnelles Vous pouvez en apprendre plus sur les proxies, comment ils sont instanciés, générés et initialisés dans la documentation du ProxyManager.. http://en.wikipedia.org/wiki/proxy_pattern. https://github.com/symfony/symfony/tree/master/src/symfony/bridge/proxymanager. https://github.com/ocramius/proxymanager/blob/master/docs/lazy-loading-value-holder.md generated on September, 0 Chapter : Les services instanciés à la demande («lazy services» en anglais) 0
Chapter 0 Processus de construction du Conteneur Dans les pages précédentes, nous n'avons que peu abordé l'emplacement où devrait se situer les divers fichiers et classes. C'est parce que cela dépend de l'application, des bibliothèques ou du framework dans lequel vous voulez utiliser le Conteneur. Comprendre comment le Conteneur est configuré et construit dans le framework full stack Symfony vous aidera à voir comment tout cela s'assemble, que vous utilisiez le framework full stack ou que vous cherchiez à utiliser les services du conteneur dans une autre application. Le framework full stack utilise le composant HttpKernel pour gérer le chargement de la configuration du conteneur de services depuis les bundles et l'application et prend également en charge la compilation et la mise en cache. Même si vous n'utilisez pas HttpKernel, cela devrait vous donner une idée de la manière dont vous devriez organiser votre configuration dans une application modulaire. Travailler avec le Conteneur en cache Avant de le construire, le noyau (kernel) vérifie s'il existe une version en cache du conteneur. Le HttpKernel possède un paramètre «debug» et s'il est à false, alors la version en cache est utilisée si elle existe. Si «debug» est à true, alors le noyau vérifie si la configuration est à jour et si c'est le cas, alors la version en cache est utilisée. Sinon, alors le conteneur est construit à partir de la configuration au niveau de l'application ainsi que de la configuration des bundles. Lisez Dumper la configuration pour plus de performance pour plus de détails sur le sujet. Configuration au niveau de l'application La configuration de l'application est chargée depuis le répertoire app/config. Plusieurs fichiers sont chargés et sont ensuite mergés selon leurs extensions. Cela permet d'avoir différentes configuration pour différents environnements, par exemple dev et prod. Ces fichiers contiennent des paramètres et des services qui sont chargés directement dans le conteneur grâce à l'initialisation du Conteneur avec des fichiers de configuration. Ils contiennent également la configuration qui est traitée selon l'extension, comme c'est expliqué dans Gérer la configuration avec les extensions. Ils sont considérés comme de la configuration de bundle car chaque bundle contient une classe Extension. generated on September, 0 Chapter 0: Processus de construction du Conteneur 0
Configuration au niveau du bundle avec les Extensions Par convention, chaque bundle contient une classe Extension qui se situe dans le répertoire DependencyInjection du bundle. Elles sont enregistrées avec le ContainerBuilder lorsque le noyau est initialisé. Lorsque le ContainerBuilder est compilé, la configuration de l'application qui correspond à l'extension du bundle est passée à l'extension qui charge également ses propres fichiers de configuration, généralement depuis le répertoire Resources/config du bundle. La configuration niveau application est généralement traitée avec un objet Configuration qui est également situé dans le répertoire DependencyInjection du bundle. Passes de compilateur pour autoriser les interactions entre bundles Les passes de compilateur sont utilisées pour permettre des interactions entre différents bundles puisqu'ils ne peuvent pas agir sur la configuration des autres bundles dans leur classe Extension. L'un des principaux usages est de traiter les services taggés, ce qui permet aux bundles d'enregistrer des services d'autres bundles, comme les loggers Monolog, les extensions Twig et les Collecteurs de Données du Web Profiler. Les passes de compilateur sont généralement placées dans le répertoire DependencyInjection/ Compiler du bundle. Compilation et mise en cache Après que le processus de compilation a chargé les services depuis la configuration, les extensions et les passes de compilateur, elle est dumpée pour que le cache puisse être utilisé la prochaine fois. La version dumpée est ensuite utilisée par les sous-requêtes, ce qui est plus efficace. generated on September, 0 Chapter 0: Processus de construction du Conteneur 0
Chapter Le Composant DomCrawler Le Composant DomCrawler facilite la navigation DOM dans les documents HTML et XML. Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt Git officiel (https://github.com/symfony/domcrawler ) ; Installez le via Composer (symfony/dom-crawler sur Packagist ). Utilisation La classe Crawler fournit des méthodes pour interroger et manipuler des documents HTML et XML. Une instance du Crawler représente un ensemble (SplObjectStorage ) d'objets DOMElement, qui sont finalement des noeuds au travers desquels vous pouvez naviguer aisément: Listing - use Symfony\Component\DomCrawler\Crawler; $html = <<<'HTML' <html> <body> <p class="message">hello World!</p> <p>hello Crawler!</p> </body> </html>. https://github.com/symfony/domcrawler. https://packagist.org/packages/symfony/dom-crawler. http://api.symfony.com/./symfony/component/domcrawler/crawler.html. http://php.net/manual/en/class.splobjectstorage.php. http://php.net/manual/en/class.domelement.php generated on September, 0 Chapter : Le Composant DomCrawler 0
0 HTML; $crawler = new Crawler($html); foreach ($crawler as $domelement) print $domelement->nodename; Les classes spécialisées Link et Form sont utiles pour interagir avec des liens et formulaires HTML lorsque vous naviguez au travers de l'arbre HTML. Le DomCrawler va essayer de réparer automatiquement votre HTML pour correspondre à la spécification officielle. Par exemple, si vous insérez une balise <p> dans une autre <p>, il sera déplacé pour être un frère de la balise parent. Ceci est prévu et fait partie de la spécification HTML. Mais si vous obtenez un comportement inattendu, alors ceci pourrait être la cause. Filtrage de Noeud Utiliser des expressions XPath est très facile: Listing - $crawler = $crawler->filterxpath('descendant-or-self::body/p'); DOMXPath::query est utilisée en interne pour exécuter une requête XPath. Filtrer des noeuds est d'autant plus facile si vous avez le Composant CssSelector installé. Cela vous permet d'utiliser des sélecteurs similaires à ceux de jquery pour naviguer dans le DOM: Listing - $crawler = $crawler->filter('body > p'); Une fonction anonyme peut être utilisée pour filtrer des noeuds à l'aide de critères plus complexes: Listing - $crawler = $crawler->filter('body > p')->reduce(function ($node, $i) // filtre les noeuds pairs return ($i % ) == 0; ); Pour supprimer un noeud, la fonction anonyme doit retourner «false». Toutes les méthodes de filtrage retournent une nouvelle instance de Crawler avec le contenu filtré. Navigation au travers des noeuds Accède au noeud par sa position dans la liste:. http://api.symfony.com/./symfony/component/domcrawler/link.html. http://api.symfony.com/./symfony/component/domcrawler/form.html. http://api.symfony.com/./symfony/component/domcrawler/crawler.html generated on September, 0 Chapter : Le Composant DomCrawler 0
Listing - $crawler->filter('body > p')->eq(0); Récupère le premier ou dernier noeud de la sélection courante: Listing - $crawler->filter('body > p')->first(); $crawler->filter('body > p')->last(); Récupère les noeuds du même niveau que la sélection courante: Listing - $crawler->filter('body > p')->siblings(); Récupère les noeuds de même niveau après ou avant la sélection courante: Listing - $crawler->filter('body > p')->nextall(); $crawler->filter('body > p')->previousall(); Récupère tous les noeuds enfants ou parents: Listing - $crawler->filter('body')->children(); $crawler->filter('body > p')->parents(); Toutes les méthodes de navigation retournent un nouvelle instance de Crawler. Accéder aux valeurs des noeuds Accède à la valeur du premier noeud de la sélection courante: Listing -0 $message = $crawler->filterxpath('//body/p')->text(); Accède à la valeur de l'attribut du premier noeud de la sélection courante: Listing - $class = $crawler->filterxpath('//body/p')->attr('class'); Extrait les valeurs de l'attribut et/ou du noeud de la liste des noeuds: Listing - $attributes = $crawler->filterxpath('//body/p')->extract(array('_text', 'class')); L'attribut spécial _text représente la valeur d'un noeud. Appelez une fonction anonyme sur chaque noeud de la liste: Listing -. http://api.symfony.com/./symfony/component/domcrawler/crawler.html generated on September, 0 Chapter : Le Composant DomCrawler 0
$nodevalues = $crawler->filter('p')->each(function ($node, $i) return $node->text(); ); La fonction anonyme reçoit la position et le noeud en tant qu'arguments. Le résultat est un tableau de valeurs retournées par les appels de fonction anonyme. Ajouter du contenu Le «crawler» supporte plusieurs façons d'ajouter du contenu: Listing - 0 $crawler = new Crawler('<html><body /></html>'); $crawler->addhtmlcontent('<html><body /></html>'); $crawler->addxmlcontent('<root><node /></root>'); $crawler->addcontent('<html><body /></html>'); $crawler->addcontent('<root><node /></root>', 'text/xml'); $crawler->add('<html><body /></html>'); $crawler->add('<root><node /></root>'); Comme l'implémentation du «Crawler» est basée sur l'extension DOM, elle est aussi capable d'intéragir avec les objets natifs DOMDocument 0, DOMNodeList et DOMNode : Listing - 0 $document = new \DOMDocument(); $document->loadxml('<root><node /><node /></root>'); $nodelist = $document->getelementsbytagname('node'); $node = $document->getelementsbytagname('node')->item(0); $crawler->adddocument($document); $crawler->addnodelist($nodelist); $crawler->addnodes(array($node)); $crawler->addnode($node); $crawler->add($document); Support des Formulaires et des Liens Un traitement spécial est réservé pour les liens et formulaires se trouvant dans l'arbre DOM. Liens Pour trouver un lien par son nom (ou une image cliquable via son attribut alt), utilisez la méthode selectlink sur un «crawler» existant. Cela retourne une instance de «Crawler» avec seulement le(s) lien(s) sélectionné(s). Appeler la méthode link() vous retourne un objet spécial Link : Listing - $linkscrawler = $crawler->selectlink('go elsewhere...'); $link = $linkscrawler->link(); 0. http://php.net/manual/en/class.domdocument.php. http://php.net/manual/en/class.domnodelist.php. http://php.net/manual/en/class.domnode.php. http://api.symfony.com/./symfony/component/domcrawler/link.html generated on September, 0 Chapter : Le Composant DomCrawler 0
// ou faites cela en une seule fois $link = $crawler->selectlink('go elsewhere...')->link(); L'objet Link possède plusieurs méthodes utiles pour récupérer plus d'informations à propos du lien sélectionné lui-même: Listing - // retourne l'uri qui peut être utilisée pour effectuer une autre requête $uri = $link->geturi(); La méthode geturi() est particulièrement utile car elle «nettoie» la valeur de href et la transforme en une valeur qui peut être utilisée. Par exemple, pour un lien tel href="#foo", cette méthode retournerait l'uri complète de la page courante suffixée avec #foo. Le retour de la méthode geturi() est toujours une URI complète avec laquelle vous pouvez effectuer l'action de votre choix. Formulaires Un traitement spécial est aussi réservé aux formulaires. Une méthode selectbutton() est disponible sur le «Crawler» qui retourne un autre «Crawler» qui a correspondu à un bouton (input[type=submit], input[type=image], ou un button) ayant le texte donné. Cette méthode est très utile car vous pouvez l'utiliser pour retourner un objet Form qui représente le formulaire dans lequel le bouton se trouve: Listing - $form = $crawler->selectbutton('validate')->form(); // ou «remplissez» les champs du formulaire avec des données $form = $crawler->selectbutton('validate')->form(array( 'name' => 'Ryan', )); L'objet Form possède de nombreuses méthodes utiles pour travailler avec les formulaires: Listing - $uri = $form->geturi(); $method = $form->getmethod(); La méthode geturi() fait plus que simplement retourner l'attribut action du formulaire. Si la méthode du formulaire est GET, alors elle simule le comportement du navigateur et retourne l'attribut action suivi par une chaîne de caractères représentant toutes les valeurs du formulaires suffixées en tant que paramètres de requête. Vous pouvez virtuellement définir et récupérer des valeurs du formulaire: Listing -0 // définit des valeurs du formulaire $form->setvalues(array( 'registration[username]' => 'symfonyfan', 'registration[terms]' =>, ));. http://api.symfony.com/./symfony/component/domcrawler/link.html. http://api.symfony.com/./symfony/component/domcrawler/form.html. http://api.symfony.com/./symfony/component/domcrawler/form.html. http://api.symfony.com/./symfony/component/domcrawler/form.html#method_geturi generated on September, 0 Chapter : Le Composant DomCrawler
0 // récupère un tableau de valeurs - tableau qui est «plat» comme ci-dessus $values = $form->getvalues(); // retourne les valeurs telles que PHP les verraient, où «registration» est son // propre tableau $values = $form->getphpvalues(); Pour travailler avec des champs multi-dimensionnels: Listing - <form> <input name="multi[]" /> <input name="multi[]" /> <input name="multi[dimensional]" /> </form> Vous devez spécifier le nom du champ entièrement qualifié: Listing - // Définit un seul champ // Set a single field $form->setvalue('multi[0]', 'value'); // Définit plusieurs champs en une seule fois $form->setvalue('multi', array( => 'value', 'dimensional' => 'an other value' )); C'est super, mais le meilleur reste à venir! L'objet Form vous permet d'intéragir avec votre formulaire comme un navigateur, en sélectionnant des valeurs de boutons radio, en cochant des cases «checkbox», et en «uploadant» des fichiers: Listing - 0 $form['registration[username]']->setvalue('symfonyfan'); // coche ou décoche une case «checkbox» $form['registration[terms]']->tick(); $form['registration[terms]']->untick(); // sélectionne une option $form['registration[birthday][year]']->select(); // sélectionne plusieurs options d'un champ «select» multiple ou // plusieurs cases «checkbox» $form['registration[interests]']->select(array('symfony', 'cookies')); // peut même simuler un «upload» de fichier $form['registration[photo]']->upload('/path/to/lucas.jpg'); Quel est le but d'effectuer tout cela? Si vous faites des tests en interne, vous pouvez récupérer les informations de votre formulaire comme s'il avait été soumis en utilisant des valeurs PHP: Listing - $values = $form->getphpvalues(); $files = $form->getphpfiles(); Si vous utilisez un client HTTP externe, vous pouvez utiliser le formulaire pour récupérer toutes les informations dont vous avez besoin pour créer une requête POST pour le formulaire: generated on September, 0 Chapter : Le Composant DomCrawler
Listing - $uri = $form->geturi(); $method = $form->getmethod(); $values = $form->getvalues(); $files = $form->getfiles(); // maintenant, utilisez n'importe quel client HTTP et postez le formulaire // en utilisant ces informations Un bel exemple d'un système intégré qui utilise tout cela est Goutte. Goutte comprend l'objet «Crawler» de Symfony et peut l'utiliser pour soumettre des formulaires directement: Listing - 0 use Goutte\Client; // effectue une requête réelle vers un site externe $client = new Client(); $crawler = $client->request('get', 'https://github.com/login'); // sélectionne le formulaire et le remplit avec quelques valeurs $form = $crawler->selectbutton('log in')->form(); $form['login'] = 'symfonyfan'; $form['password'] = 'anypass'; // soumet le formulaire $crawler = $client->submit($form);. https://github.com/fabpot/goutte generated on September, 0 Chapter : Le Composant DomCrawler
Chapter Le Composant répartiteur d'évènement Introduction La programmation Orientée Objet a parcouru du chemin afin de vous assurer une certaine extensibilité. En créant des classes qui ont des responsabilités bien définies, votre code devient plus flexible et un développeur peut les étendre avec des sous-classes pour modifier leurs comportements. Mais s'il souhaite partager ses changements avec d'autres développeurs qui ont aussi créé leurs propres sous-classes, l'héritage de code n'est plus la bonne réponse. Considérez l'exemple réel suivant : vous souhaitez fournir un système de plugin pour votre projet. Un plugin devrait être capable d'ajouter des méthodes, ou de faire quelque chose avant ou après qu'une méthode soit exécutée, sans interférer avec d'autres plugins. Ceci n'est pas un problème facile à résoudre avec de l'héritage simple, et l'héritage multiple (s'il était possible avec PHP) possède ses propres inconvénients. Le composant Répartiteur d'évènement de Symfony implémente le pattern Mediator d'une manière simple et efficace pour rendre toutes ces choses possibles et pour réaliser des projets vraiment extensibles. Prenez un exemple simple du composant HttpKernel de Symfony. Une fois qu'un objet Response a été créé, il pourrait être utile d'autoriser la modification de ce dernier par d'autres éléments du système (par exemple : ajouter des en-têtes de cache) avant que cet objet ne soit utilisé. Pour rendre cela possible, le noyau Symfony lance un évènement - kernel.response. Voilà comment cela fonctionne : Un listener ou écouteur en français (objet PHP) informe un objet répartiteur central qu'il souhaite écouter l'évènement kernel.response ; A un moment donné, le noyau Symfony dit à l'objet répartiteur de «répartir» («dispatch» en anglais) l'évènement kernel.response, en passant avec lui un objet Event qui a accès à l'objet Response ; Le répartiteur notifie (c-a-d appelle une méthode) tous les «listeners» de l'évènement kernel.response, autorisant chacun d'entre eux à effectuer des modifications sur l'objet Response.. https://fr.wikipedia.org/wiki/m%c%adiateur_(patron_de_conception). https://github.com/symfony/httpkernel generated on September, 0 Chapter : Le Composant répartiteur d'évènement
Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt Git officiel (https://github.com/symfony/eventdispatcher ); Installez le via Composer (symfony/event-dispatcher sur Packagist ). Utilisation Évènements Lorsqu'un évènement est réparti, il est identifié par un nom unique (par exemple : kernel.response), que plusieurs «listeners» peuvent écouter. Une instance de Event est aussi créée et passée à tous les «listeners». Comme vous le verrez plus tard, l'objet Event lui-même contient souvent des données à propos de l'évènement qui est réparti. Conventions de Nommage Le nom d'évènement unique peut être n'importe quelle chaîne de caractères, mais suit quelques conventions de nommage simples mais optionnelles : utilisez uniquement des lettres minuscules, des nombres, des points (.), et des tirets du bas (_) ; préfixez les noms par un espace de noms suivi d'un point (par exemple : kernel.) ; terminez les noms par un verbe qui indique quelle action est en train d'être effectuée (par exemple : request). Voici quelques exemples de nom d'évènement correct : kernel.response form.pre_set_data Noms d'évènement et objets évènements Lorsque le répartiteur notifie les «listeners», il passe un objet Event à ces derniers. La classe de base Event est très simple : elle contient une méthode pour stopper la propagation de l'évènement, et rien de plus. Souvent, des données à propos de l'évènement spécifique ont besoin d'être passées avec l'objet Event afin que les «listeners» possèdent l'information nécessaire. Dans le cas de l'évènement kernel.response, l'objet Event qui est créé et passé à chaque «listener» est en fait de type FilterResponseEvent, une sous-classe de l'objet Event de base. Cette classe contient des méthodes telles que getresponse et setresponse, permettant aux «listeners» de récupérer ou même de remplacer l'objet Response. La morale de l'histoire est la suivante : lorsque vous créez un «listener» d'un évènement, l'objet Event qui est passé au «listener» peut être une sous-classe spéciale qui possède des méthodes additionnelles pour récupérer de l'information et répondre à l'évènement.. https://github.com/symfony/eventdispatcher. https://packagist.org/packages/symfony/event-dispatcher. http://api.symfony.com/./symfony/component/eventdispatcher/event.html. http://api.symfony.com/./symfony/component/httpkernel/event/filterresponseevent.html generated on September, 0 Chapter : Le Composant répartiteur d'évènement
Le Répartiteur Le répartiteur est l'objet central du système de répartition d'évènement. En général, un répartiteur unique est créé, et qui maintient un registre des «listeners». Lorsqu'un évènement est réparti via le répartiteur, il notifie tous les «listeners» qui se sont enregistrés auprès de cet évènement: Listing - use Symfony\Component\EventDispatcher\EventDispatcher; $dispatcher = new EventDispatcher(); Connecter des Listeners Pour tirer parti d'un évènement existant, vous avez besoin de connecter un «listener» au répartiteur afin qu'il soit notifié lorsque l'évènement est réparti. Un appel à la méthode addlistener() du répartiteur associe n'importe quel «callable» PHP valide à un évènement: Listing - $listener = new AcmeListener(); $dispatcher->addlistener('foo.action', array($listener, 'onfooaction')); La méthode addlistener() prend jusqu'à trois arguments : Le nom de l'évènement (chaîne de caractères) auquel ce «listener» souhaite se connecter ; Un «callable» PHP qui sera notifié lorsqu'un évènement qu'il écoute est lancé ; Un nombre entier optionnel faisant office de priorité (plus grand signifie plus important) qui détermine quand un «listener» est exécuté par rapport à d'autres «listeners» (vaut par défaut 0). Si deux «listeners» ont la même priorité, ils sont exécutés dans l'ordre dans lequel ils ont été ajoutés au répartiteur. Un callable PHP est une variable PHP qui peut être utilisée par la fonction call_user_func() et qui retourne true lorsque passée à la fonction is_callable(). Cela peut être une instance de \Closure, un objet implémentant la méthode invoke (qui est en fait ce que les closures font), une chaîne de caractères représentant une fonction, ou un tableau représentant une méthode d'objet ou une méthode de classe. Jusqu'ici, vous avez vu comment des objets PHP peuvent être enregistrés comme des «listeners». Vous pouvez aussi enregistrer des Closures PHP en tant que «listeners»: Listing - use Symfony\Component\EventDispatcher\Event; $dispatcher->addlistener('foo.action', function (Event $event) // sera exécuté lorsque l'évènement foo.action est réparti ); Une fois qu'un «listener» est enregistré dans le répartiteur, il attend que l'évènement soit notifié. Dans l'exemple ci-dessus, lorsque l'évènement foo.action est réparti, le répartiteur appelle la méthode AcmeListener::onFooAction et lui passe l'objet Event en tant qu'argument unique: Listing - use Symfony\Component\EventDispatcher\Event; class AcmeListener. http://www.php.net/manual/fr/language.pseudo-types.php#language.types.callback. http://www.php.net/manual/fr/functions.anonymous.php generated on September, 0 Chapter : Le Composant répartiteur d'évènement
0 //... public function onfooaction(event $event) // faire quelque chose ici Dans beaucoup de cas, une sous-classe spéciale d'event qui est spécifique à l'évènement donné est passée au «listener». Cela permet au «listener» d'accéder à des informations spéciales concernant l'évènement. Jetez un oeil à la documentation ou à l'implémentation de chaque évènement pour déterminer l'instance exacte de Symfony\Component\EventDispatcher\Event qui est passée. Par exemple, l'évènement kernel.event passe une instance de Symfony\Component\HttpKernel\Event\FilterResponseEvent: Listing - use Symfony\Component\HttpKernel\Event\FilterResponseEvent public function onkernelresponse(filterresponseevent $event) $response = $event->getresponse(); $request = $event->getrequest(); //... Créer et répartir un évènement En plus d'enregistrer des «listeners» auprès d'évènements existants, vous pouvez créer et répartir vos propres évènements. Cela est utile lorsque vous créez des bibliothèques tierces ainsi que lorsque vous souhaitez garder différents composants de votre propre système flexibles et découplés. La classe statique Events Supposons que vous vouliez créer un nouvel évènement - store.order - qui est lancé chaque fois qu'une commande est créée dans votre application. Pour garder les choses organisées, commencez par créer une classe StoreEvents dans votre application qui sert à définir et documenter votre évènement: Listing - 0 namespace Acme\StoreBundle; final class StoreEvents /** * L'évènement store.order est lancé chaque fois qu'une commande * est créée dans le système. * * Le «listener» de l'évènement reçoit une instance de * Acme\StoreBundle\Event\FilterOrderEvent * * @var string */ const STORE_ORDER = 'store.order'; Notez que cette classe n'effectue en fait aucune action. Le but de la classe StoreEvents est simplement d'avoir un endroit où l'information à propos d'évènements communs puisse être centralisée. Notez aussi qu'une classe spéciale FilterOrderEvent sera passée à chacun des «listeners» de cet évènement. generated on September, 0 Chapter : Le Composant répartiteur d'évènement
Créer un objet «Event» Plus tard, lorsque vous répartirez ce nouvel évènement, vous allez créer une instance de Event et la passer au répartiteur. Ce dernier passe cette même instance à chacun des «listeners» de l'évènement. Si vous n'avez pas à passer d'information à vos «listeners», vous pouvez utiliser la classe par défaut Symfony\Component\EventDispatcher\Event. La plupart du temps, cependant, vous aurez besoin de passer de l'information concernant l'évènement à chaque «listener». Pour accomplir cela, vous allez créer une nouvelle classe qui étend Symfony\Component\EventDispatcher\Event. Dans cet exemple, chaque «listener» va avoir besoin d'accéder à un prétendu objet Order. Créez une classe Event qui rendra cela possible: Listing - 0 namespace Acme\StoreBundle\Event; use Symfony\Component\EventDispatcher\Event; use Acme\StoreBundle\Order; class FilterOrderEvent extends Event protected $order; public function construct(order $order) $this->order = $order; public function getorder() return $this->order; Chaque «listener» a maintenant accès à l'objet Order via la méthode getorder. Répartir l'évènement La méthode dispatch() notifie tous les «listeners» de l'évènement donné. Elle prend deux arguments : le nom de l'évènement à répartir et l'instance d'event à passer à chacun des «listeners» de cet évènement: Listing - 0 use Acme\StoreBundle\StoreEvents; use Acme\StoreBundle\Order; use Acme\StoreBundle\Event\FilterOrderEvent; // la commande est d'une façon ou d'une autre créée ou récupérée $order = new Order(); //... // crée le FilterOrderEvent et le répartit $event = new FilterOrderEvent($order); $dispatcher->dispatch(storeevents::store_order, $event); Notez que l'objet spécifique FilterOrderEvent est créé et passé à la méthode dispatch. Maintenant, tout «listener» de l'évènement store.order va recevoir le FilterOrderEvent et avoir accès à l'objet Order via la méthode getorder: Listing -. http://api.symfony.com/./symfony/component/eventdispatcher/eventdispatcher.html#method_dispatch generated on September, 0 Chapter : Le Composant répartiteur d'évènement
// quelconque classe «listener» qui a été enregistrée pour l'évènement "STORE_ORDER" use Acme\StoreBundle\Event\FilterOrderEvent; public function onstoreorder(filterorderevent $event) $order = $event->getorder(); // faites quelque chose avec ou sur la commande Utiliser les souscripteurs d'évènement La manière la plus commune d'écouter un évènement est d'enregistrer un «listener» d'évènement avec le répartiteur. Ce «listener» peut écouter un ou plusieurs évènements et est notifié chaque fois que ces évènements sont répartis. Une autre façon d'écouter des évènements est via un souscripteur d'évènement. Un souscripteur d'évènement est une classe PHP qui est capable de dire au répartiteur exactement à quels évènements elle souhaite s'inscrire. Elle implémente l'interface EventSubscriberInterface 0, qui requiert une unique méthode nommée getsubscribedevents. Prenez l'exemple suivant d'un souscripteur qui s'inscrit aux évènements kernel.response et store.order: Listing -0 0 0 0 namespace Acme\StoreBundle\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; class StoreSubscriber implements EventSubscriberInterface static public function getsubscribedevents() return array( 'kernel.response' => array( array('onkernelresponsepre', 0), array('onkernelresponsemid', ), array('onkernelresponsepost', 0), ), 'store.order' => array('onstoreorder', 0), ); public function onkernelresponsepre(filterresponseevent $event) //... public function onkernelresponsemid(filterresponseevent $event) //... public function onkernelresponsepost(filterresponseevent $event) //... 0. http://api.symfony.com/./symfony/component/eventdispatcher/eventsubscriberinterface.html generated on September, 0 Chapter : Le Composant répartiteur d'évènement
public function onstoreorder(filterorderevent $event) //... Ceci est très similaire à une classe «listener», excepté que la classe elle-même peut dire au répartiteur quels évènements il devrait écouter. Pour enregistrer un souscripteur dans le répartiteur, utilisez la méthode addsubscriber() : Listing - use Acme\StoreBundle\Event\StoreSubscriber; $subscriber = new StoreSubscriber(); $dispatcher->addsubscriber($subscriber); Le répartiteur va automatiquement enregistrer le souscripteur pour chaque évènement retourné par la méthode getsubscribedevents. Cette méthode retourne un tableau indexé par les noms des évènements et dont les valeurs sont soit le nom de la méthode à appeler ou soit un tableau composé de la méthode à appeler et d'une priorité. L'exemple ci-dessus montre comment enregistrer plusieurs méthodes de «listener» pour le même évènement dans le souscripteur et montre aussi comment passer la priorité de chaque méthode du «listener». Arrêter le déroulement/la propagation d'évènements Dans certains cas, cela peut être utile pour un «listener» d'empêcher n'importe quel(s) autre(s) «listener(s)» d'être appelé(s). En d'autres termes, le «listener» a besoin de pouvoir dire au répartiteur de stopper toute propagation de l'évènement aux prochains «listeners» (c-a-d de ne plus notifier d'autres «listeners»). Ceci peut être accompli depuis l'intérieur du «listener» via la méthode stoppropagation() : Listing - use Acme\StoreBundle\Event\FilterOrderEvent; public function onstoreorder(filterorderevent $event) //... $event->stoppropagation(); Maintenant, tout «listener» de store.order qui n'a pas encore été appelé ne sera pas appelé. Il est possible de détecter si un évènement a été stoppé en utilisant la méthode ispropagationstopped() qui retourne une valeur booléenne: Listing - $dispatcher->dispatch('foo.event', $event); if ($event->ispropagationstopped()) //.... http://api.symfony.com/./symfony/component/eventdispatcher/eventdispatcher.html#method_addsubscriber. http://api.symfony.com/./symfony/component/eventdispatcher/event.html#method_stoppropagation. http://api.symfony.com/./symfony/component/eventdispatcher/event.html#method_ispropagationstopped generated on September, 0 Chapter : Le Composant répartiteur d'évènement 0
Évènements et «Listeners» connaissant le répartiteur d'évènements New in version.: L'objet Event contient une référence au répartiteur l'ayant invoqué depuis Symfony.. Le Répartiteur d'évènement injecte toujours une référence de lui-même dans l'objet «évènement» passé. Cela signifie que tous les «listeners» ont un accès direct à l'objet EventDispatcher qui a notifié le «listener» via la méthode de l'objet Event passé getdispatcher(). Cela peut amener à certaines utilisations avancées de l'eventdispatcher incluant le fait de laisser des «listeners» répartir d'autres évènements, le chaînage d'évènements ou même le «chargement fainéant» («lazy loading» en anglais) de plus de «listeners» dans l'objet répartiteur. Voyez les exemples suivants : «Chargement fainéant» de «listeners»: Listing - 0 use Symfony\Component\EventDispatcher\Event; use Acme\StoreBundle\Event\StoreSubscriber; class Foo private $started = false; public function mylazylistener(event $event) if (false === $this->started) $subscriber = new StoreSubscriber(); $event->getdispatcher()->addsubscriber($subscriber); $this->started = true; //... plus de code Répartir un autre évènement depuis un «listener»: Listing - 0 use Symfony\Component\EventDispatcher\Event; class Foo public function myfoolistener(event $event) $event->getdispatcher()->dispatch('log', $event); //... plus de code Bien que le code ci-dessus soit suffisant dans la plupart des cas, si votre application utilise plusieurs instances d'eventdispatcher, vous pourriez avoir besoin d'injecter une instance spécifiquement connue de l'eventdispatcher dans vos «listeners». Cela pourrait être effectué en utilisant l'injection via constructeur ou «setter» comme suit : Injection via le constructeur: Listing -. http://api.symfony.com/./symfony/component/eventdispatcher/event.html#method_getdispatcher generated on September, 0 Chapter : Le Composant répartiteur d'évènement
0 use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Foo protected $dispatcher = null; public function construct(eventdispatcherinterface $dispatcher) $this->dispatcher = $dispatcher; Ou injection via «setter»: Listing - 0 use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Foo protected $dispatcher = null; public function seteventdispatcher(eventdispatcherinterface $dispatcher) $this->dispatcher = $dispatcher; Choisir entre les deux est une question de goût. Beaucoup préfèrent l'injection via le constructeur car les objets sont totalement initialisés au moment de la construction. Mais lorsque vous avez une longue liste de dépendances, utiliser une injection via «setter» peut être la manière de faire, et plus particulièrement lorsqu'il s'agit de dépendances optionnelles. Raccourcis du Répartiteur New in version.: La méthode EventDispatcher::dispatch() retourne l'évènement depuis Symfony.. La méthode EventDispatcher::dispatch retourne toujours un objet Event. Cela permet d'utiliser plusieurs raccourcis. Par exemple, si vous n'avez pas besoin d'avoir un objet évènement personnalisé, vous pouvez simplement utiliser un objet Event. Vous ne devez même pas passer ce dernier au répartiteur car il va en créer un par défaut à moins que vous ne lui en passiez un spécifiquement: Listing - $dispatcher->dispatch('foo.event'); De plus, l'«eventdisptacher» retourne toujours tout évènement qui a été réparti, c'est-a-dire soit l'évènement qui a été passé, soit l'évènement qui a été créé en interne pas le répartiteur. Cela permet d'utiliser des raccourcis sympas: Listing - if (!$dispatcher->dispatch('foo.event')->ispropagationstopped()) //... Ou:. http://api.symfony.com/./symfony/component/eventdispatcher/eventdispatcher.html#method_dispatch. http://api.symfony.com/./symfony/component/eventdispatcher/event.html. http://api.symfony.com/./symfony/component/eventdispatcher/event.html generated on September, 0 Chapter : Le Composant répartiteur d'évènement
Listing -0 $barevent = new BarEvent(); $bar = $dispatcher->dispatch('bar.event', $barevent)->getbar(); Ou: Listing - $response = $dispatcher->dispatch('bar.event', new BarEvent())->getBar(); et ainsi de suite... Introspection du nom de l'évènement New in version.: Le nom de l'évènement a été ajouté à l'objet Event depuis Symfony.. Comme l'eventdispatcher connaît déjà le nom de l'évènement lorsqu'il le répartit, le nom de l'évènement est aussi injecté dans les objets Event, le rendant disponible aux «listeners» d'évènement via la méthode getname(). Le nom de l'évènement (comme pour n'importe quelle autre donnée dans un objet évènement personnalisé) peut être utilisé à part entière dans la logique d'exécution du «listener»: Listing - use Symfony\Component\EventDispatcher\Event; class Foo public function myeventlistener(event $event) echo $event->getname();. http://api.symfony.com/./symfony/component/eventdispatcher/event.html. http://api.symfony.com/./symfony/component/eventdispatcher/event.html#method_getname generated on September, 0 Chapter : Le Composant répartiteur d'évènement
Chapter L'objet Évènement Générique New in version.: La classe évènement GenericEvent a été ajoutée dans Symfony.. La classe de base Event fournie par le composant Event Dispatcher est délibérément spartiate afin de permettre la création d'objets évènement spécifiques à une API grâce à l'héritage en utilisant la POO («Programmation Orientée Objet»). Cela permet d'avoir du code élégant et lisible dans des applications complexes. La classe GenericEvent est à disposition pour ceux qui souhaitent juste utiliser un objet évènement à travers leur application. Elle est adaptée à la plupart des besoins «out of the box» car elle suit le pattern standard de l'observeur où l'objet évènement encapsule un «sujet» évènement, tout en ayant la possibilité d'avoir des arguments additionnels. La classe GenericEvent possède une API simple en plus de la classe de base Event : construct() : Le constructeur prend le sujet de l'évènement et n'importe quels arguments ; getsubject() : Récupère le sujet ; setargument() : Définit un argument par clé ; setarguments() : Définit un tableau d'arguments ; getargument() : Récupère un argument par clé ; getarguments() 0 : Récupère un tableau d'arguments ; hasargument() : Retourne «true» si la clé de l'argument existe. Le GenericEvent implémente aussi ArrayAccess sur les arguments de l'évènement qui fait que cela facilite réellement le passage d'arguments supplémentaires eu égard au sujet de l'évènement.. http://api.symfony.com/./symfony/component/eventdispatcher/event.html. http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html. http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html. http://api.symfony.com/./symfony/component/eventdispatcher/event.html. http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html# construct(). http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html#getsubject(). http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html#setargument(). http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html#setarguments(). http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html#getargument() 0. http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html#getarguments(). http://api.symfony.com/./symfony/component/eventdispatcher/genericevent.html#hasargument(). http://php.net/manual/en/class.arrayaccess.php generated on September, 0 Chapter : L'objet Évènement Générique
Les exemples suivants montrent des cas d'école pour vous donner une idée générale de la flexibilité. Les exemples supposent que des «listeners» d'évènement ont été ajoutés au répartiteur. Passer simplement un sujet: Listing - 0 use Symfony\Component\EventDispatcher\GenericEvent; $event = GenericEvent($subject); $dispatcher->dispatch('foo', $event); class FooListener public function handler(genericevent $event) if ($event->getsubject() instanceof Foo) //... Passer et gérer des arguments en utilisant l'api ArrayAccess l'évènement: pour accéder aux arguments de Listing - 0 use Symfony\Component\EventDispatcher\GenericEvent; $event = new GenericEvent($subject, array('type' => 'foo', 'counter' => 0))); $dispatcher->dispatch('foo', $event); echo $event['counter']; class FooListener public function handler(genericevent $event) if (isset($event['type']) && $event['type'] === 'foo') //... faites quelque chose $event['counter']++; Filtrer des données: Listing - 0 use Symfony\Component\EventDispatcher\GenericEvent; $event = new GenericEvent($subject, array('data' => 'foo')); $dispatcher->dispatch('foo', $event); echo $event['data']; class FooListener public function filter(genericevent $event) strtolower($event['data']);. http://php.net/manual/en/class.arrayaccess.php generated on September, 0 Chapter : L'objet Évènement Générique
generated on September, 0 Chapter : L'objet Évènement Générique
Chapter Le Répartiteur d'évènement du Container Aware New in version.: Cette fonctionnalité a été déplacée dans l'«eventdispatcher» dans Symfony.. Introduction La classe ContainerAwareEventDispatcher est une implémentation spéciale du répartiteur d'évènement qui est couplé au conteneur de services qui fait partie du composant d'injection de Dépendance. Il permet aux services d'être spécifiés en tant que «listeners» d'évènement rendant le répartiteur d'évènement extrêmement puissant. Les services sont chargés de manière fainéante («lazy loading» en anglais), ce qui signifie que les services attachés en tant que «listeners» ne seront créés que si un évènement qui est réparti nécessite ces «listeners». Installation L'installation est très facile et nécessite uniquement l'injection d'une interface ContainerInterface dans la classe ContainerAwareEventDispatcher : Listing - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; $container = new ContainerBuilder(); $dispatcher = new ContainerAwareEventDispatcher($container);. http://api.symfony.com/./symfony/component/eventdispatcher/containerawareeventdispatcher.html. http://api.symfony.com/./symfony/component/dependencyinjection/containerinterface.html. http://api.symfony.com/./symfony/component/eventdispatcher/containerawareeventdispatcher.html generated on September, 0 Chapter : Le Répartiteur d'évènement du Container Aware
Ajouter des «Listeners» Le Répartiteur d'évènement du Container Aware peut soit directement charger des services spécifiques, soit des services qui implémentent EventSubscriberInterface. Les exemples suivants supposent que le conteneur de services a été chargé avec tous les services qui sont mentionnés. Les services doivent être marqués comme publics dans le conteneur. Ajouter des services Pour connecter des définitions de service existantes, utilisez la méthode addlistenerservice() où le $callback est un tableau de array($serviceid, $methodname): Listing - $dispatcher->addlistenerservice($eventname, array('foo', 'loglistener')); Ajouter des services souscripteurs Les «EventSubscribers» peuvent être ajoutés en utilisant la méthode addsubscriberservice() où le premier argument est l'id du service souscripteur, et où le second argument est le nom de la classe du service (qui doit implémenter EventSubscriberInterface ) comme suit: Listing - $dispatcher->addsubscriberservice('kernel.store_subscriber', 'StoreSubscriber'); L'EventSubscriberInterface va ressembler exactement à ce à quoi vous vous attendez: Listing - 0 0 use Symfony\Component\EventDispatcher\EventSubscriberInterface; //... class StoreSubscriber implements EventSubscriberInterface static public function getsubscribedevents() return array( 'kernel.response' => array( array('onkernelresponsepre', 0), array('onkernelresponsepost', 0), ), 'store.order' => array('onstoreorder', 0), ); public function onkernelresponsepre(filterresponseevent $event) //.... http://api.symfony.com/./symfony/component/eventdispatcher/eventsubscriberinterface.html. http://api.symfony.com/./symfony/component/eventdispatcher/containerawareeventdispatcher.html#addlistenerservice(). http://api.symfony.com/./symfony/component/eventdispatcher/containerawareeventdispatcher.html#addsubscriberservice(). http://api.symfony.com/./symfony/component/eventdispatcher/eventsubscriberinterface.html generated on September, 0 Chapter : Le Répartiteur d'évènement du Container Aware
0 public function onkernelresponsepost(filterresponseevent $event) //... public function onstoreorder(filterorderevent $event) //... generated on September, 0 Chapter : Le Répartiteur d'évènement du Container Aware
Chapter Le composant ExpressionLanguage Le composant ExpressionLanguage fournit un moteur qui peut compiler et évaluer des expressions. Une expression est un code simple qui retourne une valeur (souvent un booléen mais pas toujours). New in version.: Le composant ExpressionLanguage a été ajouté dans Symfony.. Installation Vous pouvez l'installer de deux manières différentes: Installez-le via Composer (symfony/expression-language sur Packagist ); Utilisez le dépôt Git officiel (https://github.com/symfony/expression-language ). Comment le moteur d'expression peut-il m'aider? Le but du composant est de permettre à ses utilisateurs d'utiliser des expression au sein d'une configuration qui contient une logique plus complexe. Par exemple, le Framework Symfony utilise les expressions dans la sécurité, pour la validation de règles et dans la correspondance de routes. En plus d'utiliser le composant dans le framework lui-même, le composant ExpressionLanguage est le parfait candidat pour les fondations d'un moteur de règles métier. L'idée est de laisser le webmaster d'un site configurer des choses de manière dynamique en utilisant PHP mais sans introduire de failles de sécurité: Listing - # Récupère le prix spécial si user.getgroup() in ['good_customers', 'collaborator']. https://packagist.org/packages/symfony/expression-language. https://github.com/symfony/expression-language generated on September, 0 Chapter : Le composant ExpressionLanguage 0
# Met en avant un article sur la home quand article.commentcount > 00 and article.category not in ["misc"] # Envoie une alerte quand product.stock < Les expressions peuvent être vues comme un bac à sable PHP très limité et immunisé contre les injections externes puisque vous déclarez explicitement quelles variables sont disponibles dans une expression. Usage Le composant ExpressionLanguage peut compiler et évaluer des expressions. Une expression est un petit code simple qui retourne souvent un booléen qui peut être utilisé dans un if par le code qui exécute l'expression. Un exemple simple d'une expression est +. Vous pouvez également utiliser des expressions plus complexes comme somearray[].somemethod('bar'). Le composant fournit deux méthodes pour travailler avec les expressions: evaluation: l'expression est évaluée sans être compilée en PHP; compile: l'expression est compilée en PHP, elle peut donc être mise en cache et évaluée. La classe principale du composant est ExpressionLanguage : Listing - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $language = new ExpressionLanguage(); echo $language->evaluate(' + '); // affiche echo $language->compile(' + '); // affiche ( + ) Syntaxe des expressions Lisez Syntaxe des expressions pour en savoir plus sur la syntaxe du composant ExpressionLanguage. Passer des variables Vous pouvez également passer des variables, qui peuvent être de n'importe quel type PHP valide (incluant les objets), dans une expression: Listing - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $language = new ExpressionLanguage(); class Apple public $variety;. http://api.symfony.com/./symfony/component/expressionlanguage/expressionlanguage.html generated on September, 0 Chapter : Le composant ExpressionLanguage
0 $apple = new Apple(); $apple->variety = 'Granny Smith'; echo $language->evaluate( 'fruit.variety', array( 'fruit' => $apple, ) ); Cela affichera "Granny Smith". Pour plus d'informations, lisez l'article Syntaxe des expressions et plus particulièrement Travailler avec les objets et Travailler avec les tableaux. Mise en cache Le composant fournit différentes stratégies de mise en cache, lisez Cacher les expressions en utilisant les parsers de cache pour en savoir plus. generated on September, 0 Chapter : Le composant ExpressionLanguage
Chapter Syntaxe des expressions Le composant ExpressionLanguage utilise une syntaxe spécifique basée sur la syntaxe de Twig. Dans ce document, vous trouverez toutes les syntaxes supportées. Valeurs explicites Le composant supporte: strings - simples et doubles quotes (ex 'hello') numbers - ex 0 arrays - notation JSON (ex [, ]) hashes - notation JSON (ex foo: 'bar' ) booleans - true et false null - null Travailler avec les objets Lorsque vous passez des objets à une expression, vous pouvez utiliser différentes syntaxes pour accéder aux propriétés des objets et appeler leurs méthodes. Accéder aux propriétés publiques Vous pouvez utiliser la syntaxe. pour accéder aux propriétés publiques des objets, comme en JavaScript: Listing - class Apple public $variety; $apple = new Apple(); $apple->variety = 'Granny Smith'; generated on September, 0 Chapter : Syntaxe des expressions
0 echo $language->evaluate( 'fruit.variety', array( 'fruit' => $apple, ) ); Cela affichera Granny Smith. Appeler les méthodes Comme en JavaScript, la syntaxe. peut également être utilisée pour faire appel aux méthodes de l'objet: Listing - 0 0 class Robot public function sayhi($times) $greetings = array(); for ($i = 0; $i < $times; $i++) $greetings[] = 'Hi'; return implode(' ', $greetings).'!'; $robot = new Robot(); echo $language->evaluate( 'robot.sayhi()', array( 'robot' => $robot, ) ); Cela affichera Hi Hi Hi!. Travailler avec les fonctions Vous pouvez aussi utiliser des fonctions dans les expressions en utilisant la même syntaxe qu'en PHP et JavaScript. Le composant ExpressionLanguage est fourni avec une fonction par défaut : constant(), qui retournera la valeur d'une constante PHP: Listing - define('db_user', 'root'); echo $language->evaluate( 'constant("db_user")' ); Cela affichera root. generated on September, 0 Chapter : Syntaxe des expressions
Pour savoir comment déclarer vos propres fonctions et les utiliser dans les expressions, lisez "Étendre ExpressionLanguage". Travailler avec les tableaux Si vous passez un tableau dans une expression, utilisez la syntaxe [] pour accéder aux clés du tableau, comme en JavaScript: Listing - $data = array('vie' => 0, 'univers' => 0, 'tout' => ); echo $language->evaluate( 'data["vie"] + data["univers"] + data["tout"]', array( 'data' => $data, ) ); Cela affichera. Opérateurs supportés Le composant est fourni avec de nombreux opérateurs : Opérateurs arithmétiques + (addition) - (soustraction) * (multiplication) / (division) % (modulo) ** (puissance) Par exemple: Listing - echo $language->evaluate( 'vie + univers + tout', array( 'vie' => 0, 'univers' => 0, 'tout' =>, ) ); Cela affichera. Opérateurs bit à bit & (et) (ou) ^ (ou exclusif) generated on September, 0 Chapter : Syntaxe des expressions
Opérateurs de comparaison == (égal) === (identique)!= (non égal)!== (non identique) < (inférieur à) > (supérieur à) <= (inférieur ou égal à) >= (supérieur ou égal à) matches (expression régulière) Pour tester si une chaine ne correspond pas à une regex, utilisez l'opérateur logique not combiné avec l'opérateur matches: Listing - $language->evaluate('not ("foo" matches "/bar/")'); // retourne true Vous devez utiliser les parenthèses car l'opérateur unaire not prévaut sur l'opérateur binaire matches. Exemples: Listing - 0 $ret = $language->evaluate( 'vie == tout', array( 'vie' => 0, 'univers' => 0, 'tout' =>, ) ); $ret = $language->evaluate( 'vie > tout', array( 'vie' => 0, 'univers' => 0, 'tout' =>, ) ); Les deux variables seront définies à false. Opérateurs logiques not ou! and ou && or pi Par exemple: Listing - $ret = $language->evaluate( 'vie < univers or vie < tout', array( 'vie' => 0, generated on September, 0 Chapter : Syntaxe des expressions
); ) 'univers' => 0, 'tout' =>, Cette variable $ret sera définie à true. Opérateurs de chaines ~ (concaténation) Par exemple: Listing - echo $language->evaluate( 'firstname~" "~lastname', array( 'firstname' => 'Arthur', 'lastname' => 'Dent', ) ); Cela affichera Arthur Dent. Opérateurs de tableaux in (contient) not in (ne contient pas) Par exemple: Listing -0 0 class User public $group; $user = new User(); $user->group = 'human_resources'; $ingroup = $language->evaluate( 'user.group in ["human_resources", "marketing"]', array( 'user' => $user ) ); La variable $ingroup vaudra true. Opérateurs numériques.. (plage) Par exemple: Listing - class User generated on September, 0 Chapter : Syntaxe des expressions
0 public $age; $user = new User(); $user->age = ; $language->evaluate( 'user.age in..', array( 'user' => $user, ) ); Cela vaudra true, car user.age est compris entre et. Opérateurs ternaires foo? 'yes' : 'no' foo?: 'no' (égal à foo? foo : 'no') foo? 'yes' (égal à foo? 'yes' : '') generated on September, 0 Chapter : Syntaxe des expressions
Chapter Étendre ExpressionLanguage Vous pouvez étendre le composant ExpressionLanguage en ajoutant des fonctions personnalisées. Par exemple, dans Symfony, la partie Sécurité possède ses propres fonctions pour vérifier les droits des utilisateurs. Si vous voulez savoir comment utiliser les fonctions dans une expression, lisez "Travailler avec les fonctions". Déclarer des fonctions Les fonctions sont déclarées dans chaque instance spécifique de ExpressionLanguage. Cela signifie que les fonctions peuvent être utilisées dans n'importe quelle expression exécutée par cette instance. Pour déclarer une fonction, utilisez register(). Cette méthode demande arguments : name - Le nom de la fonction dans une expression; compiler - Une fonction exécutée à la compilation d'une expression qui utilise la fonction; evaluator - Une fonction exécutée lorsque l'expression est évaluée. Listing - 0 use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $language = new ExpressionLanguage(); $language->register('lowercase', function ($str) if (!is_string($str)) return $str; return sprintf('strtolower(%s)', $str);, function ($arguments, $str) if (!is_string($str)). http://api.symfony.com/./symfony/component/expressionlanguage/expressionlanguage.html#method_register generated on September, 0 Chapter : Étendre ExpressionLanguage
return $str; return strtolower($str); ); echo $language->evaluate('lowercase("hello")'); Cela affichera hello. On passe une variable arguments aux deux méthodes compiler et evaluator. Elle est égale au second argument de evaluate() ou compile() (ex "values" lors de l'évaluation ou "names" lors de la compilation). Créer une nouvelle classe ExpressionLanguage Lorsque vous utilisez la classe ExpressionLanguage dans votre bibliothèque, il est recommandé de créer une nouvelle classe ExpressionLanguage et d'y déclarer vos fonctions. Surchargez registerfunctions pour ajouter vos propres fonctions: Listing - 0 0 namespace Acme\AwesomeLib\ExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; class ExpressionLanguage extends BaseExpressionLanguage protected function registerfunctions() parent::registerfunctions(); // n'oubliez pas de déclarer aussi les fonctions du noyau $this->register('lowercase', function ($str) if (!is_string($str)) return $str; return sprintf('strtolower(%s)', $str);, function ($arguments, $str) if (!is_string($str)) return $str; ); return strtolower($str); generated on September, 0 Chapter : Étendre ExpressionLanguage 0
Chapter Cacher les expressions en utilisant les parsers de cache Le composant ExpressionLanguage fournit déjà une méthode compile() pour mettre en cache des expressions en PHP. Mais, en interne, le composant met également en cache les expressions parsées/ analysées donc les expressions dupliquées peuvent être compilées/évaluées plus vite. Le processus Les méthodes evaluate() et compile() ont toutes les deux besoin de faire des choses avant de pouvoir retourner des valeurs. En ce qui concerne evaluate(), ce travail peut être très conséquent. Les deux méthodes ont besoin de découper (tokenizer) et analyser (parser) les expressions. Cela est réalisé par la méthode parse(). Elle retourne une ParsedExpression. Maintenant, la méthode compile() retourne juste la conversion de cet objet en chaine de caractères. La méthode evaluate() a besoin de boucler sur les "noeuds" (morceaux d'une expression enregistrés dans la ParsedExpression) et les évaluer à la volée. Pour gagner du temps, l'expressionlanguage met en cache la ParsedExpression afin d'éviter les étapes de tokenization et de parsing pour les expressions dupliquées. La mise en cache est effectuée par une instance de ParserCacheInterface (par défaut, elle utilise une ArrayParserCache ). Vous pouvez personnaliser cela en créant votre propre ParserCache et en l'injectant dans l'objet via le constructeur: Listing - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Acme\ExpressionLanguage\ParserCache\MyDatabaseParserCache;. http://api.symfony.com/./symfony/component/expressionlanguage/expressionlanguage.html#method_compile. http://api.symfony.com/./symfony/component/expressionlanguage/expressionlanguage.html#method_evaluate. http://api.symfony.com/./symfony/component/expressionlanguage/expressionlanguage.html#method_parse. http://api.symfony.com/./symfony/component/expressionlanguage/parsedexpression.html. http://api.symfony.com/./symfony/component/expressionlanguage/parsercache/parsercacheinterface.html. http://api.symfony.com/./symfony/component/expressionlanguage/parsercache/arrayparsercache.html generated on September, 0 Chapter : Cacher les expressions en utilisant les parsers de cache
$cache = new MyDatabaseParserCache(...); $language = new ExpressionLanguage($cache); Le DoctrineBridge fournit une implémentation de Parser Cache qui utilise la bibliothèque de cache Doctrine, qui vous aide à mette en cache quelle que soit la stratégie de cache comme Apc, système de fichiers ou Memcached. Utiliser les expressions parsées et sérialisées Les méthodes evaluate() et compile() peuvent toutes les deux prendre en charge une ParsedExpression ou une SerializedParsedExpression: Listing - use Symfony\Component\ExpressionLanguage\ParsedExpression; //... $expression = new ParsedExpression($language->parse(' + ')); echo $language->evaluate($expression); // affiche Listing - use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; //... $expression = new SerializedParsedExpression( serialize($language->parse(' + ')) ); echo $language->evaluate($expression); // affiche. https://github.com/symfony/doctrinebridge. http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html generated on September, 0 Chapter : Cacher les expressions en utilisant les parsers de cache
Chapter Le Composant «Filesystem» («système de fichiers» en français) Le Composant «Filesystem» fournit des utilitaires basiques pour le système de fichiers. New in version.: Le Composant «Filesystem» fait son apparition dans Symfony.. Auparavant, la classe Filesystem était située dans le composant HttpKernel. Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt officiel Git (https://github.com/symfony/filesystem ) ; Installez le via Composer (symfony/filesystem sur Packagist). Utilisation La classe Filesystem est l'unique endroit où se trouve les utilitaires pour effectuer des opérations sur le système de fichiers: Listing - use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Exception\IOException; $fs = new Filesystem(); try. https://github.com/symfony/filesystem. http://api.symfony.com/./symfony/component/filesystem/filesystem.html generated on September, 0 Chapter : Le Composant «Filesystem» («système de fichiers» en français)
0 $fs->mkdir('/tmp/random/dir/'. mt_rand()); catch (IOException $e) echo "An error occured while creating your directory"; Les méthodes mkdir(), chgrp(), chown(), remove() et touch() peuvent recevoir une chaîne de caractères, un tableau ou n'importe quel objet implémentant Traversable en tant qu'argument cible. Mkdir «Mkdir» crée un répertoire. Sur les systèmes de fichiers posix, les répertoires sont créés avec une valeur de mode qui par défaut est 0. Vous pouvez utiliser le second argument pour définir votre propre mode: Listing - $fs->mkdir('/tmp/photos', 000); Vous pouvez passer un tableau ou quelconque objet Traversable en tant que premier argument. Exists «Exists» vérifie la présence de tous les fichiers ou répertoires et retourne «false» si un fichier est manquant: Listing - // ce répertoire existe, retourne «true» $fs->exists('/tmp/photos'); // rabbit.jpg existe, bottle.png n'existe pas, retourne «false» $fs->exists(array('rabbit.jpg', 'bottle.png')); Vous pouvez passer un tableau ou n'importe quel objet Traversable 0 en tant que premier argument. Copy Cette méthode est utilisée pour copier des fichiers. Si la cible existe déjà, le fichier est copié seulement si la date de modification de la source est plus récente que celle de la cible. Ce comportement peut être surchargé par un troisième argument booléen:. http://api.symfony.com/./symfony/component/filesystem/filesystem.html#method_mkdir. http://api.symfony.com/./symfony/component/filesystem/filesystem.html#method_chgrp. http://api.symfony.com/./symfony/component/filesystem/filesystem.html#method_chown. http://api.symfony.com/./symfony/component/filesystem/filesystem.html#method_remove. http://api.symfony.com/./symfony/component/filesystem/filesystem.html#method_touch. http://php.net/manual/en/class.traversable.php. http://php.net/manual/en/class.traversable.php 0. http://php.net/manual/en/class.traversable.php generated on September, 0 Chapter : Le Composant «Filesystem» («système de fichiers» en français)
Listing - // fonctionne uniquement si image-icc a été modifié après image.jpg $fs->copy('image-icc.jpg', 'image.jpg'); // image.jpg va être écrasé $fs->copy('image-icc.jpg', 'image.jpg', true); Touch «Touch» définit la date de modification et d'accès d'un fichier. La date courante est utilisée par défaut. Vous pouvez définir la vôtre avec le second argument. Le troisième argument est la date d'accès: Listing - // définit la date de modification avec la date courante $fs->touch('file.txt'); // définit la date de modification avec la date courante + 0 secondes $fs->touch('file.txt', time() + 0); // définit la date d'accès avec la date courante - 0 secondes $fs->touch('file.txt', time(), time() - 0); Vous pouvez passer un tableau ou n'importe quel objet Traversable en tant que premier argument. Chown «Chown» est utilisée pour changer le propriétaire d'un fichier. Le troisième argument est une option récursive booléenne: Listing - // définit le propriétaire de la vidéo lolcat comme étant www-data $fs->chown('lolcat.mp', 'www-data'); // change le propriétaire du répertoire «video» de manière récursive $fs->chown('/video', 'www-data', true); Vous pouvez passer un tableau ou n'importe quel objet Traversable en tant que premier argument. Chgrp «Chgrp» est utilisée pour changer le groupe d'un fichier. Le troisième argument est une option récursive booléenne: Listing - // définit le groupe de la vidéo lolcat comme étant nginx $fs->chgrp('lolcat.mp', 'nginx'); // change le groupe du répertoire «video» de manière récursive $fs->chgrp('/video', 'nginx', true);. http://php.net/manual/en/class.traversable.php. http://php.net/manual/en/class.traversable.php generated on September, 0 Chapter : Le Composant «Filesystem» («système de fichiers» en français)
Vous pouvez passer un tableau ou n'importe quel objet Traversable en tant que premier argument. Chmod «Chmod» est utilisée pour changer le mode d'un fichier. Le troisième argument est une option récursive booléenne: Listing - // définit le mode de la vidéo comme étant 000 $fs->chmod('video.ogg', 000); // change le mode du répertoire «src» de manière récursive $fs->chmod('src', 000, true); Vous pouvez passer un tableau ou n'importe quel objet Traversable en tant que premier argument. Remove «Remove» vous permet de supprimer des fichiers, des liens symboliques et des répertoires très facilement: Listing - $fs->remove(array('symlink', '/path/to/directory', 'activity.log')); Vous pouvez passer un tableau ou n'importe quel objet Traversable en tant que premier argument. Rename «Rename» est utilisée pour renommer des fichiers et des répertoires: Listing -0 // renomme un fichier $fs->rename('/tmp/processed_video.ogg', '/path/to/store/video_.ogg'); // renomme un répertoire $fs->rename('/tmp/files', '/path/to/store/files'); symlink Crée un lien symbolique depuis une cible vers la destination. Si le système de fichiers ne supporte pas les liens symboliques, un troisième argument booléen est disponible: Listing - // crée un lien symbolique $fs->symlink('/path/to/source', '/path/to/destination');. http://php.net/manual/en/class.traversable.php. http://php.net/manual/en/class.traversable.php. http://php.net/manual/en/class.traversable.php generated on September, 0 Chapter : Le Composant «Filesystem» («système de fichiers» en français)
// duplique le répertoire source si le système de fichiers ne supporte pas les // liens symboliques $fs->symlink('/path/to/source', '/path/to/destination', true); makepathrelative Retourne le chemin relatif d'un répertoire par rapport à un autre: Listing - // retourne '../' $fs->makepathrelative('/var/lib/symfony/src/symfony/', '/var/lib/symfony/src/symfony/ Component'); // retourne 'videos' $fs->makepathrelative('/tmp', '/tmp/videos'); mirror «Reflète» un répertoire: Listing - $fs->mirror('/path/to/source', '/path/to/target'); isabsolutepath isabsolutepath retourne «true» si le chemin donné est absolu, «false» sinon: Listing - // retourne «true» $fs->isabsolutepath('/tmp'); // retourne «true» $fs->isabsolutepath('c:\\windows'); // retourne «false» $fs->isabsolutepath('tmp'); // retourne «false» $fs->isabsolutepath('../dir'); Gestion des erreurs Chaque fois que quelque chose de faux/mal intervient, une exception implémentant ExceptionInterface est lancée. Avant la version., mkdir() retournait un booléen et ne lançait pas d'exception. Depuis la version., une IOException est lancée si la création d'un répertoire échoue.. http://api.symfony.com/./symfony/component/filesystem/exception/exceptioninterface.html. http://api.symfony.com/./symfony/component/filesystem/filesystem.html#method_mkdir. http://api.symfony.com/./symfony/component/filesystem/exception/ioexception.html generated on September, 0 Chapter : Le Composant «Filesystem» («système de fichiers» en français)
Chapter 0 Le Composant Finder Le Composant Finder trouve des fichiers et des répertoires via une interface intuitive. Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt Git officiel (https://github.com/symfony/finder ) ; Installez le via Composer (symfony/finder sur Packagist ). Utilisation La classe Finder trouve des fichiers et/ou des répertoires: Listing 0-0 use Symfony\Component\Finder\Finder; $finder = new Finder(); $finder->files()->in( DIR ); foreach ($finder as $file) // affiche le chemin absolu print $file->getrealpath()."\n"; // affiche le chemin relatif d'un fichier, sans le nom du fichier print $file->getrelativepath()."\n"; // affiche le chemin relatif du fichier print $file->getrelativepathname()."\n";. https://github.com/symfony/finder. https://packagist.org/packages/symfony/finder. http://api.symfony.com/./symfony/component/finder/finder.html generated on September, 0 Chapter 0: Le Composant Finder
$file est une instance de SplFileInfo qui étend SplFileInfo afin de fournir des méthodes permettant de travailler avec les chemins relatifs. Le code ci-dessus affiche les noms de tous les fichiers dans le répertoire courant de manière récursive. La classe «Finder» utilise une interface intuitive, et de ce fait toutes les méthodes retournent une instance de «Finder». Une instance de «Finder» est un Iterator PHP. Donc, au lieu d'itérer sur le «Finder» avec un foreach, vous pouvez aussi le convertir en un tableau avec la méthode iterator_to_array, ou récupérer le nombre d'éléments grâce à iterator_count. Critères Le chemin Le chemin (c-a-d l'endroit où se trouve le fichier) est le seul critère obligatoire. Il informe le «finder» du répertoire à utiliser pour la recherche: Listing 0- $finder->in( DIR ); Vous pouvez effectuer une recherche dans plusieurs répertoires/chemins en chaînant les appels de la méthode in() : Listing 0- $finder->files()->in( DIR )->in('/elsewhere'); Vous pouvez exclure des répertoires de votre recherche grâce à la méthode exclude() 0 : Listing 0- $finder->in( DIR )->exclude('ruby'); Comme le «Finder» utilise des itérateurs PHP, vous pouvez passer n'importe quelle URL ayant un protocole supporté: Listing 0- $finder->in('ftp://example.com/pub/'); Et cela fonctionne aussi avec les flux définis par l'utilisateur: Listing 0- use Symfony\Component\Finder\Finder; $s = new \Zend_Service_Amazon_S($key, $secret); $s->registerstreamwrapper("s"); $finder = new Finder(); $finder->name('photos*')->size('< 00K')->date('since hour ago'); foreach ($finder->in('s://bucket-name') as $file). http://api.symfony.com/./symfony/component/finder/splfileinfo.html. http://php.net/manual/en/class.splfileinfo.php. http://www.php.net/manual/fr/spl.iterators.php. http://php.net/manual/en/function.iterator-to-array.php. http://php.net/manual/en/function.iterator-count.php. http://api.symfony.com/./symfony/component/finder/finder.html#in() 0. http://api.symfony.com/./symfony/component/finder/finder.html#exclude(). http://www.php.net/manual/fr/wrappers.php generated on September, 0 Chapter 0: Le Composant Finder
0 // faites quelque chose print $file->getfilename()."\n"; Lisez la documentation sur les Flux pour apprendre comment créer vos propres flux. Fichiers et répertoires Par défaut, le «Finder» retourne des fichiers et des répertoires ; mais les méthodes files() et directories() contrôlent cela: Listing 0- $finder->files(); $finder->directories(); Si vous souhaitez suivre des liens, utilisez la méthode followlinks(): Listing 0- $finder->files()->followlinks(); Par défaut, l'itérateur ignore les fichiers VCS dont le type est populaire. Cela peut être modifié grâce à la méthode ignorevcs(): Listing 0- $finder->ignorevcs(false); Triage Triez les résultats par nom ou par type (répertoires en premier, ensuite les fichiers): Listing 0-0 $finder->sortbyname(); $finder->sortbytype(); Notez que les méthodes sort* ont besoin de récupérer tous les éléments correspondants à la recherche pour effectuer leur travail. Pour des itérateurs de grande taille, cela est lent. Vous pouvez aussi définir votre propre algorithme de triage via la méthode sort(): Listing 0- $sort = function (\SplFileInfo $a, \SplFileInfo $b) return strcmp($a->getrealpath(), $b->getrealpath()); ;. http://www.php.net/streams. http://api.symfony.com/./symfony/component/finder/finder.html#files(). http://api.symfony.com/./symfony/component/finder/finder.html#directories() generated on September, 0 Chapter 0: Le Composant Finder 0
$finder->sort($sort); Nom de Fichier Restreignez les fichiers par leur nom grâce à la méthode name() : Listing 0- $finder->files()->name('*.php'); La méthode name() accepte des «globs», des chaînes de caractères, ou des expressions régulières: Listing 0- $finder->files()->name('/\.php$/'); La méthode notname() exclut les fichiers correspondant à un pattern: Listing 0- $finder->files()->notname('*.rb'); Contenus de fichier New in version.: Les méthodes contains() et notcontains() ont été introduites dans la version.. Restreignez les fichiers par leur contenu grâce à la méthode contains() : Listing 0- $finder->files()->contains('lorem ipsum'); La méthode contains() accepte des chaînes de caractères ou des expressions régulières: Listing 0- $finder->files()->contains('/lorem\s+ipsum$/i'); La méthode notcontains() exclut les fichiers correspondant à un pattern donné: Listing 0- $finder->files()->notcontains('dolor sit amet'); Path New in version.: Les méthodes path() et notpath() ont été ajoutées dans la version.. Vous pouvez restreindre les fichiers et les répertoire en fonction de leur chemin avec la méthode path() : Listing 0- $finder->path('some/special/dir'); Le slash (c-a-d /) devrait être utilisé comme séparateur de dossiers sur toutes les plateformes. La méthode path()` accepte une chaine de caractères ou une expression régulière: Listing 0- $finder->path('foo/bar'); $finder->path('/^foo\/bar/');. http://api.symfony.com/./symfony/component/finder/finder.html#name(). http://api.symfony.com/./symfony/component/finder/finder.html#contains(). http://api.symfony.com/./symfony/component/finder/finder.html#path() generated on September, 0 Chapter 0: Le Composant Finder
En interne, les chaines de caractères sont converties en expressions régulières en échappant les slashes et en ajoutant des délimiteurs : Listing 0-0 dirname ===> /dirname/ a/b/c ===> /a\/b\/c/ La méthode notpath() exclut des fichiers en fonction de leur chemin: Listing 0- $finder->notpath('other/dir'); Taille de Fichier Restreignez les fichiers par leur taille grâce à la méthode size() : Listing 0- $finder->files()->size('<.k'); Restreignez par un intervalle de taille en chaînant les appels: Listing 0- $finder->files()->size('>= K')->size('<= K'); L'opérateur de comparaison peut être l'un des suivants : >, >=, <, <=, ==,!=. New in version.: L'opérateur!= a été ajouté dans la version.. La valeur cible peut utiliser les unités suivantes : kilo-octets (k, ki), mega-octets (m, mi), ou giga-octets (g, gi). Celles suffixées avec un i utilisent la version appropriée **n en accord avec le standard IEC 0. Date de fichier Restreignez les fichiers par leur date de dernière modification grâce à la méthode date() : Listing 0- $finder->date('since yesterday'); L'opérateur de comparaison peut être l'un des suivants : >, >=, <, '<=', '=='. Vous pouvez aussi utiliser since («depuis» en français) ou after («après» en français) en tant qu'alias de >, et until («jusqu'à» en français) ou before («avant» en français) en tant qu'alias de <. La valeur cible peut être n'importe quelle date supportée par la fonction strtotime. Profondeur de répertoire Par défaut, le «Finder» parcourt les répertoires récursivement. Restreignez la profondeur de navigation grâce à la méthode depth() : Listing 0- $finder->depth('== 0'); $finder->depth('< ');. http://api.symfony.com/./symfony/component/finder/finder.html#notpath(). http://api.symfony.com/./symfony/component/finder/finder.html#size() 0. http://physics.nist.gov/cuu/units/binary.html. http://api.symfony.com/./symfony/component/finder/finder.html#date(). http://www.php.net/manual/fr/datetime.formats.php. http://api.symfony.com/./symfony/component/finder/finder.html#depth() generated on September, 0 Chapter 0: Le Composant Finder
Filtrage personnalisé Pour restreindre les fichiers correspondants à votre propre stratégie, utilisez la méthode filter() : Listing 0- $filter = function (\SplFileInfo $file) if (strlen($file) > 0) return false; ; $finder->files()->filter($filter); La méthode filter() prend une Closure en argument. Pour chaque fichier qui correspond, cette dernière est appelée avec le fichier en tant qu'instance de SplFileInfo. Le fichier est exclu de l'ensemble des résultats si la Closure retourne false. Lire le contenu des fichiers retournés New in version.: La méthode getcontents() a été ajoutée dans la version.. Les contenus des fichiers retournés peuvent être lus avec getcontents() : Listing 0- use Symfony\Component\Finder\Finder; $finder = new Finder(); $finder->files()->in( DIR ); foreach ($finder as $file) $contents = $file->getcontents();.... http://api.symfony.com/./symfony/component/finder/finder.html#filter(). http://api.symfony.com/./symfony/component/finder/splfileinfo.html. http://api.symfony.com/./symfony/component/finder/splfileinfo.html#getcontents() generated on September, 0 Chapter 0: Le Composant Finder
Chapter Le composant Formulaire Le composant Form vous permet de créer facilement, traiter et réutiliser des formulaires HTML. Le composant Form est un outil pour vous aider à résoudre le problème de permettre aux utilisateurs finaux d'intéragir et modifier des données dans votre application. Et c'est via les fomulaires HTML que le composant se concentre pour traiter des données soumises par l'utilisateur pour et depuis votre application, que la donnée provienne d'un formulaire posté normalement ou d'une API. Installation Vous pouvez installer le composant de deux manières différentes : L'installation via Composer (symfony/form on Packagist ); Utilisez le repository Git officiel (https://github.com/symfony/form ). Configuration Si vous travaillez avec le framework Symfony full stack, le composant Form est déjà configuré pour vous. Dans ce cas, passez directement à la section Création d'un formulaire simple. Dans Symfony, les formulaires sont représentés par des objets et ceux-ci sont construits en utilisant une form factory (usine à formulaire). Il est simple de construire une form factory: Listing -. https://packagist.org/packages/symfony/form. https://github.com/symfony/form generated on September, 0 Chapter : Le composant Formulaire
use Symfony\Component\Form\Forms; $formfactory = Forms::createFormFactory(); Cette factory peut déjà être utilisée pour créer des formulaires basiques, mais il lui manque des fonctionnalités très importantes : Manipulation de la requête : Support de la manipulation de l'objet Request et de téléchargement de fichiers; Protection CSRF : Support de la protection contre les attaques Cross-Site-Request-Forgery (CSRF); Templating : Intégration avec une couche templating vous permettant de réutiliser des fragments HTML lors de l'affichage du formulaire; Traduction : Support des traductions des messages d'erreur, champs labelisés et autre chaînes de caractères; Validation : Intégration avec la bibliothèque de validation pour générer les messages d'erreurs pour les données soumises. Le composant Form de Symfony est basé sur d'autres bibliothèques pour résoudre ces problèmes. La majorité du temps vous utiliserez Twig ainsi que les composants HttpFoundation, Traduction et Validation. Ceci dit, vous pouvez remplacer chacune des ces bibliothèques par celle(s) de votre choix. Les sections suivantes vous expliquent comment brancher ces bibliothèques avec la form factory. Pour un exemple fonctionnel, consultez https://github.com/bschussek/standalone-forms Manipulation de la Requête New in version.: La méthode handlerequest() a été ajoutée dans la version. de Symfony. Pour traiter les données d'un formulaire, vous aurez besoin de la méthode handlerequest() : Listing - $form->handlerequest(); En coulisses, cette méthode utilise un objet de type NativeRequestHandler pour lire les données provenants des variables super-globales PHP (i.e. $_POST``ou ``$_GET) basées sur la méthode HTTP configurée sur le formulaire (POST par défaut).. https://github.com/bschussek/standalone-forms. http://api.symfony.com/./symfony/component/form/form.html#method_handlerequest. http://api.symfony.com/./symfony/component/form/nativerequesthandler.html generated on September, 0 Chapter : Le composant Formulaire
Intégration avec le composant HttpFoundation Si vous utilisez le composant HttpFoundation, vous devriez alors ajouter la HttpFoundationExtension à votre form factory: Listing - use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; $formfactory = Forms::createFormFactoryBuilder() ->addextension(new HttpFoundationExtension()) ->getformfactory(); Maintenant, lorsque vous traitez un formulaire, vous pouvez passer l'objet Request à la méthode handlerequest() : Listing - $form->handlerequest($request); Pour plus d'informations concernant le composant HttpFoundation ou comment l'installer, consultez Le composant HttpFoundation. Protection CRSF La protection contre les attaques CSRF est incluse dans le composant formulaire, mais vous devez explicitement l'activer ou la remplacer par une solution personnalisée: Listing - 0 use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\Csrf\CsrfExtension; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; use Symfony\Component\HttpFoundation\Session\Session; // générer le secret CSRF depuis quelque part $csrfsecret = '<generated token>'; // créer un objet session depuis le composant HttpFoundation $session = new Session(); $csrfprovider = new SessionCsrfProvider($session, $csrfsecret); $formfactory = Forms::createFormFactoryBuilder() //... ->addextension(new CsrfExtension($csrfProvider)) ->getformfactory(); Pour sécuriser votre application contre les attaques CSRF, vous devez définir un sercret CSRF. Générer une chaîne de caractères aléatoires avec au moins caractères, insérez-les dans le morceaux de code cidessus et assurez vous que personne d'autre que votre server web ne peut accéder à ce secret.. http://api.symfony.com/./symfony/component/form/extension/httpfoundation/httpfoundationextension.html. http://api.symfony.com/./symfony/component/httpfoundation/request.html. http://api.symfony.com/./symfony/component/form/form.html#method_handlerequest generated on September, 0 Chapter : Le composant Formulaire
En interne, cette extension ajoutera automatiquement un champs caché à chacun des formulaires (appelé token par défaut) dont la valeur est automatiquement générée et validée lors du binding (liaison) du formulaire. Si vous n'utilisez pas le composant HttpFoundation, vous pouvez utiliser DefaultCsrfProvider à la place, qui se repose sur la manipulation de session native de PHP: Listing - use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; $csrfprovider = new DefaultCsrfProvider($csrfSecret); Le Templating avec Twig Si vous utilisez le composant formulaire pour traiter des formulaires HTML, vous aurez besoin d'une solution pour afficher facilement les champs de votre formulaire HTML (compléter avec les valeurs des champs, les erreurs et les libellés). Si vous utilisez Twig 0 comme moteur de rendu, le composant Form offre une intégration riche. Pour utiliser cette intégration, vous aurez besoin de TwigBridge, fournissant une intégration entre Twig et quelques composants Symfony. Si vous utilisez Composer, vous pouvez installer la dernière version. en ajoutant la ligne require suivante dans votre fichier composer.json: Listing - "require": "symfony/twig-bridge": "..*" L'intégration de TwigBridge vous fournit un certain nombre de fonctions Twig vous aidant à présenter chacun des widgets HTML, libellés et erreurs pour chaque champs (ainsi quelques autres petites choses). Pour configurer cette intégration, vous aurez besoin de bootstrapper ou accéder à Twig et ajouter la classe FormExtension : Listing - 0 use Symfony\Component\Form\Forms; use Symfony\Bridge\Twig\Extension\FormExtension; use Symfony\Bridge\Twig\Form\TwigRenderer; use Symfony\Bridge\Twig\Form\TwigRendererEngine; // le fichier Twig contenant toutes les balises pour afficher les formulaires // ce fichier vient avoir le TwigBridge $defaultformtheme = 'form_div_layout.html.twig'; $vendordir = realpath( DIR. '/../vendor'); // le chemin vers TwigBridge pour que Twig puisse localiser // le fichier form_div_layout.html.twig $vendortwigbridgedir = $vendordir. '/symfony/twig-bridge/symfony/bridge/twig'; // le chemin vers les autres templates $viewsdir = realpath( DIR. '/../views'); $twig = new Twig_Environment(new Twig_Loader_Filesystem(array(. http://api.symfony.com/./symfony/component/form/extension/csrf/csrfprovider/defaultcsrfprovider.html 0. http://twig.sensiolabs.org. http://api.symfony.com/./symfony/bridge/twig/extension/formextension.html generated on September, 0 Chapter : Le composant Formulaire
0 0 $viewsdir, $vendortwigbridgedir. '/Resources/views/Form', ))); $formengine = new TwigRendererEngine(array($defaultFormTheme)); $formengine->setenvironment($twig); // ajoutez à Twig la FormExtension $twig->addextension( new FormExtension(new TwigRenderer($formEngine, $csrfprovider)) ); // créez votre form factory comme d'habitude $formfactory = Forms::createFormFactoryBuilder() //... ->getformfactory(); Votre Configuration Twig peut varier, mais le but est toujours d'ajouter l'extension FormExtension à Twig, ce qui vous donne accès au fonctions twig pour afficher les formulaires. Pour faire cela, il vous faut premièrement créer un TwigRendererEngine, où vous définissez vos form themes (i.e. resources/fichiers définissant votre balisage de formulaire HTML). Pour plus de détails concernant l'affichage de formulaires, consultez Comment personnaliser le rendu de formulaire. Si vous utilisez une intégration avec Twig, lisez "Traduction" ci-dessous pour les détails nécessaires aux filtres de traduction. Traduction Si vous utilisez une intégration avec Twig avec l'un des fichiers de thème de formulaire par défaut (par exemple form_div_layout.html.twig), il y a deux filtres Twig (trans``et ``transchoice) qu'il faut utiliser pour la traduction des libellés, erreurs, texte en option et autres chaînes de caractères d'un formulaire. Pour ajouter ces filtres Twig, vous pouvez soit utiliser ceux fournis par défaut dans la classe TranslationExtension qui est intégrée avec le composant Traduction de Symfony, ou ajouter ces deux filtres vous-même, via une extension Twig. Pour utiliser l'intégration fournie par défaut, assurez-vous que votre projet dispose des composants de Symfony Traduction et doc:config </components/config/introduction> installés. Si vous utilisez Composer, vous pouvez récupérer la dernière version. de ces composants en ajoutant les lignes suivantes à votre fichier composer.json : Listing - "require": "symfony/translation": "..*", "symfony/config": "..*" Ensuite, ajoutez la classe TranslationExtension à votre instance de Twig_Environment:. http://twig.sensiolabs.org/doc/intro.html. http://api.symfony.com/./symfony/bridge/twig/extension/formextension.html. http://api.symfony.com/./symfony/bridge/twig/form/twigrendererengine.html. http://api.symfony.com/./symfony/bridge/twig/extension/translationextension.html. http://api.symfony.com/./symfony/bridge/twig/extension/translationextension.html generated on September, 0 Chapter : Le composant Formulaire
Listing -0 0 0 use Symfony\Component\Form\Forms; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Loader\XliffFileLoader; use Symfony\Bridge\Twig\Extension\TranslationExtension; // instancier un objet de la classe Translator $translator = new Translator('en'); // charger, en quelque sorte, des traductions dans ce translator $translator->addloader('xlf', new XliffFileLoader()); $translator->addresource( 'xlf', DIR.'/path/to/translations/messages.en.xlf', 'en' ); // ajoutez le TranslationExtension (nous donnant les filtres trans et transchoice) $twig->addextension(new TranslationExtension($translator)); $formfactory = Forms::createFormFactoryBuilder() //... ->getformfactory(); En fonction de la manière dont vos traductions sont chargées, vous pouvez maintenant ajouter des clés, comme des libellés de champs, et leur traductions dans vos fichiers de traductions. Pour plus de détails sur les traductions, consulter Traductions. Validation Le composant Form vient avec une petite (mais optionnelle) intégration du composant Validation de Symfony. Si vous utilisez une solution différente pour la validation, pas de problème! Prenez simplement les données soumises/liées de votre formulaire (qui sont contenues dans un tableau ou un objet) et passez les à votre propre système de validation. Pour utiliser l'intégration avec le composant Validation, premièrement assurez-vous qu'il est installé dans votre application. Si vous utilisez Composer et que vous souhaitez installer la dernière version., ajoutez ceci à votre composer.json : Listing - "require": "symfony/validator": "..*" Si vous n'êtes pas familiez avec le composant Validation de Symfony, lisez-en plus à son propos : Validation. Le composant Form vient avec la classe ValidatorExtension, qui applique automatiquement la validation lorsque vos données sont liées. Ces erreurs sont ensuite mappées au bon champs et affichées. Votre intégration avec le composant Validation ressemblera à ceci: Listing - use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\Validator\ValidatorExtension; use Symfony\Component\Validator\Validation;. http://api.symfony.com/./symfony/component/form/extension/validator/validatorextension.html generated on September, 0 Chapter : Le composant Formulaire
0 0 0 $vendordir = realpath( DIR. '/../vendor'); $vendorformdir = $vendordir. '/symfony/form/symfony/component/form'; $vendorvalidatordir = $vendordir. '/symfony/validator/symfony/component/validator'; // créez le validator - les détails varieront $validator = Validation::createValidator(); // il y a des traductions fournies pour les messages d'erreurs du coeur du composant $translator->addresource( 'xlf', $vendorformdir. '/Resources/translations/validators.en.xlf', 'en', 'validators' ); $translator->addresource( 'xlf', $vendorvalidatordir. '/Resources/translations/validators.en.xlf', 'en', 'validators' ); $formfactory = Forms::createFormFactoryBuilder() //... ->addextension(new ValidatorExtension($validator)) ->getformfactory(); Pour en apprendre plus, allez directement à la session Validation de formulaire. Accéder à la Form Factory Votre application n'a besoin que d'une form factory (usine de formulaire), et cette unique factory d'objet devrait être utilisée pour créer tous les objets Form dans votre application. Cela signifie que vous devriez la créer à un endroit central, au moment où votre application est bootstrappée puis y accéder lorsque vous souhaitez construire un formulaire. Dans ce document, la form factory est toujours dans une variable locale appelée $formfactory. Le but ici est que vous aurez probablement besoin de créer cet objet de façon plus "globale" de manière à ce que vous puissiez y accéder depuis n'importe où. C'est à vous de déterminer la manière dont vous accéderez à cette form factory. Si utilisez un Conteneur de services, vous devriez dont ajouter cette form factory à votre conteneur et le récupérer lorsque vous en aurez besoin. Si votre application utilise des variables globales ou statiques (pas une bonne idée en général), vous pouvez alors garder l'objet dans une classe statique ou une solution similaire. Sans prêter attention à comment vous avez conçu votre application, souvenez-vous simplement que vous devriez n'avoir qu'une seule form factory et que vous aurez besoin d'y accéder partout dans votre application. Création d'un formulaire simple generated on September, 0 Chapter : Le composant Formulaire 0
Si vous utilisez le framework Symfony, alors la form factory est disponible automatiquement comme service et est appelé form.factory. Aussi, la classe de contrôleur de base par défaut possède la méthode createformbuilder(), qui est un raccourci pour récupérer la form factory et appelle createbuilder dessus. La création d'un formulaire est faite via un objet FormBuilder, où vous construisez et configurez les champs du formulaire. Le form builder est créé depuis la form factory. Listing - $form = $formfactory->createbuilder() ->add('task', 'text') ->add('duedate', 'date') ->getform(); echo $twig->render('new.html.twig', array( 'form' => $form->createview(), )); Comme vous pouvez le voir, créer un formulaire est comme écrire une recette : vous appelez la méthode add pour chacun des champs que vous souhaitez créer. Le premier argument de la méthode add est le nom de votre champ, et le second est le "type" du champ. Le composant Form est fourni avec beaucoup de types par défaut. Maintenant que vous avez construit votre formulaire, apprenez comment l'afficher et effectuer le traitement lors de la soumission de formulaire. Réglage des valeurs par défaut Si vous avez besoin que votre formulaire soit chargé avec quelques valeurs par défaut (ou que vous construisez un formulaire d'édition), passez simplement les valeurs par défaut lorsque vous créez votre form builder : Listing - $defaults = array( 'duedate' => new \DateTime('tomorrow'), ); $form = $formfactory->createbuilder('form', $defaults) ->add('task', 'text') ->add('duedate', 'date') ->getform(); Dans cet exemple, les données par défaut sont dans un tableau. Plus tard, lorsque vous utiliserez l'option data_class pour lier les données directement à des objets, vos données par défaut seront une instance de cet objet. L'affichage du formulaire Maintenant que le formulaire a été créé, l'étape suivante est de l'afficher. C'est fait en passant l'objet spécial form "view" à votre template (notez le $form->createview() dans le contrôleur ci-dessus) et en utilisant une suite de fonctions helper du formulaire : Listing -. http://api.symfony.com/./symfony/bundle/frameworkbundle/controller.html#method_createformbuilder. http://api.symfony.com/./symfony/component/form/formbuilder.html generated on September, 0 Chapter : Le composant Formulaire
<form action="#" method="post" form_enctype(form) > form_widget(form) <input type="submit" /> </form> Et voilà! En écrivant form_widget(form), chaque champ du formulaire est affiché avec son label et le message d'erreur (s'il y en a un). C'est très facile, mais (pas encore) très flexible. Généralement, vous voudrez afficher chaque champs de votre formulaire individuellement ainsi vous pourrez contrôler le look de votre formulaire. Vous apprendrez comment le faire dans la session "Afficher un Formulaire dans un Template". Traitement lors de la soumission de formulaire Pour effectuer un traitement lors de la soumission de formulaire, utilisez la méthode handlerequest() 0 : Listing - 0 0 use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; $form = $formfactory->createbuilder() ->add('task', 'text') ->add('duedate', 'date') ->getform(); $request = Request::createFromGlobals(); $form->handlerequest($request); if ($form->isvalid()) $data = $form->getdata(); //... //... exécutez une action, comme enregistrer des // données dans une base de données $response = new RedirectResponse('/task/success'); $response->prepare($request); return $response->send(); Ceci définit le "workflow" commun, contenant possibilités différentes :. Sur la requête en GET initiale (i.e. lorsque l'utilisateur "surfe" sur votre page), il y a construction du formulaire et affichage de celui-ci; 0. http://api.symfony.com/./symfony/component/form/form.html#method_handlerequest generated on September, 0 Chapter : Le composant Formulaire
Si la requête est en POST, traitez les données soumises (via handlerequest()). Puis : ) si le formulaire est invalide, affichez à nouveau le formulaire (qui contiendra maintenant les erreurs); ) si le formulaire est valide, effectuez les actions nécessaires et redirigez l'utilisateur. Heureusement, vous n'avez pas besoin de décider si oui ou non un formulaire a été soumis. Passez simplement la requête courante à la méthode handlerequest(). Puis, le composant formulaire fera tout ce qui est nécessaire à votre place. Validation de formulaire La façon la plus simple pour ajouter de la validation à votre formulaire est de le faire via l'option constraints lors de la construction de chaque champ : Listing - 0 use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Type; $form = $formfactory->createbuilder() ->add('task', 'text', array( 'constraints' => new NotBlank(), )) ->add('duedate', 'date', array( 'constraints' => array( new NotBlank(), new Type('\DateTime'), ) )) ->getform(); Lorsque le formulaire est bindé (lié), ces contraintes de validation seront automatiquement appliquées et les erreurs seront affichées près du champ concerné par l'erreur. Pour une liste de toutes les contraintes de validation, consultez Contraintes de validation de référence. generated on September, 0 Chapter : Le composant Formulaire
Chapter Créer un Type Guesser personnalisé Le composant Form peut deviner le type et quelques options d'un champ de formulaire en utilisant un type guesser (déduction automatique du type). Le composant inclut déjà un type guesser qui utilise les assertions du composant Validation, mais vous pouvez également ajouter vos propres type guessers. Les Type Guessers dans les Bridges Symfony fournit également quelques type guessers de formulaire dans les bridges : PropelTypeGuesser fourni par le bridge Propel; DoctrineOrmTypeGuesser fourni par le bridge Doctrine. Créer une PHPDoc pour le Type Guesser Dans cette section, vous allez construire un guesser (devin) qui va pouvoir lire les informations concernant les champs d'un formulaire depuis la PHPDoc des propriétés. Premièrement, vous aurez besoin de créer une classe qui implémente la classe FormTypeGuesserInterface. Cette interface requiert méthodes : guesstype() - essaie de deviner le type d'un champ; guessrequired() - essaie de deviner la valeur de l'option requise; guessmaxlength() - essaie de deviner la valeur de l'option max_length; guesspattern() - esssaie de deviner la valeur de l'option pattern. Commencez par créer la classe et ses méthodes. Ensuite, vous apprendrez comment les implémenter.. http://api.symfony.com/./symfony/bridge/propel/form/propeltypeguesser.html. http://api.symfony.com/./symfony/bridge/doctrine/form/doctrineormtypeguesser.html. http://api.symfony.com/./symfony/component/form/formtypeguesserinterface.html. http://api.symfony.com/./symfony/component/form/formtypeguesserinterface.html#guesstype(). http://api.symfony.com/./symfony/component/form/formtypeguesserinterface.html#guessrequired(). http://api.symfony.com/./symfony/component/form/formtypeguesserinterface.html#guessmaxlength(). http://api.symfony.com/./symfony/component/form/formtypeguesserinterface.html#guesspattern() generated on September, 0 Chapter : Créer un Type Guesser personnalisé
Listing - 0 0 namespace Acme\Form; use Symfony\Component\Form\FormTypeGuesserInterface; class PhpdocTypeGuesser implements FormTypeGuesserInterface public function guesstype($class, $property) public function guessrequired($class, $property) public function guessmaxlength($class, $property) public function guesspattern($class, $property) Deviner le Type Lorsque vous devinez un type, la méthode retourne soit une instance de TypeGuess ou rien, pour déterminer que le type guesser ne peut pas deviner le type. Le constructeur TypeGuess requiert options : Le nom du type (l'un des types de formulaire); Des options additionnelles (par exemple, lorsque le type est entity, vous voudrez également fixer l'option class). Si aucun type n'est deviné, cela doit être fixé avec un tableau vide; Le niveau de confiance (ou probabilité) indiquant que le type deviné est correct. Cela peut être l'une des constantes de la classe Guess : LOW_CONFIDENCE, MEDIUM_CONFIDENCE, HIGH_CONFIDENCE, VERY_HIGH_CONFIDENCE. Après que tous les types guessers ont été exécutés, le type avec la confiance la plus haute est executé. Avec ce que vous venez d'apprendre, vous pouvez facilement implémenter la méthode guesstype de PHPDocTypeGuesser Listing - 0 namespace Acme\Form; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\TypeGuess; class PhpdocTypeGuesser implements FormTypeGuesserInterface public function guesstype($class, $property) $annotations = $this->readphpdocannotations($class, $property); if (!isset($annotations['var'])) return; // devine que rien n'est disponible dans l'annotation @var. http://api.symfony.com/./symfony/component/form/guess/typeguess.html. http://api.symfony.com/./symfony/component/form/guess/guess.html generated on September, 0 Chapter : Créer un Type Guesser personnalisé
0 0 0 0 // le cas échéant, basez le type sur l'annotation @var switch ($annotations['var']) case 'string': // il y a une forte probabilité que le type soit une string // lorsque @var string est utilisé return new TypeGuess('text', array(), Guess::HIGH_CONFIDENCE); case 'int': case 'integer': // les entiers peuvent également être l'id d'une entité ou une // checkbox (0 ou ) return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); case 'float': case 'double': case 'real': return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); case 'boolean': case 'bool': return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE); default: // il y a une très petite probabilité que celui-ci soit correct return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); protected function readphpdocannotations($class, $property) $reflectionproperty = new \ReflectionProperty($class, $property); $phpdoc = $reflectionproperty->getdoccomment(); // parsez le $phpdoc en tableau comme : // array('type' => 'string', 'since' => '.0') $phpdoctags =...; return $phpdoctags; Ce type guesser peut maintenant deviner le type du champ d'une propriété si elle possède une PHPDoc! Deviner les options d'un champs Les trois autres méthodes (guessmaxlength, guessrequired et guesspattern) retournent une instance de ValueGuess 0 avec la valeur de l'option. Ce constructeur possède arguments : La valeur de l'option; Le niveau de probabilité que la valeur devinée soit correcte (utilisant les constantes de la class Guess). null est deviné lorsque vous croyez que la valeur d'une option pourrait ne pas être fixée. 0. http://api.symfony.com/./symfony/component/form/guess/valueguess.html generated on September, 0 Chapter : Créer un Type Guesser personnalisé
Vous devez être très vigilant en utilisant la méthode guesspattern. Lorsque le type est un float, vous ne pouvez pas l'utiliser pour déterminer une valeur minimum ou maximum de ce float (par exemple vous voulez qu'un float soit plus grand que,. n'est pas valide alors que length(.) > length() l'est, donc le motif reussira). Dans ce cas, la valeur devrait être fixée à null avec une confiance à MEDIUM_CONFIDENCE. Enregistrer un Type Guesser La dernière chose qu'il vous reste à faire est d'enregistrer votre type guesser personnalisé en utilisant la méthode addtypeguesser() ou la méthode addtypeguessers() : Listing - use Symfony\Component\Form\Forms; use Acme\Form\PHPDocTypeGuesser; $formfactory = Forms::createFormFactoryBuilder() //... ->addtypeguesser(new PHPDocTypeGuesser()) ->getformfactory(); //... Lorsque vous utilisez le framework Symfony, vous devez enregistrer votre type guesser en tant que service et le taguer avec form.type_guesser. Pour plus d'informations, consultez les tags supportés par le Conteneur d'injection de Dépendances.. http://api.symfony.com/./symfony/component/form/formfactorybuilder.html#addtypeguesser(). http://api.symfony.com/./symfony/component/form/formfactorybuilder.html#addtypeguessers() generated on September, 0 Chapter : Créer un Type Guesser personnalisé
Chapter Le composant HttpFoundation Le composant HttpFoundation définit une couche orientée objet pour la spécification HTTP. Avec PHP, la requête est représentée par certaines variables globales ($_GET, $_POST, $_FILE, $_COOKIE, $_SESSION...) et la réponse est générée à l'aide de fonctions (echo, header, setcookie,...). Le composant Symfony HttpFoundation remplace ces variables par défauts et ces fonctions par un ensemble Orienté Objet. Installation Vous pouvez installer le composant de différentes façon : En utilisant le dépôt officiel Git (https://github.com/symfony/httpfoundation ) ; En l'installant à l'aide de Composer (symfony/http-foundation sur Packagist ). Requête La manière la plus commune pour créer une requête est de l'initialiser à l'aide des variables globales en «cours de validité» avec createfromglobals() Listing - use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); qui est l'équivalent de la forme plus verbeuse mais aussi plus flexible de l'appel construct(). https://github.com/symfony/httpfoundation. https://packagist.org/packages/symfony/http-foundation. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_createfromglobals. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method construct generated on September, 0 Chapter : Le composant HttpFoundation
Listing - $request = new Request($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); Accéder aux données provenant de la requête Un objet requête contient les informations concernant le client. Ces informations peuvent être consultées via plusieurs propriétés publiques : request: équivalent à $_POST ; query: équivalent à $_GET ($request->query->get('name')) ; cookies: équivalent à $_COOKIE ; attributes: n'a pas d'équivalent - c'est une propriété utilisée par votre application pour conserver certaines données (voir ci-dessous) ; files: équivalent à $_FILE ; server: équivalent à $_SERVER ; headers: globalement équivalent à un sous-ensemble du tableau $_SERVER ($request- >headers->get('content-type')). Chaque propriété est une instance (ou une sous-classe) de ParameterBag, qui est la classe de conservation des données : request: ParameterBag ; query: ParameterBag ; cookies: ParameterBag ; attributes: ParameterBag ; files: FileBag 0 ; server: ServerBag ; headers: HeaderBag. Toutes les instances de ParameterBag ont des méthodes pour consulter et mettre à jour les données incluses : all() : Retourne les paramètres ; keys() : Retourne les clés des paramètres ; replace() : Remplace les paramètres courants par un nouvel ensemble ; add() : Ajoute des paramètres ; get() : Retourne un paramètre par son nom ; set() : Attribue la valeur d'un paramètre nommé ; has() 0 : Retourne vrai si le paramètre est défini ; remove() : Supprime un paramètre.. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html 0. http://api.symfony.com/./symfony/component/httpfoundation/filebag.html. http://api.symfony.com/./symfony/component/httpfoundation/serverbag.html. http://api.symfony.com/./symfony/component/httpfoundation/headerbag.html. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_all. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_keys. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_replace. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_add. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_get. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_set 0. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_has. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html#method_remove generated on September, 0 Chapter : Le composant HttpFoundation
Une instance de la ParameterBag contient aussi certaines méthodes de filtrage : getalpha() : Retourne les caractères alphabétiques de la valeur d'un paramètre ; getalnum() : Retourne les caractères alphanumériques de la valeur d'un paramètre ; getdigits() : Retourne les chiffres de la valeur d'un paramètre ; getint() : Retourne la valeur d'un paramètre convertie en entier ; filter() : Filtre la valeur d'un paramètre en utilisant la fonction PHP filter_var(). Tous les accesseurs («getters» en anglais) prennent jusqu'à trois arguments : le premier est le nom du paramètre, le second la valeur par défaut si le paramètre n'existe pas: Listing - 0 // la chaîne de requête est '?foo=bar' $request->query->get('foo'); // retourne bar $request->query->get('bar'); // retourne null $request->query->get('bar', 'bar'); // retourne 'bar' Quand PHP importe la requête, il utilise des paramètres comme foo[bar]=bar d'une manière spéciale en créant un tableau. Vous pouvez ainsi utiliser le paramètre foo pour accéder au tableau contenant l'élément bar. Mais parfois, vous ne voulez que la valeur du paramètre avec son nom original: foo[bar]. C'est possible à l'aide des accesseurs du ParameterBag à l'aide de la méthode get() en utilisant un troisième argument: Listing - 0 // la chaîne de requête est '?foo[bar]=bar' $request->query->get('foo'); // retourne array('bar' => 'bar') $request->query->get('foo[bar]'); // retourne null $request->query->get('foo[bar]', null, true); // retourne 'bar' Dernier point, et non des moindres, vous pouvez aussi stocker des données additionnelles à l'intérieur de la requête, grâce à la propriété publique attribut, qui est elle-même une instance de la classe ParameterBag. Ceci est le plus souvent utilisé pour attacher des informations qui seront utiles tout au long de la Requête et dont l'accès sera disponible à différents endroits de votre application. Pour savoir comment utiliser cette propriété à l'intérieur du framework, voyez en lire plus. Identifier une Requête Si, dans votre application, vous devez identifier une requête, le plus couramment, cela peut être effectué via les informations «path info» de votre requête, disponibles à l'aide de la méthode getpathinfo() 0. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getalpha. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getalnum. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getdigits. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getint. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_filter. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_get. http://api.symfony.com/./symfony/component/httpfoundation/parameterbag.html generated on September, 0 Chapter : Le composant HttpFoundation 0
Listing - // Pour une requête http://example.com/blog/index.php/post/hello-world // le path info est «/post/hello-world» $request->getpathinfo(); Simuler une Requête A la place de créer une requête basée sur les variables globales PHP, vous pouvez simuler une requête: Listing - $request = Request::create('/hello-world', 'GET', array('name' => 'Fabien')); La méthode create() crée une requête basée sur les informations de chemin, une méthode et certains paramètres (les paramètres d'interrogations ou de requête dépendent de la méthode HTTP utilisée) ; et bien entendu, vous pouvez aussi surcharger toutes ces variables (Symfony crée des variables par défaut pour toutes les variables PHP globales). A partir de cette requête, vous pouvez ensuite ré-écrire les variables globales PHP à l'aide de la méthode overrideglobals() : Listing - $request->overrideglobals(); Vous pouvez aussi dupliquer une requête existante via duplicate() paramètres grâce à un seul appel à la méthode initialize(). ou changer quelques Accéder à la Session Si vous avez une session attachée à la requête, vous pouvez y accéder à l'aide de la méthode getsession() ; La méthode hasprevioussession() vous informe sur l'existence d'une session démarrée dans une requête antérieure. Accèder aux données Headers Accept-* Vous pouvez facilement accéder aux données des Headers Accept-* en utilisant les méthodes suivantes: getacceptablecontenttypes() : renvoie la liste des types de contenu acceptés par ordre décroissant de qualité; getlanguages() : renvoie la liste des langues acceptées par ordre décroissant de qualité; getcharsets() : renvoie la liste des langues acceptées par ordre décroissant de qualité; New in version.: La classe AcceptHeader 0 est nouvelle en Symfony.. 0. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getpathinfo. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_create. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_overrideglobals. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_duplicate. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_initialize. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getsession. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_hasprevioussession. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getacceptablecontenttypes. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getlanguages. http://api.symfony.com/./symfony/component/httpfoundation/request.html#method_getcharsets 0. http://api.symfony.com/./symfony/component/httpfoundation/acceptheader.html generated on September, 0 Chapter : Le composant HttpFoundation
Si vous avez besoin d'avoir un accès complet aux données parsées de Accept, Accept-Language, Accept- Charset or Accept-Encoding, vous pouvez utilisé AcceptHeader utility class: Listing - 0 use Symfony\Component\HttpFoundation\AcceptHeader; $accept = AcceptHeader::fromString($request->headers->get('Accept')); if ($accept->has('text/html') $item = $accept->get('html'); $charset = $item->getattribute('charset', 'utf-'); $quality = $item->getquality(); // Les éléments accepts sont triés par ordre décroissant de qualité $accepts = AcceptHeader::fromString($request->headers->get('Accept'))->all(); Accéder à d'autres données La classe «Request» contient de nombreuses autres méthodes utilisables pour accéder aux informations la concernant. Jetez un oeil à l'api pour de plus amples informations à leur propos. Réponse L'objet Response contient toutes les informations qui seront utiles lors de l'envoi de la réponse au client pour une requête donnée. Le constructeur prend jusqu'à trois arguments : le contenu de la réponse, le code du statut, et un tableau conprenant les en-têtes HTTP («HTTP headers» en anglais): Listing - use Symfony\Component\HttpFoundation\Response; $response = new Response('Content', 00, array('content-type' => 'text/html')); Ces informations peuvent aussi être manipulées après la création de l'objet Response: Listing -0 $response->setcontent('hello World'); // L'attribut public «headers» est aussi un ResponseHeaderBag $response->headers->set('content-type', 'text/plain'); $response->setstatuscode(0); Quand vous annoncez le Content-Type de la réponse, vous pouvez attribuer le «charset» («jeu de caractères» en français), mais il est conseillé de l'indiquer via la méthode setcharset() Listing - $response->setcharset('iso--'); Notez que par défaut, Symfony présume que vos réponses sont encodées en UTF-. Envoyer la réponse Avant d'envoyer la réponse, vous devez vous assurer qu'elle est conforme avec les les spécifications HTTP en appelant la méthode prepare() :. http://api.symfony.com/./symfony/component/httpfoundation/acceptheader.html. http://api.symfony.com/./symfony/component/httpfoundation/response.html. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setcharset generated on September, 0 Chapter : Le composant HttpFoundation
Listing - $response->prepare($request); Envoyez la réponse n'est ensuite qu'un simple appel à la méthode send() : Listing - $response->send(); Définir les Cookies Les cookies utilisés dans la réponse peuvent être manipulés via l'attribut public headers: Listing - use Symfony\Component\HttpFoundation\Cookie; $response->headers->setcookie(new Cookie('foo', 'bar')); La méthode setcookie() prend une instance de la classe Cookie comme argument. Vous pouvez effacer un cookie à l'aide de la méthode clearcookie(). Gestion du cache HTTP La classe Response possède de nombreuses méthodes permettant de manipuler les en-têtes HTTP en relation avec le cache : setpublic() 0 ; setprivate() ; expire() ; setexpires() ; setmaxage() ; setsharedmaxage() ; setttl() ; setclientttl() ; setlastmodified() ; setetag() ; setvary() 0. La méthode setcache() peut être utilisée afin de définir les informations les plus triviales en un seul appel:. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_prepare. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_send. http://api.symfony.com/./symfony/component/httpfoundation/responseheaderbag.html#method_setcookie. http://api.symfony.com/./symfony/component/httpfoundation/cookie.html. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_clearcookie. http://api.symfony.com/./symfony/component/httpfoundation/response.html 0. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setpublic. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setprivate. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_expire. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setexpires. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setmaxage. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setsharedmaxage. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setttl. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setclientttl. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setlastmodified. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setetag 0. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setvary. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_setcache generated on September, 0 Chapter : Le composant HttpFoundation
Listing - $response->setcache(array( 'etag' => 'abcdef', 'last_modified' => new \DateTime(), 'max_age' => 00, 's_maxage' => 00, 'private' => false, 'public' => true, )); Afin de vérifier que les validateurs de la réponse (ETag, Last-Modified) correspondent aux valeurs conditionnelles indiquées dans la requête client, vous pouvez utiliser la méthode isnotmodified() : Listing - if ($response->isnotmodified($request)) $response->send(); Si la réponse n'est pas modifiée, le code de statut indiqué sera 0 et le contenu sera supprimé. Rediriger l'utilisateur Afin de rediriger le client vers une autre URL, vous pouvez utilisez la classe RedirectResponse : Listing - use Symfony\Component\HttpFoundation\RedirectResponse; $response = new RedirectResponse('http://example.com/'); Créer un flux de réponse La classe StreamedResponse permet de retourner un flux de réponse au client. Le contenu de la réponse est représenter par une fonction PHP au lieu d'une chaine de caractères: Listing - 0 use Symfony\Component\HttpFoundation\StreamedResponse; $response = new StreamedResponse(); $response->setcallback(function () echo 'Hello World'; flush(); sleep(); echo 'Hello World'; flush(); ); $response->send(); La fonction flush() ne vide par le tampon. Si ob_start() a été appelé avant ou si l'option output_buffering du php.ini est activée, vous devrez appeler ob_flush() avant flush(). De plus, PHP n'est pas la seule couche qui peut bufferiser la sortie. Votre serveur web peut également le faire selon sa configuration. Surtout, si vous utilisez fastcgi, le buffering ne peut pas être désactivé du tout.. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_isnotmodified. http://api.symfony.com/./symfony/component/httpfoundation/redirectresponse.html. http://api.symfony.com/./symfony/component/httpfoundation/streamedresponse.html generated on September, 0 Chapter : Le composant HttpFoundation
Retourner des fichiers Lorsque vous envoyez un fichier, vous devez ajouter l'entête Content-Disposition à votre réponse. Alors que créer cet entête pour un téléchargement de fichier basique est très simple, utiliser des noms de fichier non ASCII est plus complexe. La méthode makedisposition() permet de faire abstraction d'un dur labeur grâce à une simple API: Listing - use Symfony\Component\HttpFoundation\ResponseHeaderBag; $d = $response->headers->makedisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'foo.pdf' ); $response->headers->set('content-disposition', $d); New in version.: La classe BinaryFileResponse a été ajoutée dans Symfony.. Alternativement, si vous servez un fichier statique, vous pouvez utiliser une BinaryFileResponse : Listing -0 use Symfony\Component\HttpFoundation\BinaryFileResponse; $file = 'path/to/file.txt'; $response = new BinaryFileResponse($file); La BinaryFileResponse va automatiquement gérer les entêtes Range et If-Range de la requête. Elle supporte également X-Sendfile (voir pour Nginx et Apache ). Pour en faire usage, vous devez déterminer si oui ou non l'entête X-Sendfile-Type peut être accepté et appeler trustxsendfiletypeheader() 0 si c'est le cas: Listing - BinaryFileResponse::trustXSendfileTypeHeader(); Vous pouvez toujours définir le Content-Type du fichier envoyé, ou changer son entête Content- Disposition: Listing - $response->headers->set('content-type', 'text/plain'); $response->setcontentdisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'filename.txt' ); Créer une réponse JSON Tous les types de réponse peuvent être créés via la classe Response en définissant les bons contenu et entêtes. Une réponse JSON ressemblerait à ceci: Listing -. http://api.symfony.com/./symfony/component/httpfoundation/response.html#method_makedisposition. http://api.symfony.com/./symfony/component/httpfoundation/binaryfileresponse.html. http://api.symfony.com/./symfony/component/httpfoundation/binaryfileresponse.html. http://wiki.nginx.org/xsendfile. https://tn.org/mod_xsendfile/ 0. http://api.symfony.com/./symfony/component/httpfoundation/binaryfileresponse.html#method_trustxsendfiletypeheader. http://api.symfony.com/./symfony/component/httpfoundation/response.html generated on September, 0 Chapter : Le composant HttpFoundation
use Symfony\Component\HttpFoundation\Response; $response = new Response(); $response->setcontent(json_encode(array( 'data' =>, ))); $response->headers->set('content-type', 'application/json'); Il existe également une classe JsonResponse très utile qui rend sa création encore plus facile: Listing - use Symfony\Component\HttpFoundation\JsonResponse; $response = new JsonResponse(); $response->setdata(array( 'data' => )); Elle encode votre tableau de données en JSON et définit l'entête Content-Type à application/json. Pour éviter un détournement JSON par XSSI, vous devez passer un tableau associatif à la méthode JsonResponse et non pas un tableau indexé. Ainsi, le résultat final est un objet (ex "objet": "pas dans un tableau") au lieu d'un tableau (e.g. ["objet": "dans un tableau"]). Lisez les recommandations OWASP pour plus d'informations. Seules les méthodes qui répondent au requêtes de type GET sont vulnérables aux détournements JSON par XSSI. Les méthodes qui répondent aux requêtes POST ne sont pas affectées. Callback JSONP Si vous utilisez JSONP, vous pouvez définir une fonction de callback à laquelle les données doivent être passées: Listing - $response->setcallback('handleresponse'); Dans ce cas, l'entête Content-Type sera text/javascript et le contenu de la réponse ressemblera à ceci : Listing - handleresponse('data': ); Session Les informations concernant la session se trouvent dans leur propre document : Gestion de Session.. http://api.symfony.com/./symfony/component/httpfoundation/jsonresponse.html. http://haacked.com/archive/00/0//json-hijacking.aspx. https://www.owasp.org/index.php/owasp_ajax_security_guidelines#always_return_json_with_an_object_on_the_outside generated on September, 0 Chapter : Le composant HttpFoundation
Chapter Gestion de Session Le Composant HttpFoundation de Symfony possède un sous-système de session très flexible et puissant qui est implémenté de façon à fournir une gestion de session à travers une interface orientée-objet simple et utilisant une variété de «drivers» de stockage de session. New in version.: L'interface SessionInterface, de même que de nombreux autres changements, sont nouveaux depuis Symfony.. Les sessions sont utilisées via l'implémentation simple de Session de l'interface SessionInterface. Rapide exemple: Listing - 0 use Symfony\Component\HttpFoundation\Session\Session; $session = new Session(); $session->start(); // définit et récupère des attributs de session $session->set('name', 'Drak'); $session->get('name'); // définit des messages dits «flash» $session->getflashbag()->add('notice', 'Profile updated'); // récupère des messages foreach ($session->getflashbag()->get('notice', array()) as $message) echo "<div class='flash-notice'>$message</div>"; Les sessions de Symfony sont implémentées pour remplacer plusieurs fonctions PHP natives. Les applications devraient éviter d'utiliser session_start(), session_regenerate_id(), session_id(), session_name(), et session_destroy() et utiliser à la place les APIs de la section suivante.. http://api.symfony.com/./symfony/component/httpfoundation/session/sessioninterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html. http://api.symfony.com/./symfony/component/httpfoundation/session/sessioninterface.html generated on September, 0 Chapter : Gestion de Session
Bien qu'il soit recommandé de démarrer une session explicitement, une session va en fait être démarrée sur demande, ce qui veut dire, si toute requête d'une session est faite pour lire/écrire des données de session. Les sessions de Symfony sont incompatibles avec la directive PHP ini session.auto_start =. Cette directive devrait être désactivée dans le fichier php.ini, dans les directives du serveur web ou dans un fichier.htaccess. API de Session La classe Session implémente SessionInterface. La classe Session possède une API simple comme suit (divisée en différents groupes). Flux de fonctionnement d'une session start() : Démarre la session - ne pas utiliser session_start() ; migrate() : Regénère l'id de la session - ne pas utiliser session_regenerate_id(). Cette méthode peut optionnellement changer la durée de vie du nouveau cookie qui va être émis lors de l'appel de cette méthode ; invalidate() : Supprime toutes les données de la session et regénère l'id de la session - ne pas utiliser session_destroy(). getid() 0 : Récupère l'id de la session - ne pas utiliser session_id() ; setid() : Définit l'id de la session - ne pas utiliser session_id() ; getname() : Récupère le nom de la session - ne pas utiliser session_name() ; setname() : Définit le nom de la session - ne pas utiliser session_name() ; Attributs de session set() : Définit un attribut par clé ; get() : Récupère un attribut par clé ; all() : Récupère tous les attributs sous forme de tableau de paires clé => valeur ; has() : Retourne «true» si l'attribut existe ; keys() : Retourne un tableau contenant les clés des attributs ; replace() : Définit plusieurs attributs en une fois : prend un tableau de clés et définit chaque paire de clé => valeur ; remove() 0 : Supprime un attribut par clé ;. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html. http://api.symfony.com/./symfony/component/httpfoundation/session/sessioninterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_start. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_migrate. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_invalidate 0. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_getid. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_setid. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_getname. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_setname. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_set. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_get. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_all. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_has. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_keys. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_replace 0. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_remove generated on September, 0 Chapter : Gestion de Session
clear() : Supprime tous les attributs. Les attributs sont stockés en interne dans un «Bag» («sac» en français), un objet PHP qui agit comme un tableau. Quelques méthodes existent pour gérer ce «Bag» : registerbag() : Enregistre une SessionBagInterface ; getbag() : Récupère une SessionBagInterface par nom de «bag» ; getflashbag() : Récupère la FlashBagInterface. Ceci est juste un raccourci pour plus de commodité. Métadonnées de session getmetadatabag() : Récupère le MetadataBag qui contient des informations à propos de la session. Gestion de données de session La gestion de session de PHP requiert l'utilisation de la variable super-globale $_SESSION, cependant, cela interfère d'une certaine manière avec la testabilité du code et l'encapsulation dans un paradigme POO. Pour aider à résoudre ce soucis, Symfony utilise des «bags de session» («sacs de session» en français) liés à la session pour encapsuler un ensemble de données spécifique d'«attributs» ou de «messages flash». Cette approche diminue aussi la «pollution» de l'espace de noms dans la variable super-globale $_SESSION car chaque sac stocke toutes ses données sous un espace de noms unique. Cela permet à Symfony de co-exister en toute quiétude avec d'autres applications ou bibliothèques qui pourrait utiliser la varible super-globale $_SESSION et toutes ses données restent entièrement compatibles avec la gestion de session de Symfony. Symfony fournit deux sortes de sacs de stockage, avec deux implémentations séparées. Tout est écrit à l'aide d'interfaces donc vous pourriez étendre ou créer votre propre sac si nécessaire. SessionBagInterface 0 possède l'api suivante qui est destinée principalement à des fins internes : getstoragekey() : Retourne la clé sous laquelle le sac va stocker au final son tableau dans la variable super-globale $_SESSION ; initialize() : Cette méthode est appelée en interne par les classes de stockage de session de Symfony pour lier les données du sac à la session ; getname() : Retourne le nom du sac de session.. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_clear. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_registerbag. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionbaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_getbag. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionbaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_getflashbag. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_getmetadatabag. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/metadatabag.html 0. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionbaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionbaginterface.html#method_getstoragekey. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionbaginterface.html#method_initialize. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionbaginterface.html#method_getname generated on September, 0 Chapter : Gestion de Session
Attributs Le but des «sacs» implémentant l'interface AttributeBagInterface est de gérer le stockage des attributs de session. Cela peut inclure des choses comme l'id utilisateur, les paramètres concernant le login tel «Se souvenir de moi» ou d'autres informations à propos de l'état de l'utilisateur. AttributeBag Cela est l'implémentation standard par défaut ; NamespacedAttributeBag Cette implémentation permet aux attributs d'être stockés dans un espace de noms structuré. Tout système de stockage clé => valeur est limité quant aux données complexes qui peuvent être stockées puisque chaque clé doit être unique. Vous pouvez néanmoins utiliser les espaces de noms en introduisant une convention de nommage pour les clés afin que les différentes parties de votre application puissent fonctionner sans soucis. Par exemple, module.foo et module.foo. Cependant, parfois, cela n'est pas très pratique quand les données des attributs sont sous la forme d'un tableau, par exemple pour un ensemble de jetons. Dans ce cas, gérer le tableau devient un fardeau car vous devez récupérer le tableau puis le traiter et le stocker de nouveau: Listing - $tokens = array('tokens' => array('a' => 'ace0b', 'b' => 'fabf')); Du coup, n'importe quel traitement similaire à ce que vous avez ci-dessus pourrait vite devenir moche, même en ajoutant simplement un jeton au tableau: Listing - $tokens = $session->get('tokens'); $tokens['c'] = $value; $session->set('tokens', $tokens); Avec un espace de noms structuré, la clé peut être traduite en une structure en tableau comme ceci en utilisant un caractère d'espace de noms (par défaut /): Listing - $session->set('tokens/c', $value); De cette manière, vous pouvez facilement accéder à une clé du tableau stocké. AttributeBagInterface possède une API simple : set() : Définit un attribut par clé ; get() : Récupère un attribut par clé ; all() 0 : Récupère tous les attributs en tant que tableau «clé => valeur» ; has() : Retourne «true» si l'attribut existe ; keys() : Retourne un tableau des clés d'attributs stockées ; replace() : Définit plusieurs attributs en une fois : prend un tableau de clés et définit chaque paire de clé => valeur ; remove() : Supprime un attribut par sa clé ;. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebag.html. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/namespacedattributebag.html. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_set. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_get 0. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_all. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_has. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_keys. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_replace. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_remove generated on September, 0 Chapter : Gestion de Session 0
clear() : Supprime le sac. Messages flash Le but de l'interface FlashBagInterface est de fournir une manière de définir et récupérer des messages par session. Le flux standard pour les messages flash serait la définition du message dans une requête, et l'affichage après une redirection vers une autre page. Par exemple, un utilisateur soumet un formulaire qui passe par un contrôleur de mise à jour, et ensuite, à la fin de ce contrôleur, ce dernier redirige la page vers celle mise à jour ou vers une page d'erreur. Le message flash défini dans la requête de la page précédente serait donc affiché immédiatement dans la page suivante de cette session. Cela n'est qu'un exemple d'utilisation des messages flash. AutoExpireFlashBag Cette implémentation de la définition des messages flash ne sera disponible que durant l'affichage de la prochaine page. Ces messages vont expirer de manière automatique suivant s'ils ont été recupérés ou non ; FlashBag Dans cette implémentation, les messages vont rester dans la session jusqu'à ce qu'ils soient explicitement récupérés ou supprimés. Cela rend donc possible l'utilisation du mécanisme de cache ESI. FlashBagInterface possède une API simple : add() 0 : Ajoute un message flash à la pile du type spécifié ; set() : Définit des flashs par type ; cette méthode prend soit un message «unique» en tant que chaîne de caractères ou plusieurs messages dans un tableau ; get() : Récupère les flashs par type et supprime ces derniers du sac ; setall() : Définit tous les flashs ; accepte un tableau à clé de tableaux type => array(messages) ; all() : Récupère tous les flashs (en tant que tableau à clé de tableaux) et supprime les flashs du sac ; peek() : Récupère tous les flashs par type (lecture seulement) ; peekall() : Récupère tous les flashs (lecture seulement) en tant que tableau à clé de tableaux ; has() : Retourne «true» si le type existe, «false» sinon ; keys() : Retourne un tableau des types de flash stockés ; clear() : Supprime le sac. Pour des applications simples, il est généralement suffisant d'avoir un message flash par type, par exemple une notification de confirmation après qu'un formulaire ait été soumis. Cependant, les messages flash sont stockés dans un tableau à clé par $type de flash qui signifie que votre application peut émettre plusieurs messages pour un type donné. Cela permet à l'api d'être utilisée pour un système de messages plus complexe dans votre application.. http://api.symfony.com/./symfony/component/httpfoundation/session/attribute/attributebaginterface.html#method_clear. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/autoexpireflashbag.html. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbag.html. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html 0. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_add. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_set. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_get. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_setall. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_all. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_peek. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_peekall. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_has. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_keys. http://api.symfony.com/./symfony/component/httpfoundation/session/flash/flashbaginterface.html#method_clear generated on September, 0 Chapter : Gestion de Session
Exemples de définition de plusieurs flashs: Listing - use Symfony\Component\HttpFoundation\Session\Session; $session = new Session(); $session->start(); // ajoute des messages flash $session->getflashbag()->add('warning', 'Your config file is writable, it should be set read-only'); $session->getflashbag()->add('error', 'Failed to update name'); $session->getflashbag()->add('error', 'Another error'); Afficher les messages flash pourrait ressembler à quelque chose comme ca : Affiche simplement un type de message: Listing - // affiche les avertissements foreach ($session->getflashbag()->get('warning', array()) as $message) echo "<div class='flash-warning'>$message</div>"; // affiche les erreurs foreach ($session->getflashbag()->get('error', array()) as $message) echo "<div class='flash-error'>$message</div>"; Méthode compacte de traitement de l'affichage de tous les flashs en une seule fois: Listing - foreach ($session->getflashbag()->all() as $type => $messages) foreach ($messages as $message) echo "<div class='flash-$type'>$message</div>\n"; generated on September, 0 Chapter : Gestion de Session
Chapter Configurer les Sessions et les gestionnaires de sauvegarde Cette section explique comment configurer la gestion des sessions et comment l'adapter à vos besoins spécifiques. Cette documentation parle aussi des gestionnaires de sauvegarde, qui stockent et récupèrent les données de session, et configurent le comportement des sessions. Gestionnaires de sauvegarde Le fonctionnement d'une session PHP possède six opérations possibles qui peuvent arriver. La session normale suit le schéma suivant : open («ouvrir» en français, read («lire» en français), write («écrire» en français) et close («fermer» en français), avec la possibilité d'avoir aussi destroy («détruire» en français) et gc (ramasse-miettes qui va rendre n'importe quelles anciennes sessions expirées : gc est appelée de manière aléatoire selon la documentation de PHP et si elle est appelée, elle est invoquée après l'opération open). Vous pouvez en lire plus à propos de ce sujet : php.net/session.customhandler. Gestionnaires de sauvegarde PHP natifs Les gestionnaires dits «natifs» sont des gestionnaires de sauvegarde qui sont soit compilés en PHP, soit fournis en tant qu'extensions PHP, comme PHP-Sqlite, PHP-Memcached, etc. Tous les gestionnaires de sauvegarde sont internes à PHP, et en tant que tel, n'ont pas d'api publique. Ils doivent être configurés via des directives PHP ini, généralement session.save_path et potentiellement d'autres directives spécifiques de «driver». Des détails plus précis peuvent être trouvés dans le «docblock» de la méthode setoptions() de chaque classe. Bien que les gestionnaires de sauvegarde puissent être activés en utilisant directement ini_set('session.save_handler', $name);, Symfony fournit un moyen pratique d'activer ces derniers de la même manière que pour les gestionnaires personnalisés. Symfony fournit des «drivers» pour le gestionnaire de sauvegarde natif suivant comme exemple :. http://php.net/session.customhandler generated on September, 0 Chapter : Configurer les Sessions et les gestionnaires de sauvegarde
NativeFileSessionHandler Exemple d'utilisation: Listing - use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; $storage = new NativeSessionStorage(array(), new NativeFileSessionHandler()); $session = new Session($storage); Excepté le gestionnaire des fichiers qui est intégré dans PHP et toujours à disposition, la disponibilité des autres gestionnaires dépend du fait que ces extensions PHP soient activées ou non lors de l'exécution. Les gestionnaires de sauvegarde natifs fournissent une solution rapide pour le stockage de session, cependant, dans des systèmes complexes où vous avez besoin de plus de contrôle, des gestionnaires de sauvegarde personnalisés peuvent fournir plus de liberté et de flexibilité. Symfony fournit plusieurs implémentations que vous pourriez personnaliser plus tard si nécessaire. Gestionnaires de sauvegarde personnalisés Les gestionnaires personnalisés sont ceux qui remplacent complètement les gestionnaires de sauvegarde intégrés dans PHP en fournissant six fonctions de «callback» que PHP appelle en interne à différents points durant le flux d'exécution de la session. Le composant «HttpFoundation» de Symfony en fournit quelques uns par défaut et ces derniers peuvent vous servir d'exemples si vous souhaitez écrire le(s) vôtre(s). PdoSessionHandler MemcacheSessionHandler MemcachedSessionHandler MongoDbSessionHandler NullSessionHandler Exemple d'utilisation: Listing - use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/nativefilesessionhandler.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/pdosessionhandler.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/memcachesessionhandler.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/memcachedsessionhandler.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/mongodbsessionhandler.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/nullsessionhandler.html generated on September, 0 Chapter : Configurer les Sessions et les gestionnaires de sauvegarde
$storage = new NativeSessionStorage(array(), new PdoSessionHandler()); $session = new Session($storage); Configurer les sessions PHP Le classe NativeSessionStorage peut configurer la plupart des directives de configuration PHP ini qui sont documentées ici : php.net/session.configuration. Pour configurer ces paramètres, passez les clés (en omettant la partie initiale session. de la clé) en tant qu'un tableau clés-valeurs à l'argument $options du constructeur. Ou définissez-les via la méthode setoptions() 0. Pour des raisons de clarté, certaines options clé sont expliquées dans cette documentation. Durée de vie du cookie de session Pour des raisons de sécurité, il est généralement recommandé que les jetons de sécurité soient envoyés comme des cookies de session. Vous pouvez configurer la durée de vie de ces derniers en spécifiant (en secondes) la clé cookie_lifetime dans l'argument $options du constructeur de NativeSessionStorage. Définir cookie_lifetime à 0 va avoir pour effet que le cookie perdurera uniquement jusqu'à ce que le navigateur reste ouvert. Généralement, cookie_lifetime devrait être défini avec un grand nombre de jours, semaines ou mois. Il n'est pas rare de définir des cookies pour une année ou plus dépendant de l'application. Comme les cookies de session sont juste des jetons côté client, ils sont moins importants pour le contrôle des détails de vos paramètres de sécurité qui peuvent en fin de compte être contrôlés de manière sécurisée seulement côté serveur. Le paramètre cookie_lifetime est le nombre de secondes durant lequel le cookie devrait perdurer, ce n'est pas un «timestamp Unix». Le cookie de session résultant va être estampillé avec une date d'expiration correspondant à time()``+``cookie_lifetime où la date «time» est prise depuis le serveur. Configurer le ramasse-miettes («Garbage Collector» en anglais) Lorsqu'une session débute, PHP va appeler le gestionnaire gc de manière aléatoire selon la probabilité définie par session.gc_probability / session.gc_divisor. Par exemple, si ces dernières étaient définies respectivement avec / 00, cela signifierait une probabilité de %. De même, / signifierait chances sur d'être appelé, c-a-d %. Si le gestionnaire de ramasse-miettes est invoqué, PHP va passer la valeur stockée dans la directive PHP ini session.gc_maxlifetime. La signification dans ce contexte est que n'importe quelle session stockée qui a été sauvegardée il y a plus longtemps que maxlifetime devrait être supprimée. Cela permet d'expirer des enregistrements selon leur temps d'inactivité.. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/nativesessionstorage.html. http://php.net/session.configuration 0. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/nativesessionstorage.html#method_setoptions. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/nativesessionstorage.html generated on September, 0 Chapter : Configurer les Sessions et les gestionnaires de sauvegarde
Vous pouvez configurer ces paramètres en passant un tableau contenant gc_probability, gc_divisor et gc_maxlifetime au constructeur de NativeSessionStorage ou à la méthode setoptions(). Durée de vie de la Session Quand une nouvelle session est créée, signifiant que Symfony envoie un nouveau cookie de session au client, le cookie va être estampillé avec une date d'expiration. Cette dernière est calculée en ajoutant la valeur de configuration PHP de session.cookie_lifetime à la date courante du serveur. PHP va générer un cookie une fois seulement. On attend du client qu'il stocke ce cookie pour toute la durée de vie spécifiée. Un nouveau cookie sera généré uniquement lorsque la session est détruite, le cookie du navigateur est supprimé, ou que l'id de la session est regénéré en utilisant les méthodes migrate() ou invalidate() de la classe Session. La durée de vie initiale du cookie peut être définie en configurant NativeSessionStorage grâce à la méthode setoptions(array('cookie_lifetime' => )). Une durée de vie de cookie égale à 0 signifie que le cookie expire lorsque le navigateur est fermé. Durée d'inactivité de la session/maintenir actif Il existe souvent des circonstances durant lesquelles vous souhaitez protéger ou minimiser l'utilisation d'une session quand un utilisateur étant connecté s'éloigne de son terminal en détruisant sa session après une certaine période d'inactivité. Par exemple, cela est classique pour les applications bancaires de déconnecter l'utilisateur après ou 0 minutes d'inactivité. Définir la durée de vie du cookie n'est pas approprié ici car il peut être manipulé par le client, donc nous devons rendre effectif l'expiration du côté du serveur. La manière la plus facile est d'implémenter ce mécanisme via le ramasse-miettes qui s'exécute assez fréquemment. Le cookie lifetime serait défini avec une valeur relativement grande, et la propriété maxlifetime du ramasse-miettes serait définie afin de détruire les sessions au bout de n'importe quelle période d'inactivité souhaitée. L'autre option est de vérifier spécifiquement si une session a expiré après qu'elle ait été démarrée. La session peut être détruite comme nous le souhaitons. Ce procédé permet d'intégrer l'expiration des sessions au sein de l'expérience utilisateur, par exemple, en affichant un message. Symfony enregistre quelques métadonnées basiques à propos de chaque session afin de vous offrir une liberté entière en ce qui concerne ce sujet. Métadonnées de Session Les sessions possèdent quelques métadonnées permettant d'effectuer des réglages fins sur les paramètres de sécurité. L'objet session possède un accesseur pour les métadonnées, getmetadatabag() qui expose une instance de MetadataBag : Listing -. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/nativesessionstorage.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/nativesessionstorage.html#method_setoptions. http://api.symfony.com/./symfony/component/httpfoundation/session/session.html#method_getmetadatabag. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/metadatabag.html generated on September, 0 Chapter : Configurer les Sessions et les gestionnaires de sauvegarde
$session->getmetadatabag()->getcreated(); $session->getmetadatabag()->getlastused(); Les deux méthodes retournent un «timestamp Unix» (relatif au serveur). Ces métadonnées peuvent être utilisées pour rendre explicitement une session expirée lors d'un accès au site, par exemple: Listing - $session->start(); if (time() - $session->getmetadatabag()->getlastused() > $maxidletime) $session->invalidate(); throw new SessionExpired(); // rediriger vers la page d'expiration de session Il est aussi possible de dire comment le cookie_lifetime a été défini pour un cookie particulier en observant le retour de la méthode getlifetime(): Listing - $session->getmetadatabag()->getlifetime(); La date d'expiration du cookie peut être déterminée en ajoutant le «timestamp» créé et la durée de vie. Compatibilité avec PHP. Depuis PHP..0, SessionHandler et SessionHandlerInterface sont disponibles. Symfony. fournit une compatibilité ascendante pour l'interface SessionHandlerInterface afin qu'elle puisse être utilisée avec PHP.. Cela améliore grandement l'interopérabilité avec d'autres bibliothèques. SessionHandler est une classe interne à PHP spéciale qui expose les gestionnaires de sauvegarde natifs au développeur PHP. Afin de fournir une solution à ceux qui utilisent PHP., Symfony possède une classe spéciale appelée NativeSessionHandler 0 qui sous PHP., étend SessionHandler et sous PHP. est juste une classe de base vide. Cela fournit quelques opportunités intéressantes pour tirer parti de la fonctionnalité de PHP. si elle est disponible. Gestionnaire de Sauvegarde par procuration («proxy» en anglais) Il existe deux types de gestionnaires de sauvegarde par procuration qui héritent de AbstractProxy : ce sont NativeProxy et SessionHandlerProxy. NativeSessionStorage injecte automatiquement les gestionnaires de stockage dans un gestionnaire de sauvegarde par procuration à moins qu'ils soient déjà gérés par un autre. NativeProxy est utilisée automatiquement sous PHP. quand des gestionnaires de sauvegarde PHP natifs sont spécifiés en utilisant les classes Native*SessionHandler, pendant que SessionHandlerProxy. http://php.net/manual/en/class.sessionhandler.php. http://php.net/manual/en/class.sessionhandlerinterface.php. http://php.net/manual/en/class.sessionhandlerinterface.php. http://php.net/manual/en/class.sessionhandler.php 0. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/nativesessionhandler.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/abstractproxy.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/nativeproxy.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/sessionhandlerproxy.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/nativesessionstorage.html. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/nativeproxy.html generated on September, 0 Chapter : Configurer les Sessions et les gestionnaires de sauvegarde
sera utilisé pour gérer quelconques gestionnaires de sauvegarde personnalisés, qui implémentent SessionHandlerInterface. Sous PHP. et supérieur, tous les gestionnaires de session implémentent SessionHandlerInterface incluant les classes Native*SessionHandler qui héritent de SessionHandler. Le mécanisme de procuration («proxy» en anglais) vous permet d'être impliqué plus profondément dans les classes de gestionnaire de sauvegarde de session. Un «proxy» pourrait par exemple être utilisé pour encrypter toute transaction de session sans avoir aucune connaissance du gestionnaire de sauvegarde spécifique qui est utilisé.. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/handler/sessionhandlerproxy.html. http://php.net/manual/en/class.sessionhandlerinterface.php. http://php.net/manual/en/class.sessionhandlerinterface.php. http://php.net/manual/en/class.sessionhandler.php generated on September, 0 Chapter : Configurer les Sessions et les gestionnaires de sauvegarde
Chapter Tester avec les sessions Symfony est conçu depuis ses propres fondations avec l'objectif principal que le code soit testable. Afin de rendre votre code utilisant les sessions facilement testable, nous fournissons deux mécanismes de simulation de stockage pour les tests unitaires et fonctionnels. Tester du code en utilisant des sessions réelles est délicat car le flux des états dans PHP est global et il n'est pas possible d'avoir plusieurs sessions concurrentes dans le même processus PHP. Les moteurs de simulation de stockage simulent le flux d'une session PHP sans en démarrer une en fait ; ce qui vous permet de tester votre code sans avoir de complications. Vous pourriez aussi exécuter plusieurs instances dans le même processus PHP. Les «drivers» de simulation de stockage ne lisent ni écrivent les variables globales du système session_id() ou session_name(). Des méthodes sont fournies pour simuler cela si nécessaire : getid() : Récupère l'id de la session. setid() : Définit l'id de la session. getname() : Récupère le nom de la session. setname() : Définit le nom de la session. Tester de manière unitaire Pour tester unitairement où il n'est pas nécessaire de persister la session, vous devriez simplement permuter le moteur de stockage par défaut avec MockArraySessionStorage : Listing - use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpFoundation\Session\Session; $session = new Session(new MockArraySessionStorage());. http://api.symfony.com/./symfony/component/httpfoundation/session/sessionstorageinterface.html#getid(). http://api.symfony.com/./symfony/component/httpfoundation/session/sessionstorageinterface.html#setid(). http://api.symfony.com/./symfony/component/httpfoundation/session/sessionstorageinterface.html#getname(). http://api.symfony.com/./symfony/component/httpfoundation/session/sessionstorageinterface.html#setname(). http://api.symfony.com/./symfony/component/httpfoundation/session/storage/mockarraysessionstorage.html generated on September, 0 Chapter : Tester avec les sessions
Tester de manière fonctionnelle Pour tester de manière fonctionnelle où vous pourriez avoir besoin de persister les données de session à travers des processus PHP séparés, changer simplement le moteur de stockage pour qu'il soit MockFileSessionStorage : Listing - use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; $session = new Session(new MockFileSessionStorage());. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/mockfilesessionstorage.html generated on September, 0 Chapter : Tester avec les sessions 0
Chapter Intégration avec les session "Legacy" Parfois, il peut être nécessaire d'intégrer Symfony dans une vieille application ("legacy") où vous n'avez pas le niveau de contrôle dont vous avez besoin. Comme spécifié ailleurs, les sessions Symfony sont conçues pour remplacer l'usage des fonctions natives PHP session_*() et l'utilisation de la variable superglobale $_SESSION. De plus, Symfony doit obligatoirement démarrer la session. Cependant, quand vous n'avez vraiment pas le choix, vous pouvez utiliser une passerelle spéciale PhpBridgeSessionStorage qui est conçue pour permettre à Symfony de travailler avec une session qui a été démarrée en dehors du framework Symfony. A moins d'être très prudent, vous devez savoir que la session peut être interrompue par quelque chose, par exemple, si l'application legacy efface la variable $_SESSION. Un usage typique ressemblerait à ceci: Listing - 0 use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; // l'application legacy configurer la session ini_set('session.save_handler', 'files'); ini_set('session.save_path', '/tmp'); session_start(); // interfaçage avec cette session grâce à Symfony $session = new Session(new PhpBridgeSessionStorage()); // Symfony travaille maintenant avec la session $session->start(); Cela vous permet de commencer à utiliser l'api de session Symfony et de permettre la migration de votre application vers les sessions Symfony.. http://api.symfony.com/./symfony/component/httpfoundation/session/storage/phpbridgesessionstorage.html generated on September, 0 Chapter : Intégration avec les session "Legacy"
Les sessions Symfony stockent des données comme des attributs dans des "Bags" (sacs) spéciaux qui utilisent des clés dans la variable superglobale $_SESSION. Cela signifie qu'un session Symfony ne peut pas accéder aux clés de $_SESSION qui ont été définies par l'application legacy bien que le contenu de $_SESSION ait été enregistré lors de l'enregistrement de la session. generated on September, 0 Chapter : Intégration avec les session "Legacy"
Chapter Proxies de confiance Si vous vous trouvez derrière un proxy, par exemple un load balancer, alors certains entêtes vous sont peut être envoyés via l'entête spécial X-Forwarded-*. Par exemple, l'entête HTTP Host est habituellement utilisé pour retourner l'hôte demandé. Mais lorsque vous êtes derrière un proxy, le véritable hôte peut être stocké dans l'entête X-Forwarded-Host. Comme les entêtes HTTP peuvent être trafiqués, Symfony ne fait pas confiance à ces entêtes de proxy par défaut. Si vous êtes derrière un proxy, vous devez déclarer manuellement votre proxy comme étant de confiance. New in version.: Le support de la notation CIDR a été introduit dans Symfony., donc vous pouvez déclarer tout un sous-réseau dans la liste blanche (ex 0.0.0.0/, fc00::/). Listing - use Symfony\Component\HttpFoundation\Request; // ne fait confiance qu'aux entêtes de proxy qui viennent de ces adresses IP Request::setTrustedProxies(array('.0.0.', '0.0.0.0/')); Lorsque vous utilisez le reverse proxy interne de Symfony (AppCache.php), assurez vous d'avoir ajouté.0.0. à la liste des proxies de confiance. Configurer les noms d'entêtes Par défaut, les entêtes de proxy suivant sont déclarés fiables: X-Forwarded-For Utilisé dans getclientip() ; X-Forwarded-Host Utilisé dans gethost() ; X-Forwarded-Port Utilisé dans getport() ; X-Forwarded-Proto Utilisé dans getscheme() et issecure() ;. http://api.symfony.com/./symfony/component/httpfoundation/request.html#getclientip(). http://api.symfony.com/./symfony/component/httpfoundation/request.html#gethost(). http://api.symfony.com/./symfony/component/httpfoundation/request.html#getport() generated on September, 0 Chapter : Proxies de confiance
Si votre reverse proxy utilise un nom d'entête différent de ceux-ci, vous pouvez configurer ce nom d'entête via settrustedheadername() : Listing - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X-Proxy-For'); Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X-Proxy-Host'); Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X-Proxy-Port'); Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X-Proxy-Proto'); Déclarer des entêtes comme non fiables Par défaut, si vous déclarez l'adresse IP de votre proxy dans la liste blanche, alors les entêtes listés cidessus seront considérés comme fiables. Si vous avez besoin de déclarer certains de ces entêtes comme fiables, mais pas d'autre, vous pouvez également le faire: Listing - // l'entête ``X-Forwarded-Proto`` est déclaré non fiable, l'entête par défaut est utilisé Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, '');. http://api.symfony.com/./symfony/component/httpfoundation/request.html#getscheme(). http://api.symfony.com/./symfony/component/httpfoundation/request.html#issecure(). http://api.symfony.com/./symfony/component/httpfoundation/request.html#settrustedheadername() generated on September, 0 Chapter : Proxies de confiance
Chapter Le composant HttpKernel Le composant HttpKernel fournit un process structuré pour convertir une Request en Response en utilisant l'eventdispatcher. Il est suffisamment flexible pour créer un framework full-stack (Symfony), un micro-framework (Silex) ou un système de CMS avancé (Drupal). Installation Vous pouvez installer le composant de façons : Via Composer (symfony/http-kernel sur Packagist ); Via le dépôt Git officiel (https://github.com/symfony/httpkernel ). Le Workflow d'une Requête Chaque interaction HTTP démarre avec une requête et se termine par une réponse. Votre travail en tant que développeur est de créer le code PHP qui lit les informations de cette requête (c.-à-d. l'url) et qui va créer puis renvoyer une réponse (c.-à-d. une page HTML ou une chaîne JSON).. https://packagist.org/packages/symfony/http-kernel. https://github.com/symfony/httpkernel generated on September, 0 Chapter : Le composant HttpKernel
Habituellement, certains frameworks ou systèmes sont conçus pour gérer toutes les tâches répétitives (c.-à-d. routage, sécurité, etc.) afin qu'un développeur puisse facilement construire chaque page de l'application. La façon dont ces systèmes sont implémentés varie grandement. Le composant HttpKernel fournit une interface qui formalise le processus de traitement de requête et de création d'une réponse appropriée. Le composant est conçu pour être le cœur de toute application ou framework, aussi variée l'architecture de ce système soit-elle: Listing - 0 namespace Symfony\Component\HttpKernel; use Symfony\Component\HttpFoundation\Request; interface HttpKernelInterface //... /** * @return Response A Response instance */ public function handle( Request $request, $type = self::master_request, $catch = true ); En interne, HttpKernel::handle() ( l'implémentation concrète de HttpKernelInterface::handle() ) définit un workflow qui commence par une Request et se termine avec une Response.. http://api.symfony.com/./symfony/component/httpkernel/httpkernel.html#handle(). http://api.symfony.com/./symfony/component/httpkernel/httpkernelinterface.html#handle(). http://api.symfony.com/./symfony/component/httpfoundation/request.html. http://api.symfony.com/./symfony/component/httpfoundation/response.html generated on September, 0 Chapter : Le composant HttpKernel
Les détails exacts de ce workflow sont la clé pour comprendre comment le kernel (et le Framework Symfony ou toute autre librairie utilisant le kernel) fonctionne. HttpKernel : Piloté par événements La méthode HttpKernel::handle() fonctionne en interne en déclenchant des événements. Ceci rend la méthode flexible, mais aussi abstraite étant donné que tout le "travail" d'un(e) framework/application construit(e) est réalisé dans des écouteurs d'événements. Pour aider dans les explications de ce processus, ce document traite de toutes les étapes du processus et explique comment une implémentation spécifique de HttpKernel (le Framework Symfony) fonctionne. Pour commencer, utiliser la classe HttpKernel est très simple et nécessite la création d'un EventDispatcher et d'un controller resolver (expliqué plus bas). Pour terminer votre kernel fonctionnel, vous devrez ajouter plus d'écouteurs aux événements décrits ci-dessous: Listing - 0 use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Controller\ControllerResolver; // crée l'objet Request $request = Request::createFromGlobals(); $dispatcher = new EventDispatcher(); //... on peut ajouter des écouteurs // crée le controller resolver $resolver = new ControllerResolver(); // instancie le kernel $kernel = new HttpKernel($dispatcher, $resolver); // exécute le kernel, qui transforme la requête en réponse // en lançant des événements, appelant un contrôleur et en retournant la réponse $response = $kernel->handle($request);. http://api.symfony.com/./symfony/component/httpkernel/httpkernel.html generated on September, 0 Chapter : Le composant HttpKernel
0 // envoie les headers et fait un echo du contenu $response->send(); // déclenche l'événement kernel.terminate $kernel->terminate($request, $response); Consultez "Un exemple fonctionnel complet" pour une implémentation plus concrète. Pour des informations plus générales sur l'ajout d'écouteurs aux événement décrits ci-dessous, consultez Créer un écouteur d'événements. Fabien Potencier a aussi écrit une excellente série sur l'utilisation du composant HttpKernel et d'autres composants Symfony pour créer votre propre framework. Consultez Create your own framework... on top of the Symfony Components. ) L'événement kernel.request Usage typique : Ajouter plus d'informations à la Request, initialiser des parties du système, ou encore retourner une Response si possible (par exemple une couche de sécurité qui refuse l'accès). Table d'information des événements du kernel Le premier événement déclenché dans HttpKernel::handle est kernel.request, qui peut avoir différentes sortes d'écouteurs. Les écouteurs de ces événements peuvent être variés. Certains écouteurs (comme un écouteur de sécurité) pourraient avoir suffisamment d'informations pour créer un objet Response immédiatement. Par exemple, si une couche de sécurité a déterminé que l'utilisateur n'a pas l'accès à la page demandée, cet écouteur peut retourner une RedirectResponse 0 vers la page de login ou une réponse 0 Accès refusé. Si une Response est renvoyée à ce moment là, le processus avance directement jusqu'à l'événement kernel.response.. http://fabien.potencier.org/article/0/create-your-own-framework-on-top-of-the-symfony-components-part-. http://api.symfony.com/./symfony/component/httpkernel/httpkernel.html#handle() 0. http://api.symfony.com/./symfony/component/httpfoundation/redirectresponse.html generated on September, 0 Chapter : Le composant HttpKernel
D'autres écouteurs initialisent simplement des valeurs ou ajoutent des informations à la requête. Par exemple, un écouteur pourrait déterminer la locale à partir de l'objet Request. Un autre écouteur commun est le routage. Un écouteur de routage peut traiter l'objet Request et déterminer le contrôleur qui devrait être exécuté (consultez la section suivante). En fait, l'objet Request possède un attributes bag (un conteneur d'attributs) "attributes" qui est l'endroit idéal pour stocker ces données additionnelles et spécifiques à l'application à propos de la requête. Cela signifie que si votre écouteur de routing parvient à déterminer le contrôleur, il peut le stocker sur les attributs de Request (qui peuvent ainsi être utilisés par votre controller resolver). En résumé, le rôle de l'événement kernel.request est soit de créer et renvoyer une Response directement, soit d'ajouter de l'information à la Request (c.-à-d. récupérer la locale ou définir d'autres informations sur les attributs de Request). kernel.request dans le framework Symfony L'écouteur le plus important de kernel.request dans le framework Symfony est le RouterListener. Cette classe exécute la couche de routing, qui renvoie un tableau d'informations à propos de la requête qui correspond, ceci inclut le _controller et tous les placeholders qui sont dans le pattern de la route. (c.-à-d. slug). Consultez Le Composant de Routage pour plus d'informations. Ce tableau d'informations est stocké dans la propriété attributes de l'objet Request. Ajouter les informations de routage ici ne fait rien, mais elles sont ensuite exploitées par le controller resolver. ) Résolution du Contrôleur En partant du principe qu'aucun écouteur de kernel.request n'a été capable de créer une Response, l'étape suivante dans HttpKernel est de déterminer et préparer (c.-à-d. résoudre) le contrôleur. Le contrôleur est une partie du code de l'application finale qui est responsable de la création et du renvoi de la Response pour une page spécifique. Le seul prérequis est qu'il soit un callable PHP (c.-à-d. une fonction, une méthode, un objet implémentant la méthode invoke ou une closure).. http://api.symfony.com/./symfony/component/httpkernel/eventlistener/routerlistener.html. http://api.symfony.com/./symfony/component/httpfoundation/request.html generated on September, 0 Chapter : Le composant HttpKernel
Mais la manière dont le contrôleur exact est déterminé pour une requête dépend complètement de votre application. C'est le travail du "controller resolver" (une classe qui implémente ControllerResolverInterface et qui est un des arguments du contructeur de HttpKernel). Votre travail est de créer une classe qui implémente l'interface et déclare ces deux méthodes : getcontroller et getarguments. En fait, une implémentation par défaut existe déjà, et vous pouvez l'utiliser directement ou l'étudier: ControllerResolver. Cette implémentation est expliquée dans l'encart un peu plus bas: Listing - 0 namespace Symfony\Component\HttpKernel\Controller; use Symfony\Component\HttpFoundation\Request; interface ControllerResolverInterface public function getcontroller(request $request); public function getarguments(request $request, $controller); En interne, la méthode HttpKernel::handle appelle en premier lieu getcontroller() sur le controller resolver. Cette méthode reçoit l'objet Request et doit déterminer puis retourner un callable PHP (le contrôleur) basé sur les informations de la requête. La seconde méthode, getarguments(), sera appelée après qu'un autre événement (kernel.controller) est déclenché.. http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolverinterface.html. http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolver.html. http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolverinterface.html#getcontroller(). http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolverinterface.html#getarguments() generated on September, 0 Chapter : Le composant HttpKernel 00
Résolution du contrôleur dans le Framework Symfony Le Framework Symfony utilise la classe built-in ControllerResolver (en vérité, il utilise une sous-classe avec quelques fonctionnalités additionnelles mentionnées ci-dessous). Cette classe s'appuie sur les informations qui ont été placées dans la propriété attributes de l'objet Request pendant l'exécution de RouterListener. getcontroller Le ControllerResolver cherche une clé _controller sur la propriété attributes de l'objet Request (souvenez-vous, cette information est typiquement placée dans Request via l'écouteur RouterListener). Cette chaîne de caractères est ensuite convertie en callable PHP comme suit: a) Le format AcmeDemoBundle:Default:index de _controller est changé en une autre chaîne contenant le nom complet de la classe et le nom complet de la méthode en suivant les conventions en vigueur dans Symfony (c.-à-d. Acme\DemoBundle\Controller\DefaultController::indexAction). Cette transformation est spécifique à la sous-classe ControllerResolver utilisée par le Framework Symfony.. Une nouvelle instance du contrôleur est instanciée sans arguments. c) Si le contrôleur implémente ContainerAwareInterface, la méthode setcontainer est appelée sur l'objet contrôleur et le container lui est passé en argument. Cette étape aussi est spécifique à la sous-classe ControllerResolver 0 utilisée par le Framework Symfony. Il y a aussi quelques petites variations sur le processus décrit plus haut (c.-à-d. si vous déclarez vos contrôleurs en tant que services). ) L'événement kernel.controller Usage typique : Initialiser des services ou changer le contrôleur juste avant son exécution. Table d'information des événements du kernel Après que le contrôleur callable a été déterminé, HttpKernel::handle déclenche l'événement kernel.controller. Les écouteurs de cet événement pourraient initialiser certaines parties du systèmes ayant besoin que d'autres choses aient été déterminées (c.-à-d. le contrôleur, les informations de route), mais avant que le contrôleur ne soit exécuté. Pour voir quelques exemples, regardez la section Symfony plus bas.. http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolver.html. http://api.symfony.com/./symfony/bundle/frameworkbundle/controller/controllerresolver.html. http://api.symfony.com/./symfony/component/dependencyinjection/containerawareinterface.html 0. http://api.symfony.com/./symfony/bundle/frameworkbundle/controller/controllerresolver.html generated on September, 0 Chapter : Le composant HttpKernel 0
Les écouteurs de cet événement peuvent aussi changer complètement le contrôleur callable en appelant FilterControllerEvent::setController sur l'objet événement qui est passé à l'écouteur. kernel.controller dans le Framework Symfony Il y a quelques écouteurs mineurs sur l'événement kernel.controller dans le framework Symfony, et beaucoup portent sur la collection de données pour le profiler quand celui ci est activé. Un écouteur intéressant provient de SensioFrameworkExtraBundle, qui est inclu dans la Symfony Standard Edition. La fonctionnalité @ParamConverter de cet écouteur vous permet de passer un objet complet (c.-à-d. un objet Post) à votre contrôleur à la place d'un scalaire (c.-à-d. un paramètre id déclaré dans votre route). Cet écouteur (ParamConverterListener) utilise la reflexion pour examiner chaque argument du contrôleur et essaie d'utiliser différentes méthodes pour convertir ces derniers en objets, qui sont ensuite stockés dans la propriété attributes de l'objet Request. Consultez la section suivante pour savoir en quoi c'est important. ) Récupérer les arguments du contrôleur Ensuite, HttpKernel::handle appelle getarguments(). Rappelez vous que le contrôleur renvoyé par getcontroller est un callable PHP. Le rôle de getarguments est de renvoyer un tableau d'arguments qui devraient être passés au contrôleur. Libre à vous de décider comment ceci est réalisé, cependant la classe built-in ControllerResolver est un bon exemple.. http://api.symfony.com/./symfony/component/httpkernel/event/filtercontrollerevent.html#setcontroller(). http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolverinterface.html#getarguments(). http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolver.html generated on September, 0 Chapter : Le composant HttpKernel 0
A ce moment là, le kernel dispose d'un callable PHP (le contrôleur) et d'un tableau d'arguments qui devraient être passés au callable lors de son exécution. Récupération des arguments du contrôleur dans le Framework Symfony Maintenant que vous savez exactement ce qu'est un contrôleur callable (habituellement une méthode dans un objet contrôleur), le ControllerResolver utilise reflection sur le callable pour retourner un tableau des noms (names) de chaque argument. Il parcourt ensuite ces arguments et utilise l'astuce suivante pour déterminer quelle valeur devrait être passée à chaque argument: a) Si l'attributes bag (conteneur d'attributs) de la Request contient une clé qui correspond au nom de l'argument, cette valeur est utilisée. Par exemple, lorsque le premier argument d'un contrôleur est $slug et qu'il y a une clé slug dans l'attributes bag de Request, cette valeur est utilisée (et provient en général du routing via RouterListener). b) Si l'argument du contrôleur est typé comme étant un objet Request, alors Request est passée en valeur. ) Appeler le contrôleur L'étape suivante est simple! HttpKernel::handle exécute le contrôleur.. http://php.net/manual/en/book.reflection.php. http://api.symfony.com/./symfony/component/httpfoundation/request.html generated on September, 0 Chapter : Le composant HttpKernel 0
Le rôle du contrôleur est de construire la réponse pour une ressource donnée. Cela pourrait être une page HTML, une chaîne JSON ou n'importe quoi. A l'inverse de toutes les autres étapes du processus jusqu'ici, cette étape est implémentée par le "développeur final", pour chaque page qui est construite. Habituellement, le contrôleur retourne un objet Response. Si c'est le cas, alors le travail du kernel est presque fini! Dans ce cas, l'étape suivante est l'événement kernel.response. En revanche, si le contrôleur retourne autre chose qu'une Response, alors le kernel a un peu plus de travail à faire - kernel.view (puisque le but est de toujours générer un objet Response). Un contrôleur doit renvoyer quelque chose. Si un contrôleur retourne null, une exception est levée immédiatement. generated on September, 0 Chapter : Le composant HttpKernel 0
) L'événement kernel.view Usage typique : Transformer une valeur retournée par un contrôleur autre que Response en Response Table d'information des événements du kernel Si le contrôleur ne retourne pas un objet Response, alors le kernel déclenche un autre événement (kernel.view). Le rôle d'un écouteur de cet événement est d'utiliser la valeur de retour du contrôleur (c.- à-d. un tableau ou un objet) et de créer une Response. Cela peut être utile si vous souhaitez utiliser une couche "vue" : au lieu de retourner une Response depuis le contrôleur, on renvoie des données qui représentent la page. Un écouteur de cet événement pourrait alors exploiter ces données pour créer une Response au bon format (c.-à-d. HTML, JSON, etc.). A ce moment, si aucun écouteur ne définit une réponse dans l'événement, alors une exception est levée : soit le contrôleur soit au moins un des écouteurs doivent toujours retourner une Response. kernel.view dans le Framework Symfony Il n'y a aucun écouteur par défaut dans le Framework Symfony pour l'événement kernel.view. Toutefois, un core bundle (SensioFrameworkExtraBundle) ajoute un écouteur à cet événement. Si votre contrôleur renvoie un tableau, et que vous placez l'annotation @Template au dessus du contrôleur, alors cet écouteur effectue le rendu d'un template, passe le tableau retourné par votre contrôleur à ce template et crée une Response contenant le contenu de ce template. De plus, un bundle populaire de la communauté FOSRestBundle implémente un écouteur sur cet événement et qui fournit une couche de vue robuste capable d'utiliser un seul contrôleur pour retourner plusieurs type différents de réponses (c.-à-d. HTML, JSON, XML, etc.). ) L'événement kernel.response Usage typique : Modifier l'objet Response juste avant son envoi. Table d'information des événements du kernel. https://github.com/friendsofsymfony/fosrestbundle generated on September, 0 Chapter : Le composant HttpKernel 0
L'objectif du kernel est de transformer une Request en Response. La réponse pourrait être créée pendant l'événement kernel.request, retournée depuis le controller, ou retournée par un des écouteurs de l'événement kernel.view. Quel que soit le créateur de la Response, un autre événement (kernel.response) est déclenché juste après. Un écouteur typique de cet événement va modifier l'objet Response, par exemple en modifiant un header, en ajoutant un cookie, voir même en changeant le contenu de la Response elle-même (par exemple en injectant du JavaScript avant la fin du tag </body> d'une réponse HTML). Après le déclenchement de cet événement, l'objet Response dans sa forme finale est retourné par handle(). Dans le cas d'usage le plus commun, vous pouvez directement appeler la méthode send(), qui envoie les headers et affiche le contenu de Response. kernel.response dans le Framework Symfony Il y a plusieurs écouteurs mineurs sur cet événement dans le Framework Symfony, et la plupart modifient la réponse. Par exemple, l'écouteur WebDebugToolbarListener injecte du JavaScript à la fin de votre page dans l'environnement dev qui permet l'affichage de la barre de débogage. Un autre écouteur, ContextListener 0, sérialise les informations de l'utilisateur en cours dans la session afin qu'elles puissent être rechargées lors de la requête suivante. ) L'événement kernel.terminate Usage typique : Effectuer des actions "longues" après que la réponse a été envoyée à l'utilisateur. Table d'informations des événements du kernel L'événement final du processus HttpKernel est kernel.terminate et est unique car étant déclenché après l'appel à HttpKernel::handle, et après que la réponse a été envoyée à l'utilisateur. Souvenez vous, plus haut, le code utilisant le kernel se termine comme ceci: Listing - // envoie les headers et affiche le contenu de la réponse $response->send(); // déclenche l'événement kernel.terminate $kernel->terminate($request, $response); Comme vous pouvez le voir, en appelant $kernel->terminate après avoir envoyé la réponse, vous déclencherez l'événement kernel.terminate où vous pourrez effectuer certaines actions qui ont été différées en vue de renvoyer une réponse aussi vite que possible au client (comme envoyer des emails). Utiliser kernel.terminate est optionnel, et vous ne devriez l'appeler que si le kernel implémente TerminableInterface.. http://api.symfony.com/./symfony/component/httpkernel/httpkernel.html#handle(). http://api.symfony.com/./symfony/component/httpfoundation/response.html#send(). http://api.symfony.com/./symfony/bundle/webprofilerbundle/eventlistener/webdebugtoolbarlistener.html 0. http://api.symfony.com/./symfony/component/security/http/firewall/contextlistener.html. http://api.symfony.com/./symfony/component/httpkernel/terminableinterface.html generated on September, 0 Chapter : Le composant HttpKernel 0
kernel.terminate dans le Framework Symfony Si vous utilisez SwiftmailerBundle avec Symfony avec le memory spooling, alors le EmailSenderListener est activé. Ce dernier envoie tous les emails planifiés pour envoi pendant le traitement de la requête. Gérer les exceptions : l'événement kernel.exception Usage typique : Gérer certains types d'exceptions et créer une Response pour ces exceptions. Table d'information des événements du kernel Si une exception est levée à n'importe quel instant dans HttpKernel::handle, un autre événement (kernel.exception) est déclenché. En interne, le corps de la méthode handle est encapsulé dans un bloc try-catch. Quand une exception est levée, l'événement kernel.exception est déclenché pour que votre application puisse réagir à cette exception. Tout écouteur de cet événement reçoit un objet GetResponseForExceptionEvent qui vous permet d'accéder à l'exception d'origine via la méthode getexception() Un écouteur typique sur cet événement va traiter certains types d'exceptions et créer une Response d'erreur appropriée. Par exemple, pour générer une page 0, vous pourriez lever un type spécial d'exception et ensuite ajouter un écouteur sur cet événement qui va l'intercepter et créer puis retourner une Response 0. En fait, le composant HttpKernel est fourni avec un ExceptionListener. Si vous choisissez d'utiliser ce dernier, il gérera une grande variété d'exceptions par défaut (voir l'encart plus bas pour plus de détails).. http://api.symfony.com/./symfony/bundle/swiftmailerbundle/eventlistener/emailsenderlistener.html. http://api.symfony.com/./symfony/component/httpkernel/event/getresponseforexceptionevent.html. http://api.symfony.com/./symfony/component/httpkernel/event/getresponseforexceptionevent.html#getexception(). http://api.symfony.com/./symfony/component/httpkernel/eventlistener/exceptionlistener.html generated on September, 0 Chapter : Le composant HttpKernel 0
kernel.exception dans le Framework Symfony Il y a deux écouteurs principaux pour kernel.exception lorsque vous utilisez le framework Symfony. ExceptionListener dans HttpKernel Le premier est livré avec le composant HttpKernel et est appelé ExceptionListener. Cet écouteur a plusieurs rôles: ) L'exception levée est convertie en un objet FlattenException, qui contient toutes les informations à propos de la requête, et qui peut être affiché ou sérialisée. ) Si l'exception d'origine implémente HttpExceptionInterface, alors getstatuscode et getheaders sont appelées pour alimenter le header et le code de statut de l'objet FlattenException. Ces valeurs sont utilisées pour créer la réponse finale. ) Un contrôleur est exécuté et reçoit la FlattenException. Le contrôleur exact à utiliser est passé comme argument du constructeur de l'écouteur. C'est ce contrôleur qui renverra la Response finale pour cette page d'erreur. ExceptionListener dans Security L'autre écouteur important est le ExceptionListener. Le but de cet écouteur est de gérer les exceptions de sécurité, et, quand c'est approprié, d'aider l'utilisateur à s'authentifier (c.-à-d. de le rediriger sur la page de connexion). Créer un écouteur d'événements Comme vous avez pu le constater, vous pouvez attacher des écouteurs d'événements à tous les événements déclenchés durant le cycle d'exécution de HttpKernel::handle. Typiquement, un écouteur est une classe PHP dont une méthode est exécutée, voir Le Composant répartiteur d'évènement. Le nom de chaque événement du kernel est défini en tant que constante dans la classe KernelEvents 0. De plus, chaque écouteur d'événement reçoit un argument unique, qui est une classe fille de KernelEvent. Cet objet contient les informations à propos de l'état actuel du système et chaque type d'événement possède ses propres objets. Nom Constante KernelEvents Argument passé à l'écouteur kernel.request KernelEvents::REQUEST GetResponseEvent kernel.controller KernelEvents::CONTROLLER FilterControllerEvent kernel.view KernelEvents::VIEW GetResponseForControllerResultEvent kernel.response KernelEvents::RESPONSE FilterResponseEvent kernel.terminate KernelEvents::TERMINATE PostResponseEvent. http://api.symfony.com/./symfony/component/httpkernel/eventlistener/exceptionlistener.html. http://api.symfony.com/./symfony/component/httpkernel/exception/flattenexception.html. http://api.symfony.com/./symfony/component/httpkernel/exception/httpexceptioninterface.html. http://api.symfony.com/./symfony/component/security/http/firewall/exceptionlistener.html 0. http://api.symfony.com/./symfony/component/httpkernel/kernelevents.html. http://api.symfony.com/./symfony/component/httpkernel/event/kernelevent.html. http://api.symfony.com/./symfony/component/httpkernel/event/getresponseevent.html. http://api.symfony.com/./symfony/component/httpkernel/event/filtercontrollerevent.html. http://api.symfony.com/./symfony/component/httpkernel/event/getresponseforcontrollerresultevent.html. http://api.symfony.com/./symfony/component/httpkernel/event/filterresponseevent.html. http://api.symfony.com/./symfony/component/httpkernel/event/postresponseevent.html generated on September, 0 Chapter : Le composant HttpKernel 0
kernel.exception KernelEvents::EXCEPTION GetResponseForExceptionEvent Un exemple fonctionnel complet Lors de l'utilisation du composant HttpKernel, vous êtes libre d'attacher tous les écouteurs que vous souhaitez aux événements du kernel et d'utiliser tout controller resolver qui implémente ControllerResolverInterface. Toutefois, le composant HttpKernel est fourni avec des écouteurs built-in ainsi qu'un ControllerResolver qui peuvent être utilisés pour un exemple fonctionnel: Listing - 0 0 0 use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Controller\ControllerResolver; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; $routes = new RouteCollection(); $routes->add('hello', new Route('/hello/name', array( '_controller' => function (Request $request) return new Response(sprintf("Hello %s", $request->get('name'))); ) )); $request = Request::createFromGlobals(); $matcher = new UrlMatcher($routes, new RequestContext()); $dispatcher = new EventDispatcher(); $dispatcher->addsubscriber(new RouterListener($matcher)); $resolver = new ControllerResolver(); $kernel = new HttpKernel($dispatcher, $resolver); $response = $kernel->handle($request); $response->send(); $kernel->terminate($request, $response); Sous-requêtes En plus de la requête "principale" ("main") qui est envoyée dans HttpKernel::handle, vous pouvez aussi envoyer une "sous-requête". Une sous-requête parait et agit de la même façon que n'importe quelle autre requête, mais sert typiquement à gérer le rendu de petites portions de la page au lieu d'une page complète. Vous utiliserez fréquemment les sous requêtes depuis votre contrôleur (ou peut être depuis un template, qui sera rendu par votre contrôleur).. http://api.symfony.com/./symfony/component/httpkernel/event/getresponseforexceptionevent.html. http://api.symfony.com/./symfony/component/httpkernel/controller/controllerresolverinterface.html generated on September, 0 Chapter : Le composant HttpKernel 0
Pour exécuter une sous-requête, utilisez HttpKernel::handle, mais changez les arguments comme suit: Listing - 0 use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; //... // create some other request manually as needed $request = new Request(); // for example, possibly set its _controller manually $request->attributes->add('_controller', '...'); $response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST); // do something with this response Ceci crée un autre cycle complet requête-réponse où cette nouvelle Request est transformée en Response. La seule différence en interne est que certains écouteurs, (par exemple ceux de la sécurité) peuvent uniquement agir sur la requête principale. Chaque écouteur reçoit une classe fille de KernelEvent, dont getrequesttype() 0 peut être utilisé pour définir si l'écouteur traite les requêtes principales ou les sousrequêtes. Par exemple, un écouteur qui ne traitera qu'une requête principale ressemble à ceci: Listing - 0 use Symfony\Component\HttpKernel\HttpKernelInterface; //... public function onkernelrequest(getresponseevent $event) if (HttpKernelInterface::MASTER_REQUEST!== $event->getrequesttype()) return; //.... http://api.symfony.com/./symfony/component/httpkernel/event/kernelevent.html 0. http://api.symfony.com/./symfony/component/httpkernel/event/kernelevent.html#getrequesttype() generated on September, 0 Chapter : Le composant HttpKernel 0
Chapter 0 Le Composant Locale Le composant «Locale» fournit une solution de secours pour gérer les cas où l'extension intl est manquante. De plus, il étend l'implémentation de la classe native Locale avec plusieurs méthodes pratiques. Des solutions de substitution pour les fonctions et classes suivantes sont fournies : intl_is_failure intl_get_error_code intl_get_error_message Collator IntlDateFormatter Locale NumberFormatter L'implémentation de Stub supporte uniquement la locale en. Installation Vous pouvez installer le composant de différentes manières :. http://php.net/manual/en/class.locale.php. http://php.net/manual/en/function.intl-is-failure.php. http://php.net/manual/en/function.intl-get-error-code.php. http://php.net/manual/en/function.intl-get-error-message.php. http://php.net/manual/en/class.collator.php. http://php.net/manual/en/class.intldateformatter.php. http://php.net/manual/en/class.locale.php. http://php.net/manual/en/class.numberformatter.php generated on September, 0 Chapter 0: Le Composant Locale
Utilisez le dépôt Git officiel (https://github.com/symfony/locale ) ; Installez le via Composer (symfony/locale sur Packagist 0 ). Utilisation Tirer parti de ce «code de secours» inclut le fait de requérir les bouts de fonctions et d'ajouter les morceaux de classes à l'«autoloader». Lorsque vous utilisez le composant «ClassLoader», le code suivant est suffisant pour pallier l'extension intl manquante : Listing 0- if (!function_exists('intl_get_error_code')) require DIR.'/path/to/src/Symfony/Component/Locale/Resources/stubs/functions.php'; $loader->registerprefixfallbacks(array( DIR.'/path/to/src/Symfony/Component/Locale/ Resources/stubs')); La classe Locale enrichit la classe native Locale avec des fonctionnalités supplémentaires : Listing 0-0 use Symfony\Component\Locale\Locale; // récupère les noms de pays pour une locale ou récupère tous les codes de pays $countries = Locale::getDisplayCountries('pl'); $countrycodes = Locale::getCountries(); // récupère les noms de langue pour une locale ou récupère tous les codes de langue $languages = Locale::getDisplayLanguages('fr'); $languagecodes = Locale::getLanguages(); // récupère les noms de locale pour un code donné ou récupère tous les codes de locale $locales = Locale::getDisplayLocales('en'); $localecodes = Locale::getLocales(); // récupère les versions ICU $icuversion = Locale::getIcuVersion(); $icudataversion = Locale::getIcuDataVersion();. https://github.com/symfony/locale 0. https://packagist.org/packages/symfony/locale. http://api.symfony.com/./symfony/component/locale/locale.html. http://php.net/manual/en/class.locale.php generated on September, 0 Chapter 0: Le Composant Locale
Chapter Le Composant «Process» Le Composant «Process» exécute des commandes dans des sous-processus. Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt Git officiel (https://github.com/symfony/process ) ; Installez le via Composer (symfony/process sur Packagist ). Utilisation La classe Process vous permet d'exécuter une commande dans un sous-processus: Listing - 0 use Symfony\Component\Process\Process; $process = new Process('ls -lsa'); $process->settimeout(00); $process->run(); if (!$process->issuccessful()) throw new \RuntimeException($process->getErrorOutput()); print $process->getoutput(); La méthode run() se charge des différences subtiles entre les différentes plateformes lors de l'exécution d'une commande.. https://github.com/symfony/process. https://packagist.org/packages/symfony/process. http://api.symfony.com/./symfony/component/process/process.html. http://api.symfony.com/./symfony/component/process/process.html#run() generated on September, 0 Chapter : Le Composant «Process»
New in version.: Les méthodes getincrementaloutput() et getincrementalerroroutput() ont été ajoutées dans Symfony.. La méthode getoutput() retourne toujours l'ensemble du contenu de la sortie standard de la console et la méthode geterroroutput() le contenu de l'erreur. Alternativement, les méthodes getincrementaloutput() et getincrementalerroroutput() retournent les nouvelles sorties depuis le dernier appel. Lorsque vous exécutez une commande durant un certain temps (comme effectuer un rsync de fichiers vers un serveur distant), vous pouvez donner un retour à l'utilisateur final en temps réel en passant une fonction anonyme à la méthode run() : Listing - 0 use Symfony\Component\Process\Process; $process = new Process('ls -lsa'); $process->run(function ($type, $buffer) if ('err' === $type) echo 'ERR > '.$buffer; else echo 'OUT > '.$buffer; ); Si vous voulez exécuter du code PHP de manière isolée, utilisez plutôt le PhpProcess à la place: Listing - use Symfony\Component\Process\PhpProcess; $process = new PhpProcess(<<<EOF <?php echo 'Hello World';?> EOF ); $process->run(); New in version.: La classe ProcessBuilder a été ajoutée avec la version.. Pour que votre code fonctionne mieux sur toutes les plateformes, vous pourriez vouloir utiliser la classe ProcessBuilder à la place: Listing - use Symfony\Component\Process\ProcessBuilder; $builder = new ProcessBuilder(array('ls', '-lsa')); $builder->getprocess()->run();. http://api.symfony.com/./symfony/component/process/process.html#run(). http://api.symfony.com/./symfony/component/process/processbuilder.html generated on September, 0 Chapter : Le Composant «Process»
Chapter Le Composant PropertyAccess Le composant PropertyAccess fournit des fonctions pour lire et écrire depuis/dans un objet ou un tableau en une simple chaîne de caractères. Installation Vous pouvez installer le composant de deux manières différentes : Installez-le via Composer (symfony/property-access on Packagist ); Utilisez le repository Git officiel (https://github.com/symfony/propertyaccess ). Utilisation Le point d'entrée de ce composant est la méthode factory (usine) PropertyAccess::createPropertyAccessor. Cette factory va créer une nouvelle instance de la classe PropertyAccessor avec la configuration par défaut suivante Listing - use Symfony\Component\PropertyAccess\PropertyAccess; $accessor = PropertyAccess::createPropertyAccessor(); New in version.: Avant Symfony., la méthode createpropertyaccessor() getpropertyaccessor(). était appelée. https://packagist.org/packages/symfony/property-access. https://github.com/symfony/propertyaccess. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccess.html#createpropertyaccessor(). http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessor.html. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccess.html#createpropertyaccessor() generated on September, 0 Chapter : Le Composant PropertyAccess
Lire depuis des tableaux Vous pouvez lire un tableau avec la méthode PropertyAccessor::getValue. Ceci est fait en utilisant la notation d'indice qui est utilisée dans PHP Listing - //... $person = array( 'first_name' => 'Wouter', ); echo $accessor->getvalue($person, '[first_name]'); // 'Wouter' echo $accessor->getvalue($person, '[age]'); // null Comme vous pouvez le voir, la méthode va retourner null si l'index n'existe pas. Vous pouvez également utiliser les tableaux multi dimensionnels Listing - 0 //... $persons = array( array( 'first_name' => 'Wouter', ), array( 'first_name' => 'Ryan', ) ); echo $accessor->getvalue($persons, '[0][first_name]'); // 'Wouter' echo $accessor->getvalue($persons, '[][first_name]'); // 'Ryan' Lire depuis des Objets La méthode getvalue est une méthode très robuste, et vous pouvez voir toutes ses fonctionnalités lorsque vous travaillez avec des objets. Accéder aux propriétés publiques Pour lire depuis des propriétés, utilisez la notation "point" Listing - 0 //... $person = new Person(); $person->firstname = 'Wouter'; echo $accessor->getvalue($person, 'firstname'); // 'Wouter' $child = new Person(); $child->firstname = 'Bar'; $person->children = array($child); echo $accessor->getvalue($person, 'children[0].firstname'); // 'Bar'. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessor.html#getvalue() generated on September, 0 Chapter : Le Composant PropertyAccess
Accéder à des propriétés publiques est la dernière option utilisée par PropertyAccessor. Il essaie d'accéder à la valeur en utilisant les méthodes ci-dessous avant d'utiliser directement la propriété. Par exemple, si vous avez une propriété publique qui possède un accesseur, il utilisera ce dernier. Utiliser les accesseurs La méthode getvalue supporte également la lecture en utilisant les accesseurs. La méthode sera créée en utilisant les conventions de nommage communes pour les accesseurs. Elle "camelize" le nom de la propriété ( first_name devient FirstName) et le préfixe par get. Donc la méthode devient getfirstname Listing - 0 //... class Person private $firstname = 'Wouter'; public function getfirstname() return $this->firstname; $person = new Person(); echo $accessor->getvalue($person, 'first_name'); // 'Wouter' Utiliser les Hassers/Issers Et cela ne s'arrête pas là. Si aucun getter n'a été trouvé, l'accesseur va chercher un isser ou un hasser. Cette méthode est créé en utilisant la même méthode décrite pour les getters, cela signifie que vous pouvez faire quelque chose comme ça Listing - 0 0 //... class Person private $author = true; private $children = array(); public function isauthor() return $this->author; public function haschildren() return 0!== count($this->children); $person = new Person(); if ($accessor->getvalue($person, 'author')) echo 'He is an author'; if ($accessor->getvalue($person, 'children')) generated on September, 0 Chapter : Le Composant PropertyAccess
echo 'He has children'; Cela aura pour résultat : He is an author La méthode magique get() La méthode getvalue peut également utiliser la méthode magique get Listing - 0 //... class Person private $children = array( 'Wouter' => array(...), ); public function get($id) return $this->children[$id]; $person = new Person(); echo $accessor->getvalue($person, 'Wouter'); // array(...) La méthode magique call() Finalement, getvalue peut utiliser la méthode magique call, mais vous avez besoin d'activer cette fonctionnalité en utilisant la classe PropertyAccessorBuilder Listing - 0 0 //... class Person private $children = array( 'wouter' => array(...), ); public function call($name, $args) $property = lcfirst(substr($name, )); if ('get' === substr($name, 0, )) return isset($this->children[$property])? $this->children[$property] : null; elseif ('set' === substr($name, 0, )) $value = == count($args)? $args[0] : null; $this->children[$property] = $value; $person = new Person();. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessorbuilder.html generated on September, 0 Chapter : Le Composant PropertyAccess
// Activer la méthode magique call $accessor = PropertyAccess::getPropertyAccessorBuilder() ->enablemagiccall() ->getpropertyaccessor(); echo $accessor->getvalue($person, 'wouter'); // array(...) New in version.: L'utilisation de la méthode magique call() a été ajoutée dans Symfony.. La fonctionnalité call est désactivée par défaut. Vous pouvez l'activer en appelant ma méthode PropertyAccessorBuilder::enableMagicCallEnabled consultez Activer d'autres fonctionnalités. Ecrire dans des tableaux La classe PropertyAccessor peut faire bien plus que lire dans un tableau, elle peut également écrire dans un tableau. Cela peut être effectué en utilisant la méthode PropertyAccessor::setValue Listing - //... $person = array(); $accessor->setvalue($person, '[first_name]', 'Wouter'); echo $accessor->getvalue($person, '[first_name]'); // 'Wouter' // ou // echo $person['first_name']; // 'Wouter' Ecrire dans des objets La méthode setvalue dispose des mêmes fonctionnalités que la méthode getvalue. Vous pouvez utiliser les setters (mutateurs), la méthode magique set ou les propriétés pour écrire des valeurs Listing -0 0 //... class Person public $firstname; private $lastname; private $children = array(); public function setlastname($name) $this->lastname = $name; public function set($property, $value) $this->$property = $value;. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessorbuilder.html#enablemagiccallenabled(). http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessor.html#setvalue() generated on September, 0 Chapter : Le Composant PropertyAccess
0 //... $person = new Person(); $accessor->setvalue($person, 'firstname', 'Wouter'); $accessor->setvalue($person, 'lastname', 'de Jong'); $accessor->setvalue($person, 'children', array(new Person())); echo $person->firstname; // 'Wouter' echo $person->getlastname(); // 'de Jong' echo $person->children; // array(person()); Vous pouvez également utiliser call pour écrire des valeurs mais vous avez besoin d'activer la fonctionnalités. Consultez Activer d'autres fonctionnalités. Listing - 0 0 0 //... class Person private $children = array(); public function call($name, $args) $property = lcfirst(substr($name, )); if ('get' === substr($name, 0, )) return isset($this->children[$property])? $this->children[$property] : null; elseif ('set' === substr($name, 0, )) $value = == count($args)? $args[0] : null; $this->children[$property] = $value; $person = new Person(); // Activez la méthode magique call $accessor = PropertyAccess::getPropertyAccessorBuilder() ->enablemagiccall() ->getpropertyaccessor(); $accessor->setvalue($person, 'wouter', array(...)); echo $person->getwouter(); // array(...) Mélanger les Objets et les Tableaux Vous pouvez aussi mélanger objets et tableaux ensemble Listing - //... class Person generated on September, 0 Chapter : Le Composant PropertyAccess 0
0 0 public $firstname; private $children = array(); public function setchildren($children) $this->children = $children; public function getchildren() return $this->children; $person = new Person(); $accessor->setvalue($person, 'children[0]', new Person); // équivaut à $person->getchildren()[0] = new Person() $accessor->setvalue($person, 'children[0].firstname', 'Wouter'); // équivaut à $person->getchildren()[0]->firstname = 'Wouter' echo 'Hello '.$accessor->getvalue($person, 'children[0].firstname'); // 'Wouter' // équivaut à $person->getchildren()[0]->firstname Activer d'autres fonctionnalités La classe PropertyAccessor 0 peut être configurée pour des fonctionnalités supplémentaires. Pour cela, vous pouvez utiliser la classe PropertyAccessorBuilder Listing - 0 //... $accessorbuilder = PropertyAccess::createPropertyAccessorBuilder(); // Activer la méthode magique call $accessorbuilder->enablemagiccall(); // Désactiver la méthode magique call $accessorbuilder->disablemagiccall(); // Vérifier si la méthode magique call est activée $accessorbuilder->ismagiccallenabled() // renvoie le booléen true ou false // A la fin, réccupérer l'accesseur de propriété (property accessor) configuré $accessor = $accessorbuilder->getpropertyaccessor(); // Ou tout en même temps $accessor = PropertyAccess::createPropertyAccessorBuilder() ->enablemagiccall() ->getpropertyaccessor(); Ou vous pouvez passer les paramètres directement dans le constructeur (non recommandé) Listing - 0. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessor.html. http://api.symfony.com/./symfony/component/propertyaccess/propertyaccessorbuilder.html generated on September, 0 Chapter : Le Composant PropertyAccess
//... $accessor = new PropertyAccessor(true) // cela active la gestion de la méthode magique call generated on September, 0 Chapter : Le Composant PropertyAccess
Chapter Le Composant de Routage Le Composant de Routage fait correspondre une requête HTTP à un ensemble de variables de configuration. Installation Vous pouvez l'installer de deux manières différentes: Installez le via Composer (symfony/routing sur Packagist ); Utilisez le dépôt Git officiel (https://github.com/symfony/routing ). Usage Afin d'installer un système de routage basique, vous avez besoin de trois parties : Une RouteCollection, qui contient les définitions des routes (instances de la classe Route ) Une RequestContext, qui possède les informations concernant la requête Une classe UrlMatcher, qui s'occupe de la correspondance entre la requête et une route unique Jetons un oeil à un exemple simple. Notez que ce dernier suppose que vous ayez déjà configuré votre «autoloader» afin qu'il charge le composant de routage: Listing -. https://packagist.org/packages/symfony/routing. https://github.com/symfony/routing. http://api.symfony.com/./symfony/component/routing/routecollection.html. http://api.symfony.com/./symfony/component/routing/route.html. http://api.symfony.com/./symfony/component/routing/requestcontext.html. http://api.symfony.com/./symfony/component/routing/matcher/urlmatcher.html generated on September, 0 Chapter : Le Composant de Routage
0 use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $route = new Route('/foo', array('controller' => 'MyController')); $routes = new RouteCollection(); $routes->add('route_name', $route); $context = new RequestContext($_SERVER['REQUEST_URI']); $matcher = new UrlMatcher($routes, $context); $parameters = $matcher->match('/foo'); // array('controller' => 'MyController', '_route' => 'route_name') Faites attention lorsque vous utilisez $_SERVER['REQUEST_URI'], car cette variable pourrait contenir quelconques paramètres de requête dans l'url, ce qui causerait des problèmes avec la correspondance de la route. Une manière facile de résoudre cela est d'utiliser le composant «HttpFoundation» comme expliqué ci-dessous. Vous pouvez ajouter autant de routes que vous le souhaitez dans une RouteCollection. La méthode RouteCollection::add() prend deux arguments. Le premier est le nom de la route. Le deuxième est un objet Route, qui s'attend à recevoir une URL et tout tableau de variables personnalisées dans son constructeur. Ce tableau peut être n'importe quoi qui ait du sens pour votre application, et est retourné lorsque cette route correspond à la requête. Si aucune correspondance de route ne peut être trouvée, une ResourceNotFoundException 0 sera lancée. En plus de votre tableau de variables personnalisées, une clé _route qui contient le nom de la route correspondante est ajoutée. Définition des routes Une définition du routage complète peut contenir jusqu'à sept parties :. Le pattern de l'url de la route. Une correspondance tente d'être effectuée entre la route et l'url passée au RequestContext, et peut contenir des valeurs de substitution jokers nommées (par exemple : placeholders) afin de faire correspondre les parties dynamiques de l'url.. Un tableau de valeurs par défaut. Ce dernier contient un tableau de valeurs arbitraires qui seront retournées lorsque la requête correspond à la route.. Un tableau de conditions requises. Ce dernier définit les contraintes concernant le contenu des valeurs de substitution sous forme d'expressions régulières.. Un tableau d'options. Ce dernier contient des paramètres internes pour la route et sont généralement ceux qui sont le moins souvent nécessaires.. Un hôte. La correspondance est comparée avec l'hôte de la requête. Lisez Comment faire correspondre une route en se basant sur l'hôte pour plus de détails.. Un tableau de schémas. Cela assure un certain schéma HTTP (http, https).. Un tableau de méthodes. Cela assure une certaine méthode HTTP (HEAD, GET, POST,...). Prenez la route suivante, qui combine plusieurs de ces idées:. http://api.symfony.com/./symfony/component/routing/routecollection.html. http://api.symfony.com/./symfony/component/routing/routecollection.html#add(). http://api.symfony.com/./symfony/component/routing/route.html 0. http://api.symfony.com/./symfony/component/routing/exception/resourcenotfoundexception.html generated on September, 0 Chapter : Le Composant de Routage
Listing - 0 0 $route = new Route( '/archive/month', // chemin array('controller' => 'showarchive'), // valeurs par défaut array('month' => '[0-]-[0-]', 'subdomain' => 'www m'), // prérequies array(), // options 'subdomain.example.com', // hôte array(), // schémas array() // méthodes ); //... $parameters = $matcher->match('/archive/0-0'); // array( // 'controller' => 'showarchive', // 'month' => '0-0', // 'subdomain' => 'www', // '_route' =>... // ) $parameters = $matcher->match('/archive/foo'); // throws ResourceNotFoundException Dans ce cas, la route correspond avec l'url /archive/0-0, car le joker month correspond à l'expression régulière donnée. Cependant, /archive/foo ne correspond pas, car «foo» n'a pas de correspondance avec le joker «month». Si vous voulez avoir une correspondance pour toutes les URLs qui commencent par un certain chemin et qui se terminent par un suffixe déterminé, vous pouvez utiliser la définition de route suivante: Listing - $route = new Route( '/start/suffix', array('suffix' => ''), array('suffix' => '.*') ); Utiliser des préfixes Vous pouvez ajouter des routes ou d'autres instances de RouteCollection à une autre collection. De cette façon, vous pouvez construire un arbre de routes. De plus, vous pouvez définir un préfixe, des valeurs par défaut pour les paramètres, des prérequis, des options, des schémas et un hôte pour toutes les routes d'un sous-arbre en utilisant les méthodes fournies par la classe RouteCollection: Listing - $rootcollection = new RouteCollection(); $subcollection = new RouteCollection(); $subcollection->add(...); $subcollection->add(...); $subcollection->addprefix('/prefix'); $subcollection->adddefaults(array(...)); $subcollection->addrequirements(array(...));. http://api.symfony.com/./symfony/component/routing/routecollection.html generated on September, 0 Chapter : Le Composant de Routage
0 $subcollection->addoptions(array(...)); $subcollection->sethost('admin.example.com'); $subcollection->setmethods(array('post')); $subcollection->setschemes(array('https')); $rootcollection->addcollection($subcollection); Définir les paramètres de requête La classe RequestContext fournit des informations à propos de la requête courante. Vous pouvez définir tous les paramètres d'une requête HTTP avec cette classe via son constructeur: Listing - 0 public function construct( $baseurl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpport = 0, $httpsport =, $path = '/', $querystring = '' ) Normalement, vous pouvez passer les valeurs depuis la variable $_SERVER afin de fournir les données au RequestContext. Mais si vous utilisez le composant HttpFoundation, vous pouvez vous servir de sa classe Request pour récupérer le RequestContext via un raccourci: Listing - use Symfony\Component\HttpFoundation\Request; $context = new RequestContext(); $context->fromrequest(request::createfromglobals()); Générer une URL Alors que la classe UrlMatcher essaye de trouver une route qui corresponde à la requête donnée, vous pouvez aussi construire une URL depuis une certaine route: Listing - 0 use Symfony\Component\Routing\Generator\UrlGenerator; $routes = new RouteCollection(); $routes->add('show_post', new Route('/show/slug')); $context = new RequestContext($_SERVER['REQUEST_URI']); $generator = new UrlGenerator($routes, $context); $url = $generator->generate('show_post', array( 'slug' => 'my-blog-post',. http://api.symfony.com/./symfony/component/routing/requestcontext.html. http://api.symfony.com/./symfony/component/routing/requestcontext.html. http://api.symfony.com/./symfony/component/httpfoundation/request.html. http://api.symfony.com/./symfony/component/routing/requestcontext.html. http://api.symfony.com/./symfony/component/routing/matcher/urlmatcher.html generated on September, 0 Chapter : Le Composant de Routage
)); // /show/my-blog-post Si vous avez défini la condition requise _scheme, une URL absolue est générée si le schème du RequestContext courant ne respecte pas cette condition. Charger des routes depuis un fichier Vous avez déjà vu comment vous pouvez ajouter facilement des routes à une collection directement depuis PHP. Mais vous pouvez aussi charger des routes depuis différents fichiers. Le composant de Routage est fourni avec un certain nombre de classes de chargement, chacune vous fournissant la possibilité de charger une collection de définitions de route depuis un fichier externe d'un certain format. Chaque chargeur attend une instance de FileLocator en tant qu'argument du constructeur. Vous pouvez utiliser le FileLocator pour définir un tableau de chemins dans lequel le chargeur va chercher les fichiers requis. Si le fichier est trouvé, le chargeur retourne une RouteCollection 0. Si vous utilisez le chargeur YamlFileLoader, alors les définitions de route ressemblent à cela : Listing - # routes.yml route: path: /foo defaults: _controller: 'MyController::fooAction' route: path: /foo/bar defaults: _controller: 'MyController::foobarAction' Pour charger ce fichier, vous pouvez utiliser le code suivant. Cela suppose que votre fichier routes.yml est dans le même répertoire que le code ci-dessus: Listing - use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\YamlFileLoader; // look inside *this* directory $locator = new FileLocator(array( DIR )); $loader = new YamlFileLoader($locator); $collection = $loader->load('routes.yml'); En plus du chargeur YamlFileLoader, il y a d'autres chargeurs qui fonctionnent de la même manière : XmlFileLoader PhpFileLoader. http://api.symfony.com/./symfony/component/routing/requestcontext.html. http://api.symfony.com/./symfony/component/config/filelocator.html. http://api.symfony.com/./symfony/component/config/filelocator.html 0. http://api.symfony.com/./symfony/component/routing/routecollection.html. http://api.symfony.com/./symfony/component/routing/loader/yamlfileloader.html. http://api.symfony.com/./symfony/component/routing/loader/xmlfileloader.html. http://api.symfony.com/./symfony/component/routing/loader/phpfileloader.html generated on September, 0 Chapter : Le Composant de Routage
Si vous utilisez le chargeur PhpFileLoader, vous devez fournir le nom d'un fichier PHP qui retourne une RouteCollection : Listing -0 0 // RouteProvider.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $collection = new RouteCollection(); $collection->add( 'route_name', new Route('/foo', array('controller' => 'ExampleController')) ); //... return $collection; Des routes en tant que closures Le chargeur ClosureLoader existe également. Il appelle une closure et utilise son résultat en tant que RouteCollection : Listing - use Symfony\Component\Routing\Loader\ClosureLoader; $closure = function() return new RouteCollection(); ; $loader = new ClosureLoader(); $collection = $loader->load($closure); Des Routes en tant qu'annotations Enfin, il existe aussi le AnnotationDirectoryLoader et le AnnotationFileLoader qui permettent de charger des définitions de route depuis des annotations de classe. Les détails spécifiques ne sont pas expliqués ici. Le Routeur tout-en-un La classe Router 0 est un «package» tout-en-un permettant d'utiliser rapidement le composant de Routage. Le constructeur s'attend à recevoir une instance de chargeur, un chemin vers la définition principale des routes et d'autres paramètres: Listing - public function construct( LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null,. http://api.symfony.com/./symfony/component/routing/loader/phpfileloader.html. http://api.symfony.com/./symfony/component/routing/routecollection.html. http://api.symfony.com/./symfony/component/routing/loader/closureloader.html. http://api.symfony.com/./symfony/component/routing/routecollection.html. http://api.symfony.com/./symfony/component/routing/loader/annotationdirectoryloader.html. http://api.symfony.com/./symfony/component/routing/loader/annotationfileloader.html 0. http://api.symfony.com/./symfony/component/routing/router.html generated on September, 0 Chapter : Le Composant de Routage
); array $defaults = array() Avec l'option cache_dir, vous pouvez activer le cache pour les routes (si vous fournissez un chemin) ou désactiver le cache (si le paramètre est défini comme null). Le mécanisme de cache est géré automatiquement en arrière-plan si vous souhaitez l'utiliser. Un exemple basique de la classe Router ressemblerait à cela: Listing - 0 $locator = new FileLocator(array( DIR )); $requestcontext = new RequestContext($_SERVER['REQUEST_URI']); $router = new Router( new YamlFileLoader($locator), 'routes.yml', array('cache_dir' => DIR.'/cache'), $requestcontext ); $router->match('/foo/bar'); Si vous utilisez le cache, le composant de Routage va compiler de nouvelles classes qui seront sauvegardées dans le cache_dir. Cela signifie que votre script doit avoir les permissions d'écriture nécessaires pour ce chemin.. http://api.symfony.com/./symfony/component/routing/router.html generated on September, 0 Chapter : Le Composant de Routage
Chapter Comment faire correspondre une route en se basant sur l'hôte Vous pouvez aussi vous baser sur l'hôte HTTP de la requête entrante. Listing - mobile_homepage: path: / host: m.example.com defaults: _controller: AcmeDemoBundle:Main:mobileHomepage homepage: path: / defaults: _controller: AcmeDemoBundle:Main:homepage Les deux routes correspondent au même chemin /, cependant, la première ne correspondra que dans le cas où l'hôte sera m.example.com. Utiliser des variables Cette option utilise la même syntaxe que celle utilisée pour les chemins. Cela signifie que vous pouvez utiliser des variables comme noms d'hôte: Listing - projects_homepage: path: / host: "project_name.example.com" defaults: _controller: AcmeDemoBundle:Main:mobileHomepage homepage: path: / defaults: _controller: AcmeDemoBundle:Main:homepage generated on September, 0 Chapter : Comment faire correspondre une route en se basant sur l'hôte 0
Vous pouvez également définir des prérequis et des options par défaut pour ces variables. Par exemple, si vous voulez faire correspondre m.example.com et mobile.example.com, vous pouvez procéder comme ceci: Listing - 0 mobile_homepage: path: / host: "subdomain.example.com" defaults: _controller: AcmeDemoBundle:Main:mobileHomepage subdomain: m requirements: subdomain: m mobile homepage: path: / defaults: _controller: AcmeDemoBundle:Main:homepage Vous pouvez aussi utiliser des paramètres de service si vous ne voulez pas coder le nom d'hôte en dur: Listing - 0 mobile_homepage: path: / host: "m.domain" defaults: _controller: AcmeDemoBundle:Main:mobileHomepage domain: "%domain%" requirements: domain: "%domain%" homepage: path: / defaults: _controller: AcmeDemoBundle:Main:homepage Assurez-vous également d'inclure une option par défaut pour la variable domain. Autrement, vous devrez spécifier une valeur à chaque fois que vous génèrerez une URL en utilisant cette route. Utiliser la correspondance d'hôte de routes importées Vous pouvez également définir cette option sur des routes importées: Listing - # app/config/routing.yml acme_hello: resource: "@AcmeHelloBundle/Resources/config/routing.yml" host: "hello.example.com" L'hôte hello.example.com sera défini pour chaque route chargée depuis la nouvelle ressource de routage. generated on September, 0 Chapter : Comment faire correspondre une route en se basant sur l'hôte
Tester vos contrôleurs Vous devez définir l'entête HTTP Host sur vos objets de requêtes si vous voulez tester fonctionnellement vos routes. Listing - $crawler = $client->request( 'GET', '/homepage', array(), array(), array('http_host' => 'm.'. $client->getcontainer()->getparameter('domain')) ); generated on September, 0 Chapter : Comment faire correspondre une route en se basant sur l'hôte
Chapter Le composant sécurité Introduction Le composant sécurité fournit un système de sécurité complet pour vos applications web. Il est livré avec des méthodes d'authentification utilisant HTTP basic ou l'authentification digest, ou encore un formulaire interactif de login ou via le certificat X.0, mais permet également d'implémenter vos propres stratégies d'authentification. De plus, le composant offre la possibilité d'authoriser les utilisateurs en se basant sur leurs rôles, et il contient un système avancé d'acl. Installation Vous pouvez installer le composant de deux manières différentes : L'installation via Composer (symfony/security sur Packagist ); Utilisez le repository Git officiel (https://github.com/symfony/security ). Sections Le Pare-Feu et le Contexte de Sécurité Authentification Autorisation. https://packagist.org/packages/symfony/security. https://github.com/symfony/security generated on September, 0 Chapter : Le composant sécurité
Chapter Le Pare-Feu et le Contexte de Sécurité Au centre du composant sécurité il y a le contexte de sécurité, qui est une instance de SecurityContextInterface. Lorsque toutes les étapes du processus d'authentification de l'utilisateur ont été accomplies avec succès, vous pouvez demander au contexte de sécurité si l'utilisateur authentifié a accès à une certaine action ou une ressource de l'application Listing - 0 use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Core\Exception\AccessDeniedException; // instance de Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface $authenticationmanager =...; // instance de Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface $accessdecisionmanager =...; $securitycontext = new SecurityContext($authenticationManager, $accessdecisionmanager); //... authentifier l'utilisateur if (!$securitycontext->isgranted('role_admin')) throw new AccessDeniedException(); Lisez les sections dédiées pour en apprendre plus à propos de Authentification et Autorisation.. http://api.symfony.com/./symfony/component/security/core/securitycontextinterface.html generated on September, 0 Chapter : Le Pare-Feu et le Contexte de Sécurité
Un Pare-Feu pour les requêtes HTTP L'authentification d'un utilisateur est faite par le pare-feu. Une application peut avoir de nombreuses zones sécurisées, ainsi le pare-feu est configuré en utilisant un plan de ces zones sécurisées. Pour chacune des ces zones, le plan contient un "request matcher" et une collection d'écouteurs. Le request matcher donne au pare-feu la possibilité de déterminer si la requête pointe vers une zone sécurisée. Il est demandé aux écouteurs si la requête courante peut être utilisée pour authentifier l'utilisateur Listing - 0 use Symfony\Component\Security\Http\FirewallMap; use Symfony\Component\HttpFoundation\RequestMatcher; use Symfony\Component\Security\Http\Firewall\ExceptionListener; $map = new FirewallMap(); $requestmatcher = new RequestMatcher('^/secured-area/'); // instances de Symfony\Component\Security\Http\Firewall\ListenerInterface $listeners = array(...); $exceptionlistener = new ExceptionListener(...); $map->add($requestmatcher, $listeners, $exceptionlistener); Le plan du pare-feu sera donné au pare-feu comme premier argument, accompagné de l'event dispatcher qui est utilisé par la classe HttpKernel Listing - use Symfony\Component\Security\Http\Firewall; use Symfony\Component\HttpKernel\KernelEvents; // the EventDispatcher used by the HttpKernel $dispatcher =...; $firewall = new Firewall($map, $dispatcher); $dispatcher->addlistener(kernelevents::request, array($firewall, 'onkernelrequest'); Le pare-feu est enregistré pour écouter l'évènement kernel.request qui sera dispatché par l'httpkernel au début de chaque requête initiée. De cette façon, le pare-feu peut éviter que l'utilisateur aille plus loin que ce qui est autorisé. Les écouteurs du pare-feu Lorsque le pare-feu est notifié par l'évènement kernel.request, il demande au plan du pare-feu si une requête correspond à l'une des zones sécurisées. La première zone sécurisée correspondant à cette requête retournera un ensemble d'écouteurs correspondants aux écouteurs du pare-feu (ceux qui implémentent ListenerInterface ). Il est demandé à ces écouteurs de gérer la requête courante. Ceci signifie que : il faut trouver si la requête courante pourrait contenir des informations permettant d'authentifier l'utilisateur (par exemple l'écouteur d'authentification HTTP basique vérifie si la requête contient l'entête HTTP PHP_AUTH_USER);. http://api.symfony.com/./symfony/component/httpkernel/httpkernel.html. http://api.symfony.com/./symfony/component/security/http/firewall/listenerinterface.html generated on September, 0 Chapter : Le Pare-Feu et le Contexte de Sécurité
L'écouteur d'exception Si l'un des écouteurs jète une AuthenticationException, l'écouteur d'exception qui a été donné lorsque les zones sécurisées ont été déclarées prendra la main. L'écouteur d'exception détermine ce qu'il va se passer ensuite, basé sur les arguments reçus lorsque l'exception a été créée. Cet écouteur pourrait démarrer la procédure d'authentification, peut-être demander à l'utilisateur de fournir ses identifiants à nouveau (lorsqu'il a été authentifié uniquement grâce au cookie "remember-me"), ou transformer l'exception en une exception de type AccessDeniedHttpException, qui se soldera éventuellement par une réponse "HTTP/. 0: Access Denied". Points d'entrés Lorsqu'un utilisateur n'est pas du tout authentifié (i.e. lorsque le contexte de sécurité n'a pas encore de jeton), le point d'entrée du pare-feu sera appelé pour "commencer" le processus d'authentification. Un point d'entrée doit implémenter l'interface AuthenticationEntryPointInterface, qui ne possède qu'une méthode start(). Cette méthode reçoit l'objet Request courant et l'exception par laquelle l'écouteur a été déclenchée. La méthode devrait retourner un objet de type Request. Cela pourait être, par exemple, la page contenant le formulaire de login ou, dans le cas d'une authentification HTTP basique, une réponse avec un en-tête WWW-Authenticate, qui invitera l'utilisateur a fournir son nom d'utilisateur et son mot de passe. Flux : Pare-feu, Authentification, Authorisation Vous devriez maintenant pouvoir cerner plus facilement le fonctionnement global du contexte de sécurité :. Le pare-feu est enregistré comme écouteur de l'évènement kernel.request;. le pare-feu vérifie le plan du pare-feu afin de déterminer si un pare-feu doit être activé pour cette URL;. Si un pare-feu est trouvé dans le plan pour cette URL, les écouteurs sont notifiés;. Chaque écouteur vérifie si la requête courante contient des informations d'authentification - un écouteur devra soit (a) authentifier un utilisateur, (b) jeter une AuthenticationException, ou (c) ne rien faire (parce qu'il n'y a pas d'informatuon d'authentification dans la requête);. Une fois l'utilisateur authentifié, vous utiliserez Autorisation pour refuser l'accès à certaines ressources. Lisez les prochaines sections pour en savoir plus sur l'authentification et l'autorisation.. http://api.symfony.com/./symfony/component/security/core/exception/authenticationexception.html. http://api.symfony.com/./symfony/component/httpkernel/exception/accessdeniedhttpexception.html. http://api.symfony.com/./symfony/component/security/http/entrypoint/authenticationentrypointinterface.html. http://api.symfony.com/./symfony/component/security/http/entrypoint/authenticationentrypointinterface.html#start(). http://api.symfony.com/./symfony/component/httpfoundation/request.html. http://api.symfony.com/./symfony/component/httpfoundation/request.html generated on September, 0 Chapter : Le Pare-Feu et le Contexte de Sécurité
Chapter Authentification Lorsqu'une requête pointe sur une zone sécurisée, et qu'un des écouteurs du plan du pare-feu est capable d'extraire les identifiants d'un utilisateur depuis l'objet Request courant, il doit créer un jeton, contenant ces identifiants. Ensuite, l'écouteur doit demander au gestionnaire d'authentification de valider le jeton donné, et retourner un jeton authentifié si les identifiants fournis ont pu être validés. L'écouteur devrait ensuite enregistrer le jeton authentifié dans le contexte de sécurité Listing - 0 0 use Symfony\Component\Security\Http\Firewall\ListenerInterface; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; class SomeAuthenticationListener implements ListenerInterface /** * @var SecurityContextInterface */ private $securitycontext; /** * @var AuthenticationManagerInterface */ private $authenticationmanager; /** * @var string Uniquely identifies the secured area */ private $providerkey; //... public function handle(getresponseevent $event) $request = $event->getrequest();. http://api.symfony.com/./symfony/component/httpfoundation/request.html generated on September, 0 Chapter : Authentification
0 0 $username =...; $password =...; $unauthenticatedtoken = new UsernamePasswordToken( $username, $password, $this->providerkey ); $authenticatedtoken = $this ->authenticationmanager ->authenticate($unauthenticatedtoken); $this->securitycontext->settoken($authenticatedtoken); Un jeton peut provenir de n'importe quelle classe, du moment qu'elle implémente l'interface TokenInterface. Le gestionnaire d'authentification Le gestionnaire d'authentification par défaut est une instance de AuthenticationProviderManager : Listing - 0 use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; // instances de Symfony\Component\Security\Core\Authentication\AuthenticationProviderInterface $providers = array(...); $authenticationmanager = new AuthenticationProviderManager($providers); try $authenticatedtoken = $authenticationmanager ->authenticate($unauthenticatedtoken); catch (AuthenticationException $failed) // authentification a échoué L'AuthenticationProviderManager, lorsqu'il est instancié, reçoit un certain nombre de fournisseurs d'authentification ("providers" en anglais), chacun supportant différents types de jetons. Vous pouvez bien sûr écrire votre propre gestionnaire d'authentification, celui-ci doit simplement implémenter l'interface AuthenticationManagerInterface.. http://api.symfony.com/./symfony/component/security/core/authentication/token/tokeninterface.html. http://api.symfony.com/./symfony/component/security/core/authentication/authenticationprovidermanager.html. http://api.symfony.com/./symfony/component/security/core/authentication/authenticationmanagerinterface.html generated on September, 0 Chapter : Authentification
Fournisseurs d'authentification Chaque fournisseur (puisqu'il implémente l'interface AuthenticationProviderInterface ) possède une méthode supports() par lequel le AuthenticationProviderManager peut déterminer si le jeton fournis est supporté. Si c'est le cas, le gestionnaire appelle ensuite la méthode AuthenticationProviderInterface::authenticate du fournisseur. Cette méthode devrait retourner un jeton authentifié ou jeter une AuthenticationException (ou toute autre exception qui l'étendrait). Authentifier les utilisateurs par leur nom d'utilisateur et leur mot de passe Un fournisseur d'authentification tentera d'authentifier un utilisateur en se basant sur les identifiants fournis. Habituellement, ces derniers sont un nom d'utilisateur ainsi qu'un "hash" du mot de passe combiné à un salt généré aléatoirement. Cela signifie que l'authentification consiste en général à récupérer le salt ainsi que le mot de passe "hashé" de l'utilisateur depuis le stockage de données, "hashe" le mot de passe qu'il vient tout juste de fournir (en utilisant un formulaire de login par exemple) et de comparer les deux pour déterminer si le mot de passe donné est valide. Cette fonctionnalité est offerte par la classe DaoAuthenticationProvider. Le fournisseur récupère les informations de l'utilisateur depuis l'interface UserProviderInterface 0, utilise un PasswordEncoderInterface pour créer un hash du mot de passe et retourne un jeton authentifié si le mot de passe est valide Listing - 0 0 use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\User\UserChecker; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\Encoder\EncoderFactory; $userprovider = new InMemoryUserProvider( array( 'admin' => array( // le mot de passe est "foo" 'password' => 'FZZQIkAUTZBYkoC+GsReLfmSKDsfodsLYQt+aEWoaircfMpmaLbPBhFOBiiFyLfuZmTSUwzZg==', 'roles' => array('role_admin'), ), ) ); // pour certains contrôles supplémentaires : est-ce que le compte est activé, bloqué, expiré etc.? $userchecker = new UserChecker(); // un tableau d'encodeurs (voir ci-dessous) $encoderfactory = new EncoderFactory(...); $provider = new DaoAuthenticationProvider( $userprovider, $userchecker, 'secured_area',. http://api.symfony.com/./symfony/component/security/core/authentication/provider/authenticationproviderinterface.html. http://api.symfony.com/./symfony/component/security/core/authentication/provider/authenticationproviderinterface.html#supports(). http://api.symfony.com/./symfony/component/security/core/authentication/provider/ AuthenticationProviderInterface::authenticate.html. http://api.symfony.com/./symfony/component/security/core/exception/authenticationexception.html. http://api.symfony.com/./symfony/component/security/core/authentication/provider/daoauthenticationprovider.html 0. http://api.symfony.com/./symfony/component/security/core/user/userproviderinterface.html. http://api.symfony.com/./symfony/component/security/core/encoder/passwordencoderinterface.html generated on September, 0 Chapter : Authentification
); $encoderfactory $provider->authenticate($unauthenticatedtoken); L'exemple ci-dessus explique l'utilisation du fournisseur d'utilisateur "in-memory", mais vous pouvez utiliser n'importe quel fournisseur d'utilisateur, du moment qu'il implémente l'interface UserProviderInterface. Il est également possible de laisser plusieurs fournisseurs d'utilisateurs essayer de trouver les informations de l'utilisateur, en employant le ChainUserProvider. La fabrique d'encodeur de mots de passes La classe DaoAuthenticationProvider utilise une fabrique d'encodeur pour créer un encodeur de mots de passes pour un type donné d'utilisateur. Cela vous permet d'utiliser différentes stratégies d'encodage pour différents types d'utilisateurs. Par défaut, la classe EncoderFactory reçoit un tableau d'encodeurs Listing - 0 use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; $defaultencoder = new MessageDigestPasswordEncoder('sha', true, 000); $weakencoder = new MessageDigestPasswordEncoder('md', true, ); $encoders = array( 'Symfony\\Component\\Security\\Core\\User\\User' => $defaultencoder, 'Acme\\Entity\\LegacyUser' => $weakencoder, ); //... $encoderfactory = new EncoderFactory($encoders); Chaque encodeur devrait implémenter l'interface PasswordEncoderInterface ou être un tableau avec une class et une clé arguments, ce qui permet à l'usine d'encodeur de construire un encodeur seulement quand c'est nécessaire. Créer un encodeur de mots de passes personnalisé Il y a de nombreux encodeurs de mots de passes fournis avec le composant. Mais si vous avez besoin du votre, vous devez simplement suivre ces règles :. La classe doit implémenter l'interface PasswordEncoderInterface ;. $this->checkpasswordlength($raw); doit être la première instruction executée dans les méthodes encodepassword() et ispasswordvalid() (voir CVE-0-0 ).. http://api.symfony.com/./symfony/component/security/core/user/userproviderinterface.html. http://api.symfony.com/./symfony/component/security/core/user/chainuserprovider.html. http://api.symfony.com/./symfony/component/security/core/authentication/provider/daoauthenticationprovider.html. http://api.symfony.com/./symfony/component/security/core/encoder/encoderfactory.html. http://api.symfony.com/./symfony/component/security/core/encoder/passwordencoderinterface.html. http://api.symfony.com/./symfony/component/security/core/encoder/passwordencoderinterface.html. http://symfony.com/blog/cve-0-0-security-issue-in-fosuserbundle-login-form generated on September, 0 Chapter : Authentification 0
Utiliser les encodeurs de mots de passes Lorsque la méthode getencoder() de l'usine d'encodeur de mots de passes est appelée avec l'objet utilisateur comme premier argument, elle retournera un encodeur de type PasswordEncoderInterface 0 qui devrait être utilisé pour encoder le mot de passe de cet utilisateur Listing - 0 // fetch a user of type Acme\Entity\LegacyUser $user =... $encoder = $encoderfactory->getencoder($user); // retournera $weakencoder (voir plus haut) $encodedpassword = $encoder->encodepassword($password, $user->getsalt()); // vérifie si le mot de passe est valide : $validpassword = $encoder->ispasswordvalid( $user->getpassword(), $password, $user->getsalt());. http://api.symfony.com/./symfony/component/security/core/encoder/encoderfactory.html#getencoder() 0. http://api.symfony.com/./symfony/component/security/core/encoder/passwordencoderinterface.html generated on September, 0 Chapter : Authentification
Chapter Autorisation Lorsque n'importe quel fournisseur d'authenfication (consultez Fournisseurs d'authentification) a vérifié le jeton pas encore authentifié, un jeton authentifié sera retourné. L'écouteur d'authentification devrait initialiser le jeton directement dans SecurityContextInterface en utilisant sa méthode settoken(). A partir de là, l'utilisateur est authentifié, i.e. identifié. Maintenant, les autres parties de l'application peuvent utiliser le jeton pour décider si oui ou non l'utilisateur peut demander une certaine URI, ou modifier un objet. Cette décision sera prise par une instance de la classe AccessDecisionManagerInterface. Une décision d'autorisation sera toujours basée sur quelques éléments : Le jeton courant Par exemple, le jeton de la méthode getroles du jeton devrait être utilisée pour retrouver les rôles de l'utilisateur courant (par exemple ``ROLE_SUPER_ADMIN`()), ou une décision pourrait être basée sur la classe du jeton. Un ensemble d'attributs Chaque attribut représente les droits que l'utilisateur devrait avoir, par exemple ROLE_ADMIN pour être sûre que l'utilisateur est un administrateur. Un objet (optionnel) N'importe quel objet pour lequel les contrôles d'accès doivent être vérifiés, comme un article ou un objet commentaire. Gestionnaire de décision d'accès Le processus pour déterminer si oui ou non un utilisateur est autorisé à effectuer une certaine action pouvant être compliqué, le gestionnaire standard AccessDecisionManager dépend lui même de. http://api.symfony.com/./symfony/component/security/core/securitycontextinterface.html. http://api.symfony.com/./symfony/component/security/core/securitycontextinterface.html#method_settoken. http://api.symfony.com/./symfony/component/security/core/authorization/accessdecisionmanagerinterface.html. http://api.symfony.com/./symfony/component/security/core/authorization/accessdecisionmanager.html generated on September, 0 Chapter : Autorisation
plusieurs voteurs ("voters" en anglais) et rend un verdict final basé sur tous les votes (soit positif, négatif ou neutre) qu'il a reçu. Il reconnait plusieurs stratégies : affirmatif (par défaut) consensus unanime donne accès dès que n'importe quel voteur retourne une réponse affirmative; donne accès s'il y a plus de voteurs donnant accès qu'il y a le refusant; ne donne accès que si aucun des voteurs n'a refusé l'accès; Listing - 0 0 use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; // instances de Symfony\Component\Security\Core\Authorization\Voter\VoterInterface $voters = array(...); // l'un des suivants : "affirmatif", "consensus", "unanime" $strategy =...; // donne accès ou non, si tous les voteurs s'abstiennent $allowifallabstaindecisions =...; // donne accès ou non lorsqu'il y a une majorité (ne s'applique qu'à la stratégie "consensus") $allowifequalgranteddenieddecisions =...; $accessdecisionmanager = new AccessDecisionManager( $voters, $strategy, $allowifallabstaindecisions, $allowifequalgranteddenieddecisions ); Vous pouvez changer la stratégie par défaut en configuration. Voteurs Les voteurs sont des instances de l'interface VoterInterface, ce qui signifie qu'ils doivent implémenter quelques méthodes permettant au gestionnaire de décision de les utiliser : supportsattribute($attribute) sera utilisé pour vérifier si le voteur sait comment traiter les attributs donnés; supportsclass($class) sera utilisé pour vérifier si le voteur est capable de donner accès ou le refuser pour un objet d'une classé donnée;. http://api.symfony.com/./symfony/component/security/core/authorization/voter/voterinterface.html generated on September, 0 Chapter : Autorisation
vote(tokeninterface $token, $object, array $attributes) cette méthode se charge du fameux vote et retour une valeur égale à l'une des constantes de la classe VoterInterface, i.e. VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED ou VoterInterface::ACCESS_ABSTAIN; Le composant de sécurité contient quelques voteur standards couvrants de nombreuses cas d'utilisation : AuthenticatedVoter Le voteur AuthenticatedVoter supporte les attributs IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, et IS_AUTHENTICATED_ANONYMOUSLY. Il se charge de donner les accès en se basant sur le niveau courant d'authentification, i.e. est-ce que l'utilisateur est complètement authentifié ou est-ce qu'il est authentifié grâce au cookie "se souvenir de moi", ou est-ce qu'il est authentifié anonymement? Listing - 0 use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; $anonymousclass = 'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken'; $remembermeclass = 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken'; $trustresolver = new AuthenticationTrustResolver($anonymousClass, $remembermeclass); $authenticatedvoter = new AuthenticatedVoter($trustResolver); // instance de Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token =...; // n'importe quel objet $object =...; $vote = $authenticatedvoter->vote($token, $object, array('is_authenticated_fully')); RoleVoter La classe RoleVoter supporte les attributs commençants par ROLE_ et donne accès à l'utilisateur lorsque l'attribut requis ROLE_* peut être retrouvé dans le tableau des rôles retourné par la méthode getroles() du jeton Listing - use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; $rolevoter = new RoleVoter('ROLE_'); $rolevoter->vote($token, $object, 'ROLE_ADMIN'); RoleHierarchyVoter La classe RoleHierarchyVoter 0 étend la classe RoleVoter et fournit quelques fonctionnalités supplémentaires : elle sait comment traiter la hierarchie des rôles. Par exemple, un rôle. http://api.symfony.com/./symfony/component/security/core/authorization/voter/voterinterface.html. http://api.symfony.com/./symfony/component/security/core/authorization/voter/authenticatedvoter.html. http://api.symfony.com/./symfony/component/security/core/authorization/voter/rolevoter.html. http://api.symfony.com/./symfony/component/security/core/authentication/token/tokeninterface.html#method_getroles 0. http://api.symfony.com/./symfony/component/security/core/authorization/voter/rolehierarchyvoter.html. http://api.symfony.com/./symfony/component/security/core/authorization/voter/rolevoter.html generated on September, 0 Chapter : Autorisation
ROLE_SUPER_ADMIN peut avoir les sous-rôles ROLE_ADMIN et ROLE_USER, ainsi dans le cas ou un objet requiert que l'utilisateur ait le rôle ROLE_ADMIN, l'accès est donné aux utilisateurs qui ont en fait le rôle ROLE_ADMIN, mais également au utilisateurs aillant de le rôle ROLE_SUPER_ADMIN Listing - 0 use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; use Symfony\Component\Security\Core\Role\RoleHierarchy; $hierarchy = array( 'ROLE_SUPER_ADMIN' => array('role_admin', 'ROLE_USER'), ); $rolehierarchy = new RoleHierarchy($hierarchy); $rolehierarchyvoter = new RoleHierarchyVoter($roleHierarchy); Lorsque vous faites votre propre voteur, vous devriez bien évidemment injecter dans le constructeur de celui-ci toutes les dépendances nécessaire pour une prise de décision. Les Rôles Les rôles sont des objets exprimant un certain droit qu'un utilisateur possède. Le seul prérequis est que l'objet implémente l'interface RoleInterface, ce qui signifie qu'il doit avoir une méthode getrole() qui retourne une chaîne de caractères représentant le rôle lui-même. La classe par défaut Role retourne simplement son premier argument de constructeur Listing - use Symfony\Component\Security\Core\Role\Role; $role = new Role('ROLE_ADMIN'); // va afficher 'ROLE_ADMIN' echo $role->getrole(); La majorité des jetons d'authentification étendent la classe AbstractToken, ce qui signifie que les rôles donnés à leur constructeur seront automatiquement convertis d'une chaîne de caractères à ces objets simples Role. Utiliser le gestionnaire de décision L'écouteur d'accès Le gestionnaire de décision d'accès peut être utilisé à n'importe quel moment dans une requête pour décider si oui ou non l'utilisateur courant peut avoir accès une ressource donnée. Une méthode optionnelle, mais utile, pour restreindre l'accès en se basant sur le motif d'url est la classe AccessListener, qui est l'un des écouteurs pare-feu (consultez Les écouteurs du pare-feu) qui est. http://api.symfony.com/./symfony/component/security/core/role/roleinterface.html. http://api.symfony.com/./symfony/component/security/core/role/role/roleinterface.html#method_getrole. http://api.symfony.com/./symfony/component/security/core/role/role.html. http://api.symfony.com/./symfony/component/security/core/authentication/token/abstracttoken.html generated on September, 0 Chapter : Autorisation
déclenché pour chaque requête correspondante au plan du pare-feu (consultez Un Pare-Feu pour les requêtes HTTP). Il utilise un plan d'accès (qui devrait être du type AccessMapInterface ) contenant les "request matchers" et une liste d'attributs requis pour l'utilisateur courant pour récupérer l'accès à l'application Listing - 0 use Symfony\Component\Security\Http\AccessMap; use Symfony\Component\HttpFoundation\RequestMatcher; use Symfony\Component\Security\Http\Firewall\AccessListener; $accessmap = new AccessMap(); $requestmatcher = new RequestMatcher('^/admin'); $accessmap->add($requestmatcher, array('role_admin')); $accesslistener = new AccessListener( $securitycontext, $accessdecisionmanager, $accessmap, $authenticationmanager ); Le contexte de sécurité Le gestionnaire de décision d'accès est également disponible pour les autres parties de l'application via la méthode isgranted() de la classe SecurityContext. Un appel à cette méthode délèguera directement la question au gestionnaire de décision d'accès Listing - 0 use Symfony\Component\Security\SecurityContext; use Symfony\Component\Security\Core\Exception\AccessDeniedException; $securitycontext = new SecurityContext( $authenticationmanager, $accessdecisionmanager ); if (!$securitycontext->isgranted('role_admin')) throw new AccessDeniedException();. http://api.symfony.com/./symfony/component/security/http/firewall/accesslistener.html. http://api.symfony.com/./symfony/component/security/http/accessmapinterface.html. http://api.symfony.com/./symfony/component/security/core/securitycontext.html#method_isgranted. http://api.symfony.com/./symfony/component/security/core/securitycontext.html generated on September, 0 Chapter : Autorisation
Chapter Le Composant Serializer Le composant Serializer est destiné à être utilisé pour transformer des objets en un format spécifique (XML, JSON, Yaml,...) et inversement. Pour faire cela, le composant Serializer suit le schéma simple suivant. Comme vous pouvez le voir sur l'image ci-dessus, un tableau est utilisé comme intermédiaire. De cette manière, les encodeurs (Encoder) ne se chargeront que de transformer des formats spécifiques en tableaux et vice versa. De la même manière, les normaliseurs (Normalizer) ne transformeront que des objets en tableaux et vice versa. La sérialisation est un sujet complexe, et même si ce composant ne fonctionne pas pour tout les cas de figure, il peut être un outil très utile pour sérialiser ou désérialiser vos objets. Installation Vous pouvez installer le composant de différentes manières : generated on September, 0 Chapter : Le Composant Serializer
Utilisez le dépôt Git officiel (https://github.com/symfony/serializer ); Installez le via Composer (symfony/serializer sur Packagist ). Utilisation Utiliser le composant Serializer est très simple. Vous avez juste besoin de définir la classe Serializer pour spécifier quels encodeurs et quels normaliseurs seront disponibles: Listing - use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; $encoders = array(new XmlEncoder(), new JsonEncoder()); $normalizers = array(new GetSetMethodNormalizer()); $serializer = new Serializer($normalizers, $encoders); Sérialiser un objet Pour les besoins de cet exemple, supposons que la classe suivante existe déjà dans notre projet: Listing - 0 0 namespace Acme; class Person private $age; private $name; // Getters public function getname() return $this->name; public function getage() return $this->age; // Setters public function setname($name) $this->name = $name; public function setage($age) $this->age = $age;. https://github.com/symfony/serializer. https://packagist.org/packages/symfony/serializer. http://api.symfony.com/./symfony/component/serializer/serializer.html generated on September, 0 Chapter : Le Composant Serializer
Maintenant, si vous voulez sérialiser un objet en JSON, vous devez juste utiliser le service Serializer précédemment créé: Listing - $person = new Acme\Person(); $person->setname('foo'); $person->setage(); $serializer->serialize($person, 'json'); // Output: "name":"foo","age": Le premier paramètre de la méthode serialize() est l'objet à sérialiser et le second est utilisé pour choisir l'encodeur, dans notre cas JsonEncoder. Désérialiser un objet Voyons maintenant comment faire l'opération inverse. Cette fois, l'information de la classe People est encodée en format XML: Listing - $data = <<<EOF <person> <name>foo</name> <age></age> </person> EOF; $person = $serializer->deserialize($data,'acme\person','xml'); Dans ce cas, la méthode deserialize() nécessite trois paramètres :. L'information à décoder. Le nom de la classe dans laquelle sera décodée l'information. L'encodeur utilisé pour convertir l'information en tableau Changer les noms de méthode des attributs qui contiennent un underscore New in version.: La méthode GetSetMethodNormalizer::setCamelizedAttributes a été ajoutée dans Symfony.. Parfois, les noms de propriétés du contenu sérialisé contiennent un underscore (par exemple first_name). Normalement, ces attributs utiliseront des getters et setters comme getfirst_name, alors que la bonne méthode est getfirstname. Pour changer ce comportement, utilisez la méthode setcamelizedattributes() dans la définition du normaliseur: Listing - $encoder = new JsonEncoder(); $normalizer = new GetSetMethodNormalizer(); $normalizer->setcamelizedattributes(array('first_name')); $serializer = new Serializer(array($normalizer), array($encoder)); $json = <<<EOT. http://api.symfony.com/./symfony/component/serializer/serializer.html#serialize(). http://api.symfony.com/./symfony/component/serializer/encoder/jsonencoder.html. http://api.symfony.com/./symfony/component/serializer/serializer.html#deserialize(). http://api.symfony.com/./symfony/component/serializer/normalizer/getsetmethodnormalizer.html#setcamelizedattributes(). http://api.symfony.com/./symfony/component/serializer/normalizer/getsetmethodnormalizer.html#setcamelizedattributes() generated on September, 0 Chapter : Le Composant Serializer
0 "name": "foo", "age": "", "first_name": "bar" EOT; $person = $serializer->deserialize($json, 'Acme\Person', 'json'); Comme résultat final, le désérialiseur utilise l'attribut first_name comme s'il était firstname et utilise les méthodes getfirstname et setfirstname. JMSSerializationBundle Il existe un bundle tierce populaire, JMSSerializationBundle, qui étend (et parfois remplace) la fonctionnalité de sérialisation. Cela inclut la capacité de configurer la manière dont vos objets doivent être sérialisée et désérialisés via des annotations (ou YML, XML et PHP), l'intégration avec l'orm Doctrine, et la prise en charge de cas de figure plus complexes (par exemple les références circulaires).. https://github.com/schmittjoh/jmsserializerbundle generated on September, 0 Chapter : Le Composant Serializer 0
Chapter 0 Le Composant Templating Le Composant Templating fournit tous les outils nécessaires pour construire n'importe quel système de templates. Il fournit une infrastructure qui charge des fichiers de template et qui, optionnellement, surveille s'ils changent. Il apporte aussi une implémentation concrète d'un moteur de template utilisant PHP avec des outils additionnels pour séparer les templates en blocs et couches. Installation Vous pouvez installer le composant de différentes manières : Utilisez le dépôt Git officiel (https://github.com/symfony/templating ) ; Installez le via Composer (symfony/templating sur Packagist ). Utilisation La classe PhpEngine est le point d'entrée du composant. Elle a besoin d'un analyseur de nom de template (TemplateNameParserInterface ) pour convertir un nom de template en une référence de template et d'un chargeur de template (LoaderInterface ) pour trouver le template associé à une référence: Listing 0- use Symfony\Component\Templating\PhpEngine; use Symfony\Component\Templating\TemplateNameParser; use Symfony\Component\Templating\Loader\FilesystemLoader;. https://github.com/symfony/templating. https://packagist.org/packages/symfony/templating. http://api.symfony.com/./symfony/component/templating/phpengine.html. http://api.symfony.com/./symfony/component/templating/templatenameparserinterface.html. http://api.symfony.com/./symfony/component/templating/loader/loaderinterface.html generated on September, 0 Chapter 0: Le Composant Templating
$loader = new FilesystemLoader( DIR. '/views/%name%'); $view = new PhpEngine(new TemplateNameParser(), $loader); echo $view->render('hello.php', array('firstname' => 'Fabien')); La méthode render() exécute le fichier views/hello.php et retourne le texte de sortie. Listing 0- <!-- views/hello.php --> Hello, <?php echo $firstname?>! Héritage de template avec les slots L'héritage de template est structuré de manière à partager les couches avec de nombreux templates. Listing 0- <!-- views/layout.php --> <html> <head> <title><?php $view['slots']->output('title', 'Default title')?></title> </head> <body> <?php $view['slots']->output('_content')?> </body> </html> La méthode extend() est appelée dans le sous-template pour définir son template parent. Listing 0-0 <!-- views/page.php --> <?php $view->extend('layout.php')?> <?php $view['slots']->set('title', $page->title)?> <h> <?php echo $page->title?> </h> <p> <?php echo $page->body?> </p> Pour utiliser l'héritage de template, la classe SlotsHelper doit être déclarée: Listing 0- use Symfony\Component\Templating\Helper\SlotsHelper; $view->set(new SlotsHelper()); // récupère l'objet $page echo $view->render('page.php', array('page' => $page));. http://api.symfony.com/./symfony/component/templating/phpengine.html#render(). http://api.symfony.com/./symfony/component/templating/phpengine.html#extend(). http://api.symfony.com/./symfony/component/templating/helper/slotshelper.html generated on September, 0 Chapter 0: Le Composant Templating
Avoir de multiples niveaux d'héritage est possible : une couche peut étendre une autre couche. Échappement en sortie Cette partie de la documentation est toujours en cours d'écriture. La classe d'aide «Asset» Cette partie de la documentation est toujours en cours d'écriture. generated on September, 0 Chapter 0: Le Composant Templating
Chapter Le Composant YAML Le Composant YAML charge et effectue un «dump» de fichiers YAML. Qu'est ce que c'est? Le Composant YAML de Symfony analyse des chaînes de caractères YAML afin de les convertir en tableaux PHP. Il est aussi capable de convertir des tableaux PHP en chaînes de caractères YAML. YAML, YAML Ain't Markup Language (litéralement : «YAML n'est pas un langage de balises» en français), est un standard de sérialisation de données facilement compréhensible par l'humain pour tous les langages de programmation. YAML est un format génial pour vos fichiers de configuration. Les fichiers YAML sont aussi parlant que des fichiers XML et aussi lisibles que des fichiers INI. Le Composant YAML de Symfony implémente la version. de la spécification YAML. Apprenez-en plus sur le composant Yaml en lisant l'article Le Format YAML. Installation Vous pouvez l'installer de deux manières différentes: Installez le via Composer (symfony/yaml on Packagist ); Utilisez le dépôt Git officiel (https://github.com/symfony/yaml ).. http://yaml.org/. https://packagist.org/packages/symfony/yaml. https://github.com/symfony/yaml generated on September, 0 Chapter : Le Composant YAML
Pourquoi? Rapide L'un des buts de Symfony YAML est de trouver le bon équilibre entre vitesse et fonctionnalités. Il supporte juste les fonctionnalités nécessaires à la gestion des fichiers de configuration. Un analyseur réel Il arbore un analyseur réel et est capable d'analyser («parser») un sous-ensemble important de la spécification YAML, pour tous vos besoins de configuration. Cela signifie aussi que l'analyseur est particulièrement robuste, facile à comprendre, et suffisamment simple à étendre. Des messages d'erreur clairs Chaque fois que vous avez un problème de syntaxe avec vos fichiers YAML, la bibliothèque affiche un message utile avec le nom du fichier et le numéro de la ligne où le problème est apparu. Cela facilite énormément le débuggage. Support du «Dump» Le composant est aussi capable de dumper des tableaux PHP en YAML avec le support d'objet et de configuration directement dans le code afin d'avoir un «bel» affichage. Support des Types Il supporte la plupart des types YAML intégrés comme les dates, les entiers, les octaux, les booléens, et plus encore... Support complet de la fusion par clé Support complet des références, des alias, et de la fusion par clé. Ne vous répétez pas en référençant les bouts de configuration communs. Utiliser le Composant YAML de Symfony Le Composant YAML de Symfony est très simple et consiste en deux classes principales : l'une analyse les chaînes de caractères YAML (Parser ), et l'autre affiche une chaîne de caractères YAML à partir d'un tableau PHP (Dumper ). Au-dessus de ces deux classes, la classe Yaml agit comme une fine couche supplémentaire qui simplifie les usages communs. Lire des fichiers YAML La méthode parse() analyse une chaîne de caractères YAML et la convertit en un tableau PHP : Listing -. http://api.symfony.com/./symfony/component/yaml/parser.html. http://api.symfony.com/./symfony/component/yaml/dumper.html. http://api.symfony.com/./symfony/component/yaml/yaml.html. http://api.symfony.com/./symfony/component/yaml/parser.html#parse() generated on September, 0 Chapter : Le Composant YAML
use Symfony\Component\Yaml\Parser; $yaml = new Parser(); $value = $yaml->parse(file_get_contents('/path/to/file.yml')); Si une erreur se produit durant l'analyse, l'analyseur lance une exception ParseException indiquant le type d'erreur et la ligne de la chaîne de caractères YAML où l'erreur est apparue : Listing - use Symfony\Component\Yaml\Exception\ParseException; try $value = $yaml->parse(file_get_contents('/path/to/file.yml')); catch (ParseException $e) printf("unable to parse the YAML string: %s", $e->getmessage()); Comme l'analyseur est «réutilisable», vous pouvez utiliser le même objet analyseur pour charger différentes chaînes de caractères YAML. Lorsque vous chargez un fichier YAML, il est parfois mieux d'utiliser la méthode de surcouche parse() : Listing - use Symfony\Component\Yaml\Yaml; $yaml = Yaml::parse(file_get_contents('/path/to/file.yml')); La méthode statique parse() 0 prend une chaîne de caractères YAML ou un fichier contenant du YAML. En interne, elle appelle la méthode parse(), mais elle met en valeur les erreurs si quelque chose se passe mal en ajoutant le nom du fichier au message. Parce qu'il est maintenant possible de passer un nom de fichier à cette méthode, vous devez d'abord valider la saisie. Passer un nom de fichier déprécié dans Symfony., et sera supprimé dans Symfony.0. Écrire des fichiers YAML La méthode dump() affiche n'importe quel tableau PHP en sa représentation YAML : Listing - use Symfony\Component\Yaml\Dumper; $array = array( 'foo' => 'bar', 'bar' => array('foo' => 'bar', 'bar' => 'baz'), );. http://api.symfony.com/./symfony/component/yaml/exception/parseexception.html. http://api.symfony.com/./symfony/component/yaml/yaml.html#parse() 0. http://api.symfony.com/./symfony/component/yaml/yaml.html#parse(). http://api.symfony.com/./symfony/component/yaml/parser.html#parse(). http://api.symfony.com/./symfony/component/yaml/dumper.html#dump() generated on September, 0 Chapter : Le Composant YAML
0 $dumper = new Dumper(); $yaml = $dumper->dump($array); file_put_contents('/path/to/file.yml', $yaml); Bien sûr, le «dumper» YAML de Symfony n'est pas capable d'afficher des ressources. Aussi, même si le «dumper» est capable d'afficher des objets PHP, cela est considéré comme une fonctionnalité non supportée. Si une erreur intervient durant l'affichage, l'analyseur lance une exception DumpException. Si vous avez seulement besoin d'afficher un tableau, vous pouvez utiliser la méthode statique dump() : Listing - use Symfony\Component\Yaml\Yaml; $yaml = Yaml::dump($array, $inline); Le format YAML supporte deux sortes de représentation pour les tableaux : celle étendue, et celle «en ligne» («inline» en anglais). Par défaut, l'afficheur utilise la représentation «en ligne» : Listing - foo: bar, bar: foo: bar, bar: baz Le second argument de la méthode dump() personnalise le niveau à partir duquel le rendu passe de la représentation étendue à celle «en ligne» : Listing - echo $dumper->dump($array, ); Listing - foo: bar bar: foo: bar, bar: baz Listing - echo $dumper->dump($array, ); Listing -0 foo: bar bar: foo: bar bar: baz. http://api.symfony.com/./symfony/component/yaml/exception/dumpexception.html. http://api.symfony.com/./symfony/component/yaml/yaml.html#dump(). http://api.symfony.com/./symfony/component/yaml/dumper.html#dump() generated on September, 0 Chapter : Le Composant YAML
Chapter Le Format YAML Conformément au site web officiel YAML, YAML est «un standard de sérialisation de données compréhensible facilement par l'humain pour tous les langages de programmation». Même si le format YAML peut décrire une structure de données imbriquée complexe, ce chapitre décrit uniquement l'ensemble de fonctionnalités minimal nécessaire pour utiliser YAML en tant que format de fichier de configuration. YAML est un langage simple qui décrit des données. Comme PHP, il possède une syntaxe pour les types simples comme les chaînes de caractères, les booléens, les nombres à virgule flottante, ou les entiers. Mais contrairement à PHP, il fait une différence entre les tableaux (séquences) et les «hashes» (correspondances clé-valeur). Scalaire La syntaxe pour les scalaires est similaire à la syntaxe PHP. Chaînes de caractères Listing - Une chaîne de caractères en YAML Listing - 'Une chaîne de caractères entourée par des guillemets simples en YAML'. http://yaml.org/ generated on September, 0 Chapter : Le Format YAML
Dans une chaîne de caractères entourée par des guillemets simples, un guillemet simple ' doit être doublé : Listing - 'Un guillemet simple '' dans une chaîne de caractères entourée par des guillemets simples' Listing - "Une chaîne de caractères entourée par des guillemets doubles en YAML\n" Les guillemets doubles sont utiles lorsqu'une chaîne de caractères commence ou se termine avec un ou plusieurs espaces significatifs. Le style avec doubles guillemets fournit une manière d'exprimer des chaînes de caractères arbitraires, en utilisant des séquences d'échappement \. Ceci est très utile lorsque vous avez besoin d'intégrer un \n ou un caractère unicode dans une chaîne de caractères. Lorsqu'une chaîne de caractères contient un retour à la ligne, vous pouvez utiliser le style littéral, indiqué par un séparateur vertical ( ), pour indiquer que la chaîne de caractères va s'étendre sur plusieurs lignes. Avec le style de citation littéral, les nouvelles lignes sont préservées : Listing - \/ / \/ / / Autrement, les chaînes de caractères peuvent être écrites avec le style de citation dit «plié», indiqué par le caractère >, où chaque retour à la ligne est remplacé par un espace : Listing - > Voici une très longue phrase qui est découpée en plusieurs lignes en YAML mais qui sera affichée comme une chaine de caractères sans retours à la ligne. Notez les deux espaces avant chaque ligne dans l'exemple précédent. Ils ne vont pas apparaître dans les chaînes de caractères PHP résultantes. Nombres Listing - # un entier Listing - # un octal 0 Listing - generated on September, 0 Chapter : Le Format YAML
# un hexadécimal 0xC Listing -0 # un nombre à virgule flottante. Listing - # un nombre exponentiel.e+ Listing - # l'infini.inf Nuls Les valeurs nulles en YAML peuvent être exprimées grâce à null ou à ~. Booléens Les booléens en YAML sont exprimés via true et false. Dates YAML utilise le standard ISO-0 pour exprimer les dates : Listing - 00--t::.0-0:00 Listing - # une date simple 00-- generated on September, 0 Chapter : Le Format YAML 0
Chapter Collections Un fichier YAML est rarement utilisé pour décrire un simple scalaire. La plupart du temps, il décrit une collection. Une collection peut être une séquence ou une correspondance d'éléments. Les deux sont converties en tableaux PHP. Les séquences utilisent un tiret suivi d'un espace : Listing - - PHP - Perl - Python Le fichier YAML précédent est équivalent au code PHP suivant : Listing - array('php', 'Perl', 'Python'); Les correspondances utilisent un deux-points (: ) suivi d'un espace pour marquer chaque paire clé/valeur : Listing - PHP:. MySQL:. Apache:..0 qui est équivalent au code PHP suivant : Listing - array('php' =>., 'MySQL' =>., 'Apache' => '..0'); Dans une correspondance, une clé peut être n'importe quel scalaire valide. Le nombre d'espaces entre les deux-points et la valeur n'a pas d'importance : Listing - generated on September, 0 Chapter : Collections
PHP:. MySQL:. Apache:..0 YAML utilise l'indentation avec un ou plusieurs espaces pour décrire les collections imbriquées : Listing -0 "symfony.0": PHP:.0 Propel:. "symfony.": PHP:. Propel:. Le YAML précédent est équivalent au code PHP suivant : Listing - 0 array( 'symfony.0' => array( 'PHP' =>.0, 'Propel' =>., ), 'symfony.' => array( 'PHP' =>., 'Propel' =>., ), ); Il y a une chose importante que vous devez vous rappeler lorsque vous utilisez l'indentation dans un fichier YAML : L'indentation doit être faite avec un ou plusieurs espaces, mais jamais avec des tabulations. Vous pouvez imbriquer des séquences et des correspondances comme vous le voulez : Listing - 'Chapter ': - Introduction - Event Types 'Chapter ': - Introduction - Helpers YAML peut aussi utiliser les styles dit de «flot» pour les collections, en utilisant des indicateurs explicites plutôt que l'indentation pour représenter la portée. Une séquence peut être écrite comme une liste séparée par des virgules, le tout entre crochets ([]) : Listing - [PHP, Perl, Python] Une correspondance peut être écrite comme une liste de clés/valeurs séparée par des virgules, le tout entre accolades () : Listing - PHP:., MySQL:., Apache:..0 Vous pouvez mélanger et faire correspondre les styles afin d'obtenir une meilleure lisibilité : Listing - 'Chapter ': [Introduction, Event Types] 'Chapter ': [Introduction, Helpers] generated on September, 0 Chapter : Collections
Listing - "symfony.0": PHP:.0, Propel:. "symfony.": PHP:., Propel:. Chapter Commentaires Les commentaires peuvent être ajoutés en YAML en les préfixant avec un symbole dièse (#) : Listing - # commentaire sur une ligne "symfony.0": PHP:.0, Propel:. # commentaire à la fin d'une ligne "symfony.": PHP:., Propel:. Les commentaires sont simplement ignorés par l'analyseur YAML et ne doivent pas être indentés par rapport au niveau courant d'imbrication dans une collection. generated on September, 0 Chapter : Commentaires