PHPStan pour les modules Dolibarr

Je ne vous présente pas phpstan, c'est un formidable logiciel libre d'analyse statique de code php. Lors du devcamp dolibarr de Lyon nous avons pu admirer comment phpstan peut nous aider à améliorer Dolibarr , comment phpstan peut nous aider à débusquer des bugs ou des bugs potentiels, nous pousser à faire du code plus propre ... et tout ceci nous a donné envie de le mettre en place pour les modules externes de dolibarr.

Ce document s'adresse donc en particulier aux développeurs de modules complémentaires pour Dolibarr.

Le problème n'est pas si simple: un module dolibarr fait appel à beaucoup de fonctions du cœur de dolibarr et lors de l'analyse du code d'un module phpstan devrait "avoir accès" à tout le code source de dolibarr pour vérifier qu'un appel de fonction du cœur est fait correctement, qu'un attribut d'un objet est bien utilisé etc..

Attention: Dolibarr provoque encore des alertes

Le cœur de dolibarr ne passe pas encore tous les tests phpstan comme vous pouvez le voir ici. De ce fait lorsque vous lancerez phpstan sur votre module dolibarr il vous remontera probablement des erreurs liées au cœur de dolibarr ... sauf si vous suivez la doc ci-dessous qui prend ça en compte.

Configuration

La configuration de phpstan est très bien documentée : https://phpstan.org/config-reference.

Exemple de fichier de configuration utilisé pour un de nos modules, vous pouvez voir qu'il est fait référence à l'arborescence dolibarr version 18

includes:
	- .phpstan.dist.neon
parameters:
	scanDirectories:
		- /home/dev/dolibarr/dolibarr-18/htdocs/
	excludePaths:
		analyse:
			- /home/dev/dolibarr/dolibarr-18/htdocs/
	bootstrapFiles:
		- .phpstan-bootstrap.php

 

Le fichier .phpstan.dist.neon est un peu particulier: c'est dans ce dernier que nous trouvons toutes les spécificités liées à dolibarr en particulier (ce fichier est d'ailleurs grandement inspiré du git de dolibarr dont il est issu).

parameters:
	customRulesetUsed: true
	level: 0
	fileExtensions:
		- php
	parallel:
		jobSize: 2
		maximumNumberOfProcesses: 2
		minimumNumberOfJobsPerProcess: 2
	checkAlwaysTrueCheckTypeFunctionCall: false
	checkAlwaysTrueInstanceof: false
	checkAlwaysTrueStrictComparison: false
	checkClassCaseSensitivity: false
	checkFunctionArgumentTypes: false
	checkFunctionNameCase: false
	checkArgumentsPassedByReference: false
	checkMaybeUndefinedVariables: false
	checkNullables: false
	checkThisOnly: true
	checkUnionTypes: false
	checkExplicitMixedMissingReturn: false
	checkPhpDocMissingReturn: false
	reportMaybes: false
	reportMaybesInMethodSignatures: false
	reportStaticMethodSignatures: false
	polluteScopeWithLoopInitialAssignments: true
	polluteScopeWithAlwaysIterableForeach: true
	reportMagicMethods: false
	reportMagicProperties: false
	ignoreErrors:
		- '#Undefined variable: \$langs#'
		- '#Undefined variable: \$user#'
		- '#Undefined variable: \$db#'
		- '#Undefined variable: \$conf#'
		- '#Undefined variable: \$hookmanager#'
		- '#Undefined variable: \$mysoc#'
		- '#Undefined variable: \$error#'
		- '#Undefined variable: \$errors#'
		- '#Undefined variable: \$form#'
		- '#Result of function fieldList#'
		- '#Caught class Stripe#'
		- '#Function llxHeader invoked with#'
		- '#Function llxHeaderVierge invoked with#'
		- '#If condition is always true#'
		- '#always exists and is not falsy#'
		- '#has no return type specified#'
		- '#is always true#'
		- '#is always fal#'
		- '#always exists and is not nullable#'
		- '#PHPDoc tag @return has invalid value#'
		- '#type has no value type specified in iterable type array#'
		- '#with no value type specified in iterable type array#'
		- '#Empty array passed to foreach#'
		- '#in isset\(\) is not nullable#'
		- '#Function GET#'
		- '#Function llx#'
		- '#Function dol#'
		- '#htmlPrintOnlinePaymentFooter#'
	internalErrorsCountLimit: 50
	cache:
	    nodesByFileCountMax: 512
	    nodesByStringCountMax: 512
	reportUnmatchedIgnoredErrors: false
	universalObjectCratesClasses:
		- stdClass
		- SimpleXMLElement
	earlyTerminatingMethodCalls: []
	dynamicConstantNames:
		- ICONV_IMPL
		- PHP_VERSION
		- PHP_MAJOR_VERSION
		- PHP_MINOR_VERSION
		- PHP_RELEASE_VERSION
		- PHP_VERSION_ID
		- PHP_EXTRA_VERSION
		- PHP_ZTS
		- PHP_DEBUG
		- PHP_MAXPATHLEN
		- PHP_OS
		- PHP_OS_FAMILY
		- PHP_SAPI
		- PHP_EOL
		- PHP_INT_MAX
		- PHP_INT_MIN
		- PHP_INT_SIZE
		- PHP_FLOAT_DIG
		- PHP_FLOAT_EPSILON
		- PHP_FLOAT_MIN
		- PHP_FLOAT_MAX
		- DEFAULT_INCLUDE_PATH
		- PEAR_INSTALL_DIR
		- PEAR_EXTENSION_DIR
		- PHP_EXTENSION_DIR
		- PHP_PREFIX
		- PHP_BINDIR
		- PHP_BINARY
		- PHP_MANDIR
		- PHP_LIBDIR
		- PHP_DATADIR
		- PHP_SYSCONFDIR
		- PHP_LOCALSTATEDIR
		- PHP_CONFIG_FILE_PATH
		- PHP_CONFIG_FILE_SCAN_DIR
		- PHP_SHLIB_SUFFIX
		- PHP_FD_SETSIZE
	paths:
		- .
	excludePaths:
		analyse:
			- vendor/*
			- test/*
	bootstrapFiles:
		- .phpstan-bootstrap.php
	errorFormat: table

Et enfin, le dernier fichier nécessaire : le fichier de bootstrap .phpstan-bootstrap.php dont le rôle est vraiment basique comme vous pouvez le voir: définir les trois constantes de base de dolibarr pour éviter d'avoir des retours erreurs à leur sujet.

<?php
//creation des constantes dolibarr, peu importe la valeur à ce stade
define('DOL_DOCUMENT_ROOT', '1');
define('DOL_DATA_ROOT', '2');
define('DOL_URL_ROOT', '3');

Le lancement de phpstan est maintenant possible sur un module au hasard dans notre liste :

Retour de la commande phpstan - pas d'erreurs
Retour de la commande phpstan - pas d'erreurs

 

Pas d'erreur ? passez la clé level: 0  à level: 1 pour voir ... et ainsi de suite jusqu'au maximum ... acceptable par votre équipe de dev (le max phpstan est à 9) ... pour l'instant si ça passe le niveau 1 c'est déjà bien !-)

 

Optimisation - utilisation des stubs

Avant tout il faut savoir que phpstan a un mécanisme de cache et qu'il s'en sert vraiment bien ! Le 1er lancement est beaucoup plus long que les suivants ... mais lisez quand même ces quelques lignes.

Cette méthode pose une question importante liée à la taille du cœur de dolibarr : par exemple un git clone de la branche 18.0 de dolibarr pèse 1.4Go

git clone https://github.com/Dolibarr/dolibarr.git --branch 18.0 --single-branch dolibarr-18

Si vous envisagez de lancer des outils de tests automatisés côté "intégration continue" de votre plate-forme de développeur vous ne pouvez pas ignorer que ça aura un fort impact (stockage, bande passante, charge cpu & ram)... tout ça multiplié par le nombre de modules dolibarr et le nombre de plate-formes visées par les tests ça peut devenir un véritable problème (sans parler d'environnement, n'en rajoutons pas encore une couche si nous pouvons optimiser ces aspects dès la conception).

Certes votre plate-forme aura elle aussi probablement un espace de cache dans lequel l'arborescence de dolibarr sera stockée et donc "mutualisée" entre les instances de tests ... mais tout de même ça occupera de l'espace disque.

Une grosse cure de minceur est possible avec l'utilisation des fichiers stubs (voir cet article pour plus de détails) surtout si vous appuyez vos tests sur des outils tels que docker qui n'auront donc pas besoin de cloner 1,4Go de données mais uniquement le fichier stub de quelques dizaines de Mo...

 

Mise en place

Récupération des stubs mis à votre disposition par CAP-REL sur https://packagist.org/packages/caprel/dolibarr-stubs ou via le dépôt git (un mirror existe aussi sut github comme indiqué sur packagist):

git clone --single-branch --branch v18.0 https://inligit.fr/cap-rel/dolibarr/dolibarr-stubs.git /home/dev/dolibarr/dolibarr-stubs/dolibarr-18

Je peux tout simplement modifier le fichier de configuration de phpstan pour lui dire de s'appuyer sur le dossier dans lequel se trouve le stub de dolibarr-18 plutôt que toute l'arborescence de dolibarr-18:

includes:
	- .phpstan.dist.neon
parameters:
	scanDirectories:
		- /home/dev/dolibarr/dolibarr-stubs/dolibarr-18/
	excludePaths:
		analyse:
			-  /home/dev/dolibarr/dolibarr-stubs/dolibarr-18/
	bootstrapFiles:
		- .phpstan-bootstrap.php

 

Et un peu plus ? : tests sur plusieurs versions de dolibarr

Rien ne vous empêche de le faire avec les sources de dolibarr mais stocker une arborescence totale de dolibarr  14 à 18 coûte "un peu" d'espace disque (quelques Go), avec des stubs c'est 60Mo ... nous avons été jusqu'à pousser le vice à vous faire un seul dépot git contenant tous les stubs des versions de dolibarr depuis la 14:

git clone https://inligit.fr/cap-rel/dolibarr/dolibarr-stubs-all.git

Vous pouvez ensuite faire autant de fichier de configuration phpstan pour chaque version de dolibarr, par exemple j'ai sous les yeux:

  • .phpstan.local_dolibarr_14.neon
  • .phpstan.local_dolibarr_15.neon
  • .phpstan.local_dolibarr_16.neon
  • .phpstan.local_dolibarr_17.neon
  • .phpstan.local_dolibarr_18.neon

Prochaine étape : automatisation des tests avec gitlab ... ça sera dans un prochain article ...

Vous pourriez également aimer...

Articles populaires

2 commentaire

  1. […] peux lancer des outils d’analyse & de tests de manière optimale (article à suivre sur phpstan par […]

  2. […] exemple si vous voulez lancer automatiquement le test avec phpstan vous pouvez créer un fichier .gitlab-ci.yml comme […]

Les commentaires sont fermés.