Corrections des PHPDoc du coeur
Les stubs reprennent les blocs PHPDoc des sources de Dolibarr tels qu'ils sont. Or certains sont optimistes, d'autres franchement faux, et PHPStan les prend au mot. À partir du niveau 4, il signale alors comme erroné du code parfaitement correct.
Le fichier phpstan/dolibarr-core.stub du dépôt corrige ces déclarations. Chargez-le avec stubFiles, à côté du dossier de version :
parameters:
scanDirectories:
- %env.DOLIBARR_STUBS%/dolibarr-%env.DOLIBARR_VERSION%
stubFiles:
- %env.DOLIBARR_STUBS%/phpstan/dolibarr-core.stub
Le même fichier vaut pour toutes les versions de Dolibarr. PHPStan n'en lit que les PHPDoc : les corps de classe vides qu'il contient ne masquent pas les membres déclarés dans les stubs.
[!IMPORTANT] Référencez le fichier depuis le clone ou depuis
vendor/, ne le recopiez pas dans votre module. Une copie ne reçoit plus les corrections suivantes et diverge sans prévenir.
L'exemple qui justifie tout : fetch_object()
Database::fetch_object() est documentée @return Object, alors qu'elle renvoie null en fin de curseur. Pour PHPStan, la boucle la plus courante de Dolibarr ne s'arrête donc jamais :
$nb = 0;
while ($obj = $this->db->fetch_object($resql)) {
$nb++;
}
$this->db->free($resql);
return $nb;
Analysé au niveau 4 sur Dolibarr 18 sans le fichier de corrections :
myobject.class.php:38: While loop condition is always true.
myobject.class.php:41: Unreachable statement - code above always terminates.
Avec le fichier de corrections, plus aucune erreur. Ce couple d'erreurs est la première cause des deadCode.unreachable signalés sur un module Dolibarr : tout le code qui suit une boucle de lecture est déclaré inatteignable.
Ce qui est corrigé
| Déclaration | PHPDoc d'origine | Réalité |
|---|---|---|
Database::fetch_object(), fetch_array(), fetch_row() |
Jamais null |
null en fin de curseur |
CommonObject::$thirdparty, $contact, $user, $user_creation, $user_validation |
Objet | null tant que le fetch_*() correspondant n'a pas été appelé |
CommonObject::$ref, $name, $element, $import_key, $oldref, $model_pdf, $country_code, $multicurrency_code |
string, ou même CommonObject pour $oldref |
Chaîne ou null |
CommonObject::$status, $statut, $fk_user_creat, $fk_user_modif |
Selon la version, jusqu'à int\|array<int, string> |
Entier ou null |
CommonObject::$fields |
Forme fermée à partir de Dolibarr 21 | Tableau ouvert : le coeur et les modules y lisent d'autres clés |
CommonObject::$ismultientitymanaged, $isextrafieldmanaged, $labelStatus, $labelStatusShort |
Annotées différemment selon la version | Un seul type, valable sur toutes les versions |
Societe::$name, $address, $zip, $town, $phone, $email, $idprof1, $idprof2, $capital |
Non nullables | Colonnes nullables dans llx_societe |
Facture::$ref_client, $ref_customer, $multicurrency_code |
string |
null quand la facture n'en porte pas ou n'est pas chargée |
Conf::$entity |
Vaut 1 dans le corps de la classe | L'entité courante, variable à l'exécution |
DolibarrModules::$depends, $requiredby, $conflictwith |
string[] |
Accepte aussi la forme imbriquée que le coeur lit et que le modulebuilder documente |
User::$login, CMailFile::$msgid, CommonObjectLine::$fk_unit, CommonInvoiceLine::$tva_tx |
Non nullables ou sans PHPDoc | Peuvent valoir null |
Conséquence : les empty(), ?? et is_object() qu'un module écrit autour de ces propriétés ne sont pas redondants, et PHPStan cesse de le prétendre.
Le cas des propriétés typées différemment selon la version
Plusieurs propriétés ont changé de PHPDoc d'une version de Dolibarr à l'autre. $status par exemple est annoncée int jusqu'en Dolibarr 19, int|array<int, string> en Dolibarr 20, puis null|int|array<int, string> à partir de la 21. Un module qui redéclare cette propriété ne peut pas satisfaire toutes les versions à la fois : la règle de covariance de PHPStan exige que le type de la classe fille soit compatible avec celui du parent, et le parent change.
Le fichier de corrections fixe un seul type pour toutes les versions. C'est aussi ce qui permet à une classe de module de déclarer $fields en array<string, array<string, mixed>> sans erreur de covariance sur Dolibarr 21 et suivants.
Proposer une correction
Si vous trouvez une autre PHPDoc fausse du coeur, ouvrez un ticket ou une demande de fusion sur dolibarr-stubs-all avec :
- la classe et la propriété ou la méthode concernées ;
- le code Dolibarr qui montre le comportement réel (le
return nullen fin de curseur, la colonne nullable, la propriété remplie seulement par unfetch_*()) ; - l'erreur PHPStan qu'elle provoque sur du code correct.
Deux règles d'écriture propres à ce fichier :
- Une classe nommée dans un PHPDoc doit être déclarée dans le fichier, même vide. Écrire
@var Societe|nullsans déclarerclass Societeproduithas unknown class Societe as its type, une erreur qu'on ne peut pas ignorer. - Toujours préciser le contenu des tableaux (
array<int|string, mixed>et nonarray), et ne jamais déclarer une propriété sans@var: chaque oubli devient une erreur non ignorable dans tous les modules qui chargent le fichier.
La suite : Monter le niveau d'analyse.