PHP est vicieux
Il y a quelques temps, j'ai eu un comportement très étrange avec un bout de code PHP traditionnel
.
En effet, une même fonctionnalité implémentée de deux manières différentes ne donnait pas le même résultat.
Dans un cas, PHP me générait une erreur, et dans l'autre, tout se passait sans problème.
La version qui ne posait aucun problème était la suivante :
<?php
error_reporting(E_STRICT);
class a { public function test($foo) {} }
class b extends a { public function test($foo, $bar) {} }
$b = new b();
?>
La version, qui était strictement identique fonctionnellement parlant, mais qui posait problème était quand à elle composée de trois fichiers.
Le premier fichier, nommons le a.php, contenait le code suivant :
<?php
class a { public function test($foo) {} }
?>
Le second fichier, nommons le b.php, contenait le code suivant :
<?php
class b extends a { public function test($foo, $bar) {} }
?>
Et le troisième et dernier fichier, all.php, contenait le code suivant :
<?php
error_reporting(E_STRICT);
require('a.php');
require('b.php');
$b = new b();
?>
A l'éxécution de all.php, PHP générait l'erreur suivante :
PHP Strict standards: Declaration of b::test() should be compatible with that of a::test() in /path/to/b.php on line 3
En résumé, inclure deux classes à l'aide de require() ne revenait pas au même que de déclarer les deux classes dans le même fichier...
Afin d'être sur de mon fait, j'ai demandé à mes collégues et aux personnes présentes sur #jelix sur irc de confirmer qu'ils avait bien le même comportement, et à ma grande surprise, ce fût le cas.
Vous ne me croyez pas ? cela vous semble trop gros pour être vrai ? essayez...
Il y a 85% de chances, et voir même 100% de chances si vous n'avez jamais touché à la directive error_reporting de votre php.ini, que vous obteniez le même comportement.
En effet, error_reporting a pour valeur par défaut E_ALL.
Cela signifie que lorsque PHP interpréte le code de la première version, la directive error_reporting n'a pas la valeur E_STRICT, mais bien E_ALL, puisque l'appel à error_reporting(E_STRICT) n'a pas encore été éxécuté.
En effet, c'est lors de l'éxécution du script proprement dit, et donc après son interprétation, que l'appel à error_reporting(E_STRICT) est effectué et que error_reporting prend réellement la valeur E_STRICT.
Or, avec la seconde version qui fait appel à require(), les fichiers a.php et b.php sont inclus après l'interprétation du code et lors de son éxécution. Au moment des appels à require(), error_reporting a donc la valeur E_STRICT.
Voici donc la raison pour laquelle deux codes fonctionnellement identiques n'ont pas le même comportement : l'environnement d'éxécution n'est tout simplement pas configuré de la même manière dans les deux cas.
La solution du problème est donc très simple : Il suffit de donner la valeur E_ALL|E_STRICT à la directive error_reporting dans votre php.ini.
Vicieux, non ?

Commentaires
Vicieux, oui et non, tu l'a très bien expliqué : l'erreur en question est généré lors de la compilation et la directive E_STRICT est interprété après la phase de compilation. C'est donc logique.
Carrément même !
J'avais eu le même problème, mais je n'avais pas assez creusé pour en connaitre la raison. Merci !
"PHP est vicieux"
attention, on pourrait y voir une légère inflexion de ton discours habituel
A noter qu'à partir de php 5.3, l'error_reporting est sur E_ALL | E_STRICT par défaut dans le php.ini-development
@tight:
Cela dépend de ta distribution et/ou de l'OS sur lequel tu travailles...
Il n'y a pas que Linux/Debian/Ubuntu dans la vie ;).
@fch
N'empêche que @tight a raison, le tarball des sources et le SVN contiennent bien E_ALL | E_STRICT à partir de 5.3. Si les distribs modifient cette valeur, alors c'est un autre problème.
@metagoto:
Effectivement, je confirme, E_ALL|E_STRICT est également présent par défault sous FreeBSD dans /usr/local/etc/php.ini-development.
Mais comme j'ai l'habitude d'utiliser celui de production, je suis passé à côtè.