Le WordPress d'aujourd'hui, décodé pour les développeurs

Tests

Union types PHP 8 : ce que PHPStan vérifie réellement dans une suite de tests

Ajouter un type union à une fonction rassure sur le papier. Mais que détecte réellement l'analyse statique dans les tests qui appellent cette fonction ?

Par WordPress Développement • 23 avril 2021 • 5 min de lecture • Aucun commentaire
Union types PHP 8 : ce que PHPStan vérifie réellement dans une suite de tests

« PHPStan ne remonte aucune erreur, le test devrait donc être correct » : cette déduction, tentante, est en partie fausse. Une équipe qui vient d’ajouter des types union à son code source découvre souvent, avec un mélange de soulagement et de surprise, que l’analyse statique ne couvre qu’une partie de ce qu’un test vérifie réellement.

Les types union, introduits par PHP 8.0, permettent de déclarer qu’un paramètre, un retour de fonction ou une propriété accepte plusieurs types précis, par exemple int|string, plutôt que de se rabattre sur un type générique mixed qui n’apporte aucune garantie. Comprendre ce que cet ajout change concrètement pour l’analyse statique des tests évite de lui accorder une confiance excessive.

Ce qu’un type union apporte à la signature

function recupere_identifiant( int|string $article ): int {
    if ( is_string( $article ) ) {
        $post = get_page_by_path( $article, OBJECT, 'post' );
        return $post ? $post->ID : 0;
    }
    return $article;
}

Avant PHP 8.0, cette fonction aurait accepté un paramètre non typé, ou typé mixed, sans qu’aucune information ne soit portée par la signature elle-même sur les types réellement gérés à l’intérieur. Avec un type union explicite, la signature documente précisément que seuls un entier ou une chaîne sont attendus, et rien d’autre.

Ce que PHPStan vérifie à partir de cette signature

L'essentiel à retenir : Un type union documente plusieurs formes possibles en une signature ; PHPStan vérifie la cohérence, pas la valeur réelle à l'exécution ; Certaines erreurs de test échappent totalement à l'analyse statique

PHPStan analyse le code source sans l’exécuter. À partir de la signature int|string $article, l’outil peut détecter plusieurs classes de problèmes :

  • Un appel de la fonction avec un argument d’un autre type, par exemple un tableau ou un objet, directement visible dans le code appelant.
  • Une branche du corps de la fonction qui suppose implicitement un type non couvert par la vérification is_string, par exemple un accès à une propriété d’objet qui n’existerait ni sur un entier ni sur une chaîne.
  • Un retour de fonction qui ne correspond pas au type déclaré, si une branche oublie de retourner une valeur du bon type.

Ces trois vérifications se font en lisant le code, sans jamais exécuter la fonction avec de vraies données. C’est précisément là que se situe la limite à connaître pour un test.

Ce que PHPStan ne peut pas vérifier

public function test_recupere_identifiant_depuis_un_slug(): void {
    $post_id = self::factory()->post->create( [ 'post_name' => 'mon-article' ] );

    $resultat = recupere_identifiant( 'mon-article' );

    $this->assertSame( $post_id, $resultat );
}

PHPStan confirme sans difficulté que 'mon-article' est bien une chaîne acceptée par la signature int|string. En revanche, il ne peut absolument pas vérifier que la valeur retournée correspond au bon identifiant d’article : cela dépend de l’exécution réelle de get_page_by_path contre une base de données de test, un comportement qui n’existe qu’au moment où le test tourne, jamais au moment où PHPStan lit le code.

Autrement dit, l’analyse statique et le test unitaire ne se recouvrent pas : l’une garantit la cohérence des types déclarés à travers le code, l’autre garantit un comportement observé à l’exécution avec des données concrètes. Ajouter des types union améliore la première garantie, sans jamais remplacer la seconde.

Un exemple où l’un rassure faussement sans l’autre

function formate_prix( int|float $montant ): string {
    return number_format( $montant, 2 ) . ' €';
}

PHPStan valide cette fonction sans réserve : aucun type incompatible n’est appelé, aucune branche ne pose problème. Pourtant, rien dans cette analyse ne garantit que number_format produit le séparateur décimal attendu dans le contexte de locale du site, ou que le résultat final respecte réellement le format d’affichage souhaité par l’équipe. Seul un test, avec des valeurs réelles et une assertion sur la chaîne produite, peut vérifier ce point.

Un type union correctement déclaré réduit une catégorie précise de bugs, les mélanges de types incohérents, mais il ne dispense jamais d’écrire le test qui vérifie que le comportement produit la bonne valeur.

Où les deux se complètent le mieux

La combinaison la plus efficace consiste à utiliser les types union pour documenter et faire vérifier statiquement les frontières d’une fonction, tout en réservant aux tests la vérification du comportement à l’intérieur de ces frontières. Un type union bien choisi réduit d’ailleurs le nombre de cas limites qu’un test doit couvrir explicitement, puisque certains types incorrects sont désormais rejetés avant même l’exécution, par PHP lui-même à l’exécution ou par PHPStan en amont.

En résumé

Les types union de PHP 8.0 renforcent la cohérence des signatures de fonctions et permettent à PHPStan de détecter des incompatibilités de type invisibles à l’œil nu. Ils ne remplacent en rien la vérification du comportement réel qu’apporte un test d’intégration exécuté contre de vraies données. Une équipe qui confond ces deux garanties risque de sous-estimer sa couverture de test réelle, en s’appuyant uniquement sur l’absence d’erreur remontée par l’analyse statique.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi