Elles sont enfin là ! Les enums (ou énumérations, si vous préférez) sont l’une des nouveautés les plus attendues de PHP 8.1. Ce sont des types qui ne peuvent être instanciées qu’avec des valeurs spécifiques. On les trouve déjà dans d’autres langages orientés objet, mais leur implémentation en PHP nécessitait auparavant des solutions de contournement (de type constante de classe par exemple).
Syntaxe de base des enums
Commençons tout de suite par un exemple parlant. Voici à quoi ressemble une enum :
enum PostStatus {
case DRAFT;
case PUBLISHED;
case ARCHIVED;
}
Le mot clé case est utilisé pour délimiter les valeurs spécifiques que l’enum accepte. Les valeurs sont référencées de la même manière que les constantes de classe :
$draft = PostStatus::DRAFT;
L’avantage des enums est qu’elles représentent une collection de valeurs constantes. Les enums se comportent de la même manière que les classes et les interfaces. Elles sont entièrement compatibles avec le système de types, vous pouvez ainsi indiquer qu’une fonction n’accepte qu’une valeur définie dans une enum.
class Post
{
public function __construct(
public PostStatus $status,
) {}
}
Dans notre exemple, créer une enum et le passer au constructeur de notre classe Post se fait de cette manière :
$post = new Post(PostStatus::DRAFT);
Voilà pour les bases. comme vous pouvez le constater, il n’y a rien de complexe. Allons voir maintenant en profondeur ce que nous réservent les enums.
Ajouter des méthodes à vos enums
Les énumérations étant construitent sur le modèle des classes, nous pouvons très bien leur ajouter des méthodes. Voici un exemple :
enum PostStatus {
case DRAFT;
case PUBLISHED;
case ARCHIVED;
public function label(): string
{
return match($this)
{
Status::DRAFT => 'Brouillon',
Status::PUBLISHED => 'Publié',
Status::ARCHIVED => 'Archivé',
};
}
}
Les méthodes peuvent être utilisées de manière classique :
$status = Status::ARCHIVED;
echo $status->label(); // Affiche 'Archivé'
A noter que vous pouvez très bien ajouter des méthodes statiques ainsi qu’utiliser le mot clé self dans vos enums.
Implémenter des interfaces
Encore une fois, les énums étant construites autour des classes, elles peuvent implanter des interfaces :
interface HasLabel
{
public function label(): string;
}
enum PostStatus implements HasLabel {
case DRAFT;
case PUBLISHED;
case ARCHIVED;
public function label(): string
{
/* ... */
}
}
Backed Enums
En interne, les valeurs des enums sont représentées par des objets. Il est néanmoins possible de leur attribuer une valeur si vous le souhaitez.
Cela peut être utile pour les enregistrer en base de données par exemple :
enum PostStatus: int {
case DRAFT = 0;
case PUBLISHED = 1;
case ARCHIVED = 2;
}
Faites bien attention à la déclaration de type dans la définition de l’enum. Elle indique que toutes les valeurs de l’enum sont d’un type donné. Notez que seuls les types int et string sont autorisés comme valeurs d’enum.
Vous pourriez très bien utiliser des chaînes de caractères comme valeurs :
enum PostStatus: string {
case DRAFT = 'brouillon';
case PUBLISHED = 'publié';
case ARCHIVED = 'archivé';
}
Ce type d’enums, les énumérations typées, est appelé “backed enums” car ils sont “soutenus” par une valeur plus simple. Si vous décidez d’attribuer des valeurs aux enums, tous les cas doivent avoir une valeur. Vous ne pouvez pas mélanger les types.
Les enums qui ne sont pas “adossés” sont appelés “enums purs” ou “pure enums”.
Implémenter une interace dans une énumération soutenue
Si vous souhaitez implémenter une interface dans une “backed enum”, vous devez d’abord déclarer le type de valeur avant d’utiliser le mot clé implements :
enum PostStatus: string implements HasLabel {
/* ... */
}
Traits
Tout comme les classes, les enums peuvent utiliser des traits, mais avec quelques restrictions supplémentaires. Vous n’êtes pas autorisé à surcharger les méthodes des enums, et ils ne peuvent pas contenir de propriétés de classe.
Lister les valeurs d'une enum
Vous pouvez utiliser la méthode statique Enum::cases() pour obtenir une liste de tous les cas disponibles dans une énumération :
PostStatus::cases();
/* [
PostStatus::DRAFT,
PostStatus::PUBLISHED,
PostStatus::ARCHIVED
] */
A noter que le tableau retourné contient bien les objets des énumérations :
$status = $PostStatus::cases();
foreach ($status as $s) {
echo $status->label();
}
Accéder aux valeurs d'une enum
Lorsque vous attribuez des valeurs à vos énumérations, PHP nous donne une manière d’accéder à ces valeurs ainsi que de restorer une enum depuis ces valeurs :
$status = PostStatus::DRAFT;
echo $status->value() // Affiche "brouillon"
$status = PostStatus::from('brouillon'); // PostStatus::DRAFT
Il existe également aussi une méthode tryFrom qui renvoie null si une valeur inconnue est passée. Cela évite de lancer une exception si l’on utilise from avec une valeur inconnue :
$status = PostStatus::from('inconnu'); // VException alueError
$status = PostatusStatus::tryFrom('inconnu'); // null
Cela nous permet de sécuriser notre code comme ceci afin d’éviter les exceptions :
if (null !== PostStatus::tryFrom('brouillon')) {
$status = PostStatus::from('brouillon');
}
A noter qu’il est également possible d’utiliser les fonctions serialize et unserialize sur les enums. De plus, vous pouvez utiliser json_encode en combinaison avec des backed enums, ce qui retournera la valeur de l’enum.
Les énumérations sont des objets
Comme nous l’avons vu, les enums sont représentées par des objets singleton. Ce qui signifie que nous pouvons très bien les comparer :
$statusA = PostStatus::DRAFT;
$statusB = PostStatus::DRAFT;
$statusC = PostStatus::ARCHIVED;
$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof PostStatus; // true
Quand utiliser les enums ?
Les enums sont destinées aux cas où vous avez besoin de flexibilité dans la valeur qu’une variable peut prendre, mais seulement parmi un ensemble prédéterminé de cas possibles.
La classe des article de blog qui est utilisée dans ce billet en est un exemple classique. Les articles ne peuvent se trouver que dans l’un des états connus, mais PHP n’avait jusqu’à présent aucun moyen direct de le faire. Il fallait souvent utiliser les constantes de classes un peu de cette manière :
class Post {
const PUBLISHED = 0;
const DRAFT = 1;
public int $status;
/* ... */
}
Le problème est que l’attribut $status peut en réalité prendre n’importe quel entier comme valeur, pas seulement ce que l’on a défini. Les enums sont parfaites pour ce genre de cas.
Conclusion
Les enums sont un ajout important de la version 8.1 de PHP. Elles permettent d’indiquer que les paramètres, les valeurs de retour et les propriétés doivent faire partie d’un ensemble d’options prédéterminées.
N’hésitez pas à consulter notre article sur toutes les autres nouveautés de PHP 8.1 !