Au Coeur de Zend Framework 2

317
Au cœur de Zend Framework 2 © 2012 Blanchon Vincent. Tous droits réservés. ISBN 978-1-4716-4809-0

Transcript of Au Coeur de Zend Framework 2

  • Au cur de Zend Framework 2

    2012 Blanchon Vincent. Tous droits rservs. ISBN 978-1-4716-4809-0

  • Prface

    Je me souviens encore de mon premier site internet. Javais dix ans et, lpoque, FrontPage tait mon meilleur alli. Peu importe quil produise du code non valide, quil soit compltement statique ou que son code soit vulnrable la moindre faille. Finalement, je mamusais bien et ctait le principal.

    videmment, le Web est devenu au fil des annes une industrie part entire, de plus en plus influente et aujourdhui, les dveloppeurs du monde entier le savent bien, il faut tre de plus en plus productif, crire du code toujours plus volutif, toujours plus performant, toujours plus scuris et toujours dans le mme laps de temps. cet gard, larrive des frameworks dans lunivers PHP a rvolutionn notre manire de penser et concevoir le Web. Et, surtout, il a contribu redon-ner une image crdible ce langage, bien trop souvent terni par les mmoires du vnrable PHP 4.

    sa sortie en juin 2007, la premire version stable du Zend Framework a large-ment contribu cet essor. Contrairement dautres frameworks, il nimposait rien, mais fournissait des briques rutilisables. Ce fut l sa grande force et lune des raisons de son succs. Depuis, de leau a coul. Le petit framework des dbuts a grossi, sest enrichi de nombreuses fonctionnalits et, douze versions majeures plus tard, il a atteint sa maturit mais, pour continuer voluer, il tait ncessaire de le repenser de zro.

    Cest dornavant chose faite, et cette rcriture complte du framework a, une fois de plus, t une vritable dmonstration de force de la communaut open-source, tant les chiffres donnent le tournis : plus de 200 contributeurs, plus de 1000 forks, prs de 14 000 tests unitaires crits et prs de 1800 pull requests. Le tout en un peu plus dun an. Ctait la premire fois que je participais une aventure open-source, en contribuant notamment largement au code du composant des formulaires, et je dois dire que ce fut une exprience trs formatrice. Il ny a pas de petites contribu-tions, et que cela soit pour corriger un bogue, ajouter de la documentation ou crer une nouvelle fonctionnalit, cest grce laide de tous que notre outil de travail est ce quil est aujourdhui.

    ce propos, le livre que vous tenez entre les mains constitue la toute premire ressource francophone consacre ce nouveau framework, et Vincent a ralis un incroyable travail pour vous permettre non seulement de vous lancer dans l'ap-

  • prentissage de Zend Framework 2, mais galement pour y comprendre les rouages internes (vnements, injection de dpendances), indispensables pour tirer parti au maximum de ce framework la fois simple et complexe. Que vous soyez un actuel dveloppeur de ZF 1 ou que vous ayez dj une exprience avec ZF 2, cha-cun y trouvera son compte !

    Michal Gallego

    Contributeur au Zend Framework 2

    Administrateur du forum ddi la communaut franaise du Zend Framework

  • Table des matires

    Avant-propos 11 qui sadresse ce livre ? 11Structure de louvrage 12Remerciements 13

    Chapitre 1Le Zend Framework 15

    Le Zend Framework 1 15Le Zend Framework 2 15

    PHP 5.3.3 ......................................................................................................15Design pattern ..............................................................................................16Les axes damlioration ...............................................................................17

    Le squelette de lapplication 17

    Chapitre 2 Les autoloaders 19

    Lautoloader standard 19Le ClassMapAutoloader 24La classe ModuleAutoloader 28Linterface SplAutoloader 28

    Chapitre 3 Les vnements 31

    Prsentation des vnements 31Le gestionnaire partag 33Lvnement wildcard 39Le gestionnaire dvnements global 40Le type ListenerAggregate 40Crer ses vnements personnaliss 43Lobjet de rponse 46Stopper la propagation dun vnement 47Grer lordre des notifications 49Les vnements du framework 50

    Chapitre 4Linjection de dpendances 53

    Configuration manuelle du composant Di 54

  • Configuration automatique du composant Di 56Configuration semi-automatique du Di 59Le gestionnaire dinstances 61

    Chapitre 5Le gestionnaire de services 65

    La configuration du gestionnaire 65Mise en uvre 67Le ServiceManager complmentaire du Di 77

    La fabrique du Zend\ServiceManager\Di ..............................................77La fabrique abstraite du Zend\ServiceManager\Di ..............................79

    Implmentation dans le framework 80

    Chapitre 6Les modules 83

    Configuration et couteurs 83Chargement des modules 88

    Initialisation de lautoloader du module ...................................................96Initialisation du module ..............................................................................97Notification lors de linitialisation de lapplication .................................98Configuration du gestionnaire de services ...............................................99Nous remarquons que la cl service_manager est utilise pour rcu-prer linstance de lobjet tendant de ServiceManager qui sera alors configur. ................................................................................................... 102Initialisation des instances partages ..................................................... 102Mise jour de la configuration des modules ........................................ 102Traitement postchargement .................................................................... 104

    Rcapitulatif du processus 108Processus de chargement ........................................................................ 108Squelette de notre classe Module ........................................................... 109

    Chapitre 7Les configurations 111

    Accs la configuration 111Configuration par environnement 112

    Configuration par fichiers PHP .............................................................. 113Configuration par hritage de fichier ..................................................... 114Configuration par fichier INI ................................................................. 116Configuration recommande .................................................................. 117

    Chapitre 8Le router 119

  • Le router et la brique MVC 119Les routes Http\Literal 124Les routes Http\Regex 126Les routes Http\Scheme 127Les routes Http\Method 127Les routes Http\Hostname 128Les routes Http\Segment 129Les routes Http\Wildcard 130Les routes Http\Part 132Lcouteur pour les modules 133

    Chapitre 9Les contrleurs 137

    Le contrleur AbstractActionController 137Mthodes daction par dfaut ................................................................. 143Les interfaces du contrleur de base ..................................................... 144

    Le contrleur AbstractRestfulController148

    Chapitre 10Les aides daction 151

    Laide daction Forward 151Laide daction Redirect 153Laide daction Url 155Laide daction PostRedirectGet 156Cration dune aide daction 157

    Chapitre 11Le gestionnaire de plugins 159

    La classe de base 159Les gestionnaires du framework 161

    Chapitre 12Les vues 165

    Le gestionnaire de vues 165Prparation des vues 166Prparation des vues derreurs 168Rendu de la vue 173

    Construction des vues ............................................................................. 173Rendu des vues ......................................................................................... 177Rcapitulatif du rendu ............................................................................. 191

    Types de vue193

  • Manipulation des vues 195

    Chapitre 13Les aides de vue 199

    Le fil dAriane 200Le sitemap 205Laide Layout et ViewModel 207Laide Partial 208Le rendu dlment HTML 210Le rendu de JSON 211Laide Url 212Cration dune aide de vue213

    Chapitre 14 Le cur du framework 215

    Initialisation des autoloaders 215Le bootstrap 217Le run 221

    Chapitre 15Les composants 229

    Zend\Db 229Ladaptateur de base de donnes ........................................................... 229Labstraction SQL .................................................................................... 234Linterface TableGateway ........................................................................ 235Linterface RowGateway .......................................................................... 238

    Zend\Cache 241Les adaptateurs ......................................................................................... 241Les plugins ................................................................................................. 242Analyse des plugins .................................................................................. 243Le composant Zend\Cache\Pattern ..................................................... 246

    Zend\Console 248Utilisation basique de la console ............................................................ 248Les classes dinteractions ......................................................................... 250Labstraction de lenvironnement ........................................................... 253Le router pour la console ........................................................................ 254La vue pour la console ............................................................................. 257

    Zend\Crypt 259Hachage sens unique ............................................................................. 259Hachage symtrique ................................................................................. 260

    Zend\Form 261

  • Le formulaire, les fielsets et les lments .............................................. 261Les fabriques et hydrateurs ..................................................................... 262Le composant Zend\InputFilter ........................................................... 267Les aides de vue ........................................................................................ 270

    Zend\Mail 272Zend\Session 275Zend\Stdlib 278

    La classe AbstractOptions....................................................................... 278La classe ArrayUtils .................................................................................. 280La classe CallbackHandler ....................................................................... 281La classe ErrorHandler ............................................................................ 284La classe Glob ........................................................................................... 285Les hydrateurs ........................................................................................... 285

    Chapitre 16Les exceptions 291

    Chapitre 17 Cas dutilisation 293

    Concept et fonctionnalits 293Prsentation du projet .............................................................................. 293Les besoins du projet ............................................................................... 294

    Organisation et architecture 294Personnalisation du chargement de modules 296

    La restriction de chargement .................................................................. 296Limplmentation du chargement la demande .................................. 296

    La configuration 299Les contrleurs de l'application 301Les tches automatises 305

    Implmentation ......................................................................................... 305Utilisation du composant ZendService ................................................. 307

    Les modules du framework 308

    Chapitre 18 Contribuer au framework 309

    Le planning 309La gestion des versions 311Le bug tracker 312Corriger un bogue ou proposer une amlioration 313Rcapitulatif des tapes 314

  • Avant-propos

    qui sadresse ce livre ?

    Louvrage est technique et sadresse des dveloppeurs informatiques ayant dj une exprience de la programmation en PHP, ainsi que des connaissances de base du Zend Framework, que ce soit dans sa premire ou deuxime version.

    Nous essaierons de faire, tout au long de louvrage, un parallle entre la premire et cette seconde version du framework afin de bien comprendre pourquoi et dans quel but les composants ont t rcrits, et ce que peuvent nous apporter les nou-velles fonctionnalits du framework. Bien que de nombreuses portions de code soient incluses et expliques dans louvrage, il est conseill de lire les diffrents chapitres avec le code source du framework sous la main pour mieux comprendre les explications donnes. Afin dviter lajout de code non pertinent, certaines por-tions inutiles la comprhension sont coupes et reprsentes par des crochets []. Cela peut tre le cas pour la gestion de certaines erreurs ou traitements basiques par exemple.

    Lors de la premire partie du livre et lexplication du cur du framework, nous prendrons comme exemple le squelette dapplication propos sur le compte github du zend framework : https://github.com/zendframework/ZendSkeletonApplica-tion. Nous ferons peu de modifications au niveau du code du squelette dapplica-tion, mis part pour des exemples prcis. Les premiers chapitres sont consacrs au code du Zend Framework, ce qui nous permettra den maitriser les concepts afin de pouvoir les mettre en uvre lors de la deuxime partie avec le cas dutilisation.

    Jai avant tout souhait crire ce livre pour tmoigner lintrt que jai pour le Zend Framework et le PHP en gnral, jespre que celui-ci rpondra vos attentes et vous permettra de mieux apprhender le fonctionnement du framework. La dcouverte et la comprhension en dtail du framework mont aussi permis de pouvoir contribuer au code du Zend Framework 2 travers limplmentation de nouvelles fonctionnalits et la correction derreurs. Jespre donner aussi lenvie dautres dveloppeurs de venir contribuer lamlioration de cet excellent fra-mework. Vous trouverez au tout dernier chapitre les tapes de la contribution au Zend Framework.

  • 12 Chapitre Avant-propos

    Structure de louvrage

    Louvrage se divise en deux grandes parties. La premire est consacre ltude du cur du Zend Framework, ce qui va nous permettre de comprendre les grands changements effectus lors de cette deuxime version. La deuxime partie pr-sente un cas dutilisation qui nous permet la mise en uvre dun exemple concret utilisant un grand nombre de composants.

    Le chapitre 1 est une introduction au Zend Framework 2, avec un petit histo-rique sur la premire version du framework. Ce chapitre permet de se faire rapide-ment une ide sur les amliorations apportes au Zend Framework 2, ainsi que les conditions requises afin de lexploiter au mieux.

    Le chapitre 2 prsente les autoloaders existants dans le framework, avec des exemples dutilisation et lexplication de leur comportement dtaill. La maitrise du chargement de nos classes permet de sensiblement amliorer les performances de nos applications Web, souvent peu optimises sur cet aspect.

    Le chapitre 3 tudie le gestionnaire dvnements et limplmentation de la pro-grammation vnementielle au sein du Zend Framework. Cette partie est lune des grandes nouveauts du framework, lutilisation des vnements permet lamliora-tion de la flexibilit du framework.

    Le chapitre 4 prsente le composant dinjection de dpendance qui permet aux dveloppeurs de trouver ici les bonnes pratiques de programmation en dcouplant chaque objet et supprimant les dpendances dans le code.

    Le cinquime chapitre aborde le gestionnaire de services, nouveau composant du framework, responsable de la fabrication des objets du framework. Le gestionnaire peut tre utilis en complment du composant dinjection de dpendances.

    Le chapitre 6 prsente une des autres nouveauts du framework : le fonctionne-ment des modules. Ceux-ci sont comparables des briques individuelles prtes tre intgres de projet en projet.

    Le chapitre 7 fait le point sur les configurations, notamment sur leur fonction-nement gnral ainsi que sur lutilisation de celles-ci pour chacun de nos modules.

    Le huitime chapitre nous prsente le fonctionnement du router, loptimisation de celui-ci et les diffrents types de routes existantes.

    Les contrleurs seront abords au chapitre 9 qui dtaille le processus de distribu-tion au sein du contrleur, ainsi que la possibilit de jouer nimporte quelle action dun contrleur en le rendant distribuable.

    Le chapitre 10 nous propose de comprendre comment ont t refactorises les aides daction du framework. Plus performantes et plus simples comprendre, les aides dactions sont maintenant plus efficaces.

    Les gestionnaires de plugins sont prsents au chapitre 11, ce qui nous permet de voir le fonctionnement du chargement et de la gestion des instances de nos plugins.

    Les vues, leurs types et le fonctionnement de leurs rendus sont expliqus au

  • 13Chapitre Avant-propos

    chapitre 12. Un gros travail de rcriture a t effectu par lquipe du Zend Fra-mework sur le mcanisme des vues.

    Les aides de vue et leurs optimisations sont prsents au chapitre 13.

    Le cur du patron darchitecture MVC de Zend est tudi au chapitre 14 avec lexplication de nos points dentres et le processus de lancement de lapplication.

    Plusieurs composants importants (bases de donnes, formulaires, etc.) sont d-taills au chapitre 15 afin den maitriser leur utilisation.

    Le chapitre 16 passe en revue le nouveau systme de gestion des types dexcep-tions au sein du framework.

    Le dix-septime chapitre prsente ltude dun cas dutilisation afin de nous per-mettre la mise en application de tout ce que lon a pu voir au sein des chapitres prcdents.

    Le dernier chapitre explique en dtail les tapes afin de pouvoir contribuer au Zend Framework : correction de bogue, cration de fonctionnalit, suivi du calen-drier de dveloppement, etc.

    Remerciements

    Je tiens remercier tous ceux qui mont aid rdiger cet ouvrage, que ce soit par leur relecture ou leur soutient :

    Clment Caubet pour sa magnifique couverture de livre ;

    Michel Di Bella pour le template et la mise en page du livre ;

    Yoann Gobert pour le design et lintgration du site au-coeur-de-zend-fra-mework-2.fr ;

    Christophe Mailfert et Frdric Blanc pour la relecture technique du livre ;

    Claude Leloup et Fabien Le Bris pour leur relecture orthographique ;

    Michael Gallego pour sa prface ;

    au site developpez.com et ainsi qu ses membres pour leurs relectures et com-mentaires ;

    Marie qui ma soutenu et cout pendant les six mois dcriture ;

    lquipe du Zend Framework pour leur trs bon travail, ce qui fait que ce livre existe ;

    vous qui venez de lacheter et qui lisez jusqu la dernire ligne des remerciements.

  • 14 Chapitre Avant-propos

  • 1Le Zend Framework

    Le Zend Framework 1

    La premire rvision du framework tait la version 0.1, sortie en 2006 avant quune premire version stable voie le jour en juillet 2007 aprs plus dun an et demi de dveloppement. Cette premire version nintgrait pas toutes les fonctionnalits que lon connat maintenant. De nombreux composants majeurs, comme le Zend_Layout, sont sortis avec la version 1.5 et dautres comme le Zend_Application, avec la version 1.8. La version 1.5 a marqu un tournant pour le Zend Framework, cest une des versions qui ont apport le plus de fonctionnalits. Cest dailleurs sur cette version que repose la certification Zend Framework 1.

    Seulement, au fur et mesure du temps, le Zend Framework, mme sil est com-patible avec PHP 5.3, ne tire pas tous les avantages des dernires versions de PHP. La version 1.11 du framework est considre comme trs stable et ctait donc loccasion pour commencer de dvelopper une nouvelle version qui casserait com-pltement la compatibilit avec la version prcdente. Cest aussi loccasion pour lquipe de dveloppement de mettre en place de nombreux patterns et bonnes pratiques pour les dveloppeurs qui manquent actuellement au framework.

    Le Zend Framework 2

    PHP 533

    Le Zend Framework 2 est pens et crit pour PHP 5.3, branche dvolution ma-jeure sortie en 2009. PHP 5.3 a intgr un grand nombre dvolutions comme les espaces de nom, le garbage collector, les amliorations de la SPL (Standard PHP Library, bibliothque standard de PHP qui fournit classes et interfaces pour rsoudre des problmes rcurrents), etc. Le Zend Framework 2 utilise abondam-ment ces nouveauts, en particulier les espaces de noms qui sont omniprsents dans le framework o chaque classe possde son propre espace de nom. Le fra-mework utilise aussi beaucoup les nouveauts de la SPL comme le GlobIterator ou la SplPriorityQueue qui permettent une meilleure compatibilit. Typiquement, ces classes et mthodes de la SPL sont rgulirement utilises travers la bibliothque

  • 16 Chapitre 1Avant-propos

    standard du Zend Framework 2 pour homogniser le code au sein du framework. Il y a donc une premire cassure avec le Zend Framework 1 o les espaces de noms ne pouvaient pas tre utiliss pour garder une compatibilit avec les versions infrieures PHP 5.3.

    Au niveau de la branche 5.3, le cur du langage, dont le Zend Engine, a t pro-fondment remani ce qui entraine des gains de performance ainsi que des r-ductions de consommation mmoire assez importantes. Il nest pas ncessaire de stendre plus sur les nouveauts de PHP 5.3, nous sommes aujourdhui en version 5.4, la branche 5.3 ayant dj trois ans, la documentation PHP sera plus prcise ce sujet pour ceux qui souhaitent dcouvrir les volutions de cette version. Si la version 2 du framework est reste en dveloppement pendant plus de deux ans, il semblerait que l'quipe de dveloppement souhaite acclrer lgrement les sorties de versions majeures pour toujours bnficier au maximum des dernires branches de PHP ainsi que les bonnes pratiques et design pattern. Pour venir discuter avec l'quipe, contributeurs ou autres dveloppeurs Zend Framework 2, n'hsitez pas venir sur le channel IRC ddi #zftalk.2.

    Design pattern

    Le patron de conception principal est le patron darchitecture MVC (modle - vue - contrleur), qui spare le code dune application entre le modle (traitement des donnes et interaction avec la base de donnes), la vue (gestion des fichiers de vue et du rendu) et le contrleur qui est le chef dorchestre de lapplication et va intera-gir avec les deux premiers composants pour injecter les donnes la vue.

    Dans cette nouvelle version du framework, la programmation par contrat est en-core plus prsente. Chaque classe abstraite et chaque composant indpendant im-plmentent une interface, ce qui va permettre de faciliter linjection de dpendance. En effet, un objet inject dans un autre objet devra respecter un contrat (impl-menter une interface) afin de pouvoir sassurer que celui-ci va pouvoir rpondre aux attentes de lobjet qui le reoit. Cela permet dtendre les possibilits et de supprimer les dpendances de son code.

    Comme nous le verrons, linjection de dpendances est trs prsente dans le Zend Framework 2 et autorise la suppression des dpendances codes en dur, ce qui permet aux dveloppeurs damliorer la qualit de leur code.

    Une des grandes nouveauts du Zend Framework 2 est la programmation vne-mentielle. Le principe est doffrir un systme de communication au travers de tous les composants du framework sans que celui casse le principe du pattern MVC. La programmation vnementielle a t introduite pour faciliter les changes entre composants et supprimer les mauvaises pratiques que pouvaient mettre en place les dveloppeurs pour pallier ce manque de transversalit. De plus, les vnements permettent la suppression des hooks, trop prsents dans la premire version du framework. La programmation vnementielle est une manire de programmer largement rpandue dans dautres langages comme le Java ou lActionScript, Zend Framework y intgre les bonnes pratiques. Un chapitre est consacr aux vne-ments et aux gestionnaires d'vnements.

  • 17Chapitre 1Avant-propos

    Les axes damlioration

    Les premiers axes damlioration du Zend Framework 2 concernent lamlioration des performances qui devenaient de moins en moins bonnes au fur et mesure des versions, ainsi que lamlioration de la courbe dapprentissage du framework.

    Lutilisation des mthodes magiques est en cause sur les baisses de performance, dans le Zend Framework 1, la magie est utilise tout va. Le deuxime point damlioration est le systme de chargement de classes qui reste trop gourmand, nous verrons que le Zend Framework 2 utilise un systme de chargement beau-coup plus performant. Le framework veut aussi supprimer lutilisation excessive de la directive include_path qui rend le chargement des classes beaucoup trop lent. Un nouveau systme de rsolution de nom de classe par tableau PHP dcrivant le chemin pour chacune des classes utilises en cl du tableau a t introduit. Ce chargement bas sur le couple cl/valeur devient alors beaucoup plus performant, mme sil reste la possibilit dutiliser un chargement bas sur les prfixes, espaces de noms ou sur la directive include_path .

    Au-del des composants trop gourmands en termes de performances ou de temps dapprentissage, la deuxime version du framework veut amliorer lenvironne-ment du dveloppeur en mettant laccent sur la documentation et les exemples mis en uvre. Par exemple, des classes comme le Zend_Form et ses dcorateurs sont considres comme trop complexes dutilisation.

    Afin de mettre en place toutes ces bonnes pratiques, les dveloppeurs ont d r-crire certains composants entirement et en crer dautres. Si le dveloppement a paru long pour ses utilisateurs, la priorit de lquipe tait davoir un produit fini qui leur convienne parfaitement.

    Nous allons maintenant voir comment mettre en place rapidement un squelette de projet sous Zend Framework 2, ce qui nous servira ensuite de base pour notre tude approfondie du framework.

    Le squelette de lapplication

    Lors des chapitres suivants, tous nos exemples de configurations et de modules se-ront directement tirs du ZendSkeletonApplication, disponible sur compte github de ZendFramework : https://github.com/zendframework/ZendSkeletonApplica-tion. Ce projet propose un squelette dapplication fonctionnel afin de comprendre les principales tapes de la mise en place dun projet sous Zend Framework 2.

    Voici la hirarchie des fichiers proposs :

  • 18 Chapitre 1Le Zend Framework

    Au fil des chapitres, lorsquune section sera traite, un aperu des fichiers concer-ns sera joint aux explications et chaque partie du cur de Zend Framework pas-se en revue sera affiche dans les explications de louvrage afin den faciliter la comprhension.

  • 2Les autoloaders

    Dans la liste des axes damlioration du Zend Framework 2 figurent les autoloa-ders. Trop souvent bases sur la directive include_path , les classes de charge-ments du Zend Framework 1 sont trop lentes. Si lutilisation des prfixes de classe permet de rendre le chargement un peu plus performant, cette tape reste tout de mme coteuse en performances. De nouveaux autoloaders ont fait leur appari-tion pour rendre le processus encore plus rapide, faisons un tour dhorizon pour les prsenter.

    Lautoloader standard

    Lautoloader standard, de type Zend\Loader\StandardAutoloader, permet de charger une classe suivant un prfixe de nom ou un espace de nom. Il est aussi pos-sible de configurer lautoloader standard afin dutiliser la directive include_path , ce qui serait prjudiciable en termes de performances.

    Prenons un exemple dutilisation avec le fichier dinitialisation des autoloaders qui lutilise par dfaut avec linstruction :

    init_autoloaderphpZend\Loader\AutoloaderFactory::factory(array( 'Zend\Loader\StandardAutoloader' => array( 'autoregister_zf' => true )));

    Lautoloader standard est utilis explicitement, mais sans argument, la fabrique instancie un objet de type StandardAutoloader par dfaut comme nous le voyons depuis la fabrique :

    Zend/Loader/AutoloaderFactoryphpconst STANDARD_AUTOLOADER = 'Zend\Loader\StandardAutoloader';

  • 20 Chapitre 2Les autoloaders

    Zend/Loader/AutoloaderFactoryphppublic static function factory($options = null){ if (null === $options) { if (!isset(static::$loaders[static::STANDARD_AUTOLOADER])) { $autoloader = static::getStandardAutoloader(); $autoloader->register(); static::$loaders[static::STANDARD_AUTOLOADER] = $autoloader; } return; } []}

    La mthode getStandardAutoloader() cre une instance de cet objet :

    Zend/Loader/AutoloaderFactoryphpprotected static function getStandardAutoloader(){ if (null !== static::$standardAutoloader) { return static::$standardAutoloader; } $stdAutoloader = substr(strrchr(static::STANDARD_AUTOLOADER, '\\'), 1); if (!class_exists(static::STANDARD_AUTOLOADER)) { require_once __DIR__ . "/$stdAutoloader.php"; } $loader = new StandardAutoloader(); static::$standardAutoloader = $loader; return static::$standardAutoloader;}

    La mthode register() enregistre ensuite la fonction autoload() de lobjet courant, comme mthode de chargement, auprs de la SPL :

    Zend/Loader/StandardAutoloaderphppublic fonction register(){ spl_autoload_register(array($this, 'autoload'));}

    Le constructeur de lautoloader standard enregistre par dfaut lespace de nom Zend si cela lui est demand, comme dans linitialisation des autoloaders, depuis le tableau doptions afin quil soit prt lemploi pour le chargement de classes de la bibliothque du framework :

    Zend/Loader/StandardAutoloaderphpconst AUTOREGISTER_ZF = 'autoregister_zf';

  • 21Chapitre 2Les autoloaders

    Zend/Loader/StandardAutoloaderphppublic fonction setOptions($options){[] foreach ($options as $type => $pairs) { switch ($type) { case self::AUTOREGISTER_ZF: if ($pairs) { $this->registerNamespace('Zend', dirname(__DIR__)); } break; [] } return $this;}

    Ceci explique la possibilit dinstancier ensuite un objet de lespace de nom Zend sans difficult. Plusieurs mthodes de cette classe permettent lenregistrement de prfixes et espaces de nom :

    Zend/Loader/StandardAutoloaderphppublic function registerNamespace($namespace, $directory){ $namespace = rtrim($namespace, self::NS_SEPARATOR). self::NS_SEPARATOR; $this->namespaces[$namespace] = $this->normalizeDirectory($directory); return $this;}

    Zend/Loader/StandardAutoloaderphppublic function registerPrefix($prefix, $directory){ $prefix = rtrim($prefix, self::PREFIX_SEPARATOR). self::PREFIX_SEPARATOR; $this->prefixes[$prefix] = $this->normalizeDirectory($directory); return $this;}

    Prenons dautres exemples et ajoutons un dossier la racine, nomm autoload-dir , qui contient deux dossiers Namesp/ et Prefix/ . Le dossier Namesp possde un premier fichier Test.php :

    Testphp :namespace Namesp;class Test{}

    Le dossier Prefix/ contient un autre fichier Test.php :

    Testphpclass Prefix_Test{}

  • 22 Chapitre 2Les autoloaders

    Afin de charger ces deux classes, il nous suffit de rcuprer lautoloader standard et dy ajouter les espaces de noms et prfixes dont on a besoin :

    Utilisation des prfixes

    $autoloader = Zend\Loader\AutoloaderFactory::getRegisteredAutoloader(Zend\Loader\AutoloaderFactory::STANDARD_AUTOLOADER);$autoloader->registerPrefix('Prefix_', __DIR__ . '/autoload-dir/Prefix');

    new Prefix_Test();

    Utilisation des espaces de nom$autoloader = Zend\Loader\AutoloaderFactory::getRegisteredAutoloader(Zend\Loader\AutoloaderFactory::STANDARD_AUTOLOADER);$autoloader->registerNamespace('Namesp', __DIR__ . '/autoload-dir/Namesp');

    new Namesp\Test();

    Comme nous le remarquons, lutilisation de la classe StandardAutoloader est trs simple. Comme on la dit prcdemment, il est aussi possible dutiliser la directive include_path depuis la mthode setFallbackAutoloader() :

    Utilisation de la directive include_path set_include_path(implode(PATH_SEPARATOR, array( realpath(__DIR__ . '/autoload-dir'), get_include_path())));$autoloader = Zend\Loader\AutoloaderFactory::getRegisteredAutoloader(Zend\Loader\AutoloaderFactory::STANDARD_AUTOLOADER);$autoloader->setFallbackAutoloader(true);

    new Namesp\Test();

    Si le chargement de classe est activ avec la directive include_path et que des espaces de noms ou prfixes sont enregistrs, le framework leur donnera la priorit lors du chargement :

    Zend/Loader/StandardAutoloaderphpconst NS_SEPARATOR = '\\';

    Zend/Loader/StandardAutoloaderphppublic function autoload($class){ $isFallback = $this->isFallbackAutoloader(); if (false !== strpos($class, self::NS_SEPARATOR)) { if ($this->loadClass($class, self::LOAD_NS)) { return $class; } elseif ($isFallback) { return $this->loadClass($class, self::ACT_AS_FALLBACK); } return false;}

  • 23Chapitre 2Les autoloaders

    if (false !== strpos($class, self::PREFIX_SEPARATOR)) { if ($this->loadClass($class, self::LOAD_PREFIX)) { return $class; } elseif ($isFallback) { return $this->loadClass($class, self::ACT_AS_FALLBACK); } return false;} if ($isFallback) { return $this->loadClass($class, self::ACT_AS_FALLBACK);} return false;}

    Lautoloader vrifie alors si le chargement concerne un nom de classe avec espace de nom (simple vrification de la prsence dun double-slash) et utilise la mthode loadClass() afin de la charger. Cest seulement si le chargement na pas abouti que lautoloader appelle cette mme mthode, qui utilisera alors la directive in-clude_path , comme nous lavons spcifi :

    Zend/Loader/StandardAutoloaderphpprotected fonction loadClass($class, $type){[] if ($type === self::ACT_AS_FALLBACK) { $filename = $this->transformClassNameToFilename($class, ''); $resolvedName = stream_resolve_include_path($filename); if ($resolvedName !== false) { return include $resolvedName; } return false;}[]}

    Comme son nom lindique, la mthode de chargement stream_resolve_include_path() est base sur la directive include_path . Mme si lutilisation de celle-ci est viter, nous pouvons nous en servir une fois nos prfixes et autres espaces de noms enregistrs, ceux-ci tant prioritaires lors du chargement.

    Notons quil est aussi possible de fournir un tableau doptions directement dans le constructeur de notre objet de chargement de classe ou depuis sa mthode se-tOptions() :

    Utilisation dun tableau doptions$autoloader = Zend\Loader\AutoloaderFactory::getRegisteredAutoloader(Zend\Loader\AutoloaderFactory::STANDARD_AUTOLOADER);$autoloader->setOptions(array( 'namespaces'=>array('Namesp'=>'./autoload-dir/Namesp'), 'prefixes'=>array('Prefix_'=>'./autoload-dir/Prefix'), ));

    new Namesp\Test();new Prefix_Test();

  • 24 Chapitre 2Les autoloaders

    Afin de gagner en performances, le framework propose dans cette version, un nouvel autoloader plus rapide et encore plus simple d'utilisation, le ClassMapAuto-loader.

    Avec le Zend FrAmework 1Dans la version 1 du framework, la classe de chargement Zend_Loader_Autoloader permet de charger des espaces de noms (prfixes en ralit), la classe StandardAutoloader reprend un peu ce principe. La classe Zend_Loader_Autoloader peut aussi enregistrer dautres autoloaders pour un mme prfixe et ceux-ci seront utiliss en priorit. Il faut aussi noter quil ny a pas de notion despace de nom PHP dans le Zend Framework 1, lauto-loader se base donc sur ce que fournit le dveloppeur. Dans cette nouvelle version, espace de nom PHP et prfixes sont bien diffrencis, ce qui rend les performances meilleures car le framework peut maintenant diffrencier les deux.

    Le ClassMapAutoloader

    Associant nom de classe et chemin, le Zend\Loader\ClassMapAutoloader est cer-tainement lautoloader le plus performant. Ds lors que le tableau de chemins correspondant aux noms de classes est configur, le ClassMapAutoloader est fonc-tionnel.

    Prenons un exemple avec la mme structure de dossier que pour lautoloader stan-dard de la section prcdente :

    Utilisation de la classe ClassMapAutoloaderchdir(dirname(__DIR__));require_once (getenv('ZF2_PATH') ?: 'vendor/ZendFramework/library') . '/Zend/Loader/AutoloaderFactory.php';Zend\Loader\AutoloaderFactory::factory();

    $maps = include 'config/application.autoload_classmap.php';Zend\Loader\AutoloaderFactory::factory(array('Zend\Loader\ClassMapAutoloader' => array($maps)));

    new Namesp\Test();new Prefix_Test();

    Le fichier de configuration est un simple tableau de type cl/valeur :

    config/application.autoload_classmap.php

  • 25Chapitre 2Les autoloaders

    que nous souhaitons enregistrer une instance de la classe ClassMapAutoloader auprs de la SPL. La mthode sattend recevoir en paramtre un nom de classe avec sa configuration associe :

    Zend/Loader/AutoloaderFactoryphppublic static fonction factory($options = null){ if (null === $options) { []} if (!is_array($options) && !($options instanceof \Traversable)) { []} foreach ($options as $class => $options) { if (!isset(static::$loaders[$class])) { $autoloader = static::getStandardAutoloader(); [] if ($class === static::STANDARD_AUTOLOADER) { $autoloader->setOptions($options); } else { $autoloader = new $class($options); } $autoloader->register(); static::$loaders[$class] = $autoloader; } else { static::$loaders[$class]->setOptions($options); }}}

    La mthode de fabrication des autoloaders contrle lexistence de l'instance de-mande avant de la crer avec les options passes en paramtre. Lobjet cr senre-gistre ensuite automatiquement auprs de la SPL, ce qui nous vite de lenregistrer explicitement depuis sa mthode register() . Nous remarquons aussi que si lins-tance de notre objet est existante, la fabrique se contente alors de mettre jour les options de cette instance. La mthode daltration des options setOptions() ne remplace pas mais ajoute les nouvelles options la configuration existante, comme nous le remarquons dans la classe ClassMapAutoloader :

    Zend/Loader/ClassMapAutoloaderphppublic fonction setOptions($options){ $this->registerAutoloadMaps($options return $this;}

    Zend/Loader/ClassMapAutoloaderphppublic fonction registerAutoloadMaps($locations){[] foreach ($locations as $location) { $this->registerAutoloadMap($location);} return $this;}

  • 26 Chapitre 2Les autoloaders

    Zend/Loader/ClassMapAutoloaderphppublic fonction registerAutoloadMap($map){ if (is_string($map)) { $location = $map; if ($this === ($map = $this->loadMapFromFile($location))) { return $this; }}[] $this->map = array_merge($this->map, $map); if (isset($location)) { $this->mapsLoaded[] = $location;} return $this;}

    La mise jour des options se fait ensuite avec la fusion des anciennes configura-tions avec les nouvelles, avant de stocker le rsultat. Attention, pour deux cls iden-tiques, la fusion utilise en priorit la cl du nouveau tableau doptions, car la der-nire valeur rencontre crase lancienne. Pour plus de prcision, je vous conseille daller voir directement la documentation PHP de la fonction array_merge() .

    Dans notre exemple, nous avons pass en paramtre de la fabrique un tableau doptions, mais il est galement possible dindiquer au constructeur du ClassMa-pAutoloader un chemin de fichier de configuration. Lautoloader soccupe alors de rcuprer automatiquement les options en utilisant linstruction de langage include , par exemple :

    Utilisation dun fichier de correspondance

    Zend\Loader\AutoloaderFactory::factory(array( 'Zend\Loader\ClassMapAutoloader' => array('config/application.autoload_classmap.php')));

    Zend/Loader/ClassMapAutoloaderphppublic fonction registerAutoloadMap($map){ if (is_string($map)) { $location = $map; if ($this === ($map = $this->loadMapFromFile($location))) { return $this; }}[]}

    La mthode loadMapFromFile() permet le chargement de configuration depuis un fichier. Notons que lobjet de type ClassMapAutoloader enregistre tous les che-mins de fichiers fournis dans la variable $mapsLoaded, ce qui permet de sassu-rer de ne pas faire le traitement deux fois. En lui passant un tableau directement en paramtre comme dans le premier exemple, cest au dveloppeur de sassurer de ne pas faire deux fois le traitement car aucun contrle ne sera effectu du ct de lautoloader :

  • 27Chapitre 2Les autoloaders

    Zend/Loader/ClassMapAutoloaderphpprotected fonction loadMapFromFile($location){ if (!file_exists($location)) { []} if (!$path = static::realPharPath($location)) { $path = realpath($location);} if (in_array($path, $this->mapsLoaded)) { return $this;} $map = include $path;return $map;}

    Avec quelques traitements en plus, les deux manires de faire sont identiques. Nous savons prsent paramtrer les classes de chargement, analysons maintenant len-registrement auprs de la SPL ainsi que la fonction appele lors du chargement :

    Zend/Loader/ClassMapAutoloaderphppublic function register(){ spl_autoload_register(array($this, 'autoload'), true, true);}

    Le troisime paramtre pass la mthode de la SPL indique que lon souhaite pla-cer cette classe de chargement en haut de la pile des autoloaders de notre applica-tion, par dfaut les enregistrements se placent la fin. tant donn les trs bonnes performances de lobjet ClassMapAutoloader, il est dans l'intrt de lapplication de lutiliser en premier.

    Comme il est indiqu lors de lenregistrement auprs de la SPL, cest la mthode autoload() qui soccupe du chargement de la classe :

    Zend/Loader/ClassMapAutoloaderphppublic fonction autoload($class){ if (isset($this->map[$class])) { require_once $this->map[$class];}}

    Nous comprenons alors tout de suite lintrt dutiliser la classe ClassMapLoader, le traitement est simple et performant. Il suffit que la classe demande soit bien configure depuis les options de lautoloader afin de pouvoir linclure directement dans notre code sans plus de recherche.

    Maintenant que nous avons saisi lintrt de la classe de chargement ClassMa-pAutoloader, nous pourrions vouloir lutiliser pour la bibliothque du Zend Fra-mework plutt que de passer par lenregistrement dun espace de nom auprs de notre autoloader standard. Les dveloppeurs du framework mettent disposition un fichier classemap_generator.php qui permet la gnration automatique dune configuration partir dun rpertoire cible. Il suffit de consulter les options

  • 28 Chapitre 2Les autoloaders

    du script afin den comprendre le fonctionnement :

    Aide sur la gnration de fichier de mapping

    php classmap_generator.php --help

    Une fois le fichier de configuration gnr, toutes les classes de lespace de nom Zend seront automatiquement charges depuis cet autoloader, ce qui permet un gain de performances.

    La classe ModuleAutoloader

    Comme son nom lindique et comme nous le verrons dans le chapitre consacr aux modules, le Zend\Loader\ModuleAutoloader sadapte uniquement nos mo-dules, ce que nous prouvent les premires lignes du chargement de classe :

    Zend/Loader/ModuleAutoloaderphppublic fonction autoload($class){ if (substr($class, -7) !== '\Module') { return false;}[]}

    Lautoloader ne travaille que si nous recherchons une classe nomme Module , point dentre des modules de lapplication. Lutiliser comme autoloader pour le reste de notre application naurait alors aucune utilit.

    La classe ModuleAutoloader sera tudie en dtail dans la section ddie aux mo-dules.

    Linterface SplAutoloader

    Les autoloaders ClassMapAutoloader et StandardAutoloader devraient suffire pour nos applications, leur fonctionnement tant assez complet. Cependant, il est possible de crer sa propre classe de chargement en implmentant linterface Zend\Loader\SplAutoloader qui dfinit quatre mthodes :

    Zend/Loader/SplAutoloaderphpinterface SplAutoloader{ public function __construct($options = null);

    public function setOptions($options);

    public function autoload($class);

    public function register();}

  • 29Chapitre 2Les autoloaders

    Le constructeur doit pouvoir accepter les options de lautoloader en paramtre afin de permettre la mthode factory() , de la fabrique AutoloaderFactory, de crer directement linstance en lui passant les options en paramtre comme nous lavons vu prcdemment. La mthode setOptions() sera utilise pour la mise jour de la configuration de lautoloader par la fabrique lorsque linstance de la classe demande existe dj. Les deux autres mthodes, register() et autoload() , sont galement utilises par la mthode de fabrication factory() de lAutoloa-derFactory.

  • 30 Chapitre 2Les autoloaders

  • 3Les vnements

    La programmation vnementielle est au cur du Zend Framework 2. Les vne-ments permettent aux diffrents composants de communiquer, dinteragir et de partager des informations entre eux. Ce type de programmation nest pas antipat-tern et permet dinstaurer de bonnes pratiques de codage. Cela va aussi permettre de supprimer les hooks que lon connat du Zend Framework 1 tout en augmen-tant la souplesse de notre application. Nous verrons que la gestion des priorits sur les couteurs de nos vnements est une bonne solution de remplacement aux pr et postvnements ( preDispatch et postDispatch par exemple) que lon connat.

    Prsentation des vnements

    Un nouveau composant a fait son apparition dans Zend Framework 2, le gestion-naire dvnements Zend\EventManager\EventManager. Dfinissons dabord les diffrents termes :

    un event ou vnement est une action ;

    un listener ou couteur est un couteur d'vnements ;

    un event manager ou gestionnaire dvnements est un objet qui contient un ensemble dcouteurs et qui va lancer des vnements.

    Toute mthode coutant un vnement est notifie lorsque celui-ci est lanc. Le gestionnaire dvnements de type EventManager fait la passerelle entre les cou-teurs et les vnements qui se produisent. Il est possible dattacher un vnement un objet travers la mthode attach() et de lancer un vnement depuis la mthode trigger() .

    Afin de comprendre l'intrt et le fonctionnement des vnements, prenons un exemple dutilisation du gestionnaire d'vnements.

  • 32 Chapitre 3Les vnements

    Exemple dutilisation dvnementclass Developpeur{ protected $events; protected $pauseHandler;

    public function travail(){ $this->pauseHandler = $this->getEventManager()->attach('pause', function($e) { printf('Evenement en cours "%s" sur objet "%s", avec les paramtres %s', $e->getName(), get_class($e->getTarget()), json_encode($e->getParams()) ); });} public function termine(){ if(!$this->pauseHandler){ return; } $this->getEventManager()->detach($this->pauseHandler); $this->pauseHandler = null;}

    public function setEventManager(EventManagerInterface $events){ $this->events = $events;}

    public function getEventManager(){ if (!$this->events){ $this->setEventManager(new EventManager( array(__CLASS__, get_called_class()) )); } return $this->events;}

    public function pause($params){ $this->getEventManager()->trigger(__FUNCTION__, $this, $params);}}

    $developpeur = new Developpeur();$developpeur->travail();$developpeur->pause(array('cafe', 'court'));$developpeur->termine();$developpeur->pause(array('cafe', 'long'));

    Seule la phrase Evenement en cours "pause" sur lobjet Developpeur , avec les paramtres ["cafe","court"] saffiche. En effet, lobjet $developpeur sest dsabonn de lvnement pause lorsque la mthode termine() sest effec-tue.

  • 33Chapitre 3Les vnements

    Chaque gestionnaire dvnements est identifi par une chane de caractres que lon passe son constructeur (ici le nom de la classe en cours avec __CLASS__), ce qui lui permet ensuite d'effectuer une liaison avec le gestionnaire partag.

    Le gestionnaire partag

    Un deuxime type de gestionnaire dvnements est disponible, le Zend\Event-Manager\SharedEventManager. Assez simple dutilisation, il possde un tableau dobjets de type EventManger en interne et permet aussi dattacher des couteurs un gestionnaire identifi depuis la mthode attach() :

    Cration de gestionnaire partag$events = new SharedEventManager();$events->attach('Developpeur', 'pause', function($e) { printf('Evenement en cours "%s" sur objet "%s", avec les paramtres %s', $e->getName(), get_class($e->getTarget()), json_encode($e->getParams()) );});

    Ce code instancie en ralit un gestionnaire dvnements avec comme identifiant la chane de caractres passe en premier paramtre, ici Developpeur . Analy-sons le traitement effectu dans la mthode attach() de la classe SharedEvent-Manager :

    Zend/EventManager/SharedEventManagerphppublic function attach($id, $event, $callback, $priority = 1){ $ids = (array) $id; foreach ($ids as $id) { if (!array_key_exists($id, $this->identifiers)) { $this->identifiers[$id] = new EventManager(); } $this->identifiers[$id]->attach($event, $callback, $priority);}}

    Lidentifiant Developpeur sert de cl dans le tableau associatif $identifiers , o est alors instanci un gestionnaire dvnements auquel on ajoute lcouteur pour lvnement pass en deuxime paramtre.

    Tentons maintenant de comprendre le lien entre un gestionnaire dvnements que lon a cr plus haut et lobjet SharedEventManager. Examinons de plus prs la mthode trigger() de la classe EventManager.

  • 34 Chapitre 3Les vnements

    Zend/EventManager/EventManagerphppublic function trigger($event, $target = null, $argv = array(), $callback = null){[] return $this->triggerListeners($event, $e, $callback);}

    Voici la mthode triggerListeners() :

    Zend/EventManager/EventManagerphpprotected function triggerListeners($event, EventInterface $e, $callback = null){ $responses = new ResponseCollection; $listeners = $this->getListeners($event);

    $sharedListeners = $this->getSharedListeners($event); $sharedWildcardListeners = $this->getSharedListeners('*'); $wildcardListeners = $this->getListeners('*');

    if (count($sharedListeners) || count($sharedWildcardListeners) || count($wildcardListeners)) { $listeners = clone $listeners;}

    $this->insertListeners($listeners, $sharedListeners); $this->insertListeners($listeners, $sharedWildcardListeners); $this->insertListeners($listeners, $wildcardListeners); if ($listeners->isEmpty()) { return $responses;}

    foreach ($listeners as $listener) { $responses->push(call_user_func($listener->getCallback(), $e)); if ($e->propagationIsStopped()) { $responses->setStopped(true); break; }

    if ($callback && call_user_func($callback, $responses->last())) { $responses->setStopped(true); break; }} return $responses;}

    Cette mthode rcupre la liste des couteurs du gestionnaire partag depuis la mthode getSharedListeners() :

  • 35Chapitre 3Les vnements

    Zend/EventManager/EventManagerphpprotected function triggerListeners($event, EventInterface $e, $callback = null){ [] $sharedListeners = $this->getSharedListeners($event);[]}

    Cette mthode rcupre tous les couteurs des gestionnaires dvnements du ges-tionnaire partag, dont lidentifiant correspond un des identifiants du gestion-naire courant. Le tableau de chanes de caractres pass au constructeur du ges-tionnaire sert didentifiant, nous comprenons maintenant son utilit. Par exemple, la cl passe au constructeur du gestionnaire suivant :

    Les identifiants du gestionnaire

    new EventManager( array(__CLASS__, get_called_class()))

    Ici __CLASS__ ou get_called_class() , doit tre de la mme valeur que le premier argument de la mthode attach() de la classe SharedEventManager vue prcdemment :

    Attache dcouteur au gestionnaire partag$events->attach('Developpeur', 'pause', function($e) { []});

    Dans cet exemple, le lien entre le gestionnaire dvnements instanci prcdem-ment (qui possde comme cl la constante PHP __CLASS__, qui a pour valeur le nom de la classe, ici Developpeur ) et lajout dcouteurs sur l'instance de l'objet de type SharedEventManager (dont on demande la cration dun gestionnaire pour identifiant Developpeur ) peut tre fait. Cette manire de fonctionner peut sav-rer assez utile pour ajouter facilement des vnements sans avoir disposition linstance du gestionnaire dvnements concern, si le partage est effectu.

    Cependant, ce comportement peut savrer parfois dstabilisant et il est possible de le dsactiver. Si lon examine la mthode qui s'occupe de rcuprer la liste des couteurs du gestionnaire partag, nous remarquons quelle va tout dabord rcu-prer la liste des gestionnaires dvnements du SharedEventManager avant de faire correspondre avec les identifiants.

    Zend/EventManager/EventManagerphpprotected function getSharedListeners($event){ if (!$sharedManager = $this->getSharedManager()) { return array();}$identifiers = $this->getIdentifiers();$sharedListeners = array();

  • 36 Chapitre 3Les vnements

    foreach ($identifiers as $id) { if (!$listeners = $sharedManager->getListeners($id, $event)) { continue; }

    if (!is_array($listeners) && !($listeners instanceof Traversable)) { continue; }

    foreach ($listeners as $listener) { if (!$listener instanceof CallbackHandler) { continue; } $sharedListeners[] = $listener; }} return $sharedListeners;}

    La premire ligne tente dobtenir une connexion avec notre gestionnaire partag. Cette connexion est matrialise par lexistence dune instance de type SharedE-ventManager :

    Zend/EventManager/EventManagerphpprotected function getSharedListeners($event){ if (!$sharedManager = $this->getSharedManager()) { return array();}[]}

    Zend/EventManager/EventManagerphppublic function getSharedManager(){ if (false === $this->sharedManager || $this->sharedManager instanceof SharedEventManagerInterface ) { return $this->sharedManager;} if (!StaticEventManager::hasInstance()) { return false;} $this->sharedManager = StaticEventManager::getInstance(); return $this->sharedManager;}

    La relation entre le gestionnaire dvnements courant et lobjet SharedEventMa-nager se fait automatiquement. Pour dsactiver cette fonctionnalit et bloquer la passerelle entre nos gestionnaires dvnements et notre gestionnaire statique, il suffit dappeler la mthode unsetSharedManager() qui permet cette dsacti-vation en passant la valeur false lattribut $sharedManager , valeur qui est synonyme de dsactivation comme nous venons de le voir :

  • 37Chapitre 3Les vnements

    Dsactivation de la liaison avec le gestionnaire partag

    $this->events->unsetSharedManager();

    Il est alors possible de ractiver cette liaison en rejouant le code de la mthode getSharedManager() :

    Activation de la liaison avec le gestionnaire partag$this->events->setSharedManager(StaticEventManager::getInstance());

    Le gestionnaire dvnements rcupre ensuite la liste des couteurs prsents dans les objets de type EventManager du gestionnaire partag pour lvnement cou-rant :

    Zend/EventManager/EventManagerphpprotected function getSharedListeners($event){[] $identifiers = $this->getIdentifiers(); $sharedListeners = array();

    foreach ($identifiers as $id) { if (!$listeners = $sharedManager->getListeners($id, $event)) { continue; } if (!is_array($listeners) && !($listeners instanceof Traversable)) { continue; } foreach ($listeners as $listener) { if (!$listener instanceof CallbackHandler) { continue; } $sharedListeners[] = $listener; }} return $sharedListeners;}

    Afin de bien matriser la programmation vnementielle du Zend Framework, il est important de bien assimiler que le gestionnaire partag agit comme un simple conteneur de gestionnaire dvnements. Ce nest en aucun cas un gestionnaire dvnements, il a pour seule responsabilit de stocker des objets de type Event-Manager qui peuvent avoir les mmes identifiants que dautres gestionnaires que lon a crs. Il peut ensuite faire la liaison avec dautres gestionnaires dvnements qui possdent le mme identifiant que lun de ses objets stocks. Cette liaison peut tre supprime et il est important de bien comprendre quil est diffrent dattacher un vnement sur le gestionnaire partag plutt que sur le gestionnaire dvne-ments lui-mme.

    Nous avons remarqu que le gestionnaire partag est par dfaut une instance de la classe StaticEventManager :

  • 38 Chapitre 3Les vnements

    Zend/EventManager/EventManagerphppublic function getSharedManager(){[] $this->sharedManager = StaticEventManager::getInstance(); return $this->sharedManager;}

    La classe StaticEventManager est en fait une reprsentation statique de la classe SharedEventManager :

    Zend/EventManager/StaticEventManagerphp public static function getInstance(){ if (null === static::$instance) { static::$instance = new static(); } return static::$instance;}

    La classe StaticEventManager hrite de la classe SharedEventManager et se com-porte comme un singleton afin doffrir une instance unique dobjet de type Sha-redEventManager. Ce comportement permet aux gestionnaires dvnements de partager la mme instance de gestionnaire partag afin de pouvoir ajouter des v-nements depuis nimporte quelle instance de gestionnaire dvnements.

    Examinons limplmentation du gestionnaire dvnements partag du framework avec la fabrique Zend\Mvc\Service\EventManagerFactory qui est responsable de la fabrication du gestionnaire dvnements de lapplication. Pour plus dinforma-tions sur le gestionnaire de services, qui permet de grer les fabriques dobjets, reportez-vous au chapitre qui lui est consacr. Voici la fabrique EventManagerFac-tory du gestionnaire dvnements :

    Zend/Mvc/Service/EventManagerFactoryphpclass EventManagerFactory implements FactoryInterface{ public function createService(ServiceLocatorInterface $serviceLocator){ $em = new EventManager(); $em->setSharedManager($serviceLocator->get('SharedEventManager')); return $em; }}

    La classe utilise la fabrique partage SharedEventManager afin de toujours utili-ser la mme instance de gestionnaire d'vnements partag. En effet, la fabrique SharedEventManager est une fabrique dont l'instance sera partage, car la cra-tion systmatique dune nouvelle instance du gestionnaire partag ferait perdre tout son sens la notion de partage.

    Nous pouvons donc constater que linstance du gestionnaire partag sera identique

  • 39Chapitre 3Les vnements

    avec une instanciation sans la fabrique du gestionnaire dvnements si lon utilise toujours la fabrique SharedEventManager.

    Lvnement wildcard

    Nous avons parcouru le code de la classe EventManger et nous avons remarqu la prsence dun vnement sous forme de joker :

    Analyse de la rcupration des vnements wildcard protected function triggerListeners($event, EventInterface $e, $callback = null){ $responses = new ResponseCollection; $listeners = $this->getListeners($event);

    $sharedListeners = $this->getSharedListeners($event); $sharedWildcardListeners = $this->getSharedListeners('*'); $wildcardListeners = $this->getListeners('*');[]}

    Pour chaque vnement, le gestionnaire dvnements rcupre les couteurs atta-chs sur lvnement * , ce qui leur permet dtre notifi ds quun vnement est lanc. Lcouteur notifi pourra alors faire appel la mthode getName() de l'vnement afin de savoir quel est lvnement qui a t lanc. Prenons un exemple dans la classe Module de notre module Application :

    Application/Modulephpclass Module implements AutoloaderProvider{ public function init(ModuleManager $moduleManager){ $events = $moduleManager->events()->getSharedManager(); $events->attach('Zend\Mvc\Application', '*', array($this, 'onApplicationEvent'),100); []} public function onApplicationEvent(Event $e){ echo $e->getName(); // affiche successivement route dispatch render finish}[]}

    Les vnements wildcard doivent tre utiliss avec prcaution, car nous ne connais-sons pas lavance tous les vnements qui peuvent tre lancs sur une instance de gestionnaire dvnements, il est important de sassurer de pouvoir agir lors des diffrents vnements possibles.

  • 40 Chapitre 3Les vnements

    Le gestionnaire dvnements global

    Le gestionnaire dvnements global est trs simple dutilisation. Toutes les m-thodes sont statiques, il ny a pas de notion didentifiants comme pour le ges-tionnaire partag, ni dinteraction avec dautres gestionnaires dvnements. La classe GlobalEventManager est donc accessible de nimporte o et fonctionne de manire indpendante :

    Zend/EventManager/GlobalEventManagerphpGlobalEventManager::attach('random-event', function($e){ echo $e->getName() . ' en cours, paramtre reu : ' . $e->getParam('myparam');});GlobalEventManager::trigger('random-event', null, array('myparam'=>'value1'));

    Affichage en sortie

    random-event en cours, paramtre reu : value1

    Si le gestionnaire global est plus simple de comprhension, il est conseill de mo-drer son utilisation et de ne pas sen servir comme fourre-tout afin dviter les collisions de noms dvnements. Utiliser les identifiants des gestionnaires dv-nements permet une scurit supplmentaire, ainsi quun code mieux organis. Cette manire de regrouper les vnements va permettre de donner du sens leur gestion et permettre nimporte quel dveloppeur travaillant sur lapplication de comprendre plus vite lutilit et le domaine dinteraction de chacun des gestion-naires et de leurs vnements.

    Le type ListenerAggregate

    Nous pouvons parfois avoir besoin dune classe capable de grer une multitude dvnements avec les couteurs associs. Afin de pouvoir confier cette responsa-bilit une classe indpendante, linterface Zend\EventManager\ListenerAggre-gate est disponible, voici les mthodes que lon devra alors implmenter au sein de notre classe responsable du traitement des vnements :

    Zend/EventManager/ListenerAggregatephpinterface ListenerAggregate{ public function attach(EventManagerInterface $events);

    public function detach(EventManagerInterface $events);}

    Voici un squelette de classe pouvant grer diffrents types dvnements en un seul objet :

  • 41Chapitre 3Les vnements

    Exemple de conteneur dcouteurclass TravailleurEvents implements ListenerAggregate{ protected $handlers = array();

    public function attach(EventManagerInterface $events){ $this->handlers[] = $events->attach('pause-dejeuner', array($this, 'absent')); $this->handlers[] = $events->attach('reunion', array($this, 'occupe'));}

    public function detach(EventManagerInterface $events){ foreach ($this->handlers as $key => $handler) { $events->detach($handler); unset($this->handlers[$key]); } $this->handlers = array();}

    public function absent(Event $e){ $horaire = $e->getParam(retour); echo Je suis actuellement absent du bureau.; if($horaire) { echo Je serai de retour vers . $horaire; }} public function occupe(Event $e) { $remplacant = $e->getParam(collegue,null); echo Je suis actuellement indisponible.; if($remplacant) { echo Pour toutes questions, merci de vous adresser . $remplacant; }}}

    Ce conteneur capable de grer les couteurs peut tre attach comme ceci :

    Utilisation dun conteneur dcouteur$events = new EventManager('Travailleur');$gestionHoraire = new TravailleurEvents();$events->attachAggregate($gestionHoraire);

    $midiEvent = new Event();$midiEvent->setName('pause-dejeuner');$midiEvent->setParam('retour', '14h');

    $brainstormingEvent = new Event(); $brainstormingEvent->setName('reunion');$brainstormingEvent->setParam('collegue', 'Fred');$events->trigger($midiEvent);$events->trigger($brainstormingEvent);

  • 42 Chapitre 3Les vnements

    Affichage en sortie

    Je suis actuellement absent du bureau. Je serai de retour vers 14 hJe suis actuellement indisponible. Pour toutes questions, merci de vous adresser Fred

    Les exemples ici sont trs simples, mais permettent davoir une premire approche sur l'intrt de ce genre de pratique assez rpandue dans le framework.

    Dcouvrons maintenant la mthode attachAggregate() de la classe EventMa-nager :

    Zend/EventManager/EventManagerphppublic function attachAggregate(ListenerAggregate $aggregate, $priority = 1){ return $aggregate->attach($this, $priority);}

    Comme on peut sy attendre, celle-ci passe en paramtre linstance du gestionnaire courant lagrgateur afin quil puisse attacher lensemble des vnements grs par la classe TravailleurEvents. Nous pourrions donc remplacer directement lins-truction suivante :

    Attache des couteurs du conteneur$events->attachAggregate($gestionHoraire);

    Par le code suivant :

    Attache des couteurs du conteneur$gestionHoraire->attach($events);

    Pour une question de comprhension et de lisibilit, nous conserverons la pre-mire instruction. Notons quil est aussi possible dutiliser la mthode attach() de la classe EventManager pour ajouter lagrgateur, car celle-ci a prvu de grer ce cas en vrifiant le type dargument reu :

    Zend/EventManager/EventManagerphppublic function attach($event, $callback = null, $priority = 1){ if ($event instanceof ListenerAggregate) { return $this->attachAggregate($event, $callback);}[]}

    La mthode attach() de la classe TravailleurEvents soccupe, elle, dattacher les vnements pause-dejeuner et reunion . Notons quil est aussi possible de passer la priorit de lcouteur en paramtre. Nous verrons dans la section consa-cre lordre de la gestion des notifications, lintrt de celle-ci.

  • 43Chapitre 3Les vnements

    Il est important de stocker les objets retourns depuis la mthode attach() du gestionnaire dvnements, car ces objets reprsentent un couteur dvnements :

    Zend/EventManager/EventManagerphppublic function attach($event, $callback = null, $priority = 1){[] $listener = new CallbackHandler($callback, array('event' => $event, 'priority' => $priority)); $this->events[$event]->insert($listener, $priority);return $listener;}

    Lobjet Zend\Stdlib\CallbackHandler est le type de lobjet qui reprsente lcou-teur et qui est retourn par cette mthode, il est important de garder une trace de cet objet afin de pouvoir utiliser la mthode detach() qui permet de supprimer lcoute dun vnement depuis son identifiant :

    Zend/EventManager/EventManagerphppublic function detach($listener);

    Le paramtre $listener peut tre un objet de type ListenerAggregate (comme on la vu pour la mthode attach() , cela fonctionne de la mme manire pour la mthode detach() ), ou un objet de type CallbackHandler.

    Nous pourrions vouloir amliorer lexemple prcdent avec lutilisation dvne-ments personnaliss plutt que de passer en paramtre les donnes que lon sou-haite transmettre lcouteur. Cela permettrait une encapsulation et un dcouplage plus complet et un descriptif des vnements plus intressant.

    Crer ses vnements personnaliss

    Lors du lancement dvnement, si la mthode trigger() reoit un vnement sous forme de chane de caractres, elle soccupe de lenvelopper sous la forme dobjet :

    Zend/EventManager/EventManagerphppublic function trigger($event, $target = null, $argv = array(), $callback = null){ if ($event instanceof EventInterface) { $e = $event; $event = $e->getName(); $callback = $target;} elseif ($target instanceof EventInterface) { $e = $target; $e->setName($event); $callback = $argv; } elseif ($argv instanceof EventInterface) { $e = $argv; $e->setName($event); $e->setTarget($target);

  • 44 Chapitre 3Les vnements

    } else { $e = new $this->eventClass(); $e->setName($event); $e->setTarget($target); $e->setParams($argv);}[]}

    Lobjet construit sera du type de lattribut $eventClass :

    Zend/EventManager/EventManagerphpprotected $eventClass = 'Zend\EventManager\Event';

    La classe Zend\EventManager\Event reprsente le type des vnements utilis par dfaut, il est possible de le changer avec la mthode :

    Zend/EventManager/EventManagerphppublic function setEventClass($class);

    Le paramtre $class est une chane de caractres qui reprsente le nom de la classe enveloppant les nouveaux vnements crs au sein du gestionnaire dv-nements. Cela permet dinitialiser l'vnement avec la classe de notre choix impl-mentant linterface descriptive des vnements Zend\EventManager\EventInter-face, car comme nous le voyons, la mthode triggerListeners() qui notifie les couteurs sattend un objet de ce type :

    Zend/EventManager/EventManagerphpprotected function triggerListeners($event, EventInterface $e, $callback = null);

    La premire des choses faire lors de la cration dvnements personnaliss est donc de crer une classe implmentant cette interface :

    Zend/EventManager/EventInterfacephpinterface EventInterface{ public function getName(); public function getTarget(); public function getParams(); public function getParam($name, $default = null); public function setName($name); public function setTarget($target); public function setParams($params);

    public function setParam($name, $value); public function stopPropagation($flag = true); public function propagationIsStopped();}

    Voici comment construire et utiliser un vnement personnalis en lui fournissant des options :

  • 45Chapitre 3Les vnements

    Construction dun vnement personnalis$event = new MyEvent();$event->setMyCustomParam($value);

    La classe MyEvent hrite de la classe de base Event et dfinit une mthode set-MyCustomParam() . Il ne reste alors plus qu linjecter dans la mthode trig-ger() :

    Lancement de lvnement personnalis$events->trigger(mon-evenement, $this, $event);

    Nous pouvons dfinir lattribut $target lvnement :

    Modification de la cible de lvnement

    $event->setTarget($this);

    Ou encore le nom de lvnement :

    Modification du nom de lvnement

    $event->setName(mon-evenement);

    Ce qui nous donne une encapsulation complte :

    Encapsulation de lvnement$event = new MyEvent();$event->setMyResult($value);$event->setTarget($this);$event->setName(mon-evenement);$events->trigger($event);

    Il est toujours possible de passer un callback pour le court-circuitage en deuxime argument lorsque lon utilise ce systme dencapsulation :

    Lancement de lvnement personnalis$events->trigger($event, $callback);

    Nous verrons la notion de court-circuit et de propagation dans les deux prochains chapitres.

    Maintenant que nous savons manipuler les objets de type EventInterface, nous allons nous intresser aux objets de rponse des couteurs, ainsi quau processus de propagation.

  • 46 Chapitre 3Les vnements

    Lobjet de rponse

    Chaque couteur dvnements retourne une rponse qui est stocke par le ges-tionnaire dvnements, et il peut tre parfois utile davoir accs cette liste de rponses. Nous avons pu remarquer que la mthode triggerListeners() , du ges-tionnaire dvnements, soccupe de grer les notifications et la rcupration des rponses afin de les retourner en fin de traitement :

    Zend/EventManager/EventManagerphpprotected function triggerListeners($event, EventInterface $e, $callback = null){ $responses = new ResponseCollection; $listeners = $this->getListeners($event);[] foreach ($listeners as $listener) { $responses->push(call_user_func($listener->getCallback(), $e)); if ($e->propagationIsStopped()) { $responses->setStopped(true); break; } if ($callback && call_user_func($callback, $responses->last())) { $responses->setStopped(true); break; }} return $responses;}

    Lobjet $responses de type Zend\EventManager\ResponseCollection est res-ponsable du stockage de la liste des rponses des couteurs notifis. Lobjet Res-ponseCollection hrite de la classe SplStack, pile base sur une liste doublement chane. La liste des mthodes de la classe ResponseCollection est assez courte :

    Zend/EventManager/ResponseCollectionphpclass ResponseCollection extends SplStack{ public function stopped(); public function setStopped($flag);

    public function first();

    public function last();

    public function contains($value);}

    La mthode stopped() permet de connatre ltat de la propagation, si elle a t stoppe ou non. Le premier objet de rponse peut tre rcupr par la mthode first() qui reprsente lobjet tout en bas de la pile, le dernier objet de rponse est extrait depuis la mthode last() . Enfin, la mthode contains() permet de savoir si une valeur donne est contenue dans la liste des rponses des couteurs.

    Nous allons maintenant expliquer la notion de court-circuit, ainsi que la manire

  • 47Chapitre 3Les vnements

    dagir sur la propagation des vnements, ce qui va nous permettre de contrler intgralement le workflow de lvnement.

    Stopper la propagation dun vnement

    Lors du lancement dun vnement, il est possible de stopper la propagation des notifications depuis un couteur. La premire manire de court-circuiter la pro-pagation dun vnement est de lindiquer explicitement au sein du callback de lcouteur :

    Arrt de la propagation dun vnement$events->attach(pause, function($e) { $e->stopPropagation(); return new MonObjetDeReponse();});

    Expliquons maintenant en dtail le comportement de la mthode stopPropaga-tion() :

    Zend/EventManager/Eventphppublic function stopPropagation($flag = true){ $this->stopPropagation = (bool) $flag;}public function propagationIsStopped(){ return $this->stopPropagation;}

    Ces deux mthodes permettent le maintien et la consultation de lattribut stop-Propagation qui est utilis dans le gestionnaire dvnements au sein de la m-thode triggerListeners() . En effet, cette mthode soccupe de rcuprer les couteurs de lvnement afin de les notifier :

    Zend/EventManager/EventManagerphpprotected function triggerListeners($event, EventInterface $e, $callback = null){ $responses = new ResponseCollection; $listeners = $this->getListeners($event);[] foreach ($listeners as $listener) { $responses->push(call_user_func($listener->getCallback(), $e)); if ($e->propagationIsStopped()) { $responses->setStopped(true); break; } []}[]}

  • 48 Chapitre 3Les vnements

    Le gestionnaire notifie les vnements un un et ajoute leur retour au tableau de rponses. Si lattribut $stopPropagation , que lon vrifie laide de la mthode propagationIsStopped() , est pass la valeur false au sein dun couteur, alors la pile de rponses est notifie et la propagation de lvnement est stoppe.

    La deuxime manire darrter la propagation dun vnement est de passer une fonction de callback la mthode trigger() , qui prend en paramtre la rponse retourne par le dernier couteur notifi et retourne un boolen. Si le retour de cette fonction de callback est vrai, alors la propagation sarrte.

    Reprenons la mthode triggerListener() du gestionnaire dvnements pour mieux comprendre comment est gr le court-circuitage :

    Zend/EventManager/EventManagerphpprotected function triggerListeners($event, EventInterface $e, $callback = null){ [] foreach ($listeners as $listener) { $responses->push(call_user_func($listener->getCallback(), $e)); if ($e->propagationIsStopped()) { $responses->setStopped(true); break; } if ($callback && call_user_func($callback, $responses->last())) { $responses->setStopped(true); break; }}[]}

    Lobjet de rponse est pass en paramtre la fonction de callback si elle existe, et si le retour de la fonction est vrai, alors la propagation est stoppe. Voici un exemple trivial :

    Exemple dutilisation de court-circuitage

    $events->attach('pause', function($e) { return new A();});$events->attach('pause', function($e) { return new B();});$events->attach('pause', function($e) { return new C();});$events->trigger(pause, $this, $params, function($r){ return $r instanceof B; // arrte la propagation ds quun couteur retourne un objet de type B});

    La propagation va alors sarrter ds la deuxime notification.

    Attention, comme indiqu plus haut, il suffit que le boolen de retour soit vrai, un

  • 49Chapitre 3Les vnements

    court-circuit comme ci-dessous arrte immdiatement la propagation :

    Court-circuitage depuis une valeur vraie

    $events->trigger(pause, $this, $params, function($r){ return false;});

    En effet, la condition darrt ne vrifie pas la valeur true mais si celle-ci est vrai . Pour rappel, en PHP tout ce qui nest pas faux est considr comme vrai. Pour plus de dtails, je vous suggre daller consulter la documentation de PHP pour voir que ce qui peut tre considr comme faux lors dune conversion en boolen, comme : le boolen FALSE, lentier 0, la chane vide, un tableau avec lments, le type NULL, etc.

    Nous avons donc vu quel point la notification des vnements est trs maniable et totalement contrle par le dveloppeur. Il est aussi ncessaire de comprendre comment ordonner les notifications afin de les matriser pleinement.

    Grer lordre des notificationsLors du lancement dun vnement, il est important de comprendre lordre utilis pour la notification des couteurs. Le gestionnaire dvnements se base sur un objet de queue priorit, la classe SplPriorityQueue qui permet de grer facilement les priorits. Les mthodes attach() du gestionnaire dvnements et du gestion-naire partag SharedEventManager possdent un argument facultatif qui permet de dfinir la priorit. Voici le prototype des deux mthodes :

    Zend/EventManager/EventManagerphppublic function attach($event, $callback = null, $priority = 1);

    Zend/EventManager/SharedEventManagerphppublic function attach($id, $event, $callback, $priority = 1);

    Par dfaut la priorit est gale 1. Il est donc possible de dfinir une priorit plus haute, suprieure ou gale 1, ce qui permet lcouteur dtre notifi le premier, ou une priorit plus basse qui notifiera lcouteur en fin de liste. Notons aussi quil est possible de connatre la priorit maximum de la liste en rcuprant lcouteur avec la plus haute priorit :

    Depuis la classe EventManager

    $handler = $events->getListeners('pause')->top();

    Depuis la classe SharedEventManager

    $handler = $events->getListeners('Developpeur','pause')->top();

    Ces instructions retournent un objet de type Zend\Stdlib\CallbackHandler dont

  • 50 Chapitre 3Les vnements

    il suffit de rcuprer la mtadonne associe la priorit :

    Rcupration de la priorit$priority_max = $handler->getMetadatum('priority');

    Nous comprenons maintenant que ces instructions vont tre utilises lors du lan-cement dun vnement afin de classer les couteurs par priorit :

    Zend/EventManager/EventManagerphpprotected function insertListeners($masterListeners, $listeners){ if (!count($listeners)) { return;}

    foreach ($listeners as $listener) { $priority = $listener->getMetadatum('priority'); if (null === $priority) { $priority = 1; } elseif (is_array($priority)) { $priority = array_shift($priority); } $masterListeners->insert($listener, $priority);}}

    Lobjet $masterListeners de type PriorityQueue soccupe dajouter les couteurs par priorit afin de pouvoir les parcourir dans lordre choisi par le dveloppeur. Chacun sera ensuite notifi son tour.

    Les vnements du framework

    Afin de matriser la gestion des vnements qui interviennent dans le framework et des interactions que lon va pouvoir effectuer dans notre application, nous devons tablir la liste des principaux vnements utiliss par le cur du Zend Framework 2 :

    Liste des vnements du gestionnaire de modules :

    Zend\ModuleManager\ModuleManager : loadModules lors du dbut de lini-tialisation des modules

    Pour chacun des modules :

    Zend\ModuleManager\ModuleManager : loadModule.resolve lors du chargement du module, rsolution de la classe Module du module en cours,

    Zend\ModuleManager\ModuleManager : loadModule lorsque le mo-dule est charg,

    Zend\ModuleManager\ModuleManager : loadModules.post lorsque les modules sont initialiss.

  • 51Chapitre 3Les vnements

    Liste des vnements de lapplication :

    Zend\Mvc\Application : bootstrap en fin de traitement, lorsque la mthode bootstrap a initialis les ressources ;

    Zend\Mvc\Application : route lors de la demande de routage ;

    Zend\Mvc\Application : dispatch lors de la demande du dispatch de laction en cours. Cet vnement est cout par le contrleur Zend\Mvc\Controller\Ac-tionController qui lance un vnement :

    Zend\Mvc\Controller\ActionController : dispatch lors de la distribu-tion par le contrleur abstrait ;

    Zend\Mvc\Application : render lors de la demande du rendu de la vue ;

    Zend\View\View : renderer lors de la demande du gestionnaire de rendus utiliser ;

    Zend\View\View : response lorsque le processus de rendu est termin, la rponse est prte ;

    Zend\Mvc\Application : finish lors de la fin du processus MVC, la rponse de lapplication est prte et retourne.

    Un vnement li aux erreurs du framework est disponible :

    Zend\Mvc\Application : dispatch.error lorsquune erreur est survenue lors du dispatch de laction (contrleur invalide, aucune route correspondante, etc.).

    Les vnements prsents lors du rendu de la vue ne sont pas lists ici, vous les retrouverez en dtail dans le chapitre consacr aux vues.

    Avec le Zend FrAmework 1La premire version du framework nutilise pas les vnements mais des hooks qui permettent de notifier les plugins ou contrleurs en fonction de la prsence ou non dune mthode de nom donne. Pour rappel, voici la liste des hooks disponibles sur le Zend Framework 1 :

    - routeStartup() et routeShutdown() pour notifier le dbut et la fin du routage.

    - dispatchLoopStartup() et dispatchLoopShutdown() qui notifie aprs le routage (et donc avant la distribution) et aprs la distribution.

    - preDispatch() et postDisptach() qui permettent aux plugins dtre notifis du dbut et de la fin de la distribution sur le contrleur

    - preDispatch() et postDisptach() qui permettent aux contrleurs dtre notifis avant et aprs laction courante.

    Nous avons maintenant toutes les cartes en main pour mieux comprendre les v-nements dans le Zend Framework 2. Cela nous permet de comprendre l'intrt

  • 52 Chapitre 3Les vnements

    de la programmation vnementielle compare aux hooks du Zend Framework 1 et l'tendue de tout ce quil est possible de faire. Nous verrons dans le chapitre consacr la classe Zend\Mvc\Application, point dentre de notre application, la gestion dtaille des vnements lis au cur du framework.

  • 4Linjection de dpendances

    Zend Framework 2 propose dans cette nouvelle version un composant capable de grer linjection de dpendances. Linjection de dpendances permet de crer une application o chacun des composants et chacune des classes ne seront plus dpendants fortement les uns des autres. Il ny a plus de dpendance code en dur.

    Prenons par exemple une classe A qui consomme un objet B, ncessaire son fonctionnement :

    Classe avec des dpendances dans le codeclass A{ protected $b; public function __construct(){ $this->b = new B();} public function uneaction(){ $this->b->quelquechose();}}

    La classe A est fortement couple avec la classe B. La dpendance avec B est crite en dur dans le code, ce qui diminue la flexibilit de notre application.

    Reprenons lexemple avec lajout dune interface, qui permet de sassurer que lob-jet reu en paramtre sera capable de remplir ses responsabilits en passant un contrat avec cette interface :

    Classe sans les dpendances dans le codeClass A{ protected $b; public function __construct(capabledefaireQuelquechose $b){ $this->b = $b;}

  • 54 Chapitre 4Linjection de dpendances

    public function uneaction(){ $this->b->quelquechose();}}

    interface capabledefaireQuelquechose{ public function quelquechose();}

    class B implements capabledefaireQuelquechose{ protected $b; public function quelquechose(){ // traitements}}

    Lobjet A est alors totalement dcoupl de lobjet B. Il est maintenant possible de crer un nouvel objet C, qui implmente cette nouvelle interface et que lon peut donner comme paramtre lobjet A. Il est aussi ncessaire dajouter les mthodes daltration de A, getter et setter , afin de pouvoir rcuprer ou modifier lobjet reu dans le constructeur.

    Examinons limplmentation de linjection de dpendances et du composant qui lui est ddi dans le Zend Framework 2.

    Configuration manuelle du composant DiLe composant du framework qui va nous servir grer nos injections de dpen-dances est le Zend\Di\Di. Ce composant doit tre configur afin de lui indiquer quelles vont tre les dpendances de chacun des objets. Pour le configurer manuel-lement, il est possible de lui fournir une liste de dfinitions sous forme de tableau qui lui permet denregistrer une liste de classes avec leurs dpendances. Pour les objets avec une configuration manquante, le composant se chargera lui-mme de lintrospection, il est donc prfrable dutiliser une configuration statique afin de gagner en performances.

    Voici un exemple de tableau de dfinitions que lon pourrait utiliser pour dcrire le router de lapplication ou encore la stratgie de rendu par dfaut, objets dont nous verrons l'utilit ultrieurement :

    Configuration du composant dinjection de dpendance

    $diConfig = new DiConfiguration(array('definition' => array('class' => array( 'Zend\Mvc\Router\RouteStack' => array( 'instantiator' => array( 'Zend\Mvc\Router\Http\TreeRouteStack', 'factory' ),),

  • 55Chapitre 4Linjection de dpendances

    'Zend\Mvc\Router\Http\TreeRouteStack' => array( 'instantiator' => array( 'Zend\Mvc\Router\Http\TreeRouteStack', 'factory' ),),[]'Zend\Mvc\View\DefaultRenderingStrategy' => array( 'setLayoutTemplate' => array( 'layoutTemplate' => array( 'required' => false, 'type' => false, ), ),),))));

    Chaque entre du sous-tableau class du tableau definition reprsente le nom de la classe avec pour valeur un tableau indiquant la procdure afin de linstancier et les paramtres qui lui sont ncessaires. Nous remarquons que pour obtenir une instance dobjet correspondant lidentifiant Zend\Mvc\Router\RouteStack il est ncessaire dutiliser la mthode factory() de la classe Zend\Mvc\Router\Http\TreeRouteStack.

    Lidentifiant de la configuration reprsente un type dobjet, qui nest pas ncessai-rement le type de la classe qui sera instancie. Lidentifiant Zend\Mvc\Router\RouteStack est en ralit une interface au sein du framework, elle ne peut donc pas rellement tre instancie. Lorsque nous souhaiterons obtenir, depuis notre composant dinjection de dpendances, une instance de la classe Zend\Mvc\Router\RouteStack, le gestionnaire soccupera de lire la dfinition de cet objet et pourra alors instancier la classe concerne depuis les paramtres de la dfinition. Ceci nous permet dattacher une classe de notre choix aux diffrentes interfaces, comme ici pour linterface RouteStack o nous lui affectons la correspondance avec un objet de type TreeRouteStack.

    Une fois les dfinitions cres, nous pouvons indiquer les paramtres que l'on souhaite utiliser avec les dfinitions lors de l'instanciation. Prenons un exemple de configuration avec linstanciation dun adaptateur de base de donnes et le passage de nos paramtres :

    Configuration du Di

    $di = new Zend\Di\Di;$diConfig = new Zend\Di\Configuration( array('instance' => array( 'Zend\Db\Adapter\Adapter' => array( 'parameters' => array( 'driver' => array( 'driver' => 'Pdo', 'dsn' => 'mysql:dbname=db;host=host', 'username' => 'username', 'password' => 'password' ), ) ),)));$diConfig->configure($di);$db = $di->get('Zend\Db\Adapter\Adapter');

  • 56 Chapitre 4Linjection de dpendances

    Nous savons maintenant configurer et utiliser le composant dinjection de dpen-dances. Voyons comment nous pouvons automatiser ce genre de configuration afin dviter dcrire manuellement toutes les configurations dont on pourrait avoir besoin.

    Configuration automatique du composant DiNous venons de voir comment configurer manuellement le composant dinjection de dpendances. Seulement, nous pouvons nous demander comment celui-ci gre la dfinition dune classe dont nous ne lui avons pas fourni de description. Dans ce cas, le framework va alors charger les dfinitions des classes la demande, ce qui permet de nutiliser des ressources que lorsque cela est ncessaire.

    Prenons un exemple en lui demandant de rcuprer une instance dont il ne connait pas la dfinition. La demande dobjet se fait depuis la mthode get() de la c