Dans cet article
- Les enums PHP sont disponibles nativement depuis PHP 8.1 et remplacent les constantes de classe pour les valeurs finies
- Il existe deux types d’enums : les Unit Enums (sans valeur) et les Backed Enums (avec valeur string ou int)
- Les enums PHP supportent les méthodes et interfaces, ce qui les rend bien plus puissants que de simples constantes
- Avec match(), les enums permettent d’éliminer les switch/case fragiles et d’obtenir un code exhaustif
- Doctrine et Symfony intègrent les enums nativement depuis Symfony 6.1 et Doctrine ORM 2.14
- Avant PHP 8.1, des bibliothèques comme myclabs/php-enum offraient un mécanisme similaire en PHP 7
Sommaire
- Qu’est-ce qu’un enum en PHP
- Unit Enum vs Backed Enum : quelle différence
- Cas 1 : gérer les statuts d’une entité
- Cas 2 : sécuriser les rôles utilisateurs
- Cas 3 : remplacer les switch par match
- Cas 4 : mapper un enum avec Doctrine
- Cas 5 : valider des données avec Symfony
- Méthodes et interfaces dans les enums
- Tableau comparatif : enums vs constantes vs classes abstraites
- Bonnes pratiques et pièges à éviter
J’utilise les enums PHP au quotidien dans mes projets depuis leur introduction en PHP 8.1. Après avoir passé des années à jongler avec des constantes de classe et des chaînes de caractères « magiques », je peux vous dire que les énumérations ont véritablement changé ma façon de structurer le code. Dans cet article, je vous présente cinq cas d’usage concrets que je rencontre régulièrement en formation BTS SIO et en développement professionnel. Vous verrez comment les enums rendent votre code plus fiable, plus lisible et surtout plus facile à maintenir.
Qu’est-ce qu’un enum en PHP
Un enum (abréviation d’énumération) est un type de données qui permet de définir un ensemble fini de valeurs possibles. C’est exactement comme un menu déroulant dans une interface : vous choisissez une option parmi celles qui existent, et rien d’autre n’est accepté. En PHP, les enums sont arrivés avec la version 8.1, publiée en novembre 2021, grâce à la RFC Enumerations approuvée par l’équipe de développement PHP.
Avant PHP 8.1, on simulait les énumérations avec des constantes de classe. Le problème ? Rien n’empêchait de passer une valeur invalide. Voici un exemple typique de ce qu’on faisait :
// Avant PHP 8.1 : fragile et non typé
class Status {
const DRAFT = 'draft';
const PUBLISHED = 'published';
const ARCHIVED = 'archived';
}
function setStatus(string $status): void {
// On accepte n'importe quelle chaîne...
}
Avec les enums, le typage est garanti par le langage lui-même :
// Depuis PHP 8.1 : sûr et typé
enum Status {
case Draft;
case Published;
case Archived;
}
function setStatus(Status $status): void {
// Seules les valeurs de l'enum sont acceptées
}
Pour définir une énumération en PHP, on utilise le mot-clé enum suivi du nom de l’énumération, puis on liste les cas possibles avec le mot-clé case. C’est aussi simple que cela. Chaque cas représente une instance singleton : Status::Draft === Status::Draft retourne toujours true.

Si vous travaillez encore en PHP 7 et que vous ne pouvez pas migrer immédiatement, sachez que des bibliothèques comme myclabs/php-enum offrent un mécanisme similaire. Je vous recommande cependant de planifier votre migration vers PHP 8.1 ou supérieur : les enums natifs sont bien plus performants et mieux intégrés à l’écosystème. Pour approfondir les bases du langage, consultez notre guide complet sur les énumérations PHP.
Unit Enum vs Backed Enum : quelle différence
PHP propose deux catégories d’enums, et comprendre leur différence est essentiel pour choisir le bon type selon votre contexte.
Un Unit Enum est une énumération sans valeur associée. Chaque cas est une entité en soi, sans représentation scalaire :
enum Suit {
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
Un Backed Enum associe chaque cas à une valeur string ou int. C’est celui que j’utilise le plus souvent en pratique, car il permet de stocker la valeur en base de données et de la sérialiser facilement :
enum Color: string {
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
}
// Accéder à la valeur
echo Color::Red->value; // 'red'
// Créer depuis une valeur
$color = Color::from('green'); // Color::Green
$color = Color::tryFrom('unknown'); // null (pas d'exception)
La méthode from() lève une ValueError si la valeur n’existe pas, tandis que tryFrom() retourne null en cas d’échec. J’utilise systématiquement tryFrom() pour traiter les données provenant de l’extérieur (formulaires, API) et from() pour les données internes dont je suis certain de la validité.
Tous les Backed Enums implémentent automatiquement l’interface BackedEnum et donnent accès à la méthode cases() pour lister tous les cas disponibles :
$allColors = Color::cases();
// [Color::Red, Color::Green, Color::Blue]
Cas 1 : gérer les statuts d’une entité
C’est probablement le cas d’usage le plus fréquent. Dans presque toutes les applications que je développe avec mes étudiants, nous avons des entités avec un cycle de vie : un article passe de brouillon à publié, une commande passe de en attente à expédiée. Les enums sont parfaits pour modéliser ces états.
enum OrderStatus: string {
case Pending = 'pending';
case Confirmed = 'confirmed';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
public function label(): string {
return match($this) {
self::Pending => 'En attente',
self::Confirmed => 'Confirmée',
self::Shipped => 'Expédiée',
self::Delivered => 'Livrée',
self::Cancelled => 'Annulée',
};
}
public function isFinal(): bool {
return in_array($this, [self::Delivered, self::Cancelled]);
}
public function canTransitionTo(self $next): bool {
return match($this) {
self::Pending => in_array($next, [self::Confirmed, self::Cancelled]),
self::Confirmed => in_array($next, [self::Shipped, self::Cancelled]),
self::Shipped => $next === self::Delivered,
self::Delivered, self::Cancelled => false,
};
}
}
Ce que j’apprécie dans cette approche, c’est que la logique métier vit directement avec les données. La méthode canTransitionTo() garantit qu’on ne peut pas passer une commande de « livrée » à « en attente ». Plus besoin de vérifications dispersées dans le code. C’est un gain de maintenabilité considérable sur les projets de plus de quelques milliers de lignes.
Cas 2 : sécuriser les rôles utilisateurs
La gestion des rôles et des permissions est un autre domaine où les enums brillent. Plutôt que de stocker des chaînes comme 'ROLE_ADMIN' et de risquer une faute de frappe qui crée une faille de sécurité, je définis un enum strict :
enum Role: string {
case User = 'ROLE_USER';
case Editor = 'ROLE_EDITOR';
case Admin = 'ROLE_ADMIN';
case SuperAdmin = 'ROLE_SUPER_ADMIN';
public function level(): int {
return match($this) {
self::User => 1,
self::Editor => 2,
self::Admin => 3,
self::SuperAdmin => 4,
};
}
public function hasAccessOver(self $other): bool {
return $this->level() >= $other->level();
}
}
// Utilisation
if ($currentUser->role->hasAccessOver(Role::Editor)) {
// Autoriser l'édition
}

Cette approche élimine une catégorie entière de bugs liés aux chaînes mal orthographiées. En formation, j’insiste beaucoup sur ce point : un simple 'ROLE_ADMN' au lieu de 'ROLE_ADMIN' peut passer inaperçu pendant des semaines avec des constantes, alors qu’un enum génère immédiatement une erreur de compilation. Le typage fort des enums protège votre application à chaque étape.
Cas 3 : remplacer les switch par match
L’expression match() introduite en PHP 8.0 forme un duo puissant avec les enums. Contrairement à switch, match() effectue une comparaison stricte et déclenche une erreur si un cas n’est pas couvert. C’est exactement ce qu’il faut pour travailler avec des enums.
enum PaymentMethod: string {
case CreditCard = 'credit_card';
case BankTransfer = 'bank_transfer';
case PayPal = 'paypal';
case Crypto = 'crypto';
}
function calculateFees(PaymentMethod $method, float $amount): float {
$rate = match($method) {
PaymentMethod::CreditCard => 0.029,
PaymentMethod::BankTransfer => 0.005,
PaymentMethod::PayPal => 0.035,
PaymentMethod::Crypto => 0.01,
};
return round($amount * $rate, 2);
}
Le vrai avantage apparaît lors de l’ajout d’un nouveau cas. Imaginons que je rajoute case ApplePay = 'apple_pay'; à l’enum. Toutes les expressions match() qui ne gèrent pas ce nouveau cas lèveront une UnhandledMatchError. Le code vous force à traiter tous les cas, ce qui est impossible avec un switch classique ou des constantes. En termes de sécurité du code, c’est un mécanisme que je considère indispensable.
Pour aller plus loin sur l’organisation de projets web complexes, je vous invite à consulter notre article sur les gestionnaires de projet informatique qui détaille les outils adaptés.
Cas 4 : mapper un enum avec Doctrine
Doctrine ORM (à partir de la version 2.14) supporte nativement les enums PHP. C’est un point crucial pour les développeurs Symfony, car la configuration est devenue extrêmement simple. Voici comment je mappe un enum dans une entité Doctrine :
use Doctrine\ORM\Mapping as ORM;
enum Priority: int {
case Low = 1;
case Medium = 2;
case High = 3;
case Critical = 4;
}
#[ORM\Entity]
class Ticket {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(type: 'string')]
private string $title;
#[ORM\Column(enumType: Priority::class)]
private Priority $priority;
public function getPriority(): Priority {
return $this->priority;
}
public function setPriority(Priority $priority): self {
$this->priority = $priority;
return $this;
}
}
L’attribut enumType indique à Doctrine de stocker la valeur du Backed Enum en base de données (ici un entier) et de la reconvertir automatiquement en enum à la lecture. Plus besoin de créer un type personnalisé Doctrine comme c’était le cas avant. Lors des migrations, la colonne est créée avec le type correspondant (VARCHAR ou INTEGER selon le backing type).
En Symfony, les formulaires supportent également les enums via le EnumType :
use Symfony\Component\Form\Extension\Core\Type\EnumType;
$builder->add('priority', EnumType::class, [
'class' => Priority::class,
'choice_label' => fn(Priority $p) => match($p) {
Priority::Low => 'Basse',
Priority::Medium => 'Moyenne',
Priority::High => 'Haute',
Priority::Critical => 'Critique',
},
]);
L’intégration entre Doctrine, Symfony et les enums PHP est aujourd’hui mature. C’est un écosystème cohérent qui facilite grandement le développement d’applications professionnelles. Pour approfondir d’autres technologies d’infrastructure, découvrez notre guide sur les formations Kubernetes.
Cas 5 : valider des données avec Symfony
Le dernier cas d’usage que je rencontre constamment concerne la validation des entrées utilisateur. Quand un formulaire ou une API envoie une valeur, il faut vérifier qu’elle appartient bien à l’ensemble des valeurs autorisées. Les enums rendent cette validation élégante et sûre.
enum Country: string {
case France = 'FR';
case Belgium = 'BE';
case Switzerland = 'CH';
case Luxembourg = 'LU';
case Canada = 'CA';
public static function fromCodeOrNull(string $code): ?self {
return self::tryFrom(strtoupper($code));
}
public static function isValid(string $code): bool {
return self::tryFrom(strtoupper($code)) !== null;
}
public function currencySymbol(): string {
return match($this) {
self::France, self::Belgium, self::Luxembourg => '€',
self::Switzerland => 'CHF',
self::Canada => 'CAD',
};
}
}

Dans un contrôleur Symfony, la validation devient triviale :
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
#[Route('/api/products')]
public function list(
#[MapQueryParameter] ?Country $country = null
): JsonResponse {
// $country est automatiquement converti depuis le query parameter
// Si la valeur est invalide, Symfony renvoie une 404
$products = $this->repository->findByCountry($country);
return $this->json($products);
}
Symfony résout automatiquement le paramètre de requête en enum grâce au BackedEnumValueResolver. Si la valeur envoyée ne correspond à aucun cas de l’enum, une erreur HTTP est renvoyée sans qu’on ait besoin d’écrire la moindre logique de validation. C’est un gain de temps appréciable et une source de bugs en moins.
Méthodes et interfaces dans les enums
Ce qui distingue les enums PHP de ceux d’autres langages, c’est leur capacité à implémenter des interfaces et à contenir des méthodes. Un enum ne peut pas hériter d’une classe (on ne peut pas faire extends), mais il peut implémenter autant d’interfaces que nécessaire.
interface HasDescription {
public function description(): string;
}
interface Colorable {
public function hexColor(): string;
}
enum Badge: string implements HasDescription, Colorable {
case Bronze = 'bronze';
case Silver = 'silver';
case Gold = 'gold';
case Platinum = 'platinum';
public function description(): string {
return match($this) {
self::Bronze => 'Débutant : premiers pas réussis',
self::Silver => 'Intermédiaire : compétences confirmées',
self::Gold => 'Avancé : expertise reconnue',
self::Platinum => 'Expert : maîtrise complète',
};
}
public function hexColor(): string {
return match($this) {
self::Bronze => '#CD7F32',
self::Silver => '#C0C0C0',
self::Gold => '#FFD700',
self::Platinum => '#E5E4E2',
};
}
public function minPoints(): int {
return match($this) {
self::Bronze => 0,
self::Silver => 100,
self::Gold => 500,
self::Platinum => 2000,
};
}
public static function fromPoints(int $points): self {
return match(true) {
$points >= 2000 => self::Platinum,
$points >= 500 => self::Gold,
$points >= 100 => self::Silver,
default => self::Bronze,
};
}
}
Les enums supportent aussi les traits, ce qui permet de partager du comportement entre plusieurs enums. En revanche, un enum ne peut pas avoir de propriétés d’instance : seules les constantes et les méthodes sont autorisées. C’est une limitation volontaire, car un enum représente une valeur fixe et non un objet mutable. Comme le précise la documentation officielle PHP sur les énumérations, les enums sont des « classes fermées » dont les instances sont créées par le langage.
Pour les développeurs qui souhaitent approfondir la gestion de la sécurité dans leurs applications, notre article sur Ansible Vault et le chiffrement sécurisé complète bien cette thématique.
Tableau comparatif : enums vs constantes vs classes abstraites
Pour vous aider à choisir la bonne approche selon votre version de PHP et vos contraintes, voici un comparatif détaillé des trois stratégies que je rencontre en pratique :
| Critère | Constantes de classe | Classe abstraite (PHP 7) | Enum natif (PHP 8.1+) |
|---|---|---|---|
| Typage strict | Non (valeur scalaire) | Partiel (via type-hint) | Oui (type natif) |
| Méthodes intégrées | Non | Oui | Oui |
| Interfaces | Non | Oui | Oui |
| Sérialisation JSON | Manuelle | Manuelle | Automatique (Backed) |
| Support Doctrine | Type custom requis | Type custom requis | Natif (enumType) |
| Support Symfony Forms | ChoiceType manuel | ChoiceType manuel | EnumType natif |
| Comparaison | === sur les valeurs | === sur l’instance | === sur l’instance (singleton) |
| Listing des cas | Réflexion requise | Méthode manuelle | cases() natif |
| Performance | Optimale | Bonne | Excellente (optimisé par l’engine) |
| Version PHP minimale | PHP 5.0+ | PHP 7.0+ | PHP 8.1+ |
Le choix est clair pour les nouveaux projets : si vous êtes en PHP 8.1 ou supérieur, utilisez systématiquement les enums natifs. Pour les projets legacy en PHP 7, la migration vers les enums natifs devrait faire partie de votre feuille de route technique. Le gain en fiabilité et en maintenabilité justifie amplement l’effort de migration.
Bonnes pratiques et pièges à éviter
Après plusieurs années d’utilisation quotidienne des enums PHP, voici les règles que j’applique systématiquement et que je transmets en formation :
Privilégiez les Backed Enums dès que la valeur doit être persistée (base de données, fichier, API). Les Unit Enums conviennent uniquement pour la logique interne qui ne quitte jamais le runtime PHP.
Utilisez tryFrom() pour les données externes. Les données provenant de formulaires, d’API ou de fichiers CSV peuvent contenir des valeurs inattendues. La méthode tryFrom() retourne null proprement, tandis que from() lève une exception. Réservez from() aux cas où vous êtes certain que la valeur est valide.
Ne tentez pas d’étendre un enum. Les enums PHP sont des classes finales par nature. Si vous avez besoin de partager du comportement, utilisez des interfaces et des traits. C’est une question qui revient souvent en formation : « Comment faire un extends sur un enum ? » La réponse est que l’architecture doit s’appuyer sur la composition plutôt que l’héritage.
Gardez vos enums focalisés. Un enum avec 30 cas est probablement un signe que vous mélangez des concepts. Préférez plusieurs petits enums cohérents à un seul enum fourre-tout.
Nommez vos cas en PascalCase. C’est la convention établie par la communauté PHP et recommandée par les recommandations PER du PHP-FIG. Les valeurs des Backed Enums suivent généralement la convention snake_case pour les strings.
Testez les transitions d’état. Si votre enum modélise un cycle de vie avec des méthodes comme canTransitionTo(), écrivez des tests unitaires pour chaque transition valide et invalide. Un oubli dans une matrice de transition peut avoir des conséquences importantes en production.
Pour les équipes qui automatisent leur déploiement, les enums s’intègrent parfaitement dans les pipelines CI/CD. Découvrez notre article sur le module Ansible Copy pour automatiser le déploiement de vos applications PHP.
À retenir
- Utilisez Backed Enums (avec valeur string ou int) pour toute donnée stockée en base ou exposée via API
- Combinez match() avec vos enums pour garantir que chaque cas est traité et éviter les bugs silencieux
- Préférez tryFrom() à from() pour valider les données provenant de l’extérieur (formulaires, API)
- Ajoutez des méthodes métier directement dans vos enums (labels, permissions, transitions) pour centraliser la logique
- Migrez vos constantes de classe existantes vers des enums natifs dès que votre projet tourne en PHP 8.1+
Questions fréquentes
C’est quoi le type enum ?
Le type enum (énumération) est un type de données structuré qui définit un ensemble fini et immuable de valeurs possibles. En PHP, introduit avec la version 8.1, un enum fonctionne comme une classe spéciale dont les instances sont fixes et prédéfinies. Par exemple, un enum Saison ne peut contenir que quatre valeurs : Printemps, Été, Automne, Hiver. Le typage strict garantit qu’aucune autre valeur ne peut être utilisée à la place, ce qui rend le code plus fiable que des constantes ou des chaînes de caractères.
Qu’est-ce qu’une énumération en PHP ?
Une énumération en PHP est une structure déclarée avec le mot-clé enum qui liste un ensemble de cas possibles via le mot-clé case. Il en existe deux types : les Unit Enums (sans valeur scalaire associée) et les Backed Enums (avec une valeur string ou int). Les enums PHP supportent les méthodes, les interfaces et les traits, mais ne peuvent pas avoir de propriétés d’instance ni être étendus. Ils sont disponibles depuis PHP 8.1 et sont intégrés nativement dans Symfony et Doctrine.
Comment définir une énumération en PHP ?
Pour définir une énumération en PHP, utilisez le mot-clé enum suivi du nom, puis listez les cas avec case. Pour un Unit Enum : enum Direction { case North; case South; }. Pour un Backed Enum avec valeurs : enum Direction: string { case North = 'north'; case South = 'south'; }. Le type de backing (string ou int) se précise après le nom de l’enum avec deux-points. Vous pouvez ensuite ajouter des méthodes et implémenter des interfaces directement dans la déclaration.
Comment utiliser la fonction enum ?
Enum n’est pas une fonction mais un mot-clé de langage. Pour utiliser un enum, référencez ses cas avec la syntaxe MonEnum::MonCas. Pour les Backed Enums, utilisez MonEnum::from('valeur') pour créer une instance depuis une valeur (lève une exception si invalide) ou MonEnum::tryFrom('valeur') pour un retour null en cas d’échec. La méthode MonEnum::cases() retourne un tableau de tous les cas disponibles. La propriété ->value donne la valeur scalaire d’un Backed Enum, et ->name donne le nom du cas.
Peut-on utiliser les enums PHP avec une API REST ?
Oui, les Backed Enums s’intègrent parfaitement dans les API REST. Lors de la sérialisation JSON, utilisez la propriété ->value pour convertir l’enum en sa valeur scalaire. En désérialisation, tryFrom() permet de valider et convertir la donnée reçue. Symfony gère cette conversion automatiquement grâce au composant Serializer et aux ParamConverters. Vous pouvez aussi implémenter l’interface JsonSerializable dans votre enum pour personnaliser la sérialisation.
Quelle est la différence entre enum PHP et enum Java ou TypeScript ?
Les enums PHP se rapprochent des enums Java par leur capacité à contenir des méthodes et à implémenter des interfaces, mais sans support de l’héritage. Contrairement aux enums TypeScript qui sont essentiellement des mappings clé-valeur, les enums PHP sont de véritables objets singleton typés. PHP impose un backing type unique (string ou int), là où Java autorise n’importe quel type. Les enums PHP supportent les traits mais pas les propriétés d’instance, ce qui les rend plus contraints mais aussi plus prévisibles.
Formatrice IT indépendante depuis 2016, ancienne étudiante BTS SIO SLAM. 6 ans d'expérience en entreprise.