Phan

Phan is a static analyzer for PHP

https://github.com/phan/phan

1. Installation

Pour l’installer, ouvrir une session DOS et se rendre dans le dossier racine de son projet afin de lancer composer require phan/phan --dev afin d’installer la dépendance mais uniquement pour le mode de développement.

Ceci fait, le script ...\vendor\bin\phan.bat est alors ajouté dans le dossier vendor de l’application.

2. Utilisation

L’exécution du script, toujours depuis une session DOS, se fait par cette commande : vendor\bin\phan.bat --allow-polyfill-parser -l src où l’option -l permet de spécifier le dossier à analyser; src dans l’exemple.

L’option --allow-polyfill-parser doit être spécifiée lorsqu’une extension nommée php-ast n’est pas installée sur l’ordinateur (ce qui est probablement le cas).

2.1. Installer php-ast

L’installation est aisée.

Sous DOS (puisque phan se lance sous DOS), il faut détecter la version de PHP utilisée, exécuter :

php -i | more

Afin de prendre connaissance du contenu de la variable concernant le fichier de configuration :

Loaded Configuration File => C:\Christophe\Tools\wamp64\bin\php\php7.1.5\php.ini

Il s’agit du fichier à éditer.

Dans ce fichier, chercher extension_dir pour identifier le dossier où se trouvent les DLL pour cette version de php. Par exemple C:\Christophe\Tools\wamp64\bin\php\php7.1.5\ext\. Se rendre dans ce dossier et y copier le fichier php_ast.dll qu’il faut télécharger. On peut le trouver sur http://windows.php.net/downloads/pecl/releases/ast/, la difficulté étant juste de savoir quel fichier récupérer. Sur ma machine Windows 10, le ZIP php_ast-0.1.6-7.1-ts-vc14-x64.zip est celui qui a fonctionné. Récupérer le fichier php_ast.dll et le copier dans le dossier des extensions.

De retour dans php.ini, retrouver la section où on charge les .dll (chercher la ligne ; Dynamic Extensions ;).

Vérifier si php_ast est déjà mentionné (en principe; non). Si pas, ajouter la ligne :

extension=php_ast.dll

Sauver le fichier. Cela devrait être bon (pas besoin de redémarrer le service Apache).

L’exécution du script vendor\bin\phan.bat ne devrait plus mentionner l’absence de php-ast.

3. Configuration

En exécutant vendor\bin\phan.bat --init --init-level=1 (chiffre entre 1 et 5; 1 étant le mode le plus strict); Phan créera un fichier .phan/config.php avec une proposition de configuration.

En éditant ce fichier, on peut intervenir sur différents paramètres comme par exemple l’analyse de code présumé mort (non appelé) : dead_code_detection (à ne pas utiliser sur des librairies de fonctions qui, par définition, contiennent du code “utilisable” mais pas forcément appelé).

En paramétrant directory_list, on peut ne plus mentionner le dossier à analyser en ligne de commandes :

'directory_list' => [
    'src'
],

Dorénavant vendor\bin\phan.bat --allow-polyfill-parser suffira.

4. Erreurs rencontrées

4.1. Ne plus afficher des erreurs

Le code ci-dessous va générer une erreur PhanUnusedVariableCaughtException Unused definition of variable $e as a caught exception car la variable $e n’est pas utilisée. Toutefois, il est obligation de mettre un nom de variable dans le catch car } catch (\Exception) { n’est pas valide.

try {
} catch (\Exception $e) {
}

Autre exemple : $key n’étant pas utilisé, une erreur PhanUnusedVariable est relevée.

foreach ($arrAssociative as $key => $value) {
    $line .= '<th>' . trim($value, '"') . '</th>';
}

Pour indiquer à Phan de ne plus mentionner l’erreur dans son analyse de code, il existe des directives dont @suppress ( https://github.com/phan/phan/wiki/Annotating-Your-Source-Code#suppress).

Dans le bloc doc-block en entête de la fonction où se trouve l’erreur, il suffit de mentionner @suppress suivi du nom de l’erreur, p.ex. :

@suppress PhanUnusedVariableCaughtException

4.2. Ignorer les erreurs sur une ligne

Parfois Phan rapporte des faux positifs comme lorsqu’il identifie une variable comme n’étant pas utilisée alors que c’est bien le cas. Par exemple, pour la variable ci-dessous, Phan pourrait indiquer PhanWriteOnlyPrivateProperty Possibly zero read references to private property \CLASS::debug. On peut forcer d’ignorer les erreurs sur une ligne avec la directive @phan-suppress-next-line:

/* @phan-suppress-next-line PhanReadOnlyPrivateProperty, PhanWriteOnlyPrivateProperty */
private $debug = false;

4.3. UndeclaredInterface

Une erreur telle que PhanUndeclaredInterface Class implements undeclared interface ... peut survenir lorsqu’on utilise une référence externe dans son code est que PHAN n’a pas chargé l’interface (ou la classe).

Exemple tout à fait valide :

use \Psr\Log\LoggerInterface as LoggerInterface;

class App implements LoggerInterface

Tout semble correct et pourtant PHAN mentionne que l’interface LoggerInterface n’est pas connue.

Pour corriger cela, il faut éditer le fichier .phan\config.php et dans le tableau directory_list, il faut mentionner le chemin vers le dossier où se trouve l’interface/la classe.

Voici la solution :

'directory_list' => [
    'src',
    'vendor/monolog/monolog/src/Monolog',
    'vendor/psr/log/Psr/Log',
    'vendor/phan/phan/src/Phan',
],