Parsing de l’analyse syntaxique de fichiers PHP

Afin d’améliorer les méthodes de développement dans la société pour laquelle je travaille, j’ai mis en place un hook Subversion de type « pre-commit » pour vérifier que la syntaxe PHP des fichiers que l’on souhaite commiter est correcte. Je ne parle pas là de respect de standards de codage, juste du respect de la syntaxe PHP. Cela peut paraître inutile mais je retrouve régulièrement des fichiers avec des erreurs de syntaxes.

L’idée est simplement de récupérer la liste des fichiers impactés par le commit en cours et pour chacun d’en vérifier la syntaxe à l’aide de PHP en ligne de commande. J’ai cependant été confronté à une subtilité dont je voudrais vous faire part.

Voici le code complet du hook en question :

#!/usr/bin/php <?php $repositoryPath = $_SERVER['argv'][1]; $transaction     = $_SERVER['argv'][2]; $stderr            = fopen('php://stderr', 'w'); (..) // autres vérifications (...) // Checks PHP files for syntax errors $command = "/usr/bin/svnlook changed -t $transaction $repositoryPath"; exec($command, $lines); $errorsFound  = false; $syntaxErrors = ''; foreach ($lines as $line) {         $file = trim(substr($line, 4));         if ('.php' == substr($line, -4)) {                 // KLUDGE: PHP returns found syntax errors through stderr so "2>&1" must be added to redirect stderr to stdout                 $command = "/usr/bin/svnlook cat -t '$transaction' '$repositoryPath' '$file' | /usr/bin/php -l 2>&1";                 exec($command, $output, $returnValue);                 if (0 != $returnValue) {                         $errorsFound   = true;                         $syntaxErrors .= 'Fichier: ' . $file . PHP_EOL;                         array_pop($output);                         foreach ($output as $line) {                                 $position      = strpos($line, ':');                                 $line          = substr($line, $position + 1);                                 $syntaxErrors .= ' - ' . trim($line) . PHP_EOL;                         }                         $syntaxErrors .= PHP_EOL .  str_repeat('-', 80) . PHP_EOL;         } } if ($errorsFound) {         fputs($stderr, str_repeat('-', 80) . PHP_EOL);         fputs($stderr, 'Des erreurs de syntaxe PHP on ete detectes :' . PHP_EOL . PHP_EOL);         fputs($stderr, $syntaxErrors);         fclose($stderr);         exit(1); } fclose($stderr); exit(0);

La ligne qui nous intéresse principalement est la suivante :

$command = "/usr/bin/svnlook cat -t '$transaction' '$repositoryPath' '$file' | /usr/bin/php -l 2>&1";

La première partie, à gauche du pipe, demande à Subversion d’afficher le contenu du fichier $file du dépôt situé à $repositoryPath et cela pour la transaction $transaction. On parle ici de transaction et non de révision parce que nous sommes avant le commit. Enfin, la partie après le pipe reçoit le contenu du fichier renvoyé par Subversion et l’analyse syntaxiquement.

Je souhaitais afficher des informations sur les erreurs éventuellement rencontrées afin que le développeur sache exactement pourquoi son commit a été rejeté. Le problème est que PHP m’affichait bien les détails quand je capturais la sortie écran avec de l’output buffering mais je ne les avais pas dans la variable sensée contenir la sortie écran.

La raison est en fait toute bête mais on peut y passer un peu de temps avant de trouver si on n’a pas l’habitude du shell. Nous allons donc faire un petit rappel sur le sujet.

Sur Unix, il existe des flux standards. Ce sont des canaux pour l’entrée et la sortie de données. Ces flux sont au nombre de trois, au travers desquels les programmes peuvent faire entrer ou sortir des informations. Ceux qui nous intéressent en l’occurrence sont « stdout » et « stderr ». Le premier correspond à la sortie classique tandis que le second correspond aux messages d’erreur.

Revenons maintenant à PHP. La fonction exec() permet de récupérer la sortie dans une variable. Le problème c’est que c’est le flux « stdout » qui est renvoyé et non l’ensemble de l’affichage. Les messages envoyés à « stderr » n’y figurent donc pas, ce qui explique le problème que je rencontrais.

Heureusement, il est possible de rediriger les messages du flux « stderr » vers « stdout » en ajoutant « 2>&1 » après la commande. Dès lors, la fonction exec() renvoie bien ce que j’attends et je peux afficher les messages d’erreur au développeur.

Ce contenu a été publié dans PHP. Vous pouvez le mettre en favoris avec ce permalien.

5 réponses à Parsing de l’analyse syntaxique de fichiers PHP

  1. yann dit :

    Effectivement, j’avais eu le même genre de soucis il y’a quelques temps en voulant utiliser ffmpeg et autre à travers exec()

  2. fch dit :

    J’ai eu le même problème, mais dans un contexte tout autre, car dans vim avec la commande ":!php -l % | more", qui permet de vérifier la syntaxe du fichier en cours d’edition.
    Lorsque j’avais beaucoup d’erreur, la commande more ne faisait pas son boulot.
    J’ai mis longtemps à trouver que les erreurs étaient redirigées vers la sortie d’erreur et quemore ne pouvait pas les capturer puisqu’il ne prend que la sortie standard en entrée.
    La solution a donc été, comme dans ton cas ":!php -l % 2>&1 | more".
    Et oui, vim permet bien d’éxécuter des commandes shells sans le quitter :).

  3. @fch Tu ne lâches jamais le morceau dis-donc 🙂

  4. Sport dit :

    Premier passage sur ce blog et je trouve mon bohneur au premier article :p
    En effet, je suis en train de faire un petit soft pour éviter les étapes en console pour le commit, le merge puis faire un déploiement dans la foulée.

    Y intégrer la validation du PHP est une excellent idée, et je note ca de suite 😉

    Merci

  5. @Sport: Ravi que ce billet ait aidé quelqu’un. 🙂

Les commentaires sont fermés.