Ensure

Faire du code plus strict pour les analyseurs statiques

Quand on commence à utiliser un analyseur statique comme PHPStan ou Psalm, on est vite confronté à des soucis de typage où le type est "évident" pour le développeur mais pas pour l'outil.

A fortiori si on veut avoir du type safe sans prendre de risque sur du legacy au niveau de la covariance et contravariance des méthodes, il faut trouver un moyen de dire à l'analyseur statique ce qu'on fait.

D'un autre point de vue, sur du code de domaine, des entités par exemple, où l'on veut valider à minima des types non évidents (liste d'objets par exemple), ca peut vide devenir fastidieux.

Besoin

L'écosystème php est déjà rempli de lib comme beberlei/assert qui permettent de faire des "assertions", php a lui-même des fonctions pour ça.

Le problème de ces outils, c'est qu'ils font implicitement de la mutation de type pas toujours évident à comprendre pour l'analyseur statique. Exemple :

function double($number): int
{
    // ici $number est un mixed
    Assertion::isInt($number);
    // ici $number est forcément un int, mais c'est pas trivial

    return 2 * $number;
}

Pour mes projets, j'en avais marre d'avoir toujours les mêmes complications, j'ai donc changé de paradigme en créant des méthodes "proxy" qui permettent de changer le type de manière explicite.
Pour cele j'ai introduit 2 notions :

  1. Ensure : il s'agit de s'assurer avec fermeté du type d'entrée et de le retourner typé.
  2. Enforce : il s'agit de s'assurer du type d'entrée en s'autorisant un cast safe et de le retourner typé.
    Ce que j'appele "cast safe" c'est par exemple la string "123" en int 123

Exemple :

function double($number): int
{
    // ici on vient réaffecter $number par le bon type pour garantir
    // la bonne compréhension par l'analyseur statique
    $number = Enforce::int($number);

    return 2 * $number;
}

La différence de notions entre Assert, Ensure et Enforce est subtile et leurs applications ne sont pas les mêmes. En tout cas, cette petite lib me fait gagner pas mal de temps désormais dans mon code perso.

Détails

Pour plus de détails techniques, allez voir le README sur Gitlab.

Liens