relecturedephp5_2011_01_29

156
1 Relecture de PhP 5 avancé 5ième édition http://netbeans.org/kb/trails/php.html Chapitre 1 : Qu’est-ce que Php ? PHP = Personal Home Page PDO : Php Data Object : extension de Php procurant une couche d’abstraction commune à un ensemble de SGBD. Modes de fonctionnement de php : • Fonctionnement couplé avec un serveur Web • Application en ligne de commande • Services web : php permet de consommer et de créer des services web • Applications graphiques : applications standard à l’aide de l’extension graphique gtk téléchargeable sur http://gtk.php.net/ Objets issues d’autres langages : Php permet d’instancier et de manipuler des objets COMs, des classes java et .NET Bibliothèques intégrées : Beaucoup de bibliothèques intégrées existent et permettent la création de pdf, d’images etc ... à la volée, l’envoi et la réception de mails, la connexion et la communication avec d’autres serveurs webs. PEAR et PECL : Modules additionnels de php obtenus à l’aide de PEAR. Outils liés à Php : SPIP ? FPDF : librairie pour tout ce qui traite des pdfs. PhPEDit : Editeur de code php. Symfony : Framework php EasyPhp : Auto-installeur de Apache, Php et SQL Sites sur le web : php.net : LE site PHP PHPFrance.com : nombreux articles sur Php, + un forum très actif + un espace de cours ; PHPDebutant.org : site pour débutants ; Apprendre-PHP.com : bonnes pratiques de programmation, et accent sur la sécurité ; Planète-php.fr : aggrégateur de blogs PhpIndex.com PHPTeam.net : introduction gtk, production de pdfs PHPScripts-fr.net : bibliothèques de scripts ayant fait leurs preuves. 1

Upload: bernard-gourion

Post on 28-Jun-2015

570 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: RelectureDePhp5_2011_01_29

1 Relecture de PhP 5 avancé 5ième édition

http://netbeans.org/kb/trails/php.htmlChapitre 1 : Qu’est-ce que Php ?

PHP = Personal Home PagePDO : Php Data Object : extension de Php procurant une couche d’abstraction commune à un ensemble

de SGBD.Modes de fonctionnement de php :

• Fonctionnement couplé avec un serveur Web

• Application en ligne de commande

• Services web : php permet de consommer et de créer des services web

• Applications graphiques : applications standard à l’aide de l’extension graphique gtk téléchargeable surhttp://gtk.php.net/

Objets issues d’autres langages :Php permet d’instancier et de manipuler des objets COMs, des classes java et .NET

Bibliothèques intégrées :Beaucoup de bibliothèques intégrées existent et permettent la création de pdf, d’images etc ... à la volée,

l’envoi et la réception de mails, la connexion et la communication avec d’autres serveurs webs.

PEAR et PECL :Modules additionnels de php obtenus à l’aide de PEAR.Outils liés à Php :SPIP ?FPDF : librairie pour tout ce qui traite des pdfs.PhPEDit : Editeur de code php.Symfony : Framework phpEasyPhp : Auto-installeur de Apache, Php et SQL

Sites sur le web :

php.net : LE site PHPPHPFrance.com : nombreux articles sur Php, + un forum très actif + un espace de cours ;PHPDebutant.org : site pour débutants ;Apprendre-PHP.com : bonnes pratiques de programmation, et accent sur la sécurité ;Planète-php.fr : aggrégateur de blogsPhpIndex.comPHPTeam.net : introduction gtk, production de pdfsPHPScripts-fr.net : bibliothèques de scripts ayant fait leurs preuves.

1

Page 2: RelectureDePhp5_2011_01_29

2 Chapitre 2 : Installer et configurer

Configuration de php.ini :

2.1 Les directives dans php.ini

• extension_dir = ”Adresse”; ;; spécifie le chemin où se trouve les extensions php.

valeur actuelle : extension_dir = ”C:/Program Files/wamp2/bin/php/php5.3.0/ext/”

• short_open_tag = off ; Tout code Php commence par ”<?php” mais peut aussi commencer par <? sila valeur est à off on ce qui n’est pas recommandé.

• open_basedir =”/Repertoire/limitee” ; si cette option n’est pas commentée, elle est interdit tout accèspar Php aux fichiers et répertoire qui ne se trouvent pas dans /Repertoire/limitee

• max_execution_time = 30 ; spécifie le temps maximum d’exécution d’un script php. Au-delà de cetemps, le script s’arrête et le navigateur renvoie un message d’erreur. Si 0 est mis, le temps d’exécutionest illimité.

• memory_limit = 128M; Mémoire maximale alloué à un script php.

• options à garder à off sauf pour assurer une compatibilité avec des vieux scripts php :

register_globals = off ;

magic_quotes_gpc = off ; directive qui servait à se prémunir des attaques par injection sql.

• include_path = ”.:/php/includes”; directive servant à définir où php va chercher les fichiers appeléspar include. Il est possible de définir plusieurs adresses dans lesquelles Php cherchera successivement lesfichiers recherchés. Par défaut, php cherche dans le répertoir PEAR.

• file_uploads = on ; // autorise le téléchargement de fichiers dans le fichier temporaire du système oudans le fichier spécifié avec la directive upload_max_filesize = 2M

• auto_prepend_file = ”/home/www/configuration.php”; ajoute le fichier spécifié en début de tout scriptphp.

• auto_append_file = ”/home/www/configuration.php”; ajoute le fichier spécifié en toute fin de toutscript php.

Modification de la configuration php en dehors de php.ini

Deux possibilités :

1. dans un fichier php en utilisant la fonction ini_set(directive, valeur de la directive) : change en local pourle script la valeur de la directive ; la fonction ini_get(directive, valeur de la directive) permet de récupérerla valeur de la directive pour la tester si besoin ou autre ; ini_restore(directive, valeur de la directive)permet de rétablir la valeur initiale de la directive ;

2

Page 3: RelectureDePhp5_2011_01_29

2. dans httpd.conf d’Apache, les instructions

php_flag directive on (ou off) ; permet la modification d’une directive à valeur binaire pour un répertoirou un virtual host spécifique

et php_value directive ValeurDeLaDirective permet de faire la même chose mais pour une directive àvaleur textuelle ;

3 Chapitre 3 : Les structures de base

3.1 Les types d’exécution de scripts Php :

• via un serveur web ;

• en ligne de commande en lançant php -q nomDuFichier.php (-q zape demande de ne pas afficher lesen-têtes html) ;

• en programme autonome : ça revient à la méthode ligne de commande en liant le type de fichier .php auprogramme php.exe. L’exécution sera alors lancé dans une fenêtre DOS ;

• en mode embarqué : le php est alors utilisé comme langage de script par une autre application.

3.2 Structure du langage : différences par rapport au C++La structure du langage est très semblable à celle du C++. Seules les différences sont étudiées ici :

Bloc d’instruction php :Tout bloc DOIT commencer par <?php et finir par ?>. Il est possible en utilisant la directive short_open_tag

= on dans php.ini de commencer aussi par <? mais c’est source de pb si on utilise aussi xml dont les blocscommencent eux-aussi par <? ...

Commentaires : c’est comme en C++

structure d’un fichier php :Un fichier php ne contient pas forcément que du php (même si cela peut-être le cas). Il s’insère le plus

souvent dans un document html. Toute la partie du code html est traité par le serveur web tandis que le codephp est d’abord traité par php pour être transformé en html qui sera par le serveur web ensuite.

Déclaration des variables : Les variables sont déclarées comme suit :

$NomDeLaVariable ValeurDeLaVariable;

Le premier caractère après le $ ne peut pas être un chiffre ou un caractère interdit (ex : @ . et -)

Typage des variables : typage faible et dynamique. Le typage d’une variable dépend du type de laValeurDeLaVariable et peut changer suivant les affectations.

3

Page 4: RelectureDePhp5_2011_01_29

Portée des variables : Elles n’existent que dans le bloc <?php ?> et disparaissent dès qu’on en sort (etdonc à l’affichage des pages html produites.

Les variables locales : ce sont celles définies dans un bloc interne au bloc principal <?php ?>. Par exempleles variables déclarées dans le prototype d’une fonction :

function Calcul($var1,$var2)

sont locales.

Les variables globales sont définies au niveau du bloc <?php ?> ont une portée globale et peuvent êtreappelés dans un sous-bloc en utilisant le tableau $GLOBALS des variables globales qui permet d’appeler unevariable globale par son nom comme suit :

$GLOBALS[’NomDeLaVariableGlobaleAppelée’]

On peut aussi déclarer localement une variable comme global ce qui signifie que la variable a déjà été déclaréeen dehors du bloc courant au niveau du bloc <?php ?>. Par exemple :

function CalculPrix($produit){

global $NomCalculateur;utilise($produit, $NomCalculateur);};Dans ce cas, la variable $NomCalculateur a été déclarée ailleurs et l’instruction préfixe global le spécifie

tandis que produit est une variable locale.

Variables dynamiques et nom de variable :Dans la déclaration $var1, la chaine var1 est le nom de la var et $ indique une déclaration de variable dont le

nom est la chaine var1. A une variable est donc associé un chaine comme identifiant. Un nom de variable peutdès lors être dynamique et puisqu’un nom de variable peut lui-même être identifié à partir de la valeur d’uneautre variable. Ainsi $var1 est la valeur de la variable de nom var1. $$var2 est une variable dont le nom est lavaleur de la variable $var2. celle-ci pouvant changer, $$var2 est une variable de nom dynamique.

Un nom de variable peut aussi être issue d’une opération plus complexe en définissant ce nom comme uneaccolade {}. exemple ${”va”.”r1”}; Cette déclaration est celle d’une variable dont le nom est la concaténation dela chaine ”va” et de la chaine ”r1”

Constantes :Les constantes sont déclarées avec l’instructiondefine(”ChaineCorrespondantAuNomDeLaConstante”,”ValeurDeLaConstante”);

Une constante ne peut pas jamais être changés de valeur une fois déclarée.

Type des variables et fonctions d’identification des variables :

Php gère tout seul les types de variables : booléen,entier, virgule flottante et chaine.

Les fonctions suivantes permettent d’identifier le type de la variable :

4

Page 5: RelectureDePhp5_2011_01_29

GetType($var1) : type de la variable var1.

is_string($var1); /// c’est clairis_double($var1);is_float($var1); /// pareil que la précédenteis_int($var1);is_integer($var1);is_bool($var1);is_array($var1);is_null($var1);is_object($var1);

un booléen prend TRUE ou FALSE comme valeur.

ATTENTION une valeur commençant par 0 est un octale, une valeur commençant par 0x est un hexadécimal.

PHP NE RECONNAIT PAS LES ENTIERS NON-SIGNES

Egalité entre nombres à virgules flottantes. Deux nombres à virgule flottante ne sont pas forcément égauxet il faut donc prendre une marge d’erreur dans le cadre de test d’égalité.

Interprétation de variable dans des chaines de caractères. Dans une expression de chaines de caractères, lesappels $var1 renvoi la valeur de la variable var1 dans la chaine et il est même possible en utilisant les accolades{} de faire réference à des valeurs d’un tableau ou une propriete d’un objet. Les accolades servent ainsi à insérerle résultat d’une expression complexe dans une chaine.

L’usage des accolades : {} doivent impérativement encadrer le $ de toute variable y intervenant sauf àvouloir explicitement utiliser le principe des variables dynamiques.

ATTENTION : en dehors d’une chaine, $var1 désigne la variable de nom var1 et non sa valeur.Insertion de caractère spéciaux avec le caractère d’échappement : Comme en C++, le caractère \ permet

d’insérer des caractères normalement interdit tel quel dans une expression car correspondant à des caractèresréservés par l’interpréteur php. \” correspond ainsi au char ”, \$ permet d’afficher $, etc...

Comme en C++ :\n fait un retour à la ligne, \r fait un retour chariot et \t fait une tabulation.ATTENTION ces trois dernières instructions font des nouvelles lignes ou retour chariot dans le code Html

produit en résultat par php et non dans le navigateur directement...

Toute chaine php est un tableau commençant à l’indice 0 mais contrairement à C++, elle ne se termine paspar \0 et n’a donc pas de caractère de fin de chaine propre.

Tableaux et tableaux associatifs :Les tableaux sont indexés à partir de 0 comme en C++. Php permet la création de tableau associatif. Dans

ce cas, l’index du tableau n’est pas lui-même numérique comme dans un tableau classique mais contient à laplace une clef qui est une chaine de caractères :

Tableau classique :

5

Page 6: RelectureDePhp5_2011_01_29

$tabclassic = array(10, ”element bidon”, 23.5); /// tableau dont l’élément 0 est un entier, l’élément 1 unechaine et le 2 un double

$tabAsso = array(”prénom” => ”Bernard”,”ville” => ”Paris”,”travail” => ”ingénieur”);

Il est possible d’emboiter les instructions array de manière à avoir des matrices ou des tableaux de tableaux,et il et aussi possible de mixer tableau classique et associatif en les imbriquant...

Attention pour avoir accès dans une chaine à la valeur d’un élément d’un tableau, il faut encadrer l’expressionde la valeur dans des accolades ET incorporer l’expression entre crochets dans une expression de chaine (càdentre deux guillements ” ou apostrophes ’ suivant le délimiteur choisi pour l’expression de chaine :

Exemple : $CrimTarget = array(array(”Nom” => ”Eugène de Rastignac”,”Profession” => ”Bellatre”,”Comdamnation” => ”Ruiné”),

array(”Nom” =>”Horace Bianchon”,”Profession” =>”Mèdecin”,”Comdamnation” => ”Bagne”)

);

Affichage de valeur issues de tableauxPour afficher le nom de Rastignac, il faut écrire {$CrimTarget[0][’Nom’]}, et pour faire référence à la variable,

il faut écrire la même expression sans les accolades.

Transtypage

Les casts marchent comme en C++. Exemple : (integer) var1 renvoie une nouvelle variable temporaire detype integer si possible. La fonction settype(var1,’integer’) fait la même chose.

Pour identifier le type d’une variable, on utilise la fonction gettype qui renvoie une chaine indiquant le type.Les seules subtilités concernent les chaines et les booléens.

Une chaine est convertit en valeur numérique si le début de la chaine correspond à des chiffres et donc :

echo 3+”fils2chacal”; /// donne 3

echo 3+”1conreste1con”; /// donne 4

Pour les booléens, est considéré comme FALSE les constantes FALSE et NULL, une chaine vide ou contenantuniquement 0, le nombre entier ou réel 0 et un tableau vide. Tout le reste est considéré comme TRUE en casde cast vers le type boolean.

Un cast vers une chaine fait que TRUE devient 1, et que NULL, et FALSE deviennent une chaine vide(par contre contrairement à ce que dit le bouquin, 0 ou 0.0 devient ”0”) et les tableaux, objets et ressourcesafficheront leur type comme valeur.

ATTENTION pour les objets, le transtypage vers le type chaine peut être surchargé en définissant explicite-ment la méthode __toString().

6

Page 7: RelectureDePhp5_2011_01_29

4 Chapitre 4 : Traitements de base

Opérateur d’affectation :L’opérateur d’affectation binaire = renvoie la valeur de la variable quand on fait $var1 = $var2 +3 de sorte

que echo $var1 =$var2 +3 ne fait pas qu’affecter une valeur à $var1, il renvoie aussi une valeur.

Les Références :Toutes les affectations qui ne concernent pas les objets sont faites par copie par défaut. cependant en écrivant

$var1 = &$var2; on définit $var1 comme une référence sur $var2.Toutes les affectations utilisant un objet comme leftvalue se fait par référence !!

Opérateurs de concaténation :l’opérateur de concaténation est le symbole ”.” et il s’utilise avec deux variables comme suit $var1.$var2

du moment que les deux variables sont assimilables (et castables éventuellement) en chaine. Il existe aussi unopérateur .= analogue à +=

Opérateurs de comparaison:

Pareil qu’en C++ avec en sus les opérateurs === et !== qui compare la valeur et le type de la variablepour égalité. ça permet de blinder certains tests et évite notamment des égalités non souhaités en chaine etnombre.

Opérateurs logiques : RAS à part l’opérateur XOR qui est le ou exclusif (soit l’un, soit l’autre mais pasles deux, ni aucun des deux).

Opérateur sur les bits :$a & $b: donne 1 si les deux bits valent 1 et 0 sinon ;$a| $b: donne 1 si au moins un des deux bits vaut 1 ;$a^$b: le ou exclusif entre bit1 et bit2...$a<‌<$b : décale la valeur de chaque de $a crans à gauche ;$a >‌> $b : pareil mais à droite.~$b : met chaque bit à 1 si celui-ci vaut 0 (j’ai rien compris mais on s’en fout en fait car peu probable que

cet opérateur soit utilisé un jour...)

Opérateur de contrôle d’erreur :@expressionPHP : empêche l’affichage d’un message d’erreur sur la page web quand l’instruction expression-

PHP est appelé ...Structures de contrôles :

Comme en C++ avec les changements suivants :

• L’instruction elseif est acceptée comme en VB ;

• Les instructions switch, while, do while et for sont utilisées strictement comme en C++ ;

7

Page 8: RelectureDePhp5_2011_01_29

• l’instruction foreach permet de parcourir les élements d’un tableau. La syntaxe dépend de la natureclassique ou associative du tableau :

tableau classique : foreach($TableauParcouru as $ElementDuTableauParcouru)

tableau associatif : foreach($TableauParcouru as $ClefDuTableau => $ElementDuTableauParcouru)

Dans les boucles ( for,foreach, while et do while), l’instruction continue permet de sauter les instructions dubloc de la boucle pour passer au test suivant. Exemple : on génère 10 variables aléatoires, et on affiche le nombres’il est supérieur strictement à 5 :

$Binaire = 0.0;for($i =0;$i<10; $i++){

$Binaire = mt_rand(0.0,10.0);echo ’<br> le nombre vaut :’.$Binaire;if ($Binaire <=5){continue; /// càd on revient à i++ et on repasse dans le bloc

};$temp = $Binaire-5;echo ’<br> le nombre dépasse 5 de ’.$temp.’ !!’;

};

Les fonctions utilisateurs

Par rapport au C++, elles sont déclarées sans que le type de retour soit spécifié (beurk ! mais bon, c’estcomme ça !)

Le prototype est donc le suivant :function NomDeLaFonction($argument1, $argument2 = ValeurParDefautDeLArgument2){

return Kekchose; /// le return n’est pas obligatoire};

Comme en C++, les arguments présentant des valeurs par défaut sont mis en fin de liste.

Les arguments sont toujours passés par copie par défaut sauf si :

• l’argument est une objet : tout objet est toujours passé par référence.

• l’argument est déclaré comme une référence : Pour déclarer une variable argument comme étant uneréférence il faut précéder la déclaration de l’argument de &. Ainsi la déclaration de prototype suivante :

func($var1, &$var2)

est celle d’une fonction dont le premier est passé par copie et le deuxième par référence.

8

Page 9: RelectureDePhp5_2011_01_29

Les références et les fonctions : Tout argument de fonction déclarés comme référence est modifié définitive-ment suite à l’exécution du bloc de la fonction (si modif il y a bien sur).

Attention renvoyer en retour de fonction une référence ne suffit pas à renvoyer une référence mais renvoie aufinal une copie de la référence. Pour cela, il faut aussi que le retour de la fonction soit déclaré comme référence.ex :

func1(&$toto...){...return $toto;}

le code $titi = func1() fait que la valeur de $titi est recopié de la valeur de la référence renvoyé par func1. Pourdéclarer une fonction comme renvoyant une référence, il faut précéder la déclaration du nom de la fonction de& et bien sur s’assurer que c’est bien une référence qui est renvoyée, comme suit :

&func2(...){

return $toto;}

Attention il ne faut pas écrire return &$toto, le symbole & provoque une erreur. Le symbole & doit uniquementfigurer devant le nom de la fonction (ici func2) pour que le renvoi par référence s’effectue.

Tableau comme variable de retour d’une fonction :Un tableau peut être utilisé comme variable de retour. Par ailleurs, si le nombre n d’éléments du tableau

retourné est connu à l’avance, les valeurs des éléments de ce tableau peuvent être affectés en bloc à une série den variables via l’instruction list() comme suit :

list($var1,$var2,$var3)=FonctionQuiRetourneUnTabDeDim3

Attention le nb d’arguments de list() peut être inférieur ou égal à n mais jamais supérieur.

Fonctions avec un nombre indéfinie d’arguments :Ce type de fonction a un prototype identique à une définition qui n’a pas d’arguments. Dans son bloc

d’exécution, l’appel aux fonctions natives :

• func_num_args() : permet de récupérer le nb d’arguments passés en arguments ;

• func_get_arg($position) : permet de récupérer un argument à partir de sa position $position

• func_get_args() : renvoie un tableau de la taille de la liste des arguments avec les éléments du tableauégaux aux arguments dans le même ordre.

Gestion des includes :Par rapport au C++, #include est géré par deux types d’appels : require et include. La différence est qu’avec

include(), le fichier appelé est réévalué à chaque appel d’include() alors qu’avec Require, il ne l’est pas.

9

Page 10: RelectureDePhp5_2011_01_29

La syntaxe de l’opération est la suivante :

include(”NomDuFichier.php”)

Notons qu’il n’est pas nécessaire de spécifier le chemin d’accès si le répertoire de travail est bien spécifié dansph.ini. Notons ensuite que la chaine peut être remplacée par une variable chaine de caractère et on peut donc”dynamiser” les includes. Notamment pour alléger les écritures, on peut déclarer des constantes symboliquespour référer chaque fichier (et concentrer tout dans un classpath.php ou un filepath.php...) comme suit :

define(NOMSYMBOLIQUE, ”NomDuFichier.php”);include(NOMSYMBOLIQUE);

IMPORTANT : pour éviter les include multiples qui feront planter l’exécution et/ou la compilation, il vaut mieuxrequire_once(string ”NomDuFichier.php”) et include_once(”NomDuFichier.php”) qui reviennent à combiner#include et #pragma once en une seule instruction. L’ennui, c’est qu’en C++, le #pragma once agissait auniveau du fichier inclus alors que le require_once agit lui au niveau du fichier incluant et donc un require_onced’un fichier suivi plus loin d’un require du même fichier fait planter. Il Faut donc utiliser require_once tout letemps pour éviter les merdes...

5 Chapitre 5 : Traitements de chaines

Super chiant comme chapitre mais hélàs essentiel.Affichage de chaines de caractères avec masque.

Un masque est une instruction %kekchose au sein d’une chaine de caractère. Le %kekchose est alors remplacépar la valeur d’une variable passé en argument de la fonction printf. L’affichage de la variable est alors formatésuivant le ”kekchose” %kekchose. On appelle le kekchose un spécificateur de conversion.

Chaque spécification de conversion est constituée d’un signe de pourcentage (%), suivi d’un ou plusieurs deséléments suivants, dans cet ordre :

1. Un signe optionnel qui force un nombre à être positif ou négatif (- ou +). Par défaut, seul le signe -est utilisé sur un nombre s’il est négatif. Ce spécificateur force également les nombres positifs à avoir unsigne + d’attaché, et a été ajouté en PHP 4.3.0 ;

2. Un remplisseur optionnel qui indique quel caractère sera utilisé pour compléter le résultat jusqu’à lalongueur requise. Ce peut être le caractère d’espace, ou le caractère 0. Par défaut, le remplissage se faitavec des espaces. Un autre caractère de remplissage peut être spécifié en le préfixant avec un guillemetsimple (’) : ATTENTION si l’espace est choisi comme remplisseur, sur internet explorer, tous les espacescontigus sont concaténés dans ce cas en un seul ;

3. Un spécificateur d’alignement qui indique si le résultat doit être aligné à gauche ou à droite. Pardéfaut, le résultat est aligné à droite(contrairement à ce que dit la doc de phpEdit). Le caractère - feraque le résultat sera justifié à gauche ;

4. (ajout BG) un spécificateur du nombre minimum de caractères : si la longueur de la chaine enargument est inférieur strictement à ce nombre, la chaine est complété à gauche ou à droite à l’aide duremplisseur optionnel ;

10

Page 11: RelectureDePhp5_2011_01_29

5. Un spécificateur de précision qui indique le nombre de décimales qui doivent être affichées pour lesnombres à virgule flottante (ATTENTION c’est bien ici le nombre de chiffres après la virgule). Lorsquevous utilisez ce spécificateur dans une chaîne, il agit comme un point de coupure, définissant une limitemaximale de caractères de la chaîne INITIALE passé en argument (ça signifie que la chaine est tronquéede la longueur excédentaire, c’est alors toujours la fin de la chaine qui est éliminée). Ce qui est drôle,c’est que bien que tronquée (pour une chaine) ou arrondi (pour un float), le remplissage peut ensuite seproduire car le php compte ensuite le nombre de caractères de la valeur tronquée de la variable pour savoirs’il fait du remplissage ;

6. Un spécificateur de type qui indique le type avec lequel l’argument sera traité. Plusieurs types possibles :

• % : un caractère de pourcentage littéral. Aucun argument n’est nécessaire.

• b : l’argument est traité comme un entier, et présenté comme un nombre binaire.

• c : l’argument est traité comme un entier, et présenté comme le caractère de code ASCII correspon-dant.

• d : l’argument est traité comme un entier, et présenté comme un nombre décimal signé.

• e : l’argument est traité comme une notation scientifique (e.g. 1.2e+2). Le spécificateur de précisionreprésente le nombre de chiffres après la virgule depuis PHP 5.2.1. Dans les versions antérieures, ila été pris comme nombre des chiffres significatifs (au moins un).

• u : l’argument est traité comme un entier, et présenté comme un nombre décimal non signé.

• f : l’argument est traité comme un nombre à virgule flottante (type float), et présenté comme unnombre à virgule flottante (tenant compte de la locale utilisée).

• F : l’argument est traité comme un nombre à virgule flottante (type float), et présenté comme unnombre à virgule flottante (ne tenant pas compte de la locale utilisée). Disponible depuis PHP 4.3.10et PHP 5.0.3.

• o : l’argument est traité comme un entier, et présenté comme un nombre octal.

• s : l’argument est traité et présenté comme une chaîne de caractères.

• x : l’argument est traité comme un entier, et présenté comme un nombre hexadécimal (les lettres enminuscules).

• X : l’argument est traité comme un entier, et présenté comme un nombre hexadécimal (les lettres enmajuscules).

Exemple :%−′ #− 10.5f

signifie que le signe - apparaitra, que la chaine sera complétée par le caractère #, sera justifiée à gauche,que 10 caractères minimum devront apparaitre (les caractères manquants seront alors des # et que le nombrefloat sera de 5 caractères max.

ATTENTION quand on tronque une string (s), le 5 s’applique au nombre total de caractères tandis quedans le cas d’un double, c’est uniquement le nombre de chiffres après la virgule qui compte ! Dans le cas d’unnombre entier ou décimal, la troncature ne sert à rien.

Ordonnancement des arguments sur sprintf et fonctions natives analogues : les arguments associésdans l’ordre aux différents %kekchose du masque mais en l’occurrence, il est possible de donner un ordre dansle masque de saisie. Pour spécifier l’ordre d’arrivée d’un argument dans ssprintf par rapport au masque, les

11

Page 12: RelectureDePhp5_2011_01_29

arguments dynamiques %kekchose du masque doivent intégrer un numéro correspondant à l’ordre de prise encompte. Exemple : Affichage des dates suivant la langue du navigateur :

Fonctions utiles au traitement des chaines :AVERTISSEMENT : les fonctions natives de traitement de chaines de php ne permettent pas de traiter

correctement tous les jeux de caractères, en particulier le jeu UTF-8 qui intègre tous les types de caractères.De ce fait, il faut avoir installé le module mbstring dans php. Heureusement il est installé avec l’install pardéfaut de WampServer 2. Tous les fonctions natives ci-dessous sont alors appelables en version mbstring en lespréfixant par mb_, la fonction strlen de mbstring est alors la fonction mb_strlen, etc.

POUR PARAMETRER LE MODULE MBSTRING : page 124

• sprintf : fait pareil que printf mais au lieu d’afficher en flux de sortie, envoie la chaine à afficher dansune variable ;

• sscanf : permet de faire l’inverse de sprintf puisque suivant la string paramétrée ou masque, on récupèreles morceaux distincts. Cette fonction renvoie un tableau et est donc récupérable via la fonction nativelist() ; Le prototype est :

sscanf(texteAParser,”masquedeparsing”)

rappelons qu’un exemple de parsing est ”%s %d” qui correspond à une chaine, un espace puis un nombredécimal. ATTENTION quand le masque comporte une chaine (càd %s), la lecture de la chaine s’arrêteuniquement sur un espace et ce quelle que soit les caractères suivant %s dans le masque de saisie. Ce quin’est pas vraie pour les autres types de variables. Ex : Avec le masque ”%02d-%02d-%04d”, sscanf renvoieles trois éléments numériques d’une date à partir d’une chaine du type ”08-06-2010”. Par contre le masque”%s-%02d-%04d”, renvoie un chaine unique qui correspond à la chaine en entrée !! Le parsing a alorséchoué. Même en utilisant un masque précisant la longueur de %s, ça ne marche pas !!

• ord(’a’) : renvoie le code ASCII d’un caractère (attention ’a’ correspond à n’importe quel caractère etnon à a) ;

• chr(x) : renvoie le caractère correspondant au code ASCII x ;

• strlen() : renvoie la taille d’une chaine (ATTENTION en codage UTF8, ça ne renvoie pas forcément undécompte des caractères car certains caractères comme é sont codés sur deux octets au lieu de 1 et comptedonc pour 2 dans la mesure de la taille de la chaine. En fait, strlen renvoie la taille en octets de la chaine ;

• str_word_count(chaineAParser,OptionValue) : Par défaut, OptionValue vaut 0, et dans ce cas, lafonction renvoie simplement le nombre de mots dans la chaine, sinon avec une valeur de 1, elle renvoieun tableau de chaines contenant chacun des mots, et si elle vaut 2, pareil mais l’indice dans le tableau dechaque mot est égale à la position du mot dans la chaine.

• str_pos(chaine, souschainecherchée) : renvoie la position (sous forme d’un entier) de la chainecherchée. ATTENTION renvoie FALSE si la chaine n’est pas trouvée. Si une chaine est trouvé en position0, il faut quand même d’abord utiliser le comparateur === pour éviter de confondre 0 et FALSE si lachaine n’a en fait pas été trouvé ;

• strspn(Chaine,CaractèreCherchée) : donne la longueur de la première sous-chaine de Chaine con-tenant le caractère spécifiée ;

• strcspn(Chaine,CaractèreCherchée) : donne la longueur de la première sous-chaine de Chaine NEcontenant PAS le caractère spécifiée ;

12

Page 13: RelectureDePhp5_2011_01_29

Conversions et formattages :Suivant le contexte, certaines caractères sont considérés comme spéciaux dans une chaine et peuvent aboutir

à exécuter une commande non sollicitée si leur caractère spéciale n’est pas neutralisée ou échappée.

Neutralisation simple des caractères spéciaux des chaines dans PHP :Cela se fait avec le caractère d’échappement ’\’.

Fonctions de neutralisation des caractères spéciaux :

• addslashes(chaine) : neutralise dans la chaine guillemets, apostrophes et ’\’. La neutralisation se faitpar l’ajout d’un ’\’ avant le caractère à neutraliser.

• addcslashes(chaine,chaineListeDesCaractèresANeutraliser) : neutralise dans la chaine guillemets,apostrophes, ’\’, \n, \r et les caractères ASCII qui s’écrivent \xx (avec xx est le code ASCII inférieur à36 ou supérieur à 126 éliminables) ; Par ailleurs, on peut définir une plage de caractères ASCII échappéesde la façon suivante : ”a..b”

• Les fonctions stripslashes et stripcslashes font exactement l’inverse des deux fonctions respectivesprécédentes sauf que stripclashes ne prend pas de liste de caractères à rétablir. Elles retirent les ’\’ enconvertissant toutefois tous les \n \r \t et \nnn ASCIIS dans leur équivalent affichage (retour chariot,ligne, etc.) ;

Neutralisation simple des caractères spéciaux des chaines pour les requêtes de SGBD :

Normalement chaque API de base de données a ses fonctions de protection. Dans PHP notamment, lacouche SGBD PDO a la fonction quote()

Neutralisation simple des caractères spéciaux des chaines en HTML

Les caractères spéciaux sont les caractères ’<’, ’>’ et ’&’.Les deux fonctions de protection de ces caractères pour l’affichage html sont :

htmlspecialchars(ChaineAProtéger,Optionnel ENT_COMPAT ou ENT_NOQUOTES ou ENT_QUOTES,Optionnel JeuDeCaractèresPourLaConversion);

La première option permet d’ajouter les guillemets et/ou les apostrophes dans la liste des caractères échap-

pés. Du coup, dans le code source, les caractères spéciaux du php pourront être neutralisés tandis qu’à l’affichagefinal du code HTML, ce sont les caractères spéciaux de l’HTML qui le seront (il seront alors convertis en entitésHTML du type &kekchose;) ; La seconde option permet de définir l’encodage de la chaine à l’arrivée (ex : ISO-8859-1 qui contient surtout les lettres anglo-saxonnes, ISO-8859-15 qui élargit le jeu précédent aux caractèresaccentués français, espagnol, norvégien etc., l’utf-8 qui correspond à l’unicode avec une place mémoire optimisé)

htmlEntities() (même prototype) : convertit les caractères spéciaux à neutraliser mais aussi tous lescaractères php qui ont un équivalent entité en HTML. La différence avec la fonction htmlspecialchars estuniquement visible dans le code source HTML. A l’affichage dans le navigateur (càd une fois le code HTMLexécuté) est identique entre les deux fonctions. exemple : la chaine

”<br>pété”

13

Page 14: RelectureDePhp5_2011_01_29

est converti par htmlspecialchars comme”&lt;br;&rtpété”

alors que HTMLEntities transforme ça en

”&lt;br;&rtp;&eacute;t;&eacute;”

La fonction html_entity_decode() transforme les entities html (&kekchose) en équivalent html de sorteque par exemple :

html_entity_decode(”&lt;br;&rtpété”)=”<br>pété”

A noter que la fonction get_html_translation_table permetSuppression de balises HTML :

La fonction striptags(chaineHTML, ChaineListeDesBalisesARetirer); permet de retirer les balises HTMLn’appartenant pas à la liste spécifier

Conversion des changements de ligne PHP en changement de ligne HTML :Avec la fonction nl2br(chainePHP)...

Convention d’affichage suivant le pays :La fonction setlocale(Catégorie, Chaine1, Chaine2,...). 5 catégories de localisation existent :

• LC_COLLATE : pour les comparaisons de chaines de caractères ;

• LC_CTYPE : pour la fonction strtoupper() qui transforme les minuscules d’une phrase en majusculessuivant les règles de grammaires locales (ex : les rosbeefs transforment toutes les premières lettres dechaque mot d’une phrase en majuscules et les frenchies seulement la première lettre de chaque phrase.

• LC_MONETARY : pour les conventions monétaires ;

• LC_NUMERIC : pour les séparateurs dans les valeurs numériques ;

• LC_TIME : pour les dates et les heures ;

• LC_ALL : permet de spécifier toutes les catégories en même temps ;

Les chaines spécifiant l’arbitrage sont passées en revue l’une après l’autre et la première chaine fonctionnelleest utilisée. Si une seule chaine ”0” est passée, c’est la variable d’environnement LANG qui sera utilisé pourspécifier la localisation. Si une chaine vide est passée, c’est la valeur courante utilisée qui sera renvoyé.

Quelques localisations :PrimarylanguageSublanguageLanguage stringChinese Chinese ”chinese”Chinese Chinese (simplified) ”chinese-simplified” or ”chs”Chinese Chinese (traditional) ”chinese-traditional” or ”cht”Czech Czech ”csy” or ”czech”Danish Danish ”dan” or ”danish”Dutch Dutch (default) ”dutch” or ”nld”

14

Page 15: RelectureDePhp5_2011_01_29

Dutch Dutch (Belgian) ”belgian”, ”dutch-belgian”, or ”nlb”English English (default) ”english”English English (Australian) ”australian”, ”ena”, or ”english-aus”English English (Canadian) ”canadian”, ”enc”, or ”english-can”English English (New Zealand) ”english-nz” or ”enz”English English (United Kingdom) ”eng”, ”english-uk”, or ”uk”English English (United States) ”american”, ”american english”, ”american-english”, ”english-american”, ”english-

us”, ”english-usa”, ”enu”, ”us”, or ”usa”Finnish Finnish ”fin” or ”finnish”French French (default) ”fra” or ”french”French French (Belgian) ”frb” or ”french-belgian”French French (Canadian) ”frc” or ”french-canadian”French French (Swiss) ”french-swiss” or ”frs”German German (default) ”deu” or ”german”German German (Austrian) ”dea” or ”german-austrian”German German (Swiss) ”des”, ”german-swiss”, or ”swiss”Greek Greek ”ell” or ”greek”Hungarian Hungarian ”hun” or ”hungarian”Icelandic Icelandic ”icelandic” or ”isl”Italian Italian (default) ”ita” or ”italian”Italian Italian (Swiss) ”italian-swiss” or ”its”Japanese Japanese ”japanese” or ”jpn”Korean Korean ”kor” or ”korean”Norwegian Norwegian (default) ”norwegian”Norwegian Norwegian (Bokmal) ”nor” or ”norwegian-bokmal”Norwegian Norwegian (Nynorsk) ”non” or ”norwegian-nynorsk”Polish Polish ”plk” or ”polish”Portuguese Portuguese (default) ”portuguese” or ”ptg”Portuguese Portuguese (Brazilian) ”portuguese-brazil” or ”ptb”Russian Russian (default) ”rus” or ”russian”Slovak Slovak ”sky” or ”slovak”Spanish Spanish (default) ”esp” or ”spanish”Spanish Spanish (Mexican) ”esm” or ”spanish-mexican”Spanish Spanish (Modern) ”esn” or ”spanish-modern”Swedish Swedish ”sve” or ”swedish”Turkish Turkish ”trk” or ”turkish”Jeux de caractères :Une foultitude de jeux de caractères existent et il fait éviter de s’y perdre. Initialement on a le US-ASCII qui

ne comportaient que les caractères anglosaxons, le jeu ISO-8859-15 comportent tous les symboles français dontle €. Le jeu idéal est le jeu UTF-8 car il comporte tous les jeux de caractères de toutes les langues connues...

En fait pour bien comprendre le systèmes des jeux de caractères dans le cadre du développement d’un siteweb avec php, il faut comprendre qu’il y a trois choses essentielles à bien considérer séparément :

• le jeu de caractères : c’est un ensemble de caractères qu’on peut trouver dans un ou plusieurs langageshumains suivant le jeu de caractères considérés. par exemple : le jeu de caractères US qui comporteles 26 lettres de l’alphabet, les 10 chiffres et quelques autres caractères comme ’ ” (, ) etc. Le jeu le pluspetit est justement ce dernier jeu de caractères et le plus grand est l’unicode qui contient pratiquementtous les caractères connus des langues humaines ;

15

Page 16: RelectureDePhp5_2011_01_29

• L’encodage : c’est la traduction informatique d’un jeu de caractères. A la base, les premiers encodagesde jeux de caractères étaient tels que chaque caractère était codé sur un octet. Cependant pour les jeuxde caractères les plus larges, les 8 bits d’un octet étaient insuffisants et donc certains encodages utilisentjusqu’à 4 octets au lieu de 1. On peut notamment citer l’iSO-8859 qui est l’encodage du jeu ASCII-US,l’encodage ISO-8859-15 qui est une version étendue du précédent et englobe aussi les caractères latinscomme les caractères accentués et les lettres spéciales type ”nié” espganol, et l’UTF-8 dont la taille encodéde chaque caractère varie de 1 à 4 octets : UTF-8 est ainsi un encodage de l’Unicode et qui a pourparticularité d’englober notamment l’encodage ISO-8859-15 qui est l’encodage des caractères occidentauxde sorte qu’un caractère occidental est codé sur un octet en UTF-8. Cette taille variable du caractèreencodé a pour mérite de ne pas faire trop augmenter la taille des caractères encodé quand pour la plupart,ils correspondent aux caractères occidentaux.

La liste des encodages gérés par php au sein du module mbstring est la suivante :

– pass

– auto

– wchar

– byte2be

– byte2le

– byte4be

– byte4le

– BASE64

– UUENCODE

– HTML-ENTITIES

– Quoted-Printable

– 7bit

– 8bit

– UCS-4

– UCS-4BE

– UCS-4LE

– UCS-2

– UCS-2BE

– UCS-2LE

– UTF-32

– UTF-32BE

– UTF-32LE

– UTF-16

– UTF-16BE

– UTF-16LE

– UTF-8

– UTF-7

– UTF7-IMAP

16

Page 17: RelectureDePhp5_2011_01_29

– ASCII– EUC-JP– SJIS : encodage japonais ;– eucJP-win– SJIS-win– CP51932– JIS– ISO-2022-JP– ISO-2022-JP-MS– Windows-1252 (ou CP1252) : encodage des caractères sous windows ;– Windows-1254– Windows-1256 (ou CP1256) : encodage arabe (pas dans php);– Windows-1257 (ou CP1257) ; encodage lituanien (pas dans php);– ISO-8859-1 : Encodage français avant l’euro ;– ISO-8859-2 : vieil encodage roumain– ISO-8859-3– ISO-8859-4– ISO-8859-5– ISO-8859-6– ISO-8859-7 :Encodage grec avant l’euro ;– ISO-8859-8– ISO-8859-9– ISO-8859-10– ISO-8859-13– ISO-8859-14– ISO-8859-15 : Encodage français après l’euro ;– ISO-8859-16 : nouvel Encodage roumain ;– EUC-CN– CP936– HZ– EUC-TW– BIG-5– EUC-KR– UHC– ISO-2022-KR– Windows-1251– CP866– KOI8-R : Encodage russe ;

17

Page 18: RelectureDePhp5_2011_01_29

– KOI8-U : encodage ukrainien et bulgare ;– ArmSCII-8– CP850 : encodage des chaines de caractères DOS ;

• l’encodage du fichier et des flux : Quand un flux est récupéré, il est encodé suivant un encodage donné etutilisé au sein d’un fichier qui est lui encodé potentiellement autrement. Pour éviter les problèmes, il fautalors :

– connaitre l’encodage du flux ;– convertir cette encodage dans l’encodage du fichier avec la fonction mb_convert_encoding (v.ci-

dessous). ATTENTION : si l’encodage cible gère moins de caractères que l’encodage initial, il vay avoir perte d’informations et une fois affichés les caractères non identifiés apparaaitront commereprésentés par un losange noir avec un point d’interrogation blanc en son sein.

Pour info, quelques pages wikipedia sur le sujet sont bien faites et propose des liens complets sur le sujet del’encodage des jeux de caractères :

http://fr.wikipedia.org/wiki/Codage_de_caract%C3%A8reshttp://edutechwiki.unige.ch/fr/Encodage_de_caract%C3%A8res

Les URLs compliquent les choses (!)La spécification des URLS interdit l’usage des blancs (pour plusieurs raisons). Donc normalement, pour les

blancs, on DOIT utiliser l’encodage hexadecimal ISO latin / UTF-8 dans un URL.

Mon fichier.htm

devient:

Mon%20fichier.htm

En principe il faudrait aussi appliquer ce principe pour tous les caractères accentués, mais le problème secorse:

http://edutechwiki.unige.ch/fr/Encodage_de_caractères

devient en ISO hexadecimal (et peut marcher, mais il ne faut pas parier):

http://edutechwiki.unige.ch/fr/Encodage_de_caract%E8res

Dans le wiki on voit un encodage sur 2 bytes (représentation en UTF-8):

http://edutechwiki.unige.ch/fr/Encodage_de_caract%C3%A8res

Maintenant pour revenir aux simples ”blancs”: certains logiciels ont de la peine à traduire un caractère blancdans un lien vers une solution correcte (%20). Pour les autres caractères: si vos noms de fichiers dans le textesont en UTF-8 et sur le serveur en ISO-latin voir Windows cela peut aussi poser problème (à développer).

Et c’est pour cela qu’on déconseille normalement d’utiliser des blancs pour des fichiers et leurs liens.

A propos de l’encodage des fichiers sous WINDOWS :

18

Page 19: RelectureDePhp5_2011_01_29

L’encodage d’un fichier peut-être choisie en l’ouvrant avec le bloc-notes puis en choisissant Enregistrer souspuis en cliquant sur le menu déroulant ”Encodage” !!

Un dernier lien sur le sujet des jeux de caractères et encodages en php :http://french.joelonsoftware.com/Articles/Unicode.htmlhttp://www.tuteurs.ens.fr/faq/utf8.htmlFonction de conversion d’un jeu de caractère à l’autre :

Le module mbstring fournit la fonction :mb_convert_encoding($texteAConvertir,

$JeuCibleEnChaine,$JeuInitialEnChaine)

Elle renvoie la chaine transformé pour une écriture dans le nouveau jeu de caractères. Attention une chaineUTF-8 peut apparaître bizarrement si le jeu spécifié pour l’affichage n’est pas l’UTF-8

Une deuxième fonction fait la même chose mais pour un jeu de variables. Le prototype est :

mb_convert_variables($JeuCibleEnChaine,$JeuInitialEnChaine,$texteAConvertir_1,$texteAConvertir_2,...)

ATTENTION cette merde de fonction fait DEUX CONVERSIONS. D’abord de l’encodage courant (celuidonné par un appel à mb_internal_coding) vers $JeuInitialEnChaine, puis de $JeuInitialEnChaine vers $Jeu-CibleEnChaine. Bizarre...

Pour détecter l’encodage courant, il faut utiliser la fonction mb_internal_coding() qui renvoie en chainel’encodage utilisé.

Pour changer l’encodage courant, cette même fonction est utilisée en passant en argument le nouveau en-codage sous forme de chaine type ”UTF-8” ou ”ISO-8859-1” (encodage français). Dans ce cas, mb_internal_codingrenvoie TRUE ou FALSE au lieu de l’encodage courant pour dire si le changement a marché ou pas.

La fonction utf8_encode permet d’encoder en utf-8 une chaine initialement encodée avec le jeu ISO-8859-1

Si la page affiche des caractères de ce type : ”é ”, ”î ”, ”Ô, ...=> Les données ont été enregistrées au format UTF-8, et le navigateur les affiche en pensant avoir affaire à

de l’ISO.Si la page affiche des caractères de ce type : ”f” (càd losange noir mais avec un point d’interrogation blanc

à l’intérieur)=> Les données ont été enregistrées au format ISO, et le navigateur les affiche en pensant avoir affaire à de

l’UTF-8.

Les Fonctions de manipulation de chaine courante (quand mb_ est spécifié, c’est que la fonctionexiste dans le module mbstring) :

• (mb_) strstr(chaine1,chaine2) : renvoie le bout de chaine1 à partir duquel chaine2 commence

• (mb_) strrchr(chaine1,chaine2) : idem mais en partant de la droite de chaine1 ;

19

Page 20: RelectureDePhp5_2011_01_29

• (mb_) stristr : idem mais sans respecter la casse ;

• (mb_) substr(chaine, pos, longueur) : comme mid en Visual Basic sauf que l’indiçage commence à

0 et non à 1 ;

• str_replace(chaineARemplacer, ChaineDeRemplacement, ChaineAChercher) : clair non ?Sauf qu’il est possible de passer un tableau en argument 1. Dans ce cas, chaque élément subira l’opération.Si en sus, un tableau est passé en argument2, les remplacements seront effectués par correspondanced’éléments (élem1 du tableau 1 remplacé par élém1 du tableau 2, etc...). Enfin si un quatrième argumentest passé, il est modifié par référence et devient le nb d’éléments remplacés. Merci la surdéfinition enC++...

• substr_replace(ChaineAChercher, ChaineDeRemplacement, Position) : Remplace à partir dela position spécifié dans la ChaineAChercher...

• trim : supprime les caractères dits ”blancs” (retour chariot, espace, tabulation horizontale et verticale,fin de ligne, retour chariot et caractère nul. Il est possible de spécifier une liste spécifique de caractères àsupprimer (ex : ”abrd” supprime tous les caractères ’a’, ’b’, ’r’ et ’d’ à gauche et à droite ;

• str_pad($ChaineAcompleter,$longueur, optional $CaracOuChaineDeCompletion, optionalSTR_PAD_RIGHT ou STR_PAD_LEFT, STR_PAD_BOTH) : complète une chaine pourque sa longueur soit au minimum de $longueur, complète à droite et avec des espaces par défaut ou avec$CaracOuChaineDeCompletion (attention il complète quoiqu’il arrive pour que la longueur max soit de$longueur. STR_PAD_BOTH complète alternativement la chaine une fois à droite, une fois à gauche,une fois à gauche, etc. mais toujours en commençant par la droite ;

• wordwrap(ChaineParagrapheAWrapper, Optional LongueurMaxDuneLigne,Optional ChaineCar-actèredeWrapping, Optional Bool CoupureDesMots ) : introduit des sauts de ligne PHP dans lachaine dès lors qu’on a 75 caractères successifs sans retour à la ligne ; Si le deuxième argument est spécifié,c’est lui qui est inséré. Utile pour insérer des retours à la ligne HTML (<br>) et non PHP. AT-TENTION : quoiqu’il arrive, les mots ne sont eux-mêmes jamais cassés sauf si le quatrième argumentCoupureDesMots est setté à TRUE (false par défaut). La coupure n’intervient pour un mot alors que sisa longeur dépasse LongueurMaxDuneLigne.

• strtoupper et strtolower : met resp. en majuscules et en minuscules ATTENTION : les lettres accen-tuées ne sont pas mis en majuscules et ne sont alors pas affichées !! ;

• ucfirst($chaine) : Met en majuscule si possible le premier caractère de la chaine (Là par contre, lesmajuscules accentuées sont bien gérées);

• ucwords($chaine) : Met en majuscule le premier caractère de chaque mot (même remarque que ucfirst);

6 Chapitre 6 : Utilisation des TableauxEspérons que ce chapitre sera moins lourd que le précédent.

• print_r($Tableau) : Pour afficher la structure d’un tableau ou d’une variable si c’est une variable quiest passé en argument ;

20

Page 21: RelectureDePhp5_2011_01_29

• var_dump($Tableau) : pareil mais ajoute des infos sur les types des éléments du tableau ou de lavariable ;

• count($Tableau, optional COUNT_RECURSIVE) : donne le nb d’éléments d’un tableau. Pourun tableau de tableaux, donne le nb de tableaux dans le tableau (logique, non ?) ; Renvoie un entier.ATTENTION, si l’argument n’est pas un tableau et ne vaut pas NULL, la fonction renvoie 1. L’ajout del’option COUNT_RECURSIVE renvoie le nombre d’éléments d’un tableau de tableau. L’ennui, c’estque le nombre renvoyé est le nombre d’éléments dans le tableau plus le nombre d’élements dans chaqueélément si celui-ci est à chaque fois un tableau. Ex : Pour un tableau de deux tableaux de trois éléments,count renvoie 8 et non 6 ;

• in_array($VarCherchée,$tableau,optional BOOL) : Permet de savoir si la valeur de $VarCherchéeest est un des éléments du tableau (si $VarCherchée est contenu dans un élément comme une sous-chainedans une chaine plus grande par exemple, ça ne marche pas et la fonction renvoie FALSE. En option,on peut aussi imposer que la variable $VarCherchée soit du même type que l’élément trouvé (BOOL =TRUE, FALSE par défaut). Notons que la variable cherchée peut être aussi un tableau ou un objet (!!),Renvoie un BOOL ;

• array_search() : pareil mais renvoie la clef correspondante à l’élément ;

• array_key_exists($clef, $tableau) : renvoie TRUE si la la valeur de $clef correspond à la valeur ;

• array_count_values($tableau) : renvoie un tableau d’effectifs donnant pour chaque type d’élémentsdu $tableau l’effectif concerné bref une sorte de tableau d’effectifs.

• sort($TableauATrier, optional OPTION) : trie en ordre croissant les éléments d’un tableau enprenant en argument un tableau par référence. On peut forcer le type de tri en comparaison numériqueen mettant SORT_NUMERIC (attention le classement des valeurs non-numériques est bizarre et ladoc phpedit considère que dans ce cas le résultat est imprévisible), ou en comparaison textuelle avecSORT_STRING (ou avec SORT_LOCALE_STRING pour utiliser le jeu . Par défaut, on a SORT_REGULAR,qui implique que les chiffres sont considérés comme précédant les lettres dans l’ordre des caractères. IM-PORTANT : le tri se fait en réallouant les clefs dans l’ordre obtenu.

• rsort() : pareil que la précédente mais dans l’ordre décroissant.

• asort() : pareil que sort mais la clef de chaque élément reste la même.

• arsort() : pareil que rsort mais la clef de chaque élément reste la même.

• ksort() : pareil que asort mais trie en trouvant l’ordre croissant des clefs des éléments.

• krsort() : pareil que ksort mais en sens inverse ;

• ATTENTION : Le tri des chaines comportant des lettres et des nombres fait que ces derniers sont triéschiffre par chiffre de sorte que ”Toto12” sera ainsi compris entre ”Toto1” et ”Toto2”. La fonction natsort()permet de s’assurer que le tri est fait dans l’ordre des nombres et dans ce cas cette fonction trie en fonctionde la casse. La fonction natcasesort() trie elle sans en tenir compte. Il n’y a pas de fonctions inverseséquivalentes ;

• usort($tableauAtrier, $StringNomFonction) : C’est le bon vieux sort du C++ !! Le deuxièmeargument est une chaine de caractère qui doit correspondre au nom de la fonction de comparaison quidétermine le critère de comparaison.

21

Page 22: RelectureDePhp5_2011_01_29

• array_multisort($tableau1,{Optional Liste de $CRITERE},$tableau2,{Optional Liste de$CRITERE}) : Le couteau suisse des fonctions de tri. En fait, le tri d’un tableau à plusieurs colonnes.Si aucune liste de critères n’est spécifié, le tableau 2 est trié en utilisant comme clef de tri les valeursdu tableau 1. En cas d’égalité, c’est les valeurs du tableau suivant qui déterminent l’ordre. Si les listesde critères sont utilisées, chaque tableau est trié suivant la liste qui suit. Les critères sont SORT_ASC,SORT_DESC, SORT_REGULAR, SORT_STRING, et SORT_NUMERIC... En fait, chaque tableaupassé en argument peut être considéré comme une des colonnes d’un seul tableau les concaténant tous.

• extract() : uniquement pour un tableau associatif. Cela crée en mémoires autant de variables que lenombre d’éléments du tableau et chaque variable est crée avec pour nom la clef de l’élément correspondant.FONCTION SURPUISSANTE SI ON Y PENSE !!

• implode($Separateur, $tableau) : transforme un tableau en une chaine des valeurs de chaque élémentdu tableau séparé les uns des autres de la chaine de separation ;

• explode($Separateur, $tableau) : fait l’inverse.

• array_slice($tab, $clefIni, $NbMax) : renvoie un sous-tableau du tableau de $tab. Le premier élémentdu nouveau tableau est de clef $clefIni et le tableau contient au maximum les NbMax-1 éléments suivantsdu tableau $tab ;

• array_splice($tab, $Offset,optional $lenght, optional $tabReplacement) : fonction tordue dontles subtilités me semblent pas d’une grande utilité mais bon... Quand $Offset est >0, la fonction supprimedu tableau $tab tous les éléments à partir de celui de position offset et renvoie un tableau qui correspondau sous-tableau supprimé. Quand offset est <0, c’est pareil sauf que l’index est compté depuis la fin avecle dernier élément du tableau valant -1. Quand lenght (=n) est spécifié,et qu’il est >0, les n premierséléments à partir de l’offset compris sont supprimés, quand lenght(=n) est négatif, les n derniers élémentsdu tableau à partir de l’offset sont préservés. Enfin quand un tabReplacement est spécifié, le sous-tableausupprimé est aussi remplacé par le tableau de replacement même si les deux n’ont pas la même taille.Dernier truc, si lenght est nul et qu’un tableau de remplacement est spécifié, ce dernier est simplementinséré dans le tableau d’input initial à la position d’offset ;

• array_keys($tab) : renvoie un tableau contenant les clefs du tableau $tab ;

• array_values($tab) : renvoie un tableau contenant les valeurs (et non les clefs) du tableau $tab ; CETTEFONCTION PERMET DE CONVERTIR UN TABLEAU ASSOCIATIF EN TABLEAU CLASSIQUE ;

• array_flip($tab) : renvoie un tableau ou sont inversés clef et valeur de chaque élément du tableau ;

• array_merge($tab1, $tab2,...,$tabn,...) : renvoi un tableau qui est la fusion des tableaux en entrée.Le $tab1 est a ses éléments en premier, puis le $tab2, etc. Si ce sont des tableaux associatifs avec des clefscommunes, le tableau fusionné ne comporte qu’une seule telle clef et c’est la valeur du dernier tableauayant cette clef qui est gardé...

• array_recursive_merge($tab1, $tab2,...,$tabn,...) : permet la fusion de tableau de tableaux, lessous-tableaux ayant une même clef sont alors fusionnés deux à deux au lieu de voir le suivant écraserle précédent ; IMPORTANT : Cela permet de pallier les écrasement de valeurs de tableaux associatifscontenant tous la même liste de clef. 14h 34

• array_chunk($tab, $tailleSousTableau, Optional KeepKey) : Splitte un tableau $tab en untableau contenant autant de sous-tableaux de taille $tailleSousTableau que nécessaire pour contenir lesvaleurs du tableau initial. L’option permet de savoir si les clefs sont renumérotés (keepKey = FALSE pardéfaut) ou conservés. Le dernier est de taille plus réduite que $tailleSousTableau si le nombre d’éléments

22

Page 23: RelectureDePhp5_2011_01_29

est insuffisant (càd si le tableau initial a 7 éléments et qu’on demande un split en tableau de taille 3, il yaura deux tableaux de taille 3 et un tableau de taille 1 ;

FONCTIONS DE GESTION D’UN TABLEAU COMME UNE LISTE :

• array_push($tab, $elem) : ajoute $Elem en dernier élément du tableau $tab ; Attention le pointeursous-jacent du tableau passe au premier élément du tableau (notamment faire gaffe quand cette fonctionest utilisé dans une boucle foreach qui se base sur le déplacement du pointeur explicite du tableau) ;

• array_pop($tab) : renvoie le dernier élément du tableau $tab tout en le supprimant ; Attention lepointeur sous-jacent du tableau passe au premier élément du tableau (notamment faire gaffe quand cettefonction est utilisé dans une boucle foreach qui se base sur le déplacement du pointeur explicite du tableau);

• array_unshift() ajoute un élément en début de tableau et array_shift() élémine un élément en débutde tableau comme le font array_pop et push en fin de tableau...

Deplacement du pointeur interne d’un tableau : Les fonctions reset(), end(), prev() et next() perme-ttent de déplacer manuellement le pointeur interne du tableau et renvoie ensuite la valeur de l’élément pointédu tableau. Ces quatres fonctions prennent toutes un tableau en argument. reset() met le pointeur sur lepremier élément, end() le met sur le dernier élément du tableau, prev et next se passent de commentaires... Sile pointeur est déplacé hors des limites, la fonction prev ou next renvoie FALSE.

7 Chapitre 7 : Les fonctions usuelles de PHP

Là, c’est un bestiaire un peu disparate de fonctions php :

NOTE IMPORTANTE : Les variables dites EGPCS sont les variables globales accessibles dans tout php(Environnement, GET, POST, Cookie, Session). Voir plus loin.

7.1 Fonction d’information sur la config :• phpinfo(optional (OPTION1 OR OPTION2 OR...)) : renvoie un tableau formaté d’informations de

config sur PHP. Il existe une série d’options possibles pour limiter ou forcer l’affichage de la config php :INFO_GENERAL, INFO_CREDITS, INFO_CONFIGURATION, INFO_MODULES, INFO_VARIABLES,INFO_LICENSE. Elles sont entre parenthèses car la fonction prend un seul argument qui est un entierrésultat de la combinaison de OR. Renvoie un BOOL notifiant l’échec ou la réussite ;

• print_r($var) : pour avoir un affichage d’une variable $var;

• var_dump($var) : pour avoir un affichage d’une variable $var et de son typage ;

• highlight_string($chaine) : permet d’afficher une chaine de code php de manière colorée et avec unaffichage du code HTML équivalent pour produire le code php tel que traité en interne par php ;

• highlight_file($file) : pareil mais avec le contenu d’un fichier

23

Page 24: RelectureDePhp5_2011_01_29

7.2 Fonctions mathématiques :• max(a,b, c,...) : trivial. Prend aussi des tableaux en arguments, la fonction renvoie alors un tableau de

max élément à élément. Si un seul nombre est à virgule flottante, ils sont tous considérés comme tels ;

• min : pareil mais c’est le minimum ;

• round($a, Optional $Prec) : arrondi à l’entier le plus proche sachant que l’entier supérieur est privilégié encas d’équidistance...Prec signifie à quel chiffre après la virgule on arrondit. Si Prec est négatif, on arrondità la puissance de dix correspondante ;

• ceil($a) : arrondit $a à l’entier supérieur ;

• floor($a) : arrondit $a à l’entier inférieur ;

• mt_rand($min, $max) : le bon vieux mersenne twister qui génère un entier entre $min et $max ;

• mt_srand($seed) : initialise le générateur mersenne avec la valeur de graine $seed ;

7.3 Fonctions de conversion de base :• base_convert($nombre,$Frombase, $tobase) : permet de convertir un nombre d’une base à l’autre. Les

bases possibles vont de 2 à 36 (!), renvoie le nombre converti ;

• bindec() : convertit un binaire en nombre décimal ;

• decbin() : facile à deviner ...;

• dechex() : facile à deviner ...;

• decoct() : facile à deviner ...;

• bin2hex() : facile à deviner .... (ATTENTION Lors de mes tests, je ne suis pas parvenu à comprendre lecomportement de la fonction );

7.4 Les fonctions de dates :Elles sont en bonne partie basée sur l’idée de chaine de format qui est une chaine de caractère correspondant auformat de la date telle qu’on la souhaite. Exemple la chaine de format ”d-m-Y” formatte la date du 15 juin 2010comme 15-06-2010, ou encore ”m/d/Y” la formatte à l’anglaise. Pour avoir l’heure, les minutes et les secondes,on peut choisir la chaine de format suivant ”s-i-H” et 19h54et 20 s sera alors représenté comme : 20-54-19. Anoter les formattages suivants : ”W” qui donne le numéro de la semaine dans l’année, ”Z” le jour dans l’année,”l” le jour de la semaine en anglais, ”u” les microsecondes, ”e” l’identifiant du fuseau horaire, etc.

ATTENTION : un Timestamp ne peut pas représenter une date aude-là de 2038 !!PHP gère en fait deux types de dates : les timestamp issues du monde UNIX (nb de secondes écoulées depuis

le 1er janvier 1970) et les objets dates apparus depuis php 5.2

• date($format,$timestampUNIX) : renvoie à partir d’un timestampUNIX une date sous forme dechaine formaté au format $format ;

• strtotime($dateISO) : convertit une date ISO ( par exemple ”d-m-Y,hh:mm:ss” mais ce n’est pas le seulformat accepté loin s’en faut) en timestamp UNIX ; ATTENTION certains formats pourtant courantsne sont pas des formats ISO car la fonction attend un format texte de date et la notation numérique”MM-JJ-YYYY” n’en est pas un. Faire donc attention en utilisant cette fonction ;

24

Page 25: RelectureDePhp5_2011_01_29

• mktime(heure,minute, seconde,mois,jour, annee) : renvoie un timestamp en fonction des argumentspassés (un septième argument permettait de prendre en compte l’heure d’été et d’hiver mais cette versionde la fonction est deprecated ;

• strftime(format,TimestampUNIX) : fait pareil que strtotime mais prend en compte les paramètresde localisaton (fixé avec setlocale() pour formater les dates avec le vocabulaire de la langue en locale.ATTENTION la chaine de format utilisé par strftime diffère des autres fonctions car chaque format DOITêtre précédé du symbole %.

• checkdate(mois, jour, annee) : renvoie TRUE si le triplet jour/mois/annee est cohérent et FALSEsinon ;

• time() : renvoie le timestamp courant (càd en s). Equivalent du now de VB ;

• microtime() : renvoie le timestamp avec en plus le millionnième de seconde depuis l’heure en cours (??);

• filectime($CheminDufichier) :permet de récupérer la date (sous forme de TimeStamp) à laquelle unfichier a été crée...

7.5 Fonctions Réseau

• checkdnsrr($Host,$HostType) : permet de vérifier l’existence d’une adresse IP, d’un nom de domaineinternet, etc... ATTENTION : cette fonction ne marche pas sur Windows mais il est possible de créer sapropre fonction :

function MyCheckDNSrr($hostname)

{

if (empty($hostname)===FALSE)

{

exec(”nslookup $hostname”,$result);

foreach($result as $line)

{

echo ”<br> $line”;

if (preg_match(” ’$hostname” ’,$line))

{

return true;

}

};

return false;

}

}

A propos de la commande nslookup, elle permet d’obtenir des information sur les noms de domaine existants:

Comment utiliser la commande NSLookup

25

Page 26: RelectureDePhp5_2011_01_29

Vous êtes ici : Accueil > Pas à pas > Comment utiliser la commande NSLookupPublié le 30/06/2008 vers 13h par : Saïda AZIRINSLookup (Name Server Lookup) est une commande qui permet de tester la résolution des noms d’hôtes en

adresses IP et inversement. Elle permet un rapide diagnostique des problèmes de résolution DNS.Lorsque vous tapez nslookup dans une invite de commande, le nom d’hôte et l’adresse IP du serveur DNS

sont affichées par défaut .

Lorsque l’on saisie un nom d’hôte ou un FQDN, une adresse IP est renvoyée.

De même si vous saisissez une adresse IP, nslookup renvoie le FQDN.

De plus Nslookup indique si la réponse fait autorité ou non sur le domaine.Une réponse faisant autorité (authoritative answer) signifie qu’une zone dns (donc un fichier sur le serveur

DNS auquel on est connecté) contient le domaine sur lequel on effectue la requête et qu’il n’y a donc pas besoinde récupérer l’information auprès d’une autorité racine ou de transférer la requête à un redirecteur.

Une réponse ne fait pas autorité ( Non-authoritative answer) signifie que que l’information à été récupéréedepuis un serveur DNS qui ne fait pas autorité sur la zone.

Dans cet exemple, nslookup fournit le nom et les adresses IP des serveurs de google.fr.Par défaut la commande nslookup interroge le serveur DNS sur les enregistrements de type A (mappage

entre un nom d’hôte et une adresse IPv4). Il est possible d’interroger le serveur DNS sur divers enregistrementen utilisant la commande Set type = xx (remplacer xx par l’un des types suivants : MX, NS, A, SOA, CNAME,hinfo, any).

Une fois qu’on change le type de requête, les enregistrements retournés restent sur le type spécifié. Pourrevenir sur les enregistrements de type A, tapez : Set-type = A ou fermer la fenêtre nslookup.

1.1 Set-type = MXRecueillir des informations sur les enregistrements MX (serveurs de messagerie) :> set type=MX> gmail.com

26

Page 27: RelectureDePhp5_2011_01_29

* Les deux premières lignes indique le nom et l’adresse IP du serveur DNS local.* Les cinq lignes suivantes montrent que le domaine gmail.com à 5 enregistrements MX. Les enregistrements

MX possèdent une priorité, également appelé coût. Les mails sont envoyés au serveur ayant le coût le plus faible(5). S’il n’est pas joignable, les mails sont envoyés aux serveurs avec le coût juste au dessus (10).De même s’ilsne sont pas joignables, les serveurs avec un coût de 50 seront contactés.

* Les dernières lignes montrent les adresses IP des serveurs MX distants. ( On remarque les mêmes serveursdans les 5 lignes précédentes).

1.2 Set type =NSL’enregistrement de ressource de type NS (Name Server) identifie les serveurs DNS de la zone DNS. Permet

d’identifier le Serveur de noms autoritaire pour un domaine ou l’enregistrement NS du domaine.> Set type = NS> microsoft.com

1.3 Set type =SOA> Set type =SOA> microsoft.com

27

Page 28: RelectureDePhp5_2011_01_29

* Primary Name Server : Nom du serveur qui héberge actuellement la zone DNS principale et qui fait autoritésur la zone DNS. Le nom doit être un FQDN.

* Responsible mail addr : indique l’adresse email de la personne responsable de la zone. Attention, l’adressedoit être de type user.exemple.lanet non [email protected].

* Serial : Il s’agit d’un numéro de série en 32 bit qui s’incrémente à chaque fois qu’une modification intervientsur le serveur. Ce numéro est de type : YYYYMMDDnn (Année, mois, jour, version). (Changement intervenu le24 Juin 2008 en version 1). Si il y d’autre modification dans la même journée, le numéro de version s’incrémente.

* Refresh : Le nombre de seconde depuis que le second serveur à reçu une copie de la zone. Entre parenthèseest indiqué le temps avant la prochaine vérification pour obtenir une nouvelle copie.

* Retry : Nombre de seconde que le premier serveur doit attendre dans le cas ou un refresh à échoué, avantd’effectuer un nouveau refresh avec un second serveur DNS.

* Expire : Nombre de seconde avant qu’un serveur secondaire ne considère ses informations de zone commen’étant plus autoritative. Si la copie que le serveur détient est plus vieille que 28 jours, elle est considérée commeinvalide.

* Default TTL (Time To Live) = Nombre définit en seconde, qu’un enregistrement de zone est valide dansle cache.

1.4 Set type =PTR> Set type =PTR> Adresse IPLes enregistrements PTR(PoinTeRs, pointeurs ) sont des requêtes inversés vers des noms d’hôtes.1.5 Set type =A> set type=A> nom d’hoteLes enregistrements A(Adresses lookups) constituent le type de requêtes par défaut lorsqu’on lance nslookup.

Ce sont requêtes directes, nom vers adresse IP.1.6 Set type =CNAME> set type=CNAME> nom d’hôteDNS permet d’attribuer des alias à une machine. Les enregistrements CNAME (Canonical NAME) sont

utiles pour renvoyer le nom principal d’une machine.1.7 Set type =AAAAIl est également possible d’obtenir des adresses IPv6 :> set type=AAAA ou bien set type=any> www.kame.net/

28

Page 29: RelectureDePhp5_2011_01_29

1.8 Set type =ANYANY est utilisé pour retrouver tous les enregistrements.

Bon voilà pour ce petit laius sur la fonction nslookup.

• dns_get_record($hote) : lit les données DNS associées à un hote passé en argument. Pb : cette fonctionne marche pas sous Windows ;

IL FAUT REVOIR LES FONCTIONS RESEAU !!!

29

Page 30: RelectureDePhp5_2011_01_29

7.6 Fonctions de chiffrement

Deux types de fonctions de chiffrements :

1. Les fonctions de hachage : qui encrypte une donnée sans que le décryptage soit possible en sens inverse.ça sert à 3 choses :

(a) Le stockage de mot de passe : seuls le hachage d’un mot de passe est stocké et non le mot de passelui-même ;

(b) La création d’identifiant :

(c) la vérification d’intégrité d’un fichier téléchargé

2. Les fonctions de codage/décodage :

7.6.1 Les fonctions de hachage :

• crc32($FileouString) : transforme l’input en entier signé. Attention pour échanger avec un autre prgmque php ou pour l’afficher, il faut convertir cet entier signé en non-signé (du coup, la valeur non-signé peutaller deux fois plus, et il ne suffit pas de multiplier par -1 la valeur de sortie). Algo trop facile à casser.Utile uniquement pour le contrôle de fichier. ATTENTION dans le cas d’un fichier, pour hacher le fichieret non son nom, il faut écrire crc32(file_get_contents($NomDuFile));

• md5($FileouString,optional $Raw ) : pareil mais avec un algo plus complexe et utilisable pour lehachage de mot de passe. affiche une chaine héxadécimale en sortie de 32 caractères. Si l’option $Raw estmis à TRUE, la fonction affiche l’équivalent en binaire !! (c’est donc plus long...) ;

• md5_file($CheminFile) : fait l’équivalent de md5(file_get_contents($NomDuFile))

• sha1() et sha1_file() : strictement pareil que md5() et md5_file();

• crypt($FileouString,$Alea) : algo de hachage qui utilise en sus du string à hacher un nombre aléatoirepour rendre plus difficile la découverte du mot de passe. ATTENTION : l’algo interne est initialementl’algo DES qui est pourri. Suivant le système, on peut forcer le recours aux algos md5 ou blowfish, enutilisant respectivement une chaine $alea commençant par $1$ et comportant 12 caractères ou $2$ etcomportant 16 caractères ($x$ comptent les 12 ou 16 caractères) ; Si un password est bon, le code phpsuivant :

crypt($password, $Alea) == $Alea renvoie TRUE

7.6.2 Fonctions de codage/décodage :

Toutes les fonctions correspondantes sont dans le module php mcrypt mais en fait la plupart des problématiquesde codage/encodage interviennent dans le cadre de la communication réseau. Or ces communications sont géréesdirectement et de manière transparente quand les adresses URL commencent par https et quand les socketsréseaux sont préfixés par ssl://. Apache s’occupe tout seul comme un grand de ce bazar et donc pas de questionsà se poser et le module mcrypt n’est pas directement utile...

30

Page 31: RelectureDePhp5_2011_01_29

7.7 Fonctions d’exécution de code

Fonction à l’arrêt d’un script :

• register_shutdown_function(’ChaineNomFunction’, optional $Param1DeLaFonction, optional$Param2DeLaFonction, ...) : Exécute à la fin d’un script php la fonction de nom ChaineNomFunction.IMPORTANT : Si la fonction à éxécuter est une fonction membre d’une classe, il faut passer un tableaucontenant en [0] le nom de la classe et en [1] le nom de la fonction membre ;

UN TRUC QUI TUE : Il est possible de générer du code php dynamiquement au sein de php !!!La fonction php qui permet cela est la fonction eval()

• eval($CodeChaine) : Cette fonction exécute le bout de code présent dans la chaine $CodeChaine jusqu’àla fin de la chaine $CodeChaine ou jusqu’au premier return. ATTENTION : Tous les caractères spéciauxPHP doivent être échappés dans la chaine sous d’échec de la fonction eval ; BREF UNE TUERIE !!

Un exemple de gestion d’entrée/sortie de mot de passe est donné au chapitre 7 aux pages 172 et 173.

8 Chapitre 8 : Formulaires et Superglobales

Enfin !! Ce chapitre traite de la gestion des entrées-sorties de donnés au travers de la page web !

Note à propos du paramètre register_globals dans php.ini : Cette option mis à on établit un référencemententre certaines variables globales de PHP et les données de la page web. Idéal pour les flemmards mais dangereuxcar l’utilisateur peut alors venir taper plus facilement le coeur de php et donc du serveur d’où un sérieux soucide sécurité.

Le coeur des formulaires web est lié au HTML et donc on va se taper un petit bout d’apprentissage HTML :

8.1 HTML8.1.1 Les caractères spéciaux d’HTML

Les principaux caractères sont ’<’, ’>’ et ’&’. Les deux premiers car ce sont les balises ouvrantes et fermanteset le dernier car c’est le caractère spécial qui aide à définir une entité HTML (Les entités HTML permettentl’affichage de caractères spéciaux).

Pour échapper cette caractère et permettre l’affichage tel quel ces caractères, il faut avoir recours à lafonction htmlentities(). Enfin pour échapper les guillemets et apostrophes, il faut utiliser htmlentities($Chaine,ENT_QUOTES)

L’USAGE de HTMLEntities vaut surtout quand on cherche à afficher une chaine dans un attribut HTML.ATTENTION : DANS UNE BONNE PART DES EXEMPLES ET DES EXERCICES, l’appel à htmlentities

a volontairement été squizzé pour alléger la lecture.ENFIN pour l’affichage des entités HTML &xxx, si la suite de caractères &xxx ne correspond à rien, les

caractères &xxx seront affichés et non le caractère spécial qui correspondrait à &xxx

31

Page 32: RelectureDePhp5_2011_01_29

8.1.2 Les Formulaires HTML

Un formulaire HTML est créer à partir des balises <form> et </form>. Toutes les instructions délimitées parces deux balises constituent le bloc de définition du formulaire

La balise <form> intègre plusieurs arguments potentiels :<form action =”fichier.php” method =”GET ou POST” enctype = ”MethodeEncodagePourEnvoiDeFichier>

• action : défini le fichier ouvert une fois le bouton d’envoi du formulaire appuyé (attention ce bouton nefigure pas dans l’instruction form mais doit apparaitre kekpart dans le bloc ;

• method : GET signifie que les données saisies dans le formulaire sont envoyés par URL tandis que POSTenvoie les données dans des supervariables qui sont ensuite tapables par php ;

• enctype : quand on souhaite un fichier via un formulaire, il est nécessaire de renseigner la méthoded’encodage du fichier envoyé en l’occurrence, souvent ’multipart/form-data’.

Dans le bloc du formulaire, on peut trouver :

• des champs de saisie de texte définis à l’aide de la balise <input type =’text’>. En ajoutant dansla balise

– value=’ValeurParDéfaut’, on permet l’affichage d’une valeur par défaut ;

– size = ’XX’ permet d’ajuster la taille du champs de saisie à XX caractères ;

– le paramètre name permet de donner un titre au formulaire name : et permet ensuite à PHPd’identifier la zone de texte ;

<input type =’text’ value =’texteAffichéParDéfaut’ size = XX name =’Titre du formulaire’>

• des zones de texte : ça marche avec les balises <textarea> </textarea>. Les paramètres associésà la première balise sont :

– name : permet ensuite à PHP d’identifier la zone de texte ;

– rows = ’xx’ pour définir le nombre de lignes ;

– cols = ’yy’ pour définir le nombre de colonnes ;

– les valeurs par défaut de chaque ligne sont écrites comme un texte HTML classique mis entre lesbalises <textarea> et </textarea> ;

< textarea name =’NomDeTextArea’ cols = ’50’ rows =’4’>LigneParDefaut1

...

LigneParDefaut2

</textarea>

• des cases à cocher : la balise correspondante est <input type =’checkbox’> et fait apparaître unecase à cocher, l’attriibut value permet de cocher la case par défaut càd en mettant l’attribut checked=’checked’. IMPORTANT : si on souhaite lier plusieurs boutons à cocher, on doit donner comme valeurà l’attribut name un nom de tableau càd un nom suivi de [] et ce nom doit être le même pour toutes lescases qu’on souhaite lier.

32

Page 33: RelectureDePhp5_2011_01_29

• des boutons radio (groupe de boutons à cocher et à a choix unique exclusif) : cela fonctionne comme lescases à cocher sauf que tous les boutons partageant un même nom sont à choix exclusif càd un seul d’entreeux peut être cocher ; Une ligne peut être sélectionné par défaut : il faut ajouter l’attribut selected sanslui passer une quelconque valeur (càd pas de =’kekchose’).

• Des boutons de validation avec <input type =’submit’ value =’texteDuBouton’ name =’NomPHPDuBouton’ > ;

• des listes déroulantes : La balise est alors <select> puis </select>. Chaque ligne sélectionnablede la listed déroulante s’écrit encadré des balises <option name= ’...’ value =’X’> textdeLaligne</option>. Notons que ce que récupére le serveur est la valeur de l’attribut value et non pas TextDe-LaLigne. Une ligne peut être sélectionné par défaut : il faut ajouter l’attribut selected sans lui passerune quelconque valeur (càd pas de =’kekchose’. Si l’attribut size est de valeur supérieure à ’1’, la listen’est plus déroulante mais une liste simple avec une barre de défilement vertical si le nb de possibilitésexcède la valeur de l’attribut size. Si par aileurs l’attribut multiple et lui aussi sans valeur nécessaire -est ajouté à la balise <select >, il est possible de choisir plusieurs éléments de la liste.

ATTENTION : Tous les choix par défauts ne sont affichés qu’au premier affichage de la page, un simplerefresh ne remet pas les valeurs par défauts.

ATTENTION : tout appel à un affichage au sein d’un bloc balise <select> </select> ne produit aucunaffichage !

• des champs cachés : cela sert à stocker des infos cachés à l’affichage. Cette information est cepen-dant visible en éditant le code HTML. La balise est <input type =’hidden’ name =’xxx’ value=’chainecachée’>. cela sert surtout à sauvegarder l’information issue d’une ou de plusieurs pages précé-dentes pour pouvoir restituer l’affichage de ces pages si retour en arrière il y a...

• des champs de mot de passe : la balise est <input type =’password’ name =’mot_de_passe’size =8 value =’valeurpardefautdumotdepasse’> ;

• des images cliquables : avec la balise <input type =’image’ name =’imagePetra’ src = ’Chem-inVersImage’ alt =’texteAfficherEnCasPbAvecImage’ >. Quand l’image est cliqué, le navigateurrenvoie les coordonnées de la position du clic avec le formulaire. Note : les attributs height et width de labalise permettent de redimensionner l’image.

• des interfaces d’envoi de fichier : ATTENTION : pour l’utilisation de cette interface dans un formu-laire, il est nécessaire d’ajouter un attribut enctype de valeur ’multipart/form-data’ à la balise <form...> est alors <input type =’file’ ... >

ATTENTION quand une balise de bouton ne se trouve pas dans une balise de formulaire, cliquer dessus ne sertà rien car le script php associé est définie dans la balise de formulaire.

8.2 Principes d’interaction entre les formulaires HTML et leurs scripts de gestioncorrespondants :

Chaque formulaire intègre un attribut action qui spécifie quelle action est éxécuté dès lors qu’une action devalidation est lancée. Ce lancement intervient dès lors qu’une balise de bouton appartenant au bloc de balisesdu formulaire est cliqué. ça peut être aussi bien un bouton qu’un image cliquable. L’action déclenché peut-êtreun lien vers une page html, une image, une adresse mail (ce qui en fait provoque l’ouverture d’un mail enécriture à destination de l’adresse spécifiée).

33

Page 34: RelectureDePhp5_2011_01_29

Quand l’action est un script php, le script php correspondant est le script de gestion du formulaire.Comment récupérer les valeurs renseignés dans les formulaires dans le script de gestion.Simple, dans php, il existe des tableaux de variables dites superglobales qui se comportent comme des

variables php globales dans le script de gestion désigné par l’attribut action du formulaire.Les tableaux recensant les variables contenant les valeurs renseignés dans un formulaire sont :

• $_GET[] : contient toutes les données envoyées par l’URL ;

• $_POST[] : contient les données envoyées par un formulaire quand l’attribut method vaut ’POST’ ;

• $_FILE[] : contient les informations sur les fichiers envoyés par l’utilisateur ;

• $_REQUEST : qui est une fusion des tableaux $_GET, $_POST et de $_COOKIE (voir plus loin pource dernier) ;

Chaque élément d’un tableau correspond à un des objets de saisie du formulaire à partir duquel les données auscript désigné par l’attribut action du formulaire.

La clef de chaque élément correspond à l’attribut name de l’objet de saisie HTML, et la valeur associécorrespond à l’attribut value.

Il faut savoir que pour :

• une liste déroulante ou pas à choix unique, l’élément dans $_REQUEST est unique et renvoie la value dela balise sélectionnée en choix ;

• idem pour un groupe de bouton radio ;

• une liste avec l’attribut multiple renvoi comme élément dans $_REQUEST un tableau comportant autantd’éléments que le nombre sélectionné.

• une image cliquée renvoie un tableau à deux dimensions avec l’abcisse d’abord puis l’ordonnée en nombrede pixels, ces deux données ne pouvant donc respectivement pas excéder la taille spécifiée par les attributswidth et height ;

• Pour savoir, si une variable est présente dans les tableaux $_GET,$_POST etc. Utiliser la fonctionisset($NomDeLaVariableCherchee) est la meilleure solution ;

A propos des chaines récupérés via un formulaire pour ensuite être affiché à nouveau dans un page HTML,notamment par exemple dans une zone de texte. Un retour à la ligne saisie dans une zone de texte est codécomme ’\n’ mais réécrit dans une page html, le saut à la ligne est ignoré car il faut à la place mettre unebalise <br>. Pour ça, après récupération d’une chaine dans un tableau de superglobales, si on souhaite afficherderrière cette chaine et seulement dans ce cas, il faut convertir les caractères classiques ’\n’ de sauts de ligneen balise HTML correspondante. Pour cela, l’affichage de la chaine PHP peut être fait en utilisant la fonctionnl2br($ChaineAAfficher). On écrira alors :

echo ”<br>”.nl2br($Chaine);

au lieu de

echo ”<br> $Chaine”;

Truc : si la chaine rentrée dans le formulaire intègre les balises <br> au lieu de retour à la ligne ’\n’ classiques,l’affichage initiale sera avec les <br> mais l’affichage finale sera avec retour à la ligne apparent si l’informationinitiale n’est pas filtré des balises HTML. ATTENTION : Il vaut mieux filtrer (càd ) les balises contenus dansles chaines entrées dans les formulaires.

34

Page 35: RelectureDePhp5_2011_01_29

8.3 Les fonctions de filtrage des données à la récupération dans les tableaux desvariables superglobales

Le hic avec les tableaux de var superglobales, c’est que si on connait certes les clefs et donc les noms desvariables, on peut avoir des surprises sur le type et le contenu de la valeur de chaque superglobales si on nefiltre pas.

Le principe est donc de ne pas récupérer les variables bourrinement en utilisant une instruction du type :

$VarPhp = $_REQUEST[’NomDeLaClefSouhaitee’]

car on s’expose à des risques d’attaques par injection, à des incohérences de types et ou autre problème.La solution est de :

• systématiquement avoir une idée minimale du type de la variable attendue dans le tableau superglobales

• d’utiliser une fonction de filtrage qui assure que le type de la donnée est bien celle attendue ;

• paramétrer la fonction de filtrage correctement pour s’assurer que la chaine contenu dans la valeur associéeà la clef associée au tableau ne contient pas de caractères incorrectes.

Pour cela, le module d’extension Filter Php est d’une grande utilité :Le filtrage peut être fait à deux niveaux :

• soit au niveau des contenus des variables php qui ont servi à lire dans les superglobales ;

• soit directement au niveau des superglobales.

La deuxième solution est considérée comme la plus cleanLes fonctions utiles de l’extension filter sont :

• filter_has_var($CONSTANTESOURCE, $ClefdelaSuperGlobale) : ça revient à faire un issetsur le nom de la clef dans le tableau de superglobales désigné par $CONSTANTESOURCE. Les possibilitéspour $CONSTANTESOURCE sont :

– INPUT_GET : désigne le tableau superglobale $_GET ;

– INPUT_POST : désigne le tableau superglobale $_POST ;

– INPUT_COOKIE : désigne le tableau superglobale $_COOKIE ;

– INPUT_REQUEST : désigne le tableau superglobale $_REQUEST ;

– INPUT_SESSION : désigne le tableau superglobale $_SESSION ;

– INPUT_ENV : désigne le tableau superglobale $_ENV ;

– INPUT_SERVER : désigne le tableau superglobale $_SERVER ;

– Notons que le tableau superglobal $_FILE ne fait pas partie de la liste

• filter_input($CONSTANTESOURCE,$ClefDeLaValeurAFiltrer,$FILTRE, $OPTIONDEFILTRAGE): Filtre la valeur de la superglobale de clef $ClefDeLaValeurAFiltrer se trouvant dans le tableau super-globale désigné par $CONSTANTESOURCE. ATTENTION : cette fonction ne va fonctionner que dansle cas où la variable superglobale est unidimensionnelle et n’est donc pas un TABLEAU associatif (l’autre

35

Page 36: RelectureDePhp5_2011_01_29

possibilité). Dans le cas contraire, elle renverra FALSE. Il vaut mieux s’assurer à l’avance si la clef recher-chée correspond à un tableau associatif. La nature de $FILTRE est variable : elle peut correspondre àune de constantes entières ci-dessous. Elle peut correspondre à une combinaison de constantes entières, cequi revient à cumuler les filtres. Dans ce cas, $FILTRE vaut plusieurs constantes entières séparés de | desorte qu’on peut avoir :

FILTER_SANITIZE_STRIPPED|FILTER_SANITIZE_NUMBER_FLOAT

Enfin si on souhaite avoir un filtrage intégrant des options de filtrage plus complexe, $FILTRE peut êtredéfini comme un tableau associatif (voir la fonction filter_input_array pour la spécification du tableauassociatif). Les valeurs possibles pour $FILTRE sont de trois types :

– Les filtrages de validation qui ne modifie pas la valeur en entrée :

∗ FILTER_VALIDATE_EMAIL : vérifie que la valeur est celle d’une syntaxe d’adresse mail (maisl’adresse peut ne pas exister par ailleurs) ; Pas de flag possible.

∗ FILTER_VALIDATE_URL : vérifie que la valeur correspond à une syntaxe d’URL ; Flagspossibles :

· FILTER_FLAG_SCHEME_REQUIRED - Requires URL to be an RFC compliant URL(like http://example)

· FILTER_FLAG_HOST_REQUIRED - Requires URL to include host name (like http://www.example.com)· FILTER_FLAG_PATH_REQUIRED - Requires URL to have a path after the domainname (like www.example.com/example1/test2/)

· FILTER_FLAG_QUERY_REQUIRED - Requires URL to have a query string (like ”exam-ple.php?name=Peter&age=37”)

∗ FILTER_VALIDATE_REGEXP : vérifie que la valeur correspond à une expression rationnellecompatible PERL (connait pas le PERL de toute façon...) ; Options possibles :

· regexp - specifies the regular expression to validate against∗ FILTER_VALIDATE_INT : vérifie que la valeur correspond à un nombre entier ; Options etflags possibles :

· * min_range - specifies the minimum integer value· * max_range - specifies the maximum integer value· * FILTER_FLAG_ALLOW_OCTAL - allows octal number values· * FILTER_FLAG_ALLOW_HEX - allows hexadecimal number values

∗ FILTER_VALIDATE_FLOAT : vérifie que la valeur correspond à un nombre double ; Pasd’options et filtres possibles ??

∗ FILTER_VALIDATE_BOOLEAN : vérifie que la valeur correspond à un booléen ( ”0”, ”false”,”off”et ”no” ou ”1”, ”true”,”on” et ”yes” );

∗ FILTER_VALIDATE_IP : vérifie que la valeur correspond à une syntaxe d’adresse IP (maisl’adresse peut ne pas exister par ailleurs) ; Flags possibles :

· * FILTER_FLAG_IPV4 - Requires the value to be a valid IPv4 IP (like 255.255.255.255)· * FILTER_FLAG_IPV6 - Requires the value to be a valid IPv6 IP (like 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)· * FILTER_FLAG_NO_PRIV_RANGE - Requires the value to be a RFC specified privaterange IP (like 192.168.0.1)

· * FILTER_FLAG_NO_RES_RANGE - Requires that the value is not within the reservedIP range. This flag takes both IPV4 and IPV6 values

– Les filtrage de conversion qui peuvent modifier la valeur en entrée :

36

Page 37: RelectureDePhp5_2011_01_29

∗ FILTER_SANITIZE_STRIPPED : retire les balises HTML ; flags possibles :· * FILTER_FLAG_NO_ENCODE_QUOTES - This flag does not encode quotes· * FILTER_FLAG_STRIP_LOW - Strip characters with ASCII value below 32· * FILTER_FLAG_STRIP_HIGH - Strip characters with ASCII value above 32· * FILTER_FLAG_ENCODE_LOW - Encode characters with ASCII value below 32· * FILTER_FLAG_ENCODE_HIGH - Encode characters with ASCII value above 32· * FILTER_FLAG_ENCODE_AMP - Encode the & character to &amp;

∗ FILTER_SANITIZE_STRING : autre nom du filtre précédent en fait...∗ FILTER_SANITIZE_EMAIL : retire tous les caractères foireux qui ne devraient pas se trouverdans une adresse mail (ce qui veut dire qu’il laisse notamment $-_.+!*’{}|^~[]‘#%/?@&= ;

∗ FILTER_SANITIZE_SPECIAL_CHARS : transforme les caractères spéciaux HTML et XMLen entités HTML càd This filter is used to escape ”<>& and characters with ASCII value below32 ; les flags possibles sont :

· * FILTER_FLAG_STRIP_LOW - Strip characters with ASCII value below 32· * FILTER_FLAG_STRIP_HIGH - Strip characters with ASCII value above 32· * FILTER_FLAG_ENCODE_HIGH - Encode characters with ASCII value above 32

∗ FILTER_SANITIZE_NUMBER_INT : retire tous les caractères qui n’ont rien à voir avecun nombre entier ; A noter l’absence des flags et options du filtre VALIDATE équivalent ;ATTENTION : ce qui est filtré n’est pas nécessairement un nombre et peut aussi bien être uneexpression mathématique. Il faut comprendre que les filtres prennent des chaines de caractèresen argument et renvoie des chaines ! Ainsi ”5-2f+3pp” filtré donnera ”5-2+3” ;

∗ FILTER_SANITIZE_NUMBER_FLOAT : retire tous les caractères qui n’ont rien à voir avecun nombre à virgule flottante ; Ainsi ”5-2f+3.3pp” filtré donnera ”5-2+3.3” ; flags possibles :

· * FILTER_FLAG_ALLOW_FRACTION - Allow fraction separator (like . )· * FILTER_FLAG_ALLOW_THOUSAND - Allow thousand separator (like , )· * FILTER_FLAG_ALLOW_SCIENTIFIC - Allow scientific notation (like e and E)

∗ FILTER_SANITIZE_URL : retire tous les caractères qui n’ont rien à voir avec la syntaxe d’uneadresse URL ; This filter allows all letters, digits and $-_.+!*’(),{}|\\^~[]‘”><#%;/?:@&= ; Pasde flags possibles ; ”http://www.w3schooåøls.coøm” filtré renvoie ainsi ”http://www.w3schools.com”;

∗ FILTER_SANITIZE_ENCODED : retire tous les caractères étendues d’une URL en leur équiv-alent %xx (cette écriture correspond à l’affichage des caractères hors ASCII quand on veut les af-ficher dans une URL. A VERIFIER) ; It works a lot like the urlencode() function. ”http://www.w3schools.com”filtré devient ainsi ”http%3A%2F%2Fwww.w3schools.com” ; flags possibles :

· * FILTER_FLAG_STRIP_LOW - Strip characters with ASCII value below 32· * FILTER_FLAG_STRIP_HIGH - Strip characters with ASCII value above 32· * FILTER_FLAG_ENCODE_LOW - Encode characters with ASCII value below 32· * FILTER_FLAG_ENCODE_HIGH - Encode characters with ASCII value above 32

∗ FILTER_SANITIZE_MAGIC_QUOTES : applique la fonction addslashes() (qui opères surles caractères spéciaux propres au PHP, càd ’ ” \ ) et opère un échappement des caractèresempêchant les injections sql comme le faisait l’option magic_quotes_gpc dans php.ini ;

∗ FILTER_CALLBACK : calls a user defined function to filter the value. exemple :function convertSpace($string){return str_replace(” ”, ”_”, $string);

37

Page 38: RelectureDePhp5_2011_01_29

}$string = ”Peter is a great guy!”;echo filter_var($string, FILTER_CALLBACK,array(”options”=>”convertSpace”));

Résultat : Peter_is_a_great_guy!

A noter que la fonction passé en argument peut être une fonction php dès lors qu’elle renvoieune string. Par exemple : strtoupper qui donnerait :

PETER IS A GREAT GUY!

En ce qui concerne les options de filtrage (autrement appelés dans la doc php FLAGS), on peut avoir

• filter_input_array($CONSTANTESOURCE,$TableauDuFiltrage) : pareil a priori que la fonc-tion précédente mais permet de filtrer les superglobales. Contrairement à la fonction précédente, l’argument$TableauDuFiltrage définit les clefs cherchés, le filtre et les flags/options associées au filtre.

Les tableaux associatif intervenant comme argument dans la fonction filter_input et filter_input_array :Un tableau associatif est utilisé comme argument dans la fonction filter_input pour spécifier une série de

flags et d’options de filtrage. Dans la fonction filter_input_array, le tableau associatif spécifie aussi les clefsrecherchés. Dans ce cas, les clefs du tableau sont les noms des variables qu’on cherche à filtrer

Qu’est ce qu’un flag ?Un flag permet d’affiner le filtrage choisi. Ainsi un filtre FILTER_SANITIZE_NUMBER_FLOAT avec un

flag FILTER_FLAG_ALLOW_THOUSAND renvoie un double avec une virgule mise comme séparateur desmilliers : Les flags possibles sont les suivants :

• FILTER_FLAG_NONE : Aucun drapeau ;

• FILTER_REQUIRE_SCALAR : Drapeau utilisé pour requérir un scalaire en entrée ;

• FILTER_REQUIRE_ARRAY : Requière un tableau en entrée ;

• FILTER_NULL_ON_FAILURE : Utilisation de NULL au lieu de FALSE si une erreur survient ;

• FILTER_FLAG_ALLOW_OCTAL : Alloue une notation octale (0[0-7]+) dans le filtre ”int”.

• FILTER_FLAG_ALLOW_HEX : Alloue une notation hexadécimale (0x[0-9a-fA-F]+) dans le filtre ”int”.

• FILTER_FLAG_STRIP_LOW : Supprime les caractères dont les valeurs ASCII sont inférieures à 32.

• FILTER_FLAG_STRIP_HIGH : Supprime les caractères dont les valeurs ASCII sont supérieures à 127.

• FILTER_FLAG_ENCODE_LOW : Encode les caractères dont les valeurs ASCII sont inférieures à 32.

• FILTER_FLAG_ENCODE_HIGH : Encode les caractères dont les valeurs ASCII sont supérieures à 127.

• FILTER_FLAG_ENCODE_AMP : Encode &, càd échappe le ”et commercial”.

• FILTER_FLAG_NO_ENCODE_QUOTES : N’encode pas ’, ni ”. càd n’échappe pas ces caractères ;

• FILTER_FLAG_EMPTY_STRING_NULL : pas utilisé par le module FILTER ;

• FILTER_FLAG_ALLOW_FRACTION : Alloue une partie fractionnelle dans le filtre ”number_float”(?)

38

Page 39: RelectureDePhp5_2011_01_29

• FILTER_FLAG_ALLOW_THOUSAND : Alloue une notation scientifique (e, E) dans le filtre ”num-ber_float”.

• FILTER_FLAG_SCHEME_REQUIRED : Schéma requis dans le filtre ”validate_url”.

• FILTER_FLAG_HOST_REQUIRED : Hôte requis dans le filtre ”validate_url”.

• FILTER_FLAG_PATH_REQUIRED : Chemin requis dans le filtre ”validate_url”.

• FILTER_FLAG_QUERY_REQUIRED : Requête requise dans le filtre ”validate_url”.

• FILTER_FLAG_IPV4 : N’autorise que les adresses IPv4 dans le filtre ”validate_ip”.

• FILTER_FLAG_IPV6 : N’autorise que les adresses IPv6 dans le filtre ”validate_ip”.

• FILTER_FLAG_NO_RES_RANGE : N’autorise pas les adresses réservées dans le filtre ”validate_ip”.

• FILTER_FLAG_NO_PRIV_RANGE : N’autorise pas les adresses privées dans le filtre ”validate_ip”.

Qu’est ce qu’une option ?Une option permet un contrôle de la valeur filtrée. Ce contrôle s’effectue AVANT l’application du filtre

définie à l’aide de la constante de filtrage et des flags associés.

Un exemple de filtrage avec filter_input avec un tableau associatif pour les flags et options. Imaginons qu’onsouhaite filtrer une chaine contenu dans le tableau $_POST avec pour clef str et qui doit correspondre à unentier devant être compris entre 18 et 62. L’appel de la fonction se fait de la façon suivante :

filter_input(INPUT_POST,’str’, FILTER_VALIDATE_NUMBER_INT, array(”options” => array(”min_range”=> $min, ”max_range” => $max)))

Pour un filtre vérifiant une expression régulière sur un clef Exp dans la superglobale $_POST :

$expressionReguliere = voir plus haut au point des expressions régulières :filter_input(INPUT_POST,’Exp’, FILTER_VALIDATE_REGEXP, array(”options” => array(”regexp” =>

$expressionReguliere)))

A noter que dans les deux exemples précédents, il n’y a pas de FLAGS. Le filtrage sur l’entier autorisant larécupération de nombre octaux s’écrirait :

filter_input(INPUT_POST,’str’, FILTER_VALIDATE_NUMBER_INT, FILTER_FLAG_ALLOW_OCTAL,array(”options” => array(”min_range” => $min, ”max_range” => $max)))

ou

filter_input(INPUT_POST,’str’, FILTER_VALIDATE_NUMBER_INT, array(”flags” => FILTER_FLAG_ALLOW_OCTAL,”options” => array(”min_range” => $min, ”max_range” => $max)))

Dans le cas, de l’utilisation de la fonction filter_input_array si un tableau $_POST incorporant les clefsExp et str, l’appel se ferait comme suit :

filter_input_array(INPUT_POST,array(”Str” =>array(”filters” => FILTER_VALIDATE_NUMBER_INT,

”flags” => FILTER_FLAG_ALLOW_OCTAL,

39

Page 40: RelectureDePhp5_2011_01_29

”options” => array(”min_range” => $min, ”max_range”=> $max))))

REMARQUES FINALES SUR LE FILTRAGE DE DONNEES ISSUES des tableaux de superglobales :Si une clef de tableau de superglobales correspond à un tableau associatif ou tableau classique, la récupération

avec filter_input_array et filter_var_array échoue en renvoyant false sauf quand on utilise un seul filtre pourtoutes les variables du tableau superglobale.

Le mieux est alors de filtrer ces tableaux en les récupérant dans une variable propre :

Par exemple, avec le code suivant :$tabfiltree3 = filter_input_array(INPUT_POST,array(

’RadioEx’ => FILTER_UNSAFE_RAW,’Langages_maitrisees_1000_Pattes’ => FILTER_SANITIZE_STRING,’Salaire_de_Nico’ => FILTER_SANITIZE_NUMBER_FLOAT,’mot_de_passe’=> FILTER_SANITIZE_NUMBER_INT,’imagePetra_x’=> FILTER_UNSAFE_RAW,’imagePetra_y’=> FILTER_UNSAFE_RAW,’TextLionne’=> FILTER_UNSAFE_RAW));

qui filtre différentes variables issues de formulaires HTML dont Salaire_de_Nico et Langages_maitrisees_1000_Pattesqui sont des tableaux,

Le filtrage pour ces deux clefs renverra :

”Langages_maitrisees_1000_Pattes”]=> bool(false) [”Salaire_de_Nico”]=> bool(false)ce qui signifie l’échec du filtrage.

Pour parvenir au filtrage de ces deux clefs, je suggère le recours au code suivant

foreach($_POST[’Langages_maitrisees_1000_Pattes’] as $clef => $Elem){

$NewTabRecup[”$clef”]=$Elem;};

IL FAUT BIEN RETENIR que les filtres de validation NE convertisse PAS les données et donc si les donnéesne correspondent pas : aucune donnée filtrée n’est renvoyé. Seul FALSE est renvoyé. Enfin si la clef cherchédans les tableaux superglobaux n’existe pas, c’est NULL qui est renvoyé

Quand on utilise un filtre de conversion, les données sont transformés. Si cette transformation échoue, c’estFALSE qui est renvoyé. Si la clef cherchée n’existe pas, c’est NULL qui renvoyé.

8.4 La récupération sur le serveur d’un fichier envoyé via un formulaire HTML

Les fichiers peuvent être envoyés en ayant recours à une interface d’envoi de fichier présent dans un bloc HTML<form ... enctype =’multipart/form-data’> >/form> comportant dans la balise de début un attribut enctypespécifiant le type d’encodage. . L’interface se définit avec une balise :

<input type=’file’ name =’NomDuFichier’ size=’tailleDeCaseDeSaisieDuNomFichier’ >

40

Page 41: RelectureDePhp5_2011_01_29

Les fichiers sont alors acessibles en php via le tableau de superglobales $_FILES. Ce tableau est à doubleentrée, et pour accéder aux variables superglobales associées à un fichier de nom ’NomDuFichier’, il faut écrire:

$_FILES[’NomDuFichier’][’VarSuperGlobale’]

Pour chaque fichier, on peut accéder aux superglobales suivantes :

• ’name’ : nom et adresse originels du fichier côté client ;

• ’size’ : taille du fichier en nombre d’octets ;

• ’tmp_name’ : nom et adresse temporaires du fichier coté serveur. Le fichier passé par un formulaire HTMLest stocké temporairement côté serveur dans le répertoire

• ’type’ : Le type MIME du fichier téléchargé

• ’error’ : Le type d’erreur rencontré lors du téléchargement. Il s’agit d’un entier correspondant à l’énumérationsuivante :

– UPLOAD_ERR_OK (0) : Pas d’erreur de téléchargement ;

– UPLOAD_ERR_INI_SIZE (1) : Fichier plus gros que le max autorisé dans php.ini;

– UPLOAD_ERR_FORM_SIZE (2) : Fichier plus gros qu’indiqué dans le formulaire”;

– UPLOAD_ERR_PARTIAL (3) : Fichier partiellement téléchargé ;

– UPLOAD_ERR_NO_FILE (4) : Aucun fichier téléchargé (par exemple :adresse ne correspondantà rien coté serveur) ;

Du coté serveur, tous les fichiers sont téléchargés dans le répertoire tmp de wamp (càd au même niveau quele répertoire www des fichiers webs). IMPORTANT : Ce fichier est détruit à la fin de l’exécution de chaquescript. Pour conserver ce fichier côté serveur au de-là du temps d’exécution d’un script, il faut utiliser la fonctionmove_uploaded_file :

move_uploaded_file(AncienChemin, NouveauChemin) : Bien sur, les deux chemins doivent com-porter le nom du fichier. IMPORTANT :si un fichier du même nom existe déjà dans le répertoire de destination,move_uploaded_file écrasera l’ancien fichier avec le nouveau et sans avertir.

A noter que php limite la taille des fichiers avec sa directive post_max_size dans le php.ini. La valeur pardéfaut est de 8 Mo càd que dans php.ini, on trouve :

post_max_size = 8M

8.4.1 Intérêt du filtrage de données pour la sécurité

Quatre menaces résultent de la réception de données :

• Le défaçage provoquée par une injection de code HTML ;

• Le cross site scripting provoquée par une injection de code javascript ;

• L’injection sql provoquée provoquée par une injection de commande sql précédé par un guillemet non-échappé ;

41

Page 42: RelectureDePhp5_2011_01_29

• Les failles de divulgation provoqué par la réception de noms de fichiers ;

Une filtrage des données reçues est donc nécessaire au minimum pour éviter les attaques par injection et lesusages suspects de fichiers

Pour se protéger de ses menaces, la question sera largement abordée dans le chapitre 27 ayant trait à lasécurité.

8.5 Limite de temps d’éxecution d’un script PHPNormalement dans php.ini, la directive max_execution_time qui est mesuré en s fixe une limite de tempsd’éxécution à tout script php. Cependant cette limite peut être repoussé en utilisant la fonction set_time_limitSAUF php fonctionne en safe_mode qui interdit, entre autres, de manipuler les limites de temps d’exécutiondes scripts.

• set_time_limit($RabDeTempsEnSec) : La valeur passée en argument permet d’augmenter d’autant letemps d’exécution restant pour le script, et ce quelque soit la valeur de max_execution_time ; Si lemax_execution_time est de 30 alors le script a tourné pendant 15 s quand set_time_limit(25) est exécuté,le temps total maximal restant disponible pour l’exécution complète du script courant devient alors de 25s alors qu’il restait normalement 15 s. Le temps maximal disponible pour l’exécution du script est doncpassé de 30 s à 40 s ;

Deux remarques :

• Tout temps passé en dehors du script via par exemple un appel à une base de données ou à des fonctionssystem via system() n’est pas décompté dans le temps d’éxécution du script.

• Le temps d’exécution maximal d’un script peut être mis à l’infini en settant max_execution_time à 0.Attention set_time_limit(0) n’a que pour fait de forcer la fin d’un script...

• ATTENTION : le serveur web a lui-même une directive limitant le temps d’exécution d’un script php.Timeout est la directive en question pour Apache. Cette directive n’est pas activé par défaut et est mesuréeen nombre de secondes.

POUR FINIR AVEC LES FORMULAIRES, un schéma d’interaction entre client et serveur via des formu-laires est donné page 205 du livre : On peut le résumer par le jeu d’étapes suivantes :

1. Génération du premier formulaire

2. Envoi des données du premier formulaire

3. Récepion des données

4. Vérification des données

5. Si tous les champs sont valides == > Traitement des données et production des pages web suivantes

6. Sinon retour à l’étape 1 avec les données par défaut renseignés dans le premier formulaire pour les champsfoireux et les données passées correctes pour les autres champs

42

Page 43: RelectureDePhp5_2011_01_29

9 Environnement web et superglobales

Une page web se divise en deux parties majeurs :

• l’en-tête : qui contient tous les informations techniques que s’échangent le client et le serveur ;

• le contenu : qui correspond au code HTML ou php définissant ce qui est affiché sur la page web.

ATTENTION : dans une page web, toute ligne vide signifie que le bloc d’en-tête est terminé. De même toutappel à une fonction d’affichage implique que le bloc d’en-tête est terminé. A partir de ce moment, il n’est pluspossible d’appeler une fonction php agissant sur l’en-tête comme header(), set_cookie() ou session_start().

Deux tableaux de variables superglobales correspondent aux informations technique décrivant la relationclient-serveur associé à une page web :

• $_SERVER[] : qui regroupe toutes les informations concernant la réception d’une requête auprès duserveur web ;

• $_ENV[] : contient les variables d’environnement du système dans lequel tourne PHP ;

ATTENTION : Pour maitriser le sujet, il est nécessaire de maitriser comment se définit et se structureune en-tête de page HTML. C’est compliqué. Un lien vers un doc spécifiant comment fonctionneune en-tête est disponible dans la définition de la fonction header() de l’aide PHPEdit (ou surphp.net).

9.1 Les variables superglobales de $_SERVER

Le tableau superglobale $_SERVER contient les informations liées à la réception de la requête envoyé par lenavigateur et reçu par le serveur. Cette requête est envoyée via le protocole HTTP (HyperText Transfert Pro-tocol) qui définit le code HTML (HyperText Markup Language). Les informations présentes dans $_SERVERsont :

• $_SERVER[’SERVER_NAME’] : nom du serveur (potentiellement virtuel) auquel se connecte le naviga-teur client. Chez moi : toto.localhost

• $_SERVER[’DOCUMENT_ROOT’] : nom du répertoire contenant coté serveur les pages web chargéscotés client ;

• $_SERVER[’HTTP_HOST’] : Toto.localhost dans mon cas. Correspond au nom de l’hote serveur (virtuelou pas) avec lequel communique le navigateur ;

• $_SERVER[’HTTP_USER_AGENT’] : indique quel est le système d’exploitation du côté client ainsique le navigateur avec son numéro de version précis. Chez moi, ça donne : Mozilla/5.0 (Windows; U;Windows NT 6.0; fr; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 ( .NET CLR 3.5.30729)

• $_SERVER[’HTTP_ACCEPT’] : Contenu de l’en-tête Accept: de la requête courante, s’il y en a une.Chez moi : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 RIEN COMPRIS A l’explicationdu site php.net ;

43

Page 44: RelectureDePhp5_2011_01_29

• $_SERVER[’HTTP_ACCEPT_LANGUAGE’] : Contenu de l’en-tête Accept-Language: de la requêtecourante, si elle existe. Par exemple : ’fr’. Correspond aux langues acceptées ?????

• $_SERVER[’HTTP_ACCEPT_ENCODING’] : Contenu de l’en-tête Accept-Encoding: de la requêtecourante, si elle existe. Par exemple : ’gzip’. ????

• $_SERVER[’HTTP_ACCEPT_CHARSET’] : Définit les jeux de caractères acceptés coté client. chezmoi ISO-8859-1,utf-8;q=0.7,*;q=0.7 (à noter le support de l’utf-8)

• $_SERVER[’HTTP_KEEP_ALIVE’] = 115 : ????

• $_SERVER[’HTTP_CONNECTION’] : Contenu de l’en-tête Connection: de la requête courante, si elleexiste. Par exemple : ’Keep-Alive’.

• $_SERVER[’HTTP_CACHE_CONTROL’]

JE FINIRAI LA REVUE DE CES SUPERGLOBALES UNE AUTRE FOIS, SEULS ’DOCUMENT_ROOT’et ’SERVER_NAME’ ont un intérêt dans l’immédiat.

Attention la liste des superglobales dans $_SERVER n’est pas fixe et notamment les variables gérantl’authentification HTTP ne sont pas présentes. Il faut les créer au moyen de la fonction header()

• header($ChaineVarEntete, optional bool $ReplaceOrNOt, optional $CodeReponseHTTP) : La variable$ChaineVarEntete a la forme suivante :

′NomDeLaVarEntete:DomaineEnTete=ValeurDeLaVarEntete’

header() ajoute une superglobale au tableau $_SERVER qui vaut ValeurDeLaVarEntete et dont la clef

est NomDeLaVarEntete. $ReplaceOrNOt vaut TRUE par défaut ce qui signifie que deux appels de headeravec le même NomDeLaVarEntete et le même DomaineEnTete dans $ChaineVarEntete fait que la valeurde la variable NomDeLaVarEntete est écrasé avec la valeur ValeurDeLaVarEntete passée la deuxième fois.

9.1.1 L’authentification HTTP :

Cette identification s’effectue au niveau de l’en-tête et donc au sein du protocole HTTPElle est géré de deux manières : soit par PHP, soit directement par Apache.

Sous PHP, cela revient à :

• vérifier l’existence des variables superglobales $_SERVER[’PHP_AUTH_USER’] et $_SERVER[’PHP_AUTH_PW’]avec la fonction isset() ;

• checker les deux valeurs par rapport à leurs valeurs stockées coté serveur ;

• si les variables n’existent pas ou ne sont pas de bonnes valeurs, l’instruction suivante permet de demanderl’identification à l’utilisateur :

header(’WWW-Authenticate:Basicrealm=’.$titre.” ”)

44

Page 45: RelectureDePhp5_2011_01_29

avec : $titre le message qu’on souhaite passer à l’utilisateur au moment de l’identification. La fenêtred’identification suivante apparaît alors :

• Quel que soit le résultat de l’identification, les deux superglobales $_SERVER[’PHP_AUTH_USER’]et $_SERVER[’PHP_AUTH_PW’] sont crées. Il faut noter que l’usage de la fonction header ci-dessusprovoque le renvoi d’une requête au serveur web et donc le code php qui suit N’est PAS exécuté. Pourque ce code soit exécuté, il faut appuyer sur le bouton d’échappement du clavier

• renvoyer un message indiquant la réussite ou l’échec de l’identification. L’échec n’est bien entendu spécifiéque si le bouton d’échappement a été utilisé.

La durée de l’authentification HTTP n’est pas limité tant que :

• toutes les instances du navigateur ne sont pas fermés ;

• que l’instruction header(’WWW-Authenticate:Basicrealm=’.$titre.” ”) n’est pas de nouveau appelé. Onpeut ainsi définir une fonction de vérification d’identité qui encapsule cette instruction. On pourrait parexemple gérer ça avec un système de timer etc.

Authentification avec Apache :Il suffit de passer la directive ’Require valid-user’ dans Apache. Les controles d’id seront alors gérés par

Apache. Dans ce cas, si PHP fonctionne en mode CGI ou que la directive safe_mode est active dans php.ini. Lasuperglobale $_SERVER[’PHP_AUTH_USER’] n’existe plus et c’est la variable superglobale $_SERVER[’PHP_REMOTE_USER’]qui est utilisée à la place.

9.1.2 L’adresse IP client dans $_SERVER

On peut récupérer l’adresse IP supposée du client en récupérant la valeur de $_SERVER[’REMOTE_ADDR’]ainsi que le port qu’il utilise avec $_SERVER[’REMOTE_PORT’] . L’ennui que l’adresse IP peut corre-spondre à celle d’un serveur proxy. Dans certains cas, un serveur proxy peut donner l’adresse IP du client quipasse par lui. Cette adresse est alors récupérable avec la variable superglobale $_SERVER[’X_FORWARDED_FOR’].Le hic, c’est que cette même adresse peut être celle d’un autre proxy. Par ailleurs, une adresse IP n’estpas suffisante pour identifier quelqu’un car ces adresse peuvent changer avec les connections/déconnexions de

45

Page 46: RelectureDePhp5_2011_01_29

l’utilisateur. Sans compter que deux personnes peuvent avoir en même temps, la même adresse IP. Par exemple: deux personnes d’une même boite se connectant en même temps...Par ailleurs, il ne faut pas perdre de vueque de plus en plus, ce sont les adresses IPv6 qui vont s’imposer...

9.1.3 Les noms d’hôte client dans $_SERVER

Le nom d’hote du client peut être récupéré dans la superglobale $_SERVER[’REMOTE_HOST’]. Cepen-dant cette variable peut ne pas exister. On peut vérifier avec isset et en cas d’inexistence, utiliser gethost-byaddr($IPAdresse) avec $IPAdresse valant $_SERVER[’REMOTE_ADDR’] qui existe toujours dans$_SERVER. ATTENTION : Comme $IPAdresse est plus ou moins fiable, le nom de l’hôte obtenu avec geth-ostbyaddr est aussi plus ou moins fiable...

9.1.4 L’adresse IP et le nom d’hote du serveur dans $_SERVER

Contrairement aux deux autres, ces deux infos vu du pt de vue du serveur ne sont pas bidonnables. Elles sontacessibles avec $_SERVER[’SERVER_ADDR’] et $_SERVER[’SERVER_PORT’] . A noter que sion se connecte en local à son serveur web, il n’est pas possible de connaitre IP publique.

9.1.5 Les superglobales de $_SERVER décrivant toute requête HTTP

5 type d’informations principales sont contenus dans une requête HTTP :

• Les paramètres de la requête :

– méthode d’accès (get, post etc.) avec $_SERVER[’REQUEST_METHOD’],

– l’hote virtuelle et le serveur physique proprement dit avec respectivement $_SERVER[’SERVER_NAME’]et $_SERVER[’HTTP_HOST’]. Grosso modo, un serveur physique peut héberger plusieurs siteset a potentiellement un hote virtuel par site.

– le protocole de communication utilisé avec $_SERVER[’SERVER_PROTOCOL’]. Normale-ment c’est toujours HTTP/1.1 mais attention ça peut être différent. Cette information est TRESimportante quand on cherche à envoyer des directives de cache dans des en-têtes HTTP.

• L’adresse URL demandée :

– $_SERVER[’REQUEST_URI’] : A noter qu’elle contient des informations de requête dans lecas d’une communication par la méthode GET dans un formulaire. Ces informations suivent alors lepoint d’interrogation ? comme suit :

http://www.NomDeDomaine/CheminDeLaPageVue?variable1=Valeur1&variable2=Valeur2

et les deux variables passés sont de noms variable1 et variable2 tandis que leurs valeurs respectivessont Valeur1 et Valeur 2. La variable $_SERVER[’REQUEST_URI’] contient donc parrapport à l’adresse URL précédente :

/CheminDeLaPageVue?variable1=Valeur1&variable2=Valeur2

46

Page 47: RelectureDePhp5_2011_01_29

– $_SERVER[’QUERY_STRING’] : La variable superglobale suivante permet de récupérer exclu-sivement uniquement ce qui suit le point d’interrogation dans $_SERVER[’REQUEST_URI’].Par rapport à notre exemple d’adresse URL ci-dessus, $_SERVER[’QUERY_STRING’] con-tient la chaine :

variable1=Valeur1&variable2=Valeur2

A noter que le recours à la fonction explode avec ’&’ puis ’=’ comme séparateur permet de récupérerun tableau de toutes la variables passés en arguments de la requête URL. Les noms et les clefs dechaque variable sont alors disponibles alternativement comme élément du tableau. Par exemple :un premier explode donne un tableau dont l’élément 0 vaut ’variable1=Valeur1’ et l’élément 1 vaut’variable2=Valeur2’. Un deuxième explode sur le caractère ’=’ sur chaque élément de ce premiertableau donne deux tableaux dont le premier a pour premier élément ’variable1’ et pour deuxièmeélément Valeur2. ATTENTION dans les url, seuls les caractères alphanumériques sont tolérés etdonc quand les valeurs de variables sont passés dans l’URL, les caractères non-alphanumériques sontencodées en url càd avec une syntaxe de type %xx. Pour récupérer dans une variable, des valeursencodés, on utilise la fonction urldecode. De même façon pour encrypter des variables et leursvaleurs de manière à les incorporer dans une requête URL, il faut utiliser la fonction urlencode.A noter l’existence des fonctions rawurlencode et rawurldecode qui font la même chose mais lesespaces apparaissant dans une chaine et qui sont retranscrites avec le caractère ’+’ dans l’URL nesont pas restitués en espace par la fonction rawurldecode.IMPORTANT : Pour récupérer les variables et les valeurs de $_SERVER[’QUERY_STRING’],il est préférable d’utiliser la fonction parse_str() qui renvoie un tableau associatif dont les clefs sontles noms des variables et les valeurs associés sont leurs valeurs correspondantes. Comme toute bonnefonction couteau suisse, parse_str a deux fonctions un peu différentes suivant la liste d’arguments: parse_str($AdresseURL) qui crée directement les variables issue de l’URL avec leur nom devariables et leur valeur. Truc con : dans le cas de ce premier usage, il faut connaître à l’avancele noms des variables qui vont être crées. L’autre usage est parse_str($AdresseURL, Optional$TableauDeVariables) qui crée le tableau associatif...

– $_SERVER[’PATH_INFO’] : il est possible d’adopter une arborescence virtuelle pour gérer etstructurer l’ensemble des fichiers de script HTML et php. Dans ce cas, la partie virtuelle du cheminest contenu dans $_SERVER[’PATH_INFO’].

• Les informations fournies par le client : Ces informations concernent le client : la page diteréférente du client et les caractéristiques des contenus gérés par le client.

– $_SERVER[’HTTP_REFERER’] : adresse de la dernière page où le client s’est rendu. ça renvoiedonc aussi la dernière page du server qui précéde la page courante si le client était déjà sur le site duserveur à la page précédente ; La page référente est une information par le client qui indique parquelle dernière page est passée le client avant d’accéder à la page fournie par le serveur. Cela permetnotamment de savoir par quelles pages externes, un client est parvenu jusqu’aux pages fournies parle serveur. Cela peut servir à aiguiller le client si la première page auquelle arrive le client sur cellesfournies par le serveur n’est pas adéquate notamment quand le client déboule sur le site en ayantcliquer sur une url copié-collé sur un autre site. Evidemment après une refresh de la page, cettesuperglobale ne prend pas la valeur de la page courente. ATTENTION : comme toute informationfournie par le client, rien ne dit que cette information soit fiable...

– les caractéristiques des contenus gérés par le client : Ces informations contenues dans letableau superglobale $_SERVER servent à indiquer quelles types de pages sont acceptées par lenavigateur du client. Les variables superglobales correspondantes sont :

47

Page 48: RelectureDePhp5_2011_01_29

∗ $_SERVER[’HTTP_ACCEPT’] : permet de spécifier les préférences du client en matièrede format de fichier web acceptés par son navigateur : XML, XHTML, HTML etc. en affectantune priorité entre chaque format (la priorité max étant 1 sur une échelle allant de 0 à 1) Lachaine de caractère renvoyé par $_SERVER[’HTTP_ACCEPT’] est de la forme :

Format1;q=x,Format2;q=y,Format3;q=z,...

ce qui signifie que le format Format1 est accepté avec la priorité x, le format Format2 avec lapriorité y, le format Format3 avec la priorité z et ainsi de suite. Quand la priorité n’est passpécifié, elle vaut 1 càd la priorité maximale. Un exemple est le suivant :

text/xml,text/html;q=0.8,*/*;q=0.1

ce qui signifie que le XML est le format préféré puis le html avec 0.8 en priorité et tous les autrestypes de fichiers (*/*) avec q = 0.1.IMPORTANT : récupérer cette chaine et l’analyser permet au serveur de servir au mieux le client;

∗ $_SERVER[’HTTP_ACCEPT_LANGUAGE’] : permet d’identifier la langue de l’utilisateurcôté client. Autant dire que c’est VITAL. Comme pour la superglobale $_SERVER[’HTTP_ACCEPT’],la chaine contenue dans $_SERVER[’HTTP_ACCEPT_LANGUAGE’] donne un ordre de préférenceentre plusieurs valeurs acceptées par le client. chez moi, j’ai :

fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3

càd français de France préféré en premier puis l’anglo-américain et puis l’anglais ;∗ $_SERVER[’HTTP_ACCEPT_CHARSET’] : permet de savoir quelle est le jeu de car-actères utilisé par le client càd utf-8, ISO-machin et cie... TRES IMPORTANT donc ; ATTEN-TION : le navigateur client peut accepter PLUSIEURS encodages possibles et comme pour la su-perglobale $_SERVER[’HTTP_ACCEPT’], la chaine contenue dans $_SERVER[’HTTP_ACCEPT_CHARSET’]donne un ordre de préférence entre plusieurs valeurs acceptées par le client.

ISO-8859-1,utf-8;q=0.7,*;q=0.7

∗ $_SERVER[’HTTP_ACCEPT_ENCODING’] : détermine quels format de compression,le navigateur accepte. Contrairement aux trois autres cas, il n’y a pas de système de préférences, lenavigateur accepte tous les types d’encodages de la liste $_SERVER[’HTTP_ACCEPT_ENCODING’]sans chichi. Chez moi, ça donne :

gzip,deflate

– Le nom et le numéro de version du navigateur : Ces informations sont contenues dans lasuperglobales $_SERVER[’HTTP_USER_AGENT’]. Cela a surtout pour utilité d’identifier si lenavigateur client est un navigateur de pc, de mac, d’ipad, iphone ou autre et permet donc d’adapterson site éventuellement à ces clients les plus typiques. MAIS sinon il faut éviter d’adapter son site àchaque navigateur, c’est une perte de temps et une source de complications inutiles...

9.2 Les variables superglobales de $_ENV

C’est là que c’est drôle : le contenu de $_ENV dépend du sytème. Peut-être reviendra-t’on dessus mais à cestade, une boucle sur $_ENV ne renvoie RIEN !!!

CEPENDANT deux superglobales d’environnement utiles qui se trouvent dans $_SERVER :

48

Page 49: RelectureDePhp5_2011_01_29

• $_SERVER[’SCRIPT_NAME’] : donne l’adresse relative du script en cours d’utilisation à partir durépertoire racine des fichiers web sur le serveur. A l’inverse ...

• $_SERVER[’PATH_TRANSLATED’] : donne lui le chemin absolu ;

Last but not the least :

9.3 Les SUPERGLOBALES autonomesAutonomes car n’appartenant pas à un tableau superglobales. TOUTES ces superglobales ont un nom de variablequi commencent et terminent par deux underscores du type __TOTO__. Parmi celles-ci :

• __LINE__ : donne la ligne de code en cours d’exécution ;

• __FILE__ : donne le chemin complet (càd absolue) du fichier de script en cours ;

• __CLASS__ : nom éventuel de la classe dans lequel se trouve la ligne en cours d’exécution ;

• __FUNCTION__ : nom éventuel de la fonction dans lequel se trouve la ligne en cours d’exécution ;

• __METHOD__ : nom éventuel de la méthode de classe dans lequel se trouve la ligne en cours d’exécution;

10 Chapitre 10 : Les cookies

10.1 Définition et principe d’utilisation des cookiesUn chapitre pour la fonction couteau-suisse set_cookie() et les cookies eux-mêmes.

Les cookies sont des petits fichiers à durée de vie limitée contenus sur les ordinateurs clients. Ils sont crées,modifiés et détruits par le serveur dans les blocs d’en-tête des pages web, contiennent des informations liés auclient et utiles au serveur.

Par ailleurs, en php, chaque cookie associé au nom de domaine de la page est stocké dans le tableausuperglobale $_COOKIE.

Maintenant du fait que les cookies sont crées côté client. AUCUNE information critique ne peut êtreraisonnablement stockés côté client. Càd que toute information pouvant mettre en jeu la sécurité du client oul’intégrité du serveur ne peut être stocké sous forme de cookie. Par ailleurs, la taille des cookies est généralementlimitée par les navigateurs eux-mêmes ( généralement 4 ko) et leur nombre par domaine est souvent limité (20max le plus souvent).

Il est donc FORTEMENT conseillé de créer :

• des petits cookies, càd des petits fichiers de 4 ko ;

• peu de cookies : vaut mieux donc 1 cookie de 3ko que 3 cookies de 1 ko du fait de la limitation en nb decookie ;

• des cookies ne contenant pas d’information joue sur des paramètres critiques du serveur (genre le prix duclic comme une régie publicitaire avait pu le faire) ;

49

Page 50: RelectureDePhp5_2011_01_29

• des cookies contenant des informations confidentielles du client vu que ces mêmes données sont alorspotentiellement accessible par tiers (par exemple, le cas le plus simple d’une même machine utilisée parplusieurs personnes) ;

• des cookies qui ne sont pas indispensables au bon échange entre client et serveur car certains navigateursou certains clients peuvent limiter ou carrément interdire l’usage des cookies. De ce fait, si les cookies sontconçus comme indispensables à l’échange, c’est l’usage du site web par le client qui est compromis !

Mais à quoi servent donc ces satanés cookies ?? Pas dur :

• Pré-remplir des formulaires web avec des informations non-critiques. Le cookie est alors pour simplifierla vie de l’utilisateur ;

• faire des statistiques à destination du serveur notamment par exemple stocker la dernière date de connexiondu client au serveur. Du fait de la loi des grands nombres et à l’aide d’un contrôle des valeurs, le bidonnagepar un utilisateur de ses propres cookies n’aura que peu ou pas d’impact.

Une petite remarque : dans le cas des statistiques ou d’autres données à caractères informationnelles à des-tination du serveur, il est aussi possible de créer des cookies contenant des informations cryptées à l’aide desfonctions de hachage et de cryptage vu auparavant. Attention cependant à ne pas se servir de cet argumentpour commencer à stocker dans les cookies des informations ou des paramètres critiques.

10.2 Mécanique d’utilisation des cookies :

Au première accès au site web, le serveur envoie au client une page web avec un cookie dans le bloc d’en-tête.Ensuite à chaque requête du client, celui-ci renvoie dans la requête HTTP le cookie au serveur qui va se servirde l’information contenu pour faciliter l’échange informationnel entre client et serveur.

Où sont stockés les cookies ? Cela dépend de la durée de vie de chaque cookie : si elle est limitée, lecookie est stocké sur le disque dur à un emplacement réservé dont le chemin dépend de l’OS. En l’occurence,sur Windows XP, ça va être c:\Document and Settings\votre_login\Local Settings. Sur Vista, c’est pas clairmais sur firefox on peut accéder aux cookies associés à une page donnée en faisant Outils => Informations surla page => Sécurité => Voir les cookies.

10.3 Maintenant comment créer, modifier et détruire des cookies ?Avec l’exemple même de ce que je déteste en matière d’usage de la surdéfinition de fonction : la fonctionsetcookie().

Autre remarque à propos de la rédaction du chapitre 10 : Ce chapitre omet de parler de la gestion dubuffering ou de la mise en tampon des scripts php. Cette question de la mise en tampon est seulement abordéeau chapitre 15 or cela impacte la compréhension de la logique générale de l’utilisation de la fonction setcookie.

Ecriture d’un cookie

setcookie($NomDuCookie,$ChaineValeurCookie) : Cet appel se fait dans le bloc header (ou en-tête) etAVANT tout appel à une fonction d’affichage HTML ou tout simple ligne vide du bloc d’en-tête qui comptecomme un appel d’affichage. L’effet est de créer dans le tableau superglobale $_COOKIE une variable de clef$NomDuCookie et de valeur $ChaineValeurCookie.

50

Page 51: RelectureDePhp5_2011_01_29

Impact de l’activation du buffering sur l’utilisation de setcookie : Le buffering est une optionprésente dans php.ini qui permet de ne pas envoyer au client ligne par ligne l’exécution de la page html construiteà partir du script php. Cette option est écrite dans php.ini comme :

output_buffering=on

Trois types de valeurs sont possibles pour output_buffering :

• Off : Pas de buffering ou mise en tampon de la page html. Dans ce cas, les appels à setcookie doiventstrictement être faits dans le header càd le bloc head de la page html et sans appel d’une quelconquefonction d’affichage type print_r, echo ou autre ;

• On : C’est l’inverse, tout la page html est construite par php dans le buffer avant d’être envoyé au serveurweb et donc ensuite au client. La taille de la page mise en buffer n’est alors pas limité et les appels desetcookie et header peuvent intervenir n’importe où dans le script php et notamment après des appels àdes fonctions affichage écran ATTENTION : quand output_buffering a une valeur différente de off ;

• X : avec X un entier représentant la taille maximale en octets de ce qui peut être mis en tampon càd dansle buffer avant d’être envoyé au serveur par php. Ainsi dès que la limite du buffer est atteinte, le bout depage html construit est envoyé au serveur qui le relaie au client.

Indépendament de ce paramètre de php.ini, la mise en tampon peut être directement géré dans le code phpà l’aide du jeu de fonctions suivant :

• ob_start(optional $Output_CallBack,optional $ChunkSize, optional $Erase) : met en buffertoutes les instructions html produites par php, à l’exception des instructions d’en-têtes (qui ne peuventêtre mis au buffer que via la directive Output_buffering de php.ini), à la suite de l’appel ob_start et cesoit jusqu’à ce que la chaine stockée correspondante dépasse la valeur $ChunkSize en octets, soit jusqu’àl’appel de la fonction ob_end_flush() ou ob_clean(). L’option $Erase permet de conserver le contenu dutampon jusqu’à la fin du script courant et donc après l’appel éventuel à ob_end_flush ou ob_end_clean.Enfin $Output_CallBack est le nom d’une fonction utilisateur php qui peut-être défini pour retraiter lachaine stockée dans le buffer avant son envoi au client ; NOTE : il est possible d’imbriquer plusieurstampons à l’aide de plusieurs appels à ob_start.

• ob_end_clean() : détruit le tampon et donc son contenu.

• ob_flush() : Envoi le contenu du tampon au client et efface son contenu ;

• ob_end_flush : idem mais ferme par ailleurs le tampon courant ;

• ob_get_contents() : renvoie la chaine correspondant au contenu du tampon sans effacer ce dernier ;

• ob_get_lenght() : renvoie la taille en octets (?) du contenu du tampon (ou buffer en anglais);

TRUC : Pour être sur de vider et de fermer tous les tampons mémoires ouverts :

while(@ob_end_flush()==true)

ATTENTION : Inutile d’essayer d’écrire dans $_COOKIE, ce tableau superglobale est en lecture seul. Ilfaut passer obligatoirement par la fonction setcookie.

ATTENTION : $_COOKIE est le tableau des valeurs de cookies envoyés par le client. Il est impossible demodifier sa valeur dans un bloc de code php sans passer par un renvoi de header par le client, ce renvoi contenant

51

Page 52: RelectureDePhp5_2011_01_29

une instruction setcookie($NomCookie, $NouvelleValeurCookie). Inutile donc d’essayer d’incrémenter la valeurd’un cookie dans une boucle, ça ne changera pas sa valeur... Il faut passer par setcookie appelé dans un headerde page en train d’être chargé

Supprimer un cookie :il faut alors supprimer à la fois la variable dans le tableau superglobale $_COOKIE ET le fichier crée dans

le système de fichiers du client. DEUX appels sont donc nécessaires :

• Pour supprimer le fichier cookie, il faut réexécuter la dernière instruction setcookie s’étant appliqué aucookie qu’on souhaite éliminer avec pour seul changement passer en valeur une chaine vide ” ” ou ”. Dansle bouquin, il est dit que seul setcookie($NomDuCookie) mais cela ne semblait pas bien marcher quand leoutput_buffering est activé...

• Pour supprimer la variable PHP $_COOKIE[’$NomDuCookie’] : unset($_COOKIE[’$NomDuCookie’]);

Durée de vie des cookies :Quand un cookie est crée avec l’instruction :

setcookie($NomDuCookie,$ChaineValeurCookie)

la durée de vie du cookie est par défaut celle de la session courante du navigateur. Pour spécifier une dated’expiration à un cookie, il faut utiliser l’appel suivant :

setcookie($NomDuCookie,$ChaineValeurCookie,$TimestampDateExpiration)

avec $TimestampDateExpiration un timestamp Unix donnant la date d’expiration du cookieStockage d’informations multiples dans un seul cookie : Deux méthodes sont possibles :

• spécifier que le cookie est un tableau (ce tableau ne peut alors qu’être associatif) : Pour cela, il fautdéclarer chaque élément avec setcookie. Pour effacer le cookie tableau, il faut effacer dans ce cas, chaqueélément du tableau séparément ... bonjour l’angoisse ! En fait, c’est chaque élément du tableau est alorsconsidéré comme un cookie à part entière ;

• sérialiser les informations multiples en une seul chaine de caractères qui sera ensuite passé en valeur d’unseul cookie. Il faut alors désérialiser la chaine stockée dans le cookie pour récupérer les informations stockés.Typiquement un tableau ou un type complexe (une structure par exemple) de nom Toto est stockée enutilisant la fonction serialize() comme suit :

setcookie(”TotoCookie”,serialize($Toto),mktime(0,0,0,2011,12,3))

Pour récupérer l’info, il faut alors désérializer la chaine contenu dans le cookie avec la fonction unserialize.Par exemple :

$NewToto=unserialize($_COOKIE[’TotoCookie’]);

la variable $NewToto a alors la même structure que $Toto ( ça correspond à une copie de $Toto)

Restriction de portée des cookies :

52

Page 53: RelectureDePhp5_2011_01_29

On peut restreindre l’existence d’un cookie à un domaine donné et /ou à un répertoire (et donc tous sessous-répertoires et fichiers) et surtout si le cookie est installé uniquement en connexion sécurisée (càd en SSLvia le protocole https) ou non. La surdéfinition correspondante de setcookie est :

setcookie($ChaineNomCookie,$ChaineValeurCookie,$TimeStamp,$RepertoireValide,$NomDomaineValide,$SSLTrue,$httponly )

Si on ne souhaite pas restreindre le répertoire, on met ”/” comme valeur par défaut à $RepertoireValide.Si on ne souhaite pas restreindre le nom de domaine, on ne renseigne $NomDomaineValide.Par défaut, un cookie est valide pour tout le domaine courant. Chez moi, c’est toto.localhost.$NomDomaineValide est une chaine qui contient le nom de domaine souhaité TOUJOURS précédé d’un

point.Le nom de domaine spécifié peut-être partiel par exemple, on peut mettre ’.php.net’ ce qui rendra le cookie

valide pour les domaines www.php.net ou www.toto.php.net.$RepertoireValide est une chaine qui contient le nom de répertoire souhaité TOUJOURS précédé d’un ’/’.$SSL doit valeur true si on souhaite que le cookie ne soit valide que dans le cadre de connexion sécurisée.

Par défaut, ça vaut false)$httponly passé à true signifie que le cookie n’est accessible que par connexion HTTP. Cela signifie notam-

ment que le cookie n”est pas accessible via des langages de script côté client notamment javascript. Cela limiteles ATTAQUES de type XSS. ATTENTION : certains navigateurs refuse l’usage de cette option...

11 Chapitre 11 : Les sessionsA l’inverse des cookies, une session est un ensemble d’information contenues uniquement côté serveur et permetde stocker des informations sur le client. A un client du serveur va donc correspondre une session donnée. C’estavec une session que l’information critique peut-être stocké et géré. Une session est crée à chaque connexion duclient et est détruite dès la déconnexion. La session est crée dès lors que le client a été identifié par le serveur.Ceci fait, le serveur crée une session en récupérant dans sa base les informations utiles pour gérer son interactionavec le client.

Le tableau superglobale correspondant à la session courante du client est $_SESSION.

Ce tableau est renseignée par php et est donc en lecture et écriture directe contrairement à $_COOKIEqui est n’est manipulable qu’à partir de la fonction couteau-suisses setcookie(). ATTENTION : dès lors qu’unesession est crée, une fichier de session est crée sur le serveur. Celui-ci n’est pas détruit à la fin d’un script maisest détruit à une date ultérieure aléatoire par PHP. On peut cependant s’en débarasser explicitement.

Du fait de son caractère temporaire, tout nouvelle information apportée par le client et enregistrée dans lefichier de session via le tableau superglobale $_SESSION doit ensuite être sauvé sur un fichier plus pérenne surle serveur par exemple sur une base de données.

53

Page 54: RelectureDePhp5_2011_01_29

11.0.1 Pour débuter une session :

Pour amorcer l’utilisation du tableau superglobale $_SESSION, il faut utiliser l’instruction session_start().Avant cela, le tableau $_SESSION n’est pas accessible pour le script courant. L’effet de session_start() est de:

1. Rendre accessible le tableau superglobal $_SESSION en lecture et en écriture ;

2. débuter l’écriture du fichier de session correspondant.

3. créer un cookie spécial du nom de PHPSESSID à destination du client. Ce cookie permet de donner auclient un identifiant. Ce cookie n’a pas par défaut de durée de vie et est donc effacée du client dès que lasession de navigateur est fermée ; A noter que la durée de vie du cookie est indépendant de celle du fichierde session ou de la durée de vie de $_SESSION.

ATTENTION : session_start() doit être utilisée avant tout envoi d’affichage ou de header notamment parcequ’il incorpore le cookie de session PHPSESSID.

Remarque : La directive php session.auto_start mise à 1 fait que toute page web contient automatiquementen première instruction session_start(). IL FAUT EVITER CELA car cela empêche une gestion fine des sessionset des cookies.

11.0.2 Pour reprendre une session existante :

Après un premier appel à session_start() dans le fichier courant ou dans un autre fichier php, un nouveaurecours à session_start() va rouvrir la session passé et accéder aux variables superglobales correspondantesde $_SESSION dès lors que le cookie d’ID de session existe toujours.

11.0.3 Pour stopper une session :

La fonction session_destroy() permet de fermer la session courante : càd elle détruit le fichier de session.ATTENTION : après un appel de session_destroy(), le tableau superglobale $_SESSION reste inchangée.Pour effacer ce dernier, il faut ajouter l’instruction :

unset($_SESSION);

11.0.4 Pour changer ou récupérer l’id de session du client :

session_id(optional $NewID) : permet de récupérer l’id de session du client quand aucun argument n’estpassé à la function. Si $NewID est spécifié, cela a pour effet de changer la valeur du cookie de session.

ATTENTION : tout appel à session_id DOIT être fait avant l’appel à session_start() sinon les éventuelsmodifications ne seront pas prises en compte.

ATTENTION : jouer avec les ids de session peut être casse-gueule en terme de sécurité.INFO : la constante globale SID permet d’avoir à l’ID de la session courante comme le renverrait session_id

11.0.5 Pour régénérer l’iD de session :

Il est parfois intéressant de régénérer l’id de session. C’est ce que fait la fonction session_regenerate_id().

54

Page 55: RelectureDePhp5_2011_01_29

11.0.6 Pour changer ou récupérer le nom du cookie d’Id de session :

session_name(Optional $NewName) : permet de récupérer le nom de session quand $NewName n’estpas spécifié. Dans le cas inverse, le nom du cookie de session est remplacé par le nom passé en argument.ATTENTION : $NewName ne doit contenir que des caractères alphanumériques. Par ailleurs, ce changementde nom du cookie ne sera valide que pour le script courant. Les autres scripts utiliseront le nom par défautPHPSESSID. Enfin il faut appeler cette fonction AVANT session_start() pour que ça prenne effet. La valeurpar défaut peut être changé dans php.ini via la directive session.name = PHPSESSID.

Remarque : Ne pas oublier qu’une bonne part des directives php peuvent être modifiées localement dansun script à l’aide de la fonction ini_set($DirectiveName,$NewValue)

11.0.7 Pour changer ou récupérer le chemin de sauvegarde des fichiers de session :

session_save_path(optional $NewPath) : Même fonctionnement que pour les deux fonctions précédentes SAUFque le changement de chemin de sauvegarde est permanent pour la session en cours. A noter que la session pardéfaut est défini dans php.ini avec la directive session.save_path. Chez moi, ça donne :

session.save_path=”C:/ProgramFiles/wamp2/tmp”

Ce qui signifie notamment que les fichiers de session ne sont pas sauvés dans le répertoire des fichiers web ”www”.ca évite que le client puisse avoir accès aux fichiers de sessions qui vont garder potentiellement des informationsconfidentielles...

cette fonction DOIT être appelé AVANT session_start().

11.0.8 Nom par défaut d’un fichier de session :

Par défaut, un fichier de session a pour nom sess_VALEURDUCOOKIEIDSESSION (par défaut, pas de suffixetype .txt, .xls ou .dll) . Par exemple, si mon id de session est ”SuperJoe”, le fichier de session a pour nom:

sess_superjoe

11.0.9 Ajouter, modifier et retirer des données à $_SESSION :

Cela se fait en ajoutant de nouvelles clefs au tableau superglobal $_SESSION. Par exemple :$_SESSION[’NewVar’] =10; /// création de la var NewVar$_SESSION[’NewVar’] = ”Beautiful”; /// modification de la valeur de la variableunset($_SESSION[’NewVar’]); /// Effacement de la variable ’NewVar’

Pour effacer toutes les données de $_SESSION, il NE FAUT PAS FAIRE unset($_SESSION) car c’est tropbourrin, $_SESSION devient totalement inacessible. La solution est de lui affecter un tableau vide en valeurcomme suit :

$_SESSION=array();

Attention :$_SESSION= 0;

55

Page 56: RelectureDePhp5_2011_01_29

fait que $_SESSION n’est plus un tableau. Il faut alors repasser par la fonction array() pour redéfinir $_SES-SION correctement :

$_SESSION=array();

Le recours direct à :$_SESSION[’Nom’]=”Marie-Estelle”;

aurait renvoyé une erreur stipulant que $_SESSION n’est pas un tableau.

11.0.10 Contenu du fichier de session :

Un fichier de session peut être ouvert comme un fichier texte classique dans le bloc-notes ou via une fonctionphp de lecture de fichiers (on verra ça au chapitre 13). Le contenu du fichier correspond alors au tableausuperglobale $_SESSION sérialisé comme si la fonction serialize() avait été utilisé.

ATTENTION : Les données d’un fichier de session ne sont donc pas particulièrement sécurisés ou cryptés.On verra plus loin comment protéger ces données notamment en les cryptant.

11.0.11 Paramètres du cookie d’id de session :

Les paramètres du cookie d’id de session (durée de vie, répertoire de validité, domaine de validité, présenced’une restriction à la connexion sécurisée type SSL et restriction d’accès au cookie via langages de script par leclient) peuvent être modifié avec la fonction session_set_cookie_params dont l’appel est le suivant:

session_set_cookie_params($DureeVie,Optional $Path,Optional $Domaine,Optional $Secure,Optional $httponly,Optional $)

ATTENTION : Cette modification des params du cookie d’ID de session ne sont valides que pour le scriptdans lequel il est appelé, et par ailleurs, il faut impérativement l’appeler avant l’instruction session_start(). Lasolution pour rendre ”permanent” est d’appeler session_set_cookie_params pour chaque script et avant chaqueappel à session_start().

Pour récupérer les paramètres courant du cookie de session, il faut utiliser la fonction session_get_cookie_params()

Les paramètres par défaut du cookie de session sont settables dans php.ini avec les directives suivantes :

• session.cookie_lifetime =”0” ;

• session.cookie_path =”/” ;

• session.cookie_domain = ””;

• session.cookie_secure = ””.

56

Page 57: RelectureDePhp5_2011_01_29

11.0.12 Modification du serializer de php :

Les fonctions serialize et unserialize utilisent un module de serialization installé avec php. Il est possible d’enchoisir un autre à l’aide de la directive php suivante :

session.serialize_handler = ”php” ;; ”php” est la valeur par défaut

Attention à spécifier un serializer qui tourne correctement... A voir si on a besoin de prendre un serializerplus léger et/ou plus secure que celui installé...

11.0.13 Accès multiple à une session

Php verouille le fichier de session pour éviter que plusieurs scripts tape en même temps sur le fichier de session.Le problème d’un accès multiple apparaît quand on a par exemple plusieurs fenêtres ouvertes sur un même site.Dans ce cas, un seul et unique script va avoir l’accès et php va locker autimatiquement le fichier de session. Lesautres scripts n’auront alors pas du tout accès normalement au fichier de sesson ce qui peut empêcher le bonfonctionnement des scripts surnuméraires. Les fonctions suivantes sont là pour résoudre ce problème :

• session_readonly() : appeler à la place de session_start, il permet à un script d’avoir accès à $_SESSIONet au fichier de session en lecture seulement ; ATTENTION CETTE FONCTION NE SEMBLE PASEXISTER !!

• session_write_close() : permet de fermer d’enregistrer les données de $_SESSION dans le fichier desession et de fermer la session, ce qui la rend dispo à d’autres scripts php. Autant dire qu’avant tout groscalcul, il faut appeler cette fonction ;

11.0.14 Session multiples sur une même page web :

Il est impossible d’ouvrir simultanément plusieurs sessions. Cependant, on peut tout à fait ouvrir plusieurssessions

l’une après l’autre. Dans ce cas, il faut fermer la première session sans la détruire, grâce à session_write_close(),puis assigner les nouveaux session_name et session_id, et enfin ouvrir la nouvelle session avec session_start().Une fois la session fermée, il est toujours possible d’accéder en lecture (les modificationsne seront pas prises en compte) aux variables de l’ancienne session. $_SESSION ne seravidé et rerempli qu’au prochain appel à session_start().

11.0.15 Gestion de la mise en cache des pages web et session :

Le principe est d’autoriser à d’autres machines (types proxies ou pc même du client) que le serveur la possibilitéde sauvegarder la page web du site pour la resservir au client comme page quand il se reconnecte au site web.ça permet d’alléger la charge du serveur qui n’est pas du tout sollicité. Cette autorisation est gérée au sein duprotocole HTTP et donc n’a pas besoin de php et du serveur. Le serveur fait produire la page par php qui écriten HTTP la mise en cache potentielle de la page.

Ce comportement de mise en cache d’une page web est paramétrable via la directive :

session.cache_limiter

Les valeurs possibles sont :

57

Page 58: RelectureDePhp5_2011_01_29

• ”public” : la page web est resservie tel quelle à tous les utilisateurs quelque soit la session et l’id corre-spondant de chacun. Idéal pour une page fixe, à proscrire pour une page un tant soit peu dynamique...

• ”private_no_expire” : la page n’est resservie qu’au utilisateur doté du bon id de session ; Pas de dated’expiration pour la mise en cache de cette page ; Bien réfléchir à quelle page aurait ce type de mise encache. Idéalement un portail fixe sans mise à jour de données ;

• ”private” : pareil que le précédent mais la mise en cache est limitée dans le temps via la directive ses-sion.cache_expire qui donne en secondes la durée de la mise en cache potentielle ;

• ”nocache” : Pas de mise en cache autorisées à part pour le bouton page précédente du navigateur etd’autres rares cas particuliers ;

• ”none” : strictement aucune mise en cache autorisée.

11.0.16 Expiration des sessions :

Les fichiers de session ont une durée de vie limitée aléatoire sur le serveur. Le principe est simple et est gouvernépar deux directives de php.ini : session.gc_probability et session.gc_maxlifetime. L’effacement des fichiers desession a lieu comme suit :

• A chaque démarrage de session, un nombre aléatoire compris entre 0 et 1 est tiré ;

• Si sa valeur est inférieure ou égale à session.gc_probability/session.gc_divisor, tous les fichiers dont l’âgeen secondes dépasse session.gc_maxlifetime sont effacés.

Par défaut, les valeurs des deux directives sont :

session.gc_probability = 1

session.gc_divisor = 1000

session.gc_maxlifetime = 1440

11.0.17 Directive register_globals et gestion des sessions

register_globals NE DOIT JAMAIS être mis à on car dans ce cas, les valeurs de clefs dans les tableauxsuperglobaux sont considérés comme des variables et donc $_SESSION[’login’] est équivalent à $login et doncl’instruction $login = ”toto” modifiera $_SESSION[’login’] !!

11.0.18 Fonctions obsolètes de gestion des sessions :

session_register(), session_unregister(), session_is_registered() et session_unset() sont DEPRECATED et leurusage est donc proscrits !

58

Page 59: RelectureDePhp5_2011_01_29

11.0.19 Echange d’identifiant entre client et serveur :

A la première connexion du client au serveur, le cookie d’id de session n’existe pas côté client, et c’est le serveurqui en renvoyant la première page va demander au client de créer le cookie d’id de session.

Cependant il est possible que le navigateur client ne permettent pas la création de cookie. Dans ce cas, il esttechniquement possible d’intégrer l’id de session dans l’URL (v.page 249) cependant cela expose l’id de sessiondans l’URL, ce qui n’est pas souhaitable en terme de sécurité. Si le client ne veut pas utiliser de cookie, en clairtant pis pour lui ! La configuration en terme de directive php est alors la suivante :

session.use_trans_id = 0session.use_cookie = 1

session.use_only_cookie = 1

La première ligne permet d’empêcher la transmission d’ID par URL.

11.0.20 Gestion personnalisée des sessions :

PHP gère lui-même l’initialisation, la fermeture, la lecture, l’écriture, la destruction des sessions ainsi que lenettoiement des sessions les plus anciennes. Chacune de ces 6 opérations correspond à une fonction interne dephp et constitue le fonctionnement par défaut de php en matière de gestion de sessions.

PHP écrit ainsi dans des fichiers non-cryptés et qui n’ont rien à voir avec un système de gestion de base dedonnées qui serait peut-être plus approprié.

Il est alors possible et fortement recommandé de définir un jeu de 6 fonctions alternatives propres.Des modules supplémentaires à php fournissent des gestionnaires alternatifs :

• ”mm” : Le module mm gère les sessions en mémoire et non sous forme de fichiers. pas terrible car en casd’accès multiples, il y a confusion des accès et donc altération des données suivant l’ordre des accès... Biensur, il faut que php soit compilé avec l’option - - with-mm.

• ”msession” : il faut que php soit compilé avec l’option - - with-msession. Dans ce cas, le serveur ne gèreplus lui-même les sessions et délègue cela à un serveur spécialisé.

• ”session_pgsql” : Ce module sert à gérer les sessions dans une base de données dédiée de type PostgreSQL.Ce module se trouve sur le site de PEAR.

• ”user” : pour définir son propre jeu de fonctions, il faut placer au début de chaque script utilisant lessessions et AVANT session_start(), l’instruction :

session_set_save_handler(’init’,’ferme’,’lit’,’ecrit’,’efface’,’nettoie’)

avec :’init’,’ferme’,’lit’,’ecrit’,’efface’ et ’nettoie’ qui sont les noms des fonctions utilisateurs définis pour gérerles sessions (v. page 250 pour un exemple).

11.0.21 Exemple d’application du chapitre 11 :

Comme souvent avec ce bouquin, la charrue est mise avant les boeufs de sorte que l’application du chapitre 11est inutilisable si le chapitre 17 (!!) n’a pas été lu et étudié auparavant. Ce bouquin est un peu écrit en dépitdu bon sens...

59

Page 60: RelectureDePhp5_2011_01_29

11.0.22 Conseils de sécurité ayant trait aux sessions :

1. Controler l’accès aux fichiers de session : En cas de partage d’un serveur, spécifier l’adresse desauvegarde des sessions dans un répertoire à accès restreint est capital pour éviter que qui que ce soitaccède aux fichiers de sessions :

session.save_path=”/home/utilisateur/repertoire/personnel

2. Ne pas faire passer d’id de session par URL : La possibilité d’utiliser l’ID de session par l’URL quandl’utilisateur refuse les cookies est une très mauvaise chose car une copie malheureuse de lien, le fait quel’URL de la page précédente est contenu dans la requête HTTP courante compromet la sécurité de lasession et de l’identité de son utilisateur. Bref pour s’assurer que ces problèmes n’apparaissent pas, il fautsetter :

session.use_trans_sid =0session.use_cookies =1

session.use_only_cookies =1

ce qui est la config par défaut ;

3. Toujours utiliser les identifiants générés par les fonctions de session de php : les ids sont générés aléatoire-ment et sont considérés comme safe. Inutile de réinventer la roue ;

4. Utiliser la fonction session_regenerate_id dès AVANT et APRES que l’utilisateur soit identifié pourlimiter les ATTAQUES par fixation de session (v. plus loin le chapitre sur la sécurité) ;

5. Stocker le maximum d’informations sur l’utilisateur dans sa session pour permettre de contre-checker sonidentité. Par exemple, si les infos sont très différentes d’une connexion à l’autre, on peut détruire sasession et redemander une authentification...

6. NE JAMAIS STOCKER de mot de passe ou d’infos trop confidentielles dans la session. Par exemple,pour le couple login/ mot de passe, on peut stocker le login et stocker le fait que password était valide etnon le pasword lui-même.

12 Chapitre 12 : Gestion des objets

Un chapitre sympa sur la programmation orientée objet. Pourquoi ce chapitre arrive après les cookies et lessessions ? Je n’en sais rien. L’ordre des chapitres n’est pas très cohérent de toute manière ...

12.1 Programmation Objet en PhpComme j’ai déjà vu ça en C++ et en java, on va juste passer en revue les grandes lignes de la programmationobjet en PHP ainsi que les particularités du langages :

La philosophie de la programmation objet est la même que pour java et C++ :

60

Page 61: RelectureDePhp5_2011_01_29

12.1.1 Structure d’une classe :

class Personnage{/// Bloc des attributs de la classe

public $Force; /// les attributs se déclarent comme des variablesprotected $Agilite; /// avec en sus un spécificateur de portée public,private $Dexterite; /// protected ou private;

private $PtsDeVie =100; /// Déclaration d’une valeur par défaut : 100 ici

/// Blocs des déclarations des constantes locales uniquement accessibles au sein de la classeconst GUERRIER =1 ; /// ça fonctionne comme un enum ;const SOLDAT =2; // le hic, c’est que c’en est pas un de sorte que ces constantes n’ont pasconst BOURREAU =3 ; /// d’unité logique au sein de la classe

/// Pour appeler la valeur d’une constante de classe en dehors de celle-ci,/// il faut comprendre que ces constantes sont statiques et n’appartiennent pas à une instance/// en particulier et donc on les récupère avec l’instruction NOMCLASSE::NOMCONSTANTE/// et non comme NOMINSTANCE::NOMCONSTANTE ou NOMINSTANCE->NOMCONSTANTE

/// Pour accéder à une constante de classe au sein de la même classe, il faut écrire self::NOMCONSTANTE

/// Bloc des accesseurs et mutateurspublic function Get_Force(){

return $this->Force; /// ATTENTION le getteur renvoie une référence sur Force et non une copie !}/// Bloc de méthodes

public function Frapper($Victime)

{/// contrairement au C++ où on a un fichier d’en-tête et un fichier source///pour chaque classe, en php, le bloc d’exécution d’une fonction suit directement la/// déclaration du prototype.

}};

Best Practices :

1. TOUJOURS DECLARER LES ATTRIBUTS COMME PROTECTED ou PRIVATE pour pouvoir gérerleur lecture/écriture plus finement ;

61

Page 62: RelectureDePhp5_2011_01_29

12.1.2 Les spécificateurs de portée des attributs et méthodes :

sont identiques au C++. Une méthode ou attribut private n’est accessible qu’au sein de la classe, protected :acsessible au sein de la classe et des classes dérivées

12.1.3 Instanciation d’un objet d’une classe

Pas dur, ça se fait avec l’instruction new comme en C++ :

$Thor=new Personnage();

Comme d’hab, les parenthèses vides correspondent au constructeur par défaut. Comme d’hab en C++, il fautbien sur que l’existence de la classe a été déclarée pour faire le new. La bonne pratique est de faire l’include oule require nécessaire...

12.1.4 Accès au attributs et méthodes :

L’accès aux membres attributs et méthodes se fait en appelant une instance suivi de ’->’. TOUTES les instancesde classes étant des références, quelque part ça revient à considérer qu’on accède aux instances de classes commesi on accédait à un pointeur de classe en C++.

ATTENTION : Par défaut, tout accès à un attribut membre se fait PAR REFERENCE !!

12.1.5 Accès courant au type de la classe et de la méthode appélés :

A l’aide des métaconstantes __METHOD__ et __CLASS__ :__METHOD__ : renvoie le nom de la méthode et de la classe appelée comme suit NOMCLASSE::NOMMETHODE.

Renvoie rien si appelée directement dans un bloc de script. Renvoie le nom de la fonction si appelée dans unefonction n’appartenant pas à une classe.

__CLASS__ : renvoie le nom de la classe dans lequel cette métaconstante est appelée

• get_class($Object) : Cette fonction permet d’avoir le nom de la classe de l’objet passé en argument ;

• get_parent_class($Objet) : Cette fonction permet d’avoir le nom de la classe immédiatement parente del’objet passé en argument ; Notons qu’on peut aussi bien passer un nom de classe en argument qu’uneinstance de classe.

12.1.6 $this

$this se comporte globalement comme en C++. Notamment dans le cadre d’héritage, il se réfère toujours à laclasse dans lequel il est cité. Cité dans le code d’une classe abstraite, il se comporte comme une référence surune instance de cette classe abstraite. Rappel : il n’est pas possible de définir une instance de la classe mère.par contre, une référence c’est possible (et en C++, un pointeur c’est aussi possible).

62

Page 63: RelectureDePhp5_2011_01_29

12.1.7 __Tostring()

Fonction qui permet de renvoyer une chaine de caractères quand l’affichage de la classe est demandée. Générale-ment on formate le retour du genre ”NOMCLASSE:Attribut1=Valeur1;Attribut2=Valeur2;...” mais aucune règlen’est spécialement requise. attention cette fonction DOIT renvoyer une chaine. Un exemple d’écriture de cettefonction est :

function __tostring() {return ”<br> Le personnage est de nom $this->Nom, de force $this->Force, d’agilité $this->Agilite et

d’endurance $this->Endurance”;};

Tout affichage d’une instance d’une classe où __tostring aboutit à une erreur fatale.

12.1.8 Création implicite d’attributs membres

Supposons qu’une classe Perso possède trois attributs définis : Nom, Prénom et age. Si dans du code, j’écris :

$Toto=new Perso();$Toto->a = ”Valeura”;$Toto->b = ”Valeurb”;$Toto->c = ”Valeurc”;$Toto->Pognon =110;

L’instance $Toto aura gagné un attribut du nom de Pognon. Pire encore si une classe mère a un attributPognon privé et que $Toto en dérive, si le Pognon n’est logiquement pas accessible (par exemple en faisantecho $Toto->Pognon), l’attribut Pognon peut-être crée en faisant $Toto->Pognon =NimporteQuoi; Bonjourl’angoisse sur la possibilité de coder crade et sur la sécurité de la chose...

12.1.9 COMMENT EVITER LA CREATION D’ATTRIBUTS SAUVAGES ?

En utilisant les mutateurs qui sont définis en php avec les fonctions natives __get et __set :

Le prototype de la fonction __get est :

function __get($nom)

On compare alors la valeur de nom avec les noms des attributs pour décider quelle valeur renvoyer quand ladonnée membre de nom $nom est appelé.

Le prototype de la fonction __set est :

function __set($nom,$valeur)

EN FAIT, __set agit comme une redéfinition de l’opérateur = pour les données membres de la classe danscette fonction est appelée. L’idée est de comparer là aussi $nom avec les noms des attributs membres de la classede manière à affecter au membre la valeur $valeur. Notons que si l’attribut de nom $nom est aussi une instanced’une autre classe, il faut contrôler la nature de la classe de $valeur avant de décider de l’affecter. L’affectationpeut se faire ensuite

63

Page 64: RelectureDePhp5_2011_01_29

12.1.10 Copie d’instance de classe : = clone

Tout recours à l’opérateur d’affectation ”=” entre deux instances d’une même classe de type a= b conduit à ceque a devienne une référence de b (ATTENTION : si a était une instance d’une autre classe, a deviendrait aussiune référence de b et donc la nature et le contenu précédent de b serait totalement perdu). Si on souhaite quea soit seulement une copie de b, il faut définir la fonction membre native __clone() dans la classe voulue.

Php crée toujours un fonction __clone() par défaut pour chaque classe. Celle-ci fonctionne comme unopérateur de sorte que :

a = clone b

fait que a est une copie bit à bit de b.correspond à une copie bourrine de la c Quand on définit sa propre version de __clone(), la fonction _clone

par défaut est exécuté puis la version utilisateur de sorte que celle-ci ne vient qu’en complément

Attention : dans le cadre d’une fonction dérivée, la fonction __clone appelée est toujours celle de l’objetcopié, et EN AUCUN cas, la fonction __clone de la classe mère ou grand mère n’est appelée.Par contre, il estpossible d’appeler la fonction __clone mère en écrivant parent::__clone();

A NOTER : Quand on rajoute implicitement un membre à une instance de classe, et que cette dernière estcloné, le clone récupère aussi le nouveau membre rajouté implicitement.

12.1.11 Opérateurs == et ===

Va savoir pourquoi mais quand a === b, pour les objets le triple = ne compare plus les valeurs et la naturede l’objet mais dit si a est une référence de b !! Complètement débile alors que quand a et b ne sont pas desobjets, il vérifie bien que a et b ont la même valeur ET sont du même type !!

L’opérateur == lui teste l’égalité classique.

ATTENTION : IL n’est pas possible de redéfinir les opérateurs en PHP

12.1.12 Constructeurs

C’est une fonction de nom__construct qui permet de définir un constructeur. Comme en C++, il faut appelerexplicitement le constructeur de la classe parent pour que celui-ci opère comme suit Parent::__construct(...)

ATTENTION : il ne peut y avoir qu’un seul et unique constructeur par classe !!! Par contre, en ne donnantpas d’arguments au constructeur, il est possible de définir autant de constructeur qu’on souhaite en consid-érant __construct comme une fonction sans nombre défini d’arguments avec les fonctions func_num_args(),func_get_arg() et func_get_args(). ça exige une petite gymnastique de codage mais c’est possible.

64

Page 65: RelectureDePhp5_2011_01_29

12.1.13 Destructeur en PHP

Le destructeur est défini en php comme à l’aide de l’expression native __destruct(). Dans l’absolu, le destructeurpour un objet donné si cet objet n’est plus référencé ou si la fonction unset est appelé sur la variable. Du fait dugarbage collector présent en php, le destructeur est surtout utile pour fermer les ressources ouvertes : fichiers,connexion vers le serveur de base de données etc.

Destructeur et héritage : Pas dur, pour que la destructeur d’une classe mère soit appelé dans une classedérivée, il faut que cet appel soit explicite dans le destructeur de la classe dérivée càd parent::__destruct();

12.1.14 Classe dérivée

Comme dans la plupart des langages objets modernes, une seule classe mère possible (mais autant d’Interfacesqu’on veut). Pour qu’une classe A hérite d’une classe B, à la définition de la classe A, on doit avoir :

class A extends B

Les règles générales de l’héritage sont les suivantes :

• la classe dérivée hérite de toutes les méthodes et attributs public ;

• les méthodes et attributs hérités peuvent être redéfinis dans la classe dérivée ;

• Une méthode dérivée a son nombre de paramètre optionnel et son nombre de paramètres obligatoirescontraint par ceux de la méthode de même nom de la classe mère :

– Le nombre de paramètres obligatoires de la méthode fille doit être inférieure au nombre de paramètrespossibles (càd nb de params obligatoires + nb de param optionnel) de la méthode mère (ATTEN-TION, quand la méthode mère ne comporte pas d’arguments (ex :func() ), le nombre de paramètrespossibles est infini) ;

– Le nombre de paramètres possibles de la classe dérivée doit être supérieure ou égal au nombre deparamètres possibles de la classe mère ;

– En cas de contradiction d’une de ces deux règles peu claires, un message d’erreur E_STRICT estrenvoyé (v. chapitre sur les erreurs) ;

• Aucune méthode ne peut surdéfinis au sein d’une même classe.

• L’héritage des méthodes d’une classe est dit stricte : ce qui signifie que toute instance d’une classe dérivéedoit pouvoir se manipuler strictement comme une instance de la classe mère. Notamment tous les usagesde méthodes propres à la classe mère doivent pouvoir s’appliquer à la classe dérivée.

Notons que :

• is_sub_class_of($object,$className) : renvoie true si $object est une instance d’une classe dérivéede la classe de nom $className ;

• get_parent_class($objet) : renvoie le nom de la classe mère de $objet si elle existe.

65

Page 66: RelectureDePhp5_2011_01_29

12.1.15 Spécificateur d’accès aux méthodes et aux attributs

• Toute méthode de classe peut s’appeler de manière statique en précédant le nom de la fonction deNomDeLaClasse::NomFonction(...)

Autrement l’accès aux méthodes et aux classes peuvent être précisés avant la déclaration de la méthode oude l’attribut. par défaut, l’accès est public. Comme en C++, les trois types d’accès sont public, protected etprivate et fonctionnent comme en C++ sauf qu’il n’y a pas de spécificateur d’accès pour les classes qui sonttoutes considérées comme public par défaut :

• accès public : méthode ou attribut accessibles partout ;

• accès protected : méthode ou attribut seulement du code dans les blocs de la classe et de ses classesdérivées ;

• accès private : accès uniquement dans les blocs de code de la classe .

RUSE : il est toujours possible de rendre public, un attribut ou une méthode private ou protected enl’encapsulant dans une méthode public.

12.1.16 spécificateurs d’accès des méthodes et héritage :

Le polymorphisme des méthodes implique qu’une méthode donnée ne peut pas être surdéfini dans une classedérivée avec un spécificateur plus réduit. une méthode public ne peut être dérivé que public, une protecteddevient protected ou public etc.

Dans le cas d’une méthode privé, la surdéfinition dans une classe dérivée aboutit en quelque sorte à créerune méthode de même nom propre à la classe dérivée. (Evident non ? Bizarre que le bouquin se prenne lechou là-dessus)

12.1.17 Typage des arguments objets des fonctions

L’horreur de php et une de ses forces suivant qu’on aime est le faible typage de PHP. Pour les horrifiés, il estpossible et (FORTEMENT recommandé) de typer les arguments d’une fonction dès lors que l’argument est unobjet. ca permet d’éviter de faire des contrôles basés sur instanceof à tout bout de champ en spécifiant le typede chaque argument d’une fonction.

une fonction FaireCalcul($a,$b) a deux arguments non typés tandis que la définition suivanteFaireCalcul(Classe1 $a,Classe2 $b) stipule que $a DOIT être de type Classe1 et $b de type Classe2.

RUSE : Pour retrouver un typage fort du langage, on peut encapsuler les types élémentaires dans des classespropres.

12.1.18 Classe et méthode abstraites :

Une méthode est déclarée abstraite avec le mot clé abstract avant la déclaration de la fonction et avant lespécificateur d’accès.

Une méthode abstraite ne peut être déclarée private. Elle peut seulement être protected ou public.Une classe peut être abstraite sans avoir une seule méthode abstraite, un peu bizarre mais possible.Notons qu’une classe abstraite peut avoir un constructeur __construct() et des attributs...

66

Page 67: RelectureDePhp5_2011_01_29

12.1.19 Interfaces

Les interfaces fonctionnent en php comme en java. Ce sont des listes de fonctions correspondant à une mêmeunité sémantique ou logique et encapsulé dans une classe abstraite sans AUCUN attributs.

Une classe (normale, abstraite ou finale) peut dériver de plusieurs interfaces (alors qu’il ne peut y avoirqu’une seule classe mère).

Toutes les méthodes d’une interface sont FORCEMENT d’accès PUBLIC. On ne peut pas avoir de méthodesd’interface protected ou private.

Une classe ClasseX hérite d’une ou de plusieurs interface à l’aide du mot clé implements comme suit :

ClasseX implements InterfA, InterfB

Notons qu’une classe abstraite peut hériter d’interfaces et qu’une interface peut elle-même hériter d’autresinterfaces...

IMPORTANT : Une classe ClasseY héritant de ClassX appartient à quatre types possibles : ClasseY,ClasseX, InterfA, InterfB. Du coup, dans un appel typé de fonction, on peut utiliser comme type l’un de ces 4types pour faire référence à une instance de la classeY.

12.1.20 Méthode finale

Une méthode déclaré avec le spécificateur ”final” ne peut jamais être surdéfini dans une classe dérivée.

12.1.21 Classe finale

Une classe déclarée avec le spécificateur ”final” ne peut jamais être dérivée.

12.1.22 Appel statique implicite d’un attribut ou d’une méthode

Il est possible d’appeler une méthode de manière statique, c’est à dire en écrivant NomClasse::FonctionAppeleedès lors que la fonction FonctionAppelee ne fait pas référence à $this dans son bloc de code.

Pour appeler un attribut de classe de manière statique, il faut que l’attribut soit lui-même déclaré explicite-ment comme statique : static $Toto;

12.1.23 Accès statique aux méthodes d’une classe au sein de cette classe :

Supposons qu’on soit dans la classe Tata dans une méthode a définie comme statique et qu’on souhaite accéderà une méthode b non déclarée comme statique, on peut alors y accéder statiquement en écrivant self::b(...).C’est équivalent à Tata::b(...).

67

Page 68: RelectureDePhp5_2011_01_29

12.1.24 autochargement des définitions de classe :

ça se fait en définissant une fonction __autoload($NomClasseACharger). On encapsule alors une instructionrequire_once($CheminFichierPHPDeLaClasse) dans le bloc d’exécution de la fonction __autoload. Utilité ?En fait quand PHP rencontre une classe inconnu, il recours automatiquement alors à la fonction __autoload.Cette fonction permet de faire un require dynamique en passant l’argument de la classe en argument. Du coup,plus besoin de faire des listes de require pour chaque fichier de classe, il suffit d’appeler le require du fichiercontenant la classe requise.

Par exemple, supposons que le nom d’une classe soit structuré comme suit ”C_Prefixe_NomClasse” avecle prefixe étant le type abstrait, interface, final ou normale d’une classe, le code suivant permet d’appeler lesclasses qui se trouveraient dans le répertoire Repclasse :

function __autoload($nomclasse){

if (file_exists($className . ’.php’)) /// NE PAS oublier le file_exists sous peine d’erreur...{

require_once $className . ’.php’;return true;

}return false;

}

IMPORTANT : Pour utiliser __autoload, il vaut mieux adopter :

• un fichier par classe ;

• une règle claire pour désigner le nom des classes ;

NOTONS QUE CE SYSTEME permet une gestion dynamique des includes plutôt qu’une gestion statiqueavec un require sur un fichier classpath.php contenant les require_once de toutes les classes de l’architecture.Pourquoi ? Parce que contrairement au C++, chaque page php doit être légère car elle est ré-interprété par phpà chaque exécution (càd à chaque chargement de page) et donc il vaut mieux donc faire uniquement les requiredes classes nécessaires.

12.1.25 Classes et Sessions :

Quand une instance de classe est sauvé dans le tableau superglobale $_SESSION, seul les attributs et le nom dela classe sont sauvés. ATTENTION : Quand on cherche à récupérer une instance de classe depuis $_SESSION,la classe récupéré DOIT avoir été déclaré AVANt session_start(); Par ailleurs, si sur une page, la classe n’estpas déclaré, même si aucun appel n’est fait à l’objet, celui-ci tel qu’enregistré dans $_SESSION perdra sa classepour devenir __PHP_Incomplete_Class_Name.

La principale difficulté de la gestion des classes avec les sessions est quand des instance de classes sontliés à des ressources externes type fichiers, connexion réseau et fichier DOM (Document Object Model?). Eneffet, quand l’instance de classe est sauvé dans le fichier de $_SESSION, une photographie de ses valeurs estsauvegardé, or si d’autres modifs affectent le lien de l’instance avec les ressources externes, la modif du lien n’est

68

Page 69: RelectureDePhp5_2011_01_29

pas prise en compte tant que l’instance n’est pas à nouveau sauvé dans $_SESSION. Par ailleurs, la fermetureou le refresh de la page web conduit au reload des objets tels qu’ils ont été sauvé la dernière fois en session, etnon suivant leur dernier état avant le refresh ou la fermeture. Pour gérer ces problèmes, php permet de définirdeux fonction__sleep() et__wakeup() qui vont effectuer toutes les opérations nécessaires respectivement àla sauvegarde et au rechargement d’instances de classes. Ces méthodes seront respectivement appelées par votrescript lors de l’utilisation de serialize() et de unserialize(), ces deux appels étant encapsulés dans les instructionde sauvegarde et de lecture dans $_SESSION :

LA fonction __sleep DOIT renvoyer un array dont chaque élément est une chaine de caractère correspondantchacune au nom EXACT d’un attribut de la classe. Si une classe contient trois membres $a, $b et $c et que lafonction __sleep() est définie comme :

function __sleep(){

/// instructions de fermetures des connexions aux ressources externes (BDD, fichier externe,DOM etc.)$DonneesSauvees= array(’a’,’b’);return $DonneesSauvees;

}

seul les attributs $a et $b récupéreront leur valeur respective quand session_start sera appelé.function __wakeup(){

/// instructions de réouverture

$this->c = 10; /// valeur par défaut de l’attribut dont la valeur n’a pas été sauvé};

12.1.26 Les accessors ou méthode d’accès aux méthodes

Derrière ce jolie mot ”accessor” se cache très certainement en C++ l’utilisation des template de fonctions...De la même manière que l’accès aux attributs d’une classe peut être controlé par les fonctions __get et

__set, l’accès aux méthodes d’une classe peut lui aussi être controlé avec la fonction magique __call(). Leprototype de cette fonction est :

__call($NomDelaFonctionAppelee,$ArgumentsFonction)

Le nom de la fonction appelée n’a pas besoin d’être le nom d’une fonction déjà explicitement défini, ilpeut être une chaine qu’on parse de manière à ensuite rediriger l’appel désirée vers la fonction Func désirée.$ArgumentsFonction est de type array et sert à définir quel(s) argument(s) utiliser avec la fonction Func. Sil’appel de fonction comporte x arguments, le tableau $ArgumentsFonction les intégrera comme éléments dansl’ordre d’appel de la fonction.

NOTE : En fait, __call est une sorte de fonction template...

69

Page 70: RelectureDePhp5_2011_01_29

12.2 Eléments de Design Patterns en PHP

Le bouquin examine quelque design patterns parmi les plus utilisés :

12.2.1 Les itérateurs

Honnêtement je m’en cogne un peu à ce stade, il faut juste savoir qu’une classe peut hériter de l’interface nativeIterator. De ce fait, une instance de la classe en question devient manipulable dans une boucle foreach commesuit :

foreach($liste as $uid => $login){

echo ” <br>utilisateur n ◦ uid : $login”;}

avec $liste une instance d’une classe implémentant l’interface native iterator.Une classe implémentant itérateur doit quelque part incorporer :

• un index permettant de parcourir la liste sous-jacente à l’objet ;

• une liste d’éléments avec pour chaque une clef et une valeur ;

Elle doit aussi implémenter les fonctions suivantes :

• current() : renvoie l’élément courant ;

• key() : renvoie la clef de l’élément courant ;

• next() : fait avancer l’index de 1 ;

• valid() : vérifie que si l’index est augmenté de 1, l’élément correspondant existe (utiliser la fonction count);

• rewind() : met l’index à 0 (revient au premier élément donc).

12.2.2 Interface IteratorAggregate

Note : Faire qu’une classe implémente directement l’interface native iterator n’est pas forcément super dans lamesure où cela contraint la structure de la classe. il est possible de faire plutôt implémenter la classe IteratorAggregate. La classe doit alors seulement fournir l’implémentation de la fonction GetIterator() qui doit renvoyerun objet de type Iterator, càd un objet implémentant seulement l’interface iterator.

70

Page 71: RelectureDePhp5_2011_01_29

12.2.3 Notations d’index : l’interface ArrayAccess

L’héritage de l’interface permet de forcer un objet à se comporter comme un tableau et nécessite l’implémentationdes fonctions suivantes :

• offsetExists($index) : vérifie qu’un élémente existe à l’index indiqué ;

• offsetGet($index) : Getteur de l’élément d’index $index ;

• offsetGet($index, $valeur) : sette l’élément d’index $index à $valeur ;

• offsetUnset($index) : unset (càd efface) l’élément d’index $index ;

12.2.4 Php Et UML

Il y a un passage sur l’UML et le php. Le seul point intéressant sur le sujet se trouve au chapitre sur les outilsde développement...

12.3 L’introspection (ou Reflection en anglais)

L’introspection est une série de classes PHP qui permettent :

• une auto-documentation du code dès lors qu’on respecte quelques bonnes pratiques de développement etqui permettent une exploration dynamique de classes internes de PHP ou de classes d’une extension PHP;

• une utilisation dynamique de classe et de méthodes

Les classes d’introspection sont les suivantes et correspondent chacune à une des structures qu’il est possiblede rencontrer dans du code php :

• ReflectionFunction : permet d’explorer une function ;

• ReflectionClass : permet d’explorer une classe ;

• ReflectionException : permet d’explorer une exception (v remarque ENERVE plus loin) ;

• ReflectionObject : permet d’explorer une instance de classe ;

• ReflectionProperty : permet d’explorer un attribut d’une instance de classe (càd un objet);

• ReflectionMethod : permet d’explorer une méthode de classe ;

• ReflectionParameter : permet d’explorer un argument de fonction ou de méthode ;

• ReflectionExtension : permet d’explorer une extension de PHP ;

71

Page 72: RelectureDePhp5_2011_01_29

12.3.1 Introspection de classe :

Pas dur, on crée une instance de ReflectionClass en utilisant le constructeur qui prend en argument le nom dela classe à explorer :

$ReflecClass = new ReflectionClass(’TotoClass’);

puis on utilise la fonction statique export de cette classe :

Reflection::export($ReflecClass)

pour avoir les informations sur la structure de la classe TotoClass. L’affichage est assez claire pour ne pasavoir besoin de le commenter plus ici...

Parmi les méthodes de ReflectionClass :

• getName() : renvoie le nom de la classe explorée ;

• isInternal() : détermine si la classe est native (càd interne à PHP) ou non ;

• isUserDefined() : dit si la classe n’est pas native (càd défini par le programmeur) ;

• isInstantiable() : dit si la classe est instantiable ;

• getFileName() : renvoie le nom du fichier dans laquelle la classe est définie ;

• getStartLine() : renvoie le numéro de ligne à partir de laquelle la classe est définie dans le fichier oùcette classe est définie ;

• getEndLine() : facile...

• getDocComment() : Permet de récupérer le premier commentaire précédent la déclaration du motclé ’classe’ ou ’interface’ dès lors que le commentaire est encadré par /** au début et */ à la fin ducommentaire ;

• getConstructor() : Renvoie une instance de ReflectionMethod sur la méthod __construct de la classeciblée ;

• hasMethod ($name) : renvoie true si la méthode de nom name existe dans la liste des méthodes de laclasse ciblée ;

• getMethod ($name) : renvoie une instance de ReflectionMethod sur la méthode de nom name de laclasse ciblée ;

• getMethods(Optional $filter) : renvoie un tableau de ReflectionMethod avec autant d’éléments quede méthodes définies dans la classe ciblée. Le filtre $filter est une combinaison possible de un ou plusieursdes 6 constantes définies dans la classe ReflectionMethod: IS_STATIC, IS_PUBLIC, IS_PROTECTED,IS_PRIVATE, IS_ABSTRACT et IS_FINAL ; ATTENTION, je n’ai pas d’exemple concret d’utilisationde getmethods avec ces constantes... En fait on sépare les constantes avec une barre verticale et atten-tion , il faut préfixer chaque constante par le nom de la classe ReflectionMethod. Ex : Reflection-Method::IS_STATIC|ReflectionMethod::IS_PUBLIC filtre (càd renvoie) les méthodes public et statiquesuniquement ;

• hasProperty($name) : renvoie true si l’attribut de nom $name existe dans la classe ciblée ;

72

Page 73: RelectureDePhp5_2011_01_29

• getProperty($name) : renvoie un instance de ReflectionProperty si la méthode de nom $name existedans la classe ciblée. ATTENTION dans le cas contraire, une erreur fatale est produite ;

• getProperties(optional $filter) : renvoie un tableau de ReflectionProperty correspondant aux attributsde la classe ciblée ; le filtre s’utilise comme pour getMethods avec 4 constantes seulement :IS_STATIC,IS_PUBLIC, IS_PROTECTED et IS_PRIVATE

• hasConstant($name) : renvoie true si la constante de nom $name existe dans la classe ciblée ;

• getConstants() : renvoie un tableau associatif de toutes les constantes présentes dans la classe ciblée ;

• getConstant($name) : renvoie la valeur de l’entier correspondant à la constante de nom $name. Plantefatal si $Name n’existe pas ;

• getInterfaces() : renvoie un tableau associatif de ReflectionClass contenant toutes les interfaces implé-mentées par la classe ciblée ;

• getInterfaceNames() : renvoie un tableau numérique contenant tous les noms des interfaces implémen-tées par la classe ciblée ;

• isInterface() : renvoie true si la classe ciblée est une interface ;

• isAbstract () : renvoie true si la classe ciblée est abstraite ;

• isFinal () : renvoie true si la classe ciblée est finale;

• getModifiers() : renvoie un entier (qui est en fait un enum) qui correspond aux modificateurs quipréfixe les déclarations d’attributs, de membres, de function ou de classe (type : abstract protected pourune méthode abstract protected);

• isInstance($objet) : renvoie true si $objet est une instance de la classe ciblée ;

• newInstance(...) : permet de créer une instance de la classe ciblée. La fonction fait appel à un nombrevariable d’arguments de manière à coller à la fonction __construct appeler de manière sous-jacente.

• newInstanceArgs (array $args ) : pareil en passant les arguments $args élément par élément commeargument sucessif de __construct. (quelle est l’utilité respective des deux fonctions ?) ;

• getParentClass() : renvoie une instance de ReflexionClass de la classe parent de la classe ciblée si la classeparente existe, ERREUR FATALE sinon !

• isSubclassOf($class ) : renvoie true si la classe ciblée est une classe dérivée de $class ;

• getStaticProperties() : renvoie un tableau contenant tous les attributs statiques d’une classe ;

• getStaticPropertyValue($AttributName, optional string $Default) : renvoie la valeur de l’attribut statiquede nom $AttributName de la classe ciblée. Effet de l’option ? inconnu ...;

• setStaticPropertyValue(string $AttributName, string $valeur) : sette l’AttributName de la classe ciblée à$valeur ;

• getDefaultProperties() : renvoie dans un tableau associatif les valeurs par défaut des attributs de la classeciblée ainsi que leur nom en clef en face ; (est par défaut un attribut qui est déclaré dans la classe. Unattribut peut ne pas être par défaut s’il est ajouté de manière sauvage (v. attributs sauvage précédemment)

• isIterateable() : true si la classe ciblée implémente l’interface Iterator ;

73

Page 74: RelectureDePhp5_2011_01_29

• implementsInterface($NomInterface) : Renvoie true si la classe ciblée implémente l’interface de nom $in-terface ;

• getExtension() : renvoie un objet de type ReflexionExtension si la classe ciblée fait partie d’une extension.

• getExtensionName() : renvoie le nom de l’extension à laquelle appartient éventuellement la classe ciblée,vide si pas classe ciblée pas dans une extension ;

• inNamespace($NameSpaceName) : renvoie true si la classe ciblée appartient au Namespace de nom$NameSpaceName ;

• getNamespaceName () : renvoie le nom du namespace auquel appartient la classe ciblée, renvoie VIDE sila classe n’appartient à aucun namespace ;

• getShortName() : renvoie le nom de la classe sans le nom du namespace qui précède.

12.3.2 Appel de fonction via la méthode invoke de la classe ReflectionMethod ou Reflexion-Function :

Pas dur, on crée une instance de ReflectionMethod ou ReflexionFunction basée sur la fonction ciblée, puis lafonction peut-être exécuté à partir de la classe de réflexion en utilisant la méthode membre invoke.

Exemple :

$ReflecFunc = new ReflectionFunction(’mt_rand’);$ReflecFunc->invoke(2,14); /// va générer un entier compris entre 2 et 14.

13 Chapitre 13 : Gestion des fichiers

Le chapitre traite de la lecture, écriture et manipulation de fichiers. Pourquoi il n’est pas arrivé avant, mystère... Ce chapitre est indispensable à la gestion des flux et au traitement des sockets TCP/IP...

13.1 Fonctions d’accès rapide aux fichiers

• file_get_contents($cheminFichier, Optional $flags, Optional $context, Optional $OffSet,Optional $MaxLen) : ouvre le fichier se trouvant à l’adresse $cheminfichier et balance le contenudans une string ; La valeur de flags peut être n’importe quelle combinaison des drapeaux suivants (avecquelques restrictions), joints avec l’opérateur OR binaire (|) :

– FILE_USE_INCLUDE_PATH (Recherche le fichier filename dans le dossier d’inclusion. Voir in-clude_path pour plus d’informations),

– FILE_TEXT (Si la sémantique unicode est activée, l’encodage par défaut pour la lecture des donnéessera UTF-8. Vous pouvez spécifier un encodage différent en créant un contexte personnalisé ou enmodifiant celui par défaut en utilisant la fonction stream_default_encoding(). Ce drapeau ne peutêtre utilisé avec FILE_BINARY),

74

Page 75: RelectureDePhp5_2011_01_29

– FILE_BINARY Avec ce drapeau, le fichier est lu en mode binaire. C’est la configuration pardéfaut et ne peut être utilisé avec FILE_TEXT.

La valeur de $context correspond à une ressource de contexte valide, créée avec la fonction stream_context_create().Si vous n’avez pas besoin d’utiliser un contexte particulier, vous pouvez ignorer ce paramètre en affectantla valeur NULL.

La valeur d’offset correspond à la position à partir de laquelle on commence à lire.

La valeur de maxlen correspond à la taille maximal de données à lire.

RENVOIE FALSE si une erreur survient.

• readfile(string $filename, Optional bool $use_include_path, Optional resource $context) :ouvre un fichier se trouvant à l’adresse $filename et affiche directement son contenu sur la sortie deflux principale (l’écran), $use_include_path indique si le fichier $filename doit être recherché dansles répertoires d’include de la directive php.ini include_path, $context identique à la fonction précédentemais rien n’est précisé ni dans le bouquin, ni sur le site de php.net

• fopen ( string $filename , string $mode [, bool $use_include_path [, resource $context ]] ) :ouvre le fichier de nom $filename en mode $mode. Ce mode est un des 8 modes suivants auquel on ajouteun indicateur de traduction de fin de lignes :

– ’r’ : lecture seule ;

– ’r+’ : lecture et écriture ;

– ’w’ : accès en écriture au fichier, ATTENTION le fichier est d’abord intégralement effacé avant quel’écriture puisse commencer. En clair, accès en réécriture du fichier ; Si le fichier de nom $filenamen’existait pas, php essaie de le créer.

– ’w+’ : pareil mais on peut aussi lire ce qu’on a écrit ;

– ’a’ : accès en écriture pour ajout. Le contenu est conservé à l’ouverture du fichier et ce qui écrit estajouté à la fin du fichier ; création du fichier si pas existant

– ’a+’ : pareil que ’a’ avec possibilité de lecture en + ;

– ’x’ : crée et ouvre le fichier en lecture seule. Impossible donc d’ouvrir un fichier déà existant(bizarre...?). contrairement aux autres modes marche uniquement sur des fichiers locaux. Impossiblede se servir de ce mode sur des fichiers distants.

– ’x+’ : pareil mais en plus un accès en écritureA ce mode, s’ajoute un indicateur de traduction de fin de ligne : le pb vient des différents OS qui neconsidère pas le même caractère pour signifier une fin de ligne : \n chez Linux, \r\n ches windows et\r chez Mac. En fait, faut juste retenir que windows a besoin d’avoir ’t’ comme indicateur de findeligne quand il accède à un fichier texte et besoin de ’b’ quand il accède à un fichier binaire. Pouraccéder à un fichier UNIX/Linux quel qu’il soit, TOUJOURS mettre ’b’ comme indicateur de fin deligne.RENVOIE un pointeur sur le fichier ouvert. Bien sur, ca n’apparait pas comme un pointeur en PHP.On appelle ça poliment un handle.ATTENTION fopen renvoie un warning d’erreur si on essaie d’ouvrir un fichier quin’existe pas. Il vaut mieux utiliser file_put_contents pour écrire dans un nouveau fichier.

• fclose($RessourceResultatdeFopen): permet de fermer un fichier ouvert avec fopen.

75

Page 76: RelectureDePhp5_2011_01_29

• file(string $filename, Optional int $flags, Optional resource $context) : ouvre le fichier $filenameet renvoie un tableau dont chaque élément est une ligne du fichier ouvert ; Les flags possibles sont lessuivants :

– FILE_USE_INCLUDE_PATH : Recherche le fichier dans l’include_path ;

– FILE_IGNORE_NEW_LINES : N’ajoute pas de nouvelle ligne à la fin de chaque élément dutableau.

– FILE_SKIP_EMPTY_LINES : Ignore les lignes vides.

– FILE_TEXT : Le contenu est retourné en utilisant l’encodage UTF-8. Vous pouvez spécifier unencodage différent en créant un contexte personnalisé. Ce drapeau ne peut être utilisé avec le drapeauFILE_BINARY.

– FILE_BINARY : Le contenu est lu en tant que données binaires. C’est le comportement pardéfaut et ne peut être utilisé avec le drapeau FILE_TEXT.

L’option $context est une ressource de contexte valide, créée avec la fonction stream_context_create(voirplus loin).

• parse_ini_file(string $filename, Optional bool $process_sections, Optional integer $scan-ner_mode ) : ouvre le fichier de nom $filename et s’il est bien structuré comme un .ini classique,renvoie un tableau associatif dont chaque élément contient chacun une directive en clef et sa valeur envaleur. Attention si une directive est définie comme constante avant l’appel à parse_ini_file par exemple: define(’session.name’, BGSESSID), la valeur de session.name (PHPSESSID par défaut) sera écrasée parBGSESSID dans le tableau renvoyée (MAIS PAS DANS LE FICHIER php.ini)

– $process_sections : Si true, le tableau renvoyé est un tableau composée dont chaque sous-tableaucorrespond à une sous-section du .ini (une sous-section est signalé par un nom compris entre [] type[PHP] ou [Xdebug] dans php.ini ;

– $scanner_mode : deux valeurs possibles : INI_SCANNER_NORMAL par défaut et INI_SCANNER_RAWqui fait que ne sont pas retenus dans le tableau renvoyé les valeurs optionnelles du .ini ouvert; AT-TENTION INI_SCANNER_RAW peut faire planter l’appel de la fonction...

• fgetcsv(resource $handle, Optional int $length, Optional string $delimiter, Optional string$enclosure , Optional string $escape ) : permet de lire spécifiquement un fichier csv en lui donnantle handle récupérée avec l’usage de fopen sur le chemin d’accès au fichier .csv (Note : un .csv formate unfichier comme un tableau de n ligne et de m colonnes); $lenght correspond à la longueur maximale d’uneligne du fichier (par défaut l’infini); $delimiter est le caractère de séparation d’une cellule du tableau.csv à l’autre sur une même ligne (d’où csv : comma separated values), $enclosure spécifie le caractèrede délimitation (par défaut ”), $escape spécifie le caractère d’échappement (par défaut \) ; ATTENTIONfgetcsv lit seulement une ligne du tableau csv à la fois. POUR lire n LIGNES DE SUITE, il fautappeler n fois la fonction fgetcsv. A chaque appel de fgetcsv, le handle ou pointeur ”bouge”d’une ligne.

Un exemple d’usage de lecture de tableau et affichage d’un .csv est le suivant :

$handleCSV = fopen($CheminFichierIndice,”rb”);

$csvCAC = fgetcsv($handleCSV,1000);

$row = 0;

/// on récupère les têtes de colonne

$num = count($csvCAC);

76

Page 77: RelectureDePhp5_2011_01_29

for ($i= 0;$i<$num;$i++)

{

$TetCol[$i] = $csvCAC[$i];

}

while (($csvCAC = fgetcsv($handleCSV,1000)) !== FALSE)

{

$num = count($csvCAC);

echo ”<p> $num champs à la ligne $row: <br /></p>\n”;

$row++;

for ($c=0; $c < $num; $c++)

{

echo $TetCol[$c].” = ”.$csvCAC[$c] . ”<br />\n”;

}

}

fclose($handleCSV);

• file_put_contents( string $filename , mixed $data, Optional int $flags, Optional resource$context ) : ouvre un fichier du nom de $filename (écrase le fichier si existant) et écrit $data dedans.$data doit être soit une string soit un tableau, soit une ressource de flux. Si $data est ressource de flux detype stream, c’est le buffer restant qui écrit dans le fichier. Les flags peuvent être les suivants et combinésavec l’opérateur ¦ :

– FILE_USE_INCLUDE_PATH : Recherche le fichier filename dans le dossier d’inclusion. Voirinclude_path pour plus d’informations.

– FILE_APPEND : Si le fichier filename existe déjà, ce drapeau permet d’ajouter les données aufichier au lieu de l’écraser.

– LOCK_EX : Acquière un verrou exclusif sur le fichier lors de l’opération d’écriture.

– FILE_TEXT : Les données data sont écrites en mode texte. Si la sémantique unicode est activée,l’encodage par défaut pour la lecture des données sera UTF-8. Vous pouvez spécifier un encodagedifférent en créant un contexte personnalisé ou en modifiant celui par défaut en utilisant la fonctionstream_default_encoding(). Ce drapeau ne peut être utilisé avec FILE_BINARY. Ce drapeau estuniquement disponible depuis PHP 6.

– FILE_BINARY : Les données data seront écrites en mode binaire. C’est la configuration pardéfaut et ne peut être utilisée avec le drapeau FILE_TEXT. Ce drapeau n’est disponible qu’à partirde PHP 6.

$context peut être une ressource de contexte valide créée avec la fonction stream_context_create().UTILISER cette fonction revient à utiliser successivement les fonctions fopen(), fwrite(), et fclose().

• filesize($CheminNomFichier) : renvoie la taille du fichier de nom $CheminNomFichier ;

• fscanf() : identique à sscanf sauf que sscanf utilise une chaine de caractère en entrée alors que fscanfutilise un handle de fichier. fscanf permet d’utiliser une expression régulière pour récupérer une chainepréformaté (v. chapitre 5) ;

77

Page 78: RelectureDePhp5_2011_01_29

• fwrite($handleFichier, $string, Optional $length ) : ecrit la chaine $string dans le fichier ouvertavec fopen et dont le handle est $handleFichier. Si $Lenght est spécifié, au maximum $Lenght octetsseront écrits (càd min entre mb_strlen($string) et $lenght. ATTENTION : quoiqu’il arrive, un deuxièmeappel à fwrite n’écrase pas ce qui a été écrit sur le même fichier dans un premier appel ; ATTENTION :un appel de fread après fwrite ne renvoie qu’une chaine vide car le pointeur interne de lecture se situe àla fin du fichier après l’usage de fwrite, il faut repositionner le pointeur avec rewind ou fseek (v.plus bas)pour lire le contenu du fichier à partir de la position souhaitée. SI LE FICHIER EST ouvert en mode ”a”ou ”a+”, quelque soit la position du pointeur interne de lecture/écriture, la chaine $string est ajouté enfin de fichier de sorte que rien n’est écrasé. Pour remplacer un bout du fichier, il faut ouvrir le fichier avecfopen en mode ”r+”;

• fputs(...) : c’est exactement la même fonction que fwrite, c’est an fait un alias...

• fgetc($handle) : lit et renvoie le caractère sur lequel pointe le pointeur interne puis avance ce pointeurd’un octet ;

• fgets($handle) : lit et renvoie la ligne courante sur lequel pointe le pointeur interne puis avance cepointeur jusqu’à l’octet succédant à la fin de ligne (\n ou \r ou autre suivant OS). Si la ligne courante estla dernière du fichier, le pointeur pointera sur la fin du fichier ; IMPORTANT : la fonction stream_get_linefait la même chose mais en mieux !!

13.1.1 Positions dans un fichier :

Pour gérer le position du pointeur interne de lecture/écriture dans un fichier, il faut utiliser les fonctions suivantes:

• rewind($handle) : remet le pointeur interne au début du fichier dont le handle est $handle ;

• fseek($handle, int $offset, Optional $whence) : modifie la position du pointeur du fichier de handle$handle. Le pointeur est décalé $Offset octets vers la fin ou le début du fichier suivant la valeur $whence.$whence a trois valeurs possibles :

– SEEK_SET : valeur par défaut de $whence, dans ce cas, l’exécution de fseek($handleFichier, $Offset)positionne le pointeur au $OffSet ième octets du fichier en partant du début ;

– SEEK_CUR : positionne à la position courante + $OffSet Octets ;

– SEEK_END : positionne à la fin du fichier décalé de $OffSet Octets (ATTENTION : $OffSet si onsouhaite que le pointeur pointe encore à l’intérieur du fichier);

• ftell($handle) : renvoie la position courante du pointeur dans le fichier dont le handle est $handle.

• feof($Handle) : renvoie true si le pointeur interne du fichier de handle $Handle est à la fin du fichier.C’est la fonction la plus rapide pour ce boulot.

ATTENTION : Le fait que le pointeur interne tape en dehors du fichier courant NE POSE AUCUN PB.

78

Page 79: RelectureDePhp5_2011_01_29

13.1.2 Tampons mémoires et écriture fichiers :

En appelant les fonctions d’écriture précédente, les informations ne sont pas forcément écrites instantanémentdans le fichier cible. PHP utilise un tampon mémoire dans lequel est stocké en premier lieu l’information àécrire. PHP ne recopie dans le fichier que par paquets ou juste avant la fermeture des fichiers (par exemple, sifread suit fwrite, le tampon mémoire est écrit dans le fichier avant l’exécution de fread).

Cependant on peut :

• forcer PHP à écrire avec l’instruction fflush($Handle) qui va vider le tampon et écrire son contenu dansle fichier dont le handle est $Handle ;

• setter la taille du buffer avec set_file_buffer($FileHandle, $BufferEnOctets) ou stream_set_write_buffer($FileHandle,$BufferEnOctets)

13.1.3 Accès concurrents à un même fichier :

En pratique, un script est utilisé par n clients sur le web et donc si ce script accède à un fichier en écriture et ouen lecture, il faut s’assurer que les accès concurrents ne vont pas faire corrompre le fichier (par exemple quanddeux personnes écrivent en même temps dedans.

Pour accéder à un fichier de manière générale, il faut alors :

• vérifier que le fichier existe avec ???

• l’ouvrir avec fopen ;

• le locker avec la fonction flock ;

• lire et/ou écrire dessus suivant son besoin avec fread et fwrite;

• appeler fflush pour s’assurer que toutes les écritures sont bien écrites dans le fichier ;

• délocker le fichier avec fflock ;

• fermer le fichier avec fclose.

La fonction flock() :

• flock($HandleFichier, $Operation) : locke le fichier de handle $HandleFichier suivant le mode $Op-eration retenu. Les modes possibles sont :

– LOCK_SH : donne un verrou partagé en lecture (n clients peuvent ainsi accéder au fichier ciblé) ;– LOCK_EX : donne un verrou exclusif en écriture ( 1 seul client sur le fichier en écriture)– LOCK_UN : délocke le fichier s’il était locké.

A noter que fclose équivaut à unlocker le fichier comme si flock(.,LOCK_UN) avait été appelé.

ATTENTION comme flock est appelé après fopen. Si l’ouverture se fait en mode w ou w+, le fichier esttronqué à l’ouverture et dans ce cas, deux clients peuvent éventuellement se trouver à lire et surtout écrireen même temps. LE BOUQUIN en parle page 327 -328 MAIS CE N’EST ABSOLUMENT PAS CLAIR.GROS HIC, le verrouillage n’est valable que sur un processus et apparemment sur Apache sous Windows,le recours aux multi-threads empêchent un verrouillage efficace.VOIR COMMENT GERER LE PB !!!!

79

Page 80: RelectureDePhp5_2011_01_29

• ftruncate() : permet de gérer plus finement le pb des accès concurrents en écriture. RECHERCHE AFAIRE.

13.1.4 Manipulation de fichiers

Les fonctions standard pour déplacer, copier, renommer les fichiers :

• copy($CheminDepart,$CheminArrivee,Optional $Context) : déplace le fichier se trouvant au$CheminDepart vers l’emplacement $CheminArrivee. Renvoie TRUE si succès, FALSE sinon. ATTEN-TION : $CheminDepart ET $CheminArrivee doivent contenir le nom du fichier. Dans le cas de chemind’arrivée, si le nom du fichier d’arrivée est différent, ça conduit à copier le fichier puis à le renommer; $Context correspond à un contexte (?) crée avec stream_context_create(). Comme le bouquin estbizarrement fichu, les contextes sont vus dans un chapitre ultérieur, allez savoir pourquoi...

• rename($CheminDepart,$CheminArrivee,Optional $Context) : en fait, ça ne fait pas que renom-mer, ça déplace le fichier façon couper/coller, si le chemin contenu dans $CheminArrivee n’est pas le mêmeque $CheminDepart. Un peu con comme nom de fonction...

13.1.5 Création de fichiers :

Deux méthodes pour créer un fichier :

• Ouvrir le fichier en écriture (mode w) avec fopen ;

• Ouvrir le fichier avec la fonction touch qui le créera s’il n’existe pas :touch($FileName, Optional Int $timeStamp, Optional int $aTimeStamp) : cette fonction aseulement pour but de changer la date de modification et de dernier accès du fichier ; Les deux variables$Time et $aTime sont des timestamps UNIX. L’unité est donc la seconde et donc pour ajouter 10 joursà la date courante, il faut écrire time() +10*24*3600

13.1.6 Effacement de fichiers :

Avec la fonction ulink() :

• ulink($FileName, Optional $Context ) : Efface le fichier de nom $FileName. L’option $Contextse rapporte au contexte qu’on verra plus loin ; Renvoie true si le fichier $FileName a bien été effacée, falsesinon.

Fichiers temporaires :On peut créer des fichiers temporaires qui seront stockés dans le répertoire temporaire et seront automa-

tiquement détruits à la fin du script :

• tmpfile() : crée un fichier temporaire et renvoie le handle correspondant ;

• tempnam(string $Dir, string $prefix) : Crée un fichier temporaire dans le répertoire $Dir. Le nom dufichier commencera par la chaine $prefix. Renvoie le nom et le chemin absolu complet du fichieret non son handle. ATTENTION : sous Windows, le prefix sera limité à trois caractères et $prefix seraamputé des caractères surnuméraires si besoin est !

• sys_get_temp_dir() : renvoie le chemin du repertoire tmp du système ;

80

Page 81: RelectureDePhp5_2011_01_29

13.1.7 Gestion des répertoires :

Un répertoire est en fait un fichier classique avec quelques caractéristiques propres mais en tant que fichier, ilest donc manipulable comme n’importe quel autre fichier. Pour identifier un fichier en tant que répertoire, onpeut utiliser la fonction is_dir() :

• is_dir($FileName) : renvoie true si $FileName est un répertoire, false sinon ;

• is_file($FileName) : renvoie true si $FileName est un fichier, false sinon. Renvoie false si $FileNameest un répertoire ;

Création de répertoire : La création se fait avec mkdir() :

• mkdir( string $pathname, Optional int $mode, Optional bool $recursive, Optional resource$context) : Crée le répertoire correspondant au chemin $pathname. L’option $mode régule les droitsd’accès au répertoire crée. ATTENTION : l’option $mode est ignorée sur Windows de sorte que le répertoirecrée l’est toujours avec les droits d’accès les plus laches (tout le monde a accès au répertoire crée), L’option$Recursive permet de demander la création des répertoires parents présents dans le chemin $pathnames’il n’existe pas. $context correspond à la notion de context qu’on verra plus loin (Grrr...)

• rmdir( string $dirname, Optional resource $context ) : flingue le répertoire dont le nom est$dirname. L’option sera vu plus tard...

Parcourir un répertoire Deux approches permettent de manipuler les répertoires, la première est uneapproche fonctionnelle basée sur les fonctions opendir(), readdir, rewinddir(), et closedir() :

• opendir(string $path, Optional resource $context ) : ouvre le répertoire dont le chemin est $pathet renvoie un handler dessus ; Pour l’option, on verra plus tard.

• readdir(Optional handle $dir_handle ) : Lit les fichiers un par un dans le dernier répertoire ouvertavec l’instruction opendir, et lit sinon dans le répertoire dont le handler est $dir_handle ; Un appel àread_dir() renvoie le nom du fichier en cours de lecture et false dès que tous les fichiers dans le répertoirecible ont déjà été lus. L’ordre de lecture est l’ordre dans lequel les fichiers ont été enregistrés dans lerépertoire. ATTENTION : parmi ces fichiers, on trouve les répertoires contenus dans le répertoire cibleet on trouve notamment deux répertoires spéciaux qui sont ”.” et ”..” qui correspondent respectivementau répertoire cible lui-même et à son répertoire parent. NOTE : readdir utilise un pointeur interne quidésigne à chaque appel de readdir le fichier suivant. De ce fait, une fois un fichier appelé, il est impossiblede revenir dessus avec seulement des appels de readdir. Le pointeur interne ne fait qu’avancer avec readdir.NOTE : le pointeur interne est en fait crée par la fonction opendir.

• rewinddir( handler $dir_handle ) : une suite d’appel à readdir fait qu’un fichier contenu dans lerépertoire cible ne sera jamais lu qu’une fois. rewinddir() permet de faire revenir le pointeur interne audébut de la liste des fichiers du répertoire cible ;

• closedir( handler $dir_handle ) : fermer le répertoire désigné par le handle $dir_handle et ouvertavec opendir ;

Une approche est d’avoir à un objet de type Directory avec l’instruction dir qui renvoie un tel objet.

81

Page 82: RelectureDePhp5_2011_01_29

• dir($PathName, Optional ressource $context) : renvoie un objet de type directory correspondant

au répertoire de chemin $PathName. ATTENTION : si le chemin n’est pas valide, un warning est affichéet l’exécution du script est arrêté !! Il faut donc utiliser file_exists avant pour s’assurer que le répertoireexiste .

Cet objet a alors comme méthodes : read(), rewind(), close() qui ont les mêmes prototypes que les fonctionsprécédentes correspondantes à ceci près qu’il est inutile de spécifier le handle vu qu’il s’agit de l’objet sur lesquelles méthodes sont appelées...

Filtrage de fichiers d’un répertoire :

• glob(string $pattern, Optional int $flags ) : renvoie un tableau des fichiers correspondant au masquecible $pattern, un tableau vide si aucun fichier ou répertoire ne correspond au masque $pattern et car-rément false s’il y a une erreur. ATTENTION : le masque tient compte de la casse entre minuscules etmajuscules.

ATTENTION : glob ne renvoie pas les fichiers cachés !!!

L’option $flags permet d’affiner la recherche avec les options (composables avec - | ) suivantes :

– GLOB_MARK : Ajoute un slash final à chaque dossier retourné. Par exemple : un répertoire denom Toto apparaitra comme \Toto\

– GLOB_NOSORT : Retourne les fichiers tant l’ordre d’apparence (pas de tri)

– GLOB_ONLYDIR : Ne retourne que les dossiers qui vérifient le masque

– GLOB_NOCHECK : Retourne le masque de recherche si aucun fichier n’a été trouvé. ATTEN-TION si GLOB_ONLYDIR est spécifié, le tableau renvoyé sera vide car le masque est écrit etconsidéré comme un fichier et donc GLOB_ONLYDIR éjecte le masque de retour dans le tableaurenvoyé ;

– GLOB_NOESCAPE : Ne protège aucun métacaractère d’un antislash

– GLOB_BRACE : Remplace {a,b,c} par ’a’, ’b’ ou ’c’. JE PIGE PAS MAIS ca n’a pas l’air biengrave !!

– GLOB_ERR : Stop lors d’une erreur (comme des dossiers non lisibles), par défaut, les erreurs sontignorées.

Répertoire courant de travail et répertoire de courant du script en courant : Le répertoire courantde travail est obtenu avec getcwd() tandis que le répertoire contenant le script dans lequel le code courant estexécuté est obtenu avec dirname(__FILE__);

Changement du répertoire courant de travail :

• Chdir($NewCurDir) : renvoie true si la fonction est parvenue à faire en sorte que le répertoire courantest le répertoire $NewCurDir ; ATTENTION : Le changement de répertoire courant n’est valide quejusqu’à la fin du script, ensuite le répertoire courant redevient celui spécifiée par la directive docroot ;

82

Page 83: RelectureDePhp5_2011_01_29

Informations sur les fichiers : Le truc à savoir c’est que la récup d’informations sur les fichiers est couteuseen perf pour php et donc celui-ci garde en fait un max d’infos en cache et ressert l’info stocké en cache pourrépondre à des demandes d’informations sur les fichiers. De ce fait, si on veut être sur que les informationsne sont pas datées, il faut appeler la fonction clearstatcache. Concrêtement le résultat d’un premier appelà une fonction d’information (dont la liste suit) est stocké dans le cache. Suite à un second appel, php ressertalors le résultat stocké et ne réexécute pas la fonction de récup d’info elle-même. Les fonctions impactées sont: stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(),is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), file-type(), et fileperms().

• clearstatcache(Optional bool $clear_realpath_cache, Optional string $filename ) : Sans op-tions, vide tout le cache php d’informations sur le système de fichiers, si $clear_realpath_name vaut true,nettoie le cache réel (?), si $filename est aussi spécifié et que la première option vaut true, seul le cached’informations concernant le fichier de nom $filename est vidé ;

Les fonctions impactées (en dehors de celles déjà vues) :

• file_exists($filename) : renvoie true si le fichier, répertoire OU le lien $filename existe, false sinon

• stat($filename) : renvoie un tableau associatif contenant des infos sur le fichier ciblé. Chaque info eststocké dans ce tableau. Une fois avec une clef numérique car la première version de cette fonction renvoyaitun tableau classique et une autre fois avec une clef associative car la nouvelle version de la fonction autorisede récupérer les infos en utilisant des clefs associatives plus intuitives que les clefs numériques. De ce fait,en plus de retourner ces attributs dans un tableau numérique, les infos peuvent être lues à l’aide de leursindices, tels que notés près de chacun des paramètres. Les infos contenus dans le tableau sont les suivantes:Numéro Nom (depuis PHP 4.0.6) Description

– 0 dev volume

– 1 ino Numéro d’inode (*)

– 2 mode droit d’accès à l’inode

– 3 nlink nombre de liens

– 4 uid userid du propriétaire (*)

– 5 gid groupid du propriétaire (*)

– 6 rdev type du volume, si le volume est une inode

– 7 size taille en octets

– 8 atime date de dernier accès (Unix timestamp)

– 9 mtime date de dernière modification (Unix timestamp)

– 10 ctime date de dernier changement d’inode (Unix timestamp)

– 11 blksize taille de bloc (**)

– 12 blocks nombre de blocs alloués (**)

* - Sous Windows, vaudra toujours 0.

* - uniquement sur les systèmes qui supportent le type st_blksize. Les autres systèmes (e.g. Windows)retournent -1.

En cas d’erreur, stat() retourne FALSE.

83

Page 84: RelectureDePhp5_2011_01_29

• lstat() : fait pareil que stat sauf que lstat récupère des infos sur le lien symbolique et non sur le lienphysique s’il y a une différence. Ex : si on fait un stat sur un raccourci pointant sur un fichier, on obtiendrades infos sur le fichier avec stat, et on obtiendra des infos sur le raccourci avec lstat ; PROBLEME : lstatet stat ne marchent pas sur les raccourcis de fichiers sous Windows VERIFIER.

Les dates associées aux fichiers : Trois dates sont associées à chaque fichier : date de création, date demodifcation (dernier accès en écriture seule), date de dernier accès (lecture, écriture ou exécution)

• filectime($filename) : renvoie la date de création du fichier sous forme de timestamp (ATTENTION :sous la plupart des systèmes UNIX, il n’y a pas de date de création et c’est alors la date de modif qui estrenvoyé) ;

• filemtime($filename) : renvoie la dernière date de modification du fichier sous forme de timestampUNIX (utiliser la fonction date pour récupérer un format de date classique) ;

• fileatime($filename) : renvoie la dernière date d’accès au fichier sous forme de timestamp UNIX.ATTENTION : pour motif de perf, la traque de la dernière date d’accès est souvent désactivée sur lesserveurs...

Autres fonctions :

• filesize($Nomfichier) : renvoie la taille en octets du fichier ciblé ;

• fileinode($NomFichier) : renvoie l’id du fichier sur le disque, càd l’inode et renvoie false si un problèmesurvient ; VERIFIER si ça marche sur Windows car renvoie toujours 0 ( et ce n’est pas égale à false)

• fstat(...) : pareil que stat mais prend un handle de fichier en argument au lieu d’un nom de fichier ;

• disk_free_space($path) : renvoie en octets l’espace disque disponible sur la partition auquel appartientle chemin $path ; (marche pas avec les fichiers distants)

• disk_total_space($path) : pareil mais renvoie l’espace disque total dispo et non dispo ;

• realpath($CheminVersFichier) : renvoie le chemin absolu correspondant à l’adresse $CheminVers-Fichier, utile quand cette dernière est une adresse relative ou un lien symbolique ;

• dirname($CheminVersFichier) : renvoie la partie chemin contenu dans l’adresse $CheminVersFichier(en clair tout le chemin sauf le nom du fichier lui-même. ATTENTION : renvoie le chemin relatif amputédu nom dans le cas d’un chemin relatif ;

• basename($CheminVersFichier) : fait l’inverse de dirname, càd renvoie le nom du fichier contenudans le chemin (avec le suffixe type .php ou .xls) ;

• pathinfo($CheminVersFichier, Optional int $options) : renvoie un tableau associatif contenant ledirname (comme un appel à dirname()), le basename (comme un appel à basename()), l’extensionname(ex : php ou xls càd sans le ”.” comme dans .php) et le filename (comme basename mais sans le suffixe type.php ou .xls) ; Options est une composition de constantes entières avec | . Les constantes possibles sont :PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION et PATHINFO_FILENAME.Par défaut, tous les éléments sont retournés. ATTENTION si Options est utilisé et que toutes les infos nesont pas demandés, c’est une chaine de caractère qui est renvoyé et non plus un tableau... un peu débilede changer le type de retour, non ? LA chaine de retour renvoyée dépend des options choisies. Rien devraiment intéressant et un peu bizarre dans le comportement dans ce cas d’ailleurs...

• readlink() : FONCTION PAS IMPLEMENTEE SOUS WINDOWS !!

84

Page 85: RelectureDePhp5_2011_01_29

Permissions et droits d’accès sur fichiers : Un fichier a un propriétaire utilisateur et groupe, des droitsd’exécution, de lecture, et de modification. Pour récupérer les droits associées, on utilise :

• fileperms($filename) : renvoie les droits associées au fichier, répertoire ou autre lien de nom $filename.ATTENTION : la valeur renvoyé est une valeur numérique qui doit être analysé pour récupérer réellementl’info. Le code suivant utilise le retour de fileperms pour renvoyer une chaine décrivant le type de l’objetassocié au filename (”l” : lien symbolique, ’-’ : régulier (càd un fichier normal), ’b’ : bloc spécial, ’d’: dossier, ’c’ :caractère spécial, ’p’ : PIPE info et ’u’ : unknown) et pour l’user courant, le group usercourant et les autres users, les droits en lecture (r), écriture (w) et exécution (x) sur le fichier ciblé.Quand l’utilisateur courant, le groupe utilisateur de l’utilisateur courant, ou les autres users n’a pas undroit donné, on trouvera dans la chaine le symbole ’-’ à la place de ’r’,’w’ ou ’x’ ;

• is_readable($FileName) : précise à l’utilisateur courant du script (logiquement ceux du serveur web)si le fichier $FileName peut être lu ;

• is_writable($FileName) : précise à l’utilisateur courant du script (logiquement ceux du serveur web)si le fichier $FileName peut être écrit ;

• is_executable($FileName) : précise à l’utilisateur courant du script (logiquement ceux du serveurweb) si le fichier $FileName peut être exécuté (ce qui ne signifie pas que le fichier ne puisse pas être ouvertcomme un .jpg par exemple) ;

• fileowner($FileName) : renvoie l’id du propriétaire utilisateur du fichier $FileName ;

• filegroup($FileName) : renvoie l’id du propriétaire groupe du fichier $FileName ;

• chown($FileName, $UserCible) : permet de désigner $UserCible comme utilisateur propriétaire de$FileName ; Note : Impossible de tester cette fonction faute de savoir comment identifier un autre util-isateur ;

• chgrp($FileName, $GrpCible) : permet de désigner $GrpCible comme groupe propriétaire de $File-Name ; Note : Impossible de tester cette fonction faute de savoir comment identifier un autre utilisateur;

• chmod($FileName, $NewMode) : $NewMode est une valeur octale (donc commençant par 0)correspondant au mode souhaité : Le paramètre mode est constitué de trois valeurs octales qui spécifientles droits pour le propriétaire, le groupe du propriétaire et les autres, respectivement. Chaque composantpeut être calculé en ajoutant les droits désirés. Le chiffre 1 donne les droits d’exécution, le chiffre2 les droits d’écriture et le chiffre 4 les droits de lecture. Ajoutez simplement ces nombres pourspécifier les droits voulus. Ainsi 0600 correspond à Lecture et écriture pour le propriétaire, rien pour lesautres, 0644 correspond à Lecture et écriture pour le propriétaire, lecture pour les autres, 0755 Tout pourle propriétaire, lecture et exécution pour les autres, 0750, Tout pour le propriétaire, lecture exécution pourle groupe, rien pour les autres. NE MARCHE PAS AVEC LES FICHIERS DISTANTS. Renvoie truesi le changement de droit a eu lieu, False sinon ; ATTENTION NE MARCHE PAS CORRECTEMENTSOUS WINDOWS !!

Gestion des droits sous windows : La gestion des droits sous Unix est gérée avec chmod alors que souswindows, c’est géré avec le système ACL (Acess Control List) et en pratique l’appli native windows ICACLS(ou CACLS pour les versions de Windows antérieure à Vista) appelé en ligne de commande permet d’officier.Il faut donc exécuter icacls sous php pour gérer les droits windows :

85

Page 86: RelectureDePhp5_2011_01_29

Cacls cacls est l’instruction shell utilisable en ligne de commande sous windows pour gérer les droits sur

les fichiers et répertoires. Elle est appelée avec shell_exec(”cacls suite de l’instruction) : La doc issue du site demicrosoft est plutôt bien faite :

86

Page 87: RelectureDePhp5_2011_01_29

87

Page 88: RelectureDePhp5_2011_01_29

88

Page 89: RelectureDePhp5_2011_01_29

89

Page 90: RelectureDePhp5_2011_01_29

ATTENTION : l’instruction cacls est considéré comme DEPRECATED et Mircosoft recommande l’usagede icacls à la place !

La commande icacls avec shell_exec : Depuis Vista, la gestion des droits a encore été raffiné sous

windows par rapport à cacls (cette dernière instruction reste toutefois utilisable). La doc microsoft sur icaclsest aussi très bien faite :

90

Page 91: RelectureDePhp5_2011_01_29

91

Page 92: RelectureDePhp5_2011_01_29

92

Page 93: RelectureDePhp5_2011_01_29

93

Page 94: RelectureDePhp5_2011_01_29

94

Page 95: RelectureDePhp5_2011_01_29

95

Page 96: RelectureDePhp5_2011_01_29

96

Page 97: RelectureDePhp5_2011_01_29

Le site de Microsoft peut être consulté au besoin pour avoir accès au forum des commandes CACLS etICACLS :

http://technet.microsoft.com/en-us/library/cc753525%28WS.10%29.aspx#BKMK_examplesLe bout de code suivant permet de récupérer les droits d’utilisateur associés à un fichier donné :$FilePath = ”CheminFichier”; /// on stocke le chemin du fichier$command = ”icacls CheminFichier”; /// on prépare la commande d’appel de l’appli icaclsexec($Command, $FilePath); /// on exécute la commande définieforeach($Result as $Key => $Value){

if (left($Value,1) != ” ”){

$Message = ””;for ($i = mb_strlen($Value);$i>0;$i–){

if (mid($Value,$i,1)== ” ” && $Message ==” ”)$Message = right($Value,mb_strlen($Value)-i);

}}else{

$Message = trim($Value);}echo ”<br>”.$Message;

}

14 Chapitre 14 : Gestion des fluxUn flux est une suite de données. On s’intéresse donc aux différents type de suite de données manipulables enPHP.

Deux grands types de flux :

• Les données utilisées par un prgm php ;

• Les sockets TCP/IP permettant d’ouvrir des connexions vers la plupart des services réseaux : serveurswebs, DNS, services web, etc.)

14.1 Utilisation de programmes externes à PHP :

Deux types d’utilisation :

• Sans interaction : PHP lance le programme et attend le résultat ;

• Avec Interaction : PHP échange des données pendant l’exécution du prgm externe ;

97

Page 98: RelectureDePhp5_2011_01_29

14.1.1 Exécution de prgm externes sans interaction :

• shell_exec(string $CommandShell) : exécute la commande $CommandShell et renvoie le résultatdans une string. ATTENTION :la chaine de retour de l’instruction est encodé en CP850 qui correspondà l’encodage des sorties DOS sous windows ;

• exec(string $Commande, Optional $sortieStd, Optional $CodeRetour) : exécute la commande$Commande et renvoie seulement la dernière ligne du résultat. Pour récupérer le retour complet de lacommande, il faut ajouter la première option, le retour complet sera alors stocké dans $sortieStd. Cettevariable sera alors un tableau dont chaque élément est une ligne du retour de la commande $Commande.

• passthru(string $Commande, Optional $CodeRetour) : idem qu’exec, mais renvoie le retour com-plet dans la valeur de la fonction et non optionnellement dans $sortieStd ;

• system(string $Commande, Optional $CodeRetour) : idem qu’exec sauf que system affiche directe-ment le retour (ATTENTION : cet usage est plutôt à proscrire car le flux affiché n’est pas alors encodéen utf-8 mais est encodé suivant l’encodage utilisé par le programme appelé par $Commande ;

Programme en tache de fond : Il s’agit de déconnecter le fonctionnement d’un script des processus appelésvia l’instruction exec ou system. Pour ça, il faut rediriger les flux de sortie et les flux standard. Par ailleurs,en temps normal la fin de php implique la fermeture des programmes fils appelées avec exec ou system. Dansun config de web serveur Apache, Apache redémarre tous ses programmes fils au bout d’un certain nombre derequêtes. Pour éviter la terminaison non sollicitée de programmes appelées par exec, il faut utiliser l’instructionnohup dans la commande appelée par exec ou system.

Exemple : nohup ”/etc/initd/apache startssl” >/dev/null 2>&1Ce que dit le bouquin indirectement sur le sujet : les UTILISATEURS DE WINDOWS PEUVENT ALLER

SE FAIRE VOIR !!!

A COMPLETER : comment lancer un programme déconnecté de php en tache de fond (et comment lekiller !!)

14.1.2 Exécution de prgm externes AVEC interaction :

Un programme comprend trois types de flux et une valeur de retour :

• La valeur de retour : elle vaut 0 quand le programme appelé a marché correctement et autre chosedans le cas inverse ;

• Le flux de sortie (flux d’output) : permet au programme d’envoyer une réponse à son utilisateur.Ex : affichage sur écran ou sortie sonore. Ce flux a pour identifiant 1 ;

• Le flux d’entrée (flux d’input) : permet à l’utilisateur d’envoyer des informations (instructions,données etc.) au programme Ce flux a pour identifiant 0 ;

• Le flux d’erreur : permet au programme de signaler des erreurs. Ce flux a pour identifiant 2 ;

ATTENTION : partant de cette base, un programme peut avoir plusieurs flux d’un même type. Les flux 0,1 et 2 sont alors ceux d’input, d’output et d’erreur et les suivants (3,4 etc.) seront d’input ou d’output suivantce qu’il a été spécifié dans la conception du prgm...

98

Page 99: RelectureDePhp5_2011_01_29

Ouverture des flux : Les flux d’un programme peuvent s’ouvrir individuellement et indépendament les uns

des autres de sorte qu’il est possible d’être seulement à l’écoute pour un prgm (flux output) et seulement enécriture pour un autre (flux input).

• popen($Commande, $Mode) : ouvre le flux d’écriture ou de lecture du programme appelé par $Com-mande suivant le $Mode choisie :

– ’r’ pour le flux d’output ;

– ’w’ pour le flux d’input ;popen se comporte ainsi pour un programme comme fopen se comporte pour la lecture/écriture d’unfichier ; ATTENTION popen ne permet de se connecter qu’à seul flux de la commande $Commandepassé en argument ;

• proc_open($Commande,

$DescriptorSpec,

$Pipes,

Optional $cwd,

Optional $TabEnv,

Optional $ArrayOptions) : Exécute une commande et ouvre les pointeurs defichiers pour les entrées / sorties. Concrêtement cela signifie que PHP va exécuter le prgm désigné par$Commande et va ensuite associé à chaque flux du programme soit rien, soit un fichier, soit un pipe. Dansle cas de fichier, le flux correspondant du prgm est écrit dans le fichier désigné dans le tableau associatif$DescriptorSpec. Dans le cas d’un pipe, le flux correspondant est un flux de données directe entre leprocessus fils càd le prgm désigné par $Commande et le processus père càd PHP. La fonction renvoie uneressource ou handler sur le prgm désigné par commande. Par ailleur $Pipes correspond au tableau deshandlers pointant sur les flux tels qu’utilisé par le processus père càd PHP. Voilà pour le principe général,en détail :

– $DescriptorSpec indique comment le processus fils va diriger ses flux d’input, d’output et d’erreur: Cette variable est alors un tableau dont chaque élément a pour clef le numéro du flux (0 pour input,1 pour écriture, 2 pour le flux d’erreur et les entiers suivants pour les autres flux) tandis que la valeurcorrespond à un sous-tableau indiquant comment le flux du prgm fils est redirigé : nulle part, versun fichier ou vers un flux directement récupéré par le prgm père :Un exemple est alors :

$Descriptor = array( ’0’ => array(’file’, ’BernardGourionDirectory/Info.bg’,’r’) ,’1’ => array(’pipe’, ’w’) ,’2’ => array(’pipe’,’w’))

/// l’input du prgm fils est à diriger par le prgm dans le fichier ’Info.bg’ du répertoire BernardGou-rionDirectory où le prgm a le droit de lecture./// l’output du prgm fils est à diriger dans un pipe (ou flux) direct vers le prgm père càd ici PHP./// le flux d’erreur du prgm fils est à diriger dans un pipe (ou flux) direct vers le prgm père càd iciPHP. ATTENTION quand le flux d’erreur du prgm fils est redirigé dans un pipe, il est apparemmentpréférable de le faire en mode ’a’ append qu’en mode ’w’ sous peine d’erreur.

– $pipes : il s’agit d’un tableau des descripteurs des flux crées par proc_open et dont va pouvoir seservir PHP ( le processus père) pour interagir avec le prgm appelé par la commande $Commande.Chaque valeur d’un élément du tableau correspond au handler associé dans l’ordre au flux défini dans

99

Page 100: RelectureDePhp5_2011_01_29

$Descriptor. ATTENTION : ce tableau ne contient que les descripteurs des flux (pipes) crées. Lesfichiers crées ne font pas partie de ce tableau. En fait, vu qu’on a le nom des fichiers, suffit de lesouvrir avec fopen ou autre fonction.

– $cwd : option qui permet de spécifier le dossier initial de travail de la commande. Cela doit êtreun chemin absolu vers le dossier ou NULL si vous voulez utiliser la valeur par défaut (le dossier detravail du processus courant PHP) ;

– $TabEnv : Un tableau contenant les variables d’environnement pour la commande qui doit êtreexécutée, ou NULL pour utiliser le même environnement que le processus PHP courant ;

– $ArrayOptions : Vous permet de spécifier des options supplémentaires. Les options actuellementsupportées sont :

∗ suppress_errors (windows uniquement): suppression des erreurs générées par cette fonctionlorsque définit à TRUE ;

∗ bypass_shell (windows uniquement): bypass du shell cmd.exe lorsque définit à TRUE ;∗ context: contexte du flux utilisé lors de l’ouverture des fichiers (créé avec la fonction stream_context_create())∗ binary_pipes: ouverture des pipes en mode binaire, au lieu d’utiliser l’encodage habituel stream_encoding

ATTENTION il faut impérativement fournir une variable dans lequel stocké le retour de proc_open sinonil sera impossible de récupérer les flux.

IMPORTANT : les commentaires de la fonction proc_open du site http://php.net/manual/fr/function.proc-open.php proposent quelques classes et bout de code notamment :

– 1. Deux classes ProcessManager et Process qui permet de gérer simultanément plusieurs processesouverts avec proc_open ;

2. Un code permettant de récupérer les flux de sortie et d’erreur en temps réel ;3. Une méthode pour gérer des processus vivants en même temps que PHP continue d’exécuter le

reste de ces scripts...

• proc_get_status($HandlerProgramme) : Renvoie un tableau associatif d’informations sur le pro-cessus dont le handler a été passé en argument. Le tableau renvoyé comporte les clefs et valeurs associéessuivantes :

– command : La commande passée à la fonction proc_open() ;

– pid : identifiant du processus

– running : TRUE si le processus fonctionne toujours et FALSE s’il est terminé.

– signaled : TRUE si le processus fils a été terminé par un signal inconnu. Toujours défini à FALSEsous Windows.

– stopped : TRUE si le processus fils a été stoppé par un signal. Toujours défini à FALSE sousWindows.

– exitcode : le code retourné par le processus (uniquement si l’élément running vaut FALSE). Seul lepremier appel à cette fonction retourne une valeur réelle, les prochains appels retournent -1.

– termsig : le numéro du signal qui a causé la fin de l’exécution du processus fils (uniquement significatifsi signaled vaut TRUE). 0 sous Windows

– stopsig : le numéro du signal qui a causé l’arrêt de l’exécution du processus fils (uniquement significatifsi signaled vaut TRUE). 0 sous Windows

100

Page 101: RelectureDePhp5_2011_01_29

Fermeture d’un processus ouvert avec proc_open : A l’issue de l’appel de proc_open, suivant ledescripteur $DescriptorSpec passé en argument, un ou plusieurs flux ont été ouverts pour communicationdirecte entre le processus père php et le processus fils. Avant de fermer le processus fils appelé par l’argument$Commande de proc_open proprement dit, il faut fermer les flux qui ont été ouvert avec pclose avantd’appeler proc_close pour flinguer le processus lui-même.

• pclose($HandlerPipe) : ferme le flux dont le handler est $HandlerPipe ; les flux ouverts par proc_opensont listés dans le 3ième argument de proc_open ;

• proc_close($HandlerPrgm) : ferme le processus dont le handler est $HandlerPrgm.

Protéger le recours à appels de programmes externes par un client : Lorsqu’un appel à un programmeexterne est à l’initiative du client avec possibilité pour lui de passer son instruction, il est clair qu’il faut êtreprudent et pour cela deux fonctions permettent d’incorporer un filtrage de l’instruction passé par le client :

• escapeshellcmd($Commande) : permet d’échapper les caractères spéciaux d’une chaine $Commandedestinée à servir de commande d’appel d’un programme externe. échappe tous les caractères de la chaînecommand qui pourraient avoir une signification spéciale dans une commande Shell. Cette fonction permetde s’assurer que la commande sera correctement passée à l’exécuteur de commande Shell exec() et system(),ou encore à guillemets obliques (les fameux backticks ‘ ‘ qui équivalent à shell_exec. Les caractèressuivants seront échappés : #&;‘|*?~<>^()[]{}$\, \x0A et \xFF. ’ et ” sont échappés que s’ils ne sontpas par paire. Sous Windows, tous ces caractères ainsi que % sont remplacés par un espace. Renvoie lachaine echappée

• escapeshellarg($ArgumentCommande) : permet d’échapper les caractères spéciaux d’un argumentde ligne de commande. Renvoie la chaine echappée ;

Les directives php liées à la sécurisation des prgms externes :

• La directive Safe_mode dans php.ini interdit le recours à un programme externe si elle vaut on (ellevaut off dans le cas contraire) ;

• la directive safe_mode_exec_dir de php.ini permet de spécifier une liste de répertoires dans lesquelsse trouvent les seuls programmes utilisables ;

• ATTENTION : dès lors qu’un programme externe est appelé, celui-ci n’a pas les limitations imposées parApache ou php, et la directive safe_mode_exec_dir est donc particulièrement importante pour empêcherl’exécution de programmes dont on ne connait pas le comportement.

14.2 Gestion des sockets réseaux :

Pour communiquer avec des services TCP/IP comme les serveurs web ou telnet, il faut que php ouvre une socketréseau. (socket veut dire connexion en anglais)

Une socket réseau est une connexion réseau qui peut être de type :

101

Page 102: RelectureDePhp5_2011_01_29

• TCP : càd une connexion permanente entre php et la cible ;

• UDP : une connexion ”non connecté” càd que l’adresse de destination doit être spécifié à chaque envoi carkekpart on se reconnecte à chaque envoi. C’est un peu analogue à des envois de courriers physiques.

Enfin par défaut, les sockets sont ouvertes en mode bloquant, ce qui signifie que PHp attend que les donnéesà lire arrivent de la socket, ou que le socket soit close avant de rendre la main pour la suite de l’exécution duscript.

L’autre mode est le mode non-bloquant qui permet alors d’ouvrir la socket, de la consulter pour savoir si denouvelles données sont disponibles et de continuer le script quoiqu’il arrive. Quelque part dans ce cas, on gèreles lecture successives en attente de données à l’aide d’un boucle.

14.2.1 Ouverture des socket réseaux

• resource fsockopen(string $hostname, [ int $port = -1 [, int &$errno [, string &$errstr [,float $timeout = ini_get(”default_socket_timeout”) ]]]] ) : ouvre une socket réseau à destinationde $hostname. Renvoie un pointeur de fichier (handler) ou FALSE Si l’appel échoue, la fonction retourneFALSE. :

– La chaine $hostname doit correspondre à une adresse IP ou une adresse réseau type www.php.net.On peut par ailleurs préfixer cette chaine par la chaine ”ssl://” ou ”tls://” qui sont équivalents tousles deux et correspondent à un protocole de sécurisation des échanges sur internet. Par défaut, leprotocole est ”http://”. A noter que le ”tls” est une évolution de ”ssl” en 2001 ;

– Si le port $port est spécifié, la connexion se fera sur la machine où php réside sur le port indiqué ;

– $errno : Si fourni, contient le numéro de l’erreur système qui survient lors de l’appel système àconnect(). Si la valeur retournée par errno est 0 et que la fonction retourne FALSE, ce peut être uneindication laissant penser que l’erreur est survenue avant l’appel à connect(). La plupart du temps,cela est du à un problème d’initialisation du socket.

– $errstr : Si fourni permet de récupérer l’éventuelmessage d’erreur sous la forme d’une chaîne decaractères. Cette chaine est alors encodé en ASCII.

– $timeout : Si fourni permet de spécifier le délai d’attente maximal, en secondes. Si vous avezbesoin de définir un délai limite pour lire/écrire des données à travers cette socket, utilisez la fonctionstream_set_timeout(), comme le paramètre timeout de la fonction fsockopen() uniquement appliquélors de la connexion de la socket.

Pour info, la liste des différents modes de transport, format URL, dont PHP dispose en interne pour lesflux qui exploitent les sockets, tels que fsockopen() et stream_socket_client() sont :

– TCP,

– UDP,

– SSL,

– TLS

ATTENTION Ces modes de transport ne s’appliquent pas à l’extension sockets (?).

Renvoie FALSE si l’ouverture de socket a échoué, et un handler (pointeur) si cela a marché ;

102

Page 103: RelectureDePhp5_2011_01_29

14.2.2 Gestion du mode d’accès aux sockets

• stream_set_blocking(ressource $Stream, int $mode) : sette le mode d’accès au handler $Streamau mode $mode). Le mode est non-bloquant si $mode vaut 0 (et pas FALSE attention) (càd php litle flux mais poursuit son exécution même en cas d’absence réponse, et il est bloquant si le $mode vaut1 (et pas true);

• feof(ressource $Stream) : permet de savoir si des données peuvent encore arriver du flux dont le handlerest $Stream; renvoie true si plus aucune donnée ne peut arriver, false sinon ; L’utilisation de cette fonctionest plus subtile qu’il n’y parait. Quand la lecture du flux implique de lire le dernier caractère du flux, celan’implique pas qu’un appel consécutif renvoie true. Pour cela, il faut chercher à lire une nouvelle fois leflux de sorte que le pointeur sur le flux dépasse le caractère final du flux. Ce n’est alors que dans cettesituation que feof retourne bien TRUE.

ATTENTION il faut que le pointeur (ou handler) $Stream soit valide pour que feof puisse renvoyer TRUE.En effet, en cas d’erreur de ce type, feof renvoie false au lieu de true. Sinon le comportement général dela fonction feof est retourner TRUE si le pointeur handle est à la fin du fichier ou si une erreur survient,sinon, retourne FALSE.

DERNIERE CHOSE, dans le cas d’une lecture de flux avec une boucle utilisant comme condition decontinuation feof($Flux)===false, du fait que feof ne renvoie false que dans le cas ou le $handler pointe surune position strictement postéreure au caractère de fin de flux, il est chaudement recommandé de préférerune boucle de type do {...}while(feof($Flux)===false) que de type while(feof($Flux)===false)

• stream_set_timeout($SocketHandler, $TimeOut) : fixe la durée en s aude-là de laquelle phpconsidère que la connexion s’il n’a eu aucune réponse ou donnée durant ce temps ;

14.2.3 Information sur une socket (connexion) réseau :

• stream_get_meta_data($FileOrSocketHandler) : renvoie un tableau associatif de clefs et devaleurs d’informations sur le flux de handler $SocketHandler. Le tableau renvoyé est vide si le handler necorrespond à rien. A noter que cette fonction s’applique aussi bien au fichier qu’au flux. les clefs potentielsdu tableaux sont :

– timed_out (booléen) : TRUE si le flux a atteint de délai d’expiration en attendant des donnéesdurant le dernier appel aux fonctions fread() et fgets() ;

– blocked (booléen) : TRUE si le flux est en mode bloquant. Voir aussi stream_set_blocking().– eof (booléen) : TRUE si le flux a atteint la fin du fichier. Notez que pour les sockets, cette valeur

peut être TRUE même si unread_bytes est non nul. Pour déterminer s’il reste des données à lire,utilisez plutôt la fonction feof().

– unread_bytes (entier) : le nombre d’octets actuellement placés dans le buffer interne à PHP.IMPORTANT : Vous ne devriez pas utiliser cette valeur dans un script.

– stream_type(chaîne de caractères) : un nom, qui décrit l’implémentation sous-jacente de flux.– wrapper_type (chaîne de caractères) : un nom qui décrit le gestionnaire de protocole pour

ce flux. Voyez Liste des protocoles supportés pour plus d’informations sur les gestionnaires (cfhttp://fr.php.net/manual/fr/wrappers.php ATTENTION le nombre de gestionnaire de fluxest important et il est possible de définir ses propres protocoles ;

– wrapper_data (mixed) : des données spécifiques au gestionnaire liés à ce flux. Voyez Liste desprotocoles supportés pour plus d’informations sur les gestionnaires et leurs données.

103

Page 104: RelectureDePhp5_2011_01_29

– filters(tableau) : un tableau contenant les noms de tous les filtres qui ont été attachés à ce flux.La documentation sur les filtres peut être trouvée sur l’annexe concernant les filtres.

– mode(chaîne de caractères) : le type d’accès requis pour ce flux ( voir le tableau 1 de la référencede la fonction fopen())

– seakable(booléen) : si on peut rechercher dans le flux courant.

– uri(chaîne de caractères) : l’URI/nom de fichier associé à ce flux.

• socket_get_status($FileOrSocketHandler) : cette fonction est un alias de la précédente (tout pareil!)

14.2.4 Fermeture de socket

• fclose($Handler) : ferme le flux dont le handler est $Handler. ATTENTION ça ne veut pas dire que$Handler vaut nul. En fait $Handler conserve sa valeur. NOTE : c’est la même fonction qui est utiliséepour fermer un fichier.

14.3 Création d’un serveur réseau avec les sockets :Cela revient à ce que le serveur web agisse en serveur réseau. TRES IMPORTANT Si on veut faire dialoguerdes serveurs entre eux !!!

Pour l’instant, on sait surtout se connecter à des sockets réseaux avec les fonctions précédentes. Maintenantil est aussi possible d’écouter le réseau càd à l’aide de socket. Et si on écoute, on peut aussi répondre, ce quirevient en fait à devenir un serveur réseau.

Une connection réseau est définie par deux sockets réseau : une pour écouter (recevoir des données), unesur laquelle on lit des données. Pour créer un connection réseau, on suit les étapes suivantes :

1. On crée la socket sur laquelle on va écouter (socket serveur) ou on va lire (socket client) avec la fonctionsocket_create :

$resource socket_create(int $domain , int $type , int $protocol ) : Crée une socket réseau dont:

• le domaine de communication est spécifié par $domain (AF_INET pour un protocol de type IPv4càd adresse IP classique en TCP (http ou ssl) ou UDP, AF_INET6 pareil mais avec une adresseIPv6, AF_UNIX : protocole de communication locale UNIX),

• le type de communication est définie par $type qui peut prendre l’une des valeurs constantes suivantes:

– SOCK_STREAM : connection basée sur des flux de données ”reliable”, ”full duplex”, ”connection-based” sans limite de taille, càd le type de socket utilisé par TCP ;

– SOCK_DGRAM : connection basée sur des envois connection-less càd on ne sait pas si l’envoide données réussit et la taille données envoyées est fixe càd connu à l’envoi, c’est le type de socketutilisé par UDP ;

– SOCK_SEQPACKET : fournit une connection à double sens fiable et séquencé pour des data-grammes de longueur maximale fixe. A client d’une telle connection lit tout le paquet envoyé àchaque requête de lecture sur ce flux, il ne peut pas lire partiellement un tel flux.

104

Page 105: RelectureDePhp5_2011_01_29

– SOCK_RAW : protocole d’accès brut. Ce type de socket peut-être utilisé pour construire manuelle-ment tout type de protocole de communication ; un usage commun est la requête ICMP (InternetControl Message Protocol) (comme le simple ping) : En fait, le protocole IP ne gère que les trans-ferts de données et ne gère pas l’envoi de messages d’erreur. C’est le protocole ICMP qui permetcela. c’est grâce à ce protocole qu’une machine émettrice peut savoir qu’il y a eu un incident deréseau. Pour plus de détail, voir le site wiki sur ICMP.

– SOCK_RDM : pratiquement jamais utilisée. Voir le net pour plus d’infos.

• le protocole de communication : ICMP, TCP ou UDP :

– ICMP :The Internet Control Message Protocol is used primarily by gateways and hosts to reporterrors in datagram communication. The ”ping” command (present in most modern operatingsystems) is an example application of ICMP ;

– UDP : The User Datagram Protocol is a connectionless, unreliable, protocol with fixed recordlengths. Due to these aspects, UDP requires a minimum amount of protocol overhead.

– TCP : The Transmission Control Protocol is a reliable, connection based, stream oriented, fullduplex protocol. TCP guarantees that all data packets will be received in the order in whichthey were sent. If any packet is somehow lost during communication, TCP will automaticallyretransmit the packet until the destination host acknowledges that packet. For reliability andperformance reasons, the TCP implementation itself decides the appropriate octet boundaries ofthe underlying datagram communication layer. Therefore, TCP applications must allow for thepossibility of partial record transmission.

• et renvoie son handler $ressource si la création a été couronnée de succès, FALSE sinon.

IMPORTANT : En cas d’erreur à la création, le code erreur peut être récupérée avec la fonction socket_last_error()puis transcrite en explication textual avec socket_strerror() ;

2. POUR UNE SOCKET SERVER : On lie la socket crée avec un nom d’adresse avec la fonction socket_bind().Ce DOIT être fait avant tout appel à socket_listen() :

bool socket_bind ( resource $socket , string $address [, int $port = 0 ] ) : lit la socket dehandler $socket à l’adresse $address. Celle-ci doit alors être une adresse IP ou IPv6 (PAS un nom dedomaine !!) si le domaine de la socket est de type AF_INET ou resp AF_INET6 et de type chemin desocket de domain UNix si le domaine est de type AF_UNIX. L’option $Port dans le cad AF_INET pourpréciser sur quel port la connexion réseau aura lieu ; RENVOIE true en cas de succès et false sinon, lesdeux fonctions socket_last_error et socket_strerror servent à récupérer le message d’erreur éventuel ;

3. POUR UNE SOCKET CLIENT, on connecte la socket de handler $socket à l’adresse $address avec laconnection socket_connect :

bool socket_connect ( resource $socket , string $address [, int $port = 0 ] ) : Fait la connectionentre la $socket et l’adresse $address. Si l’adresse est de type IP ou IPv6, le $port DOIT être spécifié ;

OU

POUR UNE SOCKET SERVER, on met la socket en écoute (càd en attente de connection externe) avecsocket_listen :

bool socket_listen ( resource $socket [, int $backlog = 0 ] ) : après que la création de la socket$socket avec socket_create et son attachement à une adresse réseau avec socket_bind, on peut la mettreen écoute de connection. $backlog sert à spécifier combien de requête de connexion peuvent être misesen attente. S’il y a dépassement, suivant le protocole de communication (?) soit l’émetteur de la requêtereçoit un message d’erreur ECONNREFUSED, soit la requête sera ignorée par le serveur et l’émetteurpourra réémettre sa requête...

105

Page 106: RelectureDePhp5_2011_01_29

POUR RESUMER :Pour créer une socket client (qui lit des données sur le réseau) :

socket_create –> socket_connect (puis fclose pour la fermer)

Pour créer une socket serveur (qui reçoit des données sur le réseau) :

socket_create –> socket_bind –> socket_listen (puis fclose pour la fermer)

14.3.1 Les fonctions de création de socket de connection réseau tout en un :

Création d’une socket client : fsockopen et stream_socket_client ont pratiquement le même rôle :Comme vu auparavant, la fonction fsockopen avait un côté tout en un : elle créait une socket et la con-nectait à l’adresse spécifiée. Sa seule faiblesse ? son incapacité à créer une socket de connection sécurisée detype ssl. stream_socket_client fonctionne de manière identique avec un prototype identique à l’exception :

• de sa capacité à gérer les connections sécurisées ;

• de la possibilité de passer un StreamContext en dernier argument (ce qui permet des filtrages sur lesdonnées lues) ;

resource stream_socket_client ( string $remote_socket [, int &$errno [, string &$errstr [, float$timeout = ini_get(”default_socket_timeout”) [, int $flags = STREAM_CLIENT_CONNECT[, resource $context ]]]]] )

Création d’une socket serveur : resource stream_socket_server ( string $local_socket [, int

&$errno [, string &$errstr [, int $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN[, resource $context ]]]] )

Là je resuce le site php.net car j’en ai marre et de toute manière stream_socket_server encapsule les appelssuccessifs socket_create, socket_bind, socket_listen :

• local_socket : The type of socket created is determined by the transport specified using standard URLformatting: transport://target. For Internet Domain sockets (AF_INET) such as TCP and UDP, thetarget portion of the remote_socket parameter should consist of a hostname or IP address followed bya colon and a port number. For Unix domain sockets, the target portion should point to the socket fileon the filesystem. Depending on the environment, Unix domain sockets may not be available. A listof available transports can be retrieved using stream_get_transports(). See List of Supported SocketTransports for a list of bulitin transports.

• errno : If the optional errno and errstr arguments are present they will be set to indicate the actual systemlevel error that occurred in the system-level socket(), bind(), and listen() calls. If the value returned inerrno is 0 and the function returned FALSE, it is an indication that the error occurred before the bind()call. This is most likely due to a problem initializing the socket. Note that the errno and errstr argumentswill always be passed by reference.

• errstr : See errno description.

106

Page 107: RelectureDePhp5_2011_01_29

• flags : A bitmask field which may be set to any combination of socket creation flags. For UDP sockets,you must use STREAM_SERVER_BIND as the flags parameter.

Une fois le serveur à l’écoute de connexions extérieures avec stream_socket_server ou socket_listen, il fautque php puisse savoir, côté serveur, si quelqu’un se connecte à la socket server. La fonction stream_socket_acceptsert à cela :

• resource stream_socket_accept ( resource $server_socket [, float $timeout = ini_get(”default_socket_timeout”)[, string &$peername ]] ) : renvoie un handler de socket correspondant à une connexion extérieur surle socket $server_socket du serveur, le $timeout correspond au temps qu’à la socket_serveur pour ac-cepter la connection (?), tandis que $peername correspond à l’adresse du client connecté si le transport lepermet (grosso modo pas le udp) . ATTENTION : il ne faut pas utiliser cette fonction avec une connectionde type UDP.Pour le UDP, on a :

• string stream_socket_recvfrom ( resource $socket , int $length [, int $flags = 0 [, string&$address ]] ) : accepts data from a remote socket $socket up to $length bytes, connected or not.The value of flags can be any combination of the following: STREAM_OOB = > Process OOB (out-of-band) data. STREAM_PEEK => Retrieve data from the socket, but do not consume the buffer.Subsequent calls to fread() or stream_socket_recvfrom() will see the same data. If $address is providedit will be populated with the address of the remote socket. Returns the read data, as a string

• int stream_socket_sendto ( resource $socket , string $data [, int $flags = 0 [, string $address]] ) : Sends a message to a socket, whether it is connected or not càd Sends the specified data $datathrough the socket $socket. $flag peut seulement valoir STREAM_OOB => Process OOB (out-of-band) data. $address : The address specified when the socket stream was created will be used unless analternate address is specified in address. If specified, it must be in dotted quad (or [ipv6]) format. Returnsa result code, as an integer.J’AI PAS TESTE LE FONCTIONNEMENT DE CES DEUX DERNIERES FONCTIONS... A VRAIDIRE, JE NE COMPRENDS PAS BIEN LEUR FONCTIONNEMENT...

14.4 Gestion Unifiée des flux (fichiers distants et locaux, sockets, flux) et Stream-Contexts

En fait, un bon nombre de fonctions d’accès sont utilisables sans distinction avec un flux, un socket réseau, unfichier distant ou un fichier local.

Par exemple, la fonction fopen s’applique à n’importe quel flux et pas seulement un fichier. Elle peut doncêtre utilisé à la place de fsockopen. Par ailleurs, chaque fonction peut accepter un StreamContext

La seule vraie différence est dans la désignation du nom du flux concerné. Le nom a la structure suivante :

transport : //cible : port

transport correspond au protocole d’accès à la cible (fichier local ou distant, socket réseau, fluxd’entrée/sortie de prgms) qui est soit un gestionnaire de flux, soit un gestionnaire de transportsde sockets. Le port n’est spécifié que dans le cas d’accès à des sockets réseaux.

transport peut avoir les valeurs suivantes et suivant la valeur, le type d’accès est limité :

107

Page 108: RelectureDePhp5_2011_01_29

• ”file” : la cible est fichier local NOTE : c’est le protocole de transport par défaut si transport vaut ” ”;

• ”compress.zlib” : la cible est un fichier local compressé avec gzip (extension gz). Accès en écritureappend seule ou en lecture seule, jamais les deux en même temps (càd r,a mais pas r+, w ou w+) (ne pasoublier qu’un accès w écrase le fichier avant de l’ouvrir pour écriture...) ; le module gzip doit être activépour que ça marche... Pour effacer des fichiers compressés, il faut utiliser le protocole de transport file

• ”compress.bzip2” : la cible est un fichier local compressé avec gzip (extension bz2). Même type d’accèsque pour zlib ; ATTENTION : le module bzip doit être activé pour que ça marche...

• ”http” : pour accéder à un fichier distant suivant le protocole http. Si l’accès nécessite un nom d’utilisateurlogin et mot de passe password, le protocole devient : ”http://login:password@”. Accès en lecture seuledes données de la cible. Par ailleurs, les informations d’en-têtes ne seront disponibles que par le clef”http_response_header” du tableau renvoyé par un appel à stream_get_meta_data ; Par ailleurs, lalecture peut être modifié en ce sens qu’il est possible de définir les en-têtes envoyées dans la requête HTTPvia les options de contexte (càd définir un streamcontext) ; Notamment la clef ’method’ spécifie le typede requête GET ou POST, la clef header permet de spécifier des en-têtes arbitraires et la clef ’content’contient définit le contenu de la requête (utilisé le plus souvent pour envoyer des informations en POST).Note : Ce protocole de transport utilise le protocole réseau TCP ;

• ”ftp” : pour accéder à un fichier distant sur un serveur ftp. Si l’accès nécessite un nom d’utilisateurlogin et mot de passe password, le protocole devient : ”ftp://login:password@”. Accès en lecture et enécriture (modes r, w et a) mais pas simultanément en lecture et écriture (w+ et a+). la fonction unlinkpeut permettre d’effacer un fichier sur un serveur ftp. Un accès en mode w sera refusé sauf si l’option decontexte overwrite est présente dans un streamContext à la valeur true. Note : Ce protocole de transportutilise le protocole réseau TCP;

• ”https” : pour accéder à un fichier distant de manière sécurisée suivant le protocole https. Ce protocolede transport utilise le protocole réseau SSL ; sinon pareil que ”http” ;

• ”ftps” : pour accéder à un fichier distant de manière sécurisée suivant le protocole ftps. Ce protocole detransport utilise le protocole réseau SSL ; sinon pareil que ”ftp” ;

• ”tcp://” : permet d’accéder à la socket réseau Cible ; le port doit être spécifié pour l’accès à la cible soitgaranti (sauf dans le cas des sockets UNIX où le port doit toujours valoir 0 A VERIFIER) ; Un appel àfsockopen équivaut à fopen avec ”tcp://” en protocole de transport ; La cible peut être un DNS ou uneadresse IP ou IPv6 (cette dernière doit alors être écrite entre []) ;

• ”udp://” : pareil que ”tcp://” sauf que la cible est de type UDP (accès distant en mode non connecté) ;

• ”unix://” ou ”udg” : accès à des sockets Unix ;

• ”tls://” et ”ssl://” : ce sont des surcouches du protocole ”tcp://” et en partagent donc les params etles options. Ces deux protocoles de transport ont cependant des options de context propres (voir lesStreamContext plus bas) :

– l’option verify_peer indique à true que le certificat SSL du client sera analysé et vérifié ;

– allow_self_certificate permet les certificats autosignés ;

– cafile et capath permettent de spécifier un nom de fichier de vérification et le nom du répertoirecontenant ce fichier pour vérification de la certification ;

– local_cert : permet de spécifier l’adresse local (donc chez le client) du certificat à utiliser pourvérification ;

108

Page 109: RelectureDePhp5_2011_01_29

– passphrase : le mot de passe associé à ce certificat ;

– CN_match : permet de vérifier que le common name correspond à un masque défini (???? Voir lechapitre sur le sécurité ???);

IMPORTANT : si la directive allow_url_fopen est désactivé dans php.ini; Les accès distants ne sontpas dispos !!

IMPORTANT : pour les protocoles sécurisés soit dispo dans la config php, il faut que le module exten-sion=php_openssl.dll soit spécifié dans php.ini

• ”php://stdin” : permet d’accéder au flux d’entrée du processus PHP ; Accès en lecture uniquement ; Anotre que c’est un accès bas niveau en ce sens qu’aucun filtrage n’est possible sur les données lues ; il faututiliser le protocole de transport php://input pour cela ; ATTENTION UN PEU CONFUS

• ”php://input” : pareil que précédemment sauf qu’on a accès au ”système d’entrée classique” de php etque les fonctions de filrage de flux d’entrée sont utilisables. php://input permet de lire des données POSTbruts. C’est moins gourmand en mémoire que $HTTP_RAW_POST_DATA et il n’y a pas de directivespéciale dans php.ini. php://input n’est pas disponible avec enctype=”multipart/form-data” ;

• ”php://stdout” : permet d’accéder au flux de sortie du processus PHP ; Accès en écriture uniquement; A notre que c’est un accès bas niveau en ce sens qu’aucun filtrage n’est possible sur les données ; il faututiliser le protocole de transport php://output pour cela ;

• ”php://output” : pareil que précédemment sauf qu’on a accès au ”système de sortie classique” de phpet que les fonctions de filrage de flux de sortie sont utilisables ; php://output vous permet d’écrire dansle buffer de sortie, de la même manière que print() et echo().

• ”php://stderr” : idem que ”php://stdout” pour le flux d’erreur de php ;

• ”php://filter” : est une sorte de méta-gestionnaire, prévu pour qui permet l’utilisation de filtre avec lesdonnées d’entrée au moment du démarrage du script. C’est pratique avec des fonctions compactes commereadfile(), file() et file_get_contents() où il n’y a pas d’opportunité d’appliquer un filtre aux donnéeslues.Le gestionnaire de php://filter prend les ’paramètres’ suivants dans le ’chemin’. L’utilisation de ce pro-tocole de transport et la lecture de la page web suivante éclaire bien son utilisation : http://php.net/manual/fr/wrappers.php.php

• ”php://memory” : permet d’accéder aux données en mémoire. Une sorte de tas façon C++;

• ”php://temp” : fonctionne de la même façon, mais utilise des fichiers temporaires pour stocker les don-nées lorsqu’une certaine limite mémoire est atteinte (par défaut, 2 Mo). L’utilisation de ce protocole detransport et la lecture de la page web suivante éclaire bien son utilisation : http://php.net/manual/fr/wrappers.php.php

IMPORTANT : A propos des protocoles de transport associées à php :

109

Page 110: RelectureDePhp5_2011_01_29

110

Page 111: RelectureDePhp5_2011_01_29

14.4.1 Liste des protocoles de transport gérés par une version de php :

• stream_get_wrappers() : renvoie la liste des gestionnaires de flux disponibles sur php. Sur ma configactuel de php :

Array ( [0] => php

[1] => file

[2] => glob

[3] => data

[4] => http

[5] => ftp

[6] => zip

[7] => compress.zlib

[8] => phar )

• stream_get_transports() : Liste les gestionnaires de transports de sockets disponibles ; Sur ma configactuelle de php : tcp et udp. Les protocoles de transport sécurisés ne sont pas actuellement dispos car

dans php.ini, la ligne concernant le module ssl est en commentaire ;extension=php_openssl.dll

A PROPOS des protocoles de transports sécurisés : If you can’t get the ssl protocol as a registeredtransport protocol even though you has add the extension=php_openssl.dll line on php.ini, maybe youhaven’t the libeay32.dll and / or ssleay32.dll files on your installation or in system32 folder.

Une fois, la ligne décommenté et le serveur Apache redémarré, l’appel à stream_get_transports donne :

tcp

udp

ssl

sslv3

sslv2

tls

14.4.2 Fonctions génériques d’utilisation des flux :

Ouverture : fopen : pour tout type de cible !

stream_socket_client() : pour les sockets clients (?)popen et proc_open : pour les flux de prgms

Lecture et écriture :

• fgetc : v chapitre sur la gestion des fichiers ;

• fgets : idem ; IMPORTANT : la fonction stream_get_line fait la même chose mais en mieux !!

• fread : idem ;

111

Page 112: RelectureDePhp5_2011_01_29

• fwrite : idem ;

• feof : v plus haut ;

ainsi que toutes les autres fonctions de lecture/ouverture/gestion de fichiers.

Fonctions de connexion entre flux :

• stream_copy_to_stream(( resource $source , resource $dest [, int $maxlength = -1 [, int$offset = 0 ]] )) : Fait une copie jusqu’à maxlength octets de données depuis la position courante dupointeur (ou depuis la position offset, si spécifié) dans le flux source $Source vers le flux $dest (celui-ci doitêtre ouvert dans un mode d’accès permettant l’écriture càd w ou a ). Si maxlength n’est pas spécifié, toutle reste du flux source sera copié. Renvoie le nombre total d’octets copiés. ATTENTION : une fois la copiefaite, le pointeur interne du flux source aura avancé d’$offset octets ou se trouvera à la fin du flux (eof) sioffset vaut 0 par défaut. (A noter, sur http://fr2.php.net/manual/fr/function.stream-copy-to-stream.phpdans les commentaires, une fonction de récupération de données sur une page web ;

A NOTER page 366 ; le paragraphe sur stream_copy_to stream apparait deux fois...

• stream_get_line( resource $handle , int $length [, string $ending ] ) : lit une suite de caractèresdans le flux de handler $handle jusqu’à ce que dans l’ordre :

– $lenght octets soient lus ;

– le caractère de fin de ligne ou la chaine $ending soit rencontré ; IMPORTANT : Renvoie la chainelue à l’exception du caractère de fin de ligne (\r, \n ou \r\n suivant l’OS) ou de la chaine $endingqui est sauté(e) (mieux que fgets donc). Un nouvel appel conduit à lire la ”ligne” suivante en sautantle caractère de fin de ligne ;

– EOF soit rencontré.

Fonctions de contrôle des flux

• stream_set_write_buffer(resource $streamHandle , int $buffer ) : configure le buffer d’écrituredu flux de handle $streamHandle à la taille de $buffer octets. fwrite() est habituellement configuréeavec un buffer de 8 ko. Cela signifie que si deux processus veulent écrire dans le même flux de sortie (parexemple, un fichier), ils font une pause tous les 8 ko pour laisser les autres écrire aussi. Si buffer vaut 0alors les opérations sont sans buffer. Cela garantit que les opérations avec fwrite() sont achevées avantque d’autres processus ne soient autorisés à écrire dans le flux de sortie ;

• stream_get_meta_data(resource $streamHandle ) : permet de récupérer des infos sur le flux dehandler $streamHandle ; CETTE DEFINITION EST DEJA DEFINIE ET ETUDIEE PLUS HAUT ;

Fonctions de fermeture

• fclose : qui peut fermer tous les types de flux et qui a été vu et revu plus haut ;

112

Page 113: RelectureDePhp5_2011_01_29

14.4.3 Fonctions spécifiques d’utilisation des flux

• stream_socket_get_name(resource $handle , bool $want_peer ) : permet de récupérer le nomdu flux de handler $handle. Si $want_peer est false, le nom de la socket locale sera retourné sinon cesera le nom de la socket distante ; IMPORTANT : cette fonction renvoie l’adresse IP du flux quand c’estune socket réseau et une chaine vide quand le flux est autre chose qu’un flux de socket réseau ;

14.4.4 Abstraction de transport de flux :

La liste des transports possibles pour désigner un accès à un flux dans transport : //cible : port présentée plus

haut n’est pas limitative car php permet la création de protocoles de transport personnalisé. Ce protocole prendalors la forme d’une classe devant implémenter la liste des fonctions suivantes :

• stream_open

• stream_close

• stream_read

• stream_writer

• stream_eof

• stream_tell

• stream_seek

• stream_stat

La fonction stream_wrapper_register pour associer un nom à la classe de transport crée. Bizarrement les 8fonctions précédentes ne font pas partie d’une interface et donc la classe de transport n’a donc pas besoin d’enimplémenter une..

Pour plus détails, lire les pages 374-376 du livre PHP5 avancé...

14.5 Les streamContextsLe bouquin en parle 20 fois avant de les définir à la toute fin, logique !!! :- (

Et le pire, c’est que ce n’est pas très, très compliqué à comprendre et le bouquin en dit un minimum...

113

Page 114: RelectureDePhp5_2011_01_29

14.5.1 Définition :

Les contextes sont des options accompagnant un flux, et sont passés en dernier argument des fonctions decréations/ouverture de flux comme fopen, stream_socket_client et stream_socket_server. Un context est untableau associatif à double entrée (càd un tableau associatif contenant que des tableaux associatif). Un teltableau se présente comme suit :

$options = array(’Abstraction’ =>array( ’NomOption’ = > ValeurOption););

par exemple :

$options = array(’ftp’ =>array( ’overwrite’ = >TRUE););

indique que l’option overwrite du protocole d’accès ftp est mis à TRUE (la connexion client a l’autorisationd’écraser les fichiers se trouvant sur le serveur distant.

Partant de là, on crée un contexte avec la fonction stream_context_create() :

• resource stream_context_create ([ array $options [, array $params ]] ) : Crée et renvoieun handler sur StreamContext avec les options potentiellement passé en argument (Si rien, le Stream-Context est vide) $options : Doit être un tableau associatif de tableaux associatifs avec le format$Tab[’wrapper’][’option’] = $value. $options vaut un tableau vide par défaut. Ce qu’il faut comprendrece que le terme options est plutôt inadéquat car $options correspond plus à des informations complé-mentaires aux fonctions de gestion des flux. Notamment par exemple, sur l’ouverture d’une socket clientpointant sur un site internet donné, on peut ajouter des informations relatives à la méthode (’GET’ ou’POST’) ou au header (par exemple, la chaine ”Accept-language: en\r\nCookie: foo=bar\r\n” qui ajoutedeux lignes : une spécifiant le language de la page, et l’autre la valeur bar d’un cookie de nom foo.

$params est optionnel et si renseigné , doit être un tableau associatif du format $arr[’parameter’] =$value. Un seul type de paramètre peut être passé en clef :

– callbackFunction : le nom d’une fonction en cas d’un certain événement sur le flux auquel leStreamContext s’applique. Par exemple, dans le cas d’une connection téléchargeant un fichier, çapermet de gérer un retour vers le client alors que le téléchargement se fait. par exemple, on notifie leclient quand le nom du fichier est dispo, quand la taille est dispo, combien de ko ont été téléchargé etc.La fonction de call_back est appelé quand un des événements d’une liste bien définie d’événementsintervient.IMPORTANT : Il faut comprendre que callback est le nom d’une fonction dont le prototype DOITrespecter le template suivant :void NomDeLaFonctionDeStreamCallback( int $notification_code , int $severity , string$message , int $message_code , int $bytes_transferred , int $bytes_max ) : ne renvoierien, le retour de la callback fonction au client se fait directement via des echo, ou des print_r, càdtoute fonction d’affichage écran ; Ce retour se fait suivant la notification de l’événement $notifica-tion_code et/ou suivant la severity $severity.Exemple :function stream_notification_callback($notification_code,

114

Page 115: RelectureDePhp5_2011_01_29

$severity,$message,$message_code,$bytes_transferred,$bytes_max)

{switch($notification_code) {

case STREAM_NOTIFY_RESOLVE:case STREAM_NOTIFY_AUTH_REQUIRED:case STREAM_NOTIFY_COMPLETED:case STREAM_NOTIFY_FAILURE:case STREAM_NOTIFY_AUTH_RESULT:

var_dump($notification_code, $severity, $message, $message_code, $bytes_transferred,$bytes_max);

/* Ignore */break;

case STREAM_NOTIFY_REDIRECTED:echo ”Being redirected to: ”, $message;break;

case STREAM_NOTIFY_CONNECT:echo ”Connected...”;break;

case STREAM_NOTIFY_FILE_SIZE_IS:echo ”Got the filesize: ”, $bytes_max;break;

case STREAM_NOTIFY_MIME_TYPE_IS:echo ”Found the mime-type: ”, $message;break;

case STREAM_NOTIFY_PROGRESS:echo ”Made some progress, downloaded ”, $bytes_transferred, ” so far”;break;

}echo ”\n”;

}$ctx = stream_context_create();stream_context_set_params($ctx, array(”notification” => ”stream_notification_callback”));file_get_contents(”http://php.net/contact”, false, $ctx);?>

A NOTER : Nombreuses sont les fonctions de gestion pouvant accepter des StreamContext ;

115

Page 116: RelectureDePhp5_2011_01_29

14.6 Les filtres associés aux fonctions de gestion des flux

Les filtres de flux sont des classes d’objets natifs ou personnalisés qui permettent d’incorporer directement àla lecture/écriture des flux des filtres de données, et ce de manière transparente. Natif car certains filtressont prédéfinis nativement tandis qu’il est possible de créer son propre filtre personnalisé avec une classeimplémentant l’interface php_user_filter. ”transparent” car les appels aux fonctions sur flux avec ou sansfiltres sont strictement les mêmes car l’ajout de filtres de traitement se fait uniquement en utilisant les fonctionsstream_filter_append() et stream_filter_prepend(). Il est possible d’utiliser plusieurs filtres successifsdans les traitements des flux : par exemple, un premier filtre strtolower suivi d’un string.strip_tags ce quisignifie que la chaine est d’abord mise en petites lettres puis supprimes les balises html ou hp présents dans lachaine.

L’ordre des filtres utilisés dépend de l’ordre des deux fonctions stream_filter_append et stream_filter_prepend.La première fait que le filtre s’applique après les tous les filtres déjà associés au flux géré et la seconde fait aucontraire que le filtre s’applique en premier ;

• resource stream_filter_append ( resource $stream , string $filtername [, int $read_write[, mixed $params ]] ) : Associe le filtre de nom $filtername au flux $stream. Le filtre s’appliqueen dernier par rapport à ceux déjà associés à $stream. Le filtre est associé par défaut pour un accèsen lecture si le flux a été ouvert en lecture, et pour un accès en écriture si le flux a été ouvert commetelle. $read_write permet alors de forcer le filtrage en lecture (STREAM_FILTER_READ), écriture(STREAM_FILTER_WRITE) ou les deux (STREAM_FILTER_ALL) ; $params permet d’associerdes valeurs de paramètres au filtre $filtername, maintenant aucun exemple concret... RENVOIE unhandler sur le filtre ajouté ;

• resource stream_filter_prepend ( resource $stream , string $filtername [, int $read_write[, mixed $params ]] ) : pareil mais ajoute le filtre en tête de liste des filtres ; ATTENTION : si desdonnées sont présents dans le tampon associé au flux, le filtre ajouté avec stream_filter_prepend NESERONT PAS filtrés avec le filtre (précision importante si on ajoute un filtre en cours de lecture/écrituresur un flux) ;ATTENTION : Quand l’une ou l’autre de ces fonctions est utilisé pour un ajout en écriture ou en lecture,c’est comme si le filtre était ajouté deux fois !!. RENVOIE un handler sur le filtre ajouté ;ATTENTION : chaque filtre de la liste des filtres associés à $stream a son propre tampon mémoire.la mécanique est que si trois f1, f2 et f3 s’appliquent successivement, le bout de flux filtrée passentsuccessivement par le tampon de f1, f2 et f3 et en particulier, si un filtre f4 s’ajoute en cours de lecture,s’il est ajouté après f4, il s’appliquera à ce qui est dans le tampon de f3 alors qu’il ne s’appliquera pas s’ilest ajouté au début avant f1 avec stream_filter_prepend...

• array stream_get_filters() : RENVOIE un tableau de tous les filtres natifs gérés par php. cette listedépend aussi des extensions php. Attention, dans la liste, parfois c’est le nom du module d’extension suivide ”.*” qui apparait et alors la liste des filtres associés à ce module n’est pas connue directement.

• bool stream_filter_remove ( resource $stream_filter ) : renvoie true si le filtre de handler$stream_filter a bien été retiré. Attention : pas besoin de spécifier le flux auquel est associé le filtreà retirer.

14.6.1 Création d’une classe de filtre personnalisé :

IMPORTANT : toute classe de filtre personnalisé doit être ajouté à la liste des filtres reconnus par php avec lafonction stream_filter_register()

116

Page 117: RelectureDePhp5_2011_01_29

La classe implémente l’interface php_user_filter qui contient les méthodes suivantes : filter(), onclose etoncreate()

• filter($in, $out, &$consomme, $ferme) : convertit la chaine $in en une chaine $out, $Consommeest le nombre de caractères lus sur le flux, ce paramètre doit être incrémenté du nombre de paramètretraités en ENTREE, $ferme indique avec TRUE si le flux est sur le point de se fermer càd dans ce casque PHP fait son dernier appel au filtre ; RENVOIE un entier enum : PSFS_PASS_ON si la conversionest successful, PSFS_FEED_ME qui indique que rien n’a été retourné et que le filtre attend des donnéessupplémentaires avant d’agir (En fait, ça tient qu’à ce stade, les bout de données sont gérés avec desstream_buckets). PSFS_ERR_FATAL indique une erreur fatale dans le processus ; le filtrage s’arrêtantalors. Un exemple d’implémentation avec l’utilisation des stream_buckets :

function filter($in, $out, &$consomme, $ferme)

{

// lit une chaine de caractères sur l’entrée et le récupère dans un stream_bucket

while ($donnee = stream_bucket_make_writable($in)

{

// convertit la chaine lue ($donnee->data)

donnee->data = strtoupper($donnee->data);

// ajoute la chaine lue à la sortie

stream_bucket_append($out, $donnee);

// Incrémente le nombre de caractères lus (donnee->datalen)

$consomme += $donnee->datalen;

return PSFS_PASS_ON;

}

}

• oncreate() : permet de faire les initialisation nécessaires au filtre ; appelé juste avant l’utilisation dufiltre ;

• onclose() : libère les éventuelles ressources allouées dans l’appel de oncreate() ;

• stream_filter_register(string $filtername , string $filterclass) : associe le filtre correspondant àl’implémentation de la class $filterclass au nom $filtername qui pourrait ensuite être utilisé comme nomde filtre avec les fonctions de gestion des flux (telles que fopen(), fread() etc.).

14.6.2 Utilisation des filtres utilisateurs pour les fonctions d’acc ès rapides aux flux :

Certaines fonctions comme file_get_contents ne renvoient aucun descripteur de flux (càd handler de flux). Dansce cas, il est possible d’utiliser des fonctions de filtres en spécifiant directement ceux-ci dans l’adresse du fluxciblé. Une adresse classique s’écrit :

transport://cible:port

117

Page 118: RelectureDePhp5_2011_01_29

Insérer le flux se fait ainsi :

php://filter/read=ReadFilter1|ReadFilter2|.../write=WriteFilter1|.../resource=transport://cible:port

ce qui signifie que les filtres ReadFilter1, ReadFilter2 etc. seront appliqués à la lecture du flux tandis que lesfiltres WriteFilter1, WriteFilter2 etc. seront appliqués à la lecture du flux. Le flux en question est celui désignépar resource.

15 Chapitre 15 : Flux de sorties

PHP a un flux d’entrée qui est géré en interne par PHP et Apache en second lieu. La gestion du flux de sortieest par contre elle à la charge de PHP.

Gérer le flux de sortie permet de faire principalement deux choses :

• appliquer des filtres sur le texte ou le résultat du flux de sortie de PHP ;

• en différer l’envoi du flux de sortie vers l’interface (affichage notammen) .

15.1 Comment ça marche ?PHP envoie des données vers son flux de sortie, càd vers le serveur web qui l’envoie ensuite au navigateur.Cependant PHP gère son flux de sortie avec un tampon mémoire. La gestion du flux de sortie de php revientalors à gérer le comportement du tampon mémoire associé au flux de sortie. En temp normal, PHP envoie lecontenu de son tampon par petit blocs vers le flux de sortie pour économiser des ressources.

L’utilité du tampon mémoire du flux de sortie est notamment :

• de permettre la compression zip du contenu du tampon avant envoi vers le flux de sortie ;

• de permettre l’envoi des instructions header et setcookie à l’endroit désiré du code php et non forcémentavant tout affichage car l’affichage est différé par l’usage du tampon ;

• de convertir le contenu du tampon à l’encodage désiré : càd par exemple UTF-8 au lieu de ISO-8859-1

• de récupérer tout ce qui a été envoyé vers le flux de sortie dans une variable. On peut ainsi carrémentempêcher l’envoi vers le flux de sortie en copiant puis en effaçant le contenu du tampon ;

Les fonctions permettant de gérer ce tampon sont les suivantes (on fait juste un rappel car on les a vu plus tôt) :

• ob_start() : démarre la mise en tampon de ce qui serait autrement envoyé à l’affichage ;

• ob_end_clean() : arrête la mise en tampon, et vide le contenu du tampon SANS envoi vers le flux desortie ;

• ob_end_flush() : pareil mais avec envoi vers le flux de sortie ;

• ob_flush() : vide le contenu du tampon pour l’envoyer vers le flux de sortie ; ATTENTION la mise entampon n’est pas arretée ;

118

Page 119: RelectureDePhp5_2011_01_29

• ob_end_flush() : pareil mais stope la mise en tampon ;

• on_get_contents() : récupère le contenu du tampon mais ne le vide pas, ni ne l’arrête. C’est une sorte decopie en somme ;

• ob_clean() : vide le contenu du tampon ;

IMPORTANT : A la fin d’un script php, il y a toujours implicitement un appel à ob_end_flush() si letampon est encore plein ;

15.1.1 Imbrications de tampons :

PHP peut avoir plus d’un tampon mémoire associé au flux de sortie. Les tampons sont crées avec ob_start. Lesappels de fonction ob_kekchose s’appliqueront alors TOUJOURS au dernier tampon ouvert.

IMPORTANT : A la fin d’un script php, il y a toujours implicitement un appel à ob_end_flush() pourchaque tampon encore ouvert.

15.1.2 Information sur les tampons :

• ob_get_lenght() : récupère la taille en octets du contenu du tampon mémoire du flux de sortie. RenvoieFALSE si aucun tampon mémoire n’existe pour le flux de sortie ; Attention en cas d’usage d’un filtre decompression, la taille renvoyée reste la même car ce qui est compressé, c’est ce qui est envoyé vers le fluxde sortie et non le contenu du tampon.

• ob_get_level() : spécifie le nombre de tampon encore présent.

Des filtes automatiques peuvent s’appliquer au contenu du tampon mémoire sans avoir à récupérer le contenudu tampon avec ob_get_contents() :

Les filtres automatiques de PHP permettent :

• la compression de caractères avec le module zlib ;

• la conversion du contenu du tampon d’un encodage à un autre avec le module mb_string ou le moduleiconv ;

15.1.3 Filtres et tampon mémoire de sortie :

Tout filtre de nom Filtrelambda, automatique ou personnalisé, peut être associé à un tampon mémoire avec lafonction ob_start() comme suit :

ob_start(’FiltreLambda’);

Le contenu du tampon aura été alors filtré avec le filtre lambda au préalable.

119

Page 120: RelectureDePhp5_2011_01_29

15.1.4 Les filtres automatiques du tampon mémoire du flux de sortie de PHP :

Le filtre de compression du module zlib : Un tampon peut être associé au filtre de compression desdonnées :

Les protocoles de transport http (et https) permettent l’envoi de données compressés pour gagner de labande passante. La fonction de filtre associé est ob_gzhandler(). Pour utiliser ce filtre de compression, il fautappeler ob_start de la façon suivante :

ob_start(”ob_gzhandler”)

Après tous les appels aux autres fonctions ob_kekchose sont identiques pour gérer le tampon crée.

ATTENTION : si la directive zlib.outputcompression vaut ’on’ dans php.ini, toutes les données envoyésvers le flux d sortie sont déjà compressés par défaut, et ob_start(”ob_gzhandler”) enclenchera pour le tamponconcerné une double compression des données contenues rendant illisibles les données affichées pour le tam-pon concerné. Par ailleurs, j’ai cherché à provoquer le pb de la double compression en changeant le php.inisans toutefois y parvenir... ;-) En fait, c’est normal, c’est impossible d’avoir ob_start(”ob_gzhandler”) aveczlib.outputcompression à on.

Les filtres de conversion d’un encodage à l’autre : Deux modules permettent d’ajouter un filtre deconversion d’encodages au fonction de manipulation de flux :

• mbstring

• iconv

Globalement iconv est plus rapide mais nettement moins utile dans la mesure où il gère moins d’encodages quembstring. Ce dernier révise aussi certaines fonctions de manipulation de chaines pour prendre en compte lescaractères multi-octets. mbstring est donc notre choix ;

La callback function à associé à ob_start est ”mb_output_handler” (ob_start(”mb_output_handler”)pour créer un tampon dont toutes les sorties sont converties dans l’encodage cible. L’effet de la fonction de filtragedépend alors de la directive mbstring.http_output se trouvant dans php.ini et qui spécifie quel est l’encodagecible (utf-8 au hasard...). Notons que :

• la conversion a lieu si aucun encodage n’est spécifié dans le header content-type ;

• la directive mbstring.http_output peut être setté dans le script avec ini_set() ;

• ou avec la fonction mb_http_output() :

mb_http_output ([ string $encoding ] ) : Renvoie l’encodage cible courant tel que setté avecla directive php mbstring.http_output OU sette cette directive à $encoding si cette variable optionnel estspécifié et RENVOIE TRUE si le changement s’est opéré correctement ou false sinon.

Les filtres utilisateurs Un filtre utilisateur peut être défini comme toute fonction prenant une chaine enentrée et renvoyant une chaine en sortie. A tout appel de ob_flush() ou ob_end_flush(). Le contenu du tamponpasse en entrée de la fonction utilisateur et la chaine renvoyée par la fonction filtre utilisateur est envoyée versle flux de sortie.

IMPORTANT : les filtres appliqués au tampon mémoires sont différents des filtres associés aux fonctions degestion des flux. Idéalement il vaut mieux éviter les redondances ou les doubles filtrages malheureux de ce fait.

120

Page 121: RelectureDePhp5_2011_01_29

RUSE : si on souhaite cumuler les filtres, on peut aussi bien créer un filtre utilisateur qui appelle deuxautres fonctions de filtrage dans son bloc d’exécution.

15.2 Automatisation du tampon mémoire et/ou du filtrage

La directive output_buffering de php passé à ”on” permet d’activer une mémoire tampon automatiquementà chaque début de script comme si ob_start avait été appelé. En settant la directive à une valeur entière, onfixe la taille du tampon utilisé.

ATTENTION : L’usage d’output_buffering revient à un appel automatique de ob_start() et on peut doncutiliser ob_flush, ob_end_flush, ob_end_clean et ob_clean et cie dessus comme si ces appels étaient précédésd’un ob_start()

ATTENTION : Dans le cas de l’activation du module de compression zlib (càd en settant zlib.outputcompression= On), un output buffer se met bien à exister automatiquement mais par contre, cet outputBuffer ne peut pasêtre détruit ni vider avec les fonctions ob_machin,on peut juste utiliser flush pour le vider. Du coup, ce bufferexiste jusqu’à la fin du script et PHP se charge de le supprimer tout seul.

IMPORTANTE : Si on souhaite utiliser la compression et l’outputbuffering sans avoir à gérer différemmentl’output buffer implicite lié à la directive output_buffering passé à on, il faut setter :

output_buffering = On,output_handler = ob_gzhandler ;zlib.output_compression_level = 6

16 Chapitre 16 : Envoyer et recevoir des mails

Un petit chapitre technique et marrant je crois sur l’envoi et la réception de mail en PHP.On passe sur l’utilité des mails car elle est évidente : entre la lettre d’information automatisé, la disposition

d’une gestionnaire de courrier, le forum (qui est un dérivé des techniques mails) et la validation d’inscriptionsur site web.

IMPORTANT : un mail peut être envoyé en format texte ou html mais globalement html fait plus sérieux...

ATTENTION : Plusieurs librairies et modules d’extensions php de gestion de mails existent en php. Nousallons utiliser IMAP.

Deux directives de configuration sont à setter dans php.ini :

• SMTP = AdresseDuServeurSMTP ; SMTP doit indiquer l’adresse du serveur SMTP du fournisseur d’accès,càd mail.NomDomaineDuFAI ou smtp.NomDomaineDuFAI ;

• sendmail_from = AdresseMail@parDefaut ; cette directive sert à spécifier l’adresse par défaut de toutmail envoyé ; (en l’occurrence, pour le moment, chez moi c’est [email protected]

Note : SMTP = Simple Mail Transfert Protocol

121

Page 122: RelectureDePhp5_2011_01_29

16.1 Courrier électronique au format texte

Un mail est divisé en deux parties :

• Les en-têtes : contient toutes les informations liés au transport du message (destinataire, expéditeur,etc.)ainsi que toutes les données nécessaires à la manipulaion du message (le sujet, la date d’envoi, le type decontenu etc.) ; A noter que les en-têtes sont standardisés suivant la norme RFC 822 qui structure tousles courriers mails à la manière d’un dénominateur commun. En l’occurence, c’est la norme des messagesmails au format texte ;

– Chaque en-tête se présent comme suit :

NomChamp : ValeurDuChamp

exemple :From : [email protected] Tue Oct 1 12:00:50 2004To : [email protected] : Wed, 8 Sept 2010 10:54:00 1768Simple, non ?

– ATTENTION : RFC 822 n’est pas la seule norme gouvernant la structure des mails.

∗ RFC 821 : Simple Mail Transfert Protocol (SMTP)∗ RFC 822 : Standard for ARPA Internet text messages∗ RFC 2060 : Internet Message Access Protocol (IMAP)∗ RFC 1939 : Post Office Protocol Version 3 (POP3)∗ RFC 2076 : Common Internet Message Headers.∗ RFC 2045, RFC 2046, RFC 2047, RFC 2048, RFC 2049 : Multipurpose Internet Mail Extensions(MIME) == > Norme servant à définir les mails au format html incoporant du texte, du son etde l’image !!

• Le message : Le contenu du message lui-même...

16.1.1 Envoi d’un mail avec mail()

• bool mail ( string $to , string $subject , string $message [, string $additional_headers [,string $additional_parameters ]] ) : envoie un mail vers l’adresse mail $to avec pour sujet $subjectet pour message la chaine $message. Des en-têtes $additional_headers peuvent être ajoutés au mailde même que des paramètres additionnels $additional_parameters. A propos de chaque :

– $to : Une adresse mail comme [email protected], [email protected] ou [email protected];

– $subject : chaine de caractères correspondant au sujet du message, il doit respecter la norme RFC2047 (partie intégrante de la supernorme MIME ;

– $message : La chaine de caractères correspondant au message lui-même. Cette chaine doit respecterqu’une ligne ne doit pas dépasser les 70 caractères. Un ’\n’ doit donc se trouver au maximum tousles 70 caractères. ATTENTION sous Windows uniquement : Lorsque PHP discute directementavec un serveur SMTP, si un point est trouvé en début de ligne, il sera supprimé. Pour éviter ce

122

Page 123: RelectureDePhp5_2011_01_29

comportement, remplacez ces occurrences par un double point. Par exemple tout message devra êtrepassé par la fonction suivante :

$MessageSousWindows = str_replace(”\n.”, ”\n..”, $MessageATraiter);

– $additional_headers : Chaîne à insérer à la fin des en-têtes du mail ; Ce paramètre est typiquementutilisé pour ajouter des en-têtes supplémentaires (From, Cc et Bcc). Les en-têtes supplémentairesdoivent être séparés par un caractère CRLF (\r\n). Lors de l’envoi d’un mail, le mail DOIT contenirun en-tête From. Il peut être défini par le paramètre additional_headers, ou un par défaut peutêtre défini dans le php.ini. Ne pas faire ceci causera un message d’erreur similaire à Warning:mail(): ”sendmail_from” not set in php.ini or custom ”From:” header missing. L’en-tête From définitégalement l’en-tête Return-Path sous Windows.Exemple :”From: [email protected]\nCc:[email protected],[email protected]”IMPORTANT :

∗ le caractère ”:” DOIT se trouver immédiatement après le nom du champ. En l’occurrence, icisi on a ”From : [email protected],...”, le champ From est considéré comme ayant unevaleur vide alors qu’avec ”From: [email protected]”, il vaut bien l’adresse fictive deNatacha Polony.

∗ Pour ajouter plusieurs champs, il faut passer à la ligne (càd écrire ’\n’) immédiatement après lavaleur du champ précédent ;

∗ Pour avoir plusieurs valeurs pour un champs, il faut séparer les différentes valeurs d’une virgule.Un espace est toléré avant et après chaque virgule. La seule contrainte concerne le ”:”.

∗ Pour changer l’adresse de Reply, il faut ajouter ”Reply-to: [email protected]” ;∗ Le champ copie carbone est ”Cc: AdressesMail” avec AdressesMail une suite d’adresse mailsséparés par une virgule ”,” ;

∗ Le champ copie carbone caché est ”Bcc:” ;∗ Pour modifier la priorité d’un message, on utilise le champs ”X-Priority: X” avec X valant 5(basse), 3 (normale) ou 1(urgent) ;

– $additional_parameters : Le paramètre additional_parameters peut être utilisé pour passer desdrapeaux additionnels comme options à la ligne de commande configurée pour être utilisée pourenvoyer les mails en utilisant le paramètre de configuration sendmail_path. Par exemple, ceci peutêtre utilisé pour définir l’enveloppe de l’adresse de l’expéditeur lors de l’utilisation de sendmail avecl’option -f.L’utilisateur sous lequel tourne le serveur web doit être ajouté en tant qu’utilisateur de confiancedans la configuration de sendmail afin que l’en-tête X-Warning ne soit pas ajouté au message lorsquel’enveloppe de l’expéditeur (-f) est défini en utilisant cette méthode. Pour les utilisateurs de sendmail,ce fichier est /etc/mail/trusted-users. INCOMPREHENSIBLERENVOIE TRUE si le mail a été accepté pour livraison, FALSE sinon.

• ATTENTION : envoyer des mails massivement avec la fonction mail est inefficient car pour chaque mail,la fonction va ouvrir et fermer une socket réseau. Il faut alors utiliser la fonction mail du namespacePEAR qui est une extension de php.

16.1.2 Envoi d’un mail Multimedia :

Pour cela, il faut ajouter dans la variable $header de l’appel de la fonction mail :

123

Page 124: RelectureDePhp5_2011_01_29

• ”MIME-Version: X.Y” : qui spécifie le numéro de version X.Y du format MIME (Multipurpose InternetMail Extensions). Le numéro de version habituel est 1.0 ;

• ”Content-type: TypeDeContenu” : qui spécifie le type et le sous-type des données contenues dans lemessage. Les possibilités sont :

– image/jpeg, image/png, image/gif : formats d’images jpeg, png, gif ;– text/plain : texte pur sans mise en forme (valeur par défaut quand le champ ”Content-type:” n’est

pas spécifié ;– text/enriched : texte avec mise en forme (comme le format rtf) ;– multipart/mixed : utilisé quand le mail contient plusieurs parties avec différents types données.

Un ordre est alors spécifié au type de données ;– Encodage du message : L’encodage est spécifié comme un sous-champ ”Charset=” associé au

champ ”Content-type:”. Ce sous-champ est séparé du premier avec un ”;” et vaut le nom de l’encodageutilisé comme vu auparavant. Par exemple :”Content-type: text/html; Charset= ’utf-8” ’ou ”Content-type: text/html; Charset= ’utf-8” ’

16.1.3 Limitations du protocole SMTP :

Les messages sous le protocole SMTP ont pour limitations que :

• que les données du message sont en ASCII-US codés sur 7 bits ;

• que les lignes ne peuvent pas contenir plus de 1000 caractères.

Pour surmonter ces deux limitations, on peut ajouter un champ ”Content-transfer-encoding:” avec pourvaleur ”7bit” dans le header du message. Ce champ permet de spécifier que le mail est codé en 7 bits pourl’envoi pour ensuite être décodé dans l’encodage spécifié avec le sous-champ charset du champ Content-type.Normalement quand la messagerie est compatible MIME, c’est totalement transparent... Bref 90 % du temps,on s’en fouut !

Envoyer un mail au format html : pas dur, il faut setter ”Content-type: text/html” puis écrire le message

avec des balises html classiques.

Envoyer un mail avec des images incorporés : Deux solutions :

• soit l’incorporer par un lien vers l’emplacement web de l’image souhaité (Attention au requête serveur dansle cas d’un mail d’informations envoyé à plusieurs centaines de personnes) : Pour cela, on utilise une balisehtml comme <img src=http://Le/Chemin/Du/Lien> ou >body background=http://Le/Chemin/Du/Lien>sachant que cette liste de balises html est non exhaustive.

• incorporer l’image directement dans le mail (Attention au poids de l’image) : Voir la partie suivante surl’envoi de pièces jointes ;

124

Page 125: RelectureDePhp5_2011_01_29

Envoi de messages dont le content-type vaut multipart/mixed Ce genre de mail est de type MIME.

Le mail doit alors avoir une structure bien particulière : Le début du mail correspond à son en-tête, puis chaquepartie du contenu est elle-même précédé d’un en-tête spécifiant le type de contenu (fichier attaché, image, texte,ou autre). Chacune de ses en-têtes de partie est précédé de deux lignes : une vide et une contenant un codedélimiteur. Ce délimiteur doit être le même sur tous l’ensemble du message

Tout en-tête ou contenu doit être suivi d’une ligne vide.

La structure du mail est donc :Lignes d’en-têtes généralesUn Ligne VideUn Ligne avec le délimiteurEn-tête de la partieUn Ligne VideContenu de la partieUn Ligne VideUn Ligne avec le délimiteurEn-tête de la partieUn Ligne VideContenu de la partieetc.

Un Ligne avec le délimiteur pour finirLla ligne avec le délimiteur :Elle s’écrit : ”- - $delim\n”; avec $delim = md5(uniqid(mt_rand()));

La ligne d’en-tête générale :Les deux premières lignes de l’en-tête générale DOIVENT être les suivantes :

$header = ”MIME-Version: 1.0\n”;$header .= ”Content-Type:multipart/mixed; boundary=’$delim’ \n”; (avec $delim

Pour envoyer des pièces jointes, il faut convertir la pièce jointe au format RFC 2045 compatible avec leformat MIME.

Pour cela, il faut :

• faire un file_get_contents sur le nom et chemin du fichier à joindre :

$FileContents = file_get_contents($ChemiNomnFichier);

• puis faire appliquer successivement les fonctions base64_encode() et chunk_split sur $FileContents :

$attache =chunk_split(base64_encode($FileContents))

• Le header de la partie du fichier à envoyer s’écrit alors :

”Content-type: image/gif; name=\”$CheminNomfichier\”\n”;

”Content-Transfer-Encoding: base64\n”;

• On spécifie ensuite si on veut que le fichier apparaisse dans le corps du texte (inline) ou attaché (attach-ment). ATTENTION : L’attachement sera parfois forcé par le client de messagerie.

”Content-Disposition: inline; filename =’$CheminFichier’\n”;

125

Page 126: RelectureDePhp5_2011_01_29

• Dans le contenu associé au header précédant, le fichier attaché s’ajoute en demandant l’affichage brutalede $attache : $msg .= $attache . ”\n”;

Les fonctions utilisés ci-dessus sont :

• string chunk_split ( string $body [, int $chunklen = 76 [, string $end = ”\r\n” ]] ) : Scinde lachaîne body en segments de chunklen octets de longueur. Cette fonction est très pratique pour convertirles résultats de base64_encode() au format de la RFC 2045. Elle insère le paramètre end tous les chunklencaractères. Retourne la chaîne scindée.

• string base64_encode ( string $data ) : Encode data en base64. Cet encodage est fait pour permettreaux informations binaires d’être manipulées par les systèmes qui ne gèrent pas correctement les 8 bits,comme les corps de mail. Une chaîne encodée base64 prend environ 33 % de plus que les données initiales.

Pour l’envoi d’un fichier image dans un mail texte, le code suivant peut être utilisé :

function AddPlainTextHeader(&$msg,$delim){

/// En-tête de la première partie$msg .= ”–$delim\n”;$msg .= ”Content-Type: text/plain; charset=\”utf-8\” \n”;$msg .= ”Content-Transfer-Encoding:8bit \n”;$msg .= ”\n”;

}

function AddImageHeader(&$msg, $delim, $CheminFichier){

$msg .= ”–$delim\n”;$msg .= ”Content-Type: image/jpeg; name=’$CheminFichier’\n”;$msg .= ”Content-Transfer-Encoding: base64\n”;$msg .= ”Content-Disposition: inline; filename=’$CheminFichier’\n”;$msg .= ”\n”;

}

function EnvoiMailAvecFichiers_Bg($To,$Expediteur,$Cc,$Bcc, $Subject,$Texte,$CheminFichier){

$delim = md5(uniqid(mt_rand()));$header = ”MIME-Version: 1.0\n”;$header .= ”Content-Type:multipart/mixed; boundary=\”$delim\” \n”;$header .= ”X-Priority: 1\n”;$header .= ” \n”;/// message pour les éventuels logiciels ne lisant pas le MIME$msg = ”Votre client de messagerie ne lit pas le MIME \n”;$msg .=”\n”;/// ajout d’un header plain text en utf-8AddPlainTextHeader($msg,$delim);/// ajout du plain text lui-meme càd le message $Texte

126

Page 127: RelectureDePhp5_2011_01_29

$msg .= $Texte.”\n”;$msg .= ”\n”;$attache = file_get_contents($CheminFichier);$attache = chunk_split(base64_encode($attache));AddImageHeader($msg,$delim,$CheminFichier);$msg .= $attache .”\n”;$msg .= ”\n”;mail($To,$Subject,$msg,”Reply-to:$Expediteur\nFrom: $Expediteur\nCc: $Cc\nBcc: $Bcc\n”.$header);

}

Maintenant si on veut utiliser du html et insérer l’image dans le html, il faut ajuster le code précédent :

• Dans le header du texte affiché, il faut mettre text/html au lieu de text/plain ;

• pour insérer l’image dans le coeur du message Html, il faut associer le fichier image avec un id html commesuit :

$msg.= ”Voici l’image : <img src=’cid:IdImage’ alt=\”\” >”;

• Dans la partie concernant l’image à afficher, après la ligne $msg.=”Content-Transfer-Encoding: base64\n”,il faut ajouter :

$msg .= ”Content-ID: <IdImage>\n”;

Le résultat de cette dernière manip est de lier l’image affiché à la balise img dont l’attribut src vaut IdImage.ATTENTION si aucun src ne vaut IdImage, l’image est quand même affiché mais comme dans un mail

dont le Content-Type vaut text/plain. Par ailleurs, si l’affichage du fichier n’est pas demandé, même si l’Id estcorrectement lié, l’image sera considéré comme fichier attaché et n’apparaitra pas dans le coeur du texte html.

16.2 Recevoir des mails :

Deux protocoles alternatifs régissent les échanges de mails entre client mail et serveur mail :

• POP3 (Post Office Protocol) : Tous les fichiers mails sont téléchargés intégralement en local sur le clientmail. Les échanges avec le serveur ne sont pas cryptés et dont le login et le mot de passe du client sur leserveur... Bref ça prend de la place et ce n’est pas safe du tout !! Sans compter qu’il ne peut jamais yavoir plus d’un client connecté à un même compte.

• IMAP (Internet Mail Access Protocol) : Beaucoup plus souple, ce protocole permet plein de choses dontle seul téléchargements du sujet des mails, une récupération filtrée etc. etc.

Pour récupérer ses mails stockés sur un serveur distant, il faut ouvrir un flux pointant sur la boite à lettres :

• resource imap_open ( string $mailbox,

string $username,

string $password

[, int $options = NIL

[, int $n_retries = 0

127

Page 128: RelectureDePhp5_2011_01_29

[, array $params = NULL ]]] ) : essaie de créer un handler sur le fluxcorrespondant à la boite mail défini par une adresse du server mail $mailbox(v.plus bas), un username$username, un mot de passe $password.

ATTENTION : Cette fonction peut aussi être utilisée pour ouvrir des flots sur des serveurs POP3 etNNTP mais quelques fonctions et fonctionnalités ne sont disponibles qu’avec les serveurs IMAP.

Une attention toute particulière doit être porté à la définition de $mailbox

– $mailbox : C’est une chaine de caractère qui DOIT avoir la forme suivante :{NomDeServeur:port/protocole/flag}NOMDUREPERTOIREPRINCIPALE

∗ NomDeServeur : cela dépend du serveur hébergeant la boite mail. NomDeServeur a souventl’écriture suivante : TypeProtocole.mail.NomDuFAI.fr Par exemple : pop.mail.yahoo.fr est lenom du serveur de mail de yahoo.fr accessible suivante le protocole pop.

∗ port : difficile de savoir quel est le bon port d’accès à la boite. Pour yahoo.fr, c’est 110...∗ protocole : smtp, pop3 ou imap.∗ flag : flag optionnel. La liste des flags possibles est la suivante :

· service=service : pour l’accès à la mailbox, par défaut : ”imap”· user=user : de l’utilisateur distant pour l’identification sur le serveur· authuser=user : distance d’identification ; si spécifié, ce sera le nom de l’utilisateur dontle mot de passe est utilisé (e.g. administrator)

· anonymous : accès distant en anonyme· debug : télémétrie d’enregistrement du protocole dans les logs de déboguage de l’application· secure : transmet pas un mot de passe en clair à travers le réseau· imap, imap2, imap2bis, imap4, ou imap4rev1 : équivalent de /service=imap· pop3 : équivalent de /service=pop3· nntp équivalent de /service=nntp· norsh : pas utiliser rsh ou ssh pour établir une session de pré identification IMAP· ssl : Secure Socket Layer pour crypter la session· validate-cert : les certificats depuis le serveur TLS/SSL (c’est le comportement par défaut)· novalidate-cert : pas valider les certificats depuis le serveur TLS/SSL, nécessaire si leserveur utilise des certificats auto-signés

· tls : l’utilisation de start-TLS pour chiffrer la session et rejette les connexions aux serveursqui ne le supporte pas

· notls : n’utilise pas start-TLS pour chiffrer la session, y compris avec les serveurs qui lesupporte

· readonly : un accès en lecture seule sur mailbox (IMAP uniquement ; ignoré sous NNTP,et une erreur avec SMTP et POP3)

∗ NOMDUREPERTOIREPRINCIPALE : c’est INBOX dans la plupart des cas...– $username : le nom d’utilisateur de la messagerie ;– $password : le mot de passe associé ;– $Options : options est un masque optionnel de bit vide par défaut , qui peut prendre une ou

plusieurs des valeurs suivantes (on les combine avec |):

∗ * OP_READONLY : Ouvre une boîte aux lettres en lecture seule∗ * OP_ANONYMOUS : Ne pas utiliser, ou modifier le fichier .newsrc pour les news (NNTP

uniquement)

128

Page 129: RelectureDePhp5_2011_01_29

∗ * OP_HALFOPEN : Pour les noms IMAP et NNTP, ouvre une connexion mais n’ouvre pas uneboîte aux lettres.

∗ * CL_EXPUNGE : Supprime automatiquement la boîte aux lettres de la liste, lors de la termi-naison du flux (voir aussi imap_delete() and imap_expunge())

∗ * OP_DEBUG : négociations de déboguage du protocole∗ * OP_SHORTCACHE : Cache court (elt uniquement)∗ * OP_SILENT : Ne pas transmettre les événements (utilisation interne)∗ * OP_PROTOTYPE : Retourne le prototype du driver∗ * OP_SECURE : Ne pas effectuer des identifications non sécurisées

– $n_retries : Optionnel, 0 par défaut. Le nombre maximal de tentatives de connexion.– $params : Paramètres optionnels de connexion ; Un tableau de paramètres dont les clés peuvent

être utilisées pour définir un ou plusieurs paramètres de connexion. La doc php web n’en dit pas plus.

• La liste suivante permet de connaitre les adresses serveur des principaux FAI :

– ∗ 9 Telecom* Serveur POP : pop.neuf.fr* Serveur SMTP : smtp.neuf.fr* Serveur IMAP : imap.neuf.fr

∗ 9ONLINE* Serveur POP : pop.9online.fr* Serveur SMTP : smtp.9online.fr

∗ ALICE ADSL* Serveur POP : pop.alice.fr, pop.aliceadsl.fr* Serveur SMTP : smtp.alice.fr , smtp.aliceadsl.fr* Serveur IMAP : imap.aliceadsl.fr

∗ AOL* Serveur POP : pop.aol.com (port=110)* Serveur SMTP : smtp.neuf.fr* Serveur IMAP : imap.fr.aol.com

∗ ALTERN.ORG* Serveur POP : pop.altern.org ou altern.org* Serveur SMTP : non* Serveur IMAP : imap.altern.org (à modifier)

∗ Bouygues BBOX* Serveur POP : pop3.bbox.fr* Serveur SMTP : smtp.bbox.fr* Serveur IMAP : imap4.bbox.fr

∗ Bouygues Télécom* Serveur POP : pop.bouygtel.fr* Serveur SMTP : smtp.bouygtel.fr* Serveur IMAP : imap.bouygtel.fr

∗ CARAMAIL* Serveur POP : pop.lycos.co.uk* Serveur SMTP : smtp.lycos.co.uk* Serveur IMAP : non

129

Page 130: RelectureDePhp5_2011_01_29

∗ CEGETEL* Serveur POP : pop.cegetel.net* Serveur SMTP : smtp.cegetel.net* Serveur IMAP : imap.cegetel.net

∗ CLUB INTERNET* Serveur POP : pop3.club-internet.fr* Serveur SMTP : mail.club-internet.fr* Serveur IMAP : imap.club-internet.fr

∗ DARTY BOX (DARTYBOX)* Serveur POP : pop.dbmail.com* Serveur SMTP : smtpauth.dbmail.com* Plus d’informations : o dartybox-news.fr/index.php?/pages/14-configuration-outlook-express

∗ ESTVIDEO COMMUNICATION* Serveur POP : pop.evhr.net* Serveur SMTP : smtp.evhr.net

∗ FREE* Serveur POP : pop.free.fr* Serveur SMTP : smtp.free.fr* Serveur IMAP : imap.free.fr

∗ FREESURF* Serveur POP : pop.freesurf.fr* Serveur SMTP : smtp.freesurf.fr* Serveur IMAP : imap.freesurf.fr

∗ GAWAB* Serveur POP : pop.gawab.com* Serveur SMTP : smtp.gawab.com* Serveur IMAP : imap.gawab.com

∗ GMAIL* Serveur POP : pop.gmail.com (avec port 995 et /ssl en flag de connection. Eventuellement suractivation de l’option POP de GMail)* Serveur SMTP : smtp.gmail.com* Serveur IMAP : imap.gmail.com* Plus d’informations : o http://gmail.google.com/support/bin/answer.py?answer=10350

∗ HOTMAIL* Serveur POP : pop3.live.com (Port 995 avec connexion SSL)* Serveur SMTP : smtp.live.com (Port 25 avec connexion SSL)* Serveur IMAP : non* Plus d’informations : o Relever sa boîte Hotmail avec un logiciel de messagerie o Marche àsuivre pour configurer Mozilla Thunderbird avec Hotmail)

∗ IFrance* Serveur POP : pop.ifrance.com* Serveur SMTP : smtp.ifrance.com* Serveur IMAP : non

∗ LA POSTE* Serveur POP : pop.laposte.net* Serveur SMTP : smtp.laposte.net* Serveur IMAP : imap.laposte.net

∗ MAGIC ONLINE

130

Page 131: RelectureDePhp5_2011_01_29

* Serveur POP : pop2.magic.fr* Serveur SMTP : smtp.magic.fr* Serveur IMAP : non

∗ NERIM* Serveur POP : pop.nerim.net* Serveur SMTP : smtp.nerim.net

∗ NET COURRIER* Serveur POP : mail.netcourrier.com* Serveur SMTP : idem que celui de votre FAI* Serveur IMAP : mail.netcourrier.com

∗ NOOS* Serveur POP : pop.noos.fr* Serveur SMTP : mail.noos.fr* Serveur IMAP : imap.noos.fr

∗ Numéricable* Serveur POP : pop.numericable.fr* Serveur SMTP : smtp.numericable.fr* Serveur IMAP : imap.numericable.fr

∗ ORANGE* Serveur POP : pop.orange.fr* Serveur SMTP : smtp.orange.fr* Serveur SMTP sécurisé : smtp-msa.orange.fr Port : 587 (activer l’authentification smtp)* Serveur IMAP : imap.orange.fro Aide pour paramétrage FAI Orange avec messageries non Orange

∗ SYMPATICO* Serveur POP : pop1.sympatico.ca* Serveur SMTP : smtp1.sympatico.ca* Serveur IMAP : non

∗ SFR* Serveur POP : pop.sfr.fr* Serveur SMTP : smtp.sfr.fr* Serveur IMAP : imap.sfr.fr

∗ TELE2* Serveur POP : pop.tele2.fr* Serveur SMTP : smtp.tele2.fr* Serveur IMAP : non

∗ TISCALI* Serveur POP : pop.tiscali.fr* Serveur SMTP : smtp.tiscali.fr* Serveur IMAP : non

∗ TISCALI-FREESBEE* Serveur POP : pop.freesbee.fr* Serveur SMTP : smtp.freesbee.fr* Serveur IMAP : non

∗ VOILA* Serveur POP : non* Serveur SMTP : non* Serveur IMAP : non

131

Page 132: RelectureDePhp5_2011_01_29

∗ WANADOO* Serveur POP : pop.wanadoo.fr* Serveur SMTP : smtp.wanadoo.fr* Serveur IMAP : non

∗ YAHOO* Serveur POP : pop.mail.yahoo.fr (sur activation de l’option POP3 de Yahoo) Port 995 Avecconnexion SSL* Serveur SMTP : smtp.mail.yahoo.fr Port 465 Avec connexion SSL* Serveur IMAP : non* Page de configuration détaillée (en anglais).

A FINIR

17 Chapitre 17 : Travailler avec une base de données

En fait, ce chapitre est consacré à un rappel de sql or j’ai déjà rédigé un tutoriel sql dans fichier tutoriel Symfony.

18 Chapitre 18 : Utiliser une base de données avec PHP

Depuis PHP 5, une extension unique de PHP permet de piloter une base de donnée quelle que soit la naturedu SGBD sous-jacent. Auparavant l’extension la plus répandue était l’extension mysql de PHP4. mysqli peutaussi être utilisée sous PHP 5 (i pour ”improved” càd améliorée).

En ce qui me concerne, c’est PDO qui m’intéresse. L’exposé de mysqli est beaucoup trop sommaire dans lele bouquin pour y passer du temps.

18.1 PDO = PHP Data Object

PDO unifie la notion de connection vers les SGBD. PDO est une extension PHP écrite en C ce qui la rend trèsrapide. Cen n’est cependant pas totalement une abstraction de la gestion des base des données car il existe uneextension PDO pour chaque type de SGBD (ex : pdo_mysql, pdo_sqlite, pdo_odbc, etc.).

18.1.1 Utilisation d’une base de données

L’utilisation d’une base de données via PDO sur pHP se fait en 5 étapes majeures :

1. Connexion

2. Sélection de la base de données

132

Page 133: RelectureDePhp5_2011_01_29

3. Requête

4. Exploitation des résultats

5. Fermeture de la connexion

Notion de DSN : Data Source Name Un DSN spécifie l’adresse d’une source de données à un clientsouhaitant manipuler des données. Si j’écris source de données, c’est qu’un DSN peut tout autant être une basede données qu’un fichier Access, Excel, ou autre...

Le DSN dépend de la source de données auquel on souhaite se connecter. En l’occurence pour pdo_mysql,le DSN a les éléments suivants :

• host : adresse du serveur distant sur lequel réside la base de données (nom ou adresse IP pour un serveurdistant ou localhost pour un serveur en local) ;

• dbname : nom de la base à utiliser ;

• port : Optionnel, port TCP/IP de la connexion à utiliser ;

• unix_socket : Optionnel, adresse de la socket unix pour une connexion en local sur un réseau unix.

Le DSN s’écrit alors comme une chaine ayant la forme suivante :

”NOMSGBD:host=NomHost;dbname=NOMBASE;port=NUMPORT;unix_socket=NUMSOCK”

Dans le cas d’une connexion à une base mysql sur une config Apache Windows ou linux, ça donne plusprécisément :

”mysql:host=NomHost;dbname=NOMBASE”

Les deux derniers arguments étant facultatifs.

ATTENTION : la nature des éléments d’un DSN change suivant le SGBD !!! Voir la page 462 du bouquinpour en voir d’autres.

Les principales classes de PDO Elles sont au nombre de trois :

1. La classe PDO qui gère la connexion à la base de données ;

2. La classe PDOStatement qui encapsule les structures de données obtenues en retour de requête SQL ;

3. La classe PDOException qui l’objet de type Exception renvoyé par PDO en cas d’irruption d’uneerreur ;

133

Page 134: RelectureDePhp5_2011_01_29

Les fonctions PHP d’exécution d’instruction sql : Les requêtes sql sont exécutées en utilisant les fonc-tions exec ou query :

• exec(String $RequeteSQL) : A COMPLETER : la fonction exec exécute la requête SQL passée enargument en renvoyant seulement le nombre de lignes ou le nombre de modifications générales effectuées.Elle est donc utiliser pour les requêtes SQL ne renvoyant pas de données comme INSERT, UPDATE,DELETE ou ALTER ; RENVOIE le nb de modifs ou FALSE si erreur ; RENVOIE :

– le nombre de lignes qui ont été modifiées ou effacées pour la requête SQL qui vous exécutez.

– Si aucune ligne n’est affectée, la fonction PDO::exec() retournera 0.

– FALSE si la requête produit une erreur côté sql.

ATTENTION : Dans le cas d’une requête d’effacement de tous les enregistrements d’une table, execne renvoie pas le nombre de lignes effacées car pour aller plus vite, PHP efface alors tout le fichiercorrespondant à la table avant de le recréer illico.

• query(String $RequeteSQL) : La fonction query exécute la requête SQL passée en argument et renvoieun objet de classe PDOStatement qui encapsule les données obtenues par la requête sql ; RENVOIE unobjet de classe PDOexception pour des instructions de type SELECT, EXPLAIN ( ???), SHOW (???) etDESC (????), ou FALSE si erreur.

Ca, c’est l’usage courante de la fonction : le prototype complet de la fonction peut prendre 3 autres formesdifférentes :

PDOStatement PDO::query ( string $statement , int $PDO::FETCH_COLUMN , int $colno)

PDOStatement PDO::query ( string $statement , int $PDO::FETCH_CLASS , string $class-name , array $ctorargs )

PDOStatement PDO::query ( string $statement , int $PDO::FETCH_INTO , object $object)

A partir de ces deux fonctions PDO, on peut exécuter la plupart des instructions SQL

Connexion au serveur de données en PHP Pour cela, il faut créer un objet de classe PDO en appelantson constructeur explicite qui prend en argument :

new PDO($dsn,$user,$password);

avec :”$dsn=’mysql:host=localhost;dbname=publication”

Dans le cas d’une connexion à une base se trouvant en local et s’appelant ’publication’.

134

Page 135: RelectureDePhp5_2011_01_29

Les Connexions persistantes Etablir une connexion persistante permet d’éviter d’avoir à se reconnecter àune base à chaque requête sachant que cela prend un temps conséquent...

Pour spécifier que la connexion à une base est persistante, il faut ajouter un tableau associatif d’option enargument du constructeur de l’objet de classe PDO comme suit :

new PDO($dsn, $user, $password, array(PDO::ATTR_PERSISTENT => true)

ATTENTION Apache peut avoir plusieurs threads et dans cas, il y aura autant de connexions persistentesque de threads apache pour un même utilisateur. Si le nombre d’utilisateurs devient grand, le SGBD peut êtresaturé car le nombre de connexions persistentes sera NbUsers x NbthreadsApache...

Gérer les erreurs de connexion EN cas d’erreur lors d’un appel à exec ou query sur une requête sql, unobjet de classe PDOexception est renvoyé.

ATTENTION SECURITE la non interception d’une PDOexception via try catch fait que PHP génèreun fichier de traces contenant tous le contenu de la requête dont au minimum le user et le password quipeuvent ensuite être potentiellement récupérés par un pirate lisant le fichier de traces... IL FAUT DONCIMPERATIVEMENT GERER LES ERREURS avec PDO.

Fermer une connexion Pour fermer une connexion à une base, il faut assigner NULL à l’objet PDO

correspondant ou attendre la fin du script. Le mieux étant de le faire explicitement pour éviter les mauvaisessurprises.

Gérer ses données de connexion avec un fichier de configuration Pour éviter d’avoir à redéfinirconstamment ses données de connexion à une base de données pour créer un objet PDO dès qu’une connexionest nécessaire, le mieux est d’intégrer la création de l’objet PDO d’une base dans un fichier script dédié qui seraensuite mis en include ou require dans tous les autres fichiers de scripts appelant la base de données souhaitée.

18.1.2 Les objets de classe PDO

La classe PDO qui représente une connexion entre PHP et un serveur de base de données a les méthodesmembres suivantes :

• Le constructeur __construct qui est appelé avec l’opérateur new et qu’on a déjà vu ;

• bool beginTransaction ( void ) : Démarre une transaction (càd un bloc de requêtes devant toutes réussirsous peine de les annuler toutes v. plus bas). Désactive le mode autocommit. Lorsque l’autocommit estdésactivé, les modifications faites sur la base de données via les instances des objets PDO ne sont pasappliquées tant que vous ne mettez pas fin à la transaction en appelant la fonction PDO::commit().L’appel de PDO::rollBack() annulera toutes les modifications faites à la base de données et remettra laconnexion en mode autocommit. ATTENTION : Quelques bases de données, dont MySQL, exécuterontautomatiquement un COMMIT lorsqu’une requête de définition de langage de base de données (DDL)comme DROP TABLE ou CREATE TABLE est exécutée dans une transaction. Cela concerne notammenttoutes les instructions impactant la structure d’une table de la base concernée ou la base elle-même. CeCOMMIT implicite vous empêchera d’annuler toutes autres modifications faites dans cette transaction.RENVOIE TRUE en cas de succès ou FALSE si une erreur survient.

135

Page 136: RelectureDePhp5_2011_01_29

• bool commit ( void ) : Valide une transaction (càd un bloc de requêtes devant toutes réussir sous peinede les annuler toutes v. plus bas) ; remet la connexion en mode autocommit après en attendant l’appel à lafonction PDO::beginTransaction() pour débuter une nouvelle transaction. ATTENTION : comme indiquépour la fonction beginTransaction, les requêtes modifiant la structure des tables remettent automatique-ment en place le mode autocommit neutralisant l’effet de la fonction commit(). Si une transaction n’estpas validée explicitement avec commit, alors, on présume que quelque chose s’est mal passé et l’annulationde la transaction intervient afin de garantir la sécurité de vos données. Lorsque le script se termine oulorsque la connexion est sur le point de se fermer, si vous avez une transaction en cours, PDO l’annuleraautomatiquement.

• mixed errorCode ( void ) : Retourne le SQLSTATE associé avec la dernière opération sur la base dedonnées. ATTENTION cette fonction n’est pas fiable pour renvoyer le code d’erreur quand on fait desrequêtes avec PDO. Elle ne renvoie de valeurs correctes que lorsqu’apparemment les requêtes ne passentpas par les classes et méthodes de PDO. Bref vaut mieux utiliser la méthode errorInfo suivante. Parailleurs, errorinfo renvoie lui aussi le SQLSTATE d’où l’inutilité de cette fonction...

• array errorInfo ( void ) : Retourne les informations associées à l’erreur lors de la dernière opérationsur la base de données : renvoie un tableau à trois entrées : La première contient le SQLSTATE qui estsouvent défaillant (v. la fonction errorCode à ce sujet pour savoir pourquoi), la seconde contient le codeerreur issue du driver PDO et la seconde le texte de l’erreur ;

• int exec ( string $statement ) : DEJA VU. Exécute une requête SQL et retourne le nombre de lignesaffectées

• mixed getAttribute ( int $attribute ) : Récupère un attribut d’une connexion à une base de données

• array getAvailableDrivers ( void ) : Retourne la liste des pilotes PDO disponibles

• string lastInsertId ([ string $name = NULL ] ) : Retourne l’identifiant de la dernière ligne inséréeou la valeur d’une séquence

• PDOStatement prepare ( string $statement [, array $driver_options = array() ] ) : Prépare unerequête à l’exécution et retourne un objet

• PDOStatement query ( string $statement ) : Exécute une requête SQL, retourne un jeu de résultatsen tant qu’objet PDOStatement, ou FALSE si une erreur survient ;

• string quote ( string $string [, int $parameter_type = PDO::PARAM_STR ] ) : Protège unechaîne pour l’utiliser dans une requête SQL PDO, càd échappe les caractères spéciaux de SQL dans lachaine passée en argument. $parameter_type permet éventuellement de spécifier le type de donnéespour les drivers qui ont des styles particuliers de protection (obscure mais on s’en fout). Quoiqu’il arriveil est fortement recommandé d’utiliser des requêtes préparées plutôt que traiter le problème des caractèresspéciaux à échapper avec des appels à quote à la volée... Voir la fonction prepare pour les requêtespréparées. RENVOIE la chaine échappée ou FALSE si le $parameter_type est incorrect...

• bool rollBack ( void ) : Annule la transaction courante, initié par la fonction PDO::beginTransaction().C’est une erreur que d’appeler cette méthode s’il n’y a aucune transaction active.Si la base de données est en mode autocommit, cette fonction restaurera le mode autocommit aprèsl’annulation de la transaction.Quelques bases de données, dont MySQL, exécuteront automatiquement un COMMIT lorsqu’une requêtede définition de langage de base de données (DDL) comme DROP TABLE ou CREATE TABLE est exé-cutée dans une transaction. Ce COMMIT implicite vous empêchera d’annuler toutes autres modificationsfaites dans cette transaction. RENVOIE TRUE en cas de succès ou FALSE si une erreur survient.

136

Page 137: RelectureDePhp5_2011_01_29

• bool setAttribute ( int $attribute , mixed $value ) : Configure un attribut PDO

18.1.3 Les objets de classe PDOStatement :

Un objet de type PDOstatement est renvoyée par une requête appelée avec query(). L’affichage du contenu del’objet de type PDOstatement se fait avec les deux fonctions suivantes :

• PDOStatement::fetchAll( [fetch_style] ) : Affiche l’intégralité de la table renvoyée par la requête sqlquelle que soit sa taille sous la forme d’un tableau PHP. ATTENTION : la taille des tables renvoyéespeut être énorme et la charge mémoire pour le serveur conséquente !! Le seul avantage est le fait quecontrairement à l’instruction fetch suivante, la connexion à la base n’est pas bloqué ; L’option fetch_stylepermet de définir plus précisément le type de tableau renvoyé par fetchAll :

– PDO::FETCH_ASSOC : Renvoie un tableau associatif à deux niveaux. Le deuxième contient untableau assciatif pour chaque ligne. Les clefs correspondent aux têtes de colonnes de la table sqlrenvoyée et la valeur correspond à la valeur ;

– PDO::FETCH_BOTH : Option par défaut, Retourne un tableau identique au précédent sauf quele deuxième niveau contient à la fois une indexation prenant comme clef le nom de colonne, et unedeuxième indexation contenant le numéro de ligne, ce qui fait que le contenu d’une ligne apparaitdeux fois comme suit :Array(

[0] = >Array(

[login] = > MonLogin[0] => MonLogin[nom ] = > MonNom[1] = > MonNom

))

– PDO::FETCH_BOTH : Renvoie un tableau d’objet de classe stdClass dont les données membres ontpour nom les différents champs de la table renvoyée comme suit :Array(

[0] = > stdClass Object(

[login] = > MonLogin[nom ] = > MonNom)

)

• PDOStatement::fetch( [fetch_style, [cursor_orientation, [, cursor_offset]]] ) : chaque appel àfetch lit une ligne de la table renvoyée par la requête sql, ce qui permet de limiter fortement la placemémoire occupée par le retour de query. Par contre, impossible de savoir combien de lignes contient latable renvoyée. Par ailleurs, tant que fetch est en train de lire la table, il est impossible de lancer uneautre requête sql. RENVOIE false en cas d’erreur ;

• PDOStatement::

137

Page 138: RelectureDePhp5_2011_01_29

Pour renvoyer le nombre de lignes du tableau de retour, il suffit d’utiliser la fonction count sur le tableaurenvoyé. Deuxième solution : utiliser une requête sql avec COUNT.

IMPORTANT : si une doute subsiste sur la taille du tableau renvoyé par une requête sql, il vaut mieuxd’abord une faire requête COUNT. Si les données ont besoin d’être traités, il vaut mieux utiliser la fonctionfetch() et boucler sur les enregistrements.

18.1.4 Caractères spéciaux de SQL et échappement

L’apostrophe est le caractère spécial à protéger dans une chaine devant servir comme requête SQL. L’apostropheagit délimiteur de chaine en SQL. C’est avec ce caractère que les attaques par injection ont lieu. Pour échapperun caractère apostrophe dans une chaine, c’est à dire pour que SQL le prenne pour une vraie apostrophe et noncomme un délimiteur, il faut échapper le caractère apostrophe avec le caractère spécial ’\’ de sorte que dans :

”J\’ai faim”

SQL considère que la sous-chaine ”ai faim” N’est PAS une instruction SQL mais partie intégrante de la chaine”j’ai faim” qu’on cherche à passer à SQL.

PDO fournit la fonction quote pour échapper les apostrophes des chaines d’une requête SQL. Cependant undéveloppeur peut oublier cet appel à quote

18.1.5 NEUTRALISER la directive PHP magic_quotes_gpc

Si l’hébergeur propose un serveur en ligne qui a magic_quotes_gpc à on et qu’il interdit d’en changer lavaleur, voilà comment contourner cette config :

magic_quotes_gpc impacte les superglobales $_GET, $_POST, $_COOKIE, $_REQUEST. Pour em-pêcher cela, il faut incorporer en début de tout script le bloc de code suivant :

• if (get_magic_quotes_gpc())

{

array_walk_recursive($_GET, ’stripslashes’);

array_walk_recursive($_POST, ’stripslashes’);

array_walk_recursive($_COOKIE, ’stripslashes’);

array_walk_recursive($_REQUEST, ’stripslashes’);

}

On peut également utiliser l’échappement à la volée en vérifiant la présence de la directive qui dans ce cas auradéjà oeuvré :

if (!get_magic_quotes_gpc()){

$lastname = $pdo->quote($_POST[’lastname’]);

138

Page 139: RelectureDePhp5_2011_01_29

}else{

$lastname = $_POST[’lastname’];};

18.1.6 La fonction array_walk_recursive

Cette fonction devrait plutôt figurer dans le chapitre sur les tableaux mais bon...

bool array_walk_recursive ( array &$input , callback $funcname [, mixed $userdata ] ) :Applique la fonction de nom $funcname au tableau $input et ce de manière récursive, càd que si $input estun tableau de tableaux, la fonction descendra dans chaque tableau pour s’appliquer. ATTENTION le prototypede funcname doit prendre comme premier argument une variable de nom $item et comme second argument unevariable de nom $key. Si $userdata est fourni, il doit aussi figurer comme troisième argument de la fonction$funcname.

18.2 Gestion des erreurs avec PDO

PDO utilise un code erreur unifié quel que soit le SGBD sous-jacent. La gestion d’erreurs de PDO peut être :

• de ne pas afficher les erreurs (une sorte d’opérateur @ appliqué à tous les appels PDO) : C’est le modesilencieux qui est le mode par défaut. Les méthodes errorinfo et errorcode permettent cependant derécupérer des infos sur les erreurs produits et enregistrés dans l’instance PDO qui gère la connexion auSGBD ;

• d’utiliser le mode erreur classique ;

• ou d’avoir recours aux exceptions PHP pour renvoyer des erreurs ;

18.2.1 Setter le mode d’erreur de PDO

Cela se fait avec la fonction setAttribute() de la classe PDO comme suit :

• pour mettre PDO en mode silencieux de gestion d’erreur :

$dbh->setAttribute(PDO:ATTR_ERRMODE,PDO::ERRMODE_SILENT);

• pour mettre PDO en mode classique de gestion d’erreur :

$dbh->setAttribute(PDO:ATTR_ERRMODE,PDO::ERRMODE_WARNING);

• pour mettre PDO en mode exception de gestion d’erreur :

$dbh->setAttribute(PDO:ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

Le mode Exception implique de travailler avec des blocs try et catch. Voir le chapitre dédié à la gestion deserreurs.

139

Page 140: RelectureDePhp5_2011_01_29

18.3 Gestion des transactions

Une transaction est un bloc d’instructions sql qui doivent être toutes exécutées sans erreur sous peine de quoi,elles sont toutes annulées.

ATTENTION : de ce fait, les format de tables permettant les transactions sont les tables de type InnoDBqui permettent d’annuler l’exécution d’instructions sql précédentes. Cela tient au fait que les tables innoDBenregistrent les différences successives à partir d’un état initial de la table et non l’état courant de la table...

PDO ne vérifie la possibilité d’utiliser des transactions qu’au niveau du pilote. Si certaines conditionsà l’exécution empêchent les transactions de fonctionner, PDO::beginTransaction() retournera tout de mêmeTRUE sans erreur si le serveur accepte de démarrer une transaction.

Un exemple serait d’utiliser des transactions sur des tables au format MyISAM du serveur de base de donnéesMySQL.

IMPORTANT : pour utiliser des transactions, il vaut mieux travailler en mode Exception pour la gestiondes erreurs de la connexion PDO. Sinon en mode classique, il faut utiliser la fonction ErrorInfo pour s’assureraprès chaque requête que l’instruction query ou exec n’a pas renvoyé d’erreur...

18.3.1 Pour utiliser une transaction

1. On indique à la connexion PDO qu’on commence une transaction :

$pdh= new PDO(etc.);

$pdh->beginTransaction();

2. on stocke ses requêtes dans des string comme d’hab’, et on les exécute avec $pdh->exec ou ->query suivantle type de requête ;

$ReqStr =”PremiereRequeteTypeExec”;

$res=$pdh->exec($ReqStr);

...

$LastReq = ”DernierRequeteTypeQuery”;

$LastRes = $pdh->query($LastReq);

3. Si aucune erreur n’a eu lieu, on utilise l’instruction commit de l’instance PDO pour valider définitivementles requêtes effectuées

$dbh->commit()

4. sinon une Exception de classe PDOException a été lancée et on doit utiliser la fonction RollBack del’instance PDO pour annuler toutes les requêtes de la transaction :

$dbh->rollBack()

140

Page 141: RelectureDePhp5_2011_01_29

18.4 Les requêtes préparées

L’idée des requêtes préparées est de créer des modèles de requêtes pour les requêtes les plus utilisées. Les requêtespréparées ont deux avantages :

• La vitesse d’exécution en cas de requêtes multiples : Un requête classique passe par 4 étapes successivespour s’exécuter : Analyse, Précompilation, Optimisation et Exécution Finale. Une requête préparée nepasse par ce cycle que la première fois. Les fois suivantes, elle repasse uniquement par la dernière étape ;

• La sécurité : Les requêtes sont automatiquement protégées par PDO contre les attaques par injectionSQL.

ATTENTION : il n’est pas possible de préparer deux requêtes en même temps.

18.4.1 Construction des requêtes préparées

La requête s’écrit comme avant dans une chaine de caractères. La différence est qu’on souhaite créer un modèlede requête. Ce modèle est donc une requête dont certains éléments sont spécifiés comme dynamiques.

Pour déclarer un paramètre ou une valeur de paramètres comme dynamique, il suffit de le remplacer par unpoint d’interrogation ou par une paramètre nommé comme suit :

: NomParametreNomme

Par exemple, soit la requête suivante :

$sql= ”INSERT INTO article (titre, auteur)

VALUES (’Titre du livre de la Lionne’, ’AuteurLaLionne’)”;

Si on souhaite remplacer ’Titre du livre de la Lionne’ et ’AuteurLaLionne’ respectivement par des paramètresnommés ”:titre” et ”:auteur”, le modèle de requête correspondant s’écrira :

$sql= ”INSERT INTO article (titre, auteur)

VALUES ( :titre, :auteur)”;

L’exécution d’une requête préparée se fait en suivant les étapes ci-dessous :

1. Ecriture du modèle de requête comme ci-dessus ;

2. ”Préparation” de la requête : ça correspond aux étapes Analyse, Précompilation et Optimisation. On utilisepour cela la fonction prepare de l’instance de PDO. Cette fonction renvoie un objet PDOStatement :

$pdoStat= $pdh->prepare($sql)

3. Il reste à lier les paramètres nommés à des valeurs concrêtes. Pour ça, deux méthodes:

141

Page 142: RelectureDePhp5_2011_01_29

(a) Exécution directe de la requête via la fonction execute de l’instance PDOStatement crée au pointprécédent. Dans ce cas, les valeurs des paramètres nommés sont fixés en passant un tableau associatifdont les clefs sont les noms des paramètres nommés et les éléments les valeurs comme suit :

$TabParam =array(”:titre” => $titre, ”:auteur” => ”$auteur)

$pdoStat->execute($TabParam);

avec $titre et $auteur contenant respectivement les chaines donnant le titre et l’auteur souhaités.

(b) Lier d’abord les paramètres nommés à des variables PHP prééxistantes. Pour ça, soit la valeur de lavariable PHP est lié une fois au paramètre avec la fonction BindValue, soit elle est lié dynamique-ment à une variable PHP avec la fonction BindParam. Dans le second cas, une fois le lien fait avecBindParam, si la valeur de la variable PHP change, le paramère nommé change automatiquementpour prendre la même valeur. Le paramètre nommé est alors une référence sur la variable PHP. Dansle premier cas, la valeur de la variable PHP a seulement été recopié dans le paramètre nommé aumoment du lien mais après chacun vit sa vie...Par exemple, ça donnerait :

$pdoStat->BindParam(”:titre”, $titre);

$pdoStat->BindValue(”:auteur”, $auteur);

Comme le lien des paramètres nommés a été fait, la fonction execute de l’instance pdostatementpeut-être appelée sans argument :

$pdoStat->execute();

Les fonctions de PDOStatement liés aux requêtes préparées :

• bool PDOStatement::bindValue ( mixed $parameter , mixed $value [, int $data_type =PDO::PARAM_STR ] ) : Associe une valeur $value à un paramètre nommé $parameter ou à un pointd’interrogation (comme paramètre fictif) dans la requête SQL qui fut utilisée pour préparer la requête. Apropos de :

– $parameter : Pour une requête préparée utilisant les marqueurs (ou paramètre nommé), cela seraun nom de paramètre de la forme :nom. Pour une requête préparée utilisant les points d’interrogation(comme paramètre fictif), cela sera un nombre entier correspondant à la position du paramètre dansla requête. Si c’est le premier ? de la requête, ce sera 1, si c’est le deuxième ce sera 2, etc. Parexemple :

$sth=$dbh->prepare(’SELECT nom, couleur, calories

FROM fruit

WHERE calories < ? AND couleur = ? ’);

$sth->bindValue(1, $calories, PDO::PARAM_INT);

$sth->bindValue(2, $couleur, PDO::PARAM_STR);

– $data_type : permet de spécifier explicitement le type de données pour le paramètre $parameteren utilisant les constantes de la classe PDO :

∗ PDO::PARAM_BOOL : type booléen ;∗ PDO::PARAM_NULL : Représente le type de données NULL SQL ;∗ PDO::PARAM_INT : Représente le type de données INTEGER SQL.

142

Page 143: RelectureDePhp5_2011_01_29

∗ PDO::PARAM_STR : Représente les types de données CHAR, VARCHAR ou les autres typesde données sous forme de chaîne de caractères SQL.

∗ PDO::PARAM_LOB : Représente le type de données ”objet large” SQL.∗ PDO::PARAM_STMT : Représente un type de jeu de résultats (càd un retour de requête surSGBD). N’est actuellement pas supporté par tous les pilotes.

∗ PDO::PARAM_INPUT_OUTPUT : Spécifie que le paramètre est un paramètre INOUT pourune procédure stockée. Vous devez utiliser l’opérateur OR avec un type de données explicitePDO::PARAM_*. (V. plus bas les procédures stockées).RENVOIE TRUE en cas de succès ou FALSE si une erreur survient.

• bool PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type= PDO::PARAM_STR [, int $length [, mixed $driver_options ]]] ) : idem que la fonctionprécédente sauf que :

– Contrairement à PDOStatement::bindValue(), la variable est liée en tant que référence et ne seraévaluée qu’au moment de l’appel à la fonction PDOStatement::execute().

– le paramètre $parameter est normalement un paramètre d’entrée mais il est aussi possible que lerésultat de la requête, si elle est adéquate, pour le paramètre $parametre soit stocké en retour dansla variable PHP $variable liée. Cette technique de retour est propre aux procédures stockée qu’onverra peut-être plus loin.

– le paramètre optionnel $lenght ne sert que dans le cas de procédures stockées, ce qu’on verra plusloin.RENVOIE TRUE en cas de succès ou FALSE si une erreur survient.

• bool PDOStatement::execute ([ array $input_parameters = array() ] ) : Exécute une requêtepréparée. Si la requête préparée inclut des marqueurs de positionnement, vous pouvez :

– appeler la fonction PDOStatement::bindParam() pour lier les variables PHP aux marqueurs depositionnement : les variables liées passent leurs valeurs en entrée et reçoivent les valeurs de sortie,s’il y en a, de leurs marqueurs de positionnement respectifs

– ou passer un tableau de valeurs de paramètres, uniquement en entrée comme vu plus haut ;RENVOIE true si succès ou FALSE sinon.

DERNIERE REMARQUE CONCERNANT ENCORE UNE FOIS HELAS LES ENCODAGES

Sql sous windows renvoie des résultats de requêtes qui sont encodés en CP1252. Il faut donc convertir lesrésultats issus de query avec l’instruction mb_convert_encoding comme suit :

$PDOStat = $dbh->query($MaQuerySql);...$TrucaAfficher = mb_convert_encoding($ChaineIssuDePDOStat, ”utf-8”, ”CP1252”);echo ”On peut afficher sereinement ” . $TrucaAfficher;

143

Page 144: RelectureDePhp5_2011_01_29

19 Chapitre 19 : Erreurs et Exceptions

Un chapitre sur la gestion erreurs qui arrive comme un cheveu sur la soupe entre le chapitre sur PDO et celuisur xml, et surtout un chapitre qui arrive très tard dans le livre alors qu’on a eu déjà bon nombre d’exemplesimplicites de gestion d’erreurs...

Une erreur en programmation est l’écart existant entre ce que devrait faire une application et ce qu’elle fait.

Pourquoi gérer les erreurs ?

1. pour sécuriser le développement ;

2. pour résoudre les problèmes apparus dans la version de production

Que faire avec les erreurs ?

1. Intercepter les erreurs pour les gérer directement par le code : proposer un service dégradé, prévenir lesdéveloppeurs, arrêter l’exécution du code si besoin est ;

2. Enregistrer les erreurs dans un journal d’erreurs pour les historiser afin de traiter ultérieurement lesbogues qu’elles révèlent.

19.1 Comment bourrinement éviter les erreurs

En faisant précéder une instruction php par l’opérateur @, PHP ignore l’apparition d’une éventuelle d’erreur.Notons que cela n’empêche pas l’apparition de l’erreur en soi, cela demande juste à PHP de passer outre maisrien ne garantit derrière que l’erreur ne va pas en provoquer d’autres...

L’usage de l’opérateur @ est donc à éviter...

19.2 Les erreurs dans PHPTrois types d’erreurs :

1. Les erreurs PHP : erreur qu’affiche PHP lors d’une usage illicite de fonctions ou d’instructions de code ;

2. Les assertions : affirmation fonctionnelle et logique définie par le développeur ; PHP renvoie une erreur siles assertions implémentés ne sont pas respectées (par exemple : une corrélation doit être comprise entre0 et 1, ou un prix doit être positif) ;

3. Les exceptions : Elles permettent d’envoyer et de recevoir des messages plus évolués d’erreurs ;

144

Page 145: RelectureDePhp5_2011_01_29

19.3 Les erreurs PHP

Ce sont des messages renvoyés par PHP en cas de comportement anormal du code, càd : une erreur de syntaxe,ou une erreur lié à l’utilisation incorrecte d’une fonction ou d’une variable.

Les erreurs PHP ont 4 caractéristiques :

1. Un niveau d’importance :

2. Un message explicatif :

3. Le nom du script en cause :

4. Le numéro de la ligne en cause

19.3.1 Configurer la gestion d’erreurs :

Avant de voir les différentes méthodes pour gérer les erreurs, il faut voir comment la config de php influe sur lagestion d’erreurs.

Dans php.ini, la directive display_errors = on spécifie que les erreurs sont affichés à l’écran. Idéal endéveloppement, à proscrire en production !! (mettre donc dans ce cas display_errors = off)

Quand display_errors = off, il faut que les erreurs soient sauvés dans un log et donc il faut que spécifierles directives suivantes dans php.ini

log_errors = onerror_log = /Chemin/Vers/Fichier/Erreur/phplog.txt

Une config de développement error_reporting = E_ALL | E_STRICT (la première valeur demandel’affichage de toutes les erreurs

E_STRICT permet d’obtenir des suggestions de PHPpour modifier votre

code, assurant ainsi une meilleure interopérabilité et com-patibilité de

celui-ci.display_errors = onlog_errors = onlog_errors_max_len = 1024track_errors = offerror_log = /Chemin/Vers/Fichier/Erreur/phplog.txt

Une config de production error_reporting = E_ALLdisplay_errors = offlog_errors = onlog_errors_max_len = 1024 /// donne la taille max en ko d’un message d’erreurtrack_errors = offerror_log = /Chemin/Vers/Fichier/Erreur/phplog.txt

145

Page 146: RelectureDePhp5_2011_01_29

Modifier la gestion d’erreurs depuis PHP : La fonction error_reporting() permet de changer la valeur

de la directive error_reporting directement depuis php pour le script courant :

• int error_reporting ([ int $level ] ) : error_reporting() modifie la directive error_reporting pendantl’exécution du script. PHP possède plusieurs niveaux d’erreurs, utiliser cette fonction configure ce niveaupendant la durée (d’exécution) de votre script. RENVOIE l’ancien niveau d’error_reporting ou le niveaud’erreurs courant si le paramètre level n’est pas fourni.

IMPORTANT : pour empêcher tout affichage d’erreur avec cette fonction, il suffit de mettre 0 en argument.

Les niveaux d’erreurs php : Certaines erreurs sont liés à des dysfonctionnements internes de PHP et non

des scripts eux-mêmes (brrr...) comme suit :

• E_CORE_XXX : Erreurs critiques dans le coeur de PHP.

• E_COMPILE_XXX : Erreurs critiques lors de la compilation PHP.

Les erreurs provoquées par des scripts sont :

• E_PARSE : erreur de syntaxe dans les scripts ;

• E_ERROR : erreur intervenant quand une fonction ne peut s’exécuter normalement (c’est le Fatal Erroraffiché);

• E_WARNING : erreur intervenant quand une fonction peut malgré tout continuer son exécution. Parexemple, l’ouverture d’un fichier ;

• E_NOTICE : erreur de plus faible importance, spécifie notamment quand une variable est utilisée alorsque non-initialisée ...

Les erreurs HTML Compte tenu de la nature double html/php d’un script php, des erreurs HTML peuventse produire. Dans le cas d’une erreur html, si la directive html_errors = On, toute erreur html redirige versle site officiel du langage html. On peut aussi rediriger vers une adresse locale en spécifiant les directives :

docref_root = ”http://AdresseExemple/manual/”docref_ext = ”.html”

La première directive spécifie le chemin de redirection (ne pas oublier le ”/” final) et la seconde donne lesuffixe du fichier ciblé.

NE JAMAIS METTRE html_errors = on en production

19.3.2 Gestion simple d’erreur par le code :

Pas dur, dès qu’on soupçonne une fonction ou une instruction de pouvoir produire une erreur.

146

Page 147: RelectureDePhp5_2011_01_29

La gestion d’erreur par condition de résultat de fonction La syntaxe la plus courante est la suivante :

If (!FonctionATester() ){

// instructions en cas d’échec}else{

// instructions en cas de réussite}

L’ennui de cette approche est qu’elle prend de la place et rend moins lisible le code.une solution est de passer par l’opérateur Or :

$ResultatFonction = FonctionATester() Or InstructionEnCasEchec() ;

Cette syntaxe est plus courte mais il faut définir une procédure d’erreur pour chaque fonction. Il est possiblede faire plus simple avec la fonction TriggerError() comme suit :

$ResultatFonction = FonctionATester() Or TriggerError(”Message d’erreur perso”, E_USER_ERROR) ;

Le niveau d’erreur spécifié ici n’est pas le seul possible. Voir la définition de la fonction trigger_error

• bool trigger_error ( string $error_msg [, int $error_type = E_USER_NOTICE ] ) : trig-ger_error() est utilisé pour déclencher une erreur utilisateur. Elle peut aussi être utilisée en conjonctionavec un gestionnaire d’erreurs interne, ou un gestionnaire d’erreurs utilisateur qui a été choisi comme ges-tionnaire d’erreurs avec set_error_handler(). trigger_error() est pratique lorsque vous devez générerune réponse particulière lors de l’exécution. A propos des paramètres :

– error_msg : Le message d’erreur désigné pour cette erreur. Il est limité en longueur à 1024 caractères.Tous caractères après les 1024 seront ignorés ;

– error_type : Le type d’erreur désigné pour cette erreur. Cela ne fonctionne qu’avec la famille deconstantes E_USER et sera par défaut E_USER_NOTICE.

Les constantes d’erreur de type E_USER sont :

– E_USER_ERROR (entier) : Comparable à E_ERROR qui indiquent des erreurs qui ne peuventpas être ignorées, comme des problèmes d’allocation de mémoire, par exemple. Elle est générée enPHP par l’utilisation de la fonction trigger_error().

– E_USER_WARNING (entier) : Comparable à E_WARNING qui indiquent un problème quidoit être intercepté par le script durant l’exécution du script. Par exemple, appeler ereg() avecune expression rationnelle invalide. . Elle est générée en PHP par l’utilisation de la fonction trig-ger_error().

– E_USER_NOTICE (entier) : Comparable à E_NOTICE qui indiquent indiquent que le scripta rencontré quelque chose qui peut être une erreur, mais peut aussi être un événement normal dans lavie du script. Par exemple, essayer d’accéder à une valeur qui n’a pas été déclarée, ou appeler stat()sur un fichier qui n’existe pas. Elle est générée en PHP par l’utilisation de la fonction trigger_error().

147

Page 148: RelectureDePhp5_2011_01_29

Personnaliser ses messages d’erreurs Deux directives de php permettent de personnaliser l’affichage desmessages d’erreurs PHP :

• error_prepend_string = ”INSTRUCTION HTML” : permet de faire précéder tout affichaged’erreur PHP d’une instruction html ;

• error_append_string = ”INSTRUCTION HTML” : permet de faire suivre tout affichage d’erreurPHP d’une insruction html ;

Les deux directives doivent être complémentaires car si la première contient une balise ouvrante, la deuxièmedoit contenir la balise fermante. Par exemple, pour que tous les messages d’erreur soient en rouges :

error_prepend_string = ”<font color=#ff0000>”error_append_string = ”</font>”

Journalisation des erreurs Le log d’erreur doit surtout être activé en production. Il est aussi utile en dévpour s’assurer de ne pas laisser passer d’erreurs...

En terme de gestion des erreurs, il faut vider régulièrement le log des erreurs au fur et à mesure qu’on lesrésoud. Par ailleurs, il faut aussi le recopier pour garder traces des erreurs passés ou ne pas le saturer en casd’un grand nombre d’erreur. La directive activant le logging des erreurs est log_errors= on

Le répertoire de sauvegarde du fichier de log peut aussi être personnalisé avec la directive :

error_log = Chemin/Vers/Repertoire/Du/Log/NomFichierLog.txt

On peut réduire le nombre d’erreurs affichés dans le log avec la directive :

ignore_repeated_errors = on

Dans ce cas, les erreurs répétés d’une même source sont ignorés. On peut aussi agréger les erreurs répétésde plusieurs sources en mettant dans php.ini la directive suivante :

ignore_repeated_sources = on

On peu aussi rediriger les messages d’erreur vers le log du système mais c’est franchement à éviter (V.page 506-510 du bouquin pour ça). Il faut utiliser successivement les fonctions define_syslog_variables()pour initialiser certaines constantes relatives à la journalisation système, openlog() pour ouvrir le log système,syslog() pou y écrire des messages et closelog() pour le fermer.

Envoi manuel de messages vers le log d’erreurs : On peut envoyer un message d’erreur vers le logdirectement en utilisant la fonction error_log() :

• bool error_log ( string $message [, int $message_type = 0 [, string $destination [, string$extra_headers ]]] ) :Envoie un message d’erreur à l’historique du serveur web, à un port TCP ou unfichier. En dehors de la chaine $message, les options de cette fonction sont :

– $message_type : Spécifie la destination du message d’erreur. Les types possibles de messages sont:

∗ 0 : est envoyé à l’historique PHP, qui est basé sur l’historique système ou un fichier, en fonctionde la configuration de error_log. C’est l’option par défaut.

148

Page 149: RelectureDePhp5_2011_01_29

∗ 1 est envoyé par email à l’adresse destination. C’est le seul type qui utilise le quatrième paramètreextra_headers.

∗ 2 n’est plus une option.∗ 3 est ajouté au fichier destination. Une nouvelle ligne est automatiquement ajoutée à la fin dela chaîne message.

∗ 4 est envoyé directement au gestionnaire d’identification SAPI.

– $destination : Cela dépend du paramètre message_type décrit ci-dessus.

– $extra_headers : Les en-têtes supplémentaires. Ils sont utilisés lorsque le paramètre message_typeest défini à 1. Ce type de message utilise la même fonction interne que la fonction mail().

RENVOIE TRUE en cas de succès ou FALSE si une erreur survient. ATTENTION la longueur de lachaone envoyable est déterminée par la directive log_errors_max_len de php.ini

Créer son propre gestionnaire d’erreurs Plutôt que de laisser PHP utiliser son gestionnaire d’erreur in-terne, on peut définir son propre gestionnaire d’erreurs ce qui revient à définir une fonction de rappel, par exempleMaFonctionGestionErreur et forcer PHP à l’utiliser avec la fonction set_error_handler(’MaFonctionGestionErreur’).

La fonction de rappel doit avoir le prototype suivant :

• function MaFonctionGestionErreur($niveau, $message, $fichier, $ligne) : Cette fonction DEVRA impéra-tivement :

– afficher les messages d’erreurs sur la sortie approprié ;

– enregistrer ses messages dans les logs erreurs ou dans les sorties appropriées ;

– s’occuper du filtrage dû normalement à error_reporting ou à l’opérateur @ qui sont désactivésquand un gestionnaire d’erreur personnalisé est utilisé ; Le filtrage automatique peut alors être pilotéavec la fonction error_reporting()

– faire un appel à exit(XYZ) avec le bon code erreur XYZ à chaque fois qu’on passe dans la fonctionpersonnalisé ;

Il est possible de réactiver le gestionnaire d’erreurs interne de PHP avec la fonction restore_error_handler()On peut aussi imbriquer plusieurs gestionnaire d’erreurs et en activer un suivant a partie du code où on se

trouve avec set_error_handler().

ATTENTION : En cas d’erreur fatales PHP du type, E_ERROR, E_PARSE, E_CORE_XXX, E_COMPILE_XXX,PHP stoppe automatiquement l’exécution et empêche la récupération de l’erreur par un gestionnaire person-nalisé, il faut donc bien penser son système de gestionnaires d’erreurs...

149

Page 150: RelectureDePhp5_2011_01_29

19.4 Les assertions

Gérer les erreurs générés par PHP n’est pas suffisant car sauf à contrôler systématiquement tout avec des syntaxesde contrôles (v gérer les erreurs pas le code), des failles logiques tenant à l’architecture objet apparaissentsans pour autant déclencher d’erreurs PHP. Par exemple, si dans mon univers objet, j’ai une variable réellereprésentant une corrélation ou une Recovery du capital d’un titre, le type double de PHP est beaucoup trppermissif car un double en PHP vaut, je simplifie, entre -∞ et +∞ alors qu’une corrélation est toujours compriseentre -1 et 1 et une recovery est toujours comprise entre 0 et 1.

De ce fait, soit j’ajoute du code de contrôle à chaque calcul de variable pour m’assurer que le domaine dedéfinition théorique est bien respectée, soit j’utilise les assertions.

Les assertions sont nettement moins lourdes à gérer en terme de code.

Une assertion est une affirmation fonctionnelle et logique définie par le développeur : ”Une corrélation estcomprise entre -1 et 1” et ”Une recovery est comprise entre 0 et 1” sont deux assertions.

Une assertion est une structure de contrôle qui permet de vérifier la cohérence de la valeur d’une variableou d’un objet et de signaler quand cette cohérence n’est plus respectée.

19.4.1 Utilisation d’une assertion

Il faut utiliser une assertion uniquement pour valider une affirmation qui ne doit pas pouvoir être fausse. C’estdonc une mécanique de sécurité qui ne doit pas faire partie de la logique applicative. Par exemple, supposonsque j’étudie la corrélation Spot/vol d’une action, je m’attends à ce que cette correl soit négative, cependant ellepeut ne pas l’être, contrôler sa négativité avec une assertion n’a alors pas de sens car la positivité, quoique trèspeu probable, est possible. Par contre, contrôler que la valeur absolue de la corrél ne dépasse pas strictement 1a du sens.

Les assertions et les données externes : Si une donnée est fournie par l’extérieure, une valeur impossible estpossible soit parce que l’utilisateur s’est trompé soit qu’il est malveillant. Donc dans le cas de récupération dedonnée externe, NE JAMAIS UTILISER une assertion. Il faut utiliser des structures de contrôles classiques detype if ... elseif...

Pour être clair, une assertion s’occupe des valeurs impossibles potentiellement générés par son code. Et uncode clean doit donc fonctionner avec ou sans assertions.

Pour résumer :

1. Il est recommandé de n’utiliser les assertions que comme outil de déboguage. Vous pouvezles utiliser pour les vérifications d’usage : ces conditions doivent normalement être vraies,et indiquer une erreur de programmation si ce n’est pas le cas. Vous pouvez aussi vérifierla présence de certaines extensions ou limitations du système.

2. Les assertions ne doivent pas être utilisées pour faire des opérations de vérifications enproduction, comme des vérifications de valeur d’argument. En conditions normales, votrecode doit être en état de fonctionner si la vérification d’assertion est désactivée.

150

Page 151: RelectureDePhp5_2011_01_29

19.4.2 Définir une assertion

Une assertion se définit avec la fonction assert comme suit :

assert(”ExpressionLogiqueRenvoyantUnBooleen”)

Par exemple, pour une corrélation contenu dans une variable $Correl, ça donne :

assert(” Correl<= 1 && Correl >= -1”);

• bool assert ( mixed $assertion ) : va vérifier l’assertion $assertion et prendre la mesure appropriéesi le résultat est FALSE. $assertion peut être une chaine contenant une expression logique PHP sur desvariables PHP. $assertion peut aussi correspondre à AUTRE CHOSE (voir la fonction assert_options).

19.4.3 Activer ou désactiver les assertions

Coder avec des assertions ralentit d’autant plus le code qu’il y a d’assertions. Cependant une fois le code bétonnéet sécurisé, il est possible de se passer des assertions sans avoir à les supprimer ou à les mettre en commentairesune à une, simplement en les désactivant soit avec :

• la fonction assert_options() : mixed assert_options ( int $what [, mixed $value ] ) : permet demodifier les diverses options de la fonction assert(), ou simplement connaître la configuration actuelle.A propos des paramètres de la fonction :

– $what peut prendre les valeurs constantes suivantes :

∗ ASSERT_ACTIVE : Active l’évaluation de tous les appels à la fonction assert(). Correspondà mettre la directive assert.active ; (vaut 1 par défaut)

∗ ASSERT_WARNING : génère une alerte PHP pour chaque appels à la fonction assert()renvoyant FALSE. Correspond à la directive assert.warning ; (vaut 1 par défaut)

∗ ASSERT_BAIL : Provoque la terminaison de l’exécution en cas d’assertion fausse. Correspondà la directive assert.bail qui vaut 0 par défaut. Càd que quand on fait assert_options(ASSERT_BAIL,...)on passe la directive à 1 ;

∗ ASSERT_QUIET_EVAL : Désactive le rapport d’erreur durant l’évaluation d’une assertion.Correspond à la directive assert.quiet_eval. (vaut 0 par défaut) ;

∗ ASSERT_CALLBACK : Fonction de rappel utilisateur (callback function), pour le traite-ment des assertions fausses. Correspond à la directive assert.callback. (vaut NULL par défaut,càd pas de callback function définie par défaut).

– $value : Si ce paramètre est absent, assert_options ne fait que renvoyer la valeur courante de cequi est configuré ; Si le paramètre est présent, il doit valoir 0 ou 1 pour les quatre premières optionset NULL ou le nom de la fonction de callback à utiliser ;

• en jouant sur les directives de configuration de php.ini qui sont les directives cités dans la définition de lafonction assert_options() notamment la directive assert.active à on ou off pour activer ou désactiverles assertions ;

151

Page 152: RelectureDePhp5_2011_01_29

19.4.4 Ajouter un message d’erreur personnalisé à un alerte erreur généré par une assertionfausse

Il est possible d’incorporer un message d’erreur personnalisé à la chaine contenant l’expression logique del’assertion. ATTENTION : Le bouquin se plante sur le sujet puisque le bouquin propose la syntaxe faussesuivante :

assert(”EXPRESSION LOGIQUE PHP”); // Message d’erreur personnalisé

Dans ce cas, le message d’erreur étant mis comme un vrai commentaire, ça ne peut évident pas marcher sauf àimaginer que PHP puisse distinguer les commentaires qui suivent les appels de la fonction assert.

LA VRAIE syntaxe est la suivante :

assert (”EXPRESSION LOGIQUE /*Message d’erreur personnalisé*/ ” ) ;

Cette fois-ci, le message d’erreur personnalisé fait partie intégrante de l’instruction assert ce qui semble et estplus correcte...

CONSEIL SUR LES ASSERTIONS : Il est recommandé de ne pas utiliser de fonction utilisateur dansl’expression logique car les assertions ne doivent pas dépendre dans leur fonctionnement d’autre chose qued’expression simples et/ou que de fonctions utilisateurs éprouvé càd error-proof.

19.4.5 Utilisation d’un gestionnaire personnalisé d’assertions :

Pour cela, il faut :

1. activer la directive de callback :

assert_options( ASSERT_CALLBACK, ’NomFunctionAssertCallback’);

2. Ecrire la fonction de call back avec pour nom celui spécifié à l’étape 1 en l’occurrence NomFunctionAssert-Callback :

function NomFunctionAssertCallback( $script, $line, $message )

{echo ’You have a design error in your script <b>’, $script,’</b> : line <b>’, $line,’</b> :<br

/>’;echo ’<b>’, ereg_replace( ’^.*//\*’, ”, $message ), ’</b><br /><br />’;echo ’Open the source file and check it, because it\’s not a normal behaviour !’;/// Ajouter éventuellement un code pour/// journaliser les pbs d’assertion/// dans un fichier spécifiqueexit;

}$x = 3;assert(”is_integer( $x ) && ($x >= 0) && ($x <= 10); //* $x must be an integer value from 0 to

10” );echo ”0 <= $x <= 10”;

152

Page 153: RelectureDePhp5_2011_01_29

19.5 Les exceptions

Les exceptions permettent d’envoyer des messages d’erreurs plus évolués et sophistiqués.Dans PHP, toutes les exceptions sont des classes dérivant de la classe Exception. Cette classe contient quatre

données membres protected qui déterminent les quatres éléments d’un message d’erreur intégré dans une classedérivant de Exception :

• string $message : le message d’erreur sous forme littéral. ex :”L’erreur est du type machine etc...” ;

• int $code : Le code associé à l’erreur ;

• string $file : le nom du fichier de source où apparait l’erreur ;

• int $line : le numéro de ligne où apparaît l’erreur dans le fichier en question ;

19.5.1 Création d’une exception

Cela se fait en appelant le constructeur avec l’opérateur new et en passant comme argument le message d’erreurqu’on souhaite comme suit :

$MonException = new Exception(”Grosse erreur dans ma fonction”)

Un deuxième argument optionnel, le code d’erreur, peut être ajoutée (valeur par défaut = NULL). PHP secharge par ailleurs de renseigner le fichier $file et le numéro de ligne $line. 4 getteurs sont fournis de base dansla classe Exception :

• getFile() :

• getMessage() :

• getLine() :

• getCode() :

Créer ses propres classes Exception cela se fait en créant une classe dérivée de Exception. On peut alors

ajouter des données et méthodes membres qui vont gérer spécifiquement nos erreurs. Par exemple :

class PricerPlante extends Exception{

private $TypePricer;private $TypeModel;public __construct($message, $TypePricer, $TypeModel){

$this->TypePricer = $TypePricer;$this->TypeModel = $TypeModel ;parent::__construct($message);

}

153

Page 154: RelectureDePhp5_2011_01_29

Public function getTypePricer(){

return $this->TypePricer;}

Public function getTypeModel(){

return $this->TypeModel;}

};

Lancement d’une exception Créer une Exception n’a aucune incidence directe sur le fonctionnementcourant de PHP. Une instance d’exception est objet comme les autres tant qu’il n’a pas été lancé. On lancel’exception avec l’instruction native throw comme suit :

$ErrorPricer = new PricerPlante(”Le modèle de Pricing VolLoc a planté”, ”MC”,”EquityVolLocal”);

throw $ErrorPricer;

Une fois cette dernière instruction exécutée par PHP :

1. PHP récupère l’objet de type Exception et lui ajoute le numéro de ligne et le nom de fichier dans lequell’instruction thow a été utilisée.

2. Puis PHP saute au premier gestionnaire d’exception capable de gérer le type de l’instance d’Exceptionrécupérée.

Gestionnaire d’Exception Un gestionnaire d’exception est ni plus ni moins qu’un bloc de code de type :

try{

... Code classique susceptible de produire une erreur}

catch(TypeException $e){

... code exécuté dans le cas de l’apparition d’un erreur de type TypeException... dans le bloc de code classique

}

A SAVOIR : le code suivant un bloc try{...}Catch(..){...} est toujours exécuté.

154

Page 155: RelectureDePhp5_2011_01_29

Dans le cas du gestionnaire précédant, si le type d’erreur TypeException est Exception, toutes les exceptionslancées dans le bloc de code classique sont attrapées par l’instruction catch.

Cependant on peut vouloir affiner en utilisant plusieurs catch comme suit :

try{

... Code classique susceptible de produire une erreur}

catch(TypeException1 $e){

... code exécuté dans le cas de l’apparition d’un erreur de type TypeException1... dans le bloc de code classique

}catch(TypeException2 $e){

... code exécuté dans le cas de l’apparition d’un erreur de type TypeException2... dans le bloc de code classique

}catch(TypeException3 $e){

... code exécuté dans le cas de l’apparition d’un erreur de type TypeException3... dans le bloc de code classique

}

Bien entendu, les types TypeException1, TypeException2 et TypeException3 doivent alors tous être desinstances de classes dérivant de Exception ou directement de type Exception. (Dans ce dernier cas, si PHP nes’est pas déjà arrêté dans un bloc catch, il s’arrête automatiquement dans le premier bloc catch attrapant uneexception de type Exception).

PHP va alors passer dans le premier catch dont le type d’exception est du type de l’exception lancé.

19.5.2 Imbrication des gestionnaires d’exceptions

Les gestionnaires d’Exception peuvent s’imbriquer. Dans ce cas, si le gestionnaire fils n’est pas en mesured’attraper tous les types d’exceptions de son bloc try, c’est le catch du gestionnaire parent qui prend la relève.

Par ex :

try{

try

155

Page 156: RelectureDePhp5_2011_01_29

{

/// instructionsthrow new TypeExceptionB(...);

}

catch (TypeExceptionA){

/// Bloc1 d’instructions}/// Bloc2 d’instructions

}catch (TypeExceptionB $e){

/// Bloc3 d’instructions}/// Bloc4 d’instructions

Dans notre exemple ci-dessus, si une Exception de type TypeExceptionB est lancé, les blocs 1 et 2 ne serontjamais exécutés car PHP saute directement au premier catch adéquat, passant d’abord par tous les catch dugestionnaire d’exceptions fils puis passant par tous les catchs du gestionnaire d’exception parent.

19.5.3 Rejeter une exception

Cela consiste à utiiser une instruction throw dans un bloc catch. Cela signifie qu’on souhaite basculer la ges-tionnaire de l’erreur relancé au bloc catch suivant. Ce type de stratégie consiste à dire que la gestion d’erreursdoit être distribué entre blocs de catch suivant qu’on souhaite ou non gérer l’exception dans le premier catchoù elle a été attrapée.

19.5.4 Gestionnaire d’exceptions par défaut

156