Sie sind auf Seite 1von 480

Sauf mention contraire, le contenu de cet ouvrage est publi sous la licence :

Creative Commons BY-NC-SA 2.0


La copie de cet ouvrage est autorise sous rserve du respect des conditions de la licence
Texte complet de la licence disponible sur : http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
Simple IT 2012 - ISBN : 979-10-90085-36-7
Avant-propos
L
o rsque jtais plus jeune, jai cr mes premiers sites web grce au Site du Zro
que vous connaissez sans doute. Une fois le cours de Mathieu Nebra sur le PHP
achev, je mtais dit que javais plus ou moins fait le tour de ce langage et
quil me resterait encore seulement deux ou trois choses apprendre. Dans le but
de mamliorer, jai parcouru le Web pour dcouvrir les travaux dautres passionns.
Beaucoup de personnes concevaient leur code de faon compltement indite pour moi.
Aprs quelques recherches, jai appris quil sagissait de la programmation oriente
objet. Jai alors dcid dapprofondir mes recherches sur le sujet, et ai t trs surpris
de ne trouver aucun site proposant un cours sur cette mthode de programmation.
Partout des trucs et astuces , sans que personne nexplique vritablement les bases
de ce concept et surtout ses possibilits.
Lass de ne pas trouver de cours qui me corresponde, jai dcid dapprendre par le
biais de la documentation. Jy ai alors dnich tout ce dont javais besoin sur laspect
programmation : je matrisais la syntaxe de la POO et je pouvais me dbrouiller.
Cependant, je sentais bien quil me manquait une norme part de connaissance : jtais
incapable de concevoir une application entire avec cette mthode de programmation.
Javais, en ralit, cruellement besoin de pratique.
On ma alors redirig vers un framework en me disant quil sagissait dune boite
outils contenant une multitude de fonctions prtes tre utilises pour concevoir son
site plus rapidement, le tout programm de faon oriente objet. Enthousiaste lide
de dcouvrir une nouvelle notion, je my suis prcipit. Lapprentissage fut dicile, mais
javais enn un exemple concret dutilisation de la POO. Cest ensuite en dcortiquant
cette boite outils, en analysant chaque outil et ce qui les liait, que jai enn dcouvert
toute la puissance de la POO. Jai eu un dclic, tout est devenu plus clair mes yeux :
partir de ce moment-l, jtais sr de programmer uniquement de cette faon tant ce
concept tait bien pens.
Je me suis alors dit que ce ntait pas normal quil ny ait aucun cours francophone
disponible sur le web qui explique de A Z la programmation oriente objet en PHP :
son fonctionnement, ses possibilits et son utilisation. Pour viter aux autres passionns
du web dtre perdu dans le monde de la POO, comme ce fut le cas pour moi mes
dbuts, jai dcid de crer le cours que jaurais tant aim avoir mes dbuts.
Cette nouvelle faon de penser vous intresse ? Vous avez envie de dcouvrir une nou-
velle faon de vous organiser ? Dun code propre et facilement rutilisable ? a tombe
i
CHAPITRE 0. AVANT-PROPOS
bien, ce livre est l pour a ! En partant du principe que vous ne connaissez strictement
rien la programmation oriente objet, il vous introduira dans ce nouveau monde et
vous apportera les connaissances ncessaires pour que vous soyez capables de raliser
un site complet en suivant les principes de la POO.
La POO, une faon bien dirente de voir les choses
Vous devez sans doute vous demander une chose : Dans les grandes lignes, quelle
dirence y a-t-il entre la POO et la mthode de programmation que je connais ac-
tuellement ? Lexpression mme programmation oriente objet , nous permet de
comprendre que nous allons programmer avec des objets. Mais concrtement, les objets,
quest-ce que cest ?
vrai dire, les objets avec lesquels nous allons travailler dirent peu des objets que
vous connaissez dj. Regardez autour de vous ! Des objets, il y en a partout : autour
de vous, il y a peut-tre une lampe ou une voiture. Ces objets possdent des caractris-
tiques et des fonctionnalits (la lampe sallume, la voiture dmarre, etc.). Depuis que
vous tes ns, vous vivez entours dobjets. Et bien pourquoi ne pas programmer avec
des objets galement ? Cest justement le but de la programmation oriente objet. Le
principe de base est simple : lapplication que vous dvelopperez ne sera rien dautre
quun ensemble dobjets qui interagissent entre eux. Nous allons donc crer des objets
et nous en servir et ce, tout au long de ce livre ! Comme vous vous en doutez, le nombre
de choses que nous pouvons faire avec nos objets est assez important.
Si je devais vous citer les trois des avantages majeurs de la POO, ce serait :
1. Une conception plus intuitive : au dbut, vous aurez du mal vous y faire,
mais une fois le concept bien cern (ce qui est lobjectif premier de ce livre), cela
deviendra bien plus facile et surtout, plus intuitif.
2. Une rutilisation facilite : comme nous le verrons plus tard, la programma-
tion oriente objet permet de rutiliser son code facilement en lexportant dans
une application indpendante.
3. Une distribution plus aise : si vous souhaitez partager votre code avec un
autre dveloppeur, vous vous rendrez compte que cela est plus facile faire (cest
en grande partie pour quoi les frameworks et extensions de PHP sont programms
de faon oriente objet).
Il ne faut donc pas avoir peur de la programmation oriente objet : une fois bien
matrise, elle ne vous apportera que des avantages !
Quallez-vous apprendre en lisant ce livre ?
Je pars du principe que vous navez aucune connaissance en programmation oriente
objet, seulement une base solide du langage PHP. Je vous propose de suivre quatre
parties :
ii
COMMENT LIRE CE LIVRE?
1. Les bases de la POO : cette premire partie thorique visera vous enseigner
les toutes les prrequis indispensables que vous devez connatre pour continuer.
Nous poserons ainsi les bases de ce concept et commencerons programmer
notre premire application au cours de travaux pratiques. Nous introduirons no-
tamment des notions importantes telles que les classes, les objets, les instances,
le principe dencapsulation et lhritage.
2. Techniques avances : dans cette seconde partie thorique seront abordes des
notions plus complexes. Vous entrerez ainsi dans les profondeurs de la program-
mation oriente objet, notamment en abordant les concepts dinterfaces, de traits,
dexceptions et de designs patterns par exemple. la n de cette partie, un cha-
pitre sera ddi la ralisation dun systme de news pour mettre en pratique
toute la thorie accumule.
3. Ralisation dun site web : cette troisime partie est purement pratique, au-
cune nouvelle notion thorique ne sera aborde car vous possderez toutes les
connaissances ncessaires pour raliser un site web entirement programm de
faon oriente objet. Cependant, vous manquerez cruellement de pratique, et
mme si vous matriserez la dimension syntaxique de la POO, il sera fort pos-
sible que vous narriviez pas encore penser orient objet . Cette partie est
justement l pour vous y aider.
4. Annexes : cette dernire partie contient des parties que vous pouvez lire quand
vous le souhaitez. Le niveau requis pour lire chaque partie sera spci au dbut
de ces dernires. Gnralement, les parties contenues dans ce chapitre sont des
notions que je ne pouvais pas introduire dans le cours au risque de le compliquer
inutilement (il sagit essentiellement de notions peu importantes ou rarement
utilises).
Comment lire ce livre ?
Suivez lordre des chapitres
Lisez ce livre comme on lit un roman. Il a t conu pour cela.
Contrairement beaucoup de livres techniques o il est courant de lire en diagonale et
de sauter certains chapitres, il est ici trs fortement recommand de suivre lordre du
cours, moins que vous ne soyez dj un peu expriments.
Pratiquez en mme temps
Pratiquez rgulirement. Nattendez pas davoir ni de lire ce livre pour allumer votre
ordinateur et faire vos propres essais.
iii
CHAPITRE 0. AVANT-PROPOS
Utilisez les codes web!
An de tirer parti du Site du Zro dont ce livre est issu, celui-ci vous propose ce quon
appelle des codes web . Ce sont des codes six chires saisir sur une page du Site
du Zro pour tre automatiquement redirig vers un site web sans avoir en recopier
ladresse.
Pour utiliser les codes web, rendez-vous sur la page suivante
1
:
http://www.siteduzero.com/codeweb.html
Un formulaire vous invite rentrer votre code web. Faites un premier essai avec le code
ci-dessous :

Tester le code web


Code web : 123456
Ces codes web ont deux intrts :
ils vous redirigent vers les sites web prsents tout au long du cours, vous permettant
ainsi dobtenir les logiciels dans leur toute dernire version ;
ils vous permettent de tlcharger les codes sources inclus dans ce livre, ce qui vous
vitera davoir recopier certains programmes un peu longs.
Ce systme de redirection nous permet de tenir jour le livre que vous avez entre les
mains sans que vous ayez besoin dacheter systmatiquement chaque nouvelle dition.
Si un site web change dadresse, nous modierons la redirection mais le code web
utiliser restera le mme. Si un site web disparat, nous vous redirigerons vers une page
du Site du Zro expliquant ce qui sest pass et vous proposant une alternative.
En clair, cest un moyen de nous assurer de la prennit de cet ouvrage sans que vous
ayez faire quoi que ce soit !
Remerciements
La ralisation de ce livre ne se serait pas faite sans le soutien de plusieurs personnes
que je souhaite remercier :
Ma famille, notamment mes parents, qui mont toujours soutenu dans ce que jen-
treprenais.
Lquipe de Simple IT, notamment Mathieu Nebra et Pierre Dubuc qui ont cru en ce
projet, ainsi que Zeina Hadati, Anna Schurtz et Jonathan Baudoin qui ont eectu
un trs gros travail pour le raliser.
Tous les membres du Site du Zro, notamment Baptiste Clavi (Talus), Christophe
Tafani-Dereeper (christophetd), Haku, Nami Doc, artragis, gripsou, Gugelhupf et
bien dautres, qui mont encourag et apport leurs prcieux conseils pour donner au
cours la qualit quil possde aujourdhui.
1. Vous pouvez aussi utiliser le formulaire de recherche du Site du Zro, section Code web .
iv
Sommaire
Avant-propos i
La POO, une faon bien dirente de voir les choses . . . . . . . . . . . . . . ii
Quallez-vous apprendre en lisant ce livre ? . . . . . . . . . . . . . . . . . . . . ii
Comment lire ce livre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
I [Thorie] Les bases de la POO 1
1 Introduction la POO 3
Quest-ce que la POO? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Crer une classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2 Utiliser la classe 11
Crer et manipuler un objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Les accesseurs et mutateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Le constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Lauto-chargement de classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3 Loprateur de rsolution de porte 31
Les constantes de classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Les attributs et mthodes statiques . . . . . . . . . . . . . . . . . . . . . . . . 35
4 Manipulation de donnes stockes 41
v
SOMMAIRE
Une entit, un objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Lhydratation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Grer sa BDD correctement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5 TP : Mini-jeu de combat 61
Ce quon va faire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Premire tape : le personnage . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Seconde tape : stockage en base de donnes . . . . . . . . . . . . . . . . . . . 72
Troisime tape : utilisation des classes . . . . . . . . . . . . . . . . . . . . . . 77
Amliorations possibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6 Lhritage 93
Notion dhritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Un nouveau type de visibilit : protected . . . . . . . . . . . . . . . . . . . . . 100
Imposer des contraintes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Rsolution statique la vole . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7 TP : Des personnages spcialiss 117
Ce que nous allons faire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Amliorations possibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8 Les mthodes magiques 137
Le principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Surcharger les attributs et mthodes . . . . . . . . . . . . . . . . . . . . . . . 138
Linariser ses objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Autres mthodes magiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
II [Thorie] Techniques avances 157
9 Les objets en profondeur 159
Un objet, un identiant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
Comparons nos objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Parcourons nos objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10 Les interfaces 169
vi
SOMMAIRE
Prsentation et cration dinterfaces . . . . . . . . . . . . . . . . . . . . . . . 170
Hriter ses interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Interfaces prdnies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
11 Les exceptions 189
Une dirente gestion des erreurs . . . . . . . . . . . . . . . . . . . . . . . . . 190
Des exceptions spcialises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Grer les erreurs facilement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
12 Les traits 205
Le principe des traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
Plus loin avec les traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
13 LAPI de rexivit 217
Obtenir des informations sur ses classes . . . . . . . . . . . . . . . . . . . . . 218
Obtenir des informations sur les attributs de ses classes . . . . . . . . . . . . 223
Obtenir des informations sur les mthodes de ses classes . . . . . . . . . . . . 227
Utiliser des annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
14 UML : prsentation (1/2) 241
UML, kzako ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Modliser une classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Modliser les interactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
15 UML : modlisons nos classes (2/2) 251
Ayons les bons outils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Modliser une classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Modliser les interactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Exploiter son diagramme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
16 Les design patterns 271
Laisser une classe crant les objets : le pattern Factory . . . . . . . . . . . . . 272
couter ses objets : le pattern Observer . . . . . . . . . . . . . . . . . . . . . 273
Sparer ses algorithmes : le pattern Strategy . . . . . . . . . . . . . . . . . . . 280
Une classe, une instance : le pattern Singleton . . . . . . . . . . . . . . . . . . 283
Linjection de dpendances . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
vii
SOMMAIRE
17 TP : un systme de news 291
Ce que nous allons faire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
III [Pratique] Ralisation dun site web 311
18 Description de lapplication 313
Une application, quest ce que cest ? . . . . . . . . . . . . . . . . . . . . . . . 314
Les entrailles de lapplication . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Rsum du droulement de lapplication . . . . . . . . . . . . . . . . . . . . . 323
19 Dveloppement de la bibliothque 325
Lapplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Le routeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
Le back controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
La page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
Bonus : lutilisateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
Bonus 2 : la conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
20 Le frontend 359
Lapplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
Le module de news . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Ajoutons des commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
21 Le backend 385
Lapplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
Le module de connexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
Le module de news . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Noublions pas les commentaires ! . . . . . . . . . . . . . . . . . . . . . . . . . 401
22 Grer les formulaires 409
Le formulaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
Les validateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
Le constructeur de formulaires . . . . . . . . . . . . . . . . . . . . . . . . . . 427
Le gestionnaire de formulaires . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
viii
SOMMAIRE
IV Annexes 437
23 Loprateur instanceof 439
Prsentation de loprateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
instanceof et lhritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
instanceof et les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
ix
SOMMAIRE
x
Premire partie
[Thorie] Les bases de la POO
1
Chapitre 1
Introduction la POO
Dicult :
A
lors a y est, vous avez dcid de vous lancer dans la POO en PHP? Sage dcision !
Nous allons donc plonger dans ce vaste domaine par une introduction cette nouvelle
faon de penser : quest-ce que la POO? En quoi a consiste ? En quoi est-ce si di-
rent de la mthode que vous employez pour dvelopper votre site web ? Tant de questions
auxquelles je vais rpondre.
Cependant, puisque je sais que vous avez hte de commencer, nous allons entamer srieuse-
ment les choses en crant notre premire classe ds la n de ce chapitre. Vous commencerez
ainsi vos premiers pas dans la POO en PHP!
3
CHAPITRE 1. INTRODUCTION LA POO
Quest-ce que la POO?
Il tait une fois le procdural
Commenons ce cours en vous posant une question : comment est reprsent votre
code ? La rponse est unique : vous avez utilis la reprsentation procdurale qui
consiste sparer le traitement des donnes des donnes elles-mmes. Par exemple,
vous avez un systme de news sur votre site. Dun ct, vous avez les donnes (les
news, une liste derreurs, une connexion la BDD, etc.) et de lautre ct vous avez
une suite dinstructions qui viennent modier ces donnes. Si je ne me trompe pas,
cest de cette manire que vous codez.
Cette faon de se reprsenter votre application vous semble sans doute la meilleure
puisque cest la seule que vous connaissez. Dailleurs, vous ne voyez pas trop com-
ment votre code pourrait tre reprsent de manire dirente. Eh bien cette poque
dignorance est rvolue : voici maintenant la programmation oriente objet !
Puis naquit la programmation oriente objet
Alors, quest-ce donc que cette faon de reprsenter son code ? La POO
1
, cest tout sim-
plement faire de son site un ensemble dobjets qui interagissent entre eux. En dautres
termes : tout est objet.
Dnition dun objet
Je suis sr que vous savez ce que cest. Dailleurs, vous en avez pas mal ct de vous :
je suis sr que vous avez un ordinateur, une lampe, une chaise, un bureau, ou que
sais-je encore. Ce sont tous des objets. En programmation, les objets sont sensiblement
la mme chose.
Lexemple le plus pertinent quand on fait un cours sur la POO est dutiliser lexemple
du personnage dans un jeu de combat. Ainsi, imaginons que nous ayons un objet
Personnage dans notre application. Un personnage a des caractristiques :
une force ;
une localisation ;
une certaine exprience ;
et enn des dgts.
Toutes ses caractristiques correspondent des valeurs. Comme vous le savez srement,
les valeurs sont stockes dans des variables. Cest toujours le cas en POO. Ce sont des
variables un peu spciales, mais nous y reviendrons plus tard.
Mis part ces caractristiques, un personnage a aussi des capacits. Il peut :
frapper un autre personnage ;
gagner de lexprience ;
1. Programmation Oriente Objet
4
QUEST-CE QUE LA POO?
se dplacer.
Ces capacits correspondent des fonctions. Comme pour les variables, ce sont des
fonctions un peu spciales et on y reviendra en temps voulu. En tout cas, le principe
est l.
Vous savez dsormais quon peut avoir des objets dans une application. Mais do
sortent-ils ? Dans la vie relle, un objet ne sort pas de nulle part. En eet, chaque objet
est dni selon des caractristiques et un plan bien prcis. En POO, ces informations
sont contenues dans ce quon appelle des classes.
Dnition dune classe
Comme je viens de le dire, les classes contiennent la dnition des objets que lon va
crer par la suite. Prenons lexemple le plus simple du monde : les gteaux et leur
moule. Le moule est unique. Il peut produire une quantit innie de gteaux. Dans ce
cas-l, les gteaux sont les objets et le moule est la classe : le moule va dnir la forme
du gteau. La classe contient donc le plan de fabrication dun objet et on peut sen
servir autant quon veut an dobtenir une innit dobjets.
Concrtement, une classe, cest quoi ?
Une classe est une entit regroupant des variables et des fonctions. Chacune de ces fonc-
tions aura accs aux variables de cette entit. Dans le cas du personnage, nous aurons
une fonction frapper(). Cette fonction devra simplement modier la variable $degats
du personnage en fonction de la variable $force. Une classe est donc un regroupement
logique de variables et fonctions que tout objet issu de cette classe possdera.
Dnition dune instance
Une instance, cest tout simplement le rsultat dune instanciation. Une instanciation,
cest le fait dinstancier une classe. Instancier une classe, cest se servir dune classe
an quelle nous cre un objet. En gros, une instance est un objet.
Exemple : cration dune classe
Nous allons crer une classe Personnage (sous forme de schma bien entendu). Celle-ci
doit contenir la liste des variables et des fonctions que lon a cites plus haut : cest la
base de tout objet Personnage. Chaque instance de cette classe possdera ainsi toutes
ces variables et fonctions. Voici donc cette fameuse classe la gure 1.1.
Vous voyez donc les variables et fonctions stockes dans la classe Personnage. Sachez
quen ralit, on ne les appelle pas comme a : il sagit dattributs (ou proprits) et
de mthodes. Un attribut dsigne une variable et une mthode dsigne une fonction.
5
CHAPITRE 1. INTRODUCTION LA POO
Figure 1.1 Le schma de notre classe
Ainsi, tout objet Personnage aura ces attributs et mthodes. On pourra modier ces
attributs et invoquer ces mthodes sur notre objet an de modier ses caractristiques
ou son comportement.
Le principe dencapsulation
Lun des gros avantages de la POO est que lon peut masquer le code lutilisateur
(lutilisateur est ici celui qui se servira de la classe, pas celui qui chargera la page depuis
son navigateur). Le concepteur de la classe a englob dans celle-ci un code qui peut tre
assez complexe et il est donc inutile voire dangereux de laisser lutilisateur manipuler
ces objets sans aucune restriction. Ainsi, il est important dinterdire lutilisateur de
modier directement les attributs dun objet.
Prenons lexemple dun avion o sont disponibles des centaines de boutons. Chacun de
ces boutons constituent des actions que lon peut eectuer sur lavion. Cest linterface
de lavion. Le pilote se moque de quoi est compos lavion : son rle est de le piloter.
Pour cela, il va se servir des boutons an de manipuler les composants de lavion. Le
pilote ne doit pas se charger de modier manuellement ces composants : il pourrait
faire de grosses btises.
Le principe est exactement le mme pour la POO : lutilisateur de la classe doit se
contenter dinvoquer les mthodes en ignorant les attributs. Comme le pilote de lavion,
il na pas les trifouiller. Pour instaurer une telle contrainte, on dit que les attributs
sont privs. Pour linstant, ceci peut sans doute vous paratre abstrait, mais nous y
reviendrons.
Bon, je pense que jai assez parl, commenons par crer notre premire classe !
6
CRER UNE CLASSE
Crer une classe
Syntaxe de base
Le but de cette section va tre de traduire la gure prcdente en code PHP. Avant
cela, je vais vous donner la syntaxe de base de toute classe en PHP :
1 <?php
2 class Personnage // Prsence du mot -cl class suivi du nom de
la classe.
3 {
4 // Dclaration des attributs et mthodes ici.
5 }
6 ?>
Cette syntaxe est retenir absolument. Heureusement, elle est simple.
Ce quon vient de faire est donc de crer le moule, le plan qui dnira nos objets. On
verra dans le prochain chapitre comment utiliser ce plan an de crer un objet. Pour
linstant, contentons-nous de construire ce plan et de lui ajouter des fonctionnalits.
La dclaration dattributs dans une classe se fait en crivant le nom de lattribut
crer, prcd de sa visibilit.
Visibilit dun attribut ou dune mthode
La visibilit dun attribut ou dune mthode indique partir do on peut y avoir accs.
Nous allons voir ici deux types de visibilit : public et private.
Le premier, public, est le plus simple. Si un attribut ou une mthode est public,
alors on pourra y avoir accs depuis nimporte o, depuis lintrieur de lobjet (dans les
mthodes quon a cres), comme depuis lextrieur. Je mexplique. Quand on cre un
objet, cest principalement pour pouvoir exploiter ses attributs et mthodes. Lextrieur
de lobjet, cest tout le code qui nest pas dans votre classe. En eet, quand vous crerez
un objet, cet objet sera reprsent par une variable, et cest partir delle quon pourra
modier lobjet, appeler des mthodes, etc. Vous allez donc dire PHP dans cet objet,
donne-moi cet attribut ou dans cet objet, appelle cette mthode : cest a, appeler
des attributs ou mthodes depuis lextrieur de lobjet.
Le second, private, impose quelques restrictions. On naura accs aux attributs et
mthodes seulement depuis lintrieur de la classe, cest--dire que seul le code voulant
accder un attribut priv ou une mthode prive crit(e) lintrieur de la classe
fonctionnera. Sinon, une jolie erreur fatale sachera disant que vous ne pouvez pas
accder telle mthode ou tel attribut parce quil ou elle est priv(e).
L, a devrait faire tilt dans votre tte : le principe dencapsulation! Cest de cette
manire quon peut interdire laccs nos attributs.
7
CHAPITRE 1. INTRODUCTION LA POO
Cration dattributs
Pour dclarer des attributs, on va donc les crire entre les accolades, les uns la suite
des autres, en faisant prcder leurs noms du mot-cl private, comme a :
1 <?php
2 class Personnage
3 {
4 private $_force; // La force du personnage
5 private $_localisation; // Sa localisation
6 private $_experience; // Son exprience
7 private $_degats; // Ses dgts
8 }
9 ?>
Vous pouvez constater que chaque attribut est prcd dun underscore ( _ ). Ceci
est une notation quil est prfrable de respecter (il sagit de la notation PEAR) qui
dit que chaque nom dlment priv (ici il sagit dattributs, mais nous verrons plus
tard quil peut aussi sagir de mthodes) doit tre prcd dun underscore.
Vous pouvez initialiser les attributs lorsque vous les dclarez (par exemple, leur mettre
une valeur de 0 ou autre). Exemple :
1 <?php
2 class Personnage
3 {
4 private $_force = 50; // La force du personnage ,
par dfaut 50.
5 private $_localisation = 'Lyon'; // Sa localisation , par d
faut Lyon.
6 private $_experience = 1; // Son exprience , par d
faut 1.
7 private $_degats = 0; // Ses dgts , par dfaut
0.
8 }
9 ?>
La valeur que vous leur donnez par dfaut doit tre une expres-
sion. Par consquent, leur valeur ne peut tre issue dun appel
une fonction (private $_attribut = intval (azerty)), dune op-
ration (private $_attribut = 1 + 1) ou dune concatnation (private
$_attribut = Mon . super . attribut).
Cration de mthodes
Pour la dclaration de mthodes, il sut de faire prcder le mot-cl function la
visibilit de la mthode. Les types de visibilit des mthodes sont les mmes que les
attributs. Les mthodes nont en gnral pas besoin dtre masques lutilisateur,
8
CRER UNE CLASSE
vous les mettrez souvent en public ( moins que vous teniez absolument ce que
lutilisateur ne puisse pas appeler cette mthode, par exemple sil sagit dune fonction
qui simplie certaines tches sur lobjet mais qui ne doit pas tre appele nimporte
comment).
1 <?php
2 class Personnage
3 {
4 private $_force; // La force du personnage
5 private $_localisation; // Sa localisation
6 private $_experience; // Son exprience
7 private $_degats; // Ses dgts
8
9 public function deplacer () // Une mthode qui dplacera le
personnage (modifiera sa localisation).
10 {
11
12 }
13
14 public function frapper () // Une mthode qui frappera un
personnage (suivant la force qu'il a).
15 {
16
17 }
18
19 public function gagnerExperience () // Une mthode augmentant
l'attribut $experience du personnage.
20 {
21
22 }
23 }
24 ?>
Et voil !
De mme que lunderscore prcdant les noms dlments privs, vous pouvez
remarquer que le nom des classes commence par une majuscule. Il sagit aussi
du respect de la notation PEAR.
En rsum
Une classe, cest un ensemble de variables et de fonctions (attributs et mthodes).
Un objet, cest une instance de la classe pour pouvoir lutiliser.
Tous vos attributs doivent tre privs. Pour les mthodes, peu importe leur visibilit.
Cest ce quon appelle le principe dencapsulation.
On dclare une classe avec le mot-cl class suivi du nom de la classe, et enn deux
accolades ouvrantes et fermantes qui encercleront la liste des attributs et mthodes.
9
CHAPITRE 1. INTRODUCTION LA POO
10
Chapitre 2
Utiliser la classe
Dicult :
U
ne classe, cest bien beau mais, au mme titre quun plan de construction pour
une maison, si on ne sait pas comment se servir de notre plan pour construire notre
maison, cela ne sert rien ! Nous verrons donc ainsi comment se servir de notre
classe, notre modle de base, an de crer des objets et pouvoir sen servir.
Ce chapitre sera aussi loccasion dapprofondir un peu le dveloppement de nos classes :
actuellement, la classe que lon a cre contient des mthodes vides, chose un peu inutile
vous avouerez. Pas de panique, on va soccuper de tout a !
11
CHAPITRE 2. UTILISER LA CLASSE
Crer et manipuler un objet
Crer un objet
Nous allons voir comment crer un objet, cest--dire que nous allons utiliser notre
classe an quelle nous fournisse un objet. Pour crer un nouvel objet, vous devez faire
prcder le nom de la classe instancier du mot-cl new, comme ceci :
1 <?php
2 $perso = new Personnage ();
3 ?>
Ainsi, $perso sera un objet de type Personnage. On dit que lon instancie la classe
Personnage, que lon cre une instance de la classe Personnage.
Appeler les mthodes de lobjet
Pour appeler une mthode dun objet, il va falloir utiliser un oprateur : il sagit de
loprateur -> (une che compose dun tiret suivi dun chevron fermant). Celui-ci
sutilise de la manire suivante. gauche de cet oprateur, on place lobjet que lon
veut utiliser. Dans lexemple pris juste au-dessus, cet objet aurait t $perso. droite
de loprateur, on spcie le nom de la mthode que lon veut invoquer.
Et puisquun exemple vaut mieux quun long discours. . .
1 <?php
2 // Nous crons une classe Personnage .
3 class Personnage
4 {
5 private $_force;
6 private $_localisation;
7 private $_experience;
8 private $_degats;
9
10 // Nous dclarons une mthode dont le seul but est d'afficher
un texte.
11 public function parler ()
12 {
13 echo 'Je suis un personnage !';
14 }
15 }
16
17 $perso = new Personnage ();
18 $perso ->parler ();
La ligne 18 signie donc va chercher lobjet $perso, et invoque la mthode parler()
sur cet objet . Notez donc bien quelque part lutilisation de cet oprateur : de manire
gnrale, il sert accder un lment de la classe. Ici, on sen est servi pour atteindre
une mthode, mais nous verrons plus tard quil nous permettra aussi datteindre un
attribut.
12
CRER ET MANIPULER UN OBJET
Accder un lment depuis la classe
Je viens de vous faire crer un objet, et vous tes maintenant capables dappeler une
mthode. Cependant, le contenu de cette dernire tait assez simpliste : elle ne faisait
quacher un message. Ici, nous allons voir comment une mthode peut accder aux
attributs de lobjet.
Lorsque vous avez un objet, vous savez que vous pouvez invoquer des mthodes grce
loprateur ->. Je vous ai par ailleurs dit que ctait galement grce cet oprateur
quon accdait aux attributs de la classe. Cependant, rappelez-vous : nos attributs sont
privs. Par consquent, ce code lvera une erreur fatale :
1 <?php
2 class Personnage
3 {
4 private $_force;
5 private $_experience;
6 private $_degats;
7 }
8
9 $perso = new Personnage ();
10 $perso ->_experience = $perso ->_experience + 1; // Une erreur
fatale est leve suite cette instruction.
Ici, on essaye daccder un attribut priv hors de la classe. Ceci est interdit, donc PHP
lve une erreur. Dans notre exemple (qui essaye en vain daugmenter de 1 lexprience
du personnage), il faudra demander la classe daugmenter lexprience. Pour cela,
nous allons crire une mthode gagnerExperience() :
1 <?php
2 class Personnage
3 {
4 private $_experience;
5
6 public function gagnerExperience ()
7 {
8 // Cette mthode doit ajouter 1 l'exprience du
personnage.
9 }
10 }
11
12 $perso = new Personnage ();
13 $perso ->gagnerExperience ();
Oui mais voil : comment accder lattribut $_experience dans notre mthode ?
Cest l quintervient la pseudo-variable $this.
Dans notre script, $perso reprsente lobjet. Il est donc facile dappeler une mthode
partir de cette variable. Mais dans notre mthode, nous navons pas cette variable pour
accder notre attribut $_experience pour le modier ! Du moins, cest ce que vous
croyez. En fait, un paramtre reprsentant lobjet est pass implicitement chaque
13
CHAPITRE 2. UTILISER LA CLASSE
mthode de la classe. Regardez plutt cet exemple :
1 <?php
2 class Personnage
3 {
4 private $_experience = 50;
5
6 public function afficherExperience ()
7 {
8 echo $this ->_experience;
9 }
10 }
11
12 $perso = new Personnage ();
13 $perso ->afficherExperience ();
Commentons la ligne 8. Vous avez sans doute reconnu la structure echo ayant pour
rle dacher une valeur. Ensuite, vous reconnaissez la variable $this dont je vous ai
parl : elle reprsente lobjet que nous sommes en train dutiliser. Ainsi, dans ce script,
les variables $this et $perso reprsentent le mme objet. Linstruction surligne veut
donc dire : Ache-moi cette valeur : dans lobjet utilis (donc $perso), donne-moi
la valeur de lattribut $_experience.
Ainsi, je vais vous demander dcrire la mthode gagnerExperience(). Cette mthode
devra ajouter 1 lattribut $_experience. Je suis sr que vous pouvez y arriver !
Voici la correction :
1 <?php
2 class Personnage
3 {
4 private $_experience = 50;
5
6 public function afficherExperience ()
7 {
8 echo $this ->_experience;
9 }
10
11 public function gagnerExperience ()
12 {
13 // On ajoute 1 notre attribut $_experience.
14 $this ->_experience = $this ->_experience + 1;
15 }
16 }
17
18 $perso = new Personnage ();
19 $perso ->gagnerExperience (); // On gagne de l'exprience.
20 $perso ->afficherExperience (); // On affiche la nouvelle valeur
de l'attribut.
14
CRER ET MANIPULER UN OBJET
Implmenter dautres mthodes
Nous avons cr notre classe Personnage et dj une mthode, gagnerExperience().
Jaimerais quon en implmente une autre : la mthode frapper(), qui devra iniger
des dgts un personnage.
Pour partir sur une base commune, nous allons travailler sur cette classe :
1 <?php
2 class Personnage
3 {
4 private $_degats = 0; // Les dgts du personnage.
5 private $_experience = 0; // L'exprience du personnage.
6 private $_force = 20; // La force du personnage (plus elle
est grande , plus l'attaque est puissante).
7
8 public function gagnerExperience ()
9 {
10 // On ajoute 1 notre attribut $_experience.
11 $this ->_experience = $this ->_experience + 1;
12 }
13 }
Commenons par crire notre mthode frapper(). Cette mthode doit accepter un
argument : le personnage frapper. La mthode aura juste pour rle daugmenter les
dgts du personnage pass en paramtre.
Pour vous aider visualiser le contenu de la mthode, imaginez votre code manipulant
des objets. Il doit ressembler ceci :
1 <?php
2 // On cre deux personnages
3 $perso1 = new Personnage ();
4 $perso2 = new Personnage ();
5
6 // Ensuite , on veut que le personnage n1 frappe le personnage
n2.
7 $perso1 ->frapper($perso2);
Pour rsumer :
la mthode frapper() demande un argument : le personnage frapper ;
cette mthode augmente les dgts du personnage frapper en fonction de la force
du personnage qui frappe.
On pourrait donc imaginer une classe ressemblant ceci :
1 <?php
2 class Personnage
3 {
4 private $_degats; // Les dgts du personnage.
5 private $_experience; // L'exprience du personnage.
6 private $_force; // La force du personnage (plus elle est
grande , plus l'attaque est puissante).
15
CHAPITRE 2. UTILISER LA CLASSE
7
8 public function frapper($persoAFrapper)
9 {
10 $persoAFrapper ->_degats += $this ->_force;
11 }
12
13 public function gagnerExperience ()
14 {
15 // On ajoute 1 notre attribut $_experience.
16 $this ->_experience = $this ->_experience + 1;
17 }
18 }
Commentons ensemble le contenu de cette mthode frapper(). Celle-ci comporte une
instruction compose de deux parties :
1. La premire consiste dire PHP que lon veut assigner une nouvelle valeur
lattribut $_degats du personnage frapper.
2. La seconde partie consiste donner PHP la valeur que lon veut assigner. Ici,
nous voyons que cette valeur est atteinte par $this_force.
Maintenant, grosse question : la variable $this fait-elle rfrence au personnage qui
frappe ou au personnage frapp ? Pour rpondre cette question, il faut savoir sur
quel objet est appele la mthode. En eet, souvenez-vous que $this est une variable
reprsentant lobjet partir duquel on a appel la mthode. Dans notre cas, on a appel
la mthode frapper() partir du personnage qui frappe, donc $this reprsente le
personnage qui frappe.
Linstruction contenue dans la mthode signie donc : Ajoute la valeur de la force du
personnage qui frappe lattribut $_degats du personnage frapp.
Maintenant, nous pouvons crer une sorte de petite mise en scne qui fait interagir
nos personnages. Par exemple, nous pouvons crer un script qui fait combattre les
personnages. Le personnage 1 frapperait le personnage 2 puis gagnerait de lexprience,
puis le personnage 2 frapperait le personnage 1 et gagnerait de lexprience. Procdez
tape par tape :
crez deux personnages ;
faites frapper le personnage 1 ;
faites gagner de lexprience au personnage 1 ;
faites frapper le personnage 2 ;
faites gagner de lexprience au personnage 2.
Ce script nest quune suite dappels de mthodes. Chaque puce (sauf la premire)
correspond lappel dune mthode, ce que vous savez faire. En conclusion, vous tes
aptes crer ce petit script ! Voici la correction :
1 <?php
2 $perso1 = new Personnage (); // Un premier personnage
3 $perso2 = new Personnage (); // Un second personnage
4
16
CRER ET MANIPULER UN OBJET
5 $perso1 ->frapper($perso2); // $perso1 frappe $perso2
6 $perso1 ->gagnerExperience (); // $perso1 gagne de l'exprience
7
8 $perso2 ->frapper($perso1); // $perso2 frappe $perso1
9 $perso2 ->gagnerExperience (); // $perso2 gagne de l'exprience
10 ?>
Cependant, un petit problme se pose. Puisque, la base, les deux personnages ont le
mme niveau de dgts, la mme exprience et la mme force, ils seront la n toujours
gaux. Pour pallier ce problme, il faudrait pouvoir assigner des valeurs spciques
aux deux personnages, an que le combat puisse les direncier. Or, vous ne pouvez
pas accder aux attributs en-dehors de la classe ! Pour savoir comment rsoudre ce
problme, je vais vous apprendre deux nouveaux mots : accesseur et mutateur. Mais
avant, jaimerais faire une petite parenthse.
Exiger des objets en paramtre
Reprenons lexemple du code auquel nous sommes arrivs et concentrons-nous sur la
mthode frapper(). Celle-ci accepte un argument : un personnage frapper. Cepen-
dant, quest-ce qui vous garantit quon passe eectivement un personnage frapper ?
On pourrait trs bien passer un argument compltement dirent, comme un nombre
par exemple :
1 <?php
2 $perso = new Personnage ();
3 $perso ->frapper(42);
4 ?>
Et l, quest-ce qui se passe ? Une erreur est gnre car, lintrieur de la mthode
frapper(), nous essayons dappeler une mthode sur le paramtre qui nest pas un
objet. Cest comme si on avait fait a :
1 <?php
2 $persoAFrapper = 42;
3 $persoAFrapper ->_degats += 50; // Le nombre 50 est arbitraire ,
il est cens reprsenter une force.
4 ?>
Ce qui na aucun sens. Il faut donc sassurer que le paramtre pass est bien un person-
nage, sinon PHP arrte tout et nexcute pas la mthode. Pour cela, il sut dajouter
un seul mot : le nom de la classe dont le paramtre doit tre un objet. Dans notre
cas, si le paramtre doit tre un objet de type Personnage, alors il faudra ajouter le
mot-cl Personnage, juste avant le nom du paramtre, comme ceci :
1 <?php
2 class Personnage
3 {
4 // ...
5
17
CHAPITRE 2. UTILISER LA CLASSE
6 public function frapper(Personnage $persoAFrapper)
7 {
8 // ...
9 }
10 }
Grce a, vous tes srs que la mthode frapper() ne sera excute que si le para-
mtre pass est de type Personnage, sinon PHP interrompt tout le script. Vous pouvez
donc appeler les mthodes de lobjet sans crainte quun autre type de variable soit pass
en paramtre.
Le type de la variable spcier doit obligatoirement tre un nom de classe
ou alors un tableau. Si vous voulez exiger un tableau, faites prcder le nom
du paramtre devant tre un tableau par le mot-cl array comme ceci :
public function frapper(array $coups). Vous ne pouvez pas exiger
autre chose : par exemple, il est impossible dexiger un nombre entier ou une
chane de caractres de cette faon.
Les accesseurs et mutateurs
Comme vous le savez, le principe dencapsulation nous empche daccder directement
aux attributs de notre objet puisquils sont privs : seule la classe peut les lire et les
modier. Par consquent, si vous voulez rcuprer un attribut, il va falloir le demander
la classe, de mme si vous voulez les modier.
Accder un attribut : laccesseur
votre avis, comment peut-on faire pour rcuprer la valeur dun attribut ? La solu-
tion est simple : nous allons implmenter des mthodes dont le seul rle sera de nous
donner lattribut quon leur demande ! Ces mthodes ont un nom bien spcial : ce sont
des accesseurs (ou getters). Par convention, ces mthodes portent le mme nom que
lattribut dont elles renvoient la valeur. Par exemple, voici la liste des accesseurs de
notre classe Personnage :
1 <?php
2 class Personnage
3 {
4 private $_force;
5 private $_experience;
6 private $_degats;
7
8 public function frapper(Personnage $persoAFrapper)
9 {
10 $persoAFrapper ->_degats += $this ->_force;
11 }
18
LES ACCESSEURS ET MUTATEURS
12
13 public function gagnerExperience ()
14 {
15 // Ceci est un raccourci qui quivaut crire $this ->
_experience = $this ->_experience + 1
16 // On aurait aussi pu crire $this ->_experience += 1
17 $this ->_experience ++;
18 }
19
20 // Ceci est la mthode degats () : elle se charge de renvoyer
le contenu de l'attribut $_degats.
21 public function degats ()
22 {
23 return $this ->_degats;
24 }
25
26 // Ceci est la mthode force() : elle se charge de renvoyer
le contenu de l'attribut $_force.
27 public function force ()
28 {
29 return $this ->_force;
30 }
31
32 // Ceci est la mthode experience () : elle se charge de
renvoyer le contenu de l'attribut $_experience.
33 public function experience ()
34 {
35 return $this ->_experience;
36 }
37 }
Modier la valeur dun attribut : les mutateurs
Maintenant, comment cela se passe-t-il si vous voulez modier un attribut ? Encore une
fois, il va falloir que vous demandiez la classe de le faire pour vous. Je vous rappelle
que le principe dencapsulation est l pour vous empcher dassigner un mauvais type
de valeur un attribut : si vous demandez votre classe de le faire, ce risque est
supprim car la classe contrle la valeur des attributs. Comme vous laurez peut-
tre devin, ce sera par le biais de mthodes que lon demandera notre classe de
modier tel attribut.
La classe doit imprativement contrler la valeur an dassurer son intgrit car, si
elle ne le fait pas, on pourra passer nimporte quelle valeur la classe et le principe
dencapsulation nest plus respect ! Ces mthodes ont aussi un nom spcial : il sagit de
mutateurs (ou setters). Ces mthodes sont de la forme setNomDeLAttribut(). Voici
la liste des mutateurs (ajoute la liste des accesseurs) de notre classe Personnage :
1 <?php
2 class Personnage
19
CHAPITRE 2. UTILISER LA CLASSE
3 {
4 private $_force;
5 private $_experience;
6 private $_degats;
7
8 public function frapper(Personnage $persoAFrapper)
9 {
10 $persoAFrapper ->_degats += $this ->_force;
11 }
12
13 public function gagnerExperience ()
14 {
15 $this ->_experience ++;
16 }
17
18 // Mutateur charg de modifier l'attribut $_force.
19 public function setForce($force)
20 {
21 if (! is_int($force)) // S'il ne s'agit pas d'un nombre
entier.
22 {
23 trigger_error('La force d\'un personnage doit tre un
nombre entier ', E_USER_WARNING);
24 return;
25 }
26
27 if ($force > 100) // On vrifie bien qu'on ne souhaite pas
assigner une valeur suprieure 100.
28 {
29 trigger_error('La force d\'un personnage ne peut dpasser
100', E_USER_WARNING);
30 return;
31 }
32
33 $this ->_force = $force;
34 }
35
36 // Mutateur charg de modifier l'attribut $_experience.
37 public function setExperience($experience)
38 {
39 if (! is_int($experience)) // S'il ne s'agit pas d'un nombre
entier.
40 {
41 trigger_error('L\'exprience d\'un personnage doit tre
un nombre entier ', E_USER_WARNING);
42 return;
43 }
44
45 if ($experience > 100) // On vrifie bien qu'on ne souhaite
pas assigner une valeur suprieure 100.
20
LES ACCESSEURS ET MUTATEURS
46 {
47 trigger_error('L\'exprience d\'un personnage ne peut d
passer 100', E_USER_WARNING);
48 return;
49 }
50
51 $this ->_experience = $experience;
52 }
53
54 // Ceci est la mthode degats () : elle se charge de renvoyer
le contenu de l'attribut $_degats.
55 public function degats ()
56 {
57 return $this ->_degats;
58 }
59
60 // Ceci est la mthode force() : elle se charge de renvoyer
le contenu de l'attribut $_force.
61 public function force ()
62 {
63 return $this ->_force;
64 }
65
66 // Ceci est la mthode experience () : elle se charge de
renvoyer le contenu de l'attribut $_experience.
67 public function experience ()
68 {
69 return $this ->_experience;
70 }
71 }
Voil ce que javais dire concernant ces accesseurs et mutateurs. Retenez bien ces
dnitions, vous les trouverez dans la plupart des classes !
Retour sur notre script de combat
Maintenant que nous avons vu ce qutaient des accesseurs et des mutateurs, nous
pouvons amliorer notre script de combat. Pour commencer, je vais vous demander
dacher, la n du script, la force, lexprience et le niveau de dgts de chaque
personnage.
Voici la correction :
1 <?php
2 $perso1 = new Personnage (); // Un premier personnage
3 $perso2 = new Personnage (); // Un second personnage
4
5 $perso1 ->frapper($perso2); // $perso1 frappe $perso2
6 $perso1 ->gagnerExperience (); // $perso1 gagne de l'exprience
7
21
CHAPITRE 2. UTILISER LA CLASSE
8 $perso2 ->frapper($perso1); // $perso2 frappe $perso1
9 $perso2 ->gagnerExperience (); // $perso2 gagne de l'exprience
10
11 echo 'Le personnage 1 a ', $perso1 ->force(), ' de force ,
contrairement au personnage 2 qui a ', $perso2 ->force(), '
de force.<br />';
12 echo 'Le personnage 1 a ', $perso1 ->experience (), ' d\'exp
rience , contrairement au personnage 2 qui a ', $perso2 ->
experience (), ' d\'exprience.<br />';
13 echo 'Le personnage 1 a ', $perso1 ->degats (), ' de dgts,
contrairement au personnage 2 qui a ', $perso2 ->degats (), '
de dgts.<br />';
Comme nous lavions dit, les valeurs nales des deux personnages sont identiques. Pour
pallier ce problme, nous allons modier, juste aprs la cration des personnages, la
valeur de la force et de lexprience des deux personnages. Vous pouvez par exemple
favoriser un personnage en lui donnant une plus grande force et une plus grande exp-
rience par rapport au deuxime.
Voici la correction que je vous propose (peu importe les valeurs que vous avez choisies,
lessentiel est que vous ayez appel les bonnes mthodes) :
1 <?php
2 $perso1 = new Personnage (); // Un premier personnage
3 $perso2 = new Personnage (); // Un second personnage
4
5 $perso1 ->setForce(10);
6 $perso1 ->setExperience(2);
7
8 $perso2 ->setForce(90);
9 $perso2 ->setExperience(58);
10
11 $perso1 ->frapper($perso2); // $perso1 frappe $perso2
12 $perso1 ->gagnerExperience (); // $perso1 gagne de l'exprience
13
14 $perso2 ->frapper($perso1); // $perso2 frappe $perso1
15 $perso2 ->gagnerExperience (); // $perso2 gagne de l'exprience
16
17 echo 'Le personnage 1 a ', $perso1 ->force(), ' de force ,
contrairement au personnage 2 qui a ', $perso2 ->force(), '
de force.<br />';
18 echo 'Le personnage 1 a ', $perso1 ->experience (), ' d\'exp
rience , contrairement au personnage 2 qui a ', $perso2 ->
experience (), ' d\'exprience.<br />';
19 echo 'Le personnage 1 a ', $perso1 ->degats (), ' de dgts,
contrairement au personnage 2 qui a ', $perso2 ->degats (), '
de dgts.<br />';
Ce qui achera ceci (voir gure 2.1).
Comme vous le voyez, la n, les deux personnages nont plus les mmes caractris-
22
LE CONSTRUCTEUR
Figure 2.1 Rsultat ach par le script
tiques !
Pour bien tre sr que vous me suiviez toujours, je vous ai fait un schma rsumant le
droulement du script (voir la gure 2.2).
Figure 2.2 Droulement du script
Le constructeur
Vous vous demandez peut-tre quoi servent les parenthses juste aprs Personnage
lorsque vous crez un objet ? Cest ce que je vais vous expliquer juste aprs vous avoir
23
CHAPITRE 2. UTILISER LA CLASSE
expliqu ce quest un constructeur.
Le constructeur est la mthode appele ds que vous crez lobjet avec la technique
prsente ci-dessus. Cette mthode peut demander des paramtres, auquel cas nous
devrons les placer entre les parenthses que vous voyez aprs le nom de la classe.
Eectuons un retour sur notre classe Personnage. Ajoutons-lui un constructeur. Ce
dernier ne peut pas avoir nimporte quel nom (sinon, comment PHP sait quel est le
constructeur ?). Il a tout simplement le nom suivant : __construct, avec deux unders-
cores au dbut.
Comme son nom lindique, le constructeur sert construire lobjet. Ce que je veux
dire par l, cest que si des attributs doivent tre initialiss ou quune connexion la
BDD doit tre faite, cest par ici que a se passe. Comme dit plus haut, le constructeur
est excut ds la cration de lobjet et par consquent, aucune valeur ne doit tre
retourne, mme si a ne gnrera aucune erreur. Bien sr, et comme nous lavons
vu, une classe fonctionne trs bien sans constructeur, il nest en rien obligatoire ! Si
vous nen spciez pas, cela revient au mme que si vous en aviez crit un vide (sans
instruction lintrieur).
1 <?php
2 class Personnage
3 {
4 private $_force;
5 private $_localisation;
6 private $_experience;
7 private $_degats;
8
9 public function __construct($force , $degats) // Constructeur
demandant 2 paramtres
10 {
11 echo 'Voici le constructeur !'; // Message s'affichant une
fois que tout objet est cr.
12 $this ->setForce($force); // Initialisation de la force.
13 $this ->setDegats($degats); // Initialisation des dgts.
14 $this ->_experience = 1; // Initialisation de l'exprience
1.
15 }
16
17 // Mutateur charg de modifier l'attribut $_force.
18 public function setForce($force)
19 {
20 if (! is_int($force)) // S'il ne s'agit pas d'un nombre
entier.
21 {
22 trigger_error('La force d\'un personnage doit tre un
nombre entier ', E_USER_WARNING);
23 return;
24 }
25
26 if ($force > 100) // On vrifie bien qu'on ne souhaite pas
24
LE CONSTRUCTEUR
assigner une valeur suprieure 100.
27 {
28 trigger_error('La force d\'un personnage ne peut dpasser
100', E_USER_WARNING);
29 return;
30 }
31
32 $this ->_force = $force;
33 }
34
35 // Mutateur charg de modifier l'attribut $_degats.
36 public function setDegats($degats)
37 {
38 if (! is_int($degats)) // S'il ne s'agit pas d'un nombre
entier.
39 {
40 trigger_error('Le niveau de dgts d\'un personnage doit
tre un nombre entier ', E_USER_WARNING);
41 return;
42 }
43
44 $this ->_degats = $degats;
45 }
46 }
47 ?>
Notez que je nai pas rcrit toutes les mthodes, ce nest pas ce que je veux
vous montrer ici.
Ici, le constructeur demande la force et les dgts initiaux du personnage que lon vient
de crer. Il faudra donc lui spcier ceci en paramtre :
1 <?php
2 $perso1 = new Personnage(60, 0); // 60 de force , 0 dgt
3 $perso2 = new Personnage(100 , 10); // 100 de force , 10 dgts
4 ?>
Et la sortie sachera (voir gure 2.3).
Notez quelque chose dimportant dans la classe : dans le constructeur, les valeurs sont
initialises en appelant les mutateurs correspondant. En eet, si on assignait directe-
ment ces valeurs avec les arguments, le principe dencapsulation ne serait plus respect
et nimporte quel type de valeur pourrait tre assign !
Ne mettez jamais la mthode __construct avec le type de visibilit private
car elle ne pourra jamais tre appele, vous ne pourrez donc pas instancier
votre classe ! Cependant, sachez quil existe certains cas particuliers qui n-
cessitent le constructeur en priv, mais ce nest pas pour tout de suite.
25
CHAPITRE 2. UTILISER LA CLASSE
Figure 2.3 Rsultat ach par le script
Notez que si la classe na pas implment de constructeur ou si le construc-
teur ne requiert aucun argument, alors les parenthses places aprs le nom
de la classe lorsque vous linstancierez sont inutiles. Ainsi, vous pourrez faire
$classe = new MaClasse;. Cest dailleurs sans parenthses que jinstan-
cierai les classes sans constructeur ou avec constructeur sans argument d-
sormais.
Lauto-chargement de classes
Pour une question dorganisation, il vaut mieux crer un chier par classe. Vous appelez
votre chier comme bon vous semble et placez votre classe dedans. Pour ma part, mes
chiers sont toujours appels MaClasse.class.php . Ainsi, si je veux pouvoir utiliser
la classe MaClasse, je naurais qu inclure ce chier :
1 <?php
2 require 'MaClasse.class.php'; // J'inclus la classe.
3
4 $objet = new MaClasse (); // Puis , seulement aprs, je me sers
de ma classe.
Maintenant, imaginons que vous ayez plusieurs dizaines de classes. . . Pas trs pratique
de les inclure une par une ! Vous vous retrouverez avec des dizaines dinclusions, cer-
taines pouvant mme tre inutile si vous ne vous servez pas de toutes vos classes. Et
cest l quintervient lauto-chargement des classes. Vous pouvez crer dans votre chier
principal (cest--dire celui o vous crerez une instance de votre classe) une ou plu-
sieurs fonction(s) qui tenteront de charger le chier dclarant la classe. Dans la plupart
des cas, une seule fonction sut. Ces fonctions doivent accepter un paramtre, cest le
nom de la classe quon doit tenter de charger. Par exemple, voici une fonction qui aura
pour rle de charger les classes :
1 <?php
2 function chargerClasse($classe)
26
LAUTO-CHARGEMENT DE CLASSES
3 {
4 require $classe . '.class.php'; // On inclut la classe
correspondante au paramtre pass.
5 }
6 ?>
Essayons maintenant de crer un objet pour voir si il sera charg automatiquement (je
prends pour exemple la classe Personnage et prends en compte le fait quun chier
Personnage.class.php existe).
1 <?php
2 function chargerClasse($classe)
3 {
4 require $classe . '.class.php'; // On inclut la classe
correspondante au paramtre pass.
5 }
6
7 $perso = new Personnage (); // Instanciation de la classe
Personnage qui n'est pas dclare dans ce fichier.
8 ?>
Et l. . . Bam! Erreur fatale ! La classe na pas t trouve, elle na donc pas t char-
ge. . . Normal quand on y rchi ! PHP ne sait pas quil doit appeler cette fonction
lorsquon essaye dinstancier une classe non dclare. On va donc utiliser la fonction
spl_autoload_register en spciant en premier paramtre le nom de la fonction
charger :
1 <?php
2 function chargerClasse($classe)
3 {
4 require $classe . '.class.php'; // On inclut la classe
correspondante au paramtre pass.
5 }
6
7 spl_autoload_register('chargerClasse '); // On enregistre la
fonction en autoload pour qu'elle soit appele ds qu'on
instanciera une classe non dclare.
8
9 $perso = new Personnage ();
10 ?>
Et l, comme par magie, aucune erreur ne sache ! Notre auto-chargement a donc bien
fonctionn.
Dcortiquons ce qui sest pass. En PHP, il y a ce quon appelle une pile dauto-
loads . Cette pile contient une liste de fonctions. Chacune dentre elles sera appele
automatiquement par PHP lorsque lon essaye dinstancier une classe non dclare.
Nous avons donc ici ajout notre fonction la pile dautoloads an quelle soit appele
chaque fois quon essaye dinstancier une classe non dclare.
Schmatiquement, la gure 2.4 montre ce qui sest pass.
27
CHAPITRE 2. UTILISER LA CLASSE
Figure 2.4 Lautoload en PHP
28
LAUTO-CHARGEMENT DE CLASSES
Sachez que vous pouvez enregistrer autant de fonctions en autoload que
vous le voulez avec spl_autoload_register. Si vous en enregistrez
plusieurs, elles seront appeles dans lordre de leur enregistrement jus-
qu ce que la classe soit charge. Pour y parvenir, il sut dappeler
spl_autoload_register pour chaque fonction enregistrer.
En rsum
Un objet se cre grce loprateur new.
Laccs un attribut ou une mthode dun objet se fait grce loprateur ->.
Pour lire ou modier un attribut, on utilise des accesseurs et des mutateurs.
Le constructeur dune classe a pour rle principal dinitialiser lobjet en cours de
cration, cest--dire dinitialiser la valeur des attributs (soit en assignant directement
des valeurs spciques, soit en appelant diverses mthodes).
Les classes peuvent tre charges dynamiquement (cest--dire sans avoir explicite-
ment inclus le chier la dclarant) grce lauto-chargement de classe (utilisation de
spl_autoload_register).
29
CHAPITRE 2. UTILISER LA CLASSE
30
Chapitre 3
Loprateur de rsolution de porte
Dicult :
L
oprateur de rsolution de porte ( :: ), appel double deux points ( Scope
Resolution Operator en anglais), est utilis pour appeler des lments appartenant
telle classe et non tel objet. En eet, nous pouvons dnir des attributs et mthodes
appartenant la classe : ce sont des lments statiques. Nous y reviendrons en temps
voulu dans une partie ddie ce sujet.
Parmi les lments appartenant la classe (et donc appels via cet oprateur), il y a aussi
les constantes de classe, sortes dattributs dont la valeur est constante, cest--dire quelle
ne change pas. Nous allons dailleurs commercer par ces constantes de classe.
Cet oprateur est aussi appel Paamayim Nekudotayim . Mais rassurez-vous, je ne vais
pas vous demander de le retenir (si vous y arrivez, bien jou !).
31
CHAPITRE 3. LOPRATEUR DE RSOLUTION DE PORTE
Les constantes de classe
Commenons par les constantes de classe. Le principe est peu prs le mme que
lorsque vous crez une constante laide de la fonction define. Les constantes de
classe permettent dviter tout code muet. Voici un code muet :
1 <?php
2 $perso = new Personnage(50);
3 ?>
Pourquoi est-il muet ? Tout simplement parce quon ne sait pas quoi 50 correspond.
Quest-ce que cela veut dire ? tant donn que je viens de raliser le script, je sais que ce
50 correspond la force du personnage. Cependant, ce paramtre ne peut prendre
que 3 valeurs possibles :
20, qui veut dire que le personnage aura une faible force ;
50, qui veut dire que le personnage aura une force moyenne ;
80, qui veut dire que le personnage sera trs fort.
Au lieu de passer ces valeurs telles quelles, on va plutt passer une constante au
constructeur. Ainsi, quand on lira le code, on devinera facilement que lon passe une
force moyenne au constructeur. Cest bien plus facile comprendre quun nombre
quelconque.
Une constante est une sorte dattribut appartenant la classe dont la valeur ne change
jamais. Ceci est peut-tre un peu ou, cest pourquoi nous allons passer la pratique.
Pour dclarer une constante, vous devez faire prcder son nom du mot-cl const.
Faites bien attention, une constante ne prend pas de $ devant son nom! Voici donc
comment crer une constante :
1 <?php
2 class Personnage
3 {
4 // Je rappelle : tous les attributs en priv !
5
6 private $_force;
7 private $_localisation;
8 private $_experience;
9 private $_degats;
10
11 // Dclarations des constantes en rapport avec la force.
12
13 const FORCE_PETITE = 20;
14 const FORCE_MOYENNE = 50;
15 const FORCE_GRANDE = 80;
16
17 public function __construct ()
18 {
19
20 }
21
32
LES CONSTANTES DE CLASSE
22 public function deplacer ()
23 {
24
25 }
26
27 public function frapper ()
28 {
29
30 }
31
32 public function gagnerExperience ()
33 {
34
35 }
36 }
37 ?>
Et voil ! Facile nest-ce pas ?
Bien sr, vous pouvez assigner ces constantes dautres valeurs.
Et quel est le rapport avec tes double deux points ?
Contrairement aux attributs, vous ne pouvez accder ces valeurs via loprateur ->
depuis un objet (ni $this ni $perso ne fonctionneront) mais avec loprateur ::
car une constante appartient la classe et non un quelconque objet.
Pour accder une constante, vous devez spcier le nom de la classe, suivi du symbole
double deux points, suivi du nom de la constante. Ainsi, on pourrait imaginer un code
comme celui-ci :
1 <?php
2 class Personnage
3 {
4 private $_force;
5 private $_localisation;
6 private $_experience;
7 private $_degats;
8
9 // Dclarations des constantes en rapport avec la force.
10
11 const FORCE_PETITE = 20;
12 const FORCE_MOYENNE = 50;
13 const FORCE_GRANDE = 80;
14
15 public function __construct($forceInitiale)
16 {
17 // N'oubliez pas qu'il faut assigner la valeur d'un
attribut uniquement depuis son setter !
33
CHAPITRE 3. LOPRATEUR DE RSOLUTION DE PORTE
18 $this ->setForce($forceInitiale);
19 }
20
21 public function deplacer ()
22 {
23
24 }
25
26 public function frapper ()
27 {
28
29 }
30
31 public function gagnerExperience ()
32 {
33
34 }
35
36 public function setForce($force)
37 {
38 // On vrifie qu'on nous donne bien soit une FORCE_PETITE
, soit une FORCE_MOYENNE , soit une FORCE_GRANDE
.
39 if (in_array($force , array(self:: FORCE_PETITE , self::
FORCE_MOYENNE , self:: FORCE_GRANDE)))
40 {
41 $this ->_force = $force;
42 }
43 }
44 }
45 ?>
Et lors de la cration de notre personnage :
1 <?php
2 // On envoie une FORCE_MOYENNE en guise de force initiale.
3 $perso = new Personnage(Personnage :: FORCE_MOYENNE);
4 ?>
Notez quici les noms de constantes sont en majuscules : cest encore et
toujours une convention de dnomination.
Reconnaissez que ce code est plus lisible que celui montr au dbut de cette sous-partie.
34
LES ATTRIBUTS ET MTHODES STATIQUES
Les attributs et mthodes statiques
Les mthodes statiques
Comme je lai brivement dit dans lintroduction, les mthodes statiques sont des m-
thodes qui sont faites pour agir sur une classe et non sur un objet. Par consquent, je
ne veux voir aucun $this dans la mthode ! En eet, la mthode ntant appele sur
aucun objet, il serait illogique que cette variable existe. Souvenez-vous : $this est un
paramtre implicite. Cela nest vrai que pour les mthodes appeles sur un objet !
Mme si la mthode est dite statique , il est possible de lappeler depuis
un objet ($obj->methodeStatique()), mais, mme dans ce contexte, la
variable $this ne sera toujours pas passe !
Pour dclarer une mthode statique, vous devez faire prcder le mot-cl function du
mot-cl static aprs le type de visibilit.
1 <?php
2 class Personnage
3 {
4 private $_force;
5 private $_localisation;
6 private $_experience;
7 private $_degats;
8
9 const FORCE_PETITE = 20;
10 const FORCE_MOYENNE = 50;
11 const FORCE_GRANDE = 80;
12
13 public function __construct($forceInitiale)
14 {
15 $this ->setForce($forceInitiale);
16 }
17
18 public function deplacer ()
19 {
20
21 }
22
23 public function frapper ()
24 {
25
26 }
27
28 public function gagnerExperience ()
29 {
30
31 }
32
35
CHAPITRE 3. LOPRATEUR DE RSOLUTION DE PORTE
33 public function setForce($force)
34 {
35 // On vrifie qu'on nous donne bien soit une FORCE_PETITE
, soit une FORCE_MOYENNE , soit une FORCE_GRANDE
.
36 if (in_array($force , array(self:: FORCE_PETITE , self::
FORCE_MOYENNE , self:: FORCE_GRANDE)))
37 {
38 $this ->_force = $force;
39 }
40 }
41
42 // Notez que le mot -cl static peut tre plac avant la
visibilit de la mthode (ici c'est public).
43 public static function parler ()
44 {
45 echo 'Je vais tous vous tuer !';
46 }
47 }
48 ?>
Et dans le code, vous pourrez faire :
1 <?php
2 Personnage :: parler ();
3 ?>
Comme je lai dit plus haut, vous pouvez aussi appeler la mthode depuis un objet,
mais cela ne changera rien au rsultat nal :
1 <?php
2 $perso = new Personnage(Personnage :: FORCE_GRANDE);
3 $perso ->parler ();
4 ?>
Cependant, prfrez appeler la mthode avec loprateur :: comme le montre le
premier de ces deux codes. De cette faon, on voit directement de quelle classe on
dcide dinvoquer la mthode. De plus, appeler de cette faon une mthode statique
vitera une erreur de degr E_STRICT.
Je me rpte mais jinsiste l-dessus : il ny a pas de variable $this dans la
mthode dans la mesure o la mthode est invoque an dagir sur la classe
et non sur un quelconque objet !
Les attributs statiques
Le principe est le mme, cest--dire quun attribut statique appartient la classe et
non un objet. Ainsi, tous les objets auront accs cet attribut et cet attribut aura
la mme valeur pour tous les objets.
36
LES ATTRIBUTS ET MTHODES STATIQUES
La dclaration dun attribut statique se fait en faisant prcder son nom du mot-cl
static, comme ceci :
1 <?php
2 class Personnage
3 {
4 private $_force;
5 private $_localisation;
6 private $_experience;
7 private $_degats;
8
9 const FORCE_PETITE = 20;
10 const FORCE_MOYENNE = 50;
11 const FORCE_GRANDE = 80;
12
13 // Variable statique PRIVE.
14 private static $_texteADire = 'Je vais tous vous tuer !';
15
16 public function __construct($forceInitiale)
17 {
18 $this ->setForce($forceInitiale);
19 }
20
21 public function deplacer ()
22 {
23
24 }
25
26 public function frapper ()
27 {
28
29 }
30
31 public function gagnerExperience ()
32 {
33
34 }
35
36 public function setForce($force)
37 {
38 // On vrifie qu'on nous donne bien soit un FORCE_PETITE
, soit une FORCE_MOYENNE , soit une FORCE_GRANDE
.
39 if (in_array($force , array(self:: FORCE_PETITE , self::
FORCE_MOYENNE , self:: FORCE_GRANDE)))
40 {
41 $this ->_force = $force;
42 }
43 }
44
45 public static function parler ()
37
CHAPITRE 3. LOPRATEUR DE RSOLUTION DE PORTE
46 {
47 echo self:: $_texteADire; // On donne le texte dire.
48 }
49 }
50 ?>
Quelques nouveauts dans ce code ncessitent des explications. Premirement, quoi
sert un attribut statique ?
Nous avons vu que les mthodes statiques sont faites pour agir sur la classe. Daccord,
mais quest-ce quon peut faire sur une classe ? Et bien tout simplement modier les
attributs de celle-ci car, comme je lai dj dit, des attributs appartenant une classe
ne sont autre que des attributs statiques ! Les attributs statiques servent en particulier
avoir des attributs indpendants de tout objet. Ainsi, vous aurez beau crer des tas
dobjets, votre attribut aura toujours la mme valeur (sauf si lobjet modie sa valeur,
bien sr). Mieux encore : si lun des objets modie sa valeur, tous les autres objets
qui accderont cet attribut obtiendront la nouvelle valeur ! Cest logique quand on
y pense, car un attribut statique appartenant la classe, il nexiste quen un seul
exemplaire. Si on le modie, tout le monde pourra accder sa nouvelle valeur.
La ligne 38 commence avec le mot-cl self, ce qui veut dire (en gros) moi-mme (=
la classe). Notre ligne veut donc dire : Dans moi-mme, donne-moi lattribut statique
$_texteADire. (Je sais ce nest pas bien franais mais cest la meilleure traduction
mot mot que jai pu trouver.)
Noubliez pas de mettre un $ devant le nom de lattribut. Cest souvent
source derreur donc faites bien attention.
Nous allons maintenant faire un petit exercice. Je veux que vous me fassiez une classe
toute bte qui ne sert rien. Seulement, la n du script, je veux pouvoir acher
le nombre de fois o la classe a t instancie. Pour cela, vous aurez besoin dun
attribut appartenant la classe (admettons $_compteur) qui est incrment dans le
constructeur.
Voici la correction :
1 <?php
2 class Compteur
3 {
4 // Dclaration de la variable $compteur
5 private static $_compteur = 0;
6
7 public function __construct ()
8 {
9 // On instancie la variable $compteur qui appartient la
classe (donc utilisation du mot -cl self).
10 self:: $_compteur ++;
11 }
12
38
LES ATTRIBUTS ET MTHODES STATIQUES
13 public static function getCompteur () // Mthode statique qui
renverra la valeur du compteur.
14 {
15 return self:: $_compteur;
16 }
17 }
18
19 $test1 = new Compteur;
20 $test2 = new Compteur;
21 $test3 = new Compteur;
22
23 echo Compteur :: getCompteur ();
24 ?>
Eh oui, le retour du mot-cl self. . . Pourquoi pas $this ? Souvenez-vous : on naccde
pas un attribut statique avec $this mais avec self ! self reprsente la classe tandis
que $this reprsente lobjet actuellement cr. Si un attribut statique est modi, il
nest pas modi uniquement dans lobjet cr mais dans la structure complte de la
classe ! Je me rpte, mais il est essentiel que vous compreniez a, cest trs important.
En rsum
Loprateur -> permet daccder un lment de tel objet, tandis que loprateur
:: permet daccder un lment de telle classe.
Au sein dune mthode, on accde lobjet grce la pseudo-variable $this, tandis
quon accde la classe grce au mot-cl self.
Les attributs et mthodes statiques ainsi que les constantes de classe sont des l-
ments propres la classe, cest--dire quil nest pas utile de crer un objet pour sen
servir.
Les constantes de classe sont utiles pour viter davoir un code muet, cest--dire un
code qui, sans commentaire, ne nous informe pas vraiment sur son fonctionnement.
Les attributs et mthodes statiques sont utiles lorsque lon ne veut pas avoir besoin
dun objet pour sen servir.
39
CHAPITRE 3. LOPRATEUR DE RSOLUTION DE PORTE
40
Chapitre 4
Manipulation de donnes stockes
Dicult :
T
out site web dynamique doit tre capable de sauvegarder des donnes an de les
utiliser ultrieurement. Comme vous le savez sans doute, lun des moyens les plus
simples et ecaces est la base de donnes permettant de sauvegarder des centaines
de donnes et de les rcuprer en un temps trs court.
Cependant, la gestion des donnes stockes en BDD peut tre assez dicile cerner en
POO. Le dbutant ne sait en gnral pas par o commencer et se pose tout un tas de
questions : o crer la connexion avec la BDD? O placer les requtes ? Comment traiter
les valeurs retournes ? Nous allons justement voir tout cela ensemble. Accrochez-vous bien,
un TP suivra pour voir si vous avez bien tout compris !
41
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
Une entit, un objet
Rappels sur la structure dune BDD
Commenons ce chapitre par observer la structure dune table en BDD et, plus pr-
cisment, la manire dont sont stockes les donnes. Comme vous le savez srement,
une table nest autre quun grand tableau o sont organises les donnes (voir la gure
4.1).
Figure 4.1 Exemple dune table de personnages
Nous voyons ici que notre table contient 3 personnages. Ces donnes sont stockes
sous forme dentres. Cest sous cette forme que le gestionnaire de la BDD (MySQL,
PostgreSQL, etc.) manipule les donnes. Si lon regarde du ct de lapplication qui les
manipule (ici, notre script PHP), on se rend compte que cest sous une forme similaire
que les donnes sont rcupres. Cette forme utilise, vous la connaissez bien : vous
utilisiez jusqu prsent des tableaux pour les manipuler. Exemple :
1 <?php
2 // On admet que $db est un objet PDO
3 $request = $db ->query('SELECT id, nom , forcePerso , degats ,
niveau , experience FROM personnages ');
4
5 while ($perso = $request ->fetch(PDO:: FETCH_ASSOC)) // Chaque
entre sera rcupre et place dans un array.
6 {
7 echo $perso['nom'], ' a ', $perso['forcePerso '], ' de force ,
', $perso['degats '], ' de dgts, ', $perso['experience '],
' d\'exprience et est au niveau ', $perso['niveau '];
8 }
a, si vous avez dj fait un site internet de A Z, vous avez du lcrire pas mal de
fois !
Pour les exemples, et dans la suite du cours, jutilise lAPI PDO pour accder
la base de donnes. Je vous conseille de lire le cours de M@teo21 sur PDO
pour tre laise avec !

Cours sur la PDO


Code web : 896688
Maintenant, puisque nous programmons de manire oriente objet, nous voulons tra-
vailler seulement avec des objets (cest le principe mme de la POO, rappelons-le)et
42
UNE ENTIT, UN OBJET
non plus avec des tableaux. En dautres termes, il va falloir transformer le tableau que
lon reoit en objet.
Travailler avec des objets
Avant de travailler avec des objets, encore faut-il savoir de quelle classe ils sont issus.
Dans notre cas, nous voulons des objets reprsentant des personnages. On aura donc
besoin dune classe Personnage !
1 <?php
2 class Personnage
3 {
4
5 }
6 ?>
Vient maintenant une question embtante dans votre tte pour construire cette classe :
Je commence par o?
Une classe est compose de deux parties (ventuellement trois) :
une partie dclarant les attributs. Ce sont les caractristiques de lobjet ;
une partie dclarant les mthodes. Ce sont les fonctionnalits de chaque objet ;
ventuellement, une partie dclarant les constantes de classe. Nous nous en occupe-
rons en temps voulu.
Lorsque lon veut construire une classe, il va donc falloir systmatiquement se poser les
mmes questions :
Quelles seront les caractristiques de mes objets ?
Quelles seront les fonctionnalits de mes objets ?
Les rponses ces questions aboutiront la ralisation du plan de la classe, le plus
dicile.
Commenons donc rchir sur le plan de notre classe en rpondant la premire
question : Quelles seront les caractristiques de mes objets ? Pour vous mettre sur la
piste, regardez de nouveau le tableau de la BDD contenant les entres, cela vous donnera
peut-tre la rponse. . . Et oui, les attributs de notre classe (les caractristiques) nous
sont oerts sur un plateau ! Ils sont lists en haut du tableau : id, nom, forcePerso,
degats, niveau et experience.
crivons maintenant notre classe :
1 <?php
2 class Personnage
3 {
4 private $_id;
5 private $_nom;
6 private $_forcePerso;
7 private $_degats;
8 private $_niveau;
9 private $_experience;
43
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
10 }
11 ?>
Il faut bien sr implmenter (crire) les getters et setters qui nous permettront daccder
et de modier les valeurs de notre objet. Pour rappel, un getter est une mthode charge
de renvoyer la valeur dun attribut, tandis quun setter une mthode charge dassigner
une valeur un attribut en vriant son intgrit (si vous assignez la valeur sans
aucun contrle, vous perdez tout lintrt quapporte le principe dencapsulation).
Pour construire nos setters, il faut donc nous pencher sur les valeurs possibles de chaque
attribut :
les valeurs possibles de lidentiant sont tous les nombres entiers strictement positifs ;
les valeurs possibles pour le nom du personnage sont toutes les chanes de caractres ;
les valeurs possibles pour la force du personnage sont tous les nombres entiers allant
de 1 100 ;
les valeurs possibles pour les dgts du personnage sont tous les nombres entiers
allant de 0 100 ;
les valeurs possibles pour le niveau du personnage sont tous les nombres entiers allant
de 1 100 ;
les valeurs possibles pour lexprience du personnage sont tous les nombres entiers
allant de 1 100.
On en dduit donc le code de chaque setter. Voici donc ce que donnerait notre classe
avec ses getters et setters :
1 <?php
2 class Personnage
3 {
4 private $_id;
5 private $_nom;
6 private $_forcePerso;
7 private $_degats;
8 private $_niveau;
9 private $_experience;
10
11 // Liste des getters
12
13 public function id()
14 {
15 return $this ->id;
16 }
17
18 public function nom()
19 {
20 return $this ->nom;
21 }
22
23 public function forcePerso ()
24 {
25 return $this ->forcePerso;
44
UNE ENTIT, UN OBJET
26 }
27
28 public function degats ()
29 {
30 return $this ->degats;
31 }
32
33 public function niveau ()
34 {
35 return $this ->niveau;
36 }
37
38 public function experience ()
39 {
40 return $this ->experience;
41 }
42
43 // Liste des setters
44
45 public function setId($id)
46 {
47 // On convertit l'argument en nombre entier.
48 // Si c'en tait dj un, rien ne changera.
49 // Sinon , la conversion donnera le nombre 0 ( quelques
exceptions prs, mais rien d'important ici).
50 $id = (int) $id;
51
52 // On vrifie ensuite si ce nombre est bien strictement
positif.
53 if ($id > 0)
54 {
55 // Si c'est le cas , c'est tout bon , on assigne la valeur
l'attribut correspondant.
56 $this ->_id = $id;
57 }
58 }
59
60 public function setNom($nom)
61 {
62 // On vrifie qu'il s'agit bien d'une chane de caractres.
63 if (is_string($nom))
64 {
65 $this ->_nom = $nom;
66 }
67 }
68
69 public function setForcePerso($forcePerso)
70 {
71 $forcePerso = (int) $forcePerso;
72
45
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
73 if ($forcePerso >= 1 && $forcePerso <= 100)
74 {
75 $this ->_forcePerso = $forcePerso;
76 }
77 }
78
79 public function setDegats($degats)
80 {
81 $degats = (int) $degats;
82
83 if ($degats >= 0 && $degats <= 100)
84 {
85 $this ->_degats = $degats;
86 }
87 }
88
89 public function setNiveau($niveau)
90 {
91 $niveau = (int) $niveau;
92
93 if ($niveau >= 1 && $niveau <= 100)
94 {
95 $this ->_niveau = $niveau;
96 }
97 }
98
99 public function setExperience($experience)
100 {
101 $experience = (int) $experience;
102
103 if ($experience >= 1 && $experience <= 100)
104 {
105 $this ->_experience = $experience;
106 }
107 }
108 }
109 ?>
Reprenons notre code de tout lheure, celui qui nous permettait de lire la BDD.
Modions-le un peu pour quon puisse manipuler des objets et non des tableaux :
1 <?php
2 // On admet que $db est un objet PDO.
3 $request = $db ->query('SELECT id, nom , forcePerso , degats ,
niveau , experience FROM personnages ');
4
5 while ($donnees = $request ->fetch(PDO:: FETCH_ASSOC)) // Chaque
entre sera rcupre et place dans un array.
6 {
7 // On passe les donnes (stockes dans un tableau) concernant
le personnage au constructeur de la classe.
46
LHYDRATATION
8 // On admet que le constructeur de la classe appelle chaque
setter pour assigner les valeurs qu'on lui a donnes aux
attributs correspondants.
9 $perso = new Personnage($donnees);
10
11 echo $perso ->nom(), ' a ', $perso ->forcePerso (), ' de force ,
', $perso ->degats (), ' de dgts, ', $perso ->experience (),
' d\'exprience et est au niveau ', $perso ->niveau ();
12 }
Et quelles seront les mthodes de nos classes ?
Les mthodes concernent des fonctionnalits que possde lobjet, des actions quil peut
eectuer. Voyez-vous des fonctionnalits intressantes implmenter ? Pour les opra-
tions basiques que lon eectue, il ny en a pas besoin. En eet, nous voulons juste
crer des objets et assigner des valeurs aux attributs, donc hormis les getters et setters,
aucune autre mthode nest ncessaire !
Daccord, nous avons des objets maintenant. Mais quoi cela sert-il, concr-
tement ?
Il est sr que dans cet exemple prcis, cela ne sert rien. Mais vous verrez plus tard
quil est beaucoup plus intuitif de travailler avec des objets et par consquent beaucoup
plus pratique, notamment sur de grosses applications o de nombreux objets circulent
un peu dans tous les sens.
Lhydratation
La thorie de lhydratation
Lhydratation est un point essentiel dans le domaine de la POO, notamment lorsquon
utilise des objets reprsentant des donnes stockes. Cette notion peut vite devenir
complique et crer des interrogations pour un dveloppeur dbutant si elle est aborde
de manire approximative, alors que des explications claires prouvent quil ny a rien
de compliqu l-dedans.
Quand on vous parle dhydratation, cest quon parle d objet hydrater . Hydrater
un objet, cest tout simplement lui apporter ce dont il a besoin pour fonctionner.
En dautres termes plus prcis, hydrater un objet revient lui fournir des donnes
correspondant ses attributs pour quil assigne les valeurs souhaites ces derniers.
Lobjet aura ainsi des attributs valides et sera en lui-mme valide. On dit que lobjet
a ainsi t hydrat.
47
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
Schmatiquement, une hydratation se produit comme ceci (voir la gure 4.2).
Figure 4.2 Hydratation dun objet
Au dbut, nous avons un objet Personnage dont les attributs sont vides. Comme le
schma le reprsente, lhydratation consiste assigner des valeurs aux attributs. Ainsi
lobjet est fonctionnel car il contient des attributs valides : nous avons donc bien hydrat
lobjet.
Lhydratation en pratique
Comme on la vu, hydrater un objet revient assigner des valeurs ses attributs.
Quavons-nous besoin de faire pour raliser une telle chose ? Il faut ajouter lobjet
laction de shydrater. Et qui dit action dit mthode !
Nous allons donc crire notre fonction hydrate() :
1 <?php
2 class Personnage
3 {
4 private $_id;
5 private $_nom;
6 private $_forcePerso;
48
LHYDRATATION
7 private $_degats;
8 private $_niveau;
9 private $_experience;
10
11 // Un tableau de donnes doit tre pass la fonction (d'o
le prfixe array ).
12 public function hydrate(array $donnees)
13 {
14
15 }
16
17 public function id() { return $this ->_id; }
18 public function nom() { return $this ->_nom; }
19 public function forcePerso () { return $this ->_forcePerso; }
20 public function degats () { return $this ->_degats; }
21 public function niveau () { return $this ->_niveau; }
22 public function experience () { return $this ->_experience; }
23
24 public function setId($id)
25 {
26 // L'identifiant du personnage sera , quoi qu'il arrive , un
nombre entier.
27 $this ->_id = (int) $id;
28 }
29
30 public function setNom($nom)
31 {
32 // On vrifie qu'il s'agit bien d'une chane de caractres.
33 // Dont la longueur est infrieure 30 caractres.
34 if (is_string($nom) && strlen($nom) <= 30)
35 {
36 $this ->_nom = $nom;
37 }
38 }
39
40 public function setForcePerso($forcePerso)
41 {
42 $forcePerso = (int) $forcePerso;
43
44 // On vrifie que la force passe est comprise entre 0 et
100.
45 if ($forcePerso >= 0 && $forcePerso <= 100)
46 {
47 $this ->_forcePerso = $forcePerso;
48 }
49 }
50
51 public function setDegats($degats)
52 {
53 $degats = (int) $degats;
49
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
54
55 // On vrifie que les dgts passs sont compris entre 0 et
100.
56 if ($degats >= 0 && $degats <= 100)
57 {
58 $this ->_degats = $degats;
59 }
60 }
61
62 public function setNiveau($niveau)
63 {
64 $niveau = (int) $niveau;
65
66 // On vrifie que le niveau n'est pas ngatif.
67 if ($niveau >= 0)
68 {
69 $this ->_niveau = $niveau;
70 }
71 }
72
73 public function setExperience($exp)
74 {
75 $exp = (int) $exp;
76
77 // On vrifie que l'exprience est comprise entre 0 et 100.
78 if ($exp >= 0 && $exp <= 100)
79 {
80 $this ->_experience = $exp;
81 }
82 }
83 }
84 ?>
Vient maintenant lapect le plus important : que doit-on mettre dans cette mthode ?
Souvenez-vous de sa fonction : elle doit hydrater lobjet, cest--dire assigner les
valeurs passes en paramtres aux attributs correspondant . Cependant, je vous le dis
avant que vous fassiez fausse route : il ne faut pas assigner ces valeurs directement,
comme ceci :
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 if (isset($donnees['id']))
6 {
7 $this ->_id = $donnees['id'];
8 }
9
10 if (isset($donnees['nom']))
11 {
50
LHYDRATATION
12 $this ->_nom = $donnees['nom'];
13 }
14
15 // ...
16 }
17 // ...
18 ?>
La principale raison pour laquelle cest une mauvaise faon de procder, est quen
agissant ainsi vous violez le principe dencapsulation. En eet, de cette manire, vous
ne contrlez pas lintgrit des valeurs. Qui vous garantit que le tableau contiendra un
nom valide ? Rien du tout ! Comment contrler alors lintgrit de ces valeurs ? Regardez
un peu votre classe, la rponse est sous vos yeux. . . Cest le rle des setters de faire ces
vrications ! Il faudra donc les appeler au lieu dassigner les valeurs directement :
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 if (isset($donnees['id']))
6 {
7 $this ->setId($donnees['id']);
8 }
9
10 if (isset($donnees['nom']))
11 {
12 $this ->setNom($donnees['nom']);
13 }
14
15 // ...
16 }
17 // ...
18 ?>
Thoriquement, nous avons termin le travail. Cependant, cela nest pas trs exible :
si vous ajoutez un attribut (et donc son setter correspondant), il faudra modier votre
mthode hydrate() pour ajouter la possibilit dassigner cette valeur. De plus, avec
les 6 attributs actuellement crs, il est long de vrier si la cl existe puis dappeler la
mthode correspondante.
Nous allons donc procder plus rapidement : nous allons crer une boucle qui va par-
courir le tableau pass en paramtre. Si le setter correspondant la cl existe, alors on
appelle ce setter en lui passant la valeur en paramtre. En langage naturel, lalgorithme
peut scrire de cette faon :
1 PARCOURS du tableau $donnees (avec pour cl $cle et pour valeur
$valeur)
2 On assigne $setter la valeur 'set'.$cle , en mettant la
premire lettre de $cle en majuscule (utilisation de
ucfirst ())
3 SI la mthode $setter de notre classe existe ALORS
51
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
4 On invoque $setter($valeur)
5 FIN SI
6 FIN PARCOURS
Pas de panique, je vais vous expliquer chaque ligne. Dans votre tte, prenez un exemple
de valeur pour $donnees :
1 <?php
2 $donnees = array(
3 'id' => 16 ,
4 'nom' => 'Vyk12',
5 'forcePerso ' => 5,
6 'degats ' => 55 ,
7 'niveau ' => 4,
8 'experience ' => 20
9 );
10 ?>
Vous voyez bien que chaque cl correspond un attribut de notre objet, qui on
assigne une valeur prcise. Dans notre mthode hydrate(), lorsquon parcourt notre
tableau, on a successivement la cl id, puis nom, puis forcePerso, etc., avec leur valeur
correspondante.
En rcuprant le nom de lattribut, il est facile de dterminer le setter correspondant.
En eet, chaque setter a pour nom setNomDeLAttribut.
Il est important de prciser que la premire lettre du nom de lattribut doit
tre en majuscule. Par exemple, le setter correspondant nom est setNom.
Pour linstant notre mthode ressemble ceci :
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 foreach ($donnees as $key => $value)
6 {
7 $method = 'set'.ucfirst($key);
8 // ...
9 }
10 }
11 // ...
12 ?>
Il faut maintenant vrier que la mthode existe. Pour cela, nous allons utiliser la
fonction method_exists(). Vous pouvez accder sa documentation via le code web
suivant :

fonction method_exists()
Code web : 439083
52
LHYDRATATION
Elle prend en premier paramtre le nom de la classe ou une instance de cette classe, et
en deuxime paramtre le nom de la mthode qui nous intresse. La mthode renvoie
true si la mthode existe, sinon false.
Je vous laisse ajouter cette condition qui permet de voir si le setter correspondant
existe.
Voici la correction :
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 foreach ($donnees as $key => $value)
6 {
7 $method = 'set'.ucfirst($key);
8
9 if (method_exists($this , $method))
10 {
11 // ...
12 }
13 }
14 }
15 // ...
16 ?>
Et maintenant, il ne reste plus qu appeler le setter lintrieur de la condition ! Pour
cela, je vais vous apprendre une petite astuce. Il est possible dappeler une mthode dy-
namiquement, cest--dire appeler une mthode dont le nom nest pas connu lavance
(en dautres termes, le nom est connu seulement pendant lexcution et est donc stock
dans une variable). Pour ce faire, rien de plus simple ! Regardez ce code :
1 <?php
2 class A
3 {
4 public function hello ()
5 {
6 echo 'Hello world !';
7 }
8 }
9
10 $a = new A;
11 $method = 'hello';
12
13 $a ->$method (); // Affiche : Hello world !
14 ?>
Il est bien entendu possible de passer des arguments la mthode.
53
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
Vous tes maintenant capables de crer la dernire instruction dans notre mthode !
Pour rappel, celle-ci doit invoquer le setter dont le nom est contenu dans la variable
$method.
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 foreach ($donnees as $key => $value)
6 {
7 // On rcupre le nom du setter correspondant l'attribut.
8 $method = 'set'.ucfirst($key);
9
10 // Si le setter correspondant existe.
11 if (method_exists($this , $method))
12 {
13 // On appelle le setter.
14 $this ->$method($value);
15 }
16 }
17 }
18 // ...
19 ?>
Cette fonction est trs importante, vous la retrouverez dans de nombreux codes (parfois
sous des formes direntes) provenant de plusieurs dveloppeurs. Gardez-l donc dans
un coin de votre tte.
Il est courant dimplmenter un constructeur ces classes demandant un
tableau de valeurs pour quil appelle ensuite la fonction dhydratation an
que lobjet soit hydrat ds sa cration, comme on la vu au dbut de ce
chapitre.
Grer sa BDD correctement
On vient de voir jusqu prsent comment grer les donnes que les requtes nous
renvoient, mais o placer ces requtes ? Notre but est de programmer orient objet, donc
nous voulons le moins de code possible en-dehors des classes pour mieux lorganiser.
Beaucoup de dbutants sont tents de placer les requtes dans des mthodes de la classe
reprsentant une entit de la BDD. Par exemple, dans le cas du personnage, je parie
que la plupart dentre vous seraient tents dcrire les mthodes add() et update()
dans la classe Personnage, ayant respectivement pour rle dexcuter une requte pour
ajouter et modier un personnage en BDD.
Arrtez-vous l tout de suite ! Je vais vous expliquer pourquoi vous faites fausse route,
puis vous montrerai la bonne marche suivre.
54
GRER SA BDD CORRECTEMENT
Une classe, un rle
En POO, il y a une phrase trs importante quil faut que vous ayez constamment en
tte : Une classe, un rle. Maintenant, rpondez clairement cette question : Quel
est le rle dune classe comme Personnage ?
Un objet instanciant une classe comme Personnage a pour rle de reprsenter une ligne
prsente en BDD. Le verbe reprsenter est ici trs important. En eet, reprsen-
ter est trs dirent de grer . Une ligne de la BDD ne peut pas sauto-grer ! Cest
comme si vous demandiez un ouvrier ayant construit un produit de le commerciali-
ser : louvrier est tout fait capable de le construire, cest son rle, mais il ne soccupe
pas du tout de sa gestion, il en est incapable. Il faut donc quune deuxime personne
intervienne, un commercial, qui va soccuper de vendre ce produit.
Pour revenir nos objets dorigine, nous aurons donc besoin de quelque chose qui va
soccuper de les grer. Ce quelque chose, vous laurez peut-tre devin, nest autre
quun objet. Un objet grant des entits issues dune BDD est gnralement appel un
manager .
Comme un manager ne fonctionne pas sans support de stockage (dans notre cas, une
BDD), on va prendre un exemple concret en crant un gestionnaire pour nos per-
sonnages, qui va donc se charger den ajouter, den modier, den supprimer et den
rcuprer. Puisque notre classe est un gestionnaire de personnages, je vous propose de
la nommer PersonnagesManager. Cependant, rappelez-vous les questions que lon doit
se poser pour tablir le plan de notre classe :
Quelles seront les caractristiques de mes objets ?
Quelles seront les fonctionnalits de mes objets ?
Les caractristiques dun manager
Partie dlicate, car cela est moins intuitif que de trouver les caractristiques dun
personnage. Pour trouver la rponse, vous devrez passer par une autre question (ce
serait trop simple de toujours rpondre la mme question !) : De quoi a besoin un
manager pour fonctionner ?
Mme si la rponse ne vous vient pas spontanment lesprit, vous la connaissez : elle
a besoin dune connexion la BDD pour pouvoir excuter des requtes. En utilisant
PDO, vous devriez savoir que la connexion la BDD est reprsente par un objet, un
objet daccs la BDD (ou DAO pour Database Access Object). Vous lavez peut-tre
compris : notre manager aura besoin de cet objet pour fonctionner, donc notre classe
aura un attribut stockant cet objet.
Notre classe a-t-elle besoin dautre chose pour fonctionner ?
Non, cest tout. Vous pouvez donc commencer crire votre classe :
1 <?php
2 class PersonnagesManager
3 {
4 private $_db;
55
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
5 }
6 ?>
Les fonctionnalits dun manager
Pour pouvoir grer au mieux des entits prsentes en BDD (ici, nos personnages), il va
falloir quelques fonctionnalits de base. Quelles sont-elles ?
Un manager doit pouvoir :
enregistrer une nouvelle entit ;
modier une entit ;
supprimer une entit ;
slectionner une entit.
Cest le fameux CRUD (Create, Read, Update, Delete). Noublions pas aussi dajouter
un setter pour notre manager an de pouvoir modier lattribut $_db (pas besoin
daccesseur). La cration dun constructeur sera aussi indispensable si nous voulons
assigner cet attribut un objet PDO ds linstanciation du manager.
Nous allons donc crire notre premier manager en crivant les mthodes correspondant
ces fonctionnalits. crivez dans un premier temps les mthodes en ne les remplissant
que de commentaires dcrivant les instructions qui y seront crites. Cela vous permettra
dorganiser clairement le code dans votre tte.
1 <?php
2 class PersonnagesManager
3 {
4 private $_db; // Instance de PDO.
5
6 public function __construct($db)
7 {
8 $this ->setDb($db);
9 }
10
11 public function add(Personnage $perso)
12 {
13 // Prparation de la requte d'insertion.
14 // Assignation des valeurs pour le nom , la force , les dg
ts , l'exprience et le niveau du personnage.
15 // Excution de la requte.
16 }
17
18 public function delete(Personnage $perso)
19 {
20 // Excute une requte de type DELETE.
21 }
22
23 public function get($id)
24 {
56
GRER SA BDD CORRECTEMENT
25 // Excute une requte de type SELECT avec une clause WHERE
, et retourne un objet Personnage.
26 }
27
28 public function getList ()
29 {
30 // Retourne la liste de tous les personnages.
31 }
32
33 public function update(Personnage $perso)
34 {
35 // Prpare une requte de type UPDATE.
36 // Assignation des valeurs la requte.
37 // Excution de la requte.
38 }
39
40 public function setDb(PDO $db)
41 {
42 $this ->_db = $db;
43 }
44 }
45 ?>
Une fois cette tape accomplie, vous pouvez remplacer les commentaires par les ins-
tructions correspondantes. Cest la partie la plus facile car vous lavez dj fait de
nombreuses fois en procdural : ce ne sont que des requtes taper et des valeurs
retourner.
1 <?php
2 class PersonnagesManager
3 {
4 private $_db; // Instance de PDO
5
6 public function __construct($db)
7 {
8 $this ->setDb($db);
9 }
10
11 public function add(Personnage $perso)
12 {
13 $q = $this ->_db ->prepare('INSERT INTO personnages SET nom =
:nom , forcePerso = :forcePerso , degats = :degats ,
niveau = :niveau , experience = :experience ');
14
15 $q ->bindValue(':nom', $perso ->nom());
16 $q ->bindValue(':forcePerso ', $perso ->forcePerso (), PDO::
PARAM_INT);
17 $q ->bindValue(':degats ', $perso ->degats (), PDO:: PARAM_INT);
18 $q ->bindValue(':niveau ', $perso ->niveau (), PDO:: PARAM_INT);
19 $q ->bindValue(':experience ', $perso ->experience (), PDO::
PARAM_INT);
57
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
20
21 $q->execute ();
22 }
23
24 public function delete(Personnage $perso)
25 {
26 $this ->_db ->exec('DELETE FROM personnages WHERE id = '.
$perso ->id());
27 }
28
29 public function get($id)
30 {
31 $id = (int) $id;
32
33 $q = $this ->_db ->query('SELECT id, nom , forcePerso , degats ,
niveau , experience FROM personnages WHERE id = '.$id);
34 $donnees = $q->fetch(PDO:: FETCH_ASSOC);
35
36 return new Personnage($donnees);
37 }
38
39 public function getList ()
40 {
41 $persos = array();
42
43 $q = $this ->_db ->query('SELECT id, nom , forcePerso , degats ,
niveau , experience FROM personnages ORDER BY nom');
44
45 while ($donnees = $q ->fetch(PDO:: FETCH_ASSOC))
46 {
47 $persos [] = new Personnage($donnees);
48 }
49
50 return $persos;
51 }
52
53 public function update(Personnage $perso)
54 {
55 $q = $this ->_db ->prepare('UPDATE personnages SET forcePerso
= :forcePerso , degats = :degats , niveau = :niveau ,
experience = :experience WHERE id = :id');
56
57 $q->bindValue(':forcePerso ', $perso ->forcePerso (), PDO::
PARAM_INT);
58 $q->bindValue(':degats ', $perso ->degats (), PDO:: PARAM_INT);
59 $q->bindValue(':niveau ', $perso ->niveau (), PDO:: PARAM_INT);
60 $q->bindValue(':experience ', $perso ->experience (), PDO::
PARAM_INT);
61 $q->bindValue(':id', $perso ->id(), PDO:: PARAM_INT);
62
58
GRER SA BDD CORRECTEMENT
63 $q ->execute ();
64 }
65
66 public function setDb(PDO $db)
67 {
68 $this ->_db = $db;
69 }
70 }
71 ?>
Essayons tout a !
Faisons un petit test pour sassurer que tout cela fonctionne.
Assurez-vous de bien avoir cr une table personnages dans votre BDD.
Commenons par crer un objet Personnage :
1 <?php
2 $perso = new Personnage(array(
3 'nom' => 'Victor ',
4 'forcePerso ' => 5,
5 'degats ' => 0,
6 'niveau ' => 1,
7 'experience ' => 0
8 ));
9 ?>
Maintenant, comment lajouter en BDD? Il faudra dabord crer une instance de notre
manager, en lui passant une instance de PDO.
1 <?php
2 $perso = new Personnage(array(
3 'nom' => 'Victor ',
4 'forcePerso ' => 5,
5 'degats ' => 0,
6 'niveau ' => 1,
7 'experience ' => 0
8 ));
9
10 $db = new PDO('mysql:host=localhost;dbname=tests ', 'root', '');
11 $manager = new PersonnagesManager($db);
12 ?>
Nous navons plus quune instruction entrer : celle ordonnant lajout du personnage
en BDD. Et cest tout !
59
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
1 <?php
2 $perso = new Personnage(array(
3 'nom' => 'Victor ',
4 'forcePerso ' => 5,
5 'degats ' => 0,
6 'niveau ' => 1,
7 'experience ' => 0
8 ));
9
10 $db = new PDO('mysql:host=localhost;dbname=tests ', 'root', '');
11 $manager = new PersonnagesManager($db);
12
13 $manager ->add($perso);
14 ?>
En rsum
Il est maintenant temps de mettre en pratique ce que vous venez dapprendre. Cepen-
dant, avant de continuer, assurez-vous bien de tout avoir compris, sinon vous serez
perdus car vous ne comprendrez pas le code du TP!
Du ct du script PHP, chaque enregistrement de la base de donnes est reprsent
par un objet possdant une liste dattributs identique la liste des colonnes de la
table.
Il doit tre possible dhydrater chacun des objets grce une mthode hydrate qui
a pour rle dassigner les valeurs passes en paramtre aux attributs correspondant.
La communication avec la BDD se fait par le biais dun objet dirent de lobjet
reprsentant lenregistrement (une classe = un rle). Un tel objet est un manager.
Un manager peut stocker les objets en BDD, mais peut tout--fait les stocker sur
un autre support (chier XML, chier texte, etc.).
60
Chapitre 5
TP : Mini-jeu de combat
Dicult :
V
oici un petit TP qui mettra en pratique ce que lon vient de voir. Celui-ci est troi-
tement li avec le prcdent chapitre. Ainsi, je vous conseille davoir bien compris ce
dernier avant daborder ce TP, sinon vous narriverez sans doute pas au rsultat tout
seul.
En tout, vous aurez 3 TP de la sorte dont le niveau de dicult sera croissant. Puisque
celui-ci est votre premier, tout sera expliqu dans les moindres dtails.
61
CHAPITRE 5. TP : MINI-JEU DE COMBAT
Ce quon va faire
Pour information, jutilise ici PDO. Si vous ne savez toujours pas maitriser cette API,
alors allez lire le tutoriel PDO : Interface daccs aux BDD, accessible via le code web
suivant :

Tutoriel PDO
Code web : 709358
Cahier des charges
Ce que nous allons raliser est trs simple. Nous allons crer une sorte de jeu. Chaque
visiteur pourra crer un personnage (pas de mot de passe requis pour faire simple) avec
lequel il pourra frapper dautres personnages. Le personnage frapp se verra iniger un
certain degr de dgts.
Un personnage est dni selon 2 caractristiques :
Son nom (unique).
Ses dgts.
Les dgts dun personnage sont compris entre 0 et 100. Au dbut, il a bien entendu
0 de dgt. Chaque coup qui lui sera port lui fera prendre 5 points de dgts. Une
fois arriv 100 points de dgts, le personnage est mort (on le supprimera alors de la
BDD).
Notions utilises
Voici une petite liste vous indiquant les points techniques que lon va mettre en pratique
avec ce TP :
Les attributs et mthodes ;
linstanciation de la classe ;
les constantes de classe ;
et surtout, tout ce qui touche la manipulation de donnes stockes.
Cela dit, et cest je pense ce qui sera le plus dicile, ce qui sera mis en valeur ici
sera la conception dun mini-projet, cest--dire que lon travaillera surtout ici votre
capacit programmer orient objet. Cependant, ne prenez pas trop peur : nous avons
eectu une importante partie thorique lors du prcdent chapitre, et rien de nouveau
napparatra.
Vous avez donc maintenant une ide de ce qui vous attend. Cependant, ceci tant votre
premier TP concernant la POO, il est fort probable que vous ne sachiez pas par o
commencer, et cest normal ! nous allons donc raliser le TP ensemble : je vais vous
expliquer comment penser un mini-projet et vous poser les bonnes questions.
62
PREMIRE TAPE : LE PERSONNAGE
Pr-conception
Avant de nous attaquer au cur du script, nous allons rchir son organisation. De
quoi aura-t-on besoin ? Puisque nous travaillerons avec des personnages, nous aurons
besoin de les stocker pour quils puissent durer dans le temps. Lutilisation dune base
de donnes sera donc indispensable.
Le script tant simple, nous naurons quune table personnages qui aura dirents
champs. Pour les dnir, rchissez ce qui caractrise un personnage. Ainsi nous
connaissons dj 2 champs de cette table que nous avons dnis au dbut : nom et
dgts . Et bien sr, noublions pas le plus important : lidentiant du personnage !
Chaque personnage doit possder un identiant unique qui permet ainsi de le rechercher
plus rapidement (au niveau performances) quavec son nom.
Vous pouvez donc ainsi crer votre table tous seuls via PhpMyAdmin. Si vous ntes
pas srs de vous, je vous laisse le code SQL crant cette table :
1 CREATE TABLE IF NOT EXISTS personnages (
2 id smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
3 nom varchar(50) COLLATE latin1_general_ci NOT NULL ,
4 degats tinyint(3) unsigned NOT NULL DEFAULT '0',
5 PRIMARY KEY (id ),
6 UNIQUE KEY nom (nom )
7 ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=
latin1_general_ci;

Voir le rsultat obtenir


Code web : 451073
Le rsultat prsent ici contient les amliorations proposes en n de chapitre.
Premire tape : le personnage
Nous allons commencer le TP. Pour rappel, nous allons raliser un petit jeu met-
tant en scne des personnages qui peuvent combattre. Qui dit personnages dit objets
Personnage (je pense que a, vous lavez devin, puisque nous avons travaill dessus
durant les premiers chapitres).
Arrive maintenant un moment dlicat dans votre tte : Par o je commence ?
Souvenez-vous de la premire partie du prcdent chapitre. Pour construire une classe,
vous devez rpondre deux questions qui vont vous permettre dtablir le plan de votre
classe :
Quelles seront les caractristiques de mes objets ?
Quelles seront les fonctionnalits de mes objets ?
Voici comment procder. Nous allons dans un premier temps dresser la liste des carac-
tristiques du personnage pour ensuite se pencher sur ses fonctionnalits.
63
CHAPITRE 5. TP : MINI-JEU DE COMBAT
Rappel : le traitement des donnes (cest--dire lexcution de requtes per-
mettant daller eectuer des oprations en BDD) se fera dans une autre
classe. Ne vous en proccupez donc pas maintenant !
Les caractristiques du personnage
Essayez de vous souvenir quelles sont les caractristiques dun personnage (ou plus
globalement celles dune entit reprsentant un enregistrement prsent en BDD). Nous
lavons vu dans la premire partie du prcdent chapitre.
Les attributs dun objet reprsentant un enregistrement prsent en BDD cor-
respondent aux noms des champs de cet enregistrement.
Si vous navez pas retenu cette information, notez-l quelque part, vous en aurez besoin
un trs grand nombre de fois.
Maintenant que nous avons dtermin les caractristiques dun objet Personnage, nous
savons quels attributs placer dans notre classe. Voici une premire criture de notre
classe Personnage :
1 <?php
2 class Personnage
3 {
4 private $_id ,
5 $_degats ,
6 $_nom;
7 }
Jusque l tout va bien. Tournons-nous maintenant vers les fonctionnalits que doit
possder un personnage.
Les fonctionnalits dun personnage
Comme nous lavons dit tout--lheure, pour obtenir les mthodes dun objet, il faut
se demander quelles seront les fonctionnalits de ces entits. Ici, que pourra faire un
personnage ? Relisez les consignes du dbut de chapitre et rpondez clairement la
question.
Un personnage doit pouvoir :
Frapper un autre personnage ;
recevoir des dgts.
chaque fonctionnalit correspond une mthode. crivez ces mthodes dans la classe
en mettant en commentaire ce quelles doivent faire.
64
PREMIRE TAPE : LE PERSONNAGE
Ne vous tonnez pas si vous avez un rsultat bien dirent du mien. Le
contenu des mthodes importe peu car il est issu de mon imagination et il est
normal que vous nayez pas pens le script comme moi. Gardez votre version,
nessayez pas de copier/coller mon code.
1 <?php
2 class Personnage
3 {
4 private $_id ,
5 $_degats ,
6 $_nom;
7
8 public function frapper(Personnage $perso)
9 {
10 // Avant tout : vrifier qu'on ne se frappe pas soi -mme.
11 // Si c'est le cas , on stoppe tout en renvoyant une valeur
signifiant que le personnage cibl est le personnage qui
attaque.
12
13 // On indique au personnage frapp qu'il doit recevoir des
dgts.
14 }
15
16 public function recevoirDegats ()
17 {
18 // On augmente de 5 les dgts.
19
20 // Si on a 100 de dgts ou plus , la mthode renverra une
valeur signifiant que le personnage a t tu.
21
22 // Sinon , elle renverra une valeur signifiant que le
personnage a bien t frapp.
23 }
24 }
Tout ceci nest que du franais, nous sommes encore ltape de rexion. Si vous
sentez que a va trop vite, relisez le dbut du TP et suivez bien les tapes. Cest trs
important car cest en gnral cette tape qui pose problme : la rexion! Beaucoup
de dbutants foncent tte baisse sans rendre le temps de concevoir correctement les
mthodes au dbut et se plantent royalement. Prenez donc bien votre temps.
Normalement, des petites choses doivent vous chionner.
Pour commencer, la premire chose qui doit vous interpeller se situe dans la mthode
frapper(), lorsquil faut vrier quon ne se frappe pas soi-mme. Pour cela, il sut de
comparer lidentiant du personnage qui attaque avec lidentiant du personnage cibl.
En eet, si lidentiant est le mme, alors il sagira dun seul et mme personnage.
Ensuite, certains moments dans le code, il est dit que la mthode renverra une
valeur signiant que le personnage a bien t frapp par exemple. Quest-ce que
65
CHAPITRE 5. TP : MINI-JEU DE COMBAT
cela peut bien signier ? Ce genre de commentaire laissera place des constantes de
classe (si vous laviez devin, bien jou !). Si vous regardez bien le code, vous verrez 3
commentaires de la sorte :
Mthode frapper() : [. . .] renvoyant une valeur signiant que le personnage cibl
est le personnage qui attaque la mthode renverra la valeur de la constante
CEST_MOI ;
Mthode recevoirDegats() : la mthode renverra une valeur signiant que le per-
sonnage a t tu la mthode renverra la valeur de la constante PERSONNAGE_TUE ;
Mthode recevoirDegats() : elle renverra une valeur signiant que le personnage
a bien t frapp la mthode renverra la valeur de la constante PERSONNAGE_FRAPPE.
Vous pouvez ajouter ces constantes votre classe.
1 <?php
2 class Personnage
3 {
4 private $_id ,
5 $_degats ,
6 $_nom;
7
8 const CEST_MOI = 1;
9 const PERSONNAGE_TUE = 2;
10 const PERSONNAGE_FRAPPE = 3;
11
12 public function frapper(Personnage $perso)
13 {
14 // Avant tout : vrifier qu'on ne se frappe pas soi -mme.
15 // Si c'est le cas , on stoppe tout en renvoyant une
valeur signifiant que le personnage cibl est le
personnage qui attaque.
16
17 // On indique au personnage frapp qu'il doit recevoir des
dgts.
18 }
19
20 public function recevoirDegats ()
21 {
22 // On augmente de 5 les dgts.
23
24 // Si on a 100 de dgts ou plus , la mthode renverra une
valeur signifiant que le personnage a t tu.
25
26 // Sinon , elle renverra une valeur signifiant que le
personnage a bien t frapp.
27 }
28 }
66
PREMIRE TAPE : LE PERSONNAGE
Les getters et setters
Actuellement, les attributs de nos objets sont inaccessibles. Il faut crer des getters
pour pouvoir les lire, et des setters pour pouvoir modier leurs valeurs. Nous allons
aller un peu plus rapidement que les prcdentes mthodes en crivant directement le
contenu de celles-ci car une ou deux instructions susent en gnral.
Noubliez pas de vrier lintgrit des donnes dans les setters sinon ils
perdent tout leur intrt !
1 <?php
2 class Personnage
3 {
4 private $_id ,
5 $_degats ,
6 $_nom;
7
8 const CEST_MOI = 1;
9 const PERSONNAGE_TUE = 2;
10 const PERSONNAGE_FRAPPE = 3;
11
12 public function frapper(Personnage $perso)
13 {
14 // Avant tout : vrifier qu'on ne se frappe pas soi -mme.
15 // Si c'est le cas , on stoppe tout en renvoyant une
valeur signifiant que le personnage cibl est le
personnage qui attaque.
16
17 // On indique au personnage frapp qu'il doit recevoir des
dgts.
18 }
19
20 public function recevoirDegats ()
21 {
22 // On augmente de 5 les dgts.
23
24 // Si on a 100 de dgts ou plus , la mthode renverra une
valeur signifiant que le personnage a t tu.
25
26 // Sinon , elle renverra une valeur signifiant que le
personnage a bien t frapp.
27 }
28
29 public function degats ()
30 {
31 return $this ->_degats;
32 }
33
67
CHAPITRE 5. TP : MINI-JEU DE COMBAT
34 public function id()
35 {
36 return $this ->_id;
37 }
38
39 public function nom()
40 {
41 return $this ->_nom;
42 }
43
44 public function setDegats($degats)
45 {
46 $degats = (int) $degats;
47
48 if ($degats >= 0 && $degats <= 100)
49 {
50 $this ->_degats = $degats;
51 }
52 }
53
54 public function setId($id)
55 {
56 $id = (int) $id;
57
58 if ($id > 0)
59 {
60 $this ->_id = $id;
61 }
62 }
63
64 public function setNom($nom)
65 {
66 if (is_string($nom))
67 {
68 $this ->_nom = $nom;
69 }
70 }
71 }
Hydrater ses objets
Deuxime point essentiel que nous allons r-exploiter dans ce TP : lhydratation.
Je nai pas dexplication supplmentaire ajouter, tout est dit dans le prcdent cha-
pitre. La mthode cre dans ce dernier est dailleurs rutilisable ici. Au lieu de regarder
la correction en vous replongeant dans le chapitre prcdent, essayez plutt de rcrire
la mthode. Pour rappel, celle-ci doit permettre dassigner aux attributs de lobjet les
valeurs correspondantes, passes en paramtre dans un tableau. Si vous avez essay de
rcrire la mthode, voici le rsultat que vous auriez du obtenir :
68
PREMIRE TAPE : LE PERSONNAGE
1 <?php
2 class Personnage
3 {
4 // ...
5
6 public function hydrate(array $donnees)
7 {
8 foreach ($donnees as $key => $value)
9 {
10 $method = 'set'.ucfirst($key);
11
12 if (method_exists($this , $method))
13 {
14 $this ->$method($value);
15 }
16 }
17 }
18
19 // ...
20 }
Il ne manque plus qu implmenter le constructeur pour quon puisse directement hy-
drater notre objet lors de linstanciation de la classe. Pour cela, ajoutez un paramtre :
$donnees. Appelez ensuite directement la mthode hydrate().
1 <?php
2 class Personnage
3 {
4 // ...
5
6 public function __construct(array $donnees)
7 {
8 $this ->hydrate($donnees);
9 }
10
11 // ...
12 }
Codons le tout !
La partie rexion est termin, il est maintenant temps dcrire notre classe Personnage !
Voici la correction que je vous propose :
1 <?php
2 class Personnage
3 {
4 private $_degats ,
5 $_id ,
6 $_nom;
69
CHAPITRE 5. TP : MINI-JEU DE COMBAT
7
8 const CEST_MOI = 1; // Constante renvoye par la mthode
frapper si on se frappe soi -mme.
9 const PERSONNAGE_TUE = 2; // Constante renvoye par la m
thode frapper si on a tu le personnage en le frappant.
10 const PERSONNAGE_FRAPPE = 3; // Constante renvoye par la m
thode frapper si on a bien frapp le personnage.
11
12
13 public function __construct(array $donnees)
14 {
15 $this ->hydrate($donnees);
16 }
17
18 public function frapper(Personnage $perso)
19 {
20 if ($perso ->id() == $this ->_id)
21 {
22 return self:: CEST_MOI;
23 }
24
25 // On indique au personnage qu'il doit recevoir des dgts.
26 // Puis on retourne la valeur renvoye par la mthode :
self:: PERSONNAGE_TUE ou self:: PERSONNAGE_FRAPPE
27 return $perso ->recevoirDegats ();
28 }
29
30 public function hydrate(array $donnees)
31 {
32 foreach ($donnees as $key => $value)
33 {
34 $method = 'set'.ucfirst($key);
35
36 if (method_exists($this , $method))
37 {
38 $this ->$method($value);
39 }
40 }
41 }
42
43 public function recevoirDegats ()
44 {
45 $this ->_degats += 5;
46
47 // Si on a 100 de dgts ou plus , on dit que le personnage
a t tu.
48 if ($this ->_degats >= 100)
49 {
50 return self:: PERSONNAGE_TUE;
51 }
70
PREMIRE TAPE : LE PERSONNAGE
52
53 // Sinon , on se contente de dire que le personnage a bien
t frapp.
54 return self:: PERSONNAGE_FRAPPE;
55 }
56
57
58 // GETTERS //
59
60
61 public function degats ()
62 {
63 return $this ->_degats;
64 }
65
66 public function id()
67 {
68 return $this ->_id;
69 }
70
71 public function nom()
72 {
73 return $this ->_nom;
74 }
75
76 public function setDegats($degats)
77 {
78 $degats = (int) $degats;
79
80 if ($degats >= 0 && $degats <= 100)
81 {
82 $this ->_degats = $degats;
83 }
84 }
85
86 public function setId($id)
87 {
88 $id = (int) $id;
89
90 if ($id > 0)
91 {
92 $this ->_id = $id;
93 }
94 }
95
96 public function setNom($nom)
97 {
98 if (is_string($nom))
99 {
100 $this ->_nom = $nom;
71
CHAPITRE 5. TP : MINI-JEU DE COMBAT
101 }
102 }
103 }
Prenez le temps de bien comprendre ce code et de lire les commentaires. Il est important
que vous cerniez son fonctionnement pour savoir ce que vous faites. Cependant, si vous
ne comprenez pas toutes les instructions places dans les mthodes, ne vous aolez
pas : si vous avez compris globalement le rle de chacune delles, vous naurez pas de
handicap pour suivre la prochaine sous-partie.
Seconde tape : stockage en base de donnes
Attaquons-nous maintenant la deuxime grosse partie de ce TP, celle consistant
pouvoir stocker nos personnages dans une base de donnes. Grande question mainte-
nant : comment faire ?
Nous avons rpondu cette question dans la troisime partie du prcdent chapitre.
Au cas o certains seraient toujours tents de placer les requtes qui iront chercher les
personnages en BDD dans la classe Personnage, je vous arrte tout de suite et vous
fais un bref rappel avec cette phrase que vous avez dj rencontre : une classe, un
rle.
Jespre que vous lavez retenue cette fois-ci !
Vous souvenez-vous de ce que cela signie ? Quel est le rle de notre classe
Personnage ? O placer nos requtes ?
La classe Personnage a pour rle de reprsenter un personnage prsent en BDD. Elle
na en aucun cas pour rle de les grer. Cette gestion sera le rle dune autre classe,
communment appele manager. Dans notre cas, notre gestionnaire de personnage
sera tout simplement nomme PersonnagesManager.
Comment va-t-on faire pour construire ces classes ? Quelles questions va-t-on
se poser ?
Quelles seront les caractristiques dun manager ?
Quelles seront les fonctionnalits dun manager ?
Les caractristiques dun manager
Encore une fois, ce point a t abord dans la troisime partie du prcdent chapitre.
Allez y faire un tour si vous avez un trou de mmoire ! Voici le code de la classe
contenant sa (grande) liste dattributs.
72
SECONDE TAPE : STOCKAGE EN BASE DE DONNES
1 <?php
2 class PersonnagesManager
3 {
4 private $_db;
5 }
Les fonctionnalits dun manager
Dans la troisime partie du prcdent chapitre, nous avons vu quelques fonctionnalits
de base. Notre manager pouvait :
Enregistrer un nouveau personnage ;
modier un personnage ;
supprimer un personnage ;
slectionner un personnage.
Cependant, ici, nous pouvons ajouter quelques fonctionnalits qui pourront nous tre
utiles :
Compter le nombre de personnages ;
rcuprer une liste de plusieurs personnages ;
savoir si un personnage existe.
Cela nous fait ainsi 7 mthodes implmenter !
Rappels du prcdent chapitre : noubliez pas dajouter un setter pour
notre manager an de pouvoir modier lattribut $_db. La cration dun
constructeur sera aussi indispensable si nous voulons assigner cet attribut
un objet PDO ds linstanciation du manager.
Comme dhabitude, crivez le nom des mthodes en ajoutant des commentaires sur ce
que doit faire la mthode.
1 <?php
2 class PersonnagesManager
3 {
4 private $_db; // Instance de PDO
5
6 public function __construct($db)
7 {
8 $this ->setDb($db);
9 }
10
11 public function add(Personnage $perso)
12 {
13 // Prparation de la requte d'insertion.
14 // Assignation des valeurs pour le nom du personnage.
15 // Excution de la requte.
16
73
CHAPITRE 5. TP : MINI-JEU DE COMBAT
17 // Hydratation du personnage pass en paramtre avec
assignation de son identifiant et des dgts initiaux (=
0).
18 }
19
20 public function count ()
21 {
22 // Excute une requte COUNT() et retourne le nombre de r
sultats retourn.
23 }
24
25 public function delete(Personnage $perso)
26 {
27 // Excute une requte de type DELETE.
28 }
29
30 public function exists($info)
31 {
32 // Si le paramtre est un entier , c'est qu'on a fourni un
identifiant.
33 // On excute alors une requte COUNT () avec une clause
WHERE , et on retourne un boolean.
34
35 // Sinon c'est qu'on a pass un nom.
36 // Excution d'une requte COUNT() avec une clause WHERE ,
et retourne un boolean.
37 }
38
39 public function get($info)
40 {
41 // Si le paramtre est un entier , on veut rcuprer le
personnage avec son identifiant.
42 // Excute une requte de type SELECT avec une clause
WHERE , et retourne un objet Personnage.
43
44 // Sinon , on veut rcuprer le personnage avec son nom.
45 // Excute une requte de type SELECT avec une clause WHERE
, et retourne un objet Personnage.
46 }
47
48 public function getList($nom)
49 {
50 // Retourne la liste des personnages dont le nom n'est pas
$nom.
51 // Le rsultat sera un tableau d'instances de Personnage.
52 }
53
54 public function update(Personnage $perso)
55 {
56 // Prpare une requte de type UPDATE.
74
SECONDE TAPE : STOCKAGE EN BASE DE DONNES
57 // Assignation des valeurs la requte.
58 // Excution de la requte.
59 }
60
61 public function setDb(PDO $db)
62 {
63 $this ->_db = $db;
64 }
65 }
Codons le tout !
Normalement, lcriture des mthodes devrait tre plus facile que dans la prcdente
partie. En eet, ici, il ny a que des requtes crire : si vous savez utiliser PDO, vous
ne devriez pas avoir de mal !
1 <?php
2 class PersonnagesManager
3 {
4 private $_db; // Instance de PDO
5
6 public function __construct($db)
7 {
8 $this ->setDb($db);
9 }
10
11 public function add(Personnage $perso)
12 {
13 $q = $this ->_db ->prepare('INSERT INTO personnages SET nom =
:nom');
14 $q ->bindValue(':nom', $perso ->nom());
15 $q ->execute ();
16
17 $perso ->hydrate(array(
18 'id' => $this ->_db ->lastInsertId (),
19 'degats ' => 0,
20 ));
21 }
22
23 public function count ()
24 {
25 return $this ->_db ->query('SELECT COUNT (*) FROM personnages '
)->fetchColumn ();
26 }
27
28 public function delete(Personnage $perso)
29 {
30 $this ->_db ->exec('DELETE FROM personnages WHERE id = '.
$perso ->id());
75
CHAPITRE 5. TP : MINI-JEU DE COMBAT
31 }
32
33 public function exists($info)
34 {
35 if (is_int($info)) // On veut voir si tel personnage ayant
pour id $info existe.
36 {
37 return (bool) $this ->_db ->query('SELECT COUNT (*) FROM
personnages WHERE id = '.$info)->fetchColumn ();
38 }
39
40 // Sinon , c'est qu'on veut vrifier que le nom existe ou
pas.
41
42 $q = $this ->_db ->prepare('SELECT COUNT (*) FROM personnages
WHERE nom = :nom');
43 $q->execute(array(':nom' => $info));
44
45 return (bool) $q->fetchColumn ();
46 }
47
48 public function get($info)
49 {
50 if (is_int($info))
51 {
52 $q = $this ->_db ->query('SELECT id, nom , degats FROM
personnages WHERE id = '.$info);
53 $donnees = $q->fetch(PDO:: FETCH_ASSOC);
54
55 return new Personnage($donnees);
56 }
57 else
58 {
59 $q = $this ->_db ->prepare('SELECT id, nom , degats FROM
personnages WHERE nom = :nom');
60 $q->execute(array(':nom' => $info));
61
62 return new Personnage($q->fetch(PDO:: FETCH_ASSOC));
63 }
64 }
65
66 public function getList($nom)
67 {
68 $persos = array();
69
70 $q = $this ->_db ->prepare('SELECT id, nom , degats FROM
personnages WHERE nom <> :nom ORDER BY nom');
71 $q->execute(array(':nom' => $nom));
72
73 while ($donnees = $q ->fetch(PDO:: FETCH_ASSOC))
76
TROISIME TAPE : UTILISATION DES CLASSES
74 {
75 $persos [] = new Personnage($donnees);
76 }
77
78 return $persos;
79 }
80
81 public function update(Personnage $perso)
82 {
83 $q = $this ->_db ->prepare('UPDATE personnages SET degats = :
degats WHERE id = :id');
84
85 $q ->bindValue(':degats ', $perso ->degats (), PDO:: PARAM_INT);
86 $q ->bindValue(':id', $perso ->id(), PDO:: PARAM_INT);
87
88 $q ->execute ();
89 }
90
91 public function setDb(PDO $db)
92 {
93 $this ->_db = $db;
94 }
95 }
Troisime tape : utilisation des classes
Jai le plaisir de vous annoncer que vous avez fait le plus gros du travail ! Maintenant,
nous allons juste utiliser nos classes en les instanciant et en invoquant les mthodes
souhaites sur nos objets. Le plus dicile ici est de se mettre daccord sur le droulement
du jeu.
Celui-ci tant simple, nous naurons besoin que dun seul chier. Commenons par le
dbut : que doit acher notre mini-jeu lorsquon ouvre la page pour la premire fois ?
Il doit acher un petit formulaire nous demandant le nom du personnage quon veut
crer ou utiliser.
1 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
2 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
3 <head >
4 <title >TP : Mini jeu de combat </title >
5
6 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
7 </head >
8 <body >
9 <form action="" method="post">
10 <p>
77
CHAPITRE 5. TP : MINI-JEU DE COMBAT
11 Nom : <input type="text" name="nom" maxlength="50" />
12 <input type="submit" value="Crer ce personnage" name="
creer" />
13 <input type="submit" value="Utiliser ce personnage"
name="utiliser" />
14 </p>
15 </form >
16 </body >
17 </html >
Vient ensuite la partie traitement. Deux cas peuvent se prsenter :
Le joueur a cliqu sur Crer ce personnage. Le script devra crer un objet
Personnage en passant au constructeur un tableau contenant une entre (le nom
du personnage). Il faudra ensuite sassurer que le personnage ait un nom valide et
quil nexiste pas dj. Aprs ces vrications, lenregistrement en BDD pourra se
faire.
Le joueur a cliqu sur Utiliser ce personnage. Le script devra vrier si le per-
sonnage existe bien en BDD. Si cest le cas, on le rcupre de la BDD.
Pour savoir si le nom du personnage est valide, il va falloir implmenter une
mthode nomValide() (je vous laisse rchir quelle classe) qui retournera
true ou false suivant si le nom est valide ou pas. Un nom est valide sil
nest pas vide.
Cependant, avant de faire cela, il va falloir prparer le terrain.
Un autoload devra tre cr (bien que non indispensable puisquil ny a que deux
classes).
Une instance de PDO devra tre cre.
Une instance de notre manager devra tre cre.
Puisque notre manager a t cr, pourquoi ne pas acher en haut de page le nombre
de personnages crs ?
1 <?php
2 // On enregistre notre autoload.
3 function chargerClasse($classname)
4 {
5 require $classname.'.class.php';
6 }
7
8 spl_autoload_register('chargerClasse ');
9
10 $db = new PDO('mysql:host=localhost;dbname=combats ', 'root', ''
);
11 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_WARNING); //
On met une alerte chaque fois qu'une requte a chou.
12
13 $manager = new PersonnagesManager($db);
14
78
TROISIME TAPE : UTILISATION DES CLASSES
15 if (isset($_POST['creer']) && isset($_POST['nom'])) // Si on a
voulu crer un personnage.
16 {
17 $perso = new Personnage(array('nom' => $_POST['nom'])); // On
cre un nouveau personnage.
18
19 if (!$perso ->nomValide ())
20 {
21 $message = 'Le nom choisi est invalide.';
22 unset($perso);
23 }
24 elseif ($manager ->exists($perso ->nom()))
25 {
26 $message = 'Le nom du personnage est dj pris.';
27 unset($perso);
28 }
29 else
30 {
31 $manager ->add($perso);
32 }
33 }
34
35 elseif (isset($_POST['utiliser ']) && isset($_POST['nom'])) //
Si on a voulu utiliser un personnage.
36 {
37 if ($manager ->exists($_POST['nom'])) // Si celui -ci existe.
38 {
39 $perso = $manager ->get($_POST['nom']);
40 }
41 else
42 {
43 $message = 'Ce personnage n\'existe pas !'; // S'il n'
existe pas , on affichera ce message.
44 }
45 }
46 ?>
47 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
48 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
49 <head >
50 <title >TP : Mini jeu de combat </title >
51
52 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
53 </head >
54 <body >
55 <p>Nombre de personnages crs : <?php echo $manager ->count
(); ?></p>
56 <?php
57 if (isset($message)) // On a un message afficher ?
79
CHAPITRE 5. TP : MINI-JEU DE COMBAT
58 echo '<p>', $message , '</p>'; // Si oui , on l'affiche.
59 ?>
60 <form action="" method="post">
61 <p>
62 Nom : <input type="text" name="nom" maxlength="50" />
63 <input type="submit" value="Crer ce personnage" name="
creer" />
64 <input type="submit" value="Utiliser ce personnage"
name="utiliser" />
65 </p>
66 </form >
67 </body >
68 </html >
Au cas o, je vous donne la mthode nomValide() de la classe Personnage. Jespre
cependant que vous y tes arrivs, un simple contrle avec empty() et le tour est jou.
1 <?php
2 class Personnage
3 {
4 // ...
5
6 public function nomValide ()
7 {
8 return !empty($this ->_nom);
9 }
10
11 // ...
12 }
Une fois que nous avons un personnage, que se passera-t-il ? Il faut en eet cacher ce
formulaire et laisser place dautres informations. Je vous propose dacher, dans un
premier temps, les informations du personnage slectionn (son nom et ses dgts),
puis, dans un second temps, la liste des autres personnages avec leurs informations. Il
devra tre possible de cliquer sur le nom du personnage pour le frapper.
1 <?php
2 // On enregistre notre autoload.
3 function chargerClasse($classname)
4 {
5 require $classname.'.class.php';
6 }
7
8 spl_autoload_register('chargerClasse ');
9
10 $db = new PDO('mysql:host=localhost;dbname=combats ', 'root', ''
);
11 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_WARNING); //
On met une alerte chaque fois qu'une requte a chou.
12
13 $manager = new PersonnagesManager($db);
80
TROISIME TAPE : UTILISATION DES CLASSES
14
15 if (isset($_POST['creer']) && isset($_POST['nom'])) // Si on a
voulu crer un personnage.
16 {
17 $perso = new Personnage(array('nom' => $_POST['nom'])); // On
cre un nouveau personnage.
18
19 if (!$perso ->nomValide ())
20 {
21 $message = 'Le nom choisi est invalide.';
22 unset($perso);
23 }
24 elseif ($manager ->exists($perso ->nom()))
25 {
26 $message = 'Le nom du personnage est dj pris.';
27 unset($perso);
28 }
29 else
30 {
31 $manager ->add($perso);
32 }
33 }
34
35 elseif (isset($_POST['utiliser ']) && isset($_POST['nom'])) //
Si on a voulu utiliser un personnage.
36 {
37 if ($manager ->exists($_POST['nom'])) // Si celui -ci existe.
38 {
39 $perso = $manager ->get($_POST['nom']);
40 }
41 else
42 {
43 $message = 'Ce personnage n\'existe pas !'; // S'il n'
existe pas , on affichera ce message.
44 }
45 }
46 ?>
47 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
48 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
49 <head >
50 <title >TP : Mini jeu de combat </title >
51
52 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
53 </head >
54 <body >
55 <p>Nombre de personnages crs : <?php echo $manager ->count
(); ?></p>
56 <?php
81
CHAPITRE 5. TP : MINI-JEU DE COMBAT
57 if (isset($message)) // On a un message afficher ?
58 {
59 echo '<p>', $message , '</p>'; // Si oui , on l'affiche.
60 }
61
62 if (isset($perso)) // Si on utilise un personnage (nouveau ou
pas).
63 {
64 ?>
65 <fieldset >
66 <legend >Mes informations </legend >
67 <p>
68 Nom : <?php echo htmlspecialchars($perso ->nom()); ?><br
/>
69 Dgts : <?php echo $perso ->degats (); ?>
70 </p>
71 </fieldset >
72
73 <fieldset >
74 <legend >Qui frapper ?</legend >
75 <p>
76 <?php
77 $persos = $manager ->getList($perso ->nom());
78
79 if (empty($persos))
80 {
81 echo 'Personne frapper !';
82 }
83
84 else
85 {
86 foreach ($persos as $unPerso)
87 echo '<a href ="? frapper=', $unPerso ->id(), '">',
htmlspecialchars($unPerso ->nom()), '</a> (dgts : ',
$unPerso ->degats (), ')<br />';
88 }
89 ?>
90 </p>
91 </fieldset >
92 <?php
93 }
94 else
95 {
96 ?>
97 <form action="" method="post">
98 <p>
99 Nom : <input type="text" name="nom" maxlength="50" />
100 <input type="submit" value="Crer ce personnage" name="
creer" />
101 <input type="submit" value="Utiliser ce personnage"
82
TROISIME TAPE : UTILISATION DES CLASSES
name="utiliser" />
102 </p>
103 </form >
104 <?php
105 }
106 ?>
107 </body >
108 </html >
Maintenant, quelque chose devrait vous titiller. En eet, si on recharge la page, on
atterrira nouveau sur le formulaire. Nous allons donc devoir utiliser le systme de
sessions. La premire chose faire sera alors de dmarrer la session au dbut du script,
juste aprs la dclaration de lautoload. La session dmarre, nous pouvons aisment
sauvegarder notre personnage. Pour cela, il nous faudra enregistrer le personnage en
session (admettons dans $_SESSION[perso]) tout la n du code. Cela nous per-
mettra, au dbut du script, de rcuprer le personnage sauvegard et de continuer le
jeu.
Pour des raisons pratiques, il est prfrable dajouter un lien de dconnexion
pour quon puisse utiliser un autre personnage si on le souhaite. Ce lien aura
pour eet de conduire un session_destroy().
1 <?php
2 // On enregistre notre autoload.
3 function chargerClasse($classname)
4 {
5 require $classname.'.class.php';
6 }
7
8 spl_autoload_register('chargerClasse ');
9
10 session_start (); // On appelle session_start () APRS avoir
enregistr l'autoload.
11
12 if (isset($_GET['deconnexion ']))
13 {
14 session_destroy ();
15 header('Location: .');
16 exit();
17 }
18
19 if (isset($_SESSION['perso'])) // Si la session perso existe ,
on restaure l'objet.
20 {
21 $perso = $_SESSION['perso'];
22 }
23
24 $db = new PDO('mysql:host=localhost;dbname=combats ', 'root', ''
);
83
CHAPITRE 5. TP : MINI-JEU DE COMBAT
25 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_WARNING); //
On met une alerte chaque fois qu'une requte a chou.
26
27 $manager = new PersonnagesManager($db);
28
29 if (isset($_POST['creer']) && isset($_POST['nom'])) // Si on a
voulu crer un personnage.
30 {
31 $perso = new Personnage(array('nom' => $_POST['nom'])); // On
cre un nouveau personnage.
32
33 if (!$perso ->nomValide ())
34 {
35 $message = 'Le nom choisi est invalide.';
36 unset($perso);
37 }
38 elseif ($manager ->exists($perso ->nom()))
39 {
40 $message = 'Le nom du personnage est dj pris.';
41 unset($perso);
42 }
43 else
44 {
45 $manager ->add($perso);
46 }
47 }
48
49 elseif (isset($_POST['utiliser ']) && isset($_POST['nom'])) //
Si on a voulu utiliser un personnage.
50 {
51 if ($manager ->exists($_POST['nom'])) // Si celui -ci existe.
52 {
53 $perso = $manager ->get($_POST['nom']);
54 }
55 else
56 {
57 $message = 'Ce personnage n\'existe pas !'; // S'il n'
existe pas , on affichera ce message.
58 }
59 }
60 ?>
61 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
62 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
63 <head >
64 <title >TP : Mini jeu de combat </title >
65
66 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
67 </head >
84
TROISIME TAPE : UTILISATION DES CLASSES
68 <body >
69 <p>Nombre de personnages crs : <?php echo $manager ->count
(); ?></p>
70 <?php
71 if (isset($message)) // On a un message afficher ?
72 {
73 echo '<p>', $message , '</p>'; // Si oui , on l'affiche.
74 }
75
76 if (isset($perso)) // Si on utilise un personnage (nouveau ou
pas).
77 {
78 ?>
79 <p><a href="?deconnexion=1">Dconnexion </a></p>
80
81 <fieldset >
82 <legend >Mes informations </legend >
83 <p>
84 Nom : <?php echo htmlspecialchars($perso ->nom()); ?><br
/>
85 Dgts : <?php echo $perso ->degats (); ?>
86 </p>
87 </fieldset >
88
89 <fieldset >
90 <legend >Qui frapper ?</legend >
91 <p>
92 <?php
93 $persos = $manager ->getList($perso ->nom());
94
95 if (empty($persos))
96 {
97 echo 'Personne frapper !';
98 }
99
100 else
101 {
102 foreach ($persos as $unPerso)
103 echo '<a href ="? frapper=', $unPerso ->id(), '">',
htmlspecialchars($unPerso ->nom()), '</a> (dgts : ',
$unPerso ->degats (), ')<br />';
104 }
105 ?>
106 </p>
107 </fieldset >
108 <?php
109 }
110 else
111 {
112 ?>
85
CHAPITRE 5. TP : MINI-JEU DE COMBAT
113 <form action="" method="post">
114 <p>
115 Nom : <input type="text" name="nom" maxlength="50" />
116 <input type="submit" value="Crer ce personnage" name="
creer" />
117 <input type="submit" value="Utiliser ce personnage"
name="utiliser" />
118 </p>
119 </form >
120 <?php
121 }
122 ?>
123 </body >
124 </html >
125 <?php
126 if (isset($perso)) // Si on a cr un personnage , on le stocke
dans une variable session afin d'conomiser une requte SQL.
127 {
128 $_SESSION['perso'] = $perso;
129 }
Il reste maintenant une dernire partie dvelopper : celle qui soccupera de frapper
un personnage. Puisque nous avons dj crit tout le code faisant linteraction entre
lattaquant et la cible, vous verrez que nous naurons presque rien crire.
Comment doit se passer la phase de traitement ? Avant toute chose, il faut bien vrier
que le joueur est connect et que la variable $perso existe, sinon nous niront pas
bien loin. Seconde vrication : il faut demander notre manager si le personnage que
lon veut frapper existe bien. Si ces deux conditions sont vries, alors on peut lancer
lattaque.
Pour lancer lattaque, il va falloir rcuprer le personnage frapper grce notre
manager. Ensuite, il sura dinvoquer la mthode permettant de frapper le personnage.
Cependant, nous nallons pas nous arrter l. Noubliez pas que cette mthode peut
retourner 3 valeurs direntes :
Personnage::CEST_MOI. Le personnage a voulu se frapper lui-mme.
Personnage::PERSONNAGE_FRAPPE. Le personnage a bien t frapp.
Personnage::PERSONNAGE_TUE. Le personnage a t tu.
Il va donc falloir acher un message en fonction de cette valeur retourne. Aussi, seuls
2 de ces cas ncessitent une mise jour de la BDD : si le personnage a t frapp ou
sil a t tu. En eet, si on a voulu se frapper soi-mme, aucun des deux personnages
impliqus na t modi.
1 <?php
2 // On enregistre notre autoload.
3 function chargerClasse($classname)
4 {
5 require $classname.'.class.php';
6 }
86
TROISIME TAPE : UTILISATION DES CLASSES
7
8 spl_autoload_register('chargerClasse ');
9
10 session_start (); // On appelle session_start () APRS avoir
enregistr l'autoload.
11
12 if (isset($_GET['deconnexion ']))
13 {
14 session_destroy ();
15 header('Location: .');
16 exit();
17 }
18
19 $db = new PDO('mysql:host=localhost;dbname=combats ', 'root', ''
);
20 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_WARNING); //
On met une alerte chaque fois qu'une requte a chou.
21
22 $manager = new PersonnagesManager($db);
23
24 if (isset($_SESSION['perso'])) // Si la session perso existe ,
on restaure l'objet.
25 {
26 $perso = $_SESSION['perso'];
27 }
28
29 if (isset($_POST['creer']) && isset($_POST['nom'])) // Si on a
voulu crer un personnage.
30 {
31 $perso = new Personnage(array('nom' => $_POST['nom'])); // On
cre un nouveau personnage.
32
33 if (!$perso ->nomValide ())
34 {
35 $message = 'Le nom choisi est invalide.';
36 unset($perso);
37 }
38 elseif ($manager ->exists($perso ->nom()))
39 {
40 $message = 'Le nom du personnage est dj pris.';
41 unset($perso);
42 }
43 else
44 {
45 $manager ->add($perso);
46 }
47 }
48
49 elseif (isset($_POST['utiliser ']) && isset($_POST['nom'])) //
Si on a voulu utiliser un personnage.
87
CHAPITRE 5. TP : MINI-JEU DE COMBAT
50 {
51 if ($manager ->exists($_POST['nom'])) // Si celui -ci existe.
52 {
53 $perso = $manager ->get($_POST['nom']);
54 }
55 else
56 {
57 $message = 'Ce personnage n\'existe pas !'; // S'il n'
existe pas , on affichera ce message.
58 }
59 }
60
61 elseif (isset($_GET['frapper '])) // Si on a cliqu sur un
personnage pour le frapper.
62 {
63 if (!isset($perso))
64 {
65 $message = 'Merci de crer un personnage ou de vous
identifier.';
66 }
67
68 else
69 {
70 if (!$manager ->exists ((int) $_GET['frapper ']))
71 {
72 $message = 'Le personnage que vous voulez frapper n\'
existe pas !';
73 }
74
75 else
76 {
77 $persoAFrapper = $manager ->get((int) $_GET['frapper ']);
78
79 $retour = $perso ->frapper($persoAFrapper); // On stocke
dans $retour les ventuelles erreurs ou messages que
renvoie la mthode frapper.
80
81 switch ($retour)
82 {
83 case Personnage :: CEST_MOI :
84 $message = 'Mais ... pourquoi voulez -vous vous frapper
???';
85 break;
86
87 case Personnage :: PERSONNAGE_FRAPPE :
88 $message = 'Le personnage a bien t frapp !';
89
90 $manager ->update($perso);
91 $manager ->update($persoAFrapper);
92
88
TROISIME TAPE : UTILISATION DES CLASSES
93 break;
94
95 case Personnage :: PERSONNAGE_TUE :
96 $message = 'Vous avez tu ce personnage !';
97
98 $manager ->update($perso);
99 $manager ->delete($persoAFrapper);
100
101 break;
102 }
103 }
104 }
105 }
106 ?>
107 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
108 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
109 <head >
110 <title >TP : Mini jeu de combat </title >
111
112 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
113 </head >
114 <body >
115 <p>Nombre de personnages crs : <?php echo $manager ->count
(); ?></p>
116 <?php
117 if (isset($message)) // On a un message afficher ?
118 {
119 echo '<p>', $message , '</p>'; // Si oui , on l'affiche.
120 }
121
122 if (isset($perso)) // Si on utilise un personnage (nouveau ou
pas).
123 {
124 ?>
125 <p><a href="?deconnexion=1">Dconnexion </a></p>
126
127 <fieldset >
128 <legend >Mes informations </legend >
129 <p>
130 Nom : <?php echo htmlspecialchars($perso ->nom()); ?><br
/>
131 Dgts : <?php echo $perso ->degats (); ?>
132 </p>
133 </fieldset >
134
135 <fieldset >
136 <legend >Qui frapper ?</legend >
137 <p>
89
CHAPITRE 5. TP : MINI-JEU DE COMBAT
138 <?php
139 $persos = $manager ->getList($perso ->nom());
140
141 if (empty($persos))
142 {
143 echo 'Personne frapper !';
144 }
145
146 else
147 {
148 foreach ($persos as $unPerso)
149 {
150 echo '<a href ="? frapper=', $unPerso ->id(), '">',
htmlspecialchars($unPerso ->nom()), '</a> (dgts : ',
$unPerso ->degats (), ')<br />';
151 }
152 }
153 ?>
154 </p>
155 </fieldset >
156 <?php
157 }
158 else
159 {
160 ?>
161 <form action="" method="post">
162 <p>
163 Nom : <input type="text" name="nom" maxlength="50" />
164 <input type="submit" value="Crer ce personnage" name="
creer" />
165 <input type="submit" value="Utiliser ce personnage"
name="utiliser" />
166 </p>
167 </form >
168 <?php
169 }
170 ?>
171 </body >
172 </html >
173 <?php
174 if (isset($perso)) // Si on a cr un personnage , on le stocke
dans une variable session afin d'conomiser une requte SQL.
175 {
176 $_SESSION['perso'] = $perso;
177 }
Et voil, vous avez un jeu oprationnel !
90
AMLIORATIONS POSSIBLES
Amliorations possibles
Ce code est trs basique, beaucoup damliorations sont possibles. En voici quelques
unes :
Un systme de niveau. Vous pourriez trs bien assigner chaque personnage un
niveau de 1 100. Le personnage bncierait aussi dune exprience allant de 0
100. Lorsque lexprience atteint 100, le personnage passe au niveau suivant.
Indice : le niveau et lexprience deviendraient des caractristiques du person-
nage, donc. . . Pas besoin de vous le dire, je suis sr que vous savez ce que a signie !
Un systme de force. La force du personnage pourrait augmenter en fonction de son
niveau, et les dgts inigs la victime seront donc plus importants.
Indice : de mme, la force du personnage serait aussi une caractristique du per-
sonnage.
Un systme de limitation. En eet, un personnage peut en frapper autant quil veut
dans un laps de temps indni. Pourquoi ne pas le limiter 3 coups par jour ?
Indice : il faudrait que vous stockiez le nombre de coups ports par le personnage,
ainsi que la date du dernier coup port. Cela ferait donc deux nouveaux champs
en BDD, et deux nouvelles caractristiques pour le personnage !
Un systme de retrait de dgts. Chaque jour, si lutilisateur se connecte, il pour-
rait voir ses dgts se soustraire de 10 par exemple.
Indice : il faudrait stocker la date de dernire connexion. chaque connexion, vous
regarderiez cette date. Si elle est infrieure 24h, alors vous ne feriez rien. Sinon,
vous retireriez 10 de dgts au personnage puis mettriez jour cette date de dernire
connexion.
Et la liste peut tre longue ! Je vous encourage vivement essayer dimplmenter ces
fonctionnalits et laisser libre court votre imagination, vous progresserez bien plus.
91
CHAPITRE 5. TP : MINI-JEU DE COMBAT
92
Chapitre 6
Lhritage
Dicult :
L
hritage en POO (que ce soit en C++, Java ou autre langage utilisant la POO) est
une technique trs puissante et extrmement pratique. Ce chapitre sur lhritage est
le chapitre connaitre par cur (ou du moins, le mieux possible). Pour tre bien sr
que vous ayez compris le principe, un TP vous attend au prochain chapitre.
Allez, jarrte de vous mettre la pression, allons-y !
93
CHAPITRE 6. LHRITAGE
Notion dhritage
Dnition
Quand on parle dhritage, cest quon dit quune classe B hrite dune classe A. La
classe A est donc considre comme la classe mre et la classe B est considre comme
la classe lle.
Concrtement, lhritage, cest quoi ?
Lorsquon dit que la classe B hrite de la classe A, cest que la classe B hrite de tous
les attributs et mthodes de la classe A. Si lon dclare des mthodes dans la classe
A, et quon cre une instance de la classe B, alors on pourra appeler nimporte quelle
mthode dclare dans la classe A du moment quelle est publique.
Schmatiquement, une classe B hritant dune classe A peut tre reprsente comme
ceci (gure 6.1).
Figure 6.1 Exemple dune classe Magicien hritant dune classe Personnage
Vous voyez que la classe Magicien a hrit de toutes les mthodes et daucun attribut
de la classe Personnage. Souvenez-vous : toutes les mthodes sont publiques et tous
94
NOTION DHRITAGE
les attributs sont privs. En fait, les attributs privs ont bien t hrits aussi, mais
notre classe Magicien ne pourra sen servir, cest la raison pour laquelle je ne les ai
pas reprsents. Il ny a que les mthodes de la classe parente qui auront accs ces
attributs. Cest comme pour le principe dencapsulation : ici, les lments privs sont
masqus. On dit que la classe Personnage est la classe mre et que la classe Magicien
est la classe lle.
Je nai pas mis toutes les mthodes du dernier TP dans ce schma pour ne
pas le surcharger, mais en ralit, toutes les mthodes ont bien t hrites.
Quand est-ce que je sais si telle classe doit hriter dune autre ?
Soit deux classes A et B. Pour quun hritage soit possible, il faut que vous puissiez dire
que A est un B. Par exemple, un magicien est un personnage, donc hritage. Un chien
est un animal, donc hritage aussi. Bref, vous avez compris le principe.
Procder un hritage
Pour procder un hritage (cest--dire faire en sorte quune classe hrite des attributs
et mthodes dune autre classe), il sut dutiliser le mot-cl extends. Dclarez votre
classe comme dhabitude (class MaClasse) et ajoutez extends NomDeLaClasseAHeriter
comme ceci :
1 <?php
2 class Personnage // Cration d'une classe simple.
3 {
4
5 }
6
7 class Magicien extends Personnage // Notre classe Magicien h
rite des attributs et mthodes de Personnage.
8 {
9
10 }
11 ?>
Comme dans la ralit, une mre peut avoir plusieurs lles, mais une lle ne peut avoir
plusieurs mres. La seule dirence avec la vie relle, cest quune mre ne peut avoir
une innit de lles.
Ainsi, on pourrait crer des classes Magicien, Guerrier, Brute, etc. qui hritent toutes
de Personnage : la classe Personnage sert de modle.
1 <?php
2 class Personnage // Cration d'une classe simple.
95
CHAPITRE 6. LHRITAGE
3 {
4
5 }
6
7 // Toutes les classes suivantes hriteront de Personnage.
8
9 class Magicien extends Personnage
10 {
11
12 }
13
14 class Guerrier extends Personnage
15 {
16
17 }
18
19 class Brute extends Personnage
20 {
21
22 }
23 ?>
Ainsi, ces nouvelles classes auront les mmes attributs et mthodes que Personnage.
Super, tu me cres des classes qui sont exactement les mmes quune autre. . .
Trs utile ! En plus, tout ce qui est priv je ny ai pas accs donc. . .
Nous sommes daccord, si lhritage ctait a, ce serait le concept le plus idiot et le
plus inutile de la POO. :-
Chaque classe peut crer des attributs et mthodes qui lui seront propres, et cest l
toute la puissance de lhritage : toutes les classes que lon a cres plus haut peuvent
avoir des attributs et mthodes en plus des attributs et mthodes hrits. Pour cela,
rien de plus simple. Il vous sut de crer des attributs et mthodes comme nous avons
appris jusqu maintenant. Un exemple ?
1 <?php
2 class Magicien extends Personnage
3 {
4 private $_magie; // Indique la puissance du magicien sur 100 ,
sa capacit produire de la magie.
5
6 public function lancerUnSort($perso)
7 {
8 $perso ->recevoirDegats($this ->_magie); // On va dire que la
magie du magicien reprsente sa force.
9 }
10 }
11 ?>
96
NOTION DHRITAGE
Ainsi, la classe Magicien aura, en plus des attributs et mthodes hrits, un
attribut $magie et une mthode lancerUnSort.
Si vous essayez daccder un attribut priv de la classe parente, aucune
erreur fatale ne sachera, seulement une notice si vous les avez actives
disant que lattribut nexiste pas.
Surcharger les mthodes
On vient de crer une classe Magicien hritant de toutes les mthodes de la classe
Personnage. Que diriez-vous si lon pouvait rcrire certaines mthodes, an de mo-
dier leur comportement ? Pour cela, il vous sut de dclarer nouveau la mthode
et dcrire ce que bon vous semble lintrieur.
Un problme se pose pourtant. Si vous voulez accder un attribut de la classe parente
pour le modier, vous ne pourrez pas car notre classe Magicien na pas accs aux
attributs de sa classe mre Personnage puisquils sont tous privs.
Nous allons maintenant essayer de surcharger la mthode gagnerExperience an de
modier lattribut stockant la magie (admettons $_magie) lorsque, justement, on
gagne de lexprience. Problme : si on la rcrit, nous crasons toutes les instructions
prsentes dans la mthode de la classe parente (Personnage), ce qui aura pour eet
de ne pas faire gagner dexprience notre magicien mais juste de lui augmenter sa
magie. Solution : appeler la mthode gagnerExperience de la classe parente, puis
ensuite ajouter les instructions modiant la magie.
Il sut pour cela dutiliser le mot-cl parent suivi du symbole double deux points (le
revoil celui-l !) suivi lui-mme du nom de la mthode appeler.
1 <?php
2 class Magicien extends Personnage
3 {
4 private $_magie; // Indique la puissance du magicien sur 100 ,
sa capacit produire de la magie.
5
6 public function lancerUnSort($perso)
7 {
8 $perso ->recevoirDegats($this ->_magie); // On va dire que la
magie du magicien reprsente sa force.
9 }
10
11 public function gagnerExperience ()
12 {
13 // On appelle la mthode gagnerExperience () de la classe
parente
14 parent :: gagnerExperience ();
15
16 if ($this ->_magie < 100)
17 {
97
CHAPITRE 6. LHRITAGE
18 $this ->_magie += 10;
19 }
20 }
21 }
22 ?>
Notez que si la mthode parente retourne une valeur, vous pouvez la rcuprer comme
si vous appeliez une mthode normalement. Exemple :
1 <?php
2 class A
3 {
4 public function test()
5 {
6 return 'test';
7 }
8 }
9
10 class B extends A
11 {
12 public function test()
13 {
14 $retour = parent ::test();
15
16 echo $retour; // Affiche 'test'
17 }
18 }
Comme vous pouvez le constater, jai fait appel aux getters et setters correspondant
lattribut $_magie. Pourquoi ? Car les classes enfant nont pas accs aux lments
privs, il fallait donc que la classe parente le fasse pour moi ! Il ny a que les mthodes
de la classe parente non rcrites qui ont accs aux lments privs. partir du moment
o lon rcrit une mthode de la classe parente, la mthode appartient la classe lle
et na donc plus accs aux lments privs.
Si vous surchargez une mthode, sa visibilit doit tre la mme que dans
la classe parente ! Si tel nest pas le cas, une erreur fatale sera leve. Par
exemple, vous ne pouvez surcharger une mthode publique en disant quelle
est prive.
Hritez linni !
Toute classe en POO peut tre hrite si elle ne spcie pas le contraire, vraiment
toutes. Vous pouvez ainsi reproduire un arbre rel avec autant de classes hritant les
unes des autres que vous le souhaitez.
Pour reprendre lexemple du magicien dans le cours sur la POO en C++ de M@teo21,
on peut crer deux autres classes MagicienBlanc et MagicienNoir qui hritent toutes
98
NOTION DHRITAGE
les deux de Magicien. Exemple :
1 <?php
2 class Personnage // Classe Personnage de base.
3 {
4
5 }
6
7 class Magicien extends Personnage // Classe Magicien hritant
de Personnage.
8 {
9
10 }
11
12 class MagicienBlanc extends Magicien // Classe MagicienBlanc h
ritant de Magicien.
13 {
14
15 }
16
17 class MagicienNoir extends Magicien // Classe MagicienNoir h
ritant de Magicien.
18 {
19
20 }
21 ?>
Et un petit schma qui reproduit ce code (gure 6.2).
Figure 6.2 Exemple dun hritage multiple
99
CHAPITRE 6. LHRITAGE
Ainsi, les classes MagicienBlanc et MagicienNoir hriteront de tous les attributs et
de toutes les mthodes des classes Magicien et Personnage.
Un nouveau type de visibilit : protected
Je vais prsent vous prsenter le dernier type de visibilit existant en POO : il sagit
de protected. Ce type de visibilit est, au niveau restrictif, placer entre public et
private.
Je vous rappelle brivement les rles de ces deux portes de visibilit :
Public : ce type de visibilit nous laisse beaucoup de libert. On peut accder lat-
tribut ou la mthode de nimporte o. Toute classe lle aura accs aux lments
publics.
Private : ce type est celui qui nous laisse le moins de libert. On ne peut accder
lattribut ou la mthode que depuis lintrieur de la classe qui la cr.
Toute classe lle naura pas accs aux lments privs.
Le type de visibilit protected est en fait une petite modication du type private : il
a exactement les mmes eets que private, lexception que toute classe lle aura
accs aux lments protgs.
Exemple :
1 <?php
2 class ClasseMere
3 {
4 protected $attributProtege;
5 private $_attributPrive;
6
7 public function __construct ()
8 {
9 $this ->attributProtege = 'Hello world !';
10 $this ->_attributPrive = 'Bonjour tout le monde !';
11 }
12 }
13
14 class ClasseFille extends ClasseMere
15 {
16 public function afficherAttributs ()
17 {
18 echo $this ->attributProtege; // L'attribut est protg, on
a donc accs celui -ci.
19 echo $this ->_atributPrive; // L'attribut est priv, on n'a
pas accs celui -ci , donc rien ne s'affichera (mis part
une notice si vous les avez actives).
20 }
21 }
22
23 $obj = new ClasseFille;
100
IMPOSER DES CONTRAINTES
24
25 echo $obj ->attributProtege; // Erreur fatale.
26 echo $obj ->_attributPrive; // Rien ne s'affiche (ou une notice
si vous les avez actives).
27
28 $obj ->afficherAttributs (); // Affiche Hello world ! suivi
de rien du tout ou d'une notice si vous les avez actives.
29 ?>
Comme vous pouvez le constater, il ny a pas dunderscores prcdant les noms dl-
ments protgs. Cest encore une fois la notation PEAR qui nous dit que les noms
dlments protgs ne sont pas protgs de ce caractre.
Et pour le principe dencapsulation, jutilise quoi ? private ou protected ?
La porte private est, selon moi, bien trop restrictive et contraignante. Elle empche
toute classe enfant daccder aux attributs et mthodes prives alors que cette dernire
en a souvent besoin. De manire gnrale, je vous conseille donc de toujours mettre
protected au lieu de private, moins que vous teniez absolument ce que la classe
enfant ne puisse y avoir accs. Cependant, je trouve cela inutile dans le sens o la
classe enfant a t cre par un dveloppeur, donc quelquun qui sait ce quil fait et
qui par consquent doit pouvoir modier souhait tous les attributs, contrairement
lutilisateur de la classe.
Imposer des contraintes
Il est possible de mettre en place des contraintes. On parlera alors dabstraction ou
de nalisation suivant la contrainte instaure.
Abstraction
Classes abstraites
On a vu jusqu maintenant que lon pouvait instancier nimporte quelle classe an
de pouvoir exploiter ses mthodes. On va maintenant dcouvrir comment empcher
quiconque dinstancier telle classe.
Mais quoi a sert de crer une classe si on ne peut pas sen servir ?
On ne pourra pas se servir directement de la classe. La seule faon dexploiter ses
mthodes est de crer une classe hritant de la classe abstraite.
101
CHAPITRE 6. LHRITAGE
Vous vous demandez sans doute quoi cela peut bien servir. Lexemple que je vais
prendre est celui du personnage et de ses classes lles. Dans ce que nous venons
de faire, nous ne crerons jamais dobjet Personnage, mais uniquement des objets
Magicien, Guerrier, Brute, etc. En eet, quoi cela nous servirait dinstancier la
classe Personnage si notre but est de crer un tel type de personnage ?
Nous allons donc considrer la classe Personnage comme tant une classe modle dont
toute classe lle possdera les mthodes et attributs.
Pour dclarer une classe abstraite, il sut de faire prcder le mot-cl class du mot-cl
abstract comme ceci :
1 <?php
2 abstract class Personnage // Notre classe Personnage est
abstraite.
3 {
4
5 }
6
7 class Magicien extends Personnage // Cration d'une classe
Magicien hritant de la classe Personnage.
8 {
9
10 }
11
12 $magicien = new Magicien; // Tout va bien , la classe Magicien n
'est pas abstraite.
13 $perso = new Personnage; // Erreur fatale car on instancie une
classe abstraite.
14 ?>
Simple et court retenir, il sut juste de se souvenir o lon doit le placer.
Ainsi, si vous essayez de crer une instance de la classe Personnage, une erreur fatale
sera leve. Ceci nous garantit que lon ne crera jamais dobjet Personnage (suite
une tourderie par exemple).
Mthodes abstraites
Si vous dcidez de rendre une mthode abstraite en plaant le mot-cl abstract juste
avant la visibilit de la mthode, vous forcerez toutes les classes lles crire cette
mthode. Si tel nest pas le cas, une erreur fatale sera leve. Puisque lon force la classe
lle crire la mthode, on ne doit spcier aucune instruction dans la mthode, on d-
clarera juste son prototype (visibilit + function + nomDeLaMethode + parenthses
avec ou sans paramtres + point-virgule).
1 <?php
2 abstract class Personnage
3 {
4 // On va forcer toute classe fille crire cette mthode car
chaque personnage frappe diffremment.
102
IMPOSER DES CONTRAINTES
5 abstract public function frapper(Personnage $perso);
6
7 // Cette mthode n'aura pas besoin d'tre rcrite.
8 public function recevoirDegats ()
9 {
10 // Instructions.
11 }
12 }
13
14 class Magicien extends Personnage
15 {
16 // On crit la mthode frapper du mme type de visibilit
que la mthode abstraite frapper de la classe mre.
17 public function frapper(Personnage $perso)
18 {
19 // Instructions.
20 }
21 }
22 ?>
Pour dnir une mthode comme tant abstraite, il faut que la classe elle-
mme soit abstraite !
Finalisation
Classes nales
Le concept des classes et mthodes nales est exactement linverse du concept dabs-
traction. Si une classe est nale, vous ne pourrez pas crer de classe lle hritant de
cette classe.
Pour ma part, je ne rends jamais mes classes nales (au mme titre que, quelques
exceptions prs, je mets toujours mes attributs en protected) pour me laisser la libert
dhriter de nimporte quelle classe.
Pour dclarer une classe nale, vous devez placer le mot-cl final juste avant le mot-cl
class, comme abstract.
1 <?php
2 // Classe abstraite servant de modle.
3
4 abstract class Personnage
5 {
6
7 }
8
9 // Classe finale , on ne pourra crer de classe hritant de
Guerrier.
103
CHAPITRE 6. LHRITAGE
10
11 final class Guerrier extends Personnage
12 {
13
14 }
15
16 // Erreur fatale , car notre classe hrite d'une classe finale.
17
18 class GentilGuerrier extends Guerrier
19 {
20
21 }
22 ?>
Mthodes nales
Si vous dclarez une mthode nale, toute classe lle de la classe comportant cette
mthode nale hritera de cette mthode mais ne pourra la surcharger. Si vous
dclarez votre mthode recevoirDegats en tant que mthode nale, vous ne pourrez
la surcharger.
1 <?php
2 abstract class Personnage
3 {
4 // Mthode normale.
5
6 public function frapper(Personnage $perso)
7 {
8 // Instructions.
9 }
10
11 // Mthode finale.
12
13 final public function recevoirDegats ()
14 {
15 // Instructions.
16 }
17 }
18
19 class Guerrier extends Personnage
20 {
21 // Aucun problme.
22
23 public function frapper(Personnage $perso)
24 {
25 // Instructions.
26 }
27
104
RSOLUTION STATIQUE LA VOLE
28 // Erreur fatale car cette mthode est finale dans la classe
parente.
29
30 public function recevoirDegats ()
31 {
32 // Instructions.
33 }
34 }
35 ?>
Rsolution statique la vole
Fonctionnalit disponible depuis PHP 5.3 !
Cette sous-partie va vous montrer une possibilit intressante de la POO en PHP :
la rsolution statique la vole. Cest une notion un peu complexe comprendre au
premier abord donc nhsitez pas relire cette partie autant de fois que ncessaire.
Eectuons dabord un petit ash-back sur self::. Vous vous souvenez quoi il sert ?
appeler un attribut, une mthode statique ou une constante de la classe dans
laquelle est contenu self::. Ainsi, si vous testez ce code :
1 <?php
2 class Mere
3 {
4 public static function lancerLeTest ()
5 {
6 self:: quiEstCe ();
7 }
8
9 public function quiEstCe ()
10 {
11 echo 'Je suis la classe <strong >Mere </strong > !';
12 }
13 }
14
15 class Enfant extends Mere
16 {
17 public function quiEstCe ()
18 {
19 echo 'Je suis la classe <strong >Enfant </strong > !';
20 }
21 }
22
23 Enfant :: lancerLeTest ();
105
CHAPITRE 6. LHRITAGE
24 ?>
lcran sachera ceci (voir gure 6.3).
Figure 6.3 Rsultat ach par le script
Mais quest-ce qui sest pass ? ? ?
Appel de la mthode lancerLeTest de la classe Enfant ;
la mthode na pas t rcrite, on va donc chercher la mthode lancerLeTest
de la classe mre ;
appel de la mthode quiEstCe de la classe Mere.
Pourquoi cest la mthode quiEstCe de la classe parente qui a t appele ?
Pourquoi pas celle de la classe lle puisquelle a t rcrite ?
Tout simplement parce que self:: fait appel la mthode statique de la classe dans
laquelle est contenu self::, donc de la classe parente.
Et la rsolution statique la vole dans tout a ?
Tout tourne autour de lutilisation de static::. static:: a exactement le mme eet
que self::, lexception prs que static:: appelle llment de la classe
qui est appele pendant lexcution. Cest--dire que si jappelle la mthode
lancerLeTest depuis la classe Enfant et que dans cette mthode jutilise static:: au
lieu de self::, cest la mthode quiEstCe de la classe Enfant qui sera appele et non
de la classe Mere !
1 <?php
2 class Mere
106
RSOLUTION STATIQUE LA VOLE
3 {
4 public static function lancerLeTest ()
5 {
6 static :: quiEstCe ();
7 }
8
9 public function quiEstCe ()
10 {
11 echo 'Je suis la classe <strong >Mere </strong > !';
12 }
13 }
14
15 class Enfant extends Mere
16 {
17 public function quiEstCe ()
18 {
19 echo 'Je suis la classe <strong >Enfant </strong > !';
20 }
21 }
22
23 Enfant :: lancerLeTest ();
24 ?>
Ce qui donnera ceci (voir la gure 6.4).
Figure 6.4 Rsultat ach par le script
Notez que tous les exemples ci-dessus utilisent des mthodes qui sont appeles
dans un contexte statique. Jai fait ce choix car pour ce genre de tests, il tait
inutile dinstancier la classe, mais sachez bien que la rsolution statique la
vole a exactement le mme eet quand on cre un objet puis quon appelle
une mthode de celui-ci. Il nest donc pas du tout obligatoire de rendre les
mthodes statiques pour pouvoir y placer static::. Ainsi, si vous testez ce
code, lcran sachera la mme chose que prcdemment.
1 <?php
107
CHAPITRE 6. LHRITAGE
2 class Mere
3 {
4 public function lancerLeTest ()
5 {
6 static :: quiEstCe ();
7 }
8
9 public function quiEstCe ()
10 {
11 echo 'Je suis la classe <strong >Mere </strong > !';
12 }
13 }
14
15 class Enfant extends Mere
16 {
17 public function quiEstCe ()
18 {
19 echo 'Je suis la classe <strong >Enfant </strong > !';
20 }
21 }
22
23 $e = new Enfant;
24 $e->lancerLeTest ();
25 ?>
Cas complexes
Au premier abord, vous navez peut-tre pas tout compris. Si tel est le cas, ne lisez
pas la suite cela risquerait de vous embrouiller davantage. Prenez bien le temps de
comprendre ce qui est crit plus haut puis vous pourrez continuer.
Comme le spcie le titre, il y a quelques cas complexes (des piges en quelque sorte).
Imaginons trois classes A, B et C qui hritent chacune dune autre (A est la grand-mre,
B la mre et C la lle). En PHP, on dirait plutt :
1 <?php
2 class A
3 {
4
5 }
6
7 class B extends A
8 {
9
10 }
11
12 class C extends B
13 {
14
108
RSOLUTION STATIQUE LA VOLE
15 }
16 ?>
Nous allons implmenter dans chacune des classes une mthode qui aura pour rle
dacher le nom de la classe pour pouvoir eectuer quelques tests.
1 <?php
2 class A
3 {
4 public function quiEstCe ()
5 {
6 echo 'A';
7 }
8 }
9
10 class B extends A
11 {
12 public function quiEstCe ()
13 {
14 echo 'B';
15 }
16 }
17
18 class C extends B
19 {
20 public function quiEstCe ()
21 {
22 echo 'C';
23 }
24 }
25 ?>
Crons une mthode de test dans la classe B. Pourquoi dans cette classe ? Parce quelle
hrite la fois de A et est hrite par C, son cas est donc intressant tudier.
Appelons maintenant cette mthode depuis la classe C dans un contexte statique (nul
besoin de crer dobjet, mais a marche tout aussi bien).
1 <?php
2 class A
3 {
4 public function quiEstCe ()
5 {
6 echo 'A';
7 }
8 }
9
10 class B extends A
11 {
12 public static function test()
13 {
14
109
CHAPITRE 6. LHRITAGE
15 }
16
17 public function quiEstCe ()
18 {
19 echo 'B';
20 }
21 }
22
23 class C extends B
24 {
25 public function quiEstCe ()
26 {
27 echo 'C';
28 }
29 }
30
31 C::test();
32 ?>
Plaons un peu de code dans cette mthode, sinon cest pas drle !
Nous allons essayer dappeler la mthode parente quiEstCe. L, il ny a pas de pige,
pas de rsolution statique la vole, donc lcran sachera A :
1 <?php
2 class A
3 {
4 public function quiEstCe ()
5 {
6 echo 'A';
7 }
8 }
9
10 class B extends A
11 {
12 public static function test()
13 {
14 parent :: quiEstCe ();
15 }
16
17 public function quiEstCe ()
18 {
19 echo 'B';
20 }
21 }
22
23 class C extends B
24 {
25 public function quiEstCe ()
26 {
27 echo 'C';
110
RSOLUTION STATIQUE LA VOLE
28 }
29 }
30
31 C::test();
32 ?>
Maintenant, crons une mthode dans la classe A qui sera charge dappeler la mthode
quiEstCe avec static::. L, si vous savez ce qui va sacher, vous avez tout compris !
1 <?php
2 class A
3 {
4 public function appelerQuiEstCe ()
5 {
6 static :: quiEstCe ();
7 }
8
9 public function quiEstCe ()
10 {
11 echo 'A';
12 }
13 }
14
15 class B extends A
16 {
17 public static function test()
18 {
19 parent :: appelerQuiEstCe ();
20 }
21
22 public function quiEstCe ()
23 {
24 echo 'B';
25 }
26 }
27
28 class C extends B
29 {
30 public function quiEstCe ()
31 {
32 echo 'C';
33 }
34 }
35
36 C::test();
37 ?>
Alors ? Vous avez une petite ide ? lcran sachera. . . C! Dcortiquons ce qui sest
pass :
Appel de la mthode test de la classe C ;
111
CHAPITRE 6. LHRITAGE
la mthode na pas t rcrite, on appelle donc la mthode test de la classe B ;
on appelle maintenant la mthode appelerQuiEstCe de la classe A (avec parent::) ;
rsolution statique la vole : on appelle la mthode quiEstCe de la classe qui a
appel la mthode appelerQuiEstCe ;
la mthode quiEstCe de la classe C est donc appele car cest depuis la classe C quon
a appel la mthode test.
Cest trs compliqu mais fondamental comprendre.
Remplaons maintenant parent:: par self:: :
1 <?php
2 class A
3 {
4 public function appelerQuiEstCe ()
5 {
6 static :: quiEstCe ();
7 }
8
9 public function quiEstCe ()
10 {
11 echo 'A';
12 }
13 }
14
15 class B extends A
16 {
17 public static function test()
18 {
19 self:: appelerQuiEstCe ();
20 }
21
22 public function quiEstCe ()
23 {
24 echo 'B';
25 }
26 }
27
28 class C extends B
29 {
30 public function quiEstCe ()
31 {
32 echo 'C';
33 }
34 }
35
36 C::test();
37 ?>
Et l, quest-ce qui sache lcran? Et bien toujours C! Le principe est exactement
le mme que le code plus haut.
112
RSOLUTION STATIQUE LA VOLE
Si vous cassez la chane en appelant une mthode depuis une instance ou sta-
tiquement du genre Classe::methode(), la mthode appele par static::
sera celle de la classe contenant ce code ! Ainsi, le code suivant achera
A .
1 <?php
2 class A
3 {
4 public static function appelerQuiEstCe ()
5 {
6 static :: quiEstCe ();
7 }
8
9 public function quiEstCe ()
10 {
11 echo 'A';
12 }
13 }
14
15 class B extends A
16 {
17 public static function test()
18 {
19 // On appelle appelerQuiEstCe de la classe A
normalement.
20 A:: appelerQuiEstCe ();
21 }
22
23 public function quiEstCe ()
24 {
25 echo 'B';
26 }
27 }
28
29 class C extends B
30 {
31 public function quiEstCe ()
32 {
33 echo 'C';
34 }
35 }
36
37 C::test();
38 ?>
113
CHAPITRE 6. LHRITAGE
Utilisation de static : : dans un contexte non statique
Lutilisation de static:: dans un contexte non statique se fait de la mme faon que
dans un contexte statique. Je vais prendre lexemple de la documentation pour illustrer
mes propos :
1 <?php
2 class TestChild extends TestParent
3 {
4 public function __construct ()
5 {
6 static ::qui();
7 }
8
9 public function test()
10 {
11 $o = new TestParent ();
12 }
13
14 public static function qui()
15 {
16 echo 'TestChild ';
17 }
18 }
19
20 class TestParent
21 {
22 public function __construct ()
23 {
24 static ::qui();
25 }
26
27 public static function qui()
28 {
29 echo 'TestParent ';
30 }
31 }
32
33 $o = new TestChild;
34 $o->test();
35 ?>
lcran sachera TestChild suivi de TestParent . Je vous explique ce qui sest
pass si vous navez pas tout suivi :
Cration dune instance de la classe TestChild ;
appel de la mthode qui de la classe TestChild puisque cest la mthode __construct
de la classe TestChild qui a t appele ;
appel de la mthode test de la classe TestChild ;
cration dune instance de la classe TestParent ;
appel de la mthode qui de la classe TestParent puisque cest la mthode __construct
114
RSOLUTION STATIQUE LA VOLE
de cette classe qui a t appele.
En rsum
Nous pouvons parler dhritage entre une classe A et une classe B si et seulement si
nous pouvons dire B est un A (dans ce cas-l, B hrite de A).
Une classe hritant dune autre a accs tous ses attributs et mthodes publics ou
protgs.
La visibilit protected est quivalente la visibilit private la dirence prs
quun lment protg est accessible par les classes lles, contrairement aux lments
privs.
Il est possible dinterdire linstanciation dune classe grce au mot-cl abstract (utile
pour poser un modle de base commun plusieurs classes) et lhritage dune classe
grce au mot-cl final.
Il est possible dobliger ses classes lles implmenter une mthode grce au mot-cl
abstract et de leur interdire la rcriture dune mthode grce au mot-cl final.
La rsolution statique la vole permet de savoir quelle classe a t initialement
appele pour invoquer la mthode dans laquelle on se trouve.
115
CHAPITRE 6. LHRITAGE
116
Chapitre 7
TP : Des personnages spcialiss
Dicult :
A
vez -vous bien tout retenu du dernier chapitre ? Cest ce que lon va vrier main-
tenant ! Ce TP est en fait une modication du premier (celui qui mettait en scne
notre personnage). Nous allons donc ajouter une possibilit supplmentaire au script :
le choix du personnage. Cette modication vous permettra de revoir ce que nous avons
abord depuis le dernier TP, savoir :
Lhritage ;
la porte protected ;
labstraction.
Je ne mettrai pas en pratique la rsolution statique la vole car elle ne nous est pas
utile ici. Aussi, la nalisation nest pas utilise car cest davantage une contrainte inutile
quautre chose dans cette situation.
117
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
Ce que nous allons faire
Cahier des charges
Je veux que nous ayons le choix de crer un certain type de personnage qui aura certains
avantages. Il ne doit pas tre possible de crer un personnage normal (donc il devra
tre impossible dinstancier la classe Personnage). Comme prcdemment, la classe
Personnage aura la liste des colonnes de la table en guise dattributs.
Je vous donne une liste de personnages dirents qui pourront tre crs. Chaque
personnage a un atout dirent sous forme dentier.
Un magicien. Il aura une nouvelle fonctionnalit : celle de lancer un sort qui aura
pour eet dendormir un personnage pendant $atout * 6 heures (lattribut $atout
reprsente la dose de magie du personnage).
Un guerrier. Lorsquun coup lui est port, il devra avoir la possibilit de parer le
coup en fonction de sa protection (son atout).
Ceci nest quune petite liste de dpart. Libre vous de crer dautres personnages.
Comme vous le voyez, chaque personnage possde un atout. Cet atout devra tre
augment lorsque le personnage est amen sen servir (cest--dire lorsque le magicien
lance un sort ou que le guerrier subit des dgts).
Des nouvelles fonctionnalits pour chaque personnage
tant donn quun magicien peut endormir un personnage, il est ncessaire dimpl-
menter deux nouvelles fonctionnalits :
Celle consistant savoir si un personnage est endormi ou non (ncessaire lorsque
ledit personnage voudra en frapper un autre : sil est endormi, a ne doit pas tre
possible).
Celle consistant obtenir la date du rveil du personnage sous la forme XX heures,
YY minutes et ZZ secondes , qui sachera dans le cadre dinformation du person-
nage sil est endormi.
La base de donnes
La structure de la BDD ne sera pas la mme. En eet, chaque personnage aura un
attribut en plus, et surtout, il faut savoir de quel personnage il sagit (magicien ou
guerrier). Nous allons donc crer une colonne type et une colonne atout (lattribut
quil a en plus). Une colonne timeEndormi devra aussi tre cre pour stocker le
timestamp auquel le personnage se rveillera sil a t ensorcel. Je vous propose donc
cette nouvelle structure (jai juste ajout trois nouveaux champs en n de table) :
1 CREATE TABLE IF NOT EXISTS personnages_v2 (
2 id smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
3 nom varchar(50) COLLATE latin1_general_ci NOT NULL ,
4 degats tinyint(3) unsigned NOT NULL DEFAULT '0',
118
CE QUE NOUS ALLONS FAIRE
5 timeEndormi int(10) unsigned NOT NULL DEFAULT '0',
6 type enum('magicien ','guerrier ') COLLATE latin1_general_ci
NOT NULL ,
7 atout tinyint(3) unsigned NOT NULL DEFAULT '0',
8 PRIMARY KEY (id )
9 ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=
latin1_general_ci;
Le coup de pouce du dmarrage
Les modications que je vous demande peuvent vous donner mal la tte, cest pour-
quoi je me propose de vous mettre sur la voie. Procdons classe par classe.
Le personnage
L o il y a peut-tre une dicult, cest pour dterminer lattribut $type. En eet,
o doit-on assigner cette valeur ? Qui doit le faire ?
Pour des raisons videntes de risques de bugs, ce ne sera pas lutilisateur dassigner
la valeur guerrier ou magicien cet attribut, mais la classe elle-mme. Ce-
pendant, il serait redondant dans chaque classe lle de Personnage de faire un <?php
$this->type = magicien par exemple, alors on va le faire une bonne fois pour toute
dans la classe Personnage.
Comment cela est-il possible ?
Grce une fonction bien pratique (nomme get_class(), il est possible dobtenir le
nom dune classe partir dune instance de cette classe. Par exemple, si on instancie
une classe Guerrier, alors get_class($instance) renverra Guerrier . Ici, nous
nous situons dans le constructeur de la classe Personnage : linstance du personnage
sera donc reprsente par. . . $this ! Eh oui, get_class($this), dans le constructeur
de Personnage, ne renverra pas Personnage , mais Guerrier ou Magicien .
Pourquoi ? Car get_class() ne renvoie pas le nom de la classe dans laquelle elle est
appele, mais le nom de la classe instancie par lobjet pass en argument. Or, linstance
$this nest autre quune instance de Guerrier ou Magicien.
Pour des raisons pratiques, le type du personnage doit tre en minuscules. Un
petit appel strtolower() sera donc indispensable.
119
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
Le magicien
Qui dit nouvelle fonctionnalit dit nouvelle mthode. Votre classe Magicien devra donc
implmenter une nouvelle mthode permettant de lancer un sort. Celle-ci devra vrier
plusieurs points :
La cible ensorceler nest pas le magicien qui lance le sort.
Le magicien possde encore de la magie (latout nest pas 0).
Le guerrier
Ce quon cherche faire ici est modier le comportement du personnage lorsquil subit
des dgts. Nous allons donc modier la mthode qui se charge dajouter des dgts
au personnage. Cette mthode procdera de la sorte :
Elle calculera dabord la valeur de latout.
Elle augmentera les dgts en prenant soin de prendre en compte latout.
Elle indiquera si le personnage a t frapp ou tu.
Tu nous parles dun atout depuis tout lheure, mais comment est-ce quon
le dtermine ?
Latout du magicien et du guerrier se dterminent de la mme faon :
Si les dgts sont compris entre 0 et 25, alors latout sera de 4.
Si les dgts sont compris entre 25 et 50, alors latout sera de 3.
Si les dgts sont compris entre 50 et 75, alors latout sera de 2.
Si les dgts sont compris entre 75 et 90, alors latout sera de 1.
Sinon, il sera de 0.
Comment sont-ils exploits ?
Du ct du guerrier, jutilise une simple formule : la dose de dgts reu ne sera pas de
5, mais de 5 - $atout. Du ct du magicien, l aussi jutilise une simple formule : il
endort sa victime pendant ($this->atout * 6) * 3600 secondes.

Voir le rsultat obtenir


Code web : 466920
Comme au prcdent TP, le rsultat comporte toutes les amliorations proposes en n
de chapitre.
120
CORRECTION
Correction
Nous allons maintenant corriger le TP. Les codes seront dabord prcds dexplications
pour bien mettre au clair ce qui tait demand.
Commenons dabord par notre classe Personnage. Celle-ci devait implmenter deux
nouvelles mthodes (sans compter les getters et setters) : estEndormi() et reveil().
Aussi il ne fallait pas oublier de modier la mthode frapper() an de bien vrier
que le personnage qui frappe ntait pas endormi ! Enn, il fallait assigner la bonne
valeur lattribut $type dans le constructeur.
Il ne fallait pas mettre de setter pour lattribut $type. En eet, le type dun
personnage est constant (un magicien ne se transforme pas en guerrier). Il
est dni dans le constructeur et lutilisateur ne pourra pas changer sa valeur
(imaginez le non-sens quil y aurait si lutilisateur mettait magicien
lattribut $type dun objet Guerrier).
1 <?php
2 abstract class Personnage
3 {
4 protected $atout ,
5 $degats ,
6 $id ,
7 $nom ,
8 $timeEndormi ,
9 $type;
10
11 const CEST_MOI = 1; // Constante renvoye par la mthode
frapper si on se frappe soit -mme.
12 const PERSONNAGE_TUE = 2; // Constante renvoye par la m
thode frapper si on a tu le personnage en le frappant.
13 const PERSONNAGE_FRAPPE = 3; // Constante renvoye par la m
thode frapper si on a bien frapp le personnage.
14 const PERSONNAGE_ENSORCELE = 4; // Constante renvoye par la
mthode lancerUnSort (voir classe Magicien) si on a bien
ensorcel un personnage.
15 const PAS_DE_MAGIE = 5; // Constante renvoye par la mthode
lancerUnSort (voir classe Magicien) si on veut jeter un
sort alors que la magie du magicien est 0.
16 const PERSO_ENDORMI = 6; // Constante renvoye par la mthode
frapper si le personnage qui veut frapper est endormi.
17
18 public function __construct(array $donnees)
19 {
20 $this ->hydrate($donnees);
21 $this ->type = strtolower(get_class($this));
22 }
23
24 public function estEndormi ()
121
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
25 {
26 return $this ->timeEndormi > time();
27 }
28
29 public function frapper(Personnage $perso)
30 {
31 if ($perso ->id == $this ->id)
32 {
33 return self:: CEST_MOI;
34 }
35
36 if ($this ->estEndormi ())
37 {
38 return self:: PERSO_ENDORMI;
39 }
40
41 // On indique au personnage qu'il doit recevoir des dgts.
42 // Puis on retourne la valeur renvoye par la mthode :
self:: PERSONNAGE_TUE ou self:: PERSONNAGE_FRAPPE.
43 return $perso ->recevoirDegats ();
44 }
45
46 public function hydrate(array $donnees)
47 {
48 foreach ($donnees as $key => $value)
49 {
50 $method = 'set'.ucfirst($key);
51
52 if (method_exists($this , $method))
53 {
54 $this ->$method($value);
55 }
56 }
57 }
58
59 public function nomValide ()
60 {
61 return !empty($this ->nom);
62 }
63
64 public function recevoirDegats($force)
65 {
66 $this ->degats += 5;
67
68 // Si on a 100 de dgts ou plus , on supprime le personnage
de la BDD.
69 if ($this ->degats >= 100)
70 {
71 return self:: PERSONNAGE_TUE;
72 }
122
CORRECTION
73
74 // Sinon , on se contente de mettre jour les dgts du
personnage.
75 return self:: PERSONNAGE_FRAPPE;
76 }
77
78 public function reveil ()
79 {
80 $secondes = $this ->timeEndormi;
81 $secondes -= time();
82
83 $heures = floor($secondes / 3600);
84 $secondes -= $heures * 3600;
85 $minutes = floor($secondes / 60);
86 $secondes -= $minutes * 60;
87
88 $heures .= $heures <= 1 ? ' heure' : ' heures ';
89 $minutes .= $minutes <= 1 ? ' minute ' : ' minutes ';
90 $secondes .= $secondes <= 1 ? ' seconde ' : ' secondes ';
91
92 return $heures . ', ' . $minutes . ' et ' . $secondes;
93 }
94
95 public function atout ()
96 {
97 return $this ->atout;
98 }
99
100 public function degats ()
101 {
102 return $this ->degats;
103 }
104
105 public function id()
106 {
107 return $this ->id;
108 }
109
110 public function nom()
111 {
112 return $this ->nom;
113 }
114
115 public function timeEndormi ()
116 {
117 return $this ->timeEndormi;
118 }
119
120 public function type()
121 {
123
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
122 return $this ->type;
123 }
124
125 public function setAtout($atout)
126 {
127 $atout = (int) $atout;
128
129 if ($atout >= 0 && $atout <= 100)
130 {
131 $this ->atout = $atout;
132 }
133 }
134
135 public function setDegats($degats)
136 {
137 $degats = (int) $degats;
138
139 if ($degats >= 0 && $degats <= 100)
140 {
141 $this ->degats = $degats;
142 }
143 }
144
145 public function setId($id)
146 {
147 $id = (int) $id;
148
149 if ($id > 0)
150 {
151 $this ->id = $id;
152 }
153 }
154
155 public function setNom($nom)
156 {
157 if (is_string($nom))
158 {
159 $this ->nom = $nom;
160 }
161 }
162
163 public function setTimeEndormi($time)
164 {
165 $this ->timeEndormi = (int) $time;
166 }
167 }
Penchons-nous maintenant vers la classe Guerrier. Celle-ci devait modier la mthode
recevoirDegats() an dajouter une parade lors dune attaque.
1 <?php
124
CORRECTION
2 class Guerrier extends Personnage
3 {
4 public function recevoirDegats($force)
5 {
6 if ($this ->degats >= 0 && $this ->degats <= 25)
7 {
8 $this ->atout = 4;
9 }
10 elseif ($this ->degats > 25 && $this ->degats <= 50)
11 {
12 $this ->atout = 3;
13 }
14 elseif ($this ->degats > 50 && $this ->degats <= 75)
15 {
16 $this ->atout = 2;
17 }
18 elseif ($this ->degats > 50 && $this ->degats <= 90)
19 {
20 $this ->atout = 1;
21 }
22 else
23 {
24 $this ->atout = 0;
25 }
26
27 $this ->degats += 5 - $this ->atout;
28
29 // Si on a 100 de dgts ou plus , on supprime le personnage
de la BDD.
30 if ($this ->degats >= 100)
31 {
32 return self:: PERSONNAGE_TUE;
33 }
34
35 // Sinon , on se contente de mettre jour les dgts du
personnage.
36 return self:: PERSONNAGE_FRAPPE;
37 }
38 }
Enn, il faut maintenant soccuper du magicien. La classe le reprsentant devait ajouter
une nouvelle fonctionnalit : celle de pouvoir lancer un sort.
1 <?php
2 class Magicien extends Personnage
3 {
4 public function lancerUnSort(Personnage $perso)
5 {
6 if ($this ->degats >= 0 && $this ->degats <= 25)
7 {
8 $this ->atout = 4;
125
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
9 }
10 elseif ($this ->degats > 25 && $this ->degats <= 50)
11 {
12 $this ->atout = 3;
13 }
14 elseif ($this ->degats > 50 && $this ->degats <= 75)
15 {
16 $this ->atout = 2;
17 }
18 elseif ($this ->degats > 50 && $this ->degats <= 90)
19 {
20 $this ->atout = 1;
21 }
22 else
23 {
24 $this ->atout = 0;
25 }
26
27 if ($perso ->id == $this ->id)
28 {
29 return self:: CEST_MOI;
30 }
31
32 if ($this ->atout == 0)
33 {
34 return self:: PAS_DE_MAGIE;
35 }
36
37 if ($this ->estEndormi ())
38 {
39 return self:: PERSO_ENDORMI;
40 }
41
42 $perso ->timeEndormi = time() + ($this ->atout * 6) * 3600;
43
44 return self:: PERSONNAGE_ENSORCELE;
45 }
46 }
Passons maintenant au manager. Nous allons toujours garder un seul manager parce
que nous grons toujours des personnages ayant la mme structure. Les modications
sont mineures : il faut juste ajouter les deux nouveaux champs dans les requtes et
instancier la bonne classe lorsquon rcupre un personnage.
1 <?php
2 class PersonnagesManager
3 {
4 private $db; // Instance de PDO
5
6 public function __construct($db)
7 {
126
CORRECTION
8 $this ->db = $db;
9 }
10
11 public function add(Personnage $perso)
12 {
13 $q = $this ->db ->prepare('INSERT INTO personnages_v2 SET nom
= :nom , type = :type');
14
15 $q ->bindValue(':nom', $perso ->nom());
16 $q ->bindValue(':type', $perso ->type());
17
18 $q ->execute ();
19
20 $perso ->hydrate(array(
21 'id' => $this ->db->lastInsertId (),
22 'degats ' => 0,
23 'atout' => 0
24 ));
25 }
26
27 public function count ()
28 {
29 return $this ->db ->query('SELECT COUNT (*) FROM
personnages_v2 ')->fetchColumn ();
30 }
31
32 public function delete(Personnage $perso)
33 {
34 $this ->db->exec('DELETE FROM personnages_v2 WHERE id = '.
$perso ->id());
35 }
36
37 public function exists($info)
38 {
39 if (is_int($info)) // On veut voir si tel personnage ayant
pour id $info existe.
40 {
41 return (bool) $this ->db->query('SELECT COUNT (*) FROM
personnages_v2 WHERE id = '.$info)->fetchColumn ();
42 }
43
44 // Sinon , c'est qu'on veut vrifier que le nom existe ou
pas.
45
46 $q = $this ->db ->prepare('SELECT COUNT (*) FROM
personnages_v2 WHERE nom = :nom');
47 $q ->execute(array(':nom' => $info));
48
49 return (bool) $q ->fetchColumn ();
50 }
127
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
51
52 public function get($info)
53 {
54 if (is_int($info))
55 {
56 $q = $this ->db ->query('SELECT id, nom , degats ,
timeEndormi , type , atout FROM personnages_v2 WHERE id
= '.$info);
57 $perso = $q ->fetch(PDO:: FETCH_ASSOC);
58 }
59
60 else
61 {
62 $q = $this ->db ->prepare('SELECT id, nom , degats ,
timeEndormi , type , atout FROM personnages_v2 WHERE nom
= :nom');
63 $q->execute(array(':nom' => $info));
64
65 $perso = $q ->fetch(PDO:: FETCH_ASSOC);
66 }
67
68 switch ($perso['type'])
69 {
70 case 'guerrier ': return new Guerrier($perso);
71 case 'magicien ': return new Magicien($perso);
72 default: return null;
73 }
74 }
75
76 public function getList($nom)
77 {
78 $persos = array();
79
80 $q = $this ->db ->prepare('SELECT id, nom , degats ,
timeEndormi , type , atout FROM personnages_v2 WHERE nom
<> :nom ORDER BY nom');
81 $q->execute(array(':nom' => $nom));
82
83 while ($donnees = $q ->fetch(PDO:: FETCH_ASSOC))
84 {
85 switch ($donnees['type'])
86 {
87 case 'guerrier ': $persos [] = new Guerrier($donnees);
break;
88 case 'magicien ': $persos [] = new Magicien($donnees);
break;
89 }
90 }
91
92 return $persos;
128
CORRECTION
93 }
94
95 public function update(Personnage $perso)
96 {
97 $q = $this ->db ->prepare('UPDATE personnages_v2 SET degats =
:degats , timeEndormi = :timeEndormi , atout = :atout
WHERE id = :id');
98
99 $q ->bindValue(':degats ', $perso ->degats (), PDO:: PARAM_INT);
100 $q ->bindValue(':timeEndormi ', $perso ->timeEndormi (), PDO::
PARAM_INT);
101 $q ->bindValue(':atout', $perso ->atout(), PDO:: PARAM_INT);
102 $q ->bindValue(':id', $perso ->id(), PDO:: PARAM_INT);
103
104 $q ->execute ();
105 }
106 }
Enn, nissons par la page dindex qui a lgrement chang. Quelles sont les modica-
tions ?
Le formulaire doit proposer au joueur de choisir le type du personnage quil veut
crer.
Lorsquon cre un personnage, le script doit crer une instance de la classe dsire
et non de Personnage.
Lorsquon veut frapper un personnage, on vrie que lattaquant nest pas endormi.
Un lien permettant densorceler un personnage doit tre ajout pour les magiciens
ainsi que lantidote qui va avec.
1 <?php
2 function chargerClasse($classe)
3 {
4 require $classe . '.class.php';
5 }
6
7 spl_autoload_register('chargerClasse ');
8
9 session_start ();
10
11 if (isset($_GET['deconnexion ']))
12 {
13 session_destroy ();
14 header('Location: .');
15 exit();
16 }
17
18 $db = new PDO('mysql:host=localhost;dbname=combats ', 'root', ''
);
19 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_WARNING);
20
21 $manager = new PersonnagesManager($db);
129
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
22
23 if (isset($_SESSION['perso'])) // Si la session perso existe ,
on restaure l'objet.
24 {
25 $perso = $_SESSION['perso'];
26 }
27
28 if (isset($_POST['creer']) && isset($_POST['nom'])) // Si on a
voulu crer un personnage.
29 {
30 switch ($_POST['type'])
31 {
32 case 'magicien ' :
33 $perso = new Magicien(array('nom' => $_POST['nom']));
34 break;
35
36 case 'guerrier ' :
37 $perso = new Guerrier(array('nom' => $_POST['nom']));
38 break;
39
40 default :
41 $message = 'Le type du personnage est invalide.';
42 break;
43 }
44
45 if (isset($perso)) // Si le type du personnage est valide , on
a cr un personnage.
46 {
47 if (!$perso ->nomValide ())
48 {
49 $message = 'Le nom choisi est invalide.';
50 unset($perso);
51 }
52 elseif ($manager ->exists($perso ->nom()))
53 {
54 $message = 'Le nom du personnage est dj pris.';
55 unset($perso);
56 }
57 else
58 {
59 $manager ->add($perso);
60 }
61 }
62 }
63
64 elseif (isset($_POST['utiliser ']) && isset($_POST['nom'])) //
Si on a voulu utiliser un personnage.
65 {
66 if ($manager ->exists($_POST['nom'])) // Si celui -ci existe.
67 {
130
CORRECTION
68 $perso = $manager ->get($_POST['nom']);
69 }
70 else
71 {
72 $message = 'Ce personnage n\'existe pas !'; // S'il n'
existe pas , on affichera ce message.
73 }
74 }
75
76 elseif (isset($_GET['frapper '])) // Si on a cliqu sur un
personnage pour le frapper.
77 {
78 if (!isset($perso))
79 {
80 $message = 'Merci de crer un personnage ou de vous
identifier.';
81 }
82
83 else
84 {
85 if (!$manager ->exists ((int) $_GET['frapper ']))
86 {
87 $message = 'Le personnage que vous voulez frapper n\'
existe pas !';
88 }
89
90 else
91 {
92 $persoAFrapper = $manager ->get((int) $_GET['frapper ']);
93 $retour = $perso ->frapper($persoAFrapper); // On stocke
dans $retour les ventuelles erreurs ou messages que
renvoie la mthode frapper.
94
95 switch ($retour)
96 {
97 case Personnage :: CEST_MOI :
98 $message = 'Mais ... pourquoi voulez -vous vous frapper
???';
99 break;
100
101 case Personnage :: PERSONNAGE_FRAPPE :
102 $message = 'Le personnage a bien t frapp !';
103
104 $manager ->update($perso);
105 $manager ->update($persoAFrapper);
106
107 break;
108
109 case Personnage :: PERSONNAGE_TUE :
110 $message = 'Vous avez tu ce personnage !';
131
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
111
112 $manager ->update($perso);
113 $manager ->delete($persoAFrapper);
114
115 break;
116
117 case Personnage :: PERSO_ENDORMI :
118 $message = 'Vous tes endormi , vous ne pouvez pas
frapper de personnage !';
119 break;
120 }
121 }
122 }
123 }
124
125 elseif (isset($_GET['ensorceler ']))
126 {
127 if (!isset($perso))
128 {
129 $message = 'Merci de crer un personnage ou de vous
identifier.';
130 }
131
132 else
133 {
134 // Il faut bien vrifier que le personnage est un magicien.
135 if ($perso ->type() != 'magicien ')
136 {
137 $message = 'Seuls les magiciens peuvent ensorceler des
personnages !';
138 }
139
140 else
141 {
142 if (!$manager ->exists ((int) $_GET['ensorceler ']))
143 {
144 $message = 'Le personnage que vous voulez frapper n\'
existe pas !';
145 }
146
147 else
148 {
149 $persoAEnsorceler = $manager ->get((int) $_GET['
ensorceler ']);
150 $retour = $perso ->lancerUnSort($persoAEnsorceler);
151
152 switch ($retour)
153 {
154 case Personnage :: CEST_MOI :
155 $message = 'Mais ... pourquoi voulez -vous vous
132
CORRECTION
ensorceler ???';
156 break;
157
158 case Personnage :: PERSONNAGE_ENSORCELE :
159 $message = 'Le personnage a bien t ensorcel !';
160
161 $manager ->update($perso);
162 $manager ->update($persoAEnsorceler);
163
164 break;
165
166 case Personnage :: PAS_DE_MAGIE :
167 $message = 'Vous n\'avez pas de magie !';
168 break;
169
170 case Personnage :: PERSO_ENDORMI :
171 $message = 'Vous tes endormi , vous ne pouvez pas
lancer de sort !';
172 break;
173 }
174 }
175 }
176 }
177 }
178 ?>
179 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
180 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
181 <head >
182 <title >TP : Mini jeu de combat - Version 2</title >
183
184 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
185 </head >
186 <body >
187 <p>Nombre de personnages crs : <?php echo $manager ->count
(); ?></p>
188 <?php
189 if (isset($message)) // On a un message afficher ?
190 {
191 echo '<p>', $message , '</p>'; // Si oui , on l'affiche
192 }
193
194 if (isset($perso)) // Si on utilise un personnage (nouveau ou
pas).
195 {
196 ?>
197 <p><a href="?deconnexion=1">Dconnexion </a></p>
198
199 <fieldset >
133
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
200 <legend >Mes informations </legend >
201 <p>
202 Type : <?php echo ucfirst($perso ->type()); ?><br />
203 Nom : <?php echo htmlspecialchars($perso ->nom()); ?><br
/>
204 Dgts : <?php echo $perso ->degats (); ?><br />
205 <?php
206 // On affiche l'atout du personnage suivant son type.
207 switch ($perso ->type())
208 {
209 case 'magicien ' :
210 echo 'Magie : ';
211 break;
212
213 case 'guerrier ' :
214 echo 'Protection : ';
215 break;
216 }
217
218 echo $perso ->atout();
219 ?>
220 </p>
221 </fieldset >
222
223 <fieldset >
224 <legend >Qui attaquer ?</legend >
225 <p>
226 <?php
227 // On rcupre tous les personnages par ordre alphabtique ,
dont le nom est diffrent de celui de notre personnage (on
va pas se frapper nous -mme :p).
228 $retourPersos = $manager ->getList($perso ->nom());
229
230 if (empty($retourPersos))
231 {
232 echo 'Personne frapper !';
233 }
234
235 else
236 {
237 if ($perso ->estEndormi ())
238 {
239 echo 'Un magicien vous a endormi ! Vous allez vous r
veiller dans ', $perso ->reveil (), '.';
240 }
241
242 else
243 {
244 foreach ($retourPersos as $unPerso)
245 {
134
CORRECTION
246 echo '<a href ="? frapper=', $unPerso ->id(), '">',
htmlspecialchars($unPerso ->nom()), '</a> (dgts : ',
$unPerso ->degats (), ' | type : ', $unPerso ->type(), ')
';
247
248 // On ajoute un lien pour lancer un sort si le personnage
est un magicien.
249 if ($perso ->type() == 'magicien ')
250 {
251 echo ' | <a href ="? ensorceler=', $unPerso ->id(), '">
Lancer un sort </a>';
252 }
253
254 echo '<br />';
255 }
256 }
257 }
258 ?>
259 </p>
260 </fieldset >
261 <?php
262 }
263 else
264 {
265 ?>
266 <form action="" method="post">
267 <p>
268 Nom : <input type="text" name="nom" maxlength="50" /> <
input type="submit" value="Utiliser ce personnage"
name="utiliser" /><br />
269 Type :
270 <select name="type">
271 <option value="magicien">Magicien </option >
272 <option value="guerrier">Guerrier </option >
273 </select >
274 <input type="submit" value="Crer ce personnage" name="
creer" />
275 </p>
276 </form >
277 <?php
278 }
279 ?>
280 </body >
281 </html >
282 <?php
283 if (isset($perso)) // Si on a cr un personnage , on le stocke
dans une variable session afin d'conomiser une requte SQL.
284 {
285 $_SESSION['perso'] = $perso;
286 }
135
CHAPITRE 7. TP : DES PERSONNAGES SPCIALISS
Alors, vous commencez comprendre toute la puissance de la POO et de lhritage ?
Avec une telle structure, vous pourrez tout moment dcider de crer un nouveau
personnage trs simplement ! Il vous sut de crer une nouvelle classe, dajouter le
type du personnage lnumration type en BDD et de modier un petit peu le
chier index.php et votre personnage voit le jour !
Amliorations possibles
Comme au prcdent TP, beaucoup damliorations sont possibles, commencer par
celles dj exposes dans le chapitre dudit TP :
Un systme de niveau. Vous pourriez trs bien assigner chaque personnage un
niveau de 1 100. Le personnage bncierait aussi dune exprience allant de 0
100. Lorsque lexprience atteint 100, le personnage passe au niveau suivant.
Indice : le niveau et lexprience deviendraient des caractristiques du person-
nage, donc. . . Pas besoin de vous le dire, je suis sr que vous savez ce que a signie !
Un systme de force. La force du personnage pourrait augmenter en fonction de son
niveau, les dgts inigs la victime seront donc plus importants.
Indice : de mme, la force du personnage serait aussi une caractristique du per-
sonnage.
Un systme de limitation. En eet, un personnage peut en frapper autant quil
veut dans un laps de temps indni. Pourquoi ne pas le limiter 3 coups par jour ?
Indice : il faudrait que vous stockiez le nombre de coups ports par le personnage,
ainsi que la date du dernier coup port. Cela ferait donc deux nouveaux champs
en BDD, et deux nouvelles caractristiques pour le personnage !
Un systme de retrait de dgts. Chaque jour, si lutilisateur se connecte, il pour-
rait voir ses dgts se soustraire de 10 par exemple. Indice : il faudrait stocker la
date de dernire connexion. chaque connexion, vous regarderiez cette date. Si elle
est infrieure 24h, alors vous ne feriez rien. Sinon, vous retireriez 10 de dgts au
personnage puis mettriez jour cette date de dernire connexion.
Pour reprendre cet esprit, vous pouvez vous entraner crer dautres personnages,
comme une brute par exemple. Son atout dpendrait aussi de ses dgts et viendrait
augmenter sa force lors dune attaque.
La seule dirence avec le premier TP est lapparition de nouveaux personnages, les
seules amliorations direntes possibles sont donc justement la cration de nouveaux
personnages. Ainsi je vous encourage imaginer tout un tas de dirents personnages
tous aussi farfelus les uns que les autres : cela vous fera grandement progresser et cela
vous aidera vous familiariser avec lhritage !
136
Chapitre 8
Les mthodes magiques
Dicult :
N
ous allons terminer cette partie par un chapitre assez simple. Dans ce chapitre,
nous allons nous pencher sur une possibilit que nous ore le langage : il sagit des
mthodes magiques. Ce sont de petites bricoles bien pratiques dans certains cas.
137
CHAPITRE 8. LES MTHODES MAGIQUES
Le principe
Vous devez sans doute vous poser une grande question la vue du titre du chapitre :
mais quest-ce que cest quune mthode magique ?
Une mthode magique est une mthode qui, si elle est prsente dans votre classe, sera
appele lors de tel ou tel vnement. Si la mthode nexiste pas et que lvnement est
excut, aucun eet spcial ne sera ajout, lvnement sexcutera normalement.
Le but des mthodes magiques est dintercepter un vnement, dire de faire ceci ou
cela et retourner une valeur utile pour lvnement si besoin il y a.
Bonne nouvelle : vous connaissez dj une mthode magique !
Si si, cherchez bien au fond de votre tte. . . Et oui, la mthode __construct est ma-
gique ! Comme nous lavons vu plus haut, chaque mthode magique sexcute au mo-
ment o un vnement est lanc. Lvnement qui appelle la mthode __construct est
la cration de lobjet.
Dans le mme genre que __construct on peut citer __destruct qui, elle, sera appele
lors de la destruction de lobjet. Assez intuitif, mais voici un exemple au cas o :
1 <?php
2 class MaClasse
3 {
4 public function __construct ()
5 {
6 echo 'Construction de MaClasse ';
7 }
8
9 public function __destruct ()
10 {
11 echo 'Destruction de MaClasse ';
12 }
13 }
14
15 $obj = new MaClasse;
16 ?>
Ainsi, vous verrez les deux messages crits ci-dessus la suite.
Surcharger les attributs et mthodes
Parlons maintenant des mthodes magiques lies la surcharge des attributs et m-
thodes.
Euh, deux secondes l. . . Cest quoi la surcharge des attributs et m-
thodes ? ?
138
SURCHARGER LES ATTRIBUTS ET MTHODES
Vous avez raison, il serait dabord prfrable dexpliquer ceci. La surcharge dattributs
ou mthodes consiste prvoir le cas o lon appelle un attribut ou mthode qui
nexiste pas ou du moins, auquel on na pas accs (par exemple, si un attribut ou une
mthode est priv(e)). Dans ce cas-l, on a. . . voyons. . . 6 mthodes magiques notre
disposition !
__set et __get
Commenons par tudier ces deux mthodes magiques. Leur principe est le mme, leur
fonctionnement est peu prs semblable, cest juste lvnement qui change.
Commenons par __set. Cette mthode est appele lorsque lon essaye dassigner une
valeur un attribut auquel on na pas accs ou qui nexiste pas. Cette mthode prend
deux paramtres : le premier est le nom de lattribut auquel on a tent dassigner une
valeur, le second paramtre est la valeur que lon a tent dassigner lattribut. Cette
mthode ne retourne rien. Vous pouvez simplement faire ce que bon vous semble.
Exemple :
1 <?php
2 class MaClasse
3 {
4 private $unAttributPrive;
5
6 public function __set($nom , $valeur)
7 {
8 echo 'Ah , on a tent d\'assigner l\'attribut <strong >',
$nom , '</strong > la valeur <strong >', $valeur , '</strong
> mais c\'est pas possible !<br />';
9 }
10 }
11
12 $obj = new MaClasse;
13
14 $obj ->attribut = 'Simple test';
15 $obj ->unAttributPrive = 'Autre simple test';
16 ?>
la sortie sachera ceci (voir la gure 8.1).
Tenez, petit exercice, stockez dans un tableau tous les attributs (avec leurs valeurs)
que nous avons essay de modier ou crer.
Solution :
1 <?php
2 class MaClasse
3 {
4 private $attributs = array ();
5 private $unAttributPrive;
6
139
CHAPITRE 8. LES MTHODES MAGIQUES
Figure 8.1 Rsultat ach par le script
7 public function __set($nom , $valeur)
8 {
9 $this ->attributs[$nom] = $valeur;
10 }
11
12 public function afficherAttributs ()
13 {
14 echo '<pre >', print_r($this ->attributs , true), '</pre >';
15 }
16 }
17
18 $obj = new MaClasse;
19
20 $obj ->attribut = 'Simple test';
21 $obj ->unAttributPrive = 'Autre simple test';
22
23 $obj ->afficherAttributs ();
24 ?>
Pas compliqu faire, mais cela permet de pratiquer un peu.
Parlons maintenant de __get. Cette mthode est appele lorsque lon essaye daccder
un attribut qui nexiste pas ou auquel on na pas accs. Elle prend un paramtre :
le nom de lattribut auquel on a essay daccder. Cette mthode peut retourner ce
quelle veut (ce sera, en quelque sorte, la valeur de lattribut inaccessible).
Exemple :
1 <?php
2 class MaClasse
3 {
4 private $unAttributPrive;
5
6 public function __get($nom)
7 {
8 return 'Impossible d\'accder l\'attribut <strong >' .
$nom . '</strong >, dsol !<br />';
9 }
140
SURCHARGER LES ATTRIBUTS ET MTHODES
10 }
11
12 $obj = new MaClasse;
13
14 echo $obj ->attribut;
15 echo $obj ->unAttributPrive;
16 ?>
Ce qui va acher ceci (voir la gure 8.2).
Figure 8.2 Rsultat ach par le script
Encore un exercice ! Combinez lexercice prcdent en vriant si lattribut auquel on
a tent daccder est contenu dans le tableau de stockage dattributs. Si tel est le cas,
on lache, sinon, on ne fait rien.
Solution :
1 <?php
2 class MaClasse
3 {
4 private $attributs = array ();
5 private $unAttributPrive;
6
7 public function __get($nom)
8 {
9 if (isset($this ->attributs[$nom]))
10 {
11 return $this ->attributs[$nom];
12 }
13 }
14
15 public function __set($nom , $valeur)
16 {
17 $this ->attributs[$nom] = $valeur;
18 }
19
20 public function afficherAttributs ()
141
CHAPITRE 8. LES MTHODES MAGIQUES
21 {
22 echo '<pre >', print_r($this ->attributs , true), '</pre >';
23 }
24 }
25
26 $obj = new MaClasse;
27
28 $obj ->attribut = 'Simple test';
29 $obj ->unAttributPrive = 'Autre simple test';
30
31 echo $obj ->attribut;
32 echo $obj ->autreAtribut;
33 ?>
tant donn que tous vos attributs doivent tre privs, vous pouvez facilement
les mettre en lecture seule grce __get. Lutilisateur aura accs aux
attributs, mais ne pourra pas les modier.
__isset et __unset
La premire mthode __isset est appele lorsque lon appelle la fonction isset sur
un attribut qui nexiste pas ou auquel on na pas accs. tant donn que la fonction
initiale isset renvoie true ou false, la mthode magique __isset doit renvoyer un
boolen. Cette mthode prend un paramtre : le nom de lattribut que lon a envoy la
fonction isset. Vous pouvez par exemple utiliser la classe prcdente en implmentant
la mthode __isset, ce qui peut nous donner :
1 <?php
2 class MaClasse
3 {
4 private $attributs = array ();
5 private $unAttributPrive;
6
7 public function __set($nom , $valeur)
8 {
9 $this ->attributs[$nom] = $valeur;
10 }
11
12 public function __get($nom)
13 {
14 if (isset($this ->attributs[$nom]))
15 {
16 return $this ->attributs[$nom];
17 }
18 }
19
20 public function __isset($nom)
142
SURCHARGER LES ATTRIBUTS ET MTHODES
21 {
22 return isset($this ->attributs[$nom]);
23 }
24 }
25
26 $obj = new MaClasse;
27
28 $obj ->attribut = 'Simple test';
29 $obj ->unAttributPrive = 'Autre simple test';
30
31 if (isset($obj ->attribut))
32 {
33 echo 'L\'attribut <strong >attribut </strong > existe !<br />';
34 }
35 else
36 {
37 echo 'L\'attribut <strong >attribut </strong > n\'existe pas !<
br />';
38 }
39
40 if (isset($obj ->unAutreAttribut))
41 {
42 echo 'L\'attribut unAutreAttribut existe !';
43 }
44 else
45 {
46 echo 'L\'attribut unAutreAttribut n\'existe pas !';
47 }
48 ?>
Ce qui achera ceci (voir la gure 8.3).
Figure 8.3 Rsultat ach par le script
Pour __unset, le principe est le mme. Cette mthode est appele lorsque lon tente
dappeler la fonction unset sur un attribut inexistant ou auquel on na pas accs. On
peut facilement implmenter __unset la classe prcdente de manire supprimer
143
CHAPITRE 8. LES MTHODES MAGIQUES
lentre correspondante dans notre tableau $attributs. Cette mthode ne doit rien
retourner.
1 <?php
2 class MaClasse
3 {
4 private $attributs = array ();
5 private $unAttributPrive;
6
7 public function __set($nom , $valeur)
8 {
9 $this ->attributs[$nom] = $valeur;
10 }
11
12 public function __get($nom)
13 {
14 if (isset($this ->attributs[$nom]))
15 {
16 return $this ->attributs[$nom];
17 }
18 }
19
20 public function __isset($nom)
21 {
22 return isset($this ->attributs[$nom]);
23 }
24
25 public function __unset($nom)
26 {
27 if (isset($this ->attributs[$nom]))
28 {
29 unset($this ->attributs[$nom]);
30 }
31 }
32 }
33
34 $obj = new MaClasse;
35
36 $obj ->attribut = 'Simple test';
37 $obj ->unAttributPrive = 'Autre simple test';
38
39 if (isset($obj ->attribut))
40 {
41 echo 'L\'attribut <strong >attribut </strong > existe !<br />';
42 }
43 else
44 {
45 echo 'L\'attribut <strong >attribut </strong > n\'existe pas !<
br />';
46 }
47
144
SURCHARGER LES ATTRIBUTS ET MTHODES
48 unset($obj ->attribut);
49
50 if (isset($obj ->attribut))
51 {
52 echo 'L\'attribut <strong >attribut </strong > existe !<br />';
53 }
54 else
55 {
56 echo 'L\'attribut <strong >attribut </strong > n\'existe pas !<
br />';
57 }
58
59 if (isset($obj ->unAutreAttribut))
60 {
61 echo 'L\'attribut <strong >unAutreAttribut </strong > existe !';
62 }
63 else
64 {
65 echo 'L\'attribut <strong >unAutreAttribut </strong > n\'existe
pas !';
66 }
67 ?>
Ce qui donnera ceci (voir la gure 8.4).
Figure 8.4 Rsultat ach par le script
Finissons par __call et __callStatic
Bien que la mthode magique __call soit disponible sous PHP 5.1, la m-
thode __callStatic nest disponible que sous PHP 5.3 !
Bien. Laissons de ct les attributs pour le moment et parler cette fois-ci des mthodes
que lon appelle alors quon ny a pas accs (soit elle nexiste pas, soit elle est prive).
145
CHAPITRE 8. LES MTHODES MAGIQUES
La mthode __call sera appele lorsque lon essayera dappeler une telle mthode. Elle
prend deux arguments : le premier est le nom de la mthode que lon a essay dappeler
et le second est la liste des arguments qui lui ont t passs (sous forme de tableau).
Exemple :
1 <?php
2 class MaClasse
3 {
4 public function __call($nom , $arguments)
5 {
6 echo 'La mthode <strong >', $nom , '</strong > a t appele
alors qu\'elle n\'existe pas ! Ses arguments taient les
suivants : <strong >', implode($arguments , '</strong >, <
strong >'), '</strong >';
7 }
8 }
9
10 $obj = new MaClasse;
11
12 $obj ->methode(123 , 'test');
13 ?>
Rsultat la gure 8.5.
Figure 8.5 Rsultat ach par le script
Et si on essaye dappeler une mthode qui nexiste pas statiquement ? Et bien, erreur
fatale ! Sauf si vous utilisez __callStatic. Cette mthode est appele lorsque vous
appelez une mthode dans un contexte statique alors quelle nexiste pas. La mthode
magique __callStatic doit obligatoirement tre static !
1 <?php
2 class MaClasse
3 {
4 public function __call($nom , $arguments)
5 {
6 echo 'La mthode <strong >', $nom , '</strong > a t appele
alors qu\'elle n\'existe pas ! Ses arguments taient les
146
LINARISER SES OBJETS
suivants : <strong >', implode ($arguments , '</strong >,
<strong >'), '</strong ><br />';
7 }
8
9 public static function __callStatic($nom , $arguments)
10 {
11 echo 'La mthode <strong >', $nom , '</strong > a t appele
dans un contexte statique alors qu\'elle n\'existe pas !
Ses arguments taient les suivants : <strong >', implode
($arguments , '</strong >, <strong >'), '</strong ><br />';
12 }
13 }
14
15 $obj = new MaClasse;
16
17 $obj ->methode(123 , 'test');
18
19 MaClasse :: methodeStatique(456 , 'autre test');
20 ?>
Rsultat la gure 8.6.
Figure 8.6 Rsultat ach par le script
Linariser ses objets
Voici un point important de ce chapitre sur lequel je voudrais marrter un petit instant :
la linarisation des objets. Pour suivre cette partie, je vous recommande chaudement
le tutoriel accessible via le code web suivant. Il vous expliquera de manire gnrale ce
quest la linarisation et vous fera pratiquer sur des exemples divers. Lisez le jusqu
la partie Encore plus fort !, et je vais mieux vous expliquer partir de l !

Linarisation
Code web : 267969
147
CHAPITRE 8. LES MTHODES MAGIQUES
Posons le problme
Vous avez un systme de sessions sur votre site avec une classe Connexion. Cette
classe, comme son nom lindique, aura pour rle dtablir une connexion la BDD.
Vous aimeriez bien stocker lobjet cr dans une variable $_SESSION mais vous ne
savez pas comment faire.
Ben si ! On fait $_SESSION[connexion] = $objetConnexion et puis
voil !
Oui, a fonctionne, mais savez-vous vraiment ce qui se passe quand vous eectuez une
telle opration ? Ou plutt, ce qui se passe la n du script ? En fait, la n du
script, le tableau de session est linaris automatiquement. Linariser signie que
lon transforme une variable en chane de caractres selon un format bien prcis. Cette
chane de caractres pourra, quand on le souhaitera, tre transforme dans lautre sens
(cest--dire quon va restituer son tat dorigine). Pour bien comprendre ce principe,
on va linariser nous-mmes notre objet. Voici ce que nous allons faire :
Cration de lobjet ($objetConnexion = new Connexion;) ;
transformation de lobjet en chane de caractres :
$_SESSION[connexion] = serialize($objetConnexion); ;
changement de page ;
transformation de la chane de caractres en objet :
$objetConnexion = unserialize($_SESSION[connexion]);.
Des explications simposent.
Les nouveauts rencontres ici sont lapparition de deux nouvelles fonctions : serialize
et unserialize.
La premire fonction, serialize, retourne lobjet pass en paramtre sous forme de
chane de caractres. Vous vous demandez sans doutes comment on peut transformer
un objet en chane de caractres : la rponse est toute simple. Quand on y rchit,
un objet cest quoi ? Cest un ensemble dattributs, tout simplement. Les mthodes
ne sont pas stockes dans lobjet, cest la classe qui sen occupe. Notre chane de
caractres contiendra donc juste quelque chose comme : Objet MaClasse contenant
les attributs unAttribut qui vaut Hello world! , autreAttribut qui vaut Vive la
linarisation , dernierAttribut qui vaut Et un dernier pour la route ! . Ainsi,
vous pourrez conserver votre objet dans une variable sous forme de chane de caractres.
Si vous achez cette chane par un echo par exemple, vous narriverez sans doute pas
dchirer lobjet, cest normal, ce nest pas aussi simple que la chane que jai montre
titre dexemple. Cette fonction est automatiquement appele sur larray $_SESSION
la n du script, notre objet est donc automatiquement linaris la n du script.
Cest uniquement dans un but didactique que nous linarisons manuellement.
La seconde fonction, unserialize, retourne la chane de caractres passe en para-
mtre sous forme dobjet. En gros, cette fonction lit la chane de caractres, cre une
instance de la classe correspondante et assigne chaque attribut la valeur quils avaient.
148
LINARISER SES OBJETS
Ainsi, vous pourrez utiliser lobjet retourn (appel de mthodes, attributs et diverses
oprations) comme avant. Cette fonction est automatiquement appele ds le dbut
du script pour restaurer le tableau de sessions prcdemment enregistr dans le chier.
Sachez toutefois que si vous avez linaris un objet manuellement, il ne sera jamais
restaur automatiquement.
Et quel est le rapport avec tes mthodes magiques ?
En fait, les fonctions cites ci-dessus (serialize et unserialize) ne se contentent pas
de transformer le paramtre quon leur passe en autre chose : elles vrient si, dans
lobjet pass en paramtre (pour serialize), il y a une mthode __sleep, auquel cas
elle est excute. Si cest unserialize qui est appele, la fonction vrie si lobjet
obtenu comporte une mthode __wakeup, auquel cas elle est appele.
serialize et __sleep
La mthode magique __sleep est utilise pour nettoyer lobjet ou pour sauver des
attributs. Si la mthode magique __sleep nexiste pas, tous les attributs seront sau-
vs. Cette mthode doit renvoyer un tableau avec les noms des attributs sauver.
Par exemple, si vous voulez sauver $serveur et $login, la fonction devra retourner
array(serveur, login);.
Voici ce que pourrait donner notre classe Connexion :
1 <?php
2 class Connexion
3 {
4 protected $pdo , $serveur , $utilisateur , $motDePasse ,
$dataBase;
5
6 public function __construct($serveur , $utilisateur ,
$motDePasse , $dataBase)
7 {
8 $this ->serveur = $serveur;
9 $this ->utilisateur = $utilisateur;
10 $this ->motDePasse = $motDePasse;
11 $this ->dataBase = $dataBase;
12
13 $this ->connexionBDD ();
14 }
15
16 protected function connexionBDD ()
17 {
18 $this ->pdo = new PDO('mysql:host='.$this ->serveur.';dbname=
'.$this ->dataBase , $this ->utilisateur , $this ->motDePasse
);
19 }
149
CHAPITRE 8. LES MTHODES MAGIQUES
20
21 public function __sleep ()
22 {
23 // Ici sont placer des instructions excuter juste
avant la linarisation.
24 // On retourne ensuite la liste des attributs qu'on veut
sauver.
25 return array('serveur ', 'utilisateur ', 'motDePasse ', '
dataBase ');
26 }
27 }
28 ?>
Ainsi, vous pourrez faire ceci :
1 <?php
2 $connexion = new Connexion('localhost ', 'root', '', 'tests');
3
4 $_SESSION['connexion '] = serialize($connexion);
5 ?>
unserialize et __wakeup
Maintenant, nous allons simplement implmenter la fonction __wakeup. Quallons-nous
mettre dedans ?
Rien de compliqu. . . Nous allons juste appeler la mthode connexionBDD qui se char-
gera de nous connecter notre base de donnes puisque les identiants, serveur et nom
de la base ont t sauvegards et ainsi restaurs lappel de la fonction unserialize !
1 <?php
2 class Connexion
3 {
4 protected $pdo , $serveur , $utilisateur , $motDePasse ,
$dataBase;
5
6 public function __construct($serveur , $utilisateur ,
$motDePasse , $dataBase)
7 {
8 $this ->serveur = $serveur;
9 $this ->utilisateur = $utilisateur;
10 $this ->motDePasse = $motDePasse;
11 $this ->dataBase = $dataBase;
12
13 $this ->connexionBDD ();
14 }
15
16 protected function connexionBDD ()
17 {
150
LINARISER SES OBJETS
18 $this ->pdo = new PDO('mysql:host='.$this ->serveur.';dbname=
'.$this ->dataBase , $this ->utilisateur , $this ->motDePasse
);
19 }
20
21 public function __sleep ()
22 {
23 return array('serveur ', 'utilisateur ', 'motDePasse ', '
dataBase ');
24 }
25
26 public function __wakeup ()
27 {
28 $this ->connexionBDD ();
29 }
30 }
31 ?>
Pratique, hein ?
Maintenant que vous savez ce qui se passe quand vous enregistrez un objet dans une
entre de session, je vous autorise ne plus appeler serialize et unserialize.
Ainsi, ce code fonctionne parfaitement :
1 <?php
2 session_start ();
3
4 if (!isset($_SESSION['connexion ']))
5 {
6 $connexion = new Connexion('localhost ', 'root', '', 'tests');
7 $_SESSION['connexion '] = $connexion;
8
9 echo 'Actualisez la page !';
10 }
11
12 else
13 {
14 echo '<pre >';
15 var_dump($_SESSION['connexion ']); // On affiche les infos
concernant notre objet.
16 echo '</pre >';
17 }
18 ?>
Vous voyez donc, en testant ce code, que notre objet a bel et bien t sauvegard comme
il fallait, et que tous les attributs ont t sauvs. Bref, cest magique !
151
CHAPITRE 8. LES MTHODES MAGIQUES
tant donn que notre objet est restaur automatiquement lors de lappel
de session_start(), la classe correspondante doit tre dclare avant, si-
non lobjet dsrialis sera une instance de __PHP_Incomplete_Class_Name,
classe qui ne contient aucune mthode (cela produira donc un objet inutile).
Si vous avez un autoload qui chargera la classe automatiquement, il sera
appel.
Autres mthodes magiques
Voici les dernires mthodes magiques que vous navez pas vues. Je parlerai ici de
__toString, __set_state et __invoke.
__toString
La mthode magique __toString est appele lorsque lobjet est amen tre converti
en chane de caractres. Cette mthode doit retourner la chane de caractres souhaite.
Exemple :
1 <?php
2 class MaClasse
3 {
4 protected $texte;
5
6 public function __construct($texte)
7 {
8 $this ->texte = $texte;
9 }
10
11 public function __toString ()
12 {
13 return $this ->texte;
14 }
15 }
16
17 $obj = new MaClasse('Hello world !');
18
19 // Solution 1 : le cast
20
21 $texte = (string) $obj;
22 var_dump($texte); // Affiche : string(13) "Hello world !".
23
24 // Solution 2 : directement dans un echo
25 echo $obj; // Affiche : Hello world !
26 ?>
Pas mal, hein?
152
AUTRES MTHODES MAGIQUES
__set_state
La mthode magique __set_state est appele quand vous appelez la fonction var_export
en passant votre objet exporter en paramtre. Cette fonction var_export a pour rle
dexporter la variable passe en paramtre sous forme de code PHP (chane de carac-
tres). Si vous ne spciez pas de mthode __set_state dans votre classe, une erreur
fatale sera leve.
Notre mthode __set_state prend un paramtre, la liste des attributs ainsi que leur
valeur dans un tableau associatif (array(attribut => valeur)). Notre mthode
magique devra retourner lobjet exporter. Il faudra donc crer un nouvel objet et lui
assigner les valeurs quon souhaite, puis le retourner.
Ne jamais retourner $this, car cette variable nexistera pas dans cette m-
thode ! var_export reportera donc une valeur nulle.
Puisque la fonction var_export retourne du code PHP valide, on peut utiliser la fonc-
tion eval qui excute du code PHP sous forme de chane de caractres quon lui passe
en paramtre. Vous trouverez plus dinformations sur cette fonction via le code web
suivant :

Fonction eval
Code web : 236928
Par exemple, pour retourner un objet en sauvant ses attributs, on pourrait faire :
1 <?php
2 class Export
3 {
4 protected $chaine1 , $chaine2;
5
6 public function __construct($param1 , $param2)
7 {
8 $this ->chaine1 = $param1;
9 $this ->chaine2 = $param2;
10 }
11
12 public function __set_state($valeurs) // Liste des attributs
de l'objet en paramtre.
13 {
14 $obj = new Export($valeurs['chaine1 '], $valeurs['chaine2 '])
; // On cre un objet avec les attributs de l'objet que
l'on veut exporter.
15 return $obj; // on retourne l'objet cr.
16 }
17 }
18
19 $obj1 = new Export('Hello ', 'world !');
20
21 eval('$obj2 = ' . var_export ($obj1 , true) . ';'); // On cre
153
CHAPITRE 8. LES MTHODES MAGIQUES
un autre objet , celui -ci ayant les mmes attributs que l'
objet prcdent.
22
23 echo '<pre >', print_r ($obj2 , true), '</pre >';
24 ?>
Le code achera donc ceci (voir la gure 8.7).
Figure 8.7 Rsultat ach par le script
__invoke
Disponible depuis PHP 5.3
Que diriez-vous de pouvoir utiliser lobjet comme fonction ? Vous ne voyez pas ce que
je veux dire ? Je comprends.
Voici un code qui illustrera bien le tout :
1 <?php
2 $obj = new MaClasse;
3 $obj('Petit test'); // Utilisation de l'objet comme fonction.
4 ?>
Essayez ce code et. . . BAM! Une erreur fatale (cest bizarre !). Plus srieusement, pour
rsoudre ce problme, nous allons devoir utiliser la mthode magique __invoke. Elle est
appele ds quon essaye dutiliser lobjet comme fonction (comme on vient de faire).
Cette mthode comprend autant de paramtres que darguments passs la fonction.
Exemple :
1 <?php
2 class MaClasse
3 {
4 public function __invoke($argument)
154
AUTRES MTHODES MAGIQUES
5 {
6 echo $argument;
7 }
8 }
9
10 $obj = new MaClasse;
11
12 $obj(5); // Affiche 5 .
13 ?>
En rsum
Les mthodes magiques sont des mthodes qui sont appeles automatiquement lors-
quun certain vnement est dclench.
Toutes les mthodes magiques commencent par deux underscores, vitez donc dap-
peler vos mthodes suivant ce mme modle.
Les mthodes magiques dont vous vous servirez le plus souvent sont __construct,
__set, __get et __call. Les autres sont plus gadget et vous les rencontrerez
moins souvent.
155
CHAPITRE 8. LES MTHODES MAGIQUES
156
Deuxime partie
[Thorie] Techniques avances
157
Chapitre 9
Les objets en profondeur
Dicult :
Je suis sr quactuellement, vous pensez que lorsquon fait un $objet = new MaClasse;,
la variable $objet contient lobjet que lon vient de crer. Personne ne peut vous en vouloir
puisque personne ne vous a dit que ctait faux. Et bien je vous le dis maintenant : comme
nous le verrons dans ce chapitre, une telle variable ne contient pas lobjet proprement
parler ! Ceci explique ainsi quelques comportements bizarres que peut avoir PHP avec
les objets. Nous allons ainsi parler de la dernire mthode magique que je vous avais
volontairement cache.
Une fois tout ceci expliqu, nous jouerons un peu avec nos objets en les parcourant, peu
prs de la mme faon quavec des tableaux.
Comme vous le verrez, les objets rservent bien des surprises !
159
CHAPITRE 9. LES OBJETS EN PROFONDEUR
Un objet, un identiant
Je vais commencer cette partie en vous faisant une rvlation : quand vous instanciez
une classe, la variable stockant lobjet ne stocke en fait pas lobjet lui-mme, mais un
identiant qui reprsente cet objet. Cest--dire quen faisant $objet = new Classe;,
"$objet ne contient pas lobjet lui-mme, mais son identiant unique. Cest un peu
comme quand vous enregistrez des informations dans une BDD : la plupart du temps,
vous avez un champ "id" unique qui reprsente lentre. Quand vous faites une requte
SQL, vous slectionnez llment en fonction de son id. Et bien l, cest pareil : quand
vous accdez un attribut ou une mthode de lobjet, PHP regarde lidentiant
contenu dans la variable, va chercher lobjet correspondant et eectue le traitement
ncessaire. Il est trs important que vous compreniez cette ide, sinon vous allez tre
compltement perdus pour la suite du chapitre.
Nous avons donc vu que la variable $objet contenait lidentiant de lobjet quelle a
instanci. Vrions cela :
1 <?php
2 class MaClasse
3 {
4 public $attribut1;
5 public $attribut2;
6 }
7
8 $a = new MaClasse;
9
10 $b = $a; // On assigne $b l'identifiant de $a, donc $a et $b
reprsentent le mme objet.
11
12 $a->attribut1 = 'Hello';
13 echo $b->attribut1; // Affiche Hello.
14
15 $b->attribut2 = 'Salut';
16 echo $a->attribut2; // Affiche Salut.
17 ?>
Je commente plus en dtail la ligne 10 pour ceux qui sont un peu perdus. Nous avons dit
plus haut que $a ne contenait pas lobjet lui-mme mais son identiant (un identiant
dobjet). $a contient donc lidentiant reprsentant lobjet cr. Ensuite, on assigne
$b la valeur de $a. Donc quest-ce que $b vaut maintenant ? Et bien la mme chose
que $a, savoir lidentiant qui reprsente lobjet ! $a et "$b font donc rfrence
la mme instance.
Schmatiquement, on peut reprsenter le code ci-dessus comme ceci (voir la gure 9.1).
Comme vous le voyez sur limage, en ralit, il ny a quun seul objet, quun seul
identiant, mais deux variables contenant exactement le mme identiant dobjet. Tout
ceci peut sembler abstrait, donc allez votre rythme pour bien comprendre.
Maintenant que lon sait que ces variables ne contiennent pas dobjet mais un iden-
tiant dobjet, vous tes censs savoir que lorsquun objet est pass en paramtre
160
UN OBJET, UN IDENTIFIANT
Figure 9.1 Exemple de consquences des identiants dobjet
une fonction ou renvoy par une autre, on ne passe pas une copie de lobjet mais une
copie de son identiant ! Ainsi, vous ntes pas oblig de passer lobjet en rfrence, car
vous passerez une rfrence de lidentiant de lobjet. Inutile, donc.
Cependant un problme se pose. Comment faire pour copier un objet ? Comment faire
pour pouvoir copier tous ses attributs et valeurs dans un nouvel objet unique ? On
a vu quon ne pouvait pas faire un simple $objet1 = $objet2 pour arriver cela.
Comme vous vous en doutez peut-tre, cest l quintervient le clonage dobjet.
Pour cloner un objet, cest assez simple. Il faut utiliser le mot-cl clone juste avant
lobjet copier. Exemple :
1 <?php
2 $copie = clone $origine; // On copie le contenu de l'objet
$origine dans l'objet $copie.
3 ?>
Cest aussi simple que cela. Ainsi les deux objets contiennent des identiants dirents :
par consquent, si on veut modier lun deux, on peut le faire sans quaucune proprit
de lautre ne soit modie.
Il ntait pas question dune mthode magique ?
Si si, jy viens.
Lorsque vous clonez un objet, la mthode __clone de celui-ci sera appele (du moins,
si vous lavez dnie). Vous ne pouvez pas appeler cette mthode directement. Cest
la mthode __clone de lobjet cloner qui est appele, pas la mthode __clone du
nouvel objet cr.
Vous pouvez utiliser cette mthode pour modier certains attributs pour lancien objet,
161
CHAPITRE 9. LES OBJETS EN PROFONDEUR
ou alors incrmenter un compteur dinstances par exemple.
1 <?php
2 class MaClasse
3 {
4 private static $instances = 0;
5
6 public function __construct ()
7 {
8 self:: $instances ++;
9 }
10
11 public function __clone ()
12 {
13 self:: $instances ++;
14 }
15
16 public static function getInstances ()
17 {
18 return self:: $instances;
19 }
20 }
21
22 $a = new MaClasse;
23 $b = clone $a;
24
25 echo 'Nombre d\'instances de MaClasse : ', MaClasse ::
getInstances ();
26 ?>
Ce qui achera ceci (voir la gure 9.2).
Figure 9.2 Rsultat ach par le script
162
COMPARONS NOS OBJETS
Comparons nos objets
Nous allons maintenant voir comment comparer deux objets. Cest trs simple, il sut
de faire comme vous avez toujours fait en comparant des chanes de caractres ou des
nombres. Voici un exemple :
1 <?php
2 if ($objet1 == $objet2)
3 {
4 echo '$objet1 et $objet2 sont identiques !';
5 }
6 else
7 {
8 echo '$objet1 et $objet2 sont diffrents !';
9 }
10 ?>
Cette partie ne vous expliquera donc pas comment comparer des objets mais la d-
marche que PHP excute pour les comparer et les eets que ces comparaisons peuvent
produire.
Reprenons le code ci-dessus. Pour que la condition renvoie true, il faut que $objet1 et
$objet2 aient les mmes attributs et les mmes valeurs, mais galement que les deux
objets soient des instances de la mme classe. Cest--dire que mme sils ont les mmes
attributs et valeurs mais que lun est une instance de la classe A et lautre une instance
de la classe B, la condition renverra false.
Exemple :
1 <?php
2 class A
3 {
4 public $attribut1;
5 public $attribut2;
6 }
7
8 class B
9 {
10 public $attribut1;
11 public $attribut2;
12 }
13
14 $a = new A;
15 $a ->attribut1 = 'Hello';
16 $a ->attribut2 = 'Salut';
17
18 $b = new B;
19 $b ->attribut1 = 'Hello';
20 $b ->attribut2 = 'Salut';
21
22 $c = new A;
163
CHAPITRE 9. LES OBJETS EN PROFONDEUR
23 $c->attribut1 = 'Hello';
24 $c->attribut2 = 'Salut';
25
26 if ($a == $b)
27 {
28 echo '$a == $b';
29 }
30 else
31 {
32 echo '$a != $b';
33 }
34
35 echo '<br />';
36
37 if ($a == $c)
38 {
39 echo '$a == $c';
40 }
41 else
42 {
43 echo '$a != $c';
44 }
45 ?>
Si vous avez bien suivi, vous savez ce qui va sacher, savoir ceci (voir la gure 9.3).
Figure 9.3 Rsultat ach par le script
Comme on peut le voir, $a et $b ont beau avoir les mmes attributs et les mmes
valeurs, ils ne sont pas identiques car ils ne sont pas des instances de la mme classe.
Par contre, $a et $c sont bien identiques.
Parlons maintenant de loprateur === qui permet de vrier que deux objets sont
strictement identiques. Vous navez jamais entendu parler de cet oprateur ? Allez
lire ce tutoriel :

Cours sur loprateur ===


Code web : 218729
164
COMPARONS NOS OBJETS
Cet oprateur vriera si les deux objets font rfrence vers la mme instance. Il vriera
donc que les deux identiants dobjets compars sont les mmes. Allez relire la premire
partie de ce chapitre si vous tes un peu perdu.
Faisons quelques tests pour tre sr que vous avez bien compris :
1 <?php
2 class A
3 {
4 public $attribut1;
5 public $attribut2;
6 }
7
8 $a = new A;
9 $a ->attribut1 = 'Hello';
10 $a ->attribut2 = 'Salut';
11
12 $b = new A;
13 $b ->attribut1 = 'Hello';
14 $b ->attribut2 = 'Salut';
15
16 $c = $a;
17
18 if ($a === $b)
19 {
20 echo '$a === $b';
21 }
22 else
23 {
24 echo '$a !== $b';
25 }
26
27 echo '<br />';
28
29 if ($a === $c)
30 {
31 echo '$a === $c';
32 }
33 else
34 {
35 echo '$a !== $c';
36 }
37 ?>
Et lcran sachera ceci (voir la gure 9.4).
On voit donc que cette fois ci, la condition qui renvoyait true avec loprateur == renvoie
maintenant false. $a et $c font rfrence la mme instance, la condition renvoie donc
true.
165
CHAPITRE 9. LES OBJETS EN PROFONDEUR
Figure 9.4 Rsultat ach par le script
Parcourons nos objets
Finissons en douceur en voyant comment parcourir nos objets et en quoi cela consiste.
Le fait de parcourir un objet consiste lire tous les attributs visibles de lobjet.
Quest-ce que cela veut dire ? Ceci veut tout simplement dire que vous ne pourrez pas
lire les attributs privs ou protgs en dehors de la classe, mais linverse est tout
fait possible. Je ne vous apprends rien de nouveau me direz-vous, mais ce rappel me
semblait important pour vous expliquer le parcours dobjets.
Qui dit parcours dit boucle . Quelle boucle devrons-nous utiliser pour parcourir
un objet ? Et bien la mme boucle que pour parcourir un tableau. . . Jai nomm foreach !
Son utilisation est dune simplicit remarquable (du moins, si vous savez parcourir un
tableau). Sa syntaxe est la mme. Il y en a deux possibles :
foreach ($objet as $valeur) : $valeur sera la valeur de lattribut actuellement
lu.
foreach ($objet as $attribut => $valeur) : $attribut aura pour valeur le
nom de lattribut actuellement lu et $valeur sera sa valeur.
Vous ne devez sans doute pas tre dpays, il ny a presque rien de nouveau. Comme
je vous lai dit, la boucle foreach parcourt les attributs visibles. Faisons quelques tests.
Normalement, vous devez dj anticiper le bon rsultat (enn, jespre, mais si vous
tes tomb ct de la plaque ce nest pas un drame !).
1 <?php
2 class MaClasse
3 {
4 public $attribut1 = 'Premier attribut public ';
5 public $attribut2 = 'Deuxime attribut public ';
6
7 protected $attributProtege1 = 'Premier attribut protg';
8 protected $attributProtege2 = 'Deuxime attribut protg';
9
10 private $attributPrive1 = 'Premier attribut priv';
166
PARCOURONS NOS OBJETS
11 private $attributPrive2 = 'Deuxime attribut priv';
12
13 function listeAttributs ()
14 {
15 foreach ($this as $attribut => $valeur)
16 {
17 echo '<strong >', $attribut , '</strong > => ', $valeur , '<
br />';
18 }
19 }
20 }
21
22 class Enfant extends MaClasse
23 {
24 function listeAttributs () // Redclaration de la fonction
pour que ce ne soit pas celle de la classe mre qui soit
appele.
25 {
26 foreach ($this as $attribut => $valeur)
27 {
28 echo '<strong >', $attribut , '</strong > => ', $valeur , '<
br />';
29 }
30 }
31 }
32
33 $classe = new MaClasse;
34 $enfant = new Enfant;
35
36 echo '---- Liste les attributs depuis l\'intrieur de la classe
principale ----<br />';
37 $classe ->listeAttributs ();
38
39 echo '<br />---- Liste les attributs depuis l\'intrieur de la
classe enfant ----<br />';
40 $enfant ->listeAttributs ();
41
42 echo '<br />---- Liste les attributs depuis le script global
----<br />';
43
44 foreach ($classe as $attribut => $valeur)
45 {
46 echo '<strong >', $attribut , '</strong > => ', $valeur , '<br />
';
47 }
48 ?>
Ce qui achera :
- Liste les attributs depuis lintrieur de la classe principale -
167
CHAPITRE 9. LES OBJETS EN PROFONDEUR
attribut1 => Premier attribut public
attribut2 => Deuxime attribut public
attributProtege1 => Premier attribut protg
attributProtege2 => Deuxime attribut protg
attributPrive1 => Premier attribut priv
attributPrive2 => Deuxime attribut priv
- Liste les attributs depuis lintrieur de la classe enfant -
attribut1 => Premier attribut public
attribut2 => Deuxime attribut public
attributProtege1 => Premier attribut protg
attributProtege2 => Deuxime attribut protg
- Liste les attributs depuis le script global -
attribut1 => Premier attribut public
attribut2 => Deuxime attribut public
Jai volontairement termin ce chapitre par le parcours dobjets. Pourquoi ? Car dans le
prochain chapitre nous verrons comment modier le comportement de lobjet quand il
est parcouru grce aux interfaces ! Celles-ci permettent de raliser beaucoup de choses
pratiques, mais je ne vous en dis pas plus !
En rsum
Une variable ne contient jamais dobjet proprement parler, mais leurs identiants.
Pour dupliquer un objet, loprateur = na donc pas leet dsir : il faut cloner
lobjet grce loprateur clone.
Pour comparer deux objets, loprateur == vrie que les deux objets sont issus de
la mme classe et que les valeurs de chaque attribut sont identiques, tandis que
loprateur === vrie que les deux identiants dobjet sont les mmes.
Il est possible de parcourir un objet grce la structure foreach : ceci aura pour eet
de lister tous les attributs auxquels la structure a accs (par exemple, si la structure
est situe lextrieur de la classe, seuls les attributs publics seront lists).
168
Chapitre 10
Les interfaces
Dicult :
S
i , dans lune de vos mthodes, on vous passe un objet quelconque, il vous est impossible
de savoir si vous pouvez invoquer telle ou telle mthode sur ce dernier pour la simple
et bonne raison que vous ntes pas totalement sr que ces mthodes existent. En
eet, si vous ne connaissez pas la classe dont lobjet est linstance, vous ne pouvez pas
vrier lexistence de ces mthodes.
En PHP, il existe un moyen dimposer une structure nos classes, cest--dire dobliger
certaines classes implmenter certaines mthodes. Pour y arriver, nous allons nous servir
des interfaces. Une fois toute la thorie pose, nous allons nous servir dinterfaces pr-
dnies an de jouer un petit peu avec nos objets.
169
CHAPITRE 10. LES INTERFACES
Prsentation et cration dinterfaces
Le rle dune interface
Techniquement, une interface est une classe entirement abstraite. Son rle est de
dcrire un comportement notre objet. Les interfaces ne doivent pas tre confondues
avec lhritage : lhritage reprsente un sous-ensemble (exemple : un magicien est un
sous-ensemble dun personnage). Ainsi, une voiture et un personnage nont aucune
raison dhriter dune mme classe. Par contre, une voiture et un personnage peuvent
tous les deux se dplacer, donc une interface reprsentant ce point commun pourra tre
cre.
Crer une interface
Une interface se dclare avec le mot-cl interface, suivi du nom de linterface, suivi
dune paire daccolades. Cest entre ces accolades que vous listerez des mthodes. Par
exemple, voici une interface pouvant reprsenter le point commun voqu ci-dessus :
1 <?php
2 interface Movable
3 {
4 public function move($dest);
5 }
6 ?>
1. Toutes les mthodes prsentes dans une interface doivent tre publiques.
2. Une interface ne peut pas lister de mthodes abstraites ou nales.
3. Une interface ne peut pas avoir le mme nom quune classe et vice-versa.
Implmenter une interface
Cette interface tant toute seule, elle est un peu inutile. Il va donc falloir implmenter
linterface notre classe grce au mot-cl implements ! La dmarche excuter est
comme quand on faisait hriter une classe dune autre, savoir :
1 <?php
2 class Personnage implements Movable
3 {
4
5 }
6 ?>
Essayez ce code et. . . observez le rsultat (voir la gure 10.1).
Et oui, une erreur fatale est gnre car notre classe Personnage na pas implment la
mthode prsente dans linterface Movable. Pour que ce code ne gnre aucune erreur,
il faut quil y ait au minimum ce code :
170
PRSENTATION ET CRATION DINTERFACES
Figure 10.1 Rsultat ach par le script
1 <?php
2 class Personnage implements Movable
3 {
4 public function move($dest)
5 {
6
7 }
8 }
9 ?>
Et l. . . lerreur a disparu !
Vous pouvez trs bien, dans votre classe, dnir une mthode comme tant
abstraite ou nale.
Si vous hritez une classe et que vous implmentez une interface, alors vous devez
dabord spcier la classe hriter avec le mot-cl extends puis les interfaces im-
plmenter avec le mot-cl implements.
Une interface vous oblige crire toutes ses mthodes, mais vous pouvez en
rajouter autant que vous voulez.
Vous pouvez trs bien implmenter plus dune interface par classe, condition que
celles-ci naient aucune mthode portant le mme nom! Exemple :
1 <?php
2 interface iA
3 {
4 public function test1 ();
5 }
6
7 interface iB
171
CHAPITRE 10. LES INTERFACES
8 {
9 public function test2 ();
10 }
11
12 class A implements iA , iB
13 {
14 // Pour ne gnrer aucune erreur , il va falloir crire les m
thodes de iA et de iB.
15
16 public function test1 ()
17 {
18
19 }
20
21 public function test2 ()
22 {
23
24 }
25 }
26 ?>
Les constantes dinterfaces
Les constantes dinterfaces fonctionnent exactement comme les constantes de classes.
Elles ne peuvent tre crases par des classes qui implmentent linterface. Exemple :
1 <?php
2 interface iInterface
3 {
4 const MA_CONSTANTE = 'Hello !';
5 }
6
7 echo iInterface :: MA_CONSTANTE; // Affiche Hello !
8
9 class MaClasse implements iInterface
10 {
11
12 }
13
14 echo MaClasse :: MA_CONSTANTE; // Affiche Hello !
15 ?>
Hriter ses interfaces
Comme pour les classes, vous pouvez hriter vos interfaces grce loprateur extends.
Vous ne pouvez rcrire ni une mthode ni une constante qui a dj t liste dans
linterface parente. Exemple :
172
HRITER SES INTERFACES
1 <?php
2 interface iA
3 {
4 public function test1 ();
5 }
6
7 interface iB extends iA
8 {
9 public function test1 ($param1 , $param2); // Erreur fatale :
impossible de rcrire cette mthode.
10 }
11
12 interface iC extends iA
13 {
14 public function test2 ();
15 }
16
17 class MaClasse implements iC
18 {
19 // Pour ne gnrer aucune erreur , on doit crire les mthodes
de iC et aussi de iA.
20
21 public function test1 ()
22 {
23
24 }
25
26 public function test2 ()
27 {
28
29 }
30 }
31 ?>
Contrairement aux classes, les interfaces peuvent hriter de plusieurs interfaces la
fois. Il vous sut de sparer leur nom par une virgule. Exemple :
1 <?php
2 interface iA
3 {
4 public function test1 ();
5 }
6
7 interface iB
8 {
9 public function test2 ();
10 }
11
12 interface iC extends iA , iB
13 {
14 public function test3 ();
173
CHAPITRE 10. LES INTERFACES
15 }
16 ?>
Dans cet exemple, si on imagine une classe implmentant iC, celle-ci devra implmenter
les trois mthodes test1, test2 et test3.
Interfaces prdnies
Nous allons maintenant aborder les interfaces prdnies. Grce certaines, nous allons
pouvoir modier le comportement de nos objets ou raliser plusieurs choses pratiques.
Il y a beaucoup dinterfaces prdnies, je ne vous les prsenterai pas toutes, seulement
quatre dentre elles. Dj, avec celles-ci, nous allons pouvoir raliser de belles choses,
et puis vous tes libres de lire la documentation pour dcouvrir toutes les interfaces.
Nous allons essayer ici de crer un tableau-objet .
Linterface Iterator
Commenons dabord par linterface Iterator. Si votre classe implmente cette inter-
face, alors vous pourrez modier le comportement de votre objet lorsquil est parcouru.
Cette interface comporte 5 mthodes :
current : renvoie llment courant ;
key : retourne la cl de llment courant ;
next : dplace le pointeur sur llment suivant ;
rewind : remet le pointeur sur le premier lment ;
valid : vrie si la position courante est valide.
En crivant ces mthodes, on pourra renvoyer la valeur quon veut, et pas forcment la
valeur de lattribut actuellement lu. Imaginons quon ait un attribut qui soit un array.
On pourrait trs bien crer un petit script qui, au lieu de parcourir lobjet, parcourt le
tableau ! Je vous laisse essayer. Vous aurez besoin dun attribut $position qui stocke
la position actuelle.
Correction :
1 <?php
2 class MaClasse implements Iterator
3 {
4 private $position = 0;
5 private $tableau = array('Premier lment', 'Deuxime lment
', 'Troisime lment', 'Quatrime lment', 'Cinquime l
ment');
6
7 /**
8 * Retourne l'lment courant du tableau.
9 */
10 public function current ()
11 {
174
INTERFACES PRDFINIES
12 return $this ->tableau[$this ->position ];
13 }
14
15 /**
16 * Retourne la cl actuelle (c'est la mme que la position
dans notre cas).
17 */
18 public function key()
19 {
20 return $this ->position;
21 }
22
23 /**
24 * Dplace le curseur vers l'lment suivant.
25 */
26 public function next()
27 {
28 $this ->position ++;
29 }
30
31 /**
32 * Remet la position du curseur 0.
33 */
34 public function rewind ()
35 {
36 $this ->position = 0;
37 }
38
39 /**
40 * Permet de tester si la position actuelle est valide.
41 */
42 public function valid ()
43 {
44 return isset($this ->tableau[$this ->position ]);
45 }
46 }
47
48 $objet = new MaClasse;
49
50 foreach ($objet as $key => $value)
51 {
52 echo $key , ' => ', $value , '<br />';
53 }
54 ?>
Ce qui achera ceci (voir la gure 10.2).
Alors, pratique non?
175
CHAPITRE 10. LES INTERFACES
Figure 10.2 Rsultat ach par le script
Linterface SeekableIterator
Cette interface hrite de linterface Iterator, on naura donc pas besoin dimplmenter
les deux notre classe.
SeekableIterator ajoute une mthode la liste des mthodes dIterator : la mthode
seek. Cette mthode permet de placer le curseur interne une position prcise. Elle
demande donc un argument : la position du curseur laquelle il faut le placer. Je vous
dconseille de modier directement lattribut $position an dassigner directement la
valeur de largument $position. En eet, qui vous dit que la valeur de largument
est une position valide ?
Je vous laisse rchir quant limplmentation de cette mthode. Voici la correction
(jai repris la dernire classe) :
1 <?php
2 class MaClasse implements SeekableIterator
3 {
4 private $position = 0;
5 private $tableau = array('Premier lment', 'Deuxime lment
', 'Troisime lment', 'Quatrime lment', 'Cinquime l
ment');
6
7 /**
8 * Retourne l'lment courant du tableau.
9 */
10 public function current ()
11 {
12 return $this ->tableau[$this ->position ];
13 }
14
15 /**
16 * Retourne la cl actuelle (c'est la mme que la position
dans notre cas).
17 */
176
INTERFACES PRDFINIES
18 public function key()
19 {
20 return $this ->position;
21 }
22
23 /**
24 * Dplace le curseur vers l'lment suivant.
25 */
26 public function next()
27 {
28 $this ->position ++;
29 }
30
31 /**
32 * Remet la position du curseur 0.
33 */
34 public function rewind ()
35 {
36 $this ->position = 0;
37 }
38
39 /**
40 * Dplace le curseur interne.
41 */
42 public function seek($position)
43 {
44 $anciennePosition = $this ->position;
45 $this ->position = $position;
46
47 if (!$this ->valid())
48 {
49 trigger_error('La position spcifie n\'est pas valide ',
E_USER_WARNING);
50 $this ->position = $anciennePosition;
51 }
52 }
53
54 /**
55 * Permet de tester si la position actuelle est valide.
56 */
57 public function valid ()
58 {
59 return isset($this ->tableau[$this ->position ]);
60 }
61 }
62
63 $objet = new MaClasse;
64
65 foreach ($objet as $key => $value)
66 {
177
CHAPITRE 10. LES INTERFACES
67 echo $key , ' => ', $value , '<br />';
68 }
69
70 $objet ->seek(2);
71 echo '<br />', $objet ->current ();
72 ?>
Ce qui achera ceci (voir la gure 10.3).
Figure 10.3 Rsultat ach par le script
Linterface ArrayAccess
Nous allons enn, grce cette interface, pouvoir placer des crochets la suite de notre
objet avec la cl laquelle accder, comme sur un vrai tableau ! Linterface ArrayAccess
liste quatre mthodes :
offsetExists : mthode qui vriera lexistence de la cl entre crochets lorsque
lobjet est pass la fonction isset ou empty (cette valeur entre crochet est pass
la mthode en paramtre) ;
offsetGet : mthode appele lorsquon fait un simple $obj[cl]. La valeur cl
est donc passe la mthode offsetGet ;
offsetSet : mthode appele lorsquon assigne une valeur une entre. Cette m-
thode reoit donc deux arguments, la valeur de la cl et la valeur quon veut lui
assigner.
offsetUnset : mthode appele lorsquon appelle la fonction unset sur lobjet avec
une valeur entre crochets. Cette mthode reoit un argument, la valeur qui est mise
entre les crochets.
Maintenant, votre mission est dimplmenter cette interface et de grer lattribut $tableau
grce aux quatre mthodes. Cest parti !
Correction :
1 <?php
178
INTERFACES PRDFINIES
2 class MaClasse implements SeekableIterator , ArrayAccess
3 {
4 private $position = 0;
5 private $tableau = array('Premier lment', 'Deuxime lment
', 'Troisime lment', 'Quatrime lment', 'Cinquime l
ment');
6
7
8 /* MTHODES DE L'INTERFACE SeekableIterator */
9
10
11 /**
12 * Retourne l'lment courant du tableau.
13 */
14 public function current ()
15 {
16 return $this ->tableau[$this ->position ];
17 }
18
19 /**
20 * Retourne la cl actuelle (c'est la mme que la position
dans notre cas).
21 */
22 public function key()
23 {
24 return $this ->position;
25 }
26
27 /**
28 * Dplace le curseur vers l'lment suivant.
29 */
30 public function next()
31 {
32 $this ->position ++;
33 }
34
35 /**
36 * Remet la position du curseur 0.
37 */
38 public function rewind ()
39 {
40 $this ->position = 0;
41 }
42
43 /**
44 * Dplace le curseur interne.
45 */
46 public function seek($position)
47 {
48 $anciennePosition = $this ->position;
179
CHAPITRE 10. LES INTERFACES
49 $this ->position = $position;
50
51 if (!$this ->valid())
52 {
53 trigger_error('La position spcifie n\'est pas valide ',
E_USER_WARNING);
54 $this ->position = $anciennePosition;
55 }
56 }
57
58 /**
59 * Permet de tester si la position actuelle est valide.
60 */
61 public function valid ()
62 {
63 return isset($this ->tableau[$this ->position ]);
64 }
65
66
67 /* MTHODES DE L'INTERFACE ArrayAccess */
68
69
70 /**
71 * Vrifie si la cl existe.
72 */
73 public function offsetExists($key)
74 {
75 return isset($this ->tableau[$key]);
76 }
77
78 /**
79 * Retourne la valeur de la cl demande.
80 * Une notice sera mise si la cl n'existe pas , comme pour
les vrais tableaux.
81 */
82 public function offsetGet($key)
83 {
84 return $this ->tableau[$key];
85 }
86
87 /**
88 * Assigne une valeur une entre.
89 */
90 public function offsetSet($key , $value)
91 {
92 $this ->tableau[$key] = $value;
93 }
94
95 /**
180
INTERFACES PRDFINIES
96 * Supprime une entre et mettra une erreur si elle n'existe
pas , comme pour les vrais tableaux.
97 */
98 public function offsetUnset($key)
99 {
100 unset($this ->tableau[$key]);
101 }
102 }
103
104 $objet = new MaClasse;
105
106 echo 'Parcours de l\'objet...<br />';
107 foreach ($objet as $key => $value)
108 {
109 echo $key , ' => ', $value , '<br />';
110 }
111
112 echo '<br />Remise du curseur en troisime position ...<br />';
113 $objet ->seek(2);
114 echo 'lment courant : ', $objet ->current (), '<br />';
115
116 echo '<br />Affichage du troisime lment : ', $objet[2], '<br
/>';
117 echo 'Modification du troisime lment ... ';
118 $objet[2] = 'Hello world !';
119 echo 'Nouvelle valeur : ', $objet[2], '<br /><br />';
120
121 echo 'Destruction du quatrime lment...<br />';
122 unset($objet[3]);
123
124 if (isset($objet[3]))
125 {
126 echo '$objet[3] existe toujours ... Bizarre ...';
127 }
128 else
129 {
130 echo 'Tout se passe bien , $objet[3] n\'existe plus !';
131 }
132 ?>
Ce qui ache ceci (voir la gure 10.4).
Alors, on se rapproche vraiment du comportement dun tableau, nest-ce pas ? On peut
faire tout ce quon veut, comme sur un tableau ! Enn, il manque juste un petit quelque
chose pour que ce soit absolument parfait. . .
Linterface Countable
Et voici la dernire interface que je vous prsenterai. Elle contient une mthode : la
mthode count. Celle-ci doit obligatoirement renvoyer un entier qui sera la valeur
181
CHAPITRE 10. LES INTERFACES
Figure 10.4 Rsultat ach par le script
renvoye par la fonction count appele sur notre objet. Cette mthode nest pas bien
complique implmenter, il sut juste de retourner le nombre dentres de notre
tableau.
Correction :
1 <?php
2 class MaClasse implements SeekableIterator , ArrayAccess ,
Countable
3 {
4 private $position = 0;
5 private $tableau = array('Premier lment', 'Deuxime lment
', 'Troisime lment', 'Quatrime lment', 'Cinquime l
ment');
6
7
8 /* MTHODES DE L'INTERFACE SeekableIterator */
9
10
11 /**
12 * Retourne l'lment courant du tableau.
13 */
14 public function current ()
15 {
182
INTERFACES PRDFINIES
16 return $this ->tableau[$this ->position ];
17 }
18
19 /**
20 * Retourne la cl actuelle (c'est la mme que la position
dans notre cas).
21 */
22 public function key()
23 {
24 return $this ->position;
25 }
26
27 /**
28 * Dplace le curseur vers l'lment suivant.
29 */
30 public function next()
31 {
32 $this ->position ++;
33 }
34
35 /**
36 * Remet la position du curseur 0.
37 */
38 public function rewind ()
39 {
40 $this ->position = 0;
41 }
42
43 /**
44 * Dplace le curseur interne.
45 */
46 public function seek($position)
47 {
48 $anciennePosition = $this ->position;
49 $this ->position = $position;
50
51 if (!$this ->valid())
52 {
53 trigger_error('La position spcifie n\'est pas valide ',
E_USER_WARNING);
54 $this ->position = $anciennePosition;
55 }
56 }
57
58 /**
59 * Permet de tester si la position actuelle est valide.
60 */
61 public function valid ()
62 {
63 return isset($this ->tableau[$this ->position ]);
183
CHAPITRE 10. LES INTERFACES
64 }
65
66
67 /* MTHODES DE L'INTERFACE ArrayAccess */
68
69
70 /**
71 * Vrifie si la cl existe.
72 */
73 public function offsetExists($key)
74 {
75 return isset($this ->tableau[$key]);
76 }
77
78 /**
79 * Retourne la valeur de la cl demande.
80 * Une notice sera mise si la cl n'existe pas , comme pour
les vrais tableaux.
81 */
82 public function offsetGet($key)
83 {
84 return $this ->tableau[$key];
85 }
86
87 /**
88 * Assigne une valeur une entre.
89 */
90 public function offsetSet($key , $value)
91 {
92 $this ->tableau[$key] = $value;
93 }
94
95 /**
96 * Supprime une entre et mettra une erreur si elle n'existe
pas , comme pour les vrais tableaux.
97 */
98 public function offsetUnset($key)
99 {
100 unset($this ->tableau[$key]);
101 }
102
103
104 /* MTHODES DE L'INTERFACE Countable */
105
106
107 /**
108 * Retourne le nombre d'entres de notre tableau.
109 */
110 public function count ()
111 {
184
INTERFACES PRDFINIES
112 return count($this ->tableau);
113 }
114 }
115
116 $objet = new MaClasse;
117
118 echo 'Parcour de l\'objet...<br />';
119 foreach ($objet as $key => $value)
120 {
121 echo $key , ' => ', $value , '<br />';
122 }
123
124 echo '<br />Remise du curseur en troisime position ...<br />';
125 $objet ->seek(2);
126 echo 'lment courant : ', $objet ->current (), '<br />';
127
128 echo '<br />Affichage du troisime lment : ', $objet[2], '<br
/>';
129 echo 'Modification du troisime lment ... ';
130 $objet[2] = 'Hello world !';
131 echo 'Nouvelle valeur : ', $objet[2], '<br /><br />';
132
133 echo 'Actuellement , mon tableau comporte ', count($objet), '
entres<br /><br />';
134
135 echo 'Destruction du quatrime lment...<br />';
136 unset($objet[3]);
137
138 if (isset($objet[3]))
139 {
140 echo '$objet[3] existe toujours ... Bizarre ...';
141 }
142 else
143 {
144 echo 'Tout se passe bien , $objet[3] n\'existe plus !';
145 }
146
147 echo '<br /><br />Maintenant , il n\'en comporte plus que ',
count($objet), ' !';
148 ?>
Ce qui achera ceci (voir la gure 10.5).
Bonus : la classe ArrayIterator
Je dois vous avouer quelque chose : la classe que nous venons de crer pour pouvoir crer
des objets-tableaux existe dj. En eet, PHP possde nativement une classe nom-
me ArrayIterator. Comme notre prcdente classe, celle-ci implmente les quatre
interfaces quon a vues.
185
CHAPITRE 10. LES INTERFACES
Figure 10.5 Rsultat ach par le script
Mais pourquoi tu nous as fais faire tout a ? !
Pour vous faire pratiquer, tiens !
Sachez que rcrire des classes ou fonctions natives de PHP est un excellent exercice
(et cest valable pour tous les langages de programmation). Je ne vais pas mattarder
sur cette classe, tant donn quelle sutilise exactement comme la ntre. Elle possde
les mmes mthodes, une dirence prs : cette classe implmente un constructeur
qui accepte un tableau en guise dargument.
Comme vous laurez devin, cest ce tableau qui sera transform en objet. Ainsi, si
vous faites un echo $monInstanceArrayIterator[cle], alors lcran sachera
lentre qui a pour cl cle du tableau pass en paramtre.
En rsum
Une interface reprsente un comportement.
Les interfaces ne sont autre que des classes 100% abstraites.
Une interface sutilise dans une classe grce au mot-cl implements.
Il est possibles dhriter ses interfaces grce au mot-cl extends.
186
INTERFACES PRDFINIES
Il existe tout un panel dinterfaces pr-dnies vous permettant de raliser tout un
tas de fonctionnalits intressantes, comme la cration dobjets-tableaux .
187
CHAPITRE 10. LES INTERFACES
188
Chapitre 11
Les exceptions
Dicult :
A
ctuellement , vous connaissez les erreurs fatales, les alertes, les erreurs danalyse ou
encore les notices. Nous allons dcouvrir dans ce chapitre une faon dirente de grer
les erreurs. Nous allons, en quelque sorte, crer nos propres types derreurs. Les
exceptions sont des erreurs assez direntes qui ne fonctionnent pas de la mme manire.
Comme vous le verrez, cette nouvelle faon de grer ses erreurs est assez pratique. Par
exemple, vous pouvez attraper lerreur pour lacher comme vous voulez plutt que davoir
un fameux et plutt laid Warning.
Cela ne sarrtera pas l. Puisque la gestion derreurs est assez importante sur un site, je
ddie une partie de ce chapitre au moyen de grer ses erreurs facilement et proprement,
que ce soit les erreurs que vous connaissez dj ou les exceptions.
189
CHAPITRE 11. LES EXCEPTIONS
Une dirente gestion des erreurs
Les exceptions, comme nous lavons voqu dans lintroduction de ce chapitre, sont une
faon dirente de grer les erreurs. Celles-ci sont en fait des erreurs lances par PHP
lorsque quelque chose qui ne va pas est survenu. Nous allons commencer par lancer nos
propres exceptions. Pour cela, on va devoir sintresser la classe Exception.
Lancer une exception
Une exception peut tre lance depuis nimporte o dans le code. Quand on lance
une exception, on doit, en gros, lancer une instance de la classe Exception. Cet objet
lanc contiendra le message derreur ainsi que son code. Pensez spcier au moins
le message derreur, bien que celui-ci soit facultatif. Je ne vois pas lintrt de lancer
une exception sans spcier lerreur rencontre. Pour le code derreur, il nest pas (pour
linstant) trs utile. Libre vous de le spcier ou pas. Le troisime et dernier argument
est lexception prcdente. L aussi, spciez-l si vous le souhaitez, mais ce nest pas
indispensable.
Passons lacte. Nous allons crer une simple fonction qui aura pour rle dadditionner
un nombre avec un autre. Si lun des deux nombres nest pas numrique, alors on
lancera une exception de type Exception laide du mot throw (= lancer). On va donc
lancer une nouvelle Exception. Le constructeur de la classe Exception demande en
paramtre le message derreur, son code et lexception prcdente. Ces trois paramtres
sont facultatifs.
1 <?php
2 function additionner($a , $b)
3 {
4 if (! is_numeric($a) OR !is_numeric($b))
5 {
6 // On lance une nouvelle exception grce throw et on
instancie directement un objet de la classe Exception.
7 throw new Exception('Les deux paramtres doivent tre des
nombres ');
8 }
9
10 return $a + $b;
11 }
12
13 echo additionner(12, 3), '<br />';
14 echo additionner('azerty ', 54), '<br />';
15 echo additionner(4, 8);
16 ?>
Et l, vous avez ceci (voir la gure 11.1).
Et voil votre premire exception qui apparat, dune faon assez dsagrable, devant
vos yeux bahis. Dcortiquons ce que PHP veut nous dire.
190
UNE DIFFRENTE GESTION DES ERREURS
Figure 11.1 Rsultat ach par le script
Premirement, il gnre une erreur fatale. Et oui, une exception non attrape gnre
automatiquement une erreur fatale. Nous verrons plus tard ce que signie attraper .
Deuximement, il nous dit Uncaught exception Exception with message Les deux pa-
ramtres doivent tre des nombres ce qui signie Exception Exception non attrape
avec le message Les deux paramtres doivent tre des nombres . Ce passage se pas-
sera de commentaire, la traduction parle delle-mme : on na pas attrap lexception
Exception (= le nom de la classe instancie par lobjet qui a t lanc) avec tel message
(ici, cest le message spci dans le constructeur).
Et pour nir, PHP nous dit o a t lance lexception, depuis quelle fonction, quelle
ligne, etc.
Maintenant, puisque PHP na pas lair content que lon nait pas attrap cette
exception, et bien cest ce que nous allons faire.
Ne lancez jamais dexception dans un destructeur. Si vous faites une
telle chose, vous aurez une erreur fatale : Exception thrown without a stack
frame in Unknown on line 0. Cette erreur peut aussi tre lance dans un autre
cas voqu plus tard.
Attraper une exception
An dattraper une exception, il faut dabord quelle soit lance. Le problme, cest
quon ne peut pas dire PHP que toutes les exceptions lances doivent tre attrapes :
cest nous de lui dire que lon va essayer deectuer telle ou telle instruction et, si
une exception est lance, alors on attrapera celle-ci an quaucune erreur fatale ne
soit lance et que de tels messages ne sachent plus.
Nous allons ds prsent placer nos instructions dans un bloc try. Celles-ci seront
placer entre une paire daccolades. Qui dit bloc try dit aussi bloc catch car lun ne va
pas sans lautre (si vous mettez lun sans lautre, une erreur danalyse sera leve). Ce
191
CHAPITRE 11. LES EXCEPTIONS
bloc catch a une petite particularit. Au lieu de placer catch suivi directement des
deux accolades, nous allons devoir spcier, entre une paire de parenthses place entre
catch et laccolade ouvrante, le type dexception attraper suivi dune variable qui
reprsentera cette exception. Cest partir delle que lon pourra rcuprer le message
ou le code derreur grce aux mthodes de la classe.
Commenons en douceur en attrapant simplement toute exception Exception :
1 <?php
2 function additionner($a , $b)
3 {
4 if (! is_numeric($a) OR !is_numeric($b))
5 {
6 throw new Exception('Les deux paramtres doivent tre des
nombres '); // On lance une nouvelle exception si l'un
des deux paramtres n'est pas un nombre.
7 }
8
9 return $a + $b;
10 }
11
12 try // On va essayer d'effectuer les instructions situes dans
ce bloc.
13 {
14 echo additionner(12, 3), '<br />';
15 echo additionner('azerty ', 54), '<br />';
16 echo additionner(4, 8);
17 }
18
19 catch (Exception $e) // On va attraper les exceptions "
Exception" s'il y en a une qui est leve.
20 {
21
22 }
23 ?>
Et l, miracle, vous navez plus que 15 qui sache, et plus derreur ! Par contre,
les deux autres rsultats ne sont pas achs, et il serait intressant de savoir pour-
quoi. Nous allons acher le message derreur. Pour ce faire, il faut appeler la mthode
getMessage(). Si vous souhaitez rcuprer le code derreur, il faut appeler getCode().
1 <?php
2 function additionner($a , $b)
3 {
4 if (! is_numeric($a) OR !is_numeric($b))
5 {
6 throw new Exception('Les deux paramtres doivent tre des
nombres ');
7 }
8
9 return $a + $b;
192
UNE DIFFRENTE GESTION DES ERREURS
10 }
11
12 try // Nous allons essayer d'effectuer les instructions situes
dans ce bloc.
13 {
14 echo additionner(12 , 3), '<br />';
15 echo additionner('azerty ', 54), '<br />';
16 echo additionner(4, 8);
17 }
18
19 catch (Exception $e) // On va attraper les exceptions "
Exception" s'il y en a une qui est leve
20 {
21 echo 'Une exception a t lance. Message d\'erreur : ', $e->
getMessage ();
22 }
23 ?>
Ce qui achera ceci (voir la gure 11.2).
Figure 11.2 Rsultat ach par le script
Comme vous pouvez le constater, la troisime instruction du bloc try na pas t
excute. Cest normal puisque la deuxime instruction a interrompu la lecture du
bloc. Si vous interceptez les exceptions comme nous lavons fait, alors le script nest
pas interrompu. En voici la preuve :
1 <?php
2 function additionner($a, $b)
3 {
4 if (! is_numeric($a) OR !is_numeric($b))
5 {
6 throw new Exception('Les deux paramtres doivent tre des
nombres ');
7 }
8
193
CHAPITRE 11. LES EXCEPTIONS
9 return $a + $b;
10 }
11
12 try // Nous allons essayer d'effectuer les instructions situes
dans ce bloc.
13 {
14 echo additionner(12, 3), '<br />';
15 echo additionner('azerty ', 54), '<br />';
16 echo additionner(4, 8);
17 }
18
19 catch (Exception $e) // Nous allons attraper les exceptions "
Exception" s'il y en a une qui est leve.
20 {
21 echo 'Une exception a t lance. Message d\'erreur : ', $e->
getMessage ();
22 }
23
24 echo 'Fin du script '; // Ce message s'affiche , a prouve bien
que le script est excut jusqu'au bout.
25 ?>
Vous vous demandez sans doute pourquoi on doit spcier le nom de lexception
intercepter puisque cest toujours une instance de la classe Exception. En fait, la
classe Exception est la classe de base pour toute exception qui doit tre lance, ce qui
signie que lon peut lancer nimporte quelle autre instance dune classe, du moment
quelle hrite de la classe Exception !
Des exceptions spcialises
Hriter la classe Exception
PHP nous ore la possibilit dhriter la classe Exception an de personnaliser nos
exceptions. Par exemple, nous pouvons crer une classe MonException qui rcrira des
mthodes de la classe Exception ou en crera de nouvelles qui lui seront propres.
Avant de foncer tte baisse dans lcriture de notre classe personnalise, encore faut-il
savoir quelles mthodes et attributs seront disponibles. Voici la liste des attributs et
mthodes de la classe Exception tire de la documentation :

Documentation dException
Code web : 144505
1 <?php
2 class Exception
3 {
4 protected $message = 'exception inconnu '; // Message de l'
exception.
194
DES EXCEPTIONS SPCIALISES
5 protected $code = 0; // Code de l'exception dfini par l'
utilisateur.
6 protected $file; // Nom du fichier source de l'exception.
7 protected $line; // Ligne de la source de l'exception.
8
9 final function getMessage (); // Message de l'exception.
10 final function getCode (); // Code de l'exception.
11 final function getFile (); // Nom du fichier source.
12 final function getLine (); // Ligne du fichier source.
13 final function getTrace (); // Un tableau de backtrace ().
14 final function getTraceAsString (); // Chane formatte de
trace.
15
16 /* Remplacable */
17 function __construct ($message = NULL , $code = 0);
18 function __toString (); // Chane formate pour l'affichage.
19 }
20 ?>
Ainsi, nous voyons que lon a accs aux attributs protgs de la classe et quon peut
rcrire les mthodes __construct et __toString. Toutes les autres mthodes sont
nales, nous navons donc pas le droit de les rcrire.
Nous allons donc crer notre classe MonException qui, par exemple, rcrira le construc-
teur en rendant obligatoire le premier argument ainsi que la mthode __toString pour
nacher que le message derreur (cest uniquement a qui nous intresse).
1 <?php
2 class MonException extends Exception
3 {
4 public function __construct($message , $code = 0)
5 {
6 parent :: __construct($message , $code);
7 }
8
9 public function __toString ()
10 {
11 return $this ->message;
12 }
13 }
14 ?>
Maintenant, comme vous laurez peut-tre devin, nous nallons pas lancer dexception
Exception mais une exception de type MonException.
Dans notre script, nous allons dsormais attraper uniquement les exceptions MonException,
ce qui claircira le code car cest une manire de se dire que lon ne travaille, dans
le bloc try, quavec des instructions susceptibles de lancer des exceptions de type
MonException. Exemple :
1 <?php
2 class MonException extends Exception
195
CHAPITRE 11. LES EXCEPTIONS
3 {
4 public function __construct($message , $code = 0)
5 {
6 parent :: __construct($message , $code);
7 }
8
9 public function __toString ()
10 {
11 return $this ->message;
12 }
13 }
14
15 function additionner($a , $b)
16 {
17 if (! is_numeric($a) OR !is_numeric($b))
18 {
19 throw new MonException('Les deux paramtres doivent tre
des nombres '); // On lance une exception "MonException ".
20 }
21
22 return $a + $b;
23 }
24
25 try // Nous allons essayer d'effectuer les instructions situes
dans ce bloc.
26 {
27 echo additionner(12, 3), '<br />';
28 echo additionner('azerty ', 54), '<br />';
29 echo additionner(4, 8);
30 }
31
32 catch (MonException $e) // Nous allons attraper les exceptions
"MonException" s'il y en a une qui est leve.
33 {
34 echo $e; // On affiche le message d'erreur grce la mthode
__toString que l'on a crite.
35 }
36
37 echo '<br />Fin du script '; // Ce message s'affiche , a prouve
bien que le script est excut jusqu 'au bout.
38 ?>
Ainsi, nous avons attrap uniquement les exceptions de type MonException. Essayez de
lancer une exception Exception la place et vous verrez quelle ne sera pas attrape.
Si vous dcidez dattraper, dans le bloc catch, les exceptions Exception, alors toutes
les exceptions seront attrapes car elles hritent toutes de cette classe. En fait, quand
vous hritez une classe dune autre et que vous dcidez dattraper les exceptions de la
classe parente, alors celles de la classe enfant le seront aussi.
196
DES EXCEPTIONS SPCIALISES
Emboter plusieurs blocs catch
Il est possible demboter plusieurs blocs catch. En eet, vous pouvez mettre un pre-
mier bloc attrapant les exceptions MonException suivi dun deuxime attrapant les
exceptions Exception. Si vous eectuez une telle opration et quune exception est
lance, alors PHP ira dans le premier bloc pour voir si ce type dexception doit tre
attrap, si tel nest pas le cas il va dans le deuxime, etc., jusqu ce quil tombe sur
un bloc qui lattrape. Si aucun ne lattrape, alors une erreur fatale est leve. Exemple :
1 <?php
2 class MonException extends Exception
3 {
4 public function __construct($message , $code = 0)
5 {
6 parent :: __construct($message , $code);
7 }
8
9 public function __toString ()
10 {
11 return $this ->message;
12 }
13 }
14
15 function additionner($a, $b)
16 {
17 if (! is_numeric($a) OR !is_numeric($b))
18 {
19 throw new MonException('Les deux paramtres doivent tre
des nombres '); // On lance une exception "MonException ".
20 }
21
22 if (func_num_args () > 2)
23 {
24 throw new Exception('Pas plus de deux arguments ne doivent
tre passs la fonction '); // Cette fois -ci , on lance
une exception "Exception ".
25 }
26
27 return $a + $b;
28 }
29
30 try // Nous allons essayer d'effectuer les instructions situes
dans ce bloc.
31 {
32 echo additionner(12 , 3), '<br />';
33 echo additionner(15 , 54, 45), '<br />';
34 }
35
36 catch (MonException $e) // Nous allons attraper les exceptions
"MonException" s'il y en a une qui est leve.
197
CHAPITRE 11. LES EXCEPTIONS
37 {
38 echo '[MonException] : ', $e; // On affiche le message d'
erreur grce la mthode __toString que l'on a crite.
39 }
40
41 catch (Exception $e) // Si l'exception n'est toujours pas
attrape, alors nous allons essayer d'attraper l'exception "
Exception ".
42 {
43 echo '[Exception] : ', $e->getMessage (); // La mthode
__toString () nous affiche trop d'informations , nous
voulons juste le message d'erreur.
44 }
45
46 echo '<br />Fin du script '; // Ce message s'affiche , cela
prouve bien que le script est excut jusqu'au bout.
47 ?>
Cette fois-ci, aucune exception MonException nest lance, mais une exception Exception
la t. PHP va donc eectuer les oprations demandes dans le deuxime bloc catch.
Exemple concret : la classe PDOException
Vous connaissez sans doute la bibliothque PDO? Pour rappel, voici le tutoriel qui en
traite :

Tutoriel PDO
Code web : 709358
Dans ce tutoriel, il vous est donn une technique pour capturer les erreurs gnres par
PDO : comme vous le savez maintenant, ce sont des exceptions ! Et PDO a sa classe
dexception : PDOException. Celle-ci nhrite pas directement de la classe Exception
mais de RuntimeException. Cette classe na rien de plus que sa classe mre, il sagit
juste dune classe qui est instancie pour mettre une exception lors de lexcution
du script. Il existe une classe pour chaque circonstance dans laquelle lexception est
lance : vous trouverez la liste des exceptions ici :

Liste des exceptions


Code web : 606457
Cette classe PDOException est donc la classe personnalise pour mettre une exception
par la classe PDO ou PDOStatement. Sachez dailleurs que si une extension oriente
objet doit mettre une erreur, elle mettra une exception.
Bref, voici un exemple dutilisation de PDOException :
1 <?php
2 try
3 {
4 $db = new PDO('mysql:host=localhost;dbname=tests ', 'root', ''
); // Tentative de connexion.
198
DES EXCEPTIONS SPCIALISES
5 echo 'Connexion russie !'; // Si la connexion a russi ,
alors cette instruction sera excute.
6 }
7
8 catch (PDOException $e) // On attrape les exceptions
PDOException.
9 {
10 echo 'La connexion a chou.<br />';
11 echo 'Informations : [', $e->getCode (), '] ', $e->getMessage
(); // On affiche le n de l'erreur ainsi que le message.
12 }
13 ?>
Exceptions pr-dnies
Il existe toute une quantit dexceptions pr-dnies. Vous pouvez obtenir cette liste
sur la documentation. Au lieu de lancer tout le temps une exception en instanciant
Exception, il est prfrable dinstancier la classe adapte la situation. Par exemple,
reprenons le code propos en dbut de chapitre :
1 <?php
2 function additionner($a, $b)
3 {
4 if (! is_numeric($a) OR !is_numeric($b))
5 {
6 // On lance une nouvelle exception grce throw et on
instancie directement un objet de la classe Exception.
7 throw new Exception('Les deux paramtres doivent tre des
nombres ');
8 }
9
10 return $a + $b;
11 }
12
13 echo additionner(12 , 3), '<br />';
14 echo additionner('azerty ', 54), '<br />';
15 echo additionner(4, 8);
La classe instancier ici est celle qui doit ltre lorsquun paramtre est invalide.
On regarde la documentation, et on tombe sur InvalidArgumentException. Le code
donnerait donc :
1 <?php
2 function additionner($a, $b)
3 {
4 if (! is_numeric($a) OR !is_numeric($b))
5 {
6 throw new InvalidArgumentException('Les deux paramtres
doivent tre des nombres ');
199
CHAPITRE 11. LES EXCEPTIONS
7 }
8
9 return $a + $b;
10 }
11
12 echo additionner(12, 3), '<br />';
13 echo additionner('azerty ', 54), '<br />';
14 echo additionner(4, 8);
Cela permet de mieux se reprer dans le code et surtout de mieux cibler les erreurs
grce aux multiples blocs catch.
Grer les erreurs facilement
Convertir les erreurs en exceptions
Nous allons voir une dernire chose avant de passer au prochain chapitre : la manire de
convertir les erreurs fatales, alertes et notices en exceptions. Pour cela, nous allons avoir
besoin de la fonction set_error_handler. Celle-ci permet denregistrer une fonction
en callback qui sera appele chaque fois que lune de ces trois erreurs sera lance. Il
ny a pas de rapport direct avec les exceptions : cest nous de ltablir.
Notre fonction, que lon nommera error2exception par exemple, doit demander entre
deux et cinq paramtres :
Le numro de lerreur (obligatoire).
Le message derreur (obligatoire).
Le nom du chier dans lequel lerreur a t lance.
Le numro de la ligne laquelle lerreur a t identie.
Un tableau avec toutes les variables qui existaient jusqu ce que lerreur soit ren-
contre.
Nous nallons pas prter attention au dernier paramtre, juste aux quatre premiers.
Nous allons crer notre propre classe MonException qui hrite non pas de Exception
mais de ErrorException. Bien sr, comme je lai dj dit plus haut, toutes les excep-
tions hritent de la classe Exception : ErrorException nchappe pas la rgle et
hrite de celle-ci.
La fonction set_error_handler demande deux paramtres. Le premier est la fonction
appeler, et le deuxime, les erreurs intercepter. Par dfaut, ce paramtre intercepte
toutes les erreurs, y compris les erreurs strictes.
Le constructeur de la classe ErrorException demande cinq paramtres, tous faculta-
tifs :
Le message derreur.
Le code de lerreur.
La svrit de lerreur (erreur fatale, alerte, notice, etc.) reprsentes par des constantes
pr-dnies.
200
GRER LES ERREURS FACILEMENT
Le chier o lerreur a t rencontre.
La ligne laquelle lerreur a t rencontre.
Voici quoi pourrait ressembler le code de base :
1 <?php
2 class MonException extends ErrorException
3 {
4 public function __toString ()
5 {
6 switch ($this ->severity)
7 {
8 case E_USER_ERROR : // Si l'utilisateur met une erreur
fatale;
9 $type = 'Erreur fatale ';
10 break;
11
12 case E_WARNING : // Si PHP met une alerte.
13 case E_USER_WARNING : // Si l'utilisateur met une alerte
.
14 $type = 'Attention ';
15 break;
16
17 case E_NOTICE : // Si PHP met une notice.
18 case E_USER_NOTICE : // Si l'utilisateur met une notice.
19 $type = 'Note';
20 break;
21
22 default : // Erreur inconnue.
23 $type = 'Erreur inconnue ';
24 break;
25 }
26
27 return '<strong >' . $type . '</strong > : [' . $this ->code .
'] ' . $this ->message . '<br /><strong >' . $this ->file
. '</strong > la ligne <strong >' . $this ->line . '</
strong >';
28 }
29 }
30
31 function error2exception($code , $message , $fichier , $ligne)
32 {
33 // Le code fait office de svrit.
34 // Reportez -vous aux constantes prdfinies pour en savoir
plus.
35 // http ://fr2.php.net/manual/fr/errorfunc.constants.php
36 throw new MonException($message , 0, $code , $fichier , $ligne);
37 }
38
39 set_error_handler('error2exception ');
40 ?>
201
CHAPITRE 11. LES EXCEPTIONS
Dans la mthode __toString, je mettais chaque fois E_X et E_USER_X. Les er-
reurs du type E_X sont gnres par PHP et les erreurs E_USER_X sont gnres par
lutilisateur grce trigger_error. Les erreurs E_ERROR (donc les erreurs fatales
gnres par PHP) ne peuvent tre interceptes, cest la raison pour laquelle je ne lai
pas plac dans le switch.
Ensuite, vous de faire des tests, vous verrez bien que a fonctionne merveille. Mais
gardez bien a en tte : avec ce code, toutes les erreurs (mme les notices) qui ne sont
pas dans un bloc try interrompront le script car elles mettront une exception!
On aurait trs bien pu utiliser la classe Exception mais ErrorException a t conu
exactement pour ce genre de chose. Nous navons pas besoin de crer dattribut stockant
la svrit de lerreur ou de rcrire le constructeur pour y stocker le nom du chier et
la ligne laquelle sest produite lerreur.
Personnaliser les exceptions non attrapes
Nous avons russi transformer toutes nos erreurs en exceptions en les interceptant
grce set_error_handler. tant donn que la moindre erreur lvera une excep-
tion, il serait intressant de personnaliser lerreur gnre par PHP. Ce que je veux
dire par l, cest quune exception non attrape gnre une longue et laide erreur fa-
tale. Nous allons donc, comme pour les erreurs, intercepter les exceptions grce
set_exception_handler. Cette fonction demande un seul argument : le nom de la
fonction appeler lorsquune exception est lance. La fonction de callback doit accep-
ter un argument : cest un objet reprsentant lexception.
Voici un exemple dutilisation en reprenant le prcdent code :
1 <?php
2 class MonException extends ErrorException
3 {
4 public function __toString ()
5 {
6 switch ($this ->severity)
7 {
8 case E_USER_ERROR : // Si l'utilisateur met une erreur
fatale.
9 $type = 'Erreur fatale ';
10 break;
11
12 case E_WARNING : // Si PHP met une alerte.
13 case E_USER_WARNING : // Si l'utilisateur met une alerte
.
14 $type = 'Attention ';
15 break;
16
17 case E_NOTICE : // Si PHP met une notice.
18 case E_USER_NOTICE : // Si l'utilisateur met une notice.
19 $type = 'Note';
20 break;
202
GRER LES ERREURS FACILEMENT
21
22 default : // Erreur inconnue.
23 $type = 'Erreur inconnue ';
24 break;
25 }
26
27 return '<strong >' . $type . '</strong > : [' . $this ->code .
'] ' . $this ->message . '<br /><strong >' . $this ->file
. '</strong > la ligne <strong >' . $this ->line . '</
strong >';
28 }
29 }
30
31 function error2exception($code , $message , $fichier , $ligne)
32 {
33 // Le code fait office de svrit.
34 // Reportez -vous aux constantes prdfinies pour en savoir
plus.
35 // http ://fr2.php.net/manual/fr/errorfunc.constants.php
36 throw new MonException($message , 0, $code , $fichier , $ligne);
37 }
38
39 function customException($e)
40 {
41 echo 'Ligne ', $e->getLine (), ' dans ', $e->getFile (), '<br
/><strong >Exception lance</strong > : ', $e->getMessage ();
42 }
43
44 set_error_handler('error2exception ');
45 set_exception_handler('customException ');
46 ?>
Je dis bien que lexception est intercepte et non attrape ! Cela signie que
lon attrape lexception, quon eectue des oprations puis quon la relche. Le
script, une fois customException appel, est automatiquement interrompu.
Ne lancez jamais dexception dans votre gestionnaire dexception (ici
customException). En eet, cela crerait une boucle innie puisque votre
gestionnaire lance lui-mme une exception. Lerreur lance est la mme que
celle vue prcdemment : il sagit de lerreur fatale Exception thrown wi-
thout a stack frame in Unknown on line 0 .
En rsum
Une exception est une erreur que lon peut attraper grce aux mots-cl try et catch.
Une exception est une erreur que lon peut personnaliser, que ce soit au niveau de
203
CHAPITRE 11. LES EXCEPTIONS
son achage ou au niveau de ses renseignements (chier concern par lerreur, le
numro de la ligne, etc.).
Une exception se lance grce au mot-cl throw.
Il est possible dhriter des exceptions entre elles.
Il existe un certain nombre dexceptions dj disponibles dont il ne faut pas hsiter
se servir pour respecter la logique du code.
204
Chapitre 12
Les traits
Dicult :
D
epuis sa version 5.4, PHP intgre un moyen de rutiliser le code dune mthode dans
deux classes indpendantes. Cette fonctionnalit permet ainsi de repousser les limites
de lhritage simple (pour rappel, en PHP, une classe ne peut hriter que dune et
une seule classe mre). Nous allons donc nous pencher ici sur les traits an de pallier le
problme de duplication de mthode.
205
CHAPITRE 12. LES TRAITS
Le principe des traits
Pour suivre ce chapitre, il vous faut PHP 5.4 dinstall sur votre serveur. Si vous tournez
sous Ubuntu avec un serveur LAMP et que les dpts ociels ne sont pas encore mis
jour, je vous invite visiter cette page :

Installer PHP 5.4 sous


Ubuntu
Code web : 879751
Posons le problme
Admettons que vous ayez deux classes, Writer et Mailer. La premire est charge
dcrire du texte dans un chier, tandis que la seconde envoie un texte par mail. Ce-
pendant, il est agrable de mettre en forme le texte. Pour cela, vous dcidez de forma-
ter le texte en HTML. Or, un problme se pose : vous allez devoir eectuer la mme
opration (celle de formater en HTML) dans deux classes compltement direntes et
indpendantes :
1 <?php
2 class Writer
3 {
4 public function write($text)
5 {
6 $text = '<p>Date : '.date('d/m/Y').'</p>'."\n".
7 '<p>'.nl2br($text).'</p>';
8 file_put_contents('fichier.txt', $text);
9 }
10 }
1 <?php
2 class Mailer
3 {
4 public function send($text)
5 {
6 $text = '<p>Date : '.date('d/m/Y').'</p>'."\n".
7 '<p>'.nl2br($text).'</p>';
8 mail('login@fai.tld', 'Test avec les traits ', $text);
9 }
10 }
Ici, le code est petit et la duplication nest donc pas norme, mais elle est belle et
bien prsente. Dans une application de plus grande envergure, ce code pourrait tre
dupliqu pas mal de fois. Imaginez alors que vous dcidiez de formater autrement votre
texte : catastrophe, il va falloir modier chaque partie du code qui formatait du texte !
Penchons-nous donc vers les traits pour rsoudre ce problme.
206
LE PRINCIPE DES TRAITS
Rsoudre le problme grce aux traits
Syntaxe de base
Comme vous vous en doutez peut-tre, les traits sont un moyen dexternaliser du
code. Plus prcisment, les traits dnissent des mthodes que les classes peuvent uti-
liser. Avant de rsoudre le problme voqu, nous allons nous pencher sur la syntaxe
des traits pour pouvoir sen servir.
Regardez ce code :
1 <?php
2 trait MonTrait
3 {
4 public function hello ()
5 {
6 echo 'Hello world !';
7 }
8 }
9
10 class A
11 {
12 use MonTrait;
13 }
14
15 class B
16 {
17 use MonTrait;
18 }
19
20 $a = new A;
21 $a ->hello(); // Affiche Hello world ! .
22
23 $b = new b;
24 $b ->hello(); // Affiche aussi Hello world ! .
Commentons ce code. Dans un premier temps, nous dnissons un trait. Un trait,
comme vous pouvez le constater, nest autre quune mini-classe. Dedans, nous navons
dclar quune seule mthode. Ensuite, nous dclarons deux classes, chacune utilisant le
trait que nous avons cr. Comme vous pouvez le constater, lutilisation dun trait dans
une classe se fait grce au mot-cl use. En utilisant ce mot-cl, toutes les mthodes du
trait vont tre importes dans la classe. Comme en tmoigne le code de test en n
de chier, les deux classes possdent bien une mthode hello() et celle-ci ache bien
Hello world ! .
Retour sur notre formateur
Ce que lon va faire maintenant, cest retirer le gros dfaut de notre code : grce aux
traits, nous ferons disparatre la duplication de notre code. Ce que je vous propose
207
CHAPITRE 12. LES TRAITS
de faire, cest de crer un trait qui va contenir une mthode format($text) qui va
formater le texte pass en argument en HTML.
Procdons tape par tape. Commenons par crer notre trait (il ne contient quune
mthode charge de formater le texte en HTML) :
1 <?php
2 trait HTMLFormater
3 {
4 public function format($text)
5 {
6 return '<p>Date : '.date('d/m/Y').'</p>'."\n".
7 '<p>'.nl2br($text).'</p>';
8 }
9 }
Maintenant, nous allons modier nos classes Writer et Mailer an quelles utilisent ce
trait pour formater le texte quelles exploiteront. Pour y arriver, je vous rappelle quil
vous faut utiliser le mot-cl use pour importer toutes les mthodes du trait (ici, il
ny en a quune) dans la classe. Vous pourrez ainsi utiliser les mthodes comme si elles
taient dclares dans la classe. Voici la correction :
1 <?php
2 class Writer
3 {
4 use HTMLFormater;
5
6 public function write($text)
7 {
8 file_put_contents('fichier.txt', $this ->format($text));
9 }
10 }
1 <?php
2 class Mailer
3 {
4 use HTMLFormater;
5
6 public function send($text)
7 {
8 mail('login@fai.tld', 'Test avec les traits ', $this ->format
($text));
9 }
10 }
Libre vous de tester vos classes ! Par exemple, essayez dexcuter ce code :
1 <?php
2 $w = new Writer;
3 $w->write('Hello world!');
4
5 $m = new Mailer;
208
LE PRINCIPE DES TRAITS
6 $m ->send('Hello world!');
Nous venons ici de supprimer la duplication de code anciennement prsente. Cependant,
les traits ne se rsument pas qu a ! Regardons par exemple comment utiliser plusieurs
traits dans une classe.
Utiliser plusieurs traits
Syntaxe
Pour utiliser plusieurs traits, rien de plus simple : il vous sut de lister tous les traits
utiliser spars par des virgules, comme ceci :
1 <?php
2 trait HTMLFormater
3 {
4 public function formatHTML($text)
5 {
6 return '<p>Date : '.date('d/m/Y').'</p>'."\n".
7 '<p>'.nl2br($text).'</p>';
8 }
9 }
10
11 trait TextFormater
12 {
13 public function formatText($text)
14 {
15 return 'Date : '.date('d/m/Y')."\n".$text;
16 }
17 }
18
19 class Writer
20 {
21 use HTMLFormater , TextFormater;
22
23 public function write($text)
24 {
25 file_put_contents('fichier.txt', $this ->formatHTML($text));
26 }
27 }
Je vous laisse essayer ce code, je suis sr que vous y arriverez seuls !
Rsolution des conits
Le code donn plus haut est bien beau, mais que se passerait-il si nos traits avaient tous
les deux une mthode nomme format() ? Je vous laisse essayer. . . Et oui, une erreur
fatale avec le message Trait method format has not been applied, because there are
209
CHAPITRE 12. LES TRAITS
collisions with other trait methods est leve. Pour pallier ce problme, nous pouvons
donner une priorit une mthode dun trait an de lui permettre dcraser la mthode
de lautre trait si il y en une identique.
Par exemple, si dans notre classe Writer nous voulions formater notre message en
HTML, nous pourrions faire :
1 <?php
2 class Writer
3 {
4 use HTMLFormater , TextFormater
5 {
6 HTMLFormater :: format insteadof\index{insteadof@\texttt{
insteadof }} TextFormater;
7 }
8
9 public function write($text)
10 {
11 file_put_contents('fichier.txt', $this ->format($text));
12 }
13 }
Regardons de plus prs cette ligne n6. Pour commencer, notez quelle est dnie
dans une paire daccolades suivant les noms des traits utiliser. lintrieur de cette
paire daccolades se trouve la liste des mthodes prioritaires . Chaque dclaration
de priorit se fait en se terminant par un point-virgule. Cette ligne signie donc : La
mthode format() du trait HTMLFormater crasera la mthode du mme nom du trait
TextFormater (si elle y est dnie).
Mthodes de traits vs. mthodes de classes
La classe plus forte que le trait
Terminons cette introduction aux traits en nous penchant sur les conits entre m-
thodes de traits et mthodes de classes. Si une classe dclare une mthode et quelle
utilise un trait possdant cette mme mthode, alors la mthode dclare dans la classe
lemportera sur la mthode dclare dans le trait. Exemple :
1 <?php
2 trait MonTrait
3 {
4 public function sayHello ()
5 {
6 echo 'Hello !';
7 }
8 }
9
10 class MaClasse
11 {
12 use MonTrait;
210
LE PRINCIPE DES TRAITS
13
14 public function sayHello ()
15 {
16 echo 'Bonjour !';
17 }
18 }
19
20 $objet = new MaClasse;
21 $objet ->sayHello (); // Affiche Bonjour ! .
Le trait plus fort que la mre
linverse, si une classe utilise un trait possdant une mthode dj implmente dans
la classe mre de la classe utilisant le trait, alors ce sera la mthode du trait qui sera
utilise (la mthode du trait crasera celle de la mthode de la classe mre). Exemple :
1 <?php
2 trait MonTrait
3 {
4 public function speak ()
5 {
6 echo 'Je suis un trait !';
7 }
8 }
9
10 class Mere
11 {
12 public function speak ()
13 {
14 echo 'Je suis une classe mre !';
15 }
16 }
17
18 class Fille extends Mere
19 {
20 use MonTrait;
21 }
22
23 $fille = new Fille;
24 $fille ->speak ();
211
CHAPITRE 12. LES TRAITS
Plus loin avec les traits
Dnition dattributs
Syntaxe
Nous avons vu que les traits servaient isoler des mthodes an de pouvoir les utiliser
dans deux classes totalement indpendantes. Si le besoin sen fait sentir, sachez que
vous pouvez aussi dnir des attributs dans votre trait. Ils seront alors leur tour
imports dans la classe qui utilisera ce trait. Exemple :
1 <?php
2 trait MonTrait
3 {
4 protected $attr = 'Hello !';
5
6 public function showAttr ()
7 {
8 echo $this ->attr;
9 }
10 }
11
12 class MaClasse
13 {
14 use MonTrait;
15 }
16
17 $fille = new MaClasse;
18 $fille ->showAttr ();
linverse dune mthode, un attribut ne peut pas tre static !
Conit entre attributs
Si un attribut est dni dans un trait, alors la classe utilisant le trait ne peut pas
dnir dattribut possdant le mme nom. Suivant la dclaration de lattribut, deux
cas peuvent se prsenter :
Si lattribut dclar dans la classe a le mme nom mais pas la mme valeur initiale
ou pas la mme visibilit, une erreur fatale est leve.
Si lattribut dclar dans la classe a le mme nom, une valeur initiale identique et la
mme visibilit, une erreur stricte est leve (il est possible, suivant votre congura-
tion, que PHP nache pas ce genre derreur).
Malheureusement, il est impossible, comme nous lavons fait avec les mthodes, de
dnir des attributs prioritaires. Veillez donc bien ne pas utiliser ce genre de code :
212
PLUS LOIN AVEC LES TRAITS
1 <?php
2 trait MonTrait
3 {
4 protected $attr = 'Hello !';
5 }
6
7 class MaClasse
8 {
9 use MonTrait;
10
11 protected $attr = 'Hello !'; // Lvera une erreur stricte.
12 protected $attr = 'Bonjour !'; // Lvera une erreur fatale.
13 private $attr = 'Hello !'; // Lvera une erreur fatale.
14 }
Traits composs dautres traits
Au mme titre que les classes, les traits peuvent eux aussi utiliser des traits. La faon
de procder est la mme quavec les classes, tout comme la gestion des conits entre
mthodes. Voici un exemple :
1 <?php
2 trait A
3 {
4 public function saySomething ()
5 {
6 echo 'Je suis le trait A !';
7 }
8 }
9
10 trait B
11 {
12 use A;
13
14 public function saySomethingElse ()
15 {
16 echo 'Je suis le trait B !';
17 }
18 }
19
20 class MaClasse
21 {
22 use B;
23 }
24
25 $o = new MaClasse;
26 $o ->saySomething ();
27 $o ->saySomethingElse ();
213
CHAPITRE 12. LES TRAITS
Changer la visibilit et le nom des mthodes
Si un trait implmente une mthode, toute classe utilisant ce trait a la capacit de
changer sa visibilit, cest--dire la passer en priv, protg ou public. Pour cela, nous
allons nouveau se servir des accolades qui ont suivi la dclaration de use pour y
glisser une instruction. Cette instruction fait appel loprateur as, que vous avez dj
peut-tre rencontr dans lutilisation de namespaces (si ce nest pas le cas, ce nest pas
grave du tout). Le rle est ici le mme : crer un alias. En eet, vous pouvez aussi
changer le nom des mthodes. Dans ce dernier cas, la mthode ne sera pas renomme,
mais copie sous un autre nom, ce qui signie que vous pourrez toujours y accder sous
son ancien nom.
Quelques petits exemples pour que vous compreniez :
1 <?php
2 trait A
3 {
4 public function saySomething ()
5 {
6 echo 'Je suis le trait A !';
7 }
8 }
9
10 class MaClasse
11 {
12 use A
13 {
14 saySomething as protected;
15 }
16 }
17
18 $o = new MaClasse;
19 $o->saySomething (); // Lvera une erreur fatale car on tente d'
accder une mthode protge.
1 <?php
2 trait A
3 {
4 public function saySomething ()
5 {
6 echo 'Je suis le trait A !';
7 }
8 }
9
10 class MaClasse
11 {
12 use A
13 {
14 saySomething as sayWhoYouAre;
15 }
16 }
214
PLUS LOIN AVEC LES TRAITS
17
18 $o = new MaClasse;
19 $o ->sayWhoYouAre (); // Affichera Je suis le trait A !
20 $o ->saySomething (); // Affichera Je suis le trait A !
1 <?php
2 trait A
3 {
4 public function saySomething ()
5 {
6 echo 'Je suis le trait A !';
7 }
8 }
9
10 class MaClasse
11 {
12 use A
13 {
14 saySomething as protected sayWhoYouAre;
15 }
16 }
17
18 $o = new MaClasse;
19 $o ->saySomething (); // Affichera Je suis le trait A ! .
20 $o ->sayWhoYouAre (); // Lvera une erreur fatale , car l'alias cr
est une mthode protge.
Mthodes abstraites dans les traits
Enn, sachez que lon peut forcer la classe utilisant le trait implmenter certaines
mthodes au moyen de mthodes abstraites. Ainsi, ce code lvera une erreur fatale :
1 <?php
2 trait A
3 {
4 abstract public function saySomething ();
5 }
6
7 class MaClasse
8 {
9 use A;
10 }
Cependant, si la classe utilisant le trait dclarant une mthode abstraite est elle aussi
abstraite, alors ce sera ses classes lles dimplmenter les mthodes abstraites du trait
(elle peut le faire, mais elle nest pas oblige). Exemple :
1 <?php
2 trait A
215
CHAPITRE 12. LES TRAITS
3 {
4 abstract public function saySomething ();
5 }
6
7 abstract class Mere
8 {
9 use A;
10 }
11
12 // Jusque -l, aucune erreur n'est leve.
13
14 class Fille extends Mere
15 {
16 // Par contre , une erreur fatale est ici leve, car la m
thode saySomething () n'a pas t implmente.
17 }
En rsum
Les traits sont un moyen pour viter la duplication de mthodes.
Un trait sutilise grce au mot-cl use.
Il est possible dutiliser une innit de traits dans une classe en rsolvant les conits
ventuels avec insteadof.
Un trait peut lui-mme utiliser un autre trait.
Il est possible de changer la visibilit dune mthode ainsi que son nom grce au
mot-cl as.
216
Chapitre 13
LAPI de rexivit
Dicult :
E
n tant que dveloppeur, vous savez que telle classe hrite de telle autre ou que tel
attribut est protg par exemple. Cependant, votre script PHP, lui ne le sait pas :
comment feriez-vous pour acher lcran par exemple que telle classe hrite de telle
autre (dynamiquement bien entendu) ? Cela est pour vous impossible. Je vais cependant
vous dvoiler la solution : pour y parvenir, on se sert de lAPI de rexivit qui va justement
nous permettre dobtenir des informations sur nos classes, attributs et mthodes.
Une fois que nos aurons vu tout cela, nous allons nous pencher sur un exemple dutilisation.
Pour cela, nous utiliserons une bibliothque qui se sert de lAPI de rexivit pour exploiter
les annotations, terme sans doute inconnu en programmation pour la plupart dentre vous.
217
CHAPITRE 13. LAPI DE RFLEXIVIT
Obtenir des informations sur ses classes
Qui dit Rexivit dit Instanciation de classe . Nous allons donc instancier une
classe qui nous fournira des informations sur telle classe. Dans cette section, il sagit de
la classe ReflectionClass. Quand nous linstancierons, nous devrons spcier le nom
de la classe sur laquelle on veut obtenir des informations. Nous allons prendre pour
exemple la classe Magicien de notre prcdent TP.
Pour obtenir des informations la concernant, nous allons procder comme suit :
1 <?php
2 $classeMagicien = new ReflectionClass('Magicien '); // Le nom de
la classe doit tre entre apostrophes ou guillemets.
3 ?>
La classe ReflectionClass possde beaucoup de mthodes et je ne pourrai,
par consquent, toutes vous les prsenter.
Il est galement possible dobtenir des informations sur une classe grce un objet.
Nous allons pour cela instancier la classe ReflectionObject en fournissant linstance
en guise dargument. Cette classe hrite de toutes les mthodes de ReflectionClass :
elle ne rcrit que deux mthodes (dont le constructeur). La seconde mthode rcrite
ne nous intresse pas. Cette classe nimplmente pas de nouvelles mthodes.
Exemple dutilisation trs simple :
1 <?php
2 $magicien = new Magicien(array('nom' => 'vyk12', 'type' => '
magicien '));
3 $classeMagicien = new ReflectionObject($magicien);
4 ?>
Informations propres la classe
Les attributs
Pour savoir si la classe possde tel attribut, tournons-nous prsent vers la mthode
ReflectionClass::hasProperty($atrributeName). Cette mthode retourne vrai si
lattribut pass en paramtre existe et faux sil nexiste pas. Exemple :
1 <?php
2 if ($classeMagicien ->hasProperty('magie'))
3 {
4 echo 'La classe Magicien possde un attribut $magie ';
5 }
6 else
7 {
8 echo 'La classe Magicien ne possde pas d\'attribut $magie ';
218
OBTENIR DES INFORMATIONS SUR SES CLASSES
9 }
10 ?>
Vous pouvez aussi rcuprer cet attribut an dobtenir des informations le concernant
comme nous venons de faire jusqu maintenant avec notre classe. Nous verrons cela
plus tard, une sous-partie entire sera ddie ce sujet.
Les mthodes
Si vous voulez savoir si la classe implmente telle mthode, alors il va falloir regarder
du ct de ReflectionClass::hasMethod($methodName). Celle-ci retourne vrai si la
mthode est implmente ou faux si elle ne lest pas. Exemple :
1 <?php
2 if ($classeMagicien ->hasMethod('lancerUnSort '))
3 {
4 echo 'La classe Magicien implmente une mthode lancerUnSort
()';
5 }
6 else
7 {
8 echo 'La classe Magicien n\'implmente pas de mthode
lancerUnSort ()';
9 }
10 ?>
Vous pouvez, comme pour les attributs, rcuprer une mthode, et une sous-partie sera
consacre la classe permettant dobtenir des informations sur telle mthode.
Les constantes
Dans ce cas galement, il est possible de savoir si telle classe possde telle constante.
Ceci grce la mthode ReflectionClass::hasConstant($constName). Exemple :
1 <?php
2 if ($classePersonnage ->hasConstant('NOUVEAU '))
3 {
4 echo 'La classe Personnage possde une constante NOUVEAU ';
5 }
6 else
7 {
8 echo 'La classe Personnage ne possde pas de constante
NOUVEAU ';
9 }
10 ?>
Vous pouvez aussi rcuprer la valeur de la constante grce la mthode
ReflectionClass::getConstant($constName) :
219
CHAPITRE 13. LAPI DE RFLEXIVIT
1 <?php
2 if ($classePersonnage ->hasConstant('NOUVEAU '))
3 {
4 echo 'La classe Personnage possde une constante NOUVEAU (
celle ci vaut ', $classePersonnage ->getConstant('NOUVEAU ')
, ')';
5 }
6 else
7 {
8 echo 'La classe Personnage ne possde pas de constante
NOUVEAU ';
9 }
10 ?>
Nous pouvons galement retrouver la liste complte des constantes dune classe sous
forme de tableau grce ReflectionClass::getConstants() :
1 <?php
2 echo '<pre >', print_r($classePersonnage ->getConstants (), true),
'</pre >';
3 ?>
Les relations entre classes
Lhritage
Pour rcuprer la classe parente de notre classe, nous allons regarder du ct de
ReflectionClass::getParentClass(). Cette mthode nous renvoie la classe parente
sil y en a une : la valeur de retour sera une instance de la classe ReflectionClass qui
reprsentera la classe parente ! Si la classe ne possde pas de parent, alors la valeur de
retour sera false.
Exemple :
1 <?php
2 $classeMagicien = new ReflectionClass('Magicien ');
3
4 if ($parent = $classeMagicien ->getParentClass ())
5 {
6 echo 'La classe Magicien a un parent : il s\'agit de la
classe ', $parent ->getName ();
7 }
8 else
9 {
10 echo 'La classe Magicien n\'a pas de parent ';
11 }
12 ?>
Voici une belle occasion de vous prsenter la mthode ReflectionClass::getName().
Cette mthode se contente de renvoyer le nom de la classe. Pour lexemple avec le
220
OBTENIR DES INFORMATIONS SUR SES CLASSES
magicien, cela aurait t inutile puisque nous connaissions dj le nom de la classe,
mais ici nous ne le connaissons pas (quand je dis que nous ne le connaissons pas, cela
signie quil nest pas dclar explicitement dans les dernires lignes de code).
Dans le domaine de lhritage, nous pouvons galement citer la mthode
ReflectionClass::isSubclassOf($className). Cette mthode nous renvoie vrai si
la classe spcie en paramtre est le parent de notre classe. Exemple :
1 <?php
2 if ($classeMagicien ->isSubclassOf('Personnage '))
3 {
4 echo 'La classe Magicien a pour parent la classe Personnage ';
5 }
6 else
7 {
8 echo 'La classe Magicien n\'a la classe Personnage pour
parent ';
9 }
10 ?>
Les deux prochaines mthodes que je vais vous prsenter ne sont pas en rapport
direct avec lhritage mais sont cependant utilises lorsque cette relation existe : il
sagit de savoir si la classe est abstraite ou nale. Nous avons pour cela les mthodes
ReflectionClass::isAbstract() et ReflectionClass::isFinal().
Notre classe Personnage est abstraite, vrions donc cela :
1 <?php
2 $classePersonnage = new ReflectionClass('Personnage ');
3
4 // Est -elle abstraite ?
5 if ($classePersonnage ->isAbstract ())
6 {
7 echo 'La classe Personnage est abstraite ';
8 }
9 else
10 {
11 echo 'La classe Personnage n\'est pas abstraite ';
12 }
13
14 // Est -elle finale ?
15 if ($classePersonnage ->isFinal ())
16 {
17 echo 'La classe Personnage est finale ';
18 }
19 else
20 {
21 echo 'La classe Personnage n\'est pas finale ';
22 }
23 ?>
221
CHAPITRE 13. LAPI DE RFLEXIVIT
Dans le mme genre, ReflectionClass::isInstantiable() permet galement de sa-
voir si notre classe est instanciable. Comme la classe Personnage est abstraite, elle ne
peut pas ltre. Vrions cela :
1 <?php
2 if ($classePersonnage ->isInstantiable ())
3 {
4 echo 'La classe Personnage est instanciable ';
5 }
6 else
7 {
8 echo 'La classe personnage n\'est pas instanciable ';
9 }
10 ?>
Bref, pas de grosse surprise.
Les interfaces
Voyons maintenant les mthodes en rapport avec les interfaces. Comme je vous lai dj
dit, une interface nest autre quune classe entirement abstraite : nous pouvons donc
instancier la classe ReflectionClass en spciant une interface en paramtre et vrier
si celle-ci est bien une interface grce la mthode ReflectionClass::isInterface().
Dans les exemples qui vont suivre, nous allons admettre que la classe
Magicien implmente une interface iMagicien.
1 <?php
2 $classeIMagicien = new ReflectionClass('iMagicien ');
3
4 if ($classeIMagicien ->isInterface ())
5 {
6 echo 'La classe iMagicien est une interface ';
7 }
8 else
9 {
10 echo 'La classe iMagicien n\'est pas une interface ';
11 }
12 ?>
Vous pouvez aussi savoir si telle classe implmente telle interface grce la mthode
ReflectionClass::implementsInterface($interfaceName). Exemple :
1 <?php
2 if ($classeMagicien ->implementsInterface('iMagicien '))
3 {
4 echo 'La classe Magicien implmente l\'interface iMagicien ';
5 }
222
OBTENIR DES INFORMATIONS SUR LES ATTRIBUTS DE SES CLASSES
6 else
7 {
8 echo 'La classe Magicien n\'implmente pas l\'interface
iMagicien ';
9 }
10 ?>
Il est aussi possible de rcuprer toutes les interfaces implmentes, interfaces contenues
dans un tableau. Pour cela, il y a deux mthodes : ReflectionClass::getInterfaces()
et ReflectionClass::getInterfaceNames(). La premire renvoie autant dinstances
de la classe ReflectionClass quil y a dinterfaces, chacune reprsentant une interface.
La seconde mthode se contente uniquement de renvoyer un tableau contenant le nom
de toutes les interfaces implmentes. Je pense quil est inutile de donner un exemple
sur ce point-l.
Obtenir des informations sur les attributs de ses classes
Nous avons assez parl de la classe, intressons-nous maintenant ses attributs. La
classe qui va nous permettre den savoir plus leur sujet est ReflectionProperty. Il y
a deux moyens dutiliser cette classe : linstancier directement ou utiliser une mthode
de ReflectionClass qui nous renverra une instance de ReflectionProperty.
Instanciation directe
Lappel du constructeur se fait en lui passant deux arguments. Le premier est le nom
de la classe, et le second est le nom de lattribut. Exemple :
1 <?php
2 $attributMagie = new ReflectionProperty('Magicien ', 'magie');
3 ?>
Tout simplement.
Rcupration dattribut dune classe
Rcuprer un attribut
Pour rcuprer un attribut dune classe, nous aurons besoin de la mthode
ReflectionClass::getProperty($attrName) :
1 <?php
2 $classeMagicien = new ReflectionClass('Magicien ');
3 $attributMagie = $classeMagicien ->getProperty('magie');
4 ?>
223
CHAPITRE 13. LAPI DE RFLEXIVIT
Rcuprer tous les attributs
Si vous souhaitez rcuprer tous les attributs dune classe, il va falloir se servir de
ReflectionClass::getProperties(). Le rsultat retourn est un tableau contenant
autant dinstances de ReflectionProperty que dattributs.
1 <?php
2 $classePersonnage = new ReflectionClass('Personnage ');
3 $attributsPersonnage = $classeMagicien ->getProperties ();
4 ?>
Aprs avoir vu comment rcuprer un attribut, nous allons voir ce que lon peut faire
avec.
Le nom et la valeur des attributs
Pour rcuprer le nom de lattribut, il y a la mthode ReflectionProperty::getName().
Pour obtenir sa valeur, utilisons la mthode ReflectionProperty::getValue($object).
Nous devrons spcier cette dernire mthode linstance dans laquelle nous voulons
obtenir la valeur de lattribut : chaque attribut est propre chaque instance, a naurait
pas de sens de demander la valeur de lattribut dune classe.
Pour nous exercer, nous allons lister tous les attributs de la classe Magicien.
1 <?php
2 $classeMagicien = new ReflectionClass('Magicien ');
3 $magicien = new Magicien(array('nom' => 'vyk12', 'type' => '
magicien '));
4
5 foreach ($classeMagicien ->getProperties () as $attribut)
6 {
7 echo $attribut ->getName (), ' => ', $attribut ->getValue(
$magicien);
8 }
9 ?>
Il fonctionne pas ton code, jai une vieille erreur fatale qui me bloque tout. . .
En eet ! Si cette erreur fatale est leve, cest parce que vous avez appel la mthode
ReflectionProperty::getValue() sur un attribut non public. Il faut donc rendre
lattribut accessible grce ReflectionProperty::setAccessible($accessible),
o $accessible vaut vrai ou faux selon si vous voulez rendre lattribut accessible ou
non.
1 <?php
2 $classeMagicien = new ReflectionClass('Magicien ');
224
OBTENIR DES INFORMATIONS SUR LES ATTRIBUTS DE SES CLASSES
3 $magicien = new Magicien(array('nom' => 'vyk12', 'type' => '
magicien '));
4
5 foreach ($classeMagicien ->getProperties () as $attribut)
6 {
7 $attribut ->setAccessible(true);
8 echo $attribut ->getName (), ' => ', $attribut ->getValue(
$magicien);
9 }
10 ?>
Quand vous rendez un attribut accessible, vous pouvez modier sa valeur
grce ReflectionProperty::setValue($objet, $valeur), ce qui est
contre le principe dencapsulation. Pensez donc bien rendre lattribut inac-
cessible aprs sa lecture en faisant $attribut->setAccessible(false).
Porte de lattribut
Il est tout fait possible de savoir si un attribut est priv, protg ou public grce aux
mthodes ReflectionProperty::isPrivate(), ReflectionProperty::isProtected()
et ReflectionProperty::isPublic().
1 <?php
2 $uneClasse = new ReflectionClass('MaClasse ');
3
4 foreach ($uneClasse ->getProperties () as $attribut)
5 {
6 echo $attribut ->getName (), ' => attribut ';
7
8 if ($attribut ->isPublic ())
9 {
10 echo 'public ';
11 }
12 elseif ($attribut ->isProtected ())
13 {
14 echo 'protg';
15 }
16 else
17 {
18 echo 'priv';
19 }
20 }
21 ?>
Il existe aussi une mthode permettant de savoir si lattribut est statique ou non grce
ReflectionProperty::isStatic().
1 <?php
225
CHAPITRE 13. LAPI DE RFLEXIVIT
2 $uneClasse = new ReflectionClass('MaClasse ');
3
4 foreach ($uneClasse ->getProperties () as $attribut)
5 {
6 echo $attribut ->getName (), ' => attribut ';
7
8 if ($attribut ->isPublic ())
9 {
10 echo 'public ';
11 }
12 elseif ($attribut ->isProtected ())
13 {
14 echo 'protg';
15 }
16 else
17 {
18 echo 'priv';
19 }
20
21 if ($attribut ->isStatic ())
22 {
23 echo ' (attribut statique)';
24 }
25 }
26 ?>
Les attributs statiques
Le traitement dattributs statiques dire un peu dans le sens o ce nest pas un
attribut dune instance mais un attribut de la classe. Ainsi, vous ntes pas obligs
de spcier dinstance lors de lappel de ReflectionProperty::getValue() car un
attribut statique nappartient aucune instance.
1 <?php
2 class A
3 {
4 public static $attr = 'Hello world !';
5 }
6
7 $classeA = new ReflectionClass('A');
8 echo $classeA ->getProperty('attr')->getValue ();
9 ?>
Sinon, vous pouvez appeler ReflectionClass::getStaticPropertyValue($attr), o
$attr est le nom de lattribut.
Dans le mme genre, il y ReflectionClass::setStaticPropertyValue($attr,
$value) o $value est la nouvelle valeur de lattribut.
1 <?php
226
OBTENIR DES INFORMATIONS SUR LES MTHODES DE SES CLASSES
2 class A
3 {
4 public static $attr = 'Hello world !';
5 }
6
7 $classeA = new ReflectionClass('A');
8 echo $classeA ->getStaticPropertyValue('attr'); // Affiche Hello
world !
9
10 $classeA ->setStaticPropertyValue('attr', 'Bonjour le monde !');
11 echo $classeA ->getStaticPropertyValue('attr'); // Affiche
Bonjour le monde !
12 ?>
Vous avez aussi la possibilit dobtenir tous les attributs statiques grce la mthode
ReflectionClass::getStaticProperties(). Le tableau retourn ne contient pas des
instances de ReflectionProperty mais uniquement les valeurs de chaque attribut.
1 <?php
2 class A
3 {
4 public static $attr1 = 'Hello world !';
5 public static $attr2 = 'Bonjour le monde !';
6 }
7
8 $classeA = new ReflectionClass('A');
9
10 foreach ($classeA ->getStaticProperties () as $attr)
11 {
12 echo $attr;
13 }
14
15 // l'cran s'affichera Hello world ! Bonjour le monde !
16 ?>
Obtenir des informations sur les mthodes de ses classes
Voici la dernire classe faisant partie de lAPI de rexivit que je vais vous prsenter :
il sagit de ReflectionMethod. Comme vous laurez devin, cest grce celle-ci que
lon pourra obtenir des informations concernant telle ou telle mthode. Nous pourrons
connatre la porte de la mthode (publique, protge ou prive), si elle est statique
ou non, abstraite ou nale, sil sagit du constructeur ou du destructeur et on pourra
mme lappeler sur un objet.
227
CHAPITRE 13. LAPI DE RFLEXIVIT
Cration dune instance de ReflectionMethod
Instanciation directe
Le constructeur de ReflectionMethod demande deux arguments : le nom de la classe
et le nom de la mthode. Exemple :
1 <?php
2 class A
3 {
4 public function hello($arg1 , $arg2 , $arg3 = 1, $arg4 = 'Hello
world !')
5 {
6 echo 'Hello world !';
7 }
8 }
9
10 $methode = new ReflectionMethod('A', 'hello');
11 ?>
Rcupration dune mthode dune classe
La seconde faon de procder est de rcuprer la mthode de la classe grce
ReflectionClass::getMethod($name). Elle renvoie une instance de ReflectionClass
reprsentant la mthode.
1 <?php
2 class A
3 {
4 public function hello($arg1 , $arg2 , $arg3 = 1, $arg4 = 'Hello
world !')
5 {
6 echo 'Hello world !';
7 }
8 }
9
10 $classeA = new ReflectionClass('A');
11 $methode = $classeA ->getMethod('hello');
12 ?>
Publique, protge ou prive ?
Comme pour les attributs, nous avons des mthodes pour le savoir : jai nomm
ReflectionMethod::isPublic(), ReflectionMethod::isProtected() et
ReflectionMethod::isPrivate(). Je ne vais pas mtendre sur le sujet, vous savez
dj vous en servir.
1 <?php
228
OBTENIR DES INFORMATIONS SUR LES MTHODES DE SES CLASSES
2 $classeA = new ReflectionClass('A');
3 $methode = $classeA ->getMethod('hello');
4
5 echo 'La mthode ', $methode ->getName (), ' est ';
6
7 if ($methode ->isPublic ())
8 {
9 echo 'publique ';
10 }
11 elseif ($methode ->isProtected ())
12 {
13 echo 'protge';
14 }
15 else
16 {
17 echo 'prive';
18 }
19 ?>
Je suis sr que vous savez quelle mthode permet de savoir si elle est statique ou non.
1 <?php
2 $classeA = new ReflectionClass('A');
3 $methode = $classeA ->getMethod('hello');
4
5 echo 'La mthode ', $methode ->getName (), ' est ';
6
7 if ($methode ->isPublic ())
8 {
9 echo 'publique ';
10 }
11 elseif ($methode ->isProtected ())
12 {
13 echo 'protge';
14 }
15 else
16 {
17 echo 'prive';
18 }
19
20 if ($methode ->isStatic ())
21 {
22 echo ' (en plus elle est statique)';
23 }
24 ?>
229
CHAPITRE 13. LAPI DE RFLEXIVIT
Abstraite ? Finale ?
Les mthodes permettant de savoir si une mthode est abstraite ou nale sont simples
retenir : ce sont ReflectionMethod::isAbstract() et ReflectionMethod::isFinal().
1 <?php
2 $classeA = new ReflectionClass('A');
3 $methode = $classeA ->getMethod('hello');
4
5 echo 'La mthode ', $methode ->getName (), ' est ';
6
7 if ($methode ->isAbstract ())
8 {
9 echo 'abstraite ';
10 }
11 elseif ($methode ->isFinal ())
12 {
13 echo 'finale ';
14 }
15 else
16 {
17 echo ' normale ';
18 }
19 ?>
Constructeur ? Destructeur ?
ReflectionMethod::isConstructor() et ReflectionMethod::isDestructor() per-
mettent de savoir si la mthode est le constructeur ou le destructeur de la classe.
1 <?php
2 $classeA = new ReflectionClass('A');
3 $methode = $classeA ->getMethod('hello');
4
5 if ($methode ->isConstructor ())
6 {
7 echo 'La mthode ', $methode ->getName (), ' est le
constructeur ';
8 }
9 elseif ($methode ->isDestructor ())
10 {
11 echo 'La mthode ', $methode ->getName (), ' est le destructeur
';
12 }
13 ?>
230
OBTENIR DES INFORMATIONS SUR LES MTHODES DE SES CLASSES
Pour que la premire condition renvoie vrai, il ne faut pas obligatoirement que
la mthode soit nomme __construct. En eet, si la mthode a le mme
nom que la classe, celle-ci est considre comme le constructeur de la classe
car, sous PHP 4, ctait de cette faon que lon implmentait le constructeur :
il ny avait jamais de __construct. Pour que les scripts dvelopps sous PHP
4 soient aussi compatibles sous PHP 5, le constructeur peut galement tre
implment de cette manire, mais il est clairement prfrable dutiliser la
mthode magique cre pour cet eet.
Appeler la mthode sur un objet
Pour raliser cela, nous allons avoir besoin de ReflectionMethod::invoke($object,
$args). Le premier argument est lobjet sur lequel on veut appeler la mthode. Viennent
ensuite tous les arguments que vous voulez passer la mthode : vous devrez donc pas-
ser autant darguments que la mthode appele en exige. Prenons un exemple tout
simple, vous comprendrez mieux :
1 <?php
2 class A
3 {
4 public function hello($arg1 , $arg2 , $arg3 = 1, $arg4 = 'Hello
world !')
5 {
6 var_dump($arg1 , $arg2 , $arg3 , $arg4);
7 }
8 }
9
10 $a = new A;
11 $hello = new ReflectionMethod('A', 'hello');
12
13 $hello ->invoke($a, 'test', 'autre test'); // On ne va passer
que deux arguments notre mthode.
14
15 // A l'cran s'affichera donc :
16 // string(4) "test" string(10) "autre test" int(1) string(13) "
Hello world !"
17 ?>
Une mthode semblable ReflectionMethod::invoke($object, $args) existe : il
sagit de ReflectionMethod::invokeArgs($object, $args). La dirence entre ces
deux mthodes est que la seconde demandera les arguments lists dans un tableau au
lieu de les lister en paramtres.
Lquivalent du code prcdent avec Reflection::invokeArgs() serait donc le sui-
vant :
1 <?php
2 class A
3 {
231
CHAPITRE 13. LAPI DE RFLEXIVIT
4 public function hello($arg1 , $arg2 , $arg3 = 1, $arg4 = 'Hello
world !')
5 {
6 var_dump($arg1 , $arg2 , $arg3 , $arg4);
7 }
8 }
9
10 $a = new A;
11 $hello = new ReflectionMethod('A', 'hello');
12
13 $hello ->invokeArgs($a , array('test', 'autre test')); // Les
deux arguments sont cette fois -ci contenus dans un tableau.
14
15 // Le rsultat affich est exactement le mme.
16 ?>
Si vous navez pas accs la mthode cause de sa porte restreinte, vous pou-
vez la rendre accessible comme on la fait avec les attributs, grce la mthode
ReflectionMethod::setAccessible($bool). Si $bool vaut true, alors la mthode
sera accessible, sinon elle ne le sera pas. Je me passe dexemple, je suis sr que vous
trouverez tous seuls.
Utiliser des annotations
prsent, je vais vous montrer quelque chose dintressant quil est possible de faire
grce la rexivit : utiliser des annotations pour vos classes, mthodes et attri-
buts, mais surtout y accder durant lexcution du script. Que sont les annotations ?
Les annotations sont des mta-donnes relatives la classe, mthode ou attribut, qui
apportent des informations sur lentit souhaite. Elles sont insres dans des commen-
taires utilisant le syntaxe doc block, comme ceci :
1 <?php
2 /**
3 * @version 2.0
4 */
5 class Personnage
6 {
7 // ...
8 }
Les annotations sinsrent peu prs de la mme faon, mais la syntaxe est un peu
dirente. En eet, la syntaxe doit tre prcise pour quelle puisse tre parse par la
bibliothque que nous allons utiliser pour rcuprer les donnes souhaites.
232
UTILISER DES ANNOTATIONS
Prsentation daddendum
Cette section aura pour but de prsenter les annotations par le biais de la bibliothque
addendum qui parsera les codes pour en extraire les informations. Pour cela, com-
mencez par tlcharger addendum grce au code web suivant, et dcompressez larchive
dans le dossier contenant votre projet :

Tlcharger addendum
Code web : 407819
Commenons par crer une classe sur laquelle nous allons travailler tout au long de cette
partie, comme Personnage ( tout hasard). Avec addendum, toutes les annotations
sont des classes hritant dune classe de base : Annotation. Si nous voulons ajouter
une annotation, Table par exemple, notre classe pour spcier quelle table un objet
Personnage correspond, alors il faudra au pralable crer une classe Table.
Pour travailler simplement, crez sur votre ordinateur un dossier annota-
tions contenant un chier index.php, un chier Personnage.class.php qui
contiendra notre classe, un chier MyAnnotations.php qui contiendra nos
annotations, et enn le dossier addendum.
1 <?php
2 class Table extends Annotation {}
toute annotation correspond une valeur, valeur spcier lors de la dclaration de
lannotation :
1 <?php
2 /**
3 * @Table (" personnages ")
4 */
5 class Personnage
6 {
7
8 }
Nous venons donc de crer une annotation basique, mais concrtement, nous navons
pas fait grand-chose. Nous allons maintenant voir comment rcuprer cette annotation,
et plus prcisment la valeur qui lui est assigne, grce addendum.
quoi servent-elles ces annotations ?
Les annotations sont surtout utilises par les frameworks, comme PHPUnit ou Zend
Framework par exemple, ou bien les ORM
1
tel que Doctrine, qui apportent ici des
informations pour le mapping des classes. Vous naurez donc peut-tre pas utiliser
1. Object Relational Mapping
233
CHAPITRE 13. LAPI DE RFLEXIVIT
les annotations dans vos scripts, mais il est important den avoir entendu parler si vous
dcidez dutiliser des frameworks ou bibliothques les utilisant.
Rcuprer une annotation
Pour rcuprer une annotation, il va dabord falloir rcuprer la classe via la biblio-
thque en crant une instance de ReflectionAnnotatedClass, comme nous lavions
fait en dbut de chapitre avec ReflectionClass :
1 <?php
2 // On commence par inclure les fichiers ncessaires.
3 require 'addendum/annotations.php';
4 require 'MyAnnotations.php';
5 require 'Personnage.class.php';
6
7 $reflectedClass = new ReflectionAnnotatedClass('Personnage ');
Maintenant que cest fait, nous allons pouvoir rcuprer lannotation grce la m-
thode getAnnotation. De manire gnrale, cette mthode retourne une instance de
Annotation. Dans notre cas, puisque nous voulons lannotation Table, ce sera une
instance de Table qui sera retourne. La valeur de lannotation est contenue dans lat-
tribut value, attribut public disponible dans toutes les classes lles de Annotation :
1 <?php
2 // On commence par inclure les fichiers ncessaires.
3 require 'addendum/annotations.php';
4 require 'MyAnnotations.php';
5 require 'Personnage.class.php';
6
7 $reflectedClass = new ReflectionAnnotatedClass('Personnage ');
8
9 echo 'La valeur de l\'annotation <strong >Table </strong > est <
strong >', $reflectedClass ->getAnnotation('Table')->value , '
</strong >';
Il est aussi possible, pour une annotation, davoir un tableau comme valeur. Pour
raliser ceci, il faut mettre la valeur de lannotation entre accolades et sparer les
valeurs du tableau par des virgules :
1 <?php
2 /**
3 * @Type({'brute ', 'guerrier ', 'magicien '})
4 */
5 class Personnage
6 {
7
8 }
Si vous rcuprez lannotation, vous obtiendrez un tableau classique :
234
UTILISER DES ANNOTATIONS
1 <?php
2 print_r($reflectedClass ->getAnnotation('Type')->value); //
Affiche le dtail du tableau.
Vous pouvez aussi spcier des cls pour les valeurs comme ceci :
1 <?php
2 /**
3 * @Type({ meilleur = 'magicien ', 'moins bon' = 'brute ', neutre
= 'guerrier '})
4 */
5 class Personnage
6 {
7
8 }
Notez la mise entre quotes de moins bon : elles sont utiles ici car un espace
est prsent. Cependant, comme vous le voyez avec meilleur et neutre, elles
ne sont pas obligatoires.
Enn, pour nir avec les tableaux, je prcise que vous pouvez en emboter tant que
vous voulez. Pour placer un tableau dans un autre, il sut douvrir une nouvelle paire
daccolades :
1 <?php
2 /**
3 * @UneAnnotation ({ uneCle = 1337 , {uneCle2 = true , uneCle3 = '
une valeur '}})
4 */
Savoir si une classe possde telle annotation
Il est possible de savoir si une classe possde telle annotation grce la mthode
hasAnnotation :
1 <?php
2 require 'addendum/annotations.php';
3 require 'MyAnnotations.php';
4 require 'Personnage.class.php';
5
6 $reflectedClass = new ReflectionAnnotatedClass('Personnage ');
7
8 $ann = 'Table';
9 if ($reflectedClass ->hasAnnotation($ann))
10 {
11 echo 'La classe possde une annotation <strong >', $ann , '</
strong > dont la valeur est <strong >', $reflectedClass ->
getAnnotation($ann)->value , '</strong ><br />';
12 }
235
CHAPITRE 13. LAPI DE RFLEXIVIT
Une annotation multiples valeurs
Il est possible pour une annotation de possder plusieurs valeurs. Chacune de ces valeurs
est stocke dans un attribut de la classe reprsentant lannotation. Par dfaut, une
annotation ne contient quun attribut ($value) qui est la valeur de lannotation.
Pour pouvoir assigner plusieurs valeurs une annotation, il va donc falloir ajouter des
attributs notre classe. Commenons par a :
1 <?php
2 class ClassInfos extends Annotation
3 {
4 public $author;
5 public $version;
6 }
Il est important ici que les attributs soient publics pour que le code extrieur
la classe puisse modier leur valeur.
Maintenant, tout se joue lors de la cration de lannotation. Pour assigner les valeurs
souhaites aux attributs, il sut dcrire ces valeurs prcdes du nom de lattribut.
Exemple :
1 <?php
2 /**
3 * @ClassInfos(author = "vyk12", version = "1.0")
4 */
5 class Personnage
6 {
7
8 }
Pour accder aux valeurs des attributs, il faut rcuprer lannotation, comme nous
lavons fait prcdemment, et rcuprer lattribut.
1 <?php
2 $classInfos = $reflectedClass ->getAnnotation('ClassInfos ');
3
4 echo $classInfos ->author;
5 echo $classInfos ->version;
Le fait que les attributs soient publics peut poser quelques problmes. En eet, de la
sorte, nous ne pouvons pas tre srs que les valeurs assignes soient correctes. Heureu-
sement, la bibliothque nous permet de pallier ce problme en rcrivant la mthode
checkConstraints() (dclare dans sa classe mre Annotation) dans notre classe re-
prsentant lannotation, appele chaque assignation de valeur, dans lordre dans lequel
sont assignes les valeurs. Vous pouvez ainsi vrier lintgrit des donnes, et lancer une
erreur si il y a un problme. Cette mthode prend un argument : la cible do provient
lannotation. Dans notre cas, lannotation vient de notre classe Personnage, donc le
236
UTILISER DES ANNOTATIONS
paramtre sera une instance de ReflectionAnnotatedClass reprsentant Personnage.
Vous verrez ensuite que cela peut tre une mthode ou un attribut.
1 <?php
2 class ClassInfos extends Annotation
3 {
4 public $author;
5 public $version;
6
7 public function checkConstraints($target)
8 {
9 if (! is_string($this ->author))
10 {
11 throw new Exception('L\'auteur doit tre une chane de
caractres');
12 }
13
14 if (! is_numeric($this ->version))
15 {
16 throw new Exception('Le numro de version doit tre un
nombre valide ');
17 }
18 }
19 }
Des annotations pour les attributs et mthodes
Jusquici nous avons ajout des annotations une classe. Il est cependant possible den
ajouter des mthodes et attributs comme nous lavons fait pour la classe :
1 <?php
2 /**
3 * @Table (" Personnages ")
4 * @ClassInfos(author = "vyk12", version = "1.0")
5 */
6 class Personnage
7 {
8 /**
9 * @AttrInfos(description = 'Contient la force du personnage ,
de 0 100 ', type = 'int ')
10 */
11 protected $force_perso;
12
13 /**
14 * @ParamInfo(name = 'destination ', description = 'La
destination du personnage ')
15 * @ParamInfo(name = 'vitesse ', description = 'La vitesse
laquelle se dplace le personnage ')
16 * @MethodInfos(description = 'Dplace le personnage un
autre endroit ', return = true , returnDescription = '
237
CHAPITRE 13. LAPI DE RFLEXIVIT
Retourne true si le personnage peut se dplacer ')
17 */
18 public function deplacer($destination , $vitesse)
19 {
20 // ...
21 }
22 }
Pour rcuprer une de ces annotations, il faut dabord rcuprer lattribut ou la m-
thode. Nous allons pour cela nous tourner vers ReflectionAnnotatedProperty et
ReflectionAnnotatedMethod. Le constructeur de ces classes attend en premier para-
mtre le nom de la classe contenant llment et, en second, le nom de lattribut ou de
la mthode. Exemple :
1 <?php
2 $reflectedAttr = new ReflectionAnnotatedProperty('Personnage ',
'force_perso ');
3 $reflectedMethod = new ReflectionAnnotatedMethod('Personnage ',
'deplacer ');
4
5 echo 'Infos concernant l\'attribut :';
6 var_dump($reflectedAttr ->getAnnotation('AttrInfos '));
7
8 echo 'Infos concernant les paramtres de la mthode :';
9 var_dump($reflectedMethod ->getAllAnnotations('ParamInfo '));
10
11 echo 'Infos concernant la mthode :';
12 var_dump($reflectedMethod ->getAnnotation('MethodInfos '));
Notez ici lutilisation de ReflectionAnnotatedMethod::getAllAnnotations(). Cette
mthode permet de rcuprer toutes les annotations dune entit correspondant au nom
donn en argument. Si aucun nom nest donn, alors toutes les annotations de lentit
seront retournes.
Contraindre une annotation une cible prcise
Grce une annotation un peu spciale, vous avez la possibilit dimposer un type de
cible pour une annotation. En eet, jusqu maintenant, nos annotations pouvaient tre
utilises aussi bien par des classes que par des attributs ou des mthodes. Dans le cas des
annotations ClassInfos, AttrInfos, MethodInfos et ParamInfos, cela prsenterait un
non-sens quelles puissent tre utilises par nimporte quel type dlment.
Pour pallier ce problme, retournons notre classe ClassInfos. Pour dire cette
annotation quelle ne peut tre utilise que sur des classes, il faut utiliser lannotation
spciale @Target :
1 <?php
2 /** @Target (" class") */
3 class ClassInfos extends Annotation
4 {
238
UTILISER DES ANNOTATIONS
5 public $author;
6 public $version;
7 }
prsent, essayez dutiliser lannotation @ClassInfos sur un attribut ou une mthode
et vous verrez quune erreur sera leve. Cette annotation peut aussi prendre pour valeur
property, method ou nesty. Ce dernier type est un peu particulier et cette partie
commenant dj devenir un peu imposante, jai dcid de ne pas en parler. Si cela
vous intresse, je ne peux que vous conseiller daller faire un tour sur la documentation
daddendum, accessible via le code web suivant :

Documentation daddendum
Code web : 148670
Je vous ai aussi volontairement cach une classe : la classe ReflectionParameter qui
vous permet dobtenir des informations sur les paramtres de vos mthodes. Je vous
laisse vous documenter ce sujet grce au code web suivant, son utilisation est trs
simple :

ReflectionParameter
Code web : 977502
En rsum
Il est possible dobtenir des informations sur ses classes, attributs et mthodes, res-
pectivement grce ReflectionClass, ReflectionProperty et ReflectionMethod.
Utiliser des annotations sur ses classes permet, grce une bibliothque telle quad-
dendum, de rcuprer dynamiquement leurs contenus.
Lutilisation dannotations dans un but de conguration dynamique est utilise par
certains frameworks ou ORM tel que Doctrine par exemple, ce qui permet dcono-
miser un chier de conguration en plaant la description des tables et des colonnes
directement dans des annotations.
239
CHAPITRE 13. LAPI DE RFLEXIVIT
240
Chapitre 14
UML : prsentation (1/2)
Dicult :
P
rogrammer en orient objet cest bien beau, mais au dbut, cest un peu dicile. On
a du mal sy retrouver, on ne sait pas trop comment lier nos classes ni comment
penser cet ensemble. LUML est justement lun des moyens pour y parvenir. LUML
(pour Unied Modeling Language, ou langage de modlisation uni en franais) est
un langage permettant de modliser nos classes et leurs interactions. Concrtement, cela
seectue par le biais dun diagramme : vous dessinerez vos classes et les lierez suivant des
conventions bien prcises. Cela vous permettra ainsi de mieux visualiser votre application
et de mieux la penser.
La prsentation de lUML ne sera volontairement pas trs approfondie, le but nest pas de
faire de vous des pros de la modlisation, mais juste de vous enseigner les bases an que
vous puissiez tre capables de modliser vous-mmes.
241
CHAPITRE 14. UML : PRSENTATION (1/2)
UML, kzako ?
LUML est un langage de modlisation objet. Il permet donc de modliser vos objets
et ainsi reprsenter votre application sous forme de diagramme. Avant daller plus
loin, attardons nous un peu sur la signication dUML : Unied Modeling Language,
langage de modlisation uni . UML nest pas un langage proprement parler,
plutt une sorte de mthodologie.
Mais quest-ce que cest concrtement ? quoi a pourra bien me servir ?
Grce UML, vous pourrez modliser toute votre application. Quand je dis toute, jen-
tends par l la plupart de votre application car PHP nest pas un langage orient objet :
le modle objet lui a t implment au cours des versions (ds la version 4). Grce
ces diagrammes, vous pourrez donc reprsenter votre application : son fonctionnement,
sa mise en route, les actions susceptibles dtre eectues par lapplication, etc. Ces
diagrammes ont t conus pour que quelquun nayant aucune connaissance en in-
formatique puisse comprendre le fonctionnement de votre application. Certes, certains
diagrammes sont rservs aux dveloppeurs car assez diciles expliquer si on ne sait
pas programmer : ce sont ces diagrammes que nous allons tudier.
Daccord, je peux modliser mon application mais en quoi cela va-t-il maider ?
Si vous avez un gros projet (et l je ne parle pas forcment de PHP mais de tout langage
implmentant la POO), il peut tre utile de le modliser an dy voir plus clair. Si vous
vous focalisez sur la question Par o commencer ? au dbut du projet, cest quil
vous faut un regard plus objectif sur le projet. Les diagrammes vous apporteront ce
regard dirent sur votre application.
LUML peut aussi tre utile quand on commence programmer OO mais quon ne sait
pas trop comment sy prendre. Par exemple, vous ne savez pas trop encore comment
programmer orient objet de faon concrte (vous avez vu comment crer un mini-jeu,
cest cool, mais un livre dor ou un systme de news, vous savez ?). Sachez donc que
lUML va vous tre dune grande aide au dbut. a vous aidera mieux penser objet
car les diagrammes reprsentent vos classes, vous vous rendrez ainsi mieux compte
de ce quil est intelligent de faire ou pas. Cependant, lUML nest pas un remde
miracle et vous ne saurez toujours pas comment vous y prendre pour raliser votre
toute premire application, mais une fois que je vous aurai montr et que vous aurez
acquis un minimum de pratique, lUML pourra vous aider.
Enn, lUML a aussi un rle de documentation. En eet, quand vous reprsenterez
votre projet sous forme de diagramme, quiconque sachant le dchirer pourra (du
moins, si vous lavez bien construit) comprendre le droulement de votre application
et ventuellement reprendre votre projet (cest toujours mieux que la phpdoc que nous
242
UML, KZAKO?
utilisions jusqu prsent pour commenter nos classes (revenez au dernier TP si vous
avez un trou de mmoire)).
Bref, que ce soient pour les gros projets personnels ou professionnels, lUML vous suivra
partout. Cependant, tout le monde ne trouve pas lUML trs utile et certains prfrent
modliser le projet dans leur tte . Vous verrez avec le temps si vous avez rellement
besoin de lUML ou si vous pouvez vous en passer, mais pour linstant, je vous conseille
de modliser (enn, ds le prochain chapitre).
Il existe plusieurs types de diagrammes. Nous, nous allons tudier les diagrammes
de classe. Ce sont des diagrammes modlisant vos classes et montrant les direntes
interactions entre elles. Un exemple ? Regardez plutt la gure 14.1.
Figure 14.1 Diagramme UML modlisant notre dernier TP
Vous aurez sans doutes reconnu notre dernier TP reprsent sur un diagramme.
Ce diagramme a lavantage dtre trs simple tudier. Cependant, il y a des cas
complexes et des conventions connatre car, par exemple, des mthodes abstraites
nont pas le mme style dcriture quune mthode nale. Nous allons tudier les bases,
petit petit, puis, la n de ce chapitre, vous serez capable danalyser compltement
un diagramme de classe.
243
CHAPITRE 14. UML : PRSENTATION (1/2)
Modliser une classe
Premire approche
Nous allons commencer en douceur par analyser une simple classe modlise. Nous
allons prendre notre classe News simplie (voir la gure 14.2).
Figure 14.2 Notre classe News modlise
Nous avons donc ici une simple classe. Commenons par analyser celle-ci.
En haut, en gras et gros, nous avons le nom de la classe (ici, News).
Ensuite, spar du nom de la classe, vient un attribut de cette classe : il sagit de id
prcd dun #, nous verrons ce que a signie juste aprs.
Enn, spare de la liste dattributs, vient la liste des mthodes de la classe. Toutes
sont prcdes dun +, qui a, comme le #, une signication.
Nous allons commencer par analyser la signication du # et des +. Je ne vous fais pas
attendre : ces signes symbolisent la porte de lattribut ou de la mthode. Voici la liste
des trois symboles :
Le signe + : llment suivi de ce signe est public.
Le signe # : llment suivi de ce signe est protg.
Le signe - : llment suivi de ce signe est priv.
Maintenant que vous savez quoi correspondent ces signes, regardez droite de lattri-
but : suivi de deux points, on peut lire int. Cela veut dire que cet attribut est de type
int, tout simplement. int est le diminutif de integer qui veut dire entier : notre
attribut est donc un nombre entier.
droite des mthodes, nous pouvons apercevoir la mme chose. Ceci indique le type
de la valeur de retour de celle-ci. Si elle ne renvoie rien, alors on dit quelle est vide (=
void). Si elle peut renvoyer plusieurs types de rsultats dirents, alors on dit quelle
est mixte (= mixed). Par exemple, une mthode __get peut renvoyer une chane de
caractres ou un entier (regardez la classe Personnage sur le premier diagramme).
Encore une dernire chose expliquer sur ce diagramme : ce quil y a entre les pa-
renthses suivant le nom des mthodes. Comme vous vous en doutez, elles contiennent
tous les attributs ainsi que leur type (entier, tableau, chane de caractres, etc.). Si un
paramtre peut tre de plusieurs types, alors, comme pour les valeurs de retour des
mthodes, on dit quil est mixte.
Si un attribut, une mthode ou un paramtre est une instance ou en renvoie
une, il ne faut pas dire que son type est object. Son type est le nom de la
classe, savoir Personnage, Brute, Magicien, Guerrier, etc.
244
MODLISER UNE CLASSE
Ensuite, il y a des conventions concernant le style dcriture des attributs, des constantes
et des mthodes.
Sans style dcriture particulier, la mthode est normale , cest--dire ni abstraite,
ni nale (comme ici). Si la mthode est en italique, cela signie quelle est abstraite.
Pour montrer quune mthode est nale, on le spcie en strotype (on place le mot
leaf entre chevrons ct de la mthode).
Si lattribut ou la mthode est soulign, cela signie que llment est statique.
Une constante est reprsente comme tant un attribut public, statique, de type
const et est crite en majuscules.
Exemple rcapitulant tout a (voir la gure 14.3).
Figure 14.3 Exemple de classe modlise
Tous ces codes (textes en gras, souligns ou en italiques) sont une norme
quil faut respecter. Tout le monde sait quune mthode en italique signie
quelle est abstraite par exemple : ne faites donc pas comme bon vous semble !
Exercices
Maintenant que vous savez tout cela, je vais vous donner quelques attributs et mthodes
et vous allez essayer de deviner quels sont ses particularits (porte, statique ou non,
abstraite, nale, ou ni lun ni lautre, les arguments et leur type. . .).
Exercice 1 : -maMethode (param1:int) : void
Correction : nous avons ici une mthode maMethode qui est abstraite (car en italique)
et statique (car souligne). Elle ne renvoie rien et accepte un argument : $param1, qui
est un entier. Sans oublier sa visibilite, symbolise par le signe -, qui est prive.
Exercice 2 : #maMethode (param1:mixed) : array
Correction : nous avons ici une mthode maMethode ni abstraite, ni nale. Elle renvoie
un tableau et accepte un argument : $param1, qui peut tre de plusieurs types. Sans
oublier sa visibilite, symbolise par le signe #, qui est protge.
Exercice 3 : +leaf maMethode() : array
Correction : cette fois-ci, la mthode est nale (signale par le mot leaf) et statique (car
souligne). Elle ne prend aucun argument et renvoie un tableau. Quant sa visibilit,
le signe + nous informe quelle est publique.
245
CHAPITRE 14. UML : PRSENTATION (1/2)
Les dirents codes (italique, soulignements, etc.) rentreront dans votre tte au l du
temps, je ne vous impose pas de tous les apprendre sur-le-champ. Rfrez-vous autant
que ncessaire cette partie en cas de petits trous de mmoire.
Maintenant que vous savez analyser un objet, il serait bien de savoir analyser les inter-
actions entre ceux-ci, non ?
Modliser les interactions
Nous attaquons la dernire partie de ce chapitre. Nous allons voir comment modliser
les interactions entre les objets que lon a modliss. Jusqu prsent, nous savions
comment les reconnatre, mais lavantage de la POO est de crer un ensemble dobjets
qui interagissent entre eux an de crer une vritable application.
Lhritage
Parmi les interactions, on peut citer lhritage. Comme montr dans le premier dia-
gramme que vous avez vu, lhritage est symbolis par une simple che, comme indiqu
la gure 14.4.
Figure 14.4 Exemple de modlisation dune classe lle et dune classe mre
Ce diagramme quivaut au code suivant :
1 <?php
2 class Mere
3 {
4 protected $attribut1;
5 protected $attribut2;
6
7 public function methode ()
8 {
9
10 }
11 }
12
13 class Fille extends Mere
14 {
15 protected $attribut3;
16
17 public function __set($nom , $valeur)
18 {
19
246
MODLISER LES INTERACTIONS
20 }
21 }
22 ?>
Les interfaces
Vous connaissez galement les interactions avec les interfaces. En eet, comme je lai
dj dit, une interface nest rien dautre quune classe entirement abstraite, elle est
donc considre comme tel. Si une classe doit implmenter une interface, alors on
utilisera la che en pointills, comme la gure 14.5.
Figure 14.5 Exemple de modlisation dune classe implmentant une interface
Traduit en PHP, a donne :
1 <?php
2 interface iMaClasse
3 {
4 public function methode1 ();
5 public function methode2 ();
6 }
7
8 class MaClasse implements iMaClasse
9 {
10 protected $attribut;
11
12 public function methode ()
13 {
14
15 }
16
17 // Ne pas oublier d'implmenter les mthodes de l'interface !
18
19 public function methode1 ()
20 {
21
22 }
23
24 public function methode2 ()
25 {
26
27 }
28 }
29 ?>
247
CHAPITRE 14. UML : PRSENTATION (1/2)
Lassociation
Nous allons voir encore trois interactions qui se ressemblent. La premire est lasso-
ciation. On dit que deux classes sont associes lorsquune instance des deux classes est
amene interagir avec lautre instance. Lassociation entre deux classes est modlise
comme la gure 14.6.
Figure 14.6 Exemple de modlisation dune association
Lassociation est ici caractrise par le fait quune mthode de la classe NewsManager
entre en relation avec une instance de la classe News.
Attends deux secondes. . . Je vois des choses crites sur la ligne ainsi quaux
extrmits, quest-ce que cest ?
Le mot crit au centre, au-dessus de la ligne est la dnition de la relation. Il est suivi
dun petit symbole indiquant le sens de lassociation. Ainsi, on peut lire facilement
NewsManager gre News .
Ensuite, vous voyez le chire 1 crit gauche et une astrisque droite. Ce sont
les cardinalits. Ces cardinalits prsentent le nombre dinstances qui participent
linteraction. Nous voyons donc quil y a 1 manager pour une innit de news. On peut
dsormais lire facilement 1 NewsManager gre une innit de News . Les cardinalits
peuvent tre crites sous direntes formes :
x (nombre entier) : tout simplement la valeur exacte de x.
x..y : de x y (exemple : 1..5).
* : une innit.
x..* : x ou plus (exemple : 5..*).
Lagrgation
La deuxime che que je vais vous prsenter est lagrgation. Il sagit dune forme
dassociation un peu particulire : on parlera dagrgation entre deux classes lorsque
lune dentre elles contiendra au moins une instance de lautre classe. Une agrgation
est caractrise de la sorte (voir gure 14.7).
Figure 14.7 Exemple de modlisation dune agrgation
248
MODLISER LES INTERACTIONS
Vous pouvez remarquer quil ny a pas de cardinalit du ct du losange. En eet, le
ct ayant le losange signie quil y a obligatoirement une et une seule instance de la
classe par relation (ici la classe est NewsCollection).
La composition
La dernire interaction que je vais vous prsenter est la composition. La composition
est une agrgation particulire. Imaginons que nous avons une classe A qui contient
une ou plusieurs instance(s) de B. On parlera de composition si, lorsque linstance de A
sera supprime, toutes les instances de B contenues dans linstance de A sont elles aussi
supprimes (ce qui ntait pas le cas avec lagrgation). Une composition est reprsente
de la sorte (voir la gure 14.8).
Figure 14.8 Exemple de modlisation dune composition
Notez la prsence de la mthode __destruct() qui sera charge de dtruire
les instances de la classe News. Cependant, celle-ci nest pas obligatoire : vous
pouvez trs bien, dans lune de vos mthodes, placer un $this->liste[] =
new News;, et linstance cre puis stocke dans lattribut $liste sera donc
automatiquement dtruit.
Ce chapitre ne se veut pas complet pour la simple et bonne raison quil serait beaucoup
trop long de faire le tour de lUML. En eet, un cours complet sur lUML stalerait
sur beaucoup de chapitres, et ce nest clairement pas le but du tutoriel. Si ce sujet
vous intresse, je peux vous renvoyer sur le site dUML, o vous trouverez toutes les
informations ncessaires :

Cours UML
Code web : 974030
En rsum
LUML est un moyen parmi dautres de modliser son application an de mieux sy
retrouver.
La modlisation dune classe et des interactions se ralise en respectant certaines
conventions.
Il y a association lorsquune classe A se sert dune classe B.
Une agrgation est une association particulire : il y a agrgation entre A et B si
lobjet A possde une ou plusieurs instances de B.
249
CHAPITRE 14. UML : PRSENTATION (1/2)
Une composition est une agrgation particulire : il y a composition entre A et B si
toutes les instances de B contenues dans A sont supprimes lorsque A est supprime.
250
Chapitre 15
UML : modlisons nos classes (2/2)
Dicult :
V
oici la suite directe du prcdent chapitre ayant pour objectif de vous introduire
lUML. Nous savons actuellement analyser des diagrammes de classes et les interac-
tions entre elles, mais que diriez-vous de crer vous-mme ces diagrammes ?
Pour ce faire, nous allons avoir besoin dun programme : je vais vous prsenter Dia.
251
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Ayons les bons outils
Avant de se lancer tte baisse dans la modlisation, il serait bien davoir les logiciels qui
le permettront (a pourrait aider, sait-on jamais). Pour cela, nous allons avoir besoin
dun logiciel de modlisation de diagrammes UML. Il en existe plusieurs, pour ma part,
jai choisi Dia.
La raison de ce choix est simple : une extension qui permet de convertir les diagrammes
UML en code PHP a t dveloppe pour ce logiciel ! Ainsi, en quelques clics, toutes vos
classes contenant les attributs et mthodes ainsi que les interactions seront gnres,
le tout comment avec une phpdoc. Bref, cela nous simpliera grandement la tche !
Installation
Des installateurs sont disponibles sur le site suivant :

Installer Dia
Code web : 132920
Le lien de tlchargement pour Windows est sur la page daccueil. Deux petits liens
situs en-dessous de ce lien de tlchargement mnent aux pages proposant les liens
pour Linux et Mac OS X.
Installation de lextension uml2php5
Vous avez correctement install Dia sur votre ordinateur. Il faut maintenant installer
lextension uml2php5 qui nous permettra de gnrer automatiquement le code de nos
classes partir de nos diagrammes. Pour cela, il vous faut tlcharger cinq chiers.
Ces chiers sont disponibles sur le site de lextension, en cliquant sur Download
gauche. Tlchargez larchive .zip ou .tar.gz suivant vos prfrences. Dcompressez
cette archive et copiez / collez les cinq chiers dans le dossier xslt prsent dans le
rpertoire dinstallation de Dia.

Installer uml2php5
Code web : 481115
Lancer Dia
Histoire que nous soyons tous au point, je vais vous demander de lancer votre logiciel
an que nous soyons bien srs davoir tous le mme (voir la gure 15.1). Si linterface
est dirente, veillez bien avoir tlcharg la version 0.97 du logiciel (actuellement la
dernire).
252
AYONS LES BONS OUTILS
Figure 15.1 Fentre principale de Dia
253
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Deux zones principales
Linterface tant assez intuitive, je ne vais pas mtendre sur sa prsentation. Je vais
me contenter de vous donner le rle des deux parties de cette interface.
La premire partie, gauche, contient tous les outils (crer une classe, une interaction,
ou bien un trait, un cercle, etc.). La seconde, droite, est beaucoup plus grande : elle
contiendra notre diagramme. Cest lintrieur delle que lon eectuera nos oprations
pour tout mettre en uvre.
Unir les fentres
Il se peut que vous ayez deux fentres spares. Si cest le cas, cest que vous navez
pas pass largument integrated au programme. Vous pouvez passer cet argument
en lanant le logiciel en ligne de commande. Il y a cependant plus pratique, voici donc
des petites astuces suivant votre systme dexploitation.
Sous Windows
Sous Windows, vous pouvez crer un raccourci. Pour cela, allez dans le dossier dins-
tallation de Dia puis dans le dossier bin. Faites un clic droit sur le chier diaw.exe
et cliquez sur Copier. Allez dans le rpertoire o vous voulez crer le raccourci, faites
un clic droit dans ce rpertoire puis choisissez loption Coller le raccourci. Faites un
clic droit sur le chier cr, puis cliquez sur Proprits. Dans le champ texte Cible,
rajoutez la n (en dehors des guillemets) integrated. Ainsi, quand vous lancerez
Dia depuis ce raccourci, les deux fentres seront fusionnes.
Par dfaut, le raccourci prsent dans le menu dmarrer lance Dia avec loption
intergrated. Si ce nest pas le cas, modiez la cible de celui-ci comme on
vient de le faire (Clic droit > Proprits > . . .).
Sous Linux
Je vais ici vous donner une manipulation simple sous Ubuntu an de modier la cible
du lien pointant sur le logiciel dans le menu Applications. Dans le menu Systme,
allez dans Prfrences puis Menu principal. gauche de la fentre qui est apparue,
sous le menu Applications, cliquez sur Graphisme. Au milieu de la fentre, cliquez
sur diteur de diagrammes Dia. droite, cliquez sur Proprits. Dans le champ
texte Commande, placez-y dia integrated %F. Fermez le tout, relancez votre
programme via le menu et vous avez vos deux fentres fusionnes.
Sous les autres distributions, lide est la mme : il faut vous arranger pour modier le
raccourci an dajouter loption integrated.
254
MODLISER UNE CLASSE
Sous Mac OS
Hlas sous Mac OS, vous ne pouvez crer de raccourci. La solution consiste donc
lancer le logiciel en ligne de commande. Heureusement, celle-ci est assez courte et
simple retenir. En eet, un simple dia integrated sut.
Cependant, vous pouvez tlcharger un lanceur qui vous facilitera la tche pour lancer
lapplication :

lanceur Dia pour Mac OS


Code web : 644011
Modliser une classe
Crer une classe
Lanons-nous maintenant dans la cration dun diagramme. Pour commencer, mod-
lisons notre premire classe. Pour cela, rien de plus simple. Je vais vous demander de
cliquer sur cette icne (voir gure 15.2).
Figure 15.2 Modliser une classe
Cette icne, comme linfobulle nous lindique, reprsente loutil permettant de crer une
classe. Ainsi, dans la zone contenant notre diagramme, il sura de cliquer nimporte
o pour quune classe soit cre lendroit o lon a cliqu. Essayez donc.
Normalement, vous devriez avoir obtenu quelque chose ressemblant ceci (voir gure
15.3).
Figure 15.3 Diagramme de classe vierge
Vous voyez huit carrs ainsi que deux croix bleues autour de la classe. Si les carrs sont
prsents, cela veut dire que la classe est slectionne (par dfaut slectionne quand
vous en crez une). Si vous voulez enlever cette slection, il vous sut de cliquer ct.
Vous verrez ainsi les huit carrs remplacs par de nouvelles petites croix bleues qui ont
une signication bien particulire. Je vous expliquerai ceci plus tard.
255
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Modier notre classe
Nous allons accder aux proprits de notre classe an de pouvoir la modier. Pour
cela, je vais vous demander de double-cliquer dessus an dobtenir cette fentre (voir
gure 15.4).
Figure 15.4 Fentre permettant de modier la classe
Dcortiquons un peu cette fentre. Tout ceci est en fait trs simple, il sut juste de
quelques explications.
Tout en haut, vous voyez une liste de cinq onglets :
Classe : permet de grer les options concernant cette classe.
Attributs : permet de grer la liste des attributs de cette classe.
Oprations : permet de grer la liste des mthodes de cette classe.
Modles : inutile en PHP tant donn quil ny a pas de templates. Si a vous
intrigue, allez voir en C++ par exemple ;).
Style : permet de modier le style de la classe (couleurs et eets de texte).
Gestion des options de la classe
En haut, vous pouvez apercevoir trois champs texte. Le premier est le nom de la classe.
Vous pouvez par exemple y indiquer le nom Personnage. Ensuite vient le strotype.
Ce champ est utiliser pour spcier que la classe est particulire. Par exemple, sil
sagit dune interface, on y crira simplement interface . Enn, dans le dernier champ
texte, nous placerons des commentaires relatifs la classe.
256
MODLISER UNE CLASSE
Ensuite viennent une srie de cases cocher. La premire signie (comme vous vous
en doutez) que la classe est abstraite ou non. Par la suite, nous pouvons apercevoir
trois cases cocher permettant dacher ou non des lments (acher ou masquer
les attributs, mthodes ou commentaires). Si vous dcidez de masquer les attributs ou
mthodes, leur bloc disparatra de la classe (les bordures entourant le bloc comprises),
tandis que si vous dcidez de supprimer les attributs ou mthodes (via les cases
cocher de droite), le bloc restera visible mais ne contiendra plus leur liste dattributs
ou mthodes. Le masquage des attributs est utile dans le cas o la classe est une
interface par exemple.
Pour que vous vous rendiez compte des modications que vous eectuez en
temps rel, cliquez sur Appliquer. Vous verrez ainsi votre classe se modier
avec les nouvelles options. Si a ne vous plait pas et que vous voulez annuler
tout ce que vous avez fait, il vous sut de cliquer sur Fermer. Par contre, si
vous voulez enregistrer vos informations, cliquez sur Valider.
Gestion des attributs
Commenons par ajouter des attributs notre classe. Pour cela, cliquez sur longlet
Attributs (voir gure 15.5).
Figure 15.5 Fentre permettant de grer les attributs
Analysons le contenu de cette fentre. La partie la plus vidente que vous voyez est
le gros carr blanc en haut gauche : cest lintrieur de ce bloc que seront lists
257
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
tous les attributs. Vous pourrez, partir de l, en slectionner an den modier,
den supprimer, den monter ou descendre dun cran dans la liste. An deectuer ces
actions, il vous sura dutiliser les boutons situs droite de ce bloc :
Le bouton Nouveau crera un nouvel attribut.
Le bouton Supprimer supprimera lattribut slectionn.
Le bouton Monter montera lattribut dun cran dans la liste (sil nest pas dj tout
en haut).
Le bouton Descendre descendra lattribut dun cran dans la liste (sil nest pas dj
tout en bas).
Crez donc un nouvel attribut en cliquant sur le bouton adapt. Vous voyez maintenant
tous les champs du bas qui se dgrisent. Regardons de plus prs ce dont il sagit.
Un premier champ texte est prsent an dy spcier le nom de lattribut (par
exemple, vous pouvez y inscrire force).
En dessous est prsent un champ texte demandant le type de lattribut (sil sagit
dune chane de caractres, dun nombre entier, dun tableau, dun objet, etc.). Par
exemple, pour lattribut cr, vous pouvez entrer int.
Ensuite vient un champ texte demandant la valeur de lattribut. Ce nest pas obli-
gatoire, il faut juste y spcier quelque chose quand on souhaite que notre attribut
ait une valeur par dfaut (par exemple, vous pouvez entrer 0).
Le dernier champ texte est utile si vous souhaitez laisser un commentaire sur lattri-
but (ce que je vous conseille de faire).
Vient ensuite une liste droulante permettant dindiquer la visibilit de lattribut. Il y
a quatre options. Vous reconnatrez parmi elles les trois types de visibilits. En plus de
ces trois types, vous pouvez apercevoir un quatrime type nomm Implmentation.
Ne vous en proccupez pas, vous nen aurez pas besoin.
La dernire option est une case cocher, suivie de Visibilit de la classe. Si vous
cochez cette case, cela voudra dire que votre attribut sera statique.
Entranez-vous crer quelques attributs bidons (ou bien ceux de la classe Personnage
si vous tes cours dide). Vous pourrez ainsi tester les quatre boutons situs droite
du bloc listant les attributs an de bien comprendre leur utilisation (bien que ceci ne
doit pas tre bien dicile, mais sait-on jamais).
Gestion des constantes
Comme je lavais brivement voqu au chapitre prcdent, une constante se reconnat
grce plusieurs signes. En eet, une constante se dclare comme un attribut sur un
diagramme, mais respecte plusieurs contraintes :
Elle doit tre crite en majuscules et les espaces sont remplaces par des underscores
(comme dans votre script PHP).
Elle doit avoir une visibilit publique.
Elle doit tre de type const.
Elle doit tre statique.
Elle doit possder une valeur.
258
MODLISER UNE CLASSE
Je vous ai appris crer des attributs, il serait donc redondant que je vous apprenne
crer des constantes qui ne sont, comme je viens de le dire, que des attributs particuliers.
Gestion des mthodes
Aprs avoir ajout des attributs notre classe, voyons comment lui ajouter des m-
thodes. Pour cela, cliquez sur longlet Oprations (voir la gure 15.6).
Figure 15.6 Fentre permettant de grer les mthodes
Vous constatez dj un peu plus de bazar.
Cependant, si vous observez bien et que vous avez bien suivi la prcdente partie sur la
gestion des attributs, vous ne devriez pas tre entirement dpayss. En eet, en haut,
nous avons toujours notre bloc blanc qui listera, cette fois-ci, nos mthodes, ainsi que
les quatre boutons droite eectuant les mmes oprations. Crez donc une nouvelle
mthode en cliquant simplement sur le bouton Nouveau, comme pour les attributs,
et bidouillons-la un peu.
Pour commencer, je vais parler de cette partie de la fentre (voir la gure 15.7).
La partie encadre de rouge concerne la mthode en elle-mme. Les champs qui restent
concernent ses paramtres, mais on verra a juste aprs.
Les deux premiers champs textes (Nom et Type) ont le mme rle que pour les
attributs (je vous rappelle que le type de la mthode est le type de la valeur renvoye
par celle-ci). Ensuite est prsent un champ texte Strotype. Cela est utile pour
acher une caractristique de la mthode. Par exemple, si une mthode est nale,
on mettra leaf dans ce champ. Nous avons, comme pour les attributs, deux autres
259
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Figure 15.7 Zone permettant de modier la mthode
options qui refont leur apparition : la visibilit et la case cocher Visibilit de la
classe. Inutile de vous rappeler ce que cest je pense (si toutefois vous avez un trou
de mmoire, il vous sut de remonter un tout petit peu dans ce cours). Et pour en
nir sur les points communs avec les attributs, vous pouvez apercevoir tout droite le
champ texte Commentaire qui a aussi pour rle de spcier les commentaires relatifs
la mthode.
Par contre, contrairement aux attributs, nous avons ici deux nouvelles options. La
premire est Type dhritage qui est une liste droulante prsentant trois options :
Abstraite : slectionner si la mthode est abstraite.
Polymorphe (virtuelle) : cela ne nous concerne pas (nous travaillons avec PHP
qui nimplmente pas le concept de mthode virtuelle).
Feuille (nale) : slectionner si la mthode nest ni abstraite, ni nale (contrai-
rement ce qui est indiqu, cela ne veut pas dire que la mthode est nale !).
La deuxime option est une case cocher. ct de celle-ci est crit Requte. Vous
vous demandez sans doute ce quune requte vient faire ici, non? En fait, si vous cochez
cette case, cela veut dire que la mthode ne modiera aucun attribut de la classe. Par
exemple, vos accesseurs (les mthodes tant charges de renvoyer la valeur dattributs
privs ou protgs) sont considrs comme de simples requtes an daccder ces
valeurs. Elles pourront donc avoir cette case de coche.
Entranez-vous crer des mthodes, il est toujours utile de pratiquer.
Maintenant que vous tes prts (enn jespre), nous allons leur ajouter des arguments.
Pour cela, nous allons nous intresser cette partie de la fentre (voir la gure 15.8).
260
MODLISER UNE CLASSE
Figure 15.8 Zone permettant de modier les paramtres de la mthode
Encore une fois, nous remarquons des ressemblances avec tout ce que nous avons vu.
En eet, nous retrouvons notre bloc blanc ainsi que ses quatre boutons, toujours dles
au rendez-vous. Inutile de vous expliquer le rle de chacun hein je crois ! Au cas o vous
nayez pas devin, la gestion des paramtres de la mthode seectue cet endroit.
Crez donc un nouveau paramtre. Les champs de droite, comme leur habitude, se
dgrisent. Je fais une brve description des fonctionnalits des champs, tant donn
que cest du dj vu.
Le champ Nom indique le nom de largument.
Le champ Type spcie le type de largument (entier, chane de caractres, tableau,
etc.).
Le champ Val. par df. rvle la valeur par dfaut de largument.
Le champ Commentaire permet de laisser un petit commentaire concernant lar-
gument.
Loption Direction est inutile.
Gestion du style de la classe
Si vous tes sous Windows, vous avez peut-tre remarqu que tous vos attributs et
mthodes ont le mme style, quels que soient leurs spcicits. Pour remdier ce
problme, il faut modier le style de la classe en cliquant sur longlet Style (voir la
gure 15.9).
Je ne pense pas que de plus amples explications simposent, je ne ferais que rpter ce
261
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Figure 15.9 Fentre permettant de grer le style de la classe
que la fentre vous ache.
Pour modier le style de toutes les classes, slectionnez-les toutes (en cliquant sur
chacune tout en maintenant la touche

Shift ) puis double-cliquez sur lune delle.


Modiez le style dans la fentre ouverte, validez et admirez.
Je crois avoir fait le tour de toutes les fonctionnalits. Pour vous entraner, vous pouvez
essayer de reconstituer la classe Personnage.
Modliser les interactions
Vous avez russi crer une belle classe avec plein dattributs et de mthodes. Je vais
vous demander den crer une autre an de les faire interagir entre elles. Nous allons
voir les quatre interactions abordes dans le prcdent chapitre.
Cration des liaisons
Lhritage
Nous allons crer notre premire interaction : lhritage entre deux classes. Pour cela,
il faut cliquer sur licne reprsentant linteraction de lhritage (voir la gure 15.10).
Ensuite, dirigez-vous sur la partie de droite contenant vos deux classes. Cliquez sur
une croix bleue de la classe mre et, tout en maintenant le clic de la souris enfonc,
262
MODLISER LES INTERACTIONS
Figure 15.10 Modliser un hritage
glissez jusqu une croix bleue de votre classe lle. Lorsque vous dplacez votre che,
le curseur prend la forme dune croix. Une fois quil survole une croix bleue, il est
transform en une autre croix reprsentant trois chemins qui se croisent, et la classe
relie se voit entoure dun pais trait rouge : cest ce moment-l que vous pouvez
lcher le clic.
Normalement, vous devriez avoir obtenu ceci (voir la gure 15.11).
Figure 15.11 Modlisation dun hritage simple
Notez les deux points rouges sur la che ainsi que le point orange au milieu.
Ceux-ci indiquent que la che est slectionne. Il se peut que lun des points
des extrmits de la che soit vert, auquel cas cela signie que ce point nest
pas situ sur une croix bleue ! Si elle nest pas sur une croix bleue, alors elle
nest relie aucune classe. Le point orange du milieu ne bougera jamais
(sauf si vous le dplacez manuellement). La che passera toujours par ce
point. Ainsi, si vous voulez dplacer vos deux classes sans toucher ce point,
votre che fera une trajectoire assez trange.
Alors, premires impressions ? Si vous tes parvenus sans encombre raliser ce quon
vient de faire, les quatre prochaines interactions seront toutes aussi simples !
Les interfaces
Passons maintenant limplmentation dinterfaces. Crez une classe banale et, histoire
que les classes coordonnent avec linteraction, crivez interface dans le champ texte
Strotype de la classe. Pensez aussi dcocher la case Attributs visibles.
Licne reprsentant linteraction de limplmentation dinterface est situe juste
gauche de celui de lhritage (voir la gure 15.12).
Comme pour la prcdente interaction, cliquez sur une des croix bleues de linterface,
puis glissez la souris sur une croix bleue de la classe limplmentant et relchez la souris.
263
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Figure 15.12 Modliser une implmentation dinterface
Si tout sest bien droul, vous devriez avoir obtenu ceci (voir gure 15.13).
Figure 15.13 Exemple dimplmentation dinterface
Lassociation
Voyons maintenant comment modliser lassociation. Cette icne est place juste
droite de licne reprsentant lhritage (voir la gure 15.14).
Figure 15.14 Modliser une association
Cliquez donc sur la croix bleue de la classe ayant un attribut stockant une instance de
lautre classe, puis glissez sur cette autre classe. Vous devriez avoir ceci sous vos yeux
(voir la gure 15.15).
Figure 15.15 Premire tape pour crer une association
Cette fois-ci, les points rouges et le point orange napparaissent pas. Cela
signie simplement que la che nest pas slectionne.
264
MODLISER LES INTERACTIONS
Voyons maintenant comment dnir lassociation et placer les cardinalits. En fait, ce
sont des options de lassociation que lon peut modier en accdant la conguration
de cette liaison. Pour cela, double-cliquez sur la ligne. Vous devriez avoir obtenu cette
fentre (voir la gure 15.16).
Figure 15.16 Paramtrer lassociation
La dnition de lassociation se place dans le premier champ texte correspondant au
nom. Mettez-y par exemple gre . Les cardinalits se placent dans les champs texte
Multiplicity. La premire colonne Side A correspond la classe qui lie lautre
classe. Dans notre exemple, Side A correspond NewsManager et Side B
News. Si vous vous tes bien dbrouills, vous devriez avoir obtenu quelque chose dans
ce genre (voir la gure 15.17).
Figure 15.17 Exemple dune association complte
Lagrgation
Intressons-nous maintenant la che reprsentant lagrgation. On peut le faire de
deux faons direntes. La premire est la mme que la prcdente (jentends par l que
licne sur laquelle cliquer est la mme). Cette fois-ci, il ne faut pas acher le sens de la
liaison et acher un losange. Pour ce faire, il sut de slectionner Aggregation dans la
265
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
liste droulante Type et cliquer sur le gros bouton Oui de la ligne Show direction.
La seconde solution consiste utiliser une autre icne qui construira directement la
che avec le losange. En ralit, il sagit simplement dun raccourci de la premire
solution.
Cette icne se trouve juste droite de la prcdente (voir la gure 15.18).
Figure 15.18 Modliser une agrgation
Je pense quil est inutile de vous dire comment lier vos deux classes tant donn que
le principe reste le mme.
La composition
Enn, terminons par la composition. La composition na pas dicne pour la repr-
senter. Vous pouvez soit cliquer sur licne de lassociation, soit cliquer sur licne de
lagrgation (cette dernire est prfrer dans le sens o une composition se rapproche
beaucoup plus dune agrgation que dune association). Reliez vos classes comme nous
lavons fait jusqu maintenant puis double-cliquez sur celles-ci. Nous allons regarder la
mme liste droulante que celle utilise dans lagrgation : je parle de la liste droulante
Type. lintrieur de celle-ci, choisissez loption Composition puis validez.
Exercice
Vous avez dsormais accumul toutes les connaissances pour faire un diagramme com-
plet. Je vais donc vous demander de me reconstituer le diagramme que je vous ai donn
au dbut (voir la gure 15.19).
Exploiter son diagramme
Vous avez maintenant un diagramme consquent avec un grand nombre dinteractions,
mais quallez vous en faire ? Je vais ici vous expliquer comment exporter votre dia-
gramme dans deux formats dirents bien pratiques. Lun sera sous forme dimage,
lautre sous forme de code PHP.
266
EXPLOITER SON DIAGRAMME
Figure 15.19 Diagramme modlisant le dernier TP
Enregistrer son diagramme
Le chemin menant au dossier dans lequel vous allez enregistrer votre dia-
gramme ne doit pas contenir despace ou de caractres spciaux (lettres ac-
centues, signes spciaux comme le , etc.). Je vous conseille donc (pour
les utilisateurs de Windows), de crer un dossier la racine de votre disque
dur. Sil comporte des caractres spciaux, vous ne pourrez pas double-cliquer
dessus an de le lancer (comme vous faites avec tout autre document), et
sil contient des espaces ou caractres spciaux vous ne pourrez pas exporter
par la suite votre diagramme sous forme de code PHP (et tout autre langage
que propose le logiciel).
Avant toute exportation, il faut sauvegarder son diagramme, sinon cela ne fonctionnera
pas. Pour ce faire, cliquez sur le menu Fichier puis cliquez sur Enregistrer (voir la
gure 15.20).
Ou cliquez sur cette icne (voir la gure 15.21).
Cette fentre souvre vous (voir la gure 15.22).
Tout est crit en franais, je ne pense pas avoir besoin dexpliquer en dtails. Cette
fentre contient un explorateur. gauche sont lists les raccourcis menant aux dossiers
les plus courants (le bureau, les documents, ainsi que les disques), tandis qu droite
sont lists tous les chiers et dossiers prsents dans celui o vous vous trouvez (dans
mon cas, le dossier est vierge).
Le champ texte situ tout en haut contient le nom du chier sous lequel doit tre
267
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Figure 15.20 Enregistrer le diagramme par le menu
Figure 15.21 Enregistrer le diagramme par la barre doutils
Figure 15.22 Fentre permettant denregistrer le diagramme
268
EXPLOITER SON DIAGRAMME
enregistr le diagramme. Une fois que vous avez bien tout paramtr, cliquez sur En-
registrer. Votre diagramme est maintenant enregistr au format .dia.
Exporter son diagramme
Sous forme dimage
Nous avons enregistr notre diagramme, nous sommes maintenant n prts lexporter.
Pour obtenir une image de ce diagramme, nous allons cliquer sur Exporter dans le
menu Fichier (voir la gure 15.23).
Figure 15.23 Exporter le diagramme par le menu
Ou cliquez sur cette icne (voir la gure 15.24).
Figure 15.24 Exporter le diagramme par la barre doutils
Une fentre semblable celle de lenregistrement du diagramme apparat. Cependant,
il y a une liste droulante qui a fait son apparition (voir la gure 15.25).
Figure 15.25 Dterminer le type du chier
Pour exporter votre diagramme sous forme dimage, vous avez deux possibilits. Soit
vous placez lextension .png la n du nom de votre image en haut, soit, dans la
liste droulante, vous slectionnez loption Pixbuf[png] (*.png). Avec cette deuxime
solution, le champ texte du haut contiendra le nom de votre image avec lextension
.png. Cliquez sur Enregistrer, puis contemplez votre image gnre. Pas mal, nest-
ce pas ?
269
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Sous forme de code PHP
Au dbut, la marche suivre reste la mme, cest--dire quil faut aller dans le menu
Fichier puis cliquer sur Exporter. Cette fois-ci, nous allons choisir une autre option
(logique !). Vous pouvez l aussi raliser lopration de deux faons. Soit vous placez
la n du nom du diagramme lextension .code, soit vous slectionnez Filtre de
transformation XSL (*.code) dans la liste droulante. Cliquez sur Enregistrer.
Cette nouvelle fentre apparat (voir la gure 15.26).
Figure 15.26 Dterminer le langage dans lequel sera export le diagramme
Dans la premire liste droulante, choisissez UML-CLASSES-EXTENDED. Dans
la seconde liste, choisissez PHP5 puis cliquez sur Valider. Regardez les nouveaux
chiers gnrs dans votre dossier. Alors, a en jette non ?
Si vous navez pas loption UML-CLASSES-EXTENDED cest que vous
navez pas install comme il faut lextension uml2php5. Relisez donc bien la
premire partie du cours.
En rsum
Dia est un logiciel permettant de crer facilement des diagrammes UML.
Ce logiciel a lavantage de pouvoir exporter vos diagrammes en images et en codes
PHP.
La modlisation grce UML est trs apprcie dans le milieu professionnel, nen
ayez pas peur !
270
Chapitre 16
Les design patterns
Dicult :
D
epuis les dbuts de la programmation, tout un tas de dveloppeurs ont rencontr
dirents problmes de conception. La plupart de ces problmes taient rcurrents.
Pour viter aux autres dveloppeurs de buter sur le mme souci, certains groupes de
dveloppeurs ont dvelopp ce quon appelle des design patterns (ou masques de concep-
tions en franais). Chaque design pattern rpond un problme prcis. Comme nous le
verrons dans ce chapitre, certains problmes reviennent de faon rcurrente et nous allons
utiliser les moyens de conception dj invents pour les rsoudre.
Ce chapitre est donc divis en plusieurs sous-parties o chacune rpond un problme
prcis. Nous procderons ainsi par tude de cas en posant le problme puis en le rsolvant
grce aux moyens de conception connus.
271
CHAPITRE 16. LES DESIGN PATTERNS
Laisser une classe crant les objets : le pattern Factory
Le problme
Admettons que vous venez de crer une application relativement importante. Vous avez
construit cette application en associant plus ou moins la plupart de vos classes entre
elles. prsent, vous voudriez modier un petit morceau de code an dajouter une
fonctionnalit lapplication. Problme : tant donn que la plupart de vos classes sont
plus ou moins lies, il va falloir modier un tas de chose ! Le pattern Factory pourra
srement vous aider.
Ce motif est trs simple construire. En fait, si vous implmentez ce pattern, vous
naurez plus de new placer dans la partie globale du script an dinstancier une
classe. En eet, ce ne sera pas vous de le faire mais une classe usine. Cette classe
aura pour rle de charger les classes que vous lui passez en argument. Ainsi, quand vous
modierez votre code, vous naurez qu modier le masque dusine pour que la plupart
des modications prennent eet. En gros, vous ne vous soucierez plus de linstanciation
de vos classes, ce sera lusine de le faire !
Voici comment se prsente une classe implmentant le pattern Factory :
1 <?php
2 class DBFactory
3 {
4 public static function load($sgbdr)
5 {
6 $classe = 'SGBDR_ ' . $sgbdr;
7
8 if (file_exists($chemin = $classe . '.class.php'))
9 {
10 require $chemin;
11 return new $classe;
12 }
13 else
14 {
15 throw new RuntimeException('La classe <strong >' . $classe
. '</strong > n\'a pu tre trouve !');
16 }
17 }
18 }
19 ?>
Dans votre script, vous pourrez donc faire quelque chose de ce genre :
1 <?php
2 try
3 {
4 $mysql = DBFactory ::load('MySQL');
5 }
6 catch (RuntimeException $e)
7 {
272
COUTER SES OBJETS : LE PATTERN OBSERVER
8 echo $e->getMessage ();
9 }
10 ?>
Exemple concret
Le but est de crer une classe qui nous distribuera les objets PDO plus facilement.
Nous allons partir du principe que vous avez plusieurs SGBDR, ou plusieurs BDD qui
utilisent des identiants dirents. Pour rsumer, nous allons tout centraliser dans une
classe.
1 <?php
2 class PDOFactory
3 {
4 public static function getMysqlConnexion ()
5 {
6 $db = new PDO('mysql:host=localhost;dbname=tests ', 'root',
'');
7 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_EXCEPTION
);
8
9 return $db;
10 }
11
12 public static function getPgsqlConnexion ()
13 {
14 $db = new PDO('pgsql:host=localhost;dbname=tests ', 'root',
'');
15 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_EXCEPTION
);
16
17 return $db;
18 }
19 }
20 ?>
Ceci vous simpliera normment la tche. Si vous avez besoin de modier vos identi-
ants de connexion, vous naurez pas aller chercher dans tous vos scripts : tout sera
plac dans notre factory.
couter ses objets : le pattern Observer
Le problme
Dans votre script est prsente une classe qui soccupe de la gestion dun module. Lors
dune action prcise, vous excutez une ou plusieurs instructions. Celles-ci nont quune
seule chose en commun : le fait quelles soient appeles car telle action sest produite.
273
CHAPITRE 16. LES DESIGN PATTERNS
Elles ont t places dans la mthode parce quil faut bien les appeler et quon sait
pas o les mettre . Il est intressant dans ce cas-l de sparer les direntes actions
eectues lorsque telle action survient. Pour cela, nous allons regarder du ct du
pattern Observer.
Le principe est simple : vous avez une classe observe et une ou plusieurs autre(s)
classe(s) qui lobserve(nt). Lorsque telle action survient, vous allez prvenir toutes
les classes qui lobservent. Nous allons, pour une raison dhomognit, utiliser les
interfaces prdnies de la SPL
1
. Il sagit dune librairie standard qui est fournie doce
avec PHP. Elle contient direntes classes, fonctions, interfaces, etc. Vous vous en tes
dj servi en utilisant spl_autoload_register().
Attardons nous plutt sur ce qui nous intresse, savoir deux interfaces : SplSubject
et SplObserver.
La premire interface, SplSubject, est linterface implmente par lobjet observ. Elle
contient trois mthodes :
attach (SplObserver $observer) : mthode appele pour ajouter une classe ob-
servatrice notre classe observe.
detach (SplObserver $observer) : mthode appele pour supprimer une classe
observatrice.
notify() : mthode appele lorsque lon aura besoin de prvenir toutes les classes
observatrices que quelque chose sest produit.
Linterface SplObserver est linterface implmente par les dirents observateurs. Elle
ne contient quune seule mthode qui est celle appele par la classe observe dans la
mthode notify() : il sagit de update ( (SplSubject $subject)).
Voici un diagramme mettant en uvre ce design pattern (voir la gure 16.1).
Figure 16.1 Diagramme modlisant une mise en place du design pattern Observer
Nous allons maintenant imaginer le code correspondant au diagramme. Commenons
par la classe observe :
1 <?php
2 class Observee implements SplSubject
1. Standard PHP Library
274
COUTER SES OBJETS : LE PATTERN OBSERVER
3 {
4 // Ceci est le tableau qui va contenir tous les objets qui
nous observent.
5 protected $observers = array ();
6
7 // Ds que cet attribut changera on notifiera les classes
observatrices.
8 protected $nom;
9
10 public function attach(SplObserver $observer)
11 {
12 $this ->observers [] = $observer;
13 }
14
15 public function detach(SplObserver $observer)
16 {
17 if (is_int($key = array_search($observer , $this ->observers ,
true)))
18 {
19 unset($this ->observers[$key]);
20 }
21 }
22
23 public function notify ()
24 {
25 foreach ($this ->observers as $observer)
26 {
27 $observer ->update($this);
28 }
29 }
30
31 public function getNom ()
32 {
33 return $this ->nom;
34 }
35
36 public function setNom($nom)
37 {
38 $this ->nom = $nom;
39 $this ->notify ();
40 }
41 }
42 ?>
Vous pouvez constater la prsence du nom des interfaces en guise dargument.
Cela signie que cet argument doit implmenter linterface spcie.
Voici les deux classes observatrices :
275
CHAPITRE 16. LES DESIGN PATTERNS
1 <?php
2 class Observer1 implements SplObserver
3 {
4 public function update(SplSubject $obj)
5 {
6 echo __CLASS__ , ' a t notifi ! Nouvelle valeur de l\'
attribut <strong >nom </strong > : ', $obj ->getNom ();
7 }
8 }
9
10 class Observer2 implements SplObserver
11 {
12 public function update(SplSubject $obj)
13 {
14 echo __CLASS__ , ' a t notifi ! Nouvelle valeur de l\'
attribut <strong >nom </strong > : ', $obj ->getNom ();
15 }
16 }
17 ?>
Ces deux classes font exactement la mme chose, ce ntait qu titre dexemple basique
que je vous ai donn ces exemples, an que vous puissiez vous rendre compte de la
syntaxe de base lors de lutilisation du pattern Observer.
Pour tester nos classes, vous pouvez utiliser ce bout de code :
1 <?php
2 $o = new Observee;
3 $o->attach(new Observer1); // Ajout d'un observateur.
4 $o->attach(new Observer2); // Ajout d'un autre observateur.
5 $o->setNom('Victor '); // On modifie le nom pour voir si les
classes observatrices ont bien t notifies.
6 ?>
Vous pouvez constater quajouter des classes observatrices de cette faon peut tre
assez long si on en a cinq ou six. Il y a une petite technique qui consiste pouvoir
obtenir ce genre de code :
1 <?php
2 $o = new Observee;
3
4 $o->attach(new Observer1)
5 ->attach(new Observer2)
6 ->attach(new Observer3)
7 ->attach(new Observer4)
8 ->attach(new Observer5);
9
10 $o->setNom('Victor '); // On modifie le nom pour voir si les
classes observatrices ont bien t notifies.
11 ?>
276
COUTER SES OBJETS : LE PATTERN OBSERVER
Pour eectuer ce genre de manuvres, la mthode attach() doit retourner linstance
qui la appel (en dautres termes, elle doit retourner $this).
Exemple concret
Regardons un exemple concret prsent. Nous allons imaginer que vous avez, dans
votre script, une classe grant les erreurs gnres par PHP. Lorsquune erreur est
gnre, vous aimeriez quil se passe deux choses :
Que lerreur soit enregistre en BDD.
Que lerreur vous soit envoye par mail.
Pour cela, vous pensez donc coder une classe comportant une mthode attrapant ler-
reur et eectuant les deux oprations ci-dessus. Grave erreur ! Ceci est surtout vi-
ter : votre classe est charge dintercepter les erreurs, et non de les grer ! Ce sera
dautres classes de sen occuper : ces classes vont observer la classe grant lerreur et
une fois notie, elles vont eectuer laction pour laquelle elles ont t conues. Vous
voyez un peu la tte quaura le script ?
Rappel : pour intercepter les erreurs, il vous faut utiliser
set_error_handler(). Pour faire en sorte que la fonction de call-
back appele lors de la gnration dune erreur soit une mthode dune
classe, passez un tableau deux entres en premier argument. La premire
entre est lobjet sur lequel vous allez appeler la mthode, et la seconde est
le nom de la mthode.
Vous tes capables de le faire tout seul. Voici la correction.
ErrorHandler : classe grant les erreurs
1 <?php
2 class ErrorHandler implements SplSubject
3 {
4 // Ceci est le tableau qui va contenir tous les objets qui
nous observent.
5 protected $observers = array ();
6
7 // Attribut qui va contenir notre erreur formate.
8 protected $formatedError;
9
10 public function attach(SplObserver $observer)
11 {
12 $this ->observers [] = $observer;
13 return $this;
14 }
15
16 public function detach(SplObserver $observer)
17 {
277
CHAPITRE 16. LES DESIGN PATTERNS
18 if (is_int($key = array_search($observer , $this ->observers ,
true)))
19 {
20 unset($this ->observers[$key]);
21 }
22 }
23
24 public function getFormatedError ()
25 {
26 return $this ->formatedError;
27 }
28
29 public function notify ()
30 {
31 foreach ($this ->observers as $observer)
32 {
33 $observer ->update($this);
34 }
35 }
36
37 public function error($errno , $errstr , $errfile , $errline)
38 {
39 $this ->formatedError = '[' . $errno . '] ' . $errstr . "\n"
. 'Fichier : ' . $errfile . ' (ligne ' . $errline . ')'
;
40 $this ->notify ();
41 }
42 }
43 ?>
MailSender : classe soccupant denvoyer les mails
1 <?php
2 class MailSender implements SplObserver
3 {
4 protected $mail;
5
6 public function __construct($mail)
7 {
8 if (preg_match(' ^[a-z0-9._-]+@[a-z0 -9._-]{2,}\.[a-z]{2,4}$
', $mail))
9 {
10 $this ->mail = $mail;
11 }
12 }
13
14 public function update(SplSubject $obj)
15 {
16 mail($this ->mail , 'Erreur dtecte !', 'Une erreur a t d
tecte sur le site. Voici les informations de celle -ci :
278
COUTER SES OBJETS : LE PATTERN OBSERVER
' . "\n" . $obj ->getFormatedError ());
17 }
18 }
19 ?>
BDDWriter : classe soccupant de lenregistrement en BDD
1 <?php
2 class BDDWriter implements SplObserver
3 {
4 protected $db;
5
6 public function __construct(PDO $db)
7 {
8 $this ->db = $db;
9 }
10
11 public function update(SplSubject $obj)
12 {
13 $q = $this ->db ->prepare('INSERT INTO erreurs SET erreur = :
erreur ');
14 $q ->bindValue(':erreur ', $obj ->getFormatedError ());
15 $q ->execute ();
16 }
17 }
18 ?>
Testons notre code !
1 <?php
2 $o = new ErrorHandler; // Nous crons un nouveau gestionnaire d
'erreur.
3 $db = PDOFactory :: getMysqlConnexion ();
4
5 $o ->attach(new MailSender('login@fai.tld'))
6 ->attach(new BDDWriter($db));
7
8 set_error_handler(array($o, 'error')); // Ce sera par la m
thode error () de la classe ErrorHandler que les erreurs
doivent tre traites.
9
10 5 / 0; // Gnrons une erreur
11 ?>
Daccord, cela en fait du code ! Je ne sais pas si vous vous en rendez compte, mais ce que
nous venons de crer l est une excellente manire de coder. Nous venons de sparer
notre code comme il se doit et nous pourrons le modier aisment car les direntes
actions ont t spares avec logique.
279
CHAPITRE 16. LES DESIGN PATTERNS
Sparer ses algorithmes : le pattern Strategy
Le problme
Vous avez une classe ddie une tche spcique. Dans un premier temps, celle-ci
eectue une opration suivant un algorithme bien prcis. Cependant, avec le temps,
cette classe sera amene voluer, et elle suivra plusieurs algorithmes, tout en eectuant
la mme tche de base. Par exemple, vous avez une classe FileWriter qui a pour rle
dcrire dans un chier ainsi quune classe DBWriter. Dans un premier temps, ces classes
ne contiennent quune mthode write() qui ncrira que le texte pass en paramtre
dans le chier ou dans la BDD. Au l du temps, vous vous rendez compte que cest
dommage quelles ne fassent que a et vous aimeriez bien quelles puissent crire en
dirents formats (HTML, XML, etc.) : les classes doivent donc formater puis crire.
Cest ce moment quil est intressant de se tourner vers le pattern Strategy. En
eet, sans ce design pattern, vous seriez obligs de crer deux classes direntes pour
crire au format HTML par exemple : HTMLFileWriter et HTMLDBWriter. Pourtant,
ces deux classes devront formater le texte de la mme faon : nous assisterons une
duplication du code, la pire chose faire dans un script ! Imaginez que vous voulez
modier lalgorithme dupliqu une dizaine de fois. . . Pas trs pratique nest-ce pas ?
Exemple concret
Passons directement lexemple concret. Nous allons suivre lide que nous avons
voque linstant : laction dcrire dans un chier ou dans une BDD. Il y aura pas
mal de classes crer donc au lieu de vous faire un grand discours, je vais dtailler le
diagramme reprsentant lapplication (voir la gure 16.2).
Figure 16.2 Diagramme modlisant une mise en place du design pattern Strategy
a en fait des classes ! Pourtant (je vous assure) le principe est trs simple comprendre.
La classe Writer est abstraite (a naurait aucun sens de linstancier : on veut crire,
280
SPARER SES ALGORITHMES : LE PATTERN STRATEGY
daccord, mais sur quel support ?) et implmente un constructeur qui acceptera un
argument : il sagit du formateur que lon souhaite utiliser. Nous allons aussi placer
une mthode abstraite write(), ce qui forcera toutes les classes lles de Writer
implmenter cette mthode qui appellera la mthode format() du formateur associ
(instance contenue dans lattribut $formater) an de rcuprer le texte format. Allez,
au boulot !
Commenons par linterface. Rien de bien compliqu, elle ne contient quune seule
mthode :
1 <?php
2 interface Formater
3 {
4 public function format($text);
5 }
6 ?>
Ensuite vient la classe abstraite Writer que voici :
1 <?php
2 abstract class Writer
3 {
4 // Attribut contenant l'instance du formateur que l'on veut
utiliser.
5 protected $formater;
6
7 abstract public function write($text);
8
9 // Nous voulons une instance d'une classe implmentant
Formater en paramtre.
10 public function __construct(Formater $formater)
11 {
12 $this ->formater = $formater;
13 }
14 }
15 ?>
Nous allons maintenant crer deux classes hritant de Writer : FileWriter et DBWriter.
1 <?php
2 class DBWriter extends Writer
3 {
4 protected $db;
5
6 public function __construct(Formater $formater , PDO $db)
7 {
8 parent :: __construct($formater);
9 $this ->db = $db;
10 }
11
12 public function write ($text)
13 {
281
CHAPITRE 16. LES DESIGN PATTERNS
14 $q = $this ->db ->prepare('INSERT INTO lorem_ipsum SET text =
:text');
15 $q->bindValue(':text', $this ->formater ->format($text));
16 $q->execute ();
17 }
18 }
19 ?>
1 <?php
2 class FileWriter extends Writer
3 {
4 // Attribut stockant le chemin du fichier.
5 protected $file;
6
7 public function __construct(Formater $formater , $file)
8 {
9 parent :: __construct($formater);
10 $this ->file = $file;
11 }
12
13 public function write($text)
14 {
15 $f = fopen($this ->file , 'w');
16 fwrite($f, $this ->formater ->format($text));
17 fclose($f);
18 }
19 }
20 ?>
Enn, nous avons nos trois formateurs. Lun ne fait rien de particulier (TextFormater),
et les deux autres formatent le texte en deux langages dirents (HTMLFormater et
XMLFormater). Jai dcid dajouter le timestamp dans le formatage du texte histoire
que le code ne soit pas compltement inutile (surtout pour la classe qui ne fait pas de
formatage particulier).
1 <?php
2 class TextFormater implements Formater
3 {
4 public function format($text)
5 {
6 return 'Date : ' . time() . "\n" . 'Texte : ' . $text;
7 }
8 }
9 ?>
1 <?php
2 class HTMLFormater implements Formater
3 {
4 public function format($text)
5 {
282
UNE CLASSE, UNE INSTANCE : LE PATTERN SINGLETON
6 return '<p>Date : ' . time() . '<br />' ."\n". 'Texte : ' .
$text . '</p>';
7 }
8 }
9 ?>
1 <?php
2 class XMLFormater implements Formater
3 {
4 public function format($text)
5 {
6 return '<?xml version ="1.0" encoding ="ISO -8859 -1"?>' ."\n".
7 '<message >' ."\n".
8 "\t". '<date >' . time() . '</date >' ."\n".
9 "\t". '<texte >' . $text . '</texte >' ."\n".
10 '</message >';
11 }
12 }
13 ?>
Et testons enn notre code :
1 <?php
2 function autoload($class)
3 {
4 if (file_exists($path = $class . '.class.php') || file_exists
($path = $class . '.interface.php'))
5 {
6 require $path;
7 }
8 }
9
10 spl_autoload_register('autoload ');
11
12 $writer = new FileWriter(new HTMLFormater , 'file.html');
13 $writer ->write('Hello world !');
14 ?>
Ce code de base a lavantage dtre trs exible. Il peut paratre un peu imposant pour
notre utilisation, mais si lapplication est amene obtenir beaucoup de fonctionnalits
supplmentaires, nous aurons dj prpar le terrain !
Une classe, une instance : le pattern Singleton
Nous allons terminer par un pattern qui est en gnral le premier quon vous prsente.
Si je ne vous lai pas prsent au dbut cest parce que je veux que vous fassiez attention
en lemployant car il peut tre trs mal utilis et se transformer en mauvaise pratique.
On considrera alors le pattern comme un anti-pattern . Cependant, il est trs connu
283
CHAPITRE 16. LES DESIGN PATTERNS
et par consquent il est essentiel de le connatre et de savoir pourquoi il ne faut pas
lutiliser dans certains contextes.
Le problme
Nous avons une classe qui ne doit tre instancie quune seule fois. premire vue, a
vous semble impossible et cest normal. Jusqu prsent, nous pouvions faire de mul-
tiples $obj = new Classe; jusqu linni, et nous nous retrouvions avec une innit
dinstances de Classe. Il va donc falloir empcher ceci.
Pour empcher la cration dune instance de cette faon, cest trs simple : il sut de
mettre le constructeur de la classe en priv ou en protg !
Tes marrant toi, on ne pourra jamais crer dinstance avec cette technique !
Bien sr que si ! Nous allons crer une instance de notre classe lintrieur delle-
mme ! De cette faon nous aurons accs au constructeur.
Oui mais voil, il ne va falloir crer quune seule instance. . . Nous allons donc crer un
attribut statique dans notre classe qui contiendra. . . linstance de cette classe ! Nous
aurons aussi une mthode statique qui aura pour rle de renvoyer cette instance. Si on
lappelle pour la premire fois, alors il faut instancier la classe puis retourner lobjet,
sinon on se contente de le retourner.
Il reste un petit dtail rgler. Nous voulons vraiment une seule instance, et l, il
est encore possible den avoir plusieurs. En eet, rien nempche lutilisateur de cloner
linstance ! Il faut donc bien penser interdire laccs la mthode __clone().
Ainsi, une classe implmentant le pattern Singleton ressemblerait ceci :
1 <?php
2 class MonSingleton
3 {
4 protected static $instance; // Contiendra l'instance de notre
classe.
5
6 protected function __construct () { } // Constructeur en priv
.
7 protected function __clone () { } // Mthode de clonage en
priv aussi.
8
9 public static function getInstance ()
10 {
11 if (!isset(self:: $instance)) // Si on n'a pas encore
instanci notre classe.
12 {
13 self:: $instance = new self; // On s'instancie nous -mmes.
14 }
284
LINJECTION DE DPENDANCES
15
16 return self:: $instance;
17 }
18 }
19 ?>
Ceci est le strict minimum. vous dimplmenter de nouvelles mthodes comme vous
lauriez fait dans votre classe normale.
Voici donc une utilisation de la classe :
1 <?php
2 $obj = MonSingleton :: getInstance (); // Premier appel : instance
cre.
3 $obj ->methode1 ();
4 ?>
Exemple concret
Un exemple concret pour le pattern Singleton? Non, dsol, nous allons devoir nous
en passer. :-
Quoi ? Tu te moques de nous ? Alors il sert rien ce design pattern ?
Selon moi, non. Je nai encore jamais eu besoin de lutiliser. Ce pattern doit tre employ
uniquement si plusieurs instanciations de la classe provoquaient un dysfonctionnement.
Si le script peut continuer normalement alors que plusieurs instances sont cres, le
pattern Singleton ne doit pas tre utilis.
Donc ce que nous avons appris l, a ne sert rien ?
Non. Il est important de connatre ce design pattern, non pas pour lutiliser, mais au
contraire pour ne pas y avoir recours, et surtout savoir pourquoi. Cependant, avant de
vous rpondre, je vais vous prsenter un autre pattern trs important : linjection de
dpendances.
Linjection de dpendances
Comme tout pattern, celui-ci est n cause dun problme souvent rencontr par les
dveloppeurs : le fait davoir de nombreuses classes dpendantes les unes des autres.
Linjection de dpendances consiste dcoupler nos classes. Le pattern singleton que
nous venons de voir favorise les dpendances, et linjection de dpendances palliant ce
285
CHAPITRE 16. LES DESIGN PATTERNS
problme, il est intressant dtudier ce nouveau pattern avec celui que nous venons de
voir.
Soit le code suivant :
1 <?php
2 class NewsManager
3 {
4 public function get($id)
5 {
6 // On admet que MyPDO tend PDO et qu'il implmente un
singleton.
7 $q = MyPDO:: getInstance ()->query('SELECT id, auteur , titre ,
contenu FROM news WHERE id = '.(int)$id);
8
9 return $q ->fetch(PDO:: FETCH_ASSOC);
10 }
11 }
Vous vous apercevez quici, le singleton a introduit une dpendance entre deux classes
nappartenant pas au mme module. Deux modules ne doivent jamais tre lis de cette
faon, ce qui est le cas dans cet exemple. Deux modules doivent tre indpendants
lun de lautre. Dailleurs, en y regardant de plus prs, cela ressemble fortement
une variable globale. En eet, un singleton nest rien dautre quune variable globale
dguise (il y a juste une tape en plus pour accder la variable) :
1 <?php
2 class NewsManager
3 {
4 public function get($id)
5 {
6 global $db;
7 // Revient EXACTEMENT au mme que :
8 $db = MyPDO:: getInstance ();
9
10 // Suite des oprations.
11 }
12 }
Vous ne voyez pas o est le problme ? Souvenez-vous de lun des points forts de la
POO : le fait de pouvoir redistribuer sa classe ou la rutiliser. Dans le cas prsent,
cest impossible, car notre classe NewsManager dpend de MyPDO. Quest-ce qui vous
dit que la personne qui utilisera NewsManager aura cette dernire ? Rien du tout, et
cest normal. Nous sommes ici face une dpendance cre par le singleton. De plus,
la classe dpend aussi de PDO : il y avait donc dj une dpendance au dbut, et le
pattern Singleton en a cr une autre. Il faut donc supprimer ces deux dpendances.
Comment faire alors ?
286
LINJECTION DE DPENDANCES
Ce quil faut, cest passer notre DAO
2
au constructeur, sauf que notre classe ne doit
pas tre dpendante dune quelconque bibliothque. Ainsi, notre objet peut trs bien
utiliser PDO, MySQLi ou que sais-je encore, la classe se servant de lui doit fonctionner
de la mme manire. Alors comment procder ? Il faut imposer un comportement
spcique notre objet en lobligeant implmenter certaines mthodes. Je ne
vous fais pas attendre : les interfaces sont l pour a. Nous allons donc crer une
interface iDB contenant (pour faire simple) quune seule mthode : query().
1 <?php
2 interface iDB
3 {
4 public function query($query);
5 }
Pour que lexemple soit parlant, nous allons crer deux classes utilisant cette structure,
lune utilisant PDO et lautre MySQLi. Cependant, un problme se pose : le rsul-
tat retourn par la mthode query() des classes PDO et MySQLi sont des instances
de deux classes direntes, les mthodes disponibles ne sont, par consquent, pas les
mmes. Il faut donc crer dautres classes pour grer les rsultats qui suivent eux aussi
une structure dnie par une interface (admettons iResult).
1 <?php
2 interface iResult
3 {
4 public function fetchAssoc ();
5 }
Nous pouvons donc prsent crire nos quatre classes : MyPDO, MyMySQLi, MyPDOStatement
et MyMySQLiResult.
1 <?php
2 class MyPDO extends PDO implements iDB
3 {
4 public function query($query)
5 {
6 return new MyPDOStatement(parent :: query($query));
7 }
8 }
1 <?php
2 class MyPDOStatement implements iResult
3 {
4 protected $st;
5
6 public function __construct(PDOStatement $st)
7 {
8 $this ->st = $st;
9 }
10
2. Database Access Object
287
CHAPITRE 16. LES DESIGN PATTERNS
11 public function fetchAssoc ()
12 {
13 return $this ->st ->fetch(PDO:: FETCH_ASSOC);
14 }
15 }
1 <?php
2 class MyMySQLi extends MySQLi implements iDB
3 {
4 public function query($query)
5 {
6 return new MyMySQLiResult(parent :: query($query));
7 }
8 }
1 <?php
2 class MyMySQLiResult implements iResult
3 {
4 protected $st;
5
6 public function __construct(MySQLi_Result $st)
7 {
8 $this ->st = $st;
9 }
10
11 public function fetchAssoc ()
12 {
13 return $this ->st ->fetch_assoc ();
14 }
15 }
Nous pouvons donc maintenant crire notre classe NewsManager. Noubliez pas de vri-
er que les objets sont bien des instances de classes implmentant les interfaces dsires.
1 <?php
2 class NewsManager
3 {
4 protected $dao;
5
6 // On souhaite un objet instanciant une classe qui implmente
iDB.
7 public function __construct(iDB $dao)
8 {
9 $this ->dao = $dao;
10 }
11
12 public function get($id)
13 {
14 $q = $this ->dao ->query('SELECT id, auteur , titre , contenu
FROM news WHERE id = '.(int)$id);
15
288
LINJECTION DE DPENDANCES
16 // On vrifie que le rsultat implmente bien iResult.
17 if (!$q instanceof iResult)
18 {
19 throw new Exception('Le rsultat d\'une requte doit tre
un objet implmentant iResult ');
20 }
21
22 return $q->fetchAssoc ();
23 }
24 }
Testons maintenant notre code.
1 <?php
2 $dao = new MyPDO('mysql:host=localhost;dbname=news', 'root', ''
);
3 // $dao = new MyMySQLi('localhost ', 'root ', '', 'news ');
4
5 $manager = new NewsManager($dao);
6 print_r($manager ->get(2));
Je vous laisse commenter les deux premires lignes pour vrier que les deux fonc-
tionnent. Aprs quelques tests, vous vous rendrez compte que nous avons bel et bien d-
coupl nos classes ! Il ny a ainsi plus aucune dpendance entre notre classe NewsManager
et une quelconque autre classe.
Le problme, dans notre cas, cest quil est dicile de faire de linjection
de dpendances pour quune classe supporte toutes les bibliothques daccs
aux BDD (PDO, MySQLi, etc.) cause des rsultats des requtes. De son
ct, PDO a la classe PDOStatement, tandis que MySQLi a MySQLi_STMT
pour les requtes prpares et MySQLi_Result pour les rsultats de requtes
classiques. Cela est donc dicile de les conformer au mme modle. Nous
allons donc, dans le TP venir, utiliser une autre technique pour dcoupler
nos classes.
Pour conclure
Le principal problme du singleton est de favoriser les dpendances entre deux classes. Il
faut donc tre trs mant de ce ct-l, car votre application deviendra dicilement
modiable et lon perd alors les avantages de la POO. Je vous recommande donc
dutiliser le singleton en dernier recours : si vous dcidez dimplmenter ce pattern, cest
pour garantir que cette classe ne doit tre instancie quune seule fois. Si vous vous
rendez compte que deux instances ou plus ne causent pas de problme lapplication,
alors nimplmentez pas le singleton. Et par piti : nimplmentez pas un singleton
pour lutiliser comme une variable globale ! Cest la pire des choses faire car
cela favorise les dpendances entre classes comme nous lavons vu.
289
CHAPITRE 16. LES DESIGN PATTERNS
Si vous voulez en savoir plus sur linjection de dpendances (notamment sur lutilisation
de conteneurs), je vous invite lire cet excellent tutoriel :

Cours sur linjection de d-


pendances en PHP
Code web : 501916
En rsum
Un design pattern est un moyen de conception rpondant un problme rcurrent.
Le pattern factory a pour but de laisser des classes usine crer les instances votre
place.
Le pattern observer permet de lier certains objets des couteurs eux-mmes
chargs de notier les objets auxquels ils sont rattachs.
Le pattern strategy sert dlocaliser la partie algorithmique dune mthode an de
le permettre rutilisable, vitant ainsi la duplication de cet algorithme.
Le pattern singleton permet de pouvoir instancier une classe une seule et unique fois,
ce qui prsente quelques soucis au niveau des dpendances entre classes.
Le pattern injection de dpendances a pour but de rendre le plus indpendantes
possible les classes.
290
Chapitre 17
TP : un systme de news
Dicult :
L
a plupart des sites web dynamiques proposent un systme dactualits. Je sais par
consquent que cest lexemple auquel la plupart dentre vous sattend : nous allons
donc raliser ici un systme de news ! Cela sera le dernier petit TP permettant de
clarier vos acquis avant celui plus important qui vous attend. Ce TP est aussi loccasion
pour vous de voir un exemple concret qui vous sera utile pour votre site !
291
CHAPITRE 17. TP : UN SYSTME DE NEWS
Ce que nous allons faire
Cahier des charges
Commenons par dnir ce que nous allons faire et surtout, ce dont nous allons avoir
besoin.
Ce que nous allons raliser est trs simple, savoir un systme de news basique avec
les fonctionnalits suivantes :
Achage des cinq premires news laccueil du site avec texte rduit 200 caractres.
Possibilit de cliquer sur le titre de la news pour la lire entirement. Lauteur et la
date dajout apparatront, ainsi que la date de modication si la news a t modie.
Un espace dadministration qui permettra dajouter / modier / supprimer des news.
Cet espace tient sur une page : il y a un formulaire et un tableau en-dessous listant
les news avec des liens modier / supprimer. Quand on clique sur Modier , le
formulaire se pr-remplit.
Pour raliser cela, nous allons avoir besoin de crer une table news dont la structure
est la suivante :
1 CREATE TABLE news (
2 id smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
3 auteur varchar(30) NOT NULL ,
4 titre varchar(100) NOT NULL ,
5 contenu text NOT NULL ,
6 dateAjout datetime NOT NULL ,
7 dateModif datetime NOT NULL ,
8 PRIMARY KEY (id )
9 );
Concernant lorganisation des classes, nous allons suivre la mme structure que pour
les personnages, savoir :
Une classe News qui contiendra les champs sous forme dattributs. Son rle sera de
reprsenter une news.
Une classe NewsManager qui grera les news. Cest elle qui interagira avec la BDD.
Cependant, je voudrais vous enseigner quelque chose de nouveau. Je voudrais que la
classe NewsManager ne soit pas dpendante de PDO. Nous avons vu dans le chapitre pr-
cdent que linjection de dpendances pouvait tre intressante mais nous compliquait
trop la tche concernant ladaptation des DAO. Au lieu dinjecter la dpendance, nous
allons plutt crer des classes spcialises, cest--dire qu chaque API correspondra
une classe. Par exemple, si nous voulons assurer la compatibilit de notre systme de
news avec lAPI PDO et MySQLi, alors nous aurons deux managers dirents, lun
eectuant les requtes avec PDO, lautre avec MySQLi. Nanmoins, tant donn que
ces classes ont une nature en commun (celle dtre toutes les deux des managers de
news), alors elles devront hriter dune classe reprsentant cette nature.

Rsultat obtenir
Code web : 954276
292
CE QUE NOUS ALLONS FAIRE
Retour sur le traitement des rsultats
Je vais vous demander dessayer de vous rappeler le dernier TP, celui sur les per-
sonnages, et, plus prcisment, la manire dont nous rcuprions les rsultats. Nous
obtenions quelque chose comme a :
1 <?php
2 // On admet que $db est une instance de PDO
3
4 $q = $db ->prepare('SELECT id, nom , degats FROM personnages
WHERE nom <> :nom ORDER BY nom');
5 $q ->execute(array(':nom' => $nom));
6
7 while ($donnees = $q ->fetch(PDO:: FETCH_ASSOC))
8 {
9 $persos [] = new Personnage($donnees);
10 }
Comme vous pouvez le constater, on rcupre la liste des personnages sous forme
de tableau grce la constante PDO::FETCH_ASSOC, puis on instancie notre classe
Personnage en passant ce tableau au constructeur. Je vais vous dvoiler un moyen
plus simple et davantage optimis pour eectuer cette opration. je ne lai pas abord
prcdemment car je trouvais quil tait un peu tt pour vous en parler ds la premire
partie.
Avec PDO (comme avec MySQLi, mais nous le verrons plus tard), il existe une constante
qui signie retourne-moi le rsultat dans un objet (au mme titre que la constante
PDO::FETCH_ASSOC signie retourne-moi le rsultat dans un tableau ). Cette constante
est tout simplement PDO::FETCH_CLASS. Comment sutilise-t-elle ? Comme vous vous
en doutez peut-tre, si nous voulons que PDO nous retourne les rsultats sous forme
dinstances de notre classe News, il va falloir lui dire ! Or, nous ne pouvons pas lui dire le
nom de notre classe directement dans la mthode fetch() comme nous le faisons avec
PDO::FETCH_ASSOC. Nous allons nous servir dune autre mthode, setFetchMode() :

setFetchMode()
Code web : 728845
Si vous avez lu les toutes premires lignes de la page pointe par le code web que jai
donn, vous devriez savoir utiliser cette mthode :
1 <?php
2 // On admet que $db est une instance de PDO
3
4 $q = $db ->prepare('SELECT id, nom , degats FROM personnages
WHERE nom <> :nom ORDER BY nom');
5 $q ->execute(array(':nom' => $nom));
6
7 $q ->setFetchMode(PDO:: FETCH_CLASS , 'Personnage ');
8
9 $persos = $q ->fetchAll ();
293
CHAPITRE 17. TP : UN SYSTME DE NEWS
Notez quil nest plus utile de parcourir chaque rsultat pour les stocker dans
un tableau puisque nous navons aucune opration eectuer. Un simple
appel fetchAll() sut donc amplement.
Comme vous le voyez, puisque nous avons dit PDO la ligne 7 que nous voulions une
instance de Personnage en guise de rsultat, il nest pas utile de spcier de nouveau
quoi que ce soit lorsquon appelle la mthode fetchAll().
Vous pouvez essayer ce code et vous verrez que a fonctionne. Maintenant, je vais vous
demander deectuer un petit test. Achez la valeur des attributs dans le constructeur
de News (avec des simples echo, nallez pas chercher bien loin). Lancez de nouveau le
script. Sur votre cran vont alors sacher les valeurs que PDO a assignes notre
objet. Rien ne vous titille ? Dois-je vous rappeler que, normalement, le constructeur
dune classe est appel en premier et quil a pour rle principal dinitialiser lobjet ?
Pensez-vous que ce rle est accompli alors que les valeurs ont t assignes avant
mme que le constructeur soit appel ? Si vous aviez un constructeur qui devait
assigner des valeurs par dfaut aux attributs, celui-ci aurait cras les valeurs assignes
par PDO.
Pour rsumer, mmorisez quavec cette technique, lobjet nest pas instanci comme il
se doit. Lobjet est cr (sans que le constructeur soit appel), puis les valeurs
sont assignes aux attributs par PDO, enn le constructeur est appel.
Pour tout remettre dans lordre, il y a une autre constante : PDO::FETCH_PROPS_LATE.
Elle va de paire avec PDO::FETCH_CLASS. Essayez ds prsent cette modication :
1 <?php
2 // On admet que $db est une instance de PDO
3
4 $q = $db ->prepare('SELECT id, nom , degats FROM personnages
WHERE nom <> :nom ORDER BY nom');
5 $q->execute(array(':nom' => $nom));
6
7 $q->setFetchMode(PDO:: FETCH_CLASS | PDO:: FETCH_PROPS_LATE , '
Personnage ');
8
9 $persos = $q ->fetchAll ();
Achez avec des echo la valeur de quelques attributs dans le constructeur et vous
verrez quils auront tous une valeur nulle, pour la simple et bonne raison que cette
fois-ci, le constructeur a t appel avant que les valeurs soient assignes aux attributs
par PDO.
Maintenant, puisque le cahier des charges vous demande de rendre ce script compatible
avec lAPI MySQLi, je vais vous dire comment procder. Cest beaucoup plus simple :
au lieu dappeler la mthode fetch_assoc() sur le rsultat, appelez tout simplement
la mthode fetch_object(NomDeLaClasse).
294
CORRECTION
Mes attributs sont privs ou protgs, pourquoi PDO et MySQLi peuvent-ils
modier leur valeur sans appeler les setters ?
Procder de cette faon, cest de la bidouille de bas niveau. PDO et MySQLi sont
des API compiles avec PHP, elles ne fonctionnent donc pas comme les classes que vous
dveloppez. En plus de vous dire des btises, cela serait un hors sujet de vous expliquer
le pourquoi du comment, mais sachez juste que les API compiles ont quelques super
pouvoirs de ce genre.
Correction
Diagramme UML
Avant de donner une correction du code, je vais corriger la construction des classes en
vous donnant le diagramme UML reprsentant le module (voir la gure 17.1).
Figure 17.1 Diagramme modlisant le systme de news
Le code du systme
Pour des raisons dorganisation, jai dcid de placer les quatre classes dans un dossier
lib.
1 <?php
2 /**
3 * Classe reprsentant une news , cre l'occasion d'un TP du
tutoriel La programmation oriente objet en PHP
disponible sur http ://www.siteduzero.com/
295
CHAPITRE 17. TP : UN SYSTME DE NEWS
4 * @author Victor T.
5 * @version 2.0
6 */
7 class News
8 {
9 protected $erreurs = array (),
10 $id ,
11 $auteur ,
12 $titre ,
13 $contenu ,
14 $dateAjout ,
15 $dateModif;
16
17 /**
18 * Constantes relatives aux erreurs possibles rencontres
lors de l'excution de la mthode.
19 */
20 const AUTEUR_INVALIDE = 1;
21 const TITRE_INVALIDE = 2;
22 const CONTENU_INVALIDE = 3;
23
24
25 /**
26 * Constructeur de la classe qui assigne les donnes spcifi
es en paramtre aux attributs correspondants.
27 * @param $valeurs array Les valeurs assigner
28 * @return void
29 */
30 public function __construct($valeurs = array())
31 {
32 if (!empty($valeurs)) // Si on a spcifi des valeurs ,
alors on hydrate l'objet.
33 {
34 $this ->hydrate($valeurs);
35 }
36 }
37
38 /**
39 * Mthode assignant les valeurs spcifies aux attributs
correspondant.
40 * @param $donnees array Les donnes assigner
41 * @return void
42 */
43 public function hydrate($donnees)
44 {
45 foreach ($donnees as $attribut => $valeur)
46 {
47 $methode = 'set'.ucfirst($attribut);
48
49 if (is_callable(array($this , $methode)))
296
CORRECTION
50 {
51 $this ->$methode($valeur);
52 }
53 }
54 }
55
56 /**
57 * Mthode permettant de savoir si la news est nouvelle.
58 * @return bool
59 */
60 public function isNew ()
61 {
62 return empty($this ->id);
63 }
64
65 /**
66 * Mthode permettant de savoir si la news est valide.
67 * @return bool
68 */
69 public function isValid ()
70 {
71 return !(empty($this ->auteur) || empty($this ->titre) ||
empty($this ->contenu));
72 }
73
74
75 // SETTERS //
76
77 public function setId($id)
78 {
79 $this ->id = (int) $id;
80 }
81
82 public function setAuteur($auteur)
83 {
84 if (! is_string($auteur) || empty($auteur))
85 {
86 $this ->erreurs [] = self:: AUTEUR_INVALIDE;
87 }
88 else
89 {
90 $this ->auteur = $auteur;
91 }
92 }
93
94 public function setTitre($titre)
95 {
96 if (! is_string($titre) || empty($titre))
97 {
98 $this ->erreurs [] = self:: TITRE_INVALIDE;
297
CHAPITRE 17. TP : UN SYSTME DE NEWS
99 }
100 else
101 {
102 $this ->titre = $titre;
103 }
104 }
105
106 public function setContenu($contenu)
107 {
108 if (! is_string($contenu) || empty($contenu))
109 {
110 $this ->erreurs [] = self:: CONTENU_INVALIDE;
111 }
112 else
113 {
114 $this ->contenu = $contenu;
115 }
116 }
117
118 public function setDateAjout($dateAjout)
119 {
120 if (is_string($dateAjout) && preg_match('le [0-9]{2}/[0-9
]{2}/[0-9]{4} [0-9]{2}h[0-9]{2}', $dateAjout))
121 {
122 $this ->dateAjout = $dateAjout;
123 }
124 }
125
126 public function setDateModif($dateModif)
127 {
128 if (is_string($dateModif) && preg_match('le [0-9]{2}/[0-9
]{2}/[0-9]{4} [0-9]{2}h[0-9]{2}', $dateModif))
129 {
130 $this ->dateModif = $dateModif;
131 }
132 }
133
134 // GETTERS //
135
136 public function erreurs ()
137 {
138 return $this ->erreurs;
139 }
140
141 public function id()
142 {
143 return $this ->id;
144 }
145
146 public function auteur ()
298
CORRECTION
147 {
148 return $this ->auteur;
149 }
150
151 public function titre ()
152 {
153 return $this ->titre;
154 }
155
156 public function contenu ()
157 {
158 return $this ->contenu;
159 }
160
161 public function dateAjout ()
162 {
163 return $this ->dateAjout;
164 }
165
166 public function dateModif ()
167 {
168 return $this ->dateModif;
169 }
170 }
1 <?php
2 abstract class NewsManager
3 {
4 /**
5 * Mthode permettant d'ajouter une news.
6 * @param $news News La news ajouter
7 * @return void
8 */
9 abstract protected function add(News $news);
10
11 /**
12 * Mthode renvoyant le nombre de news total.
13 * @return int
14 */
15 abstract public function count ();
16
17 /**
18 * Mthode permettant de supprimer une news.
19 * @param $id int L'identifiant de la news supprimer
20 * @return void
21 */
22 abstract public function delete($id);
23
24 /**
25 * Mthode retournant une liste de news demande.
299
CHAPITRE 17. TP : UN SYSTME DE NEWS
26 * @param $debut int La premire news slectionner
27 * @param $limite int Le nombre de news slectionner
28 * @return array La liste des news. Chaque entre est une
instance de News.
29 */
30 abstract public function getList($debut = -1, $limite = -1);
31
32 /**
33 * Mthode retournant une news prcise.
34 * @param $id int L'identifiant de la news rcuprer
35 * @return News La news demande
36 */
37 abstract public function getUnique($id);
38
39 /**
40 * Mthode permettant d'enregistrer une news.
41 * @param $news News la news enregistrer
42 * @see self::add()
43 * @see self:: modify ()
44 * @return void
45 */
46 public function save(News $news)
47 {
48 if ($news ->isValid ())
49 {
50 $news ->isNew() ? $this ->add($news) : $this ->update($news)
;
51 }
52 else
53 {
54 throw new RuntimeException('La news doit tre valide pour
tre enregistre');
55 }
56 }
57
58 /**
59 * Mthode permettant de modifier une news.
60 * @param $news news la news modifier
61 * @return void
62 */
63 abstract protected function update(News $news);
64 }
1 <?php
2 class NewsManager_PDO extends NewsManager
3 {
4 /**
5 * Attribut contenant l'instance reprsentant la BDD.
6 * @type PDO
7 */
300
CORRECTION
8 protected $db;
9
10 /**
11 * Constructeur tant charg d'enregistrer l'instance de PDO
dans l'attribut $db.
12 * @param $db PDO Le DAO
13 * @return void
14 */
15 public function __construct(PDO $db)
16 {
17 $this ->db = $db;
18 }
19
20 /**
21 * @see NewsManager ::add()
22 */
23 protected function add(News $news)
24 {
25 $requete = $this ->db->prepare('INSERT INTO news SET auteur
= :auteur , titre = :titre , contenu = :contenu , dateAjout
= NOW(), dateModif = NOW()');
26
27 $requete ->bindValue(':titre', $news ->titre());
28 $requete ->bindValue(':auteur ', $news ->auteur ());
29 $requete ->bindValue(':contenu ', $news ->contenu ());
30
31 $requete ->execute ();
32 }
33
34 /**
35 * @see NewsManager :: count()
36 */
37 public function count ()
38 {
39 return $this ->db ->query('SELECT COUNT (*) FROM news')->
fetchColumn ();
40 }
41
42 /**
43 * @see NewsManager :: delete ()
44 */
45 public function delete($id)
46 {
47 $this ->db->exec('DELETE FROM news WHERE id = '.(int) $id);
48 }
49
50 /**
51 * @see NewsManager :: getList ()
52 */
53 public function getList($debut = -1, $limite = -1)
301
CHAPITRE 17. TP : UN SYSTME DE NEWS
54 {
55 $sql = 'SELECT id , auteur , titre , contenu , DATE_FORMAT (
dateAjout , \'le %d/%m/%Y %Hh%i\') AS dateAjout ,
DATE_FORMAT (dateModif , \'le %d/%m/%Y %Hh%i\') AS
dateModif FROM news ORDER BY id DESC';
56
57 // On vrifie l'intgrit des paramtres fournis.
58 if ($debut != -1 || $limite != -1)
59 {
60 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
61 }
62
63 $requete = $this ->db->query($sql);
64 $requete ->setFetchMode(PDO:: FETCH_CLASS | PDO::
FETCH_PROPS_LATE , 'News');
65
66 $listeNews = $requete ->fetchAll ();
67
68 $requete ->closeCursor ();
69
70 return $listeNews;
71 }
72
73 /**
74 * @see NewsManager :: getUnique ()
75 */
76 public function getUnique($id)
77 {
78 $requete = $this ->db->prepare('SELECT id, auteur , titre ,
contenu , DATE_FORMAT (dateAjout , \'le %d/%m/%Y %Hh%i
\') AS dateAjout , DATE_FORMAT (dateModif , \'le %d/%m/%Y
%Hh%i\') AS dateModif FROM news WHERE id = :id');
79 $requete ->bindValue(':id', (int) $id , PDO:: PARAM_INT);
80 $requete ->execute ();
81
82 $requete ->setFetchMode(PDO:: FETCH_CLASS | PDO::
FETCH_PROPS_LATE , 'News');
83
84 return $requete ->fetch ();
85 }
86
87 /**
88 * @see NewsManager :: update ()
89 */
90 protected function update(News $news)
91 {
92 $requete = $this ->db->prepare('UPDATE news SET auteur = :
auteur , titre = :titre , contenu = :contenu , dateModif =
NOW() WHERE id = :id');
93
302
CORRECTION
94 $requete ->bindValue(':titre', $news ->titre());
95 $requete ->bindValue(':auteur ', $news ->auteur ());
96 $requete ->bindValue(':contenu ', $news ->contenu ());
97 $requete ->bindValue(':id', $news ->id(), PDO:: PARAM_INT);
98
99 $requete ->execute ();
100 }
101 }
1 <?php
2 class NewsManager_MySQLi extends NewsManager
3 {
4 /**
5 * Attribut contenant l'instance reprsentant la BDD.
6 * @type MySQLi
7 */
8 protected $db;
9
10 /**
11 * Constructeur tant charg d'enregistrer l'instance de
MySQLi dans l'attribut $db.
12 * @param $db MySQLi Le DAO
13 * @return void
14 */
15 public function __construct(MySQLi $db)
16 {
17 $this ->db = $db;
18 }
19
20 /**
21 * @see NewsManager ::add()
22 */
23 protected function add(News $news)
24 {
25 $requete = $this ->db->prepare('INSERT INTO news SET auteur
= ?, titre = ?, contenu = ?, dateAjout = NOW(),
dateModif = NOW()');
26
27 $requete ->bind_param('sss', $news ->auteur (), $news ->titre()
, $news ->contenu ());
28
29 $requete ->execute ();
30 }
31
32 /**
33 * @see NewsManager :: count()
34 */
35 public function count ()
36 {
37 return $this ->db ->query('SELECT id FROM news')->num_rows;
303
CHAPITRE 17. TP : UN SYSTME DE NEWS
38 }
39
40 /**
41 * @see NewsManager :: delete ()
42 */
43 public function delete($id)
44 {
45 $id = (int) $id;
46
47 $requete = $this ->db->prepare('DELETE FROM news WHERE id =
?');
48
49 $requete ->bind_param('i', $id);
50
51 $requete ->execute ();
52 }
53
54 /**
55 * @see NewsManager :: getList ()
56 */
57 public function getList($debut = -1, $limite = -1)
58 {
59 $listeNews = array ();
60
61 $sql = 'SELECT id , auteur , titre , contenu , DATE_FORMAT (
dateAjout , \'le %d/%m/%Y %Hh%i\') AS dateAjout ,
DATE_FORMAT (dateModif , \'le %d/%m/%Y %Hh%i\') AS
dateModif FROM news ORDER BY id DESC';
62
63 // On vrifie l'intgrit des paramtres fournis.
64 if ($debut != -1 || $limite != -1)
65 {
66 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
67 }
68
69 $requete = $this ->db->query($sql);
70
71 while ($news = $requete ->fetch_object('News'))
72 {
73 $listeNews [] = $news;
74 }
75
76 return $listeNews;
77 }
78
79 /**
80 * @see NewsManager :: getUnique ()
81 */
82 public function getUnique($id)
83 {
304
CORRECTION
84 $id = (int) $id;
85
86 $requete = $this ->db->prepare('SELECT id, auteur , titre ,
contenu , DATE_FORMAT (dateAjout , \'le %d/%m/%Y %Hh%i
\') AS dateAjout , DATE_FORMAT (dateModif , \'le %d/%m/%Y
%Hh%i\') AS dateModif FROM news WHERE id = ?');
87 $requete ->bind_param('i', $id);
88 $requete ->execute ();
89
90 $requete ->bind_result($id , $auteur , $titre , $contenu ,
$dateAjout , $dateModif);
91
92 $requete ->fetch();
93
94 return new News(array(
95 'id' => $id ,
96 'auteur ' => $auteur ,
97 'titre' => $titre ,
98 'contenu ' => $contenu ,
99 'dateAjout ' => $dateAjout ,
100 'dateModif ' => $dateModif
101 ));
102 }
103
104 /**
105 * @see NewsManager :: update ()
106 */
107 protected function update(News $news)
108 {
109 $requete = $this ->db->prepare('UPDATE news SET auteur = ?,
titre = ?, contenu = ?, dateModif = NOW() WHERE id = ?')
;
110
111 $requete ->bind_param('sssi', $news ->auteur (), $news ->titre
(), $news ->contenu (), $news ->id());
112
113 $requete ->execute ();
114 }
115 }
Pour accder aux instances de PDO et MySQLi, nous allons nous aider du design
pattern factory. Veuillez donc crer une simple classe DBFactory.
1 <?php
2 class DBFactory
3 {
4 public static function getMysqlConnexionWithPDO ()
5 {
6 $db = new PDO('mysql:host=localhost;dbname=news', 'root', '
');
305
CHAPITRE 17. TP : UN SYSTME DE NEWS
7 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_EXCEPTION
);
8
9 return $db;
10 }
11
12 public static function getMysqlConnexionWithMySQLi ()
13 {
14 return new MySQLi('localhost ', 'root', '', 'news');
15 }
16 }
Nous allons crer deux pages : index.php qui sera accessible au grand public et lis-
tera les news, ainsi que admin.php qui nous permettra de grer les news. Dans ces
deux pages, nous aurons besoin dun autoload. Nous allons donc crer un chier auto-
load.inc.php dans le dossier lib qui contiendra notre autoload. Il sagit dun simple
chier, voyez par vous-mme :
1 <?php
2 function autoload($classname)
3 {
4 if (file_exists($file = dirname (__FILE__) . '/' . $classname
. '.class.php'))
5 {
6 require $file;
7 }
8 }
9
10 spl_autoload_register('autoload ');
Maintenant que nous avons cr la partie interne, nous allons nous occuper des pages
qui sacheront devant vos yeux. Il sagit bien entendu de la partie la plus facile, le
pire est derrire nous.
Commenons par la page dadministration :
1 <?php
2 require 'lib/autoload.inc.php';
3
4 $db = DBFactory :: getMysqlConnexionWithMySQLi ();
5 $manager = new NewsManager_MySQLi($db);
6
7 if (isset($_GET['modifier ']))
8 {
9 $news = $manager ->getUnique ((int) $_GET['modifier ']);
10 }
11
12 if (isset($_GET['supprimer ']))
13 {
14 $manager ->delete ((int) $_GET['supprimer ']);
15 $message = 'La news a bien t supprime !';
306
CORRECTION
16 }
17
18 if (isset($_POST['auteur ']))
19 {
20 $news = new News(
21 array(
22 'auteur ' => $_POST['auteur '],
23 'titre' => $_POST['titre'],
24 'contenu ' => $_POST['contenu ']
25 )
26 );
27
28 if (isset($_POST['id']))
29 {
30 $news ->setId($_POST['id']);
31 }
32
33 if ($news ->isValid ())
34 {
35 $manager ->save($news);
36
37 $message = $news ->isNew() ? 'La news a bien t ajoute !'
: 'La news a bien t modifie !';
38 }
39 else
40 {
41 $erreurs = $news ->erreurs ();
42 }
43 }
44 ?>
45 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
46 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
47 <head >
48 <title >Administration </title >
49 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
50
51 <style type="text/css">
52 table , td {
53 border: 1px solid black;
54 }
55
56 table {
57 margin:auto;
58 text -align: center;
59 border -collapse: collapse;
60 }
61
62 td {
307
CHAPITRE 17. TP : UN SYSTME DE NEWS
63 padding: 3px;
64 }
65 </style >
66 </head >
67
68 <body >
69 <p><a href=".">Accder l'accueil du site </a></p>
70
71 <form action =" admin.php" method ="post">
72 <p style ="text -align: center">
73 <?php
74 if (isset($message))
75 {
76 echo $message , '<br />';
77 }
78 ?>
79 <?php if (isset($erreurs) && in_array(News::
AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
80 Auteur : <input type="text" name="auteur" value="<?php
if (isset($news)) echo $news ->auteur (); ?>" /><br />
81
82 <?php if (isset($erreurs) && in_array(News::
TITRE_INVALIDE , $erreurs)) echo 'Le titre est
invalide.<br />'; ?>
83 Titre : <input type="text" name="titre" value=" <?php if
(isset($news)) echo $news ->titre (); ?>" /><br />
84
85 <?php if (isset($erreurs) && in_array(News::
CONTENU_INVALIDE , $erreurs)) echo 'Le contenu est
invalide.<br />'; ?>
86 Contenu :<br /><textarea rows="8" cols="60" name="
contenu" ><?php if (isset($news)) echo $news ->contenu
(); ?></textarea ><br />
87 <?php
88 if(isset($news) && !$news ->isNew())
89 {
90 ?>
91 <input type="hidden" name="id" value=" <?php echo $news
->id(); ?>" />
92 <input type="submit" value="Modifier" name="modifier"
/>
93 <?php
94 }
95 else
96 {
97 ?>
98 <input type="submit" value="Ajouter" />
99 <?php
100 }
308
CORRECTION
101 ?>
102 </p>
103 </form >
104
105 <p style="text -align: center">Il y a actuellement <?php
echo $manager ->count (); ?> news. En voici la liste :</p>
106
107 <table >
108 <tr><th>Auteur </th ><th>Titre </th ><th >Date d'ajout </th><th
>Dernire modification </th ><th>Action </th ></tr >
109 <?php
110 foreach ($manager ->getList () as $news)
111 {
112 echo '<tr><td>', $news ->auteur (), '</td><td>', $news ->titre()
, '</td><td>', $news ->dateAjout (), '</td><td>', ($news ->
dateAjout () == $news ->dateModif () ? '-' : $news ->dateModif
()), '</td><td><a href="?modifier=', $news ->id(), '">
Modifier </a> | <a href="?supprimer=', $news ->id(), '">
Supprimer </a></td ></tr>', "\n";
113 }
114 ?>
115 </table >
116 </body >
117 </html >
Et enn, la partie visible tous vos visiteurs :
1 <?php
2 require 'lib/autoload.inc.php';
3
4 $db = DBFactory :: getMysqlConnexionWithMySQLi ();
5 $manager = new NewsManager_MySQLi($db);
6 ?>
7 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
8 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
9 <head >
10 <title >Accueil du site </title >
11 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
12 </head >
13
14 <body >
15 <p><a href="admin.php">Accder l'espace d'administration
</a></p>
16 <?php
17 if (isset($_GET['id']))
18 {
19 $news = $manager ->getUnique ((int) $_GET['id']);
20
309
CHAPITRE 17. TP : UN SYSTME DE NEWS
21 echo '<p>Par <em>', $news ->auteur (), '</em>, ', $news ->
dateAjout (), '</p>', "\n",
22 '<h2>', $news ->titre(), '</h2>', "\n",
23 '<p>', nl2br($news ->contenu ()), '</p>', "\n";
24
25 if ($news ->dateAjout () != $news ->dateModif ())
26 {
27 echo '<p style ="text -align: right;"><small ><em>Modifie ',
$news ->dateModif (), '</em ></small ></p>';
28 }
29 }
30
31 else
32 {
33 echo '<h2 style="text -align:center">Liste des 5 dernires
news </h2>';
34
35 foreach ($manager ->getList(0, 5) as $news)
36 {
37 if (strlen($news ->contenu ()) <= 200)
38 {
39 $contenu = $news ->contenu ();
40 }
41
42 else
43 {
44 $debut = substr($news ->contenu (), 0, 200);
45 $debut = substr($debut , 0, strrpos($debut , ' ')) . '...';
46
47 $contenu = $debut;
48 }
49
50 echo '<h4><a href ="?id=', $news ->id(), '">', $news ->titre()
, '</a></h4>', "\n",
51 '<p>', nl2br($contenu), '</p>';
52 }
53 }
54 ?>
55 </body >
56 </html >
310
Troisime partie
[Pratique] Ralisation dun site
web
311
Chapitre 18
Description de lapplication
Dicult :
I
l serait temps de faire un TP consquent pour mettre en pratique tout ce que nous
avons vu : nous allons raliser un site web (souvent compar une application). Ce
sera loccasion de faire un point sur vos connaissances en orient objet.
Pour y arriver, nous allons crer une bibliothque, un module de news avec commentaires
ainsi quun espace dadministration complet. Le plus dicile consiste dvelopper notre
bibliothque.
Ceci est une tape importante quil ne faut surtout pas ngliger. Ne lisez donc pas ce
chapitre trop rapidement, sinon vous risquez dtre perdus par la suite. Par ailleurs, ne vous
dcouragez pas si vous ne comprenez pas tout dun coup : ce chapitre est dun niveau plus
lev , il est donc normal de ne pas tout comprendre ds la premire lecture !
313
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
Une application, quest ce que cest ?
Le droulement dune application
Avant de commencer crer une application, encore faudrait-il savoir ce que cest et
en quoi cela consiste. En fait, il faut dcomposer le droulement des actions eectues
du dbut la n (le dbut tant la requte envoye par le client et la n tant la
rponse renvoye ce client). De manire trs schmatique, voici le droulement dune
application (voir la gure 18.1).
Figure 18.1 Schma simpli du droulement dune application
Dtaillons ce schma, tape par tape.
Lancement de lapplication : lorsque linternaute accdera votre site, un chier
PHP est excut sur le serveur. Dans notre cas, ce chier sera excut chaque
fois que le visiteur voudra accder une page, quelle que soit cette dernire.
Que le visiteur veuille acher une news, ouvrir un forum ou poster un commentaire,
cest ce chier qui sera excut (nous verrons comment plus tard). Ce chier sera trs
rduit : il ne fera que lancer lapplication (nous verrons plus tard quil se contente
dinstancier une classe et dinvoquer une mthode).
Chargement de la requte du client : cette tape consiste analyser la requte
envoye par le client. Cest lors de cette tape que lapplication ira chercher les
variables transmises par formulaire ou par URL (les fameuses variables GET et
POST).
Excution de la page dsire : cest ici le cur de lexcution de lapplication.
Mais comment lapplication connat-elle la page excuter ? Quelle action le visiteur
veut-il excuter ? Veut-il acher une news, visiter un forum, poster un commentaire ?
Cette action est dtermine par ce quon appelle un routeur. En analysant lURL, le
routeur est capable de savoir ce que le visiteur veut. Par exemple, si lURL entre par
le visiteur est http ://www.monsupersite.com/news-12.html, alors le routeur saura
que le visiteur veut acher la news ayant pour identiant 12 dans la base de donnes.
Le routeur va donc retourner cette action lapplication qui lexcutera (nous verrons
plus tard ce dont il sagit vraiment).
314
UNE APPLICATION, QUEST CE QUE CEST?
Envoi de la rponse au client : aprs avoir excut laction dsire (par exemple,
si le visiteur veut acher une news, laction correspondante est de rcuprer cette
news), lapplication va acher le tout, et lexcution du script sera termine. Cest
ce moment-l que la page sera envoye au visiteur.
Pour bien cerner le principe, prenons un exemple. Si le visiteur veut voir la news n12,
alors il demandera la page news-12.html. Schmatiquement, voici ce qui va se passer
(voir la gure 18.2).
Figure 18.2 Dtails des oprations eectues lorsquune page est demande
Il est trs important de comprendre ce principe. Si vous ne saisissez pas tout, nhsitez
pas relire les explications, sinon vous risquez de ne pas suivre la suite du cours.
Vous tes maintenant au courant quune application sexcute et quelle a pour rle
315
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
dorchestrer lexcution du script an de donner la page au visiteur. Dans un tutoriel
sur la POO en PHP, je suis sr que vous savez comment sera reprsent cette application
dans votre script. . . Oui, notre application sera un objet ! Qui dit objet dit classe, et
qui dit classe dit fonctionnalits. Reprenez le schma et noncez-moi les fonctionnalits
que possde notre classe Application. . .
Pour linstant, elle ne possde quune fonctionnalit : celle de sexcuter (nous verrons
par la suite quelle en possdera plusieurs autres, mais vous ne pouviez pas les deviner).
Cette fonctionnalit est obligatoire quelle que soit lapplication : quel serait lintrt
dune application si elle ne pouvait pas sexcuter ?
En gnral, dans un site web, il y a deux applications : le frontend et le backend. La
premire est lapplication accessible par tout le monde : cest travers cette application
quun visiteur pourra acher une news, lire un sujet dun forum, poster un commen-
taire, etc. La seconde application est lespace dadministration : laccs est bloqu aux
visiteurs. Pour y accder, une paire identiant-mot de passe est requise. Comme vous
laurez peut-tre compris, ces deux applications seront reprsentes par deux classes
hritant de notre classe de base Application.
Un peu dorganisation
Nous avons vu susamment de notions pour se pencher sur larchitecture de notre
projet. En eet, nous savons que notre site web sera compos de deux applications
(frontend et backend) ainsi que dune classe de base Application.
La classe Application fait partie de notre bibliothque (ou library en anglais),
comme de nombreuses autres classes dont nous parlerons plus tard. Toutes ces classes
vont, par consquent, tre places dans un dossier /Library.
Pour les deux applications, cest un peu plus compliqu. En eet, une application ne
tient pas dans un seul chier : elle est divise en plusieurs parties. Parmi ces parties,
nous trouvons la plus grosse : la partie contenant les modules.
Quest-ce quun module ?
Un module est un ensemble dactions et de donnes concernant une partie du site. Par
exemple, les actions acher une news et commenter une news font partie du
mme module de news, tandis que les actions acher ce sujet et poster dans ce
sujet font partie du module du forum.
Ainsi, je vous propose larchitecture suivante :
Le dossier /Applications contiendra nos applications (frontend et backend).
Le sous-dossier /Applications/Nomdelapplication/Modules contiendra les mo-
dules de lapplication (par exemple, si lapplication frontend possde un module de
news, alors il y aura un dossier /Applications/Frontend/Modules/News).
Ne vous inquitez pas, nous reviendrons en dtail sur ces fameux modules. Jai introduit
316
UNE APPLICATION, QUEST CE QUE CEST?
la notion ici pour parler de larchitecture (et surtout de ce qui va suivre). En eet, vous
devriez vous posez une question : quand lutilisateur veut acher une page, quel chier
PHP sera excut en premier ? Ou pourrai-je placer mes feuilles de style et mes images ?
Tous les chiers accessibles au public devront tre placs dans un dossier /Web. Pour
tre plus prcis, votre serveur HTTP ne pointera pas vers la racine du projet, mais vers
le dossier /Web. Je mexplique.
Sur votre serveur local, si vous tapez localhost dans la barre dadresse, alors votre
serveur renverra le contenu du dossier C :\Wamp\www si vous tes sous Wamp-
Server, ou du dossier /var/www si vous tes sous LAMP. Vous tes-vous dj de-
mand comment est-ce que cela se faisait ? Qui a dcrt cela ? En fait, cest crit
quelque part, dans un chier de conguration. Il y en a un qui dit que si on tape
localhost dans la barre dadresse, alors on se connectera sur lordinateur. Ce chier
de conguration est le chier hosts. Le chemin menant ce chier est C :\win-
dows\system32\drivers\etc\hosts sous Windows et /etc/hosts sous Linux et
Mac OS. Vous pouvez lditer ( condition davoir les droits). Faites le test : ajou-
tez la ligne suivante la n du chier.
1 127.0.0.1 monsupersite
Sauvegardez le chier et fermez-le. Ouvrez votre navigateur, tapez monsupersite et. . .
vous atterrissez sur la mme page que quand vous tapiez localhost !
Maintenant, nous allons utiliser un autre chier. La manipulation va consister dire
lordinateur que lorsquon tape monsupersite, on ne voudra pas le contenu de
C :\Wamp\www ou de /var/www, mais de C :\Wamp\www\monsupersite\Web
si vous tes sous Windows ou de /var/www/monsupersite/Web si vous tes sous
Linux ou Mac OS.
Tout dabord, je vous annonce que nous allons utiliser le module mod_vhost_alias
dApache. Assurez-vous donc quil soit bien activ. Le chier que nous allons mani-
puler est le chier de conguration dApache, savoir httpd.conf sous WampServer.
Rajoutez la n du chier :
1 <VirtualHost\index{VirtualHost} *:80 >
2 ServerAdmin webmaster@localhost
3
4 # Mettez ici le nom de domaine que vous avez utilis dans le
fichier hosts.
5 ServerName monsupersite
6
7 # Mettez ici le chemin vers lequel doit pointer le domaine.
8 # Je suis sous Linux. Si vous tes sous Windows , le chemin
sera de la forme C:\Wamp\www\monsupersite\Web
9 DocumentRoot /home/victor/www/monsupersite/Web
10 <Directory /home/victor/www/monsupersite/Web >
11 Options Indexes FollowSymLinks MultiViews
12
13 # Cette directive permet d'activer les .htaccess.
14 AllowOverride All
15
317
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
16 # Si le serveur est accessible via l'Internet mais que vous
n'en faites qu'une utilisation personnelle
17 # pensez interdire l'accs tout le monde
18 # sauf au localhost , sinon vous ne pourrez pas y accder !
19 deny from all
20 allow from localhost
21 </Directory >
22 </VirtualHost >
Si vous avez un serveur LAMP, nessayez pas de trouver le chier httpd.conf, il nexiste
pas !
La cration dhtes virtuels seectue en crant un nouveau chier contenant la congu-
ration de ce dernier. Le chier crer est /etc/apache2/sites-available/monsupersite
(remplacez monsupersite par le domaine choisi). Placez-y lintrieur le contenu que
je vous ai donn. Ensuite, il ny a plus qu activer cette nouvelle conguration grce
la commande sudo a2ensite monsupersite (remplacez monsupersite par le nom
du chier contenant la conguration de lhte virtuel).
Dans tous les cas, que vous soyez sous Windows, Linux, Mac OS ou quoi que ce soit
dautre, redmarrez Apache pour que la nouvelle conguration soit prise en compte.
Essayez dentrer monsupersite dans la barre dadresse, et vous verrez que le naviga-
teur vous ache le dossier spci dans la conguration dApache !
Les entrailles de lapplication
Avant daller plus loin, il est indispensable (voire obligatoire) de savoir utiliser le pattern
MVC
1
. Pour cela, je vous conseille daller lire le tutoriel suivant :

Cours sur le modle MVC


Code web : 313530
En eet, ce cours explique la thorie du pattern MVC et je ne ferais que crer un
doublon si je vous expliquais mon tour ce motif.
Nous pouvons donc passer directement aux choses srieuses.
Retour sur les modules
Comme je vous lavais brivement indiqu, un module englobe un ensemble dactions
agissant sur une mme partie du site. Cest donc lintrieur de ce module que nous
allons crer le contrleur, les vues et les modles.
1. Modle-Vue-Contrleur
318
LES ENTRAILLES DE LAPPLICATION
Le contrleur
Nous allons donc crer pour chaque module un contrleur qui contiendra au moins
autant de mthodes que dactions. Par exemple, si dans le module de news je veux
pouvoir avoir la possibilit dacher lindex du module (qui nous dvoilera la liste des
cinq dernires news par exemple) et acher une news, jaurais alors deux mthodes
dans mon contrleur : executeIndex et executeShow. Ce chier aura pour nom Nom-
DuModuleController.class.php, ce qui nous donne, pour le module de news, un
chier du nom de NewsController.class.php. Celui-ci est directement situ dans le
dossier du module.
Les vues
Chacune de ces actions correspond, comme vous le savez, une vue. Nous aurons donc
pour chaque action une vue du mme nom. Par exemple, pour laction show, nous
aurons un chier show.php. Toutes les vues sont placer dans le dossier Views du
module.
Les modles
En fait, les modles, vous les connaissez dj : il sagit des managers. Ce sont eux qui
feront oce de modles. Les modles ne sont rien dautre que des chiers permettant
linteraction avec les donnes. Pour chaque module, nous aurons donc au moins deux -
chiers constituant le modle : le manager abstrait de base (NewsManager.class.php)
et au moins une classe exploitant ce manager (exemple : NewsManager_PDO.class.php).
Tous les modles devront tre placs dans le dossier /Library/Models an quils
puissent tre utiliss facilement par deux applications direntes (ce qui est souvent le
cas avec les applications backend et frontend). Cependant, ces modles ont besoin des
classes reprsentant les entits quils grent. La classe reprsentant un enregistrement
(comme News dans notre cas) sera, elle, place dans le dossier /Library/Entities.
Nous aurons loccasion de faire un petit rappel sur cette organisation lors du prochain
chapitre.
Le back controller de base
Tous ces contrleurs sont chacun des back controller. Et que met-on en place quand
on peut dire quune entit B est une entit A? Un lien de parent, bien videmment !
Ainsi, nous aurons au sein de notre bibliothque une classe abstraite BackController
dont hritera chaque back controller. Lternelle question que lon peut se poser : mais
que permet de faire cette classe ? Pour linstant, il ny en a quune : celle dexcuter
une action (donc une mthode).
Je voudrais faire un petit retour sur le fonctionnement du routeur. Souvenez-vous :
je vous avais dit que le routeur savait quoi correspondait lURL et retournait en
consquence laction excuter lapplication, qui sera donc apte lexcuter. Cela
319
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
prendra plus de sens maintenant que nous avons vu la notion de back controller. Le
routeur aura pour rle de rcuprer la route correspondant lURL. Lapplica-
tion, qui exploitera le routeur, instanciera donc le contrleur correspondant la route
que le routeur lui aura renvoye. Par exemple, si lutilisateur veut acher une news,
le routeur retournera la route correspondante, et lapplication crera une instance de
NewsController en lui ayant spci quil devra eectuer laction show. Lapplication
naura donc plus qu excuter le back controller.
Souvenez-vous du schma sur le droulement de lapplication. Si nous faisions un petit
zoom sur le routeur, nous obtiendrions ceci (voir la gure 18.3).
Figure 18.3 Zoom sur le routeur
Vous vous posez peut-tre des questions sur le contenu de la deuxime bulle associe
au routeur. En fait, le routeur analyse toute lURL et dcrypte ce qui sy cache (nous
verrons plus tard comment). Quand je dis que tout le monde pourra y accder
travers la requte du client , cest que nous pourrons accder cette valeur travers
la classe qui reprsentera la requte du client (que nous construirons dailleurs durant
le prochain chapitre).
La page
Nous avons parl jusqu prsent du droulement de lapplication et nous avons com-
menc creuser un petit peu en parlant de lorganisation interne avec lutilisation du
pattern MVC. Cependant, comment est gnre la page que le visiteur aura devant
les yeux ? Cette page, vous laurez peut-tre devin, sera reprsente par une classe.
Comme dhabitude, qui dit classe dit fonctionnalits. En eet, une page en possde
320
LES ENTRAILLES DE LAPPLICATION
plusieurs :
Celle dajouter une variable la page (le contrleur aura besoin de passer des donnes
la vue).
Celle dassigner une vue la page (il faut quon puisse dire notre page quelle vue
elle doit utiliser).
De gnrer la page avec le layout de lapplication.
Des explications simposent, notamment sur ce que sont prcisment ces vues et ce
fameux layout. Le layout est le chier contenant lenveloppe du site (dclaration
du doctype, inclusion des feuilles de style, dclaration des balises meta, etc.). Voici un
exemple trs simple :
1 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
2 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
3 <head >
4 <title >Mon super site </title >
5 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
6 </head >
7
8 <body >
9 <?php echo $content; ?>
10 </body >
11 </html >
Le layout, spcique chaque application, doit se placer dans /Applications/Nom-
delapplication/Templates, sous le nom de layout.php. Comme vous pouvez vous
en apercevoir, il y a une variable $content qui trane. Vous aurez sans doute devin
que cette variable sera le contenu de la page : ce sera donc notre classe Page qui se
chargera de gnrer la vue, de stocker ce contenu dans $content puis dinclure le layout
correspondant lapplication.
Schmatiquement, voici comment la page se construit (voir la gure 19.8).
Lautoload
Lautoload sera trs simple. En eet, comme nous le verrons plus tard, chaque classe
se situe dans un namespace. Ces namespaces correspondent aux dossiers dans lesquels
sont places les classes.
Si vous navez jamais entendu parler des namespaces, je vous invite lire le tutoriel
sur les espaces de noms en PHP :

Les espaces de noms en PHP


Code web : 584007
Prenons lexemple de la classe Application. Comme nous lavons vu, cette classe fait
partie de notre bibliothque, donc est place dans le dossier /Library, stock dans le
chier Application.class.php. Ce chier, puisquil est plac dans le dossier /Library,
321
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
Figure 18.4 Reprsentation schmatique de la construction dune page
contiendra donc la classe Application dans le namespace Library :
1 <?php
2 namespace Library;
3
4 class Application
5 {
6 // ...
7 }
Maintenant, regardons du ct de lautoload. Souvenez-vous : notre fonction, appele
par PHP lorsquune classe non dclare est invoque, possde un paramtre, le nom de
la classe. Cependant, si la classe si situe dans un namespace, alors il ny aura pas que le
nom de la classe qui sera pass en argument notre fonction, mais le nom de la classe
prcd du namespace qui la contient. Si lon prend lexemple de notre classe
Application, et que lon linstancie pour que lautoload soit appel, alors largument
vaudra Library\Application. Vous voyez quoi ressemblera notre autoload ? Il se
contentera de remplacer les antislashes (\) par des slashes (/) de largument et dinclure
le chier correspondant.
Notre autoload, situ dans /Library, ressemblera donc a :
1 <?php
2 function autoload($class)
3 {
4 require '../'.str_replace('\\', '/', $class).'.class.php';
5 }
6
7 spl_autoload_register('autoload ');
Gardez-le dans un coin de votre ordinateur, vous le comprendrez sans doute mieux au
322
RSUM DU DROULEMENT DE LAPPLICATION
l de la cration de lapplication.
Rsum du droulement de lapplication
Nous allons ici rsumer tout ce que nous avons vu travers un gros schma. Je vous
conseille de limprimer sur une feuille qui sera toujours ct de votre cran quand
vous lirez le cours : vous comprendrez mieux ce que nous serons en train de faire (voir
la gure 18.5).
Figure 18.5 Droulement dtaill de lapplication
Il est indispensable que vous compreniez ce schma. Si vous ne voulez pas lapprendre
par cur, imprimez-le an de toujours lavoir sous les yeux quand vous travaillerez
sur ce projet !
323
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
324
Chapitre 19
Dveloppement de la bibliothque
Dicult :
L
e plus gros a t fait : nous savons comment fonctionnera notre application, nous
savons o nous voulons aller. Maintenant, il ne reste plus qu nous servir de tout cela
pour construire les diagrammes UML qui vont lier toutes nos classes, nous permettant
ainsi de les crire plus facilement.
Accrochez-vous vos claviers, a ne va pas tre de tout repos !
325
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
Lapplication
Lapplication
Commenons par construire notre classe Application. Souvenez-vous : nous avions dit
que cette classe possdait une fonctionnalit, celle de sexcuter. Or je ne vous ai pas
encore parl des caractristiques de lapplication. La premire ne vous vient peut-tre
pas lesprit, mais je lai pourtant dj voqu : il sagit du nom de lapplication.
Il en existe deux autres. Nous en avons brivement parl, et il est fort probable que
vous les ayez oublies : il sagit de la requte ainsi que la rponse envoye au client.
Ainsi, avant de crer notre classe Application, nous allons nous intresser ces deux
entits.
La requte du client
Schmatisons
Comme vous vous en doutez, nous allons reprsenter la requte du client au travers
dune instance de classe. Comme pour toute classe, intressons-nous aux fonctionnalits
attendues. Quest-ce qui nous intresse dans la requte du client ? Quelles fonctionna-
lits seraient intressantes ? partir de cette instance, il serait pratique de pouvoir :
Obtenir une variable POST.
Obtenir une variable GET.
Obtenir un cookie.
Obtenir la mthode employe pour envoyer la requte (mthode GET ou POST).
Obtenir lURL entre (utile pour que le routeur connaisse la page souhaite).
Et pour la route, voici un petit diagramme (jen ai prot pour ajouter des mthodes
permettant de vrier lexistence de tel cookie / variable GET / variable POST) - voir la
gure 19.1.
Figure 19.1 Modlisation de la classe HTTPRequest
Codons
Le contenu est assez simple, les mthodes eectuent des oprations basiques. Voici donc
le rsultat auquel vous tiez cens arriver :
326
LAPPLICATION
1 <?php
2 namespace Library;
3
4 class HTTPRequest
5 {
6 public function cookieData($key)
7 {
8 return isset($_COOKIE[$key]) ? $_COOKIE[$key] : null;
9 }
10
11 public function cookieExists($key)
12 {
13 return isset($_COOKIE[$key]);
14 }
15
16 public function getData($key)
17 {
18 return isset($_GET[$key]) ? $_GET[$key] : null;
19 }
20
21 public function getExists($key)
22 {
23 return isset($_GET[$key]);
24 }
25
26 public function method ()
27 {
28 return $_SERVER['REQUEST_METHOD '];
29 }
30
31 public function postData($key)
32 {
33 return isset($_POST[$key]) ? $_POST[$key] : null;
34 }
35
36 public function postExists($key)
37 {
38 return isset($_POST[$key]);
39 }
40
41 public function requestURI ()
42 {
43 return $_SERVER['REQUEST_URI '];
44 }
45 }
Un petit mot sur la toute premire ligne, celle qui contient la dclaration du namespace.
Jen avais dj parl lors de lcriture de lautoload, mais je me permets de faire une
petite piqre de rappel. Toutes les classes de notre projet sont dclares dans des
namespaces. Cela permet dune part de structurer son projet et, dautre part, dcrire
327
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
un autoload simple qui sait directement, grce au namespace contenant la classe, le
chemin du chier contenant ladite classe.
Par exemple, si jai un contrleur du module news, celui-ci sera plac dans le dossier
/Applications/Frontend/Modules/News (si vous avez oubli ce chemin, ne vous
inquitez pas, nous y reviendrons !). La classe reprsentant ce contrleur (NewsController)
sera donc dans le namespace Applications\Frontend\Modules\News !
La rponse envoye au client
Schmatisons
L aussi, nous allons reprsenter la rponse envoye au client au travers dune entit.
Cette entit, vous laurez compris, nest autre quune instance dune classe. Quelles
fonctionnalits attendons-nous de cette classe ? Que voulons-nous envoyer au visiteur ?
La rponse la plus vidente est la page. Nous voulons pouvoir assigner une page
la rponse. Cependant, il est bien beau dassigner une page, encore faudrait-il pouvoir
lenvoyer ! Voici une deuxime fonctionnalit : celle denvoyer la rponse en gnrant
la page.
Il existe de nombreuses autres fonctionnalits accessoires . Pour ma part, je trouvais
intressant de pouvoir rediriger le visiteur vers une erreur 404, lui crire un cookie et
dajouter un header spcique. Pour rsumer, notre classe nous permettra :
Dassigner une page la rponse.
Denvoyer la rponse en gnrant la page.
De rediriger lutilisateur.
De le rediriger vers une erreur 404.
Dajouter un cookie.
Dajouter un header spcique.
Et la gure 19.2, le schma tant attendu!
Figure 19.2 Modlisation de la classe HHTPResponse
Notez la valeur par dfaut du dernier paramtre de la mthode setCookie() : elle est
true, alors quelle est false sur la fonction setcookie() de la librairie standard
de PHP. Il sagit dune scurit quil est toujours prfrable dactiver.
Concernant la redirection vers la page 404, laissez-l vide pour linstant, nous nous
chargerons de son implmentation par la suite.
328
LAPPLICATION
Codons
Voici le code que vous devriez avoir obtenu :
1 <?php
2 namespace Library;
3
4 class HTTPResponse
5 {
6 protected $page;
7
8 public function addHeader($header)
9 {
10 header($header);
11 }
12
13 public function redirect($location)
14 {
15 header('Location: '.$location);
16 exit;
17 }
18
19 public function redirect404 ()
20 {
21 }
22
23 public function send()
24 {
25 // Actuellement , cette ligne a peu de sens dans votre
esprit.
26 // Promis , vous saurez vraiment ce qu'elle fait d'ici la
fin du chapitre
27 // (bien que je suis sr que les noms choisis sont assez
explicites !).
28 exit($this ->page ->getGeneratedPage ());
29 }
30
31 public function setPage(Page $page)
32 {
33 $this ->page = $page;
34 }
35
36 // Changement par rapport la fonction setcookie () : le
dernier argument est par dfaut true.
37 public function setCookie($name , $value = '', $expire = 0,
$path = null , $domain = null , $secure = false , $httpOnly =
true)
38 {
39 setcookie($name , $value , $expire , $path , $domain , $secure ,
$httpOnly);
40 }
329
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
41 }
Retour sur notre application
Schmatisons
Maintenant que nous avons vu comment sont reprsentes la requte du client et la r-
ponse que nous allons lui envoyer, nous pouvons rchir pleinement ce qui compose
notre classe. Elle possde une fonctionnalit (celle de sexcuter) et trois caractris-
tiques : son nom, la requte du client et la rponse que nous allons lui envoyer.
Je vous rappelle que nous construisons une classe Application dont hritera chaque
classe reprsentant une application. Par consquent, cela na aucun sens dinstancier
cette classe. Vous savez ce que cela signie ? Oui, notre classe Application est abs-
traite ! La reprsentation graphique de notre classe sera donc telle que vous pouvez la
voir sur la gure 19.3.
Figure 19.3 Modlisation de la classe Application
Codons
Le code est trs basique. Le constructeur se charge uniquement dinstancier les classes
HTTPRequest et HTTPResponse. Quant aux autres mthodes, ce sont des accesseurs,
nous avons donc vite fait le tour. :-
1 <?php
2 namespace Library;
3
4 abstract class Application
5 {
6 protected $httpRequest;
7 protected $httpResponse;
8 protected $name;
9
10 public function __construct ()
11 {
12 $this ->httpRequest = new HTTPRequest;
13 $this ->httpResponse = new HTTPResponse;
14 $this ->name = '';
330
LAPPLICATION
15 }
16
17 abstract public function run();
18
19 public function httpRequest ()
20 {
21 return $this ->httpRequest;
22 }
23
24 public function httpResponse ()
25 {
26 return $this ->httpResponse;
27 }
28
29 public function name()
30 {
31 return $this ->name;
32 }
33 }
Dans le constructeur, vous voyez quon assigne une valeur nulle lattribut name. En
fait, chaque application (qui hritera donc de cette classe) sera charge de spcier son
nom en initialisant cet attribut (par exemple, lapplication frontend assignera la valeur
Frontend cet attribut).
Les composants de lapplication
Les deux dernires classes (comme la plupart des classes que nous allons crer) sont
des composants de lapplication. Toutes ces classes ont donc une nature en com-
mun et doivent hriter dune mme classe reprsentant cette nature : jai nomm
ApplicationComponent.
Que permet de faire cette classe ?
Dobtenir lapplication laquelle lobjet appartient. Cest tout ! :- Cette classe se
chargera juste de stocker, pendant la construction de lobjet, linstance de lapplication
excute. Nous avons donc une simple classe ressemblant celle-ci (voir la gure 19.4).
Figure 19.4 Modlisation de la classe ApplicationComponent
Niveau code, je pense quon peut dicilement faire plus simple :
331
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
1 <?php
2 namespace Library;
3
4 abstract class ApplicationComponent
5 {
6 protected $app;
7
8 public function __construct(Application $app)
9 {
10 $this ->app = $app;
11 }
12
13 public function app()
14 {
15 return $this ->app;
16 }
17 }
Pensez donc ajouter le lien de parent aux classes HTTPRequest et
HTTPResponse. Et noubliez donc pas de passer linstance de lapplication lors
de linstanciation de ces deux classes dans le constructeur de Application.
Le routeur
Rchissons, schmatisons
Comme nous lavons vu, le routeur est lobjet qui va nous permettre de savoir quelle
page nous devons excuter. Pour en tre capable, le routeur aura sa disposition des
routes pointant chacune vers un module et une action.
Rappel : une route, cest une URL associe un module et une action. Crer
une route signie donc assigner une URL un module et une action.
La question que lon se pose alors est : o seront crites les routes ? Certains
dentre vous seraient tents de les crire directement lintrieur de la classe et faire
une sorte de switch / case sur les routes pour trouver laquelle correspond lURL.
Cette faon de faire prsente un norme inconvnient : votre classe reprsentant le
routeur sera dpendante du projet que vous dveloppez. Par consquent, vous ne
pourrez plus lutiliser sur un autre site ! Il va donc falloir externaliser ces dnitions
de routes.
Comment pourrions-nous faire alors ? Puisque je vous ai dit que nous nallons pas
toucher la classe pour chaque nouvelle route ajouter, nous allons placer ces routes
dans un autre chier. Ce chier doit tre plac dans le dossier de lapplication, et
332
LE ROUTEUR
puisque a touche la conguration de celle-ci, nous le placerons dans un sous-dossier
Config. Il y a aussi un dtail rgler : dans quel format allons-nous crire le chier ? Je
vous propose le format XML car ce langage est intuitif et simple parser, notamment
grce la bibliothque native DOMDocument de PHP. Si ce format ne vous plat pas,
vous tes libre den choisir un autre, cela na pas dimportance, le but tant que vous
ayez un chier que vous arrivez parser. Le chemin complet vers ce chier devient
donc /Applications/Nomdelapplication/Cong/routes.xml.
Comme pour tout chier XML qui se respecte, celui-ci doit suivre une structure prcise.
Essayez de deviner la fonctionnalit de la ligne 3 :
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <routes >
3 <route url="/news.html" module="News" action="index" />
4 </routes >
Alors, avez-vous une ide du rle de la troisime ligne ? Lorsque nous allons aller sur
la page news.html, le routeur dira donc lapplication : le client veut accder au
module News et excuter laction index .
Un autre problme se pose. Par exemple, si je veux acher une news spcique en
fonction de son identiant, comment faire ? Ou, plus gnralement, comment passer des
variables GET? Lidal serait dutiliser des expressions rgulires (ou regex) en guise
dURL. Chaque paire de parenthses reprsentera une variable GET. Nous spcierons
leur nom dans un quatrime attribut vars.
Comme un exemple vaut mieux quun long discours, voyons donc un cas concret :
1 <route url="/news -(.+) -([0-9]+)\.html" module="News" action="
show" vars="slug ,id" />
Ainsi, toute URL vriant cette expression pointera vers le module News et excutera
laction show. Les variables $_GET[slug] et $_GET[id] seront cres et auront
pour valeur le contenu des parenthses capturantes.
Puisquil sagit dune expression rgulire et quelle sera vrie par
preg_match(), il est important dchapper le point prcdent html. En ef-
fet, dans une expression rgulire, un point signie tout caractre . Il faut
donc lchapper pour quil perde cette fonctionnalit.
On sait dsormais que notre routeur a besoin de routes pour nous renvoyer celle qui
correspond lURL. Cependant, sil a besoin de routes, il va falloir les lui donner !
Pourquoi ne peut-il pas aller chercher lui-mme les routes ?
Sil allait les chercher lui-mme, notre classe serait dpendante de larchitecture de
lapplication. Si vous voulez utiliser votre classe dans un projet compltement dirent,
vous ne pourrez pas, car le chier contenant les routes nexistera tout simplement pas.
333
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
De plus, dans ce projet, les routes ne seront peut-tre pas stockes dans un chier XML,
donc le parsage ne se fera pas de la mme faon. Or, je vous lavais dj dit dans les
premiers chapitres, mais lun des points forts de la POO est son caractre rutilisable.
Ainsi, votre classe reprsentant le routeur ne dpendra ni dune architecture, ni du
format du chier stockant les routes.
De cette faon, notre classe prsente dj deux fonctionnalits :
Celle dajouter une route.
Celle de renvoyer la route correspondant lURL.
Avec, bien entendu, une caractristique : la liste des routes attache au routeur. Cepen-
dant, une autre question se pose : nous disons quon passe une route au routeur.
Mais comment est matrialise une route ? Puisque vous pensez orient objet, vous
devriez automatiquement me dire une route, cest un objet ! .
Intressons-nous donc maintenant notre objet reprsentant une route. Cet objet,
quest-ce qui le caractrise ?
Une URL.
Un module.
Une action.
Un tableau comportant les noms des variables.
Un tableau cl/valeur comportant les noms/valeurs des variables.
Quelle dirence entre les deux dernires caractristiques ?
En fait, lorsque nous crerons les routes, nous allons assigner les quatre premires
caractristiques (souvenez-vous du chier XML : nous dnissons une URL, un module,
une action, et la liste des noms des variables). Cest donc cette dernire liste de variables
que nous allons assigner notre route. Ensuite, notre routeur ira parcourir ces routes
et cest lui qui assignera les valeurs des variables. Cest donc ce moment-l que le
tableau comportant les noms/valeurs des variables sera cr et assign lattribut
correspondant.
Nous pouvons maintenant dresser la liste des fonctionnalits de notre objet reprsentant
une route :
Celle de savoir si la route correspond lURL.
Celle de savoir si la route possde des variables (utile, nous le verrons, dans notre
routeur).
Pour rsumer, voici le diagramme UML reprsentant nos classes (voir la gure 19.5).
Codons
Commenons par nos deux classes Router et Route :
1 <?php
334
LE ROUTEUR
Figure 19.5 Modlisation des classes Router et Route
2 namespace Library;
3
4 class Router
5 {
6 protected $routes = array();
7
8 const NO_ROUTE = 1;
9
10 public function addRoute(Route $route)
11 {
12 if (! in_array($route , $this ->routes))
13 {
14 $this ->routes [] = $route;
15 }
16 }
17
18 public function getRoute($url)
19 {
20 foreach ($this ->routes as $route)
21 {
22 // Si la route correspond l'URL.
23 if (( $varsValues = $route ->match($url)) !== false)
24 {
25 // Si elle a des variables.
335
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
26 if ($route ->hasVars ())
27 {
28 $varsNames = $route ->varsNames ();
29 $listVars = array();
30
31 // On cr un nouveau tableau cl/valeur.
32 // (Cl = nom de la variable , valeur = sa valeur .)
33 foreach ($varsValues as $key => $match)
34 {
35 // La premire valeur contient entirement la
chaine capture (voir la doc sur preg_match).
36 if ($key !== 0)
37 {
38 $listVars[$varsNames[$key - 1]] = $match;
39 }
40 }
41
42 // On assigne ce tableau de variables la route.
43 $route ->setVars($listVars);
44 }
45
46 return $route;
47 }
48 }
49
50 throw new \RuntimeException('Aucune route ne correspond l
\'URL', self:: NO_ROUTE);
51 }
52 }
1 <?php
2 namespace Library;
3
4 class Route
5 {
6 protected $action;
7 protected $module;
8 protected $url;
9 protected $varsNames;
10 protected $vars = array();
11
12 public function __construct($url , $module , $action , array
$varsNames)
13 {
14 $this ->setUrl($url);
15 $this ->setModule($module);
16 $this ->setAction($action);
17 $this ->setVarsNames($varsNames);
18 }
19
336
LE ROUTEUR
20 public function hasVars ()
21 {
22 return !empty($this ->varsNames);
23 }
24
25 public function match($url)
26 {
27 if (preg_match('^'.$this ->url.'$', $url , $matches))
28 {
29 return $matches;
30 }
31 else
32 {
33 return false;
34 }
35 }
36
37 public function setAction($action)
38 {
39 if (is_string($action))
40 {
41 $this ->action = $action;
42 }
43 }
44
45 public function setModule($module)
46 {
47 if (is_string($module))
48 {
49 $this ->module = $module;
50 }
51 }
52
53 public function setUrl($url)
54 {
55 if (is_string($url))
56 {
57 $this ->url = $url;
58 }
59 }
60
61 public function setVarsNames(array $varsNames)
62 {
63 $this ->varsNames = $varsNames;
64 }
65
66 public function setVars(array $vars)
67 {
68 $this ->vars = $vars;
69 }
337
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
70
71 public function action ()
72 {
73 return $this ->action;
74 }
75
76 public function module ()
77 {
78 return $this ->module;
79 }
80
81 public function vars()
82 {
83 return $this ->vars;
84 }
85
86 public function varsNames ()
87 {
88 return $this ->varsNames;
89 }
90 }
Tout cela est bien beau, mais il serait tout de mme intressant dexploiter notre
routeur an de lintgrer dans notre application. Pour cela, nous allons implmenter une
mthode dans notre classe Application qui sera charge de nous donner le contrleur
correspondant lURL. Pour cela, cette mthode va parcourir le chier XML pour
ajouter les routes au routeur. Ensuite, elle va rcuprer la route correspondante lURL
(si une exception a t leve, on lvera une erreur 404). Enn, la mthode instanciera
le contrleur correspondant la route et le renverra (il est possible que vous ne sachiez
pas comment faire car nous navons pas encore vu le contrleur en dtail, donc laissez
a de ct et regardez la correction, vous comprendrez plus tard).
Voici notre nouvelle classe Application :
1 <?php
2 namespace Library;
3
4 abstract class Application
5 {
6 protected $httpRequest;
7 protected $httpResponse;
8 protected $name;
9
10 public function __construct ()
11 {
12 $this ->httpRequest = new HTTPRequest($this);
13 $this ->httpResponse = new HTTPResponse($this);
14
15 $this ->name = '';
16 }
17
338
LE ROUTEUR
18 public function getController ()
19 {
20 $router = new \Library\Router;
21
22 $xml = new \DOMDocument;
23 $xml ->load(__DIR__.'/../ Applications/'.$this ->name.'/Config
/routes.xml');
24
25 $routes = $xml ->getElementsByTagName('route');
26
27 // On parcourt les routes du fichier XML.
28 foreach ($routes as $route)
29 {
30 $vars = array ();
31
32 // On regarde si des variables sont prsentes dans l'URL.
33 if ($route ->hasAttribute('vars'))
34 {
35 $vars = explode(',', $route ->getAttribute('vars'));
36 }
37
38 // On ajoute la route au routeur.
39 $router ->addRoute(new Route($route ->getAttribute('url'),
$route ->getAttribute('module '), $route ->getAttribute('
action '), $vars));
40 }
41
42 try
43 {
44 // On rcupre la route correspondante l'URL.
45 $matchedRoute = $router ->getRoute($this ->httpRequest ->
requestURI ());
46 }
47 catch (\ RuntimeException $e)
48 {
49 if ($e->getCode () == \Library\Router :: NO_ROUTE)
50 {
51 // Si aucune route ne correspond , c'est que la page
demande n'existe pas.
52 $this ->httpResponse ->redirect404 ();
53 }
54 }
55
56 // On ajoute les variables de l'URL au tableau $_GET.
57 $_GET = array_merge($_GET , $matchedRoute ->vars());
58
59 // On instancie le contrleur.
60 $controllerClass = 'Applications \\'.$this ->name.'\\ Modules
\\'.$matchedRoute ->module ().'\\'.$matchedRoute ->module ()
.'Controller ';
339
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
61 return new $controllerClass($this , $matchedRoute ->module (),
$matchedRoute ->action ());
62 }
63
64 abstract public function run();
65
66 public function httpRequest ()
67 {
68 return $this ->httpRequest;
69 }
70
71 public function httpResponse ()
72 {
73 return $this ->httpResponse;
74 }
75
76 public function name()
77 {
78 return $this ->name;
79 }
80 }
Le back controller
Nous venons de construire notre routeur qui donne lapplication le contrleur associ.
An de suivre la logique du droulement de lapplication, construisons maintenant notre
back controller de base.
Rchissons, schmatisons
Remmorons-nous ce que permet de faire un objet BackController. Nous avions vu
quun tel objet norait quune seule fonctionnalit : celle dexcuter une action. Comme
pour lobjet Application, je ne vous avais pas parl des caractristiques dun back
controller. Quest-ce qui caractrise un back controller ? Si vous connaissez larchitec-
ture MVC, vous savez quune vue est associe au back controller : cest donc lune de
ses caractristiques.
Maintenant, pensons la nature dun back controller. Celui-ci est propre un module,
et si on la instanci cest que nous voulons quil excute une action. Cela fait donc
deux autres caractristiques : le module et laction.
Enn, il y en a une dernire que vous ne pouvez pas deviner : la page associe au
contrleur. Comme nous le verrons bientt, cest travers cette instance reprsentant
la page que nous allons envoyer au visiteur que le contrleur transmettra des donnes
la vue. Pour linstant, mmorisez juste lide que le contrleur est associ une page
stocke en tant quinstance dans un attribut de la classe BackController.
340
LE BACK CONTROLLER
Une instance de BackController nous permettra donc :
Dexcuter une action (donc une mthode).
Dobtenir la page associe au contrleur.
De modier le module, laction et la vue associs au contrleur.
Cette classe est une classe de base dont hritera chaque contrleur. Par consquent, elle
se doit dtre abstraite. Aussi, il sagit dun composant de lapplication, donc un lien
de parent avec ApplicationComponent est crer. Nous arrivons donc une classe
ressemblant a (voir la gure 19.6).
Figure 19.6 Modlisation de la classe Backcontroller
Notre constructeur se chargera dans un premier temps dappeler le constructeur de son
parent. Dans un second temps, il crera une instance de la classe Page quil stockera
dans lattribut correspondant. Enn, il assignera les valeurs au module, laction et
la vue (par dfaut la vue a la mme valeur que laction).
Concernant la mthode execute(), comment fonctionnera-t-elle ? Son rle est dinvo-
quer la mthode correspondant laction assigne notre objet. Le nom de la m-
thode suit une logique qui est de se nommer executeNomdelaction(). Par exemple, si
nous avons une action show sur notre module, nous devrons implmenter la mthode
executeShow() dans notre contrleur. Aussi, pour une question de simplicit, nous
passerons la requte du client la mthode. En eet, dans la plupart des cas, les m-
thodes auront besoin de la requte du client pour obtenir une donne (que ce soit une
variable GET, POST, ou un cookie).
Codons
Voici le rsultat qui tait obtenir :
1 <?php
2 namespace Library;
341
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
3
4 abstract class BackController extends ApplicationComponent
5 {
6 protected $action = '';
7 protected $module = '';
8 protected $page = null;
9 protected $view = '';
10
11 public function __construct(Application $app , $module ,
$action)
12 {
13 parent :: __construct($app);
14
15 $this ->page = new Page($app);
16
17 $this ->setModule($module);
18 $this ->setAction($action);
19 $this ->setView($action);
20 }
21
22 public function execute ()
23 {
24 $method = 'execute '.ucfirst($this ->action);
25
26 if (! is_callable(array($this , $method)))
27 {
28 throw new \RuntimeException('L\'action "'.$this ->action.'
" n\'est pas dfinie sur ce module ');
29 }
30
31 $this ->$method($this ->app ->httpRequest ());
32 }
33
34 public function page()
35 {
36 return $this ->page;
37 }
38
39 public function setModule($module)
40 {
41 if (! is_string($module) || empty($module))
42 {
43 throw new \InvalidArgumentException('Le module doit tre
une chaine de caractres valide ');
44 }
45
46 $this ->module = $module;
47 }
48
49 public function setAction($action)
342
LE BACK CONTROLLER
50 {
51 if (! is_string($action) || empty($action))
52 {
53 throw new \InvalidArgumentException('L\'action doit tre
une chaine de caractres valide ');
54 }
55
56 $this ->action = $action;
57 }
58
59 public function setView($view)
60 {
61 if (! is_string($view) || empty($view))
62 {
63 throw new \InvalidArgumentException('La vue doit tre une
chaine de caractres valide ');
64 }
65
66 $this ->view = $view;
67 }
68 }
Accder aux managers depuis le contrleur
Un petit souci se pose : comment le contrleur accdera aux managers ? On pourrait
les instancier directement dans la mthode, mais les managers exigent le DAO
1
lors
de la construction de lobjet et ce DAO nest pas accessible depuis le contrleur. Nous
allons donc crer une classe qui grera les managers : jai nomm Managers. Nous
instancierons donc cette classe au sein de notre contrleur en lui passant le DAO. Les
mthodes lles auront accs cet objet et pourront accder aux managers facilement.
Petit rappel sur la structure dun manager
Je vais faire un bref rappel concernant la structure des managers. Un manager, comme
nous lavons vu durant le TP des news, est divis en deux parties. La premire partie
est une classe abstraite listant toutes les mthodes que le manager doit implmenter.
La seconde partie est constitue des classes qui vont implmenter ces mthodes, sp-
ciques chaque DAO. Pour reprendre lexemple des news, la premire partie tait
constitue de la classe abstraite NewsManager et la seconde partie de NewsManager_PDO
et NewsManager_MySQLi.
En plus du DAO, il faudra donc spcier notre classe grant ces managers lAPI que
lon souhaite utiliser. Suivant ce quon lui demande, notre classe nous retournera une
instance de NewsManager_PDO ou NewsManager_MySQLi par exemple.
1. Database Access Object
343
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
La classe Managers
Schmatiquement, voici quoi ressemble la classe Managers (voir la gure 19.7).
Figure 19.7 Modlisation de la classe Managers
Cette instance de Managers sera stocke dans un attribut de lobjet BackController
comme $managers par exemple. Lattribution dune instance de Managers cet attribut
se fait dans le constructeur de la manire suivante :
1 <?php
2 namespace Library;
3
4 abstract class BackController extends ApplicationComponent
5 {
6 // ...
7 protected $managers = null;
8
9 public function __construct(Application $app , $module ,
$action)
10 {
11 parent :: __construct($app);
12
13 $this ->managers = new Managers('PDO', PDOFactory ::
getMysqlConnexion ());
14 $this ->page = new Page($app);
15
16 $this ->setModule($module);
17 $this ->setAction($action);
18 $this ->setView($action);
19 }
20
21 // ...
22 }
Niveau code, voici quoi ressemble la classe Managers :
1 <?php
2 namespace Library;
3
4 class Managers
5 {
6 protected $api = null;
7 protected $dao = null;
344
LE BACK CONTROLLER
8 protected $managers = array ();
9
10 public function __construct($api , $dao)
11 {
12 $this ->api = $api;
13 $this ->dao = $dao;
14 }
15
16 public function getManagerOf($module)
17 {
18 if (! is_string($module) || empty($module))
19 {
20 throw new \InvalidArgumentException('Le module spcifi
est invalide ');
21 }
22
23 if (!isset($this ->managers[$module ]))
24 {
25 $manager = '\\ Library \\ Models \\'.$module.'Manager_ '.$this
->api;
26 $this ->managers[$module] = new $manager($this ->dao);
27 }
28
29 return $this ->managers[$module ];
30 }
31 }
Et maintenant, comment je passe linstance de PDO au constructeur de
Managers ? Je linstancie directement ?
Non car cela vous obligerait modier la classe BackController chaque modication,
ce qui nest pas trs exible. Je vous conseille plutt dutiliser le pattern factory que
nous avons vu durant la prcdente partie avec la classe PDOFactory :
1 <?php
2 namespace Library;
3
4 class PDOFactory
5 {
6 public static function getMysqlConnexion ()
7 {
8 $db = new \PDO('mysql:host=localhost;dbname=news', 'root',
'');
9 $db ->setAttribute (\PDO:: ATTR_ERRMODE , \PDO::
ERRMODE_EXCEPTION);
10
11 return $db;
12 }
345
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
13 }
propos des managers
Nous trouvons ici des natures en commun : les managers sont tous des managers ! Et
les entits, et bien ce sont toutes des entits ! :-
Cela peut vous sembler bte (oui, a lest), mais si je vous dis ceci cest pour mettre
en vidence le lien de parent qui saute forcment aux yeux aprs cette phrase. Tous
les managers devront donc hriter de Manager et chaque entit de Entity. La classe
Manager se chargera dimplmenter un constructeur qui demandera le DAO par le biais
dun paramtre, comme ceci :
1 <?php
2 namespace Library;
3
4 abstract class Manager
5 {
6 protected $dao;
7
8 public function __construct($dao)
9 {
10 $this ->dao = $dao;
11 }
12 }
Par contre, la classe Entity est lgrement plus complexe. En eet, celle-ci ore
quelques fonctionnalits :
Implmentation dun constructeur qui hydratera lobjet si un tableau de valeurs lui
est fourni.
Implmentation dune mthode qui permet de vrier si lenregistrement est nouveau
ou pas. Pour cela, on vrie si lattribut $id est vide ou non (ce qui inclut le fait que
toutes les tables devront possder un champ nomm id).
Implmentation des getters / setters.
Implmentation de linterface ArrayAccess (ce nest pas obligatoire, cest juste que
je prfre utiliser lobjet comme un tableau dans les vues).
Le code obtenu devrait sapparenter celui-ci :
1 <?php
2 namespace Library;
3
4 abstract class Entity implements \ArrayAccess
5 {
6 protected $erreurs = array (),
7 $id;
8
9 public function __construct(array $donnees = array())
10 {
346
LE BACK CONTROLLER
11 if (!empty($donnees))
12 {
13 $this ->hydrate($donnees);
14 }
15 }
16
17 public function isNew ()
18 {
19 return empty($this ->id);
20 }
21
22 public function erreurs ()
23 {
24 return $this ->erreurs;
25 }
26
27 public function id()
28 {
29 return $this ->id;
30 }
31
32 public function setId($id)
33 {
34 $this ->id = (int) $id;
35 }
36
37 public function hydrate(array $donnees)
38 {
39 foreach ($donnees as $attribut => $valeur)
40 {
41 $methode = 'set'.ucfirst($attribut);
42
43 if (is_callable(array($this , $methode)))
44 {
45 $this ->$methode($valeur);
46 }
47 }
48 }
49
50 public function offsetGet($var)
51 {
52 if (isset($this ->$var) && is_callable(array($this , $var)))
53 {
54 return $this ->$var();
55 }
56 }
57
58 public function offsetSet($var , $value)
59 {
60 $method = 'set'.ucfirst($var);
347
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
61
62 if (isset($this ->$var) && is_callable(array($this , $method)
))
63 {
64 $this ->$method($value);
65 }
66 }
67
68 public function offsetExists($var)
69 {
70 return isset($this ->$var) && is_callable(array($this , $var)
);
71 }
72
73 public function offsetUnset($var)
74 {
75 throw new \Exception('Impossible de supprimer une
quelconque valeur ');
76 }
77 }
La page
Toujours dans la continuit du droulement de lapplication, nous allons nous intresser
maintenant la page qui, nous venons de le voir, tait attache notre contrleur.
Rchissons, schmatisons
Commenons par nous intresser aux fonctionnalits de notre classe Page. Vous le
savez, une page est compose de la vue et du layout an de gnrer le tout. Ainsi, nous
avons une premire fonctionnalit : celle de gnrer une page. De plus, le contrleur
doit pouvoir transmettre des variables la vue, stocke dans cette page : ajouter une
variable la page est donc une autre fonctionnalit. Enn, la page doit savoir quelle
vue elle doit gnrer et ajouter au layout : il est donc possible dassigner une vue
la page (voir la gure 19.8).
Pour rsumer, une instance de notre classe Page nous permet :
Dajouter une variable la page (le contrleur aura besoin de passer des donnes
la vue).
Dassigner une vue la page.
De gnrer la page avec le layout de lapplication.
Avant de commencer coder cette classe, voici le diagramme la reprsentant (voir la
gure 19.9).
348
LA PAGE
Figure 19.8 Composition dune page
Figure 19.9 Modlisation de la classe Page
349
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
Codons
La classe est, me semble-t-il, plutt facile crire. Cependant, il se peut que vous vous
demandiez comment crire la mthode getGeneratedPage(). En fait, il faut inclure
les pages pour gnrer leur contenu, et stocker ce contenu dans une variable grce
aux fonctions de tamporisation de sortie pour pouvoir sen servir plus tard. Pour la
transformation du tableau stock dans lattribut $vars en variables, regardez du ct
de la fonction extract.

fonctions de tamporisation de
sortie
Code web : 313485

Documentation extract
Code web : 779902
1 <?php
2 namespace Library;
3
4 class Page extends ApplicationComponent
5 {
6 protected $contentFile;
7 protected $vars = array();
8
9 public function addVar($var , $value)
10 {
11 if (! is_string($var) || is_numeric($var) || empty($var))
12 {
13 throw new \InvalidArgumentException('Le nom de la
variable doit tre une chaine de caractre non nulle')
;
14 }
15
16 $this ->vars[$var] = $value;
17 }
18
19 public function getGeneratedPage ()
20 {
21 if (! file_exists($this ->contentFile))
22 {
23 throw new \RuntimeException('La vue spcifie n\'existe
pas');
24 }
25
26 extract($this ->vars);
27
28 ob_start ();
29 require $this ->contentFile;
30 $content = ob_get_clean ();
31
32 ob_start ();
350
LA PAGE
33 require __DIR__.'/../ Applications/'.$this ->app ->name().'/
Templates/layout.php';
34 return ob_get_clean ();
35 }
36
37 public function setContentFile($contentFile)
38 {
39 if (! is_string($contentFile) || empty($contentFile))
40 {
41 throw new \InvalidArgumentException('La vue spcifie est
invalide ');
42 }
43
44 $this ->contentFile = $contentFile;
45 }
46 }
Retour sur la classe BackController
Maintenant que nous avons crit notre classe Page, je peux vous faire crire une instruc-
tion dans la classe BackController et, plus particulirement, la mthode setView($view).
En eet, lorsque lon change de vue, il faut en informer la page concerne grce la
mthode setContentFile() de notre classe Page :
1 <?php
2 namespace Library;
3
4 abstract class BackController extends ApplicationComponent
5 {
6 // ...
7
8 public function setView($view)
9 {
10 if (! is_string($view) || empty($view))
11 {
12 throw new \InvalidArgumentException('La vue doit tre une
chaine de caractres valide ');
13 }
14
15 $this ->view = $view;
16
17 $this ->page ->setContentFile(__DIR__.'/../ Applications/'.
$this ->app ->name().'/Modules/'.$this ->module.'/Views/'.
$this ->view.'.php');
18 }
19 }
351
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
Retour sur la mthode HTTPResponse::redirect404()
tant donn que vous avez compris comment fonctionne un objet Page, vous tes
capables dcrire cette mthode laisse vide jusqu prsent. Comment procder ?
On commence dabord par crer une instance de la classe Page que lon stocke dans
lattribut correspondant.
On assigne ensuite le chier qui fait oce de vue gnrer la page. Ce chier
contient le message derreur format. Vous pouvez placer tous ces chiers dans le
dossier /Errors par exemple, sous le nom code.html. Le chemin menant au chier
contenant lerreur 404 sera donc /Errors/404.html.
On ajoute un header disant que le document est non trouv (HTTP/1.0 404 Not
Found).
On envoie la rponse.
Et voici ce que lon obtient :
1 <?php
2 namespace Library;
3
4 class HTTPResponse extends ApplicationComponent
5 {
6 // ...
7
8 public function redirect404 ()
9 {
10 $this ->page = new Page($this ->app);
11 $this ->page ->setContentFile(__DIR__.'/../ Errors/404.html');
12
13 $this ->addHeader('HTTP/1.0 404 Not Found');
14
15 $this ->send();
16 }
17
18 // ...
19 }
Bonus : lutilisateur
Cette classe est un bonus , cest--dire quelle nest pas indispensable lapplication.
Cependant, nous allons nous en servir plus tard donc ne sautez pas cette partie ! Mais
rassurez-vous, nous aurons vite fait de la crer.
Rchissons, schmatisons
Lutilisateur, quest-ce que cest ? Lutilisateur est celui qui visite votre site. Comme
tout site web qui se respecte, nous avons besoin denregistrer temporairement lutilisa-
352
BONUS : LUTILISATEUR
teur dans la mmoire du serveur an de stocker des informations le concernant. Nous
crons donc une session pour lutilisateur. Vous connaissez sans doute ce systme de
sessions avec le tableau $_SESSION et les fonctions ce sujet que propose lAPI. Notre
classe, que nous nommerons User, devra nous permettre de grer facilement la session
de lutilisateur. Nous pourrons donc, par le biais dun objet User :
Assigner un attribut lutilisateur.
Obtenir la valeur dun attribut.
Authentier lutilisateur (cela nous sera utile lorsque nous ferons un formulaire de
connexion pour lespace dadministration).
Savoir si lutilisateur est authenti.
Assigner un message informatif lutilisateur que lon achera sur la page.
Savoir si lutilisateur a un tel message.
Et enn, rcuprer ce message.
Cela donne naissance une classe de ce genre (voir la gure 19.10).
Figure 19.10 Modlisation de la classe User
Codons
Avant de commencer coder la classe, il faut que vous ajoutiez linstruction invoquant
session_start() au dbut du chier, en dehors de la classe. Ainsi, ds linclusion du
chier par lautoload, la session dmarrera et lobjet cr sera fonctionnel.
Ceci tant, voici le code que je vous propose :
1 <?php
2 namespace Library;
3
4 session_start ();
5
6 class User extends ApplicationComponent
7 {
8 public function getAttribute($attr)
9 {
353
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
10 return isset($_SESSION[$attr ]) ? $_SESSION[$attr] : null;
11 }
12
13 public function getFlash ()
14 {
15 $flash = $_SESSION['flash'];
16 unset($_SESSION['flash']);
17
18 return $flash;
19 }
20
21 public function hasFlash ()
22 {
23 return isset($_SESSION['flash']);
24 }
25
26 public function isAuthenticated ()
27 {
28 return isset($_SESSION['auth']) && $_SESSION['auth'] ===
true;
29 }
30
31 public function setAttribute($attr , $value)
32 {
33 $_SESSION[$attr] = $value;
34 }
35
36 public function setAuthenticated($authenticated = true)
37 {
38 if (! is_bool($authenticated))
39 {
40 throw new \InvalidArgumentException('La valeur spcifie
la mthode User:: setAuthenticated () doit tre un
boolean ');
41 }
42
43 $_SESSION['auth'] = $authenticated;
44 }
45
46 public function setFlash($value)
47 {
48 $_SESSION['flash'] = $value;
49 }
50 }
Comme promis, ce fut court, et tout ce qui compose cette classe est, il me semble,
facilement comprhensible.
354
BONUS 2 : LA CONFIGURATION
Pensez modier votre classe Application an dajouter un attribut $user
et crer lobjet User dans le constructeur que vous stockerez dans lattribut
cr.
Bonus 2 : la conguration
Cette classe est galement un bonus dans la mesure o elle nest pas essentielle pour
que lapplication fonctionne. Je vous encourage vous entraner crer cette classe :
tout comme la classe User, celle-ci nest pas complique (si tant est que vous sachiez
parser du XML avec une bibliothque telle que DOMDocument).
Rchissons, schmatisons
Tout site web bien conu se doit dtre congurable souhait. Par consquent, il faut
que chaque application possde un chier de conguration dclarant des paramtres
propres ladite application. Par exemple, si nous voulons acher un nombre de news
prcis sur laccueil, il serait prfrable de spcier un paramtre nombre_de_news
lapplication que nous mettrons par exemple cinq plutt que dinsrer ce nombre en
dur dans le code. De cette faon, nous aurons modier uniquement ce nombre dans
le chier de conguration pour faire varier le nombre de news sur la page daccueil.
Un format pour le chier
Le format du chier sera le mme que le chier contenant les routes, savoir le format
XML. La base du chier sera celle-ci :
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <definitions >
3 </definitions >
Chaque paramtre se dclarera avec une balise define comme ceci :
1 <define var="nombre_news" value="5" />
Emplacement du chier
Le chier de conguration est propre chaque application. Par consquent, il devra
tre plac aux cts du chier routes.xml sous le doux nom de app.xml. Son chemin
complet sera donc /Applications/Nomdelapplication/Cong/app.xml.
Fonctionnement de la classe
Nous aurons donc une classe soccupant de grer la conguration. Pour faire simple,
nous nallons lui implmenter quune seule fonctionnalit : celle de rcuprer un para-
355
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
mtre. Il faut galement garder lesprit quil sagit dun composant de lapplica-
tion, donc il faut un lien de parent avec. . . Je suis sr que vous savez !
La mthode get($var) (qui sera charge de rcuprer la valeur dun paramtre) ne
devra pas parcourir chaque fois le chier de conguration, cela serait bien trop lourd.
Sil sagit du premier appel de la mthode, il faudra ouvrir le chier XML en instanciant
la classe DOMDocument et stocker tous les paramtres dans un attribut (admettons
$vars). Ainsi, chaque fois que la mthode get() sera invoque, nous naurons qu
retourner le paramtre prcdemment enregistr.
Notre classe, plutt simple, ressemble donc ceci (voir la gure 19.11).
Figure 19.11 Modlisation de la classe Cong
Codons
Voici le rsultat qui vous deviez obtenir :
1 <?php
2 namespace Library;
3
4 class Config extends ApplicationComponent
5 {
6 protected $vars = array();
7
8 public function get($var)
9 {
10 if (!$this ->vars)
11 {
12 $xml = new \DOMDocument;
13 $xml ->load(__DIR__.'/../ Applications/'.$this ->app ->name()
.'/Config/app.xml');
14
15 $elements = $xml ->getElementsByTagName('define ');
16
17 foreach ($elements as $element)
18 {
19 $this ->vars[$element ->getAttribute('var')] = $element ->
getAttribute('value');
356
BONUS 2 : LA CONFIGURATION
20 }
21 }
22
23 if (isset($this ->vars[$var]))
24 {
25 return $this ->vars[$var];
26 }
27
28 return null;
29 }
30 }
Il faut, comme nous lavons fait en crant la classe User, ajouter un nou-
vel attribut notre classe Application qui stockera linstance de Config.
Appelez-le par exemple $config (pour tre original :- ).
Notre bibliothque est crite, voil une bonne chose de faite ! Il ne reste plus qu crire
les applications et dvelopper les modules.
Cependant, avant de continuer, je vais massurer que vous me suiviez toujours. Voici
larborescence de larchitecture, avec des explications sur chaque dossier (voir la gure
19.12).
Figure 19.12 Arborescence
Il est possible que des dossiers vous paraissent sortir de nulle part. Cependant, je
vous assure que jen ai parl au moins une fois lors de la cration de certaines classes.
Nhsitez surtout pas relire le chapitre en vous appuyant sur cette arborescence, vous
comprendrez sans doute mieux.
357
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
358
Chapitre 20
Le frontend
Dicult :
N
ous allons enn aborder quelque chose de concret en construisant notre premire
application : le frontend. Cette application est la partie visible par tout le monde.
Nous allons construire un module de news avec commentaires, il y a de quoi faire,
donc ne perdons pas une seconde de plus !
Il est indispensable de connatre la classe DateTime. Cependant, ne vous inquitez pas,
cette classe est trs simple dutilisation, surtout que nous nutiliserons que deux mthodes
(le constructeur et la mthode format()).
359
CHAPITRE 20. LE FRONTEND
Lapplication
Nous allons commencer par crer les chiers de base dont nous aurons besoin. Vous
en connaissez quelques uns dj : la classe reprsentant lapplication, le layout, et les
deux chiers de conguration. Cependant, il nous en faut deux autres : un chier
qui instanciera notre classe et qui invoquera la mthode run(), et un .htaccess qui
redirigera toutes les pages sur ce chier. Nous verrons cela aprs avoir cr les quatre
chiers prcdemment cits.
La classe FrontendApplication
Commenons par crer notre classe FrontendApplication. Avant de commencer, assurez-
vous que vous avez bien cr le dossier /Applications/Frontend qui contiendra notre
application. Crez lintrieur le chier contenant notre classe, savoir FrontendAp-
plication.class.php.
Bien. Commencez par crire le minimum de la classe avec le namespace correspondant
(je le rappelle : le namespace est identique au chemin venant vers le chier contenant
la classe) en implmentant les deux mthodes crire, savoir __construct() (qui
aura pour simple contenu dappeler le constructeur parent puis de spcier le nom de
lapplication), et run(). Cette dernire mthode crira cette suite dinstruction :
Obtention du contrleur grce la mthode parente getController().
Excution du contrleur.
Assignation de la page cre par le contrleur la rponse.
Envoi de la rponse.
Votre classe devrait ressembler ceci :
1 <?php
2 namespace Applications\Frontend;
3
4 class FrontendApplication extends \Library\Application
5 {
6 public function __construct ()
7 {
8 parent :: __construct ();
9
10 $this ->name = 'Frontend ';
11 }
12
13 public function run()
14 {
15 $controller = $this ->getController ();
16 $controller ->execute ();
17
18 $this ->httpResponse ->setPage($controller ->page());
19 $this ->httpResponse ->send();
20 }
21 }
360
LAPPLICATION
Finalement, le droulement est assez simple quand on y regarde de plus prs.
Le layout
Tout site web qui se respecte se doit davoir un design. Nous nallons pas nous taler sur
ce type de cration, ce nest pas le sujet qui nous occupe. Nous allons donc nous servir
dun pack libre anciennement disponible sur un site de designs : jai nomm Envision.
Cest un design trs simple et facilement intgrable, idal pour ce que nous avons
faire. Vous pouvez le tlcharger grce au code web suivant :

Tlcharger Envision
Code web : 534847
Je vous laisse tlcharger le pack et dcouvrir les chiers quil contient. Pour rappel, les
chiers sont placer dans /Web. Vous devriez ainsi vous retrouver avec deux dossiers
dans /Web : /Web/css et /Web/images.
Revenons-en au layout. Celui-ci est assez simple et respecte les contraintes imposes
par le design.
1 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
2 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
3 <head >
4 <title >
5 <?php if (! isset($title)) { ?>
6 Mon super site
7 <?php } else { echo $title; } ?>
8 </title >
9
10 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
11
12 <link rel="stylesheet" href="/css/Envision.css" type="text/
css" />
13 </head >
14
15 <body >
16 <div id="wrap">
17 <div id="header">
18 <h1 id="logo -text"><a href="/">Mon super site </a></h1>
19 <p id="slogan">Comment a il ny a presque rien ? </
p>
20 </div >
21
22 <div id="menu">
23 <ul>
24 <li><a href="/">Accueil </a></li >
25 <?php if ($user ->isAuthenticated ()) { ?>
26 <li><a href="/admin/">Admin </a></li >
361
CHAPITRE 20. LE FRONTEND
27 <li><a href="/admin/news -insert.html">Ajouter une
news </a></li >
28 <?php } ?>
29 </ul>
30 </div >
31
32 <div id="content -wrap">
33 <div id="main">
34 <?php if ($user ->hasFlash ()) echo '<p style ="text -
align: center;">', $user ->getFlash (), '</p>'; ?>
35
36 <?php echo $content; ?>
37 </div >
38 </div >
39
40 <div id="footer"></div >
41 </div >
42 </body >
43 </html >
Quest-ce que cest que ces variables $user ?
La variable $user fait rfrence linstance de User. Elle doit tre initialise dans la
mthode getGeneratedPage() de la classe Page :
1 <?php
2 namespace Library;
3
4 class Page extends ApplicationComponent
5 {
6 // ...
7
8 public function getGeneratedPage ()
9 {
10 if (! file_exists($this ->contentFile))
11 {
12 throw new \InvalidArgumentException('La vue spcifie n\'
existe pas');
13 }
14
15 $user = $this ->app ->user();
16
17 extract($this ->vars);
18
19 ob_start ();
20 require $this ->contentFile;
21 $content = ob_get_clean ();
362
LAPPLICATION
22
23 ob_start ();
24 require dirname(__FILE__).'/../ Applications/'.$this ->app
->name().'/Templates/layout.php';
25 return ob_get_clean ();
26 }
27
28 // ...
29 }
Notez que si vous utilisez la variable $this, elle fera rfrence lobjet Page
car le layout est inclut dans la mthode Page::getGeneratedPage().
Les deux chiers de conguration
Nous allons prparer le terrain en crant les deux chiers de conguration dont nous
avons besoin : les chiers app.xml et routes.xml, pour linstant quasi-vierges :
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <definitions >
3 </definitions >
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <routes >
3 </routes >
Linstanciation de FrontendApplication
Pour instancier FrontendApplication, il va falloir crer un chier frontend.php. Ce
chier devra dabord inclure lautoload. Celui-ci aura donc pour simple contenu :
1 <?php
2 require '../ Library/autoload.php';
3
4 $app = new Applications\Frontend\FrontendApplication;
5 $app ->run();
Rcrire toutes les URL
Il faut que toutes les URL pointent vers ce chier. Pour cela, nous allons nous pencher
vers lURL rewriting. Voici le contenu du .htaccess :
1 RewriteEngine On
2
363
CHAPITRE 20. LE FRONTEND
3 # Si le fichier auquel on tente d'accder existe (si on veut
accder une image par exemple).
4 # Alors on ne rcrit pas l'URL.
5 RewriteCond %{ REQUEST_FILENAME} !-f
6 RewriteRule ^(.*)$ frontend.php [QSA ,L]
Le module de news
Nous allons commencer en douceur par un systme de news. Pourquoi en douceur ? Car
vous avez dj fait cet exercice lors du prcdent TP! Ainsi, nous allons voir comment
lintgrer au sein de lapplication, et vous verrez ainsi plus clair sur la manire dont
elle fonctionne. Pour ne perdre personne, nous allons refaire le TP petit petit pour
mieux lintgrer dans lapplication. Ainsi, je vais commencer par vous rappeler ce que
jattends du systme de news.
Fonctionnalits
Il doit tre possible dexcuter deux actions direntes sur le module de news :
Acher lindex du module. Cela aura pour eet de dvoiler les cinq dernires news
avec le titre et lextrait du contenu (seuls les 200 premiers caractres seront achs).
Acher une news spcique en cliquant sur son titre. Lauteur apparatra, ainsi que
la date de modication si la news a t modie.
Comme pour tout module, commencez par crer les dossiers et chiers de base, savoir :
Le dossier /Applications/Frontend/Modules/News qui contiendra notre mo-
dule.
Le chier /Applications/Frontend/Modules/News/NewsController.class.php
qui contiendra notre contrleur.
Le dossier /Applications/Frontend/Modules/News/Views qui contiendra les
vues.
Le chier /Library/Models/NewsManager.class.php qui contiendra notre ma-
nager de base ;
Le chier /Library/Models/NewsManager_PDO.class.php qui contiendra notre
manager utilisant PDO.
Le chier /Library/Entities/News.class.php qui contiendra la classe reprsen-
tant un enregistrement.
Structure de la table news
Une news est constitue dun titre, dun auteur et dun contenu. Aussi, il faut stocker
la date dajout de la news ainsi que sa date de modication. Cela nous donne une table
news ressemblant ceci :
1 CREATE TABLE IF NOT EXISTS news (
364
LE MODULE DE NEWS
2 id smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
3 auteur varchar(30) COLLATE latin1_general_ci NOT NULL ,
4 titre varchar(100) COLLATE latin1_general_ci NOT NULL ,
5 contenu text COLLATE latin1_general_ci NOT NULL ,
6 dateAjout datetime NOT NULL ,
7 dateModif datetime NOT NULL ,
8 PRIMARY KEY (id )
9 ) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ;
Nous pouvons dsormais crire la classe reprsentant cette entit : News.
1 <?php
2 namespace Library\Entities;
3
4 class News extends \Library\Entity
5 {
6 protected $auteur ,
7 $titre ,
8 $contenu ,
9 $dateAjout ,
10 $dateModif;
11
12 const AUTEUR_INVALIDE = 1;
13 const TITRE_INVALIDE = 2;
14 const CONTENU_INVALIDE = 3;
15
16 public function isValid ()
17 {
18 return !(empty($this ->auteur) || empty($this ->titre) ||
empty($this ->contenu));
19 }
20
21
22 // SETTERS //
23
24 public function setAuteur($auteur)
25 {
26 if (! is_string($auteur) || empty($auteur))
27 {
28 $this ->erreurs [] = self:: AUTEUR_INVALIDE;
29 }
30 else
31 {
32 $this ->auteur = $auteur;
33 }
34 }
35
36 public function setTitre($titre)
37 {
38 if (! is_string($titre) || empty($titre))
39 {
365
CHAPITRE 20. LE FRONTEND
40 $this ->erreurs [] = self:: TITRE_INVALIDE;
41 }
42 else
43 {
44 $this ->titre = $titre;
45 }
46 }
47
48 public function setContenu($contenu)
49 {
50 if (! is_string($contenu) || empty($contenu))
51 {
52 $this ->erreurs [] = self:: CONTENU_INVALIDE;
53 }
54 else
55 {
56 $this ->contenu = $contenu;
57 }
58 }
59
60 public function setDateAjout (\ DateTime $dateAjout)
61 {
62 $this ->dateAjout = $dateAjout;
63 }
64
65 public function setDateModif (\ DateTime $dateModif)
66 {
67 $this ->dateModif = $dateModif;
68 }
69
70 // GETTERS //
71
72 public function auteur ()
73 {
74 return $this ->auteur;
75 }
76
77 public function titre ()
78 {
79 return $this ->titre;
80 }
81
82 public function contenu ()
83 {
84 return $this ->contenu;
85 }
86
87 public function dateAjout ()
88 {
89 return $this ->dateAjout;
366
LE MODULE DE NEWS
90 }
91
92 public function dateModif ()
93 {
94 return $this ->dateModif;
95 }
96 }
Laction index
La route
Commenons par implmenter cette action. La premire chose faire est de crer une
nouvelle route : quelle URL pointera vers cette action ? Je vous propose simplement
que ce soit la racine du site web, donc ce sera lURL /. Pour crer cette route, il va
falloir modier notre chier de conguration et y ajouter cette ligne :
1 <route url="/" module="News" action="index" />
Vient ensuite limplmentation de laction dans le contrleur.
Le contrleur
Qui dit nouvelle action dit nouvelle mthode, et cette mthode cest executeIndex().
Cette mthode devra rcuprer les cinq dernires news (le nombre cinq devra tre
stock dans le chier de conguration de lapplication, savoir /Applications/Fron-
tend/Cong/app.xml). Il faudra parcourir cette liste de news an de nassigner aux
news quun contenu de 200 caractres au maximum. Ensuite, il faut passer la liste des
news la vue :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $nombreNews = $this ->app ->config ()->get('nombre_news ');
9 $nombreCaracteres = $this ->app ->config ()->get('
nombre_caracteres ');
10
11 // On ajoute une dfinition pour le titre.
12 $this ->page ->addVar('title', 'Liste des '.$nombreNews.'
dernires news');
13
14 // On rcupre le manager des news.
15 $manager = $this ->managers ->getManagerOf('News');
16
367
CHAPITRE 20. LE FRONTEND
17 // Cette ligne , vous ne pouviez pas la deviner sachant qu'
on n'a pas encore touch au modle.
18 // Contentez -vous donc d'crire cette instruction , nous
implmenterons la mthode ensuite.
19 $listeNews = $manager ->getList(0, $nombreNews);
20
21 foreach ($listeNews as $news)
22 {
23 if (strlen($news ->contenu ()) > $nombreCaracteres)
24 {
25 $debut = substr($news ->contenu (), 0, $nombreCaracteres)
;
26 $debut = substr($debut , 0, strrpos($debut , ' ')) . '...
';
27
28 $news ->setContenu($debut);
29 }
30 }
31
32 // On ajoute la variable $listeNews la vue.
33 $this ->page ->addVar('listeNews ', $listeNews);
34 }
35 }
Comme vous le voyez, jutilise le chier de conguration pour rcuprer le nombre de
news acher et le nombre maximum de caractres. Voici le chier de conguration :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <definitions >
3 <define var="nombre_news" value="5" />
4 <define var="nombre_caracteres" value="200" />
5 </definitions >
La vue
toute action correspond une vue du mme nom. Ici, la vue crer sera /Applica-
tions/Frontend/Modules/News/Views/index.php. Voici un exemple trs simple
pour cette vue :
1 <?php
2 foreach ($listeNews as $news)
3 {
4 ?>
5 <h2><a href="news -<?php echo $news['id ']; ?>.html"><?php echo
$news['titre']; ?></a></h2 >
6 <p><?php echo nl2br($news['contenu ']); ?></p>
7 <?php
8 }
368
LE MODULE DE NEWS
Notez lutilisation des news comme des tableaux : cela est possible du fait que lobjet
est une instance dune classe qui implmente ArrayAccess.
Le modle
Nous allons modier deux classes faisant partie du modle, savoir NewsManager et
NewsManager_PDO. Nous allons implmenter cette dernire classe une mthode :
getList(). Sa classe parente doit donc aussi tre modie pour dclarer cette m-
thode.
1 <?php
2 namespace Library\Models;
3
4 abstract class NewsManager extends \Library\Manager
5 {
6 /**
7 * Mthode retournant une liste de news demande
8 * @param $debut int La premire news slectionner
9 * @param $limite int Le nombre de news slectionner
10 * @return array La liste des news. Chaque entre est une
instance de News.
11 */
12 abstract public function getList($debut = -1, $limite = -1);
13 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 public function getList($debut = -1, $limite = -1)
9 {
10 $sql = 'SELECT id, auteur , titre , contenu , dateAjout ,
dateModif FROM news ORDER BY id DESC';
11
12 if ($debut != -1 || $limite != -1)
13 {
14 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
15 }
16
17 $requete = $this ->dao ->query($sql);
18 $requete ->setFetchMode (\PDO:: FETCH_CLASS | \PDO::
FETCH_PROPS_LATE , '\Library\Entities\News');
19
20 $listeNews = $requete ->fetchAll ();
21
22 foreach ($listeNews as $news)
23 {
369
CHAPITRE 20. LE FRONTEND
24 $news ->setDateAjout(new \DateTime($news ->dateAjout ()));
25 $news ->setDateModif(new \DateTime($news ->dateModif ()));
26 }
27
28 $requete ->closeCursor ();
29
30 return $listeNews;
31 }
32 }
Vous pouvez faire le test : accdez la racine de votre site et vous dcouvrirez. . .
un gros blanc, car aucune news nest prsente en BDD :- . Vous pouvez en ajou-
ter manuellement via phpMyAdmin en attendant que nous ayons construit lespace
dadministration.
Si la constante PDO::FETCH_CLASS vous est inconnue, je vous invite relire
la premire partie du TP sur la ralisation dun systme de news.
Laction show
La route
Je vous propose que les URL du type news-id.html pointent vers cette action. Modiez
donc le chier de conguration des routes pour y ajouter celle-ci :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/" module="News" action="index" />
4 <route url="/news -([0-9]+)\.html" module="News" action="show"
vars="id"/>
5 </routes >
Le contrleur
Le contrleur implmentera la mthode executeShow(). Son contenu est simple : le
contrleur ira demander au manager la news correspondant lidentiant puis, il pas-
sera cette news la vue, en ayant pris soin de remplacer les sauts de lignes par des
balises <br> dans le contenu de la news.
Si la news nexiste pas, il faudra rediriger lutilisateur vers une erreur 404.
1 <?php
2 namespace Applications\Frontend\Modules\News;
370
LE MODULE DE NEWS
3
4 class NewsController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $nombreNews = $this ->app ->config ()->get('nombre_news ');
9 $nombreCaracteres = $this ->app ->config ()->get('
nombre_caracteres ');
10
11 // On ajoute une dfinition pour le titre.
12 $this ->page ->addVar('title', 'Liste des '.$nombreNews.'
dernires news');
13
14 // On rcupre le manager des news.
15 $manager = $this ->managers ->getManagerOf('News');
16
17 $listeNews = $manager ->getList(0, $nombreNews);
18
19 foreach ($listeNews as $news)
20 {
21 if (strlen($news ->contenu ()) > $nombreCaracteres)
22 {
23 $debut = substr($news ->contenu (), 0, $nombreCaracteres)
;
24 $debut = substr($debut , 0, strrpos($debut , ' ')) . '...
';
25
26 $news ->setContenu($debut);
27 }
28 }
29
30 // On ajoute la variable $listeNews la vue.
31 $this ->page ->addVar('listeNews ', $listeNews);
32 }
33
34 public function executeShow (\ Library\HTTPRequest $request)
35 {
36 $news = $this ->managers ->getManagerOf('News')->getUnique(
$request ->getData('id'));
37
38 if (empty($news))
39 {
40 $this ->app ->httpResponse ()->redirect404 ();
41 }
42
43 $this ->page ->addVar('title', $news ->titre());
44 $this ->page ->addVar('news', $news);
45 }
46 }
371
CHAPITRE 20. LE FRONTEND
La vue
La vue se contente de grer lachage de la news. Faites comme bon vous semble, cela
na pas trop dimportance. Voici la version que je vous propose :
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
2 <h2 ><?php echo $news['titre']; ?></h2>
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
Le modle
Nous allons l aussi toucher nos classes NewsManager et NewsManager_PDO en ajoutant
la mthode getUnique().
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 /**
9 * Mthode retournant une liste de news demande.
10 * @param $debut int La premire news slectionner
11 * @param $limite int Le nombre de news slectionner
12 * @return array La liste des news. Chaque entre est une
instance de News.
13 */
14 abstract public function getList($debut = -1, $limite = -1);
15
16 /**
17 * Mthode retournant une news prcise.
18 * @param $id int L'identifiant de la news rcuprer
19 * @return News La news demande
20 */
21 abstract public function getUnique($id);
22 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
372
LE MODULE DE NEWS
6 class NewsManager_PDO extends NewsManager
7 {
8 public function getList($debut = -1, $limite = -1)
9 {
10 $sql = 'SELECT id, auteur , titre , contenu , dateAjout ,
dateModif FROM news ORDER BY id DESC';
11
12 if ($debut != -1 || $limite != -1)
13 {
14 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
15 }
16
17 $requete = $this ->dao ->query($sql);
18 $requete ->setFetchMode (\PDO:: FETCH_CLASS | \PDO::
FETCH_PROPS_LATE , '\Library\Entities\News');
19
20 $listeNews = $requete ->fetchAll ();
21
22 foreach ($listeNews as $news)
23 {
24 $news ->setDateAjout(new \DateTime($news ->dateAjout ()));
25 $news ->setDateModif(new \DateTime($news ->dateModif ()));
26 }
27
28 $requete ->closeCursor ();
29
30 return $listeNews;
31 }
32
33 public function getUnique($id)
34 {
35 $requete = $this ->dao ->prepare('SELECT id, auteur , titre ,
contenu , dateAjout , dateModif FROM news WHERE id = :id')
;
36 $requete ->bindValue(':id', (int) $id , \PDO:: PARAM_INT);
37 $requete ->execute ();
38
39 $requete ->setFetchMode (\PDO:: FETCH_CLASS | \PDO::
FETCH_PROPS_LATE , '\Library\Entities\News');
40
41 if ($news = $requete ->fetch())
42 {
43 $news ->setDateAjout(new \DateTime($news ->dateAjout ()));
44 $news ->setDateModif(new \DateTime($news ->dateModif ()));
45
46 return $news;
47 }
48
49 return null;
50 }
373
CHAPITRE 20. LE FRONTEND
51 }
Ajoutons des commentaires
Cahier des charges
Nous allons ajouter une action notre module de news : lajout dun commentaire. Il
ne faudra pas oublier de modier notre module de news, et plus spcialement laction
show pour laisser apparatre la liste des commentaires ainsi quun lien menant au
formulaire dajout de commentaire.
Avant toute chose, il va falloir crer les modles nous permettant dinteragir avec la
BDD pour accder aux commentaires :
Le chier /Library/Models/CommentsManager.class.php qui contiendra notre
manager de base.
Le chier /Library/Models/CommentsManager_PDO.class.php qui inclura
notre manager utilisant PDO.
Le chier /Library/Entities/Comment.class.php qui comportera la classe re-
prsentant un enregistrement.
Structure de la table comments
Un commentaire est assign une news. Il est constitu dun auteur et dun contenu,
ainsi que de sa date denregistrement. Notre table comments doit donc tre constitue
de la sorte :
1 CREATE TABLE IF NOT EXISTS comments (
2 id mediumint(9) NOT NULL AUTO_INCREMENT ,
3 news smallint(6) NOT NULL ,
4 auteur varchar(50) COLLATE latin1_general_ci NOT NULL ,
5 contenu text COLLATE latin1_general_ci NOT NULL ,
6 date datetime NOT NULL ,
7 PRIMARY KEY (id )
8 ) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ;
Puisque nous connaissons la structure dun commentaire, nous pouvons crire une
partie du modle, savoir la classe Comment :
1 <?php
2 namespace Library\Entities;
3
4 class Comment extends \Library\Entity
5 {
6 protected $news ,
7 $auteur ,
8 $contenu ,
9 $date;
374
AJOUTONS DES COMMENTAIRES
10
11 const AUTEUR_INVALIDE = 1;
12 const CONTENU_INVALIDE = 2;
13
14 public function isValid ()
15 {
16 return !(empty($this ->auteur) || empty($this ->contenu));
17 }
18
19 // SETTERS
20
21 public function setNews($news)
22 {
23 $this ->news = (int) $news;
24 }
25
26 public function setAuteur($auteur)
27 {
28 if (! is_string($auteur) || empty($auteur))
29 {
30 $this ->erreurs [] = self:: AUTEUR_INVALIDE;
31 }
32 else
33 {
34 $this ->auteur = $auteur;
35 }
36 }
37
38 public function setContenu($contenu)
39 {
40 if (! is_string($contenu) || empty($contenu))
41 {
42 $this ->erreurs [] = self:: CONTENU_INVALIDE;
43 }
44 else
45 {
46 $this ->contenu = $contenu;
47 }
48 }
49
50 public function setDate (\ DateTime $date)
51 {
52 $this ->date = $date;
53 }
54
55 // GETTERS
56
57 public function news()
58 {
59 return $this ->news;
375
CHAPITRE 20. LE FRONTEND
60 }
61
62 public function auteur ()
63 {
64 return $this ->auteur;
65 }
66
67 public function contenu ()
68 {
69 return $this ->contenu;
70 }
71
72 public function date()
73 {
74 return $this ->date;
75 }
76 }
Laction insertComment
La route
Nous nallons pas faire dans la fantaisie pour cette action, nous allons prendre une
URL basique : commenter-idnews.html. Rajoutez donc cette nouvelle route dans le
chier de conguration des routes :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/" module="News" action="index" />
4 <route url="/news -([0-9]+)\.html" module="News" action="show"
vars="id"/>
5 <route url="/commenter -([0-9]+)\.html" module="News" action="
insertComment" vars="news" />
6 </routes >
La vue
Dans un premier temps, nous allons nous attarder sur la vue car cest lintrieur de
celle-ci que nous allons construire le formulaire. Cela nous permettra donc de savoir
quels champs seront traiter par le contrleur. Je vous propose un formulaire trs
simple demandant le pseudo et le contenu lutilisateur :
1 <h2>Ajouter un commentaire </h2 >
2 <form action="" method="post">
3 <p>
4 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
376
AJOUTONS DES COMMENTAIRES
5 <label >Pseudo </label >
6 <input type="text" name="pseudo" value=" <?php if (isset(
$comment)) echo htmlspecialchars($comment['auteur ']); ?>
" /><br />
7
8 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu
est invalide.<br />'; ?>
9 <label >Contenu </label >
10 <textarea name="contenu" rows="7" cols="50" ><?php if (isset
($comment)) echo htmlspecialchars($comment['contenu ']);
?></textarea ><br />
11
12 <input type="submit" value="Commenter" />
13 </p>
14 </form >
Lidentiant de la news est stock dans lURL. Puisque nous allons envoyer le formulaire
sur cette mme page, lidentiant de la news sera toujours prsent dans lURL et donc
accessible via le contrleur.
Le contrleur
Notre mthode executeInsertComment() se chargera dans un premier temps de vri-
er si le formulaire a t envoy en vriant si la variable POST pseudo existe. Ensuite,
elle procdera la vrication des donnes et insrera le commentaire en BDD si toutes
les donnes sont valides.
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsertComment (\ Library\HTTPRequest
$request)
9 {
10 $this ->page ->addVar('title', 'Ajout d\'un commentaire ');
11
12 if ($request ->postExists('pseudo '))
13 {
14 $comment = new \Library\Entities\Comment(array(
15 'news' => $request ->getData('news'),
16 'auteur ' => $request ->postData('pseudo '),
17 'contenu ' => $request ->postData('contenu ')
18 ));
19
20 if ($comment ->isValid ())
21 {
377
CHAPITRE 20. LE FRONTEND
22 $this ->managers ->getManagerOf('Comments ')->save(
$comment);
23
24 $this ->app ->user()->setFlash('Le commentaire a bien t
ajout, merci !');
25
26 $this ->app ->httpResponse ()->redirect('news -'.$request ->
getData('news').'.html');
27 }
28 else
29 {
30 $this ->page ->addVar('erreurs ', $comment ->erreurs ());
31 }
32
33 $this ->page ->addVar('comment ', $comment);
34 }
35 }
36
37 // ...
38 }
Le modle
Nous aurons besoin dimplmenter une mthode dans notre classe CommentsManager :
save(). En fait, il sagit dun raccourci , cette mthode appelle elle-mme une autre
mthode : add() ou modify() selon si le commentaire est dj prsent en BDD. Notre
manager peut savoir si lenregistrement est dj enregistr ou pas grce la mthode
isNew().
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 /**
9 * Mthode permettant d'ajouter un commentaire
10 * @param $comment Le commentaire ajouter
11 * @return void
12 */
13 abstract protected function add(Comment $comment);
14
15 /**
16 * Mthode permettant d'enregistrer un commentaire.
17 * @param $comment Le commentaire enregistrer
18 * @return void
19 */
20 public function save(Comment $comment)
378
AJOUTONS DES COMMENTAIRES
21 {
22 if ($comment ->isValid ())
23 {
24 $comment ->isNew() ? $this ->add($comment) : $this ->modify(
$comment);
25 }
26 else
27 {
28 throw new \RuntimeException('Le commentaire doit tre
valid pour tre enregistr');
29 }
30 }
31 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 protected function add(Comment $comment)
9 {
10 $q = $this ->dao ->prepare('INSERT INTO comments SET news = :
news , auteur = :auteur , contenu = :contenu , date = NOW()
');
11
12 $q ->bindValue(':news', $comment ->news(), \PDO:: PARAM_INT);
13 $q ->bindValue(':auteur ', $comment ->auteur ());
14 $q ->bindValue(':contenu ', $comment ->contenu ());
15
16 $q ->execute ();
17
18 $comment ->setId($this ->dao ->lastInsertId ());
19 }
20 }
Limplmentation de la mthode modify() se fera lors de la construction de lespace
dadministration.
Achage des commentaires
Modication du contrleur
Il sut simplement de passer la liste des commentaires la vue. Une seule instruction
sut donc :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
379
CHAPITRE 20. LE FRONTEND
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeShow (\ Library\HTTPRequest $request)
9 {
10 $news = $this ->managers ->getManagerOf('News')->getUnique(
$request ->getData('id'));
11
12 if (empty($news))
13 {
14 $this ->app ->httpResponse ()->redirect404 ();
15 }
16
17 $this ->page ->addVar('title', $news ->titre());
18 $this ->page ->addVar('news', $news);
19 $this ->page ->addVar('comments ', $this ->managers ->
getManagerOf('Comments ')->getListOf($news ->id()));
20 }
21 }
Modication de la vue achant une news
La vue devra parcourir la liste des commentaires passs pour les acher. Les liens
pointant vers lajout dun commentaire devront aussi gurer sur la page.
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
2 <h2 ><?php echo $news['titre']; ?></h2>
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
8
9 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
10
11 <?php
12 if (empty($comments))
13 {
14 ?>
15 <p>Aucun commentaire na encore t post. Soyez le premier
en laisser un !</p>
16 <?php
17 }
18
380
AJOUTONS DES COMMENTAIRES
19 foreach ($comments as $comment)
20 {
21 ?>
22 <fieldset >
23 <legend >
24 Post par <strong ><?php echo htmlspecialchars($comment['
auteur ']); ?></strong > le <?php echo $comment['date'
]->format('d/m/Y H\hi'); ?>
25 </legend >
26 <p><?php echo nl2br(htmlspecialchars($comment['contenu ']));
?></p>
27 </fieldset >
28 <?php
29 }
30 ?>
31
32 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
Modication du manager des commentaires
Le manager des commentaires devra implmenter la mthode getListOf() dont a
besoin notre contrleur pour bien fonctionner. Voici la version que je vous propose :
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 /**
9 * Mthode permettant d'ajouter un commentaire.
10 * @param $comment Le commentaire ajouter
11 * @return void
12 */
13 abstract protected function add(Comment $comment);
14
15 /**
16 * Mthode permettant d'enregistrer un commentaire.
17 * @param $comment Le commentaire enregistrer
18 * @return void
19 */
20 public function save(Comment $comment)
21 {
22 if ($comment ->isValid ())
23 {
24 $comment ->isNew() ? $this ->add($comment) : $this ->modify(
$comment);
381
CHAPITRE 20. LE FRONTEND
25 }
26 else
27 {
28 throw new \RuntimeException('Le commentaire doit tre
valid pour tre enregistr');
29 }
30 }
31
32 /**
33 * Mthode permettant de rcuprer une liste de commentaires.
34 * @param $news La news sur laquelle on veut rcuprer les
commentaires
35 * @return array
36 */
37 abstract public function getListOf($news);
38 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 protected function add(Comment $comment)
9 {
10 $q = $this ->dao ->prepare('INSERT INTO comments SET news = :
news , auteur = :auteur , contenu = :contenu , date = NOW()
');
11
12 $q->bindValue(':news', $comment ->news(), \PDO:: PARAM_INT);
13 $q->bindValue(':auteur ', $comment ->auteur ());
14 $q->bindValue(':contenu ', $comment ->contenu ());
15
16 $q->execute ();
17
18 $comment ->setId($this ->dao ->lastInsertId ());
19 }
20
21 public function getListOf($news)
22 {
23 if (! ctype_digit($news))
24 {
25 throw new \InvalidArgumentException('L\'identifiant de la
news pass doit tre un nombre entier valide ');
26 }
27
28 $q = $this ->dao ->prepare('SELECT id, news , auteur , contenu ,
date FROM comments WHERE news = :news');
29 $q->bindValue(':news', $news , \PDO:: PARAM_INT);
382
AJOUTONS DES COMMENTAIRES
30 $q ->execute ();
31
32 $q ->setFetchMode (\PDO:: FETCH_CLASS | \PDO:: FETCH_PROPS_LATE
, '\Library\Entities\Comment ');
33
34 $comments = $q->fetchAll ();
35
36 foreach ($comments as $comment)
37 {
38 $comment ->setDate(new \DateTime($comment ->date()));
39 }
40
41 return $comments;
42 }
43 }
383
CHAPITRE 20. LE FRONTEND
384
Chapitre 21
Le backend
Dicult :
N
otre application est compose dun systme de news avec commentaires. Or, nous
ne pouvons actuellement pas ajouter de news, ni modrer les commentaires. Pour
ce faire, nous allons crer un espace dadministration, qui nest autre ici que le ba-
ckend. Cet espace dadministration sera ainsi compos dun systme de gestion de news
(ajout, modication et suppression) ainsi que dun systme de gestion de commentaires
(modication et suppression).
Ayant dj cr le frontend, ce chapitre devrait tre plus facile pour vous. Nanmoins, une
nouveaut fait son apparition : celle dinterdire le contenu de lapplication aux visiteurs. Ne
tranons donc pas, nous avons pas mal de travail qui nous attend !
385
CHAPITRE 21. LE BACKEND
Lapplication
Comme pour lapplication Frontend, nous aurons besoin de crer les chiers de base : la
classe reprsentant lapplication, le layout, les deux chiers de conguration et le chier
qui instanciera notre classe. Assurez-vous galement davoir cr le dossier /Applica-
tions/Backend.
La classe BackendApplication
Cette classe ne sera pas strictement identique la classe FrontendApplication. En
eet, nous devons scuriser lapplication an que seuls les utilisateurs authentis y
aient accs.
Pour rappel, voici le fonctionnement de la mthode run() de la classe FrontendAp-
plication :
Obtention du contrleur grce la mthode parente getController().
Excution du contrleur.
Assignation de la page cre par le contrleur la rponse.
Envoi de la rponse.
La classe BackendApplication fonctionnera de la mme faon, la dirence prs
que la premire instruction ne sera excute que si lutilisateur est authenti. Si-
non, nous allons rcuprer le contrleur du module de connexion que nous allons
crer dans ce chapitre. Voici donc le fonctionnement de la mthode run() de la classe
BackendApplication :
Si lutilisateur est authenti :
obtention du contrleur grce la mthode parente getController().
Sinon :
instanciation du contrleur du module de connexion.
Excution du contrleur.
Assignation de la page cre par le contrleur la rponse.
Envoi de la rponse.
Aide : nous avons un attribut $user dans notre classe qui reprsente lutilisa-
teur. Regardez les mthodes que nous lui avons implmentes si vous ne savez
pas comment on peut savoir sil est authenti ou non. Rappel : noubliez
pas dinclure lautoload au dbut du chier !
Vous devriez avoir une classe de ce type :
1 <?php
2 namespace Applications\Backend;
3
4 class BackendApplication extends \Library\Application
5 {
6 public function __construct ()
7 {
386
LAPPLICATION
8 parent :: __construct ();
9
10 $this ->name = 'Backend ';
11 }
12
13 public function run()
14 {
15 if ($this ->user ->isAuthenticated ())
16 {
17 $controller = $this ->getController ();
18 }
19 else
20 {
21 $controller = new Modules\Connexion\ConnexionController(
$this , 'Connexion ', 'index');
22 }
23
24 $controller ->execute ();
25
26 $this ->httpResponse ->setPage($controller ->page());
27 $this ->httpResponse ->send();
28 }
29 }
Le layout
Le layout est le mme que celui du frontend. Sachez quen pratique, cela est rare et
vous aurez gnralement deux layouts dirents (chaque application a ses spcicits).
Cependant, ici il nest pas ncessaire de faire deux chiers dirents. Vous pouvez donc
soit copier/coller le layout du frontend dans le dossier /Applications/Backend/-
Templates, soit crer le layout et inclure celui du frontend :
1 <?php require dirname(__FILE__).'/../../ Frontend/Templates/
layout.php';
Les deux chiers de conguration
L aussi il faut crer les deux chiers de conguration. Mettez-y, comme nous lavons
fait au prcdent chapitre, les structures de base.
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <definitions >
3 </definitions >
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <routes >
3 </routes >
387
CHAPITRE 21. LE BACKEND
Linstanciation de BackendApplication
Linstanciation de BackendApplication se fera dans le chier /Web/backend.php,
comme nous avons procd pour linstanciation de FrontendApplication.
Rappel : pensez inclure lautoload avant dinstancier la classe
BackendApplication !
1 <?php
2 require '../ Library/autoload.php';
3
4 $app = new Applications\Backend\BackendApplication;
5 $app ->run();
Rcrire les URL
Nous allons maintenant modier le chier .htaccess. Actuellement, toutes les URL sont
rediriges vers frontend.php. Nous garderons toujours cette rgle, mais nous allons
dabord en ajouter une autre : nous allons rediriger toutes les URL commenant par
admin/ vers backend.php. Vous tes libres de choisir un autre prxe, mais il faut
bien choisir quelque chose pour slectionner lapplication concerne par lURL.
Le .htaccess ressemble donc ceci :
1 RewriteEngine On
2
3 RewriteRule ^admin/ backend.php [QSA ,L]
4
5 RewriteCond %{ REQUEST_FILENAME} !-f
6
7 RewriteRule ^(.*)$ frontend.php [QSA ,L]
Le module de connexion
Ce module est un peu particulier. En eet, aucune route ne sera dnie pour pointer
vers ce module. De plus, ce module ne ncessite aucun stockage de donnes, nous
naurons donc pas de modle. La seule fonctionnalit attendue du module est dacher
son index. Cet index aura pour rle dacher le formulaire de connexion et de traiter
les donnes de ce formulaire.
Avant de commencer construire le module, nous allons prparer le terrain. O stocker
lidentiant et le mot de passe permettant daccder lapplication? Je vous propose
tout simplement dajouter deux dnitions dans le chier de conguration de lappli-
cation :
388
LE MODULE DE CONNEXION
1 <?xml version="1.0" encoding="utf -8" ?>
2 <definitions >
3 <define var="login" value="admin" />
4 <define var="pass" value="mdp" />
5 </definitions >
Vous pouvez maintenant, si ce nest pas dj fait, crer le dossier /Applications/Ba-
ckend/Modules/Connexion.
La vue
Nous allons dbuter par crer la vue correspondant lindex du module. Ce sera un
simple formulaire demandant le nom dutilisateur et le mot de passe linternaute :
1 <h2>Connexion </h2 >
2
3 <form action="" method="post">
4 <label >Pseudo </label >
5 <input type="text" name="login" /><br />
6
7 <label >Mot de passe </label >
8 <input type="password" name="password" /><br /><br />
9
10 <input type="submit" value="Connexion" />
11 </form >
Le contrleur
Procdons maintenant llaboration du contrleur. Ce contrleur implmentera une
seule mthode : executeIndex(). Cette mthode devra, si le formulaire a t envoy,
vrier si le pseudo et le mot de passe entrs sont corrects. Si cest le cas, lutilisateur
est authenti, sinon un message derreur sache.
1 <?php
2 namespace Applications\Backend\Modules\Connexion;
3
4 class ConnexionController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $this ->page ->addVar('title', 'Connexion ');
9
10 if ($request ->postExists('login'))
11 {
12 $login = $request ->postData('login');
13 $password = $request ->postData('password ');
14
15 if ($login == $this ->app ->config ()->get('login') &&
$password == $this ->app ->config ()->get('pass'))
389
CHAPITRE 21. LE BACKEND
16 {
17 $this ->app ->user()->setAuthenticated(true);
18 $this ->app ->httpResponse ()->redirect('.');
19 }
20 else
21 {
22 $this ->app ->user()->setFlash('Le pseudo ou le mot de
passe est incorrect.');
23 }
24 }
25 }
26 }
Ce fut court, mais essentiel. Nous venons de scuriser en un rien de temps lapplication
toute entire. De plus, ce module est rutilisable dans dautres projets ! En eet, rien
ne le lie cette application. partir du moment o lapplication aura un chier de
conguration adapt (cest--dire quil a dclar les variables login et pass), alors elle
pourra sen servir.
Le module de news
Nous allons maintenant attaquer le module de news sur notre application. Comme
dhabitude, nous commenons par la liste des fonctionnalits attendues.
Fonctionnalits
Ce module doit nous permettre de grer le contenu de la base de donnes. Par cons-
quent, nous devons avoir quatre actions :
Laction index qui nous ache la liste des news avec des liens pour les modier ou
supprimer.
Laction insert pour ajouter une news.
Laction update pour modier une news.
Laction delete pour supprimer une news.
Laction index
La route
Tout dabord, dnissons lURL qui pointera vers cette action. Je vous propose tout
simplement que ce soit laccueil de lespace dadministration :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 </routes >
390
LE MODULE DE NEWS
Le contrleur
Le contrleur se chargera uniquement de passer la liste des news la vue ainsi que le
nombre de news prsent. Le contenu de la mthode est donc assez simple :
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $this ->page ->addVar('title', 'Gestion des news');
9
10 $manager = $this ->managers ->getManagerOf('News');
11
12 $this ->page ->addVar('listeNews ', $manager ->getList ());
13 $this ->page ->addVar('nombreNews ', $manager ->count());
14 }
15 }
Comme vous le voyez, nous nous resservons de la mthode getList() que nous avions
implmente au cours du prcdent chapitre au cours de la construction du frontend.
Cependant, il nous reste implmenter une mthode dans notre manager : count().
Le modle
La mthode count() est trs simple : elle ne fait quexcuter une requte pour renvoyer
le rsultat.
1 <?php
2 namespace Library\Models;
3
4 abstract class NewsManager extends \Library\Manager
5 {
6 /**
7 * Mthode renvoyant le nombre de news total.
8 * @return int
9 */
10 abstract public function count ();
11
12 // ...
13 }
1 <?php
2 namespace Library\Models;
3
4 class NewsManager_PDO extends NewsManager
5 {
6 public function count ()
391
CHAPITRE 21. LE BACKEND
7 {
8 return $this ->dao ->query('SELECT COUNT (*) FROM news')->
fetchColumn ();
9 }
10
11 // ...
12 }
La vue
La vue se contente de parcourir le tableau de news pour en acher les donnes. Faites
dans la simplicit.
1 <p style="text -align: center">Il y a actuellement <?php echo
$nombreNews; ?> news. En voici la liste :</p>
2
3 <table >
4 <tr><th>Auteur </th ><th>Titre </th><th >Date dajout </th ><th >
Dernire modification </th ><th >Action </th ></tr >
5 <?php
6 foreach ($listeNews as $news)
7 {
8 echo '<tr><td>', $news['auteur '], '</td><td>', $news['titre'
], '</td><td>le ', $news['dateAjout ']->format('d/m/Y H\
hi'), '</td><td>', ($news['dateAjout '] == $news['dateModif
'] ? '-' : 'le '.$news['dateModif ']->format('d/m/Y H\hi'
)), '</td><td><a href="news -update -', $news['id'], '.html
"><img src ="/ images/update.png" alt=" Modifier" /></a> <a
href="news -delete -', $news['id'], '.html"><img src="/
images/delete.png" alt=" Supprimer" /></a></td ></tr >', "\n"
;
9 }
10 ?>
11 </table >
Laction insert
La route
Je vous propose que lURL qui pointera vers cette action soit /admin/news-insert.html.
Je pense que maintenant vous savez comment dnir une route, mais je remets le chier
pour que tout le monde suive bien :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
392
LE MODULE DE NEWS
5 </routes >
Le contrleur
Le contrleur vrie si le formulaire a t envoy. Si cest le cas, alors il procdera
la vrication des donnes et insrera la news en BDD si tout est valide. Cependant,
il y a un petit problme : lorsque nous implmenterons laction update, nous allons
devoir rcrire la partie traitement du formulaire car la validation des donnes suit
la mme logique. Nous allons donc crer une autre mthode au sein du contrleur,
nomme processForm(), qui se chargera de traiter le formulaire et denregistrer la
news en BDD.
Rappel : le manager contient une mthode save() qui se chargera soit
dajouter la news si elle est nouvelle, soit de la mettre jour si elle est dj
enregistre. Cest cette mthode que vous devez invoquer.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsert (\ Library\HTTPRequest $request)
9 {
10 if ($request ->postExists('auteur '))
11 {
12 $this ->processForm($request);
13 }
14
15 $this ->page ->addVar('title', 'Ajout d\'une news');
16 }
17
18 public function processForm (\ Library\HTTPRequest $request)
19 {
20 $news = new \Library\Entities\News(
21 array(
22 'auteur ' => $request ->postData('auteur '),
23 'titre' => $request ->postData('titre'),
24 'contenu ' => $request ->postData('contenu ')
25 )
26 );
27
28 // L'identifiant de la news est transmis si on veut la
modifier.
29 if ($request ->postExists('id'))
30 {
31 $news ->setId($request ->postData('id'));
393
CHAPITRE 21. LE BACKEND
32 }
33
34 if ($news ->isValid ())
35 {
36 $this ->managers ->getManagerOf('News')->save($news);
37
38 $this ->app ->user()->setFlash($news ->isNew() ? 'La news a
bien t ajoute !' : 'La news a bien t modifie !')
;
39 }
40 else
41 {
42 $this ->page ->addVar('erreurs ', $news ->erreurs ());
43 }
44
45 $this ->page ->addVar('news', $news);
46 }
47 }
Le modle
Nous allons implmenter les mthodes save() et add() dans notre manager an que
notre contrleur puisse tre fonctionnel.
Rappel : la mthode save() simplmente directement dans NewsManager
puisquelle ne dpend pas du DAO.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 /**
9 * Mthode permettant d'ajouter une news.
10 * @param $news News La news ajouter
11 * @return void
12 */
13 abstract protected function add(News $news);
14
15 /**
16 * Mthode permettant d'enregistrer une news.
17 * @param $news News la news enregistrer
18 * @see self::add()
19 * @see self:: modify ()
20 * @return void
394
LE MODULE DE NEWS
21 */
22 public function save(News $news)
23 {
24 if ($news ->isValid ())
25 {
26 $news ->isNew() ? $this ->add($news) : $this ->modify($news)
;
27 }
28 else
29 {
30 throw new \RuntimeException('La news doit tre valide
pour tre enregistre');
31 }
32 }
33
34 // ...
35 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 protected function add(News $news)
9 {
10 $requete = $this ->dao ->prepare('INSERT INTO news SET auteur
= :auteur , titre = :titre , contenu = :contenu ,
dateAjout = NOW(), dateModif = NOW()');
11
12 $requete ->bindValue(':titre', $news ->titre());
13 $requete ->bindValue(':auteur ', $news ->auteur ());
14 $requete ->bindValue(':contenu ', $news ->contenu ());
15
16 $requete ->execute ();
17 }
18
19 // ...
20 }
La vue
L aussi, nous utiliserons de la duplication de code pour acher le formulaire. En eet,
la vue correspondant laction update devra galement acher ce formulaire. Nous
allons donc crer un chier qui contiendra ce formulaire et qui sera inclus au sein des
vues. Je vous propose de lappeler _form.php (le _ est ici utilis pour bien indiquer
quil ne sagit pas dune vue mais dun lment inclure).
395
CHAPITRE 21. LE BACKEND
1 <h2>Ajouter une news </h2>
2 <?php require '_form.php';
1 <form action="" method="post">
2 <p>
3 <?php if (isset($erreurs) && in_array (\ Library\Entities\
News:: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
4 <label >Auteur </label >
5 <input type="text" name="auteur" value=" <?php if (isset(
$news)) echo $news['auteur ']; ?>" /><br />
6
7 <?php if (isset($erreurs) && in_array (\ Library\Entities\
News:: TITRE_INVALIDE , $erreurs)) echo 'Le titre est
invalide.<br />'; ?>
8 <label >Titre </label ><input type="text" name="titre" value="
<?php if (isset($news)) echo $news['titre ']; ?>" /><br
/>
9
10 <?php if (isset($erreurs) && in_array (\ Library\Entities\
News:: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu est
invalide.<br />'; ?>
11 <label >Contenu </label ><textarea rows="8" cols="60" name="
contenu" ><?php if (isset($news)) echo $news['contenu '];
?></textarea ><br />
12 <?php
13 if(isset($news) && !$news ->isNew())
14 {
15 ?>
16 <input type="hidden" name="id" value=" <?php echo $news['id
']; ?>" />
17 <input type="submit" value="Modifier" name="modifier" />
18 <?php
19 }
20 else
21 {
22 ?>
23 <input type="submit" value="Ajouter" />
24 <?php
25 }
26 ?>
27 </p>
28 </form >
396
LE MODULE DE NEWS
Laction update
La route
Pas doriginalit ici, nous allons choisir une URL basique : /admin/news-update-
id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 </routes >
Le contrleur
La mthode executeUpdate() est quasiment identique executeInsert(). La seule
dirence est quil faut passer la news la vue si le formulaire na pas t envoy.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeUpdate (\ Library\HTTPRequest $request)
9 {
10 if ($request ->postExists('auteur '))
11 {
12 $this ->processForm($request);
13 }
14 else
15 {
16 $this ->page ->addVar('news', $this ->managers ->getManagerOf
('News')->getUnique($request ->getData('id')));
17 }
18
19 $this ->page ->addVar('title', 'Modification d\'une news');
20 }
21
22 // ...
23 }
397
CHAPITRE 21. LE BACKEND
Le modle
Ce code fait appel deux mthodes : getUnique() et modify(). La premire a dj
t implmente au cours du prcdent chapitre et la seconde avait volontairement t
laisse de ct. Il est maintenant temps de limplmenter.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de modifier une news.
12 * @param $news news la news modifier
13 * @return void
14 */
15 abstract protected function modify(News $news);
16
17 // ...
18 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 // ...
9
10 protected function modify(News $news)
11 {
12 $requete = $this ->dao ->prepare('UPDATE news SET auteur = :
auteur , titre = :titre , contenu = :contenu , dateModif =
NOW() WHERE id = :id');
13
14 $requete ->bindValue(':titre', $news ->titre());
15 $requete ->bindValue(':auteur ', $news ->auteur ());
16 $requete ->bindValue(':contenu ', $news ->contenu ());
17 $requete ->bindValue(':id', $news ->id(), \PDO:: PARAM_INT);
18
19 $requete ->execute ();
20 }
21
22 // ...
23 }
398
LE MODULE DE NEWS
La vue
De la mme faon que pour laction insert, ce procd tient en deux lignes : il sagit
seulement dinclure le formulaire, cest tout !
1 <h2>Modifier une news </h2>
2 <?php require '_form.php';
Laction delete
La route
Pour continuer dans loriginalit, lURL qui pointera vers cette action sera du type
/admin/news-delete-id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 <route url="/admin/news -delete -([0-9]+)\.html" module="News"
action="delete" vars="id" />
7 </routes >
Le contrleur
Le contrleur se chargera dinvoquer la mthode du manager qui supprimera la news.
Ensuite, il redirigera lutilisateur laccueil de lespace dadministration en ayant pris
soin de spcier un message qui sachera au prochain chargement de page. Ainsi,
cette action ne possdera aucune vue.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeDelete (\ Library\HTTPRequest $request)
9 {
10 $this ->managers ->getManagerOf('News')->delete($request ->
getData('id'));
11
12 $this ->app ->user()->setFlash('La news a bien t supprime
!');
13
399
CHAPITRE 21. LE BACKEND
14 $this ->app ->httpResponse ()->redirect('.');
15 }
16
17 // ...
18 }
Le modle
Ici, une simple requte de type DELETE sut.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de supprimer une news.
12 * @param $id int L'identifiant de la news supprimer
13 * @return void
14 */
15 abstract public function delete($id);
16
17 // ...
18 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 // ...
9
10 public function delete($id)
11 {
12 $this ->dao ->exec('DELETE FROM news WHERE id = '.(int) $id);
13 }
14
15 // ...
16 }
400
NOUBLIONS PAS LES COMMENTAIRES!
Noublions pas les commentaires !
Finissons de construire notre application en implmentant les dernires fonctionnalits
permettant de grer les commentaires.
Fonctionnalits
Nous allons faire simple et implmenter deux fonctionnalits :
La modication de commentaires.
La suppression de commentaires.
Laction updateComment
La route
Commenons, comme dhabitude, par dnir lURL qui pointera vers ce module. Je
vous propose quelque chose de simple comme /admin/comments-update-id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 <route url="/admin/news -delete -([0-9]+)\.html" module="News"
action="delete" vars="id" />
7 <route url="/admin/comment -update -([0-9]+)\.html" module="
News" action="updateComment" vars="id" />
8 </routes >
Le contrleur
La mthode que lon implmentera aura pour rle de contrler les valeurs du formulaire
et de modier le commentaire en BDD si tout est valide. Vous devriez avoir quelque
chose de semblable ce que nous avons dans lapplication Frontend. Il faudra ensuite
rediriger lutilisateur sur la news quil lisait.
Aide : pour rediriger lutilisateur sur la news, il va falloir obtenir lidentiant
de cette dernire. Il faudra donc ajouter un champ cach dans le formulaire
pour transmettre ce paramtre.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
401
CHAPITRE 21. LE BACKEND
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeUpdateComment (\ Library\HTTPRequest
$request)
9 {
10 $this ->page ->addVar('title', 'Modification d\'un
commentaire ');
11
12 if ($request ->postExists('pseudo '))
13 {
14 $comment = new \Library\Entities\Comment(array(
15 'id' => $request ->getData('id'),
16 'auteur ' => $request ->postData('pseudo '),
17 'contenu ' => $request ->postData('contenu ')
18 ));
19
20 if ($comment ->isValid ())
21 {
22 $this ->managers ->getManagerOf('Comments ')->save(
$comment);
23
24 $this ->app ->user()->setFlash('Le commentaire a bien t
modifi !');
25
26 $this ->app ->httpResponse ()->redirect('/news -'.$request
->postData('news').'.html');
27 }
28 else
29 {
30 $this ->page ->addVar('erreurs ', $comment ->erreurs ());
31 }
32
33 $this ->page ->addVar('comment ', $comment);
34 }
35 else
36 {
37 $this ->page ->addVar('comment ', $this ->managers ->
getManagerOf('Comments ')->get($request ->getData('id'))
);
38 }
39 }
40
41 // ...
42 }
402
NOUBLIONS PAS LES COMMENTAIRES!
Le modle
Nous avons ici besoin dimplmenter deux mthodes : modify() et get(). La premire
se contente dexcuter une requte de type UPDATE et la seconde une requte de type
SELECT.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de modifier un commentaire.
12 * @param $comment Le commentaire modifier
13 * @return void
14 */
15 abstract protected function modify(Comment $comment);
16
17 /**
18 * Mthode permettant d'obtenir un commentaire spcifique.
19 * @param $id L'identifiant du commentaire
20 * @return Comment
21 */
22 abstract public function get($id);
23 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 // ...
9
10 protected function modify(Comment $comment)
11 {
12 $q = $this ->dao ->prepare('UPDATE comments SET auteur = :
auteur , contenu = :contenu WHERE id = :id');
13
14 $q ->bindValue(':auteur ', $comment ->auteur ());
15 $q ->bindValue(':contenu ', $comment ->contenu ());
16 $q ->bindValue(':id', $comment ->id(), \PDO:: PARAM_INT);
17
18 $q ->execute ();
19 }
20
403
CHAPITRE 21. LE BACKEND
21 public function get($id)
22 {
23 $q = $this ->dao ->prepare('SELECT id, news , auteur , contenu
FROM comments WHERE id = :id');
24 $q->bindValue(':id', (int) $id , \PDO:: PARAM_INT);
25 $q->execute ();
26
27 $q->setFetchMode (\PDO:: FETCH_CLASS | \PDO:: FETCH_PROPS_LATE
, '\Library\Entities\Comment ');
28
29 return $q ->fetch();
30 }
31 }
La vue
La vue ne fera que contenir le formulaire et acher les erreurs sil y en a.
1 <form action="" method="post">
2 <p>
3 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
4 <label >Pseudo </label ><input type="text" name="pseudo" value
="<?php echo htmlspecialchars($comment['auteur ']); ?>"
/><br />
5
6 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu
est invalide.<br />'; ?>
7 <label >Contenu </label ><textarea name="contenu" rows="7"
cols="50" ><?php echo htmlspecialchars($comment['contenu '
]); ?></textarea ><br />
8
9 <input type="hidden" name="news" value=" <?php echo $comment
['news ']; ?>" />
10 <input type="submit" value="Modifier" />
11 </p>
12 </form >
Modication de la vue de lachage des commentaires
Pour des raisons pratiques, il serait prfrable de modier lachage des commentaires
an dajouter un lien chacun menant vers la modication du commentaire. Pour
cela, il faudra modier la vue correspondant laction show du module news de
lapplication Frontend.
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
404
NOUBLIONS PAS LES COMMENTAIRES!
2 <h2 ><?php echo $news['titre']; ?></h2 >
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
8
9 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
10
11 <?php
12 if (empty($comments))
13 {
14 ?>
15 <p>Aucun commentaire na encore t post. Soyez le premier
en laisser un !</p>
16 <?php
17 }
18
19 foreach ($comments as $comment)
20 {
21 ?>
22 <fieldset >
23 <legend >
24 Post par <strong ><?php echo htmlspecialchars($comment['
auteur ']); ?></strong > le <?php echo $comment['date'
]->format('d/m/Y H\hi'); ?>
25 <?php if ($user ->isAuthenticated ()) { ?> -
26 <a href="admin/comment -update -<?php echo $comment['id
']; ?>.html">Modifier </a>
27 <?php } ?>
28 </legend >
29 <p><?php echo nl2br(htmlspecialchars($comment['contenu ']));
?></p>
30 </fieldset >
31 <?php
32 }
33 ?>
34
35 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
405
CHAPITRE 21. LE BACKEND
Laction deleteComment
La route
Faisons l aussi trs simple : nous navons qu prendre une URL de ce type :
/admin/comments-delete-id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 <route url="/admin/news -delete -([0-9]+)\.html" module="News"
action="delete" vars="id" />
7 <route url="/admin/comment -update -([0-9]+)\.html" module="
News" action="updateComment" vars="id" />
8 <route url="/admin/comment -delete -([0-9]+)\.html" module="
News" action="deleteComment" vars="id" />
9 </routes >
Le contrleur
Il faut dans un premier temps invoquer la mthode du manager permettant de suppri-
mer un commentaire. Redirigez ensuite lutilisateur sur laccueil de lespace dadminis-
tration.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeDeleteComment (\ Library\HTTPRequest
$request)
9 {
10 $this ->managers ->getManagerOf('Comments ')->delete($request
->getData('id'));
11
12 $this ->app ->user()->setFlash('Le commentaire a bien t
supprim !');
13
14 $this ->app ->httpResponse ()->redirect('.');
15 }
16 }
Aucune vue nest donc ncessaire ici.
406
NOUBLIONS PAS LES COMMENTAIRES!
Le modle
Il sut ici dimplmenter la mthode delete() excutant une simple requte DELETE.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de supprimer un commentaire.
12 * @param $id L'identifiant du commentaire supprimer
13 * @return void
14 */
15 abstract public function delete($id);
16
17 // ...
18 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 // ...
9
10 public function delete($id)
11 {
12 $this ->dao ->exec('DELETE FROM comments WHERE id = '.(int)
$id);
13 }
14
15 // ...
16 }
Modication de lachage des commentaires
Nous allons l aussi insrer le lien de suppression de chaque commentaire an de nous
faciliter la tche. Modiez donc la vue de laction show du module news de lapplica-
tion frontend :
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
2 <h2 ><?php echo $news['titre']; ?></h2 >
407
CHAPITRE 21. LE BACKEND
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
8
9 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
10
11 <?php
12 if (empty($comments))
13 {
14 ?>
15 <p>Aucun commentaire na encore t post. Soyez le premier
en laisser un !</p>
16 <?php
17 }
18
19 foreach ($comments as $comment)
20 {
21 ?>
22 <fieldset >
23 <legend >
24 Post par <strong ><?php echo htmlspecialchars($comment['
auteur ']); ?></strong > le <?php echo $comment['date'
]->format('d/m/Y H\hi'); ?>
25 <?php if ($user ->isAuthenticated ()) { ?> -
26 <a href="admin/comment -update -<?php echo $comment['id
']; ?>.html">Modifier </a> |
27 <a href="admin/comment -delete -<?php echo $comment['id
']; ?>.html">Supprimer </a>
28 <?php } ?>
29 </legend >
30 <p><?php echo nl2br(htmlspecialchars($comment['contenu ']));
?></p>
31 </fieldset >
32 <?php
33 }
34 ?>
35
36 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
408
Chapitre 22
Grer les formulaires
Dicult :
I
l est possible que quelque chose vous chionne un petit peu. En eet, dans le frontend,
nous avons cr un formulaire pour ajouter un commentaire. Dans le backend, nous
avons recr quasiment le mme : nous avons fait de la duplication de code. Or,
puisque vous tes un excellent programmeur, cela devrait vous piquer les yeux !
Pour pallier ce problme courant de duplication de formulaires, nous allons externaliser nos
formulaires laide dune API, cest--dire que le code crant le formulaire sera accessible,
un autre endroit, par nimporte quel module de nimporte quelle application. Cette technique
fera dune pierre deux coups : non seulement nos formulaires seront dcentraliss (donc
rutilisables une innit de fois), mais la cration se fera de manire beaucoup plus aise !
Bien sr, comme pour la conception de lapplication, cela deviendra rapide une fois lAPI
dveloppe.
409
CHAPITRE 22. GRER LES FORMULAIRES
Le formulaire
Conception du formulaire
Commenons dans ce chapitre par crer un premier formulaire. Un formulaire, vous le
savez, nest autre quun ensemble de champs permettant dinteragir avec le contenu du
site. Par exemple, voici notre formulaire dajout de commentaire :
1 <form action="" method="post">
2 <p>
3 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
4 <label >Pseudo </label >
5 <input type="text" name="pseudo" value=" <?php if (isset(
$comment)) echo htmlspecialchars($comment['auteur ']); ?>
" /><br />
6
7 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu
est invalide.<br />'; ?>
8 <label >Contenu </label >
9 <textarea name="contenu" rows="7" cols="50" ><?php if (isset
($comment)) echo htmlspecialchars($comment['contenu ']);
?></textarea ><br />
10
11 <input type="submit" value="Commenter" />
12 </p>
13 </form >
Cependant, vous conviendrez quil est long et fastidieux de crer ce formulaire. De plus,
si nous voulons diter un commentaire, il va falloir le dupliquer dans lapplication ba-
ckend. Dans un premier temps, nous allons nous occuper de laspect long et fastidieux :
laissons un objet gnrer tous ces champs notre place !
Lobjet Form
Comme nous venons de le voir, un formulaire nest autre quune liste de champs.
Vous connaissez donc dj le rle de cet objet : il sera charg de reprsenter le formulaire
en possdant une liste de champs.
Commenons alors la liste des fonctionnalits de notre formulaire. Notre formulaire
contient divers champs. Nous devons donc pouvoir ajouter des champs notre for-
mulaire. Ensuite, que serait un formulaire si on ne pouvait pas lacher ? Dans notre
cas, le formulaire ne doit pas tre capable de sacher mais de gnrer tous les champs
qui lui sont attachs an que le contrleur puisse rcuprer le corps du formulaire pour
le passer la vue. Enn, notre formulaire doit possder une dernire fonctionnalit : le
capacit de dclarer si le formulaire est valide ou non en vriant que chaque champ
lest.
410
LE FORMULAIRE
Pour rsumer, nous avons donc trois fonctionnalits. Un objet Form doit tre capable :
Dajouter des champs sa liste de champs.
De gnrer le corps du formulaire.
De vrier si tous les champs sont valides.
Ainsi, au niveau des caractristiques de lobjet, nous en avons qui saute aux yeux : la
liste des champs !
Cependant, un formulaire est galement caractris par autre chose. En eet, si je vous
demande de me dire comment vous allez vrier si tous les champs sont valides, vous
sauriez comment faire ? aucun moment nous navons pass des valeurs notre formu-
laire, donc aucune vrication nest eectuer. Il faudrait donc, dans le constructeur
de notre objet Form, passer un objet contenant toutes ces valeurs. Ainsi, lors de lajout
dun champ, la mthode irait chercher la valeur correspondante dans cet objet et las-
signerait au champ (nous verrons plus tard comment la mthode sait quel attribut
de lentit correspond le champ). votre avis, quoi vont ressembler ces objets ? En
fait, vous les avez dj crs ces objets : ce sont toutes les classes lles de Entity ! Par
exemple, si vous voulez modier un commentaire, vous allez crer un objet Comment
que vous allez hydrater, puis vous crerez un objet Form en passant lobjet Comment au
constructeur.
Ainsi, voici notre classe Form schmatise (voir la gure 22.1).
Figure 22.1 Modlisation de la classe Form
Vous pouvez remarquer que la mthode add() renvoie un objet Form. En fait,
il sagit du formulaire auquel on a ajout le champ : cela permet denchaner
facilement les appels la mthode add() comme nous le verrons juste aprs.
Lobjet Field
Puisque lobjet Form est intimement li ses champs, intressons-nous la conception
de ces champs (ou elds en anglais). Vous laurez peut-tre devin : tous nos champs
seront des objets, chacun reprsentant un champ dirent (une classe reprsentera un
champ texte, une autre classe reprsentera une zone de texte, etc.). ce stade, un tilt
devrait stre produit dans votre tte : ce sont tous des champs, ils doivent donc hriter
dune mme classe reprsentant leur nature en commun, savoir une classe Field !
Commenons par cette classe Field. Quelles fonctionnalits attendons-nous de cette
411
CHAPITRE 22. GRER LES FORMULAIRES
classe ? Un objet Field doit tre capable :
De renvoyer le code HTML reprsentant le champ.
De vrier si la valeur du champ est valide.
Je pense que vous aviez ces fonctionnalits plus ou moins en tte. Cependant, il y
a encore une autre fonctionnalit que nous devons implmenter. En eet, pensez aux
classes qui hriteront de Field et qui reprsenteront chacune un type de champ. Chaque
champ a des attributs spciques. Par exemple, un champ texte (sur une ligne) possde
un attribut maxlength, tandis quune zone de texte (un textarea) possde des attributs
rows et cols. Chaque classe lle aura donc des attributs elles seules. Il serait pratique,
ds la construction de lobjet, de passer ces valeurs notre champ (par exemple, assigner
50 lattribut maxlength). Pour rsoudre ce genre de cas, nous allons procder dune
faon qui ne vous est pas inconnue : nous allons crer une mthode permettant lobjet
de shydrater ! Ainsi, notre classe Field possdera une mthode hydrate(), comme les
entits.
Voici la gure 22.2 le schma reprsentant notre classe Field lie la classe Form,
avec deux classes lles en exemple (StringField reprsentant un champ texte sur une
ligne et la classe TextField reprsentant un textarea).
Dveloppement de lAPI
La classe Form
Pour rappel, voici de quoi la classe Form est compose :
Dun attribut stockant la liste des champs.
Dun attribut stockant lentit correspondant au formulaire.
Dun constructeur rcuprant lentit et invoquant le setter correspondant.
Dune mthode permettant dajouter un champ la liste des champs.
Dune mthode permettant de gnrer le formulaire.
Dune mthode permettant de vrier si le formulaire est valide.
Voici la classe Form que vous auriez du obtenir :
1 <?php
2 namespace Library;
3
4 class Form
5 {
6 protected $entity;
7 protected $fields;
8
9 public function __construct(Entity $entity)
10 {
11 $this ->setEntity($entity);
12 }
13
14 public function add(Field $field)
15 {
412
LE FORMULAIRE
Figure 22.2 Modlisation de la classe Field
413
CHAPITRE 22. GRER LES FORMULAIRES
16 $attr = $field ->name(); // On rcupre le nom du champ.
17 $field ->setValue($this ->entity ->$attr()); // On assigne la
valeur correspondante au champ.
18
19 $this ->fields [] = $field; // On ajoute le champ pass en
argument la liste des champs.
20 return $this;
21 }
22
23 public function createView ()
24 {
25 $view = '';
26
27 // On gnre un par un les champs du formulaire.
28 foreach ($this ->fields as $field)
29 {
30 $view .= $field ->buildWidget ().'<br />';
31 }
32
33 return $view;
34 }
35
36 public function isValid ()
37 {
38 $valid = true;
39
40 // On vrifie que tous les champs sont valides.
41 foreach ($this ->fields as $field)
42 {
43 if (!$field ->isValid ())
44 {
45 $valid = false;
46 }
47 }
48
49 return $valid;
50 }
51
52 public function entity ()
53 {
54 return $this ->entity;
55 }
56
57 public function setEntity(Entity $entity)
58 {
59 $this ->entity = $entity;
60 }
61 }
414
LE FORMULAIRE
La classe Field et ses lles
Voici un petit rappel sur la composition de la classe Field. Cette classe doit tre
compose :
Dun attribut stockant le message derreur associ au champ.
Dun attribut stockant le label du champ.
Dun attribut stockant le nom du champ.
Dun attribut stockant la valeur du champ.
Dun constructeur demandant la liste des attributs avec leur valeur an dhydrater
lobjet.
Dune mthode (abstraite) charge de renvoyer le code HTML du champ.
Dune mthode permettant de savoir si le champ est valide ou non.
Les classes lles, quant elles, nimplmenteront que la mthode abstraite. Si elles pos-
sdent des attributs spciques (comme lattribut maxlength pour la classe StringField),
alors elles devront implmenter les mutateurs correspondant (comme vous le verrez plus
tard, ce nest pas ncessaire dimplmenter les accesseurs).
Voici les trois classes que vous auriez du obtenir (la classe Field avec deux classes lles
en exemple, StringField et TextField) :
1 <?php
2 namespace Library;
3
4 abstract class Field
5 {
6 protected $errorMessage;
7 protected $label;
8 protected $name;
9 protected $value;
10
11 public function __construct(array $options = array())
12 {
13 if (!empty($options))
14 {
15 $this ->hydrate($options);
16 }
17 }
18
19 abstract public function buildWidget ();
20
21 public function hydrate($options)
22 {
23 foreach ($options as $type => $value)
24 {
25 $method = 'set'.ucfirst($type);
26
27 if (is_callable(array($this , $method)))
28 {
29 $this ->$method($value);
415
CHAPITRE 22. GRER LES FORMULAIRES
30 }
31 }
32 }
33
34 public function isValid ()
35 {
36 // On crira cette mthode plus tard.
37 }
38
39 public function label ()
40 {
41 return $this ->label;
42 }
43
44 public function name()
45 {
46 return $this ->name;
47 }
48
49 public function value ()
50 {
51 return $this ->value;
52 }
53
54 public function setLabel($label)
55 {
56 if (is_string($label))
57 {
58 $this ->label = $label;
59 }
60 }
61
62 public function setName($name)
63 {
64 if (is_string($name))
65 {
66 $this ->name = $name;
67 }
68 }
69
70 public function setValue($value)
71 {
72 if (is_string($value))
73 {
74 $this ->value = $value;
75 }
76 }
77 }
1 <?php
416
LE FORMULAIRE
2 namespace Library;
3
4 class StringField extends Field
5 {
6 protected $maxLength;
7
8 public function buildWidget ()
9 {
10 $widget = '';
11
12 if (!empty($this ->errorMessage))
13 {
14 $widget .= $this ->errorMessage.'<br />';
15 }
16
17 $widget .= '<label >'.$this ->label.'</label ><input type="
text" name="'.$this ->name.'"';
18
19 if (!empty($this ->value))
20 {
21 $widget .= ' value="'.htmlspecialchars($this ->value).'"';
22 }
23
24 if (!empty($this ->maxLength))
25 {
26 $widget .= ' maxlength ="'.$this ->maxLength.'"';
27 }
28
29 return $widget .= ' />';
30 }
31
32 public function setMaxLength($maxLength)
33 {
34 $maxLength = (int) $maxLength;
35
36 if ($maxLength > 0)
37 {
38 $this ->maxLength = $maxLength;
39 }
40 else
41 {
42 throw new \RuntimeException('La longueur maximale doit
tre un nombre suprieur 0');
43 }
44 }
45 }
1 <?php
2 namespace Library;
3
417
CHAPITRE 22. GRER LES FORMULAIRES
4 class TextField extends Field
5 {
6 protected $cols;
7 protected $rows;
8
9 public function buildWidget ()
10 {
11 $widget = '';
12
13 if (!empty($this ->errorMessage))
14 {
15 $widget .= $this ->errorMessage.'<br />';
16 }
17
18 $widget .= '<label >'.$this ->label.'</label ><textarea name="
'.$this ->name.'"';
19
20 if (!empty($this ->cols))
21 {
22 $widget .= ' cols="'.$this ->cols.'"';
23 }
24
25 if (!empty($this ->rows))
26 {
27 $widget .= ' rows="'.$this ->rows.'"';
28 }
29
30 $widget .= '>';
31
32 if (!empty($this ->value))
33 {
34 $widget .= htmlspecialchars($this ->value);
35 }
36
37 return $widget.'</textarea >';
38 }
39
40 public function setCols($cols)
41 {
42 $cols = (int) $cols;
43
44 if ($cols > 0)
45 {
46 $this ->cols = $cols;
47 }
48 }
49
50 public function setRows($rows)
51 {
52 $rows = (int) $rows;
418
LE FORMULAIRE
53
54 if ($rows > 0)
55 {
56 $this ->rows = $rows;
57 }
58 }
59 }
Testons nos nouvelles classes
Testons ds maintenant nos classes. Dans notre contrleur de news du frontend, nous
allons modier laction charge dajouter un commentaire. Crons notre formulaire avec
nos nouvelles classes :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsertComment (\ Library\HTTPRequest
$request)
9 {
10 // Si le formulaire a t envoy, on cre le commentaire
avec les valeurs du formulaire.
11 if ($request ->method () == 'POST')
12 {
13 $comment = new \Library\Entities\Comment(array(
14 'news' => $request ->getData('news'),
15 'auteur ' => $request ->postData('auteur '),
16 'contenu ' => $request ->postData('contenu ')
17 ));
18 }
19 else
20 {
21 $comment = new \Library\Entities\Comment;
22 }
23
24 $form = new Form($comment);
25
26 $form ->add(new \Library\StringField(array(
27 'label' => 'Auteur ',
28 'name' => 'auteur ',
29 'maxLength ' => 50),
30 )))
31 ->add(new \Library\TextField(array(
32 'label' => 'Contenu ',
33 'name' => 'contenu ',
419
CHAPITRE 22. GRER LES FORMULAIRES
34 'rows' => 7,
35 'cols' => 50 ,
36 )));
37
38 if ($form ->isValid ())
39 {
40 // On enregistre le commentaire
41 }
42
43 $this ->page ->addVar('comment ', $comment);
44 $this ->page ->addVar('form', $form ->createView ()); // On
passe le formulaire gnr la vue.
45 $this ->page ->addVar('title', 'Ajout d\'un commentaire ');
46 }
47
48 // ...
49 }
1 <h2>Ajouter un commentaire </h2 >
2 <form action="" method="post">
3 <p>
4 <?php echo $form; ?>
5
6 <input type="submit" value="Commenter" />
7 </p>
8 </form >
Cependant, avouez que ce nest pas pratique davoir ceci en plein milieu de notre
contrleur. De plus, si nous avons besoin de crer ce formulaire un autre endroit,
nous devrons copier/coller tous ces appels la mthode add() et recrer tous les
champs. Niveau duplication de code, nous sommes servi ! Nous rsoudrons ce problme
dans la suite du chapitre. Mais avant cela, intressons-nous la validation du formu-
laire. En eet, le contenu de la mthode isValid() est rest vide : faisons appel aux
validateurs !
Les validateurs
Un validateur, comme son nom lindique, est charg de valider une donne. Mais atten-
tion : un validateur ne peut valider quune contrainte. Par exemple, si vous voulez
vrier que votre valeur nest pas nulle et quelle ne dpasse pas les cinquante carac-
tres, alors vous aurez besoin de deux validateurs : le premier vriera que la valeur
nest pas nulle, et le second vriera que la chaine de caractres ne dpassera pas les
cinquante caractres.
L aussi, vous devriez savoir ce qui vous attend au niveau des classes : nous aurons une
classe de base (Validator) et une innit de classes lles (dans le cas prcdent, on
peut imaginer les classes NotNullValidator et MaxLengthValidator). Attaquons-les
ds maintenant !
420
LES VALIDATEURS
Conception des classes
La classe Validator
Notre classe de base, Validator, sera charge, comme nous lavons dit, de valider une
donne. Et cest tout : un validateur ne sert rien dautre que valider une donne. Au
niveau des caractristiques, il ny en a dans ce cas galement, une seule : le message
derreur que le validateur doit pouvoir renvoyer si la valeur passe nest pas valide.
Voici donc notre classe schmatise (voir la gure 22.3).
Figure 22.3 Modlisation de notre classe Validator
Les classes lles
Les classes lles sont trs simples. Commenons par la plus facile : NotNullValidator.
Celle-ci, comme toute classe lle, sera charge dimplmenter la mthode isValid($value).
Et cest tout ! La seconde classe, MaxLengthValidator, implmente elle aussi cette
mthode. Cependant, il faut quelle connaisse le nombre de caractres maximal que la
chane doit avoir ! Pour cela, cette classe implmentera un constructeur demandant ce
nombre en paramtre, et assignera cette valeur lattribut correspondant.
Ainsi, voici nos deux classes lles hritant de Validator (voir la gure 22.4).
Figure 22.4 Modlisation des classes MaxLengthValidator et NotNullValidator
421
CHAPITRE 22. GRER LES FORMULAIRES
Dveloppement des classes
La classe Validator
Cette classe (comme les classes lles) est assez simple dvelopper. En eet, il ny a
que laccesseur et le mutateur du message derreur implmenter, avec un constructeur
demandant ledit message derreur. La mthode isValid(), quant elle, est abstraite,
donc rien crire de ce ct-l !
1 <?php
2 namespace Library;
3
4 abstract class Validator
5 {
6 protected $errorMessage;
7
8 public function __construct($errorMessage)
9 {
10 $this ->setErrorMessage($errorMessage);
11 }
12
13 abstract public function isValid($value);
14
15 public function setErrorMessage($errorMessage)
16 {
17 if (is_string($errorMessage))
18 {
19 $this ->errorMessage = $errorMessage;
20 }
21 }
22
23 public function errorMessage ()
24 {
25 return $this ->errorMessage;
26 }
27 }
Les classes lles
Comme la prcdente, les classes lles sont trs simples concevoir. Jespre que vous
y tes parvenus !
1 <?php
2 namespace Library;
3
4 class NotNullValidator extends Validator
5 {
6 public function isValid($value)
7 {
422
LES VALIDATEURS
8 return $value != '';
9 }
10 }
1 <?php
2 namespace Library;
3
4 class MaxLengthValidator extends Validator
5 {
6 protected $maxLength;
7
8 public function __construct($errorMessage , $maxLength)
9 {
10 parent :: __construct($errorMessage);
11
12 $this ->setMaxLength($maxLength);
13 }
14
15 public function isValid($value)
16 {
17 return strlen($value) <= $this ->maxLength;
18 }
19
20 public function setMaxLength($maxLength)
21 {
22 $maxLength = (int) $maxLength;
23
24 if ($maxLength > 0)
25 {
26 $this ->maxLength = $maxLength;
27 }
28 else
29 {
30 throw new \RuntimeException('La longueur maximale doit
tre un nombre suprieur 0');
31 }
32 }
33 }
Modication de la classe Field
Comme nous lavions vu, pour savoir si un champ est valide, il lui faut des validateurs.
Il va donc falloir passer, dans le constructeur de lobjet Field cr, la liste des vali-
dateurs que lon veut imposer au champ. Dans le cas du champ auteur par exemple,
nous lui passerons les deux validateurs : nous voulons la fois que le champ ne soit pas
vide et que la valeur ne dpasse pas les cinquante caractres. La cration du formulaire
ressemblerait donc ceci :
1 <?php
423
CHAPITRE 22. GRER LES FORMULAIRES
2 // $form reprsente le formulaire que l'on souhaite crer.
3 // Ici , on souhaite lui ajouter le champ auteur .
4 $form ->add(new \Library\StringField(array(
5 'label' => 'Auteur ',
6 'name' => 'auteur ',
7 'maxLength ' => 50 ,
8 'validators ' => array(
9 new \Library\MaxLengthValidator('L\'auteur spcifi est
trop long (50 caractres maximum)', 50),
10 new \Library\NotNullValidator('Merci de spcifier l\'auteur
du commentaire '),
11 )
12 )));
De cette faon, quelques modications au niveau de notre classe Field simposent. En
eet, il va falloir crer un attribut $validators, ainsi que laccesseur et le mutateur
correspondant. De la sorte, notre mthode hydrate() assignera automatiquement les
validateurs passs au constructeur lattribut $validators. Je vous laisse faire cela.
Vient maintenant limplmentation de la mthode isValid(). Cette mthode doit par-
courir tous les validateurs et invoquer la mthode isValid($value) sur ces validateurs
an de voir si la valeur passe au travers du let de tous les validateurs. De cette faon,
nous sommes srs que toutes les contraintes ont t respectes ! Si un validateur renvoie
une rponse ngative lorsquon lui demande si la valeur est valide, alors on devra lui
demander le message derreur qui lui a t assign et lassigner notre tour lattribut
correspondant. Ainsi, voici la nouvelle classe Field :
1 <?php
2 namespace Library;
3
4 abstract class Field
5 {
6 protected $errorMessage;
7 protected $label;
8 protected $name;
9 protected $validators = array ();
10 protected $value;
11
12 public function __construct(array $options = array())
13 {
14 if (!empty($options))
15 {
16 $this ->hydrate($options);
17 }
18 }
19
20 abstract public function buildWidget ();
21
22 public function hydrate($options)
23 {
24 foreach ($options as $type => $value)
424
LES VALIDATEURS
25 {
26 $method = 'set'.ucfirst($type);
27
28 if (is_callable(array($this , $method)))
29 {
30 $this ->$method($value);
31 }
32 }
33 }
34
35 public function isValid ()
36 {
37 foreach ($this ->validators as $validator)
38 {
39 if (!$validator ->isValid($this ->value))
40 {
41 $this ->errorMessage = $validator ->errorMessage ();
42 return false;
43 }
44 }
45
46 return true;
47 }
48
49 public function label ()
50 {
51 return $this ->label;
52 }
53
54 public function length ()
55 {
56 return $this ->length;
57 }
58
59 public function name()
60 {
61 return $this ->name;
62 }
63
64 public function validators ()
65 {
66 return $this ->validators;
67 }
68
69 public function value ()
70 {
71 return $this ->value;
72 }
73
74 public function setLabel($label)
425
CHAPITRE 22. GRER LES FORMULAIRES
75 {
76 if (is_string($label))
77 {
78 $this ->label = $label;
79 }
80 }
81
82 public function setLength($length)
83 {
84 $length = (int) $length;
85
86 if ($length > 0)
87 {
88 $this ->length = $length;
89 }
90 }
91
92 public function setName($name)
93 {
94 if (is_string($name))
95 {
96 $this ->name = $name;
97 }
98 }
99
100 public function setValidators(array $validators)
101 {
102 foreach ($validators as $validator)
103 {
104 if ($validator instanceof Validator && !in_array(
$validator , $this ->validators))
105 {
106 $this ->validators [] = $validator;
107 }
108 }
109 }
110
111 public function setValue($value)
112 {
113 if (is_string($value))
114 {
115 $this ->value = $value;
116 }
117 }
118 }
Vous pouvez apercevoir lutilisation de loprateur instanceof dans le code.
Pour en savoir plus ce sujet, je vous invite aller lire le chapitre ddi cet
oprateur.
426
LE CONSTRUCTEUR DE FORMULAIRES
Le constructeur de formulaires
Comme nous lavons vu, crer le formulaire au sein du contrleur prsente deux in-
convnients. Premirement, cela encombre le contrleur. Imaginez que vous ayez une
dizaine de champs, cela deviendrait norme ! Le contrleur doit tre clair, et la cration
du formulaire devrait donc se faire autre part. Deuximement, il y a le problme de
duplication de code : si vous voulez utiliser ce formulaire dans un autre contrleur,
vous devrez copier/coller tout le code responsable de la cration du formulaire. Pas
trs exible vous en conviendrez ! Pour cela, nous allons donc crer des constructeurs
de formulaire. Il y aura par consquent autant de constructeurs que de formulaires
dirents.
Conception des classes
La classe FormBuilder
La classe FormBuilder a un rle bien prcis : elle est charge de construire un formu-
laire. PAinsi, il ny a quune seule fonctionnalit implmenter. . . celle de construire le
formulaire ! Mais, pour ce faire, encore faudrait-il avoir un objet Form. Nous le crerons
donc dans le constructeur et nous lassignerons lattribut correspondant.
Nous avons donc :
Une mthode abstraite charge de construire le formulaire.
Un attribut stockant le formulaire.
Laccesseur et le mutateur correspondant.
Voici notre classe schmatise (voir la gure 22.5).
Figure 22.5 Modlisation de la classe FormBuilder
Les classes lles
Un constructeur de base cest bien beau, mais sans classe lle, dicile de construire
grand-chose. Je vous propose donc de crer deux constructeurs de formulaire : un
constructeur de formulaire de commentaires, et un constructeur de formulaire de news.
Nous aurons notre classe FormBuilder dont hriteront deux classes, CommentFormBuilder
et NewsFormBuilder (voir la gure 22.6).
427
CHAPITRE 22. GRER LES FORMULAIRES
Figure 22.6 Modlisation des classes CommentFormBuilder et NewsFormBuilder
Dveloppement des classes
La classe FormBuilder
Cette classe est assez simple crer, jespre que vous y tes parvenus !
1 <?php
2 namespace Library;
3
4 abstract class FormBuilder
5 {
6 protected $form;
7
8 public function __construct(Entity $entity)
9 {
10 $this ->setForm(new Form($entity));
11 }
12
13 abstract public function build();
14
15 public function setForm(Form $form)
16 {
17 $this ->form = $form;
18 }
19
20 public function form()
21 {
22 return $this ->form;
23 }
24 }
Les classes lles
Les classes lles sont simples crer. En eet, il ny a que la mthode build()
implmenter, en ayant pour simple contenu dappeler successivement les mthodes
add() sur notre formulaire. Pour lemplacement des chiers stockant les classes, je
428
LE CONSTRUCTEUR DE FORMULAIRES
vous propose de les placer dans le dossier /Library/FormBuilder.
1 <?php
2 namespace Library\FormBuilder;
3
4 class CommentFormBuilder extends \Library\FormBuilder
5 {
6 public function build ()
7 {
8 $this ->form ->add(new \Library\StringField(array(
9 'label' => 'Auteur ',
10 'name' => 'auteur ',
11 'maxLength ' => 50 ,
12 'validators ' => array(
13 new \Library\MaxLengthValidator('L\'auteur spcifi
est trop long (50 caractres maximum)', 50),
14 new \Library\NotNullValidator('Merci de spcifier l\'
auteur du commentaire '),
15 ),
16 )))
17 ->add(new \Library\TextField(array(
18 'label' => 'Contenu ',
19 'name' => 'contenu ',
20 'rows' => 7,
21 'cols' => 50 ,
22 'validators ' => array(
23 new \Library\NotNullValidator('Merci de spcifier
votre commentaire '),
24 ),
25 )));
26 }
27 }
1 <?php
2 namespace Library\FormBuilder;
3
4 class NewsFormBuilder extends \Library\FormBuilder
5 {
6 public function build ()
7 {
8 $this ->form ->add(new \Library\StringField(array(
9 'label' => 'Auteur ',
10 'name' => 'auteur ',
11 'maxLength ' => 20 ,
12 'validators ' => array(
13 new \Library\MaxLengthValidator('L\'auteur spcifi
est trop long (20 caractres maximum)', 20),
14 new \Library\NotNullValidator('Merci de spcifier l\'
auteur de la news'),
15 ),
16 )))
429
CHAPITRE 22. GRER LES FORMULAIRES
17 ->add(new \Library\StringField(array(
18 'label' => 'Titre',
19 'name' => 'titre',
20 'maxLength ' => 100 ,
21 'validators ' => array(
22 new \Library\MaxLengthValidator('Le titre spcifi
est trop long (100 caractres maximum)', 100),
23 new \Library\NotNullValidator('Merci de spcifier le
titre de la news'),
24 ),
25 )))
26 ->add(new \Library\TextField(array(
27 'label' => 'Contenu ',
28 'name' => 'contenu ',
29 'rows' => 8,
30 'cols' => 60 ,
31 'validators ' => array(
32 new \Library\NotNullValidator('Merci de spcifier le
contenu de la news'),
33 ),
34 )));
35 }
36 }
Modication des contrleurs
Lajout de commentaire (frontend)
Eectuons des premires modications, en commenant par le formulaire dajout de
commentaire dans le frontend. En utilisant nos classes, voici les instructions que nous
devons excuter :
Si la requte est de type POST (formulaire soumis), il faut crer un nouveau com-
mentaire en le remplissant avec les donnes envoyes, sinon on cre un nouveau
commentaire.
On instancie notre constructeur de formulaire en lui passant le commentaire en ar-
gument.
On invoque la mthode de construction du formulaire.
Si le formulaire est valide, on enregistre le commentaire en BDD.
On passe le formulaire gnr la vue.
Voil ce que a donne :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
430
LE CONSTRUCTEUR DE FORMULAIRES
7
8 public function executeInsertComment (\ Library\HTTPRequest
$request)
9 {
10 // Si le formulaire a t envoy.
11 if ($request ->method () == 'POST')
12 {
13 $comment = new \Library\Entities\Comment(array(
14 'news' => $request ->getData('news'),
15 'auteur ' => $request ->postData('auteur '),
16 'contenu ' => $request ->postData('contenu ')
17 ));
18 }
19 else
20 {
21 $comment = new \Library\Entities\Comment;
22 }
23
24 $formBuilder = new \Library\FormBuilder\CommentFormBuilder(
$comment);
25 $formBuilder ->build();
26
27 $form = $formBuilder ->form();
28
29 if($request ->method () == 'POST' && $form ->isValid ())
30 {
31 $this ->managers ->getManagerOf('Comments ')->save($comment)
;
32 $this ->app ->user()->setFlash('Le commentaire a bien t
ajout, merci !');
33 $this ->app ->httpResponse ()->redirect('news -'.$request ->
getData('news').'.html');
34 }
35
36 $this ->page ->addVar('comment ', $comment);
37 $this ->page ->addVar('form', $form ->createView ());
38 $this ->page ->addVar('title', 'Ajout d\'un commentaire ');
39 }
40
41 // ...
42 }
La modication de commentaire, lajout et la modication de news (ba-
ckend)
Normalement, vous devriez tre capables, grce lexemple prcdent, de parvenir
crer ces trois autres formulaires. Voici le nouveau contrleur :
1 <?php
431
CHAPITRE 22. GRER LES FORMULAIRES
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsert (\ Library\HTTPRequest $request)
9 {
10 $this ->processForm($request);
11
12 $this ->page ->addVar('title', 'Ajout d\'une news');
13 }
14
15 public function executeUpdate (\ Library\HTTPRequest $request)
16 {
17 $this ->processForm($request);
18
19 $this ->page ->addVar('title', 'Modification d\'une news');
20 }
21
22 public function executeUpdateComment (\ Library\HTTPRequest
$request)
23 {
24 $this ->page ->addVar('title', 'Modification d\'un
commentaire ');
25
26 if ($request ->method () == 'POST')
27 {
28 $comment = new \Library\Entities\Comment(array(
29 'id' => $request ->getData('id'),
30 'auteur ' => $request ->postData('auteur '),
31 'contenu ' => $request ->postData('contenu ')
32 ));
33 }
34 else
35 {
36 $comment = $this ->managers ->getManagerOf('Comments ')->get
($request ->getData('id'));
37 }
38
39 $formBuilder = new \Library\FormBuilder\CommentFormBuilder(
$comment);
40 $formBuilder ->build();
41
42 $form = $formBuilder ->form();
43
44 if ($request ->method () == 'POST' && $form ->isValid ())
45 {
46 $this ->managers ->getManagerOf('Comments ')->save($comment)
;
432
LE CONSTRUCTEUR DE FORMULAIRES
47 $this ->app ->user()->setFlash('Le commentaire a bien t
modifi');
48 $this ->app ->httpResponse ()->redirect('/admin/');
49 }
50
51 $this ->page ->addVar('form', $form ->createView ());
52 }
53
54 public function processForm (\ Library\HTTPRequest $request)
55 {
56 if ($request ->method () == 'POST')
57 {
58 $news = new \Library\Entities\News(
59 array(
60 'auteur ' => $request ->postData('auteur '),
61 'titre' => $request ->postData('titre'),
62 'contenu ' => $request ->postData('contenu ')
63 )
64 );
65
66 if ($request ->getExists('id'))
67 {
68 $news ->setId($request ->getData('id'));
69 }
70 }
71 else
72 {
73 // L'identifiant de la news est transmis si on veut la
modifier.
74 if ($request ->getExists('id'))
75 {
76 $news = $this ->managers ->getManagerOf('News')->
getUnique($request ->getData('id'));
77 }
78 else
79 {
80 $news = new \Library\Entities\News;
81 }
82 }
83
84 $formBuilder = new \Library\FormBuilder\NewsFormBuilder(
$news);
85 $formBuilder ->build();
86
87 $form = $formBuilder ->form();
88
89 if ($request ->method () == 'POST' && $form ->isValid ())
90 {
91 $this ->managers ->getManagerOf('News')->save($news);
433
CHAPITRE 22. GRER LES FORMULAIRES
92 $this ->app ->user()->setFlash($news ->isNew() ? 'La news a
bien t ajoute !' : 'La news a bien t modifie !')
;
93 $this ->app ->httpResponse ()->redirect('/admin/');
94 }
95
96 $this ->page ->addVar('form', $form ->createView ());
97 }
98 }
Le gestionnaire de formulaires
Terminons ce chapitre en amliorant encore notre API permettant la cration de for-
mulaire. Je voudrais attirer votre attention sur ce petit passage, que lon retrouve
chaque fois (que ce soit pour ajouter ou modier une news ou un commentaire) :
1 <?php
2 // Nous sommes ici au sein d'un contrleur
3 if($request ->method () == 'POST' && $form ->isValid ())
4 {
5 $this ->managers ->getManagerOf('Manager ')->save($comment);
6 // ...
7 }
Bien que rduit, ce bout de code est lui aussi dupliqu. De plus, si lon veut vraiment
externaliser la gestion du formulaire, alors il va falloir le sortir du contrleur. Ainsi, il
ne restera plus dopration de traitement dans le contrleur. On sparera donc bien les
rles : le contrleur naura plus rchir sur le formulaire quil traite. En eet, il ne
fera que demander au constructeur de formulaire de construire le formulaire quil veut,
puis demandera au gestionnaire de formulaire de soccuper de lui sil a t envoy. On
ne se souciera donc plus de laspect interne du formulaire !
Conception du gestionnaire de formulaire
Comme nous venons de le voir, le gestionnaire de formulaire est charg de traiter le for-
mulaire une fois quil a t envoy. Nous avons donc dores et dj une fonctionnalit de
notre classe : celle de traiter le formulaire. Du ct des caractristiques, penchons-nous
du ct des lments dont notre gestionnaire a besoin pour fonctionner. Le premier
lment me parat vident : comment soccuper dun formulaire si on ny a pas ac-
cs ? Ce premier lment est donc bien entendu le formulaire dont il est question. Le
deuxime lment, lui, est aussi vident : comment enregistrer lentit correspondant
au formulaire si on na pas le manager correspondant ? Le deuxime lment est donc
le manager correspondant lentit. Enn, le troisime lment est un peu plus subtil,
et il faut rchir au contenu de la mthode qui va traiter le formulaire. Cette mthode
devra savoir si le formulaire a t envoy pour pouvoir le traiter (si rien na t envoy,
il ny a aucune raison de traiter quoi que ce soit). Ainsi, pour savoir si le formulaire a
434
LE GESTIONNAIRE DE FORMULAIRES
t envoy, il faut que notre gestionnaire de formulaire ait accs la requte du client
an de connaitre le type de la requte (GET ou POST). Ces trois lments devront
tre passs au constructeur de notre objet.
Schmatiquement, voici notre gestionnaire de formulaire (voir la gure 22.7).
Figure 22.7 Modlisation de la classe FormHandler
Dveloppement du gestionnaire de formulaire
Voici le rsultat que vous auriez du obtenir :
1 <?php
2 namespace Library;
3
4 class FormHandler
5 {
6 protected $form;
7 protected $manager;
8 protected $request;
9
10 public function __construct (\ Library\Form $form , \Library\
Manager $manager , \Library\HTTPRequest $request)
11 {
12 $this ->setForm($form);
13 $this ->setManager($manager);
14 $this ->setRequest($request);
15 }
16
17 public function process ()
18 {
19 if($this ->request ->method () == 'POST' && $this ->form ->
isValid ())
20 {
21 $this ->manager ->save($this ->form ->entity ());
22
23 return true;
24 }
25
26 return false;
27 }
435
CHAPITRE 22. GRER LES FORMULAIRES
28
29 public function setForm (\ Library\Form $form)
30 {
31 $this ->form = $form;
32 }
33
34 public function setManager (\ Library\Manager $manager)
35 {
36 $this ->manager = $manager;
37 }
38
39 public function setRequest (\ Library\HTTPRequest $request)
40 {
41 $this ->request = $request;
42 }
43 }
Modication des contrleurs
Ici, la modication est trs simple. En eet, nous avons juste dcentralis ce bout de
code :
1 <?php
2 if($request ->method () == 'POST' && $form ->isValid ())
3 {
4 $this ->managers ->getManagerOf('Manager ')->save($comment);
5 // Autres oprations (affichage d'un message informatif ,
redirection , etc.).
6 }
Il sut donc de remplacer ce code par la simple invocation de la mthode process()
sur notre objet FormHandler :
1 <?php
2 // On rcupre le gestionnaire de formulaire (le paramtre de
getManagerOf () est bien entendu remplacer).
3 $formHandler = new \Library\FormHandler($form , $this ->managers
->getManagerOf('Comments '), $request);
4
5 if ($formHandler ->process ())
6 {
7 // Ici ne rsident plus que les oprations effectuer une
fois l'entit du formulaire enregistre
8 // (affichage d'un message informatif , redirection , etc.).
9 }
Je vous fais conance pour mettre jour vos contrleurs comme il se doit !
436
Quatrime partie
Annexes
437
Chapitre 23
Loprateur instanceof
Dicult :
V
ous tes-vous dj demand sil tait possible de savoir si un objet tait une instance
de telle classe ? Si vous vous tes dj pos cette question, vous navez normalement
pas trouv de rponse vraiment claire. Un moyen simple de vrier une telle chose
est dutiliser loprateur instanceof. Ce sera un court chapitre car cette notion nest pas
bien dicile. Il faut juste possder quelques pr-rquis.
En voici la liste :
Bien matriser les notions de classe, dobjet et dinstance .
Bien matriser le concept de lhritage (si vous ne matrisez pas bien la rsolution statique
la vole ce nest pas bien important).
Savoir ce quest une interface et savoir sen servir.
439
CHAPITRE 23. LOPRATEUR INSTANCEOF
Prsentation de loprateur
Loprateur instanceof permet de vrier si tel objet est une instance de telle classe.
Cest un oprateur qui sutilise dans une condition. Ainsi, on pourra crer des conditions
comme si $monObjet est une instance de MaClasse, alors. . . .
Maintenant voyons comment construire notre condition. gauche de notre opra-
teur,nous allons y placer notre objet. droite de notre oprateur, nous allons placer,
comme vous vous en doutez srement, le nom de la classe.
Exemple :
1 <?php
2 class A { }
3 class B { }
4
5 $monObjet = new A;
6
7 if ($monObjet instanceof A) // Si $monObjet est une instance de
A.
8 {
9 echo '$monObjet est une instance de A';
10 }
11 else
12 {
13 echo '$monObjet n\'est pas une instance de A';
14 }
15
16 if ($monObjet instanceof B) // Si $monObjet est une instance de
B.
17 {
18 echo '$monObjet est une instance de B';
19 }
20 else
21 {
22 echo '$monObjet n\'est pas une instance de B';
23 }
24 ?>
Bref, je pense que vous avez compris le principe.
Si votre version de PHP est ultrieure la version 5.1, alors aucune erreur
fatale ne sera gnre si vous utilisez loprateur instanceof en spciant
une classe qui na pas t dclare. La condition renverra tout simplement
false.
Il y a cependant plusieurs faons de procder et quelques astuces (cest dailleurs pour
toutes les prsenter que jai cr ce chapitre).
Parmi ces mthodes, il y en a une qui consiste placer le nom de la classe pour laquelle
on veut vrier que tel objet est une instance dans une variable sous forme de chane
440
PRSENTATION DE LOPRATEUR
de caractres. Exemple :
1 <?php
2 class A { }
3 class B { }
4
5 $monObjet = new A;
6
7 $classeA = 'A';
8 $classeB = 'B';
9
10 if ($monObjet instanceof $classeA)
11 {
12 echo '$monObjet est une instance de ', $classeA;
13 }
14 else
15 {
16 echo '$monObjet n\'est pas une instance de ', $classeA;
17 }
18
19 if ($monObjet instanceof $classeB)
20 {
21 echo '$monObjet est une instance de ', $classeB;
22 }
23 else
24 {
25 echo '$monObjet n\'est pas une instance de ', $classeB;
26 }
27 ?>
Attention ! Vous ne pouvez spcier le nom de la classe entre apostrophes ou
guillemets directement dans la condition ! Vous devez obligatoirement passer
par une variable. Si vous le faites directement, vous obtiendrez une belle erreur
danalyse.
Une autre faon dutiliser cet oprateur est de spcier un autre objet la place du
nom de la classe. La condition renverra true si les deux objets sont des instances de
la mme classe. Exemple :
1 <?php
2 class A { }
3 class B { }
4
5 $a = new A;
6 $b = new A;
7 $c = new B;
8
9 if ($a instanceof $b)
10 {
11 echo '$a et $b sont des instances de la mme classe ';
441
CHAPITRE 23. LOPRATEUR INSTANCEOF
12 }
13 else
14 {
15 echo '$a et $b ne sont pas des instances de la mme classe ';
16 }
17
18 if ($a instanceof $c)
19 {
20 echo '$a et $c sont des instances de la mme classe ';
21 }
22 else
23 {
24 echo '$a et $c ne sont pas des instances de la mme classe ';
25 }
26 ?>
Et voil. Vous connaissez les trois mthodes possibles pour utiliser cet oprateur. Pour-
tant, il existe encore quelques eets que peut produire instanceof. Poursuivons donc
ce chapitre tranquillement.
instanceof et lhritage
Lhritage est de retour ! En eet, instanceof a un comportement bien particulier avec
les classes qui hritent entre elles. Voici ces eets.
Vous vous souvenez sans doute (enn jespre :- ) de la premire faon dutiliser
loprateur. Voici une rvlation : la condition renvoie true si la classe spcie est une
classe parente de la classe instancie par lobjet spci. Exemple :
1 <?php
2 class A { }
3 class B extends A { }
4 class C extends B { }
5
6 $b = new B;
7
8 if ($b instanceof A)
9 {
10 echo '$b est une instance de A ou $b instancie une classe qui
est une fille de A';
11 }
12 else
13 {
14 echo '$b n\'est pas une instance de A et $b instancie une
classe qui n\'est pas une fille de A';
15 }
16
17 if ($b instanceof C)
18 {
442
INSTANCEOF ET LES INTERFACES
19 echo '$b est une instance de C ou $b instancie une classe qui
est une fille de C';
20 }
21 else
22 {
23 echo '$b n\'est pas une instance de C et $b instancie une
classe qui n\'est pas une fille de C';
24 }
25 ?>
Voil, jespre que vous avez compris le principe car celui-ci est le mme avec les
deuxime et troisime mthodes.
Nous allons donc maintenant terminer ce chapitre avec une dernire partie concernant
les ractions de loprateur avec les interfaces. Ce sera un mlange des deux premires
parties, donc si vous tes perdus, relisez bien tout (eh oui, jespre que vous navez pas
oubli lhritage entre interfaces :- ).
instanceof et les interfaces
Voyons maintenant les eets produits par loprateur avec les interfaces.
Hein ? Comment a ? Je comprends pas. . . Comment peut-on vrier quun
objet soit une instance dune interface sachant que cest impossible ?
Comme vous le dites si bien, il est impossible de crer une instance dune interface
(au mme titre que de crer une instance dune classe abstraite, ce quest peu prs
une interface). Loprateur va donc renvoyer true si tel objet instancie une classe
implmentant telle interface.
Voici un exemple :
1 <?php
2 interface iA { }
3 class A implements iA { }
4 class B { }
5
6 $a = new A;
7 $b = new B;
8
9 if ($a instanceof iA)
10 {
11 echo 'Si iA est une classe , alors $a est une instance de iA
ou $a instancie une classe qui est une fille de iA. Sinon ,
$a instancie une classe qui implmente iA.';
12 }
13 else
14 {
443
CHAPITRE 23. LOPRATEUR INSTANCEOF
15 echo 'Si iA est une classe , alors $a n\'est pas une instance
de iA et $a n\'instancie aucune classe qui est une fille
de iA. Sinon , $a instancie une classe qui n\'implmente
pas iA.';
16 }
17
18 if ($b instanceof iA)
19 {
20 echo 'Si iA est une classe , alors $b est une instance de iA
ou $b instancie une classe qui est une fille de iA. Sinon ,
$b instancie une classe qui implmente iA.';
21 }
22 else
23 {
24 echo 'Si iA est une classe , alors $b n\'est pas une instance
de iA et $b n\'instancie aucune classe qui est une fille
de iA. Sinon , $b instancie une classe qui n\'implmente
pas iA.';
25 }
26 ?>
Ce code se passe de commentaires, les valeurs aches dtaillant assez bien je pense.
Aprs avoir vu lutilisation de loprateur avec les interfaces, nous allons voir comment
il ragit lorsquon lui passe en paramtre une interface qui est hrite par une autre
interface qui est implmente par une classe qui est instancie. Vous voyez peu prs
quoi je fais srfrence ? Je vais procder en PHP au cas o vous nayez pas tout suivi.
1 <?php
2 interface iParent { }
3 interface iFille extends iParent { }
4 class A implements iFille { }
5
6 $a = new A;
7
8 if ($a instanceof iParent)
9 {
10 echo 'Si iParent est une classe , alors $a est une instance de
iParent ou $a instancie une classe qui est une fille de
iParent. Sinon , $a instancie une classe qui implmente
iParent ou une fille de iParent.';
11 }
12 else
13 {
14 echo 'Si iParent est une classe , alors $a n\'est pas une
instance de iParent et $a n\'instancie aucune classe qui
est une fille de iParent. Sinon , $a instancie une classe
qui n\'implmente ni iParent , ni une de ses filles.';
15 }
16 ?>
444
INSTANCEOF ET LES INTERFACES
Vous savez maintenant tous les comportements que peut adopter cet oprateur et tous
les eets quil peut produire (tout est crit dans le prcdent code).
En rsum
Loprateur instanceof permet de vrier la nature de la classe dont lobjet test
est une instance.
Cet oprateur permet de vrier quun certain objet est bien une instance dune
classe lle de telle classe.
Cet oprateur permet de vrier quun certain objet est une instance dune classe
implmentant telle interface, ou que lune de ses classes mre limplmente.
445
CHAPITRE 23. LOPRATEUR INSTANCEOF
446
Index
A
abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
abstration. . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
accesseur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
addendum. . . . . . . . . . . . . . . . . . . . . . . 233
application. . . . . . . . . . . . . . . . . . . . . . . . . . . 314
backend . . . . . . . . . . . . . . . . . . . . . . . . . 316
frontend. . . . . . . . . . . . . . . . . . . . . . . . . 316
ArrayIterator . . . . . . . . . . . . . . . . . . . . . . 185
as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
attribut statique . . . . . . . . . . . . . . . . . . . . . . 36
auto-chargement . . . . . . . . . . . . . . . . . . . . . . 26
autoload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
C
catch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
clone. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
const. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . 24
D
DAO. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
E
encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . 6
ErrorException . . . . . . . . . . . . . . . . . . . . . 200
Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
InvalidArgumentException . . . . 199
PDOException. . . . . . . . . . . . . . . . . . . 198
RuntimeException . . . . . . . . . . . . . . 198
extends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
F
final. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
nalisation. . . . . . . . . . . . . . . . . . . . . . . . . . . 103
G
getter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
H
hosts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
hydratation . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
I
identiant dobjet . . . . . . . . . . . . . . . . . . . . 160
implements. . . . . . . . . . . . . . . . . . . . . . . . . . 170
injection de dpendances . . . . . . . . . . . . . 285
instance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
instanceof. . . . . . . . . . . . . . . . . . . . . . . . . . 440
interaction UML . . . . . . . . . . . . . . . . . . . . . 246
agrgation. . . . . . . . . . . . . . . . . . . . . . . 248
association . . . . . . . . . . . . . . . . . . . . . . 248
composition . . . . . . . . . . . . . . . . . . . . . 249
hritage . . . . . . . . . . . . . . . . . . . . . . . . . 246
interface. . . . . . . . . . . . . . . . . . . . . . . . . 247
interface . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
interface prdnie . . . . . . . . . . . . . . . . . . . 174
ArrayAccess . . . . . . . . . . . . . . . . . . . . . 178
Countable . . . . . . . . . . . . . . . . . . . . . . . 181
Itarator . . . . . . . . . . . . . . . . . . . . . . . . . 174
SeekableIterator . . . . . . . . . . . . . . . . . 176
SplObserver . . . . . . . . . . . . . . . . . . . . . 274
SplSubject . . . . . . . . . . . . . . . . . . . . . . 274
447
INDEX
L
late static bindings . . . . . . . . . . . . . . . . . . . 105
linarisation. . . . . . . . . . . . . . . . . . . . . . . . . .147
serialize . . . . . . . . . . . . . . . . . . . . . . 148
unserialize . . . . . . . . . . . . . . . . . . . . 148
M
manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
masque de conception. . . . . . . . . . . . . . . . 271
mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
methode magique
__clone. . . . . . . . . . . . . . . . . . . . . . . . . 161
mthode magique . . . . . . . . . . . . . . . . . . . . 137
__call . . . . . . . . . . . . . . . . . . . . . . . . . . 146
__callStatic . . . . . . . . . . . . . . . . . . . . 146
__construct . . . . . . . . . . . . . . . . . . . . . 24
__destruct . . . . . . . . . . . . . . . . . . . . . 138
__get. . . . . . . . . . . . . . . . . . . . . . . . . . . 140
__invoke . . . . . . . . . . . . . . . . . . . . . . . 154
__isset . . . . . . . . . . . . . . . . . . . . . . . . . 142
__set . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
__set_state . . . . . . . . . . . . . . . . . . . . 153
__sleep. . . . . . . . . . . . . . . . . . . . . . . . . 149
__toString . . . . . . . . . . . . . . . . . . . . . 152
__unset . . . . . . . . . . . . . . . . . . . . . . . . 143
__wakeup . . . . . . . . . . . . . . . . . . . . . . 150
mthode statique. . . . . . . . . . . . . . . . . . . . . . 35
module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
mutateur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
N
new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
O
objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
P
Paamayim Nekudotayim . . . . . . . . . . . . . . 31
parent. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
procdural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
proprit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
R
reexivite
ReectionClass . . . . . . . . . . . . . . . . . . 218
ReectionMethod. . . . . . . . . . . . . . . . 227
ReectionObject . . . . . . . . . . . . . . . . 218
ReectionParameter . . . . . . . . . . . . . 239
ReectionProperty . . . . . . . . . . . . . . 223
rexivit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
rsolution statique la vole . . . . . . . . . 105
S
self . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
set_error_handler . . . . . . . . . . . . . . . . . 200
set_exception_handler. . . . . . . . . . . . . 202
setter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
spl_autoload_register. . . . . . . . . . . . . . 27
static. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
satic:: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
T
this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
throw. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
trait . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
trigger_error . . . . . . . . . . . . . . . . . . . . . . 202
try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
U
UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
V
visibilit. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7
private . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
protected . . . . . . . . . . . . . . . . . . . . . . 100
public . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
448
Dpt lgal : dcembre 2012
ISBN : 979-10-90085-36-7
Code diteur : 979-10-90085
Imprim en France
Achev dimprimer le 3 dcembre 2012
sur les presses de Corlet Imprimeur (Cond-sur-Noireau)
Numro imprimeur : 151588
Mentions lgales :
Conception couverture : Fan Jiyong
Illustrations chapitres : Fan Jiyong

Das könnte Ihnen auch gefallen