Minimisez les étourderies avec les énumérations dans PHP 8.1

Avatar de Benjamin Crozat.

Publié le par Benjamin Crozat

Temps de lecture estimé : 3 minutes

Les énumérations sont un ajout important de PHP 8.1. Apprendre à les utiliser vous permettra d'écrire du code plus rigoureux moins susceptible aux régressions.

À quoi servent les enums ?

Si je paraphrase la définition des enums donnée par php.net, on obtient quelque chose dans ce goût-là : les enums permettent de définir un ensemble fixe de valeurs pour un type donné.

Pas très parlant n’est-ce pas ? Dans ce genre de situation, rien ne vaut la pratique.

Comment utiliser les enums ?

Imaginons une classe Pokemon avec trois propriétés : name, firstType et secondType. Chacune d’entre elles demande un string. Sauf pour la troisième qui peut également être null.

class Pokemon {
public function __construct(
public readonly string $name,
public readonly string $firstType,
public readonly ?string $secondType = null,
) {
}
}

Créons maintenant notre premier Pokémon : Bulbizarre (ou Bulbasaur en anglais) :

new Pokemon(
name: 'Bulbasaur',
firstType: 'Grass',
secondType: 'Poison'
);

Parfait n’est-ce pas ? Cela semble propre. Mais disons que nous avons fait une faute de frappe dans un des types de notre Pokémon et que nous ne l’avions pas remarqué. Pire encore, nous avons créé les centaines d’autres Pokémon existants et il est fort probable que nous ayons fait encore plus d’erreurs.

Heureusement, depuis PHP 8.1, les énumérations (ou enums) peuvent nous venir en aide. Dans l’exemple ci-dessous, nous définissons un enum Type contenant 18 cas. Chaque cas représentant un type qu’il est possible d’assigner à un Pokémon.

enum Type {
case Bug;
case Dark;
case Dragon;
case Electric;
case Fairy;
case Fighting;
case Fire;
case Flying;
case Ghost;
case Grass;
case Ground;
case Ice;
case Normal;
case Poison;
case Psychic;
case Rock;
case Steel;
case Water;
}

Mettons à jour notre classe Pokemon pour qu’elle accepte des enums Type plutôt que des strings.

class Pokemon {
public function __construct(
public readonly string $name,
public readonly Type $firstType,
public readonly ?Type $secondType = null,
) {
}
}
 
new Pokemon(
name: 'Bulbasaur',
firstType: Type::Grass,
secondType: Type::Poison
);

Dorénavant, il sera impossible d’assigner une valeur erronée au type d’un Pokémon. Si vous essayez, vous vous retrouverez avec ce genre d’erreur :

new Pokemon(
name: 'Bulbasaur',
firstType: Type::Grass,
// Undefined constant Type::Poisson
secondType: Type::Poisson
);

Nous voici donc avec l’ensemble de valeurs fixes pour un type donné dont je parlais dans l’introduction de cet article. 😅

Les énumérations typées

Les énumérations typées sont très utiles. Dans notre exemple, nous pouvons ajouter le type string à notre enum.

enum Type: string {
case Bug = 'Bug';
case Dark = 'Dark';
case Dragon = 'Dragon';
}

Imaginons maintenant que nous récupérions nos Pokémon depuis un web service. Il serait tout à fait possible de transformer la chaîne de caractères issue du JSON en une valeur de type Type. Ce qui donnerait :

$pokemon = fetch_pokemon_from_web_service();
 
// object(Pokemon)#1 (3) {
// ["name"]=>
// string(9) "Bulbasaur"
// ["firstType"]=>
// enum(Type::Grass)
// ["secondType"]=>
// enum(Type::Poison)
// }
var_dump($pokemon);

Avis aux fans des DTO ! 👍 (Par exemple spatie/data-transfer-object, qui exploite pleinement les enums.)

Lister les valeurs d’une énumération

Et si nous affichions la totalité des types sur notre site à l’aide d’une liste ? Écrivons du PHP WordPress-fashioned.

<ul>
<?php foreach (Type::cases() as $type) : ?>
<li><?php echo $type->value; ?></li>
<?php endforeach; ?>
</ul>

Les méthodes d’énumération

Chaque type est associé à une couleur. Nous pourrions à l’intérieur de notre enum définir une méthode color() capable de retourner la couleur d’un type donné.

enum Type: string {
 
public function color() : string
{
return match($this) {
static::Fire => 'red',
static::Grass => 'green',
static::Water => 'blue',
};
}
}

Ensuite, égayons un peu notre liste avec une couleur d’arrière-plan pour chaque type :

<ul>
<?php foreach (Type::cases() as $type) : ?>
<li style="background-color: <?php echo $type->color(); ?>">
<?php echo $type->value; ?>
</li>
<?php endforeach; ?>
</ul>

Les méthodes statiques d’énumération

Et si nous avions besoin d’un tableau contenant toutes les valeurs possibles au format string de Type plutôt que d’utiliser Type::cases() comme dans le premier exemple ?

enum Type: string {
 
public static function values() : array {
return array_map(fn ($i) => $i->value, static::cases());
}
}

Les constantes d’énumération

Les enums peuvent aussi contenir des constantes. Cela peut se révéler très pratique dans notre cas où nous aimerions connaitre le multiplicateur de dégâts d’un type face à d’autres. Nous pouvons créer une méthode multiplierAgainst() qui se servira des trois constantes suivantes :

enum Type: string {
public const NO_EFFECT = 0;
 
public const NOT_EFFECTIVE = 0.5;
 
public const EFFECTIVE = 1.0;
 
public const SUPER_EFFECTIVE = 2.0;
 
public function multiplierAgainst(self $firstType, ?self $secondType = null) : float {
}
}
 
// 0
echo Type::Normal->multiplierAgainst(Type::Ghost);

Conclusion

J’espère que cet article vous aura permis de rendre le sujet moins mystérieux à vos yeux et qu’il vous aura inspiré pour vos projets.

Mais ce n’est pas tout ! Saviez-vous qu’en plus des constantes et des méthodes, il est possible d’utiliser des Traits ? Ou que Laravel vous aide à valider vos champs de formulaires grâce aux enums ?

Bref, vous l’aurez compris, seule votre imagination limite la manière dont vous pourriez tirer parti des enums.

Continuez votre lecture et devenez un expert en PHP :

0 commentaire

Besoin d'aide ? Envie de partager ?
Inscrivez-vous ou connectez-vous d'abord.

Ça vous a plu ?
Abonnez-vous à la newsletter !

Recevez régulièrement news, trucs et astuces à propos de Laravel et son ecosystème.

Autres articles à lire