Vous êtes sur la page 1sur 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
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 <? si
la 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ès
par 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 ce
temps, le script s’arrête et le navigateur renvoie un message d’erreur. Si 0 est mis, le temps d’exécution
est 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és
par include. Il est possible de définir plusieurs adresses dans lesquelles Php cherchera successivement les
fichiers 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 ou


dans 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 script
php.

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


script 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 pour
le script la valeur de la directive ; la fonction ini_get(directive, valeur de la directive) permet de récupérer
la 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
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épertoir
ou 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 les
en-têtes html) ;

• en programme autonome : ça revient à la méthode ligne de commande en liant le type de fichier .php au
programme 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 blocs
commencent 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 code
php 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 la
ValeurDeLaVariable et peut changer suivant les affectations.

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

Les variables locales : ce sont celles définies dans un bloc interne au bloc principal <?php ?>. Par exemple
les 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 être
appelés dans un sous-bloc en utilisant le tableau $GLOBALS des variables globales qui permet d’appeler une
variable 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ée
en 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 peut
dès lors être dynamique et puisqu’un nom de variable peut lui-même être identifié à partir de la valeur d’une
autre variable. Ainsi $var1 est la valeur de la variable de nom var1. $$var2 est une variable dont le nom est la
valeur 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 une
accolade {}. exemple ${”va”.”r1”}; Cette déclaration est celle d’une variable dont le nom est la concaténation de
la chaine ”va” et de la chaine ”r1”

Constantes :
Les constantes sont déclarées avec l’instruction
define(”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
GetType($var1) : type de la variable var1.

is_string($var1); /// c’est clair


is_double($var1);
is_float($var1); /// pareil que la précédente
is_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 égaux
et 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, les
appels $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érer
le 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ères
ré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 pas
par \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 à la
place une clef qui est une chaine de caractères :

Tableau classique :

5
$tabclassic = array(10, ”element bidon”, 23.5); /// tableau dont l’élément 0 est un entier, l’élément 1 une
chaine 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’expression
de la valeur dans des accolades ET incorporer l’expression entre crochets dans une expression de chaine (càd
entre 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 tableaux


Pour 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 de
type 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 contenant
uniquement 0, le nombre entier ou réel 0 et un tableau vide. Tout le reste est considéré comme TRUE en cas
de 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 ressources
afficheront 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
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 un
opé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 variable
pour égalité. ça permet de blinder certains tests et évite notamment des égalités non souhaités en chaine et
nombre.

Opérateurs logiques : RAS à part l’opérateur XOR qui est le ou exclusif (soit l’un, soit l’autre mais pas
les 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
• l’instruction foreach permet de parcourir les élements d’un tableau. La syntaxe dépend de la nature
classique 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 du
bloc de la boucle pour passer au test suivant. Exemple : on génère 10 variables aléatoires, et on affiche le nombre
s’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’est
comme ç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 une
ré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
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 au
final 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. Pour
dé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 uniquement
figurer 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 de
n 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
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é dans
ph.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 symboliques
pour 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 mieux
require_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 au
niveau du fichier inclus alors que le require_once agit lui au niveau du fichier incluant et donc un require_once
d’un fichier suivi plus loin d’un require du même fichier fait planter. Il Faut donc utiliser require_once tout le
temps 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 un
signe + 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’à la
longueur requise. Ce peut être le caractère d’espace, ou le caractère 0. Par défaut, le remplissage se fait
avec des espaces. Un autre caractère de remplissage peut être spécifié en le préfixant avec un guillemet
simple (’) : ATTENTION si l’espace est choisi comme remplisseur, sur internet explorer, tous les espaces
contigus 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. Par
défaut, le résultat est aligné à droite(contrairement à ce que dit la doc de phpEdit). Le caractère - fera
que le résultat sera justifié à gauche ;
4. (ajout BG) un spécificateur du nombre minimum de caractères : si la longueur de la chaine en
argument est inférieur strictement à ce nombre, la chaine est complété à gauche ou à droite à l’aide du
remplisseur optionnel ;

10
5. Un spécificateur de précision qui indique le nombre de décimales qui doivent être affichées pour les
nombres à virgule flottante (ATTENTION c’est bien ici le nombre de chiffres après la virgule). Lorsque
vous utilisez ce spécificateur dans une chaîne, il agit comme un point de coupure, définissant une limite
maximale de caractères de la chaîne INITIALE passé en argument (ça signifie que la chaine est tronquée
de 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 se
produire car le php compte ensuite le nombre de caractères de la valeur tronquée de la variable pour savoir
s’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écision
représente le nombre de chiffres après la virgule depuis PHP 5.2.1. Dans les versions antérieures, il
a é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 un
nombre à 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 un
nombre à virgule flottante (ne tenant pas compte de la locale utilisée). Disponible depuis PHP 4.3.10
et 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 en
minuscules).
• X : l’argument est traité comme un entier, et présenté comme un nombre hexadécimal (les lettres en
majuscules).

Exemple :
% −0 # − 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 nombre
float sera de 5 caractères max.
ATTENTION quand on tronque une string (s), le 5 s’applique au nombre total de caractères tandis que
dans le cas d’un double, c’est uniquement le nombre de chiffres après la virgule qui compte ! Dans le cas d’un
nombre entier ou décimal, la troncature ne sert à rien.

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

11
arguments dynamiques %kekchose du masque doivent intégrer un numéro correspondant à l’ordre de prise en
compte. 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 par
défaut de WampServer 2. Tous les fonctions natives ci-dessous sont alors appelables en version mbstring en les
pré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 dans
une variable ;

• sscanf : permet de faire l’inverse de sprintf puisque suivant la string paramétrée ou masque, on récupère
les morceaux distincts. Cette fonction renvoie un tableau et est donc récupérable via la fonction native
list() ; Le prototype est :
sscanf(texteAParser,”masquedeparsing”)
rappelons qu’un exemple de parsing est ”%s %d” qui correspond à une chaine, un espace puis un nombre
décimal. ATTENTION quand le masque comporte une chaine (càd %s), la lecture de la chaine s’arrête
uniquement sur un espace et ce quelle que soit les caractères suivant %s dans le masque de saisie. Ce qui
n’est pas vraie pour les autres types de variables. Ex : Avec le masque ”%02d-%02d-%04d”, sscanf renvoie
les 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 et
non à 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 un
décompte des caractères car certains caractères comme é sont codés sur deux octets au lieu de 1 et compte
donc 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, la


fonction renvoie simplement le nombre de mots dans la chaine, sinon avec une valeur de 1, elle renvoie
un tableau de chaines contenant chacun des mots, et si elle vaut 2, pareil mais l’indice dans le tableau de
chaque 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 chaine
cherchée. ATTENTION renvoie FALSE si la chaine n’est pas trouvée. Si une chaine est trouvé en position
0, il faut quand même d’abord utiliser le comparateur === pour éviter de confondre 0 et FALSE si la
chaine 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 NE


contenant PAS le caractère spécifiée ;

12
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 fait
par 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ées
de la façon suivante : ”a..b”

• Les fonctions stripslashes et stripcslashes font exactement l’inverse des deux fonctions respectives
précédentes sauf que stripclashes ne prend pas de liste de caractères à rétablir. Elles retirent les ’\’ en
convertissant 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, la
couche 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’affichage
final du code HTML, ce sont les caractères spéciaux de l’HTML qui le seront (il seront alors convertis en entités
HTML 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ères
accentué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 les
caractères php qui ont un équivalent entité en HTML. La différence avec la fonction htmlspecialchars est
uniquement visible dans le code source HTML. A l’affichage dans le navigateur (càd une fois le code HTML
exécuté) est identique entre les deux fonctions. exemple : la chaine

”<br>pété”

13
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 sorte
que par exemple :
html_entity_decode(”&lt;br;&rtpété”)=”<br>pété”
A noter que la fonction get_html_translation_table permet
Suppression de balises HTML :

La fonction striptags(chaineHTML, ChaineListeDesBalisesARetirer); permet de retirer les balises HTML


n’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 majuscules
suivant les règles de grammaires locales (ex : les rosbeefs transforment toutes les premières lettres de
chaque 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 fonctionnelle
est utilisée. Si une seule chaine ”0” est passée, c’est la variable d’environnement LANG qui sera utilisé pour
spécifier la localisation. Si une chaine vide est passée, c’est la valeur courante utilisée qui sera renvoyé.

Quelques localisations :
Primary
language
Sublanguage
Language string
Chinese 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
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 dont
le €. 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 site
web 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 langages
humains suivant le jeu de caractères considérés. par exemple : le jeu de caractères US qui comporte
les 26 lettres de l’alphabet, les 10 chiffres et quelques autres caractères comme ’ ” (, ) etc. Le jeu le plus
petit est justement ce dernier jeu de caractères et le plus grand est l’unicode qui contient pratiquement
tous les caractères connus des langues humaines ;

15
• L’encodage : c’est la traduction informatique d’un jeu de caractères. A la base, les premiers encodages
de jeux de caractères étaient tels que chaque caractère était codé sur un octet. Cependant pour les jeux
de caractères les plus larges, les 8 bits d’un octet étaient insuffisants et donc certains encodages utilisent
jusqu’à 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 latins
comme 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 pour
particularité d’englober notamment l’encodage ISO-8859-15 qui est l’encodage des caractères occidentaux
de sorte qu’un caractère occidental est codé sur un octet en UTF-8. Cette taille variable du caractère
encodé 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
– 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
– 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é et
utilisé au sein d’un fichier qui est lui encodé potentiellement autrement. Pour éviter les problèmes, il faut
alors :

– 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 va
y avoir perte d’informations et une fois affichés les caractères non identifiés apparaaitront comme
repré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 de
l’encodage des jeux de caractères :
http://fr.wikipedia.org/wiki/Codage_de_caract%C3%A8res
http://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 se
corse:

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 blanc
dans un lien vers une solution correcte (%20). Pour les autres caractères: si vos noms de fichiers dans le texte
sont 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
L’encodage d’un fichier peut-être choisie en l’ouvrant avec le bloc-notes puis en choisissant Enregistrer sous
puis 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.html
http://www.tuteurs.ens.fr/faq/utf8.html
Fonction 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 chaine
UTF-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 (celui
donné 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 chaine
l’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_coding
renvoie 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.
f
Si la page affiche des caractères de ce type : ” ” (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 fonction
existe 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
• (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 correspondance
d’éléments (élem1 du tableau 1 remplacé par élém1 du tableau 2, etc...). Enfin si un quatrième argument
est passé, il est modifié par référence et devient le nb d’éléments remplacés. Merci la surdéfinition en
C++...
• substr_replace(ChaineAChercher, ChaineDeRemplacement, Position) : Remplace à partir de
la 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, optional
STR_PAD_RIGHT ou STR_PAD_LEFT, STR_PAD_BOTH) : complète une chaine pour
que 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 la
chaine 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 argument
CoupureDesMots est setté à TRUE (false par défaut). La coupure n’intervient pour un mot alors que si
sa 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, les
majuscules 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 Tableaux


Espé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 qui
est passé en argument ;

20
• var_dump($Tableau) : pareil mais ajoute des infos sur les types des éléments du tableau ou de la
variable ;
• count($Tableau, optional COUNT_RECURSIVE) : donne le nb d’éléments d’un tableau. Pour
un 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 de
l’option COUNT_RECURSIVE renvoie le nombre d’éléments d’un tableau de tableau. L’ennui, c’est
que 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ée
est est un des éléments du tableau (si $VarCherchée est contenu dans un élément comme une sous-chaine
dans 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éments
du $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 en
prenant en argument un tableau par référence. On peut forcer le type de tri en comparaison numérique
en mettant SORT_NUMERIC (attention le classement des valeurs non-numériques est bizarre et la
doc phpedit considère que dans ce cas le résultat est imprévisible), ou en comparaison textuelle avec
SORT_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és
chiffre 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 fonction
de 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ème
argument est une chaine de caractère qui doit correspondre au nom de la fonction de comparaison qui
détermine le critère de comparaison.

21
• 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 valeurs
du tableau 1. En cas d’égalité, c’est les valeurs du tableau suivant qui déterminent l’ordre. Si les listes
de 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 tableau
passé 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 le
nombre 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ément
du 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ément
du nouveau tableau est de clef $clefIni et le tableau contient au maximum les NbMax-1 éléments suivants
du tableau $tab ;
• array_splice($tab, $Offset,optional $lenght, optional $tabReplacement) : fonction tordue dont
les subtilités me semblent pas d’une grande utilité mais bon... Quand $Offset est >0, la fonction supprime
du tableau $tab tous les éléments à partir de celui de position offset et renvoie un tableau qui correspond
au sous-tableau supprimé. Quand offset est <0, c’est pareil sauf que l’index est compté depuis la fin avec
le 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éments
du tableau à partir de l’offset sont préservés. Enfin quand un tabReplacement est spécifié, le sous-tableau
supprimé 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 simplement
insé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 ; CETTE
FONCTION 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 clefs
communes, le tableau fusionné ne comporte qu’une seule telle clef et c’est la valeur du dernier tableau
ayant cette clef qui est gardé...

• array_recursive_merge($tab1, $tab2,...,$tabn,...) : permet la fusion de tableau de tableaux, les


sous-tableaux ayant une même clef sont alors fusionnés deux à deux au lieu de voir le suivant écraser
le précédent ; IMPORTANT : Cela permet de pallier les écrasement de valeurs de tableaux associatifs
contenant tous la même liste de clef. 14h 34
• array_chunk($tab, $tailleSousTableau, Optional KeepKey) : Splitte un tableau $tab en un
tableau contenant autant de sous-tableaux de taille $tailleSousTableau que nécessaire pour contenir les
valeurs du tableau initial. L’option permet de savoir si les clefs sont renumérotés (keepKey = FALSE par
défaut) ou conservés. Le dernier est de taille plus réduite que $tailleSousTableau si le nombre d’éléments

22
est insuffisant (càd si le tableau initial a 7 éléments et qu’on demande un split en tableau de taille 3, il y
aura 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 pointeur
sous-jacent du tableau passe au premier élément du tableau (notamment faire gaffe quand cette fonction
est 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 le
pointeur sous-jacent du tableau passe au premier élément du tableau (notamment faire gaffe quand cette
fonction 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ébut
de 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 le
premier élément, end() le met sur le dernier élément du tableau, prev et next se passent de commentaires... Si
le 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 entier
ré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 un
affichage 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
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é en
cas 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 le
comportement 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 au
format de la date telle qu’on la souhaite. Exemple la chaine de format ”d-m-Y” formatte la date du 15 juin 2010
comme 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. A
noter 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 de
chaine formaté au format $format ;
• strtotime($dateISO) : convertit une date ISO ( par exemple ”d-m-Y,hh:mm:ss” mais ce n’est pas le seul
format accepté loin s’en faut) en timestamp UNIX ; ATTENTION certains formats pourtant courants
ne 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
• mktime(heure,minute, seconde,mois,jour, annee) : renvoie un timestamp en fonction des arguments
passés (un septième argument permettait de prendre en compte l’heure d’été et d’hiver mais cette version
de la fonction est deprecated ;
• strftime(format,TimestampUNIX) : fait pareil que strtotime mais prend en compte les paramètres
de 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 FALSE
sinon ;

• 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 un
fichier 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 domaine
internet, etc... ATTENTION : cette fonction ne marche pas sur Windows mais il est possible de créer sa
propre 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
Vous êtes ici : Accueil > Pas à pas > Comment utiliser la commande NSLookup
Publié le 30/06/2008 vers 13h par : Saïda AZIRI
NSLookup (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 besoin
de 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ée
depuis 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 enregistrement
en 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é. Pour
revenir sur les enregistrements de type A, tapez : Set-type = A ou fermer la fenêtre nslookup.
1.1 Set-type = MX
Recueillir des informations sur les enregistrements MX (serveurs de messagerie) :
> set type=MX
> gmail.com

26
* 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’ils
ne 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 serveurs
dans les 5 lignes précédentes).
1.2 Set type =NS
L’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
* 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’adresse
doit être de type user.exemple.lanet non user@exemple.lan.
* Serial : Il s’agit d’un numéro de série en 32 bit qui s’incrémente à chaque fois qu’une modification intervient
sur le serveur. Ce numéro est de type : YYYYMMDDnn (Année, mois, jour, version). (Changement intervenu le
24 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èse
est 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é, avant
d’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 comme
n’étant plus autoritative. Si la copie que le serveur détient est plus vieille que 28 jours, elle est considérée comme
invalide.
* Default TTL (Time To Live) = Nombre définit en seconde, qu’un enregistrement de zone est valide dans
le cache.
1.4 Set type =PTR
> Set type =PTR
> Adresse IP
Les 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’hote
Les 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ôte
DNS 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 =AAAA
Il est également possible d’obtenir des adresses IPv6 :
> set type=AAAA ou bien set type=any
> www.kame.net/

28
1.8 Set type =ANY
ANY 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 fonction
ne marche pas sous Windows ;

IL FAUT REVOIR LES FONCTIONS RESEAU !!!

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 passe
lui-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 prgm
que php ou pour l’afficher, il faut convertir cet entier signé en non-signé (du coup, la valeur non-signé peut
aller 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 fichier
et 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 le
hachage de mot de passe. affiche une chaine héxadécimale en sortie de 32 caractères. Si l’option $Raw est
mis à 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éatoire
pour rendre plus difficile la découverte du mot de passe. ATTENTION : l’algo interne est initialement
l’algo DES qui est pourri. Suivant le système, on peut forcer le recours aux algos md5 ou blowfish, en
utilisant respectivement une chaine $alea commençant par $1$ et comportant 12 caractères ou $2$ et
comportant 16 caractères ($x$ comptent les 12 ou 16 caractères) ; Si un password est bon, le code php
suivant :
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ématiques
de codage/encodage interviennent dans le cadre de la communication réseau. Or ces communications sont gérées
directement et de manière transparente quand les adresses URL commencent par https et quand les sockets
ré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
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 tableau
contenant 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éciaux
PHP 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érencement
entre certaines variables globales de PHP et les données de la page web. Idéal pour les flemmards mais dangereux
car l’utilisateur peut alors venir taper plus facilement le coeur de php et donc du serveur d’où un sérieux souci
de 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 HTML
8.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 fermantes
et le dernier car c’est le caractère spécial qui aide à définir une entité HTML (Les entités HTML permettent
l’affichage de caractères spéciaux).
Pour échapper cette caractère et permettre l’affichage tel quel ces caractères, il faut avoir recours à la
fonction 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
8.1.2 Les Formulaires HTML

Un formulaire HTML est créer à partir des balises <form> et </form>. Toutes les instructions délimitées par
ces 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 ne
figure 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 POST
envoie 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éthode
d’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 dans
la 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 à PHP
d’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 les
balises <textarea> et </textarea> ;

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


LigneP arDéf aut1
...
LigneP arDéf aut2
</textarea>

• des cases à cocher : la balise correspondante est <input type =’checkbox’> et fait apparaître une
case à 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 les
cases qu’on souhaite lier.

32
• des boutons radio (groupe de boutons à cocher et à a choix unique exclusif) : cela fonctionne comme les
cases à cocher sauf que tous les boutons partageant un même nom sont à choix exclusif càd un seul d’entre
eux peut être cocher ; Une ligne peut être sélectionné par défaut : il faut ajouter l’attribut selected sans
lui 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électionnable
de 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 passer
une quelconque valeur (càd pas de =’kekchose’. Si l’attribut size est de valeur supérieure à ’1’, la liste
n’est plus déroulante mais une liste simple avec une barre de défilement vertical si le nb de possibilités
excè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 simple
refresh ne remet pas les valeurs par défauts.
ATTENTION : tout appel à un affichage au sein d’un bloc balise <select> </select> ne produit aucun
affichage !
• 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 navigateur
renvoie les coordonnées de la position du clic avec le formulaire. Note : les attributs height et width de la
balise 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 gestion
correspondants :

Chaque formulaire intègre un attribut action qui spécifie quelle action est éxécuté dès lors qu’une action de
validation est lancée. Ce lancement intervient dès lors qu’une balise de bouton appartenant au bloc de balises
du formulaire est cliqué. ça peut être aussi bien un bouton qu’un image cliquable. L’action déclenché peut-être
un 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
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 pour
ce dernier) ;

Chaque élément d’un tableau correspond à un des objets de saisie du formulaire à partir duquel les données au
script 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 de
la 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 autant
d’é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 nombre
de pixels, ces deux données ne pouvant donc respectivement pas excéder la taille spécifiée par les attributs
width et height ;
• Pour savoir, si une variable est présente dans les tableaux $_GET,$_POST etc. Utiliser la fonction
isset($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 une
balise <br>. Pour ça, après récupération d’une chaine dans un tableau de superglobales, si on souhaite afficher
derrière cette chaine et seulement dans ce cas, il faut convertir les caractères classiques ’\n’ de sauts de ligne
en balise HTML correspondante. Pour cela, l’affichage de la chaine PHP peut être fait en utilisant la fonction
nl2br($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’information
initiale n’est pas filtré des balises HTML. ATTENTION : Il vaut mieux filtrer (càd ) les balises contenus dans
les chaines entrées dans les formulaires.

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

Le hic avec les tableaux de var superglobales, c’est que si on connait certes les clefs et donc les noms des
variables, on peut avoir des surprises sur le type et le contenu de la valeur de chaque superglobales si on ne
filtre 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 clean


Les fonctions utiles de l’extension filter sont :
• filter_has_var($CONSTANTESOURCE, $ClefdelaSuperGlobale) : ça revient à faire un isset
sur le nom de la clef dans le tableau de superglobales désigné par $CONSTANTESOURCE. Les possibilités
pour $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 dans
le cas où la variable superglobale est unidimensionnelle et n’est donc pas un TABLEAU associatif (l’autre

35
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, ce
qui revient à cumuler les filtres. Dans ce cas, $FILTRE vaut plusieurs constantes entières séparés de | de
sorte 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 être
défini comme un tableau associatif (voir la fonction filter_input_array pour la spécification du tableau
associatif). 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 (mais
l’adresse peut ne pas exister par ailleurs) ; Pas de flag possible.
∗ FILTER_VALIDATE_URL : vérifie que la valeur correspond à une syntaxe d’URL ; Flags
possibles :
· 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 domain
name (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 rationnelle
compatible 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 et
flags 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 ; Pas
d’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 (mais
l’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:037
· * FILTER_FLAG_NO_PRIV_RANGE - Requires the value to be a RFC specified private
range IP (like 192.168.0.1)
· * FILTER_FLAG_NO_RES_RANGE - Requires that the value is not within the reserved
IP range. This flag takes both IPV4 and IPV6 values
– Les filtrage de conversion qui peuvent modifier la valeur en entrée :

36
∗ 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 trouver
dans une adresse mail (ce qui veut dire qu’il laisse notamment $-_.+!*’{}|^~[]‘#%/?@&= ;
∗ FILTER_SANITIZE_SPECIAL_CHARS : transforme les caractères spéciaux HTML et XML
en entités HTML càd This filter is used to escape ”<>& and characters with ASCII value below
32 ; 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 avec
un 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 une
expression mathématique. Il faut comprendre que les filtres prennent des chaines de caractères
en 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 avec
un 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’une
adresse URL ; This filter allows all letters, digits and $-_.+!*’(),{}|\\^~[]‘”><#%;/?:@&= ; Pas
de 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 sur
les caractères spéciaux propres au PHP, càd ’ ” \ ) et opère un échappement des caractères
empê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
}
$string = ”Peter is a great guy!”;
echo filter_var($string, FILTER_CALLBACK,
array(”options”=>”convertSpace”));

Résultat : P eter_is_a_great_guy!

A noter que la fonction passé en argument peut être une fonction php dès lors qu’elle renvoie
une 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 clefs
recherché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 des
milliers : 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
• 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’on
souhaite filtrer une chaine contenu dans le tableau $_POST avec pour clef str et qui doit correspondre à un
entier 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 la
ré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_


”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 clefs
Exp 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
”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 pour
toutes 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_Pattes
qui 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ées
ne 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’est
FALSE 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 enctype
spécifiant le type d’encodage. . L’interface se définit avec une balise :

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

40
Les fichiers sont alors acessibles en php via le tableau de superglobales $_FILES. Ce tableau est à double
entré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 HTML
est 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ération
suivante :

– 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 que
le répertoire www des fichiers webs). IMPORTANT : Ce fichier est détruit à la fin de l’exécution de chaque
script. Pour conserver ce fichier côté serveur au de-là du temps d’exécution d’un script, il faut utiliser la fonction
move_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 par
dé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
• 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 les
usages suspects de fichiers
Pour se protéger de ses menaces, la question sera largement abordée dans le chapitre 27 ayant trait à la
sécurité.

8.5 Limite de temps d’éxecution d’un script PHP


Normalement dans php.ini, la directive max_execution_time qui est mesuré en s fixe une limite de temps
d’éxécution à tout script php. Cependant cette limite peut être repoussé en utilisant la fonction set_time_limit
SAUF php fonctionne en safe_mode qui interdit, entre autres, de manipuler les limites de temps d’exécution
des scripts.

• set_time_limit($RabDeTempsEnSec) : La valeur passée en argument permet d’augmenter d’autant le


temps d’exécution restant pour le script, et ce quelque soit la valeur de max_execution_time ; Si le
max_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 25
s alors qu’il restait normalement 15 s. Le temps maximal disponible pour l’exécution du script est donc
passé 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 fonctions
system 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ée
en 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 champs
foireux et les données passées correctes pour les autres champs

42
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 tout
appel à une fonction d’affichage implique que le bloc d’en-tête est terminé. A partir de ce moment, il n’est plus
possible 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 relation
client-serveur associé à une page web :
• $_SERVER[] : qui regroupe toutes les informations concernant la réception d’une requête auprès du
serveur 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 structure
une en-tête de page HTML. C’est compliqué. Un lien vers un doc spécifiant comment fonctionne
une en-tête est disponible dans la définition de la fonction header() de l’aide PHPEdit (ou sur
php.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 le
navigateur 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 $_SERVER
sont :

• $_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és
cotés client ;
• $_SERVER[’HTTP_HOST’] : Toto.localhost dans mon cas. Correspond au nom de l’hote serveur (virtuel
ou pas) avec lequel communique le navigateur ;

• $_SERVER[’HTTP_USER_AGENT’] : indique quel est le système d’exploitation du côté client ainsi


que 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’explication
du site php.net ;

43
• $_SERVER[’HTTP_ACCEPT_LANGUAGE’] : Contenu de l’en-tête Accept-Language: de la requête
courante, 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ête
courante, si elle existe. Par exemple : ’gzip’. ????

• $_SERVER[’HTTP_ACCEPT_CHARSET’] : Définit les jeux de caractères acceptés coté client. chez


moi 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 elle
existe. 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érant
l’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 :
0
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 header
avec le même NomDeLaVarEntete et le même DomaineEnTete dans $ChaineVarEntete fait que la valeur
de 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 HTTP


Elle 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 demander
l’identification à l’utilisateur :

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

44
avec : $titre le message qu’on souhaite passer à l’utilisateur au moment de l’identification. La fenêtre
d’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-dessus
provoque le renvoi d’une requête au serveur web et donc le code php qui suit N’est PAS exécuté. Pour
que 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é. On
peut ainsi définir une fonction de vérification d’identité qui encapsule cette instruction. On pourrait par
exemple 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. La
superglobale $_SERVER[’PHP_AUTH_USER’] n’existe plus et c’est la variable superglobale $_SERVER[’PHP_REMOTE_
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 qui
passe 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’est
pas suffisante pour identifier quelqu’un car ces adresse peuvent changer avec les connections/déconnexions de

45
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 vue
que 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 sont
acessibles avec $_SERVER[’SERVER_ADDR’] et $_SERVER[’SERVER_PORT’] . A noter que si
on 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 sites
et 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 TRES
importante 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 le


cas d’une communication par la méthode GET dans un formulaire. Ces informations suivent alors le
point 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 respectives
sont Valeur1 et Valeur 2. La variable $_SERVER[’REQUEST_URI’] contient donc par
rapport à l’adresse URL précédente :

/CheminDeLaPageVue?variable1=Valeur1&variable2=Valeur2

46
– $_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érer
un tableau de toutes la variables passés en arguments de la requête URL. Les noms et les clefs de
chaque 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 premier
tableau 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 et
donc quand les valeurs de variables sont passés dans l’URL, les caractères non-alphanumériques sont
encodées en url càd avec une syntaxe de type %xx. Pour récupérer dans une variable, des valeurs
encodés, on utilise la fonction urldecode. De même façon pour encrypter des variables et leurs
valeurs 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 les
espaces apparaissant dans une chaine et qui sont retranscrites avec le caractère ’+’ dans l’URL ne
sont 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 sont
les noms des variables et les valeurs associés sont leurs valeurs correspondantes. Comme toute bonne
fonction 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 de
variables et leur valeur. Truc con : dans le cas de ce premier usage, il faut connaître à l’avance
le 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 et
structurer l’ensemble des fichiers de script HTML et php. Dans ce cas, la partie virtuelle du chemin
est contenu dans $_SERVER[’PATH_INFO’].

• Les informations fournies par le client : Ces informations concernent le client : la page dite
ré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 renvoie


donc aussi la dernière page du server qui précéde la page courante si le client était déjà sur le site du
serveur à la page précédente ; La page référente est une information par le client qui indique par
quelle dernière page est passée le client avant d’accéder à la page fournie par le serveur. Cela permet
notamment de savoir par quelles pages externes, un client est parvenu jusqu’aux pages fournies par
le serveur. Cela peut servir à aiguiller le client si la première page auquelle arrive le client sur celles
fournies par le serveur n’est pas adéquate notamment quand le client déboule sur le site en ayant
cliquer sur une url copié-collé sur un autre site. Evidemment après une refresh de la page, cette
superglobale ne prend pas la valeur de la page courente. ATTENTION : comme toute information
fournie 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 le
tableau superglobale $_SERVER servent à indiquer quelles types de pages sont acceptées par le
navigateur du client. Les variables superglobales correspondantes sont :

47
∗ $_SERVER[’HTTP_ACCEPT’] : permet de spécifier les préférences du client en matière
de format de fichier web acceptés par son navigateur : XML, XHTML, HTML etc. en affectant
une priorité entre chaque format (la priorité max étant 1 sur une échelle allant de 0 à 1) La
chaine 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 la
priorité y, le format Format3 avec la priorité z et ainsi de suite. Quand la priorité n’est pas
spé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 autres
types 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’utilisateur
cô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érence
entre 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_CHARS
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, le
navigateur 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 la
superglobales $_SERVER[’HTTP_USER_AGENT’]. Cela a surtout pour utilité d’identifier si le
navigateur client est un navigateur de pc, de mac, d’ipad, iphone ou autre et permet donc d’adapter
son 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 à ce
stade, une boucle sur $_ENV ne renvoie RIEN !!!

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

48
• $_SERVER[’SCRIPT_NAME’] : donne l’adresse relative du script en cours d’utilisation à partir du
ré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 autonomes


Autonomes car n’appartenant pas à un tableau superglobales. TOUTES ces superglobales ont un nom de variable
qui 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 cookies


Un 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 au
client et utiles au serveur.

Par ailleurs, en php, chaque cookie associé au nom de domaine de la page est stocké dans le tableau
superglobale $_COOKIE.
Maintenant du fait que les cookies sont crées côté client. AUCUNE information critique ne peut être
raisonnablement stockés côté client. Càd que toute information pouvant mettre en jeu la sécurité du client ou
l’intégrité du serveur ne peut être stocké sous forme de cookie. Par ailleurs, la taille des cookies est généralement
limitée par les navigateurs eux-mêmes ( généralement 4 ko) et leur nombre par domaine est souvent limité (20
max 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 de
cookie ;
• des cookies ne contenant pas d’information joue sur des paramètres critiques du serveur (genre le prix du
clic comme une régie publicitaire avait pu le faire) ;

49
• des cookies contenant des informations confidentielles du client vu que ces mêmes données sont alors
potentiellement accessible par tiers (par exemple, le cas le plus simple d’une même machine utilisée par
plusieurs personnes) ;
• des cookies qui ne sont pas indispensables au bon échange entre client et serveur car certains navigateurs
ou certains clients peuvent limiter ou carrément interdire l’usage des cookies. De ce fait, si les cookies sont
conç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 simplifier
la vie de l’utilisateur ;

• faire des statistiques à destination du serveur notamment par exemple stocker la dernière date de connexion
du client au serveur. Du fait de la loi des grands nombres et à l’aide d’un contrôle des valeurs, le bidonnage
par 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 des
fonctions de hachage et de cryptage vu auparavant. Attention cependant à ne pas se servir de cet argument
pour 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 servir
de 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, le
cookie 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 clair
mais sur firefox on peut accéder aux cookies associés à une page donnée en faisant Outils => Informations sur
la 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 fonction
setcookie().
Autre remarque à propos de la rédaction du chapitre 10 : Ce chapitre omet de parler de la gestion du
buffering ou de la mise en tampon des scripts php. Cette question de la mise en tampon est seulement abordée
au 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) et


AVANT tout appel à une fonction d’affichage HTML ou tout simple ligne vide du bloc d’en-tête qui compte
comme 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
Impact de l’activation du buffering sur l’utilisation de setcookie : Le buffering est une option
pré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 doivent
strictement être faits dans le header càd le bloc head de la page html et sans appel d’une quelconque
fonction 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 serveur
web et donc ensuite au client. La taille de la page mise en buffer n’est alors pas limité et les appels de
setcookie 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 dans
le buffer avant d’être envoyé au serveur par php. Ainsi dès que la limite du buffer est atteinte, le bout de
page 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 buffer
toutes 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 ce
soit 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 du
tampon 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 la
chaine stockée dans le buffer avant son envoi au client ; NOTE : il est possible d’imbriquer plusieurs
tampons à 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. Il
faut passer obligatoirement par la fonction setcookie.
ATTENTION : $_COOKIE est le tableau des valeurs de cookies envoyés par le client. Il est impossible de
modifier sa valeur dans un bloc de code php sans passer par un renvoi de header par le client, ce renvoi contenant

51
une instruction setcookie($NomCookie, $NouvelleValeurCookie). Inutile donc d’essayer d’incrémenter la valeur
d’un cookie dans une boucle, ça ne changera pas sa valeur... Il faut passer par setcookie appelé dans un header
de 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é au
cookie qu’on souhaite éliminer avec pour seul changement passer en valeur une chaine vide ” ” ou ”. Dans
le bouquin, il est dit que seul setcookie($NomDuCookie) mais cela ne semblait pas bien marcher quand le
output_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 date
d’expiration à un cookie, il faut utiliser l’appel suivant :

setcookie($NomDuCookie,$ChaineValeurCookie,$TimestampDateExpiration)

avec $TimestampDateExpiration un timestamp Unix donnant la date d’expiration du cookie


Stockage 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 faut
dé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 alors
considé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’un
seul 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 en
utilisant 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
On peut restreindre l’existence d’un cookie à un domaine donné et /ou à un répertoire (et donc tous ses
sous-répertoires et fichiers) et surtout si le cookie est installé uniquement en connexion sécurisée (càd en SSL
via 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 limite
les ATTAQUES de type XSS. ATTENTION : certains navigateurs refuse l’usage de cette option...

11 Chapitre 11 : Les sessions


A l’inverse des cookies, une session est un ensemble d’information contenues uniquement côté serveur et permet
de stocker des informations sur le client. A un client du serveur va donc correspondre une session donnée. C’est
avec une session que l’information critique peut-être stocké et géré. Une session est crée à chaque connexion du
client 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 interaction
avec 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 à $_COOKIE
qui est n’est manipulable qu’à partir de la fonction couteau-suisses setcookie(). ATTENTION : dès lors qu’une
session 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 mais
est 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 le
fichier de session via le tableau superglobale $_SESSION doit ensuite être sauvé sur un fichier plus pérenne sur
le serveur par exemple sur une base de données.

53
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 au
client 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 la
session de navigateur est fermée ; A noter que la durée de vie du cookie est indépendant de celle du fichier
de 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 parce
qu’il incorpore le cookie de session PHPSESSID.

Remarque : La directive php session.auto_start mise à 1 fait que toute page web contient automatiquement
en première instruction session_start(). IL FAUT EVITER CELA car cela empêche une gestion fine des sessions
et 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 nouveau
recours à session_start() va rouvrir la session passé et accéder aux variables superglobales correspondantes
de $_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’est
passé à 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 éventuels
modifications 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
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’est
pas 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 changement
de nom du cookie ne sera valide que pour le script courant. Les autres scripts utiliseront le nom par défaut
PHPSESSID. Enfin il faut appeler cette fonction AVANT session_start() pour que ça prenne effet. La valeur
par 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 dans
un 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 SAUF
que le changement de chemin de sauvegarde est permanent pour la session en cours. A noter que la session par
dé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 informations
confidentielles...
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 suffixe
type .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 variable
unset($_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 trop
bourrin, $_SESSION devient totalement inacessible. La solution est de lui affecter un tableau vide en valeur
comme suit :
$_SESSION=array();
Attention :
$_SESSION= 0;

55
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 fonction
php de lecture de fichiers (on verra ça au chapitre 13). Le contenu du fichier correspond alors au tableau
superglobale $_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ésence
d’une restriction à la connexion sécurisée type SSL et restriction d’accès au cookie via langages de script par le
client) 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 script
dans lequel il est appelé, et par ailleurs, il faut impérativement l’appeler avant l’instruction session_start(). La
solution pour rendre ”permanent” est d’appeler session_set_cookie_params pour chaque script et avant chaque
appel à 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
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’en
choisir 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 serializer
plus 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. Les
autres scripts n’auront alors pas du tout accès normalement au fichier de sesson ce qui peut empêcher le bon
fonctionnement 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 à $_SESSION
et au fichier de session en lecture seulement ; ATTENTION CETTE FONCTION NE SEMBLE PAS
EXISTER !!

• session_write_close() : permet de fermer d’enregistrer les données de $_SESSION dans le fichier de


session et de fermer la session, ce qui la rend dispo à d’autres scripts php. Autant dire qu’avant tout gros
calcul, 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 plusieurs
sessions
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 modifications
ne seront pas prises en compte) aux variables de l’ancienne session. $_SESSION ne sera
vidé 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 du
protocole HTTP et donc n’a pas besoin de php et du serveur. Le serveur fait produire la page par php qui écrit
en 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
• ”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 date
d’expiration pour la mise en cache de cette page ; Bien réfléchir à quelle page aurait ce type de mise en
cache. 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 et
d’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 de
session 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’âge
en 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_maxlif etime = 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 tableaux
superglobaux sont considérés comme des variables et donc $_SESSION[’login’] est équivalent à $login et donc
l’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 leur
usage est donc proscrits !

58
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 serveur
qui 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 est
techniquement possible d’intégrer l’id de session dans l’URL (v.page 249) cependant cela expose l’id de session
dans l’URL, ce qui n’est pas souhaitable en terme de sécurité. Si le client ne veut pas utiliser de cookie, en clair
tant pis pour lui ! La configuration en terme de directive php est alors la suivante :
session.use_trans_id =0
session.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 le
nettoiement des sessions les plus anciennes. Chacune de ces 6 opérations correspond à une fonction interne de
php 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 de
donné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 cas
d’accès multiples, il y a confusion des accès et donc altération des données suivant l’ordre des accès... Bien
sur, 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ère
plus 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 les
sessions 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érer
les 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 11
est inutilisable si le chapitre 17 (!!) n’a pas été lu et étudié auparavant. Ce bouquin est un peu écrit en dépit
du bon sens...

59
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 de
sauvegarde des sessions dans un répertoire à accès restreint est capital pour éviter que qui que ce soit
accè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 quand
l’utilisateur refuse les cookies est une très mauvaise chose car une copie malheureuse de lien, le fait que
l’URL de la page précédente est contenu dans la requête HTTP courante compromet la sécurité de la
session et de l’identité de son utilisateur. Bref pour s’assurer que ces problèmes n’apparaissent pas, il faut
setter :

session.use_trans_sid =0
session.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é pour
limiter 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 son
identité. Par exemple, si les infos sont très différentes d’une connexion à l’autre, on peut détruire sa
session 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 et
non 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 les
sessions ? 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 Php


Comme j’ai déjà vu ça en C++ et en java, on va juste passer en revue les grandes lignes de la programmation
objet 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
12.1.1 Structure d’une classe :

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

public $Force; /// les attributs se déclarent comme des variables


protected $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 classe
const 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 pas
const 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 mutateurs


public 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érer
leur lecture/écriture plus finement ;

61
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 faut
bien sur que l’existence de la classe a été déclarée pour faire le new. La bonne pratique est de faire l’include ou
le 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 instances
de classes étant des références, quelque part ça revient à considérer qu’on accède aux instances de classes comme
si 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 une
fonction 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 de
l’objet passé en argument ; Notons qu’on peut aussi bien passer un nom de classe en argument qu’une
instance 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 à la
classe dans lequel il est cité. Cité dans le code d’une classe abstraite, il se comporte comme une référence sur
une 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
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ègle
n’est spécialement requise. attention cette fonction DOIT renvoyer une chaine. Un exemple d’écriture de cette
fonction 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 attribut
Pognon privé et que $Toto en dérive, si le Pognon n’est logiquement pas accessible (par exemple en faisant
echo $Toto->Pognon), l’attribut Pognon peut-être crée en faisant $Toto->Pognon =NimporteQuoi; Bonjour
l’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 la
donné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 dans
cette fonction est appelée. L’idée est de comparer là aussi $nom avec les noms des attributs membres de la classe
de manière à affecter au membre la valeur $valeur. Notons que si l’attribut de nom $nom est aussi une instance
d’une autre classe, il faut contrôler la nature de la classe de $valeur avant de décider de l’affecter. L’affectation
peut se faire ensuite

63
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 à ce
que a devienne une référence de b (ATTENTION : si a était une instance d’une autre classe, a deviendrait aussi
une référence de b et donc la nature et le contenu précédent de b serait totalement perdu). Si on souhaite que
a 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 un
opé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’objet
copié, et EN AUCUN cas, la fonction __clone de la classe mère ou grand mère n’est appelée.Par contre, il est
possible 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 est
cloné, 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 nature
de 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 des
objets, 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 appeler
explicitement 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 donnant
pas 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
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 destructeur
pour un objet donné si cet objet n’est plus référencé ou si la fonction unset est appelé sur la variable. Du fait du
garbage 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 classe
dé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’Interfaces
qu’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 obligatoires
contraint 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ètres
possibles (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ètres
possibles est infini) ;
– Le nombre de paramètres possibles de la classe dérivée doit être supérieure ou égal au nombre de
paramè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 est
renvoyé (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ée
doit pouvoir se manipuler strictement comme une instance de la classe mère. Notamment tous les usages
de 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ée
de la classe de nom $className ;
• get_parent_class($objet) : renvoie le nom de la classe mère de $objet si elle existe.

65
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 de
NomDeLaClasse::NomFonction(...)

Autrement l’accès aux méthodes et aux classes peuvent être précisés avant la déclaration de la méthode ou
de l’attribut. par défaut, l’accès est public. Comme en C++, les trois types d’accès sont public, protected et
private et fonctionnent comme en C++ sauf qu’il n’y a pas de spécificateur d’accès pour les classes qui sont
toutes 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 classes
dé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 en
l’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 classe
dérivée avec un spécificateur plus réduit. une méthode public ne peut être dérivé que public, une protected
devient 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éer
une méthode de même nom propre à la classe dérivée. (Evident non ? Bizarre que le bouquin se prenne le
chou 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 est
possible et (FORTEMENT recommandé) de typer les arguments d’une fonction dès lors que l’argument est un
objet. ca permet d’éviter de faire des contrôles basés sur instanceof à tout bout de champ en spécifiant le type
de chaque argument d’une fonction.
une fonction FaireCalcul($a,$b) a deux arguments non typés tandis que la définition suivante
FaireCalcul(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 classes
propres.

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 le
spé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
12.1.19 Interfaces

Les interfaces fonctionnent en php comme en java. Ce sont des listes de fonctions correspondant à une même
unité 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 avoir
qu’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éthodes
d’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’autres
interfaces...

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 4
types 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::FonctionAppelee
dè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
12.1.24 autochargement des définitions de classe :
ça se fait en définissant une fonction __autoload($NomClasseACharger). On encapsule alors une instruction
require_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 fichier
contenant la classe requise.
Par exemple, supposons que le nom d’une classe soit structuré comme suit ”C_Prefixe_NomClasse” avec
le prefixe étant le type abstrait, interface, final ou normale d’une classe, le code suivant permet d’appeler les
classes 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 statique
avec 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 require
des 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 de
la 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’est
pas déclaré, même si aucun appel n’est fait à l’objet, celui-ci tel qu’enregistré dans $_SESSION perdra sa classe
pour devenir __PHP_Incomplete_Class_Name.

La principale difficulté de la gestion des classes avec les sessions est quand des instance de classes sont
liés à des ressources externes type fichiers, connexion réseau et fichier DOM (Document Object Model?). En
effet, quand l’instance de classe est sauvé dans le fichier de $_SESSION, une photographie de ses valeurs est
sauvegardé, or si d’autres modifs affectent le lien de l’instance avec les ressources externes, la modif du lien n’est

68
pas prise en compte tant que l’instance n’est pas à nouveau sauvé dans $_SESSION. Par ailleurs, la fermeture
ou le refresh de la page web conduit au reload des objets tels qu’ils ont été sauvé la dernière fois en session, et
non suivant leur dernier état avant le refresh ou la fermeture. Pour gérer ces problèmes, php permet de définir
deux 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 votre
script lors de l’utilisation de serialize() et de unserialize(), ces deux appels étant encapsulés dans les instruction
de sauvegarde et de lecture dans $_SESSION :

LA fonction __sleep DOIT renvoyer un array dont chaque élément est une chaine de caractère correspondant
chacune au nom EXACT d’un attribut de la classe. Si une classe contient trois membres $a, $b et $c et que la
fonction __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(). Le
prototype 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, il
peut ê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. Si
l’appel de fonction comporte x arguments, le tableau $ArgumentsFonction les intégrera comme éléments dans
l’ordre d’appel de la fonction.
NOTE : En fait, __call est une sorte de fonction template...

69
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 native
Iterator. De ce fait, une instance de la classe en question devient manipulable dans une boucle foreach comme
suit :

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 la
mesure où cela contraint la structure de la classe. il est possible de faire plutôt implémenter la classe Iterator
Aggregate. La classe doit alors seulement fournir l’implémentation de la fonction GetIterator() qui doit renvoyer
un objet de type Iterator, càd un objet implémentant seulement l’interface iterator.

70
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émentation
des 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 outils
de 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 et
qui 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 possible
de 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
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 de
la 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 pas
avoir 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 mot
clé ’classe’ ou ’interface’ dès lors que le commentaire est encadré par /** au début et */ à la fin du
commentaire ;
• getConstructor() : Renvoie une instance de ReflectionMethod sur la méthod __construct de la classe
ciblée ;
• hasMethod ($name) : renvoie true si la méthode de nom name existe dans la liste des méthodes de la
classe ciblée ;
• getMethod ($name) : renvoie une instance de ReflectionMethod sur la méthode de nom name de la
classe ciblée ;
• getMethods(Optional $filter) : renvoie un tableau de ReflectionMethod avec autant d’éléments que
de méthodes définies dans la classe ciblée. Le filtre $filter est une combinaison possible de un ou plusieurs
des 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’utilisation
de 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 statiques
uniquement ;
• hasProperty($name) : renvoie true si l’attribut de nom $name existe dans la classe ciblée ;

72
• getProperty($name) : renvoie un instance de ReflectionProperty si la méthode de nom $name existe
dans la classe ciblée. ATTENTION dans le cas contraire, une erreur fatale est produite ;
• getProperties(optional $filter) : renvoie un tableau de ReflectionProperty correspondant aux attributs
de 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. Plante
fatal 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 qui
préfixe les déclarations d’attributs, de membres, de function ou de classe (type : abstract protected pour
une 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 nombre
variable 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 comme
argument 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 classe
parente 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 statique
de 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 classe
ciblée ainsi que leur nom en clef en face ; (est par défaut un attribut qui est déclaré dans la classe. Un
attribut 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
• 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 si
la 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 la
fonction 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 contenu
dans une string ; La valeur de flags peut être n’importe quelle combinaison des drapeaux suivants (avec
quelques 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ées
sera UTF-8. Vous pouvez spécifier un encodage différent en créant un contexte personnalisé ou en
modifiant celui par défaut en utilisant la fonction stream_default_encoding(). Ce drapeau ne peut
être utilisé avec FILE_BINARY),

74
– FILE_BINARY Avec ce drapeau, le fichier est lu en mode binaire. C’est la configuration par
dé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 affectant
la 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 de
flux principale (l’écran), $use_include_path indique si le fichier $filename doit être recherché dans
les répertoires d’include de la directive php.ini include_path, $context identique à la fonction précédente
mais 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 ajoute
un 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 que
l’écriture puisse commencer. En clair, accès en réécriture du fichier ; Si le fichier de nom $filename
n’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 est
ajouté à 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. Impossible
de se servir de ce mode sur des fichiers distants.
– ’x+’ : pareil mais en plus un accès en écriture
A ce mode, s’ajoute un indicateur de traduction de fin de ligne : le pb vient des différents OS qui ne
considè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 finde
ligne quand il accède à un fichier texte et besoin de ’b’ quand il accède à un fichier binaire. Pour
accéder à un fichier UNIX/Linux quel qu’il soit, TOUJOURS mettre ’b’ comme indicateur de fin de
ligne.
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 qui
n’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
• file(string $filename, Optional int $flags, Optional resource $context) : ouvre le fichier $filename
et renvoie un tableau dont chaque élément est une ligne du fichier ouvert ; Les flags possibles sont les
suivants :

– 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 du
tableau.
– FILE_SKIP_EMPTY_LINES : Ignore les lignes vides.
– FILE_TEXT : Le contenu est retourné en utilisant l’encodage UTF-8. Vous pouvez spécifier un
encodage différent en créant un contexte personnalisé. Ce drapeau ne peut être utilisé avec le drapeau
FILE_BINARY.
– FILE_BINARY : Le contenu est lu en tant que données binaires. C’est le comportement par
dé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(voir
plus 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 en
valeur. 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 par
BGSESSID 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-tableau
correspond à 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_RAW
qui 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 donnant
le handle récupérée avec l’usage de fopen sur le chemin d’accès au fichier .csv (Note : un .csv formate un
fichier comme un tableau de n ligne et de m colonnes); $lenght correspond à la longueur maximale d’une
ligne 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ère
de délimitation (par défaut ”), $escape spécifie le caractère d’échappement (par défaut \) ; ATTENTION
fgetcsv lit seulement une ligne du tableau csv à la fois. POUR lire n LIGNES DE SUITE, il faut
appeler 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
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 de
type stream, c’est le buffer restant qui écrit dans le fichier. Les flags peuvent être les suivants et combinés
avec l’opérateur ¦ :

– FILE_USE_INCLUDE_PATH : Recherche le fichier filename dans le dossier d’inclusion. Voir


include_path pour plus d’informations.
– FILE_APPEND : Si le fichier filename existe déjà, ce drapeau permet d’ajouter les données au
fichier 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 encodage
différent en créant un contexte personnalisé ou en modifiant celui par défaut en utilisant la fonction
stream_default_encoding(). Ce drapeau ne peut être utilisé avec FILE_BINARY. Ce drapeau est
uniquement disponible depuis PHP 6.
– FILE_BINARY : Les données data seront écrites en mode binaire. C’est la configuration par
défaut et ne peut être utilisée avec le drapeau FILE_TEXT. Ce drapeau n’est disponible qu’à partir
de 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 fscanf
utilise un handle de fichier. fscanf permet d’utiliser une expression régulière pour récupérer une chaine
préformaté (v. chapitre 5) ;

77
• fwrite($handleFichier, $string, Optional $length ) : ecrit la chaine $string dans le fichier ouvert
avec fopen et dont le handle est $handleFichier. Si $Lenght est spécifié, au maximum $Lenght octets
seront écrits (càd min entre mb_strlen($string) et $lenght. ATTENTION : quoiqu’il arrive, un deuxième
appel à 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é en
fin de fichier de sorte que rien n’est écrasé. Pour remplacer un bout du fichier, il faut ouvrir le fichier avec
fopen 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 pointeur
d’un octet ;
• fgets($handle) : lit et renvoie la ligne courante sur lequel pointe le pointeur interne puis avance ce
pointeur jusqu’à l’octet succédant à la fin de ligne (\n ou \r ou autre suivant OS). Si la ligne courante est
la dernière du fichier, le pointeur pointera sur la fin du fichier ; IMPORTANT : la fonction stream_get_line
fait 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 on
souhaite 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
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ément
dans 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, si
fread 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 dans
le fichier dont le handle est $Handle ;
• setter la taille du buffer avec set_file_buffer($FileHandle, $BufferEnOctets) ou stream_set_write_buffer($F
$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 ou
en lecture, il faut s’assurer que les accès concurrents ne vont pas faire corrompre le fichier (par exemple quand
deux 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 est
tronqué à l’ouverture et dans ce cas, deux clients peuvent éventuellement se trouver à lire et surtout écrire
en 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
• ftruncate() : permet de gérer plus finement le pb des accès concurrents en écriture. RECHERCHE A
FAIRE.

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 chemin
d’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 est
bizarrement 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ême
que $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 a
seulement 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 $Context
se rapporte au contexte qu’on verra plus loin ; Renvoie true si le fichier $FileName a bien été effacée, false
sinon.

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 du
fichier commencera par la chaine $prefix. Renvoie le nom et le chemin absolu complet du fichier
et non son handle. ATTENTION : sous Windows, le prefix sera limité à trois caractères et $prefix sera
amputé des caractères surnuméraires si besoin est !
• sys_get_temp_dir() : renvoie le chemin du repertoire tmp du système ;

80
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, il
est donc manipulable comme n’importe quel autre fichier. Pour identifier un fichier en tant que répertoire, on
peut 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 $FileName
est 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 droits
d’accès au répertoire crée. ATTENTION : l’option $mode est ignorée sur Windows de sorte que le répertoire
cré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 $pathname
s’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 une
approche 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 $path
et 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 ouvert
avec 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épertoire
cible ont déjà été lus. L’ordre de lecture est l’ordre dans lequel les fichiers ont été enregistrés dans le
répertoire. ATTENTION : parmi ces fichiers, on trouve les répertoires contenus dans le répertoire cible
et on trouve notamment deux répertoires spéciaux qui sont ”.” et ”..” qui correspondent respectivement
au répertoire cible lui-même et à son répertoire parent. NOTE : readdir utilise un pointeur interne qui
désigne à chaque appel de readdir le fichier suivant. De ce fait, une fois un fichier appelé, il est impossible
de 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 le
répertoire cible ne sera jamais lu qu’une fois. rewinddir() permet de faire revenir le pointeur interne au
dé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 ouvert
avec opendir ;

Une approche est d’avoir à un objet de type Directory avec l’instruction dir qui renvoie un tel objet.

81
• 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épertoire
existe .

Cet objet a alors comme méthodes : read(), rewind(), close() qui ont les mêmes prototypes que les fonctions
précédentes correspondantes à ceci près qu’il est inutile de spécifier le handle vu qu’il s’agit de l’objet sur lesquel
les 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 masque
cible $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 et
majuscules.
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 de
nom 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 et
considéré comme un fichier et donc GLOB_ONLYDIR éjecte le masque de retour dans le tableau
renvoyé ;
– 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 bien
grave !!
– GLOB_ERR : Stop lors d’une erreur (comme des dossiers non lisibles), par défaut, les erreurs sont
ignorées.

Répertoire courant de travail et répertoire de courant du script en courant : Le répertoire courant


de travail est obtenu avec getcwd() tandis que le répertoire contenant le script dans lequel le code courant est
exé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 courant
est le répertoire $NewCurDir ; ATTENTION : Le changement de répertoire courant n’est valide que
jusqu’à la fin du script, ensuite le répertoire courant redevient celui spécifiée par la directive docroot ;

82
Informations sur les fichiers : Le truc à savoir c’est que la récup d’informations sur les fichiers est couteuse
en perf pour php et donc celui-ci garde en fait un max d’infos en cache et ressert l’info stocké en cache pour
répondre à des demandes d’informations sur les fichiers. De ce fait, si on veut être sur que les informations
ne 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 ressert
alors 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 cache
d’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 est
stocké dans ce tableau. Une fois avec une clef numérique car la première version de cette fonction renvoyait
un tableau classique et une autre fois avec une clef associative car la nouvelle version de la fonction autorise
de 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 leurs
indices, 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
• lstat() : fait pareil que stat sauf que lstat récupère des infos sur le lien symbolique et non sur le lien
physique s’il y a une différence. Ex : si on fait un stat sur un raccourci pointant sur un fichier, on obtiendra
des infos sur le fichier avec stat, et on obtiendra des infos sur le raccourci avec lstat ; PROBLEME : lstat
et 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 de
modifcation (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 est
renvoyé) ;
• filemtime($filename) : renvoie la dernière date de modification du fichier sous forme de timestamp
UNIX (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 les
serveurs...
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ème
survient ; 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 appartient
le 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 contenu
dans le chemin (avec le suffixe type .php ou .xls) ;
• pathinfo($CheminVersFichier, Optional int $options) : renvoie un tableau associatif contenant le
dirname (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 ne
sont pas demandés, c’est une chaine de caractère qui est renvoyé et non plus un tableau... un peu débile
de changer le type de retour, non ? LA chaine de retour renvoyée dépend des options choisies. Rien de
vraiment intéressant et un peu bizarre dans le comportement dans ce cas d’ailleurs...
• readlink() : FONCTION PAS IMPLEMENTEE SOUS WINDOWS !!

84
Permissions et droits d’accès sur fichiers : Un fichier a un propriétaire utilisateur et groupe, des droits
d’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éellement
l’info. Le code suivant utilise le retour de fileperms pour renvoyer une chaine décrivant le type de l’objet
associé 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 user
courant 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 un
droit 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 serveur
web) si le fichier $FileName peut être exécuté (ce qui ne signifie pas que le fichier ne puisse pas être ouvert
comme 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écifient
les droits pour le propriétaire, le groupe du propriétaire et les autres, respectivement. Chaque composant
peut être calculé en ajoutant les droits désirés. Le chiffre 1 donne les droits d’exécution, le chiffre
2 les droits d’écriture et le chiffre 4 les droits de lecture. Ajoutez simplement ces nombres pour
spécifier les droits voulus. Ainsi 0600 correspond à Lecture et écriture pour le propriétaire, rien pour les
autres, 0644 correspond à Lecture et écriture pour le propriétaire, lecture pour les autres, 0755 Tout pour
le propriétaire, lecture et exécution pour les autres, 0750, Tout pour le propriétaire, lecture exécution pour
le groupe, rien pour les autres. NE MARCHE PAS AVEC LES FICHIERS DISTANTS. Renvoie true
si le changement de droit a eu lieu, False sinon ; ATTENTION NE MARCHE PAS CORRECTEMENT
SOUS WINDOWS !!

Gestion des droits sous windows : La gestion des droits sous Unix est gérée avec chmod alors que sous
windows, 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
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 de
microsoft est plutôt bien faite :

86
87
88
89
ATTENTION : l’instruction cacls est considéré comme DEPRECATED et Mircosoft recommande l’usage
de 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 icacls
est aussi très bien faite :

90
91
92
93
94
95
96
Le site de Microsoft peut être consulté au besoin pour avoir accès au forum des commandes CACLS et
ICACLS :
http://technet.microsoft.com/en-us/library/cc753525%28WS.10%29.aspx#BKMK_examples
Le 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 icacls
exec($Command, $FilePath); /// on exécute la commande définie
foreach($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 flux


Un flux est une suite de données. On s’intéresse donc aux différents type de suite de données manipulables en
PHP.
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 : serveurs
webs, 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
14.1.1 Exécution de prgm externes sans interaction :

• shell_exec(string $CommandShell) : exécute la commande $CommandShell et renvoie le résultat


dans 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 la
commande, il faut ajouter la première option, le retour complet sera alors stocké dans $sortieStd. Cette
variable 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és
via 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. Dans
un config de web serveur Apache, Apache redémarre tous ses programmes fils au bout d’un certain nombre de
requêtes. Pour éviter la terminaison non sollicitée de programmes appelées par exec, il faut utiliser l’instruction
nohup dans la commande appelée par exec ou system.
Exemple : nohup ”/etc/initd/apache startssl” >/dev/null 2>&1
Ce 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 le
killer !!)

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 chose
dans 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 suivant
ce qu’il a été spécifié dans la conception du prgm...

98
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’un
fichier ; ATTENTION popen ne permet de se connecter qu’à seul flux de la commande $Commande
passé en argument ;

• proc_open($Commande,
$DescriptorSpec,
$Pipes,
Optional $cwd,
Optional $TabEnv,
Optional $ArrayOptions) : Exécute une commande et ouvre les pointeurs de
fichiers 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. Dans
le 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 le
processus fils càd le prgm désigné par $Commande et le processus père càd PHP. La fonction renvoie une
ressource ou handler sur le prgm désigné par commande. Par ailleur $Pipes correspond au tableau des
handlers 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 valeur
correspond à un sous-tableau indiquant comment le flux du prgm fils est redirigé : nulle part, vers
un 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 ici
PHP. ATTENTION quand le flux d’erreur du prgm fils est redirigé dans un pipe, il est apparemment
pré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 se
servir 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
$Descriptor. ATTENTION : ce tableau ne contient que les descripteurs des flux (pipes) crées. Les
fichiers crées ne font pas partie de ce tableau. En fait, vu qu’on a le nom des fichiers, suffit de les
ouvrir avec fopen ou autre fonction.
– $cwd : option qui permet de spécifier le dossier initial de travail de la commande. Cela doit être
un chemin absolu vers le dossier ou NULL si vous voulez utiliser la valeur par défaut (le dossier de
travail du processus courant PHP) ;
– $TabEnv : Un tableau contenant les variables d’environnement pour la commande qui doit être
exé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 actuellement
supportées sont :
∗ suppress_errors (windows uniquement): suppression des erreurs générées par cette fonction
lorsque 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 sinon
il 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 processes
ouverts 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ées
suivantes :

– 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 à FALSE
sous Windows.
– stopped : TRUE si le processus fils a été stoppé par un signal. Toujours défini à FALSE sous
Windows.
– exitcode : le code retourné par le processus (uniquement si l’élément running vaut FALSE). Seul le
premier 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 significatif
si 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 significatif
si signaled vaut TRUE). 0 sous Windows

100
Fermeture d’un processus ouvert avec proc_open : A l’issue de l’appel de proc_open, suivant le
descripteur $DescriptorSpec passé en argument, un ou plusieurs flux ont été ouverts pour communication
directe 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 avant
d’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_open
sont 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 programme
externe est à l’initiative du client avec possibilité pour lui de passer son instruction, il est clair qu’il faut être
prudent 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 $Commande


destinée à servir de commande d’appel d’un programme externe. échappe tous les caractères de la chaîne
command qui pourraient avoir une signification spéciale dans une commande Shell. Cette fonction permet
de 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ères
suivants seront échappés : #&;‘|*?~<>^()[]{}$\, \x0A et \xFF. ’ et ” sont échappés que s’ils ne sont
pas par paire. Sous Windows, tous ces caractères ainsi que % sont remplacés par un espace. Renvoie la
chaine echappée
• escapeshellarg($ArgumentCommande) : permet d’échapper les caractères spéciaux d’un argument
de 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 (elle
vaut off dans le cas contraire) ;

• la directive safe_mode_exec_dir de php.ini permet de spécifier une liste de répertoires dans lesquels
se trouvent les seuls programmes utilisables ;
• ATTENTION : dès lors qu’un programme externe est appelé, celui-ci n’a pas les limitations imposées par
Apache ou php, et la directive safe_mode_exec_dir est donc particulièrement importante pour empêcher
l’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 socket
réseau. (socket veut dire connexion en anglais)

Une socket réseau est une connexion réseau qui peut être de type :

101
• 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 car
kekpart 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 du
script.
L’autre mode est le mode non-bloquant qui permet alors d’ouvrir la socket, de la consulter pour savoir si de
nouvelles données sont disponibles et de continuer le script quoiqu’il arrive. Quelque part dans ce cas, on gère
les 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 à destination
de $hostname. Renvoie un pointeur de fichier (handler) ou FALSE Si l’appel échoue, la fonction retourne
FALSE. :

– 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 tous
les deux et correspondent à un protocole de sécurisation des échanges sur internet. Par défaut, le
protocole 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 une
indication 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 de
caractè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 avez
besoin de définir un délai limite pour lire/écrire des données à travers cette socket, utilisez la fonction
stream_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 les
flux 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
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 $Stream
au mode $mode). Le mode est non-bloquant si $mode vaut 0 (et pas FALSE attention) (càd php lit
le flux mais poursuit son exécution même en cas d’absence réponse, et il est bloquant si le $mode vaut
1 (et pas true);
• feof(ressource $Stream) : permet de savoir si des données peuvent encore arriver du flux dont le handler
est $Stream; renvoie true si plus aucune donnée ne peut arriver, false sinon ; L’utilisation de cette fonction
est plus subtile qu’il n’y parait. Quand la lecture du flux implique de lire le dernier caractère du flux, cela
n’implique pas qu’un appel consécutif renvoie true. Pour cela, il faut chercher à lire une nouvelle fois le
flux de sorte que le pointeur sur le flux dépasse le caractère final du flux. Ce n’est alors que dans cette
situation 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 de
la 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 de
continuation feof($Flux)===false, du fait que feof ne renvoie false que dans le cas ou le $handler pointe sur
une position strictement postéreure au caractère de fin de flux, il est chaudement recommandé de préférer
une 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 php
considè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 de
valeurs d’informations sur le flux de handler $SocketHandler. Le tableau renvoyé est vide si le handler ne
correspond à rien. A noter que cette fonction s’applique aussi bien au fichier qu’au flux. les clefs potentiels
du tableaux sont :

– timed_out (booléen) : TRUE si le flux a atteint de délai d’expiration en attendant des données
durant 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 (cf
http://fr.php.net/manual/fr/wrappers.php ATTENTION le nombre de gestionnaire de flux
est 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 des
protocoles supportés pour plus d’informations sur les gestionnaires et leurs données.

103
– 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érence
de 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ée
pour 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 dialoguer
des serveurs entre eux !!!
Pour l’instant, on sait surtout se connecter à des sockets réseaux avec les fonctions précédentes. Maintenant
il est aussi possible d’écouter le réseau càd à l’aide de socket. Et si on écoute, on peut aussi répondre, ce qui
revient 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), une
sur 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 fonction
socket_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 IPv4
càd adresse IP classique en TCP (http ou ssl) ou UDP, AF_INET6 pareil mais avec une adresse
IPv6, 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’envoi
de données réussit et la taille données envoyées est fixe càd connu à l’envoi, c’est le type de socket
utilisé 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
– 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 (Internet
Control 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 permet
cela. c’est grâce à ce protocole qu’une machine émettrice peut savoir qu’il y a eu un incident de
ré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 report
errors in datagram communication. The ”ping” command (present in most modern operating
systems) is an example application of ICMP ;
– UDP : The User Datagram Protocol is a connectionless, unreliable, protocol with fixed record
lengths. Due to these aspects, UDP requires a minimum amount of protocol overhead.
– TCP : The Transmission Control Protocol is a reliable, connection based, stream oriented, full
duplex protocol. TCP guarantees that all data packets will be received in the order in which
they were sent. If any packet is somehow lost during communication, TCP will automatically
retransmit the packet until the destination host acknowledges that packet. For reliability and
performance reasons, the TCP implementation itself decides the appropriate octet boundaries of
the underlying datagram communication layer. Therefore, TCP applications must allow for the
possibility 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 de
handler $socket à l’adresse $address. Celle-ci doit alors être une adresse IP ou IPv6 (PAS un nom de
domaine !!) si le domaine de la socket est de type AF_INET ou resp AF_INET6 et de type chemin de
socket de domain UNix si le domaine est de type AF_UNIX. L’option $Port dans le cad AF_INET pour
préciser sur quel port la connexion réseau aura lieu ; RENVOIE true en cas de succès et false sinon, les
deux 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 la
connection socket_connect :
bool socket_connect ( resource $socket , string $address [, int $port = 0 ] ) : Fait la connection
entre 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) avec
socket_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 mettre
en écoute de connection. $backlog sert à spécifier combien de requête de connexion peuvent être mises
en attente. S’il y a dépassement, suivant le protocole de communication (?) soit l’émetteur de la requête
reçoit un message d’erreur ECONNREFUSED, soit la requête sera ignorée par le serveur et l’émetteur
pourra réémettre sa requête...

105
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 de
type 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 les
donné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 appels
successifs socket_create, socket_bind, socket_listen :
• local_socket : The type of socket created is determined by the transport specified using standard URL
formatting: transport://target. For Internet Domain sockets (AF_INET) such as TCP and UDP, the
target portion of the remote_socket parameter should consist of a hostname or IP address followed by
a colon and a port number. For Unix domain sockets, the target portion should point to the socket file
on the filesystem. Depending on the environment, Unix domain sockets may not be available. A list
of available transports can be retrieved using stream_get_transports(). See List of Supported Socket
Transports for a list of bulitin transports.
• errno : If the optional errno and errstr arguments are present they will be set to indicate the actual system
level error that occurred in the system-level socket(), bind(), and listen() calls. If the value returned in
errno 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 arguments
will always be passed by reference.
• errstr : See errno description.

106
• 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 faut
que php puisse savoir, côté serveur, si quelqu’un se connecte à la socket server. La fonction stream_socket_accept
sert à cela :
• resource stream_socket_accept ( resource $server_socket [, float $timeout = ini_get(”default_socket_t
[, string &$peername ]] ) : renvoie un handler de socket correspondant à une connexion extérieur sur
le 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 le
permet (grosso modo pas le udp) . ATTENTION : il ne faut pas utiliser cette fonction avec une connection
de 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 provided
it 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 $data
through 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 an
alternate address is specified in address. If specified, it must be in dotted quad (or [ipv6]) format. Returns
a result code, as an integer.
J’AI PAS TESTE LE FONCTIONNEMENT DE CES DEUX DERNIERES FONCTIONS... A VRAI
DIRE, 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, un
fichier 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, flux
d’entrée/sortie de prgms) qui est soit un gestionnaire de flux, soit un gestionnaire de transports
de 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
• ”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 écriture
append seule ou en lecture seule, jamais les deux en même temps (càd r,a mais pas r+, w ou w+) (ne pas
oublier 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ès
que 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’utilisateur
login et mot de passe password, le protocole devient : ”http://login:password@”. Accès en lecture seule
des 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, la
lecture peut être modifié en ce sens qu’il est possible de définir les en-têtes envoyées dans la requête HTTP
via les options de contexte (càd définir un streamcontext) ; Notamment la clef ’method’ spécifie le type
de 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’utilisateur
login 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 unlink
peut permettre d’effacer un fichier sur un serveur ftp. Un accès en mode w sera refusé sauf si l’option de
contexte overwrite est présente dans un streamContext à la valeur true. Note : Ce protocole de transport
utilise le protocole réseau TCP;
• ”https” : pour accéder à un fichier distant de manière sécurisée suivant le protocole https. Ce protocole
de 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 de
transport 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 soit
garanti (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 une
adresse 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 et
les options. Ces deux protocoles de transport ont cependant des options de context propres (voir les
StreamContext 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épertoire
contenant ce fichier pour vérification de la certification ;
– local_cert : permet de spécifier l’adresse local (donc chez le client) du certificat à utiliser pour
vérification ;

108
– passphrase : le mot de passe associé à ce certificat ;
– CN_match : permet de vérifier que le common name correspond à un masque défini (???? Voir le
chapitre sur le sécurité ???);

IMPORTANT : si la directive allow_url_fopen est désactivé dans php.ini; Les accès distants ne sont
pas 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 ; A
notre que c’est un accès bas niveau en ce sens qu’aucun filtrage n’est possible sur les données lues ; il faut
utiliser 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 et
que les fonctions de filrage de flux d’entrée sont utilisables. php://input permet de lire des données POST
bruts. C’est moins gourmand en mémoire que $HTTP_RAW_POST_DATA et il n’y a pas de directive
spé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 faut
utiliser 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 php
et que les fonctions de filrage de flux de sortie sont utilisables ; php://output vous permet d’écrire dans
le 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 les
données d’entrée au moment du démarrage du script. C’est pratique avec des fonctions compactes comme
readfile(), file() et file_get_contents() où il n’y a pas d’opportunité d’appliquer un filtre aux données
lues.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://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 de
transport 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
110
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 config
actuel 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 config
actuelle 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 registered
transport protocol even though you has add the extension=php_openssl.dll line on php.ini, maybe you
haven’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
• 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 du
pointeur (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é, tout
le reste du flux source sera copié. Renvoie le nombre total d’octets copiés. ATTENTION : une fois la copie
faite, le pointeur interne du flux source aura avancé d’$offset octets ou se trouvera à la fin du flux (eof) si
offset vaut 0 par défaut. (A noter, sur http://fr2.php.net/manual/fr/function.stream-copy-to-stream.php
dans 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ères
dans 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 chaine
lue à l’exception du caractère de fin de ligne (\r, \n ou \r\n suivant l’OS) ou de la chaine $ending
qui est sauté(e) (mieux que fgets donc). Un nouvel appel conduit à lire la ”ligne” suivante en sautant
le 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’écriture


du flux de handle $streamHandle à la taille de $buffer octets. fwrite() est habituellement configurée
avec un buffer de 8 ko. Cela signifie que si deux processus veulent écrire dans le même flux de sortie (par
exemple, un fichier), ils font une pause tous les 8 ko pour laisser les autres écrire aussi. Si buffer vaut 0
alors les opérations sont sans buffer. Cela garantit que les opérations avec fwrite() sont achevées avant
que 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 de


handler $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
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 nom
du flux de handler $handle. Si $want_peer est false, le nom de la socket locale sera retourné sinon ce
sera le nom de la socket distante ; IMPORTANT : cette fonction renvoie l’adresse IP du flux quand c’est
une 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 prend
alors 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 8
fonctions précédentes ne font pas partie d’une interface et donc la classe de transport n’a donc pas besoin d’en
implémenter une..

Pour plus détails, lire les pages 374-376 du livre PHP5 avancé...

14.5 Les streamContexts


Le 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
14.5.1 Définition :
Les contextes sont des options accompagnant un flux, et sont passés en dernier argument des fonctions de
créations/ouverture de flux comme fopen, stream_socket_client et stream_socket_server. Un context est un
tableau associatif à double entrée (càd un tableau associatif contenant que des tableaux associatif). Un tel
tableau 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’autorisation
d’é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 renvoie
un 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 comprendre
ce 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 client
pointant 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 ajoute
deux 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 le
StreamContext s’applique. Par exemple, dans le cas d’une connection téléchargeant un fichier, ça
permet de gérer un retour vers le client alors que le téléchargement se fait. par exemple, on notifie le
client 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énements
intervient.
IMPORTANT : Il faut comprendre que callback est le nom d’une fonction dont le prototype DOIT
respecter le template suivant :
void NomDeLaFonctionDeStreamCallback( int $notification_code , int $severity , string
$message , int $message_code , int $bytes_transferred , int $bytes_max ) : ne renvoie
rien, le retour de la callback fonction au client se fait directement via des echo, ou des print_r, càd
toute 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
$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
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 filtres
sont prédéfinis nativement tandis qu’il est possible de créer son propre filtre personnalisé avec une classe
implémentant l’interface php_user_filter. ”transparent” car les appels aux fonctions sur flux avec ou sans
filtres sont strictement les mêmes car l’ajout de filtres de traitement se fait uniquement en utilisant les fonctions
stream_filter_append() et stream_filter_prepend(). Il est possible d’utiliser plusieurs filtres successifs
dans les traitements des flux : par exemple, un premier filtre strtolower suivi d’un string.strip_tags ce qui
signifie que la chaine est d’abord mise en petites lettres puis supprimes les balises html ou hp présents dans la
chaine.

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 au
contraire 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’applique
en dernier par rapport à ceux déjà associés à $stream. Le filtre est associé par défaut pour un accès
en lecture si le flux a été ouvert en lecture, et pour un accès en écriture si le flux a été ouvert comme
telle. $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’associer
des valeurs de paramètres au filtre $filtername, maintenant aucun exemple concret... RENVOIE un
handler 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 des
données sont présents dans le tampon associé au flux, le filtre ajouté avec stream_filter_prepend NE
SERONT PAS filtrés avec le filtre (précision importante si on ajoute un filtre en cours de lecture/écriture
sur 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 passent
successivement 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’il
est 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 liste
dépend aussi des extensions php. Attention, dans la liste, parfois c’est le nom du module d’extension suivi
de ”.*” 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 la
fonction stream_filter_register()

116
La classe implémente l’interface php_user_filter qui contient les méthodes suivantes : filter(), onclose et
oncreate()
• filter($in, $out, &$consomme, $ferme) : convertit la chaine $in en une chaine $out, $Consomme
est le nombre de caractères lus sur le flux, ce paramètre doit être incrémenté du nombre de paramètre
traités en ENTREE, $ferme indique avec TRUE si le flux est sur le point de se fermer càd dans ce cas
que PHP fait son dernier appel au filtre ; RENVOIE un entier enum : PSFS_PASS_ON si la conversion
est successful, PSFS_FEED_ME qui indique que rien n’a été retourné et que le filtre attend des données
supplémentaires avant d’agir (En fait, ça tient qu’à ce stade, les bout de données sont gérés avec des
stream_buckets). PSFS_ERR_FATAL indique une erreur fatale dans le processus ; le filtrage s’arrêtant
alors. 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 du
filtre ;

• 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 nom
de 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). Dans
ce cas, il est possible d’utiliser des fonctions de filtres en spécifiant directement ceux-ci dans l’adresse du flux
ciblé. Une adresse classique s’écrit :
transport://cible:port

117
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 les
filtres 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 sortie
est 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 revient
alors à gérer le comportement du tampon mémoire associé au flux de sortie. En temp normal, PHP envoie le
contenu 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ément
avant 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ément
empê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 de
sortie ;

• 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 en
tampon n’est pas arretée ;

118
• 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 de
copie 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 le


tampon 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. Les
appels 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() pour
chaque 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. Renvoie
FALSE si aucun tampon mémoire n’existe pour le flux de sortie ; Attention en cas d’usage d’un filtre de
compression, la taille renvoyée reste la même car ce qui est compressé, c’est ce qui est envoyé vers le flux
de 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 contenu
du 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 module
iconv ;

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 la
fonction ob_start() comme suit :

ob_start(’FiltreLambda’);

Le contenu du tampon aura été alors filtré avec le filtre lambda au préalable.

119
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 des
données :
Les protocoles de transport http (et https) permettent l’envoi de données compressés pour gagner de la
bande passante. La fonction de filtre associé est ob_gzhandler(). Pour utiliser ce filtre de compression, il faut
appeler 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és
vers le flux d sortie sont déjà compressés par défaut, et ob_start(”ob_gzhandler”) enclenchera pour le tampon
concerné 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.ini
sans toutefois y parvenir... ;-) En fait, c’est normal, c’est impossible d’avoir ob_start(”ob_gzhandler”) avec
zlib.outputcompression à on.

Les filtres de conversion d’un encodage à l’autre : Deux modules permettent d’ajouter un filtre de
conversion 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 que
mbstring. Ce dernier révise aussi certaines fonctions de manipulation de chaines pour prendre en compte les
caractè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 filtrage
dépend alors de la directive mbstring.http_output se trouvant dans php.ini et qui spécifie quel est l’encodage
cible (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é avec
la directive php mbstring.http_output OU sette cette directive à $encoding si cette variable optionnel est
spé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 en
entrée et renvoyant une chaine en sortie. A tout appel de ob_flush() ou ob_end_flush(). Le contenu du tampon
passe en entrée de la fonction utilisateur et la chaine renvoyée par la fonction filtre utilisateur est envoyée vers
le flux de sortie.

IMPORTANT : les filtres appliqués au tampon mémoires sont différents des filtres associés aux fonctions de
gestion des flux. Idéalement il vaut mieux éviter les redondances ou les doubles filtrages malheureux de ce fait.

120
RUSE : si on souhaite cumuler les filtres, on peut aussi bien créer un filtre utilisateur qui appelle deux
autres 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, on
fixe la taille du tampon utilisé.
ATTENTION : L’usage d’output_buffering revient à un appel automatique de ob_start() et on peut donc
utiliser ob_flush, ob_end_flush, ob_end_clean et ob_clean et cie dessus comme si ces appels étaient précédés
d’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 buffer
existe 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éremment
l’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’inscription
sur 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. Nous
allons 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 tout
mail envoyé ; (en l’occurrence, pour le moment, chez moi c’est bg@pricingnation.com

Note : SMTP = Simple Mail Transfert Protocol

121
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 de
contenu etc.) ; A noter que les en-têtes sont standardisés suivant la norme RFC 822 qui structure tous
les courriers mails à la manière d’un dénominateur commun. En l’occurence, c’est la norme des messages
mails au format texte ;

– Chaque en-tête se présent comme suit :

NomChamp : ValeurDuChamp

exemple :
From : bg@pricingnation.com Tue Oct 1 12:00:50 2004
To : bernardgourion@yahoo.fr
Date : Wed, 8 Sept 2010 10:54:00 1768
Simple, 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 et
de 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 $subject
et pour message la chaine $message. Des en-têtes $additional_headers peuvent être ajoutés au mail
de même que des paramètres additionnels $additional_parameters. A propos de chaque :

– $to : Une adresse mail comme toto@alaplage.com, bernardgourion@yahoo.fr ou bg@pricingnation.com


;
– $subject : chaine de caractères correspondant au sujet du message, il doit respecter la norme RFC
2047 (partie intégrante de la supernorme MIME ;
– $message : La chaine de caractères correspondant au message lui-même. Cette chaine doit respecter
qu’une ligne ne doit pas dépasser les 70 caractères. Un ’\n’ doit donc se trouver au maximum tous
les 70 caractères. ATTENTION sous Windows uniquement : Lorsque PHP discute directement
avec un serveur SMTP, si un point est trouvé en début de ligne, il sera supprimé. Pour éviter ce

122
comportement, remplacez ces occurrences par un double point. Par exemple tout message devra être
passé 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 typiquement
utilisé pour ajouter des en-têtes supplémentaires (From, Cc et Bcc). Les en-têtes supplémentaires
doivent être séparés par un caractère CRLF (\r\n). Lors de l’envoi d’un mail, le mail DOIT contenir
un 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: Natacha.Polony@gmail.com\nCc:Etienne.Mougeottehimself@gmail.com,bernardgourion@yahoo.fr”
IMPORTANT :
∗ le caractère ”:” DOIT se trouver immédiatement après le nom du champ. En l’occurrence, ici
si on a ”From : Natacha.Polony@gmail.com,...”, le champ From est considéré comme ayant une
valeur vide alors qu’avec ”From: Natacha.Polony@gmail.com”, il vaut bien l’adresse fictive de
Natacha Polony.
∗ Pour ajouter plusieurs champs, il faut passer à la ligne (càd écrire ’\n’) immédiatement après la
valeur 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: Adresse.DeReply@domaine.com” ;
∗ Le champ copie carbone est ”Cc: AdressesMail” avec AdressesMail une suite d’adresse mails
sé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 des
drapeaux additionnels comme options à la ligne de commande configurée pour être utilisée pour
envoyer 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 avec
l’option -f.
L’utilisateur sous lequel tourne le serveur web doit être ajouté en tant qu’utilisateur de confiance
dans la configuration de sendmail afin que l’en-tête X-Warning ne soit pas ajouté au message lorsque
l’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. INCOMPREHENSIBLE
RENVOIE 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 namespace
PEAR 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
• ”MIME-Version: X.Y” : qui spécifie le numéro de version X.Y du format MIME (Multipurpose Internet
Mail 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 le
message. 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’encodage
utilisé 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 pour
valeur ”7bit” dans le header du message. Ce champ permet de spécifier que le mail est codé en 7 bits pour
l’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 dans
le cas d’un mail d’informations envoyé à plusieurs centaines de personnes) : Pour cela, on utilise une balise
html 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 sur
l’envoi de pièces jointes ;

124
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 chaque
partie 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 code
dé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érales
Un Ligne Vide
Un Ligne avec le délimiteur
En-tête de la partie
Un Ligne Vide
Contenu de la partie
Un Ligne Vide
Un Ligne avec le délimiteur
En-tête de la partie
Un Ligne Vide
Contenu de la partie
etc.

Un Ligne avec le délimiteur pour finir


Lla 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 le
format 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
• Dans le contenu associé au header précédant, le fichier attaché s’ajoute en demandant l’affichage brutale
de $attache : $msg .= $attache . ”\n”;
Les fonctions utilisés ci-dessus sont :
• string chunk_split ( string $body [, int $chunklen = 76 [, string $end = ”\r\n” ]] ) : Scinde la
chaîne body en segments de chunklen octets de longueur. Cette fonction est très pratique pour convertir
les résultats de base64_encode() au format de la RFC 2045. Elle insère le paramètre end tous les chunklen
caractères. Retourne la chaîne scindée.
• string base64_encode ( string $data ) : Encode data en base64. Cet encodage est fait pour permettre
aux 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-8
AddPlainTextHeader($msg,$delim);
/// ajout du plain text lui-meme càd le message $Texte

126
$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 comme
suit :
$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 est
correctement 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 client
mail. Les échanges avec le serveur ne sont pas cryptés et dont le login et le mot de passe du client sur le
serveur... Bref ça prend de la place et ce n’est pas safe du tout !! Sans compter qu’il ne peut jamais y
avoir plus d’un client connecté à un même compte.
• IMAP (Internet Mail Access Protocol) : Beaucoup plus souple, ce protocole permet plein de choses dont
le 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
[, array $params = NULL ]]] ) : essaie de créer un handler sur le flux
correspondant à 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 et
NNTP 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 souvent
l’écriture suivante : TypeProtocole.mail.NomDuFAI.fr Par exemple : pop.mail.yahoo.fr est le
nom 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 dont
le 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 le
serveur utilise des certificats auto-signés
· tls : l’utilisation de start-TLS pour chiffrer la session et rejette les connexions aux serveurs
qui ne le supporte pas
· notls : n’utilise pas start-TLS pour chiffrer la session, y compris avec les serveurs qui le
supporte
· 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
∗ * OP_HALFOPEN : Pour les noms IMAP et NNTP, ouvre une connexion mais n’ouvre pas une
boî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
∗ 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 sur
activation 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
* 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.fr
o 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
∗ 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 Avec
connexion 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 nature
du SGBD sous-jacent. Auparavant l’extension la plus répandue était l’extension mysql de PHP4. mysqli peut
aussi ê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 le
le 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ès
rapide. Cen n’est cependant pas totalement une abstraction de la gestion des base des données car il existe une
extension 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
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 client
souhaitant manipuler des données. Si j’écris source de données, c’est qu’un DSN peut tout autant être une base
de 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 serveur
distant 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 plus
pré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 bouquin
pour 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’une
erreur ;

133
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 en
argument 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, exec
ne renvoie pas le nombre de lignes effacées car pour aller plus vite, PHP efface alors tout le fichier
correspondant à 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 renvoie
un objet de classe PDOStatement qui encapsule les données obtenues par la requête sql ; RENVOIE un
objet de classe PDOexception pour des instructions de type SELECT, EXPLAIN ( ???), SHOW (???) et
DESC (????), ou FALSE si erreur.
Ca, c’est l’usage courante de la fonction : le prototype complet de la fonction peut prendre 3 autres formes
diffé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 appelant
son 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
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 en
argument 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 persistentes
que de threads apache pour un même utilisateur. Si le nombre d’utilisateurs devient grand, le SGBD peut être
saturé 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, un
objet de classe PDOexception est renvoyé.
ATTENTION SECURITE la non interception d’une PDOexception via try catch fait que PHP génère
un fichier de traces contenant tous le contenu de la requête dont au minimum le user et le password qui
peuvent ensuite être potentiellement récupérés par un pirate lisant le fichier de traces... IL FAUT DONC
IMPERATIVEMENT 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 mauvaises
surprises.

Gérer ses données de connexion avec un fichier de configuration Pour éviter d’avoir à redéfinir
constamment ses données de connexion à une base de données pour créer un objet PDO dès qu’une connexion
est 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 sera
ensuite 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éthodes
membres 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éussir
sous peine de les annuler toutes v. plus bas). Désactive le mode autocommit. Lorsque l’autocommit est
désactivé, les modifications faites sur la base de données via les instances des objets PDO ne sont pas
appliqué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 la
connexion en mode autocommit. ATTENTION : Quelques bases de données, dont MySQL, exécuteront
automatiquement 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 notamment
toutes les instructions impactant la structure d’une table de la base concernée ou la base elle-même. Ce
COMMIT 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
• bool commit ( void ) : Valide une transaction (càd un bloc de requêtes devant toutes réussir sous peine
de les annuler toutes v. plus bas) ; remet la connexion en mode autocommit après en attendant l’appel à la
fonction 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’est
pas validée explicitement avec commit, alors, on présume que quelque chose s’est mal passé et l’annulation
de la transaction intervient afin de garantir la sécurité de vos données. Lorsque le script se termine ou
lorsque la connexion est sur le point de se fermer, si vous avez une transaction en cours, PDO l’annulera
automatiquement.
• mixed errorCode ( void ) : Retourne le SQLSTATE associé avec la dernière opération sur la base de
données. ATTENTION cette fonction n’est pas fiable pour renvoyer le code d’erreur quand on fait des
requêtes avec PDO. Elle ne renvoie de valeurs correctes que lorsqu’apparemment les requêtes ne passent
pas par les classes et méthodes de PDO. Bref vaut mieux utiliser la méthode errorInfo suivante. Par
ailleurs, 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ération
sur la base de données : renvoie un tableau à trois entrées : La première contient le SQLSTATE qui est
souvent défaillant (v. la fonction errorCode à ce sujet pour savoir pourquoi), la seconde contient le code
erreur 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 lignes
affecté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ée
ou la valeur d’une séquence
• PDOStatement prepare ( string $statement [, array $driver_options = array() ] ) : Prépare une
requête à l’exécution et retourne un objet
• PDOStatement query ( string $statement ) : Exécute une requête SQL, retourne un jeu de résultats
en tant qu’objet PDOStatement, ou FALSE si une erreur survient ;
• string quote ( string $string [, int $parameter_type = PDO::PARAM_STR ] ) : Protège une
chaîne pour l’utiliser dans une requête SQL PDO, càd échappe les caractères spéciaux de SQL dans la
chaine passée en argument. $parameter_type permet éventuellement de spécifier le type de données
pour les drivers qui ont des styles particuliers de protection (obscure mais on s’en fout). Quoiqu’il arrive
il est fortement recommandé d’utiliser des requêtes préparées plutôt que traiter le problème des caractères
spéciaux à échapper avec des appels à quote à la volée... Voir la fonction prepare pour les requêtes
pré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ès
l’annulation de la transaction.
Quelques bases de données, dont MySQL, exécuteront automatiquement 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. Ce COMMIT 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.

136
• 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 de
l’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 sql


quelle que soit sa taille sous la forme d’un tableau PHP. ATTENTION : la taille des tables renvoyées
peut être énorme et la charge mémoire pour le serveur conséquente !! Le seul avantage est le fait que
contrairement à l’instruction fetch suivante, la connexion à la base n’est pas bloqué ; L’option fetch_style
permet 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 un


tableau assciatif pour chaque ligne. Les clefs correspondent aux têtes de colonnes de la table sql
renvoyée et la valeur correspond à la valeur ;
– PDO::FETCH_BOTH : Option par défaut, Retourne un tableau identique au précédent sauf que
le deuxième niveau contient à la fois une indexation prenant comme clef le nom de colonne, et une
deuxième indexation contenant le numéro de ligne, ce qui fait que le contenu d’une ligne apparait
deux 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 ont
pour 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 place
mémoire occupée par le retour de query. Par contre, impossible de savoir combien de lignes contient la
table renvoyée. Par ailleurs, tant que fetch est en train de lire la table, il est impossible de lancer une
autre requête sql. RENVOIE false en cas d’erreur ;

• PDOStatement::

137
Pour renvoyer le nombre de lignes du tableau de retour, il suffit d’utiliser la fonction count sur le tableau
renvoyé. 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 mieux
d’abord une faire requête COUNT. Si les données ont besoin d’être traités, il vaut mieux utiliser la fonction
fetch() 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’apostrophe
agit délimiteur de chaine en SQL. C’est avec ce caractère que les attaques par injection ont lieu. Pour échapper
un caractère apostrophe dans une chaine, c’est à dire pour que SQL le prenne pour une vraie apostrophe et non
comme 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 un
dé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 la
valeur, 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 aura
déjà oeuvré :

if (!get_magic_quotes_gpc())
{
$lastname = $pdo->quote($_POST[’lastname’]);

138
}
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 est
un tableau de tableaux, la fonction descendra dans chaque tableau pour s’appliquer. ATTENTION le prototype
de funcname doit prendre comme premier argument une variable de nom $item et comme second argument une
variable 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 mode
silencieux qui est le mode par défaut. Les méthodes errorinfo et errorcode permettent cependant de
récupérer des infos sur les erreurs produits et enregistrés dans l’instance PDO qui gère la connexion au
SGBD ;
• 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 des
erreurs.

139
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 InnoDB
qui permettent d’annuler l’exécution d’instructions sql précédentes. Cela tient au fait que les tables innoDB
enregistrent 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ême
TRUE 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ées
MySQL.

IMPORTANT : pour utiliser des transactions, il vaut mieux travailler en mode Exception pour la gestion
des erreurs de la connexion PDO. Sinon en mode classique, il faut utiliser la fonction ErrorInfo pour s’assurer
aprè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 suivant
le 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éfinitivement
les requêtes effectuées
$dbh->commit()

4. sinon une Exception de classe PDOException a été lancée et on doit utiliser la fonction RollBack de
l’instance PDO pour annuler toutes les requêtes de la transaction :

$dbh->rollBack()

140
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êtes
préparées ont deux avantages :

• La vitesse d’exécution en cas de requêtes multiples : Un requête classique passe par 4 étapes successives
pour s’exécuter : Analyse, Précompilation, Optimisation et Exécution Finale. Une requête préparée ne
passe 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 injection
SQL.

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èle
de 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 un
point d’interrogation ou par une paramètre nommé comme suit :

: N omP arametreN omme

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ètres
nommé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 utilise
pour 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
(a) Exécution directe de la requête via la fonction execute de l’instance PDOStatement crée au point
précédent. Dans ce cas, les valeurs des paramètres nommés sont fixés en passant un tableau associatif
dont 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 la
variable 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 avec
BindParam, si la valeur de la variable PHP change, le paramère nommé change automatiquement
pour prendre la même valeur. Le paramètre nommé est alors une référence sur la variable PHP. Dans
le premier cas, la valeur de la variable PHP a seulement été recopié dans le paramètre nommé au
moment 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 pdostatement
peut-ê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 point
d’interrogation (comme paramètre fictif) dans la requête SQL qui fut utilisée pour préparer la requête. A
propos de :

– $parameter : Pour une requête préparée utilisant les marqueurs (ou paramètre nommé), cela sera
un 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 dans
la requête. Si c’est le premier ? de la requête, ce sera 1, si c’est le deuxième ce sera 2, etc. Par
exemple :
$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 $parameter
en 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
∗ PDO::PARAM_STR : Représente les types de données CHAR, VARCHAR ou les autres types
de 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 sur
SGBD). N’est actuellement pas supporté par tous les pilotes.
∗ PDO::PARAM_INPUT_OUTPUT : Spécifie que le paramètre est un paramètre INOUT pour
une procédure stockée. Vous devez utiliser l’opérateur OR avec un type de données explicite
PDO::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 fonction
pré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 le
résultat de la requête, si elle est adéquate, pour le paramètre $parametre soit stocké en retour dans
la variable PHP $variable liée. Cette technique de retour est propre aux procédures stockée qu’on
verra peut-être plus loin.
– le paramètre optionnel $lenght ne sert que dans le cas de procédures stockées, ce qu’on verra plus
loin.
RENVOIE TRUE en cas de succès ou FALSE si une erreur survient.

• bool PDOStatement::execute ([ array $input_parameters = array() ] ) : Exécute une requête


pré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 de
positionnement : 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 les
ré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
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 celui
sur xml, et surtout un chapitre qui arrive très tard dans le livre alors qu’on a eu déjà bon nombre d’exemples
implicites 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 les
dé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 les
bogues 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 mais
rien 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 PHP


Trois 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 si
les assertions implémentés ne sont pas respectées (par exemple : une corrélation doit être comprise entre
0 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
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 la
gestion d’erreurs.
Dans php.ini, la directive display_errors = on spécifie que les erreurs sont affichés à l’écran. Idéal en
dé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écifier
les directives suivantes dans php.ini

log_errors = on
error_log = /Chemin/Vers/Fichier/Erreur/phplog.txt

Une config de développement error_reporting = E_ALL | E_STRICT (la première valeur demande
l’affichage de toutes les erreurs
E_STRICT permet d’obtenir des suggestions de PHP
pour modifier votre
code, assurant ainsi une meilleure interopérabilité et com-
patibilité de
celui-ci.
display_errors = on
log_errors = on
log_errors_max_len = 1024
track_errors = off
error_log = /Chemin/Vers/Fichier/Erreur/phplog.txt

Une config de production error_reporting = E_ALL


display_errors = off
log_errors = on
log_errors_max_len = 1024 /// donne la taille max en ko d’un message d’erreur
track_errors = off
error_log = /Chemin/Vers/Fichier/Erreur/phplog.txt

145
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 pendant


l’exécution du script. PHP possède plusieurs niveaux d’erreurs, utiliser cette fonction configure ce niveau
pendant la durée (d’exécution) de votre script. RENVOIE l’ancien niveau d’error_reporting ou le niveau
d’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 Error
affiché);
• E_WARNING : erreur intervenant quand une fonction peut malgré tout continuer son exécution. Par
exemple, l’ouverture d’un fichier ;
• E_NOTICE : erreur de plus faible importance, spécifie notamment quand une variable est utilisée alors
que non-initialisée ...

Les erreurs HTML Compte tenu de la nature double html/php d’un script php, des erreurs HTML peuvent
se produire. Dans le cas d’une erreur html, si la directive html_errors = On, toute erreur html redirige vers
le 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 le
suffixe 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
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 possible
de 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 conjonction
avec 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érer
une 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 de
constantes 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 peuvent
pas être ignorées, comme des problèmes d’allocation de mémoire, par exemple. Elle est générée en
PHP par l’utilisation de la fonction trigger_error().
– E_USER_WARNING (entier) : Comparable à E_WARNING qui indiquent un problème qui
doit être intercepté par le script durant l’exécution du script. Par exemple, appeler ereg() avec
une 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 script
a rencontré quelque chose qui peut être une erreur, mais peut aussi être un événement normal dans la
vie 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
Personnaliser ses messages d’erreurs Deux directives de php permettent de personnaliser l’affichage des
messages d’erreurs PHP :
• error_prepend_string = ”INSTRUCTION HTML” : permet de faire précéder tout affichage
d’erreur PHP d’une instruction html ;
• error_append_string = ”INSTRUCTION HTML” : permet de faire suivre tout affichage d’erreur
PHP d’une insruction html ;
Les deux directives doivent être complémentaires car si la première contient une balise ouvrante, la deuxième
doit 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év
pour 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 les
résoud. Par ailleurs, il faut aussi le recopier pour garder traces des erreurs passés ou ne pas le saturer en cas
d’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és
de 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 log
directement 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 un
fichier. 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 fonction
de la configuration de error_log. C’est l’option par défaut.

148
∗ 1 est envoyé par email à l’adresse destination. C’est le seul type qui utilise le quatrième paramètre
extra_headers.
∗ 2 n’est plus une option.
∗ 3 est ajouté au fichier destination. Une nouvelle ligne est automatiquement ajoutée à la fin de
la 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_type
est 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 la
chaone 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 exemple
MaFonctionGestionErreur 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és
quand 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 fonction
personnalisé ;

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
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 syntaxes
de contrôles (v gérer les erreurs pas le code), des failles logiques tenant à l’architecture objet apparaissent
sans pour autant déclencher d’erreurs PHP. Par exemple, si dans mon univers objet, j’ai une variable réelle
représentant une corrélation ou une Recovery du capital d’un titre, le type double de PHP est beaucoup trp
permissif car un double en PHP vaut, je simplifie, entre -∞ et +∞ alors qu’une corrélation est toujours comprise
entre -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 de
dé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 est
comprise 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 variable
ou 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’est
donc une mécanique de sécurité qui ne doit pas faire partie de la logique applicative. Par exemple, supposons
que j’étudie la corrélation Spot/vol d’une action, je m’attends à ce que cette correl soit négative, cependant elle
peut ne pas l’être, contrôler sa négativité avec une assertion n’a alors pas de sens car la positivité, quoique très
peu probable, est possible. Par contre, contrôler que la valeur absolue de la corrél ne dépasse pas strictement 1
a du sens.
Les assertions et les données externes : Si une donnée est fournie par l’extérieure, une valeur impossible est
possible soit parce que l’utilisateur s’est trompé soit qu’il est malveillant. Donc dans le cas de récupération de
donnée externe, NE JAMAIS UTILISER une assertion. Il faut utiliser des structures de contrôles classiques de
type if ... elseif...

Pour être clair, une assertion s’occupe des valeurs impossibles potentiellement générés par son code. Et un
code 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 pouvez
les 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érifier
la 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 en
production, comme des vérifications de valeur d’argument. En conditions normales, votre
code doit être en état de fonctionner si la vérification d’assertion est désactivée.

150
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ée
si le résultat est FALSE. $assertion peut être une chaine contenant une expression logique PHP sur des
variables 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 commentaires
une à une, simplement en les désactivant soit avec :

• la fonction assert_options() : mixed assert_options ( int $what [, mixed $value ] ) : permet de


modifier 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 ce
qui est configuré ; Si le paramètre est présent, il doit valoir 0 ou 1 pour les quatre premières options
et 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 la
fonction assert_options() notamment la directive assert.active à on ou off pour activer ou désactiver
les assertions ;

151
19.4.4 Ajouter un message d’erreur personnalisé à un alerte erreur généré par une assertion
fausse
Il est possible d’incorporer un message d’erreur personnalisé à la chaine contenant l’expression logique de
l’assertion. ATTENTION : Le bouquin se plante sur le sujet puisque le bouquin propose la syntaxe fausse
suivante :
assert(”EXP RESSION LOGIQU E P HP ”); // 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 (”EXP RESSION LOGIQU E /*Message d’erreur personnalisé*/ ” ) ;

Cette fois-ci, le message d’erreur personnalisé fait partie intégrante de l’instruction assert ce qui semble et est
plus correcte...

CONSEIL SUR LES ASSERTIONS : Il est recommandé de ne pas utiliser de fonction utilisateur dans
l’expression logique car les assertions ne doivent pas dépendre dans leur fonctionnement d’autre chose que
d’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écifique
exit;
}
$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
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 classe
dé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’erreur
qu’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 se
charge par ailleurs de renseigner le fichier $file et le numéro de ligne $line. 4 getteurs sont fournis de base dans
la 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
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 fonctionnement
courant de PHP. Une instance d’exception est objet comme les autres tant qu’il n’a pas été lancé. On lance
l’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 lequel
l’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’Exception
ré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
Dans le cas du gestionnaire précédant, si le type d’erreur TypeException est Exception, toutes les exceptions
lancé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 des
instances de classes dérivant de Exception ou directement de type Exception. (Dans ce dernier cas, si PHP ne
s’est pas déjà arrêté dans un bloc catch, il s’arrête automatiquement dans le premier bloc catch attrapant une
exception 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 mesure
d’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
{

/// instructions
throw 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 seront
jamais exécutés car PHP saute directement au premier catch adéquat, passant d’abord par tous les catch du
gestionnaire 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’erreurs
doit être distribué entre blocs de catch suivant qu’on souhaite ou non gérer l’exception dans le premier catch
où elle a été attrapée.

19.5.4 Gestionnaire d’exceptions par défaut

156

Vous aimerez peut-être aussi