Bonnes pratiques PHP #2 typage et comparaison stricte

Le typage en PHP ne doit avoir aucun secret pour toi.

Au programme de ce bonnes pratiques PHP #2 :

Introduction au typage en PHP

PHP a souvent été décrié pour son manque de typage. Mais depuis PHP 7 (2016) le typage est disponible dans PHP et doit donc être utilisé au maximum.

Typer permet, entre autres, de structurer et renforcer ton code. En effet, si le typage n’est pas respecté, PHP déclenche mécaniquement une erreur fatale. Cela peut permettre aussi d’éviter que PHP ne fasse les conversions de types qui l’arrangent le plus, et qui peuvent produire des comportements inattendus.

En PHP, il est possible de typer :

  • Les paramètres d’une fonction ou méthode
  • Le retour d’une fonction ou méthode
  • Les propriétés d’une classe
  • Prochainement, en PHP 8.3, il sera possible de typer les constantes de classe.

Pour illustrer les exemples, on va partir sur le thème de la cuisine ! J’espère que ça te donnera faim 🙂

Typer les paramètres d’une fonction ou d’une méthode en PHP

Allez, on allume le four et on découpe les légumes :

function allumerFour(int $temperature)
{
    // ...
}

function decouper(string $ingredient, int $tailleDeCoupe)
{
    // ...
}

La fonction allumerFour a un paramètre de type int, c’est à dire qu’on attend un nombre entier. La fonction decouper a 2 paramètres, chacun typé, un paramètre de type string, c’est à dire une chaine de caractères et un entier.

Peu importe le nombre de paramètres, ils peuvent tous êtres typés.

Typer le retour d’une fonction ou d’une méthode en PHP

On peut également préciser ce que la fonction (ou méthode) va retourner. On utilise la syntaxe « : » avec le type attendu.

Quand je veux allumer mon four, soit j’y arrive, soit le four a un problème, la fonction peut donc retourner « vrai » ou « faux » (true ou false), elle va donc retourner un type bool.

function allumerFour(int $temperature): bool
{
    // ...
}

Quand je découpe mon ingrédient à une certaine taille, je termine avec plein de petits bouts de cet ingrédient. La fonction va donc retourner un type array, c’est à dire un tableau car plusieurs éléments retournés.

function decouper(string $ingredient, int $tailleDeCoupe): array
{
    // ...
}

Dans la section suivante, on verra d’autres types de retour…

Typer les propriétés d’une classe en PHP

Un peu d’objet maintenant ! On reste dans le thème avec un objet Pates :

class Pates
{
    // Propriété typée avec une valeur par défaut
    public bool $aldente = false;

    // Promotion de propriétés dans le constructeur
    public function __construct(
        public string $pates,
        public int $quantite,
        public int $tempsDeCuissonIdeal
    ) // Attention, le constructeur NE PEUT PAS avoir un type de retour
    {}

    // Oh un type de retour "void", mais qu'est ce que c'est ?
    public function cuisson(int $temps): void
    {
        // ...
    }
}

J’ai typé toutes mes propriétés. On retrouve les types déjà vus : bool, int et string.

Un nouveau type de retour a fait son apparition : void. Cela signifie que la fonction ne retourne rien !

Les types disponibles en PHP

Il y a les types disponibles avec le langage qu’on a déjà vu précédemment :

  • int : un entier
  • string : une chaine de caractères
  • bool : un booléen, ne peut donc contenir que true ou false
  • array : un tableau

Mais il y en a d’autres :

  • float : un nombre décimal à virgule
  • object : l’instance d’une classe
  • callable : une fonction qui peut être appelée
  • iterable : cela signifie qu’il peut être parcouru par un foreach. Donc soit un tableau, soit un objet qui implémente l’interface Iterator
  • mixed : représente n’importe quel type !
  • ressource : représente une ressource externe, comme des fichiers ou la connexion à une base de données
  • null : ne peut contenir que la valeur « null »
  • true : ne peut contenir que la valeur « true »
  • false : ne peut contenir que la valeur « false »

Et les types dédiés aux retours :

  • void : indique qu’une fonction ne retourne pas de valeur
  • never : indique qu’une fonction ne retourne jamais rien, par exemple si elle émet une exception ou si elle déclenche (volontairement) une boucle infinie

Attention, certains de ces types ne sont disponibles qu’à partir de PHP 8.2. Selon ta version de PHP, tu n’auras peut être pas accès à tous ces types.

Mélanger les types en PHP #1 la notion de nullable

On peut définir qu’une variable est nullable, c’est à dire que pour différentes raisons, par exemple à l’initialisation du programme, elle ne contient rien, sa valeur est donc null. On utilisera la syntaxe avec un point d’interrogation « ? » :

function essai(?string $truc): ?int
{
   // ...
}

Cette notion s’applique à la déclaration du paramètre et au retour.

Mélanger les types en PHP #2 combiner plusieurs types

La combinaison de plusieurs types n’est pas recommandée, cela est parfois un indicateur que le code est mal architecturé : une fonction accepte des paramètres trop différents, une fonction retourne des choses différentes selon son usage, etc. C’est souvent le signe qu’une fonction doit être découpée en plusieurs fonctions. Mais ça peut tout de même arriver qu’on ait besoin de préciser que plusieurs types sont possibles. On utilisera la syntaxe avec un pipe « | » :

function essai2(int|string $truc): bool|string
{
   // ...
}

Une bonne illustration avec le typage de la « vieille » fonction « strpos » :

strpos(string $haystack, string $needle, int $offset = 0): int|false

On ne met pas int|bool comme type de retour, car c’est toujours false qui est retourné si la chaine $needle n’est pas présente dans $haystack, et jamais true !

Une infinité de types en PHP

Quoi ? Comment ça une infinité ? Eh bien oui ! Chaque classe créée devient un type disponible à la fois comme paramètre ou type de retour.

// Dans ma classe Pates : 
public function ajouteSauce(Sauce $sauce): void
{
    // ...
}

// Dans une classe Cuisinier
public function prepare(Recette $recette): Plat
{
    // ...
}

Typage strict en PHP

PHP a des comportements parfois qualifiés de « magiques », c’est à dire qu’ils réalisent des opérations tout seul, là où d’autres langages génèreraient des erreurs.

Une de ces opérations consiste à changer le type de variable pour s’efforcer de faire fonctionner le code.

Par exemple, ce code ne génère pas d’erreur :

function essai(string $mot)
{
   echo $mot;
}

essai(1);

// Va afficher "1"

1 est un entier, et la fonction « essai » attend une chaine de caractères ! Pas de problème pour PHP, il transforme tout seul 1 en « 1 ». Dans le même genre, il va transformer 0 en false, etc. Ce fonctionnement est un vestige de PHP et il est préférable de ne jamais s’appuyer dessus pour s’assurer qu’on maitrise à 100% le déroulé de son code.

Pour éviter ce fonctionnement, il faut préciser à PHP qu’on va respecter strictement le typage.

Il faudra alors ajouter ces lignes au tout début de chaque fichier :

declare(strict_types=1);

Avec cette ligne, le code précédent génère bien une erreur.

Les limites du typage en PHP

Typer te permet de renforcer et sécuriser ton code. En effet, PHP déclenche une erreur fatale si le typage n’est pas respecté. Pour une méthode, plus besoin de tester la nature du ou des paramètres, PHP le fera directement.

Malgré cela, il y a quelques limites… Une des plus faciles à comprendre concerne le typage « array ». Ok c’est un tableau, mais qu’est ce qu’il contient ? Nativement, PHP ne permet pas de gérer cela. Pour déclarer la structure d’un tableau, il faut utiliser la PHPDoc, c’est à dire des commentaires en amont de la déclaration de la fonction.

Par exemple un tableau d’ingredients, si chaque ingredient est une chaine de caractères :

// Dans une classe Recette par exemple :

/**
 * @var string[] $ingredients
 */
public array $ingredients

Documentation officielle

N’hésite pas à te référer à la documentation officielle pour plus d’informations sur le typage en PHP.

Comparaison stricte en PHP

Les opérateurs de comparaison stricte « === » ou « !== » permettent d’inclure la correspondance du type dans la comparaison. Et donc de comparer des choses semblables, sans se reposer sur la « magie » de PHP cf. section « typage strict »).

Exemples :

<?php
$sentence = 'Bryan is in the kitchen';
if (strpos($sentence, 'Bryan') != false) {
	echo 'Bryan is here';
}

// Ce code n'affiche rien

strpos renvoie la position de « Bryan » dans $sentence et va renvoyer false s’il ne trouve pas « Bryan ». Comme « Bryan » est au début, strpos renvoie 0. Et en PHP, 0 et false, dans une opération booléenne, c’est la même chose ! Du coup, il n’y a pas d’affichage. Il faut donc écrire :

$sentence = 'Bryan is in the kitchen';
if (strpos($sentence, 'Bryan') !== false) {
	echo 'Bryan is here';
}

// Ce code affiche bien "Bryan is here"

Avec donc « !== » au lieu de simplement « != » PHP va faire une comparaison stricte, en impliquant le type. Et du coup 0 n’est plus la même chose que false.

Encore une fois, en utilisant la comparaison stricte 100% du temps, tu renforces et sécurises ton code. Certaines erreurs (comme celle présentée dans l’exemple) sont aussi plus faciles à identifier.

Ne pas se reposer sur la magie de PHP

En complément, quelques exemples à faire attention, pour améliorer la compréhension de son code :

// Dans un if, je teste des booléens
if ($monBooleen) {
    // Aucun soucis ici   
}

// Si j'ai un nombre, un nombre d'occurence par exemple
if ($nbOccurences) {
    // PHP va considérer false si la valeur est 0, true sinon
    // La compréhension n'est pas optimale
}

// Plus précis :
if ($nbOccurences !== 0) {
   // ...
}

// Si j'ai un tableau, un tableau de compétences par exemple
if ($skills) {
    // PHP va considérer false si le tableau est vide, true sinon
    // La compréhension n'est pas optimale
}

// Plus précis :
if (! empty($skills)) {
    // ...
}

// ou encore
if ($skills !== []) {
    // ...
}

Conclusion

Le typage renforce ton code à tous les niveaux, de sa compréhension, à son exécution, en passant par sa rédaction. Car ton IDE va lire le typage (et la PHPDoc) dynamiquement pour t’aider dans la rédaction du code.

PHP a longtemps été décrié pour son manque de typage. Mais cela fait maintenant plusieurs années que le typage est disponible en PHP et il s’améliore à chaque nouvelle version. Il faut donc l’utiliser, de façon stricte.

On continue les bonnes pratiques PHP

Bonnes pratiques PHP #3 construire ses fonctions et méthodes

Pour aller + loin

D’autres articles en lien avec les concepts vus ici :


Qui a codé ce superbe contenu ?

Keep learning

Autres contenus à découvrir


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.