Sommaire

BONJOUR
Musique "Invention BWV988" J.S. BACH 1685-1750

Tutoriel et Help

- PHP -

Nous veillons aux respects des droits d'auteur : si par erreur un contenu protégé est disponible sans indication sur ce site, merci de contacter l'administrateur, il sera retiré ou mis à jour dans les plus brefs délais.


FAQ fr.comp.lang.php PHP

FAQ fr.comp.lang.php

Mise à jour : 22 décembre 2008

Cette FAQ reprend les questions posées régulièrement sur le newsgroup fr.comp.lang.php (anciennement fr.comp.infosystemes.www.auteurs.php)

1. Présentation de la FAQ

  • 1 Miroirs
  • 2 Présentation des réponses :
  • 3 Charte et modération
  • 4 La différence avec alt.fr.comp.lang.php
  • 5 Premiers rédacteurs de cette FAQ
  • 6 Remerciements
  • 7 Points juridiques
  • 8 Maintenance de cette FAQ, remarques, liens rompus

2. Les notions de base sur PHP

  • 1 Comment débuter en PHP
  • 2 php is not mysql !
  • 3 HTTP : client serveur en mode asynchrone non connecté
  • 4 Quel hébergeur choisir avec PHP ?
  • 5 Des problèmes d'installation
  • 6 Editeurs PHP
  • 7 Passage de variable(s) entre les scripts
  • 8 Passer des variables JavaScript à PHP
  • 9 La différence entre include( ) et require ( )
  • 10 Les chemins pour include( ) et require ( )
  • 11 Du bon usage de la fonction header()
  • 12 Des problèmes avec la fonction header( )
  • 13 Remplacer les retours chariots dans une chaîne
  • 14 Les expressions régulières
  • 15 Variables dynamiques $var_$i =>$var_1, $var_2 ...
  • 16 Un @ devant la commande ?
  • 17 Mon code PHP s'affiche au lieu d'être éxécuté
  • 18 Les quotes : dois-je utiliser ' ou " ??

3. La connexion à un sgbdr

  • 1 ...0 is not a valid resource ...
  • 2 [souvent HS] Problèmes de connexion à MySQL
  • 3 Exemple de fonction de connexion à MySQL
  • 4 La protection de mes mots de passe de connexion à une base

4. Techniques de développement

  • 1 ...parse error on line... Undefined index... Undefined constant
  • 2 Le multitasking / exécutions parallèles (UNIX)
  • 3 Recherches par mots-clefs dans une base de données
  • 4 Lancer des commandes à intervalles réguliers ("crontab")
  • 5 Vérifier la présence d'un enregistrement avant de le mettre à jour
  • 6 Gérer plusieurs langues sur un site
  • 7 Connaître la version du navigateur et la résolution de l'écran
  • 8 Imprimer
  • 9 Alterner les couleurs dans un tableau
  • 10 L'upload de fichiers
  • 11 L'envoi de fichiers vers le navigateur

5. Les mails et les forums NNTP

  • 1 Le mail chez free.fr
  • 2 Envoi d'un mail.
  • 3 Tester la validité d'une adresse email
  • 4 Forums usenet

6. Les outils déjà prêts

  • 1 Code d'un forum ou BBS en PHP
  • 2 Code d'un caddie virtuel en PHP
  • 3 Code d'un "chat" en PHP
  • 4 PHP-NUKE
  • 5 Webmail
  • 6 Spip (CMS)
  • 7 Typo3 (CMS)
  • 8 w-agora (CMS)
  • 9 Administration SGBDR

7. Gérer les sessions utilisateur

  • 1 Coder votre propre gestion de session
  • 2 Utiliser l'excellente PHPLib
  • 3 Le support natif des sessions de php4
  • 4 Comptabiliser le nombre courant de personnes "connectées" sur le site
  • 5 Suivre ses visiteurs sur son site / l'adresse IP de vos visiteurs
  • 6 Mot de passe d'accès à un répertoire

8. L'affichage de résultats de requêtes par tranches

  • 1 Présentation du problème
  • 2 Faire une série de requêtes SQL classiques et traiter en retour
  • 3 Utiliser la clause LIMIT du select de MySql
  • 4 Utiliser une table tampon

9. Ressources PHP

  • 1 Manuels php
  • 2 Ouvrages "papier"
  • 3 Archives du NG fr.comp.infosystemes.www.auteurs.php
  • 4 Interfacer php et Oracle
  • 5 Interfacer php et Sybase
  • 6 Sites en français
  • 7 Sites en anglais

10. Ressources connexes

  • 1 Problèmes avec un hébergeur
  • 2 Référence HTML
  • 3 Problèmes avec du SQL, MySQL, Postgres, Oracle, Sybase, etc...
  • 4 Ouvrages "papier"
  • 5 Problème de configuration Apache, IIS...
  • 6 Request For Comments

11. Génération de graphiques

  • 1 GD Lib
  • 2 Bibliothèques
  • 3 Redimensionner une image
  • 4 Flash dynamique

12. Bugs php ?

  • 1 Comportement bizarre : un bug PHP ?

13. Les concurrents de PHP

  • 1 PHP et ASP
  • 2 Benchmarks sur les plateformes web dynamique

14. Génération de PDF

  • 1 Comparatif des solutions existantes
  • 2 Modification

15. Compiler / encoder ou "obfusquer" des scripts PHP

  • 1 Ne pas livrer son code PHP en clair
  • 2 Zend Encoder
  • 3 ionCube
  • 4 phpSHIELD
  • 5 turkMMcache / eAccelerator
  • 6 bcompiler
  • 7 APC
  • 8 POBS

1. Présentation de la FAQ

Contenu de cette section

1.1 Mirroirs

Cette FAQ est également disponible sur http://www.phpindex.com/fciwap/ et http://www.saphirtech.fr/fr.comp.lang.php/

1.2 Présentation des réponses :

Les questions prises en compte dans cette FAQ sont celles directement liées à PHP, ainsi que quelques questions limite ou hors sujet revenant régulièrement sur le forum, indiquées par :
[HS](Hors Sujet)
[OT] (Off Topic).

A chaque fois que possible, un lien direct vers les versions traduite et originale du manuel approprié sera fourni.
Tous les liens s'ouvriront dans une nouvelle fenêtre (pour les navigateurs qui le supportent).
Toute réponse mettant en jeu du code PHP sera accompagnée d'un exemple, testé par le rédateur de la réponse sauf mention contraire.
La "l" (lettre L") devant une variable (exemple $l_dad) indique une variable locale à la fonction, ce qui est bien entendu seulement une convention de nommage.
Le terme "unix" n'est pas restreint à linux.

1.3 Charte et modération

Le forum fr.comp.lang.php est un forum modéré. Il est donc normal d'observer un délai avant la parution de votre article.
Pour être publiés, vos articles doivent se conformer à la Charte de ce forum, disponible sur http://www.usenet-fr.net/fur/chartes/comp.lang.php.html: la lecture de ce document est donc fortement conseillée.
Pour joindre les modérateurs, vous pouvez utiliser l'adresse moderateurs-php@listserv.usenet-fr.net en n'oubliant pas de joindre l'article auquel vous faites référence avec ses en-têtes complètes. Il ne vous est pas interdit de rester poli et courtois : un refus de publication est, sauf erreur, motivé par le respect de la Charte, ce n'est ni une punition ni une insulte.
Que faire si votre article n'apparait pas au bout de 24h ? Si vous avez mis une adresse email invalide, les modérateurs n'ont aucun moyen de vous prévenir que votre article a été refusé. Dans TOUS les autres cas, vous recevrez, en cas de refus, la raison pour laquelle votre article n'a pas été publié.
Donc si votre article n'est toujours pas publié après 24h et que votre adresse FROM: ou REPLY-TO: est valide, le plus probable est que votre article s'est perdu suite à une mauvaise configuration de votre serveur de news. Prenez contact avec les modérateurs.

1.4 La différence avec alt.fr.comp.lang.php

Un peu plus d'un an après le passage en statut modéré de l'ancien fr.comp.infosystemes.www.auteurs.php qui est devenu fr.comp.lang.php au passage, la hiérarchie alt.* a vu l'ajout d'un équivalent de son ancêtre (fciwap). La différence entre fclphp et alt.fclphp est que le second n'est pas modéré. Avec dans les deux cas les avantages et inconvénients de la modération ou de la non-modération.

1.5 Premiers rédacteurs de cette FAQ

(Par ordre alphabétique)
Thierry André, thierry.andre@freesbee.fr, @lias Théo
Thomas Broyer, étudiant, ptittom@free.fr, alias (TOM) ou Tom. aka M. RTFM
echoes@free.fr alias Echoes
Armel Fauveau, Webmaster PHPIndex, webmaster@phpindex.com
John Gallet, Ingénieur EFREI, john.gallet@wanadoo.fr, aka M. Off Topic

1.6 Remerciements

- à François Boudot pour son script permettant l'indexation automatique qui a bien servi avant la migration vers phpFAQTory
- à Armel pour phpFAQTory
- aux personnes nous ayant envoyé leurs remarques concernant ce document.

1.7 Points juridiques

Autorisation est donnée à quiconque le souhaite de faire du "framing" pour afficher cette FAQ ou de la recopier et de la diffuser sans la modifier, ni dans son contenu, ni dans sa forme.
N'hésitez pas à nous contacter(voir plus bas le paragraphe maintenance) si vous avez besoin d'une autorisation pour en citer des parties.
La totalité des informations contenues dans cette FAQ est disponible sur internet et considérée comme de notoriété publique.
Tous les noms de produits et autres marques cités dans cette FAQ sont des marques déposées par leurs propriétaires respectifs.
L'intégralité du code cité est le fruit de la rédaction des auteurs de cette FAQ, tiré de code diffusé publiquement sur des forums de discussion ou avec autorisation de l'auteur.

1.8 Maintenance de cette FAQ, remarques, liens rompus

Cette FAQ est maintenue par john.gallet@wanadoo.fr.
Vous pouvez lui soumettre des articles, lui signaler des erreurs, liens rompus (avec la nouvelle valeur si possible).

Contenu de cette section

2. Les notions de base sur PHP

Contenu de cette section

2.1 Comment débuter en PHP

"Bienvenue chez les fous" :-) ((C)A.F. 1999)
PHP est un langage simple tant dans sa syntaxe que dans ses fonctions. Il faut connaître le HTML (ou l'apprendre en même temps). L'achat des ouvrages indiqués dans la section ressources (en bas de cette FAQ) est vivement conseillé. Installez PHP et MySQL ou Postgres sur votre ordinateur par exemple avec le package d'Emmanuel Faivre (windows) et testez vos scripts en local. Lisez ce document, apprenez à lire le manuel de PHP, à chercher un peu par vous même dans les multiples sites cités dans cette FAQ, et à poser vos questions au bon endroit : fclphp pour les questions liées à PHP, news:fr.comp.applications.sgbd (fcas) pour les questions sur le langage SQL ou la configuration de votre SGBDR (MySQL, Postgres, etc...) et news:fr.comp.infosystemes.www.auteurs pour les questions HTML , news:fr.comp.lang.javascript pour le JS, ainsi qu'au support technique (aide en ligne, forums réservés) de votre hébergeur. Partez du principe qu'il est probable que quelqu'un d'autre a déjà été confronté au même problème : si votre question n'est pas mentionnée dans cette FAQ, cherchez sur les archives de ce NG : sur http://www.phpindex.com/ng/ ou sur google : http://groups.google.com
En respectant ces règles simples, vous aurez plus rapidement une réponse pertinente à votre question.

2.2 php is not mysql !

Bien que la plupart des questions traitées dans ce document concernent l'utilisation de PHP de concert avec une base de données, et dans la plupart des cas MySQL, il est faux et restrictif de penser que PHP nécessite un Système de Gestion de Base de Données Relationnel (SGBDR), et encore plus faux de penser que MySQL est "le" SGBDR incontournable avec PHP (on pourrait déjà discuter longtemps du qualificatif de SGBDR pour MySQL ...) : n'oublions pas Postgres, Oracle et Sybase pour ne citer qu'eux.
De même, la majeure partie des questions sur fclphp concernent l'utilisation de PHP couplé à un serveur web (Apache, IIS, PWS, Xitami, NES, etc...) mais PHP peut tout à fonctionner en tant qu'exécutable "stand-alone" : c'est une application à part entière.

2.3 HTTP : client serveur en mode asynchrone non connecté

Un script PHP utilisé pour générer dynamiquement une page web, comme un script ASP par exemple, s'exécute sur le serveur web qui reçoit une demande de la part d'un navigateur client. Le script PHP va faire certaines choses sur le serveur (mise à jour / interrogation de base de données, écriture dans des fichiers sur le serveur, consultation ou envoi de mail, etc...) puis va renvoyer au navigateur le résultat (HTML, PNG...) de ses actions. La page HTML qui revient vers le navigateur client n'a pas d'existence "physique", elle n'est pas ce que l'on appelle une page "statique", stockée sur le serveur (par FTP en général) et "servie", identique, au navigateur client à la demande. Néanmoins, en recevant le code HTML, le navigateur n'a pas moyen de savoir que c'est une page dynamique ou statique : le client ne sait rien de ce qui s'est passé sur le serveur où tourne PHP.
Remarque importante car à la source de nombreuses confusions :
En particulier, du JavaScript qui tourne sur cette page dans le navigateur client n'a aucun moyen de savoir que PHP en a généré une partie. Ceci implique que PHP peut passer des arguments à du JavaScript (client), (en fait, on peut tout à fait faire du JS dynamiquement généré par du PHP), mais également que pour passer des arguments JS depuis le client vers PHP et donc le serveur, il faudra soumettre un formulaire ou faire une redirection/un refresh en JS qui appellera une URL en passant les valeurs JS en argument. Bref, il faut bien que le client (JS) appelle le script PHP serveur en lui passant les valeurs qui vont bien.

Notons ici néanmoins que PHP peut ouvrir des connexions par sockets vers d'autres serveurs.

2.4 Quel hébergeur choisir avec PHP ?

Hébergements payants : Reportez vous au site abc-hébergement : http://www.abchebergement.com/
Si vous cherchez un hébergeur gratuit et vous voulez savoir quelles extensions/fonctions php il propose, vous trouverez sur http://www.centrale-lyon.org/ng/phpinfo.html une liste d'hébergeurs proposant php gratuitement. On y trouve aussi des associations et des sociétés proposant des formules à bas prix. Attention aux remarques en bas de la page avant de vous engager pour 24 mois...
Un autre comparatif : http://cyberzoide.developpez.com/php4/hebergeurs/

2.5 Des problèmes d'installation

Voici une liste de pointeurs pour l'installation de PHP avec différents serveurs web / SGBDR.

Avec windows :

Le package Easy-PHP (windows 9X/NT d'apache + mysql + php + phpmyadmin), est disponible à l'URL: http://www.easyphp.org/
A noter : il est plus judicieux de poser vos questions relatives à ce package sur la mailing list reservée à cet usage qui est en place sur http://www.easyphp.org/

Configuration du PWS pour PHP
Un guide complet, pas à pas, avec captures d'écran : http://davidubois.free.fr/cfg/index.html

Installation détaillée :

http://phm.nerim.net/wamp/Apache, MySQL et PHP sur un OS MS Windows 2000.
http://lwest.free.fr/doc/php/configuration_NT_fr.html php3 sur NT

Avec Unix :

De manière générale, commencer par enlever tous les RPMs plus ou moins pre-intallés, télécharger les tarballs et tout recompiler. Si vous faites des installations sucessives, penser à commencer par un make distclean ou carréement repartir des tarballs d'origine.
Concernant SSL : il n'y a pas que mod-sll dans la vie, on peut aussi compiler Apache avec support natif de SSL 128 bits. http://www.apache-ssl.org/ donne le détail du patch des sources et de la compilation.

Avec Sybase les options --with-sybase et --with-sybase-ct ne doivent pas être utilisées ensemble (conflit de la db et ct libs).

Avec Nescape Entreprise Server sur Solaris
Lire le manuel : http://fr2.php.net/manual/en/install.unix.sun.php

Avec Macintosh MacOS X
Deux packages sont disponibles sur http://www.entropy.ch/software/macosx/php/
Traduction des procs d'install sur http://www.mosx.net/logiciels/php_mysql.shtml

2.6 Editeurs PHP

Cette liste est fournie pour le confort des yeux.

N'importe quel éditeur ASCII fait l'affaire.

Un article complet rédigé par S. Pineau est disponible sur http://steph.pineau.free.fr/php/index.php?LNK=EDIT avec un moteur de recherche multicritères pour vous guider dans votre choix.
  • IDE complets
    • Eclipse: http://www.eclipse.org/
    • Zend Studio: http://www.zend.com/
  • Macintosh :
    • BBEdit : http://www.barebones.com/products/bbedit/index.shtml
  • Dos / windows 3x
    • edit
  • Windows NT / 9X /2000
    Il nait presque un éditeur par jour pour windows. Le but n'est pas de les lister tous.
    • notepad
    • PHPEdit : http://www.soysal.com/PHPEd
    • HTML-Kit : http://www.chami.com/html-kit/
    • UltraEdit : http://www.ultraedit.com
    • Textpad
      • http://www.textpad.com/
      • http://www.textpad.com/add-ons/syna2g.html (liste complete des addons)
  • Unix
    • vi
    • nedit http://nedit.org
    • (Linux) KPHPDevelop : http://kphpdev.sourceforge.net/
    • (Linux KDE) Quanta Plus : http://quanta.sourceforge.net
  • Emacs et emacs win32 : http://www.emacs.org/ : "plugins"
  • vim et vim win32:http://www.vim.org/

2.7 Passage de variable(s) entre les scripts

[HS] La solution la plus courante et la plus simple consiste à transmettre les variables dans l'URL sous la forme normalisée:
http://mon_url.fr/mon_beau_script.php3?variable1=valeur1&variable2=valeur&...&variableN=valeurN
Depuis la version 4.x de PHP, par défaut, les variables $variable1, $variable2, ... $variableN ne SONT PLUS définies. Il faut donc utiliser à la place les tableaux $_GET[], $_POST[] ou depuis la version 4.0.6 $_REQUEST[].
Autrement dit, là oû avant en appelant : script.php?toto=1 il était possible d'utiliser directement la variable $toto (qui valait 1) il faut maintenant par exemple commencer par affecter $toto=$_REQUEST['toto']; pour que $toto soit disponible. Noter les simples quotes autour de 'toto'.
Voir la documentation sur http://fr2.php.net/manual/en/language.variables.external.php [en anglais] ou http://fr2.php.net/manual/fr/language.variables.external.php [en francais] pour plus d'informations.
ATTENTION : vous devez vous préoccuper de la sécurité de votre programme, et donc faire attention au contenu de ces variables.

Rappelons aussi l'emploi parfois judicieux d'urlencode ( ) et de rawurlencode( ) ainsi que la préférence de la méthode POST et non GET.
Si vous vous posez ce genre de questions, il est probable que vous aurez aussi d'autres questions de base sur le HTML et il est probable que créer quelques pages statiques avant de vous intéresser à la création de pages dynamiques serait plus logique, afin d'échelonner les difficultés. N'hésitez pas à user et abuser de la liste des ressources présente dans cette FAQ.

2.8 Passer des variables JavaScript à PHP

Lire plus haut : HTTP : client serveur en mode asynchrone non connecté

2.9 La différence entre include( ) et require ( )

Remarque : include_once() et require_once(), introduits dans PHP4, ont le même comportement. Ils garantissent de plus que le fichier inclus ne le sera qu'une fois et une seule, ce qui facilite l'inclusion de fichiers définissant des fonctions (même si on peut très facilement se passer de ces fonctions).

Manuel pour include ( ) http://fr2.php.net/manual/function.include.php [en anglais] ou http://fr2.php.net/manual/fr/function.include.php [en français]
Manuel pour require ( ) http://fr2.php.net/manual/function.require.php [en anglais] ou http://fr2.php.net/manual/fr/function.require.php [en français]

include( ) comme require( ) permettent toutes les deux d'inclure un fichier dans un script php.
Une application pratique courante est de définir des fonctions dans un script qui sera appelé par include( ) ou require( ) pour rendre ces fonctions disponibles dans ce script.

En PHP 4.x
La seule différence enter les deux fonctions est leur comportement dans le cas où le fichier à inclure est introuvable/inaccessible. include('fichier_inaccessible.toto'); produira un warning mais le script continuera. require('fichier_inaccessible.toto'); stoppera l'exécution (comme s'il y avait un appel à exit() ou die() qui est un synonyme d'exit()). (lire aussi ci-dessous la version PHP3 et son utilisation)
Par conséquent, en PHP4 et PHP5, sauf cas exceptionnel, vous devriez toujours utiliser require() ou require_once() et non plus include() et include_once().

En PHP 3.x
L'instruction require ( ) est traitée avant l'exécution réelle du script, par le préprocesseur PHP. Elle relève plus de la macro que de la fonction. La fonction include( ) est appelée, si nécessaire, au cours de l'exécution du programme.

Par exemple :
if($toto)
{
include("fic1.php3");
require("fic2.php3");
}
Dans ce code, le fichier fic2.php3 sera TOUJOURS inclus dans le script courant. En revanche, fic1.php3 ne sera inclus QUE si le test sur $toto renvoie TRUE.
Si pour une raison quelconque vous voulez inclure un fichier juste après l'avoir généré dynamiquement, vous êtes donc obligé de passer par include ( );
Dans toutes les versions
Si vous tenez *vraiment* à faire des versions différentes de votre site selon le navigateur, ceci peut vous permettre de définir de deux manières différentes la même fonction PHP, en adaptant ce qu'elle doit faire aux spécificités d'IE ou NS ( sorte de couche d'abstraction), ainsi le code du corps de votre site reste indépendant de tout ça.

2.10 Les chemins pour include( ) et require ( )

Les deux fonctions ont le même comportement pour aller chercher le fichier à inclure. Si on fait un require("../titi.inc"); dans le fichier toto.php, alors, le chemin de titi doit être relatif à toto.php. Dans cet exemple, il se trouve un répertoire au dessus.

Attention 1 : chez la plupart des hébergeurs , php est configuré de manière à interdire l'include de fichiers d'un répertoire parent pour d'évidentes raisons de sécurité.

Attention 2 : il est plus que conseillé de nommer ses fichier à inclure avec une extension .php ou .php3 car sinon, n'importe qui connaissant le nom du fichier le lira en clair dans son navigateur.

2.11 Du bon usage de la fonction header()

Il est important de comprendre le fonctionnement de la fonction header() avant de l'utiliser à tout va. La fonction header() envoit vers le navigateur une trame http ne contenant pas de données type html mais seulement, comme son nom l'indique, des "headers" c'est à dire des entêtes techniques.

En particulier, elle peut donc être utilisée pour annoncer que vont suivre des données de type binaire et donc être utilisée pour proposer directement une fenêtre de téléchargement d'un fichier. Un exemple à ce sujet est disponible dans cette FAQ (cf Techniques de développement, L'envoi de fichiers vers le navigateur) .
Il est également possible d'utiliser header("Location:...") pour rediriger l'internaute vers un autre site ou vers son propre site mais en changeant de protocole pour passer en https/ssl. A noter que header("Location:http://www.autresite.com"); est strictement équivalente dans son résultat et le transit des données à l'action d'une balise <META http-equiv="refresh" content="0; http://www.autresite.com"> en HTML pur : on donne au navigateur l'instruction de demander une autre page sans intervention de l'internaute. Il y a donc transmission de données du serveur vers le navigateur, puis nouvelle requête HTTP de la part du navigateur en direction du site http://www.autresite.com (avec éventuellement requête DNS).
Remarque : il y a des différences entre les header http et les balises meta mais le trajet de l'information est le même. Plus d'informations sur : http://www.w3.org/QA/Tips/reback

Par conséquent, vous ne devriez pas utiliser header("Location:...script2.php") pour rediriger entre deux scripts de votre site. Cela imposerait un aller-retour inutile entre le serveur et le navigateur (traffic réseau inutile et dégradation des temps de réponse de votre site proportionnels à la vitesse de connexion de l'internaute). Il est bien plus rapide d'ajouter simplement dans le code php les instructions : require("script2.php"); exit();

De plus, l'utilisation de header:Location ouvre souvent la faille de sécurité suivante :
- on reçoit les données sur check.php, qui les vérifie dans tous les sens
- si problème on redirige vers erreur.php ou generer_formulaire.php (jusqu'ici, c'est sous-optimal, mais pas encore dangereux)
- si tout va bien, on appelle header:Location vers insert.php et là en revanche il est donc possible d'appeler directement insert.php dans l'URL en lui passant n'importe quoi, les arguments ne seront jamais vérifiés.

2.12 Des problèmes avec la fonction header( )

Pour tout appel à la fonction header( ), il ne doit en aucun cas y avoir un quelconque caractère retourné au navigateur avant son appel.
En particulier, faire la chasse aux espaces et aux retours à la ligne avant le tag d'ouverture <?php ou après le tag de fermeture ?> des scripts php.

De même, des problèmes ont été constatés lors de l'appel à une fonction qui faisait un return; vide dont le code de retour n'était pas traité dans la fonction appelante, un peu comme si la valeur retournée était parasite.

Manuel : http://fr2.php.net/manual/function.header.php [en anglais] ou http://fr2.php.net/manual/fr/function.header.php [en français]
Un autre moyen de s'affranchir de la chasse aux espaces et aux retours chariots oubliés est d'utiliser l'output buffering, qui consiste en gros à mettre ob_start() en première ligne et ob_end_flush() en dernière. Voir le manuel : http://fr2.php.net/manual/en/ref.outcontrol.php
.

2.13 Remplacer les retours chariots dans une chaîne

$chaine = ereg_replace("(\r|\n){1,2}", " ",$chaine);

2.14 Les expressions régulières

Sujet trop vaste pour être traité dans une FAQ de NG, et non réellement lié à PHP. Des exemples sont présents dans cette FAQ.
Vous pouvez aussi consulter utilement les liens suivants :

http://www.linuxfocus.org/Francais/July1998/article53.html
http://www.phpbuilder.com/columns/dario19990616.php3
http://www.sitepoint.com/article/regular-expressions-php
http://www.zytrax.com/tech/web/regex.htm
ainsi que le chapitre Regexp de l'ouvrage PHP et MySQL en ligne de Jean Carfantan disponible sur phpindex.com : http://www.phpindex.com/download/regex.pdf

2.15 Variables dynamiques $var_$i =>$var_1, $var_2 ...

Il est possible en PHP de gérer des variables dynamiques, par exemple :

for ($n = 0; $n<4; $n++)
{
${"var_".$n}= "tutu".$n;
}

Cette boucle génère 5 variables : $var_0 à $var_4, qui contiennent le texte tutu0, tutu1...tutu4

2.16 Un @ devant la commande ?

On rencontre parfois des scripts de ce genre :
@mysql_connect(...

La présence du @ empêchera l'affichage d'une éventuelle erreur.
http://fr2.php.net/manual/en/language.operators.errorcontrol.php[EN] ou http://fr2.php.net/manual/fr/language.operators.errorcontrol.php[FR]

ATTENTION : il vaut mieux GERER les erreurs que de les IGNORER.

17 Mon code PHP s'affiche au lieu d'être éxécuté

Le plus probable est que vous avez nommé votre fichier avec une extension .html ou .htm et non .php3 ou .php, c'est aussi simple que cela. Si le fichier a bien une extension .php3 ou .php, alors votre serveur http n'est pas configuré correctement ou n'a pas été redémarré pour prendre en compte les modifications.
NB : il est possible de forcer l'association des fichiers .html avec PHP, mais attention aux performances. Ce genre de paramétrage n'a pas de raison d'être sauf dans des cas trè précis et spécifiques.
Une autre erreur classique est d'ouvrir directement le fichier PHP dans le navigateur quand on travaille sur la machine où se trouve le serveur http, ce qui se détecte aisément car l'URL est de la forme file:// et non http://

18 Les quotes : dois-je utiliser ' ou " ??

La règle absolue, et subjective, est la lisibilité du code. Les différences de performance sont suffisement négligeables par rapport aux algorithmes à optimiser pour ne pas rentrer en ligne de compte.

Contenu de cette section

3. La connexion à un sgbdr

Contenu de cette section

3.1 ...0 is not a valid resource ...

ou encore ...0 is not a valid mysql index on line...

La requête SQL a échoué et vous n'avez pas pensé qu'il faut toujours tester la valeur de retour d'une fonction aussi cruciale pour le résultat attendu qu'une requête sur BD, mysql_query( ) ici. Donc le premier appel au buffer de stockage du résultat, par mysql_fetch_*( ), provoque une erreur.
Pour trouver l'erreur dans votre requête SQL, affichez par un appel à echo "";ou print( ); la requête passée dans l'appel à mysql_query( ); et exécutez cette requête en ligne de commande dans MySQL.
Pensez à ne pas mettre de ";" final dans la requête envoyée à Mysql.
Ceci donne donc schématiquement :
$dad=...; // le resultat de mysql_connect() voir une fonction pour ce faire dans cette FAQ.
$query="SELECT les colonnes FROM la table WHERE conditions SANS LA ; FINALE";
echo "DEBUG#$query#";
$res=mysql_query($query,$dad);
if($res==FALSE) exit("Erreur.");
while($row=mysql_fetch_XXXX($res))
{
// utiliser $row
}

3.2 [souvent HS] Problèmes de connexion à MySQL

Manuel de MySQL (FR): http://dev.mysql.com/doc/mysql/fr/index.html
Quelques messages d'erreurs rencontrés assez souvent et ce qu'ils peuvent cacher :

Message d'erreur du genre :
Fatal error : call to unsupported or undefined function mysql_connect() in [fichier].php on line [XXX]

ATTENTION : PHP4 possède un support natif de mysql, cette réponse n'est applicable QUE à PHP3.
Vérifier que dans le fichier php3.ini ou php.ini une ligne n'est pas encore commentée à tort.
Par exemple dans ce cas, sous windows, dans la section ;Windows Extensions il faut enlever le ; devant extension=php3_mysql.dll (mais donc laisser cette ligne commentée avec PHP4)

Message d'erreur du genre :
... Can't connect to mysql server on ...[HS]
Vérifier que le "démon" mysqld (ou mysqld-shareware) a bien été lancé en ligne de commande, par "c:\mysql\bin\mysqld.exe --standalone" sous windows 9x, en tant que service sous Windows NT (c'est le plus simple) et par "$MYSQL/bin/safe_mysqld &" sous unix (en standard $MYSQL vaudra /usr/local mais ceci dépend de votre installation).

Message d'erreur du genre :
... Access Denied for user [toto]@localhost ... [HS]
Si votre script tourne chez un hébergeur, contactez son support technique et lisez sa documentation interne.
Si vous avez la main sur le serveur MySQL, vérifiez les droits de connexion de ce [toto] en vous connectant, en ligne de commande, à MySQL en tant que user root, en faisant une fois connecté un select * from user where user='[toto]'; et regarder tout particulièrement la colonne Host.
Voir le manuel de mysql : http://dev.mysql.com/doc/mysql/en/privilege-system.html [en anglais] ou
http://dev.mysql.com/doc/mysql/fr/privilege-system.html [en français] de MySQL pour plus d'informations.

3.3 Exemple de fonction de connexion à MySQL

Cette fonction de connexion appelle une fonction fx_footer( ), que vous devrez définir vous même, avec des arguments qui vous conviennent, par exemple dans le cas où vous disposez d'un menu de navigation paramétrable. Sinon, un echo "</BODY></HTML>"; fera l'affaire.
La terminologie DAD (Database Access Descriptor) est empruntée à Oracle.
Remarque : De manière générale, il vaut mieux forcer les valeurs par défaut à des valeurs que vous matrîsez.
function fx_std_connect( )
{
/* remplacer éventuellement localhost par le nom de serveur approprié.
Voir la doc de votre hébergeur. */
$l_dad=mysql_connect("localhost","votre_login","votre_password");
if ($l_dad == FALSE)
/*
NB1 : on peut faire appel à la fonction mysql_error() ou mysql_errno() pour obtenir le
message de retour de la base
NB2 : selon la configuration de PHP, utiliser eventuellement le caractere @ avant l'appel a mysql_connect
ou mysql_select_db pour supprimer les messagse d'erreur
*/
    {
    echo "Connexion impossible.\n";
    require("std_footer.php");
    fx_footer("parametre1","parametre2");
    exit();
    }
if(mysql_select_db("votre_base",$l_dad)!=TRUE)
    {
    echo "Database inaccessible.\n";
    require("std_footer.php");
    fx_footer("parametre3","parametre4");
    exit();
    }
return ($l_dad);
}

Pour chaque nouvelle connexion, appeler directement $DAD=fx_std_connect( );

Il va de soi que cette fonction doit être définie dans chaque script où elle est appelée, le plus simple étant de faire un require_once("std_connect.php"); où std_connect.php est le fichier qui en contient sa définition.
Si le fait de mettre ainsi votre login et mot de passe en clair dans un fichier vous effraie, lisez dans cette FAQ la section appropriée : "La protection de mes mots de passe de connexion à une base".

3.4 La protection de mes mots de passe de connexion à une base

Il existe de nombreuses méthodes permettant de sécuriser les informations relatives à la connexion vers une base de données. Nous allons en présenter ici quelques unes. En premier lieu, précisons qu'il est pratique de placer ces informations dans un fichier et/ou une fonction. Cette approche, modulaire, permet de procéder à des modifications ultérieures avec souplesse (changement du mot de passe d'accès à la base, etc.).

Par exemple, dans le cas particulier de MySQL, un fichier regroupant ces informations pourrait ressembler à celui-ci :

$host="mon_host";
$user="mon_user";
$password="mon_password";
$acces_bd=mysql_connect($host, $user, $password);

** Première méthode de protection Cas où vous n'avez pas la maîtrise du serveur qui héberge vos scripts
Nommer ce fichier en prenant soin de lui attribuer l'extension .php3 ou .php pour PHP4. Par exemple, base.inc.php3. Par ce simple fait, si un utilisateur mal intentionné tente d'ouvrir ce fichier à l'aide d'un simple navigateur, ce fichier sera de toutes facons "parsé" (interprété) par PHP coté serveur. Et le navigateur ne renverra rien de visible au client.

** Deuxième méthode de protection Cas où vous avez la maîtrise du serveur qui héberge vos scripts
Rien ne vous empèche de placer le fichier contenant les informations de connexion à votre base, en dehors de l'arborescence du serveur HTTP. C'est une bonne approche. Un serveur HTTP dispose d'une racine en aval de laquelle vont être placés et interprétés vos scripts et vos pages. Mais en amont, non. Par exemple, sous Apache, la racine (directive DocumentRoot de httpd.conf) est souvent /usr/local/apache/htdocs. Les pages et autres scripts se trouvant en aval de cette racine sont potentiellement visible depuis un navigateur. Il n'en va pas de même de ceux qui se trouvent en amont. Vous pouvez donc judicieusement placer votre fichier sous /usr/local/etc par exemple.
Une autre technique consiste à récupérer le login et password depuis des variables shell unix par un getenv();

** Troisième méthode de protection Cas où vous disposez des droits d'administration de votre base
Une méthode trop souvent ignorée consiste tout simplement à paramétrer finement les droits d'accès à votre base. Notamment afin de n'autoriser des connexions que depuis la machine locale (localhost). De ce fait, même si un utilisateur mal intentionné dispose des informations de connexion (et dans le cas où il ne dispose pas d'accès sur la machine afin d'y déposer des scripts, bien évidement), il ne pourra pas accéder à la base. Reportez vous au manuel du sgbd que vous utilisez.

** Les autres méthodes Ajoutons qu'il est aussi possible de bricoler les fichiers de configuration des serveurs HTTP (type Apache) afin de demander au serveur de "parser" les fichiers .inc ou encore de jouer avec les droits d'accès à certains répertoires ou fichiers (par le biais des fichiers .htaccess par exemple), y compris dans des sous répertoires si l'hébergeur interdit de "remonter" dans l'arborescence.

N'oublions pas qu'il est plus probable que votre mot de passe soit tout simplement "sniffé" quand il passera en clair avec votre clickdrome (tm) favori d'administration de base...

Contenu de cette section

4. Techniques de développement

Contenu de cette section

4.1 ...parse error on line... Undefined index... Undefined constant...

parse error

parse error on line 55 : une erreur de parsing (en français dans le texte) est une erreur de syntaxe php.
Il est important de comprendre que c'est l'équivalent d'une erreur de compilation pour un langage compilé. Il est donc futile d'essayer d'écrire un gestionnaire d'erreurs qui trappe les parse errors, ça n'a tout simplement pas de sens (sauf si vous écrivez un moteur qui accepte du PHP en entrée).
Une erreur déclarée sur la ligne N est très souvent causée sur la ligne N-1 sur cet exemple, la ligne 54.
L'erreur la plus courante est un oubli de ';' en fin de ligne...
Un autre grand classique est l'oubli de '\' devant des double-quotes ou l'oubli des double-quotes finales dans une chaîne de caractères.
On rencontre aussi une "parse error" en fin de fichier quand on oublie une accolade fermante } dans un bloc conditionnel. Typiquement, si votre fichier fait 204 lignes et que vous avez une "parse error" sur la 205eme, c'est un très probablement un problème d'accolades.
Undefined index

Selon le niveau d'erreur réglé dans php.ini (ou autrement), PHP va signaler les accès aux variables non définies et en particulier les tableaux.
Si dans un script vous accédez à $tableau['toto'] mais que jamais cet indice du tableau n'a été initialié, vous aurez cette erreur.
Vous devez alors :
- ou vous assurer que la variable sera toujours initialisée
- ou utiliser la fonction isset( )

4.2 Le multitasking / exécutions parallèles (UNIX)

Si vous désirez lancer des scripts en parallèle et/ou en asynchrone (on les lance et on les oublie, on continue le script courant sans attendre qu'ils se terminent) vous pouvez utiliser les fonctions PHP exec(), passthru(), ou system().
Par exemple : exec(" nohup toto.sh >mon_log.txt &");
En revanche, il faudra analyser le résultat en disséquant ("parser") le fichier de log : ouvrir ce fichier avec fopen(); puis le lire ligne par ligne en cherchant des erreurs éventuelles, etc...

4.3 Recherches par mots-clefs dans une base de données

Il n'est pas rare de devoir effectuer des recherches dans une basesur des données partielles, comme par exemple un mot-clef.
De manière générale, deux principes peuvent être mis en oeuvre :
- gardez une table spécifique pour la recherche, qui contiendra uniquement les mots-clefs en majuscules et sans accents.
- si le sgbdr le supporte (mysql par exemple) utilisez plutôt les regexp DANS la requête SQL au lieu de faire du "WHERE machin LIKE '%toto%'".
http://www.mysql.com/doc/R/e/Regexp.html

4.4 Lancer des commandes à intervalles réguliers ("crontab")

Commencez avant tout par vérifier si votre script doit être absolument lancé à une heure précise en rapport direct avec le temps newtonien. Ceci est en fait assez rare, et est en général lié à des traitements assez longs à exécuter de nuit.
Dans beaucoup de cas, vous pourrez vous passer de crontab. Considérons par exemple la purge de données obsolètes. "Tous les jours à minuit" est une approche, mais en fait, il suffit de purger avant la prise en compte ou l'affichage de ces données. Il suffit donc d'appeller un script de purge avant le traitement. On peut ensuite par exemple utiliser un fichier de configuration sur le serveur (un peu comme un cookie) ou tester l'heure courante par rapport à une plage horaire pour éviter de lancer la purge à chaque appel.

Dans certains cas, néanmoins, cette méthode n'est pas applicable. L'idéal est bien sûr alors d'avoir accès à une crontab.

Sinon, on peut faire appel à des services de sites qui appelleront votre script à l'heure voulue comme par exemple: http://www.witbe.net/ ou http://www.webcron.org/ (gratuit)

Il est également parfois possible de faire un "daemon" en PHP, grâce à une boucle infinie testant la présence d'un fichier par exemple, mais attention aux obstacles suivants :
- Dans la plupart des cas le temps maximum d'exécution d'un script est limité, en particulier par un paramètre de php.ini. Ce temps semble être du temps CPU, mais ceci est à confirmer, en particulier la gestion de ce paramètre est os-dependant.
- Attention à ne pas oublier de faire un appel à la fonction clearstatcache( ) si vous utilisez effectivement un test sur la présence d'un fichier pour arrêter votre démon ou c'est la boucle infine garantie.

4.5 Vérifier la présence d'un enregistrement avant de le mettre à jour

(La stratégie d'update / insert, limite hors sujet )
Pour vérifier la présence d'un enregistrement avant de le mettre à jour on peut procéder de la manière suivante :
1.Faire un select dans la table appropriée.
2.Tester le retour du nombre de rangs, par exemple grâce à mysql_num_rows ()
3.Si l'enregistrement n'existe pas, alors on fait un insert.
Sinon, on fait un update.
Une méthode plus rapide est la suivante :
Lancer l'update. Si le nombre de rangs mis à jour est égal à zéro, c'est que le rang n'existait pas dans la base.
Il n'y a plus qu'à l'insérer.

Comparaison du code des deux méthodes :
$result=mysql_query("SELECT * FROM ma_table WHERE ma_clef='$MA_VAR'");
$nbre_rangs=mysql_num_rows( $result);
if ($nbre_rangs == 1)
 {
 $modif=mysql_query("UPDATE ma_table SET colonne=colonne+1 WHERE ma_clef='$MA_VAR'");
 if (!$modif)
   {  return(FALSE); }
 }
else
 {
 $modif=mysql_query("INSERT INTO ma_table VALUES ('$MA_VAR',0)");
 if (!$modif)
    { return(FALSE); }
 }

Deuxième méthode :
/* $DAD = Database Access Descriptor, retour de mysql_connect() */
if (! mysql_query("UPDATE ma_table SET colonne=colonne+1 WHERE ma_clef='$MA_VAR'"),$DAD)
   {
   if(mysql_affected_rows($DAD)==0)
                 { mysql_query("INSERT INTO ma_table VALUES ( '$MA_VAR', 0)",$DAD); }
   }

Il est à noter que le code de la deuxième méthode, beaucoup plus compact et plus performant, est néanmoins plus permissif car il n'intègre pas de gestion des erreurs.

NB : dans le cas de MySQL intéressez vous à la commande REPLACE.

4.6 Gérer plusieurs langues sur un site

Voir le travail de Nicolas Hoizey : phpLang sur http://www.phpHeaven.net/

Par ailleurs, il est possible de définir la même variable PHP, avec une valeur différente selon la langue simplement en se servant de fichiers à inclure.
Soit par exemple une gestion de trois langues: allemand (DE), anglais (EN), et français (FR). Le code de toute page PHP commencera par le code suivant :
$l_langue=fx_filtrer($i_langue);
/* On filtre toute variable arrivant du monde extérieur */
switch ($l_langue)
{
/* Profitons de la capacité de PHP à accepter des chaînes
de caractères dans les switch( ) */
case "FR" : include("FR_mesg.php");
break;
case "EN" : include("EN_mesg.php");
break;
case "DE" : include("DE_mesg.php");
break;
default : echo "Langue non reconnue
";
fx_footer("parametres divers");
exit();
/* N'oublions pas le cas par défaut qui permet de traiter TOUTES
les erreurs d'un seul coup */
}
Les fichiers contiendront une définition différente des mêmes variables / constantes :
Dans FR_mesg.php : define(msg1,"Bonjour");
Dans EN_mesg.php : define(msg1,"Hello");
Dans DE_mesg.php : define(msg1,"Gunten Tag");
Dans le code du script, il suffira d'afficher la constante msg1 sans réfléchir : la bonne langue sera automatiquement sélectionnée.
Il est bien entendu aussi possible de stocker des messages dans une table d'une base de données qui aura trois colonnes : l'identifiant du message (msg1,...msgn), la
langue, et le texte, la clef primaire étant bien sûr le couple (identifiant, langue).

4.7 Connaître la version du navigateur et la résolution de l'écran

Remarque : il n'est pas particulièrement conseillé de faire des versions différentes d'un site selon la configuration du client qui va le voir (version du navigateur, résolution de l'écran...), en particulier pour la maintenance du code. Pour plus d'informations sur ce débat qui n'a rien à voir avec PHP, reportez vous au newsgroup fr.comp.infosystemes.www.auteurs ou fciwa et en particulier à ses archives sur http://groups.google.com/

La variable globale HTTP_USER_AGENT contient la version du navigateur ayant demandé la page (ou celle qu'il a bien voulu donner par exemple dans le cas d'un "aspirateur").
Des multitudes de scripts tous aussi mal écris les uns que les autres triturent cette variable dans tous les sens. Intéressez vous plutôt à la fonction PHP get_browser () : http://fr2.php.net/manual/en/function.get-browser.php
La résolution de l'écran n'est pas transmise au serveur par le navigateur. Si vous souhaitez vraiment vous en servir, utilisez JavaScript.

4.8 Imprimer

Non, on ne peut pas. Le serveur sur lequel tourne PHP n'a aucune connaissance des millions d'imprimantes connectées aux millions de PC/mac/autres de ceux qui regardent vos pages, c'est aussi simple que ça.
Si pour une raison quelconque, le bouton "imprimer" du navigateur ne vous convient pas, c'est en JS qu'il faudra gérer vos traitement spécifiques, côté client.

4.9 Alterner les couleurs dans un tableau

Pour autant que cela ait un réel intérêt graphique (en tous cas, c'est à la mode), il est très simple d'alterner les couleurs du fond d'un tableau.
Un exemple :
while($row =mysql_fetch_row($result_buffer) )
{
if($bg_color=="red"){$bg_color="green"}else{$bg_color="red"}
print "<TR><TD BGCOLOR=$bg_color>$row[1]</TD></TR>";
}

Remarque : pour les navigateurs qui gèrent correctement les CSS, on peut utiliser la méthode suivante :
<style type="text/css" media="print">
td { border-bottom : thin solid Gray;}
</style>

4.10 L'upload de fichiers

A voir dans la doc : http://fr2.php.net/manual/features.file-upload.php [en anglais] ou http://fr2.php.net/manual/fr/features.file-upload.php [en français]
Prérequis dans le formulaire :
- le formulaire doit être en encodage (enctype) multipart/form-data et en méthode POST.
<form action="script.php" enctype="multipart/form-data" method="POST">

- un champ (en général caché) nommé MAX_FILE_SIZE dont la valeur sera la taille maximale
acceptée pour les fichiers, exprimée en octets. Ce champ est facultatif.
<input type="hidden" name="MAX_FILE_SIZE" value="1000">

- un champ de type "file", le nom n'importe pas, appelé LeFichier pour la suite.
<input type="file" name="LeFichier" >

Gestion dans le script :
Le script recevant le formulaire va créer 5 variables pour chaque fichier chargé :

$_FILES['LeFichier']['tmp_name'] : l'emplacement temporaire du fichier sur le serveur
$_FILES['LeFichier']['name'] : le nom du fichier initial dans l'ordinateur client
$_FILES['LeFichier']['size'] : la taille du fichier exprimée en octets
$_FILES['LeFichier']['type'] : le type MIME du fichier (à condition que le navigateur l'ait renseigné!)
$_FILES['LeFichier']['error'] : le code retour de l'upload (php version>=4.2).
Ce code retour peut avoir les valeurs suivantes :
0 : tout va bien, fichier uploadé
1 : la taille du fichier dépassait la limite fixée dans php.ini
2 : la taille dépassait la limite fixée par le formulaire
3 : le chargement du fichier a été interrompu
4: le fichier n'a pas été chargé

On peut aussi simplement tester is_uploaded_file($_FILES['LeFichier']['tmp_name'])) qui renvoit FALSE si le fichier n'a pas pu être uploadé.

Reste donc simplement à déplacer le fichier temporaire dans un autre répertoire, par exemple : $DOCUMENT_ROOT."/upfiles/"
Ce répertoire cible doit être accessible en écriture. On peut le vérifier avec is_writeable($DOCUMENT_ROOT."/upfiles/")
http://fr2.php.net/manual/en/function.is-writeable.php [en anglais] ou http://fr2.php.net/manual/fr/function.is-writeable.php [en français]
Ensuite donc, on déplace : la fonction copy ( ) est utilisablee car rename( ) ne fonctionne pas toujours en multivolumes :
if (!copy($_FILES['LeFichier']['tmp_name'], $DOCUMENT_ROOT."/upfiles/".$_FILES['LeFichier']['name']))

Mais il vaut mieux encore appeler move_uploaded_file() (PHP 4 >= 4.0.3).

if(!move_uploaded_file($_FILES['LeFichier']['tmp_name'],
            $DOCUMENT_ROOT.'/upfiles/'.$_FILES['LeFichier']['name']))
   print 'erreur à la copie du fichier '.$_FILES['LeFichier']['name'];

http://fr2.php.net/manual/function.move-uploaded-file.php
http://fr2.php.net/manual/fr/function.move-uploaded-file.php
Remarques:
Si vous avez des problèmes à uploader les fichiers, vérifiez les paramètres suivants du fichier php.ini :
file_uploads=1
upload_tmp_dir= un répertoire temporaire auquel l'utilisateur php a accès en écriture (sous windows, c:/windows/temp semble un bon candidat)
upload_max_filesize=taille maxi d'un fichier uploadé
post_max_size=taille maxi des données envoyées par un formulaire (y compris les fichiers uploadés)

Sachez aussi que cette méthode d'upload par le protocole http n'est ni fiable ni adaptée aux gros fichiers; s'ils font plus de 2Mo envisagez plutôt un transfert par ftp.

Il est inutile de stocker des fichiers complets dans une base de données, leur chemin suffit. Une exception néanmoins si le but est de faire du PHP dynamique avec eval() mais ce seront des "petits" fichiers.

En particulier, il est rigoureusement inutile de stocker des fichiers binaires comme des images dans une base de données. Si vous le souhaitez vraiment (on ne peut pas vous empêcher...), pensez sous Oracle à utiliserle type BFILE, qui n'est pas stocké dans le tablespace.

Pour des raisons de sécurité, vérifiez toujours l'extension des fichiers uploadés pour bloquer ceux qui seraient exécutables (extensions '.php', '.php3', '.cgi', etc.), de préférence avec une liste d'extension explicitement autorisées, ou bien stockez tous vos fichiers uploadés dans un répertoire rendu inaccessible par le serveur HTTP (sous Apache .htaccess par exemple).

L'upload de fichiers fonctionne bizarement chez l'hébergeur Free. Il semble que le script appelé doive avoir l'extension php3.

4.11 L'envoi de fichiers vers le navigateur

Pour envoyer un fichier vers le navigateur du client par un simple lien http, il est possible d'utiliser la fonction header() (avec les précautions rappelées dans cette FAQ) :

header("Content-disposition: attachment; filename=nom_fichier.txt");

Contenu de cette section

5. Les mails et les forums NNTP

Contenu de cette section

5.1 Le mail chez free.fr

Oui, il est possible d'envoyer des mails depuis php chez free.fr.
http://www.free.fr/assistance/946-acces-libre-php-fonction-email.html
Remarque : 'Protomail' de Samuel Kabak n'est plus maintenu. Vous le trouverez néanmoins en faisant une recherche sur votre moteur favori.

5.2 Envoi d'un mail.

Utilisez ou disséquez la très bonne classe de gestion de Léo West, disponible sur http://lwest.free.fr/doc/php/lib/Mail/ Documentation en français sur http://lwest.free.fr/doc/php/lib/index.php3?page=mail&lang=fr

5.3 Tester la validité d'une adresse email

(merci à O.Miakinen pour la nouvelle rédaction de ce paragraphe)

Définition : une adresse électronique est valide si elle est syntaxiquement correcte et si elle est sur un nom de domaine qui existe et si le compte existe dans ce domaine et si l'adresse appartient bien à la personne qui vous l'a donnée. Cette dernière vérification est indispensable pour éviter d'inscrire quelqu'un contre son gré. La meilleure façon de s'en assurer -- sinon la seule -- consiste à envoyer à cette adresse une demande de confirmation, accompagnée d'un identifiant unique généré aléatoirement. Si l'adresse est valide et que son propriétaire est bien celui qui a donné cette adresse sur votre site, il renverra la confirmation et vous pourrez valider sa demande ; sinon, vous annulerez cette demande après un certain délai (48 heures par exemple). Toute autre sorte de validation sera (au mieux) incomplète ; au pire elle refusera des adresses valides. Oubliez donc toute tentative de vérification basée sur l'interrogation d'enregistrements DNS de type MX, tentative qui sera aussi coûteuse qu'inefficace.

En réalité, il faut appliquer à une adresse email exactement le même genre de contrôle qu'à tout ce qui vient de l'extérieur, pour éviter qu'elle serve de point d'attaque, par exemple à un spammeur. Ainsi, il faut au moins en exclure les sauts de ligne et les chevrons (< et >), de même que tout ce qui pourrait provoquer un effet de bord dans vos programmes. Une règle élémentaire dictant de lister les caractères autorisés plutôt que les caractères interdits, voici un code qui reconnaît les syntaxes simples (c'est-à-dire sans caractères d'échappement "" ou \) :
 $ltext = '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]+';
 $rtext = '[A-Za-z0-9-]+';
 $pattern = ":^$ltext(\.$ltext)*@$rtext(\.$rtext)+$:";
 if (preg_match($pattern, $email)) { ... }
En cherchant un peu sur Internet, vous pouvez trouver des expressions régulières gigantesques gérant la syntaxe complète décrite dans les RFC 822 et 2822 (avec ou sans les commentaires), mais avant d'utiliser ce genre de syntaxe posez-vous la question de savoir si vous voulez vraiment accepter des adresses de la forme "@"@example.com ou "@"(si ! si !)@example.(ici aussi)com.

Si au contraire vous préférez une expression plus simple puisque de toute manière la vraie vérification se fera par un message de confirmation, voilà pour vous satisfaire sans frustrer vos visiteurs honnêtes :
 $pattern = ':^[.A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]+@[.A-Za-z0-9-]+$:';
 if (preg_match($pattern, $email)) { ... }
Enfin, si vous êtes vraiment parano, vous pouvez restreindre le répertoire utilisé pour la partie gauche de l'adresse, par exemple pour en exclure les caractères guillemet simple ('), pourcent (%) et esperluette (&), mais par pitié n'en excluez-pas le caractère plus (+) :
 if (preg_match('/^[.A-Za-z0-9+_-]+@[.A-Za-z0-9-]+$/', $email)) { ... }

5.4 Forums usenet

1) Il est possible d'utiliser soit les sockets, soit les fonctions IMAP pour accèder en lecture / écriture à un serveur de news :

- L'article d'Armel Fauveau (en VF) traitant de la gestion des sockets avec php en lien avec un serveur NNTP :
http://www.phpheaven.net/articles/php_et_les_sockets_article9.html
- pour imap, le manuel :
http://fr2.php.net/manual/fr/ref.imap.php
- Sans oublier la RFC 977 traitant du NNTP : http://www.ietf.org/rfc/rfc0977.txt?number=977
2) classes NNTP disponibles en php :
http://www.vhconsultants.com/nnrp/nnrp.htm avec un tuto de présentation disponible ici :
http://www.phpteam.net/affiche.php?quoi=socket1
http://phpclasses.upperdesign.com/browse.html/package/157
Le projet PEAR : http://cvs.php.net/cvs.php/pear/Net_NNTP
3) Des projet de client NNTP via php :
http://florian-amrhein.de/newsportal/index-english.php3
http://sourceforge.net/projects/phnntp/

Contenu de cette section

6. Les outils déjà prêts

Contenu de cette section

6.1 Code d'un forum ou BBS en PHP

ATTENTION. Une extrême vigilance sur les mises à jour de sécurité doit être de mise si vous utilisez de tels forums.

Il existe des projets "tout prêts" et gratuits. Par exemple :
- phorum http://phorum.org/
- agora http://w-agora.net

Si vous préférez faire le votre, un très bon exercice, inspirez vous de leur code (ce qui est du "hacking" au bon sens du terme).
Si vous possédez le livre de Leon Atkinson (cf en bas pour toutes ses références), voir le chapitre 16 pour un exemple.

6.2 Code d'un caddie virtuel en PHP

Intéressez vous au projet en Open Source d'Alexandre Trusch : http://www.w3-concept.net/
Et la démo de ce projet : http://demo.w3-concept.net/.
On peut également citer http://www.oscommerce.org/ sur lequel des versions en français sont disponibles sous forme de packages complémentaires.

6.3 Code d'un "chat" en PHP

Voir le travail de Nicolas Hoizey : phpMyChat sur http://www.phpHeaven.net/

6.4 PHP-NUKE

En cas de problème avec php-nuke, merci de commencer par consulter les ressources en ligne disponibles avant de poster sur fclphp.

Site officiel : http://www.phpnuke.org

Liste de diffusion (en français) : http://fr.groups.yahoo.com/group/php-nuke/

Alternatives diverses :
http://www.phpscripts-fr.net/?page=scripts&cat=Portails

6.5 Webmail

Une interface de consultation de mails en PHP :
SquirrelMail : http://www.squirrelmail.org/
IMP http://www.horde.org/imp/
Attention, cette application nécessite une foultitude de dépendances, en particulier sur le "framework" horde et sur pear.

6.6 Spip (CMS)

SPIP : http://www.spip.net/

6.7 Typo3 (CMS)

Typo3 : http://www.typo3.org/

6.8 W-agora (CMS)

W-agora : http://www.w-agora.net/

6.9 Administration SGBDR

Quelques outils de gestion de SGBD en PHP :
Mysql : http://www.phpmyadmin.net/
Postgresql : http://phppgadmin.sourceforge.net/
Oracle : http://phporacleadmin.org/

Contenu de cette section

7. Gérer les sessions utilisateur

Contenu de cette section

7.1 Coder votre propre gestion de session

Cette méthode, la seule possible sous PHP3, reste la plus souple et la plus fiable en ce qui concerne la capacité à maintenir le code. On utilise en général une table dans un SGBDR, contenant :
- un identifiant de session
- une colonne de type date, mise à jour à chaque demande de page, permettant aussi de faire expirer la session
- autant de colonnes que l'on veut associer de données à la session (dépendant de votre application)

L'identifiant de session est transmis dans les liens et les formulaires. A toute nouvelle connexion, on purge toutes les sessions inactives depuis plus de N minutes, puis on vérifie si la session courante est encore active.

Un générateur de session renvoie suffisament de caractère "aléatoirement" qu'il est impossible en termes de probabilités quepndant les N minutes de connexion, le même identifiant soit généré deux fois.

Deux grandes méthodes sont utilisées pour coder un générateur d'identifiants. La première consiste à se donner une liste de caractères autorisés et à "piocher" aléatoirement dans cette liste le nombre de fois voulu avec la fonction random(). La seconde repose sur le système d'exploitation et des "bidouilles" plus ou moins personnelles à chaque développeur, faisant appel à des concaténations de l'heure système et du PID du process courant par exemple.

Il est à noter que ce type de sessions peut être historisé dans une aute table, associée à un login/password par exemple, ce qui rend l'utilisation de cookies totalement inutile et perfidement, obligatoire pour l'internaute qui ne peut pas savoir ce qu'on stocke sur le serveur, mais peut effacer ou refuser les biscuits.

7.2 Utiliser l'excellente PHPLib

Ressources :
Site officiel de la PHPLib
https://sourceforge.net/projects/phplib/

NewsGroup dedié à la PHPLib
news://news.netimages.com/php3.phplib

Archives de la liste PHPLib du site PHPBuilder
http://www.geocrawler.com/lists/4/Web/195/0/

7.3 Le support natif des sessions de php4 & php5

A lire : http://fr2.php.net/manual/en/ref.session.php ou en français http://fr2.php.net/manual/fr/ref.session.php
A noter : il est très tentant de faire n'importe quoi et de stocker n'importe quoi dans des sessions avec php4. En particulier, on ne peut PAS stocker un identifiant de connexion à une base de données (par exemple le retour de mysl_connect) dans un identifiant session.
Les sessions en PHP4 devraient être réservées à des développeurs ayant un minimum d'expérience et de compréhension des mécanismes impliqués.

7.4 Comptabiliser le nombre courant de personnes "connectées" sur le site

Disposant d'un identifiant unique par session, vous pouvez stocker ces identifiants dans une table d'un SGBD, avec une colonne de fonctionnalité "timestamp" qui permettra de savoir quand l'enregistrement a eu lieu (mis systématiquement à l'heure système). Vous pouvez alors sélectionner toutes les sessions différentes "actives" dans, mettons, les 3 dernières minutes : souvenons nous que HTTP sur TCP/IP est un protocole utilisé en mode non connecté, il n'y a personne qui soit connecté (au sens ouverture de session avec authentification et fermeture explicite de session) à votre serveur en regardant vos pages.
Vous pouvez aussi vous servir de telles tables pour "suivre à la trace" les visteurs par leur identifiant. Notez que l'adresse IP de votre visiteur n'est a priori pas un identifiant fiable.
Remarque : si vous ne stockez qu'un identifiant qui n'a aucun lien direct avec l'internaute que vous "pistez", vous n'êtes en rien en contradiction avec la CNIL. Le stockage d'IP, bien que techniquement délirant, est plus sujet à caution.

7.5 Suivre ses visiteurs sur son site / l'adresse IP de vos visiteurs

La solution la plus simple consiste à utiliser des identifiants de session transmis d'une page à l'autre.

Les autres solutions côté serveur (à l'exclusion, donc, des cookies qui sont côté client) mettent en jeu plusieurs variables PHP :
Dans le cas où il n'y a pas de proxy(s) entre le client et le script php, $REMOTE_ADDR (ou $_SERVER['REMOTE_ADDR'] ) est l'adresse IP du client.
Dans le cas où il y a un (au moins) proxy, les variables $HTTP_X_COMING_FROM (ou $_SERVER['HTTP_X_COMING_FROM']) et $HTTP_VIA (ou $_SERVER['HTTP_VIA']) sont positionnées.

Attention donc, il n'est pas fiable d'utiliser $REMOTE_ADDR comme identifiant.

Si le problème vous intéresse, consultez la fonction GetIdentifier de Marc Meurrens : http://www.cgsa.net/php/identifierShow.php

7.6 Mot de passe d'accès à un répertoire

La quasi totalité des hébergeurs proposent une interface graphique permettant de protéger l'accès à un répertoire par un couple (login / mot de passe).
[HS] Chez free.fr : http://http://faq.free.fr/accesgratuit/Administration_du_compte_et_services/Pages_Persos/Personnaliser_sa_page_perso/Comment_gerer_les_restrictions_d_acces

Dans le cas où il vous faudrait faire ceci "à la main", une réponse sera rédigée en cas de demande. Il suffit en gros de faire un "man htpasswd"

Ceci est à différencier d'une gestion d'identification par login/passwd stockés dans une base de données.

Contenu de cette section

8. L'affichage de résultats de requêtes par tranches

Contenu de cette section

8.1 Présentation du problème

Lorsqu'une requête SQL renvoie un grand nombre d'enregistrements, on choisit souvent de n'afficher qu'un sous-ensemble de ceux-ci et de proposer à l'utilisateur de naviguer entre plusieurs pages de réponses (à la façon des moteurs de recherche).

Pour cela, plusieurs solutions sont possibles pour afficher les réponse $deb à $fin (deux variables passées en paramètre dans l'URL). Nous en présentons ici trois.

8.2 Faire une série de requêtes SQL classiques et traiter en retour

Avantages :
Compatible avec toute les bases.
On peut connaitre facilement le nombre total de réponses avec mysql_num_rows()
Inconvénient :
cette méthode est sous-optimale (!) en terme d'accès au sgbd et en post-traitement, et est présentée pour la forme.

$res = mysql_query("select nom, prenom from personne where age=30");
/* Boucler sur les résultats, en filtrant à l'affichage : */
$i = 0;
while ($a = mysql_fetch_row($res))
{
if (($i >= $deb) && ($i <= $fin))
afficher_rang($a);
$i++;
}

8.3 Utiliser la clause LIMIT du select de MySql

Avantage :
Utilisation optimale de MySql (et tout autre base acceptant un mécanisme semblable)
Inconvénients :
Non applicable à la plupart des sgbdr (qui n'acceptent pas cette notion de LIMIT)
On ne connait plus le nombre total de réponses. Pour l'obtenir il faut effectuer une requête supplémentaire lors avant la première tranche à afficher : select count(*) from personne where age=30;

$query = "select nom, prenom from personne where age=30 LIMIT $deb ,$fin ";
$res = mysql_query($query);
while ($a = mysql_fetch_row($res))
affiche_row($a);

/* Lien avec $deb et $fin2 en paramètre pour la page suivante */

$fin2 = $fin-$deb +1;
$deb=$fin2;

8.4 Utiliser une table tampon

Avantages :
Totalement compatible / portable quel que soit le sgbdr utilisé.
Compromis correct en utilisation des ressources.
Inconvénients :
Nécessite une table supplémentaire et un identifiant requête (ou session).
Remarque : la gestion des identifiants session est disponible dans cette FAQ.

Ci-dessous, les grandes lignes de la méthode. Un document plus détaillé est disponible donnant un exemple de moteur de recherche affichant ses résultats page par page sur http://johng.free.fr/requetes_tranches.html

1 - Créer une table fille, qui contient tout les champs de la table mère qu'on veut rendre disponible à la requête + 3 autres champs:
- une colonne id_session, contenant l'id de session du visiteur qui permettra d'identifier le résultat de la requête du visiteur, clef primaire de la table avec l'ordre_session.
- une colonne ordre_session qui sera incrémentée par chaque enregistrement d'une ligne de la requête sur la table mère.
- une colonne temp_session de type timestamp qui permettra de connaitre la date de création des lignes de la requête pour les purger le moment venu.

2.1 - On fait la requête sur la table mère. Avec le résultat on l'insère ligne par ligne dans la table fille, en n'oubliant pas de créer pour chaque ligne un champ
ordre-session incrémenté de 1, et deux champs id_session et temp_session identiques pour toutes les lignes de cette requête. On peut ici limiter le nombre maximum
d'enregistrements à présenter. On dispose alors du nombre total de lignes à afficher par tranches, $nombre_lignes.

2.2 - On purge la table des enregistrements dont le temp_session >= 20 minutes. (par exemple).

3 - On crée une variable borne_inferieure = 1. On crée ensuite une variable $borne_superieure = $borne_inferieure + $nombre_lignes;

En boucle pour chaque tranche :
4 - On fait une requête du style:

SELECT * FROM table_fille WHERE id_session='$id_visiteur'
AND ordre_session > $borne_inferieure AND ordre_session < $borne_superieure
ORDER BY ordre_session;

5 - On incrémente $borne_inferieure = $borne_superieure + 1; et $borne_superieure += $nombre_lignes;
(ou $borne_superieur=$nombre_lignes; si dépassement).

6 - On crée un lien contenant $id_session du visiteur,$borne_inferieure, $borne_superieure et $nombre_lignes. Retour acte - 5.
(sauf si $borne_inférieure >$nombre_lignes car dernière page)

Contenu de cette section

9. Ressources PHP

Contenu de cette section

9.1 Manuels php

Attention, les traductions en français ne sont parfois pas à jour. En cas de doute, référez-vous toujours à la version en anglais.

http://fr2.php.net et http://fr2.php.net/docs.php (anglais et français) dont la référence rapide : http://fr2.php.net/quickref.php
Accès direct en français : http://fr2.php.net/manual/fr/

9.2 Ouvrages "papier"

NB : cette FAQ n'a pas pour finalité de tenir le rythme imposé par les éditeurs qui sortent un nouvel ouvrage tous les mois ou presque. Il s'agit là d'exemples.

Pratique de PHP-Mysql P. Rigaux Editions O'Reilly
A noter que ce livre a été rédigé en français et bien qu'il soit publié chez O'Reilly, n'a donc aucune horreurs de traduction.
Programmation web avec PHP (quatre co auteurs)
Editions Eyrolles. 360 pages. http://clauer.free.fr/livrephp.php3
Programmation en PHP par Leon Atkinson
Editions Campus Press. 450 pages, environ 200F.
Remarque : la première édition comporte de nombreuses erreurs de traduction. Une deuxième édition relue par Marc Meurrens est disponible. Un réédition a eut lieu récemment (2004)
Professionnal PHP programming(cinq co auteurs)
Editions Wrox. 900 pages, environ 360 F.
Disponible traduit chez Eyrolles.
Pages Web dynamiques avec ASP-PHP-SQL de Jean-Marc Herellier et Philippe Mérigod.
Editions Campus Press.

9.3 Archives du NG fr.comp.lang.php

Les archives de phpindex http://www.phpindex.com/ng/
Dispose aussi des archives de fcas et fciwa
Google : http://groups.google.com/

9.4 Interfacer php et Oracle

Technology Network d'Oracle :
http://www.oracle.com/technology/tech/opensource/
http://www.oracle.com/technology/pub/articles/deployphp/
Oracle et Mac OS X:
http://www.oracle.com/technology/pub/articles/rohrer_macosx_10g.html

php-oracle (FR) : http://callista.free.fr/php_oracle/php_oracle.php3
Rappel : Oracle est téléchargeable sur le Technet Oracle : http://technet.oracle.com
Si vous avez des erreurs de connexion de type "TNS coud not resolve..." voyez les user notes du manuel php.

9.5 Interfacer php et Sybase

Avec Windows : http://www.jf-lebon.com/

9.6 Sites en français

phpindex (FR): http://www.phpindex.com/ et en particulier la FAQ de phpindex : http://www.phpindex.com/faq/
Comment ça marche (FR): http://www.commentcamarche.net/
phpfrance (FR) : http://phpfrance.com/
Site de Christophe Lauer (FR) : http://clauer.free.fr/
phpdebutant.org (FR) : http://www.phpdebutant.org/
phpinfo(non maintenu): http://fr2.phpinfo.net/
Developpez.com : http://php.developpez.com/cours/
Cours et sécurité http://www.saphirtech.com/cours.html

9.7 Sites en anglais

Google. Une recherche bien ciblée vous donnera souvent beaucoup de scripts ou d'informations. http://www.google.com
SourceForge : http://www.sourceforge.net
PKB PHP Knowledge BASE (ENG) mais sections en en français : http://www.faqts.com/knowledge-base/index.phtml/fid/51/
PHP Classes Repository (ENG) : http://www.phpclasses.upperdesign.com/
PHP Builder (ENG) : http://phpbuilder.com
PHP Club (ENG + RU ): http://phpclub.unet.ru/
PHP Wizard (ENG) : http://www.phpwizard.net/
Weberdev (ENG) : http://www.weberdev.com/
Hot Scripts (ENG) : http://www.hotscripts.com/PHP/Scripts_and_Programs/
Site de Hans Anderson (ENG) : http://www.hansanderson.com/php/ Le code d'un moteur de recherche et d'indexation en php y est disponible.

Contenu de cette section

10. Ressources connexes

Contenu de cette section

10.1 Problèmes avec un hébergeur

Avant tout, le support technique de cet hébergeur !!

Amen
Web : http://www.amen.fr
Contact : http://support.amen.fr/contact/
Support : http://support.amen.fr/
OVH
Web : http://www.ovh.com/
Support : http://www.ovh.com/fr/support/

Online
Web : http://www.online.fr
Contact Technique : support@online.net
Contact Commercial : hebergement@online.net
Support : http://www.online.fr/support/fr/
Free
Web : http://www.free.fr
Support : http://support.free.fr/

Forez
Web : http://www.forez.com/portail/
Contact : webmestre@forez.com
Nexen :
Web : http://www.nexenservices.com/
Support : http://www.nexenservices.com/

En complément :

- certains prestataires disposent de newsgroups privés et autres listes de
diffusion. C'est le cas en particulier de Proxad (Free et Online) avec
news:proxad.online.hebergement (vous devez disposer d'un compte). Ne pas hésitez à y aller.

- ne pas négliger le forum fr.reseaux.internet.hebergement souvent plus adapté que fclphp (genre coup de gueule, demande de renseignements, etc.). Les principaux hébergeurs y sont souvent assez actifs.

10.2 Référence HTML

Norme 4.0 du W3C : http://www.w3.org/TR/REC-html40/
All Html (en français) : http://www.allhtml.com/
Papier : HTML Pocket Reference, Jennifer Niederst, ed. O'Reilly

10.3 Problèmes avec du SQL, MySQL, Postgres, Oracle, Sybase, etc...

Le newsgroup qui permettra d'obtenir les réponses pertinentes : news://fr.comp.applications.sgbd
La documentation MySQL (en français en milieu de page et en anglais): http://dev.mysql.com/doc/
Le technet Oracle : http://technet.oracle.com/ (inscription gratuite, pas de spam à outrance constaté, Oracle 8i à télécharger gratuitement pour développement sous unix)
Le support Sybase : http://www.sybase.com/support/ (inscription gratuite, pas de spam à outrance constaté)

10.4 Ouvrages "papier"

Oracle Design Dave Ensor and Ian Stevenson ed. O'Reilly
Contrairement à son titre, cet ouvrage est très généralist sur les SGBDR et en particulier traite clairemnt et simplement des Formes Normales de Codd. 500+ pages, env 320F. Ne pas confondre avec sa mise à jour Oracle 8i.

10.5 Problème de configuration Apache, IIS...

Le newsgroup approprié news://fr.comp.infosystemes.www.serveurs

10.6 Request For Comments

Les RFC (Request For Comment) définissent les protocloes utilisés quotidiennement par nos lecteurs de news et autres navigateurs. Elles sont toutes disponibles sur http://www.faqs.org/faqs/
Des grands classiques :
http : RFC 2616
nntp : RFC 977

Contenu de cette section

11. Génération de graphiques

Contenu de cette section

11.1 GD Lib

Il est possible d'afficher des statistiques sous forme de graphe avec PHP. Intéressez vous pour ceci à la GD LIB.
Il est à noter qu'à cause de problèmes de copyrights et de décalage entre PHP et la GD lib, vous pouvez avoir des problèmes de compilation.
GD Lib : http://www.boutell.com/gd/

La GD Lib possède de nouveau le support des GIFs.

11.2 Bibliothèques

Comparatif : http://cyberzoide.developpez.com/php4/graphismes/
Des bibliothèques s'appyant sur la GD lib :

vh graph : http://www.vhconsultants.com/
JpGraph : http://www.aditus.nu/jpgraph/
Vagrant : http://vagrant.sourceforge.net/
PHPLOT : http://www.phplot.com/

11.3 Redimensionner une image

http://fr2.php.net/manual/en/function.imagecopyresized.php
Et bien lire les user notes, qui sont (comme souvent) intéressantes, avec beaucoup d'exemples de code, des patches pour l'amélioration de la qualité des thumbnails,etc...
En particulier :
- Vous pouvez recompiler la GD lib ? Du code C et les moyens de patcher sont donnés
- Vous ne pouvez pas patcher la GD lib ? Ce sera moins performant, mais du code 100% PHP est disponible, donné par rze at counter-strike dot net le 11-Jul-2001.

Des exemples de manipulation sur http://clauer.free.fr/resphp.html (au milieu de la page)

11.4 Flash dynamique

PHP Peut également générer des animations flash à la volée.
Exemple d'application aboutie (vérifiez la license): http://www.maani.us/charts/

Contenu de cette section

12. Bugs php ?

Contenu de cette section

12.1 Comportement bizarre : un bug PHP ?

Quand un script se comporte étrangement, la probabilité la plus forte est que c'est votre script qui se plante, ou que les permissions des fichiers que vous traitez sont mauvaises.
Néanmoins, il y a des bugs (ou des "problèmes résiduels" ((C) A.F. 2000)...) aussi dans PHP, il peut donc être intéressant de les connaître, surtout si vous utilisez une version un peu ancienne de PHP.

La liste officielle des bugs déclarés est disponible sur le site de PHP : http://bugs.php.net/

Contenu de cette section

13. Les concurrents de PHP

Contenu de cette section

13.1 PHP et ASP

Une magnifique tarte à la crème qui mobilise beaucoup d'électrons, l'éternel débat sur "lequel qu'est le mieux ?" a déjà largement fait transité des Mo. Avant de le relancer, vous pouvez consulter :

[Stratégie de développement} Analyse comparative PHP / ASP
en date du 01-08-200 par leylek@onera.fr
ID 4Muh5.3432$_y2.5664889@nnrp5.proxad.net

php versus asp
en date du 02-02-2001 par Théo
ID 95ek7q$2jp$1@nnrp1.deja.com

Php Vs Asp en date du 17-07-2001 par par rage
ID 3B544BC6.49C43EB1@caramail.com

Ces articles sont disponibles sur http://groups.google.com et http://www.phpindex.com/ng/ng_index.php3?c=fciwap

13.2 Benchmarks sur les plateformes web dynamique

Quelques benchs :
http://www.chamas.com/bench/

Contenu de cette section

14. Génération de PDF

Contenu de cette section

14.1 Comparatif des solutions existantes

Olivier PLATHEY a présenté un excellent comparatif des solutions au Forum PHP 2002 de l'AFUP : http://www.afup.org/forumphp2002/fpdf.pdf

14.2 Modification

Outre la pdflib et la clibpdf , fpdi permet d'ajouter des données dans un PDF. http://fpdi.setasign.de/

15. Compiler / encoder ou "obfusquer" des scripts PHP

Contenu de cette section

15.1 Ne pas livrer son code PHP en clair

Il est classique de devoir livrer des scripts PHP en clair, que n'importe qui ayant accès à la machine peut ensuite lire et modifier.
Mais il est également possible de :
  • rendre le code source moins lisible avec un "obfuscateur". Il s'agit principalement de renommer les variables, fonctions, classes et méthodes, de supprimer les commentaires et la mise en forme du code. Du code "obscurci"/obfusqué" ne nécessite aucune instalation d'outils supplémentaires pour être exécuté.
  • precompiler le code source en op-code lisible uniquement par le moteur d'exécution de PHP, le Zend Engine. On est alors dans une situation similaire à la livraison d'une multitudes de .class en java, à ceci près que le reverse engineering est beaucoup plus complexe. En juillet 2005, il n'existe pas de moyen fiable public de revenir au code source d'origine.
    L'utilisation d'encoders qui produisent de l'op-code nécessitera l'instalation en plus de PHP du Zend Optimizer (gratuit).
    Dans certains cas, l'utilisation d'encodeurs permet de :
    - gagner un temps énorme de recompilation. Au lieu que le moteur de PHP doive, à *chaque* requête, recompiler tous les scripts, ils sont compilés une bonne fois pour toutes.
    - être couplé à un système de gestion de license et de nombre d'utilisateurs simultanés, etc...

    Beaucoup des solutions d'encodage proposées ci-dessous sont payantes.
ATTENTION DANS LES DEUX CAS : les mots de passe peuvent néanmoins être lus en clair.
Quelques benchs :
http://meta.wikimedia.org/wiki/PHP_caching_and_optimization
http://www.phpindex.com/: les solutions de cache ou directement http://www.phpindex.com/download/BenchWeb_V1.1.pdf

15.2 Zend Encoder

C'est la solution de la société ZEND TECHNOLOGIES qui a développé le coeur de PHP 4 et PHP 5. http://www.zend.com/

15.3 ionCube

Une solution d'encodage "à la carte" pour quelques scripts est également disponible.
http://www.ioncube.com/

15.4 phpSHIELD

http://www.phpshield.com/

15.5 turkMMcache / eAccelerator

Le développeur principal de turkMMcache ayant été embauché par ZEND, le projet, qui malgré son nom possède bien une solution d'encodage (cache d'op-code), a été repris pour donner eAccelerator. http://eaccelerator.net/

15.6 bcompiler

PHP bytecode Compiler. Il s'agit d'une extension standard ("experimental" en juillet 2005).
http://fr2.php.net/manual/en/ref.bcompiler.php

15.7 APC

Alternative PHP Compiler. http://apc.communityconnect.com/ ou directement : http://pecl.php.net/package/APC

15.8 POBS

PHP Obfuscator. Il ne s'agit pas d'un encoder. http://pobs.mywalhalla.net/


Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours

Cours de PHP 5

Image non disponible

Table des matières Plier Déplier

  • Page 1
    • I. Introduction
      • I-A. Remerciements
      • I-B. Préambule
      • I-C. Problématique
      • I-D. Communauté
      • I-E. Outils de développement
      • I-F. Documentation
      • I-G. Utilisations de PHP
        • I-G-1. Exemple Web
        • I-G-2. Exemple "CLI"
        • I-G-3. Exemple "GUI"
  • Page 2
    • II. Exemples de scripts (mise en jambe)
      • II-A. Exécution des scripts d'exemple
      • II-B. Affichage
      • II-C. Boucles
  • Page 3
    • III. Syntaxe du langage PHP
      • III-A. Préambule
      • III-B. Présentation du code
      • III-C. Parenthèses
      • III-D. Accolades
      • III-E. Commentaires
      • III-F. Opérateurs
        • III-F-1. Arithmétique (+ - * / %)
        • III-F-2. Affectation de variable (= += -= *= /= .=)
        • III-F-3. Comparaison (== === != <> !== > >= < <= instanceof)
        • III-F-4. Condition (?)
        • III-F-5. Incrémentation / diminution (++ --)
        • III-F-6. Arithmétique logique, aka opérateurs de bits (& |)
        • III-F-7. Comparaison logique (&& || and or)
        • III-F-8. Opérateurs de tableaux (+ == === <> != !==)
        • III-F-9. Opérateurs spéciaux (@ ``)
      • III-G. Types
        • III-G-1. Préambule
        • III-G-2. Type chaîne de caractères (string)
        • III-G-3. Type numérique (int, float)
        • III-G-4. Types spéciaux (null resource object)
        • III-G-5. Fonctions utiles
      • III-H. Variables
        • III-H-1. Syntaxe
        • III-H-2. Superglobales
        • III-H-3. Références
        • III-H-4. Fonctions utiles
        • III-H-5. Bonnes pratiques
      • III-I. Constantes
        • III-I-1. Syntaxe
        • III-I-2. Constantes magiques
        • III-I-3. Fonctions utiles
        • III-I-4. Utilisation
      • III-J. Tableaux
        • III-J-1. Syntaxe
        • III-J-2. Pointeur interne
        • III-J-3. Fonctions utiles
      • III-K. Structures de contrôle
        • III-K-1. Conditionnelle "if"
        • III-K-2. Alternative "switch"
        • III-K-3. Boucle "for"
        • III-K-4. Boucle "while"
        • III-K-5. Boucle "do while"
        • III-K-6. Boucle "each"
        • III-K-7. Boucle "foreach"
      • III-L. Fonctions
        • III-L-1. Syntaxe
        • III-L-2. Visibilité des variables
        • III-L-3. Fonctions et constantes utiles
  • Page 4
    • IV. Programmation Orientée Objet (POO)
      • IV-A. Modèle objet
        • IV-A-1. Bref historique
        • IV-A-2. Terminologie
        • IV-A-3. Les mots réservés
        • IV-A-4. Syntaxe
        • IV-A-5. Droits d'accès
        • IV-A-6. Résolution de portée
        • IV-A-7. Interfaces
        • IV-A-8. Références et clônage
        • IV-A-9. Late Static Bindings (LSB)
        • IV-A-10. Exceptions
        • IV-A-11. Fonctions et constantes utiles
      • IV-B. Espaces de noms
        • IV-B-1. Introduction
        • IV-B-2. Syntaxe
        • IV-B-3. Exemple d'utilisation
  • Page 5
    • V. Configuration par le fichier php.ini
      • V-A. Introduction
      • V-B. Core
        • V-B-1. short_open_tag
        • V-B-2. output_buffering
        • V-B-3. safe_mode
        • V-B-4. disable_*
        • V-B-5. max_*_time
        • V-B-6. memory_limit
        • V-B-7. error_reporting
        • V-B-8. display_errors
        • V-B-9. display_startup_errors
        • V-B-10. log_errors
        • V-B-11. error_prepend_string
        • V-B-12. error_log
        • V-B-13. register_globals
        • V-B-14. post_max_size
        • V-B-15. magic_quotes_*
        • V-B-16. default_mimetype
        • V-B-17. default_charset
        • V-B-18. include_path
        • V-B-19. extension_dir
        • V-B-20. enable_dl
        • V-B-21. upload_max_filesize
        • V-B-22. allow_url_*
      • V-C. Modules
        • V-C-1. Date
        • V-C-2. mail function
        • V-C-3. Session
        • V-C-4. Tidy
      • V-D. Extensions
  • Page 6
    • VI. Concepts fondamentaux
      • VI-A. Fonctionnement d'un script
        • VI-A-1. Introduction
        • VI-A-2. Contrôle de l'exécution
          • VI-A-2-a. Arrêt du script
          • VI-A-2-b. Contrôle d'erreurs
        • VI-A-3. Contrôle du flux de sortie
      • VI-B. Structure d'un script
      • VI-C. Inclure un script dans un autre script
        • VI-C-1. Introduction
        • VI-C-2. Les instructions include, include_once, require et require_once
        • VI-C-3. Chargement automatique de classes (inclusion implicite)
        • VI-C-4. Dangers
        • VI-C-5. Bonnes pratiques
      • VI-D. Sécurité au niveau du script
        • VI-D-1. Introduction
        • VI-D-2. Validation des données
        • VI-D-3. Filtrage des données
        • VI-D-4. Utilisation des données
        • VI-D-5. Dangers
        • VI-D-6. Bonnes pratiques
      • VI-E. En-têtes HTTP (headers)
        • VI-E-1. Introduction
        • VI-E-2. Quand faut-il envoyer les en-têtes ?
        • VI-E-3. Dangers
        • VI-E-4. Bonnes pratiques
      • VI-F. Liens, URLs et paramètres GET
        • VI-F-1. Introduction
        • VI-F-2. Utilisation
        • VI-F-3. Construire une bonne URL
        • VI-F-4. Construire un bon lien (balise HTML)
        • VI-F-5. Dangers
        • VI-F-6. Bonnes pratiques
      • VI-G. Encodage des caractères
        • VI-G-1. Introduction
        • VI-G-2. Encodage du script : système de fichiers
        • VI-G-3. Encodage du document : entités HTML
        • VI-G-4. Encodage d'URL : la RFC 3986
        • VI-G-5. Entités HTML + RFC 3986
        • VI-G-6. Exemple d'encodage UTF-8
        • VI-G-7. Exemple d'encodage ISO
        • VI-G-8. Exemple de caractères UTF-8 encodés en ISO
        • VI-G-9. Exemple de caractères ISO encodés en UTF-8
        • VI-G-10. Espace occupé par l'encodage
        • VI-G-11. Dangers
        • VI-G-12. Bonnes pratiques
        • VI-G-13. Le module iconv
        • VI-G-14. L'extension mbstring
        • VI-G-15. Expressions régulières (PCRE) et Unicode
  • Page 7
    • VII. Manipulation de données
      • VII-A. Bases de données relationnelles
        • VII-A-1. Introduction
        • VII-A-2. Accès aux données
        • VII-A-3. Performances
        • VII-A-4. Bonnes pratiques
      • VII-B. Fichiers XML
        • VII-B-1. Introduction
        • VII-B-2. Lecture : SimpleXML
        • VII-B-3. Écriture : DOM
      • VII-C. Services Web
        • VII-C-1. Introduction
        • VII-C-2. SOAP (anciennement "Simple Object Access protocol")
        • VII-C-3. Remote Procedure Calls (RPC)
        • VII-C-4. Service-oriented architecture (SOA)
        • VII-C-5. REpresentational State Transfer (REST)
        • VII-C-6. Conclusion
      • VII-D. Autres formats : PDF, ZIP, Flash, images, etc.
  • Page 8
    • VIII. Exemples d'application
      • VIII-A. Introduction
      • VIII-B. Application simple
        • VIII-B-1. Introduction
        • VIII-B-2. Les scripts
        • VIII-B-3. Avantages
        • VIII-B-4. Inconvénients
      • VIII-C. Inclusions
        • VIII-C-1. Introduction
        • VIII-C-2. Les scripts
        • VIII-C-3. Nouveaux avantages
        • VIII-C-4. Nouveaux inconvénients
      • VIII-D. Sans l'extension ".php"
        • VIII-D-1. Introduction
        • VIII-D-2. Les scripts modifiés
        • VIII-D-3. Nouveaux avantages
        • VIII-D-4. Nouveaux inconvénients
      • VIII-E. Modèles (classes pour la BDD)
        • VIII-E-1. Introduction
        • VIII-E-2. Les scripts
        • VIII-E-3. Nouveaux avantages
        • VIII-E-4. Nouveaux inconvénients
      • VIII-F. URL Rewriting, ou Routage
        • VIII-F-1. Introduction
        • VIII-F-2. Les scripts
        • VIII-F-3. Nouveaux avantages
        • VIII-F-4. Nouveaux inconvénients
      • VIII-G. Design pattern MVC (modèle-vue-contrôleur)
        • VIII-G-1. Introduction
        • VIII-G-2. Tous les scripts
        • VIII-G-3. Conclusion
  • Page 9
    • IX. Démarche qualité
      • IX-A. Introduction
      • IX-B. Environnements
        • IX-B-1. Introduction
        • IX-B-2. Serveur "dev"
        • IX-B-3. Serveur "staging/test"
        • IX-B-4. Serveur "production/live"
        • IX-B-5. Conclusion
      • IX-C. Tests
        • IX-C-1. Introduction
        • IX-C-2. Tests unitaires
        • IX-C-3. Test-driven development (TDD)
        • IX-C-4. Conclusion
      • IX-D. Débogage
        • IX-D-1. Introduction
        • IX-D-2. Xdebug
      • IX-E. Motifs de conception (design patterns)
        • IX-E-1. Introduction
        • IX-E-2. Pattern MVC
        • IX-E-3. Pattern Singleton
        • IX-E-4. Conclusion
      • IX-F. Frameworks
        • IX-F-1. Introduction
        • IX-F-2. CakePHP
        • IX-F-3. eZ Components
        • IX-F-4. PEAR
        • IX-F-5. symfony
        • IX-F-6. Zend Framework
        • IX-F-7. Conclusion
  • Page 10
    • X. Optimisation
      • X-A. Optimiser un script PHP
      • X-B. Mise en cache
      • X-C. Compiler un script PHP
      • X-D. Compiler PHP
      • X-E. Développer une extension
  • Page 11
    • XI. Aider la communauté
      • XI-A. Introduction
      • XI-B. Détection et correction de bugs
      • XI-C. Tester le code source de PHP
      • XI-D. Documentation
      • XI-E. Éducation
  • Page 12
    • XII. Conclusion
      • XII-A. Épilogue
      • XII-B. Liens

Ce cours vous apprendra à vous initier à tous les aspects du langage PHP, depuis la syntaxe jusqu'aux meilleures techniques de programmation. De nombreux exemples facilitent la compréhension par l'expérience.

Nous verrons également d'autres aspects comme les méthodes de Test d'applications ainsi qu'une vision globale de la communauté PHP.

20 commentaires Donner une note à l'article (5)

Lire l'article.

Article lu 132834 fois.

L'auteur

Guillaume Rossolini Site personnel Blog

L'article

Publié le 12 mai 2008 - Mis à jour le 24 avril 2010

Version en ligne

Version PDF Version hors-ligne

ePub , Azw et Mobi

Lire l'article


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/?page=exemples

II. Exemples de scripts (mise en jambe)

II-A. Exécution des scripts d'exemple

Dans mes exemples, le logiciel serveur utilisé est Zend Core ; le chemin local de DOCUMENT_ROOT est : "C:\Web\online\http".

PHP permet d'exécuter des scripts de deux manières principales : en tant que code incrusté dans une page Web ou bien comme un script système. Je vous recommande pour commencer d'utiliser les lignes de commande (MS-DOS, shell, etc .) pour lancer vos premiers scripts, car cela vous évite d'avoir à installer Apache httpd, c'est donc a priori plus simple.

Pour exécuter un script PHP depuis les lignes de commande, il faut invoquer le binaire "php" en lui donnant en paramètre le nom du script.

Sélectionnez
cd "C:\Program Files\Zend\Core\bin"
php "C:\Web\online\http\tests\error.php"

Si vous avez déjà un serveur Web configuré avec PHP, par exemple en ayant installé l'un des outils "tout-en-un" mentionnés auparavant, alors vous pouvez charger l'URL suivante dans votre navigateur : http://localhost/tests/error.php en supposant que votre script se situe ici : {DOCUMENT_ROOT}/tests/error.php

Pour afficher le contenu des variables, j'utiliserai principalement les fonctions echo et print_r(). S'il s'agit de contenus complexes (tableaux ou objets), en lignes de commandes l'affichage ne posera pas de problème ; en revanche, dans une page Web vous pourriez avoir des surprises, il vous faudra donc utiliser la balise HTML <pre> pour forcer la mise en page fixe.

En CLI :
Sélectionnez
<?php
print_r(array('début', 'milieu', 'fin'));
?>
En page Web :
Sélectionnez
<pre>
<?php
print_r(array('début', 'milieu', 'fin'));
?>
</pre>

II-B. Affichage

Commençons par un exemple, cela nous permettra de nous immerger plus facilement dans la théorie.

index.php
Sélectionnez
<?php
echo "Hello world!";
?>

Vous pouvez voir certaines similitudes avec le HTML, par exemple l'utilisation d'une balise de début "<?php" et d'une balise de fin "?>". Toute portion de code PHP doit donc être placée entre une balise d'ouverture et une balise de fermeture.

Si votre code n'est pas correctement écrit, PHP arrête l'exécution du script en lançant une erreur :

index.php
Sélectionnez
1.
2.
3.
<?php
echo;
?>
Sélectionnez
Parse error: syntax error, unexpected ';' in C:\Web\online\http\tests\error.php on line 2

L'erreur donne de très nombreuses informations permettant de résoudre le problème. Ici par exemple, on nous informe qu'une erreur de syntaxe se trouve près d'un point virgule, et que ce point virgule n'est pas ce qui était attendu. En effet, "echo" sert à afficher quelque chose, il manque donc le nombre ou le texte à afficher.

Si vous regardez dans le fichier de log de votre serveur Web, vous remarquerez sans doute que cette erreur s'y trouve également :
Sun Nov 25 17:21:08 2007] [error] [client 127.0.0.1] PHP Parse error: syntax error, unexpected ';' in C:\\Web\\online\\http\\tests\\error.php on line 3, referer: http://localhost/tests/

Nous reviendrons plus tard sur ces détails, mais il est important de savoir dès à présent que les erreurs du code sont faciles à retrouver. Votre éditeur de code peut lui aussi (sans doute) vous aider à ce sujet.

L'affichage n'est pas ce qui fait l'intérêt d'un langage de script par rapport au HTML standard, néanmoins c'est un aspect essentiel. PHP permet d'afficher à l'aide des mots clefs "echo" ou bien "print", selon le choix du développeur.

II-C. Boucles

Les boucles sont l'un des attraits des langages de script, car c'est quelque chose que le HTML ne peut pas faire.

Afficher les chiffres de 1 à 1000
Sélectionnez
<?php
for($i=0; $i<1000; ++$i)
{
    echo $i;
}

Il suffit de mettre n'importe quelle suite d'actions à la place du "echo $i". L'une des applications classiques d'une boucle est d'afficher certains éléments extraits d'une base de données.


Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/?page=syntaxe

III. Syntaxe du langage PHP

Afin de réduire la quantité de code pouvant parasiter la compréhension, dans cette page j'utiliserai principalement des scripts CLI.

III-A. Préambule

Cette partie est nécessaire mais tout lire assidûment dès le départ n'est peut-être pas la meilleure méthode pour apprendre PHP. Pour votre lecture, je vous propose de lire rapidement la syntaxe dans un premier temps, et de revenir à ces paragraphes au fur et à mesure que d'autres concepts apparaissent ou que vous avez des doutes syntaxiques.

Un bloc de code PHP est délimité par une balise d'ouverture <?php et une balise de fermeture ?>.

Un bloc de code PHP peut être le seul bloc de code dans le fichier, ou bien être mélangé à d'autres langages (par exemple HTML ou XML).

Création d'image (PHP pur) :
Sélectionnez
<?php
$image = imagecreatetruecolor(100, 50);
$text_color = imagecolorallocate($image, 255, 0, 0);
imagestring($image, 1, 5, 5, 'Hello, world!', $text_color);
header('Content-Type: image/png'); //type MIME
imagepng($image);
PHP incrusté dans du HTML :
Sélectionnez
<?php
/*
  Récupération des variables $title, $charset et $body
  par exemple depuis un formulaire ou une BDD
*/
header('Content-Type: text/html; charset='.$charset); //type MIME
?>
<?xml version="1.0" encoding="<?php echo $charset; ?>"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title><?php echo $title; ?></title>
    <meta http-equiv="content-Type" content="text/html; charset=<?php echo $charset;?>" />
</head>
<body>
<?php echo $body; ?>
</body>
</html>

La balise de fermeture de code PHP ?> n'étant pas nécessaire lorsque le script se termine (tel que dans l'exemple d'image ci-dessus), nous l'utiliserons le moins possible. Cela permet d'éviter certaines erreurs, surtout en débutant. Elle est nécessaire uniquement si l'on souhaite alterner PHP et un autre langage.

III-B. Présentation du code

Vous pouvez varier la présentation du code source (ajouter des espaces, des tabulations et des sauts de ligne) sans que cela affecte l'exécution.

Une "instruction" est une portion de code se terminant par un point virgule (;). Par convention, on la représente sur une ligne de code propre.

Sélectionnez
<?php
echo "Hello World!";
echo "Je m'appelle Guillaume Rossolini";

L'indentation du code désigne l'utilisation des espaces et des tabulations pour structurer visuellement le code en niveaux. Cela ne change aucunement le résultat de l'exécution, mais facilite la relecture du code par un collaborateur ou par soi-même. Un code très mal indenté peut induire le programmeur en erreur et causer de grands dysfonctionnements lors d'opérations de maintenance.

Adrien Pellegrini vous propose ses conseils : Guide de style pour bien coder.

III-C. Parenthèses

Le code d'une instruction peut être groupé au moyen de parenthèses "()" afin de faciliter la lecture ou de forcer les priorités. PHP définit des priorités même s'il n'y a pas de parenthèses, mais il est parfois nécessaire de modifier les priorités imposées par le langage. Le principe est exactement le même qu'en mathématiques :

Sélectionnez
<?php
echo 1+2*3; //affiche "7"
echo (1+2)*3; //affiche "9"

Utilisez les parenthèses pour clarifier l'ordre dans lequel les opérations doivent avoir lieu, mais ne surchargez pas votre code pour autant. Ne mettre aucune parenthèse peut porter à confusion (tout le monde ne connaît pas par coeur la priorité de chacun des opérateurs) mais trop de parenthèses rend le code confus. Il faut donc trouver un équilibre.

III-D. Accolades

Les blocs d'instructions (une à plusieurs instructions) peuvent être identifiés par des accolades "{}". Dans d'autres langages, cela permet de limiter la portée (scope) d'une variable, mais en PHP cet effet n'est pas pris en compte.

En revanche, les accolades définissent la portée d'une structure de contrôle (cf. plus loin) et les limites du corps d'une fonction.

Sélectionnez
<?php
function additionner($x, $y)
{
    //nous sommes dans le corps de la fonction "additionner"
    return $x + $y;
}

//ici, nous ne sommes plus dans le corps de la fonction "additionner"

Les accolades sont facultatives dans certaines situations (par exemple s'il n'y a qu'une seule instruction dans un "if") mais je vous recommande de systématiquement les mettre, car cela ne prend pas beaucoup de temps alors que la valeur ajoutée est très grande (lisibilité du code).

III-E. Commentaires

Les commentaires en PHP permettent d'introduire des sections de texte qui ne sont pas exécutées. L'utilisation principale est pour la maintenance, l'évolution du code, afin de permettre au développeur d'introduire des commentaires explicites sur ses intentions.

Ils prennent généralement deux formes :

Sélectionnez
<?php
//Commentaire sur une ligne
Sélectionnez
<?php
/*
Commentaire sur plusieurs lignes
*/

Il existe également une forme avec un caractère dièse (#) au lieu du double slash (//), mais cette syntaxe n'est pas conseillée car ce type de commentaire est moins courant parmi les langages de programmation. Sachez simplement qu'elle existe afin de ne pas vous étonner de la voir des scripts écrits par d'autres personnes.

Le commentaire // est parfois utilisé en fin d'instruction pour expliquer son utilité.
Le commentaire /* */ est extensivement utilisé par les générateurs de documentation automatique de code.

Sélectionnez
<?php

/**
 * Chien
 *
 * @author Yogui
 * @copyright Guillaume Rossolini (c) 2007
 * @version 8.12.2007
 * @access public
 */
class Chien
{
    private $âge; //en années

    /**
     * Chien::__construct()
     *
     * @param int $âge
     */
    public function __construct($âge)
    {
        $this->âge = (int)$âge;
    }
}

III-F. Opérateurs

III-F-1. Arithmétique (+ - * / %)

PHP dispose des opérateurs classiques pour effectuer des calculs :

Addition
Sélectionnez
<?php
echo 1+1; //addition
echo 1-1; //soustraction
echo 1*1; //multiplication
echo 1/1; //division
echo 1%1; //modulo (reste de la division)

III-F-2. Affectation de variable (= += -= *= /= .=)

Les opérateurs d'affectation permettent de donner une valeur à une variable.

Valeurs numériques :
Sélectionnez
<?php
$x = 5;
$x += 1; //ajoute à la valeur existante
$x -= 2; //soustrait à la valeur existante
$x *= 3; //multiplie la valeur existante
$x /= 4; //divise la valeur existante
echo $x; //affiche "3"
Chaînes :
Sélectionnez
<?php
$str = 'texte';
$str .= ' additionnel'; //concatène à la suite de la chaîne existante
echo $str; //affiche "texte additionnel"

III-F-3. Comparaison (== === != <> !== > >= < <= instanceof)

L'opérateur "==" est un opérateur de comparaison de valeurs. Il ne tient pas compte du type de la valeur, puisque PHP est un langage à typage faible. L'opérateur "!=" est l'inverse de "==".

Sélectionnez
<?php
echo 1==1; //affiche "1"
echo 1==2; //n'affiche rien puisque c'est faux
echo 1==1.0; //affiche "1" puisque ce sont deux valeurs entières équivalentes
echo '1'==1.0; //affiche "1" puisque la chaîne "1" est équivalente à l'entier numérique "1"
echo '1'=='1.0'; //affiche "1" puisque leur valeur numérique évalue à la même valeur entière

L'opérateur "===" est le même que le précédent, sauf qu'il n'effectue pas de conversion de type. Il est donc plus rapide que l'opérateur "==" et il ne donne pas les mêmes résultats. L'opérateur "!==" est l'inverse de "===".

Sélectionnez
<?php
echo 1===1; //affiche "1" puisque les deux valeurs sont identiques en valeur et en type
echo 1===2; //n'affiche rien puisque c'est faux
echo 1===1.0; //n'affiche rien puisque le type diffère (int et float)
echo '1'===1.0; //n'affiche rien puisque le type diffère (string et float)
echo '1'==='1.0'; //n'affiche rien puisque les deux chaînes ne sont pas égales
Voir également :
  • Récapitulatif sur la comparaison de types à l'aide de l'opérateur ==
  • Récapitulatif sur la comparaison de types à l'aide de l'opérateur ===
  • Récapitulatif sur la comparaison de types à l'aide des fonctions PHP prédéfinies

Les opérateurs "<>" et "!=" sont identiques, ils permettent à des développeurs issus de différents horizons de s'adapter sans problème à PHP. Ils fonctionnent aussi bien pour les nombres que pour les chaînes.

Les opérateurs "<", ">", "<=" et ">=" sont assez classiques pour ne pas nécessiter d'explications. Ils fonctionnent aussi bien pour les nombres que pour les chaînes.

L'opérateur instanceof permet de comparer les classes :

Sélectionnez
<?php
if($objet instanceof MaClasse) //attention, ne pas mettre le nom de classe entre guillemets
{
    echo 'même classe ou classe fille';
}

if($objet_1 instanceof $object_2)
{
    echo 'même classe ou classe fille';
}

L'opérateur instanceof fonctionne également avec les classes héritées et avec les interfaces.

III-F-4. Condition (?)

L'opérateur ternaire "?" est une alternative : "est-ce vrai ? valeur si oui : valeur sinon".

Sélectionnez
<?php
echo 1==1 ? 'vrai' : 'faux'; //affiche "vrai"
echo 1==2 ? 'vrai' : 'faux'; //affiche "faux"

On utilise souvent les parenthèses pour faciliter la lecture de cet opérateur.

Sélectionnez
<?php
echo (1==1) ? 'vrai' : 'faux';
echo (1==2) ? 'vrai' : 'faux';

Cet opérateur est plus lent à l'exécution qu'une structure conditionnelle if/else (cf. nos benchmarksTests et benchmarks en PHP 5). De plus, il peut rendre le code illisible s'il est utilisé trop souvent ou dans des instructions trop longues. Il est donc à utiliser avec précaution, et exclusivement dans des instructions très courtes (notamment sans appel de fonction).

Cet opérateur peut, sous certaines conditions, être utilisé pour affecter des variables. L'exemple qui suit montre un exemple d'affectation (sécurisée et sans erreur) de la variable $id :

Sélectionnez
<?php
//la valeur entière de $_GET['id'] ou zéro si la variable n'existe pas
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;

Depuis PHP 5.3 et 6.0, il existe une alternative abrégée :

Sélectionnez
<?php
$id = $_GET['id'] ?: 0; //$_GET['id'] ou zéro si la variable est vide

Le problème de cette dernière approche est que dans de très nombreuses situations, elle met le script en danger. Dans l'exemple ci-dessus par exemple, nous n'effectuons aucune vérification sur le type ou le contenu de la variable... Il convient donc d'utiliser la version abrégée de l'opérateur ternaire uniquement dans des situations parfaitement maîtrisées. De plus, écrire par exemple "echo isset($_GET['id']) ?: 0;" n'aurait aucun sens, ce qui peut facilement causer des alertes E_NOTICE.

III-F-5. Incrémentation / diminution (++ --)

  • ++ : Augmenter de 1 ;
  • -- : Diminuer de 1.
Sélectionnez
<?php
$x = 5;
echo ++$x; //incrémente puis affiche 6
echo $x; //affiche 6

$x = 5;
echo $x++; //affiche 5 puis incrémente
echo $x; //affiche 6

$x = 5;
echo --$x; //réduit puis affiche 4
echo $x; //affiche 4

$x = 5;
echo $x--; //affiche 5 puis réduit
echo $x; //affiche 4

Ces opérateurs fonctionnent également avec les chaînes.

III-F-6. Arithmétique logique, aka opérateurs de bits (& |)

Ces opérateurs permettent d'effectuer des opérations de bits, très utilisées par exemple pour les variables de configuration. Les opérations logiques sont bien plus rapides que les opérations décimales, il peut donc dans certains cas être intéressant de les utiliser.

Les opérateurs ayant un équivalent ensembliste :
  • & (and) : Équivalent à une intersection d'ensembles (les bits à 1 dans les deux ensembles donnent 1, les autres donnent 0) ;
  • | (or) : Équivalent à une union d'ensembles ("ou" inclusif : les bits à 1 dans au moins l'un des ensembles donnent 1, les autres donnent 0) ;
  • ^ (xor) : Équivalent à une union d'ensembles ("ou" exclusif : les bits à 1 dans exactement l'un des ensembles donnent 1, les autres donnent 0) ;
Sélectionnez
<?php
echo 0&0; //affiche "0"
echo 0&1; //affiche "0"
echo 1&1; //affiche "1"

echo 0|0; //affiche "0"
echo 0|1; //affiche "1"
echo 1|1; //affiche "1"

echo 0^0; //affiche "0"
echo 0^1; //affiche "1"
echo 1^1; //affiche "0"
Sélectionnez
<?php
echo 1&5; //affiche "1"
echo 1|5; //affiche "5"
echo 1^5; //affiche "4"

Si on calcule l'union des équivalents binaires de 1 et 4 (soit "un ou quatre inclusif"), on obtient l'équivalent de 5 :

Sélectionnez
001
100 |
101 =
Sélectionnez
<?php
echo 1|4; //affiche "5"

L'union de 4 et 5 vaut également 5 :

Sélectionnez
100
101 |
101 =
Sélectionnez
<?php
echo 4|5; //affiche "5"

En revanche, par la différence logique de 1 et 4 (soit "un et quatre"), on obtient 0 :

Sélectionnez
001
100 &
000 =
Sélectionnez
<?php
echo 1&4; //affiche "0"

Mais l'intersection de 4 et 5 vaut 4 :

Sélectionnez
100
101 &
100 =
Sélectionnez
<?php
echo 4&5; //affiche "4"

C'est par exemple cette méthode qui est utilisée pour le niveau d'erreurs géré par PHP (directive error_reporting du fichier php.ini).

Les autres opérateurs de bits :
  • ~ $x : Inversion du positionnement des bits dans $x (not) ;
  • $x << $n : $x est multiplié $n fois par 2 (décalage à gauche) ;
  • $x >> $n : $x est divisé $n fois par 2 (décalage à droite).

Une manière optimisée d'effectuer des divisions ou des multiplications par 2 est d'utiliser le décalage de bits. Cependant, cela ne peut fonctionner que sous certaines conditions, par exemple se limiter à des nombres de 32 bits pour les systèmes 32 bits.

III-F-7. Comparaison logique (&& || and or)

Comme dans tout langage de programmation, ces opérateurs permettent de vérifier plusieurs conditions à la fois dans un même test. On peut écrire les opérateurs "and" et "or" en minuscules ou en majuscules.

Sélectionnez
<?php
if($variable > 2 and $variable < 10)
{
    echo 'valeur entre 2 et 10 (exclus)';
}
Sélectionnez
<?php
if($variable > 2 && $variable < 10)
{
    echo 'valeur entre 2 et 10 (exclus)';
}
Sélectionnez
<?php
if($variable > 2 or $variable < 10)
{
    echo 'valeur supérieure à 2 ou inférieure à 10 (exclus)';
}
Sélectionnez
<?php
if($variable > 2 || $variable < 10)
{
    echo 'valeur supérieure à 2 ou inférieure à 10 (exclus)';
}
  • "&&" et "and" sont identiques sauf pour la priorité qui leur est attribuée ;
  • "||" et "or" sont identiques sauf pour la priorité qui leur est attribuée.

Ma recommandation est d'utiliser "and" et "or" (au détriment de && et ||) puisqu'ils portent la sémantique en eux et qu'ils ne prêtent pas à confusion avec les opérateurs de bits.

Il s'agit d'opérateurs dits "paresseux" : avec l'opérateur "or", une seule condition vraie dans la liste permet d'avérer l'ensemble de l'instruction ; avec "and", une seule condition fausse dans la liste permet de refuser l'ensemble de l'instruction.

Démonstration de leurs priorités respectives :
Sélectionnez
<?php
//différence entre "&&" et "and"
var_dump(0 and 0 || 1); // FALSE car équivalent à : 0 and (0 or 1)
var_dump(0 && 0 || 1); // TRUE car équivalent à : (0 and 0) or 1

//différence entre "||" et "or"
var_dump(1 or 0 and 0); // TRUE car équivalent à : 1 or (0 and 0)
var_dump(1 || 0 and 0); // FALSE car équivalent à : (1 or 0) and 0

III-F-8. Opérateurs de tableaux (+ == === <> != !==)

C'est le même principe que les opérateurs antérieurs, mais avec les tableaux :
  • "+" : Union de tableaux ;
  • "==" : Les mêmes paires clef/valeur ;
  • "===" : Les mêmes paires clef/valeur, dans le même ordre et de même type ;
  • "<>" ou != : Au moins une paire clef/valeur ne correspond pas ;
  • !== : Au moins un triplet clef/valeur/type ne correspond pas.

III-F-9. Opérateurs spéciaux (@ ``)

L'arobase @ sert à contrôler l'affichage des erreurs. L'utiliser est considéré comme une mauvaise pratique, car cela suppose que votre code n'est pas sécurisé, qu'il ne fait pas les contrôles nécessaires. Par ailleurs, cet opérateur empêche PHP d'enregistrer l'erreur dans un fichier de log (errors.log), ce qui vous oblige à enregistrer vous-même l'erreur dans un log, ce qui finalement ne fait pas gagner de temps et ne simplifie pas le code. De plus, il faut activer une certaine directive de configuration pour avoir accès aux messages d'erreur si on utilise cet opérateur.

Rappel : Vous devriez toujours garder un log des erreurs. Consultez le log de temps en temps pour améliorer la sécurité de votre serveur et de vos applications.

L'opérateur "apostrophe arrière `" (ou "accent grave") permet d'exécuter une commande système (shell). Utiliser cet opérateur revient à utiliser la fonction shell_exec(), qui est moins intéressante que la fonction exec() de par le manque de paramètres.

III-G. Types

III-G-1. Préambule

Dès l'école primaire, on nous apprend à ne pas additionner des choux avec des carottes lorsque l'on apprend les "unités" en mathématiques. Les "types" sont exactement la même chose en programmation, ils ne sont pas nécessairement compatibles entre eux. Il existe cependant des moyens de convertir un type en un autre, tout comme on peut convertir des grammes en kilogrammes.

Un type définit une valeur, il permet de savoir comment on doit la traiter. PHP ne doit pas, ne peut pas traiter de la même manière un nombre et du texte, tout comme des litres et des grammes ne peuvent pas être utilisés de la même manière. Par exemple, additionner deux nombres est une opération mathématique, tandis qu'additionner des ensembles de valeurs revient à trouver leur union (ensemble des deux). La même opération ne doit pas être effectuée de la même façon sur deux types différents.

Les types sont définis à la fois pour aider le programmeur à connaître les données qu'il manipule (sécurité) et pour optimiser la gestion de la mémoire par l'interpréteur PHP. Il convient donc de connaître les différents types disponibles en PHP. Nous reviendrons plus loin sur leurs utilisations.

Les types PHP sont :
  • boolean : un contraste "vrai" ou bien "faux", "blanc" ou "noir", "1" ou "0", "yin" ou "yang"... ;
  • integer : une valeur numérique entière ;
  • double : une valeur numérique flottante (à virgule) ;
  • string : une chaîne de caractères (texte) ;
  • array : un tableau (ensemble de valeurs) ;
  • object : un objet (instance de classe) ;
  • resource : une ressource (type abstrait, inutilisable par le programmeur, utilisé uniquement pour des fonctions) ;
  • NULL : un type spécial qui désigne l'absence de valeur.

PHP est un langage à types dynamiques. Cela signifie que l'on ne peut pas définir de manière fixe le type d'une variable pour toute la durée de l'exécution du script ; au contraire, on parle de type juggling, à savoir qu'une variable peut changer de type selon les besoins (cf. exemples plus loin).

III-G-2. Type chaîne de caractères (string)

La chaîne de caractères est le moyen classique pour désigner du texte en programmation.

Dans de nombreux langages de programmation (ainsi qu'en bases de données), il faut déclarer la longueur de la chaîne (nombre de caractères dans le texte) avant d'affecter du texte à une variable. PHP reste cependant très flexible dans la gestion de la mémoire, cette déclaration n'est donc ni utile ni possible ici.

Une chaîne de caractères peut s'écrire de diverses manières en PHP, chacune utilisant un "délimiteur" bien précis :

Sélectionnez
<?php
//Délimitation par des guillemets :
echo "Hello World!";

//Délimitation par des apostrophes :
echo 'Hello World!';

//Délimitation par la syntaxe HereDoc :
$string = <<<END
Hello World!
END;
echo $string;

//Délimitation par la syntaxe NowDocs :
$string = <<<'END'
Hello World!
END;
echo $string;

//Caractère $ avec la syntaxe HereDoc :
$string = <<<END
Le signe \$ doit être échappé : \$var
END;
echo $string;

//Caractère $ avec la syntaxe NowDocs :
$string = <<<'END'
Le signe $ peut être utilisé : $var
END;
echo $string;

Les deux premières formes sont les plus communes. La 3° (HereDoc) est très largement moins utilisée à cause de sa complexité, ce qui est dommage car elle offre certains avantages. La 4° (NowDocs) est encore en discussion pour PHP 5.3.

La syntaxe des guillemets permet d'utiliser sans crainte les apostrophes, mais tout se complique dès que l'on souhaite utiliser des guillemets :

Sélectionnez
<?php
echo "Voici un exemple d'apostrophe";
echo "Voici un exemple de \"guillemets\"";

La syntaxe des apostrophes permet d'utiliser des guillemets dans le texte, mais nous ennuie avec les apostrophes :

Sélectionnez
<?php
echo 'Voici un exemple de "guillemets"';
echo 'Voici un exemple d\'apostrophe';

Si l'on souhaite pouvoir utiliser à la fois des guillemets et des apostrophes dans un même texte, plusieurs solutions s'offrent à nous :

Par échappement :
Sélectionnez
<?php
echo "Voici un exemple d'apostrophe suivi de \"guillemets\"";
Par échappement :
Sélectionnez
<?php
echo 'Voici un exemple d\'apostrophe suivi de "guillemets"';
HereDoc :
Sélectionnez
<?php
echo <<<EOT
Voici un exemple d'apostrophe suivi de "guillemets"
EOT;
Concaténation :
Sélectionnez
<?php
echo "Voici un exemple d'apostrophe" . ' suivi de "guillemets"';

Charge à chaque développeur de voir la syntaxe qui l'intéresse le plus pour déclarer une chaîne.

Les différences entre les délimiteurs de chaînes (guillements ou apostrophes) a été évoquée plus en profondeur par Pierre-Baptiste Naigeon : Apostrophes ou guillemets : lesquels choisir ?

Le point permet de concaténer des chaînes, c'est-à-dire de les ajouter les unes à la suite des autres. L'opérateur de concaténation n'est disponible que pour les chaînes de caractères, il ne l'est pour aucun autre type. Bien entendu, un point situé à l'intérieur des délimiteurs n'est pas un opérateur de concaténation mais un simple point, il faut qu'il soit positionné entre deux chaînes pour qu'il joue le rôle d'opérateur.

Il existe également une syntaxe à base d'accolades pour accéder aux caractères d'une chaîne : echo $string{8}; affiche le caractère de $string à la position 8 (c'est-à-dire le 9° caractère).

Sélectionnez
<?php
$user = 'Yogui';
echo $user; //affiche "Yogui"
echo $user[2]; //affiche "g" : gestion de la variable comme d'un tableau de caractères
echo $user{2}; //affiche "g" : gestion de la variable comme d'une chaîne (string)
echo substr($user, 2, 1); //affiche "g"

Les chaînes doivent toujours être traitées de manière spécifique, au cas par cas. Par exemple lors de l'affichage dans une page Web, il faut leur appliquer htmlentities() avant echo. Quelle que soit la destination de la chaîne, prenez soin de toujours appliquer la fonction de conversion adaptée.

III-G-3. Type numérique (int, float)

En PHP, il n'y a pas toujours de distinction entre "entier" et "flottant". Plus précisément, PHP effectue des changements de type selon la nécessité :

Test de types :
Sélectionnez
<?php
echo 1.2;
echo 1.0;
Sélectionnez
1.2
1

Cependant, le type est conservé :

Test de types :
Sélectionnez
<?php
var_dump(1.2);
var_dump(1.0);
var_dump(1);
Sélectionnez
float(1.2)
float(1)
int(1)

Il existe quelques subtilités au sujet des nombres à virgule flottante. L'article suivant permet d'éclaircir certains détails : http://docs.sun.com/source/806-3568/ncg_goldberg.html

III-G-4. Types spéciaux (null resource object)

  • NULL : Valeur/variable vide ou inexistante ;
  • resource : Par exemple une variable permettant d'identifier une connexion à une base de données ;
  • object : Utilisé en Programmation Orientée Objet (POO, cf. plus loin).
Ressource fichier :
Sélectionnez
<?php
$file = fopen(__FILE__, 'r');
echo $file;
var_dump($file);
fclose($file);
Sélectionnez
Resource id #3
resource(3) of type (stream)
Ressource objet :
Sélectionnez
<?php
var_dump(new stdClass());
Sélectionnez
object(stdClass)#1 (0) { }

III-G-5. Fonctions utiles

La fonction var_dump() affiche le type d'une variable et son contenu (ainsi que sa taille si c'est une chaîne). Elle s'applique aussi bien aux variables scalaires qu'aux objets, tableaux, ressources...

Texte :
Sélectionnez
<?php
var_dump("Voici du texte...");
Sélectionnez
string(17) "Voici du texte..."
Flottant :
Sélectionnez
<?php
var_dump(1.2);
Sélectionnez
float(1.2)
Flottant assimilable à un entier :
Sélectionnez
<?php
var_dump(1.0);
Sélectionnez
float(1)
Entier :
Sélectionnez
<?php
var_dump(1);
Sélectionnez
int(1)

La fonction gettype() permet de déterminer le type d'une valeur, ou get_resource_type() si c'est une ressource. Il existe également une fonction is_*() spécifique pour chaque type.

Déterminer le type :
Sélectionnez
<?php
echo gettype($variable); //affiche le type de la variable
echo get_resource_type($variable); //affiche le type de la ressource
Valeurs de retour possibles pour gettype() :
  • boolean
  • integer
  • double
  • string
  • array
  • object
  • resource
  • NULL

La fonction settype() permet de modifier le type d'une variable pendant l'exécution du programme. Des fonctions spécifiques intval(), floatval() et strval() permettent d'effectuer la même opération, et il est possible d'appliquer des "cast" à la mode du langage C.

Fonction settype() :
Sélectionnez
<?php
$x = 5.2;
var_dump($x);
settype($x, 'int');
var_dump($x);
Sélectionnez
float(5.2)
int(5)
Fonctions spécifiques :
Sélectionnez
<?php
var_dump(intval('5.2 ans'));
var_dump(floatval('5.2 ans'));
var_dump(strval('5.2 ans'));
Sélectionnez
int(5)
float(5.2)
string(7) "5.2 ans"

On appelle "transtypage" l'opération qui consiste à modifier le type d'une valeur de manière ponctuelle. Cela permet par exemple d'obtenir la valeur entière d'un nombre à virgule, ou bien au contraire de considérer comme un nombre entier comme étant un nombre à virgule.

Transtypage :
Sélectionnez
<?php
var_dump((int) 5.2);
var_dump((bool) 5.2);
var_dump((float) 5);
var_dump((string) 5.2);
var_dump((array) 5.2);
var_dump((object) 5.2);
Sélectionnez
int(5)
bool(true)
float(5)
string(3) "5.2"
array(1) {
  [0]=>
  float(5.2)
}
object(stdClass)#1 (1) {
  ["scalar"]=>
  float(5.2)
}
Valeurs possibles pour settype() :
  • bool
  • int
  • float
  • string
  • array
  • object
  • null

Les paramètres possibles de settype() et les retours possibles de gettype() sont différents pour des raisons historiques de l'évolution du langage.

III-H. Variables

III-H-1. Syntaxe

Une variable PHP est identifiée par le signe dollar ($) suivi d'une lettre puis d'une suite de lettres, chiffres et traits soulignés (_).

Par convention, un nom de variable ne commence pas par une majuscule. S'il faut plusieurs mots pour composer le nom, ils sont habituellement séparés par des soulignés (_).

Exemples :
Sélectionnez
<?php
$variable; //ok
$variable_2; //ok
$Variable; //ok mais pas conventionnel (majuscule)
$2; //nom incorrect
$2 */-+$% = 'essai'; //nom incorrect
Le nom d'une variable est sensible à la casse :
Sélectionnez
<?php
$var = 1;
$Var = 2;

// $var et $Var sont deux variables distinctes :
echo $var; //affiche "1"
echo $Var; //affiche "2"

Il existe d'autres manières de construire des noms de variables (en particulier des noms illégaux) mais, lorsqu'un tel besoin se fait sentir, il est souvent préférable d'utiliser des tableaux (nous y reviendrons par la suite).

Variable dynamique :
Sélectionnez
<?php
//exemple correct mais à éviter :
${'1 */-+$%'} = 'essai'; //ce qui est entre accolades est le nom de la variable
var_dump(${'1 */-+$%'}); //ok

//alternative à éviter aussi :
$index = '1 */-+$%';
${$index} = 'essai'; //le nom de variable correspond ici au contenu de "$index"
var_dump(${$index}); //ok

Contrairement à d'autres langages, il n'est pas nécessaire de déclarer une variable avant de pouvoir lui affecter une valeur. Cependant, prenez garde à ne pas utiliser la valeur d'une variable avant d'être certain de lui avoir effectivement donné une valeur car cela lancerait un avertissement.

Code incorrect :
Sélectionnez
<?php
echo $x; //PHP lance un avertissement puisque $x n'existe pas encore
Code ok :
Sélectionnez
<?php
$x = 3; //la première affectation sert de déclaration
echo $x;

Le type d'une variable PHP peut changer au cours de l'exécution d'un même script. Pour cela, il suffit de lui affecter une nouvelle valeur.

Exemple :
Sélectionnez
<?php
$variable = 1;
var_dump($variable);

$variable = 1.1;
var_dump($variable);

$variable = 'texte';
var_dump($variable);

$variable = new stdClass();
var_dump($variable);
Sélectionnez
int(1)
float(1.1)
string(5) "texte"
object(stdClass)#1 (0) {
}

Les variables ne sont visibles (scope) que dans le bloc dans lequel elles sont déclarées. Une variable déclarée dans une fonction, classe ou méthode n'est pas visible en-dehors de cette fonction, classe ou méthode.

III-H-2. Superglobales

Les variables superglobales sont mises en place par PHP lors du début du traitement d'une demande par Apache.

Ces variables n'obéissent pas aux limites habituelles des variables en termes de visibilité à l'intérieur d'une fonction. Elles sont accessibles de partout, c'est pourquoi elles portent le nom de "superglobales".

Voici les superglobales :
  • $_GET : Les valeurs provenant de l'URL ;
  • $_POST : Les valeurs envoyées par formulaire ;
  • $_FILE : Les fichiers envoyés par formulaire ;
  • $_SERVER : Les valeurs mises en place par le serveur Web (elles peuvent donc changer d'une configuration à l'autre) ;
  • $_ENV : Les variables d'environnement (système d'exploitation) ;
  • $_SESSION : Les valeurs mises dans le magasin des sessions ;
  • $_COOKIE : Les valeurs transmises au moyen de cookies par le navigateur ;
  • $GLOBALS : L'ensemble des variables du script.

Du point de vue de la sécurité, aucune de ces variables ne devrait être introduite telle quelle dans un script PHP, car elles sont toutes potentiellement modifiables par l'internaute. Cela signifie que l'internaute peut pirater nos scripts par l'intermédiaire de toutes ces variables, il faut donc les filtrer et les valider à chaque utilisation. Nous reviendrons plus loin sur les méthodes permettant d'y parvenir.

III-H-3. Références

Transmettre une variable en "entrée-sortie" à une fonction s'apparente à la passer par référence. En PHP5, il n'y a qu'une manière de faire :

Sélectionnez
<?php
$i = 0;
increment($i);
increment($i);
increment($i);
increment($i);

echo $i; //affiche "4"

function increment(&$nb)
{
    ++$nb;
}

Cette approche est très efficace en utilisation de mémoire, car elle évite à la fonction de recopier la valeur de ses paramètres transmis par référence. En revanche, il faut faire très attention lorsque l'on modifie ces valeurs, car les changements sont répercutés dans le scope appelant la fonction...
Nous verrons plus loin que les objets PHP5 sont systématiquement transmis par référence.

III-H-4. Fonctions utiles

L'affichage du contenu d'une variable se fait généralement au moyen de la fonction echo ou bien print, selon le choix du programmeur. Ces deux fonctions agissent presque de la même manière. Pour leur part, les fonctions var_export(), print_r() et var_dump() sont utiles principalement pour le débogage des scripts.

Affichage :
Sélectionnez
<?php
$x = 5;
echo $x; //affichage de la valeur
print $x; //affichage de la valeur
var_export($x); //affichage de la représentation PHP
print_r($x); //affichage du contenu
var_dump($x); //affichage du type et du contenu
Test d'existence :
Sélectionnez
<?php
if(isset($variable))
{
    //la variable existe
}
else
{
    //la variable n'existe pas
}
Test de nullité :
Sélectionnez
<?php
if(empty($variable))
{
    //la variable est nulle
}
else
{
    //la variable est non nulle
}
Destruction :
Sélectionnez
<?php
unset($variable);

Contrairement à echo, les constructs isset() et empty() ne lancent aucun avertissement si la variable n'existe pas.

III-H-5. Bonnes pratiques

Utilisez des noms de variable ayant du sens. Évitez les abréviations, les initiales, etc.

Évitez les noms du style "cas particulier", par exemple pour une couleur il faut éviter de décrire le contenu ($blue, $green...) et préférer la description de l'utilité de chaque variable : $row_color, $background_color... Appeler une variable en fonction de sa valeur revient à dupliquer les informations, ce qu'il faut toujours éviter de faire en programmation.

Si vous devez utiliser une syntaxe complexe pour afficher une variable, comme par exemple ce qui est dans la documentation officielle de PHP, préférez la concaténation ou l'utilisation de sprintf() :

À éviter :
Sélectionnez
echo "Ceci fonctionne : {$arr['foo'][3]}";
Ceci est préférable :
Sélectionnez
echo "Ceci fonctionne : " . $arr['foo'][3];
Meilleure solution :
Sélectionnez
echo sprintf("Ceci fonctionne : %s", $arr['foo'][3]); //préciser le type correct à la place de "%s"

III-I. Constantes

III-I-1. Syntaxe

Une constante est un nom qui permet de donner une sémantique à une valeur. Cette valeur est figée pour toute la durée de l'exécution du script PHP. Par convention, on exclut les lettres minuscules dans le nom d'une constante.

Déclaration :
Sélectionnez
<?php
define('NOM_ADMIN', 'Yogui'); //constante de type string
define('MAX_LIGNES', 5); //constante de type entier

L'utilisation des constantes est similaire à des variables. La différence est qu'il ne faut pas utiliser le signe dollar ($) car il s'applique uniquement aux variables. Par ailleurs, il ne faut pas utiliser "=" pour affecter une valeur à une constante, mais uniquement "define". Une fois que la valeur d'une constante est définie, elle ne peut plus être modifiée jusqu'à la fin du script.

Sélectionnez
<?php
define('NOM_ADMIN', 'Yogui');
echo NOM_ADMIN; //remarquez l'absence de guillemets et de signe dollar

Par convention, les noms des constantes sont écrits en lettres majuscules et en chiffres. S'il faut mettre plusieurs mots dans le même nom de constante (comme ci-dessus), on démarque chaque mot par un underscore.

Les constantes sont très utiles pour les valeurs de configuration qui ne doivent pas changer durant toute l'exécution d'un script, car elles nous assurent que la valeur ne sera modifiée à aucun moment de l'exécution.

Exemple d'utilisation incorrecte (erreur PHP) :
Sélectionnez
1.
2.
3.
<?php
define('MAX_LIGNES', 3);
define('MAX_LIGNES', 5);
Sélectionnez
Notice: Constant MAX_LIGNES already defined in C:\Web\online\http\tests\error.php on line 3

Un aspect intéressant des constantes est que, si l'on utilise une constante qui n'est pas définie, PHP envoie une alerte mais n'arrête pas le script : il utilise le nom de la supposée constante comme sa valeur. Il est évident qu'il faut éviter ce genre de situation, mais cela vaut la peine de le savoir.

Les constantes ne sont pas limitées en termes de visibilité (scope). Une fois déclarée, une constante est visible depuis toutes les fonctions et toutes les classes.

Sélectionnez
<?php

$user = 'Yogui';
define('USER', 'Yogui');

function test()
{
    echo $user; //lance un avertissement PHP "Undefined variable: user"
    echo USER; //ok
}

//lancemant de la démonstration
test();

III-I-2. Constantes magiques

Il existe des "constantes magiques" qui n'obéissent pas totalement aux règles des constantes. Elles existent dans tous les scripts sans qu'il soit nécessaire de les déclarer par programmation. On ne peut bien entendu pas les modifier par programmation, néanmoins leur valeur peut changer au fil de l'exécution du script. De plus, on peut les appeler indifféremment en majuscules ou en minuscules. Chacune de ces constantes contient une valeur qui change selon le contexte dans lequel elle est appelée.

  • __LINE__ : La ligne de code en cours ;
  • __FILE__ : Le nom complet du script en cours ;
  • __DIR__ : Le nom du répertoire du script en cours (depuis les versions 5.3 et 6.0 de PHP) ;
  • __FUNCTION__ : La fonction en cours ;
  • __CLASS__ : La classe en cours, similaire à get_class($this) ;
  • __METHOD__ : La méthode en cours ;
  • __NAMESPACE__ : L'espace de noms en cours (depuis les versions 5.3 et 6.0 de PHP).
Exemple :
Sélectionnez
<?php
echo __LINE__; //affiche "2"
echo __LINE__; //affiche "3"
echo __LINE__; //affiche "4"

III-I-3. Fonctions utiles

define() déclare une nouvelle constante :
Sélectionnez
<?php
define('MAX_LIGNES', 3);
defined() permet de savoir si une constante est déjà définie :
Sélectionnez
<?php
if(defined('MAX_LIGNES'))
{
    echo 'Constante définie';
}
else
{
    echo 'Constante non définie';
}
get_defined_constants() retourne toutes les constantes définies :
Sélectionnez
<?php
print_r(get_defined_constants());
contant() retourne la valeur d'une constante :
Sélectionnez
<?php
define('LOGIN', 'Yogui');

echo LOGIN; //ok
echo constant('LOGIN'); //identique à la ligne précédente, sauf qu'on peut aussi bien mettre une variable contenant "LOGIN"

III-I-4. Utilisation

Les constantes sont particulièrment utiles pour les valeurs de configuration. Par exemple, si vous avez besoin d'un nom d'utilisateur et d'un mot de passe pour vous connecter à une base de données, vous devriez utiliser des constantes afin d'éviter qu'une portion de votre script ne modifie ces valeurs par inadvertance (une collision de noms est si vite arrivée...).

Sélectionnez
<?php
define('DB_LOGIN', 'Yogui');
define('DB_PASSWORD', 'pass');

Si, dans la suite du script, vous souhaitez utiliser le même nom de constante, PHP vous avertira par une erreur car ce nom existe déjà. Si vous utilisiez une variable, sa valeur serait écrasée sans préavis...

III-J. Tableaux

III-J-1. Syntaxe

Un tableau est une variable contenant plusieurs valeurs. En PHP, les variables étant faiblement typées, les tableaux sont très simples à manipuler.

On accède au tableau entier en utilisant le nom de la variable, ou bien à un élément concret au moyen des crochets [ et ]. Ce qui se situe entre les crochets est appelé "index" ou encore "clef" de l'élément du tableau, et un même index est unique dans un tableau.

Affectation d'un tableau entier :
Sélectionnez
<?php
$nombres = array(3, 6, 9); //ce tableau contient trois valeurs

echo $nombres[1]; //accès direct à l'élément d'index 1, c'est-à-dire le deuxième élément
print_r($nombres); //affichage du tableau complet
Ajout de cases à un tableau vide ou existant :
Sélectionnez
<?php
$nombres = array();
$nombres[] = 3; //ajout à la position suivante = zéro
$nombres[] = 6; //ajout à la position suivante = un
$nombres[] = 9; //ajout à la position suivante = deux

var_dump($nombres[1]); //accès direct à l'élément en position 1
print_r($nombres); //affichage complet
Sélectionnez
int(6)
Array
(
    [0] => 3
    [1] => 6
    [2] => 9
)

L'indice permettant d'indexer le tableau peut être numérique ou chaîne. Si on ne spécifie rien (comme ci-dessus), ce sont des valeurs numériques successives. On peut voir ici que le premier élément d'un tableau prend l'index zéro, ce qui est tout à fait habituel dans les langages de programmation.

Affectation d'un tableau entier :
Sélectionnez
<?php
$positions = array(
    0 => 'début',
    1 => 'milieu',
    2 => 'fin'
);

echo $positions[1]; //accès direct à l'élément en position 1
print_r($positions); //affichage complet
Ajout de cases à un tableau vide ou existant :
Sélectionnez
<?php
$positions = array();
$positions[0] = 'début'; //ajout à la position zéro
$positions[1] = 'milieu'; //ajout à la position un
$positions[2] = 'fin'; //ajout à la position deux

var_dump($positions[1]); //accès direct à l'élément en position 1
print_r($positions); //affichage complet
Sélectionnez
string(6) "milieu"
Array
(
    [0] => début
    [1] => milieu
    [2] => fin
)

L'index d'un élément permet d'y accéder aussi bien en lecture qu'en modification ou suppression :

Modification d'un élément :
Sélectionnez
<?php
$nombres = array();
$nombres[] = 3; //ajout à la position suivante = zéro
$nombres[] = 6; //ajout à la position suivante = un
$nombres[] = 9; //ajout à la position suivante = deux

print_r($nombres); //affichage complet

$nombres[1] = 12; //modification de l'élément en position 1

print_r($nombres); //affichage complet
Sélectionnez
Array
(
    [0] => 3
    [1] => 6
    [2] => 9
)
Array
(
    [0] => 3
    [1] => 12
    [2] => 9
)

Puisque chaque case d'un tableau est une variable à part entière, elle peut être de n'importe quel type (nombre, chaîne, ressource, tableau, objet...), ce qui nous permet d'imbriquer les tableaux si nécessaire.

Lorsqu'un tableau est indexé par des chaînes plutôt que par des nombres, on parle de tableau associatif :

Ajout de cases à un tableau vide ou existant :
Sélectionnez
<?php
$positions = array();
$positions['zéro'] = 'début';
$positions['un'] = 'milieu';
$positions['deux'] = 'fin';

var_dump($positions['un']); //accès direct à l'élément en position (string)"un"
print_r($positions); //affichage complet
Sélectionnez
string(6) "milieu"
Array
(
    [zéro] => début
    [un] => milieu
    [deux] => fin
)

Dans le cas d'un tableau associatif, on peut utiliser n'importe quelle chaîne comme index. Il n'y a pas de restriction sur le nom des indexes comme il y en a pour le nom des variables. On peut même utiliser le contenu d'une variable pour indexer un élément d'un tableau :

Sélectionnez
<?php
$positions = array();
$positions['zéro'] = 'début';
$positions['un'] = 'milieu';
$positions['deux'] = 'fin';

$index = 'un';
echo $positions[$index]; //affiche "milieu"

Il existe plusieurs manières de parcourir un tableau :

Sélectionnez
<?php
$nombres = array(3, 6, 9);
foreach($nombres as $nombre)
{
    echo $nombre.'<br/>';
}
Sélectionnez
<?php
$nombres = array(3, 6, 9);
foreach($nombres as $i => $nombre)
{
    echo $i.' '.$nombre.'<br/>';
}
Sélectionnez
<?php
$nombres = array(3, 6, 9);
for($i=0; $i<count($nombres); ++$i)
{
    echo $i.' '.$nombres[$i].'<br/>';
}
Sélectionnez
<?php
$nombres = array(3, 6, 9);
while(list($i, $nombre) = each($nombres))
{
    echo $i.' '.$nombre.'<br/>';
}

III-J-2. Pointeur interne

Le pointeur interne d'un tableau est une valeur manipulée uniquement par PHP lui-même, en arrière-plan. C'est la position à laquelle PHP s'est arrêté lorsqu'il a utilisé le tableau pour la dernière fois (dans le cas d'un parcours séquentiel).

Nous y reviendrons par la suite, notamment lors des chapitres sur les boucles (structures de contrôle) et sur les interfaces (programmation orientée objet).

Cela permet par exemple d'avoir un tableau indexé par des numéros mais dont l'ordre des éléments ne suit pas ces numéros, ou bien de réorganiser les clefs d'un tableau associatif, etc.

III-J-3. Fonctions utiles

  • count() : Compter le nombre d'éléments d'un tableau ;
  • *sort() : Trier un tableau (nombreuses fonctions disponibles) ;
  • array_*() : cf. la documentation (nombreuses fonctions disponibles) ;
  • list() : Assigne plusieurs valeurs en une opération (habituellement depuis un tableau) ;
  • current() : Retourne l'élément du tableau désigné par son pointeur interne ;
  • reset() : Réinitialise le pointeur interne du tableau ;
  • next() : Avance le pointeur interne puis agit comme current() ;
  • prev() : Recule le pointeur interne puis agit comme current().

III-K. Structures de contrôle

III-K-1. Conditionnelle "if"

Syntaxe :
Sélectionnez
<?php
if(<expression>)
{
    <instructions>
}

La structure conditionnelle if/else est l'une des plus classiques des langages de programmation. Elle permet d'effectuer des opérations ou d'autres opérations selon certaines conditions.

Elle s'écrit avec le mot clef "if" et un paramètre qui peut être une valeur, une variable, une comparaison, bref tout type d'instruction. Si l'instruction est évaluée à TRUE, alors on rentre dans la branche ; sinon, on rentre dans la branche du "else" (qui est facultative).

Le code à exécuter (après la condition entre parenthèses) peut être une simple instruction ou bien un bloc d'instructions délimitées par des accolades. L'une des bonnes pratiques de programmation indique la préférence systématique pour les accolades.

Exemple :
Sélectionnez
<?php
if(2*4 > 5)
{
    echo 'strictement supérieur à 5';
}
else
{
    echo 'inférieur ou égal à 5';
}
Équivalent à (mais il est préférable d'éviter cette écriture) :
Sélectionnez
<?php
if(2*4 > 5) echo 'strictement supérieur à 5';
else echo 'inférieur ou égal à 5';

III-K-2. Alternative "switch"

Syntaxe :
Sélectionnez
<?php
switch(<expression>)
{
    case <valeur 1>:
        <instructions>
    break;

    case <valeur 2>:
        <instructions>
    break;

    ...

    default:
        <instructions>
}

Lorsqu'une variable ou une instruction peut prendre une valeur parmi une liste connue, alors il est préférable d'utiliser le mot clef "switch" plutôt que des "if/else".

Exemple :
Sélectionnez
<?php
switch($variable)
{
    case 1:
        echo 'Choix numéro un';
    break;

    case 2:
        echo 'Choix numéro deux';
    break;

    case 3:
        echo 'Choix numéro trois';
    break;

    default:
        echo 'Choix invalide';
}
Équivalent à (mais il est préférable d'éviter cette écriture) :
Sélectionnez
<?php
if($variable == 1)
{
    echo 'Choix numéro un';
}
else
{
    if($variable == 2)
    {
        echo 'Choix numéro deux';
    }
    else
    {
        if($variable == 3)
        {
            echo 'Choix numéro trois';
        }
        else
        {
            echo 'Choix invalide';
        }
    }
}

Le nombre de case peut varier suivant l'utilisation, cela dépend de la situation. Le mot clef "break" oblige PHP à sortir du bloc switch.

Certains langages ont des restrictions sur le case, qui ne rendent le mot clef switch utilisable qu'avec des valeurs entières. PHP étant un langage à typage faible, cette restriction ne s'applique pas et chaque case peut contenir une chaîne voire même une instruction complète.

Le mot clef break est facultatif : s'il n'est pas précisé, le case suivant est évalué à TRUE jusqu'à rencontrer la fin du switch ou bien un break. Ici par exemple, le même traitement est appliqué à "BrYs", "mathieu" et "Yogui" :

Sélectionnez
<?php
switch($membre)
{
    case 'Marc Lussac':
        echo 'Bonjour ô grand manitou';
    break;

    case 'BrYs':
    case 'mathieu':
    case 'Yogui':
        echo 'Bonjour '.$membre;
    break;

    default:
        echo 'Utilisateur invalide';
}

Le mot clef default est facultatif, mais toujours le mettre fait partie des bonnes pratiques. Il est le cas par défaut, c'est-à-dire si aucun autre cas n'a été validé. Dans l'exemple ci-dessus, default n'est exécuté que si $membre ne vaut aucun des quatre noms mentionnés.

L'instruction switch est parfois subtile, mais souvent très intéressante. Sa maîtrise vient avec la pratique, il faut donc faire des exercices en situation réelle.

Sélectionnez
<?php
$i = 4; //modifier pour tester l'effet
switch($i)
{
    case 1:
        echo 'un';
    break;

    case 2:
        echo 'deux';
    break;

    default: //valide si $i ne vaut ni 1, ni 2, ni 3
        echo $i;
    break;

    case 3:
        echo 'trois';
    break;
}

Ici, le bon sens nous souffle que le cas default est mal situé : il devrait être en dernière position car c'est le dernier recours.

III-K-3. Boucle "for"

Syntaxe :
Sélectionnez
<?php
for(<initialisation> ; <continuer tant que> ; <incrémentation>)
{
    <instructions>
}

La boucle "for" permet d'effectuer un groupe d'instructions un nombre déterminé de fois. Elle comprend trois paramètres qui sont habituellement des instructions :

Exemples :
Sélectionnez
<?php
//afficher tous les chiffres de 0 à 9
for($i=0; $i<10; ++$i)
{
    echo $i;
}

//afficher toutes les lettres de l'alphabet français
for($lettre='a'; $lettre<='z', ++$lettre)
{
    echo $lettre;
}
Voici comment cela se passe dans le 1° exemple :
  1. $i est initialisé à "0" ;
  2. PHP vérifie si l'instruction "$i<10" est TRUE ;
  3. Si c'est TRUE on continue, sinon la boucle est terminée ;
  4. PHP exécute le corps de la boucle ;
  5. PHP exécute l'instruction d'incrémentation : "++$i" ;
  6. Retour au 2° point.

La boucle "for" est à utiliser exclusivement lorsque l'on connaît d'avance le nombre de tours de boucle à effectuer.

Le deuxième paramètre (aka "continuer tant que", qui vaut ici "$i<10") est évalué à chaque tour de boucle. Il est donc préférable que ce soit une valeur fixe afin d'économiser le temps processeur et sous peine de nous exposer à des "boucles infinies".

Exemple de perte de performances :
Sélectionnez
<?php
$users = array('BrYs', 'mathieu', 'Yogui');

//perte de performances puisque count() est recalculé à chaque itération :
for($i=0; $i<count($users); ++$i)
{
    echo $i.' '.$users[$i].'<br/>';
}

//ok :
$nb_users = count($users);
for($i=0; $i<$nb_users; ++$i)
{
    echo $i.' '.$users[$i].'<br/>';
}
Exemples de boucles infinies (à éviter) :
Sélectionnez
<?php
for(;;) //aucune condition de fin de boucle
{
    //...
}

for($i=0; $i=-1; ++$i) //$i n'atteindra jamais -1
{
    //...
}

III-K-4. Boucle "while"

Syntaxe :
Sélectionnez
<?php
while(<continuer tant que>)
{
    <instructions>
}

Une boucle "while" est prévue pour effectuer un groupe d'instructions un nombre indéfini de fois. La condition de fin de boucle dépend souvent du résultat d'une comparaison de valeurs. Un exemple typique d'utilisation est la lecture d'un flux, par exemple un fichier :

Sélectionnez
<?php
$file = fopen('file.ext', 'r'); //ouverture d'un fichier en lecture
while(!feof($file)) //tant que ce n'est pas la fin du fichier (End Of File)
{
    echo fread($file, 8192); //lecture d'une ligne
}
fclose($file); //fermeture du descripteur de fichier
Voici comment cela se passe dans l'exemple :
  1. $file est initialisé à l'état du fichier (EOF si le fichier est vide, une valeur non nulle sinon) ;
  2. PHP vérifie la condition, donc ici si nous ne sommes pas à la fin du fichier ;
  3. Si c'est TRUE on continue, sinon la boucle est terminée ;
  4. PHP exécute le corps de la boucle ;
  5. Retour au 2° point.

Une autre utilisation classique de la boucle "while" est de mettre une ligne d'affectation comme condition :

Sélectionnez
//exécution d'une requête définie plus haut dans le script
$db_result = mysql_query($sql);
while($row = mysql_fetch_assoc($db_result)) //tant qu'il y a un résultat
{
    print_r($row);
}

Dans ce cas, la condition du while est la valeur de l'expression "$line = fread($file, 8192)", c'est-à-dire le résultat de l'opération, soit la valeur de $line après affectation. Cela peut s'exprimer par la question "est-ce que l'affectation a pu se faire ?".

La boucle "while" est à utiliser exclusivement lorsque l'on ne connaît pas d'avance le nombre de tours de boucle à effectuer. Cela restreint beaucoup ses cas d'utilisation, car PHP dispose de très nombreux moyens de compter le nombre d'éléments d'un tableau, de lire des fichiers, etc. sans obliger à l'utilisation de "while".

Exemple de boucle infinie :
Sélectionnez
<?php
while(TRUE)
{
    //...
}

III-K-5. Boucle "do while"

Syntaxe :
Sélectionnez
<?php
do
{
    <instructions>
} while(<continuer tant que>);

Cette boucle fonctionne sur un principe similaire au "while", mais le premier tour de boucle est effectué avant toute chose.

Dans d'autres langages, une boucle "do...while" est utilisée par exemple pour afficher un menu à l'écran et attendre la réponse de l'utilisateur : on sait d'avance que le menu est affiché au moins une fois, mais on ne sait pas d'avance quand l'utilisateur souhaitera quitter l'application. Cet exemple est malheureusement inefficace en PHP, puisqu'un script se termine sans que l'utilisateur puisse interagir.

Sélectionnez
<?php
do
{
    menu_display(); //définir cette fonction selon le besoin
    $choice = get_user_choice(); //définir cette fonction selon le besoin
    switch($choice)
    {
        //instructions selon la valeur de $choice
    }
} while($choice != CHOICE_LEAVE); //définir cette constante selon le besoin

III-K-6. Boucle "each"

Cette instruction n'est pas vraiment une boucle mais on l'utilise généralement conjointement à un type de boucle. Elle est prévue spécialement pour parcourir les tableaux :

Sélectionnez
<?php
$membres = array('BrYs', 'mathieu', 'Yogui');
print_r(each($membres));
print_r(each($membres));
print_r(each($membres));

Chaque utilisation de la fonction fait avancer le pointeur interne du tableau, c'est-à-dire la position à laquelle PHP s'est arrêté la dernière fois qu'il a consulté le tableau.

Afin de simplifier son utilisation, on la couple souvent avec une boucle "while" :

Sélectionnez
<?php
$membres = array('BrYs', 'mathieu', 'Yogui');
while(list($i, $membre) = each($membres))
{
    echo $membre;
}

À mon avis, cette utilisation n'est pas optimale à cause de la sémantique du langage. L'exécution est rapide, mais le code n'est pas très clair. Le construct foreach est souvent plus intéressant de ce point de vue.

III-K-7. Boucle "foreach"

Sélectionnez
<?php
foreach(<tableau> as <element>)
{
    <instructions>
}
Sélectionnez
<?php
foreach(<tableau> as <clef> => <element>)
{
    <instructions>
}

Le mot clef "each" étant assez complexe à utiliser, PHP propose une boucle très pratique : foreach.

Sélectionnez
<?php
$membres = array('BrYs', 'mathieu', 'Yogui');

//parcours du tableau :
foreach($membres as $membre)
{
    echo $membre; //affiche tour à tour "BrYs" puis "mathieu" puis "Yogui"
}

//parcours avec clefs :
foreach($membres as $i => $membre)
{
    //affiche tour à tour "0 BrYs" puis "1 mathieu" puis "2 Yogui"
    echo $i . ' ' . $membre;
}

//alternative :
foreach($membres as $i => $membre)
{
    //affiche tour à tour "0 BrYs" puis "1 mathieu" puis "2 Yogui"
    echo $i . ' ' . $membres[$i];
}

III-L. Fonctions

III-L-1. Syntaxe

Syntaxe :
Sélectionnez
<?php
function <nom de la fonction> (<noms des paramètres>)
{
    <instructions>
    return <valeur>; (optionnel)
}

Une fonction permet de réutiliser facilement du code PHP. Par exemple si vous devez effectuer la même suite d'opérations à plusieurs endroits de votre code, il est sans doute judicieux de la mettre dans une fonction. Cela vous permet de ne pas dupliquer le code source et d'en faciliter la maintenance.

Une fonction se déclare en utilisant le mot clef "function" suivi du nom de la fonction (mêmes règles que pour les noms de variables), d'un couple de parenthèses et d'un couple d'accolades. Les parenthèses contiennent la liste des paramètres (de zéro à l'infini) et les accolades contiennent les actions de la fonction.

En PHP, le terme "procédure" est très peu utilisé car on part du principe que toutes les fonctions devraient retourner une valeur : au minimum, si la fonction a réussi ou non (booléen). Toutefois, il ne s'agit ici que d'une convention : d'un point de vue syntaxique, il est possible d'avoir une fonction qui n'utilise pas le mot clef "return" (mais ce serait probablement une mauvaise idée).

La valeur de retour d'une fonction est définie par le mot clef "return". Dès que ce mot clef est exécuté, PHP sort de la fonction.

Exemple :
Sélectionnez
<?php
function ma_fonction()
{
    echo 'essai 1';
    return TRUE; //PHP sort de la fonction en renvoyant la valeur "vrai"
    echo 'essai 2'; //cette instruction ne sera jamais exécutée
}

Voyons un exemple d'application :

Code répétitif :
Sélectionnez
<?php
var_dump(3 * 100 / 8);
var_dump(2 * 100 / 72);
var_dump(5 * 100 / 50);
Code mis en facteur :
Sélectionnez
<?php
function pourcent($x, $y)
{
    return $x * 100 / $y;
}

var_dump(pourcent(3, 8));
var_dump(pourcent(2, 72));
var_dump(pourcent(5, 50));
Sélectionnez
float(37.5)
float(2.7777777777778)
int(10)

La logique de calcul est ici centralisée dans la fonction pourcent(), ce qui facilite la maintenance. On peut par exemple utiliser des fonctions pour la gestion des grands nombres, sans devoir retoucher le code de toute l'application :

Code répétitif modifié (à titre indicatif) :
Sélectionnez
<?php
var_dump(bcdiv(bcmul(3, 100), 8, 2));
var_dump(bcdiv(bcmul(2, 100), 72, 2));
var_dump(bcdiv(bcmul(5, 100), 50, 2));
Code mis en facteur modifié :
Sélectionnez
<?php
function pourcent($x, $y)
{
    return bcdiv(bcmul($x, 100), $y, 2);
}

var_dump(pourcent(3, 8));
var_dump(pourcent(2, 72));
var_dump(pourcent(5, 50));
Sélectionnez
string(5) "37.50"
string(4) "2.77"
string(5) "10.00"

Notez que seul le corps de la fonction a été modifié dans le code mis en facteur, mais que tout le programme est affecté.

Il ne faut pas faire des fonctions à tort et à travers, il est par exemple préférable que la fonction retourne une valeur simple plutôt qu'une valeur complexe, et il vaut mieux éviter d'inclure du code HTML dans le retour d'une fonction.

III-L-2. Visibilité des variables

La visibilité (ou "scope") d'une variable dépend de sa première utilisation. Une variable n'est visible que dans la fonction dans laquelle elle a été définie :

Sélectionnez
<?php
$x = 5;
echo $x; //affiche "5"
test(); //affiche une erreur
echo $x; //affiche "5"

function test()
{
    echo $x; //illégal car $x n'est pas défini dans la fonction test()
}
Sélectionnez
<?php
$x = 5;
echo $x; //affiche "5"
test();
echo $x; //affiche "5" car ce $x-ci n'est plus celui de la fonction test()

function test()
{
    $x = 7;
    echo $x; //affiche "7" car $x appartient à la fonction test()
}
Sélectionnez
<?php
$x = 5;
echo $x; //affiche "5"
$objet_test = new Test(7);
echo $objet_test->x; //affiche "7"

class Test
{
    public $x;
    public function __construct($valeur)
    {
        $this->x = $valeur;
    }
}

Il existe cependant un mot clef "global" permettant d'outrepasser cette restriction :

php $x = 5; echo $x; //affiche "5" test(); //affiche "5" echo $x; //affiche "5" function test() { global $x; echo $x; //reprend le "$x" du scope global } $x = 5; echo $x; //affiche "5" test(); //affiche "7" echo $x; //affiche "7" function test() { global $x; $x = 7; echo $x; //modifie "$x" dans le scope global }

III-L-3. Fonctions et constantes utiles

Fonctions :
  • function_exists() : Est-ce que la fonction existe ?
  • get_defined_functions() : Liste des fonctions définies ;
  • func_num_args() : Nombre de paramètres transmis à la fonction courante ;
  • func_get_arg() : Un paramètre de la fonction courante ;
  • func_get_args() : Tous les paramètres de la fonction courante ;
  • create_function() : Créer une fonction utilisateur à partir du code PHP fourni en commentaires.

L'utilisation de create_function() doit se faire uniquement après mûre réflexion (question de sécurité)...

Constantes magiques :
  • __FUNCTION__ : Donne le nom de la fonction en cours.
Sélectionnez


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/?page=poo

IV. Programmation Orientée Objet (POO)

IV-A. Modèle objet

IV-A-1. Bref historique

La programmation orientée objet (POO) a fait son apparition dans la version 3 de PHP. C'était alors simplement un moyen d'autoriser la syntaxe OO (par opposition au procédural), mais pas réellement un moyen de programmer efficacement avec des objets.

PHP4 a continué dans la lancée, proposant de nouveaux mots clefs, mais toujours sans proposer une syntaxe proche de langages ayant une plus grande maturité comme C++ ou Java. C'est ce qui a facilité le passage des applications et des hébergements de PHP3 à PHP4, et c'est ce qui retarde la mise en place massive de PHP5 à travers le Web.

PHP5, en revanche, introduit de véritables concepts OO : le constructeur est plus clairement identifié, le destructeur fait son apparition, les objets sont tous pris en charge comme des références, de nouveaux mots clefs font leur apparition (public, protected et private) ainsi que des interfaces et des classes abstraites...

IV-A-2. Terminologie

Une classe est une représentation abstraite d'un objet. C'est la somme des propriétés de l'objet et des traitements dont il est capable. Une Chaise, un Livre, un Humain, une Rivière sont autant d'exemples possibles de classes. Une classe peut généralement être rendu concrète au moyen d'une instance de classe, que l'on appelle objet : on parle d'instancier la classe. Un objet est donc un exemple concret de la définition abstraite qu'est la classe.

On parle d'héritage lorsque l'on définit une hiérarchie de classes. On peut par exemple définir la classe Chien mais elle nous semble trop abstraite : si j'instancie Chien par un objet $médor, je ne sais pas de quelle race est mon chien. Je pourrais avoir une propriété "race" dans la classe Chien, mais la solution de l'héritage me donne plus de souplesse : elle me permet de redéfinir des propriétés et des méthodes de la classe Chien, tout en héritant de celles que je ne modifie pas.

IV-A-3. Les mots réservés

Ces mots permettent de déclarer des classes en PHP :
  • class : Déclaration de classe ;
  • const : Déclaration de constante de classe ;
  • function : Déclaration d'une méthode ;
  • public/protected/private : Accès (par défaut "public" si aucun accès n'est explicitement défini) ;
  • new : Création d'objet ;
  • self : Résolution de portée (la classe elle-même) ;
  • parent : Résolution de portée (la classe "parent") ;
  • static : Résolution de portée (appel statique) disponible depuis PHP 5.3 et 6.0 ;
  • extends : Héritage de classe ;
  • implements : Implémentation d'une interface (dont il faut redéclarer toutes les méthodes).

Les mots clefs "self" et "parent" sont utiles pour accéder à une propriété ou méthode (statique ou non) de la classe elle-même ou de son parent.

Le mot clef "static" a la même utilité mais il résoud la portée au moment de l'exécution du script (cf. plus loin).

Les méthodes magiques sont des méthodes qui, si elles sont déclarées dans une classe, ont une fonction déjà prévue par le langage.

Méthodes magiques
  • __construct() : Constructeur de la classe ;
  • __destruct() : Destructeur de la classe ;
  • __set() : Déclenchée lors de l'accès en écriture à une propriété de l'objet ;
  • __get() : Déclenchée lors de l'accès en lecture à une propriété de l'objet ;
  • __call() : Déclenchée lors de l'appel d'une méthode inexistante de la classe (appel non statique) ;
  • __callstatic() : Déclenchée lors de l'appel d'une méthode inexistante de la classe (appel statique) : disponible depuis PHP 5.3 et 6.0 ;
  • __isset() : Déclenchée si on applique isset() à une propriété de l'objet ;
  • __unset() : Déclenchée si on applique unset() à une propriété de l'objet ;
  • __sleep() : Exécutée si la fonction serialize() est appliquée à l'objet ;
  • __wakeup() : Exécutée si la fonction unserialize() est appliquée à l'objet ;
  • __toString() : Appelée lorsque l'on essaie d'afficher directement l'objet : echo $object; ;
  • __set_state() : Méthode statique lancée lorsque l'on applique la fonction var_export() à l'objet ;
  • __clone() : Appelés lorsque l'on essaie de cloner l'objet ;
  • __autoload() : Cette fonction n'est pas une méthode, elle est déclarée dans le scope global et permet d'automatiser les "include/require" de classes PHP.

Enfin, la variable $this utilisée à l'intérieur d'une classe, vaut toujours une référence vers l'objet lui-même. Pour plus d'informations sur les références, cf. plus loin à la section "Références et clônage".

IV-A-4. Syntaxe

Je ne prétends pas refaire un cours complet sur la POO, car il y a d'excellents cours sur le Net et dans de nombreux livres. Je vais toutefois survoler le sujet afin de rappeler les aspects en rapport avec PHP.

La POO représente la programmation par objets. Un objet est la concrétisation d'une classe, une classe étant un ensemble générique (par opposition à concret) de propriétés et de fonctionnalités. On parle d'instancier une classe pour en faire un objet.

Une classe s'écrit au moyen du mot "class" suivi du nom de la classe et d'accolades (pas de point virgule). Les conventions d'écriture de code recommandent d'écrire ce mot comme un nom propre, à savoir avec une seule majuscule. Certaines recommandations (PEAR, Zend Framework etc.) préconisent d'utiliser l'underscore pour identifier l'héritage :

<?php class Animal{} class Animal_Chien extends Animal{} class Animal_Chien_Caniche extends Animal_Chien{} class Animal_Chien_Labrador extends Animal_Chien{}

La création d'une instance de classe se fait au moyen du mot clef "new". L'accès aux propriétés et méthodes de l'objet se fait par la flèche "->", et l'accès statique par l'opérateur de résolution de portée "::". Le point n'est pas utilisé dans la POO de PHP.

IV-A-5. Droits d'accès

Les classes ont des propriétés et méthodes privées, c'est-à-dire internes et qui ne concernent pas l'extérieur. Ces propriétés sont déclarées en tant que "private".

<?php class Caniche { private $nbPattes; public function __construct() { $this->nbPattes = 4; } } $froufrou = new Caniche(); echo $froufrou->nbPattes; //illégal depuis l'extérieur de la classe
Sélectionnez
Fatal error: Cannot access private property Caniche::$nbPattes
    in C:\Web\online\http\tests\error.php on line 8
Il faut déclarer une méthode accesseur :
Sélectionnez
<?php
class Caniche
{
    private $nbPattes;

    public function __construct()
    {
        $this->nbPattes = 4;
    }

    public function nbPattes() //voici ce que l'on appelle un "getter"
    {
        return $this->nbPattes; //ok depuis l'intérieur de la classe
    }
}

$froufrou = new Caniche();
echo $froufrou->nbPattes(); //affiche "4"

Les propriétés ou méthodes "protected" concernent les objets de la même classe ainsi que ses dérivées, mais pas ceux des classes étrangères.

Sélectionnez
<?php
class Chien
{
    protected $presenceQueue;

    public function __construct()
    {
        $this->presenceQueue = TRUE;
    }
}

class Chien_Labrador extends Chien
{
    public function aUneQueue()
    {
        if($this->presenceQueue) //ok depuis une classe fille
        {
            return 'oui';
        }
        else
        {
            return 'non';
        }
    }
}

$médor = new Chien_Labrador();
echo $médor->aUneQueue(); //affiche "oui"

Les propriétés ou méthodes "public" sont visibles et manipulables par tous les objets, même s'ils sont d'autres classes.

Sélectionnez
<?php
class Caniche
{
    public $âge = 2;

    public function __construct()
    {
        $this->âge = 2;
    }
}

$froufrou = new Caniche();
echo $froufrou->âge; //affiche "2"

IV-A-6. Résolution de portée

Les mots clefs "parent" et "self" combinés à l'opérateur "::" résolvent respectivement la classe dont ils héritent et leur propre classe :

Sélectionnez
<?php
class Chien
{
    protected function aboyer()
    {
        return 'Je suis un chien';
    }
}

class Chien_Labrador extends Chien
{
    protected function aboyer()
    {
        return 'Je suis un labrador';
    }

    public function identifierParent()
    {
        return parent::aboyer();
    }

    public function identifierSelf()
    {
        return self::aboyer();
    }
}

$médor = new Chien_Labrador();
echo $médor->identifierParent().'<br/>';
echo $médor->identifierSelf().'<br/>';
Sélectionnez
Je suis un chien
Je suis un labrador

Les variables et méthodes "static" sont communes à l'ensemble des objets d'une même classe au moyen de l'opérateur "::". On parle alors de propriétés ou de méthodes "de classe" puisqu'elles n'appartiennent pas à un objet en particulier.

Sélectionnez
<?php
class Caniche
{
    public static $caniches = 0;
    public function __construct()
    {
        ++self::$caniches;
    }
}

$froufrou = new Caniche();
$froufrette = new Caniche();

echo Caniche::$caniches; //affiche "2"

L'accès "public/protected/private" s'applique de la même manière que pour les accès non statiques :

Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<?php
class Caniche
{
    protected static $caniches = 0;
    public function __construct()
    {
        ++self::$caniches;
    }
}

$froufrou = new Caniche();
$froufrette = new Caniche();

echo Caniche::$caniches;
Sélectionnez
Fatal error: Cannot access protected property Caniche::$caniches
    in C:\Web\online\http\tests\error.php on line 14

IV-A-7. Interfaces

Une interface est un ensemble de méthodes que les classes doivent définir si elles veulent l'implémenter.

Ne pas implémenter toutes les méthodes de l'interface cause une erreur fatale :
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<?php
interface Joueur
{
    // une classe qui veut implémenter l'interface Joueur
    // doit définir la méthode jouer()
    public function jouer();
}

class Labrador implements Joueur{} //illégal car pas de définition de jouer()

$médor = new Labrador();
$médor->jouer();
Sélectionnez
Fatal error: Class Labrador contains 1 abstract method
    and must therefore be declared abstract or implement the remaining methods
    (Joueur::jouer) in C:\Web\online\http\tests\error.php on line 7
Il faut donc surcharger les méthodes décrites dans l'interface :
Sélectionnez
<?php
interface Joueur
{
    // une classe qui veut implémenter l'interface Joueur
    // doit définir la méthode jouer()
    public function jouer();
}

class Labrador implements Joueur
{
    public function jouer()
    {
        echo 'Ouah!';
    }
}

$médor = new Labrador();
$médor->jouer();

De nombreuses interfaces intéressantes sont définies dans la Standard PHP Library (SPL).

Obtenir la liste des interfaces déclarées :
Sélectionnez
<pre>
<?php
print_r(get_declared_interfaces());

IV-A-8. Références et clônage

Depuis PHP 5, les objets sont tous des références. Ainsi, copier un objet vers un autre au moyen de l'opérateur "=" ne duplique pas l'objet, au contraire il créé une deuxième référence vers le même objet.

Lorsque l'on créé un objet avec l'opérateur new, PHP créé l'objet en mémoire et référence son emplacement en mémoire dans la variable :

Sélectionnez
$object = new stdClass();

La variable $object contient maintenant une référence vers un objet de la classe stdClass, mais elle ne contient pas l'objet lui-même. Pour détruire cet objet en mémoire, il faut détruire toutes les références vers cette instance de la classe.

Sélectionnez
$object = new stdClass();
unset($object);
//toutes les références sont détruites, l'objet n'existe donc plus en mémoire
Sélectionnez
$object_1 = new stdClass();
$object_2 = $object_1;
unset($object_1); //il reste une référence, l'objet persiste donc en mémoire
unset($object_2); //l'objet n'est plus référencé par aucune variable, il est donc détruit

Pour nous en convaincre, essayons le script suivant :

Sélectionnez
<?php
class Test
{
    public function __destruct()
    {
        echo 'Objet détruit<br/>';
    }
}

$obj_1 = new Test();
$obj_2 = $obj_1;
$obj_3 = $obj_2;

echo 'Marqueur n° 1<br/>';
unset($obj_1);

echo 'Marqueur n° 2<br/>';
unset($obj_2);

echo 'Marqueur n° 3<br/>';
Sélectionnez
Marqueur n° 1
Marqueur n° 2
Marqueur n° 3
Objet détruit

La phrase "Objet détruit" apparaît uniquement lorsque la fin du script cause la destruction de la 3° et dernière référence (appel automatique du destructeur).

Voyons maintenant avec des clônes de l'objet initial :

Sélectionnez
<?php
class Test
{
    public function __destruct()
    {
        echo 'Objet détruit<br/>';
    }
}

$obj_1 = new Test();
$obj_2 = clone $obj_1;
$obj_3 = clone $obj_2;

echo 'Marqueur n° 1<br/>';
unset($obj_1);

echo 'Marqueur n° 2<br/>';
unset($obj_2);

echo 'Marqueur n° 3<br/>';
Sélectionnez
Marqueur n° 1
Objet détruit
Marqueur n° 2
Objet détruit
Marqueur n° 3
Objet détruit

Ici, il y a bien une seule référence de chaque objet.

IV-A-9. Late Static Bindings (LSB)

Une innovation de PHP 5.3 et 6.0 est ce que l'on appelle les "Late Static Bindings".

Les LSB sont une nouvelle faculté de PHP : résoudre les appels statiques plus tard dans la chronologie des évènements. C'est une question de fonctionnement interne de PHP. Le plus important pour nous est de savoir que les deux exemples suivants agissent différemment :

Mot clef "self" :
Sélectionnez
<?php
class Chien
{
    protected function aboyer()
    {
        return 'Je suis un chien';
    }

    public function identifier()
    {
        return self::aboyer(); //appel à la classe elle-même
    }
}

class Chien_Labrador extends Chien
{
    protected function aboyer()
    {
        return 'Je suis un labrador';
    }
}

$médor = new Chien();
$félix = new Chien_Labrador();

echo $médor->identifier().'<br/>';
echo $félix->identifier().'<br/>';
Sélectionnez
Je suis un chien
Je suis un chien

Ici, la méthode Chien::identifier() est bien transmise par héritage à la classe Chien_Labrador mais elle n'est pourtant pas appelée. Cela a été perçu comme un problème par de nombreuses personnes, et a donné lieu aux Late Static Bindings. De nombreux articles ont été écrits sur le sujet, si vous souhaitez en savoir davantage.

Les Late Static Bindings permettent d'utiliser un nouveau mot clef "static::" pour résoudre correctement la portée statique :

Mot clef "static" :
Sélectionnez
<?php
class Chien
{
    protected function aboyer()
    {
        return 'Je suis un chien';
    }

    public function identifier()
    {
        return static::aboyer(); //appel statique
    }
}

class Chien_Labrador extends Chien
{
    protected function aboyer()
    {
        return 'Je suis un labrador';
    }
}

$médor = new Chien();
$félix = new Chien_Labrador();

echo $médor->identifier().'<br/>';
echo $félix->identifier().'<br/>';
Sélectionnez
Je suis un chien
Je suis un labrador

Dans cet exemple, "static::" permet de négocier l'appel de la méthode aboyer() au moment de l'exécution, et ainsi d'utiliser la méthode Chien_Labrador::aboyer()... Au moment de la compilation du script, on ne sait pas encore quelle classe fournira la méthode : c'est à l'exécution que tout devient clair.

Une application particulièrement intéressante des LSB est dans le cadre de projets ORM (bases de données) :

Sélectionnez
<?php
class Table
{
    static function getByPk($id)
    {
        return 'SELECT * FROM '.get_called_class().' WHERE id = '.(int)$id;
    }
}

class Album extends Table{}
class Artist extends Table{}

echo Album::getByPk(3);
echo Artist::getByPk(6);
Sélectionnez
SELECT * FROM Album WHERE id = 3
SELECT * FROM Artist WHERE id = 6

La fonction get_called_class() s'apparente à la constante magique __CLASS__, à la différence qu'elle retourne la classe appelée par le développeur plutôt que la classe actuelle dans le code.

Les late static bindings ne sont disponibles qu'à partir de PHP 5.3, qui est actuellement en cours de développement. Par conséquent, rien de ce qui a ici trait aux LSB n'est final (jusqu'à la sortie effective de PHP 5.3). Des mots clefs peuvent notamment apparaître, disparaître ou être renommés.

IV-A-10. Exceptions

Il existe déjà de nombreux articles sur ce sujet, je ne vais donc pas m'étendre. Exemple : Exceptions et PHP5Tutoriel POO en PHP, par Guillaume Affringue

Les exceptions sont un moyen de gérer les situations marginales mais dont nous savons qu'elles peuvent survenir. Un exemple est l'indisponibilité du serveur pendant la connexion à une base de données : ce n'est pas une situation normale, néanmoins elle est facilement prévisible et identifiable. Un modèle Orienté Objet permet de repérer ces situations et de les gérer de manière personnalisée.

Lancement et identification d'une exception :
Sélectionnez
try
{
    throw new Exception('incident');
}
catch(Exception $e)
{
    echo $e->getMessage(); //affiche "incident"
}

Lorsqu'une exception est lancée depuis un bloc "try", le bloc "catch" permet de l'attraper et de traiter l'incident. Dans l'exemple ci-dessus, nous avons lancé nous-mêmes volontairement une exception.

PHP permet d'utiliser plusieurs blocs "catch" à la suite, mais ne dispose pas (encore ?) d'instruction "finally" comme dans d'autres langages.

IV-A-11. Fonctions et constantes utiles

Fonctions :
  • class_parents() : Retourne un tableau de la classe parent et de tous ses parents ;
  • class_implements() : Retourne un tableau de toutes les interfaces implémentées par la classe et par tous ses parents ;
  • get_class() : Retourne la classe de l'objet passé en paramètre ;
  • get_called_class() : À utiliser dans une classe, retourne la classe appelée explicitement dans le code PHP et non au sein de la classe ;
  • class_exists() : Vérifie qu'une classe a été définie ;
  • get_declared_classes() : Liste des classes définies ;
  • get_class_methods() : Liste des méthodes d'une classe ;
  • get_class_vars() : Liste des propriétés d'une classe.
Constantes magiques :
  • __CLASS__ : Donne le nom de la classe en cours ;
  • __METHOD__ : Donne le nom de la méthode en cours.

get_class($this) et __CLASS__ ont le même effet.

IV-B. Espaces de noms

IV-B-1. Introduction

Les espaces de noms, aka namespaces, sont un moyen de résoudre les collisions de noms de constantes, fonctions et classes.

Par exemple si j'ai besoin d'une classe pour filtrer mes variables, je peux avoir envie de créer une classe nommée "Filter". Or, ce nom est sans doute déjà utilisé par PHP ou par l'une des bibliothèques incluses dans mes scripts. La solution classique est de préfixer le nom de la classe de manière à le rendre unique : "DVP_Filter".

Ou dans un contexte MVC, j'ai souvent des modèles et des contrôleurs pour le même concept. Selon le framework utilisé, la nomenclature change mais le nom de classe est généralement préfixé lui aussi.

PHP 5.3 introduit le concept des namespaces dans PHP. Cela nous permet de définir des noms de classes, fonctions etc. au sein d'un espace de noms, par exemple le namespace "DVP" peut contenir la classe "Filter" alors que l'espace de noms global contient également une classe "Filter". Nous pouvons ainsi avoir autant de classes "Filter" que nous pouvons imaginer de noms de namespaces.

Les espaces de noms ne sont disponibles qu'à partir de PHP 5.3, qui est actuellement en cours de développement. Par conséquent, rien de ce qui a ici trait aux espaces de noms n'est final (jusqu'à la sortie effective de PHP 5.3). Des mots clefs peuvent notamment apparaître, disparaître ou être renommés.

IV-B-2. Syntaxe

Déclaration :
Sélectionnez
<?php
namespace <Nom>;
...
Utilisation :
Sélectionnez
<?php
use <Nom> as <Alias>;
$object = new Models::Member(); //avec le nom complet
$object = new M::Member(); //avec l'alias

Les espaces de noms composés peuvent être importés tels quels ou avec un alias :

Sélectionnez
<?php
namespace Cours::Models;
...
Sélectionnez
<?php
use Cours::Models;
use Cours::Models as M;
...

En revanche, les noms qui ne sont pas composés doivent être importés avec un alias :

Sélectionnez
<?php
namespace Models;
...
Sélectionnez
<?php
use Models as M;
...

Erreur si on ne définit pas d'alias :

Sélectionnez
<?php
use Models;
...
Sélectionnez
Warning: The use statement with non-compound name 'Models' has
    no effect in C:\Web\online\http\cours-php\namespaces\index.php on line 6

Bien entendu, il est possible d'imbriquer les espaces de noms :

Sélectionnez
<?php
namespace Offline::Sites::Application::Models;
Sélectionnez
<?php
use Offline::Sites::Application::Models as M;
$object = new M::Member();

Si vous êtes dans un espace de noms (c'est-à-dire dans un script contenant par exemple "namespace Cours;"), vous pouvez vous abstenir d'utiliser le préfixe "Cours::" devant les symboles de cet espace de noms. En effet, PHP suppose que vous utilisez des symboles du même espace de noms, ou bien un symbole du langage. Si vous voulez utiliser un symbole du scope global (mais pas interne à PHP), préfixez le nom du symbole par "::". Si vous utilisez un symbole d'un autre espace de noms, il faut bien entendu préfixer votre symbole de l'espace de noms complet.

Si vous êtes hors d'un espace de noms, il faut systématiquement préfixer soit de l'alias, soit du nom complet.

N'avez-vous jamais rêvé d'utiliser un nom de fonction déjà réservé par le langage ? Les espaces de noms vous permettent d'utiliser les noms déjà réservés pour des constantes, fonctions ou classes internes de PHP.

Il était initialement prévu d'utiliser le mot clef "import" comme en Java, mais cela a finalement été changé pour le mot clef "use". Voir les archives de php.internals[PHP-DEV] import/use last call pour plus d'informations.

IV-B-3. Exemple d'utilisation

Reprenons un contexte MVC assez simple pour illustrer une utilité possible des namespaces.

Ajouter à "httpd.conf" :
Sélectionnez
<Directory "C:/Web/online/http/cours-php/namespaces">
    AllowOverride None
    php_value include_path ".;C:/Web/offline/sites/cours-php/namespaces"
    SetEnv HTTP_ROOT /cours-php/namespaces/
    RewriteEngine on
    RewriteCond %{REQUEST_URI} !\.(js|css|jpg|png|gif)$
    RewriteRule .* index.php
</Directory>
controllers/member.php
Sélectionnez
<?php
namespace Cours::Controllers;
class Member
{
    public static function whoAmI()
    {
        return 'Je suis un Contrôleur';
    }
}
models/member.php
Sélectionnez
<?php
namespace Cours::Models;
class Member
{
    public static function whoAmI()
    {
        return 'Je suis un Modèle';
    }
}
views/member.php
Sélectionnez
<?php
namespace Cours::Views;
class Member
{
    public static function whoAmI()
    {
        return 'Je suis une Vue';
    }
}
index1.php
Sélectionnez
<?php
require 'models/member.php';
require 'views/member.php';
require 'controllers/member.php';

header('Content-Type: text/html; charset=utf-8');
echo Cours::Models::Member::whoAmI().'<br/>';
echo Cours::Views::Member::whoAmI().'<br/>';
echo Cours::Controllers::Member::whoAmI();
index2.php
Sélectionnez
<?php
require 'models/member.php';
require 'views/member.php';
require 'controllers/member.php';

use Cours::Models;
use Cours::Views;
use Cours::Controllers;

header('Content-Type: text/html; charset=utf-8');
echo Models::Member::whoAmI().'<br/>';
echo Views::Member::whoAmI().'<br/>';
echo Controllers::Member::whoAmI();
index3.php
Sélectionnez
<?php
require 'models/member.php';
require 'views/member.php';
require 'controllers/member.php';

use Cours::Models as M;
use Cours::Views as V;
use Cours::Controllers as C;

header('Content-Type: text/html; charset=utf-8');
echo M::Member::whoAmI().'<br/>';
echo V::Member::whoAmI().'<br/>';
echo C::Member::whoAmI();
Sélectionnez
Je suis un Modèle
Je suis une Vue
Je suis un Contrôleur


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/?page=ini

V. Configuration par le fichier php.ini

V-A. Introduction

Le comportement de PHP est dicté par sa configuration, établie dans le fichier php.ini. Ce fichier standard de configuration est habituellement placé dans le même répertoire que PHP, et nous pouvons le modifier à l'aide de n'importe quel éditeur de texte.

Le fichier php.ini est donc utile à la configuration de PHP, mais aussi de toutes ses extensions. En effet, chacune des extensions de PHP peut avoir sa propre section du php.ini et ses propres directives.

Nous allons voir ici les directives les plus courantes du fichier php.ini, et la valeur recommandée dans chaque situation (ie. développement ou production).

Les directives peuvent être de plusieurs types :
  • booléen : Prend les valeurs "On/Off" ou bien "1/0" (les majuscules n'ont pas d'influence) ;
  • numérique : Le temps se compte en secondes, et certaines valeurs acceptent des unités (K, M ou G) ;
  • chaîne : Souvent entre guillemets.

De très nombreuses directives de ce fichier peuvent être modifiées pour la durée d'une requête par le fichier httpd.conf, par un fichier .htaccess ou au moment de l'exécution du script par la fonction ini_set().

Ce comportement est attribué dans le code source de PHP par l'une des constantes suivantes :
  • PHP_INI_USER : Valeurs définies dans un script PHP ou dans le registre de Windows (aucune directive du core n'utilise PHP_INI_USER) ;
  • PHP_INI_PERDIR : Valeurs définies dans php.ini, httpd.conf ou .htaccess ;
  • PHP_INI_SYSTEM : Valeurs définies dans php.ini ou http.conf ;
  • PHP_INI_ALL : Valeurs définies dans php.ini, httpd.conf, .htaccess, le registre de Windows ou un script PHP.

V-B. Core

La documentation officielle se trouve ici : http://php.net/ini.coreDescription des directives internes du php.ini

V-B-1. short_open_tag

Active ou désactive le tag "<?" (par opposition à "<?php") pour ouvrir un bloc PHP dans le script.

Activer cette directive est utile pour conserver la compatibilité avec d'anciens scripts (notamment de l'époque de PHP3), mais une bonne pratique est plutôt de réécrire les scripts de cette époque à cause de leur ancienneté. Les pratiques de sécurité ont changé depuis l'époque de PHP3. Ce tag apporte l'inconvénient de générer des erreurs de syntaxe lorsque l'on veut servir des documents XML sur le même serveur, puisque c'est le même type d'ouverture de balise : "<?xml", reconnu par php.ini comme une ouverture de bloc PHP, ce qui est bien sûr incorrect.

Désactiver cette directive est considéré comme une bonne pratique. Il en va de même pour asp_tags, qui devrait être réservé au langage ASP.

Configuration recommandée :
  • Développement : Off ;
  • Production : Off.

PHP_INI_PERDIR

V-B-2. output_buffering

Cette directive est habituellement désactivée, car la majorité des applications n'en ont pas besoin. De plus, celles qui utilisent ce type de buffer ont généralement un besoin très particulier qu'il est préférable de manipuler dans le script lui-même par la fonction ob_start().

Configuration recommandée :
  • Développement : Off ;
  • Production : Off.

PHP_INI_PERDIR

V-B-3. safe_mode

Le Safe Mode a beaucoup fait parler de lui, à tel point que le PHP Group prévoit de totalement le supprimer de la configuration.

Le problème de cette directive est que son nom promet un niveau de sécurité qu'elle ne peut fournir. Elle ne rend pas l'exécution de scripts "sûre", en fait c'est seulement un ensemble prédéfini de directives de configuration.

Il est préférable de ne pas utiliser cette option, et de sécuriser les scripts pendant leur développement.

Les options safe_mode_* ne fonctionnent pas si safe_mode est désactivé.

Configuration recommandée :
  • Développement : Off ;
  • Production : Off.

PHP_INI_SYSTEM

V-B-4. disable_*

disable_functions permet de désactiver certaines fonctions dans l'ensemble des scripts PHP. De nombreux hébergeurs mutualisés désactivent par exemple "exec" et "dl", pour ne citer que deux exemples.

disable_classes fonctionne sur le même principe mais pour des classes.

Uniquement dans le php.ini

V-B-5. max_*_time

Bien que 30 secondes soit un temps extrêmement long pour un script PHP (dont le temps moyen d'exécution ne dépasse pas 1 seconde), l'option max_execution_time est souvent laissée à sa valeur par défaut de 30 secondes. Cela n'influe pas véritablement sur les performances des scripts, mais réduire cette valeur permet de savoir plus rapidement si un script tourne en rond. La très large majorité des applications n'arrivent pas à timeout, et celles qui y arrivent ont probablement besoin d'une valeur spécifique précisée au niveau du script.

La fonction PHP set_time_limit() permet de modifier cette option pendant l'exécution d'un script.

On peut noter que le temps d'exécution d'un script ne compte que l'exécution du script : tout envoi de requête à un SGBD par exemple, ne compte pas dans l'intervalle défini par cette option. Seul le code PHP est pris en compte.

L'option max_input_time est son équivalent pour le traitement de données par formulaire. La valeur par défaut de 60 secondes me semble exagérée, mais là aussi elle n'a que peu d'influence sur le comportement de PHP.

max_execution_time : PHP_INI_ALL
max_input_time : PHP_INI_PERDIR

V-B-6. memory_limit

Cette option est importante en fonction des extensions que votre serveur utilise. Plus vous utilisez de traitements complexes, plus vous aurez besoin d'augmenter cette valeur. Par exemple, si vous utilisez des traitements graphiques avec la bibliothèque GD ou similaire, il est probable que vous ayez besoin de l'augmenter.

Personne ne peut vous dire quelle est la configuration recommandée. Tout dépend des capacités de votre serveur, de la consommation de vos scripts, des données d'entrée, etc.

PHP_INI_ALL

V-B-7. error_reporting

Définit quelles erreurs doivent être rapportées par PHP. Les erreurs sont habituellement affichées dans la sortie standard et enregistrées dans un fichier de log. Comme nous le verrons ailleurs dans ce cours, PHP dispose de divers niveaux d'erreur : c'est ce niveau d'erreur qui est choisi par l'option error_reporting. Un niveau élevé permet de connaître plus facilement les erreurs des scripts, donc les bugs ou encore les failles de sécurité.

Depuis PHP 5, E_STRICT permet de connaître certaines bonnes pratiques de programmation. Il faut donc utiliser E_ALL | E_STRICT pour avoir toutes les erreurs.

E_STRICT est intégré à E_ALL à partir de PHP 5.3, mais un nouveau niveau d'erreurs E_DEPRECATED est apparu dans le même temps. Depuis PHP 5.3, pour afficher toutes les erreurs il faut donc utiliser E_ALL | E_DEPRECATED.

Voici les niveaux d'erreur fréquemment utilisés :

PHP >= 5 et < 5.3
Sélectionnez
error_reporting = E_ALL | E_STRICT
PHP >= 5.3
Sélectionnez
error_reporting = E_ALL | E_DEPRECATED
Configuration recommandée :
  • Développement : le maximum possible ;
  • Production : le maximum possible.

PHP_INI_ALL

V-B-8. display_errors

Cette option est malheureusement très souvent méprise pour la précédente, error_reporting. Ces deux directives ont pourtant des objectifs bien différents : error_reporting définit le niveau d'erreur qui doit être filtré par PHP (donc également dans les fichiers de log du serveur), tandis que display_errors définit si PHP doit afficher les erreurs dans la sortie standard, indépendamment du niveau d'error_reporting.

Afficher les erreurs internes de vos scripts à l'utilisateur final lui donne des informations préciseuses sur le fonctionnement et les bugs, donc les failles, de vos scripts. Afficher les erreurs est une invitation à se faire pirater. PHP offre un moyen d'enregistrer les erreurs sans les afficher : le fichier de log.

Il est donc préférable d'activer error_reporting au maximum dans toutes les situations, mais d'activer ou désactiver display_errors selon le cas.

Configuration recommandée :
  • Développement : On ;
  • Production : Off.

PHP_INI_ALL

V-B-9. display_startup_errors

Comme le dit le fichier php.ini lui-même, cette option n'est utile que pour le débogage (donc dans de très rares cas), d'autant que de toute manière le fichier de log standard d'Apache indique les erreurs de chargement des extensions PHP.

Configuration recommandée :
  • Développement : On ;
  • Production : Off.

PHP_INI_ALL

V-B-10. log_errors

Il est évident que cette option devrait être laissée active dans toutes les situations. Sans fichier de log, vous n'auriez aucune idée des problèmes qui surviennent sur votre serveur, et vous n'auriez aucun moyen d'éviter leur récurrence.

Configuration recommandée :
  • Développement : On ;
  • Production : On.

PHP_INI_ALL

V-B-11. error_prepend_string

Dans certaines situations, il peut être pratique d'ajouter de manière automatique une chaîne au début du message d'erreur. Par exemple pour les exceptions qui ne sont pas attrapées dans le code, j'ai tendance à demander l'affichage de "<pre>" afin d'améliorer la lisibilité du message.

Configuration recommandée :
  • Développement : "<pre>" ;
  • Production : Off.

PHP_INI_ALL

V-B-12. error_log

Si vous ne souhaitez pas mixer les erreurs Apache et les erreurs PHP, cette option est pour vous. Attention aux droits d'accès au fichier de destination. Personnellement, j'ai tendance à ne pas activer cette option (ce qui est donc la configuration par défaut).

PHP_INI_ALL

V-B-13. register_globals

Voici une autre directive qui était sensée apporter un confort d'utilisation, mais qui a été mal jugée et qui finalement a conduit à davantage de problèmes qu'elle aurait pu en régler.

Elle permet d'importer les variables autoglobales dans le scope du script, par exemple $_GET['page'] devient automatiquement $page. Le problème est que les programmeurs ne filtraient pas du tout ces variables, puisqu'elles étaient déjà dans le scope de leur application, et de nombreux problèmes de sécurité en ont découlé.

Certaines applications ont encore besoin de cette option. Si vous ne pouvez vraiment pas éviter d'utiliser l'une de ces applications, je vous recommande de ne pas activer register_autoglobals dans votre php.ini pour autant, mais plutôt d'utiliser httpd.conf ou .htaccess pour définir une configuration particulière pour le script qui a besoin des autoglobales.

Configuration recommandée :
  • Développement : Off ;
  • Production : Off.

PHP_INI_PERDIR

V-B-14. post_max_size

Taille maximum des données que PHP accepte depuis un formulaire POST.

PHP_INI_PERDIR

V-B-15. magic_quotes_*

Apparues dans la même période que safe_mode, les directives magic_quotes_* sont tout aussi dangereuses. Elles donnent un faux sentiment de sécurité, car les données ne sont en rien protégées contre les attaques. Cette directive devrait disparaître de php.ini d'ici peu, il est donc fortement recommandé de ne plus l'utiliser et de réécrire les scripts qui en ont besoin.

Configuration recommandée :
  • Développement : Off ;
  • Production : Off.

magic_quotes_gpc : PHP_INI_PERDIR
magic_quotes_runtime : PHP_INI_ALL
magic_quotes_sybase : PHP_INI_ALL

V-B-16. default_mimetype

Le type de document par défaut envoyé par Apache au navigateur Web. Il est fortement recommandé de laisser cela à la valeur proposée, puisque la quasi totalité des scripts du marché se reposent sur cette configuration. Modifier cette valeur peut vous poser bien des ennuis.

Configuration recommandée :
  • Développement : "text/html" ;
  • Production : "text/html".

PHP_INI_ALL

V-B-17. default_charset

L'encodage par défaut transmis par le serveur Web.

Là aussi, je vous recommande de ne pas modifier la configuration par défaut (option désactivée), car cela peut causer des problèmes avec certains scripts existants. Par contre, comme nous le verrons plus loin, il est souhaitable de modifier cette valeur dans tous les scripts PHP.

PHP_INI_ALL

V-B-18. include_path

Utile pour simplifier vos appels à require() ou include() dans vos scripts.

PHP_INI_ALL

V-B-19. extension_dir

Le répertoire vers vos extensions PHP. Préférez un chemin absolu et sans caractères spéciaux (espaces compris).

PHP_INI_SYSTEM

V-B-20. enable_dl

Autorise le chargement dynamique d'extensions PHP au moyen de la fonction dl(). Peut être très dangereux si vous autorisez l'exécution de code par d'autres développeurs puisque cette option vous retire la maîtrise des applications binaires disponibles sur votre propre machine.

Configuration recommandée :
  • Développement : Off ;
  • Production : Off.

PHP_INI_SYSTEM

V-B-21. upload_max_filesize

Taille maximum des fichiers que PHP accepte depuis un formulaire POST. Cette valeur correspond à ce qui devrait apparaître dans le champ MAX_INPUT_SIZE de chacun de ces formulaires. Comme toujours, réduisez cette valeur au minimum, puis augmentez au niveau du répertoire (grâce au httpd.conf ou à un .htaccess) s'il y a besoin de plus.

PHP_INI_PERDIR

V-B-22. allow_url_*

allow_url_fopen permet d'utiliser simplement les fichiers situés sur d'autres serveurs Web, mais il faut bien entendu faire attention à traiter ce qu'ils contiennent avec beaucoup de prudence.

allow_url_include permet d'inclure du code provenant d'un autre serveur, comme si c'était un script local. Cette technique est vivement déconseillée, car vous n'avez pas la maîtrise du contenu de scripts distants, ce qui est donc très dangereux pour la sécurité de votre serveur. De plus, inclure un script distant a un effet dramatique sur les performances.

allow_url_fopen : PHP_INI_SYSTEM
allow_url_include : PHP_INI_SYSTEM

V-C. Modules

La documentation officielle se trouve ici : http://php.net/iniDirectives du php.ini

V-C-1. Date

  • date.timezone : Cette option est utilisée pour localiser le serveur dans le monde et pour adapter la valeur de retour de certaines fonctions de dates. Cette valeur devrait être mise par défaut selon votre fuseau horaire, par exemple à "Europe/Paris" si votre serveur est en France, et adaptée au moment de l'exécution de chaque script selon la localisation géographique de l'internaute.

V-C-2. mail function

Windows :
  • SMTP (PHP_INI_ALL) : L'adresse du serveur SMTP à utiliser, typiquement votre hébergeur ou votre machine.
  • smtp_port (PHP_INI_ALL) : 25 par défaut ;
  • sendmail_from (facultatif - PHP_INI_ALL) : Adresse par défaut en tant qu'émetteur des e-mails.
Unix :
  • sendmail_path (facultatif - PHP_INI_SYSTEM) : Chemin jusqu'au programme d'envoi d'e-mails, peut contenir des paramètres (par défaut : "sendmail -t -i").

V-C-3. Session

  • session.save_handler (PHP_INI_ALL) : Par défaut "files", peut être modifié dans le script avec un appel à session_set_save_handler() ;
  • session.save_path (PHP_INI_ALL) : Chemin vers le magasin des sessions, cf. ci-dessous pour des exemples ;
  • session.use_cookies (PHP_INI_ALL) : Doit être activé pour utiliser la transmission par cookie, ce qui est la méthode conseillée ;
  • session.name (PHP_INI_ALL) : Le nom de la variable de session, qui est aussi le nom du cookie, par défaut "PHPSESSID" ;
  • session.auto_start (PHP_INI_ALL) : Démarrer la session à chaque requête ;
  • session.cookie_lifetime (PHP_INI_ALL) : Habituellement laissé à sa valeur par défaut, c'est-à-dire zéro ;
  • session.cookie_path (PHP_INI_ALL) : Généralement laissée à la valeur par défaut, qui correspond à la racine "/" du site Web ;
  • session.cookie_domain (PHP_INI_ALL) : Doit correspondre au domaine hébergeant l'application, par exemple exemple.com ou .exemple.com pour inclure les sous domaines ;
  • session.cookie_httponly (PHP_INI_ALL) : Activer ce flag augmente la sécurité mais interdit l'utilisation de la session par AJAX ;
  • session.hash_function (PHP_INI_ALL) : "0" pour MD5, ou "1" pour SHA-1 (conseillé) ;
  • url_rewriter.tags (PHP_INI_ALL) : Utilisé si vous transmettez l'ID de session par URL.

Exemples pour session.save_path :

Simplement le répertoire de stockage :
Sélectionnez
session.save_path = "/path"
Construire N sous répertoires :
Sélectionnez
session.save_path = "N;/path"
Enregistrer les fichiers en mode (CHMOD) :
Sélectionnez
session.save_path = "N;MODE;/path"

V-C-4. Tidy

  • tidy.clean_output (PHP_INI_PERDIR) : Activer ou désactiver le nettoyage automatique : ne pas utiliser cette option si vous n'avez pas que du contenu HTML (par exemple des images, PDF, etc.) ; il peut être intéressant de l'activer au niveau de chaque script.

V-D. Extensions

Chaque extension PHP dispose de ses propres directives dans le fichier php.ini, il est donc à votre charge de reporter ces directives au moment de l'activation de toute extension.


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/?page=concepts

VI. Concepts fondamentaux

Les scripts montrés dans cette page ne sont plus à exécuter en CLI mais à partir d'un navigateur Web.

VI-A. Fonctionnement d'un script

VI-A-1. Introduction

Le principe d'exécution d'un script est le suivant : PHP est utilisé par le serveur Apache uniquement pour le code entre balises PHP. Le reste du source est géré directement par Apache ou bien par un autre langage (dotNET, Ruby...) selon le cas.

Ainsi, afficher du code HTML au moyen d'un echo en PHP est une mauvaise solution car cela oblige PHP à traiter quelque chose qu'Apache pourrait faire tout seul :

Sélectionnez
<?php
/*
  Faire ici la récupération des variables $title, $charset et $body
  par exemple depuis un formulaire ou une BDD
*/
echo '<?xml version="1.0" encoding="'.$charset.'"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>'.$title.'</title>
    <meta
        http-equiv="content-type"
        content="text/html; charset='.$charset.'" />
</head>
<body>
'.$body.'
</body>
</html>';

Il est habituellement préférable d'utiliser PHP le moins possible, et de laisser Apache traiter directement un maximum d'affichage :

Sélectionnez
<?php
/*
  Faire ici la récupération des variables $title, $charset et $body
  par exemple depuis un formulaire ou une BDD
*/
?>
<?xml version="1.0" encoding="<?php echo $charset; ?>"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title><?php echo $title; ?></title>
    <meta
        http-equiv="content-type"
        content="text/html; charset=<?php echo $charset; ?>" />
</head>
<body>
<?php echo $body; ?>
</body>
</html>

Ici, PHP est appelé cinq fois (dont quatre pour des tâches très courtes), tandis qu'Apache s'occupe de la majorité de l'affichage.

VI-A-2. Contrôle de l'exécution

VI-A-2-a. Arrêt du script

Un script PHP s'exécute dans un intervalle de temps défini par la variable max_execution_time du fichier de configuration php.ini, dont la valeur vaut 30 secondes par défaut.

Sélectionnez
max_execution_time = 30

Cette valeur peut être modifiée au cours de l'exécution du script par la fonction set_time_limit(). Il est recommandé de ne jamais l'augmenter dans la configuration globale (php.ini) mais plutôt dans les scripts individuels qui en font la demande.

Pour forcer l'arrêt d'un script en cours d'exécution, on peut utiliser les mots clefs die() et exit(). Ce sont des alias, on peut donc utiliser indifféremment l'un ou l'autre. Tous deux acceptent un paramètre optionnel : le message d'erreur à afficher.

VI-A-2-b. Contrôle d'erreurs

Toutes les erreurs lancées par PHP dépendent du niveau d'erreur configuré dans le fichier php.ini à la valeur error_reporting.

Voici le niveau d'erreur recommandé en fonction de votre version de PHP :

PHP < 5.3 :
Sélectionnez
error_reporting = E_ALL | E_STRICT
PHP >= 5.3 :
Sélectionnez
error_reporting = E_ALL | E_DEPRECATED

L'affichage des messages d'erreurs est indépendant du niveau de reporting, il est contrôlé par la directive display_errors du php.ini :

Configuration en développement et test :
Sélectionnez
display_errors = On
Configuration en production :
Sélectionnez
display_errors = Off

Quelle que soit votre configuration display_errors, il est fortement recommandé de laisser le paramètre log_errors à "On".

VI-A-3. Contrôle du flux de sortie

Avec la configuration par défaut, PHP envoie les données au fur et à mesure qu'il les calcule. Par exemple avec une boucle qui récupère des informations d'une BDD et qui les affiche immédiatement, le navigateur les reçoit petit à petit. C'est parfois pour cela que les pages se chargent par paquets, surtout si la base de données met du temps à répondre à PHP.

Cependant, il est parfois utile de conserver toutes ces informations afin d'effectuer un traitement global avant de les transmettre au navigateur. Cela peut être effectué très simplement au moyen des fonctions de contrôle de flux. Ces fonctions demandent à PHP de conserver dans une variable interne (mémoire tampon) tout ce qu'il veut envoyer à la sortie standard, et d'autres fonctions nous permettent de manipuler ce flux.

Ces fonctions sont rarement utilisées, car cela dénote souvent un problème dans la conception de l'application, c'est une sorte de rustine sur du code mal conçu. C'est le cas de Tidy, qui peut corriger le code HTML avant de l'envoyer au navigateur.

Cependant, il y a des utilisations intéressantes. Le module iconv et l'extension mbstring (que nous verrons plus loin) proposent des fonctions permettant de convertir le charset du texte de la page, en une seule passe au moment d'envoyer le document au navigateur : cela évite d'appeler des fonctions de conversion tout au long du code de l'application.

VI-B. Structure d'un script

PHP est souvent décrit comme un moteur de templates. Cela signifie qu'il sert à récupérer des informations et à produire des documents en fonction de ces informations. Dans l'exemple ci-dessus, un document HTML est produit à partir de trois variables. En situation réelle, nous aurions probablement plusieurs accès à une base de données et quelques boucles pour afficher le contenu, filtré et organisé comme il se doit.

Structure conseillée pour un script PHP :
  1. Récupération des informations (+ validation et filtres) ;
  2. Construction du document (cela peut parfois être fait au moment de l'affichage) ;
  3. Envoi des en-têtes HTTP (facultatif s'ils sont correctement envoyés par Apache) ;
  4. Affichage du document.

Retarder l'envoi des en-têtes et l'affichage des informations jusqu'à la fin du script permet de choisir le type de document à afficher et de simplifier le débogage.

Par exemple, avec la même variable $message, on peut produire de nombreux documents totalement différents :

Page Web :
Sélectionnez
<?php
//récupération des données
$message = empty($_GET['message']) ? 'Hello, world!' : $_GET['message'];

//affichage
header('Content-Type: text/html; charset=iso-8859-1');
?>
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title><?php
        echo htmlentities($message, ENT_QUOTES, 'iso-8859-1');
        ?></title>
    <meta http-equiv="content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<h1><?php echo htmlentities($message, ENT_QUOTES, 'iso-8859-1'); ?></h1>
</body>
</html>
Image PNG :
Sélectionnez
<?php
//récupération des données
$message = empty($_GET['message']) ? 'Hello, world!' : $_GET['message'];

//construction du document
$image = imagecreatetruecolor(100, 50);
$text_color = imagecolorallocate($image, 255, 0, 0);
imagestring($image, 1, 5, 5, $message, $text_color);

//affichage
header('Content-Type: image/png');
imagepng($image);
Animation Flash :
Sélectionnez
<?php
//récupération des données
$message = empty($_GET['message']) ? 'Hello, world!' : $_GET['message'];

//construction du document
$t = new SWFTextField();
$t->setFont(new SWFFont('arial.ttf'));
$t->setColor(255, 0, 0);
$t->addString($message);
$movie = new SWFMovie(7);
$movie->add($t);

//affichage
header('Content-Type: application/x-shockwave-flash');
$movie->output();
Document PDF :
Sélectionnez
<?php
//récupération des données
$message = empty($_GET['message']) ? 'Hello, world!' : $_GET['message'];

//construction du document
require 'fpdf.php';
$document = new FPDF();
$document->AddPage();
$document->SetFont('Arial', 'B', 16);
$document->Cell(40, 10, $message);

//affichage
header('Content-Type: application/pdf');
$document->Output();

Imaginons maintenant que la variable $message soit remplie au moyen d'un formulaire ou bien d'une base de données. Avec différentes sources de données (formulaire, BDD, etc.), PHP nous permet d'obtenir des documents de types différents (HTML, image, SWF...). Nous pouvons obtenir une sortie en image, HTML, SWF ou d'autres formats tout en conservant le même bloc de code pour la récupération des données. De même, nous pouvons modifier la récupération des données (formulaire, XML, BDD...) tout en conservant le même format de sortie. C'est ce que l'on appelle la modularité, la séparation des couches ou encore l'architecture MVC.

Formulaire :
Sélectionnez
<?php
if(strtolower($_SERVER['REQUEST_METHOD']) == 'post')
{
    //récupération des données
    $message = $_POST['message'];

    //construction du document
    $image = imagecreatetruecolor(100, 50);
    $text_color = imagecolorallocate($image, 255, 0, 0);
    imagestring($image, 1, 5, 5, $message, $text_color);

    //affichage
    header('Content-Type: image/png');
    imagepng($image);
}
else
{
    ?>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>New document</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
    <form method="post" action="<?php echo basename(__FILE__); ?>">
        <input type="text" name="message"/>
        <input type="submit" value="Générer le bouton"/>
    </form>
</body>
</html>
    <?php
}
BDD :
Sélectionnez
<?php
//récupération des données
mysql_connect('host', 'login', 'password');
mysql_select_db('developpez');
$sql = 'SELECT text FROM message HAVING id = max(id) GROUP BY text';
$db_message = mysql_query($sql);

if($tmp = mysql_fetch_assoc($db_message))
{
    $message = $tmp['text'];
}
else
{
    $message = 'Aucun message';
}

//construction du document
$image = imagecreatetruecolor(100, 50);
$text_color = imagecolorallocate($image, 255, 0, 0);
imagestring($image, 1, 5, 5, $message, $text_color);

//affichage
header('Content-Type: image/png');
imagepng($image);

La tâche principale de PHP est donc de récupérer des informations depuis une source externe au script, puis de les afficher dans un format défini. Dans ce cours, nous allons voir comment nous pouvons programmer la récupération des informations depuis différentes sources de données, ainsi que diverses manières de les afficher.

VI-C. Inclure un script dans un autre script

VI-C-1. Introduction

L'un des aspects qui font de PHP un langage dynamique, est la capacité d'inclure des scripts dans d'autres scripts. Cela permet de "mettre en facteur" des portions de code qui se répètent d'une page à l'autre.

Un exemple simple : index.php, products.php, links.php sont 3 pages du site ayant un en-tête de page et un pied de page identiques.

header.php
Sélectionnez
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
<head>
    <title><?php echo htmlentities($title, ENT_QUOTES, 'iso-8859-1'); ?></title>
    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<h1><?php echo htmlentities($title, ENT_QUOTES, 'iso-8859-1'); ?></h1>
footer.php
Sélectionnez
<span class="copyright">Auteur : <?php echo $author; ?>,
    <?php echo $date; ?></span>
</body>
</html>
index.php
Sélectionnez
<?php
$title = 'Accueil';
$author = 'Yogui';
$date = date('Y', filemtime(__FILE__));

include 'header.php';
?>
<p>Bienvenue sur notre site</p>
<p>Notre entreprise fut fondée en...</p>
<?php
include 'footer.php';
products.php
Sélectionnez
<?php
$title = 'Nos produits';
$author = 'Yogui';
$date = date('Y', filemtime(__FILE__));

include 'header.php';
?>
<ul>
    <li>...</li>
    <li>...</li>
    <li>...</li>
    <li>...</li>
</ul>
<?php
include 'footer.php';
links.php
Sélectionnez
<?php
$title = 'Nos partenaires';
$author = 'Yogui';
$date = date('Y', filemtime(__FILE__));

include 'header.php';
?>
<ul>
    <li><a href="..." title="...">...</a></li>
    <li><a href="..." title="...">...</a></li>
    <li><a href="..." title="...">...</a></li>
</ul>
<?php
include 'footer.php';

Une autre utilisation classique des inclusions est de séparer les fonctions, les classes, etc. : chaque script regroupe ces éléments par thème. Les conventions préconisent notamment de ne déclarer qu'une seule classe PHP par script.

Exemple :

functions.php
Sélectionnez
<?php
function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
config.php
Sélectionnez
<?php
define('DB_TYPE', 'mysql');
define('DB_HOST', 'localhost');
define('DB_USER', 'utilisateur');
define('DB_PASSWORD', 'motdepasse');
define('DB_NAME', 'developpez');

VI-C-2. Les instructions include, include_once, require et require_once

PHP dispose de 2 instructions pour inclure un script :
  • include() : Inclure le code du script indiqué, lancer un avertissement si le fichier est introuvable ;
  • require() : Inclure le code du script indiqué, lancer une erreur fatale si le fichier est introuvable ;

Chacune de ces instructions se décline en une instruction *_once() qui oblige PHP à vérifier si le script demandé a déjà été inclus au cours de la requête actuelle (très pratique pour les déclarations de fonctions et de classes). Les fonctions *_once() ne sont pas nécessairement plus lentes à l'exécution que leurs grandes soeurs, mais elles consomment légèrement plus de mémoire. La différence étant dérisoire, il est inutile de s'en soucier.

Ainsi, ce script inclut deux fois "header.php" :

Sélectionnez
<?php
include 'header.php';
include 'header.php';

Tandis que celui-ci ne l'inclut qu'une seule fois :

Sélectionnez
<?php
include_once 'header.php';
include_once 'header.php';
include_once 'header.php';
include_once 'header.php';
include_once 'header.php';

VI-C-3. Chargement automatique de classes (inclusion implicite)

Une fonction particulièrement utile pour les classes est __autoload(). Si cette fonction magique est déclarée dans vos scripts, alors toute classe utilisée mais n'ayant pas été chargée jusque-là, est chargée à l'aide de cette fonction.

Sélectionnez
<?php
function __autoload($class)
{
    require_once $class.'.php';
}

$object = new MyClass(); //chargement automatique de "MyClass.php"
Sélectionnez
<?php
function __autoload($class)
{
    require_once str_replace('_', '/', $class).'.php';
}

//chargement automatique de "My/Special/Class.php"
$object = new My_Special_Class();

Puisque nous avons fréquemment besoin de plusieurs bibliothèques dans un même projet, et que chaque bibliothèque a toutes les chances de définir son propre __autoload(), nous avons besoin d'un moyen de faire fonctionner tous ces __autoload() en même temps. C'est la SPL qui nous donne la fonction à utiliser : spl_autoload_register()

Sélectionnez
<?php
spl_autoload_register('basic_autoload');
spl_autoload_register('dotted_autoload');
spl_autoload_register('pear_autoload');

$object = new My_Special_Class();

function basic_autoload($class)
{
    $file = $class.'.php';
    if(file_exists($file))
    {
        require_once $file;
    }
    //echo $file.'<br/>';
}

function dotted_autoload($class)
{
    $file = str_replace('_', '.', $class).'.php';
    if(file_exists($file))
    {
        require_once $file;
    }
    //echo $file.'<br/>';
}

function pear_autoload($class)
{
    $file = str_replace('_', '/', $class).'.php';
    if(file_exists($file))
    {
        require_once $file;
    }
    //echo $file.'<br/>';
}

Enlevez les commentaires pour savoir ce qu'il se passe pendant l'exécution du script.

Le chargement automatique de classes ralentit l'exécution du codeCommentaire de Rasmus Lerdorf. Pour améliorer les performances, il faut utiliser un optimiseur de code ainsi qu'un cache d'opcode. Nous reviendrons sur ces notions par la suite.

VI-C-4. Dangers

Faites attention lorsque vous utilisez des scripts écrits par d'autres développeurs, qu'ils n'introduisent pas des failles de sécurité dans votre application. De même, évitez d'inclure des scripts situés sur d'autres serveurs car vous n'en avez pas le contrôle, ils peuvent être dangereux.

VI-C-5. Bonnes pratiques

Il faut faire très attention à ce qui est mis à disposition sur le Web, car il est parfaitement impossible de faire oublier quelque chose à Internet. Dès lors qu'un document est disponible sur Internet, il est potentiellement déjà repris par Google (et mis en cache), des bots spammeurs, WebArchive.com... Il est futile de croire que l'on peut "supprimer" un document qui a été mis en ligne (même pour très peu de temps), et par conséquent il faut apporter un soin minutieux à ce qui peut être mis en ligne et à ce qui doit rester hors ligne.

À cet effet, les scripts destinés à être inclus ne doivent jamais être placés dans un répertoire du serveur Web accessible depuis Internet, même si vous n'en diffusez jamais l'adresse. Ne croyez jamais que les utilisateurs ne devineront pas l'URL puisque vous ne l'avez pas devinée, ce serait sous estimer le hasard, les pirates, ou simplement notre capacité inhérente à faire des erreurs... La solution la plus sûre est toujours de ne pas tenter le diable, et ici cela se traduit par "ne pas mettre en ligne des fichiers qui n'ont pas besoin de l'être". Apprenez à maîtriser votre serveur Web (le système hôte comme le daemon HTTP), à inclure depuis PHP des fichiers qui ne sont accessibles que par le système de fichiers et non par Internet, etc.

Personnellement, j'ai plusieurs répertoires :
  • /web/offline-shared : Les bibliothèques PHP (PEAR, Zend Framework, FPDF...) ;
  • /web/offline-sites : Les classes métier spécifiques à chaque application (classes dérivées, scripts communs...) ;
  • /web/online-http : Scripts PHP, images, scripts JS etc. accessibles en ligne.

Bien entendu, dans le même ordre d'idées, le SGBD doit être configuré pour que seul votre réseau local, voire quelques machines précises, puissent s'y connecter.

Apprenez à utiliser la directive "include_path" de votre fichier php.ini afin de ne pas surcharger vos scripts de chemins absolus :

Sélectionnez
<?php
set_include_path('.'
    . PATH_SEPARATOR . '/web/offline-shared'
    . PATH_SEPARATOR . '/web/offline-sites/test'
    . PATH_SEPARATOR . get_include_path());

//situé dans "/web/offline-sites/test/config.php"
include 'config.php';

//situé dans "/web/offline-shared/session-start.php"
include 'session-start.php';

//situé dans "/web/offline-sites/test/header.php"
include 'header.php';

Prenez garde aux noms des scripts (collisions) et à l'ordre des inclusions.

La fonction __autoload() est utile uniquement si spl_autoload_register() n'est pas appelée. Dès lors que spl_autoload_register() est appelée dans le code, __autoload() perd sa propriété magique et il faut appeler spl_autoload_register('__autoload'), ce qui est sémantiquement absurde. Par conséquent, il est préférable d'éviter d'utiliser __autoload() seul, au profit de spl_autoload_register(). Cela vous évitera des surprises en utilisant des bibliothèques développées par d'autres personnes ou en distribuant vos propres bibliothèques.

Les déclinaisons *_once() sont prévues pour les scripts qui ne sont nécessaires qu'une fois, par exemple un script de déclarations de fonctions ou de classe. C'est le besoin le plus fréquent, require() et include() sont donc moins souvent utilisables que leurs équivalents *_once().

Il faut utiliser les instructions include et include_once lorsque le script à inclure n'est pas primordial pour le bon fonctionnement du reste du programme, et les instructions require et require_once dans le cas contraire.

index.php
Sélectionnez
<?php
require_once 'config.php';
require_once 'functions.php';

include_once 'header.php';
...
include_once 'footer.php';

VI-D. Sécurité au niveau du script

VI-D-1. Introduction

La majorité des utilisateurs de notre application sont légitimes, ils utilisent simplement l'interface que nous leur proposons. Cependant, certains d'entre eux font des erreurs de manipulation innocentes et d'autres cherchent à s'approprier des droits d'accès plus élevés (piratage). Ces situations peuvent être désastreuses pour notre application, nos données et nos utilisateurs.

En aucun cas il ne faut faire confiance à une donnée provenant d'un utilisateur. Cela implique simplement de vérifier ces données avant de les utiliser, mais il faut le faire systématiquement.

Par exemple, si nous utilisons une variable non validée dans une requête SQL, nous sommes exposés à une faille d'injection SQL.

La sécurité des données s'applique à deux moments :
  1. Lors de la récupération des données, il convient de les valider et/ou de les filtrer (input validation) ;
  2. Lors de leur utilisation, il faut les convertir dans le format de sortie (output escaping).

Si ces deux principes ne sont pas scrupuleusement respectés, vous avez toutes les chances de vous faire pirater

Valider des informations revient à s'assurer que les variables contiennent ce qu'elles devraient contenir (leur valeur correspond à un schéma défini). La validation d'un schéma XML à l'aide d'un schéma XSD est un parfait exemple.

Filtrer les données, c'est éliminer le danger dans les variables qui ne passent pas la validation. Le filtrage des données survient donc en cas d'échec (ou à la place) de la validation.

VI-D-2. Validation des données

Il faut toujours vérifier le type d'une variable. Bien sûr, le protocole HTTP transmet uniquement du texte et PHP est un langage faiblement typé. Nous avons vu ce genre de code dans la partie sur les types de données :

Sélectionnez
<?php
var_dump(is_int("1")); //bool(false)

On ne peut pas compter sur les fonctions is_*() car elles sont trop restrictives. Voici des exemples plus exacts :

Sélectionnez
<?php
if(!empty($_GET['id']) and ctype_digit($_GET['id']))
{
    //"id" est de type numérique entier
}
else
{
    //nombre invalide, agir en conséquence
}
Sélectionnez
<?php
if(!empty($_POST['password']) and ctype_print($_POST['password']))
{
    //"password" contient uniquement des caractères imprimables
}
else
{
    //mot de passe impossible, agir en conséquence :
    //par exemple proposer un mot de passe aléatoire
}

Les fonctions ctype_*()Comment vérifier le type d'une chaîne en PHP ? sont un très bon moyen de vérifier le type d'une donnée. Si le type attendu est une chaîne, alors il faut souvent effectuer un contrôle plus poussé au moyen d'expressions rationnellesLes regex en PHP.

La validation doit être appliquée à toutes les données provenant de l'extérieur du programme, que ce soit d'un formulaire, d'une variable superglobale, d'un fichier (même s'il est placé sur le serveur), d'une base de données, etc. Toute variable utilisée sans avoir été validée dans le script est un risque pour le reste de l'application, pour votre base de données, pour vos données client...

Les fichiers transmis par des utilisateurs sont à considérer avec autant de vigilence. La validation d'un fichier se fait raisonablement par son type. Ne vous fiez ni à l'extension du fichier, ni au type fourni fans le tableau $_FILES puisque ce sont des informations transmises par le navigateur : s'il s'agit d'un piratage, ces informations sont sans doute falsifiées pour vous induire en erreur. Le type MIME d'un fichier peut être déterminé grâce à l'extension Fileinfo.

VI-D-3. Filtrage des données

Si la validation échoue, il faut filtrer les données afin qu'elles correspondent au type et au contenu attendus. Cela se fait au moyen du transtypage pour les types autres que les chaînes, ou avec des expressions rationnelles pour les chaînes.

Dans le cas de nombres entiers (qui constituent la majorité des valeurs transmises par l'utilisateur), le plus simple est le transtypage systématique :

Sélectionnez
<?php
if(empty($_GET['id']))
{
    $id = 0;
}
else
{
    $id = (int)$_GET['id']; //on transtype "id" au type numérique entier
}

Pour certains types de données complexes et pour les fichiers, si la validation échoue, alors il peut être judicieux d'arrêter le script plutôt que d'essayer de filtrer la valeur. Par exemple pour un document XML, il est parfois impossible (ou trop complexe) de reconstruire un document qui puisse remplacer le document invalide.

Chris Shiflett, expert sécurité, propose de mettre les variables filtrées dans un tableau PHP afin de mettre en valeur le fait qu'elles sont filtrées :

Sélectionnez
<?php
$clean = array();
if(empty($_GET['id']))
{
    $clean['id'] = 0;
}
else
{
    //on transtype "id" au type numérique entier
    $clean['id'] = (int)$_GET['id'];
}

Une variable filtrée ne doit pas pour autant être utilisée sans précautions. On sait simplement que sa valeur correspond à ce que l'on en attend, par exemple ce n'est pas un mot de passe à la place d'un identifiant numérique. L'utilisation de la variable est une autre histoire, elle dépend du contexte de destination.

VI-D-4. Utilisation des données

Lors de l'utilisation d'une donnée (affichage, envoi dans une requête SQL ou dans une commande shell...), il faut systématiquement protéger la valeur, la convertir dans le format attendu par le destinataire.

Si l'on souhaite afficher la valeur dans une page Web, il faut utiliser une des fonctions suivantes :
  • utf8_encode() : Pour afficher au format UTF-8 (approche recommandée pour les chaînes UTF-8) ;
  • htmlentities() : Pour convertir tous les caractères en leur entité HTML correspondante, attention à bien utiliser les deux paramètres optionnels ;
  • htmlspecialchars() : Pour convertir uniquement les entités HTML fondamentales (fonction insuffisante si elle est utilisée seule).
Exemple :
Sélectionnez
<?php
$string = "Developpez.com, le club des développeurs.";

header('Content-Type: text/html; charset=iso-8859-1');
echo htmlentities($string, ENT_QUOTES, 'iso-8859-1');
Code source produit :
Sélectionnez
Developpez.com, le club des d&eacute;veloppeurs.

Une notion fondamentale en sécurité Web est la faille XSS (cross-site scripting). Il s'agit simplement de tromper l'utilisateur et de lui faire exécuter du code destiné à un autre site. C'est possible si une variable utilisateur est affichée sans protection, par exemple avec echo $_GET['login'] on peut facilement produire une faille XSS.
Comment se protéger de la faille XSS ?

La seconde notion fondamentale est la faille CSRF (cross-site request forgery), légèrement plus complexe à mettre en place que XSS du point de vue du pirate, mais elle est également très facile à éviter pour le développeur du site. Nous y reviendrons en parlant des formulaires.

Dans le cas d'attaques XSS ou CSRF, les utilisateurs de votre site sont des victimes à 100%. Ils ne savent pas ce qu'il se passe avant qu'il soit trop tard (en supposant qu'ils le sachent un jour).

Les autres menaces principales au moment de l'utilisation des variables sont des "failles d'injection".

De nombreux articles décrivent les bonnes méthodes pour se protéger des injections, notamment :
  • Comment se protéger des failles d'injection ?
  • Développement web : Généralités sur la sécurité, par Julien Pauli

Dans le cas d'une requête SQL, le meilleur moyen est d'utiliser des requêtes préparées : on ne traite alors plus des chaînes mais leur représentation hexadécimale (donc inoffensive).

C'est possible avec les APIs OO, notamment :
  • MySQLi : $db->prepare()
  • PDO : $db->prepare()

VI-D-5. Dangers

Faites bien attention à ne jamais utiliser une variable non filtrée ou non validée. La majorité des failles de sécurité sont dues à cette erreur.

VI-D-6. Bonnes pratiques

Plusieurs approches peuvent vous aider :
  • Une approche de type "poka-yoké" vous évite d'utiliser une donnée sans utiliser une méthode explicite de filtrage ;
  • Une extension PHP comme GRASPCORE GRASP ou celle proposée par Wietse VenemaTaint support for PHP.

Comme le dit très bien M. Wietse, aucune extension ne pourra vous dire dans 100% des cas et avec 100% de certitude, que vous avez ou n'avez pas protégé correctement vos données. Les outils sus mentionnés sont des aides pour développer avec le moins d'erreurs possibles, mais c'est aussi votre rôle de programmeur d'auditer votre code.

Dans un monde idéal, ne stockez jamais une valeur "untainted", par exemple le résultat de htmlentities(). Il est préférable de l'utiliser directement dans la fonction de destination, par exemple echo ou mysql_query(). Cela permet de rester conscient du fait qu'une donnée est convertir selon son contexte.

Exemple d'erreur :
Sélectionnez
<?php
/*
 * connexion au SGBD...
 */

$clean = array();
$clean['login'] = htmlspecialchars($_POST['login'], ENT_QUOTES);

//possibilité d'injection SQL
$db->query(sprintf(
    "SELECT id, name FROM user WHERE name='%s'",
    $clean['login']));

Le code ci-dessus présente deux erreurs : d'une part la valeur n'est pas protégée au format SQL ("escape" ou requête préparée), et d'autre part ce qui est enregistré dans la BDD est vraisemblablement un format HTML plutôt qu'une représentation brute. Ce dernier point est une erreur fondamentale d'analyse des besoins : n'enregistrez jamais la conversion d'un texte, à moins d'enregistrer également la version brute.

VI-E. En-têtes HTTP (headers)

VI-E-1. Introduction

Lorsqu'un client demande une page Web, c'est-à-dire lorsqu'un internaute clique sur un lien ou valide une adresse dans son navigateur, il envoie une requête HTTP au serveur Web. Le protocole HTTP est décliné en plusieurs versions : celles qui sont le plus couramment utilisées sur le Web sont la 1.0 et la 1.1, et sont décrites en détail par Mathieu LemoineLe Protocole HTTP.

Exemple de requête :
Sélectionnez
GET /cours/ HTTP/1.1
Host: example.org
User-Agent: Mozilla/1.4
Accept: text/xml, image/png, image/jpeg, image/gif, */*

Lorsque le serveur Web reçoit une telle demande, il évalue comment il peut y répondre (quel code HTTP renvoyer), puis il construit la réponse et il envoie le tout.

Exemple simplifié de réponse :
Sélectionnez
HTTP/1.1 200 OK
Content-Length: 61

<html>
<body>
<img src="http://example.org/image.png" />
</body>
</html>

On voit très bien ici que la réponse HTTP et la page demandée font partie du même envoi. Le serveur Web envoie le code réponse HTTP et le document à la suite l'un de l'autre, comme s'il s'agissait d'un seul document. Les en-têtes HTTP ne peuvent pas être dissociés du document de réponse, ils ne peuvent être envoyés ni au milieu du document ni en plusieurs parties. L'ensemble des en-têtes HTTP doivent donc être prêts avant que le serveur commence à envoyer le document au navigateur qui en a fait la demande.

C'est pour cela qu'utiliser la fonction header() après avoir commencé à envoyer du contenu, mène à l'erreur "headers already sent". Tout ce qui est echo, print, var_dump, print_r etc. sert à transmettre des informations au client, il faut donc terminer l'envoi des en-têtes avant de les utiliser.

C'est aussi pour cela qu'il est impossible d'envoyer deux documents différents en une seule réponse. Une réponse HTTP a un type unique. Lorsqu'une page Web contient des images, des feuilles de style, des animations Flash, des scripts Javascript etc., le serveur envoie chaque élément dans une réponse HTTP séparée, chacun à la demande du navigateur. Le navigateur détermine les éléments à demander au serveur en fonction du document HTML qu'il reçoit ainsi que des préférences de l'utilisateur (ie. désactiver JavaScript ou ne pas afficher d'images), et chaque demande se fait sous la forme d'une requête HTTP.

Pour rappel, voici l'ordre que j'ai proposé dans un paragraphe précédent :
  1. Construction du document demandé ;
  2. Envoi des en-têtes HTTP ;
  3. Envoi du document.

Sous Apache, vous pouvez voir l'ensemble des transactions HTTP entre un navigateur et votre serveur Web dans le fichier apache/logs/access.log. C'est une excellente source d'informations pour un administrateur consciencieux, et vous devriez fréquemment analyser ce fichier log.

VI-E-2. Quand faut-il envoyer les en-têtes ?

Des en-têtes doivent être envoyés à chaque transaction. Le serveur Web est généralement configuré pour envoyer certains en-têtes par défaut, mais le développeur peut les remplacer ou les compléter s'il le juge nécessaire.

Le fichier php.ini dispose par exemple de deux directives default_mimetype et default_charset, qui permettent de définir un type par défaut de contenu et son jeu de caractères. La première est habituellement laissée à "text/html", tandis que la deuxième est généralement mise de côté. En effet, la majorité des requêtes concernent des pages Web au format texte/html, mais pour les autres requêtes le jeu de caractères n'a probablement pas de sens.

L'envoi explicite d'en-têtes depuis le code PHP n'est donc pas systématiquement nécessaire. Le serveur Web se charge de transmettre l'en-tête suivant dans la majorité des cas :

Sélectionnez
HTTP/1.1 200 OK
Content-Type: text/html

Le document doit alors correspondre à cet en-tête afin que le navigateur puisse l'interpréter. Par exemple, il serait mal venu d'envoyer une image avec ce Content-Type... C'est ici qu'intervient la fonction header() en PHP.

La fonction header() permet d'envoyer un en-tête HTTP brut, en remplaçant un en-tête précédent similaire. Nous savons que le serveur Web envoie systématiquement le Content-Type text/html, mais si nous avons le code suivant, cet en-tête sera remplacé par le nôtre :

Sélectionnez
<?php
header('Content-Type: image/png');
Sélectionnez
HTTP/1.1 200 OK
Content-Type: image/png

Nous pouvons ainsi dire au navigateur de l'internaute quel est le type exact du contenu que nous lui envoyons. C'est de cette manière que l'on évite les problèmes d'accents, force le téléchargement d'un fichier, évite la mise en cache du document, etc.

À titre d'illustration, voici une requête complète sur une page générant une erreur PHP :

Sélectionnez
<?php
header('HTTP/1.1 500 Internal Server Error');
Requête HTTP :
Sélectionnez
GET /tests/error.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11)
    Gecko/20071127 Firefox/2.0.0.11
Accept: text/xml,application/xml,application/xhtml+xml,text/html;
    q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cache-Control: max-age=0
Réponse HTTP avec Zend Core 2.5 :
Sélectionnez
HTTP/1.x 500 Internal Server Error
Date: Mon, 31 Dec 2007 22:41:19 GMT
Server: Apache/2.2.4 (Win32)
Content-Length: 535
Connection: close
Content-Type: text/html; charset=iso-8859-1
Réponse HTTP avec Apache 2.2 et PHP 5.3 :
Sélectionnez
HTTP/1.x 500 Internal Server Error
Date: Mon, 31 Dec 2007 22:39:42 GMT
Server: Apache/2.2.4 (Win32) PHP/5.3.0-dev
X-Powered-By: PHP/5.3.0-dev
Content-Length: 0
Connection: close
Content-Type: text/html
Réponse HTTP avec Apache 1.3 et PHP 5.2 :
Sélectionnez
HTTP/1.x 500 Internal Server Error
Date: Mon, 31 Dec 2007 22:40:04 GMT
Server: Apache/1.3.35 (Win32) PHP/5.2.5
X-Powered-By: PHP/5.2.5
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html

VI-E-3. Dangers

Prenez garde aux injections de headers si vous utilisez des variables utilisateur dans vos en-têtes. Il est très facile d'injecter un en-tête dans une application qui ne se protège pas, et cela peut avoir des conséquences dramatiques pour vos utilisateurs. Cette mise en garde est valable aussi bien pour un en-tête HTTP que pour l'en-tête d'un e-mail.

VI-E-4. Bonnes pratiques

Si vous êtes sûr de la configuration de votre serveur, n'envoyer que les en-têtes HTTP nécessaires vous fera gagner du temps de développement. Sinon, leur envoi explicite est un bon moyen de vous assurer que le navigateur traite correctement le document qui lui est envoyé dans la suite de la réponse. Le développeur peut s'appuyer sur la configuration du serveur Web, mais il est conseillé d'envoyer des en-têtes personnalisés à chaque réponse HTTP. Renseignez-vous sur le protocole HTTP, sur les particularités des navigateurs Web à ce sujet et souvenez-vous qu'un en-tête HTTP n'est pas perçu comme un ordre par le navigateur, mais plutôt comme un conseil.

Live HTTP Headers est une excellente extension pour Firefox, elle vous permet de voir quels en-têtes sont envoyés ou reçus par votre navigateur.

VI-F. Liens, URLs et paramètres GET

VI-F-1. Introduction

Les liens sont la plus commune des manières de transmettre une demande de l'utilisateur, et d'introduire du dynamisme dans un site Web.

Sans doute connaissez-vous ce lien :

Sélectionnez
http://www.google.com/search?hl=en&q=php

Sa signification est transparente : charger la page "search" sur le site "google.com", avec les paramètres "hl" en anglais et la question "php".

C'est grâce aux paramètres (à savoir ce qui suit le "?") que Google sait que je veux mes résultats en anglais et pour le mot clef "php". Si je modifie l'un de ces paramètres, Google me répond autre chose.

Les paramètres GET sont mis dans le tableau superglobal $_GET de PHP. C'est un tableau associatif, donc l'exemple ci-dessus se traduit par :

Sélectionnez
Array
(
    [hl] => en
    [q] => php
)

URL est l'acronyme d'Uniform Resource Locator : cela signifie que, d'un chargement à l'autre de la même URL, le document résultant doit être sensiblement équivalent. On considère les modifications entre deux chargements comme des "mises à jour" et non comme des "contenus différents". L'URL est l'identifiant privilégié pour avoir accès à une ressource. Une URL pointe vers un seul document, et un document n'est accessible que par une seule URL active.

Si vous avez plusieurs URLs pour une même ressource, vous devriez mettre en place au plus vite des redirections HTTP.

La superglobale $_SERVER permet de retrouver la requête originale du navigateur sous diverses formes. Le contenu exact de ce tableau dépend de votre configuration, mais certaines variables se retrouvent, en particulier REQUEST_URI et QUERY_STRING :

La requête :
Sélectionnez
http://localhost/tests/error.php?hl=en&q=php
$_SERVER avec Zend Core 2.5
Sélectionnez
Array
(
    [ZendEnablerConfig] => C:/Program Files/Zend/Core/etc/fastcgi.conf
    [PHP_FCGI_MAX_REQUESTS] => 10000
    [PHP_FCGI_CHILDREN] => 1
    [PATH] => /* ma variable système PATH */
    [TEMP] => C:\Program Files\Zend\Core\temp
    [OS] => Windows_NT
    [SystemRoot] => C:\WINDOWS
    [ComSpec] => C:\WINDOWS\system32\cmd.exe
    [_FCGI_MUTEX_] => 1888
    [_FCGI_SHUTDOWN_EVENT_] => 1892
    [_FCGI_NTAUTH_IMPERSONATE_] => 1
    [FCGI_ROLE] => RESPONDER
    [HTTP_HOST] => localhost
    [HTTP_USER_AGENT] => Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US;
        rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11
    [HTTP_ACCEPT] => text/xml,application/xml,application/xhtml+xml,text/html;
        q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
    [HTTP_ACCEPT_LANGUAGE] => en-us,en;q=0.5
    [HTTP_ACCEPT_ENCODING] => gzip,deflate
    [HTTP_ACCEPT_CHARSET] => ISO-8859-1,utf-8;q=0.7,*;q=0.7
    [HTTP_KEEP_ALIVE] => 300
    [HTTP_CONNECTION] => keep-alive
    [HTTP_CACHE_CONTROL] => max-age=0
    [COMSPEC] => C:\WINDOWS\system32\cmd.exe
    [PATHEXT] => .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
    [WINDIR] => C:\WINDOWS
    [SERVER_SIGNATURE] =>
    [SERVER_SOFTWARE] => Apache/2.2.4 (Win32)
    [SERVER_NAME] => localhost
    [SERVER_ADDR] => 127.0.0.1
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => 127.0.0.1
    [DOCUMENT_ROOT] => C:/Web/online/http
    [SERVER_ADMIN] => @@ServerAdmin@@
    [SCRIPT_FILENAME] => C:\Web\online\http\tests\error.php
    [REMOTE_PORT] => 1125
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => hl=en&q=php
    [REQUEST_URI] => /tests/error.php?hl=en&q=php
    [SCRIPT_NAME] => /tests/error.php
    [ORIG_SCRIPT_FILENAME] => C:/Web/online/http/tests/error.php
    [PHP_SELF] => /tests/error.php
    [REQUEST_TIME] => 1199154411
    [argv] => Array
        (
            [0] => hl=en&q=php
        )

    [argc] => 1
)

VI-F-2. Utilisation

Afin de simplifier la résolution des problèmes de conversion et de sécurité posés par la transmission de chaînes de caractères, il est préférable de transmettre autant que possible des valeurs numériques. Ces valeurs sont transmises en tant que texte pendant les échanges HTTP, mais elles n'ont aucun encodage spécial et nos scripts peuvent les filtrer sans complications.

Prendez garde à choisir des noms de paramètres utilisant uniquement l'alphabet anglais, des chiffres et pas d'espace, cela simplifie les opérations.

Il reste à parler de l'encodage, mais c'est un sujet qui mérite son propre chapitre.

VI-F-3. Construire une bonne URL

Par défaut, le protocole HTTP permet de faire des liens paramétrés que tout langage de script sait interpréter.

Sélectionnez
http://www.exemple.com/products.php?family=97&manufacturer=46

L'un des problèmes de cette approche est que l'URL est relativement laide pour un oeil humain, et par conséquent les humains la lisent rarement. Cela peut même conduire à certaines techniques de phishing, dans la mesure où personne ne fait plus attention au lien sur lequel il clique. Par ailleurs, les moteurs de recherche ont tendance à ne pas référencer toutes les URLs qui leurs semblent dynamiques, car ils savent qu'il y a trop de combinaisons possibles. Enfin, ces mêmes moteurs ne peuvent pas toujours trouver de mots clefs dans une URL dynamique, à cause de la majorité d'identifiants utilisés. Par exemple ci-dessus, "manufacturer" et "family" sont tout à fait inutiles pour le référencement de mon site, alors que le nom de la famille et du constructeur seraient d'une grande aide à la place de leurs identifiants.

Accessoirement, le mot clef "php" est enregistré ici pour une page dans laquelle il n'est aucunement question de PHP ou même de programmation.

Voici un exemple de conséquence : cherchez un podcast ou une conférence PHP sur eMule simplement en tapant "php" avec un type "audio". Vous trouverez de nombreux fichiers nommés "*.php.mp3" car ils ont probablement été téléchargés depuis un script PHP. L'apparition de "php" dans les noms des morceaux n'a aucune valeur mercatique, pourtant il apparaît et cela produit de très nombreux faux résultats dans nos recherches.

Un remède à certains de ces élément pourrait être une URL de ce type :

Sélectionnez
http://www.exemple.com/products/97/46/

Nous n'affichons pas les noms de variables (inutiles pour le référencement), l'URL est plus courte, on n'a pas l'impression d'avoir affaire à des paramètres mais à une arborescence de fichiers HTML statiques. Les avantages semblent être nombreux, mais l'inconvénient majeur est que nous avons peu de mots clefs. L'URL est propre, mais elle est peu efficace : "47" et "96" ne reflètent pas le contenu de ma base de données, ils n'aident pas au référencement de mon site.

Voici une solution vraisemblablement idéale :

Sélectionnez
http://www.exemple.com/products/processeurs/amd/

Si le serveur Web sait interpréter cette URL avec les mêmes paramètres que dans l'exemple précédent, alors elle est bien plus efficace.

Malheureusement, l'un des problèmes de cette approche est que les identifiants sont numériques dans la BDD (contrairement aux chaînes "processeurs" et "amd"), que le nom du fabricant peut être modifié à tout moment (ce qui aurait pour fâcheuse tendance de produire des erreurs 404 partout dans votre site) et que rien n'indique qu'il utilise exclusivement l'alphabet anglais en lettres minuscules (rappelez-vous que ce sont des impératifs pour une bonne URL). Et il y a aussi le problème de l'internationalisation (i18n).

Ainsi, une alternative acceptable (parmi d'autres) peut être :

Sélectionnez
http://fr.exemple.com/produits/97-processeurs/46-amd/

Mais bien sûr, de telles URLs ne correspondent pas à des fichiers sur le serveur Web car c'est impraticable, prend trop de place sur le disque ou bien augmente trop les accès disques. Il faut voir ces URLs comme un moyen virtuel d'avoir accès à une véritable ressource, mais c'est le serveur Web qui s'occupe de l'aspect virtuel (de manière totalement transparente pour l'internaute). Tous les grands serveurs Web du marché savent gérer ce que l'on appelle "URL Rewriting", ou encore "rééciture de liens".

Pour plus de renseignements : Tutoriel d'URL RewritingTutoriel de réécriture de liens en PHP avec Apache

VI-F-4. Construire un bon lien (balise HTML)

Construire un lien ne se fait pas à la légère. Renseignez-vous sur le WebmarketingCours et tutoriels de Webmarketing si vous souhaitez approfondir (ce que je vous recommande), mais sachez déjà que le plus important est d'avoir des URLs courtes et d'y mettre des mots clefs. Un lien efficace est uniquement construit par un travail particulièrement soigné.

L'un des aspects passe par le respect des standards. Lisez la documentation concernant le doctype que vous utilisez, que ce soit HTML 4 Transitionnal ou XHTML 1 Strict (pour ne citer que les extrêmes). Dans la plupart des cas, vous aurez affaire aux mêmes éléments du lien : href, title et le texte affiché dans la page. Chacun de ces éléments porte une sémantique précise et différente des autres, il faut donc les utiliser avec dicernement.

Il faut par exemple éviter de négliger la propriété title, qui permet d'ajouter des mots clefs parfois précieux.

Un élément fondamental de la balise lien est bien évidemment la propriété href. Il faut donc y apporter le plus grand soin. Le texte ou le titre d'un lien peuvent changer, mais l'URL (comme son nom l'indique) est un identifiant universel. Il ne faut changer l'URL d'un document qu'après mûre réflexion, et après s'être assuré que l'ancienne URL redirige correctement vers la nouvelle.

VI-F-5. Dangers

L'évolution d'un site cause parfois le changement de technologie (par ex. de JSP vers PHP), ce qui a des conséquences parfois dramatiques sur le référencement (erreurs 404 etc.) ; il faut donc à tout prix éviter de montrer l'extension du script dans son URL. Cela permet au serveur de changer de langage de script sans changer l'URL de chacun des documents.

VI-F-6. Bonnes pratiques

Demandez-vous ce qu'un utilisateur attentif aimerait voir apparaître dans l'URL comme indicateur de ce que contient le document. Utilisez aussi vos notions de Webmarketing et votre expérience de l'évolution d'un site pour déterminer la meilleure URL pour chaque document.

VI-G. Encodage des caractères

VI-G-1. Introduction

L'en-tête HTTP "Content-Type" contient un paramètre "charset". La valeur de ce paramètre permet au navigateur de savoir dans quel jeu de caractères est encodé le document qu'il est en train d'afficher. Par exemple, un document encodé en iso-8859-1 ne peut pas contenir certains caractères accentués dans son code source. Il existe néanmoins des équivalents appelés entités HTML : c'est ce que permet d'obtenir la fonction PHP htmlentities().

Comprendre toute la problématique des jeux de caractères est un travail fastidieux qui ne présente pas un grand intérêt. Chaque jeu de caractères est une représentation informatique des caractères utilisés dans un groupe de langues, par exemple ISO-8859-1 contient suffisamment de caractères pour représenter tous les caractères utilisés dans 24 langues (cf. WikipediaModern languages with complete coverage of their alphabet), ce qui n'est pas l'ensemble des langues au monde. Or nous nous intéressons ici à Internet, espace de rencontre et d'échange de toutes les cultures du monde : chaque site doit être en mesure d'afficher du texte issu de n'importe quelle langue, UTF-8 est donc le choix le plus judicieux à cet effet.
Les autres choix sont UTF-7, envers lequel l'Internet Mail Consortium a émis un avis défavorable ; et UTF-16, dont la représentation interne n'est pas compatible avec les formats antérieurs, ce qui implique de nombreux problèmes de migration des applications.

La fonction htmlspecialchars() permet d'encoder uniquement les caractères ayant une signification en XML (et par conséquent en HTML et en XHTML). La fonction inverse est html_entity_decode() et l'ensemble du jeu de caractères peut être déterminé par la fonction get_html_translation_table() :

Sélectionnez
<pre><?php
print_r(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES));
Caractères spéciaux HTML :
Sélectionnez
Array
(
    ["] => &quot;
    ['] => &#39;
    [<] => &lt;
    [>] => &gt;
    [&] => &amp;
)

La fonction htmlentities() renvoie la représentation HTML de chacun des caractères, donc les mêmes que pour htmlspecialchars() ainsi que les caractères accentués, etc. La fonction inverse est html_entity_decode() et l'ensemble du jeu de caractères peut être déterminé par la fonction get_html_translation_table() :

Sélectionnez
<pre><?php
print_r(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES));
Entités HTML :
Sélectionnez
Array
(
    [ ] => &nbsp;
    [&#161;] => &iexcl;
    [&#162;] => &cent;
    [£] => &pound;
    [&#164;] => &curren;
    [¥] => &yen;

    [&#166;] => &brvbar;
    [&#167;] => &sect;
    [¨] => &uml;
    [©] => &copy;
    [&#170;] => &ordf;
    [«] => &laquo;

    [&#172;] => &not;
    [&#173;] => &shy;
    [®] => &reg;
    [&#175;] => &macr;
    [°] => &deg;
    [&#177;] => &plusmn;

    [&#178;] => &sup2;
    [&#179;] => &sup3;
    [&#180;] => &acute;
    [µ] => &micro;
    [&#182;] => &para;
    [&#183;] => &middot;

    [&#184;] => &cedil;
    [&#185;] => &sup1;
    [&#186;] => &ordm;
    [»] => &raquo;
    [&#188;] => &frac14;
    [&#189;] => &frac12;

    [&#190;] => &frac34;
    [&#191;] => &iquest;
    [À] => &Agrave;
    [Á] => &Aacute;
    [Â] => &Acirc;
    [Ã] => &Atilde;

    [Ä] => &Auml;
    [Å] => &Aring;
    [Æ] => &AElig;
    [Ç] => &Ccedil;
    [È] => &Egrave;
    [É] => &Eacute;

    [Ê] => &Ecirc;
    [Ë] => &Euml;
    [Ì] => &Igrave;
    [Í] => &Iacute;
    [Î] => &Icirc;
    [Ï] => &Iuml;

    [&#208;] => &ETH;
    [&#209;] => &Ntilde;
    [Ò] => &Ograve;
    [Ó] => &Oacute;
    [Ô] => &Ocirc;
    [Õ] => &Otilde;

    [Ö] => &Ouml;
    [&#215;] => &times;
    [&#216;] => &Oslash;
    [Ù] => &Ugrave;
    [Ú] => &Uacute;
    [Û] => &Ucirc;

    [Ü] => &Uuml;
    [&#221;] => &Yacute;
    [&#222;] => &THORN;
    [ß] => &szlig;
    [à] => &agrave;
    [á] => &aacute;

    [â] => &acirc;
    [ã] => &atilde;
    [ä] => &auml;
    [å] => &aring;
    [æ] => &aelig;
    [ç] => &ccedil;

    [è] => &egrave;
    [é] => &eacute;
    [ê] => &ecirc;
    [ë] => &euml;
    [ì] => &igrave;
    [í] => &iacute;

    [î] => &icirc;
    [ï] => &iuml;
    [&#240;] => &eth;
    [&#241;] => &ntilde;
    [ò] => &ograve;
    [ó] => &oacute;

    [ô] => &ocirc;
    [õ] => &otilde;
    [ö] => &ouml;
    [&#247;] => &divide;
    [&#248;] => &oslash;
    [ù] => &ugrave;

    [ú] => &uacute;
    [û] => &ucirc;
    [ü] => &uuml;
    [&#253;] => &yacute;
    [&#254;] => &thorn;
    [&#255;] => &yuml;

    ["] => &quot;
    ['] => &#39;
    [<] => &lt;
    [>] => &gt;
    [&] => &amp;

)

La fonction utf8_encode(), quant à elle, renvoie une représentation UTF-8 de la chaîne en paramètre. L'encodage en UTF-8 permet de représenter plus de jeux de caractères que les jeux latins. Un jeu de caractères ISO-8859-1 est représenté par un octet, soit 2^8 valeurs possibles. UTF-8 dispose de 4 octets, soit 2^32 caractères possibles (moins les caractères de contrôle).

Prenez soin de toujours encoder les caractères en fonction du format de sortie (HTML ou autre). Ne pas utiliser htmlspecialchars() rend vos visiteurs vulnérables à des attaques XSS, et ne pas utiliser un format d'encodage unique tout au long du document génère des caractères illisibles (cf. quelques chapitres plus loin pour des exemples).

La meilleure tactique est de tout encoder en UTF-8. De cette manière, vous pouvez même afficher dans vos pages des caractères issus de jeux de caractères qui n'ont pas d'équivalent HTML, comme les langages dont l'alphabet n'est pas latin (arabe, chinois, japonais...).

Vous pouvez aussi utiliser htmlentities() sur toutes vos chaînes encodées en UTF-8 car cela produit du HTML indépendant du jeu de caratères, et le navigateur le comprend parfaitement l'encodage HTML. C'est un bon moyen d'éviter les erreurs si vous ne maîtrisez pas totalement le sujet, mais ce n'est pas la solution absolue puisque les jeux de caractères autres que l'UTF-8 ont généralement des lacunes : votre site serait hermétique à certaines langues.

En fait, tout le texte de vos pages Web devrait être affiché de cette manière :

Sélectionnez
echo utf8_encode(htmlspecialchars($text, ENT_QUOTES));

VI-G-2. Encodage du script : système de fichiers

Le script lui-même (en tant que fichier) porte un encodage dans le système de fichiers. C'est votre éditeur de code qui vous permet de modifier cet encodage, dont les valeurs les plus courantes sont "ANSI" ou bien "UTF-8".

L'encodage du script influe la manière dont PHP traite les chaînes de caractères présentes dans les scripts eux-mêmes.

Si vous essayez par exemple d'utiliser des caractères japonais dans vos scripts, vous aurez de mauvaises surprises si vos scripts sont encodés en ANSI. Certains EDI sont très confus à cause de cela. Modifiez l'encodage interne du script à "UTF-8" et vous pourrez utiliser les caractères du langage que vous voulez, à l'intérieur de vos scripts.

Ici par exemple, j'essaie de faire tenir le mot "japonais" (en japonais) dans un script encodé en UTF-8 puis en ANSI :

Image non disponible
Image non disponible

Il semble évident qu'UTF-8 doit être l'encodage de préférence pour vos scripts. Pensez à le préciser de manière bien visible dans la documentation de votre projet.

VI-G-3. Encodage du document : entités HTML

L'URL est affichée ainsi dans la barre d'adresse du navigateur :

Sélectionnez
http://localhost/tests/error.php?hl=en&q=php

Pourtant, il faut l'encoder en entités HTML dans le code source HTML :

Sélectionnez
<html>
<body>
http://localhost/tests/error.php?hl=en&amp;q=php
</body>
</html>

Pour y parvenir, PHP dispose des fonctions htmlspecialchars() et htmlentities(). La première convertit uniquement les caractères ayant une signification spéciale, tandis que l'autre convertit en entités HTML tous les caractères ayant une correspondance dans le jeu de caractères sélectionné en 3° paramètre.

On peut aussi utiliser la fonction ord() pour convertir du texte brut en texte HTML, mais cela produit des documents bien plus volumineux :

Sélectionnez
<?php
echo '&#'.ord('a'); //envoie "&#97" dans le source HTML, que le navigateur affiche "a"

La solution la plus efficace est soit d'utiliser htmlentities() avec le bon charset si on sait que le site n'est disponible que dans un certain nombre de langues (ce qui est extrêmement rare), soit d'utiliser htmlspecialchars() en convertissant le texte dans le charset de destination (par exemple UTF-8). Puisqu'htmlentities() ne sait pas gérer toutes les langues au monde, la solution avec htmlspecialchars() est largement meilleure :

Sélectionnez
<?php
$string = "...Du texte ici...";

header('Content-Type: text/html; charset=utf-8');
echo utf8_encode(htmlspecialchars($string, ENT_QUOTES));

VI-G-4. Encodage d'URL : la RFC 3986

Un autre aspect à maîtriser est la barre d'adresse du navigateur, qui représente le format des URLs dans les requêtes HTTP. Ce format est défini dans la RFC 3986RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax.

Si je charge l'URL suivante dans mon navigateur, il la traduit avant de la transmettre au serveur Web :

URL écrite à la main dans la barre d'adresse :
Sélectionnez
http://localhost/test.php?q=forum cinéma
Requête HTTP transmise par le navigateur au serveur Web :
Sélectionnez
GET /test.php?q=forum%20cin%C3%A9ma HTTP/1.1
Host: localhost

Il faut encoder séparément au format "RFC 3986" tous les éléments de la "query string" (donc ici la valeur "forum cinéma"), mais pas le reste de l'URL. D'excellents commentaires de la documentation officielle expliquent très clairement ce qui doit être encodé et avec quelle fonction (urlencode() ou bien rawurlencode()) :

  • http://fr2.php.net/manual/fr/function.rawurlencode.php#25182
  • http://fr2.php.net/manual/fr/function.rawurlencode.php#26869
  • http://fr2.php.net/manual/fr/function.urlencode.php#56426
  • http://fr2.php.net/manual/fr/function.urlencode.php#71706

Tous les caractères non alphanumériques et hors "-_." ont un code "URL encoded". Par exemple, le simple mot "cinéma" est encodé ainsi pour une URL (attention à l'encodage interne de votre script, cf. les options de votre éditeur de code) :

Script encodé en ANSI :
Sélectionnez
<?php

//affiche "forum+cin?ma"
echo urlencode('forum cinéma');

//affiche "forum cin?ma"
echo rawurlencode('forum cinéma');

//affiche "forum+cinéma"
echo urlencode(utf8_encode('forum cinéma'));

//affiche "forum cinéma"
echo rawurlencode(utf8_encode('forum cinéma'));
Script encodé en UTF-8 :
Sélectionnez
<?php

//affiche "forum+cin?ma"
echo urlencode(utf8_decode('forum cinéma')).'<br/>';

//affiche "forum cin?ma"
echo rawurlencode(utf8_decode('forum cinéma')).'<br/>';

//affiche "forum+cinéma"
echo urlencode('forum cinéma').'<br/>';

//affiche "forum cinéma"
echo rawurlencode('forum cinéma').'<br/>';

On voit ici que la représentation interne des caractères du script (encodage du script, ou file encoding) a une incidence sur la représentation "URL encoded". Dans l'idéal, la représentation URL des caractères doit être identique au format d'encodage du document, mais c'est très complexe à mettre en place de manière exacte. Malheureusement, et bien que les navigateurs se débrouillent très bien à ce niveau, leur laisser un peu de liberté débouche trop souvent sur une faille de sécurité (même si la faute est bien souvent du site et non du navigateur). Ma recommandation est de tout encoder au format UTF-8, qui tend à devenir le standard sur Internet. L'arrivée d'Unicode dans PHP6 permettra de clarifier tout cela.

PHP décode automatiquement les caractères encodés selon la RFC 3986 avant le début du script, il est donc rarement nécessaire d'utiliser urldecode().

Les caractères à encoder sont les noms de fichiers, les noms des variables et leurs valeurs. Il ne faut pas encoder "?", "=" et "&" s'ils sont utilisés comme séparateurs, mais uniquement en tant que valeurs.

Par exemple, si je souhaite afficher un lien pour une recherche Google à propos de "Boule & Bill", les espaces et le "&" doivent être encodés puisqu'ils ne font pas partie de l'alphabet anglais :

Sélectionnez
http://www.google.com/search?hl=fr&q=Boule%20%26%20Bill

Voici un autre exemple avec "E=mc²", où "=" est converti uniquement lorsqu'il est utilisé comme partie de la question :

Sélectionnez
http://www.google.com/search?hl=en&q=E%3Dmc%C2%B2

VI-G-5. Entités HTML + RFC 3986

Ne pas confondre l'encodage en vue d'afficher le lien dans une page HTML, et l'encodage en vue de transmettre une requête GET !

Reprenons l'exemple de "Boule & Bill". Je dois convertir ce texte d'abord selon la RFC 3986 puis en entités HTML :

En-tête HTTP à transmettre par le navigateur :
Sélectionnez
GET /search?hl=fr&q=Boule%20%26%20Bill HTTP/1.1
Host: www.google.com
Lien à mettre dans le code HTML :
Sélectionnez
<a
    href="http://www.google.com/search?hl=fr&amp;q=Boule%20%26%20Bill"
    title="Recherche Google">Boule &amp; Bill</a>
Construction du lien en PHP :
Sélectionnez
<?php
//correspond à l'en-tête HTTP (brute) à envoyer
$url = 'http://www.google.com/search?hl='
    . rawurlencode('fr') //paramètre à encoder selon la RFC 3986
    . '&q='
    . rawurlencode('Boule & Bill'); //paramètre à encoder selon la RFC 3986

header('Content-Type: text/html; charset=utf-8');
?>
<a
    href="<?php echo html($url);?>"
    title="<?php echo html('Recherche Google'); ?>"
    ><?php echo html('Boule & Bill');?></a>
<?php

function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
Il faut bien faire la distinction entre toutes les formes du caractère "&"
  • & est la forme brute, elle porte une signification dans la "query string" finale puisqu'elle sépare les paramètres : c'est un séparateur dans l'URL ;
  • &amp; est la forme encodée en HTML, qui doit être placée dans la page Web afin que le navigateur la comprenne : elle a une signification HTML ;
  • %26 est la forme encodée selon la RFC 3986, elle ne porte aucune signification spéciale ni dans l'URL ni dans le HTML : elle est utilisée seulement en tant que valeur.

Cette méthode ne laisse pas de place aux erreurs, les failles de sécurité sont donc bien plus rares. Prenez garde aux séparateurs car ils peuvent avoir une signification différente (ou ne pas en avoir) selon le contexte, comme "&". Dans l'exemple ci-dessus, la chaîne "Boule & Bill" a été encodée de trois manières successives : une pour chaque contexte. L'ordre d'encodage est de type LIFO.

Certains navigateurs affichent ce lien en texte clair (décodé) dans la barre d'état au passage de la souris, mais transmettent malgré tout l'en-tête encodé.

Image non disponible
Image non disponible
Image non disponible

VI-G-6. Exemple d'encodage UTF-8

URL :
Sélectionnez
http://localhost/test.php?q=forum+cinéma
test.php :
Sélectionnez
<?php
header('text/html; charset=utf-8');
?>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>Test</title>
    <meta http-equiv="content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<?php echo htmlentities($_GET['q'], ENT_QUOTES, 'utf-8') ;?>
</body>
</html>
Image non disponible

VI-G-7. Exemple d'encodage ISO

URL :
Sélectionnez
http://localhost/test.php?q=forum+cin?ma
test.php :
Sélectionnez
<?php
header('text/html; charset=iso-8859-1');
?>
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>Test</title>
    <meta http-equiv="content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<?php echo htmlentities($_GET['q'], ENT_QUOTES, 'iso-8859-1') ;?>
</body>
</html>
Image non disponible

VI-G-8. Exemple de caractères UTF-8 encodés en ISO

Sélectionnez
http://localhost/test.php?q=forum+cinéma
Sélectionnez
<?php
header('text/html; charset=iso-8859-1');
?>
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>Test</title>
    <meta http-equiv="content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<?php echo htmlentities($_GET['q'], ENT_QUOTES, 'iso-8859-1') ;?>
</body>
</html>
Image non disponible
Image non disponible
Image non disponible

VI-G-9. Exemple de caractères ISO encodés en UTF-8

Sélectionnez
http://localhost/test.php?q=forum+cin?ma
Sélectionnez
<?php
header('text/html; charset=utf-8');
?>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>Test</title>
    <meta http-equiv="content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<?php echo htmlentities($_GET['q'], ENT_QUOTES, 'utf-8') ;?>
</body>
</html>
Image non disponible
Image non disponible
Image non disponible

Si, dans cet exemple, Internet Explorer affiche des caractères lisibles, c'est en fait une erreur de sa part puisque le format du document est incorrect. Internet Explorer estime que le format d'encodage est incorrect, et prend la liberté de l'adapter à sa manière. Lucky guess, comme disent les anglophones. En revanche, Firefox et Opera ont raison de ne pas afficher une chaîne lisible. Il me semble d'ailleurs qu'Internet Explorer 6 affichait un simple "?".

VI-G-10. Espace occupé par l'encodage

L'un des aspects pris en compte par certains projets pour la sélection du charset, ou plutôt l'un des facteurs qui porte injustement du tort à UTF-8, est la taille de l'encodage des caractères. De nombreuses personnent pensent que, puisqu'UTF-8 est un jeu de caractères multibytes, des documents encodés en UTF-8 prendront nécessairement plus de place qu'en ANSI ou ISO. Cette information n'est pas totalement vraie, et il y a un moyen très simple pour le démontrer.

Le document produit par ce code pèse 36 octets :

Sélectionnez
<?php
header('Content-Type: text/html; charset=ISO-8859-1');
echo htmlspecialchars('Developpez, le Club des développeurs', ENT_QUOTES);

Le document produit par ce code pèse 37 octets :

Sélectionnez
<?php
header('Content-Type: text/html; charset=UTF-8');
echo utf8_encode(htmlspecialchars(
    'Developpez, le Club des développeurs',
    ENT_QUOTES));

L'exemple ci-dessus montre que tout caractère est encodé le plus bas possible : tous les caractères du jeu ASCII standard sont encodés sur 7 bits ; seul l'accent prend plus d'un octet puisqu'il ne fait pas partie d'ASCII (encodé sur 7 bits) et puisqu'ASCII étendu (encodé sur un octet complet) n'est pas pleinement standardisé ou suivi.

Ainsi, on peut parfaitement encoder intégralement nos documents en UTF-8 sans avoir une taille de fichier excessive. Tous les caractères UTF-8 ne sont pas encodés sur 4 octets, la taille du texte reste donc acceptable. Naturellement, il en va de même pour les bases de données et toutes les autres représentations d'UTF-8.

VI-G-11. Dangers

Un document mal encodé est souvent vulnérable à des failles de sécurité comme XSS. Même dans les situations qui ne présentent pas de danger, un document mal encodé n'est pas valide et cela peut amener le navigateur à réagir de manière inattendue. Les captures d'écran fournies ci-dessus en sont un bon exemple.

VI-G-12. Bonnes pratiques

Comme nous l'avons vu, le meilleur choix pour présenter des pages Web est d'utiliser un jeu de caractères UTF-8. C'est celui qui offre le panel le plus vaste de caractères, c'est le plus complet des jeux de caractères "multibytes" sans pour autant être très compliqué ou très lourd.

Par ailleurs, nous avons vu qu'il y a plusieurs méthodes pour renseigner le navigateur sur le charset d'une page Web :

En-tête HTTP implicite depuis php.ini (les deux directives sont PHP_INI_ALL) :
Sélectionnez
default_mimetype = "text/html"
default_charset = "utf-8"
En-tête HTTP explicite dans le script PHP :
Sélectionnez
<?php
header('Content-Type: text/html; charset=utf-8');
Balise META dans le document :
Sélectionnez
<meta http-equiv="content-Type" content="text/html; charset=utf-8" />

La configuration du php.ini semble pratique, mais elle n'est pas nécessairement appropriée si l'on utilise des scripts écrits par d'autres personnes, issus d'un autre projet, etc. Il ne faut pas se reposer sur cette configuration puisqu'elle ne correspond pas nécessairement à notre besoin.

Utiliser les deux autres méthodes dans un même document semble redondant, mais il faut prendre en compte certains aspects.

D'une part, transmettre un document HTML depuis le serveur Web vers un navigateur est une chose, et les en-têtes HTTP permettent au navigateur de savoir de quoi il s'agit, mais ils ne persistent pas dans le document une fois qu'il est copié sur une clef USB, dans un e-mail, etc. Il est donc intéressant d'avoir l'information du charset dans le document lui-même, afin que toutes les copies puissent être restituées fidèlement.

D'autre part, il faut prendre en compte la configuration par défaut du serveur Web : si Apache est configuré pour envoyer un charset ISO alors que notre document est encodé en UFT-8, cela produit un conflit et le navigateur Web peut avoir un comportement imprévisible. Utiliser la fonction header() en PHP permet de remplacer l'en-tête envoyé par défaut par Apache, et ainsi de solutionner le problème. Nous ne pouvons pas non plus n'envoyer aucun en-tête par défaut avec Apache, car alors les documents que nous enverrons sans en-tête (sans doute par mégarde ou parce que ce sont des scripts d'une personne tierce qui n'a pas prévu tous ces cas de figure) seront transmis sans aucun en-tête HTTP et sans information sur le type de contenu et l'encodage, ce qui peut avoir un effet désastreux au moment de l'affichage du document.

Il nous faut donc envoyer le type de document et son encodage à la fois au moyen d'en-têtes HTTP et dans une balise META, sans pour autant désactiver la configuration par défaut du serveur Web. Cela fait triple emploi mais c'est la seule solution à toute épreuve.

Utilisez toujours les outils de validationLes meilleurs outils (X)HTML > Validateurs de code pour vérifier que vos documents sont corrects.

Voici une citation de Rasmus Lerdorf pour terminer : "Ultimately we need to get to Unicode everywhereArchives de php.internals".

VI-G-13. Le module iconv

Le module iconv est une extension PHP incluse dans le noyau. Son activation a donc lieu lors de la compilation de PHP.

L'un des problèmes d'iconv est son manque de fonctionnalités. L'extension mbstring est bien plus intéressante à ce niveau.

URL à charger :
Sélectionnez
http://localhost/test.php?string=Le club des d?veloppeurs
test.php
Sélectionnez
<?php
header('Content-Type: text/html; charset=utf-8');
echo empty($_GET['string']) ?
    '' :
    iconv('ISO-8859-1', 'UTF-8', $_GET['string']);
Sélectionnez
Le club des développeurs

Ici par exemple, il nous faut savoir avec exactitude le charset des données en entrée. La moindre erreur est ennuyeuse :

Sélectionnez
Notice: iconv() [function.iconv]: Detected an illegal character
    in input string in C:\Web\online\http\test.php on line 2

Voici une solution plus élégante mais qui nécessite mbstring :

php.ini
Sélectionnez
mbstring.detect_order = ISO-8859-1, UTF-8
URL à charger :
Sélectionnez
http://localhost/test.php?string=Le club des d?veloppeurs
test.php
Sélectionnez
<?php
header('Content-Type: text/html; charset=utf-8');
echo empty($_GET['string']) ?
    '' :
    iconv(mb_detect_encoding($_GET['string']), 'UTF-8', $_GET['string']);
Sélectionnez
Le club des développeurs

Bien entendu, il est toujours possible d'utiliser une fonction pour simplifier la procédure :

Sélectionnez
<?php
header('Content-Type: text/html; charset=utf-8');
echo empty($_GET['string']) ? '' : to_utf8($_GET['string']);

function to_utf8($string)
{
    return iconv(mb_detect_encoding($string), 'UTF-8', $string);
}

Pour conclure sur iconv, le module me semble peu intéressant, comparé à par exemple l'extension mbstring. En effet, il contraint le développeur soit à utiliser une autre extension pour déterminer le charset des variables (ce qui est absurde, dans la mesure où cette autre extension peut fonctionner seule), soit à savoir exactement de quel encodage est chaque variable (ce qui est totalement utopique dans un environnement Web). Il est possible d'utiliser les headers HTTP envoyés par le navigateur client, mais que se passerait-il si un navigateur malicieux cherchait à faire passer une chaîne UTF-8 pour de l'ISO-8859-1 ? Je crains que cela ouvre l'application à des failles de sécurité...

Cela dit, certaines fonctions d'iconv ont leur utilité, par exemple iconv_mime_decode_headers().

VI-G-14. L'extension mbstring

Jusqu'à l'arrivée de PHP 6, le langage PHP n'est pas prévu pour fonctionner nativement avec Unicode. Toutes les opérations classiques sur les chaînes, par exemple strpos() et substr(), ne fonctionnent pas avec les jeux de caractères "multibyte" tel que le japonais. Aucun alphabet non latin ne fonctionne correctement avec ces fonctions.

La bibliothèque mbstring permet de remédier à ce problème. Elle fournit des fonctions alternatives pour les tâches les plus courantes, par exemple mb_strlen() à la place de strlen() ou encore mb_strpos() au lieu de strpos(). Toutes les opérations sur les chaînes ne sont cependant pas disponibles, par exemple l'extension PCRE n'a pas été adaptée.

Voici un exemple de script qui ne fonctionne pas comme on pourrait s'y attendre :

Image non disponible

Imaginez que je veuille utiliser substr($string, 0, 2), est-ce que j'obtiendrais les 2 premiers caractères ou bien les 2/3 du premier caractère ? <insert smiley here>

En revanche, la bibliothèque mbstring permet de préciser l'encodage interne des caractères et d'en tenir compte :

Image non disponible

Cette fois, si j'utilise mb_substr($string, 0, 2), je suis sûr d'obtenir les 2 premiers caractères du mot.

La plupart des applications Web développées pour nos pays n'ont pas besoin de fonctions de texte aussi complexes. Vous ne devriez considérer l'utilisation de cette extension que si vous en avez réellement besoin, ou peut-être attendre PHP 6 pour uniformiser le comportement des chaînes Unicode.

NB : Les caractères utilisés ci-dessus sont la traduction japonaise du mot "japonais" selon Wikipédia.

Voici un exemple de conversion de chaînes d'entrée :

Sélectionnez
<?php
mb_internal_encoding('UTF-8');

$variable_1 = to_utf8($variable_1);
$variable_2 = to_utf8($variable_2);
$variable_3 = to_utf8($variable_3);

function to_utf8($string)
{
    return mb_convert_variables('UTF-8', mb_detect_encoding($string), $string);
}

Vous pouvez aussi utiliser cette autre méthode, moins fastidieuse, qui convertit automatiquement toutes les variables d'entrée :

Configuration PHP_INI_PERDIR :
Sélectionnez
[mbstring]
mbstring.internal_encoding = UTF-8
mbstring.http_output = UTF-8
mbstring.encoding_translation = On
mbstring.detect_order = auto
mbstring.substitute_character = none
mbstring.strict_encoding = On

VI-G-15. Expressions régulières (PCRE) et Unicode

Je ne compte pas entrer ici dans le détail des regex, puisque c'est un sujet trop vaste pour être abordé convenablement en quelques lignes. Cependant, un aspect de PCRE est souvent omis : les fonctions preg_*() peuvent gérer les caractères multibytes à condition de les écrire sous leur forme hexadécimale et d'utiliser le modificateur "u".

Par exemple, pour vérifier qu'un texte fait partie de l'un des trois systèmes d'écriture japonaise, on peut utiliser la regex suivante :

Sélectionnez
<?php
if(preg_match('/[&#19968;-&#40895;]/u', $variable))
{
    echo 'Kanji';
}
else if(preg_match('/[&#12352;-&#12447;]/u', $variable))
{
    echo 'Hiragana';
}
else if(preg_match('/[&#12448;-&#12543;]/u', $variable))
{
    echo 'Katagana';
}
else
{
    echo 'Pas un mot japonais';
}

Une chaîne UTF-8 invalide arrête la regex sans rien trouver. Avant d'utiliser ces masques, il est donc nécessaire de suivre quelques conseils, cf. ce commentaire de la documentationRegarding the validity of a UTF-8 string when using the /u pattern modifier.


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/

VII. Manipulation de données

Dans un environnement Web, il y a trois sources principales de données : une base de données relationnelle ou objet, des fichiers XML et des services Web.

VII-A. Bases de données relationnelles

VII-A-1. Introduction

Les bases de données permettent d'emmagasiner et de retrouver de manière simple et efficace de très grandes quantités d'informations de tous types : numérique, texte, multimédia...

Pour accéder à une base de données, il faut généralement disposer d'un code d'accès, de l'adresse IP du serveur et du nom de la base de données sur le serveur. Une dernière information nous indique le type de la BDD qui nous servira pour lancer nos requêtes (MySQL, PostgreSQL, Oracle...).

La plupart des APIs fonctionnent sur un principe procédural, orienté objet ou bien les deux. Nous utiliserons de préférence la POO pour la flexibilité qu'elle nous offre.

Je vais partir du principe que vous connaissez les bases de données relationnelles et le langage SQL.

L'accès à une BDD peut se faire soit au moyen d'une API spécialisée pour votre BDD (MySQL, PostgreSQL, Oracle...), soit à travers une API générique (aussi appelée couche d'abstraction). Puisque les couches d'abstraction laissent davantage de liberté et de flexibilité que les APIs, il n'y a aucun avantage à utiliser une API spécifique. En revanche, le plus gros avantage d'utiliser une telle couche d'abstraction est de donner le choix du SGBD avec un minimum de modifications sur le code de l'application PHP.

Je vous propose donc d'utiliser PHP Data ObjectsFAQ PHP (alias PDO), qui est la couche d'abstraction fournie en standard dans PHP.

Extensions PHP nécessaires : php_pdo + php_pdo_mysql (cette deuxième extension correspond à votre SGBD, dans mon cas MySQL).

L'avantage principal de PDO est l'extrême simplicité de changement de SGBD : il suffit de changer un paramètre lors de l'instanciation de PDO, le reste du code est inchangé. Cela permet par exemple de coder une application et de la vendre à n'importe quel client, quel que soit son SGBD de prédilection, sans surcharge de travail. Par conséquent, il n'est pas nécessaire d'apprendre le fonctionnement de chaque API spécifique, PDO est suffisant. Vos développements prennent donc moins de temps.

Des exemples de chaînes de connexion se trouvent dans la FAQ PHPExemples PDO, et d'autres tutoriels sont disponibles ici : Cours SGBD en PHP Les meilleurs cours et tutoriels PHP : SGBD.

VII-A-2. Accès aux données

L'accès à une base de données est soumis à diverses étapes :
  • Ouverture de la connexion au serveur ;
  • Choix de la BDD sur le serveur (cela se fait parfois au moment de l'ouverture de la connexion) ;
  • Préparation de requêtes (facultatif, dépend de l'API utilisée) ;
  • Exécution de requêtes et récupération des résultats.

L'envoi de requêtes implique souvent le parcours des résultats, et dans certains cas la préparation préalable des requêtes (c'est le cas de PDO).

La connexion au serveur et la sélection de la BDD se font lors de l'instanciation de l'objet PDO :

Sélectionnez
$db = new PDO('mysql:host=localhost;dbname=developpez', 'utilisateur', 'motdepasse');

Pour envoyer une requête, il y a plusieurs solutions. La plus sécurisée est de passer par des requêtes préparées (prepared statements) :

Sélectionnez
$db = new PDO('mysql:host=localhost;dbname=developpez', 'utilisateur', 'motdepasse');

$select_users = $db->prepare('SELECT id, name FROM user');
$select_user = $db->prepare('SELECT id, name FROM user WHERE user_id = ?');
$insert_user = $db->prepare('INSERT INTO user (name, password) VALUES (?, ?)');

//récupère tous les utilisateurs (aucun puisque la table est vide)
$select_users->execute();
$users = $select_users->fetchAll();

$insert_user->execute(array('BrYs', '4321'));
$insert_user->execute(array('mathieu', '4321'));
$insert_user->execute(array('Yogui', '4321'));

$select_users->execute(); //récupère tous les utilisateurs (les 3)
$users = $select_users->fetchAll();

$select_user->execute(array(1)); //récupère l'utilisateur numéro 1
$user_1 = $select_user->fetchAll();

$select_user->execute(array(2)); //récupère l'utilisateur numéro 2
$user_2 = $select_user->fetchAll();

$select_user->execute(array(3)); //récupère l'utilisateur numéro 3
$user_3 = $select_user->fetchAll();

Comme nous pouvons le voir, une requête se prépare une seule fois pour l'ensemble du script. Chaque exécution de la requête se fait alors par la méthode execute(), et la récupération des résultats par la méthode fetchAll().

Bien sûr, il est possible d'envoyer des requêtes directement depuis l'objet $db, mais alors il n'y a aucune forme de sécurisation des données puisque la méthode quote() n'est pas disponible pour tous les pilotes de BDD. Un code qui envoie des requêtes non préparées n'est donc pas portable.

Il y a deux avantages majeurs à préparer les requêtes :
  • Chaque exécution est plus rapide en requête préparée qu'en envoyant la totalité de la requête à chaque fois ;
  • Les paramètres sont transmis à la requête sous forme binaire, ce qui évite tout risque d'injection SQL.

Les paramètres peuvent être transmis à la requête préparée soit dans l'ordre, soit sous forme nommée. Les paramètres nommés peuvent être envoyés dans n'importe quel ordre :

Sélectionnez
<?php
$db = new PDO(
    'mysql:host=localhost;dbname=developpez',
    'utilisateur',
    'motdepasse');

$insert_1 = $db->prepare(
    'INSERT INTO user (name, password) VALUES (?, ?)');

$insert_2 = $db->prepare(
    'INSERT INTO user (name, password) VALUES (:name, :password)');

$insert_1->execute(array('BrYs', '4321'));
$insert_2->execute(array(
    ':password' => '4321',
    ':name' => 'BrYs'
));

VII-A-3. Performances

Dans une architecture classique, le serveur HTTP et le SGBD sont deux machines différentes sur un même réseau local. Exactement combien de machines entrent en jeu n'est pas notre propos, mais cela nous indique le protocole de communication habituellement utilisé pour le dialogue entre les deux applications (par exemple entre Apache et MySQL) : TCP/IP. Toutes les requêtes et leurs résultats transitent par la couche réseau. Il est donc plutôt facile de congestionner un réseau avec des requêtes renvoyant de nombreux résultats, surtout si elles sont fréquemment exécutées. C'est l'une des raisons en défaveur de la pratique "SELECT *", puisqu'elle retourne plus de champs qu'il n'est nécessaire, et fait donc transiter un gand nombre d'informations inutiles par le réseau.

C'est aussi à cause de ces échanges qu'il est important de réduire le nombre de requêtes exécutées à chaque page, sans pour autant que cela devienne une guerre sainte... Chaque exécution de requête implique d'attendre la réponse du SGBD avant de pouvoir poursuivre l'exécution du script, ce qui correspond à un aller-retour TCP/IP entre le serveur HTTP et le SGBD. La communication entre deux machines par un réseau local est certes rapide, mais c'est sans commune mesure avec des optimisations de type "for plutôt que foreach". Soyez conscients que ce qui ralentit le plus votre application est sans doute davantage le temps nécessaire pour faire un aller-retour de requête et de son résultat, que des optimisations in-line de votre code PHP. J'ai entendu Tim BrayConférence Internationale PHP 2006 - Keynote rappeler aux développeurs qu'un système de fichiers est souvent plus rapide qu'un SGBD : évaluez votre besoin, comparez ce que vous apporte chaque SGBD. SQLite est parfois un bon compromis.

Le principe des requêtes préparées permet d'enregistrer temporairement chaque requête du côté du SGBD. Ainsi, en plus des optimisations que cela permet d'obtenir côté serveur, pour exécuter chaque requête il suffit de renvoyer les paramètres (au lieu de l'ensemble de la requête). Avec l'avantage sécurité que nous avons déjà mentionné, cela nous donne une longue liste d'avantages en faveur des requêtes préparées...

À des fins d'illustration, voici un exemple (à ne pas suivre) utilisant une base de données de forum avec une clef étrangère entre message.user_id et user.id :

Sélectionnez
<?php
mysql_connect('localhost', 'utilisateur', 'motdepasse');
mysql_select_db('developpez');

$sql = 'SELECT id, title, user_id
        FROM message
        ORDER BY post_date';
$db_messages = mysql_query($sql);

header('Content-Type: text/html; charset=utf-8');
while($message = mysql_fetch_array($db_messages))
{
    $sql = 'SELECT name
            FROM user
            WHERE id = '.(int)$message['user_id'];

    $db_user = mysql_query($sql);
    $user = mysql_fetch_array($db_user);

    echo utf8_encode(
        htmlspecialchars($message['title'].' par '.$user['name'], ENT_QUOTES))
        .'<br/>';
}
Voici les erreurs du script ci-dessus :
  • Utilisation de l'API MySQL plutôt que PDO ou même MySQLi, ce qui est un mauvais choix par rapport aux performances et à la portabilité du code ;
  • Utilisation de *fetch_array, qui retourne un tableau de résultats à la fois sous forme d'index numérique et d'index associatif, ce qui consomme inutilement de la mémoire ;
  • Exécution de nombreuses requêtes alors qu'INNER JOIN aurait été plus efficace.

Le script suivant donne le même résultat :

Sélectionnez
<?php
$db = new PDO(
    'mysql:host=localhost;dbname=developpez',
    'utilisateur',
    'motdepasse');

$sql = 'SELECT m.id, m.title, u.name AS author
        FROM message AS m
        INNER JOIN user AS u ON m.user_id = u.id
        ORDER BY post_date';

$select_messages = $db->prepare($sql);
$select_messages->setFetchMode(PDO::FETCH_ASSOC);
$select_messages->execute();

header('Content-Type: text/html; charset=utf-8');
foreach($select_messages->fetchAll() as $message)
{
    echo utf8_encode(htmlspecialchars(
        $message['title'].' par '.$message['author'], ENT_QUOTES))
        .'<br/>';
}

VII-A-4. Bonnes pratiques

La POO autorisant le principe d'héritage, autant en profiter. Dans l'exemple ci-dessus, on peut voir que les sélections utilisent deux méthodes à la suite (execute() puis fetchAll()) : cela peut être simplifié par l'héritage. Il faut également prendre en compte la configuration de la classe : chaque connexion dispose d'un certain nombre d'attributs, et PDO peut être configuré pour envoyer des exceptions plutôt que des erreurs PHP. Tout cela peut être fait dans une classe dérivée de PDO, afin de nous assurer que tous nos objets $db en bénéficient dans tous nos projets.

Nous aimerions avoir du code applicatif simple et concis, par exemple :

Sélectionnez
<?php
$db = new MyPDO(
    'mysql:host=localhost;dbname=developpez', 'utilisateur', 'motdepasse');

$userTable = new UserTable($db);

$userTable->truncate();
$userTable->insert('Yogui', '4321');
$userTable->insert('mathieu', '4321');
$userTable->insert('BrYs', '4321');

?><pre><?php
print_r($userTable->selectById(1));
print_r($userTable->selectByName('u'));
print_r($userTable->selectAll());

Pour y parvenir, nous avons besoin d'une classe "UserTable" qui représente la table du même nom :

Sélectionnez
<?php
class UserTable
{
    /*
     * Requêtes préparées
     */
    private $selectById;
    private $selectByName;
    private $selectAll;
    private $insert;
    private $truncate;

    public function __construct($db)
    {
        $this->selectAll = $db->prepare(
            "SELECT id, name FROM user ORDER BY name, id");

        $this->selectById = $db->prepare(
            "SELECT id, name FROM user WHERE id = ?");

        $this->selectByName = $db->prepare(
            "SELECT id, name FROM user WHERE name LIKE ? ORDER BY name, id");

        $this->insert = $db->prepare(
            "INSERT INTO user (name, password) VALUES (:name, :password)");

        $this->truncate = $db->prepare("TRUNCATE user");
    }

    public function insert($name, $password)
    {
        $this->insert->execute(
            array(':name' => $name, ':password' => $password));

        return $this->insert->rowCount();
    }

    public function selectAll()
    {
        $this->selectAll->execute();
        return $this->selectAll->fetchAll();
    }

    public function selectById($id)
    {
        $this->selectById->execute(array($id));
        return $this->selectById->fetch();
    }

    public function selectByName($name)
    {
        $this->selectByName->execute(array('%'.$name.'%'));
        return $this->selectByName->fetchAll();
    }

    public function truncate()
    {
        return $this->truncate->execute();
    }
}

Tout cela repose sur notre classe dérivée de PDO, qui configure la connexion ainsi que la manière de préparer les requêtes :

Sélectionnez
<?php
class MyPDO extends PDO
{
    public function __construct($dsn, $user=NULL, $password=NULL)
    {
        parent::__construct($dsn, $user, $password);
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    public function prepare($sql, $options=NULL)
    {
        $statement = parent::prepare($sql);
        if(strpos(strtoupper($sql), 'SELECT') === 0) //requête "SELECT"
        {
            $statement->setFetchMode(PDO::FETCH_ASSOC);
        }

        return $statement;
    }
}

Résultat :

Sélectionnez
Array
(
    [id] => 1
    [name] => Yogui
)
Array
(
    [0] => Array
        (
            [id] => 2
            [name] => mathieu
        )

    [1] => Array
        (
            [id] => 1
            [name] => Yogui
        )

)
Array
(
    [0] => Array
        (
            [id] => 3
            [name] => BrYs
        )

    [1] => Array
        (
            [id] => 2
            [name] => mathieu
        )

    [2] => Array
        (
            [id] => 1
            [name] => Yogui
        )

)

Maintenant est un bon moment pour vous remémorer mon exemple de code lors de l'explication des Late Static Bindings, et pour faire des essais de votre côté.

Tous les frameworks PHP proposent une solution bien mieux adaptée que celle que je présente ici. Par exemple, symfony génère deux classes pour chaque table de la BDD : l'une contient uniquement des méthodes statiques d'accès aux données au niveau de la table, et l'autre est la représentation objet d'un seul tuple de la table. Zend Framework, quant à lui, opte pour deux classes à dériver : Zend_Db_Table_Abstract et Zend_Db_Table_Row_Abstract.

La base de données étant une source de données externe au script, les données qui en proviennent doivent être traitées de la même manière que les données utilisateur : avec prudence. Il faut valider et filtrer les données issues de la BDD.

Pour information, voici le code applicatif ci-dessus écrit avec l'API de base de PHP pour MySQL. Le majeur problème de cette approche est qu'elle est spécifique à MySQL et que nous obligeons ainsi tous nos clients à utiliser MySQL, même s'ils avaient déjà un SGBD en lequel ils avaient confiance (par exemple PostgreSQL ou Oracle). Changer de SGBD dans nos scripts nous oblige à effectuer de nombreuses modifications : PDO permet d'éviter ce genre de problèmes.

Sélectionnez
<?php
mysql_connect('localhost', 'utilisateur', 'motdepasse');
mysql_select_db('developpez');

db_user_truncate();
db_user_insert('Yogui', '4321');
db_user_insert('mathieu', '4321');
db_user_insert('BrYs', '4321');

?><pre><?php
print_r(db_user_select_by_id(1));
print_r(db_user_select_by_name('u'));
print_r(db_user_select_all());
Sélectionnez
<?php
function db_user_insert($name, $password)
{
    $sql = "INSERT INTO user(name, password) VALUES('%s', '%s')";
    mysql_query(sprintf(
        $sql,
        mysql_real_escape_string($name),
        mysql_real_escape_string($password)));

    return mysql_affected_rows();
}

function db_user_select_by_id($id)
{
    $sql = "SELECT id, name FROM user WHERE id = %d";
    $db_result = mysql_query(sprintf($sql, $id));
    if($user = mysql_fetch_assoc($db_result))
    {
        return $user;
    }
    else
    {
        return NULL;
    }
}

function db_user_select_by_name($name)
{
    $sql = "SELECT id, name FROM user WHERE name LIKE '%s' ORDER BY name";
    $db_result = mysql_query(sprintf(
        $sql,
        mysql_real_escape_string('%'.$name.'%')));

    $users = array();
    while($user = mysql_fetch_assoc($db_result))
    {
        $users[] = $user;
    }
    return $users;
}

function db_user_select_all()
{
    $sql = "SELECT id, name FROM user ORDER BY name";
    $db_result = mysql_query($sql);
    $users = array();
    while($user = mysql_fetch_assoc($db_result))
    {
        $users[] = $user;
    }
    return $users;
}

function db_user_truncate()
{
    mysql_query("TRUNCATE user");
}

D'autres inconvénients de l'approche par API spécifique est que ces API ne disposent pas toujours de méthodes pour protéger les requêtes, comme ici mysql_real_escape_string(). Par ailleurs, il est très facile d'oublier d'utiliser ces fonctions ou de transtyper la valeur. Cela peut très facilement conduire à des failles de sécurité dans les applications.

Il est donc indéniablement préférable d'utiliser des requêtes préparées.

VII-B. Fichiers XML

VII-B-1. Introduction

Extensions nécessaires : aucune, tout est inclus en standard dans PHP.

Les fichiers XML sont très utiles pour faire transiter les informations entre deux bases de données, par e-mail, etc. Naturellement, PHP fournit des classes permettant de faciliter leur manipulation.

Il existe deux méthodes d'accès à un fichier XML :
  • SAX : Lecture ou écriture séquentielle, comme un flux de données, au fur et à mesure de l'avancement du pointeur de fichier ; cette approche est avantageuse pour la faible quantité de mémoire utilisée, puisque chaque noeud est libéré de la mémoire après utilisation, mais elle est très complexe à utiliser ;
  • DOM : Lecture de l'arbre complet du XML lors de l'ouverture du document (ou manipulation par hiérarchie dans le cas d'écriture) ; cette approche est plus simple mais plus gourmande en mémoire, et n'est pas adaptée pour les grands fichiers.

Nous allons nous concentrer sur une approche DOM, suffisante pour débuter et dans de très nombreuses situations.

VII-B-2. Lecture : SimpleXML

SimpleXML est l'API la plus simple pour la lecture de documents XML. La lecture du fichier se fait selon une approche DOM, et on peut facilement filtrer les données par des requêtes XPath.

Prenons l'exemple d'une BDD de messages. Le fichier XML suivant peut être le dump d'une table message par exemple, et la table user peut être dans un autre fichier XML.

messages.xml
Sélectionnez
<?xml version="1.0" encoding="UTF-8" ?>
<messages>
    <message>
        <id>1</id>
        <user_id>1</user_id>
        <title>Bonjour</title>
        <body>Un bonjour de Paris ;)</body>
    </message>
    <message>
        <id>2</id>
        <user_id>2</user_id>
        <title>Super site</title>
        <body>J'apprécie ton site, continue ainsi !</body>
    </message>
    <message>
        <id>3</id>
        <user_id>2</user_id>
        <title>Merci pour tout</title>
        <body>J'oubliais de dire que ton site m'a beaucoup servi !</body>
    </message>
</messages>
index.php
Sélectionnez
<?php
//lecture du XML dans un objet PHP
$messages = simplexml_load_file('messages.xml');

foreach($messages as $message) //parcours du XML comme d'un tableau
{
    $id = (int)$message->id;

    //pas de validation, le message doit être affiché tel quel
    $body = $message->body;
    echo html($id.' - '.$body).'<br/>';
}

function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
Sélectionnez
1 - Un bonjour de Paris ;)
2 - J'apprécie ton site, continue ainsi !
3 - J'oubliais de dire que ton site m'a beaucoup servi !

Notez que j'ai pris soin de filtrer le texte (décoder l'UTF-8) lorsque je le récupère : cela m'évite d'avoir des problèmes d'encodage. Notez également que l'on peut utiliser foreach sur un objet SimpleXMLElement : c'est parce qu'il implémente l'interface ArrayAccess issu de la SPL.

Un fichier XML est à considérer comme toute autre source de données externes au script : il faut filtrer les valeurs qui en proviennent.

VII-B-3. Écriture : DOM

Admettons maintenant que nous ayons besoin d'écrire le fichier XML ci-dessus à partir de notre BDD. Voici le SQL pour la mise en place de la table et de son contenu :

Sélectionnez
CREATE TABLE message (
  id int(12) NOT NULL auto_increment,
  user_id int(12) default NULL,
  title varchar(50) default NULL,
  body varchar(1000) default NULL,
  PRIMARY KEY (id)
);

INSERT INTO message (user_id, title, body)
VALUES (
    1,
    "Bonjour",
    "Un bonjour de Paris ;)");

INSERT INTO message (user_id, title, body)
VALUES (
    2,
    "Super site",
    "J'apprécie ton site, continue ainsi !");

INSERT INTO message (user_id, title, body)
VALUES (
    2,
    "Merci pour tout",
    "J'oubliais de dire que ton site m'a beaucoup servi !");

Nous aurons besoin de notre classe MyPDO et d'une classe MessageTable :

Sélectionnez
<?php
class MessageTable
{
    private $select;

    public function __construct($db)
    {
        $this->select = $db->prepare(
            "SELECT id, user_id, title, body FROM message");
    }

    public function selectAll()
    {
        $this->select->execute();
        return $this->select->fetchAll();
    }
}

Ajoutons à cela une classe permettant de simplifier la création du XML :

Sélectionnez
<?php
class MyDOMDocument extends DOMDocument
{
    public function __construct($version=NULL, $charset=NULL)
    {
        parent::__construct($version, $charset);
        $this->strictErrorChecking = TRUE;
        $this->appendChild($this->createElement('messages'));
        $this->formatOutput = TRUE;
    }

    public function appendMessage($id, $user_id, $title, $body)
    {
        $message = $this->createElement('message');
        $this->documentElement->appendChild($message);

        $message->appendChild(
            $this->createElement('id', (int)$id));

        $message->appendChild(
            $this->createElement('user_id', (int)$user_id));

        $message->appendChild(
            $this->createElement('title', $this->escapeText($title)));

        $message->appendChild(
            $this->createElement('body', $this->escapeText($body)));
    }

    protected function escapeText($string)
    {
        return utf8_encode(htmlspecialchars($string));
    }
}

Et enfin notre script principal :

Sélectionnez
<?php
$db = new MyPDO(
    'mysql:host=localhost;dbname=developpez', 'utilisateur', 'motdepasse');

$xml = new MyDOMDocument('1.0', 'utf-8');
$messageTable = new MessageTable($db);

foreach($messageTable->selectAll() as $message)
{
    $id = (int)$message['id'];
    $user_id = (int)$message['user_id'];
    $body = preg_replace('/[[:cntrl:]]/', '', $message['body']);

    if(ctype_print($message['title']))
    {
        $title = $message['title'];
    }
    else
    {
        $title = preg_replace('/[^[:graph:]]/', '', $message['title']);
    }

    $xml->appendMessage($id, $user_id, $title, $body);
}

header('Content-Type: application/xml; charset=utf-8');
echo $xml->saveXML();

Notez comme je filtre les données en provenance de la BDD dans l'appel à appendMessage(). De plus, je les convertis selon leur format de destination lors de l'appel à createElement().

L'objet $messageTable n'ayant aucune raison d'apparaître plusieurs fois dans notre code, on peut mettre en place un système pour éviter la création de multiples instances de la classe MessageTable. Il s'agit du design pattern "Singleton", que nous aborderons plus loin.

VII-C. Services Web

VII-C-1. Introduction

Les services Web sont un moyen de mettre un service (c'est-à-dire des informations) à disposition par l'intermédiaire d'un réseau. Appeler un Web service est la même chose qu'appeler une fonction dans un programme, mais au travers d'un réseau informatique (souvent Internet).

De nombreux services existent sur le Web, et tous n'utilisent pas le même protocole de communication. Certains utilisent un protocole REST, d'autres RPC, d'autres encore SOAP. Chaque utilisation dépend des méthodes et paramètres définis par l'API du Web service auquel vous faites appel.

Les différents types de services :
  • SOAP utilise XML pour formater les messages à transmettre, ce qui se fait généralement par HTTP : une URL peut correspondre à plusieurs fonctions ;
  • RPC utilise XML ou JSON pour formater les messages à transmettre, ce qui se fait généralement par des sockets : une URL peut correspondre à plusieurs fonctions;
  • REST utilise le protocole HTTP pour transmettre ses messages : l'URL correspond à la fonction appelée et la méthode HTTP à l'action à mener, le message lui-même étant formaté en fonction des en-têtes HTTP qui l'accompagnent.

On a beaucoup parléBlog de nicorama : Un Blog sur le web RESTful de l'affrontement entre les standards du W3C (WS-*) et REST. Tim Bray lui-même ne semblait pas emballéConférence Internationale PHP 2006 - Keynote par WS-* lors de la Conférence Internationale 2006, tandis que REST lui semblait plus séduisant.

Outre le format du message à transmettre par le service, les services Web SOAP et RPC utilisent un format XML pour décrire leur fonctionnement : le Web Services Description Language (WSDL).

L'action d'utiliser un service Web s'appelle la "consommation".

Un service Web peut être consommé par n'importe quelle API compatible. Nous allons créér ici des services en PHP et nous allons les consommer en PHP, mais nous pouvons aussi les consommer à l'aide d'un autre langage ; ou bien consommer en PHP un service Web écrit dans un autre langage. C'est aussi à cela que sert le WSDL.

Une fois le WSDL généré, le service Web est exposé au monde entier. À partir de ce moment-là, il ne faut plus modifier la signature des fonctions ou le protocole de communication, le WSDL ne doit donc plus évoluer. Si un jour nous devions nous passer de PHP, la nouvelle application devrait faire en sorte de correspondre exactement à ce WSDL afin que les consommateurs actuels du service ne soient pas perturbés. Ne pas générer de WSDL pose donc des problèmes de pérennité du service Web.

VII-C-2. SOAP (anciennement "Simple Object Access protocol")

SOAP est un protocole de communication entre applications. Le format XML est utilisé pour les messages, ce qui permet une grande interopérabilité. L'organisme en charge des spécifications de SOAP est le W3C.

Ce protocole utilise le format WSDL pour décrire et pour interpréter la description d'un service Web.

PHP étant doté de variables à type dynamique, il n'a pas été jugé nécessaire de requérir un fichier WSDL. Le fichier WSDL est donc facultatif mais souvenez-vous que d'autres technologies peuvent avoir besoin d'un tel fichier pour être en mesure de consommer votre service...

L'extension php_soap est apparue avec PHP 5. Il faut donc l'activer dans php.ini avant de poursuivre ce tutoriel. Attention, certaines directives de configuration ne sont apparues qu'avec PHP 5.1.5 :

Options de configuration :
  • soap.wsdl_cache_enabled : Activation du cache local du WSDL ;
  • soap.wsdl_cache_dir : Chemin local (répertoire) du cache ;
  • soap.wsdl_cache_ttl : Temps de validité du cache ;
  • soap.wsdl_cache_limit : Nombre maximal de WSDL en cache local ;
  • soap.wsdl_cache : Type du cache.

Voici un exemple de service d'inversement de texte :

C:\Web\online\http\tests\soap\services\reverse.php
Sélectionnez
<?php
$server = new SoapServer(NULL, array('uri' => 'http://Reversing'));
$server->addFunction('reverse');
$server->handle();

function reverse($string)
{
    return utf8_encode(strrev(utf8_decode($string)));
}
C:\Web\online\http\tests\soap\index.php
Sélectionnez
<?php
$service = new SoapClient(NULL, array(
    'location' => 'http://tests.localhost/soap/services/reverse.php',
    'uri' => 'http://Reversing'
));

echo $service->reverse('Yogui').'<br/>';
echo $service->reverse('BrYs').'<br/>';
echo $service->reverse('mathieu');

function html($string)
{
    return htmlspecialchars($string, ENT_QUOTES);
}
URL à appeler depuis le navigateur :
Sélectionnez
http://localhost/tests/soap/
Résultat :
Sélectionnez
iugoY
sYrB
ueihtam

VII-C-3. Remote Procedure Calls (RPC)

Les premiers services Web étaient du style RPC, une approche très similaire à un appel de fonction. RPC fonctionne avec des sockets.

Les bibliothèques et les frameworks récents continuent de proposer des méthodes facilitant l'utilisation de services Web RPC.

Voici un exemple d'exposition de service avec Zend Framework (en supposant que ZendFramework soit ajouté à include_path dans votre configuration) :

C:\Web\online\http\tests\xmlrpc\services\reverse.php
Sélectionnez
<?php
require_once 'Zend/XmlRpc/Server.php';

/**
 * Returns the reverse of a string
 *
 * @param string $string Value to reverse
 * @return string reverse of value
 */
function reverse($string)
{
    return utf8_encode(strrev(utf8_decode($string)));
}

$server = new Zend_XmlRpc_Server();
$server->addFunction('reverse');

echo $server->handle();

Zend Framework n'a pas besoin de fichier WSDL car il utilise des annotations dans le code pour exposer le service Web. C'est d'ailleurs cette tendance des services RPC à implémenter le service trop proche du langage, qui leur vaut une mauvaise réputation aujourd'hui.

Voici maintenant un exemple de consommation de ce service :

C:\Web\online\http\tests\xmlrpc\index.php
Sélectionnez
<?php
require_once 'Zend/XmlRpc/Client.php';

$consumer = new Zend_XmlRpc_Client(
    'http://localhost/tests/xmlrpc/services/reverse.php');

$service = $consumer->getProxy();

header('Content-Type: text/html; charset=utf-8');
echo html($service->reverse('Yogui')).'<br/>';
echo html($service->reverse('BrYs')).'<br/>';
echo html($service->reverse('mathieu'));

function html($string)
{
    return htmlspecialchars($string, ENT_QUOTES);
}
URL à appeler depuis le navigateur :
Sélectionnez
http://localhost/tests/xmlrpc/
Résultat :
Sélectionnez
iugoY
sYrB
ueihtam

Contrairement aux scripts de Zend Framework, index.php et services/reverse.php doivent être exposés sous DocumentRoot.

Pour qu'un service Web RPC soit disponible, il faut pouvoir ouvrir un socket sur le serveur. Si le WS est très utilisé, cela peut avoir des conséquences sur la charge de votre serveur.

VII-C-4. Service-oriented architecture (SOA)

En PHP, ce type de service est implémenté à l'aide de Service Component Architecture (SCA) et il nécessite un fichier WSDL. La page du projetSOA PHP Homepage (IBM.com) contient toute la documentation nécessaire. Le projet OpenSOA pour PHP est composé de SCA et SDO, regroupés dans une extension et plusieurs scripts. Pour mettre en place des services Web SOA en PHP, il faut maîtriser SCA ; on peut également utiliser SDO, pratique pour utiliser diverses sources de données de la même manière, mais ce n'est pas obligatoire.

SCA se charge de générer le fichier WSDL à partir des commentaires présents dans le code source des classes PHP du service. Une valeur de retour SCA est toujours l'un des quatre types scalaires de PHP : booléen, entier, réel ou chaîne.

Pour installer SCA_SDO :
  • Placez l'extension SDO dans le répertoire des extensions PHP ;
  • Activez les extensions SDO, CURL et SOAP dans php.ini ;
  • Copiez le contenu du répertoire des sources du projet SCA_SDO dans un répertoire accessible depuis include_path mais situé à l'extérieur de DocumentRoot ;
  • Vérifiez que PHP a les permissions nécessaires pour écrire dans le dossier que vous utiliserez pour les classes PHP de vos services (dans mon cas "C:\Web\online\http\tests\sca\services").

Voici un exemple de service d'inversement de texte (le même que l'exemple RPC ci-dessus) :

C:\Web\online\http\tests\sca\services\Reversing.php
Sélectionnez
<?php
include 'SCA/SCA.php';

/**
 * @service
 * @binding.soap
 */
class Reversing
{
    /**
     * @param string $string
     * @return string
     */
    public function reverse($string)
    {
        return utf8_encode(strrev(utf8_decode($string)));
    }
}
Génération du WSDL :
Sélectionnez
<?php
$wsdl = 'C:\Web\online\http\tests\sca\services\Reversing.wsdl';
if(!file_exists($wsdl))
{
    file_put_contents(
        $wsdl,
        file_get_contents(
            'http://localhost/tests/sca/services/Reversing.php?wsdl')
    );
}
WSDL généré :
Sélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
  xmlns:tns2="http://Reversing"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  targetNamespace="http://Reversing">
  <types>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://Reversing"
      elementFormDefault="qualified">
      <xs:element name="reverse">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="name" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="reverseResponse">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="reverseReturn" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
  </types>

  <wsdl:message name="reverseRequest">
    <wsdl:part name="reverseRequest" element="tns2:reverse"/>
  </wsdl:message>
  <wsdl:message name="reverseResponse">
    <wsdl:part name="return" element="tns2:reverseResponse"/>
  </wsdl:message>
  <wsdl:portType name="ReversingPortType">
    <wsdl:operation name="reverse">
      <wsdl:input message="tns2:reverseRequest"/>
      <wsdl:output message="tns2:reverseResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="ReversingBinding" type="tns2:ReversingPortType">
    <soap:binding
        transport="http://schemas.xmlsoap.org/soap/http"
        style="document"/>
    <wsdl:operation name="reverse">
      <soap:operation soapAction=""/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="ReversingService">
    <wsdl:port name="ReversingPort" binding="tns2:ReversingBinding">
      <soap:address
        location="http://localhost/tests/sca/services/Reversing.php"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

<!-- this line identifies this file as WSDL generated by SCA for PHP. Do not remove -->

Voici maintenant un exemple de consommation de ce service :

C:\Web\online\http\tests\sca\index.php
Sélectionnez
<?php
include 'SCA/SCA.php';

$service = SCA::getService(
    'http://localhost/tests/sca/services/Reversing.wsdl');

header('Content-Type: text/html; charset=utf-8');
echo $service->reverse('Yogui').'<br/>';
echo $service->reverse('BrYs').'<br/>';
echo $service->reverse('mathieu');

function html($string)
{
    return htmlspecialchars($string, ENT_QUOTES);
}
URL à appeler depuis le navigateur :
Sélectionnez
http://localhost/tests/sca/
Résultat :
Sélectionnez
iugoY
sYrB
ueihtam

Contrairement aux scripts contenus sous le répertoire "SCA", les fichiers index.php, services/Reversing.php et services/Reversing.wsdl doivent être exposés sous DocumentRoot. Idéalement, le script de génération des fichiers WSDL devrait être situé en-dehors de DocumentRoot et appelé en lignes de commandes.

Le fichier WSDL est systématiquement généré à côté du script PHP qui lui correspond.
Il ne faut pas faire include_once ou require_once du script SCA/SCA.php, mais plutôt include ou require.

VII-C-5. REpresentational State Transfer (REST)

Les services RESTful n'utilisent pas le protocole SOAP, ils n'ont pas besoin de fichier WSDL ; d'après le W3C, il est toutefois possible de décrire un service REST grâce à un fichier WSDL. Nous n'allons néanmoins pas nous en occuper ici, dans la mesure où les standards d'utilisation de services RESTful suffisent à assurer sa pérennité.

Les services RESTful sont consommés au moyen du protocole HTTP :
  • POST : Mise à jour de données ;
  • GET : Lecture de données ;
  • PUT : Enregistrement de nouvelles données ;
  • DELETE : Suppression de données.
Recherche sur Yahoo en PHP pur :
Sélectionnez
<?php
$url = sprintf(
    'http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=%s&query=%s&region=fr',
    rawurlencode('<mettre ici votre clef>'),
    rawurlencode('<mettre ici votre question>'));

$xml = simplexml_load_file($url);

header('Content-Type: text/html; charset=utf-8');
foreach($xml as $result)
{
    echo htmlspecialchars($result->Title, ENT_QUOTES).'<br/>';
}

L'utilisation de services REST en méthode GET est très simple, mais les autres méthodes (POST, PUT et DELETE) sont plus complexes à mettre en place. Il est préférable d'utiliser une bibliothèque afin de ne pas compliquer notre code, Zend Framework étant un exemple parmi d'autres.

Recherche sur Yahoo avec Zend Framework :
Sélectionnez
<?php
require_once 'Zend/Service/Yahoo.php';

$yahoo = new Zend_Service_Yahoo('<mettre ici votre clef>');
$results = $yahoo->webSearch('<mettre ici votre question>');

header('Content-Type: text/html; charset=utf-8');
foreach($results as $result)
{
    echo htmlspecialchars($result->Title, ENT_QUOTES).'<br />';
}
Recherche de livres sur Amazon avec Zend Framework :
Sélectionnez
<?php
require_once 'Zend/Service/Amazon.php';

$amazon = new Zend_Service_Amazon('<mettre ici votre clef>');
$results = $amazon->itemSearch(array(
    'SearchIndex' => 'Books',
    'Keywords' => '<mettre ici vos mots clefs>'));

header('Content-Type: text/html; charset=utf-8');
foreach($results as $result)
{
    echo htmlspecialchars($result->Title, ENT_QUOTES).'<br />';
}

Voyons maintenant comment mettre en place nous-mêmes un service REST.

C:\Web\online\http\tests\rest\services\reverse.php
Sélectionnez
<?php
require_once 'Zend/Rest/Server.php';

/**
 * Returns the reverse of a string
 *
 * @param string $string Value to reverse
 * @return string reverse of value
 */
function reverse($string)
{
    return utf8_encode(strrev(utf8_decode($string)));
}

$server = new Zend_Rest_Server();
$server->addFunction('reverse');

$server->handle();
C:\Web\online\http\tests\rest\index.php
Sélectionnez
<?php
require_once 'Zend/Rest/Client.php';

$client = new Zend_Rest_Client(
    'http://localhost/tests/rest/services/reverse.php');

echo html($client->reverse('Yogui', '')->get()).'<br/>';
echo html($client->reverse('BrYs', '')->get()).'<br/>';
echo html($client->reverse('mathieu', '')->get());

function html($string)
{
    return htmlspecialchars($string);
}
URL à appeler depuis le navigateur :
Sélectionnez
http://localhost/tests/rest/
Résultat :
Sélectionnez
iugoY
sYrB
ueihtam

VII-C-6. Conclusion

Avant de mettre en place un service Web pour votre organisation, renseignez-vous sur l'existant. De nombreuses applications utilisent déjà une architecture orientée vers les services (SOA), il peut être judicieux dans ces situations d'utiliser SCA pour PHP. Dans les autres cas, peut-être que SOAP ou REST sont plus simples à mettre en place, à maintenir et à interfacer avec d'autres services (Google, Yahoo, Amazon...).

VII-D. Autres formats : PDF, ZIP, Flash, images, etc.

Ce cours ne peut pas couvrir la totalité des formats que PHP permet de gérer, surtout si l'on prend en compte la diversité des extensions et bibliothèques disponibles.

Par exemple pour générer des fichiers PDF, on peut utiliser :
  • PDFlib ;
  • FPDF ;
  • File_PDF (PEAR)PEAR : File_PDF ;
  • Zend_PdfZend Framework : Zend_Pdf ;
  • etc.

PHP dispose de bibliothèques ou d'extensions pour gérer tous types de format, même des formats multimédia ou des formats complexes comme Excel. Si vous ne trouvez pas votre bonheur sur php.net ou pecl.php.net, tentez votre chance avec Google ;)

En particulier, les formats d'images sont rendus très accessibles par de nombreuses bibliothèques permettant de générer notamment des graphiques. PHP peut ainsi construire facilement des rapports détaillés et agrémentés de visuels attractifs.


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/

VIII. Exemples d'application

VIII-A. Introduction

Les exemples qui suivent sont tous bâtis sur le même modèle, à savoir un site de tutoriels. Nous verrons plusieurs manières d'aborder cette thématique, les avantages et inconvénients de chaque méthode, et les conclusions qui s'imposent. Attention, l'ergonomie n'est pas au programme : il faudra utiliser vos propres dons ou vous inspirer d'une autre source.

VIII-B. Application simple

VIII-B-1. Introduction

Ce site est fait le plus simplement possible, chaque script étant indépendant des autres.

Arborescence sous "C:\Web\online\http\cours-php\1-simple" (DocumentRoot) :
  • /
  • /index.php
  • /cours.php
  • /auteur.php

VIII-B-2. Les scripts

index.php
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>G. Rossolini - Tutoriels PHP</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<ul style="text-align: center">
    <li style="display: inline"><a
        href="./"
        title="G. Rossolini - Tutoriels PHP">Accueil</a> -</li>
    <li style="display: inline"><a
        href="./articles.php"
        title="G. Rossolini - Liste des publications">Publications</a> -</li>
    <li style="display: inline"><a
        href="./auteurs.php"
        title="G. Rossolini - Liste des auteurs">Auteurs</a></li>
</ul>
<h1 style="text-align: center">Guillaume Rossolini - Tutoriels PHP</h1>
<h2 style="text-decoration: underline">Présentation</h2>
<p>J'ai suivi une formation dans plusieurs domaines, notamment la communication,
    le développement de logiciels, la gestion de projet,
    <span style="font-style: italic">etc</span>.</p>
<p>Aujoud'hui, je suis un développeur PHP muni de la certification Zend :<br />
<a
    href="http://www.zend.com/store/education/certification/authenticate.php?ClientCandidateID=ZEND005053&RegistrationID=221302133"
    title="Zend Certified Engineer Details"><img
        src="http://www.zend.com/images/training/php5_zce_logo.gif"
        alt="Logo ZCE"
        style="border: none"/></a></p>
<p style="text-align: center; font-size: 0.7em;">Copyright
    Guillaume Rossolini 2007-<?php echo date('Y'); ?><br />
Contact : <a
    href="mailto:g-rossolini@developpez.com"
    title="E-mail">g-rossolini@developpez.com</a></p>
</body>
</html>
articles.php
Sélectionnez
<?php
mysql_connect('localhost', 'utilisateur', 'motdepasse');
mysql_select_db('developpez');
function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
?>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>G. Rossolini - Articles</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<ul style="text-align: center">
    <li style="display: inline"><a
        href="./"
        title="G. Rossolini - Tutoriels PHP">Accueil</a> -</li>
    <li style="display: inline"><a
        href="./articles.php"
        title="G. Rossolini - Liste des publications">Publications</a> -</li>
    <li style="display: inline"><a
        href="./auteurs.php"
        title="G. Rossolini - Liste des auteurs">Auteurs</a></li>
</ul>
<?php
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Publications</h1>
    <?php
    $sql = 'SELECT c.id, c.name, COUNT(*) AS nb_articles
            FROM category AS c
            INNER JOIN article AS a ON a.category_id = c.id
            GROUP BY c.id, c.name
            ORDER BY c.name';
    $db_categories = mysql_query($sql);
    while($category = mysql_fetch_assoc($db_categories))
    {
        ?><h2><?php echo html($category['name']); ?></h2><?php
        $sql = 'SELECT ar.id, ar.title,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.category_id = %d
                ORDER BY ar.title';
        $db_article = mysql_query(sprintf($sql, $category['id']));
        ?><ul><?php
        while($article = mysql_fetch_assoc($db_article))
        {
            ?>
                <li><a href="./articles.php?id=<?php
                    echo (int)$article['id'];
                ?>" title="<?php echo html($article['title']); ?>"><?php
                    echo html($article['title']);
                ?></a>, par <a href="./auteurs.php?id=<?php
                    echo (int)$article['author_id'];
                ?>" title=""><?php
                    echo html($article['author_name']);
                ?></a></li>
            <?php
        }
        ?></ul><?php
    }
}
else
{
    $id = (int)$_GET['id'];
    $sql = 'SELECT ar.title, ar.text,
                au.id AS author_id, au.name AS author_name
            FROM article AS ar
            INNER JOIN author AS au ON ar.author_id = au.id
            WHERE ar.id = %d';
    $db_article = mysql_query(sprintf($sql, $id));
    if($article = mysql_fetch_assoc($db_article))
    {
        $article['text'] = utf8_decode($article['text']);
        ?>
        <h1 style="text-align: center"><?php
            echo html($article['title']);
        ?></h1>
        <div style="text-align: center; font-style: italic">Par <a
            href="./auteurs.php?id=<?php
                echo html($article['author_id']);
            ?>"
            title="Publications de <?php
                echo html($article['author_name']);
            ?>"><?php
                echo html($article['author_name']);
            ?></a></div>
        <?php
        echo nl2br(html($article['text']));
    }
    else
    {
        echo "Cet article n'existe pas...";
    }
}
?>
<p style="text-align: center; font-size: 0.7em;">Copyright
    Guillaume Rossolini 2007-<?php echo date('Y'); ?><br />
Contact : <a
    href="mailto:g-rossolini@developpez.com"
    title="E-mail">g-rossolini@developpez.com</a></p>
</body>
</html>
auteurs.php
Sélectionnez
<?php
mysql_connect('localhost', 'utilisateur', 'motdepasse');
mysql_select_db('developpez');
function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
?>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>G. Rossolini - Publications</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<ul style="text-align: center">
    <li style="display: inline"><a
        href="./"
        title="G. Rossolini - Tutoriels PHP">Accueil</a> -</li>
    <li style="display: inline"><a
        href="./articles.php"
        title="G. Rossolini - Liste des publications">Publications</a> -</li>
    <li style="display: inline"><a
        href="./auteurs.php"
        title="G. Rossolini - Liste des auteurs">Auteurs</a></li>
</ul>
<?php
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Auteurs</h1>
    <ul style="list-style-type: none">
    <?php
    $sql = 'SELECT au.id, au.name, COUNT(*) AS nb_articles
            FROM author AS au
            LEFT JOIN article AS ar ON ar.author_id = au.id
            GROUP BY au.id, au.name
            ORDER BY name';
    $db_authors = mysql_query($sql);
    while($author = mysql_fetch_assoc($db_authors))
    {
        ?><li><a
            href="./auteurs.php?id=<?php echo (int)$author['id']; ?>"
            title="<?php echo html($author['name']); ?>"><?php
                echo html($author['name']);
            ?></a> (<?php
                echo (int)$author['nb_articles'];
            ?> articles)</li><?php
    }
    ?>
    </ul>
    <?php
}
else
{
    $id = (int)$_GET['id'];
    $sql = 'SELECT name
            FROM author
            WHERE id = %d';
    $db_author = mysql_query(sprintf($sql, $id));
    if($author = mysql_fetch_assoc($db_author))
    {
        ?>
        <h1 style="text-align: center">Publications de <?php
            echo html($author['name']);
        ?></h1>
        <ul>
        <?php
        $sql = 'SELECT id, title
                FROM article
                WHERE author_id = %d
                ORDER BY title';
        $db_article = mysql_query(sprintf($sql, $id));
        while($article = mysql_fetch_assoc($db_article))
        {
            ?><li><a
                href="./articles.php?id=<?php echo html($article['id']); ?>"
                title="Article par <?php echo html($author['name']); ?>"><?php
                    echo html($article['title']);
                ?></a></li><?php
        }
        ?>
        </ul>
        <?php
    }
    else
    {
        echo "Cet auteur n'existe pas...";
    }
}
?>
<p style="text-align: center; font-size: 0.7em;">Copyright
    Guillaume Rossolini 2007-<?php echo date('Y'); ?><br />
Contact : <a
    href="mailto:g-rossolini@developpez.com"
    title="E-mail">g-rossolini@developpez.com</a></p>
</body>
</html>

Même si tout est parfaitement fonctionnel et que le code XHTML produit est parfaitement standard, voyons maintenant les inconvénients de cette approche, si souvent utilisée par les débutants avant qu'ils prennent de l'expérience.

VIII-B-3. Avantages

Ce code est très rapide à écrire.

Puisque le code de chaque script n'est pas très long, nous avons une bonne vision détaillée de leur comportement (en un clin d'oeil).

VIII-B-4. Inconvénients

L'en-tête et le pied de page (code XHTML) sont répétés dans tous les documents, alors qu'ils sont toujours identiques. Nous pourrions définir des fonctions, mais le plus adéquat est d'inclure des fichiers.

Les paramètres d'accès et la connexion à la base de données sont eux aussi répétés dans plusieurs scripts. Une inclusion serait également bienvenue.

Dans le même ordre d'idée, la fonction html() est déclarée à l'identique dans tous les scripts.

Le dialogue avec la base de données se fait au moyen de l'API standard de MySQL, ce qui est très restrictif. Il serait préférable d'utiliser PDO.

À chaque retour de requête, nous sommes obligés de faire manuellement une conversion d'encodage. Utiliser une classe étendue de PDO nous permettra de nous affranchir de ce problème.

Un problème moins évident est que le terme "php" apparaît dans les URL. Par conséquent, ces URLs sont éligibles pour toutes les recherches sur "php" dans les moteurs de recherche. Dans mon cas ce n'est pas grave puisqu'il s'agit justement de tutoriels PHP, mais imaginons un site sur l'alimentaire ou tout autre sujet en décalage complet avec PHP... En ce cas, l'extension de fichier dans l'URL n'est pas une bonne chose, il est préférable de s'en débarasser. Cela peut même améliorer la sécurité de votre site...

Enfin, il y a bien trop de mélanges de PHP et de XHTML, le code du script est donc très complexe à lire. Imaginez que j'introduise du code CSS, JavaScript etc ., le code source de mon script deviendrait rapidement incompréhensible.

L'inconvénient majeur est donc la maintenabilité du code : nous n'avons adopté que très peu de règles d'écriture du code, et par conséquent il est difficile à relire.

VIII-C. Inclusions

VIII-C-1. Introduction

Dans un premier temps, nous allons corriger la répétition du code.

Arborescence sous "C:\Web\online\http\cours-php\2-includes" (DocumentRoot) :
  • /
  • /index.php
  • /articles.php
  • /auteurs.php
Arborescence sous "C:\Web\offline\sites\cours-php\2-includes" (hors de DocumentRoot) :
  • /
  • /footer.html
  • /header.html
  • /common.php
  • /functions.php

Le moyen le plus efficace de rendre les fichiers à inclure accessibles aux scripts du site, est de modifier la configuration d'Apache via le httpd.conf ou .htaccess. Cela nous évitera de mettre tout le chemin vers les fichiers dans tous nos scripts. Voici comment cela se présente dans mon cas :

VIII-C-2. Les scripts

Ajouter à "httpd.conf" :
Sélectionnez
<Directory "C:/Web/online/http/cours-php/2-includes">
    AllowOverride None
    php_value include_path ".;C:/Web/offline/sites/cours-php/2-includes"
</Directory>
index.php
Sélectionnez
<?php
include 'header.tpl';
?>
<h1 style="text-align: center">Guillaume Rossolini - Tutoriels PHP</h1>
<h2 style="text-decoration: underline">Présentation</h2>
<p>J'ai suivi une formation dans plusieurs domaines, notamment la communication,
    le développement de logiciels, la gestion de projet,
    <span style="font-style: italic">etc</span>.</p>
<p>Aujoud'hui, je suis un développeur PHP muni de la certification Zend :<br />
<a href="http://www.zend.com/store/education/certification/authenticate.php?ClientCandidateID=ZEND005053&RegistrationID=221302133"
    title="Zend Certified Engineer Details"><img
        src="http://www.zend.com/images/training/php5_zce_logo.gif"
        alt="Logo ZCE"
        style="border: none"/></a></p>
<?php
include 'footer.tpl';
articles.php
Sélectionnez
<?php
require_once 'common.php';
require_once 'functions.php';
include 'header.tpl';
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Publications</h1>
    <?php
    $sql = 'SELECT c.id, c.name, COUNT(*) AS nb_articles
            FROM category AS c
            INNER JOIN article AS a ON a.category_id = c.id
            GROUP BY c.id, c.name
            ORDER BY c.name';
    $db_categories = mysql_query($sql) or die(mysql_error());
    while($category = mysql_fetch_assoc($db_categories))
    {
        ?><h2><?php echo html($category['name']); ?></h2><?php
        $sql = 'SELECT ar.id, ar.title,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.category_id = %d
                ORDER BY ar.title';
        $db_article = mysql_query(sprintf($sql, $category['id']));
        ?><ul><?php
        while($article = mysql_fetch_assoc($db_article))
        {
            ?><li><a href="./articles.php?id=<?php echo (int)$article['id']; ?>"
                title="<?php echo html($article['title']); ?>"><?php
                    echo html($article['title']);
                ?></a>, par <a href="./auteurs.php?id=<?php
                    echo (int)$article['author_id'];
                ?>" title=""><?php
                    echo html($article['author_name']);
                ?></a></li><?php
        }
        ?></ul><?php
    }
}
else
{
    $id = (int)$_GET['id'];
    $sql = 'SELECT ar.title, ar.text,
                au.id AS author_id, au.name AS author_name
            FROM article AS ar
            INNER JOIN author AS au ON ar.author_id = au.id
            WHERE ar.id = %d';
    $db_article = mysql_query(sprintf($sql, $id));
    if($article = mysql_fetch_assoc($db_article))
    {
        $article['text'] = utf8_decode($article['text']);
        ?>
        <h1 style="text-align: center"><?php
            echo html($article['title']);
        ?></h1>
        <div style="text-align: center; font-style: italic">Par <a
            href="./auteurs.php?id=<?php echo (int)$article['author_id']; ?>"
            title="Publications de <?php
                echo html($article['author_name']);
            ?>"><?php echo html($article['author_name']); ?></a></div>
        <?php
        echo nl2br(html($article['text']));
    }
    else
    {
        echo "Cet article n'existe pas...";
    }
}
include 'footer.tpl';
auteurs.php
Sélectionnez
<?php
require_once 'common.php';
require_once 'functions.php';
include 'header.tpl';
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Auteurs</h1>
    <ul style="list-style-type: none">
    <?php
    $sql = 'SELECT au.id, au.name, COUNT(*) AS nb_articles
            FROM author AS au
            LEFT JOIN article AS ar ON ar.author_id = au.id
            GROUP BY au.id, au.name
            ORDER BY name';
    $db_authors = mysql_query($sql);
    while($author = mysql_fetch_assoc($db_authors))
    {
        ?><li><a
            href="./auteurs.php?id=<?php echo (int)$author['id']; ?>"
            title="<?php echo html($author['name']); ?>"><?php
                echo html($author['name']);
            ?></a> (<?php
                echo (int)$author['nb_articles'];
            ?> articles)</li><?php
    }
    ?>
    </ul>
    <?php
}
else
{
    $id = (int)$_GET['id'];
    $sql = 'SELECT name
            FROM author
            WHERE id = %d';
    $db_author = mysql_query(sprintf($sql, $id));
    if($author = mysql_fetch_assoc($db_author))
    {
        ?>
        <h1 style="text-align: center">Publications de <?php
            echo html($author['name']);
        ?></h1>
        <ul>
        <?php
        $sql = 'SELECT id, title
                FROM article
                WHERE author_id = %d
                ORDER BY title';
        $db_article = mysql_query(sprintf($sql, $id));
        while($article = mysql_fetch_assoc($db_article))
        {
            ?><li><a
                href="./articles.php?id=<?php echo (int)$article['id']; ?>"
                title="Article par <?php
                    echo html($author['name']);
                ?>"><?php echo html($article['title']); ?></a></li><?php
        }
        ?>
        </ul>
        <?php
    }
    else
    {
        echo "Cet auteur n'existe pas...";
    }
}
include 'footer.tpl';
common.php
Sélectionnez
<?php
mysql_connect('localhost', 'utilisateur', 'motdepasse');
mysql_select_db('developpez');
functions.php
Sélectionnez
<?php
function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
header.html
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>G. Rossolini - Tutoriels PHP</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<ul style="text-align: center">
    <li style="display: inline"><a
        href="./"
        title="G. Rossolini - Tutoriels PHP">Accueil</a> -</li>
    <li style="display: inline"><a
        href="./articles.php"
        title="G. Rossolini - Liste des publications">Publications</a> -</li>
    <li style="display: inline"><a
        href="./auteurs.php"
        title="G. Rossolini - Liste des auteurs">Auteurs</a></li>
</ul>
footer.html
Sélectionnez

<p style="text-align: center; font-size: 0.7em;">Copyright
    Guillaume Rossolini 2007-<?php echo date('Y'); ?><br />
Contact : <a
    href="mailto:g-rossolini@developpez.com"
    title="E-mail">g-rossolini@developpez.com</a></p>
</body>
</html>

VIII-C-3. Nouveaux avantages

Toute modification de l'en-tête ou du pied de page est immédiatement répercutée dans toutes les pages du site.

Puisqu'ils sont hors du DocumentRoot , les fichiers à inclure sont protégés de tout accès externe. Leur chemin d'accès étant dans l' include_path de PHP, ils sont également facilement accessibles.

VIII-C-4. Nouveaux inconvénients

Les fichiers à inclure ne sont plus intégralement copiés dans les scripts effectifs, mais l'on se rend compte que les instructions require* et include* se répètent d'un script à l'autre. Nous verrons plus loin que MVC nous permet d'éviter cette situation.

Par exemple, si je souhaite conserver des statistiques sur mes visiteurs, une solution serait de créer un script "stats.php" à inclure dans toutes mes pages. Je serais donc obligé de répéter ce code dans tous les scripts :

Sélectionnez
<?php
require_once 'common.php';
require_once 'functions.php';
include 'stats.php';
include 'header.tpl';

Un problème évident avec le code ci-dessus est que nous pourrions oublier d'inclure "stats.php" dnas l'un des scripts... Un autre est que le bloc d'includes/requires est tout simplement du code répété, or il nous faut éviter ce genre de situations.

VIII-D. Sans l'extension ".php"

VIII-D-1. Introduction

Nous avons déjà établi que l'extension ".php" est un problème dans l'URL. Maintenant que notre menu d'en-tête est géré par des inclusions, nous pouvons plus facilement modifier les liens du site.

Le principe est d'utiliser des URLs absolues à partir de la racine du site, donc commençant par "/" plutôt que "./", pour chacun des liens. Nous allons mettre chaque script dans un répertoire, ce qui nous permettra de l'appeler "index.php" et ainsi de nous passer totalement du nom du script dans l'URL. Un nouveau fichier de configuration permet de respecter DRY ( don't repeat yourself ).

Arborescence sous "C:\Web\online\http\cours-php\3-extensions" (DocumentRoot) :
  • /
  • /index.php
  • /articles/index.php
  • /auteurs/index.php
Arborescence sous "C:\Web\offline\sites\cours-php\3-extensions" (hors de DocumentRoot) :
  • /
  • /footer.html
  • /header.html
  • /common.php
  • /functions.php

VIII-D-2. Les scripts modifiés

Ajouter à "httpd.conf" :
Sélectionnez
<Directory "C:/Web/online/http/cours-php/3-extensions">
    AllowOverride None
    php_value include_path ".;C:/Web/offline/sites/cours-php/3-extensions"
    SetEnv HTTP_ROOT /cours-php/3-extensions/
</Directory>
header.html
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR" lang="fr-FR">
<head>
    <title>G. Rossolini - Tutoriels PHP</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<ul style="text-align: center">
    <li style="display: inline"><a
        href="<?php echo html($_SERVER['HTTP_ROOT']); ?>"
        title="G. Rossolini - Tutoriels PHP">Accueil</a> -</li>
    <li style="display: inline"><a
        href="<?php echo html($_SERVER['HTTP_ROOT']); ?>articles/"
        title="G. Rossolini - Liste des publications">Publications</a> -</li>
    <li style="display: inline"><a
        href="<?php echo html($_SERVER['HTTP_ROOT']); ?>auteurs/"
        title="G. Rossolini - Liste des auteurs">Auteurs</a></li>
</ul>
articles/index.php
Sélectionnez
<?php
require_once 'common.php';
require_once 'functions.php';
include 'header.tpl';
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Publications</h1>
    <?php
    $sql = 'SELECT c.id, c.name, COUNT(*) AS nb_articles
            FROM category AS c
            INNER JOIN article AS a ON a.category_id = c.id
            GROUP BY c.id, c.name
            ORDER BY c.name';
    $db_categories = mysql_query($sql) or die(mysql_error());
    while($category = mysql_fetch_assoc($db_categories))
    {
        ?><h2><?php echo html($category['name']); ?></h2><?php
        $sql = 'SELECT ar.id, ar.title,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.category_id = %d
                ORDER BY ar.title';
        $db_article = mysql_query(sprintf($sql, $category['id']));
        ?><ul><?php
        while($article = mysql_fetch_assoc($db_article))
        {
            ?><li><a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>articles/?id=<?php
                echo (int)$article['id'];
            ?>" title="<?php
                echo html($article['title']);
            ?>"><?php
                echo html($article['title']);
            ?></a>, par <a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>auteurs/?id=<?php
                echo (int)$article['author_id'];
            ?>" title=""><?php
                echo html($article['author_name']);
            ?></a></li><?php
        }
        ?></ul><?php
    }
}
else
{
    $id = (int)$_GET['id'];
    $sql = 'SELECT ar.title, ar.text,
                au.id AS author_id, au.name AS author_name
            FROM article AS ar
            INNER JOIN author AS au ON ar.author_id = au.id
            WHERE ar.id = %d';
    $db_article = mysql_query(sprintf($sql, $id));
    if($article = mysql_fetch_assoc($db_article))
    {
        $article['text'] = utf8_decode($article['text']);
        ?>
        <h1 style="text-align: center"><?php
            echo html($article['title']);
        ?></h1>
        <div style="text-align: center; font-style: italic">Par <a href="<?php
            echo html($_SERVER['HTTP_ROOT']);
        ?>auteurs/?id=<?php
            echo (int)$article['author_id'];
        ?>" title="Publications de <?php
            echo html($article['author_name']);
        ?>"><?php
            echo html($article['author_name']);
        ?></a></div>
        <?php
        echo nl2br(html($article['text']));
    }
    else
    {
        echo "Cet article n'existe pas...";
    }
}
include 'footer.tpl';
auteurs/index.php
Sélectionnez
<?php
require_once 'common.php';
require_once 'functions.php';
include 'header.tpl';
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Auteurs</h1>
    <ul style="list-style-type: none">
    <?php
    $sql = 'SELECT au.id, au.name, COUNT(*) AS nb_articles
            FROM author AS au
            LEFT JOIN article AS ar ON ar.author_id = au.id
            GROUP BY au.id, au.name
            ORDER BY name';
    $db_authors = mysql_query($sql);
    while($author = mysql_fetch_assoc($db_authors))
    {
        ?><li><a href="<?php
            echo html($_SERVER['HTTP_ROOT']);
        ?>auteurs/?id=<?php
            echo (int)$author['id'];
        ?>" title="<?php
            echo html($author['name']);
        ?>"><?php
            echo html($author['name']);
        ?></a> (<?php
            echo (int)$author['nb_articles'];
        ?> articles)</li><?php
    }
    ?>
    </ul>
    <?php
}
else
{
    $id = (int)$_GET['id'];
    $sql = 'SELECT name
            FROM author
            WHERE id = %d';
    $db_author = mysql_query(sprintf($sql, $id));
    if($author = mysql_fetch_assoc($db_author))
    {
        ?>
        <h1 style="text-align: center">Publications de <?php
            echo html($author['name']);
        ?></h1>
        <ul>
        <?php
        $sql = 'SELECT id, title
                FROM article
                WHERE author_id = %d
                ORDER BY title';
        $db_article = mysql_query(sprintf($sql, $id));
        while($article = mysql_fetch_assoc($db_article))
        {
            ?><li><a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>articles/?id=<?php
                echo (int)$article['id'];
            ?>" title="Article par <?php
                echo html($author['name']);
            ?>"><?php
                echo html($article['title']);
            ?></a></li><?php
        }
        ?>
        </ul>
        <?php
    }
    else
    {
        echo "Cet auteur n'existe pas...";
    }
}
include 'footer.tpl';

VIII-D-3. Nouveaux avantages

L'extension ".php" n'apparaît plus dans l'URL, les moteurs de recherche peuvent donc référencer la totalité de nos URLs sans trouver de mot inutile.

VIII-D-4. Nouveaux inconvénients

Nous avons maintenant autant de répertoires publics que de scripts, ce qui n'est pas nécessairement très pratique ou utile pour notre organisation interne.

VIII-E. Modèles (classes pour la BDD)

VIII-E-1. Introduction

Arborescence sous "C:\Web\online\http\cours-php\4-modeles" (DocumentRoot) :
  • /
  • /index.php
  • /articles.php
  • /auteurs.php
Arborescence sous "C:\Web\offline\sites\cours-php\4-modeles" (hors de DocumentRoot) :
  • /
  • /AbstractTable.php
  • /Article.php
  • /Author.php
  • /Category.php
  • /common.php
  • /footer.html
  • /functions.php
  • /header.html
  • /MyPDO.php

VIII-E-2. Les scripts

Ajouter à "httpd.conf" :
Sélectionnez
<Directory "C:/Web/online/http/cours-php/4-modeles">
    AllowOverride None
    php_value include_path ".;C:/Web/offline/sites/cours-php/4-modeles"
    SetEnv HTTP_ROOT /cours-php/4-modeles/
</Directory>
AbstractTable.php
Sélectionnez
<?php
abstract class AbstractTable
{
    protected $db;
    protected $selectAll;
    protected $selectById;
    public function __construct($db)
    {
        $this->db = $db;
    }
    public function getAll()
    {
        $this->selectAll->execute();
        return $this->selectAll->fetchAll();
    }
    public function getById($id)
    {
        $this->selectById->execute(array($id));
        $row = $this->selectById->fetchAll();
        if(empty($row[0]))
        {
            return array();
        }
        else
        {
            return $row[0];
        }
    }
}
common.php
Sélectionnez
<?php
spl_autoload_register('generic_autoload');
$db = new MyPDO(
    'mysql:host=localhost;dbname=developpez', 'utilisateur', 'motdepasse');
functions.php
Sélectionnez
<?php
function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
function generic_autoload($class)
{
    require_once $class.'.php';
}
MyPDO.php
Sélectionnez
<?php
class MyPDO extends PDO
{
    public function __construct($dsn, $user=NULL, $password=NULL)
    {
        parent::__construct($dsn, $user, $password);
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    public function prepare($sql, $options=NULL)
    {
        $statement = parent::prepare($sql);
        if(strpos(strtoupper($sql), 'SELECT') === 0) //requête "SELECT"
        {
            $statement->setFetchMode(PDO::FETCH_ASSOC);
        }
        return $statement;
    }
}
Article.php
Sélectionnez
<?php
class Article extends AbstractTable
{
    protected $selectById;
    protected $selectByAuthor;
    protected $selectByCategory;
    public function __construct($db)
    {
        parent::__construct($db);
        $sql = 'SELECT ar.title, ar.text,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.id = ?';
        $this->selectById = $this->db->prepare($sql);
        $sql = 'SELECT id, title
                FROM article
                WHERE author_id = ?
                ORDER BY title';
        $this->selectByAuthor = $this->db->prepare($sql);
        $sql = 'SELECT ar.id, ar.title,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.category_id = ?
                ORDER BY ar.title';
        $this->selectByCategory = $this->db->prepare($sql);
    }
    public function getById($id)
    {
        $this->selectById->execute(array($id));
        $articles = $this->selectById->fetchAll();
        if(empty($articles[0]))
        {
            return array();
        }
        else
        {
            $articles[0]['text'] = utf8_decode($articles[0]['text']);
            return $articles[0];
        }
    }
    public function getByAuthor($id)
    {
        $this->selectByAuthor->execute(array($id));
        return $this->selectByAuthor->fetchAll();
    }
    public function getByCategory($id)
    {
        $this->selectByCategory->execute(array($id));
        return $this->selectByCategory->fetchAll();
    }
}
Author.php
Sélectionnez
<?php
class Author extends AbstractTable
{
    public function __construct($db)
    {
        parent::__construct($db);
        $sql = 'SELECT au.id, au.name, COUNT(*) AS nb_articles
                FROM author AS au
                LEFT JOIN article AS ar ON ar.author_id = au.id
                GROUP BY au.id, au.name
                ORDER BY name';
        $this->selectAll = $this->db->prepare($sql);
        $sql = 'SELECT name
                FROM author
                WHERE id = ?';
        $this->selectById = $this->db->prepare($sql);
    }
}
Category.php
Sélectionnez
<?php
class Category extends AbstractTable
{
    public function __construct($db)
    {
        parent::__construct($db);
        $sql = 'SELECT c.id, c.name, COUNT(*) AS nb_articles
                FROM category AS c
                INNER JOIN article AS a ON a.category_id = c.id
                GROUP BY c.id, c.name
                ORDER BY c.name';
        $this->selectAll = $this->db->prepare($sql);
    }
}
/index.php
Sélectionnez
<?php
require_once 'functions.php';
include 'header.tpl';
?>
<h1 style="text-align: center">Guillaume Rossolini - Tutoriels PHP</h1>
<h2 style="text-decoration: underline">Présentation</h2>
<p>J'ai suivi une formation dans plusieurs domaines, notamment la communication,
    le développement de logiciels, la gestion de projet,
    <span style="font-style: italic">etc</span>.</p>
<p>Aujoud'hui, je suis un développeur PHP muni de la certification Zend :<br />
<a href="http://www.zend.com/store/education/certification/authenticate.php?ClientCandidateID=ZEND005053&RegistrationID=221302133"
    title="Zend Certified Engineer Details"><img
        src="http://www.zend.com/images/training/php5_zce_logo.gif"
        alt="Logo ZCE"
        style="border: none"/></a></p>
<?php
include 'footer.tpl';
/articles/index.php
Sélectionnez
<?php
require_once 'functions.php';
require_once 'common.php';
include 'header.tpl';
$articles = new Article($db);
$categories = new Category($db);
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Publications</h1>
    <?php
    foreach($categories->getAll() as $category)
    {
        ?><h2><?php echo html($category['name']); ?></h2><?php
        ?><ul><?php
        foreach($articles->getByCategory($category['id']) as $article)
        {
            ?><li><a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>articles/?id=<?php
                echo (int)$article['id'];
            ?>" title="<?php
                echo html($article['title']);
            ?>"><?php
                echo html($article['title']);
            ?></a>, par <a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>auteurs/?id=<?php
                echo (int)$article['author_id'];
            ?>" title=""><?php
                echo html($article['author_name']);
            ?></a></li><?php
        }
        ?></ul><?php
    }
}
else
{
    $id = (int)$_GET['id'];
    if($article = $articles->getById($id))
    {
        ?>
        <h1 style="text-align: center"><?php
            echo html($article['title']);
        ?></h1>
        <div style="text-align: center; font-style: italic">Par <a
            href="./auteurs.php?id=<?php echo (int)$article['author_id']; ?>"
            title="Publications de <?php
                echo html($article['author_name']);
            ?>"><?php echo html($article['author_name']); ?></a></div>
        <?php
        echo nl2br(html($article['text']));
    }
    else
    {
        echo "Cet article n'existe pas...";
    }
}
include 'footer.tpl';
auteurs.php
Sélectionnez
<?php
require_once 'functions.php';
require_once 'common.php';
include 'header.tpl';
$authors = new Author($db);
$articles = new Article($db);
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Auteurs</h1>
    <ul style="list-style-type: none">
    <?php
    foreach($authors->getAll() as $author)
    {
        ?><li><a href="<?php
            echo html($_SERVER['HTTP_ROOT']);
        ?>auteurs/?id=<?php
            echo (int)$author['id'];
        ?>" title="<?php echo html($author['name']); ?>"><?php
            echo html($author['name']);
        ?></a> (<?php echo (int)$author['nb_articles']; ?> articles)</li><?php
    }
    ?>
    </ul>
    <?php
}
else
{
    $id = (int)$_GET['id'];
    if($author = $authors->getById($id))
    {
        ?>
        <h1 style="text-align: center">Publications de <?php
            echo html($author['name']);
        ?></h1>
        <ul>
        <?php
        foreach($articles->getByAuthor($id) as $article)
        {
            ?><li><a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>articles/?id=<?php
                echo (int)$article['id'];
            ?>" title="Article par <?php echo html($author['name']); ?>"><?php
                echo html($article['title']);
            ?></a></li><?php
        }
        ?>
        </ul>
        <?php
    }
    else
    {
        echo "Cet auteur n'existe pas...";
    }
}
include 'footer.tpl';

VIII-E-3. Nouveaux avantages

Le code peut maintenant être porté sans aucun problème d'un SGBD à un autre. De plus, les requêtes sont facilement identifiables puisqu'elles sont regroupées dans des classes, et nous sommes totalement protégés contre les injections SQL.

VIII-E-4. Nouveaux inconvénients

Les scripts à inclure sont tous regroupés et mélangés, il n'y a aucune organisation. Cela peut convenir pour un site de quelques pages mais, à mesure que le site grossit, il devient apparent qu'il faut structurer ces fichiers. Il est temps d'adopter une structure MVC.

Il reste encore beaucoup de mélange PHP/HTML. Heureusement, nous avons effectué de nombreuses mises en facteur du reste du code, et par conséquent ces mélanges PHP/HTML sont à présent tout à fait lisibles.

VIII-F. URL Rewriting, ou Routage

VIII-F-1. Introduction

Nous pouvons maintenant résoudre le problème des URLs d'accès à nos pages. En effet, nous avons vu plus tôt dans ce cours que les URLs à base de paramètres GET visibles sont peu efficaces, tant pour l'oeil humain que pour les robots qui s'occupent de référencer nos sites.

Une solution à ces problèmes est la réécriture de liens, aussi appelée "URL Rewriting" ou encore "routage".

Nous allons utiliser certains aspects avancés d'Apache, et en particulier le module mod_rewrite (qu'il faut donc activer dans httpd.conf). Il nous faudra aussi de bonnes connaissances en expressions régulières ( aka "regex" ou "expressions rationnelles").

Le principe de la réécriture de liens est d'utiliser une URL publique ne correspondant pas à un fichier sur le serveur. Le mod_rewrite nous permet de router ces URLs vers les scripts de notre serveur, en interne, c'est-à-dire sans causer de redirection HTTP et sans que le client s'en rende compte. Nous pouvons ainsi choisir les paramètres GET les plus adaptés, sans pour autant encombrer le code de nos scripts PHP.

Arborescence sous "C:\Web\online\http\cours-php\5-routing" (DocumentRoot) :
  • /
  • /index.php
  • /articles.php
  • /auteurs.php
Arborescence sous "C:\Web\offline\sites\cours-php\5-routing" (hors de DocumentRoot) :
  • /
  • /AbstractTable.php
  • /Article.php
  • /Author.php
  • /Category.php
  • /common.php
  • /footer.html
  • /functions.php
  • /header.html
  • /MyPDO.php

Pour pouvoir utiliser la réécriture avec Apache dans un fichier .htaccess, vous aurez besoin d'activer l'option +FollowSymLinks :

Sélectionnez
Options +FollowSymLinks
RewriteEngine On

Sans cette option, utiliser le RewriteEngine dans un fichier .htaccess donnera lieu à une erreur HTTP 403 et à une entrée log de ce style :

Sélectionnez
Options FollowSymLinks or SymLinksIfOwnerMatch is off
    which implies that RewriteRule directive is forbidden

VIII-F-2. Les scripts

Nous aurons peu de modifications. Les liens doivent maintenant être absolus afin de ne pas porter à confusion ; pour le reste, le httpd.conf est à peu près le seul fichier à modifier. Je vais indiquer ici uniquement les fichiers modifiés :

Ajouter à "httpd.conf" :
Sélectionnez
<Directory "C:/Web/online/http/cours-php/5-routing">
    AllowOverride None
    php_value include_path ".;C:/Web/offline/sites/cours-php/5-routing"
    SetEnv HTTP_ROOT /cours-php/5-routing/
    RewriteEngine on
    RewriteBase /cours-php/5-routing/
    RewriteRule ^auteurs/$ auteurs.php [L]
    RewriteRule ^auteur/([0-9])-?.*$ auteurs.php?id=$1 [L]
    RewriteRule ^articles/$ articles.php [L]
    RewriteRule ^article/([0-9])-?.*$ articles.php?id=$1 [L]
</Directory>
functions.php
Sélectionnez
<?php
function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
function url_format($string)
{
    $string = str_replace(' ', '-', strtolower($string));
    return html(preg_replace('#[^a-z0-9-]#', '', $string));
}
function generic_autoload($class)
{
    require_once $class.'.php';
}
auteurs.php
Sélectionnez
<?php
require_once 'functions.php';
require_once 'common.php';
include 'header.tpl';
$authors = new Author($db);
$articles = new Article($db);
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Auteurs</h1>
    <ul style="list-style-type: none">
    <?php
    foreach($authors->getAll() as $author)
    {
        ?><li><a href="<?php
            echo html($_SERVER['HTTP_ROOT']);
        ?>auteur/<?php
            echo url_format($author['id'].'-'.$author['name']);
        ?>" title="<?php
            echo html($author['name']);
        ?>"><?php
            echo html($author['name']);
        ?></a> (<?php echo (int)$author['nb_articles']; ?> articles)</li><?php
    }
    ?>
    </ul>
    <?php
}
else
{
    $id = (int)$_GET['id'];
    if($author = $authors->getById($id))
    {
        ?>
        <h1 style="text-align: center">Publications de <?php
            echo html($author['name']);
        ?></h1>
        <ul>
        <?php
        foreach($articles->getByAuthor($id) as $article)
        {
            ?><li><a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>article/<?php
                echo url_format($article['id'].'-'.$article['title']);
            ?>" title="Article par <?php
                echo html($author['name']);
            ?>"><?php echo html($article['title']); ?></a></li><?php
        }
        ?>
        </ul>
        <?php
    }
    else
    {
        echo "Cet auteur n'existe pas...";
    }
}
include 'footer.tpl';
articles.php
Sélectionnez
<?php
require_once 'functions.php';
require_once 'common.php';
include 'header.tpl';
$articles = new Article($db);
$categories = new Category($db);
if(empty($_GET['id']))
{
    ?>
    <h1 style="text-align: center">Publications</h1>
    <?php
    foreach($categories->getAll() as $category)
    {
        ?><h2><?php echo html($category['name']); ?></h2><?php
        ?><ul><?php
        foreach($articles->getByCategory($category['id']) as $article)
        {
            ?><li><a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>article/<?php
                echo url_format($article['id'].'-'.$article['title'])
            ?>" title="<?php
                echo html($article['title']);
            ?>"><?php
                echo html($article['title']);
            ?></a>, par <a href="<?php
                echo html($_SERVER['HTTP_ROOT']);
            ?>auteur/<?php
                echo url_format(
                    $article['author_id'].'-'.$article['author_name']);
            ?>" title=""><?php echo html($article['author_name']); ?></a></li><?php
        }
        ?></ul><?php
    }
}
else
{
    $id = (int)$_GET['id'];
    if($article = $articles->getById($id))
    {
        ?>
        <h1 style="text-align: center"><?php
            echo html($article['title']);
        ?></h1>
        <div style="text-align: center; font-style: italic">Par <a
            href="./auteurs.php?id=<?php echo html($article['author_id']); ?>"
            title="Publications de <?php
                echo html($article['author_name']);
            ?>"><?php echo html($article['author_name']); ?></a></div>
        <?php
        echo nl2br(html($article['text']));
    }
    else
    {
        echo "Cet article n'existe pas...";
    }
}
include 'footer.tpl';

VIII-F-3. Nouveaux avantages

VIII-F-4. Nouveaux inconvénients

L'inconvénient majeur est que nous avons maintenant une infinité de points d'entrée pour chaque page. En effet, les anciennes URLs fonctionnent encore : c'est inévitable puisque les scripts auteurs.php et articles.php sont encore accessibles depuis l'extérieur.

Par ailleurs, on peut maintenant mettre n'importe quelle chaîne à la suite des identifiants numériques : la présence de l'identifiant est suffisante pour charger la page. Cela pose de gros problèmes de duplicate content (plusieurs URLs pour une seule ressource).

Ces deux URLs chargent exactement le même document :
  • http://localhost/cours-php/4-routing/article/6-dcouverte-des-principaux-moteurs-de-template-en-php
  • http://localhost/cours-php/4-routing/article/6-alterner-entre-plusieurs-versions-dapache-et-de-php

Dans cet exemple, la 2° URL est fausse puisque le titre de l'article n° 5 est donné à l'URL conduisant à l'article n° 6. Cependant, nous n'avons mis en place aucun mécanisme pour indiquer à l'utilisateur qu'il n'utilise pas la bonne URL. Si nous envoyons encore une redirection HTTP, les navigateurs risquent de déceler une "redirection infinie" et de ne jamais afficher le document souhaité.

VIII-G. Design pattern MVC (modèle-vue-contrôleur)

VIII-G-1. Introduction

Un design pattern permet de résoudre la plupart des inconvénients énoncés ci-dessus : MVC.

L'objectif est d'utiliser des contrôleurs pour traiter les données, des modèles pour les récupérer et des vues pour les afficher. Un contrôleur principal, appelé front controller ou encore contrôleur frontal , est utilisé pour centraliser l'ensemble des requêtes du client. Ainsi, un seul script PHP est exposé en HTTP, ce qui limite les problèmes de duplicate content et améliore la sécurité du site. C'est le front controller qui répartit ( dispatch ) le travail aux autres contrôleurs.

Puisque les URLs d'un site utilisant une architecture MVC sont fictives, cela nous impose d'utiliser une forme d'URL Rewriting. Ainsi, les URLs peuvent rester identiques à celles définies depuis nos essais d'URL Rewriting. Tout dépend de notre découpage modules/actions.

Notre site actuel est trop simple pour être un exemple suffisamment explicite, mais nous pouvons facilement dégager des actions au sein de chaque contrôleur.

Les contrôleurs :
  • Article : Permet de lister (et d'administrer) les articles ;
  • Auteur : Permet de lister (et d'administrer) les auteurs ;
  • Erreur : Dicte la conduite en cas d'erreur.
Actions dans le contrôleur "Auteur" :
  • index : Liste les acteurs ;
  • display : Affiche les articles d'un auteur ;
  • (filter : Dans un site plus complet, cette action permet d'avoir une liste filtrée) ;
  • (update : Dans un site plus complet, cette action met à jour un auteur) ;
  • (delete : Dans un site plus complet, cette action supprime un auteur) ;
  • etc .
Actions dans le contrôleur "Article" :
  • index : Liste les articles ;
  • display : Affiche un article ;
  • (filter : Dans un site plus complet, cette action permet d'avoir une liste filtrée) ;
  • (update : Dans un site plus complet, cette action met à jour un article) ;
  • (delete : Dans un site plus complet, cette action supprime un article) ;
  • etc .
Actions dans le contrôleur "Erreur" :
  • index : Serveur indisponible ;
  • http : Redirection, page inaccessible ;
  • (sql : Erreur interne) ;
  • etc .

Il est maintenant très clair que nous devrions avoir un contrôleur "Catégorie" et que, depuis le début de ce tutoriel, il manque un script pour afficher les catégories. Une bonne architecture peut aider à déceler les problèmes dans les applications.

Arborescence sous "C:\Web\online\http\cours-php\6-mvc" (DocumentRoot) :
  • /
  • /index.php
Arborescence sous "C:\Web\offline\sites\cours-php\6-mvc" (hors de DocumentRoot) :
  • /
  • /MyPDO.php
  • /Controller
  • /Controller/Article.php
  • /Controller/Author.php
  • /Controller/Error.php
  • /Controller/Index.php
  • /Controller/Template.php
  • /Model
  • /Model/Article.php
  • /Model/Author.php
  • /Model/Category.php
  • /Model/Template.php
  • /View
  • /View/footer.tpl
  • /View/header.tpl
  • /View/article/
  • /View/article/index.tpl
  • /View/article/display.tpl
  • /View/author/
  • /View/author/index.tpl
  • /View/author/display.tpl
  • /View/error/
  • /View/error/index.tpl
  • /View/index/
  • /View/index/index.tpl

VIII-G-2. Tous les scripts

Ajouter à "httpd.conf" :
Sélectionnez
<Directory "C:/Web/online/http/cours-php/6-mvc">
    AllowOverride None
    php_value include_path ".;C:/Web/offline/sites/cours-php/6-mvc"
    SetEnv HTTP_ROOT /cours-php/6-mvc/
    RewriteEngine on
    RewriteCond %{REQUEST_URI} !\.(js|css|jpg|png|gif)$
    RewriteRule .* index.php
</Directory>

La directive RewriteCond ci-dessus n'est pas utile dans notre exemple, mais elle vous montre comment on évite aux images ainsi qu'aux scripts CSS et Javascript etc . de passer par notre contrôleur frontal PHP. En effet, ce serait une très grande perte de ressources et cela n'aurait aucune valeur ajoutée.

index.php
Sélectionnez
<?php
require 'functions.php';
spl_autoload_register('generic_autoload');
Controller_Template::$db = new MyPDO(
    'mysql:host=localhost;dbname=developpez', 'utilisateur', 'motdepasse');
preg_match(
    '#^'.$_SERVER['HTTP_ROOT'].'(?:([a-z]+)/)?#',
    $_SERVER['REQUEST_URI'],
    $match);
if(empty($match[1]))
{
    $controller = Controller_Index::getInstance();
    $controller->index();
}
else
{
    switch($match[1])
    {
        case 'articles':
            $controller = Controller_Article::getInstance();
            $controller->index();
        break;
        case 'auteurs':
            $controller = Controller_Author::getInstance();
            $controller->index();
        break;
        case 'article':
            if(preg_match(
                '#^'.$_SERVER['HTTP_ROOT'].'article/([0-9]+)-.*$#',
                $_SERVER['REQUEST_URI'],
                $match))
            {
                $controller = Controller_Article::getInstance();
                $controller->display($match[1]);
            }
            else
            {
                Controller_Error::documentNotFound(
                    "Page introuvable : URL incorrecte");
            }
        break;
        case 'auteur':
            if(preg_match(
                '#^'.$_SERVER['HTTP_ROOT'].'auteur/([0-9]+)-.*$#',
                $_SERVER['REQUEST_URI'],
                $match))
            {
                $controller = Controller_Author::getInstance();
                $controller->display($match[1]);
            }
            else
            {
                Controller_Error::documentNotFound(
                    "Page introuvable : URL incorrecte");
            }
        break;
        default:
            Controller_Error::documentNotFound(
                "Page introuvable : URL incorrecte");
    }
}
functions.php

function html($string)
{
    return utf8_encode(htmlspecialchars($string, ENT_QUOTES));
}
function url_format($string)
{
    $string = str_replace(' ', '-', strtolower($string));
    return html(preg_replace('#[^a-z0-9-]#', '', $string));
}
function generic_autoload($class)
{
    require_once str_replace('_', '/', $class).'.php';
}
MyPDO.php
Sélectionnez
<?php
class MyPDO extends PDO
{
    public function __construct($dsn, $user=NULL, $password=NULL)
    {
        parent::__construct($dsn, $user, $password);
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    public function prepare($sql, $options=NULL)
    {
        $statement = parent::prepare($sql);
        if(strpos(strtoupper($sql), 'SELECT') === 0) //requête "SELECT"
        {
            $statement->setFetchMode(PDO::FETCH_ASSOC);
        }
        return $statement;
    }
}
Controller/Article.php
Sélectionnez
<?php
class Controller_Article extends Controller_Template
{
    protected $categoriesModel;
    protected function __construct()
    {
        parent::__construct();
        $this->selfModel = new Model_Article();
        $this->categoriesModel = NULL;
    }
    public function index()
    {
        $title = "Liste des publications";
        $this->categoriesModel = new Model_Category();
        $categories = $this->categoriesModel->getAll();
        foreach($categories as $i => $category)
        {
            $categories[$i]['articles'] = $this->selfModel->getByCategory(
                $category['id']);
        }
        header('Content-Type: text/html; charset=utf-8');
        require 'View/header.tpl';
        require 'View/article/index.tpl';
        require 'View/footer.tpl';
    }
    public function display($id)
    {
        $article = $this->selfModel->getById($id);
        if(!$article)
        {
            Controller_Error::documentNotFound();
        }
        else
        {
            $title = $article['title'];
            header('Content-Type: text/html; charset=utf-8');
            require 'View/header.tpl';
            require 'View/article/display.tpl';
            require 'View/footer.tpl';
        }
    }
}
Controller/Author.php
Sélectionnez
<?php
class Controller_Author extends Controller_Template
{
    protected $articlesModel;
    protected function __construct()
    {
        parent::__construct();
        $this->selfModel = new Model_Author();
        $this->articlesModel = NULL;
    }
    public function index()
    {
        $title = "Liste des auteurs";
        $authors = $this->selfModel->getAll();
        header('Content-Type: text/html; charset=utf-8');
        require 'View/header.tpl';
        require 'View/author/index.tpl';
        require 'View/footer.tpl';
    }
    public function display($id)
    {
        $author = $this->selfModel->getById($id);
        if(!$author)
        {
            Controller_Error::documentNotFound("Cet auteur n'existe pas");
        }
        else
        {
            $title = "Publications de ".$author['name'];
            $this->articlesModel = new Model_Article();
            $articles = $this->articlesModel->getByAuthor($id);
            header('Content-Type: text/html; charset=utf-8');
            require 'View/header.tpl';
            require 'View/author/display.tpl';
            require 'View/footer.tpl';
        }
    }
}
Controller/Error.php
Sélectionnez
<?php
abstract class Controller_Error extends Controller_Template
{
    public static function documentNotFound($title)
    {
        header('HTTP/1.1 404 Not Found');
        header('Content-Type: text/html; charset=utf-8');
        include 'View/header.tpl';
        include 'View/error/404.tpl';
        include 'View/footer.tpl';
    }
}
Controller/Index.php
Sélectionnez
<?php
class Controller_Index extends Controller_Template
{
    public function index()
    {
        $title = "Guillaume Rossolini - Tutoriels PHP";
        header('Content-Type: text/html; charset=utf-8');
        require 'View/header.tpl';
        require 'View/index/index.tpl';
        require 'View/footer.tpl';
    }
}
Controller/Template.php
Sélectionnez
<?php
abstract class Controller_Template
{
    protected $selfModel;
    protected static $instance;
    public static $db;
    protected function __construct()
    {
    }
    public static function getInstance()
    {
        $class = get_called_class();
        if(!$class::$instance)
        {
            $class::$instance = new $class();
            return $class::$instance;
        }
    }
}

L'utilisation de la fonction get_called_class() nous oblige à utiliser au minimum la version 5.3 de PHP.

Model/Article.php
Sélectionnez
<?php
class Model_Article extends Model_Template
{
    protected $selectById;
    protected $selectByAuthor;
    protected $selectByCategory;
    public function __construct()
    {
        parent::__construct();
        $sql = 'SELECT ar.title, ar.text,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.id = ?';
        $this->selectById = Controller_Template::$db->prepare($sql);
        $sql = 'SELECT id, title
                FROM article
                WHERE author_id = ?
                ORDER BY title';
        $this->selectByAuthor = Controller_Template::$db->prepare($sql);
        $sql = 'SELECT ar.id, ar.title,
                    au.id AS author_id, au.name AS author_name
                FROM article AS ar
                INNER JOIN author AS au ON ar.author_id = au.id
                WHERE ar.category_id = ?
                ORDER BY ar.title';
        $this->selectByCategory = Controller_Template::$db->prepare($sql);
    }
    public function getById($id)
    {
        $this->selectById->execute(array($id));
        $articles = $this->selectById->fetchAll();
        if(empty($articles[0]))
        {
            return array();
        }
        else
        {
            $articles[0]['text'] = utf8_decode($articles[0]['text']);
            return $articles[0];
        }
    }
    public function getByAuthor($id)
    {
        $this->selectByAuthor->execute(array($id));
        return $this->selectByAuthor->fetchAll();
    }
    public function getByCategory($id)
    {
        $this->selectByCategory->execute(array($id));
        return $this->selectByCategory->fetchAll();
    }
}
class Model_Author extends Model_Template { public function __construct () { parent : : __construct () ; $sql = ' SELECT au . id , au . name , COUNT ( * ) AS nb_articles FROM author AS au LEFT JOIN article AS ar ON ar . author_id = au . id GROUP BY au . id , au . name ORDER BY name ' ; $this - > selectAll = Controller_Template : : $db - > prepare ( $sql ) ; $sql = ' SELECT name FROM author WHERE id = ? ' ; $this - > selectById = Controller_Template : : $db - > prepare ( $sql ) ; } }
class Model_Category extends Model_Template { public function __construct () { parent : : __construct () ; $sql = ' SELECT c . id , c . name , COUNT ( * ) AS nb_articles FROM category AS c INNER JOIN article AS a ON a . category_id = c . id GROUP BY c . id , c . name ORDER BY c . name ' ; $this - > selectAll = Controller_Template : : $db - > prepare ( $sql ) ; } }
abstract class Model_Template { protected $selectAll ; protected $selectById ; public function __construct () { } public function getAll() { $this - > selectAll - > execute () ; return $this - > selectAll - > fetchAll () ; } public function getById( $id ) { $this - > selectById - > execute ( array ( $id )) ; $row = $this - > selectById - > fetchAll () ; if ( empty ( $row [ 0 ] )) { return array () ; } else { return $row [ 0 ] ; } } } <! DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict//EN " " http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd " > < html xmlns = " http : / / www . w3 . org / 1999 / xhtml " xml : lang = " fr - FR " lang = " fr - FR " > < head > < title > <?php echo html( $title ) ; ?> < / title > < meta http - equiv = " content - type " content = " text / html ; charset = utf - 8 " / > <style type = " text / css " > h1 { text-align : center ; } </ style> < / head > < body > < ul style = " text - align : center " > < li style = " display : inline " > < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> " title = " G . Rossolini - Tutoriels PHP " > Accueil < / a > - < / li > < li style = " display : inline " > < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> articles / " title = " G . Rossolini - Liste des publications " > Publications < / a > - < / li > < li style = " display : inline " > < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> auteurs / " title = " G . Rossolini - Liste des auteurs " > Auteurs < / a > < / li > < / ul > < h1 > <?php echo html( $title ) ; ?> < / h1 > < p style = " text - align : center ; font - size : 0 . 7em ; " > Copyright Guillaume Rossolini 2007- <?php echo date ( ' Y ' ) ; ?> < br / > Contact : < a href = " mailto : g - rossolini @ developpez . com " title = " E - mail " > g-rossolini@developpez.com < / a > < / p > < / body > < / html > href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> auteur / <?php echo url_format( $article [ ' author_id ' ] . ' - ' . $article [ ' author_name ' ] ) ; ?> " title = " Publications de <?php echo html( $article [ ' author_name ' ] ) ; ?> " > <?php echo html( $article [ ' author_name ' ] ) ; ?> < / a > < / div > <?php echo nl2br (html( $article [ ' text ' ] )) ; ?> < h2 > <?php echo html( $category [ ' name ' ] ) ; ?> < / h2 > < ul > <?php foreach ( $category [ ' articles ' ] as $article ) : ?> < li > < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> article / <?php echo url_format( $article [ ' id ' ] . ' - ' . $article [ ' title ' ] ) ?> " title = " <?php echo html( $article [ ' title ' ] ) ; ?> " > <?php echo html( $article [ ' title ' ] ) ; ?> < / a > , par < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> auteur / <?php echo url_format( $article [ ' author_id ' ] . ' - ' . $article [ ' author_name ' ] ) ; ?> " title = " " > <?php echo html( $article [ ' author_name ' ] ) ; ?> < / a > < / li > <?php endforeach ; ?> < / ul > <?php endforeach ; ?> > <?php foreach ( $articles as $article ) : ?> < li > < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> article / <?php echo url_format( $article [ ' id ' ] . ' - ' . $article [ ' title ' ] ) ; ?> " title = " Article par <?php echo html( $author [ ' name ' ] ) ; ?> " > <?php echo html( $article [ ' title ' ] ) ; ?> < / a > < / li > <?php endforeach ; ?> < / ul > > <?php foreach ( $authors as $author ) : ?> < li > < a href = " <?php echo html( $_SERVER [ ' HTTP_ROOT ' ] ) ; ?> auteur / <?php echo url_format( $author [ ' id ' ] . ' - ' . $author [ ' name ' ] ) ; ?> " title = " <?php echo html( $author [ ' name ' ] ) ; ?> " > <?php echo html( $author [ ' name ' ] ) ; ?> < / a > ( <?php echo (int) $author [ ' nb_articles ' ] ; ?> articles) < / li > <?php endforeach ; ?> < / ul > ?> n'existe pas. > < p > J'ai suivi une formation dans plusieurs domaines, notamment la communication, le développement de logiciels, la gestion de projet, < span style = " font - style : italic " > etc < / span > . < / p > < p > Aujoud'hui, je suis un développeur PHP muni de la certification Zend : < br / > < a href = " http : / / www . zend . com / store / education / certification / authenticate . php ? ClientCandidateID = ZEND005053 & RegistrationID = 221302133 " title = " Zend Certified Engineer Details " > < img src = " http : / / www . zend . com / images / training / php5_zce_logo . gif " alt = " Logo ZCE " style = " border : none " / > < / a > < / p >

VIII-G-3. Conclusion

Cette architecture est largement plus robuste et flexible que celle du premier exemple, mais elle n'est pas encore tout à fait parfaite. MVC a tendance à pousser à l'utilisation de nombreux Design patterns , notamment Singleton et Registry . Je n'ai pas mis en place un système trop complexe car notre petit site d'exemple ne le justifie pas, mais par exemple la connexion à la BDD mériterait un pattern Registry plutôt que d'être mise dans un contrôleur ou dans un modèle, alors que ni l'un ni l'autre ne sont prévus pour cela.

Par ailleurs, la simplicité fonctionnelle du site actuel ne rend pas justice au front controller . Je vous recommande d'étudier le fonctionnement de frameworks MVC et de juger par vous-mêmes de l'approche la plus adaptée à vos projets.

Ne vous fiez pas à la taille des scripts présentés ici pour juger de l'utilité de ce que je viens de vous montrer. Ce site d'exemple est bien trop petit pour que MVC soit utilisé correctement. Pour mieux comprendre l'intérêt du pattern MVC, ajoutez des fonctionnalités à ce site dans chacune des approches vues ici. C'est un très bon exercice de style...

Model_Author


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours

IX. Démarche qualité

IX-A. Introduction

Je ne vais évoquer ici que ce qui touche directement à PHP. L'analyse (UML ou autre), les outils de contrôle de versions de code source (SCM) et les outils de rapport de bug (bug tracking system) ne rentrent donc pas dans le cadre de mon introdution à la démarche qualité.

IX-B. Environnements

IX-B-1. Introduction

Je vais vous proposer une méthode pour développer des applications de manière à la fois flexible et qualitative.

Nous allons utiliser trois serveurs par projet :
  1. Développement ;
  2. Staging / Test ;
  3. Production / Live.

IX-B-2. Serveur "dev"

Le premier niveau est le serveur de Développement. Il est destiné aux développeurs, eux seuls y ont donc accès.

Une technique flexible pour le développement en équipe, consiste à utiliser un seul serveur pour le développement des applications. Si le développeur est seul dans son équipe, il peut utiliser sa propre machine ; dans le cas contraire, il est préférable d'utiliser une seule machine commune à tous les développeurs. Le serveur Web Apache permet de configurer des hôtes virtuels (VirtualHost) dans son fichier de configuration httpd.conf : c'est une manière idéale d'attribuer un serveur Web complet à chaque développeur.

Un module très utile pour configurer simplement Apache dans un tel cas de figure est mod_macro, qui permet de définir une macro (un "gabarit") puis de la réutiliser pour chaque développeur. Si vous avez un serveur de noms (DNS) sur votre réseau, vous pouvez l'utiliser pour lier chaque VirtualHost à un développeur.

Côté PHP, vous aurez besoin de toutes les extensions du serveur de Production ainsi que d'extensions spécifiques au développement, par exemple Xdebug (cf. le débogage ci après).

IX-B-3. Serveur "staging/test"

C'est le serveur Staging qui contient le dépôt (repository) de votre outil SCM (par exemple CVS ou Subversion). Vous pouvez utiliser un utilitaire de "post-commit" pour que le code envoyé par les développeurs soit déployé automatiquement sur le serveur Web de l'environnement Staging.

La configuration de ce serveur Web est strictement identique à celle du serveur de Production : cela permet de s'assurer que la transition Staging/Prod se fera sans problème. L'idée est de faire valider par l'équipe, puis par le client, l'intégralité de l'application avant de la déployer (mise en Production).

Un nom alternatif pour Staging est Test.

IX-B-4. Serveur "production/live"

Le serveur de Production est la dernière phase. L'application doit avoir été validée par l'équipe puis par le client avant d'être envoyée en Production.

Souvenez-vous de ne jamais rien modifier directement en Production : développez un patch en Dev, faites un commit dans le SCM en Test puis utilisez le serveur Web de Test pour faire valider le patch. Une fois que vous avez la certitude que le code déposé dans votre outil SCM fonctionne, déployez-le en Production.

Un nom alternatif pour Production est Live.

IX-B-5. Conclusion

Ne modifiez jamais le code en Production. Déployez à partir de votre dépôt en Test (après validation).

IX-C. Tests

IX-C-1. Introduction

Les tests sont une étape fondamentale de la démarche qualité. Ils permettent de valider le fonctionnement de l'application avant de la soumettre au client.

IX-C-2. Tests unitaires

Les tests unitaires sont les tests les plus élémentaires. Il s'agit de vérifier qu'une fonctionnalité réagit bien comme on s'y attend.

Les tests ne sont pas utiles s'ils ne sont pas automatisés, c'est pourquoi on parle se "suites de tests".

Il existe plusieurs outils PHP pour écrire les tests de vos applications : PHPUnit, SimpleTest, phpt...

N'oubliez pas de rédiger un test à chaque rapport de bug : cela permet de valider à la fois l'existence du bug et sa résolution effective. Intégrer ces tests de bugs à vos suites de tests permet d'éviter les régressions (réapparition d'anciens bugs) lors de la vie d'une application. Un test peut permettre de valider soit qu'une partie de l'application fonctionne correctement, soit qu'un bug n'est pas réintroduit.

Site de PHPUnit : http://www.phpunit.de/
Site de SimpleTest : http://simpletest.sourceforge.net/
Site de phpt : http://qa.php.net/write-test.php

PHPUnit est intégré en standard dans plusieurs EDI du marché. Renseignez-vous sur les EDI avant d'investir votre temps et/ou votre argent dans l'un d'eux.

IX-C-3. Test-driven development (TDD)

Le développement orienté par les tests consiste à écrire les tests avant d'écrire le code qui répond à la fonctionnalité testée. C'est le test qui décrit le besoin fonctionnel. Cela permet d'une part de ne pas oublier de tests et, d'autre part, de s'assurer que la fonctionnalité a bien été codée en conformité avec le besoin.

IX-C-4. Conclusion

Les tests sont une étape fondamentale de la vie d'une application. Toutefois, c'est à vous de choisir votre stratégie.

IX-D. Débogage

IX-D-1. Introduction

La complexité des applications développées aujourd'hui ne permet plus de déboguer un site avec des armées d'echo ou die(). Ces techniques fonctionnent bien pour de petites applications mais, dès que l'application gagne en complexité et en taille, ajouter des instructions echo augmente le risque d'apparition de nouveaux bugs.

Par conséquent, il est primordial de mettre en place une solution permettant de déboguer les scripts à la manière d'un compilateur C++ : avec des points d'arrêt, des états instantanés sur les variables, une exécution pas à pas, etc.

C'est possible en installant une extension PHP. Historiquement, la première extension à permettre ce type de débogage est DBG. Bien que son protocole de communication soit encore utilisé, elle est aujourd'hui remplacée par sa concurrente Xdebug, bien plus performante et mise à jour plus fréquemment.

Installer une telle extension permet non seulement de déboguer l'application pas à pas, mais aussi d'en établir un profil afin de déterminer quels scripts consomment le plus de mémoire ou de temps CPU. Le débogage est donc non seulement un outil de développement, mais aussi d'optimisation.

  • APD (Advanced PHP Debugger - extension PECL dont le développement semble arrêté depuis 2004) ;
  • DBGDBG (extension incompatible avec PHP 5.3 et 6) ;
  • XdebugXdebug (extension PECL) ;
  • Zend StudioZend Studio (application commerciale).

IX-D-2. Xdebug

Image non disponible

Site : http://xdebug.org/Xdebug pour PHP

Xdebug est l'extension de débogage PHP la plus efficace en ce moment. Elle est développée par Derick Rethans, elle est disponible pour toutes les versions de PHP, elle est gratuite (licence compatible PHP) et elle s'interface avec n'importe quel EDI capable de débogage avec le protocole DBGp.

Exemple de configuration php.ini pour Xdebug
Sélectionnez
zend_extension_ts = "full/path/to/php_xdebug.ext"
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.extended_info = On
xdebug.remote_mode = req; "req" ou "jit"
xdebug.profiler_enable = On
xdebug.profiler_enable_trigger = On
xdebug.profiler_output_dir = "path/to/profiling"
xdebug.profiler_output_name = "cachegrind.out.%t"
xdebug.trace_output_dir = "path/to/temp"
xdebug.trace_output_name = "trace.%c"

IX-E. Motifs de conception (design patterns)

IX-E-1. Introduction

Un motif de conception (design pattern) est une technique permettant de résoudre un problème standard. Plutôt que d'avoir recours à votre propre solution à un problème, il est préférable de réutiliser un motif déjà identifié et résolu par d'excellents concepteurs de logiciels.

Un exemple simple concerne les variables globales. Tous les enseignements sont formels (avec raison) : il faut éviter les variables globales. Pourtant, il arrive fréquemment d'avoir besoin d'une variable dans plusieurs contextes à la fois, et les problèmes de portée (scope) nous mettent des bâtons dans les roues. La solution à ce problème est le pattern Registry. Il s'agit d'une classe qui regroupe toutes les variables qui pourraient être globales et qui nous les donne au besoin. Ce pattern repose sur le pattern Singleton, qui nous assure d'avoir une seule instance du registre dans tout notre code.

Les motifs de conception reposent très largement sur la POO, il est donc fortement recommandé d'être à l'aise dans ce domaine.

IX-E-2. Pattern MVC

Voici l'un des plus connus des motifs de conception pour sites Web.

Le principe est d'avoir un unique point d'entrée au site Web : le front controller. Cela permet de placer la presque totalité de vos scripts en-dehors de la racine du serveur Web, et ainsi de protéger votre application. De plus, cela vous incite à utiliser une technique de réécriture d'URL (URL rewriting), ce qui a de nombreux avantages. Nous avons déjà débattu de tout cela dans ce tutoriel, je ne vais donc pas y revenir.

Le pattern MVC oblige le développeur à séparer la structure de son application en trois types de composants :
  • M pour "modèles" : Ce sont les classes donnant accès aux données ;
  • V pour "vues" : C'est la partie de l'application qui se charge de l'affichage des données ;
  • C pour "contrôleurs" : Ces classes s'occupent de charger les modèles adéquats, d'appliquer les traitements nécessaires et d'envoyer aux vues les données demandées.

IX-E-3. Pattern Singleton

Le pattern Singleton permet de s'assurer qu'il n'existe qu'une seule instance de la classe. C'est utile dans certaines situations précises, comme par exemple certains composants d'architecture MVC ou un pattern Registry.

L'idée du Singleton est de rendre le constructeur protected afin d'empêcher l'instanciation de la classe, et d'utiliser une méthode statique pour construire une instance unique. Par convention, cette méthode s'appelle getInstance().

Sélectionnez
<?php
class MyClass
{
    protected $instance;

    protected function __construct(){}

    public static function getInstance()
    {
        if(empty(self::$instance))
        {
            self::$instance = new self();
        }

        return self::$instance;
    }
}

Au lieu d'instancier cette classe (opérateur "new"), le programmeur appelle la méthode getInstance().

Sélectionnez
<?php
$objet = new MyClass(); //utilisation standard, ne peut pas fonctionner ici
Sélectionnez
<?php
$objet = MyClass::getInstance(); //pattern Singleton

Si vous êtes certain de n'avoir toujours besoin que d'une seule connexion à la base de données dans vos scripts, vous pouvez par exemple utiliser ce pattern pour implémenter votre classe de connexion. Un autre exemple peut être votre classe de configuration.

IX-E-4. Conclusion

Ne sur estimez jamais votre capacité à résoudre seul les problèmes auxquels vous faites face. D'autres développeurs y ont été confrontés avant vous et ils ont eu tout le temps de parvenir à des solutions à la fois élégantes, simples et robustes. Ayez l'humilité d'utiliser leurs solutions plutôt que d'essayer d'y parvenir vous-même car, ayant moins d'expérience, vos solutions ne seront probablement pas aussi efficaces. Vous aurez tout le temps de révolutionner le monde du développement logiciel lorsque vous aurez suffisamment de recul pour pouvoir vous permettre de critiquer l'existant.

De nombreux autres motifs sont disponibles. Nous ne pouvons pas tous les expliquer ici car c'est l'objectif d'ouvrages bien plus complets.

IX-F. Frameworks

IX-F-1. Introduction

Lorsque vous commencez à développer, il est préférable de simplement apprendre à utiliser le langage. Cependant, avec le temps, vous vous rendez compte que vous résolvez sans arrêt les mêmes problèmes et que vous réutilisez (recopiez) des composants d'une application à l'autre. Cela signifie que vous avez développé une sorte de framework utilisé par 0% des autres développeurs.

De plus, se plonger dans le code écrit (sans framework) par un autre développeur est souvent un cauchemard parce qu'il n'utilise pas les mêmes conventions que vous. Maintenir les applications des autres développeurs est donc problématique à cause de la courbe d'apprentissage du framework spécifique à chaque application (également utilisé par environ 0% des développeurs).

Une solution à tous ces problèmes est d'adopter des composants standards, écrits et utilisés par une communauté de développeurs. Cela ne vous immunise pas totalement dans le cas où vous devez maintenir du code développé avec un autre framework, mais les probabilités de rencontrer un framework inconnu passent de 100% à un nombre bien plus raisonnable. Un framework regroupe des composants standards, choisis spécialement et adaptés pour fonctionner parfaitement ensemble, et propose des conventions de codage afin de réduire au maximum les différences dans les styles de programmation des développeurs de la communauté.

Utiliser un framework ne produira pas un site Web en deux clics (ce n'est pas un CMS). En revanche, il vous permettra de produire des sites uniques tout en simplifiant les problématiques standards.

Les frameworks sont généralement classés comme "glue" ou bien "full-stack". Les premiers sont simplement des ensembles flexibles de composants, tandis que les autres obligent le développeur à suivre une ligne de conduite bien précise. Les deux catégories ont leurs avantages, et le choix d'un framework par rapport à un autre dépend du cahier des charges de chaque projet.

IX-F-2. CakePHP

Image non disponible

Site : http://cakephp.org/

Tutoriel rapide : http://manual.cakephp.org/appendix/blog_tutorialThe Cake Blog Tutorial

CakePHP est un framework de type "full-stack".

Il adhère au principe "convention over configuration" qui prône les conventions de codage plutôt que la configuration. Cela permet de réduire les temps de développement, mais enlève au développeur une marge de liberté sur la structure de sa BDD ou de son code, voire dans certains cas sur l'aspect de son site.

La documentation de CakePHP suit de bons principes et le champ sémantique homogène (cookies, bakery...) dénote une bonne ambiance au sein de la communauté.

Liste des conventions de CakePHP : http://manual.cakephp.org/appendix/conventions

IX-F-3. eZ Components

Image non disponible

Site : http://ezcomponents.org/

Vue d'ensemble : http://ezcomponents.org/overview

eZ Components est la bibliothèque développée par eZ Systems et utilisée dans eZ Publish. Ce n'est pas un framework à proprement parler puisqu'il ne se présente pas en tant que tel, mais on peut probablement en parler comme d'un framework de type "glue". eZ Components a le soutien de Derick Rethans, l'un des participants majeurs à PHP (internals, xdebug...).

Tutoriels eZ Publish : http://php.developpez.com/cours/?page=scripts#ezp

IX-F-4. PEAR

Image non disponible

Site : http://pear.php.net/

PEAR est l'un des plus anciens frameworks "glue" pour PHP. Ils proposent des conventions de codage depuis de nombreuses années, et certaines ont été reprises par d'autres projets depuis lors. C'est aujourd'hui une bibliothèque de composants parfois rendus obsolètes par les frameworks concurrents.

Le groupe PEAR a su standardiser la diffusion d'extensions et de composants, ainsi que distribuer un outil qui n'est pas sans rappeler apt-get (Debian GNU/Linux).

PEAR est aujourd'hui connu principalement pour sa démarche qualité et pour ses outils de distribution (go-pear etc.).

IX-F-5. symfony

Image non disponible

Site : http://www.symfony-project.org/

Framework de type "full-stack".

Le projet symfony, d'origine française, s'appuie sur l'expérience de ses développeurs en matière de développement de sites Internet. Toutes les problématiques rencontrées de manière récurrente par les développeurs du framework ou par ses utilisateurs sont répertoriées, mises en facteur et éventuellement incluses dans une version ultérieure.

L'un des gros points forts de symfony est sa documentation : il existe un livre, disponible à la vente ou bien en téléchargement gratuit, également consultable en ligne et traduit en français.

Le projet symfony a le soutien de l'entreprise française SensioLabs.

Tutoriels symfony : http://php.developpez.com/cours/?page=frameworks#symfony

IX-F-6. Zend Framework

Image non disponible

Site : http://framework.zend.com/

Framework de type "glue" à tendances "full-stack".

Zend, qui fournit notamment le Zend Engine depuis de nombreuses versions de PHP, a commencé en 2007 à diffuser son propre framework. L'idée est de diffuser un framework de très haute qualité en matière de développement de sites Web : best practices, design patterns, OOP, sécurité...

Le framework suit un procesus communautaire de développement, chaque proposition est étudiée avec soin puis soumise à validation par l'ensemble de la communauté, la documentation (traduite dans de nombreuses langues) est révisée fréquemment, des tests unitaires sont systématiquement exigés, etc.

Le projet Zend Framework est soutenu par Zend Technologies.

Tutoriels Zend Framework : http://zend-framework.developpez.com/cours/

IX-F-7. Conclusion

Au risque de me répéter...

Ne sur estimez jamais votre capacité à résoudre seul les problèmes auxquels vous faites face. D'autres développeurs y ont été confrontés avant vous et ils ont eu tout le temps de parvenir à des solutions élégantes. Ayez l'humilité d'utiliser leurs solutions plutôt que d'essayer d'y parvenir vous-même car, ayant moins d'expérience, vos solutions ne seront probablement pas aussi efficaces. Vous aurez tout le temps de révolutionner le monde du développement logiciel lorsque vous aurez suffisamment de recul pour pouvoir vous permettre de critiquer l'existant.


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours/?page=optimiser

X. Optimisation

X-A. Optimiser un script PHP

Une fois que votre application fonctionne parfaitement, vous pouvez essayer d'optimiser son temps d'exécution ou sa consommation de mémoire. Cependant, prenez garde à ne pas tomber dans l'eccès.

Optimiser le code PHP est généralement un faux problème pour plusieurs raisons :
  • Ce qui prend le plus de temps n'est pas le code PHP mais les accès réseau (requêtes BDD, webscraping) ou disque (includes) ;
  • Un outil de cache d'opcode peut se charger d'optimiser votre code PHP à votre place (dans une certaine mesure) ;
  • Optimiser le code à outrance produit habituellement du code très complexe, donc difficile à maintenir.

X-B. Mise en cache

La mise en cache vous permet de ne pas éxécuter de nouveau du code qui a déjà été exécuté récemment. Cela comporte une notion de temporalité.

Prenez l'exemple d'un site d'actualités dans lequel la page d'accueil présente les actualités les plus récentes. Chaque visiteur de la page d'accueil oblige PHP à envoyer une requête SQL par le réseau, attendre la réponse du SGBD, récupérer les données depuis le socket ouvert pour l'occasion, les traiter puis les afficher. Sachant que le taux de visites est très nettement supérieur à la fréquence de mise à jour des actualités, la très grande majorité des requêtes sont redondantes.

Admettons que notre site d'actualités génère 500 visites quotidiennes mais qu'il y a tout juste 10 nouvelles actualités par jour. Cela suppose 10 mises à jour de la page d'accueil contre 500 visites : entre 490 et 500 de ces visites quotidiennes obligent donc PHP à effectuer inutilement les traitements résumés ci-dessus...

Une solution de mise en cache serait par exemple de générer le code HTML complet de la page d'accueil à chaque mise à jour de la BDD. Ainsi, aucune requête inutile n'est envoyée au SGBD. Cependant, cette technique n'est pas raisonnable pour la totalité du site à cause de la trop grande quantité potentielle de contenu à compiler à l'avance. De plus, le contenu d'une URL peut changer en fonction de l'internaute. Il faut donc avoir recours à des techniques plus subtiles, et c'est ici que les outils de mise en cache deviennent intéressants.

Exemples d'outils :
  • APCAlternative PHP Cache (extension PECL, incluse en standard à partir de PHP6) ;
  • eAcceleratoreAccelerator (extension) ;
  • memcacheFonctions Memcache (extension PECL)
  • Zend_Cache (composant d'un framework) ;
  • Zend PlatformZend Platform (application commerciale) ;
  • etc.

X-C. Compiler un script PHP

Dans certaines situations, il peut être intéressant de compiler son code PHP en bytecode. Nous pourrions par exemple avoir besoin de distribuer notre application sous forme d'exécutable afin d'éviter aux utilisateurs de devoir installer PHP ; une autre raison est simplement de distribuer une application PHP en protégeant son code source (obfuscation).

Exemples d'outils :
  • bcompilerCompilateur bytecode pour PHP (extension PECL) ;
  • ionCube PHP EncoderionCube PHP Encoder (application commerciale) ;
  • WinbinderWinBinder: The native Windows binding for PHP (extension pour Windows) ;
  • Zend GuardZend Guard (application commerciale) ;
  • etc.

X-D. Compiler PHP

N'oubliez pas que compiler PHP peut améliorer les temps de réponse, en particulier si vous êtes sous Windows puisque les exécutables distribués sont compilés contre d'anciennes DLL. Certaines personnes ont remarqué jusqu'à 30% d'amélioration.

Compiler PHP vous permet aussi d'activer uniquement les extensions dont vous avez besoin, ce qui réduit l'empreinte mémoire.

Je ne vais pas m'étendre sur le sujet, puisqu'il y a beaucoup de concepts qui dépassent le cadre des connaissances requises d'un développeur PHP.

Pour compiler PHP, vous devez également compiler ses dépendances. Certaines sont obligatoires (distribution standard : bindlib, libiconv et libxml2), d'autres sont optionnelles (extensions). Si vous souhaitez compiler PHP pour l'utiliser comme module Apache, vous aurez également besoin de compiler Apache.

Si vous êtes sous un système Unix ou dérivé, vous êtes probablement habitué à compiler les applications avant de les utiliser.

En revanche, si vous êtes sous Windows, sachez que les fichiers binaires distribués sur php.net sont compilés avec Microsoft Visual Studio 6. Ce compilateur n'étant plus distribué par Microsoft, il devient difficile de se le procurer mais PHP peut être compilé à l'aide d'une version plus récente de Visual Studio. Cependant, il n'est pour le moment pas possible de compiler Apache avec une version plus récente que Visual Studio 6. En résumé, pour compiler PHP comme module Apache, vous avez besoin de Visual Studio 6.

Cela pose certains problèmes outre la disponibilité du compilateur : les nouvelles versions de Visual Studio sont plus efficaces, les binaires compilés sont plus rapides, plus fiables, etc. Il n'y a pour le moment pas de solution à ce problème, seul VS 6 permet de compiler PHP comme module Apache.

Elizabeth Marie Smith est en train de chercher à compiler PHP et toutes ses dépendances avec Visual Studio, version 2005 au minimum. Si vous souhaitez l'aider, rendez-vous sur son blog : http://elizabethmariesmith.com/

X-E. Développer une extension

Voici un sujet à la fois vaste et primordial. Il n'est pas demandé à un développeur PHP de savoir développer une extension pour PHP, mais il faut savoir que c'est possible.

PHP étant écrit en langage C, ses extensions suivent naturellement son exemple. Une extension PHP utilise l'API du Zend Engine. Pour rappel, PHP 5 utilise le Zend Engine version 2, tandis que PHP 6 en utilisera la version 3.

Une extension écrite en langage C donne de meilleures performances qu'un script écrit en langage PHP. Dans certaines situations, il peut être intéressant de compiler une extension plutôt que d'écrire un script.

Pour en savoir plus : PHP at the Core: A Hacker's Guide to the Zend Engine


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours

XI. Aider la communauté

XI-A. Introduction

Le site officiel de PHP utilise CVS pour la majorité des contributions. Dans le jargon, avoir un compte CVS sur php.net s'appelle "avoir du karma". Les mainteneurs accordent un accès CVS à ceux qui apportent des patches ou du nouveau code.

Bien que PHP lui-même soit écrit en langage C, vous n'avez pas nécessairement besoin de savoir programmer en langage C pour aider la communauté. En fait, vous n'avez pas besoin de savoir programmer du tout... Si vous connaissez le langage C, vous pouvez apporter votre aide au développement du core ou des extensions ; si vous connaissez seulement PHP, vous pouvez aider à l'écriture de tests ; dans tous les cas, vous pouvez aider à la documentation (anglaise ou traductions) et à l'éducation des autres développeurs.

Les personnes ayant du karma sont souvent considérées comme des mentors.

XI-B. Détection et correction de bugs

Lorsque vous pensez avoir détecté un bug dans le fonctionnement de PHP, vérifiez à deux fois. Le but est de ne pas faire un rapport de bug en double ou factice, car cela donne trop de travail de modération au PHP Group et car cela ralentit le développement du langage.

Vous devriez suivre ces étapes :
  1. Vérifier dans le Bug System de php.net : http://bugs.php.net/ PHP bug tracking system ;
  2. Chercher dans les listes de diffusion une ancienne mention de ce bug (c'est peut-être une feature que vous ne connaissiez pas) : http://php.net/mailing-lists.php Listes de diffusion PHP ;
  3. Envoyer un e-mail à la liste de diffusion concernée, en précisant que vous n'avez rien trouvé ni dans le système de rapport de bugs ni dans les listes ;
  4. Lorsque quelqu'un ayant du "karma" vous répond que vous devriez soumettre un rapport de bug, allez-y.

Si vous souhaitez résoudre un bug, proposez simplement un patch à la liste de diffusion adéquate : http://php.net/mailing-lists.php Listes de diffusion PHP

N'envoyez pas de message inutile aux listes de développement et à Internals car elles sont prévues essentiellement pour faire avancer PHP. Toute question doit se faire sur la liste php-general, réservez les autres listes pour les patches.

XI-C. Tester le code source de PHP

Le code source de PHP est testé au moyen de scripts PHP portant (par convention) l'extension ".phpt", un format proposé par la "PHP Quality Assurance Team" : http://qa.php.net/.

Les statistiques sur la couverture du code par les tests est visible ici : Test and code coverage analysis PHP: Test and Code Coverage Analysis .

Les tests phpt sont apparus avec la version 5 de PHP. Le groupe QA s'est construit à l'initiative de Stefan Esser, qui l'a depuis quitté pour des raisons personnelles.

Tous les tests sont disponibles depuis CVS mais aussi dans les archives téléchargées du code source de PHP.

Voici la syntaxe complète d'un test :

http://cvs.php.net/viewvc.cgi/qaweb/phpt_details.php?view=markup
Sélectionnez
--TEST--
--CREDITS--
--SKIPIF--
--POST--
--GZIP_POST--
--DEFLATE_POST--
--POST_RAW--
--GET--
--COOKIE--
--STDIN--
--INI--
--ARGS--
--ENV--
--FILE--
--FILEEOF--
--FILE_EXTERNAL--
--EXPECT--
--UEXPECT--
--EXPECTF--
--UEXPECTF--
--EXPECTREGEX--
--UEXPECTREGEX--
--REDIRECTTEST--
--HEADERS--
--EXPECTHEADERS--
--CLEAN--
===DONE===

Chaque section est indiquée par un marqueur --SECTION-- sur une ligne vide. Le marqueur est suivi de son contenu, par exemple :

PHP-SRC/tests/classes/abstract.phpt
Sélectionnez
--TEST--
ZE2 An abstract method may not be called
--SKIPIF--
<?php if (version_compare(zend_version(), '2.0.0-dev', '<')) die('skip ZendEngine 2 needed'); ?>
--FILE--
<?php
abstract class fail {
    abstract function show();
}
class pass extends fail {
    function show() {
        echo "Call to function show()\n";
    }
    function error() {
        parent::show();
    }
}
$t = new pass();
$t->show();
$t->error();
echo "Done\n"; // shouldn't be displayed
?>
--EXPECTF--
Call to function show()
Fatal error: Cannot call abstract method fail::show() in %s on line %d

L'effet d'un test est facile à deviner d'après le code du test. Il s'agit de tests unitaires extrêmement simples, ayant le minimum possible de code permettant de vérifier qu'une fonctionnalité précise donne le résultat attendu, etc . Dans le cas ci-dessus, le test s'assure qu'une méthode abstraite ne peut pas être appelée par du code PHP. Si le test indiquait le mot "Done", alors il faudrait revoir (dans le code C) la manière dont les classes abstraites sont gérées par PHP.

Lancer tous les tests en ligne de commandes :
Sélectionnez
cd path/to/php
php run-tests.php

XI-D. Documentation

La documentation anglaise est la première ressource. Ce qui n'est pas encore documenté doit l'être d'abord en anglais, puis éventuellement traduit dans d'autres langues si des volontaires se proposent. Toutes les traductions obsolètes (non mises à jour) ont été retirées du site officiel ainsi que des miroirs, afin d'éviter d'orienter les lecteurs dans de mauvaises directions.

La documentation de PHP est écrite au format DocBook About the DocBook Standard , un schéma XML standard qui permet de générer la documentation finale dans le format voulu (gabarits) grâce à certaines transformations XSLT.

À l'heure de la rédaction de ce cours, un outil est en cours d'écriture et d'adoption par le PHP Group afin de faciliter les participations à la traduction de la documentation PHP : PHPDOC Online XML Editing Tool The PHPDOC Online XML Editing Tool (attention, ce lien peut changer à tout moment puisque l'outil est encore en bêta). Les sources de cet outil sont disponiles sur le CVS officiel.

La documentation finale est compilée à l'aide de PhD PhD -- Building the PHP manuals of the future .

XI-E. Éducation

Enseigner PHP est sans doute la manière la plus simple d'aider la communauté. C'est aussi très complexe, puisqu'il faut connaître tous les aspects du langage et en suivre les évolutions.

De très nombreuses communautés naissent, grandissent et disparaissent avec le temps. Autant la diversité a du bon, autant au jourd'hui il faut se faire une raison : créer une nouvelle communauté autour de PHP n'aide en rien la communauté au sens large. Il est nettement plus utile de rejoindre une communauté existante et d'y joindre vos efforts, que d'essayer d'en faire naître une nouvelle qui rentrera "en concurrence" avec les autres (au sens où elle partagera le temps des lecteurs entre les diverses communautés).

Outre la création d'une communauté, vous pouvez tout simplement rédiger des billets de blog, des tutoriels, des livres, etc . Toutes ces ressources, bien rédigées, organisées et référencées, sont extrêmement utiles aux développeurs de tous niveaux.


Cours de PHP 5

Vu dans ... https://g-rossolini.developpez.com/tutoriels/php/cours

XII. Conclusion

XII-A. Épilogue

Historiquement, PHP est un langage de script.

Aujourd'hui, PHP conserve cet héritage mais il permet également de construire des applications très complexes, d'un niveau de qualité tel qu'une entreprise peut l'exiger. Il existe des frameworks, des standards de codage, des extensions, une communauté, etc.

PHP n'est pas adapté pour toutes les situations mais, dans un environnement Web, c'est aujourd'hui un excellent choix face à d'autres technologies comme Java, .NET ou ColdFusion.

XII-B. Liens

Tous les sites du PHP Group sont recencés ici : PHP.net: A Tourist's Guide.


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant/

Table des matières

  • Page 1
    • I. PHP ETAPE 1 : INTRODUCTION
      • I-0. Les pré-requis
        • I-0-a. Ce qu'il vous faut connaître
        • I-0-b. Ce qu'il vous faut installer
          • I-0-b-a. Si vous êtes sous Windows
          • I-0-b-b. Si vous êtes sur Mac
          • I-0-b-c. Si vous êtes sous Linux
        • I-0-c. Et c'est tout pour ce tutoriel !
      • I-1. Que fait PHP ?
      • I-2. Quand PHP intervient-il ?
      • I-3. Un zeste d'algorithmique
        • I-3-a. Tout d'abord le concept de variable
        • I-3-b. La programmation
        • I-3-c. L'instruction en boucle
        • I-3-d. L'instruction en condition
        • I-3-e. Pour conclure sur la programmation
      • I-4. TP1
      • I-5. Correction du TP1
  • Page 2
    • II. PHP ETAPE 2
      • II-1. Comment ça marche ?
      • II-2. Les variables
      • II-3. echo
      • II-4. Guillemets ou Apostrophes ?
        • II-4-a. Ce que je ne conseille pas
        • II-4-b. Ce que je conseille
      • II-5. TP2
      • II-6. Correction du TP2
  • Page 3
    • III. PHP ETAPE 3
      • III-1. Les formulaires
      • III-2. La syntaxe de la condition if
      • III-3. Les formulaires, suite
      • III-4. Les formulaires, fin
      • III-5. TP3
      • III-6. Correction du TP3
  • Page 4
    • IV. PHP ETAPE 4
      • IV-1. Les tableaux simples
      • IV-2. Les commentaires
      • IV-3. Les tableaux associatifs
      • IV-4. La boucle foreach
      • IV-5. Boucle foreach et variables POST
      • IV-6. TP4
      • IV-7. Correction du TP4
  • Page 5
    • V. PHP ETAPE 5
      • V-1. Les opérateurs
      • V-2. Les fonctions
        • V-2-a. Les fonctions dans la page
        • V-2-b. Plusieurs paramètres passés à la fonction
        • V-2-c. Les fonctions dans un fichier à part
        • V-2-d. Fonction qui renvoie une valeur de retour
      • V-3. La boucle for
      • V-4. La commande switch
      • V-5. TP5
      • V-6. Correction du TP5
  • Page 6
    • VI. PHP ETAPE 6
      • VI-1. Les bases de données : introduction
        • VI-1-a. A quoi ça sert ?
        • VI-1-b. Comment ça marche ?
      • VI-2. Alimenter sa base via PHP
        • VI-2-a. Présenter le formulaire
        • VI-2-b. Se connecter à notre base via PHP
        • VI-2-c. Pour travailler proprement (Généralités)
        • VI-2-d. Insérer des données dans notre base via PHP
        • VI-2-e. On récapitule le code ?
      • VI-3. TP6
      • VI-4. Correction du TP6
  • Page 7
    • VII. PHP ETAPE 7
      • VII-1. Les bases de données : suite et fin
        • VII-1-a. Pour travailler proprement (Généralités)
        • VII-1-b. Le code pour récupérer toutes les filles
      • VII-2. Un exemple (plus complexe) de relation dynamique entre PHP et SQL
      • VII-3. TP7
      • VII-4. Correction du TP7
  • Page 8
    • VIII. PHP ETAPE 8 : CONCLUSION
      • VIII-1. En guise de conclusion
        • VIII-1-a. PHP, un langage qui ne peut pas tout
        • VIII-1-b. Conceptualisation d'un projet PHP
      • VIII-2. Quelques réflexes pour progresser en PHP
      • VIII-3. TP8
      • VIII-4. Correction du TP8
      • VIII-5. Mes liens favoris pour l'apprentissage du développement web
        • VIII-5-a. Les tutos et cours Developpez.com
        • VIII-5-b. D'autres tutos et cours qui m'ont beaucoup appris
      • VIII-6. Remerciements
Ce tutoriel, comme son nom l'indique, est intégralement conçu pour des grands débutants pressés.

Appelons grand débutant celui qui n'a jamais programmé ni entamé aucune initiation sur les notions générales de la programmation.
Nous ambitionnons donc de satisfaire ce public, s'il est prêt à travailler régulièrement et à y consacrer une semaine intensive, par exemple...

Mais bien entendu, qui peut le plus peut le moins !

Aussi ce tutoriel ne devrait pas rebuter l'informaticien qui connaît déjà un ou plusieurs autres langages, mais souhaiterait se mettre à PHP,
langage devenu incontournable pour la programmation web.
Ce lecteur-là pourra parcourir à son rythme le tuto qui suit et y trouver réponses à ses questions...

Dans tous les cas, bonne lecture !


Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant/?page=intro

PHP : Le tutoriel pour grands débutants pressés

I. PHP ETAPE 1 : INTRODUCTION

I-0. Les pré-requis

I-0-a. Ce qu'il vous faut connaître

Pour profiter de ce tutoriel PHP pour grand débutant pressé, il faut répondre aux critères suivants :

a) Connaître le langage d'affichage HTML dans les grands principes...
Si ça n'est pas le cas, commencez par là. (Voir liste tutos recommandés sur la question dans le chapitre conclusion de ce cours)

b) Posséder un ordinateur et une connexion internet

I-0-b. Ce qu'il vous faut installer

I-0-b-a. Si vous êtes sous Windows

a) Wamp server 2 : un environnement (gratuit bien sûr) qui vous permettra de programmer PHP en local...

télécharger WampServer

b) Notepad++

Un éditeur de texte (gratuit aussi) qui colore automatiquement le code que l'on entre en fonction de la logique de votre langage informatique, ce qui vous permettra une relecture facile... Cette aide indispensable s'appelle la coloration syntaxique.

Télécharger notepad++, un éditeur de texte avec coloration syntaxique

I-0-b-b. Si vous êtes sur Mac

Equivalent Wamp server

télécharger Mamp

Equivalent Notepad++

télécharger Smultron

I-0-b-c. Si vous êtes sous Linux

Equivalent Wamp server

télécharger Lamp

Equivalent Notepad++

télécharger Bluefish

I-0-c. Et c'est tout pour ce tutoriel !

Pour le reste, on va essayer d'avancer, même si vous n'avez jamais programmé.
Ce tuto ne prétend pas être un topo savant et encore moins exhaustif : c'est une initiation, une première couche de peinture, indispensable pour faire vos premiers pas dans PHP, voire dans la programmation sans trop de souffrance, et pour que les couches suivantes tiennent.

Il se découpe en 8 étapes, avec des travaux pratiques. Il est bien évident que si vous êtes un grand débutant et que vous ne faites pas les travaux pratiques vous-mêmes, tout ce que vous lirez vous sortira par la tête aussi vite que c'est entré.

Il est donc recommandé, pour les grands débutants bien évidemment, de travailler un jour par étape, et de faire les travaux pratiques, pas simplement de se précipiter sur les réponses.

Amusez-vous bien durant votre semaine d'apprentissage !

I-1. Que fait PHP ?

Vous avez de bonnes notions de HTML et CSS, vous savez donc que ces deux langages ne sont pas des langages de programmation, mais des langages de simple affichage statique.

Facile d'afficher pour vous une page web qui dit : "bonjour, on est lundi." Le seul souci, c'est que l'on sera toujours lundi sur votre page...

Pas très dynamique tout ça. C'est là qu'intervient PHP qui est un langage de programmation web.

Il produit du code HTML.

En quoi est-il utile ?

Parce que le code HTML que produit PHP change en fonction des circonstances que vous avez programmées. On dira qu'il introduit du dynamisme dans la page web.

I-2. Quand PHP intervient-il ?

Le code PHP que vous avez inséré dans vos pages agit à chaque chargement (et donc rafraîchissement) de page web. Ceci pour relativiser son "dynamisme".
L'interactivité avec l'utilisateur se limite à certaines actions de l'utilisateur.

Un rafraîchissement de page, c'est par exemple le clic de l'utilisateur sur le bouton 'submit' inclus dans une balise form, ou bien une première arrivée sur une page web.

I-3. Un zeste d'algorithmique

Il faut enfin aborder dans cette introduction quatre notions clés de la programmation.

I-3-a. Tout d'abord le concept de variable

Un exemple suffira. On a tous eu entre les mains une publicité faussement personnalisée du type :

"Bonjour Madame Michu
Vous avez gagné notre canapé extra-cuir lors du tirage du 20/12/2008."

Il est clair que l'entreprise qui envoie cette pub possède une base de données avec des tas de noms. Et que c'est un programme automatique qui stocke les valeurs de Madame, de Michu, et la date dans des variables. Le reste est du texte figé. On va dire que civilité, nom et date sont des variables...

I-3-b. La programmation

Autre chose, programmer, c'est donner une suite d'instructions à la machine. Exactement comme l'on construirait une recette de cuisine. Sauf que l'on écrit la recette à l'usage d'une machine qui ne comprend que le binaire et qu'une instruction à la fois. On avance, on avance, instruction par instruction, de façon linéaire.

Dans la structure du programme, deux types de construction peuvent s'éloigner de cette linéarité :

I-3-c. L'instruction en boucle

On a une action affreusement répétitive. Du genre, découpe les trois plaques de chocolat en carrés individuels. En programmation, on ne va pas dire 150 fois, détache le carré de chocolat, détache le carré de chocolat, détache le carré de chocolat...

Alors on va faire ce que l'on nomme une boucle (il y en a de plusieurs sortes), du genre :

Boucle tant que
tant qu'il y a une plaquette de chocolat{
    détache le carré de chocolat
}

Une boucle parce que le programme lit la condition, entre dans la boucle après la première accolade (si la condition est vraie) et "boucle" tant que la condition est vraie...

I-3-d. L'instruction en condition

On a une condition. Restons sur notre recette de mousse au chocolat.

Si j'ai 6 gourmands, je casse 8 oeufs, si j'ai 6 personnes au régime, je casse 4 oeufs, mais par défaut, une bonne mousse, c'est 6 oeufs pour 6...

Conditions qui s'excluent l'une l'autre
Sors le plat.
si gourmand{
    casse 8 oeufs
}
sinon si régime{
    casse 4 oeufs
}
sinon{
    casse 6 oeufs
}
Bien mélanger...

Cette fois, vous comprendrez que le cheminement logique linéaire se sépare en trois voies parallèles, un peu comme des rails, puis se regroupe à nouveau à la fin.
Le programme ne doit passer que par une et une seule des trois voies.

I-3-e. Pour conclure sur la programmation

Programmer, c'est finalement ramener toute problématique à l'une des trois possibilités suivantes :
- une instruction qui s'exécute automatiquement,
- une instruction qui s'exécute autant de fois que nécessaire dans une boucle,
- une instruction qui s'exécute dans une condition.

Ajouté à tout cela un certain nombre de variables que l'on manipule joyeusement...
Et vous avez les grandes lignes de la programmation.


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant/

II. PHP ETAPE 2

II-1. Comment ça marche ?

L'ossature de toute page HTML
<html>
    <head>
        <title>Ma page de test</title>
    </head>
    <body>
        <h1>Bienvenue sur le site de toto </h1>
        <p>Le blabla de ma page...</p>
    </body>
</html>

Voici une page HTML bien traditionnelle comme on les aime.
Copiez-collez en le contenu dans votre éditeur de texte notepad++.
Enregistrez et nommez-la test.html

Vous remarquerez que le code de la page se colore de façon syntaxique, ce qui le rend bien plus compréhensible. C'était l'intérêt de vous faire télécharger cet éditeur de texte.
Vous apprécierez beaucoup dès que l'affaire se corsera.

Pour l'instant, enregistrez ce fichier sur votre bureau, fermez notepad++, et double-cliquez maintenant directement sur votre fichier test.html, cela vous ouvrira une page web comme si vous étiez sur internet... Pourtant, vous n'y êtes pas, sur internet. Vous êtes "en local", bref, sur votre ordi, et vous ne faites que voir votre fichier à la façon d'une page web.

Rouvrez maintenant l'éditeur de texte et copiez/collez le bout de code suivant dans lequel on a introduit des balises PHP.

test.php contient des balises PHP
<html>
    <head>
        <title>Ma page d'accueil </title>
    </head>
    <body>
        <h1>Bienvenue sur le site de toto </h1>
        <p> Toto fait de l'anglais :</p>
        <?php
        echo '<p>Hello ! What is the day today ? It is '.date("l").' !</p>';
        ?>
    </body>
</html>

Quand vous l'enregistrez, sur votre bureau, renommez-le test.php, (changez l'extension de .html en .php).
Ainsi, le serveur php est avisé que cette page contient du code PHP (que le navigateur ne sait pas afficher en ce qui le concerne).

Fermez votre éditeur. Double-cliquez directement sur votre page test.php : Quelle déception !
Cela ne vous ouvre pas une page web mais... le code que vous venez de quitter, sous un éditeur de texte qui est probablement bloc-notes sans coloration syntaxique.
(Au passage, profitez-en pour paramétrer votre ordi pour ouvrir notepad++ par défaut pour les fichiers PHP)...

C'est ici que le fait d'avoir installé WampServer sur votre machine va vous être utile. En effet, il vous faut un interpréteur PHP installé en local.

Ouvrez votre serveur wamp. Une fois démarré, cliquez sur son icône dans la barre d'état à droite, une sorte de demi-sphère jaune, un peu comme un demi-pamplemousse.
Choisissez www directory.

Dans la fenêtre qui s'ouvre, crééz un dossier PHP et glissez-y votre fichier test.php.

Vous passerez toujours par cette icône "pamplemousse" pour accéder à vos fichiers.

Cliquez maintenant sur cette icône, puis sur localhost. Dans la fenêtre qui s'ouvre, rubrique vos projets, ouvrez le dossier PHP...
Cliquez sur votre fichier test.php et admirez le travail... Le jour affiché en anglais, date qui sera toujours à jour...

Cliquez affichage/source sur votre navigateur : Vous pouvez constater qu'il n'y a plus trace de PHP, il ne reste que du code HTML...

Voici rapidement ce qui se passe à chaque rafraichissement :

le serveur PHP va droit aux fichiers qui ont une extension PHP, puis il va droit aux balises PHP de ce fichier, et interprète ce qu'il y a dedans :
ici, il affiche (echo) tout ce qui est entre parenthèses, puis concatène ou colle si vous voulez (le point) le résultat de la fonction date qui renvoie le jour en anglais...

Il a donc interprété du PHP pour le traduire en HTML.
Ensuite, il repasse le fichier au navigateur, qui lui ne sait afficher QUE du HTML (ou du CSS)... et bien ça tombe bien, il ne voit que du HTML dorénavant...

Mais si vous revenez demain sur cette page, la fonction date affichera un autre jour dans le HTML. Voici pour l'aspect technique.

Pour la prise en main de Wamp, ne retenez que ceci pour l'instant :

Prise en main de Wamp
  1. Quand vous voulez retrouver votre code : icône puis wwwdirectory et PHP/votre fichier...
  2. Quand vous voulez voir le produit de votre travail, bref, tester votre code : icône puis localhost, répertoire PHP et votre fichier...

Ca deviendra vite un réflexe...

Maintenant que tout tourne, notre première étape portera sur les variables et sur la structure echo qui signifie donc affiche et que l'on utilise en permanence...
Voici quelques exemples... que vous testerez en les rajoutant sur votre fichier au sein des balises PHP bien entendu.

II-2. Les variables

$ devant une chaîne de caractères signifie que nous parlons d'une variable, on la nomme comme on veut ensuite, mais jamais d'accent ni d'espace dans un nom de variable.
Une variable est typée : elle peut prendre 4 types (dans l'ordre ci-dessous) :

Les types des variables
  • string (chaîne de caractères)
  • integer (nombre entier)
  • bool (booléen qui signifie logique binaire genre vrai ou faux)
  • float (nombre réel)
syntaxe des variables
<?php
$nom='Mickaël';
$age=17;
$gars=true;
$taille=1.75;
?>
Étudiez bien chaque détail
  • Toutes les instructions (toutes) se terminent par un point virgule.
  • Seule la variable string exige les guillemets (simples ou doubles, préférez les apostrophes pour l'instant, nous verrons pourquoi un peu plus loin)...
  • La booléenne prend la valeur true, mais sans guillemets, elle a pour contraire... false.
  • Le nombre réel prend un point à l'anglo-saxonne et non une virgule...

N'hésitez pas à personnaliser ces exemples et à les triturer en en changeant les données pour les assimiler.

II-3. echo

Bien maintenant que nous avons défini le type des variables en même temps que nous leur avons affecté une valeur, affichons les grâce à echo, dans un environnement HTML :

syntaxe de la commande echo
<?php
echo'<p>Bonjour à tous.<br/>
Mon vrai nom n\'est pas Toto.<br/>
Mon vrai nom est '.$nom.'<br/>
J\'ai '.$age.' ans et je mesure '.$taille.'m.<br/>
Et comme mon nom l\'indique, je suis ';
if ($gars==true){
    echo 'un garçon.</p>';
}
else{
    echo 'une fille. </p>';
}
?>

Commentaires sur cette instruction echo :

Ici je n'ai fait que deux instructions 'affiche' : Une automatique, et une conditionnelle.

Vous remarquerez que je suis allée à la ligne au sein de l'affichage grâce à la balise br/. Vous remarquerez que toutes les apostrophes qui sont seulement du texte ont été neutralisées en plaçant \ devant chacune d'elle pour qu'elles ne soient pas considérées comme la fin de la chaîne de caractères.

Vous remarquerez que l'affichage du contenu de la variable se fait automatiquement, en mettant la variable nue dans le code.

Vous remarquerez que le point sert à concaténer (ou coller du texte bout à bout).

Il vous faut gérer les espaces à l'affichage au sein des guillemets. Ici la coloration syntaxique devient indispensable.

Enfin vous remarquerez que ma condition (si c'est un garçon ou une fille), bien qu'écrite à la ligne dans mon code et ouvrant une seconde instruction echo, s'affiche tout de même dans la continuité de mon précédent echo...

Autrement dit, c'est toujours au niveau du code HTML que se gèrent les retours à la ligne de l'affichage (balises br/, balises p ou autres).
Les retours à la ligne que vous faites spontanément dans votre code PHP après chaque instruction ne sont que des retours à la ligne à destination du programmeur pour une meilleure lisibilité de son code.

Dernière remarque : Prenez bien l'habitude de savoir à quel niveau vous êtes : au niveau du méta-langage PHP ou bien au niveau du code HTML qui aboutit à un affichage.

II-4. Guillemets ou Apostrophes ?

II-4-a. Ce que je ne conseille pas

Si vous êtes un peu flemmard, vous trouverez bien pratique de savoir que les guillemets "" (contrairement aux guillements simples ou apostrophes) permettent ceci en PHP:

Le guillemet double permet ce code raccourci
<?php
$age=18;
//Ceci affichera directement
//J'ai 18 ans.
echo"J'ai $age ans.";
?>

Alors vous allez peut-être trouver cela un peu tordu d'utiliser les guillemets simples pour PHP du coup, puisque l'exemple ci-dessus règle en un seul coup de cuiller à pot le problème de l'interprétation directe de la variable et le problème de l'apostrophe du texte "J'ai"...

Pourtant, je pense que vous devrez procéder de façon plus rigoureuse si vous êtes amené à continuer avec PHP, aussi je vous prie d'adopter la convention suivante, pour ce tuto du moins :

II-4-b. Ce que je conseille

A l'avenir, vous allez fréquemment produire du HTML avec votre code PHP, puisque c'est sa fonction essentielle. Ainsi en adoptant la convention suivante, vous saurez toujours si vous êtes au niveau de PHP ou bien de HTML.

Gestion des apostrophes et des guillemets recommandée
<?php
echo'<div class="contenu">blablabla</div>';
?>

Quand vous utilisez un ' : on est au niveau de PHP.
Quand vous utilisez un " : on est au niveau du HTML.

Pour la clarté de vos idées, je conseille donc ceci :

Un code logique
<?php
$age=18;
echo'J\'ai '.$age.' ans.';
?>

Bien sûr, il vous restera du coup à avoir le réflexe :

Attention, dans le contenu de mon HTML, entre deux balises, si j'ai une apostrophe dans mon texte (ce qui revient somme toute assez rarement) :
il faut que je pense au caractère d'échappement.

Mais ça ne devrait pas poser de problème majeur, toutes les phrases ne contenant pas autant d'apostrophes que dans l'exemple ci-dessous...

Une apostrophe ne fait pas le printemps
<?php
echo'<div class="contenu">L\'hirondelle vole à tire d\'ailes vers l\'Europe du Nord : L\'hiver s\'éteint.</div>';
?>

II-5. TP2

Pour vos premiers travaux pratiques, vous programmerez ceci :
A partir d'une page vierge de notepad++ et sans copier-coller, vous ferez la page emploi.php qui devra afficher ceci :

Image non disponible

Bien entendu les données suivantes : 2000, L'informatique, et 11.5 ainsi que mon option (dans laquelle je travaille ou souhaite travailler) sont des variables car on peut les modifier à souhait.
Vous prendrez soin de les afficher en gras, ces variables...

Bon courage... Et interdiction de courir à la solution au premier échec.

Lisez bien les messages d'erreur, ils indiquent la ligne à modifier (ou parfois la ligne du dessus).
Il est inévitable de faire beaucoup d'erreurs de ce type au début...
Donc patience, relisez le cours, mais ne venez à la solution que quand votre code produit quelque chose de satisfaisant et sans copier/coller.


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant

III. PHP ETAPE 3

Pour l'instant, on a manipulé des variables que l'on entrait "à la main" soi-même.
Il nous faut maintenant voir comment manipuler véritablement des variables, entrées par l'utilisateur.

III-1. Les formulaires

Vous le savez, un formulaire en HTML, c'est la suite de balises suivante :

un formulaire typique sur la page saisie.php

<form name="inscription" method="post" action="saisie.php">
        Entrez votre pseudo : <input type="text" name="pseudo"/> <br/>
        Entrez votre ville : <input type="text" name="ville"/><br/>
        <input type="submit" name="valider" value="OK"/>
    </form>

Ici, ce formulaire présente une zone de saisie pour entrer son pseudo, va à la ligne, une zone de saisie pour entrer sa ville, va à la ligne, et enfin un bouton pour valider sur lequel sera écrit 'OK'...

Le but, vous l'aurez compris, c'est de récupérer, via PHP, les infos entrées par n'importe quel usager.

Voici comment les choses vont se découper :

Les attributs dans la balise form précisent le nom du formulaire, puis précisent que les variables contenues dans ce formulaire seront envoyées par la méthode POST (au moment où l'utilisateur cliquera sur le bouton "submit") à la page saisie.php (disons que c'est notre page de départ, oui, celle où il y a le formulaire)...

Quelles variables y aura-t-il, que comporteront-elles et surtout, quels noms porteront ces variables ?

Il y aura la variable $_POST['pseudo'], qui constitue le texte entré dans la zone pseudo par l'utilisateur avant d'avoir cliqué sur submit...
Et on continue ainsi, selon la formule immuable $_POST['name'] pour chaque input, car les variables sont automatiquent nommées ainsi.
Pareil pour $_POST['ville']...
Enfin $_POST['valider'] sera la variable qui dira, si elle existe bien entendu, qu'il y a eu clic sur la validation, et si elle n'existe pas, qu'il n'y a pas eu clic...
Information très importante, nous y reviendrons souvent...
Les choses sont claires ?

Comment allons-nous donc récupérer maintenant cette affaire, sur le fichier qui se nomme donc saisie.php et comporte ceci ?

saisie.php
<html>
    <head><title>Ma page d'accueil</title></head>
    <body>
        <h1>Bienvenue sur le site de toto </h1>
        <h2>Commencez-donc par vous inscrire :</h2>
        <form name="inscription" method="post" action="saisie.php">
            Entrez votre pseudo : <input type="text" name="pseudo"/> <br/>
            Entrez votre ville : <input type="text" name="ville"/><br/>
            <input type="submit" name="valider" value="OK"/>
        </form>
    </body>
</html>

En PHP, il faut toujours commencer par classer vos idées ainsi :
Quelle est la condition pour que mon code s'exécute ?
Autrement dit, quelle action de l'utilisateur va déclencher mon code...

Ah, tout ça sera donc dans une condition, voyons donc immédiatement la syntaxe du si :

III-2. La syntaxe de la condition if

Structure d'une condition if basique
if(user a cliqué sur valider){
    récupère la variable pseudo;
    récupère la variable ville;
    écris 'Salut (son pseudo) de (sa ville). Bienvenue sur mon site !';
}

Voilà donc la structure d'une condition...

On entoure la condition entre des parenthèses, on encadre toutes les instructions dans des accolades.
Et encore une fois bien sûr, chaque instruction se termine toujours par un point-virgule.

Oubliez un détail de ce genre, et tout plante... Alors la syntaxe d'un truc aussi fréquent, il vaut mieux la répéter jusqu'à la savoir par coeur...

Tout en restant près de notre formulaire, imaginons que nous compliquons un peu notre condition :

Dans le premier si (il a cliqué), nous aimerions dire qu'en plus, si sa ville est Paris, nous lui proposons de le rencontrer...
Ce sera un 'si' imbriqué.

Même principe, mais cette fois, pensez à indenter votre code dès son élaboration, pour ne pas vous prendre les pieds dans le tapis.
Indenter, c'est décaler d'un cran le code pour raisonner par niveau d'imbrication...

Une condition imbriquée
if(user a cliqué sur valider){
    récupère la variable pseudo;
    récupère la variable ville;
    écris 'Salut (son pseudo) de (sa ville). Bienvenue sur mon site !';
    if(sa ville)=='Paris'{
        écris 'On est plusieurs de Paris sur le site. Si tu veux qu'on se voie, contacte-nous !';
    }
}

Remarquez une chose essentielle :

if($ville=='Paris') comporte deux signes =
C'est indispensable pour une comparaison.
La variable ne reçoit pas 'Paris' (signe = d'affectation), elle est comparée (==) à Paris.

Vous ferez forcément cette erreur au début, d'autant plus difficile à repérer qu'elle ne provoquera pas de message d'erreur :
Le programme comprendra => Si la variable reçoit Paris, et ce sera toujours vrai.
Autrement dit, votre programme ne fera pas ce que vous voulez, mais le langage ne pourra pas vous préciser votre bug...

Donc bien penser à ça en premier quand une condition ne tourne pas comme on veut.
(Ai-je bien mis un double égal dans la comparaison pour la condition ?)

Une dernière chose pour les si, s'il y a un si, il faut parfois un ou plusieurs sinonsi, puis un sinon pour conclure en tout dernier...

Restons toujours dans notre thématique formulaire :
Mettons que j'ai un formulaire qui m'a renvoyé l'âge entré par l'utilisateur :

<?php
$age=$_POST['age'];
?>

La syntaxe donnera ceci :

Conditions successives qui s'excluent l'une l'autre
<?php
if($age<5){
    $verdict='Ouh le bébé !';
}
elseif($age<13){
    $verdict='Vous êtes un enfant !';
}
elseif($age<18){
    $verdict='Vous êtes un(e) ado !';
}
else{
    $verdict='Ah ! enfin un(e) adulte !';
}
echo $verdict;
?>

Ici, pas d'imbrication, puisque ce sont des conditions successives et non imbriquées l'une dans l'autre.
La dernière (le else), c'est bien sûr 'sinon', le truc par défaut...

Ici, que signifie-t-il concrètement ?
Que user a 18 ans ou plus, n'importe quel entier > ou = à 18...

Dans tous les cas, il est affecté une valeur à $verdict au moment où il parvient à l'instruction 'affiche moi le verdict'...
L'instruction n'est pas dans une condition, elle s'exécute à tous les coups.
Mais la variable $verdict elle, n'est pas initialisée de la même façon selon que c'est l'une ou l'autre des 4 conditions de ce système de conditions qui l'a initialisée.

En d'autres termes, dès l'instant où le programme tombe sur une condition qui se réalise, il cesse de tester la suite et passe à l'exécution de l' instruction...

III-3. Les formulaires, suite

Revenons maintenant sur le traitement de notre formulaire de départ, avec une toute petite condition bien classique :

if(user a cliqué sur valider){
    récupère la variable pseudo;
    récupère la variable ville;
    écris 'Salut (son pseudo) de (sa ville). Bienvenue sur mon site !';
}

Comment allons-nous dire en PHP la condition ou les instructions ? De la façon suivante, que je commenterai ensuite :

la syntaxe pour récupérer une variable POST
<?php
if(isset($_POST['valider'])){
    $pseudo=$_POST['pseudo'];
    $ville=$_POST['ville'];
    echo 'Salut '. $pseudo.'de '. $ville.'<br/>Bienvenue sur mon site !';
}
?>

Traduction :

s'il existe une variable POST qui a pour nom 'valider' et que donc user a validé,
$pseudo reçoit la variable POST qui a pour nom 'pseudo'
$ville reçoit la variable POST qui a pour nom 'ville'
affiche... bon la fonction echo n'a plus de secret pour vous...

Remarque :

On peut très bien se passer de ranger la variable de type $_POST['name'] dans une variable au nom simplifié de type $name, et la manipuler directement !
Mais si on doit la manipuler sans arrêt, c'est plus agréable (et plus stable) de la renommer...

Autre remarque :

Puisque c'est l'attribut name dans la balise d'un objet type formulaire qui va déterminer le nom de la variable POST, pensez-bien à ne pas y mettre d'accent, ni d'espace...
Le name ne sert pas à l'affichage, le user ne le verra pas ! C'est pour le programmeur, pour vous...

III-4. Les formulaires, fin

On remet tout ça bout à bout dans le code maintenant !

Fichier saisie.php : un formulaire et son traitement
<html>
    <head><title>Ma page d'accueil</title></head>
    <body>
        <h1>Bienvenue sur le site de toto </h1>
        <h2>Commencez-donc par vous inscrire :</h2>
        <form name="inscription" method="post" action="saisie.php">
            Entrez votre pseudo : <input type="text" name="pseudo"/> <br/>
            Entrez votre ville : <input type="text" name="ville"/><br/>
            <input type="submit" name="valider" value="OK"/>
        </form>
        <?php
        if(isset($_POST['valider'])){
            $pseudo=$_POST['pseudo'];
            $ville=$_POST['ville'];
            echo 'Salut '. $pseudo.'de '. $ville.'<br/>Bienvenue sur mon site !';
        }
        ?>
    </body>
</html>

Vous expérimentez tout ça en local bien entendu...
Changez des détails, triturez, appropriez-vous la méthode et la syntaxe...

D'autant que le TP3 est un véritable travail de programmation cette fois.
Prenez bien votre temps...

III-5. TP3

Sans copier, ni copier/coller le cours, concevez une page qui s'appelle imc.php, et propose un formulaire sous la forme suivante :

Entrez votre prénom :
Entrez votre taille (sous la forme 1.70) :
Entrez votre poids (en kilos) :
OK

Ensuite vous traiterez le formulaire en PHP pour que quand la personne clique, elle voit apparaître :

Bonjour (son prénom)
Votre IMC (indice de masse corporelle) est exactement : (son imc)
Vous avez (une corpulence normale ou bien Vous êtes en surpoids, ou bien Vous êtes obèse etc...)

Pour la petite opération, l'IMC, c'est le poids divisé par la taille divisé par la taille...

En voici la syntaxe :
$poids/($taille*$taille)

(Nous reviendrons plus tard sur les opérateurs)...

Pour les conditions qui déterminent les tranches de l'IMC, consultez wikipedia par exemple, rubrique : interprétation de l'IMC, classification de l'OMS.
N'omettre aucune tranche bien entendu !

http://fr.wikipedia.org/wiki/Indice_de_masse_corporelle


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant/?page=tableaux

IV. PHP ETAPE 4

Vous avez un formulaire qui propose des saisies au client (client au sens informatique du mot, par opposition à serveur). Disons une dizaine.
Vous vous en souvenez, cela créera une dizaine de variables $_POST['nom_de_l_input'].
Il se trouve que PHP range automatiquement ces variables dans un tableau. Nous y reviendrons à la fin de cette étape.

Commençons par voir ce qu'est un tableau :

IV-1. Les tableaux simples

Un tableau, c'est un moyen de stocker plusieurs variables, selon un plan qui vous paraît logique.

C'est comparable à un meuble avec ses tiroirs.
Dans le tiroir 0, (oui, le tableau commence par le tiroir zéro), vous rangez la variable lundi par exemple, dans le tiroir 1, vous rangez la variable mardi etc.

En informatique, on appelle index ou indice le numéro de tiroir (la position de la variable dans le tableau), et valeur la valeur de la variable entreposée.

Voici la syntaxe d'un tableau tout simple

On construit le tableau des jours de la semaine
<?php
$semaine=array('lundi','mardi','mercredi','jeudi','vendredi','samedi','dimanche');
?>

Par cette simple ligne, vous venez de construire un tableau (qui vous le remarquerez, est une variable en lui même puisqu'il commence par $, mais une variable complexe, organisée).

$semaine est le nom du tableau entier.

Vous lui avez affecté des valeurs, (via la commande array, il sait que c'est un tableau).
Et ici, par défaut, l'index commence à 0, donc dimanche aura pour index... 6 et non 7.

Une fois construit ce tableau, comment convoquer une valeur ?
$semaine[2] sera...mercredi et ainsi de suite... selon la règle : $semaine[index]

Vous pouvez le vérifier en tapant la commande suivante :

<?php
echo $semaine[2];
?>

Bien sûr ici, nous avons construit un tableau contenant des variables string (les jours de la semaine) et des indices numériques (0,1,2 etc...).

On peut tout-à-fait ranger des valeurs numériques dans un tableau.
Ainsi ce tableau qui stocke quelques années marquantes de l'histoire de France...

Histoire de France
<?php
$dates=array(1789,1830,1848,1851,1871,1914,1918,1936,1939,1945,1958,1968);
echo $dates[3];
?>

La commande echo renverra... 1851 ici.

IV-2. Les commentaires

Au sein de votre code, il est bon de poser parfois quelques commentaires, quelques lignes d'explication qui ne seront bien entendu pas considérées par la machine comme des lignes de programmation à exécuter mais des lignes qu'elle pourra zapper, et qui ne sont destinées qu'au programmeur, qui remet le nez dans son programme parfois dix ans après et ne sait plus pourquoi ou comment il a conçu son code.

Parfois, c'est une autre personne qui hérite du programme. Dans tous les cas, il faut donc commenter son code.

Deux sortes de syntaxe pour des commentaires en PHP
  • Petit commentaire sur une seule ligne // en début de ligne
  • Commentaire sur plusieurs lignes /* au début et */ en dernier...
Comment rédiger des commentaires dans son code
<?php
/*
TOUS LES TABLEAUX
TOUTES LES DATES
TOUS LES PAYS
*/
//tableau dates marquantes en France
$datesF=array(1789,1830,1848,1851,1871,1914,1918,1936,1939,1945,1958,1968);
//tableau dates marquantes aux Etats-unis
$datesUS=array(1861,1865,1917,1918,1929,1934,1941,1945,1959,1975);
?>

PS : à partir de maintenant dans ce tuto, les commentaires me permettront également d'insérer mes commentaires didactiques directement dans mon code...

C'est-y-pas plus facile ?

IV-3. Les tableaux associatifs

Ici, la seule différence, par rapport à un tableau simple, c'est que l'index n'est plus numérique, mais lui-même une variable string :

Par exemple, votre tableau veut stocker une adresse.
Sachant que chaque adresse comporte en gros et dans cet ordre, un nom, un prénom, un numéro, une rue, un code postal et une ville...

On peut construire le tableau suivant :

Un tableau associatif pour stocker une adresse
<?php
//On signale que notre variable $adresse4 sera un tableau
$adresse4 = array();
//on le remplit
$adresse4 ['nom']='DUPONT';
$adresse4 ['prenom']='Mickaël';
$adresse4 ['num'] = 12;
$adresse4 ['rue']  = 'rue des églantines';
$adresse4 ['cp']  = 93000;
$adresse4 ['ville'] = 'SAINT-DENIS';
?>

'num' est ici un index du tableau adresse. 12 est la valeur stockée à l'index 'num'.

<?php
//Pour convoquer 12
echo $adresse4['num'];
?>

Bien entendu, l'intérêt d'un tableau d'adresses, c'est d'en stocker plus d'une !

Alors nous procèderons... à un tableau de tableaux, un tableau imbriqué en fait...

Nous venons de voir que $adresse4 est un tableau.

Construction d'un tableau de tableaux
<?php
//construction de mon tableau $agenda
$agenda=array($adresse0, $adresse1, $adresse2, $adresse3 ,$adresse4);
?>

Je procède ensuite en deux étapes :

Convoquer une donnée dans un tableau de tableaux - Méthode 1
<?php
//récupérer l'adresse totale de Mickaël
$adresseMick= $agenda[4];
/*
En effet, l'adresse de Mickaël se trouve dans notre agenda à l'index 4 (l'index numérique construit automatiquement)...
*/
//Récupérer enfin le nom de famille de Mickaël
echo $adresseMick['nom'];
?>

Compris ? Allez, même si ça pique un peu la tête, signalons au passage que l'on aurait pu retrouver le nom de Mickaël dans ce tableau de tableaux par la syntaxe suivante, plus condensée :

Convoquer une donnée dans un tableau de tableaux - Méthode 2
<?php
echo $agenda[4]['nom'];
?>

IV-4. La boucle foreach

Bien sûr, l'idéal pour parcourir les valeurs d'un tableau, c'est une boucle.
La boucle foreach (pour chaque élément) présente l'avantage de parcourir la totalité d'un tableau, même si l'on n'a aucune idée du nombre d'éléments qu'il contient.

Reprenons notre tableau des jours de la semaine, comment le parcourir ?

Égrener les jours de la semaine
<?php
//construction du tableau semaine
$semaine=array('lundi','mardi','mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche');
//parcours du tableau
foreach($semaine as $jour){
    echo'- '.$jour.'<br/>';
}
/*
Pour chaque valeur du tableau $semaine, compose la variable $jour et affiche le jour puis va à la ligne...
*/
?>

Copiez, collez maintenant le gros morceau de code qui suit, dont nous avons vu l'essentiel tout à l'heure, la construction à la main d'un agenda d'adresses :

L'agenda : un tableau de tableaux
<html>
    <head><title>Mon agenda</title></head>
    <body>
        <?php
        $adresse0 = array();
        //on le remplit
        $adresse0 ['nom']='ZERO';
        $adresse0 ['prenom']='Toto';
        $adresse0 ['num'] = 10;
        $adresse0 ['rue']  = 'rue des rosiers';
        $adresse0 ['cp']  = 94000;
        $adresse0 ['ville'] = 'IVRY-SUR-SEINE';
        $adresse1 = array();
        //on le remplit
        $adresse1 ['nom']='AIN';
        $adresse1 ['prenom']='Anne';
        $adresse1 ['num'] = 11;
        $adresse1 ['rue']  = 'rue des moineaux';
        $adresse1 ['cp']  = 57000;
        $adresse1 ['ville'] = 'METZ';
        $adresse2 = array();
        //on le remplit
        $adresse2 ['nom']='DEUX';
        $adresse2 ['prenom']='Al';
        $adresse2 ['num'] = 2;
        $adresse2 ['rue']  = 'rue des arbres';
        $adresse2 ['cp']  = 88000;
        $adresse2 ['ville'] = 'EPINAL';
        $adresse3 = array();
        //on le remplit
        $adresse3 ['nom']='TROIS';
        $adresse3 ['prenom']='Léa';
        $adresse3 ['num'] = 3;
        $adresse3 ['rue']  = 'rue des éléphants';
        $adresse3 ['cp']  = 69000;
        $adresse3 ['ville'] = 'LYON';
        $adresse4 = array();
        //on le remplit
        $adresse4 ['nom']='DUPONT';
        $adresse4 ['prenom']='Mick';
        $adresse4 ['num'] = 4;
        $adresse4 ['rue']  = 'rue des églantines';
        $adresse4 ['cp']  = 93000;
        $adresse4 ['ville'] = 'SAINT-DENIS';
        //on déclare et remplit l'agenda avec toutes les adresses précédentes :
        $agenda=array($adresse0,$adresse1,$adresse2,$adresse3,$adresse4);
        ?>
    </body>
</html>

Ceux qui ont suivi le devinent, qui dit tableau de tableaux dit... boucles imbriquées pour la lecture du tableau :

Une boucle pour lire chaque champ dans l'adresse imbriquée dans une boucle pour lire chaque adresse
<?php
//pour chaque élément de $agenda crée la variable $adresse
foreach($agenda as $adresse){
    //pour chaque élément de $adresse crée la variable $element
    foreach($adresse as $element){
    //écris le $element sur la même ligne avec un tiret et des espaces
        echo '- '.$element.'  ';
    }
    //A chaque nouveau $adresse, saute une ligne
    echo'<br/>';
}
?>

Et voici notre agenda qui s'affiche de façon assez lisible...

Bien, nous arrivons maintenant au coeur de notre problème, et nous revenons à notre formulaire :

Comment récupérer des variables $_POST, résultats d'un formulaire de saisie, quand elles sont nombreuses ?

IV-5. Boucle foreach et variables POST

PHP construit automatiquement un tableau associatif dès que l'on soumet un formulaire.
Ce tableau se nomme $_POST, chaque élément a pour index le 'name' d'un élément du formulaire, et chaque valeur, la valeur entrée par l'utilisateur dans chaque champ avant de cliquer sur le bouton 'submit'.

On peut donc égrener tranquillement notre tableau de variables POST de cette façon :

Une boucle foreach pour récupérer les variables POST
<?php
/*pour chaque élement du tableau $_POST,
récupère et affecte la valeur de l'index,
puis récupère et affecte la valeur associée à cet index*/
foreach($_POST as $index=>$valeur){
    echo '- '.$valeur.'<br/>';
}
?>

Pour vous en convaincre, reprenons le formulaire sur l'IMC, (correction du TP3) et voyons ce que cette boucle produit :

Récupérons tout le tableau $_POST de notre petit code sur l'IMC
<html>
    <head><title>Votre IMC</title></head>
    <body>
        <h1>Déterminez votre IMC et sachez quelle est votre corpulence d'un point de vue médical</h1>
        <h2>Entrez les données suivantes </h2>
        <form name="formulaire" method="post" action="tableau.php">
            Entrez votre prénom : <input type="text" name="prenom"/> <br/>
            Entrez votre taille (sous la forme 1.70) : <input type="text" name="taille"/> <br/>
            Entrez votre poids (en kilos) : <input type="text" name="poids"/> <br/>
            <input type="submit" name="valider" value="OK"/>
        </form>
        <?php
        echo'Vos données entrées : '.'<br/>';
        if(isset($_POST['valider'])){
            foreach($_POST as $index=>$valeur){
                echo '- '.$index.' : '.$valeur.'<br/>';
            }
        }
        ?>
    </body>
</html>

Ce petit bout de code ne fait qu'afficher la valeur des variables que le client vient d'entrer plus la valeur (permanente) de valider qui est 'ok'...

IV-6. TP4

Sur le fichier tp4.php, offrez via un formulaire la saisie du nom, du prénom, de l'âge, de la ville et de l'activité.

Quand l'utilisateur clique sur le bouton valider, il voit apparaître en dessous du même formulaire le message suivant :
'Vous venez de saisir :' puis à la ligne à chaque fois un tiret son nom, un tiret son prénom etc...

Comme d'habitude sans copier... De mémoire seulement...
Par ailleurs, on ne veut pas voir 'ok' dans la liste de ce que l'on vient de saisir...


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant/?page=syntaxe

V. PHP ETAPE 5

V-1. Les opérateurs

Rapidement les opérateurs essentiels :

Presque tous les opérateurs
<?php
/*
LES OPERATEURS ARITHMETIQUES
*/
//la variable reçoit 5 (simple égal : opérateur d'affectation)
$valeur=5;

//plus et moins
$nombre=(4+6)-2;
//$nombre vaudra 8;

//multiplier ou diviser
$nombre=(4*6)/2;
//$nombre vaudra 12;

//Priorités si vous ne mettez pas de parenthèses :

$valeur=4+6*2;
//multiplier ou diviser est prioritaire.
//Si vous risquez d'oublier, sécurisez avec des parenthèses...
$valeur=4+(6*2);
//Sans parenthèses, cela revient à 16;

//Si vous souhaitez une autre priorité, déplacez les parenthèses :
$valeur=(4+6)*2;
//Cela revient à 20;

//modulo ou ce qui reste après division entière
$nombre=6%2;
//$nombre renverra 0 (car 6 est divisible par 2 donc reste zéro).
$nombre=27%4,
//$nombre renverra 3 (car 6x4=24 reste 3).

//comparer
//RAPPEL IMPORTANT : penser au double égal
if ($nombre==4)

//son contraire
if($nombre!=4)
//Différent de

//inférieur, supérieur
if($nombre<4)
if($nombre>4)

//inférieur ou égal, supérieur ou égal
if($nombre<=4)
if($nombre>=4)

/*
LES COMPARAISONS LOGIQUES
*/
//si l'âge est compris entre 15 et 25 ans :
if (($age>=15)&& ($age<=25))
//Notez && pour le 'et' logique (AND marche aussi)
//Notez les parenthèses imbriquées pour chaque sous-condition

//si $truc est 'papa' ou 'maman'
//RAPPEL IMPORTANT : penser au double égal de comparaison

if(($truc=='papa')||($truc=='maman')){
    echo'Chouette, mes parents !';
}
//Notez || (2 fois la touche AltGr+6)  pour le 'ou' logique
//on l'appelle aussi le ou inclusif (OR marche aussi)
//Notez les parenthèses imbriquées pour chaque sous-condition
?>

V-2. Les fonctions

V-2-a. Les fonctions dans la page

Imaginons le problème suivant. Votre programme affiche les notes/20 d'un élève.

Vous souhaitez mettre en rouge toutes les notes (strictement) inférieures à 10.
Vous souhaitez mettre en vert toutes les notes supérieures (ou égales) à 15.

Cela veut dire qu'à chaque apparition d'une note, il faut la tester et lui appliquer la couleur adéquate.
Comme c'est une tâche répétitive, on va la mettre en forme dans une fonction.

Voici comment opérer :

On place évidemment la fonction que l'on va construire au début du code, avant de l'utiliser.
Par exemple, dans le début du body (au sein de balises PHP bien sûr).
Mais on ne la crée qu'une fois, tandis qu'on la convoque autant qu'on veut un peu plus loin sur le même fichier PHP.
C'est pourquoi on va lui donner un nom simple à retenir (comme les variables, les noms de fonctions sont libres, il faut juste bannir les espaces et les accents.)
Bien entendu, il faut aussi retenir une chose : c'est que si le nom de votre fonction est libre, une fois que vous l'avez choisi, vous ne pourrez le convoquer qu'en ne changeant rien (ni une lettre, ni une majuscule ou minuscule)...

Mais bon, vous devez commencer à comprendre que comme tout langage de programmation, PHP ne supporte aucune coquille.

Le cadre de notre fonction se présentera ainsi :

function colore($nombre){
    toutes les instructions se situeront à l'intérieur de ces accolades;
    et se termineront par un ;
}

Dans la création de la fonction, il est obligatoire de commencer par écrire function, ensuite vient le nom (libre).

Enfin ici, notre fonction comporte un paramètre (une variable imaginaire, $nombre, dont on ne connaît pas encore la valeur)...
On fait comme si $nombre existait déjà, et on fait le petit bout de code qui effectue ce qu'on veut obtenir :

Ma fonction qui colore les notes
<?php
function colore($nombre){
    if($nombre<10){
        echo'<font color="red">'.$nombre.'</font>';
    }
    elseif($nombre>=15){
        echo'<font color="green">'.$nombre.'</font>';
    }
    //cas par défaut(noir)
    else{
        echo $nombre;
    }
}
?>

Vous voulez tester jusqu'au bout cette nouvelle fonction ?

Convoquer notre fonction dans une boucle
<?php
//Construisons notre tableau de notes :

$notes=array(2,5,7,10,11,13,15,17,18);

/*Scannons-le grâce à une boucle foreach du type

echo 'Vos notes du trimestre :<br/>';
foreach($notes as $note){
    echo '- '.$note.'<br/>';
}*/

//Et maintenant, précisons notre boucle foreach pour y convoquer notre fonction.

echo 'Vos notes du trimestre :<br/>';
foreach($notes as $note){
    echo '- '.colore($note).'<br/>';
}
?>

En définitive, notre fichier notes.php complet donnera ceci :

Le fichier complet
<html>
    <head><title>Notes du trimestre</title></head>
    <body>
        <?php
        //Cette fonction colore en rouge les notes<10
        //et en vert les notes >=15
        function colore($nombre){
            if($nombre<10){
                echo'<font color="red">'.$nombre.'</font>';
            }
            elseif($nombre>=15){
                echo'<font color="green">'.$nombre.'</font>';
            }
            //cas par défaut(affiche sans modifier couleur)
            else{
                echo $nombre;
            }
        }

        //Construisons notre tableau de notes :
        $notes=array(2,5,7,10,11,13,15,17,18);

        //La boucle foreach scanne le tableau
        //en appliquant la fonction colore
        echo 'Vos notes du trimestre :<br/>';
        foreach($notes as $note){
            echo '- ';
            colore($note);
            echo '<br/>';
        }

        ?>
    </body>
</html>

Vous serez peut-être étonné du fait que cela fonctionne, bien que votre fonction disait colore($nombre) et que lorsque vous la convoquez, vous dites colore($note)...

Mais ça ne pose aucun problème. Le nom même de l'attribut au sein de la fonction est celui que vous voulez.
Quand vous la convoquez, il remplace de toute façon la variable 'théorique' de la fonction par la variable courante que vous lui passez en attribut...

V-2-b. Plusieurs paramètres passés à la fonction

Une remarque importante, en ce qui concerne les fonctions : elle peuvent être définies avec plusieurs paramètres.

Par exemple, je peux définir une fonction moyenne annuelle telle qu'elle soit la moyenne des trois trimestres scolaires :

<?php
function MoyenneAnnuelle($trim1, $trim2, $trim3){
    $MA=($trim1+$trim2+$trim3)/3;
    echo 'Votre moyenne annuelle : '.$MA;
}
?>

Dans ce cas, pour la convoquer, on pourra écrire :

<?php
MoyenneAnnuelle(8,10,12);
//ceci aura pour résultat :
// Votre moyenne annuelle : 10
?>

V-2-c. Les fonctions dans un fichier à part

Si vous développez un programme un peu ambitieux, vous aurez un certain nombre de fonctions.
Vous pouvez tout-à-fait les mettre à part dans un fichier voisin que vous appelleriez fonctions.php par exemple, ou comme vous voulez en fait.
Il vous suffira ensuite de le convoquer une seule fois au tout début de chacune de vos pages php, au sein de balises PHP bien entendu :

Par la formule suivante

<?php
include('fonctions.php');
?>

Ceci vous permet ensuite de convoquer n'importe quelle fonction définie dans votre page fonctions.php.

Par exemple, vous avez développé tout un site et vous souhaitez sur chacune des pages signaler le dernier événement mis à jour.

Vous pourriez procéder ainsi :

Dans fonctions.php, vous rédigez cette fonction :

une fonction de simple affichage
<?php
function DerniereMaj(){
    echo'Dernière parution mise à jour :<br/>
    PIF GADGET<br/>
    le 10/01/2009';
}
?>

Dans chacune des pages de votre site maintenant, vous vous contentez de mettre, à l'endroit où vous souhaitez que votre avis de mise à jour (maj) apparaisse, la ligne suivante :

Pour convoquer la fonction
<?php
DerniereMaj();
?>

Bien entendu aussi, vous aurez songé auparavant à mettre votre "include" de la page fonctions.php dans chaque page de votre site...
Ceci vous permettra, à chaque nouvel avis de maj, de changer centralement et une seule fois dans fonctions.php votre texte.
Et cela aura pour conséquence de le changer sur chacune de vos pages.

Par exemple :

<?php
function DerniereMaj(){
    echo'Dernier publication mise à jour :<br/>
    PIF ET PIFOU<br/>
    le 13/01/2009';
}

//etc...
?>

Vous remarquerez que la fonction DerniereMaj n'a pas de paramètres. C'est une simple fonction d'affichage.
Une fonction sans paramètres conserve toutefois les parenthèses vides, dans sa définition comme à chaque convocation.

V-2-d. Fonction qui renvoie une valeur de retour

Une dernière chose sur les fonctions : Si l'on ne veut pas une fonction qui affiche, mais une fonction qui par exemple calcule et renvoie une valeur, on utilisera le mot clé return.
Par exemple, réalisons une fonction qui nous retourne le verdict du nombre que l'on vient d'entrer : pair ou impair ?

Un formulaire de saisie, et une analyse du nombre entré
<html>
    <head><title><Pair ou impair ?</title></head>
    <body>
        <?php
        //fonction qui fait le diagnostic
        function parite($nombre){
            //si le reste de la division est zéro, c'est pair
            if (($nombre%2)==0){
                //on initialise les deux valeurs de verdict
                $verdict='pair';
            }
            else{
                $verdict='impair';
            }
            //on renvoie le verdict, tout à la fin
            return $verdict;
        }
        ?>
        <form method="POST" action="fonction.php">
            Entrez votre nombre<input type="text" name="num"/>
            <input type="submit" name="valider" value="OK"/>
        </form>
        <?php
        //si user a cliqué OK
        if(isset($_POST['valider'])){
            //récupère la valeur entrée
            $nombre=$_POST['num'];
            //place dans $toto la valeur de retour de ma fonction
            $toto=parite($nombre);
            //affiche le verdict entier
            echo 'Ce nombre est '.$toto.'.';
        }
        ?>
    </body>
</html>

Quelques commentaires supplémentaires : return stoppe la fonction.
Il faut donc le placer en tout dernier, juste avant l'accolade finale qui ferme la fonction.

Pour la même raison, return ne peut renvoyer qu'une seule valeur, libre à vous cependant de rendre cette valeur complexe comme un tableau.

Une dernière chose, au moment de la convocation d'une fonction qui retourne une valeur, bien penser à "ranger" la valeur retournée dans une variable "réception", peu importe son nom (c'est bien pourquoi je l'ai appelée ici $toto)...

V-3. La boucle for

Une boucle for, c'est une boucle qui dit "pour chaque valeur de tant à tant, recommence".

Traditionnellement, $i est le nom de la variable qui sert un peu de compteur pour une boucle for, mais rien n'interdit de lui donner un nom plus explicite.
La syntaxe de cette boucle est la suivante :

Ca serait sympa si on pouvait faire les punitions en PHP
<?php
//Copiez-moi 50 fois la punition !!!

//pour i égal zéro, i inférieur à 50, i plus plus
for($i=0;$i<50;$i++){
    echo 'Je ne tricherai plus à un devoir. Limite je meurs de honte là.<br/>';
}
?>

Plusieurs remarques :

Puisque $i commence à 0 et non à 1 (oui, pareil que pour les indices de tableaux), pensez bien à mettre ensuite strictement inférieur à la valeur de la limite supérieure de la boucle.
Ici on peut traduire par : pour $i de 0 à 49, ce qui fait bien... 50 tours de boucle.

Bon en plus, si vous en avez marre de commencer à zéro, vous pouvez commencer à 1 ou à ce que vous voulez pour les boucles (c'est pas comme les tableaux)...

Mais c'est la syntaxe suivante : $i++ qui doit vous sembler un peu surprenante.
Elle signifie $i=$i+1;

Autrement dit, augmente notre compteur de 1 à chaque tour de boucle, cela se nomme aussi l'incrémentation.

V-4. La commande switch

Voici une commande qui est une condition, en tout cas, qui gère très simplement, le principe des conditions multiples un peu lourd à gérer avec des if, elseif, else, if imbriqués, ordre des if, cas particulier qui vient faire planter tout notre échaffaudage laborieux de conditions etc.

Nous verrons plus tard pourquoi cette commande switch est quasiment vitale en PHP.
Pour l'instant, contentons-nous d'en comprendre la structure.

Switch, c'est un interrupteur.

Prenons le cas d'un "verdict" du genre de notre TP sur l'IMC (voir correction du TP3).

Dans ce cas, le switch s'écrirait ainsi :

Un switch pour des conditions qui s'excluent l'une l'autre
<?php
switch($corpulence){
    case 'denutrition':
        $verdict='Vous êtes en dénutrition.';
        //placer ici un lien vers un centre d'aide aux troubles du comportement alimentaire
    break;
    case 'maigre':
        $verdict='Vous êtes maigre.';
        //placer ici quelques conseils d'hygiène de vie
        //MANGEZ bougez
    break;
    case 'normal':
        $verdict='Vous avez une corpulence normale.';
        //placer ici quelques conseils "Continuez commme ça"
    break;
    case 'surpoids':
        $verdict='Vous êtes en surpoids.';
        //placer ici quelques conseils d'hygiène de vie
        //BOUGEZ mangez
    break;
    case 'obese_modere':
        $verdict='Vous êtes en état d\'obésité modérée.';
        //placer ici quelques conseils d'hygiène de vie
        //BOUGEZ BOUGEZ mangez
    break;
    case 'obese_severe':
        $verdict='Vous êtes en état d\'obésité sévère.';
        //placer ici quelques conseils d'hygiène de vie
        //BOUGEZ BOUGEZ BOUGEZ REVOYEZ TOUT VOTRE MODE DE VIE
    break;
    case 'obese-massive':
        $verdict='Vous êtes en état d\'obésité massive.';
        //placer ici un lien vers un centre d'aide aux troubles du comportement alimentaire
}
?>

Et voici l'explication :

Dans le cas (case) où la variable $corpulence est égale à 'denutrition' : affecte telle 'formule' à la variable verdict et procède à telle série d'instructions, dans le case 'maigre' affecte telle autre valeur à $verdict etc. etc.

On sait que ces conditions s'excluent l'une l'autre grâce à l'instruction break; qui signifie : 'quand tu as finis, sors de ce switch'.
D'où la raison pour laquelle le dernier case 'obese_massive' ne nécessite pas de break, il pourrait en prendre un, mais disons que ça ne sert à rien, on est en fin de switch (accolade finale)...

Bien sûr, avant ce switch, il faudrait s'assurer que $corpulence, via des conditions, se voit bien affecter tout cet éventail de valeurs...

Bien sûr, après ce switch, il faudrait placer la suite des instructions, suite commune quelle que soit la valeur de $corpulence et/ou $verdict ...

instruction après le switch, qui a initialisé $verdict
<?php
echo '<h3>'.$verdict.'</h3><br/>';
echo '<p>Mais ne prenez pas ce constat, aussi brut soit-il, trop à coeur. <br/>
Une corpulence, s\'il en est besoin, peut changer grâce aux conseils ci-dessus.</br></br>
Ce n\'est qu\'une norme qui doit vous indiquer si votre hygiène de vie est à faire évoluer ou non.
<br/><br/>
Pas le verdict du tribunal d\'inquisition !.</p>';
?>

Nous y reviendrons dans la conclusion de ce tutoriel, mais le switch a ceci de très agréable que quand vous développerez des sites ou des applications un peu plus costaudes en PHP :

Il rendra votre code beaucoup plus lisible.

Vous pourrez caser des lignes et des lignes d'instructions à l'intérieur d'un case, au nom parlant, lui même à l'intérieur d'un switch($procedure) qui fait que l'on ne sera pas perdu en relisant votre code...

V-5. TP5

Dans fonctions.php, vous créerez une fonction qui, quand on lui passe un nombre, affiche le verdict "est un nombre premier" ou "n'est pas un nombre premier";

Pour rappel, un nombre premier est un nombre qui n'est divisible QUE par lui-même et par 1 (et ce, de façon distincte, donc 1 n'est pas un nombre premier).
A ce propos, 0 n'est pas un nombre premier non plus (car on ne peut pas le diviser par zéro), mais vous le gérerez comme une exception...

Dans premiers.php, vous proposerez un formulaire pour que l'utilisateur entre un nombre, disons entre 1 et 10 000, puis traiterez le nombre entré en appelant la fonction en cas de clic sur validation.

Vous afficherez alors le verdict sous la forme :

"99 n'est pas un nombre premier." ou bien "59 est un nombre premier" etc...

Si vous bloquez vraiment, après avoir cherché, lisez le préambule de la correction avant de vous replonger dans la réflexion... Cela devrait vous aider sans tout vous mâcher...


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php

VI. PHP ETAPE 6

VI-1. Les bases de données : introduction

VI-1-a. A quoi ça sert ?

Pour rappel, le code HTML ne sert qu'à l'affichage.
Le code PHP sert à créer un HTML dynamique jusqu'à un certain point, en tout cas, PHP peut organiser à chaque chargement de votre page web le HTML de façon différente.

Cependant, PHP n'a pas le "pouvoir" de mémoriser des données entre deux chargements de page (en dehors de celles que vous transmettez à chaque fois d'une page web à l'autre, via un formulaire HTML !)...

Alors si vous voulez conserver sur la durée les données entrées par les visiteurs de votre site via votre formulaire, de façon automatique et sans plus vous occuper de rien, il va falloir encore intégrer de bonnes notions des bases de données.

VI-1-b. Comment ça marche ?

Cliquez sur l'icône de WampServer, puis cliquez sur phpMyAdmin.

Image non disponible
Accès à phpMyAdmin

C'est le troisième et dernier lien que nous suivrons sur wampServer, après wwwdirectory (le code) et localhost (le test comme si on était sur le web).

C'est ici, sur phpMyAdmin, que vous allez gérer tout ce qui concerne les bases de données.

Une base de données, qui possède un nom, c'est l'ensemble des tables qui recensent vos données.
Une table, qui a un nom également, c'est une structure qui organise vos données de la façon citée en exemple ci-dessous.

Nous allons commencer par une base qui ne comporte qu'une seule table, pour faire simple.

Cliquez le bouton "Créer une base", nommez-la MaBase, puis cliquez "Créer".
Créez une nouvelle table "Utilisateurs" avec 5 champs...
Ils se découperont ainsi (ce qui nous intéresse surtout, c'est le type des champs)...

Nos 5 champs et leur type respectif
  • ID, qui est un INT (integer : entier), cochez AI (auto-incrémenté pour que phpMyAdmin gère ce champ tout seul)
  • pseudo qui est un VARCHAR (caractères dont le nombre varie), entrez 15 à taille/longueur (nombre maximum de carcactères autorisés)
  • sexe qui est un CHAR (caractères dont le nombre est fixe), entrez 1 à taille/longueur (nous mettrons simplement G ou F)
  • age qui est un INT, entrez 3 à taille (le nombre de chiffres). Pour un âge, c'est sympa de penser aux centenaires...
  • et enfin dateInscription (sélectionnez date)...

Ca y est ? Votre table est créée avec sa structure ?

Maintenant, on va la remplir "à la main", enfin, pour les 5 premières personnes.

A gauche dans votre arborescence, sélectionnez votre table 'inscriptions'.
Vous voyez la structure de votre table qui s'affiche... sous forme de tableau.
Cliquez 'Insérer' dans le menu du haut... Par défaut, phpMyAdmin vous donne un formulaire d'insertion deux entrées par deux entrées...

Laissez toujours le champ ID vide. On a dit qu'il se remplirait automatiquement.
Et remplissez ainsi vos 5 insertions :
Si les dates vous paraissent bizarres, c'est qu'elles sont à l'anglaise (année/mois/jour)...

Nos 5 premiers enregistrements
  • Lili F 18 2009-01-02
  • Toto G 13 2008-01-02
  • Loulou G 25 2008-12-30
  • Zézette F 20 2009-01-02
  • Mimi F 15 2009-01-10

On dira que dans votre table Utilisateurs (qui compte donc 5 champs), il y a 5 enregistrements, 5 personnes enregistrées autrement dit, avec toutes leurs données.
La seule façon certaine de les distinguer, c'est leur ID (identification data).

Pour le reste, deux personnes peuvent avoir le même pseudo, le même sexe, ou la même date d'inscription...

Pour convoquer un enregistrement, ou un morceau d'enregistrement, on passera donc toujours par l'ID.

Image non disponible
Ma première table

Le langage qu'on va maintenant utiliser, pour faire des requêtes ou des insertions ou des suppressions dans notre base, se nomme SQL.
Il est assez intuitif et pour l'étude sommaire que nous allons en faire, il consistera en cinq-six mots d'anglais de base...

Je veux récupérer le jour d'inscription de Zézette ?

La requête sera la suivante :

Sélectionne le champ dateInscription dans la table Utilisateurs qui correspond à l'ID 4 :

SELECT dateInscription FROM Utilisateurs WHERE ID="4"

Je veux récupérer toutes les infos sur les filles ?

Sélectionne tous les enregistrements dans la table Utilisateurs qui correspondent au sexe F :

SELECT * FROM Utilisateurs WHERE sexe="F"

Je ne veux récupérer que les pseudos des filles ?

Sélectionne tous les pseudos dans la table Utilisateurs qui correspondent au sexe F :

SELECT pseudo FROM Utilisateurs WHERE sexe="F"

Voilà pour l'essentiel des requêtes de base en SQL, nous verrons au cours de la suite quelques syntaxes supplémentaires...

VI-2. Alimenter sa base via PHP

De même que PHP vous permet de créer un langage HTML plus dynamique et personnalisé, PHP vous permet maintenant de créer vos requêtes SQL de façon à automatiser l'alimentation de vos bases.

Voici un exemple très concret :

Vous allez créer un formulaire qui permettra de continuer à remplir automatiquement votre base de données MaBase, plus précisément sur votre table Utilisateurs, table que pour l'instant vous avez commencé à remplir à la main, via l'interface PHPMyAdmin.

En voici les étapes :

VI-2-a. Présenter le formulaire

Sur un fichier form.php, entrez ce code :

Notre formulaire de saisie utilisateur
<html>
    <head><title>Formulaire de saisie utilisateur </title></head>
    <body>
        <h1>Inscrivez-vous !</h1>
        <h2>Entrez les données demandées :</h2>
        <form name="inscription" method="post" action="form.php">
            Entrez votre pseudo : <input type="text" name="pseudo"/> <br/>
            Garçon ou fille ? 	<input type="radio" name="sexe" value="G"/>Garçon<input type="radio" name="sexe" value="F"/>Fille<br/>
            Entrez votre age : <input type="text" name="age"/><br/>
            <input type="submit" name="valider" value="OK"/>
        </form>
    </body>
</html>

Vous remarquerez que nous ne demandons que 3 éléments d'identification à l'utilisateur, alors que la base en comporte 5 par enregistrement :
C'est normal, le premier champ de notre base est rempli automatiquement par mysql et le dernier, la date d'inscription, nous allons le remplir aussi automatiquement, via PHP cette fois, en entrant la date du jour dès que l'utilisateur clique 'OK'.

Vous remarquerez que pour des données aussi peu variées que le sexe de la personne, nous avons préféré contrôler la saisie en proposant à l'utilisateur deux boutons radio.
Pour limiter les erreurs dans la base SQL, bien songer à proposer des listes déroulantes ou des cases à cocher quand le champ s'y prête...

Qu'allons-nous faire ensuite quand l'utilisateur va cliquer "ok" ?

VI-2-b. Se connecter à notre base via PHP

A chaque fois que nous voulons que PHP se connecte à notre base, on lui donnera une série d'instructions...
Donc on va commencer par coller les paramètres de connexion dans une fonction pour ne pas trop rabâcher dans notre code...

On crée donc un fichier fonctions.php, dans lequel (entre autres) on insère la fonction suivante :

Ma fonction de connection
<?php
function connectMaBase(){
    $base = mysql_connect ('localhost', 'root', '');
    mysql_select_db ('MaBase', $base) ;
}
?>

Vous n'avez rien à modifier...
Sachez simplement que '' est votre mot de passe, laissé vierge ici comme il l'est par défaut dans votre configuration de départ.

Pour expliquer maintenant, cette fonction crée une variable $base qui prépare la commande de connection à votre base avec le nom du serveur (ici localhost), le nom de l'utilisateur (ici root qui signifie administrateur de votre propre base locale, en d'autres termes, root, c'est le patron) et enfin le mot de passe.

La ligne suivante lance la commande de sélection de votre base (où l'on entre donc le nom de votre base), puis on reprend la variable qui contient toute la commande de connexion...

Bien entendu, pour convoquer la fonction que vous venez de créer sur fonctions.php, il vous faudra sur form.php faire un include de ce fichier fonctions.php, puis lancer la fonction au moment opportun par la ligne suivante :

<?php
connectMaBase();
?>

VI-2-c. Pour travailler proprement (Généralités)

Retenez bien ceci :
Quand on rédige une commande d'insertion SQL via PHP, on préfère procéder ainsi, qui paraît un peu compliqué au début, mais simplifie toute la compréhension ensuite.

Insérer une donnée SQL via PHP, l'ossature
  1. On se connecte à la base (en utilisant notre fonction de connexion toute prête).
  2. On prépare la commande sql en la stockant dans une variable PHP du type $sql (pour langage sql);
  3. On la lance.
  4. On ferme la connexion.

VI-2-d. Insérer des données dans notre base via PHP

Bien entendu, le code suivant ira sur le fichier form.php dans notre condition => si l'utilisateur a cliqué OK

Insérer une donnée sql via PHP, le code
<?php
//On récupère les valeurs entrées par l'utilisateur :
$pseudo=$_POST['pseudo'];
$age=$_POST['age'];
$sexe=$_POST['sexe'];

//On créée une variable date du jour grâce à la fonction date() de PHP
$today = date("d.m.y");

//On se connecte
connectMaBase();

//On prépare la commande sql d'insertion
$sql = 'INSERT INTO Utilisateurs VALUES("","'.$pseudo.'","'.$sexe.'","'.$age.'","'.$today.'")';

/*on lance la commande (mysql_query) et au cas où,
on rédige un petit message d'erreur si la requête ne passe pas (or die)
(Message qui intègrera les causes d'erreur sql)*/
mysql_query ($sql) or die ('Erreur SQL !'.$sql.'<br />'.mysql_error());

// on ferme la connexion
mysql_close();
?>

C'est probablement cette partie qui vous semble la plus complexe :

<?php
//On prépare la commande sql d'insertion
$sql = 'INSERT INTO Utilisateurs VALUES("","'.$pseudo.'","'.$sexe.'","'.$age.'","'.$today.'")';
?>

La première valeur est laissée en blanc car c'est l'ID auto-incrémenté...
Si vous l'oubliez, ce blanc, il vous renverra un message d'erreur sql disant que le nombre de données insérées ne coïncident pas avec le nombre de champs... en anglais.

Ensuite il faut gérer au sein des guillemets simples (apostrophes) tout ce qui est PHP
et au sein des guillemets doubles tout ce qui est sql...

On place les guillemets au sein des apostrophes pour les afficher dans le sql, on place les variables 'nues' dans PHP.

VI-2-e. On récapitule le code ?

fonctions.php
<?php
function connectMaBase(){
    $base = mysql_connect ('localhost', 'root', '');
    mysql_select_db ('MaBase', $base) ;
}
?>
form.php
<?php
include("fonctions.php");
?>
<html>
    <head><title>Formulaire de saisie utilisateur </title></head>
    <body>
        <h1>Inscrivez-vous !</h1>
        <h2>Entrez les données demandées :</h2>
        <form name="inscription" method="post" action="form.php">
            Entrez votre pseudo : <input type="text" name="pseudo"/> <br/>
            Garçon ou fille ? 	<input type="radio" name="sexe" value="G"/>Garçon<input type="radio" name="sexe" value="F"/>Fille<br/>
            Entrez votre age : <input type="text" name="age"/><br/>
            <input type="submit" name="valider" value="OK"/>
        </form>
        <?php
        if (isset ($_POST['valider'])){
            //On récupère les valeurs entrées par l'utilisateur :
            $pseudo=$_POST['pseudo'];
            $age=$_POST['age'];
            $sexe=$_POST['sexe'];
            //On construit la date d'aujourd'hui
            //strictement comme sql la construit
            $today = date("y-m-d");
            //On se connecte
            connectMaBase();

            //On prépare la commande sql d'insertion
            $sql = 'INSERT INTO Utilisateurs VALUES("","'.$pseudo.'","'.$sexe.'","'.$age.'","'.$today.'")';

            /*on lance la commande (mysql_query) et au cas où,
            on rédige un petit message d'erreur si la requête ne passe pas (or die)
            (Message qui intègrera les causes d'erreur sql)*/
            mysql_query ($sql) or die ('Erreur SQL !'.$sql.'<br />'.mysql_error());

            // on ferme la connexion
            mysql_close();
        }
        ?>
    </body>
</html>

Je vous laisse le plaisir de tester et d'alimenter votre base en saisissant des données dans votre formulaire...

Magique non ? Mieux encore, informagique...

Ne vous étonnez pas si quand vous cliquez sur OK, il semble ne rien se passer.
Ne cliquez pas trois fois et allez voir votre table utilisateurs.

Ca a très bien fonctionné. S'il semble ne rien se passer, c'est que nous avons simplement géré dans notre code l'insertion de données, sans penser à avertir l'utilisateur que son action a bien eu l'effet escompté.

Nous verrons cela plus tard... dans le TP par exemple ?

VI-3. TP6

SANS COPIER COLLER et en ne regardant que ça et là des parties du cours :

Vous proposerez un formulaire d'abonnement (abonnement.php) du type

Votre formulaire
POUR VOUS ABONNER :

Veuillez saisir vos données d'identité :
Bouton radio : Monsieur Madame ou Mademoiselle
Nom :
Prénom :
Age :
Adresse :
Code Postal :(Bloquer via HTML la saisie à 5 chiffres)
Ville : Numéro de téléphone (Bloquer via HTML la saisie à 10 chiffres)

Veuillez cocher le magazine choisi (un seul choix possible donc bouton radio) :

- J'ai la main verte.
- J'ai le pied marin.
- J'ai l'oeil vif.
- J'ai la rate qui se dilate.

Valider

Au clic sur valider de la part de l'utilisateur, vous ferez deux choses simultanées :

1) Afficher ceci

'Pour rappel, vous avez saisi :'
toutes les données entrées par l'utilisateur, présentées proprement, du genre

'VOUS ETES :'
'VOUS AVEZ CHOISI :'
'Merci de vous être abonné à ce magazine !'

2) Insérer dans une table Abonnes que vous aurez créée auparavant via phpMyAdmin toutes les données entrées par l'utilisateur.

PS : vous placerez dans le fichier fonctions.php la fonction de connexion bien entendu.

Bon courage !

VI-4. Correction du TP6

Veuillez cliquer sur le symbole à droite pour découvrir la solution quand vous aurez fini.

fonctions.php
<?php
function connectMaBase(){
    $base = mysql_connect ('localhost', 'root', '');
    mysql_select_db ('MaBase', $base) ;
}
?>

Dans phpMyAdmin, voici à quoi doit ressembler votre structure (de plus, il faut cocher AI (auto-incrémentation) pour l'ID).

Image non disponible
La structure de votre table abonnes

Au passage, c'est un hasard si les valeurs de nos publications sont toujours de 4 lettres (oeil, main etc.), mais autant en profiter pour mettre un CHAR 4 plutôt qu'un VARCHAR.
Mais attention aux valeurs qu'on leur affecte si l'on intègre de nouveaux magazines !

Dans abonnement.php enfin :

(Peut-être vous êtes vous cassé la tête avec cette histoire de nouvel écran quand l'utilisateur clique...
et ce damné formulaire qui restait en ligne, vos commentaires de rappel ne faisant que s'ajouter au HTML...)

Voici une solution à retenir : Dans vos if, commencer par mettre la condition d'affichage la plus "avancée" en premier, contrairement à ce que l'on a envie de faire spontanément :
remontez le temps à l'envers, si vous voulez...


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant

VII. PHP ETAPE 7

VII-1. Les bases de données : suite et fin

Nous savons maintenant remplir une base.
Reste à savoir l'exploiter et y récupérer les données souhaitées dans le cadre PHP.

Pour la suite de ce paragraphe, reprenez votre base MaBase, et votre table utilisateurs déjà riche d'au moins 5 enregistrements, voire plus si vous avez entré des données via votre formulaire...
En passant, avant de continuer, vérifiez dans votre base qu'il n'y a aucun doublon sur votre table, et supprimez-les manuellement avant de continuer...

Pour supprimer un enregistrement, cliquez d'abord sur rechercher dans la table, qui vous affiche toutes les données de votre table.
Puis cliquez sur l'enregistrement à annuler, puis sur la croix rouge pour l'action "delete".

On a déjà mentionné la syntaxe sql d'une simple requête :

SELECT * WHERE pseudo="Zézette"

pour récupérer toutes la ligne d'infos concernant notre amie Zézette par exemple.

Sauf que ce sont plusieurs infos (l'ID, le pseudo, le sexe, l'âge, et la date d'inscription de notre amie), et qu'en PHP, on va devoir stocker ça dans un... tableau bien sûr.

Chaque enregistrement récupéré sur ma base devra donc être stocké dans un tableau pour PHP.

Du coup, pour récupérer un enregistrement dans ce tableau, on va utiliser la fonction PHP destinée à mysql : mysql_fetch_array($TrucRecupereSurSql), pour être sûr de ne rien rater :

Ainsi l'on récupère un tableau associatif qui a pour indice... le nom de chaque champ.

C'est pas plus pratique comme ça ?

VII-1-a. Pour travailler proprement (Généralités)

Voici cette fois l'ordre logique pour un code PHP propre qui lance une requête sql
  1. On se connecte à la base (en utilisant notre fonction de connexion toute prête).
  2. On prépare la commande sql en la stockant dans une variable PHP du type $sql (pour langage sql).
  3. On la lance, en récupérant le résultat dans une variable que nous appellerons $req (pour requete sql et qui pourra être un tableau si le résultat dépasse un élément).
  4. Si c'est un tableau : On scanne $req avec une boucle while (car on ne sait pas toujours le nombre de champs, ni d'enregistrements) et grâce à la fonction mysql_fetch_array($req), chaque élément de ce tableau se convoquera ainsi : $data['champ'].
  5. Maintenant qu'on a tout récupéré dans des variables "solides" PHP, on libère la mémoire sql mobilisée par cette requête.
  6. On ferme la connexion sql.

Maintenant le code devrait vous paraître moins barbare...

VII-1-b. Le code pour récupérer toutes les filles

Je souhaite construire une page infos.php qui affiche toutes les infos sur toutes les filles dans ma base MaBase, table utilisateurs.

Un code de récupération de données sql : Où sont les femmes ?
Sélectionnez
<?php
include("fonctions.php");
?>
<html>
    <head><title>TOUTES LES INFOS SUR LES INSCRITS DU SITE</title></head>
    <body>
        <?php
        //On se connecte
        connectMaBase();

        // On prépare la requête
        $sql = 'SELECT * FROM utilisateurs WHERE sexe="F"';

        // On lance la requête (mysql_query) et on impose un message d'erreur si la requête ne se passe pas (or die)
        $req = mysql_query($sql) or die('Erreur SQL !<br />'.$sql.'<br />'.mysql_error());

        //on organise $req en tableau associatif  $data['champ']
        //en scannant chaque enregistrement récupéré
        //on en profite pour gérer l'affichage

        //titre de la page avant la boucle
        echo'<h2>TOUTES LES FILLES INSCRITES :</h2>';

        //boucle
        while ($data = mysql_fetch_array($req)) {
            // on affiche les résultats
            echo 'Pseudo : <strong>'.$data['Pseudo'].'</strong><br />';
            echo 'Son âge : '.$data['Age'].'<br />';
            echo 'Sa date d\'inscription : '.$data['DateInscription'].'<br /><br/>';
        }
        //On libère la mémoire mobilisée pour cette requête dans sql
        //$data de PHP lui est toujours accessible !
        mysql_free_result ($req);

        //On ferme sql
        mysql_close ();
        ?>
    </body>
</html>

C'est beau hein ? Et bon, ça marche.
Testez, bidouillez, triturez...
Cherchez les hommes, cherchez les gens âgés de plus de tel âge etc...

Bref, familiarisez vous avec le code avant d'attaquer le paragraphe suivant, qui pousse un peu plus loin...

VII-2. Un exemple (plus complexe) de relation dynamique entre PHP et SQL

Creusons ce chapitre pour aborder une idée supplémentaire :

Vous souhaitez laisser à l'utilisateur (imaginons que vous programmez une partie administration ou back office d'un site) le choix de son critère de recherche.
Il entre dans un formulaire les critères choisis, et votre code fait le reste.

Cela signifie que l'on veut obtenir une variable $sql du type :

$sql = 'SELECT * FROM utilisateurs WHERE '.$champ.'="'.$critere.'"';

Ne restent plus qu'à proposer un formulaire où l'administrateur entrera son choix de champ et de critère...
Il faut donc lui construire la liste de champs sous forme de liste déroulante en "tapant" dans la base une première fois : il n'est pas censé connaître la base par coeur...

Quelle requête pouvons nous faire pour récupérer tous les champs d'une table ?

Recherche tous les champs de la table utilisateurs :
Sélectionnez

SHOW fields FROM utilisateurs

Bon ben, ya plus qu'à !

Partie admin du site : exemple de codage de back-office
Sélectionnez
<?php
include("fonctions.php");
?>
<html>
    <head><title>ADMINISTRATION DU SITE</title></head>
    <body>
        <h2>Choisissez le champ qui vous intéresse et entrez manuellement un critère</h2>
        <h4>Une absence de critères vous montre toutes les données du champ</h4>
        <!--
            Commentaires HTML
            On construit une liste déroulante ( un select et plusieurs options)
            Chaque option sera remplie par une donnée SQL récupérée par notre requête PHP
        -->
        <form method="post" action="admin.php">
            <select name="champ">
                <?php
                //On se connecte
                connectMaBase();
                //On prépare la requête SQL qui récupère les champs
                $sql = 'Show fields from utilisateurs';
                /* On lance la requête (mysql_query)
                et on impose un message d'erreur si la requête ne passe pas (or die) */
                $req = mysql_query($sql) or die('Erreur SQL !<br />'.$sql.'<br />'.mysql_error());
                //On scanne le résultat et on construit chaque option avec
                while($data = mysql_fetch_array($req)){
                    // on affiche chaque champ
                    echo '<option name="'.$data['Field'].'">'.$data['Field'].'</option>';
                }
                //On libère mysql de cette première requête
                mysql_free_result ($req);
                //On ferme le select
                ?>
            </select>
            Entrez votre critère de sélection sur ce champ : <input type="text" name="critere"/>
            <input type="submit" name="Valider" value="OK"/>
        </form>
        <!--
            On ferme le formulaire
        -->
        <?php
        //On traite le formulaire
        if(isset($_POST['Valider'])){
            $champ=$_POST['champ'];
            $critere=$_POST['critere'];

            // On prépare la requête
            //requête différente selon qu'on veut tout le champ
            //ou un champ avec une condition
            if(($critere=='')||($critere==NULL)){
                $sql='SELECT '.$champ.' FROM utilisateurs';
            }
            else{
                $sql = 'SELECT * FROM utilisateurs WHERE '.$champ.'="'.$critere.'"';
            }
            /* On lance la requête (mysql_query)
            et on impose un message d'erreur si la requête ne passe pas (or die)*/
            $req = mysql_query($sql) or die('Erreur SQL !<br />'.$sql.'<br />'.mysql_error());

            //Affichage du résultat
            echo'<h2>Résultat</h2>';

            //On scanne chaque résultat et affiche
            while($data = mysql_fetch_array($req)){
                /* on affiche les résultats
                C'est pas très propre mais la fonction print_r vous permet de tout voir sur votre objet tableau :
                Quand vous êtes complètement perdu sur ce que votre tableau est censé comporter :
                Tapez cette commande print_r($tableau),
                vous retrouverez facilement la structure du tableau (index et valeurs)*/

                print_r($data);
                echo'<br/>';
            }
            //On libère la mémoire mobilisée pour cette seconde requête dans SQL
            mysql_free_result ($req);

            //On ferme sql
            mysql_close ();
        }
        ?>
    </body>
</html>

VII-3. TP7

Repartez de la correction du TP6 qui gérait la table abonnements.

Si ce n'est déjà fait, récupérez les fichiers abonnement.php, fonctions.php et veillez à construire la table abonnes dans phpMyAdmin.

Insérez (via le formulaire) au moins 10 enregistrements en variant largement les données du type age, civilité et Code Postal.
Si vous manquez totalement d'imagination, voici un exemple :

Un exemple d'abonnés à nos abonnements
Un exemple d'abonnés à nos abonnements

Maintenant, construisez la partie administration dans un fichier infoabo.php qui se présente ainsi :

Sélectionnez
Bonjour à l'administrateur du site.

Vous souhaitez voir : (proposez une liste déroulante avec les 6 options ci dessous) et un bouton OK.

Toutes les dames et demoiselles abonnées (commentaire : sous la forme Mme DUPONT Sandrine)

Tous les messieurs abonnés

Tous les abonnés qui ont moins de 30 ans

Tous les abonnés qui ont 30 ans ou plus

Tous les abonnés par magazine (commentaire : on veut titre du mag : tous les abos etc... 4 fois)

Tous les codes postaux des abonnés (juste les codes postaux)

Quand l'administrateur clique OK, l'info sélectionnée apparaît propre en dessous du formulaire de départ, ce qui lui permet de recommencer à loisir ces recherches.

Ajoutez enfin un bouton quitter, qui vous ramène à la page abonnement.php.

Et une fois n'est pas coutume, vous pouvez copier-coller tout ce que vous voulez !
(heu, sauf la correction qui suit bien sûr)...


PHP : Le tutoriel pour grands débutants pressés

Vu dans ... https://sylvie-vauthier.developpez.com/tutoriels/php/grand-debutant/

VIII. PHP ETAPE 8 : CONCLUSION

VIII-1. En guise de conclusion

Programmer en PHP, c'est garder à l'esprit constamment les généralités qu'il y avait dans cette introduction :

à savoir ce que peut PHP, et ce qu'il ne peut pas, ou pas bien, en relation avec la façon dont il fonctionne.
Je vais répéter cela de façon très carrée.

Avant de développer quoi que ce soit d'un peu ambitieux en PHP, comme un projet même petit, il faut inscrire ceci :

A quelles actions de l'utilisateur je vise quelles réactions dans mon code ?

En sachant que les actions que peut recevoir PHP, c'est essentiellement un submit, qui envoie des informations fournies par l'utilisateur, et c'est au rafraîchissement de la page occasionné par le submit qu'on peut programmer des instructions nouvelles en fonction des informations reçues.

C'est déjà pas mal.

VIII-1-a. PHP, un langage qui ne peut pas tout

Mais cela signifie que le concepteur du projet doit avoir en tête à l'avance toutes les actions de l'utilisateur avec lesquelles il veut interagir.

Tout se passe du côté du serveur, et doit être programmé d'avance.

Il y a d'autres langages qui se passent du côté client, du côté de l'utilisateur et sont susceptibles de réagir au moindre de ses battements de cil. Ce n'est pas le cas de PHP !

Pour parler d'un problème très concret et qui revient toujours en PHP : les contrôles de saisie.

Nous avons vu dans ce tutoriel comment remplir et traiter des formulaires avec PHP.
J'ai fait le choix de ne pas aborder, et pour cause, tout l'aspect contrôle de saisie.
A peine l'ai-je effleuré en vous disant une fois 'N'oubliez pas maxlength="10"' dans les attributs d'une zone de texte qui propose la saisie d'un téléphone.
Et encore n'était-ce pas du PHP, mais du HTML !

Le contrôle de saisie, c'est ce genre de choses.
Limiter et prévenir les erreurs, doublons, coquilles, maladresses, plaisanteries qui sinon se retrouvent directement dans votre base de données et la rendent pratiquement inutilisable à court terme.

Ainsi aurait-on dû également forcer la saisie de nombres pour le code postal à 5 etc...

Autre chose, vous avez sans doute en travaillant ce tuto cliqué parfois plusieurs fois sur un formulaire et vous êtes aperçu ensuite que la même valeur était du coup entrée plusieurs fois, autant de fois que vous avez cliqué.
C'est normal, rien dans votre code n'interdisait la saisie de doublons, qui se retrouvaient donc inéluctablement dans la base de données !

Pour gérer tout ceci, très utile donc, on préfèrera traditionnellement le sous-traiter soit à un langage comme javascript (non, on ne le dira jamais assez, ça n'a rigoureusement rien à voir avec java, qui est un autre langage)...

Javascript est un langage côté client qui réagit très souplement aux événements de l'utilisateur.
Ceci dit, javascript limitera grandement le taux d'erreurs des gens de bonne foi, mais pas des plaisantins qui ont désactivé le javascript sur leur navigateur !

Donc pour plus de sûreté, on combinera un formatage serré dans notre HTML et on programmera des regex (regular expressions) dans notre code PHP.
Je n'entrerai pas dans les arcanes des regex ici, mais sachez juste que c'est un type de code qui permet un formatage très rigoureux que l'on peut personnaliser à souhait et qui, côté serveur, permet de tester les données entrées avant de les insérer dans une base.

Vous y viendrez si vous voulez programmer des contrôles de saisie efficaces en PHP.

Dans tous les cas, l'idée est la suivante, PHP doit recueillir des infos "propres" et homogènes dès qu'on lui fait manipuler des bases de données.

s

VIII-1-b. Conceptualisation d'un projet PHP

Ayez le réflexe de raisonner en bloc, de façon modulaire...

Une méthode de travail rigoureuse
  1. Recensez toutes les actions que vous solliciterez de la part de l'utilisateur.
  2. Notez toutes les variables que vous aurez besoin d'initialiser ou d'affecter pour avancer.
  3. En vue du switch qui organisera la structure générale de votre programme, nommez ces blocs par exemple case 'debut', case 'inscription', case 'confirmation', case'erreur' etc.
  4. Rangez toutes les instructions dans ces blocs... Vérifier que chaque bloc est fonctionnel avant de passer au suivant...
  5. Enfin, vérifiez bien le déclencheur qui vous fait entrer dans chaque bloc d'instructions. Il a cliqué sur tel bouton que je lui présente dans tel bloc, il n'a rien cliqué etc...

Ainsi, quand vous avez tout cela clair en tête : vous pouvez vous lancer dans la structure de votre page, qui peut se présenter ainsi, en partant de la condition des écrans les plus avancés et en remontant jusqu'à l'écran d'arrivée...

Le mot clé sera MODULARISER.

une structure type
Sélectionnez
<?php
/****************************Actions de l'utilisateur***********************/
//il a cliqué confirmer
if(isset($_POST['confirmer'])){
    $bloc='insertionbase';
}
//il a cliqué form2
elseif(isset($_POST['validForm2'])){
    $bloc='traiteform2';
}
//il a cliqué valider formulaire1
elseif(isset($_POST['validForm1'])){
    $bloc='traiteform1';
}
//il arrive juste
else{
    $bloc='accueil';
}
/***************************Blocs d'instruction***************************/
/*Signalons au passage que si l'ordre était primordial dans notre floppée de if
Il n'a plus aucune importance dans nos blocs d'instructions !
Aussi on peut reprendre l'ordre chronologique et intuitif.*/
switch($bloc){
    case 'accueil':
        //toutes les instructions de présentation du formulaire1
    break;
    case 'traiteform1':
        //toutes les instructions de traitement du formulaire1
        //appel fonction machin
        //présentation du form2
    break;
    case 'traiteform2':
        //toutes les instructions de traitement du formulaire2
        //appel fonction truc
    break;
    case 'insertionbase':
        //appel fonction connexion
        //toutes les modifications de base de données
}
?>

Ainsi la structure de votre programme, même si le programme est touffu, est claire comme de l'eau de roche.

VIII-2. Quelques réflexes pour progresser en PHP

PHP est un excellent langage pour démarrer la programmation.

Il est assez intuitif et peu contraignant. Il est très gratifiant et vous donne vite des ailes.
Si déjà vous sortez de ce tutoriel avec les réflexes suivants, vous en aurez vite un usage très fluide.

Une dernière liste de conseils de Mamie Dendrite
  • Bien indenter votre code.
  • Bien repérer les actions très répétitives et les "ranger" dans des fonctions bien conçues.
  • Bien faire des commentaires pédagogiques pour vous même dans votre code de débutant pour toujours savoir qui fait quoi à quel moment.
  • Quand vous ne comprenez pas pourquoi un code ne marche pas, ne continuez pas de changer des choses au hasard. Arrêtez-vous. Vérifiez étape par étape que chaque module fonctionne.
  • Quand le module fatidique est cerné, le forum est là pour vous désembourber en un quart de tour avec des développeurs chevronnés !
  • Entreprenez un vrai projet personnel, comme le développement d'un site qui vous tient à coeur. Rien de tel pour vous porter que le souffle d'un projet web qui vous inspire !

Bon vent !

Et allez, quittons-nous sur un TP un peu plus costaud que les précédents, qui ne sera pas le plus bavard pour autant !

VIII-3. TP8

Dans la correction du TP5, nous nous attelions au problème des nombres premiers.

Vous pouvez vous en inspirer, et même la copier/coller pour résoudre le problème suivant.

Vous rédigerez un programme (affichepremiers.php) qui affiche dès que l'on clique sur un bouton OK la liste des nombres premiers...
Disons en tout cas jusqu'à 10 000. Il faut bien poser une limite à l'utilisateur.
Après les avoir tous répertoriés (une ligne par nombre), il affiche leur nombre (x nombres premiers entre 0 et 10 000)...

Bon courage.

PS : rien ne vous empêche ensuite de copier coller tout ça dans un fichier texte et de continuer... 10 000 par 10 000... Quand on pense que de grands mathématiciens y passaient des nuits quand l'informatique n'existait pas !

VIII-4. Correction du TP8

Veuillez cliquer sur le symbole à droite pour découvrir la solution quand vous aurez fini.

Listing des nombres premiers jusqu'à 10 000
<html>
    <head><title>Tous les nombres premiers</title></head>
    <body>
        <form method="POST" action="affichepremiers.php">
            Prêt pour la liste des nombres premiers =>10000 ? <input type="submit" name="ok" value="OK"/>
        </form>
        <?php
        if (isset ($_POST['ok'])){
            $flag=0;
            $compteur=0;
            //Teste chaque nombre de 2 à 10 000
            for($nombre=2;$nombre<=10000;$nombre++){
                //Divise le par 2 puis 3 puis 4 etc...
                for($i=2;$i<$nombre;$i++){
                    //S'il est multiple
                    //Allume le flag
                    if($nombre%$i==0){
                            $flag=1;
                    }
                }
                //Si le nombre est premier
                //Ecris-le, et incrémente le compteur
                if ($flag==0){
                    echo $nombre.'<br/>';
                    $compteur=$compteur+1;
                }
                //Dans tous les cas
                //Remets le flag à zéro pour la suite
                $flag=0;
            }
            //Quand tout est fini
            //Affiche $compteur
            echo'<h4>Il y a '.$compteur.' nombres premiers de 0 à 10 000.</h4>';
        }
        ?>
    </body>
</html>

Il ne vous aura pas échappé que le code précédent, certes tourne, mais il est diablement laborieux !
Par exemple, il est idiot de tester tous les nombres pairs, puisque l'on sait qu'un nombre premier (en dehors de 2) est forcément impair !

Aussi, comme Jpeg69 qui m'a soufflé l'idée de cet ajout, et s'est attelé à cette correction bis, nous allons procéder maintenant, pour ceux que le sujet intéresse, à une optimisation du code.
Vous aurez besoin de certains pré-requis pour vous en approcher.

4 pré-requis informatiques pour optimiser le code
  • D'algorithmie tout d'abord... Sachez que pour savoir si un nombre est premier, on peut se contenter de vérifier qu'il n'est divisible par aucun des nombres premiers qui lui sont inférieurs, et mieux encore... On s'arrête à la racine carrée de ce nombre !
  • Syntaxe : sqrt($nombre) renvoie la valeur de la racine carrée de $nombre.
  • Syntaxe : array_pad($tableau,$index,$element) permet de remplir un tableau proprement.
  • Syntaxe : la boucle {chaque instruction avec un point virgule} while(condition) est une variante de la boucle while... La différence, c'est qu'elle procède une fois à l'instruction AVANT de rentrer dans la boucle. Tandis qu'une boucle while "classique" ne fait rien tant que la condition n'est pas vérifiée.

A vous de jouer maintenant !

Le code optimisé de Jpeg69, merci à lui ! Cacher

<?php
$flag=0;
//On initialise le tableau des nombres premiers.
$premier=array(2,3);
//Le compteur commence donc à 2.
$compteur=2;
//On écrit ces 2 premiers nombres.
echo '2<br/>3<br/>';
//Nombre testé
for($nombre=5;$nombre<=10000;$nombre++){
    /*COMMENTAIRE GENERAL :
    L'initialisation de l'index doit être avant le "do while", le premier diviseur doit être 3 et l'incrémentation doit être placée avant le "if".
    En effet, voici les raisons données par Jpeg69 :

    "A premiere vue cette incrémentation est trop tôt. Mais il est inutile de tester la division par 2.
    On pourrait donc initialiser l'index directement à 1 et l'incrémenter après le test "if".
    Mais la valeur du diviseur n'est plus la même entre le test dans le "if" et le test dans le "while".
    Entre le test de la division et le test de supériorité à la racine carré du nombre testé.
    De plus, ça génère des bugs dès le début.
    Ainsi l'initialisation de l'index doit être avant le "do while", le premier diviseur doit être 3 et l'incrémentation doit être placé avant le "if"."
    */
    //Initialisation de l'index du tableau des nombres premiers diviseurs
    $i=0;
    do {
        $i++;
        if ($nombre%$premier[$i]==0){
            //C'est un nombre multiple d'un nombre premier inférieur à sa racine carrée
            //Bref, ce n'est pas un nombre premier !
            $flag=1;
        }
    }
    //condition : tant que nombre pas premier et que nombre diviseur est inférieur à la racine carrée du nombre testé
    while($flag==0 && $premier[$i]<sqrt($nombre));
    //Si nombre premier : l'afficher, incrémenter le compteur, et alimenter le tableau des diviseurs premiers
    if ($flag==0){
        echo $nombre.'<br/>';
        $compteur=$compteur+1;
        $premier = array_pad($premier,$compteur,$nombre);
    }
    //Dans tous les cas, remettre le drapeau en berne pour la suite
    $flag=0;
    $nombre++;
}
//Affichage de fin
echo '<h4>Il y a '.$compteur.' nombres premiers de 0 à 10 000.</h4>';
?>

VIII-5. Mes liens favoris pour l'apprentissage du développement web

VIII-5-a. Les tutos et cours Developpez.com

Un cours developpez.com pour apprendre XHTML

Un cours developpez.com pour apprendre les CSS

L'excellent cours PHP5 de developpez.com pour aller beaucoup plus loin, par notre maître Yogui !

Un cours developpez.com pour apprendre Javascript

VIII-5-b. D'autres tutos et cours qui m'ont beaucoup appris

Un excellent tutoriel pour apprendre de conserve HTML et CSS ("on repart de zéro", mais on arrive pourtant très loin !)

Un excellent tutoriel pour découvrir (avec des exercices corrigés) l'algorithmique en grand débutant

Un chouette tutoriel pour apprendre la syntaxe PHP vite fait pour débutant très très très pressé


Cours sur L'API Winodos de openclassrooms

PHP

Un article de Wikipédia, l'encyclopédie libre.

PHP: Hypertext Preprocessor[3], plus connu sous son sigle PHP (acronyme récursif), est un langage de programmation libre [4] principalement utilisé pour produire des pages Web dynamiques via un serveur HTTP [3], mais pouvant également fonctionner comme n'importe quel langage interprété de façon locale. PHP est un langage impératif orienté objet comme C++.

PHP a permis de créer un grand nombre de sites web célèbres, comme Facebook, YouTube, Wikipedia, etc.[5] Il est aujourd'hui considéré comme la base de la création des sites Internet dits dynamiques.

Sommaire

  • 1 Historique
    • 1.1 Historique des versions
    • 1.2 PHP 6 et Unicode
    • 1.3 PHP 7
  • 2 Présentation
  • 3 Fonctionnement
    • 3.1 Hello world
    • 3.2 Balises
    • 3.3 Instructions
    • 3.4 Structures de contrôle
    • 3.5 Génération de code HTML
    • 3.6 Programmation orientée objet (POO)
  • 4 Mascotte
  • 5 Quelques exemples d'application
  • 6 Différentes plates-formes
  • 7 Accélération
  • 8 Notes et références
  • 9 Voir aussi
    • 9.1 Bibliographie
    • 9.2 Articles connexes
    • 9.3 Liens externes

Historique

Le langage PHP fut créé en 1994 par Rasmus Lerdorf pour son site web. C'était à l'origine une bibliothèque logicielle en C [6] dont il se servait pour conserver une trace des visiteurs qui venaient consulter son CV. Au fur et à mesure qu'il ajoutait de nouvelles fonctionnalités, Rasmus a transformé la bibliothèque en une implémentation capable de communiquer avec des bases de données et de créer des applications dynamiques et simples pour le Web. Rasmus décida alors en 1995 de publier son code, pour que tout le monde puisse l'utiliser et en profiter[7]. PHP s'appelait alors PHP/FI (pour Personal Home Page Tools/Form Interpreter). En 1997, deux étudiants, Andi Gutmans et Zeev Suraski, redéveloppèrent le cœur de PHP/FI. Ce travail aboutit un an plus tard à la version 3 de PHP, devenu alors PHP: Hypertext Preprocessor. Peu de temps après, Andi Gutmans et Zeev Suraski commencèrent la réécriture du moteur interne de PHP. Ce fut ce nouveau moteur, appelé Zend Engine - le mot Zend est la contraction de ZEev et aNDi - qui servit de base à la version 4 de PHP[8].

En 2002, PHP est utilisé par plus de 8 millions de sites Web à travers le monde[9], en 2007 par plus de 20 millions[10] et en 2013 par plus de 244 millions[11].

La version actuelle est la version 5, sortie le 13 juillet 2004[12]. Elle utilise Zend Engine 2 et introduit une modélisation objet plus performante, une gestion des erreurs fondée sur le modèle des exceptions, ainsi que des fonctionnalités de gestion pour les entreprises. PHP 5 apporte beaucoup de nouveautés, telles que le support de SQLite ainsi que des moyens de manipuler des fichiers et des structures XML basés sur libxml2 :

  • une API simple nommée SimpleXML ;
  • une API Document Object Model assez complète ;
  • une interface XPath utilisant les objets DOM et SimpleXML ;
  • intégration de libxslt pour les transformations XSLT via l'extension XSL ;
  • une bien meilleure gestion des objets par rapport à PHP 4, avec des possibilités qui tendent à se rapprocher de celles de Java.

La dernière mise à jour est la 5.6.13 datant du 3 septembre 2015[13].

Depuis juin 2011 et le nouveau processus de livraison de PHP, le cycle de livraison de PHP est d'une mise à jour annuelle comportant des changements fonctionnels importants, la durée de vie d'une branche est de 3 ans et trois branches stables sont maintenues. Cela signifie que lorsqu'une nouvelle version de PHP 5.x sort, la version 5.x-3 n'est plus supportée.

Depuis la sortie de PHP 5.6, la configuration est :

  • branche de développement : 7.0 ;
  • branches encore supportées (corrections de bugs et de la sécurité) : 5.6, 5.5 ;
  • branche en obsolescence et ne recevant que des correctifs de sécurité : 5.4 ;
  • toutes les branches < 5.4 ne sont plus supportées.

Il est à noter qu'historiquement, PHP disposait d'une configuration par défaut privilégiant la souplesse à la sécurité (par exemple register globals, qui a été activé par défaut jusqu'à PHP 4.2[14]). Cette souplesse a permis à de nombreux développeurs d'apprendre PHP mais le revers de la médaille a été que de nombreuses applications PHP étaient mal sécurisées. Le sujet a bien été pris en main par le PHPGroup qui a mis en place des configurations par défaut mettant l'accent sur la sécurité. Il en résultait une réputation de langage peu sécurisé, réputation d'insécurité qui n'a plus de raison d'être.

Historique des versions

 | Code des couleurs
Rouge | Ancienne version, non supportée
Orange | Ancienne version, correctifs de sécurité uniquement
Vert | Version actuelle, améliorations et correctifs de sécurité
Bleu | Version future
Historique des versions
Version majeure | Version mineure | Date de sortie | Notes
1.0 | 1.0.0 | 8 juin 1995 | Appelé officiellement "Personal Home Page Tools (PHP Tools)", outils pour page personnelle. C'est la première apparition du nom "PHP"[15].
2.0 | 2.0.0 | 12 novembre 1997 |
3.0 | 3.0.0 | 6 juin 1998 | Passage d'une personne à une équipe de développeurs. Zeev Suraski et Andi Gutmans réécrivent la base de cette version[15].
4.0 | 4.0.0 | 22 mai 2000 | Ajout d'un système d'analyse syntaxique plus avancé appelé le Zend Engine. Ce moteur procède en deux étapes d'analyse puis d'exécution.
 | 4.1.0 | 10 décembre 2001 | Introduit les superglobals ($_GET, $_POST, $_SESSION, etc.)
 | 4.2.0 | 22 avril 2002 | Désactive register_globals par default. Les données en provenance du réseau ne sont plus insérées directement dans l'espace de nom global, réduisant ainsi les possibilités de failles de sécurité
 | 4.3.0 | 27 décembre 2002 | Introduit le CLI, en addition au CGI.
 | 4.4.0 | 11 juillet 2005 | Ajout du manuel (pages accessibles par la commande man) pour les scripts phpize et php-config.
5.0 | 5.0.0 | 13 juillet 2004 | Zend Engine II avec un nouveau modèle objet[16].
5.1 | 5.1.0 | 24 novembre 2005 | Amélioration de performances par l'introduction de variables de compilation dans un moteur PHP repensé[16].
5.2 | 5.2.0 | 2 novembre 2006 | Activation par défaut de l'extension «filter»[16].
5.3 | 5.3.0 | 30 juin 2009[17] | Support des espaces de noms ; Late Static Bindings (résolution statique à la volée) ; étiquettes de saut (goto limité) ; fermetures (closures) ; Native PHP archives (phar) ; ramasse-miettes de références cycliques ; connexion persistante avec MySQLi, sqlite3 ; ajout de fileinfo en tant que remplacement de mime_magic pour un meilleur support de MIME ; raccourci pour l’opérateur ternaire (?:) ; fonctions anonymes ; nombreuses corrections de bug[18]...
 | 5.3.3 | 22 juillet 2010 | Introduit PHP-FPM [19]
5.4 | 5.4.0 | 1er mars 2012 | Support des traits ; syntaxe courte des tableaux introduite ; accès aux index de tableaux en sortie de fonctions ; les fermetures supportent $this ; <?= toujours disponible ; accès aux attributs d'un objet dès l'instanciation ; ajout syntaxe Class::{expr}() ; ajout format de nombres binaires ; suivi des envois de fichiers via l'extension de session ; serveur web embarqué en CLI[20] dédié au développement ; support de Dtrace ; suppression des extensions magic quotes, safe_mode, register_globals et register_long_array[20].
 | 5.4.26 | 6 mars 2014 | Ajout de ldap_modify_batch()
5.5 | 5.5.0 | 20 juin 2013 | Support des générateurs et des coroutines ; ajout d'une API de hachage des mots de passe ; intégration en standard du système de cache Zend OPcache[21] ; correction de nombreux bugs ; arrêt du support de Windows XP et Windows Server 2003 ; mise à jour de la bibliothèque GD ; les fonctions mysql_ sont maintenant considérées comme obsolètes (mise en avant de mysqli_ et de PDO_MySQL).
 | 5.5.4 | 19 septembre 2013 | Amélioration de fputcsv().
 | 5.5.5 | 17 octobre 2013 | Nouvelle fonction opcache_compile_file(), support du système GNU Hurd, ajout du type MIME application/pdf dans le serveur de développement.
 | 5.5.6 | 14 novembre 2013 | Amélioration des performances de array_merge() et func_get_args().
 | 5.5.7 | 12 décembre 2013 | Ajout du support de getallheaders() et de types MIME dans le serveur de développement, ajout de apache_response_headers().
 | 5.5.10 | 6 mars 2014 | Passage à PCRE 8.34. Ajout de ldap_modify_batch()
 | 5.5.13 | 29 mai 2014 | Ajout des types MIME xsl/xml au serveur de développement intégré.
5.6 | 5.6.0 | 28 août 2014 | Nouvelle syntaxe pour les fonctions à paramètres variables et dépaquetage de paramètres, importation de fonctions au sein d'espaces de noms, intégration du débogueur phpdbg en standard, amélioration de l'extension zip, expressions acceptées pour la définition de constantes, amélioration de la gestion des données POST, support de la surcharge d'opérateurs dans le Zend Engine, nouvelle méthode magique __debugInfo() fournissant des informations de débogage sur les objets, améliorations diverses sur le support de SSL/TLS, ajout de l'opérateur d'exponentiation « ** », téléchargements de poids supérieur à 2 Go désormais supportés.
7.0 | 7.0.0 RC 2 | 4 septembre 2015 | Performance améliorée grâce au moteur « phpng » (jusqu'à deux fois plus rapide que PHP 5.6), support homogène des architectures 64 bits, ajout d'exceptions permettant d'intercepter des erreurs fatales, ajout des opérateurs « ?? » (dit « null coalescing (en) ») et « <=> » (dit « combined comparison »), possibilité de déclarer explicitement le type retourné par une fonction ainsi que les types scalaires des arguments, support des classes anonymes[22] ...

PHP 6 et Unicode

En 2005, le projet de faire de PHP un langage supportant et fonctionnant nativement en Unicode a été lancé par Andrei Zmievski, ceci en s'appuyant sur la bibliothèque International Components for Unicode (ICU) et en utilisant UTF-16 pour représenter les chaînes de caractères dans le moteur[23].

Étant donné que cela représentait un changement majeur tant dans le fonctionnement du langage que dans le code PHP créé par ses utilisateurs, il fut décidé d'intégrer cela dans une nouvelle version 6.0 avec d'autres fonctionnalités importantes alors en développement[24]. Toutefois, le manque de développeurs experts en Unicode ainsi que les problèmes de performance résultant de la conversion des chaînes de et vers UTF-16 (rarement utilisé dans un contexte web), ont conduit au report récurrent de la livraison de cette version[25]. Par conséquent, une version 5.3 fut créée en 2009 intégrant de nombreuses fonctionnalités non liées à Unicode qui était initialement prévues pour la version 6.0, notamment le support des espaces de nommage (namespaces) et des fonctions anonymes. En mars 2010, le projet 6.0 intégrant unicode fut abandonné et la version 5.4 fut préparée afin d'intégrer la plupart des fonctionnalités non liées à l'unicode encore dans la branche 6.0, telles que les traits ou l'extension des closures au modèle objet[26].

Le projet est depuis passé à un cycle de livraison prévisible (annuel) contenant des avancées significatives mais contenues tout en préservant au maximum la rétro-compatibilité avec le code PHP existant (5.4 en 2012, 5.5 en 2013, 5.6 prévue pour l'été 2014). Depuis janvier 2014[27], l'idée d'une nouvelle version majeure introduisant Unicode mais se basant sur UTF-8 (largement devenu depuis le standard du Web pour l'Unicode) et permettant certains changements pouvant casser la rétro-compatibilité avec du code PHP ancien est de nouveau discutée et les RFC sont maintenant triées selon leur implémentation en 5.x (évolutions ne causant pas ou marginalement de cassure de la rétro-compatibilité) ou dans la future version majeure (évolutions majeures du moteur et évolutions impliquant une non-compatibilité ascendante).

PHP 7

Au vu des orientations différentes prises par le langage que celles prévues par PHP 6, une partie des développeurs propose de nommer la version succédant à PHP 5 « PHP 7 » au lieu de « PHP 6 ». Un vote parmi les développeurs valident cette proposition par 58 voix contre 24[28].

La nouvelle version propose une optimisation du code et, d'après la société Zend, offre des performances dépassant celles de machines virtuelles comme HHVM [29],[30]. Les benchmarks externes montrent des performances similaires pour HHVM et PHP 7, avec un léger avantage d'HHVM dans la plupart des scénarios[31].

Présentation

PHP est un langage de script utilisé le plus souvent côté serveur : dans cette architecture, le serveur interprète le code PHP des pages web demandées et génère du code (HTML, XHTML, CSS par exemple) et des données (JPEG, GIF, PNG par exemple) pouvant être interprétés et rendues par un navigateur. PHP peut également générer d'autres formats comme le WML, le SVG, le PDF.

Il a été conçu pour permettre la création d'applications dynamiques, le plus souvent développées pour le Web. PHP est le plus souvent couplé à un serveur Apache bien qu'il puisse être installé sur la plupart des serveurs HTTP tel que IIS. Ce couplage permet de récupérer des informations issues d'une base de données, d'un système de fichiers (contenu de fichiers et de l'arborescence) ou plus simplement des données envoyées par le navigateur afin d'être interprétées ou stockées pour une utilisation ultérieure.

C'est un langage peu typé et souple et donc facile à apprendre par un débutant mais, de ce fait, des failles de sécurité peuvent rapidement apparaître dans les applications. Pragmatique, PHP ne s'encombre pas de théorie et a tendance à choisir le chemin le plus direct. Néanmoins, le nom des fonctions (ainsi que le passage des arguments) ne respecte pas toujours une logique uniforme, ce qui peut être préjudiciable à l'apprentissage.

Son utilisation commence avec le traitement des formulaires puis par l'accès aux bases de données. L'accès aux bases de données est aisé une fois l'installation des modules correspondants effectuée sur le serveur. La force la plus évidente de ce langage est qu'il a permis au fil du temps la résolution aisée de problèmes autrefois compliqués et est devenu par conséquent un composant incontournable des offres d'hébergements.

Il est multiplate-forme : autant sur Linux qu'avec Windows il permet aisément de reconduire le même code sur un environnement à peu près semblable (prendre en compte les règles d'arborescences de répertoires qui peuvent changer).

Libre, gratuit, simple d'utilisation et d'installation, ce langage nécessite comme tout langage de programmation une bonne compréhension des principales fonctions usuelles ainsi qu'une connaissance aiguë des problèmes de sécurité liés à ce langage.

La version 5.3 a introduit de nombreuses fonctionnalités : les espaces de noms — un élément fondamental de l'élaboration d'extensions, de bibliothèques et de frameworks structurés, les fonctions anonymes, les fermetures, etc.

Aujourd'hui près de 80% des sites internet utilisent le langage PHP sous ses différentes versions. Plusieurs développeurs PHP responsables de ces sites utilisent en majorité la version 5.4 (38%) dans leurs missions quotidiennes[32].

Le langage PHP fait l'objet depuis plusieurs années maintenant d'un forum dédié organisé par l'AFUP (l'Association Française des Utilisateurs de PHP), ou experts de la programmation et du milieu se retrouvent pour échanger autour du PHP et de ses développeurs. Ce forum est par ailleurs un moment important pour ces acteurs car il est synonyme du lancement d'une étude dédiée aux développeurs PHP réalisée par l'AFUP et le cabinet de recrutement Agence-E.

Fonctionnement

PHP appartient à la grande famille des descendants du C, dont la syntaxe est très proche. En particulier, sa syntaxe et sa construction ressemblent à celles des langages Java et Perl, à la différence que du code PHP peut facilement être mélangé avec du code HTML au sein d'un fichier PHP.

Dans une utilisation Web, l'exécution du code PHP se déroule ainsi : lorsqu'un visiteur demande à consulter une page Web, son navigateur envoie une requête au serveur HTTP correspondant. Si la page est identifiée comme un script PHP (généralement grâce à l'extension .php), le serveur appelle l'interprète PHP qui va traiter et générer le code final de la page (constitué généralement d'HTML ou de XHTML, mais aussi souvent de CSS et de JS). Ce contenu est renvoyé au serveur HTTP, qui l'envoie finalement au client.

Ce schéma explique ce fonctionnement :

Php arch shema.png

Une étape supplémentaire est souvent ajoutée : celle du dialogue entre PHP et la base de données. Classiquement, PHP ouvre une connexion au serveur de SGBD voulu, lui transmet des requêtes et en récupère le résultat, avant de fermer la connexion.

Capture d'écran de Vim en fond, affichant un script PHP, et à droite le résultat du script interprété par PHP

L'utilisation de PHP en tant que générateur de pages Web dynamiques est la plus répandue, mais il peut aussi être utilisé comme langage de programmation ou de script en ligne de commande sans utiliser de serveur HTTP ni de navigateur. Il permet alors d'utiliser de nombreuses fonctions du langage C et plusieurs autres sans nécessiter de compilation à chaque changement du code source.

Pour réaliser en Linux/UNIX un script PHP exécutable en ligne de commande, il suffit comme en Perl ou en Bash d'insérer dans le code en première ligne le shebang : #! /usr/bin/php[33]. Sous un éditeur de développement comme SciTE, même en Windows, une première ligne <?php suffit, si le fichier possède un type .php.

Il existe aussi une extension appelée PHP-GTK permettant de créer des applications clientes graphiques sur un ordinateur disposant de la bibliothèque graphique GTK+, ou encore son alternative WinBinder.

PHP possède un grand nombre de fonctions permettant des opérations sur le système de fichiers, exécuter des commandes dans le terminal, la gestion des bases de données, des fonctions de tri et hachage, le traitement de chaînes de caractères, la génération et la modification d'images, des algorithmes de compression...

Le moteur de Wikipédia, MediaWiki, est écrit en PHP et interagit avec une base MySQL ou PostgreSQL

Hello world

Quelques exemples du traditionnel Hello world :

<?php
    // forme la plus simple, recommandée
    echo 'Hello World';
?>

echo étant une structure du langage, il est possible – et même recommandé – de ne pas mettre de parenthèses, ce qui lui permet en plus d'accepter plusieurs paramètres :

<?php
    echo 'Hello ', 'World';
?>

Il est aussi possible d'utiliser la version raccourcie :

<?='Hello World'?>

Résultat affiché :

  Hello World

Balises

Le code PHP doit être inséré entre des balises <?php et ?>.

Il existe des notations raccourcies : <? et ?>, voire la notation ASP <% et %>, mais celles-ci sont déconseillées, car elles peuvent être désactivées dans la configuration du serveur (php.ini ou .htaccess) : la portabilité du code est ainsi réduite.

Il existe aussi cette syntaxe, peu courante : <script language="php"> et </script>.

Depuis PHP 7, les notations ASP et script ont été supprimées.[34]

Instructions

Les instructions sont séparées par des ";" (il n'est pas obligatoire après la dernière instruction) et les sauts de ligne ne modifient pas le fonctionnement du programme. Il serait donc possible d'écrire :

<?php echo 'Hello World'; echo 'Comment allez-vous ?'; echo 'Il fait beau non ?' ?>

Pour des raisons de lisibilité, il est néanmoins recommandé d'écrire une seule instruction par ligne. Il est aussi préférable d'écrire le dernier ";".

Structures de contrôle

Le code PHP est composé par des appels à des fonctions, dans le but d'attribuer des valeurs à des variables, le tout encadré dans des conditions, des boucles. Exemple :

<?php
    // la fonction strtolower renvoie en minuscules la chaîne de caractères passée en paramètre
    $lang = strtolower($_POST['lang']);

    if ($lang === 'fr')
        echo 'Vous parlez français !';
    elseif ($lang === 'en')
        echo 'You speak English!';
    else
        echo 'Je ne vois pas quelle est votre langue !';

Une condition est appliquée quand l'expression entre parenthèses est évaluée à 'true', et elle ne l'est pas dans le cas de 'false'. Sous forme numérique, 0 représente le 'false', et 1 (et tous les autres nombres) représentent le 'true'.

Le code précédent pourrait aussi être écrit de cette manière :

<?php
    $lang = strtolower($_POST['lang']);
    $is_lang_fr = $lang === 'fr';
    $is_lang_en = $lang === 'en';

    if ($is_lang_fr)
        echo 'Vous parlez français !';
    elseif ($is_lang_en)
        echo 'You speak English!';
    else
        echo 'Je ne vois pas quelle est votre langue !';

Ici on teste l'égalité entre $lang et "fr", mais pas directement dans le if : le test retourne un boolean (c'est-à-dire soit true, soit false) qui est stocké dans la variable $is_lang_fr. On entre ensuite cette variable dans le if et celui-ci, selon la valeur de la variable, effectuera ou non le traitement.

Les blocs if, elseif et else sont généralement délimités par les caractères "{" et "}", qui peuvent être omis, comme dans les codes précédents, lorsque ces blocs ne contiennent qu'une instruction.

Il est également possible d'écrire else if en deux mots, comme en C/C++.

Génération de code HTML

On peut tout à fait générer du code HTML avec le script PHP, comme ceci par exemple :

<?php
    $nom = 'Albert';
    echo '<div>Bonjour ' . $nom . ' !</div>';

Il est également possible d'utiliser une syntaxe alternative pour la structure if/else :

<?php
    $nomsAutorises = array('Albert', 'Bertrand');
    $nomEnCours    = 'Eve';
?>

<?php if (in_array($nomEnCours, $nomsAutorises)): ?>
    <p>Bonjour <?php echo $nomEnCours; ?> !</p>
<?php else : ?>
    <p>Vous n'êtes pas un utilisateur autorisé !</p>
<?php endif ?>

Une autre approche pour générer du code HTML est de concaténer l'intégralité du code HTML dans une variable et de réaliser un echo de la variable en fin de fichier :

<?php
    $out = '';

    // On ajoute progressivement tout le code html
    $out .= '<p>a' . 'b' . 'c</p>';
    $out .= '<div class="bottom"><p>d' . 'e' . 'f</p></div>';

    // On affiche le contenu
    echo $out;

Dans le cas où l'utilisateur aura préféré l'utilisation de la commande echo à la concaténation, il lui sera possible de capturer le flux en utilisant les fonctions ob_start() et ob_get_clean() :

<?php
    // On place le flux dans un tampon
    ob_start();

    // On fait des affichages
    echo 'a', 'b', 'c', "\n";
    echo 'd', 'e', 'f', "\n";
    require_once 'fichier.php'; // De nombreux affichages (echo)

    // On stoppe la mise en tampon, on récupère son contenu et on met le tampon à vide (pour une éventuelle prochaine tamporisation)
    $out = ob_get_clean();

    // On affiche le contenu du tampon
    echo $out;

PHP, tout comme JavaScript, permet aussi de construire un modèle objet de document (DOM)[35], ce qui permet de créer ou modifier un document (X)HTML sans écrire de HTML, comme le montre l'exemple suivant :

<?php
    $doctype = DOMImplementation::createDocumentType('html');
    $dom = DOMImplementation::createDocument(null, "html", $doctype);
    $html = $dom->documentElement;
    $html->head = $dom->createElement("head");
    $html->appendChild($html->head);
    $html->head->title = $dom->createElement("title");
    $html->head->title->nodeValue = "Exemple de HTML";
    $html->head->appendChild($html->head->title);
    $html->head->charset = $dom->createElement("meta");
    $html->head->charset->setAttribute("http-equiv", "Content-Type");
    $html->head->charset->setAttribute("content", "text/html; charset=utf-8");
    $html->head->appendChild($html->head->charset);
    $html->body = $dom->createElement("body");
    $html->appendChild($html->body);
    $html->body->p = $dom->createElement("p");
    $html->body->p->nodeValue = "Ceci est un paragraphe.";
    $html->body->appendChild($html->body->p);
    $html->body->p->br = $dom->createElement("br");
    $html->body->p->appendChild($html->body->p->br);
    $html->body->p->a = $dom->createElement("a");
    $html->body->p->a->nodeValue = "Ceci est un lien.";
    $html->body->p->a->setAttribute("href", "cible.html");
    $html->body->p->appendChild($html->body->p->a);

    echo $dom->saveHTML();

Qui crée le code HTML suivant :

<!DOCTYPE html>
<html>
<head>
<title>Exemple de HTML</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body><p>Ceci est un paragraphe.<br/><a href="cible.html">Ceci est un lien.</a></p></body>
</html>

Cette méthode est cependant peu utilisée pour générer un document complet, on l'utilise généralement pour générer un fichier XML.

La commande phpinfo() est aussi utilisée pour générer un code HTML décrivant les paramètres du serveur ; elle est aussi très utilisée pour tester la bonne exécution du moteur d’exécution PHP.

Programmation orientée objet (POO)

Comme en C++, les versions actuelles de PHP (à partir de PHP 5) permettent de programmer en orienté objet, en créant des classes contenant des attributs et des méthodes, des instances de classes. L'héritage (simple) entre les classes existe aussi. Voici un exemple de création d'une classe :

<?php
    class Perso {
        const PV_initial = 2000;
        private $pv;

        public function __construct($pv = false) { // Paramètre optionnel
            if (!is_numeric($pv) || $pv < 0 || $pv > 100000000)
                $this->pv = self::PV_initial;
            else
                $this->pv = $pv;
        }

        // Accesseurs
        public function getPv() {
            return $this->pv;
        }

        public function isDead(){
            return $this->pv == 0;
        }
    }

    // Création d'une classe enfant de Perso
    class Magicien extends Perso {
        private $magie;
    }

    // Création d'une instance de classe
    $perso = new Perso(1000);

    // Utilisation de l'objet
    echo 'Votre personnage a ' . $perso->getPV() . ' PV.';

    // Constantes de classes
    echo 'Le PV par défaut attribué à un nouveau personnage est de ' . Perso::PV_initial . '.';

    // Destruction de l'objet
    unset($perso);

Mascotte

L'éléPHPant, la mascotte de PHP.

Comme de nombreux projets Open Source, PHP possède une mascotte. Il s'agit de l'éléPHPant, dessiné en 1998 par El Roubio[36].

El Roubio s'est inspiré de la ressemblance des lettres PHP avec un éléphant et du fait que deux des lettres du langage soient déjà présentes dans ce mot, ce qui a permis de créer le néologisme éléPHPant. Toutes les œuvres d'El Roubio sont distribuées sous licence GNU GPL. Une peluche de l'ÉléPHPant[37] bleu existe. D'autres versions ont vu le jour ces dernières années (rose, jaune, rouge, violet et orange) sous l'impulsion de sociétés (PHP Architect ou Zend Technologies) ou de groupes utilisateurs comme PHP Women.

Quelques exemples d'application

  • Wiki (MediaWiki, WikiNi, DokuWiki...)
  • forum (phpBB, IPB, punBB...)
  • Systèmes de gestion de blog (Dotclear, WordPress...)
  • Systèmes de gestion de contenu (appelés aussi CMS) (Spip, ExpressionEngine, Drupal, Xoops, Joomla...)
  • Administration de bases de données (phpMyAdmin, phpPgAdmin, Adminer, Eskuel...)
  • Frameworks (Zend Framework, CodeIgniter, CakePHP, Symfony, etc.)
  • Logiciel ECM (Dynacase Platform)
  • Logiciel BPM (Dynacase Platform)
  • E-commerce (Magento, osCommerce, PrestaShop, etc.)

Différentes plates-formes

Un serveur Web en architecture trois tiers est composé d'un système d'exploitation, un serveur HTTP, un langage serveur et enfin un système de gestion de base de données (SGBD), cela constituant une plate-forme.

Dans le cas de PHP comme langage serveur, les combinaisons les plus courantes sont celles d'une plateforme LAMP (pour Linux Apache MySQL PHP) et WAMP (Windows Apache MySQL PHP). Une plate-forme WAMP s'installe généralement par le biais d'un seul logiciel qui intègre Apache, MySQL et PHP, par exemple EasyPHP, VertrigoServ, WampServer ou UwAmp. Il existe le même type de logiciels pour les plates-formes MAMP (Mac OS Apache MySQL PHP), à l'exemple du logiciel MAMP.

Il existe d'autres variantes, par exemple les plates-formes LAPP (le M de MySQL est remplacé par le P de PostgreSQL) ou encore le logiciel XAMPP (Apache MySQL Perl PHP ; le X indique que le logiciel est multiplate-forme), un kit de développement multiplate-forme.

On peut décliner une grande variété d'acronymes sous cette forme. Des confusions peuvent parfois exister entre la plate-forme en elle-même et le logiciel permettant de l'installer, si elles ont le même nom. Il faut également remarquer que la grande majorité des logiciels « tout en un » sont destinés au développement d'applications Web en local, et non à être installés sur des serveurs Web. Une exception à cette règle est peut-être Zend Server, le serveur distribué par Zend Technologies, qui est prévu pour fonctionner aussi bien en environnement de développement que de production.

Accélération

PHP est à la base un langage interprété, ce qui est au détriment de la vitesse d'exécution du code. Sa forte popularité associée à son utilisation sur des sites Web à très fort trafic (Yahoo, Facebook [38]) ont amené un certain nombre de personnes à chercher à améliorer ses performances pour pouvoir servir un plus grand nombre d'utilisateurs de ces sites Web sans nécessiter l'achat de nouveaux serveurs.

La réécriture du cœur de PHP, qui a abouti au Zend Engine pour PHP 4 puis au Zend Engine 2 pour PHP 5, est une optimisation. Le Zend Engine compile en interne le code PHP en bytecode exécuté par une machine virtuelle. Les projets open source APC et eAccelerator fonctionnent en mettant le bytecode produit par Zend Engine en cache afin d'éviter à PHP de charger et d'analyser les scripts à chaque requête. À partir de la version 5.5 de PHP, le langage dispose d'un cache d'opcode natif (appelé OpCache) rendant obsolète le module APC.

Il existe également des projets pour compiler du code PHP :

  • Roadsend et phc compilent du PHP en C,
  • Quercus compile du PHP en bytecode Java exécutable sur une machine virtuelle Java,
  • Phalanger compile du PHP en Common Intermediate Language exécutable sur le Common Language Runtime du framework .NET.
  • HipHop for PHP transforme du PHP en C++ qui est ensuite compilé en code natif. Ce projet open source a été démarré par Facebook[39].

Notes et références

  1. ↑ Manuel PHP – Préface
  2. ↑ source
  3. ↑ a et b « Manuel PHP : Préface », sur PHP.net (consulté le 7 novembre 2007)
  4. ↑ (en) « La licence de PHP est reconnue libre par la Free Software Foundation » (consulté le 7 novembre 2007)
  5. ↑ « Introduction à PHP », sur openclassrooms.com (consulté le 14 juillet 2015)
  6. ↑ Rasmus Lerdorf, « I wonder why people keep writing that PHP was ever written in Perl. It never was. #php » [html], Twitter,‎ 20 juillet 2012 (consulté le 5 septembre 2014)
  7. ↑ https://groups.google.com/forum/#!msg/comp.infosystems.www.authoring.cgi/PyJ25gZ6z7A/M9FkTUVDfcwJ
  8. ↑ « Histoire de PHP », sur PHP.net (consulté le 7 novembre 2007)
  9. ↑ (en) « Netcraft web Server Survey - février 2002 » (consulté le 7 novembre 2007)
  10. ↑ http://www.php.net/usage.php
  11. ↑ http://news.netcraft.com/archives/2013/01/31/php-just-grows-grows.html
  12. ↑ (en) « Historique des sorties de PHP », sur PHP.net (consulté le 7 novembre 2007)
  13. ↑ (en) « Dernière sortie de PHP en date », sur PHP.net (consulté le 10 novembre 2007)
  14. ↑ « Register Globals ».
  15. ↑ a et b (en) « History of PHP », The PHP Group (consulté le 18 juin 2010)
  16. ↑ a, b et c (en) « PHP: PHP 5 ChangeLog », The PHP Group,‎ 8 novembre 2007 (consulté le 22 février 2008)
  17. ↑ (en) PHP 5.3.0 Released!
  18. ↑ (en) PHP 5.3.0 Changelog
  19. ↑ (en) FPM in 5.3
  20. ↑ a et b [1]
  21. ↑ La version 5.5.0 est disponible PHP.net
  22. ↑ PHP 7.0.0 RC 2 Released
  23. ↑ Andrei Zmievski, « PHP Unicode support design document (mailing list post) »,‎ 10 août 2005 (consulté le 9 février 2014)
  24. ↑ « PHP 5.5 or 6.0 » (consulté le 9 février 2014)
  25. ↑ Andrei Zmievski, « The Good, the Bad, and the Ugly: What Happened to Unicode and PHP 6 » (consulté le 9 février 2014)
  26. ↑ Rasmus Lerdorf, « PHP 6 (mailing list post) »,‎ 11 mars 2010 (consulté le 7 février 2014)
  27. ↑ Adam harvey, « Ruminations on PHP 5++ (message de liste de discussion) »,‎ 11 mars 2010 (consulté le 7 février 2014)
  28. ↑ Andrea Faulds, Zeev Suraski, « PHP RFC: Name of Next Release of PHP »,‎ 2004 (consulté le 22 novembre 2014)
  29. ↑ « PHP Next Generation is outperforming Facebook’s HHVM », sur The Zend Blog,‎ 17 juillet 2014 (consulté le 22 novembre 2014)
  30. ↑ Antoine Crochet-Damais, « Andi Gutmans (Zend Technologies) : "PHP Next Generation est plus performant que HHVM de Facebook" », sur Le Journal du Net,‎ 25 juin 2014 (consulté le 22 novembre 2014)
  31. ↑ Zeev Suraski, « Benchmarking PHPNG! », sur Zeev's blog,‎ juillet 2014 (consulté le 22 novembre 2014)
  32. ↑ Agence-E, « Baromètre AFUP - Agence-E 2015 : Les salaires dans l'écosystème PHP en France » [PDF], sur Agence-e.fr,‎ 25 septembre 2014 (consulté le 11 décembre 2014)
  33. ↑ /usr/bin/ est le répertoire standard des fichiers binaires exécutables sur la plupart des distributions.
  34. ↑ (en) « PHP RFC: Remove alternative PHP tags »
  35. ↑ http://php.net/manual/fr/book.dom.php
  36. ↑ Présentation des éléPHPants par El Roubio
  37. ↑ http://www.elephpant.com/
  38. ↑ Facebook looks to fix PHP performance with HipHop virtual machine
  39. ↑ HipHop for PHP: Move Fast, Haiping Zhao le 2 février 2010

Voir aussi

Sur les autres projets Wikimedia :

  • PHP, sur le Wiktionnaire
  • PHP, sur Wikiversity
  • PHP, sur Wikibooks

Bibliographie

  • (en) Luke Welling, Laura Thomson, PHP and MySQL Web development, Sams Publishing,‎ 2008, 4e éd. (ISBN 0-672-32916-6, OCLC 854795897)
  • Damien Seguy, Philippe Gamache, Sécurité PHP 5 et MySQL, 3ème édition, Eyrolles,‎ 1er décembre 2011, 277 p. (ISBN 2-212-13339-1)

Articles connexes

  • Paamayim Nekudotayim : nom de l'opérateur :: en PHP
  • Liste de frameworks PHP : liste des cadres de développement (Frameworks) en PHP

Liens externes

  • (en) Site officiel
  • Manuel officiel PHP, sur le site php.net
  • Association Française des Utilisateurs de PHP, sur le site afup.org
  • Entretien avec Rasmus Lerdorf à l'occasion du 10e Forum PHP (2010)
  • Catégorie PHP de l’annuaire DMOZ
  • Baromètre Agence-e - AFUP : Les salaires de l'écosystème PHP en France
v · m Langages de programmation
Orientés objet | ActionScript · AppleScript · C++ · C# · CoffeeScript · D · Delphi · Eiffel · Groovy · Java · JavaScript · Lisaac · Logo · Objective-C · PHP · Python · Ruby · Scala · Simula · Smalltalk · Visual Basic
Impératifs | Algol · APL · ASP · Assembleur · BASIC · BCPL · Shell Unix · C · COBOL · Natural · Forth · Fortran · Go · Limbo · Lua · Modula-2 · NQC · NXC · OPL · Pascal · Perl · PHP · Rust · PL/I · Tcl · Windows PowerShell · WLangage
Fonctionnels | Haskell · Lisp · Common Lisp · ML · OCaml · Gallina · F# · Standard ML · Opa · Scheme · XSLT
Déclaratifs | Clips · Prolog · ASP
Concurrents | Ada · Erlang
Conception · Codage · Tests · Optimisations
  • Portail des logiciels libres Portail des logiciels libres
  • Portail de la programmation informatique Portail de la programmation informatique
Ce document provient de « https://fr.wikipedia.org/w/index.php?title=PHP&oldid=118323445 ».
Catégories :
  • PHP
  • Langage impératif
  • Langage de script
  • Langage pour pages Web dynamiques
  • Langage orienté objet
  • Logiciel sous licence libre
  • Logiciel pour Windows
  • Logiciel pour Unix
Catégories cachées :
  • Article contenant un appel à traduction en anglais
  • Portail:Logiciels libres/Articles liés
  • Portail:Logiciel/Articles liés
  • Portail:Informatique/Articles liés
  • Portail:Programmation informatique/Articles liés
  • Bon article en anglais


Vu dans ... https://vviale.developpez.com/tutoriels/appli-web/

Créer sa première Application Web

Table des matières

  • I. Introduction
  • II. Installation de l'environnement de travail
  • III. Construction de la base de données
    • III-A. Concevoir la base de données
    • III-B. Création de la base de données
      • III-B-1. Manuellement
      • III-B-2. Importer un fichier
  • IV. Construction de l'application Web
    • IV-A. Introduction
    • IV-B. Que doit-on voir sur la première page ?
    • IV-C. Schématisation de la gestion des emprunts
  • V. Construction de la page d'accueil
    • V-A. Avant de commencer
    • V-B. Les balises
    • V-C. La structure de la page d'accueil
    • V-D. La page « index.php »
      • V-D-1. Structure et fonctionnement
      • V-D-2. Les autres pages
  • VI. Autres fichiers utiles
    • VI-A. Connexion à la base de données
    • VI-B. Les fonctions
  • VII. Répertoire de la « Maîtresse »
  • VIII. Application Web
  • IX. Remerciements

Nous allons créer notre première application Web, sur le langage PHP avec une base de données MySQL.

Cette application sera créée pour mes propres besoins, donc il peut y avoir des variantes, des solutions plus optimales, etc. mais pour ma part, je vais vous montrer les grands principes, à chacun par la suite de faire évoluer cette application en fonction de ses besoins et de ses connaissances.

I. Introduction

L'écriture de cet article m'a paru nécessaire, car n'ayant aucune formation en informatique à par une initiation au Basic, je ne savais pas comment m'y prendre pour créer une application Web simple.

Cela faisait suite à un problème auquel, un jour, je me suis confronté. Je devais développer une application qui puisse fonctionner à la fois sur plusieurs postes et surtout qu'elle soit gratuite. En passant un peu de temps sur Internet, je découvris qu'il était possible de le faire par des applications de type Web. Et « cerise sur le gâteau », tous les outils étaient gratuits, que ce soit les programmes à installer sur un poste (Apache, PHP et MySQL), ou les outils permettant de faire le développement. L'accessibilité par les autres postes était des plus simples car il ne nécessitait l'installation d'aucun programme sur les autres postes, puisque qu'avec juste un navigateur Internet cela fonctionnait.

II. Installation de l'environnement de travail

L'environnement que je vais choisir est un environnement Apache, PHP et MySQL fonctionnant sous Windows.

Il existe deux façons d'installer l'environnement de travail. La première est d'installer chaque élément sur le poste de travail. Mais dans cet article, je vais plutôt partir sur une installation « tout en un ».

Les solutions « tout en un » sont nombreuses, quelques-unes sont visibles sur le lien suivant :

http://php.developpez.com/telecharger/index/categorie/210/Outils-pour-PHP : nginx, EasyPHP, MAMP, WAMP Server, XAMPP et Zend Server

Et il en existe d'autres, en voici quelques-unes :

  • fvpat Webdev Server (fvpatwds) : http://sourceforge.net/projects/fvpatwds/ ;
  • VertrigoServ : http://vertrigo.sourceforge.net/ ;
  • Mov'Amp : http://www.clubic.com/telecharger-fiche32659-mov-amp.html ;

Quasiment toutes ces applications sont portables avec chacune ses caractéristiques et son lot d'applications qui va avec. Mov'Amp est une vieille version, l'avantage de cette version est quelle ne fait que ~24 Mo une fois décompressée, elle peut être utilisée pour faire des applications simples et sur des clefs de faible contenance .

Dans mon cas je vais utiliser EasyPHP, la dernière version est la 12.1 :

Image non disponible

L'installation nécessite ~180 Mo d'espace disque.

Lancer le programme :

Image non disponible
Image non disponible
Image non disponible
Image non disponible
Image non disponible
Image non disponible
Image non disponible
Image non disponible
Image non disponible

Une fois l'application ouverte, l'on aperçoit dans la barre de tâche l'icône suivante : Image non disponible

Un clic droit sur l'icône permet de voir toutes les commandes :

Image non disponible

Explorer : ouvrira le répertoire qui contiendra tous les fichiers de nos pages Web.

Administration : ouvrira une page Web contenant tous les paramétrages, c'est par ce lien que l'on ira travailler sur la base de données.

Web local : ouvrira la page Web du site que nous aurons créé.

III. Construction de la base de données

III-A. Concevoir la base de données

Pour concevoir une base de données correcte, il faut au préalable savoir ce que l'on en attend. Pour cela rien de mieux que de faire un schéma de ce que l'on souhaite faire et obtenir.

Dans mon exemple, je vais prendre le cas de la gestion d'une bibliothèque scolaire pour des élèves de CP.

Les contraintes peuvent être d'ordre technique et fonctionnelle :

  • Technique : de quoi j'ai besoin pour que le site Web fonctionne comme je le souhaite, tout en respectant une certaine logique, etc.
  • Fonctionnelle : par qui va-t-elle être utilisée ? Quels sont les besoins qui me sont réellement nécessaires ? Etc.

Il faut pour cela bien questionner le demandeur et bien lui faire reformuler sa demande.

Voici une liste non exhaustive des besoins qui m'ont été demandés :

Question | Type | Besoin | Contraintes
Qui devra utiliser l'application ? | F | La maîtresse. |
 |  | Les enfants. | En arrivant au CP les enfants ne savent pas lire, il faudra prévoir quelque chose pour qu'ils y arrivent : peut-être avec des images par exemple.
De quelles fonctionnalités j'aurai besoin ? | F | Saisir un emprunt de livre. | Tous.
 |  | Saisir un retour de livre. | Tous.
 |  | Pouvoir récupérer la liste des livres qui sont empruntés. | Maîtresse.
 |  | Pouvoir récupérer la liste des livres qu'un élève aura empruntés.
 |  | Saisir un nouveau livre.
 |  | Saisir un nouvel élève.
 |  | Pouvoir récupérer la liste des élèves pour un livre donné.
Quelle info sur l'élève ? | T | Prénom, Nom (car il peut y avoir deux prénoms identiques) et dans mon cas l'image de l'enfant (car certains sont fâchés avec leur prénom). |
Quelle info sur le livre ? | T | Titre, image. | Dans mon cas, je vais aussi prévoir des dates d'emprunt et de retour car certains livres sont empruntés à la bibliothèque municipale. Et je vais aussi rajouter un état (emprunté, disponible et indisponible).
Quelle info sur l'emprunt ? | T | Élève, livre, date d'emprunt, date prévue de retour, date de retour. |

Et suivant les besoins de chacun, il est possible de rajouter d'autres critères ou d'autres tables...

Donc voici après analyse à quoi pourrait ressembler les différentes tables :

Image non disponible

Dans les tables : élève et livre, je crée un index avec numérotation automatique, et je récupérerai ces index dans la table des emprunts. Je rajoute aussi le fait que certains champs ne pourront pas être nul.

Exemple de la table livre :

Image non disponible

III-B. Création de la base de données

Pour créer la base de données, je vais lancer PHPMyAdmin :

Image non disponible

Sur la page Web qui apparaît, il faudra cliquer sur PHPMyAdmin :

Image non disponible

Ensuite, on arrive sur le programme lui-même :

Image non disponible

Pour créer la base de données, il y a deux possibilités :

  • soit manuellement, en créant la base et les différentes tables qui la composent ;
  • soit en important les données qui sont contenues dans un fichier, c'est cette opération que nous allons effectuer (je fournirai les fichiers nécessaires).

III-B-1. Manuellement

Dans certaines applications, les tables suivantes existent, il ne faut surtout pas les supprimer :

« information_schema » et « mysql ».

Pour créer les tables manuellement, il faut commencer par créer la base :

Image non disponible

Il faut saisir le nom de la base de données, dans l'emplacement réservé « Créer une base ».

Image non disponible

Une fois que la base est créée, il suffit de créer les différentes tables, pour cela il faut le nom de la table et le nombre de champ…

Image non disponible

III-B-2. Importer un fichier

Dans ce cas, cliquer sur « Importer » et l'écran suivant apparaît :

Image non disponible

Sélectionner « Emplacement du fichier texte » et cliquer sur « Parcourir ».

Télécharger le fichier « creation_table.sql », et ensuite cliquer sur « Exécuter », le masque suivant apparaîtra :

Image non disponible

On voit sur la gauche que les trois tables ont été créées.

Faire la même opération avec le fichier « donnees_table.sql », ensuite il suffit de cliquer sur la table « bibli_eleve » ou « bibli_livre » pour voir que les données ont été insérées dans la table.

IV. Construction de l'application Web

IV-A. Introduction

Comme nous l'avons vu dans les détails des contraintes, l'application va se scinder en deux parties :

  • une partie qui servira à tout le monde (élèves et maîtresse) pour emprunter et rendre un livre ;
  • une autre qui servira à la maîtresse pour faire le suivi des emprunts, etc.

Pour obtenir cela nous allons mettre sur la racine du site la partie commune à tous et on va créer un répertoire qui contiendra le complément pour la maîtresse. Nous nommerons ce répertoire « maitresse » (éviter de mettre des accents, des espaces, etc.).

Cela donne le résultat suivant :

Image non disponible

Avant de nous lancer dans la construction des pages du site, nous allons définir ce qui devra apparaître sur celles-ci en commençant pas la partie commune, nous ferons la partie « maitresse » sur le même principe.

IV-B. Que doit-on voir sur la première page ?

Le titre | Qui est généralement le nom de l'application
Les différentes commandes | Dans notre cas nous aurons besoin de commandes pour la gestion des emprunts (« Emprunt » et « Retour » des livres) et d'une commande qui nous permettra de revenir au départ.

Donc la première page (page d'accueil) pourrait avoir la forme suivante :

Image non disponible

IV-C. Schématisation de la gestion des emprunts

Maintenant nous allons décrire les différentes étapes qui composent l'emprunt d'un livre ou le retour d'un livre, voici le résultat :

Image non disponible

V. Construction de la page d'accueil

V-A. Avant de commencer

Je vais vous expliquer comment j'ai procédé pour créer l'application Web. Elle n'est certes pas la meilleure, mais elle fonctionne correctement et elle ne fait pas appel à de grandes notions.

Il y a deux types de technologies que nous allons utiliser :
- Statique : basés sur HTML (mise en forme de la structure de la page)et CSS (mise en forme de la page)
- Dynamique : basé sur PHP (communique avec le serveur et qui permet de faire un échange avec ce serveur)

Je vais créer deux types de fichiers, un fichier « style.css » (feuille de style : qui me permettra par la suite de modifier tout le site d'un seul coup) et des fichiers *.php (je vais éviter de créer des fichiers *.html pour garder une certaine clarté).

Avant de commencer, je vais aussi créer différents répertoires pour une meilleure clarté : un répertoire « images » (qui contiendra toutes les images du site), un répertoire « eleve » (qui contiendra les photos des élèves) et un répertoire « livre » (qui contiendra les images des livres). Et on peut en créer autant que l'on veut suivant les besoins.

Maintenant sous la racine du site j'ai quatre répertoires : maitresse, images, eleve et livre.

V-B. Les balises

La construction d'une page Web passe obligatoirement par l'utilisation de balises. Les balises par paire : ont une balise qui ouvre « <balise...> » et une autre qui ferme « </balise> ». Les balises seules : elles s'ouvrent et se ferment dans la même commande « <balise … /> ».

Et voici les principales que je vais utiliser dans les pages que je vais créer :

<html> « 1re ligne. dernière ligne » </html> : englobera toute la page, on l'ouvre à la première ligne et on la ferme à la dernière.

<head> « différentes informations » </head> : contient des éléments sur différentes informations.

<title> « nom de ma page Web » </titre> : permet de donner un nom à la page Web, c'est le nom qui apparaîtra dans le navigateur.

<meta /> : contient des informations sur la page.

<link /> : permet d'indiquer au navigateur que l'on appelle une autre ressource (par exemple les feuilles de styles)

<body> « corps de la page » </body> : permet de délimiter le contenu de la page.

<?php...?> : est identique à la balise html mais c'est un autre langage. La différence « en gros » est que html est utilisé pour des sites statiques et php est utilisé pour des sites dynamiques.

<table> « tableau » </table> : permet d'insérer un tableau.

<tr> « colonne » </tr> : permet d'insérer une ligne dans un tableau.

<td> « ligne » </td> : permet d'insérer une colonne dans un tableau.

<img « image » /> : permet d'insérer une image.

<a /> : permet d'associer une action (peut être utilisé avec du texte ou une image).

<div> </div> : permet d'ajouter un bloc nous l'utiliserons associer avec la feuille de style.

V-C. La structure de la page d'accueil

Comme nous l'avons déjà vu la « page d'accueil » aura la forme suivante, et elle se nommera « index.php » (la première page doit toujours s'appeler « default » ou « index ») :

Image non disponible

Titre : que je nommerai Bandeau contiendra le titre de l'application (sous forme d'image), le retour à la page d'accueil (sous forme d'image) et je vais aussi rajouter la date du jour.

Commandes : que je nommerai Contenu contiendra tout le reste de l'application de l'emprunt au retour des livres.

Dans mon cas je ne vais travailler qu'avec la page « index.php », donc la partie Contenu contiendra tour à tour les différentes pages. Nous verrons plus loin comment cela est possible.

Pour faire tenir ensemble le bandeau et le contenu, je vais travailler avec les feuilles de styles.

Je crée donc une feuille de styles que je nomme « styles.css » :

styles.css
div {
    text-align: left;
    vertical-align: top;
    }
    div#bandeau {
    width: 1050px;
    height: 94px;
    background: url('images/bandeau.png') top left no-repeat;
    text-align: right;
    vertical-align: bottom;
    font: 10px verdana, sans-serif, "Times New Roman";
    color: White;
    }
    div#contenu {
    float:left;
    width:1050px;
    }

Et je crée ensuite la page « index.php », qui ressemblera à ceci dans un premier temps :

index.php
...
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <link rel="stylesheet" type="text/css" href="styles.css" />
    <title>Bibliothèque</title>
    </head>
    <body>
    <!-- Création d'un tableau pour positionner le corps au milieu de la page -->
    <table border="0" cellpadding="0" cellspacing="0" >
    <tr width="100%" align="center">
    <!-- Création de la marge de gauche -->
    <td width="20%">
    </td>
    <!-- Création du contenu de la page -->
    <td align="center">
    <!-- Introduction de l'heure dans le bandeau -->
    <div id="bandeau">
    <?php echo "Le ".jourdate_fr(date('Y-m-d-w'));?>
    <br>&nbsp;<br>
    <a href="?"><img border="0" src="images/accueil.png"></a>&nbsp;
    </div>
    <!-- Corps de la page -->
    <div id="contenu">
    ...
    </div>
    </td>
    <!-- Création de la marge de droite-->
    <td width="20%">
    </td>
    </tr>
    </table>
    </body>
    </html>

Dans le bandeau, j'insère la date à l'aide d'une fonction, on verra l'utilisation des fonctions plus tard.

V-D. La page « index.php »

V-D-1. Structure et fonctionnement

Comme je l'ai dit précédemment, je ne vais travailler qu'avec la page « index.php ». Ce qui implique que je vais devoir tout mettre dans la page, mais dans mon cas pour en faciliter la compréhension, je ferai différentes pages que j'insérerai dans la page, je vais pour cela utiliser la commande PHP « include ».

Si l'on reprend le schéma d'emprunt et retour de livre, il va nous falloir les pages suivantes :

Commande | Besoin | Page
Accueil | Une page qui contiendra les boutons : Emprunt et Retour. | accueil.php
Emprunt | Une page pour lister les élèves. | eleve1.php
 | Une page pour lister les livres. | livre1.php
 | Une page pour indiquer que l'emprunt est fait. | emprunt1.php
Retour | Une page pour lister les élèves. | eleve2.php
 | Une page pour lister les livres. | livre2.php
 | Une page pour indiquer que le retour a été fait. | retour2.php

Nous allons aussi travailler avec des variables, je vais utiliser la fonction GET pour cela, elle permet de faire passer les variables d'une page à l'autre dans la barre d'adresse, les variables seront précédées du terme « $ » (exemple : $var).

Besoin | Variable
Nom de l'élève. | $el
Titre du livre. | $li
Choix pour indiquer si je suis sur l'emprunt ou le retour. La valeur 1 sera pour l'emprunt et la valeur 2 pour le retour. | $id

Pour tester l'existence des variables je vais utiliser la fonction suivante :

code.php
if (empty($_GET['id'])) {
    $id = 0;
    } else {
    $id = $_GET['id'];
    }

Pour inclure les différentes pages je vais utiliser la fonction suivante :

code.php
switch($id) {
    case 0:
    include 'page1.php';
    break;
    case 1:
    include 'page2.php';
    break;
    default:
    include 'page3.php';
    break;
    }

V-D-2. Les autres pages

Les différents fichiers contiennent toutes les informations nécessaires pour comprendre la création des différentes pages.

VI. Autres fichiers utiles

VI-A. Connexion à la base de données

Dans la construction d'un site dynamique, on interagit avec des bases de données, je fais créer un fichier « connection.php » qui contiendra les paramètres de connexion à la base de données.

VI-B. Les fonctions

Je vais aussi créer un fichier « fonction.php » qui contiendra différentes fonctions. Une fonction est une suite d'actions dont le résultat est une donnée. En général, on crée des fonctions quand on sait que l'on s'en servira plus tard ou dans plusieurs pages. Dans notre cas, je vais surtout créer des fonctions sur le formatage des dates pour les faire apparaître en français.

Exemple de fonction donnant la date sous la forme suivante 18/05/2011 :

function.php
function date_fr($val_date) {
    list($annee, $mois, $jour) = explode('-', $val_date);
    $datefr = $jour.'/'.$mois.'/'.$annee;
    return $datefr;
    }

La première ligne est la valeur qui est envoyée à la fonction, la deuxième décompose la date (ici elle est au format anglais : aaaa-mm-jj), la troisième met la date au format français, et la quatrième renvoie la date modifiée.

VII. Répertoire de la « Maîtresse »

Cette partie sera construite de la même façon que l'autre. Il va donc falloir les pages suivantes en plus de la page « index.php » :

Commande | Besoin | Page
Accueil | Une page qui contiendra toutes les commandes. | accueil.php
Liste des livres empruntés par la classe | Une page pour lister les emprunts. | admin1.php
Liste des livres empruntés par un élève | Une page pour lister les élèves. | eleve2.php
 | Une page pour lister les livres empruntés par l'élève. | admin2.php
Liste des élèves qui ont emprunté un livre | Une page pour lister les livres. | livre3.php
 | Une page pour lister les élèves qui ont emprunté par le livre. | admin3.php
Liste des livres qui ne sont pas à l'école | Une page pour lister les livres qui ne sont pas à l'école. | admin4.php
Ajouter un élève | Une page pour saisir les informations nécessaires. | eleve10.php
 | Une page pour indiquer que l'enregistrement a été fait. | admin10.php
Supprimer un élève | Une page pour lister les élèves. | eleve11.php
 | Une page pour indiquer que la suppression a été faite. | admin11.php
Ajouter un livre | Une page pour saisir les informations nécessaires. | livre12.php
 | Une page pour indiquer que l'enregistrement a été fait. | admin12.php
Supprimer un livre | Une page pour lister les livres. | eleve13.php
 | Une page pour indiquer que la suppression a été faite. | admin13.php

Nous allons utiliser les mêmes variables :

Besoin | Variable
Nom de l'élève | $el
Titre du livre | $li
Choix pour indiquer sur quelle commande je suis | $id

Pour ce qui est des images, je me servirai des images précédentes.

VIII. Application Web

Voici le fichier zip contenant toute l'application : Première application Web


Concevez votre site web avec PHP et MySQL

 s
Bienvenue sur OpenClassrooms ! En poursuivant votre navigation, vous acceptez l'utilisation de cookies. En savoir plus ok
Comment Matrix et Terminator peuvent-ils vous aider à comprendre ce qu'est le Big Data ? Réponse ici !
OpenClassrooms.com
  • Cours
  • Parcours
  • Forums
  • Livres
  • Études
  • Premium
S'inscrire Se connecter

Inscription

  • S'inscrire avec Facebook S'inscrire avec Google

  • Vous recevrez votre mot de passe sur cette adresse email.

Connexion

  • Se connecter avec Facebook Se connecter avec Google
  • Mot de passe oublié

Pas encore membre ? Inscrivez-vous gratuitement en 30s.

Français
  • Deutsch
  • English
  • Español
  • 中文
  • Aider à traduire
Fil d'Ariane
  • Accueil
  • Cours
  • Concevez votre site web avec PHP et MySQL
Publicité

Concevez votre site web avec PHP et MySQL

 Mis à jour le vendredi 14 août 2015
  • 30 heures
  • Facile
Licence

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Introduction du cours

Choisissez votre offre pour suivre le cours :

  • Free

    0 € à vie

    Suivez le cours sur 30 heures et faites les exercices

    S'inscrire au cours
    Comment ça marche ?

    Choisissez votre rythme

    Pour voir cette vidéo, vous devez simplement vous inscrire au cours.

    Pour accéder à cet exercice, vous devez simplement vous inscrire au cours.

    Réservé aux membres Premium

    Inscription Premium Inscription classique
  • Premium

    20 € par mois

    Suivez le cours à votre rythme et obtenez le certificat de réussite

    En savoir plus
    Inscription Premium
  • Premium Plus

    300 € par mois

    Faites-vous accompagner par un mentor dédié en visioconférence tout au long du cours

    En savoir plus
    Inscription Premium Plus
https://vimeo.com/87516214

Blogs, réseaux sociaux, pages d'accueil personnalisables... Depuis quelques années, les sites web ont gagné en fonctionnalités et sont devenus dans le même temps de plus en plus complexes.

Que le temps de la "page web perso" est loin ! Il y a une époque où l'on pouvait se contenter de créer un site basique. Un peu de texte, quelques images : hop là, notre site perso était prêt. :-°
Aujourd'hui, c'est différent : il faut que ça bouge ! On s'attend à ce qu'un site soit régulièrement mis à jour : on veut voir des actualités sur la page d'accueil, on veut pouvoir les commenter, discuter sur des forums, bref, participer à la vie du site.

Le langage PHP a justement été conçu pour créer des sites "vivants" (on parle de sites dynamiques). Et si vous voulez apprendre à créer vous aussi des sites web dynamiques, c'est votre jour de chance : vous êtes sur un cours pour vrais débutants en PHP !
L'essentiel, c'est de lire en entier les chapitres dans l'ordre. Après, ça passe tout seul et vous vous étonnerez bientôt de ce que vous êtes capable de faire ! :)

Pour utiliser PHP, il faut connaître au préalable les langages HTML et CSS.
Comment ça, ces langages ne vous disent rien ? Vous ne savez même pas ce qu'est un "langage" ? Il faut donc que vous lisiez d'abord mon cours de création de site web en HTML5 et CSS3 avant de revenir ici !

Nos cours vous ont permis de travailler dans les métiers du Web ? Aidez-nous à faire reconnaître nos formations et délivrer un diplôme reconnu par l’Etat. Grâce à vos témoignages, nous pourrons constituer notre dossier, et délivrer le diplôme rétroactivement jusqu’à 3 ans à tous ceux concernés. Comment ? En répondant à ce questionnaire ! ;)

  • #

  • Partie 1 - Les bases de PHP

     Devenez Premium

    Cette partie supplémentaire, facultative pour réussir le cours, vous expliquera tout sur les effets avancés en CSS.

    1. Introduction à PHP

    2. Préparer son ordinateur

    3. Premiers pas avec PHP

    4. Inclure des portions de page

    5. Les variables

    6. Les conditions

    7. Les boucles

    8. Les fonctions

    9. Les tableaux

    • Quiz

  • Partie 2 - Transmettre des données de page en page

     Devenez Premium

    Cette partie supplémentaire, facultative pour réussir le cours, vous expliquera tout sur les effets avancés en CSS.

    1. Transmettre des données avec l'URL

    2. Transmettre des données avec les formulaires

    3. TP : page protégée par mot de passe

    4. Variables superglobales, sessions et cookies

    5. Lire et écrire dans un fichier

    • Quiz

  • Partie 3 - Stocker des informations dans une base de données

     Devenez Premium

    Cette partie supplémentaire, facultative pour réussir le cours, vous expliquera tout sur les effets avancés en CSS.

    1. Présentation des bases de données

    2. phpMyAdmin

    3. Lire des données

    4. Écrire des données

    5. TP : un mini-chat

    6. Les fonctions SQL

    7. Les dates en SQL

    8. TP : un blog avec des commentaires

    9. Les jointures entre tables

    • Activité : Le mini-chat amélioré
    • Quiz

  • Partie 4 - Utilisation avancée de PHP

     Devenez Premium

    Cette partie supplémentaire, facultative pour réussir le cours, vous expliquera tout sur les effets avancés en CSS.

    1. Créer des images en PHP

    2. Les expressions régulières (partie 1/2)

    3. Les expressions régulières (partie 2/2)

    4. La programmation orientée objet

    5. Organiser son code selon l'architecture MVC

    6. TP : créer un espace membres

    • Quiz

  • Partie 5 - Annexes

     Devenez Premium

    Cette partie supplémentaire, facultative pour réussir le cours, vous expliquera tout sur les effets avancés en CSS.

    1. Envoyez votre site sur le web

    2. Codez proprement

    3. Utilisez la documentation PHP !

    4. Au secours ! Mon script plante !

    5. Protéger un dossier avec un .htaccess

    6. Mémento des expressions régulières

  • Certificat de réussite (voir un exemple)

Waouh !

Très heureux de voir que nos cours vous plaisent, déjà 3 pages lues aujourd'hui ! Vous pouvez continuer la lecture de nos cours en devenant un Roomie, un membre de la communauté d'OpenClassrooms. C'est gratuit !

Vous pourrez aussi suivre votre avancement dans le cours, faire les exercices et discuter avec les autres Roomies.

S'inscrire Se connecter
  • Thématiques du cours : Base de données MySQL PHP Web
Vous ne pouvez pas encore obtenir de certificat de réussite à l'issue de ce cours.
Accéder au forum

Avec le soutien de

Vous souhaitez aller plus loin ? Découvrez les formations proposées par l’ESGI

5 000 étudiants

L'auteur

Mathieu Nebra

Entrepreneur à plein temps, auteur à plein temps et co-fondateur d'OpenClassrooms :o)

Découvrez aussi ce cours en...

  • Premium
    eBook
  • Livre papier
  • Premium
    PDF
  • Premium
    Vidéo
Pour télécharger toutes les vidéos de ce cours, devenez Premium
Exemple de certificat de réussite

Le téléchargement des vidéos de nos cours est accessible pour les membres Premium. Vous pouvez toutefois les visionner en streaming gratuitement.

Devenez Premium

OpenClassrooms

  • Qui sommes-nous ?
  • Fonctionnement de nos cours
  • Recrutement
  • Nous contacter

Professionnels

  • Affiliation
  • Entreprises
  • Universités et écoles

Participez

  • Créer un cours
  • CourseLab
  • Aider à traduire
  • Conditions Générales d'Utilisation

Suivez-nous

  • Le blog OpenClassrooms


Introduction au langage SQL

<div class="disclaimer disclaimer--error"> Veuillez utiliser un <a href='http://browsehappy.com/'>navigateur internet moderne</a> avec JavaScript activé pour naviguer sur OpenClassrooms.com </div>
Bienvenue sur OpenClassrooms ! En poursuivant votre navigation, vous acceptez l'utilisation de cookies. En savoir plus ok
Comment Matrix et Terminator peuvent-ils vous aider à comprendre ce qu'est le Big Data ? Réponse ici !
OpenClassrooms.com
  • Cours
  • Parcours
  • Forums
  • Livres
  • Études
  • Premium
S'inscrire Se connecter

Inscription

  • S'inscrire avec Facebook S'inscrire avec Google

  • Vous recevrez votre mot de passe sur cette adresse email.

Connexion

  • Se connecter avec Facebook Se connecter avec Google
  • Mot de passe oublié

Pas encore membre ? Inscrivez-vous gratuitement en 30s.

Français
  • Deutsch
  • English
  • Español
  • 中文
  • Aider à traduire
Fil d'Ariane
  • Accueil
  • Cours
  • Apprenez à programmer en VB .NET
  • Introduction au langage SQL
Publicité

Apprenez à programmer en VB .NET

 Mis à jour le lundi 24 juin 2013
  • 20 heures
  • Facile
Licence

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !
Les bases de données
La théorie et les bases Le côté visuel de VB La programmation orientée... Les bases de données La communication par le réseau Annexes
Devenez
Premium

Introduction au langage SQL

Dans l'introduction aux bases de données, je vous ai parlé d'un certain langage SQL. Nous allons faire une première approche de ce fameux langage afin de nous familiariser avec ce dernier. Le langage SQL (Structured Query Language) permet de dialoguer avec la base de données. Je vous ai aussi signalé qu'il existait différentes versions du langage SQL en fonction de la base de données utilisée. Mais SQL dispose également d'une syntaxe élémentaire, normalisée et indépendante de toute base de données.

Rechercher des informations

Avant toute chose, sachez que ce chapitre est loin d'être exhaustif et qu'il n'enseignera que rapidement les fondamentaux du langage SQL. Si vous voulez aller plus loin, je vous conseille l'excellent cours consacré à MySQL de Taguan.

Grâce au langage SQL, vous pourrez rechercher certains enregistrements afin de les extraire, dans l'ordre dans lequel vous souhaitez les extraire. Par exemple, vous pouvez créer une instruction SQL qui extrait les informations de plusieurs tables simultanément, ou alors un enregistrement spécifique.

Pour ce faire, nous allons utiliser l'instruction SELECT : cette instruction est utilisée pour renvoyer des champs spécifiques d'une ou de plusieurs tables de la base de données.

Par exemple, cette instruction…

SELECT Artiste, Titre FROM Musiques

… renverra la liste des Artistes et des Titres de tous les enregistrements de la table Musiques.

Vous pouvez aussi utiliser le symbole « * » à la place de la liste des champs pour lesquels vous souhaitez la valeur :

SELECT * FROM Musiques

Ainsi, cette requête vous renverra un tableau contenant toutes les informations sur toutes les musiques présentes dans la table.

Nous pouvons aussi limiter le nombre d'enregistrements sélectionnés. Nous allons pour cela utiliser un ou plusieurs champs qui vont permettre de filtrer la recherche. Nous allons maintenant voir certaines clauses disponibles qui permettront ce filtrage.

La clause WHERE

La clause WHERE va permettre de spécifier les conditions : seule une partie des enregistrements seront concernés.
Prenons un exemple : nous voulons retrouver les informations concernant le titre Nothing Else Matters.

SELECT * FROM Musiques WHERE Titre = 'Nothing Else Matters'

Vous avez sûrement remarqué les guillemets : lorsque l'on utilise la syntaxe de la clause WHERE, les guillemets servent de délimiteurs de chaînes de caractères.

On peut traduire la commande WHERE par « où » en français, donc l'instruction WHERE Titre = 'Nothing Else Matters' peut se traduire par « où le champ Titre est égal à Nothing Else Matters ».

Il existe en SQL des conditions associées à la clause WHERE tout comme en VB .NET :

Condition | Valeur
= | Égal
!= | Différent
< | Inférieur
> | Supérieur
<= | Inférieur ou égal
>= | Supérieur ou égal
AND ou && | Et
OR ou || | Ou

Par exemple :

SELECT * FROM Musiques WHERE Titre != 'Nothing Else Matters'

Ce code affichera toutes les informations dont le champ est différent de Nothing Else Matters. Nous allons maintenant découvrir d'autres conditions de la clause WHERE…

La clause WHERE… IN

Cette clause permet de renvoyer tous les enregistrements qui répondent à une liste de critères.
Par exemple, nous pouvons rechercher les artistes nés en France :

SELECT * FROM Artistes WHERE Pays IN ('France')

Clause WHERE… BETWEEN

Nous pouvons sélectionner quelques enregistrements se trouvant entre deux critères de recherche spécifiés. La requête suivante permet de récupérer la liste des albums de l'année 2011 :

SELECT * FROM Albums WHERE DateSortieAlbum BETWEEN '01/01/11' AND '31/01/11'

Vous remarquerez l'utilisation de AND pour dire « et » : « Entre… et… ».

La clause WHERE… LIKE

Cette clause permet de renvoyer tous les enregistrements pour lesquels il existe une condition particulière dans un champ donné. Ci-dessous la commande qui permet de rechercher tous les artistes dont le nom commence par un « s » :

SELECT * FROM Artistes WHERE Nom LIKE 's%'

Le symbole % est utilisé pour remplacer une séquence de caractères quelconque.

Et enfin, une dernière clause un peu différente de celles vues jusqu'à maintenant…

La clause ORDER BY

La clause ORDER BY permet de renvoyer les enregistrements dans un ordre donné. Il en existe deux :

Option | Traduction
ASC | Ordre croissant
DESC | Ordre décroissant

Il peut y avoir plusieurs champs spécifiés comme ordre de tri. Ils sont analysés de la gauche vers la droite.

SELECT * FROM Artistes ORDER BY Nom DESC, Prenom ASC

Ce code va retourner les artistes triés par ordre décroissant sur le nom, et en cas d'égalité, par ordre croissant sur le prénom.

Ajouter des informations

Grâce au SQL, nous pouvons aussi ajouter des informations dans une table avec la commande INSERT INTO. Pour ce faire, il faut indiquer la table dans laquelle on souhaite intégrer une ligne ainsi que la liste des champs pour lesquels on spécifie une valeur, et enfin la liste des valeurs correspondantes. Voyons cela avec un petit exemple :

INSERT INTO Musiques (Numero, Artiste, Titre, Album) VALUES (5, 'Blood Brothers', 'Iron Maiden', 'Brave New World')

On utilise INSERT INTO en spécifiant à côté dans quelle table nous allons ajouter des informations (ici la table Musiques) ; ensuite, entre parenthèses, on déclare la liste des champs dans lesquels nous allons entrer des valeurs. Le mot-clé VALUES va permettre d'entrer les données entre parenthèses, on entre les informations dans le même ordre que celles d'avant VALUES.

Il y a un moyen de raccourcir le code précédent :

INSERT INTO Musiques VALUES (5, 'Blood Brothers', 'Iron Maiden', 'Brave New World')

En effet, lors du dernier code j'avais spécifié les champs dans lesquels je voulais entrer des valeurs, mais j'avais spécifié tous les champs de la table Musiques ! Donc au lieu de mettre tous les champs qui composent cette table, j'ai le droit de déclarer uniquement le nom de la table dans laquelle je souhaite ajouter des informations.

Si vous n'indiquez pas de valeur pour un certain champ dans une table, celui-ci doit prendre la valeur NULL par défaut : l'instruction INSERT exige que tous les champs soient remplis. Donc vous devez mettre NULL si vous n'avez aucune information à placer dans un champ.

Comme ceci :

INSERT INTO Musiques VALUES (5, 'Blood Brothers', NULL, NULL)

La mise à jour d'informations

On peut modifier certains champs d'enregistrements existants grâce au mot-clé UPDATE : cette instruction permet de mettre à jour plusieurs champs de plusieurs enregistrements d'une table, à partir des expressions qui lui sont fournies.

Pour ce faire, vous devez indiquer le nom de la table à mettre à jour ainsi que les nouvelles valeurs à affecter aux champs.

Le mot-clé SET va permettre cette affectation.

Si l'on veut que les modifications ne se fassent que sur un ensemble limité d'enregistrements, on doit utiliser la clause WHERE. Si aucune clause n'est indiquée, la modification se fera sur tous les enregistrements de la table !

Par exemple, pour modifier le titre d'une chanson , on utilise le code suivant :

UPDATE Musique SET Titre = 'nouveau titre'  WHERE Numero = 4

La modification du titre de la chanson ne portera ici que sur l'enregistrement qui porte le numéro 4 ! Tandis que si l'on souhaite que la mise à jour soit globale, inutile de mettre de clause.

Supprimer des informations

Il se peut que l'on soit amené à supprimer un ou plusieurs enregistrements d'une table, il existe pour cela l'instruction DELETE FROM. On doit alors fournir au minimum le nom de la table sur laquelle va s'effectuer la suppression.

Si il y a uniquement le nom de la table de renseignée, alors tous les enregistrements de cette table seront supprimés.

Pour limiter la suppression, on réutilise la clause WHERE.
La commande suivante efface tous les enregistrements de la table Musiques :

DELETE FROM Musiques

Tandis que celle-ci ne supprime qu'un seul enregistrement :

DELETE FROM Musiques WHERE Numero = 2

  • SELECT permet d'effectuer des recherches et de récupérer des données.

  • UPDATE met à jour des données.

  • INSERT ajoute des données.

  • DELETE supprime des données.

  • #
J'ai terminé ce chapitre et je passe au suivant
Introduction sur les bases de données Création et remplissage de la BDD

Waouh !

Très heureux de voir que nos cours vous plaisent, déjà 3 pages lues aujourd'hui ! Vous pouvez continuer la lecture de nos cours en devenant un Roomie, un membre de la communauté d'OpenClassrooms. C'est gratuit !

Vous pourrez aussi suivre votre avancement dans le cours, faire les exercices et discuter avec les autres Roomies.

S'inscrire Se connecter
S'inscrire au cours
Comment ça marche ?

Choisissez votre rythme

Pour voir cette vidéo, vous devez simplement vous inscrire au cours.

Pour accéder à cet exercice, vous devez simplement vous inscrire au cours.

Réservé aux membres Premium

Inscription Premium Inscription classique
Les bases de données
  1. Introduction sur les bases de données
  2. Introduction au langage SQL
  3. Création et remplissage de la BDD
  4. La communication VB .NET - BDD
  5. Le DataSet à la loupe
  6. L'utilisation graphique : le DataGrid
  7. TP : ZBiblio V2

    Créé par

    OpenClassrooms, 1ère plateforme e-Education en Europe

    L'auteur

    Thomas Martinet

    Découvrez aussi ce cours en...

    • Premium
      eBook
    • Livre papier
    • Premium
      PDF
    Exemple de certificat de réussite

    OpenClassrooms

    • Qui sommes-nous ?
    • Fonctionnement de nos cours
    • Recrutement
    • Nous contacter

    Professionnels

    • Affiliation
    • Entreprises
    • Universités et écoles

    Participez

    • Créer un cours
    • CourseLab
    • Aider à traduire
    • Conditions Générales d'Utilisation

    Suivez-nous

    • Le blog OpenClassrooms


Accueil

Accueil CHM

Normal, dit comme ça,
vous ne risquez pas de comprendre.