La gestion des enums en PHP

Un enum, on parle également de type énuméré ou énumération en français, "est un type de donnée qui consiste en un ensemble de valeurs constantes" (source Wikipédia). Il s'agit d'une structure très pratique, mais qui n'existe malheureusement pas nativement dans PHP.

Si l'on se réfère à la documentation officielle de PHP, il existe bien un type similaire au travers de la classe SplEnum. Mais cette structure de données a le défaut de nécessiter l'installation d'une extension supplémentaire ce qui peut ne pas être une opération triviale pour une application de production si cela n'est pas prévu en avance.

C'est pour cela qu'il existe de nombreux composants qui permettent de mettre en place un système qui se rapproche d'un enum. Pour ma part, j'utilise régulièrement myclabs/php-enum. Une bibliothèque qui m'a rendu de nombreux services à maintes reprises et qui fait très bien le travail.

Elle a pourtant un défaut qui m'a posé un problème sur un projet récemment. La plupart des composants d'enum en PHP fonctionnent suivant le même principe. Il s'agit d'une classe de base qui est étendue dans notre code et à laquelle on attache des constantes. Par exemple:

class WeekDay extends Enum
{
    public const MONDAY = 'monday';
    public const TUEDAY = 'tuedsay';
    // ...
}

Il est ensuite possible dans notre code, d'utiliser la notation WeekDay::MONDAY() pour récupérer une instance de notre enum.

Le problème que j'ai par exemple rencontré avec myclabs/php-enum est que chaque appel à WeekDay::MONDAY() retourne une nouvelle instance de classe. Cela signifie que lorsque l'on récupère deux fois le même enum.

WeekDay::MONDAY() == WeekDay::MONDAY() // true
WeekDay::MONDAY() === WeekDay::MONDAY() // false

Cela peut-être problématique car dans mon cas, il m'arrive par exemple d'utiliser la classe SplObjectStorage pour stocker des associations de données.

Par exemple, si je conçois une application qui permet à des utilisateurs d'indiquer leur humeur de la journée, je souhaite par exemple pouvoir associer un jour de la semaine (via notre enum WeekDay) avec une humeur (qui serait également géré au travers d'un enum Mood). Or si chaque élément de mon énumération est systématiquement une instance d'objet différente, il est alors impossible de travailler avec le code suivant:

$userMood = new SplObjectStorage();
$userMood->attach(WeekDay::MONDAY(), Mood::GOOD());

$userMood->contains(WeekDay::MONDAY()); // false

Pour remédier à cette problématique, j'ai découvert le composant marc-mabe/php-enum qui permet également de gérer un système d'énumération. A la différence de myclabs/php-enum, cette bibliothèque renverra toujours la même instance pour une même donnée de notre enum. Ce qui en reprenant notre code précédent, donnerait la chose suivante:

WeekDay::MONDAY() == WeekDay::MONDAY() // true
WeekDay::MONDAY() === WeekDay::MONDAY() // true

A ce stade, vous vous dites certainement que mon choix est maintenant fait et que dorénavant j'utiliserai de manière systématique marc-mabe/php-enum. Pourtant tout n'est pas aussi simple. Si on a vu un avantage d'une des bibliothèques par rapport à l'autre, il y a une fonctionnalité de myclabs/php-enum que j'affectionne tout particulièrement: le fait de pouvoir mettre les constantes en privées.

class WeekDay extends Enum
{
    private const MONDAY = 'monday';
    private const TUEDAY = 'tuedsay';
    // ...
}

C'est pour moi un énorme avantage car lorsque j'utilise un enum, je ne souhaite pas savoir comment il est construit à l'intérieur, comment il fonctionne. Je veux simplement l'utiliser. Or si les constantes sont publiques, cela signifie que la donnée est visible par le développeur et pire encore, qu'il est possible qu'il l'utilise en dehors de l'énumération.

Comme toujours dans l'ingénierie logicielle, il vous faudra faire le bon choix selon votre problématique et le choix de votre implémentation technique.