Il s’agit d’un bug dans atoum qui ne se produit que sous Windows et uniquement avec le code contenu dans la branche « autoloader ».
Il y a quelques jours, j’ai cherché une solution pour rendre insensible à la casse le mécanisme d’auto-chargement de classe d’atoum (l’objectif recherché ainsi que les raisons historiques du fonctionnement actuel sont hors de propos).
Pour y parvenir, j’ai donc fait en sorte que la méthode atoum\autoloader::addDirectory()
parcourt chaque répertoire qu’elle reçoit en argument pour construire un tableau de correspondance entre un nom de classe et un chemin d’accès sur le système de fichier, tableau qui est ensuite utilisé pour localiser et charger à l’aide de require
le fichier contenant la classe requise par le fonctionnement du programme.
Évidemment, ce parcourt a un impact sur les performances, et dans le cas d’atoum, il est très important, car chaque méthode de test est exécutée par défaut dans un processus PHP différent.
En conséquence, avec une implémentation trop naïve de ce mécanisme, les répertoires concernés sont donc parcourus pour chaque méthode de test, ce qui est coûteux et surtout complètement inutile.
J’ai donc modifié le moteur qui exécute chaque méthode de test de façon à ce qu’il transmette le tableau évoqué précédemment au processus PHP concerné pour supprimer ce trop coûteux et parfaitement inutile parcours de répertoire.
Pour cela, j’ai tout simplement passé en argument à la fonction var_export()
l’objet responsable de l’autochargement de classe dans atoum et j’ai envoyé le résultat au processus PHP via un pipeline ouvert à l’aide de la fonction proc_open()
.
Sous UNIX, tout a alors fonctionné sans le moindre problème et j’ai obtenu des performances tout à fait similaires à celles obtenues avec l’ancienne version du mécanisme d’autochargement de classe.
Par contre, les tests effectués par thehawk (que je remercie grandement encore une fois pour sa patience), l’un des contributeurs à atoum utilisant Windows, ont révélé un problème.
En effet, il obtient systématiquement le message d’erreur suivant lorsqu’il exécute la commande php ./bin/atoum --test-it
permettant d’exécuter l’intégralité des tests unitaires de atoum :
ERROR: PHP Parse error: syntax error, unexpected ''O:26:"mageekguy\atoum\autoloa' (T_ENCAPSED_AND_WHITESPACE), expecting ')' in - on line 1
Évidemment, nous avons fait quelques vérifications et je peux déjà vous dire que :
- Le code transmis au processus PHP est bien un code PHP syntaxiquement valide, car l’utilisation de la commande
php -l
l’a confirmé. - Le code transmis au processus PHP est parfaitement exécutable et génère le résultat escompté sous Windows comme sous UNIX lorsqu’il est mis dans un fichier et exécuté en ligne de commande à l’aide de
php path/to/file.php
. - Le code généré sous Windows et envoyé au processus PHP s’exécute sans le moindre problème sous UNIX, si ce n’est qu’évidemment les chemins d’accès aux classes sont invalides et que cela provoque une erreur lors de l’inclusion des fichiers.
- Le responsable de l’erreur de syntaxe semble se cacher dans le contenu de la propriété
classes
de la classeatoum\autoloader
.
Pour obtenir ces informations, j’ai notamment remplacé l’appel à la fonction var_export()
par un appel à la fonction serialize()
.
J’ai de plus implémenté l’interface serializable
au niveau de la classe atoum\autoloader
afin de pouvoir contrôler finement ce qu’il se passe lorsqu’une de ses instances est passée en argument à serialize()
.
Pour autant, je n’ai aucune idée de l’origine du problème qui semble être lié à l’utilisation de proc_open()
puisque l’erreur de syntaxe ne se manifeste que dans ce contexte.
Je subodore que les \
utilisés dans les chemins de fichier sous Windows sont responsable, mais je n’ai pas réussi à en avoir la preuve.
Et j'ai encore plus étrange, si jamais cela ne vous suffit pas, puisque l'ajout d'un print_r($this->classes)
dans le code de la méthode atoum\autoloader::serialize()
fait disparaître l'erreur de syntaxe.
Donc, si vous êtes amateur de prise de tête, que vous êtes sous Windows et que vous plonger dans les rouages de atoum ne vous effraye pas le moins du monde, je vous invite à forker le dépôt d’atoum, à vous positionner sur la branche « autoloader » et à tenter de résoudre cette énigme.
Et si d’aventure vous trouvez la solution et surtout l’explication, je vous invite à la partager ici en commentaire ou bien en tout autre lieu que vous jugerez adéquat.
Et si vous voulez des informations supplémentaires, je vous propose de me les demander soit en commentaire, soit plus simplement sur le canal IRC officiel d’atoum, à savoir ##atoum
sur le réseau Freenode.
Et j’ai failli oublier le plus important : <privatejoke>pour ceux qui ne saurait pas encore, atoum est un framework de test unitaire, simple, moderne et intuitif !</privatejoke>
11 réactions
1 De Uld - 07/03/2013, 14:46
Ca pue le caractère caché, le genre de truc du à un encodage étrangement interprété sous windows. Les erreurs à la noix du style saut de ligne ou caractère fantôme...
2 De jails - 08/03/2013, 01:31
Personnellement ça me fait plus penser à un flush oublié (surtout si un print_r fait disparaître l'erreur).
3 De kao98 - 08/03/2013, 08:54
Ce n'est pas lié aux '\'. Un simple str_replace pour remplacer les '\' par des '/' après la sérialisation de l'autoloader ne corrige pas le problème.
4 De thehawk - 08/03/2013, 09:02
Le print_r nous a permis de mettre en lumière l'élément déclencheur, le problème se situerai vers un flux qui s'engorge et bloque a 8192 octets, la solution viable pour le moment et d'attendre que le flux se vide avec un sleep(1) mais comprenez que c'est pas viable :D.
Je pense que le souci vient du fait que l'on envoi trop d'informations rapidement.
@jails : J'ai testé a tous hasard le fflush mais sans changements
5 De kao98 - 08/03/2013, 11:43
Pfiou !!
Ca y est, j'ai réussi à les exécuter ces tests sous Windows.
Le problème principal, c'est dans mageekguy\atoum\test\engines\concurrent : lorsque l'on veut écrire, à l'aide de fwrite, le code php du test à exécuté. Comme on le voit dans la doc php sur les fonction de gestion des flux (fread, fwrite, sur les fichiers, les cannaux, ...), sous windows, passé les 8Mo, ça devient hasardeux.
Dans un premier temps, j'ai tenté d'envoyer le $phpCode en bloc, et non tout d'un coup. J'ai réussi à envoyer jusqu'à 16Mo avec des blocs de 4Mo, mais pas au delà. Or, $phpCode pèse 25Mo !
J'ai alors décider qu'il fallait compresser les données. J'ai compressé la chaine sérialisée de l'autoloader avec gzdeflate, puis encodée en base64. Ainsi, $phpCode passe sous les 5Mo. Il est alors envoyé en intégralité dans le pipe, et tout se passe bien !
L'utilisation de base64 permet de sécuriser le transfert. Après passage par gzdeflate, on est susceptible d'avoir tout un tas de caractères fort étranges qui peuvent parasiter le flux.
Accessoirement, j'ai modifié le code pour que les méthode serialize et unserialize de la classe autoloader soient effectivement appelée, ce qui n'est pas le cas sur la branche "autoloader" (en tout cas, ce n'était pas le cas hier :p).
Ceci dit, le problème reste entier : le jour ou la compression n'est plus suffisante, le problème sera de retour :s
6 De mageekguy - 08/03/2013, 13:26
@kao98 : bien joué, nous étions arrivé de notre côté à la même conclusion en ce qui concerne la barrière des 8 Mo.
Il semblerait que ce soit un bug dans PHP, mais c'est encore à confirmer.
J'avais pensé à la compression, mais comme tu le fais remarquer, le problème reste entier au final, même si en l'état actuel du code de atoum, ça fonctionne.
Du coup, j'ai changé mon fusil d'épaule et les premiers résultats sont très intéressants.
7 De kao98 - 08/03/2013, 13:56
Petite erreur d'unités : on a parlé de Méga-octets. En l'occurrence, il s'agit de Kilo-octets :p
@mageekguy, effectivement, il y a bien d'autres façon de procéder Tu as déjà soumis quelque chose de neuf sur Github ?
8 De kao98 - 08/03/2013, 13:59
Pour me répondre à moi-même à ma dernière question :
https://github.com/atoum/atoum/tree...
exactement ce à quoi je pensais
9 De mageekguy - 08/03/2013, 14:07
@kao98 : thehawk étant indisponible, ton feedback sous windows m'intéresse fortement (et tu es le bienvenu sur le canal IRC de atoum, ##atoum sur le réseau Freenode).
10 De Manu1400 - 23/04/2013, 10:11
Aujourd'hui, a t-on plus d'infos sur ce bug ?
11 De mageekguy - 23/04/2013, 16:18
@Manu1400 : Il est résolu depuis longtemps.