Un enum de PHP (PHP 8.1+) define un tipo con un conjunto fijo de valores posibles, sus casos: enum Status { case Active; case Banned; }. Usas un caso como Status::Active, y un parámetro con el tipo Status no acepta nada más. Un backed enum, enum Status: string, le da además a cada caso un valor que puedes guardar y volver a convertir con Status::from().
Antes de los enums, esto mismo era una lista de constantes de texto, y nada impedía que una errata como 'actve' llegara a la base de datos. Con Status como tipo del parámetro, solo se pueden pasar los tres casos.
Enums puros y backed enums
Un enum puro tiene casos que son solo nombres. Un backed enum declara un tipo después del nombre, int o string, y entonces cada caso debe tener un valor único de ese tipo. Todos los casos tienen ->name; solo los casos respaldados tienen ->value.
Elige un backed enum siempre que el valor salga del programa: una columna de base de datos, una URL, un campo de formulario, JSON. Elige un enum puro para valores que solo existen dentro del código, como el estado de un analizador.
Convertir un valor con from() y tryFrom()
Los backed enums tienen dos métodos estáticos para convertir un valor guardado de vuelta en un caso. from() lanza un ValueError con un valor desconocido; tryFrom() devuelve null, que combina bien con ?? para un valor por defecto:
Usa tryFrom() con todo lo que te haya enviado un usuario, y from() con valores que escribió tu propio código, donde un valor desconocido significa un bug del que quieres enterarte. El argumento sigue las reglas normales de los tipos de parámetros: en un enum int, Priority::from('5') devuelve Priority::Normal en el modo por defecto porque '5' se convierte antes en 5, Priority::from('5x') es un TypeError, y con declare(strict_types=1) cualquier cadena es un TypeError. Eso incluye tryFrom(): Priority::tryFrom('abc') lanza un error en lugar de devolver null, así que comprueba antes la entrada de un formulario para un enum int con filter_var($raw, FILTER_VALIDATE_INT).
Listar todos los casos con cases()
Enum::cases() devuelve un array con todos los casos, en el orden en que se declararon. Así construyes un desplegable, validas una lista u obtienes todos los valores con array_column:
Ejecútalo, elige un plan y pulsa Choose: el mismo script se ejecuta otra vez con $_POST['plan'] lleno, y tryFrom() convierte la cadena enviada de vuelta en un caso de Plan. Un valor falsificado como plan=gold da null, y no se selecciona ninguna opción.
Métodos, constantes e interfaces en los enums
Los enums pueden tener métodos, métodos estáticos y constantes, y pueden implementar interfaces. Dentro de un método, $this es el caso actual, lo que hace de match ($this) la forma natural de asociar datos a cada caso. Lo que un enum no puede tener son propiedades: los datos de cada caso vienen de métodos.
Enums en match
match compara con ===, y un caso de un enum solo es idéntico a sí mismo, así que match y los enums encajan bien. Si olvidas un caso y ese caso llega al match, PHP lanza un UnhandledMatchError en lugar de no devolver nada en silencio:
Consulta match para ver en qué se diferencia match de switch.
Enums en JSON y como cadenas
json_encode() escribe un caso respaldado como su valor. Un enum puro no se puede codificar, y hacer echo de cualquier caso falla, porque un enum es un objeto, no una cadena. Imprime ->value o ->name en su lugar:
echo Role::Admin; lanza un Error ("Object of class Role could not be converted to string"), y los enums no pueden implementar __toString(). A la vuelta, el JSON da una cadena simple, así que conviértela con from() o tryFrom() como se muestra. Hay más sobre la codificación en json_encode.
Errores frecuentes con los enums
Tres errores aparecen una y otra vez. Un caso nunca es igual a su valor, un caso no puede ser una clave de array y new no funciona con un enum:
Si necesitas un mapa cuyas claves sean los propios casos, usa SplObjectStorage o WeakMap, que aceptan objetos como claves.
Preguntas frecuentes
¿Cómo obtengo el valor de un enum en PHP?
En un backed enum, lee ->value: Status::Active->value es 'active'. Cada caso tiene además ->name, el nombre del caso como cadena ('Active'). Un enum puro (sin : string ni : int) solo tiene ->name.
¿Cuál es la diferencia entre from() y tryFrom() en los enums de PHP?
Los dos convierten un valor en un caso de un backed enum. Status::from('active') devuelve el caso o lanza un ValueError cuando ningún caso tiene ese valor; Status::tryFrom('nope') devuelve null en su lugar. Usa tryFrom() con la entrada del usuario y from() cuando un valor incorrecto sea un bug.
¿Cómo obtengo todos los valores de un enum en PHP?
Status::cases() devuelve todos los casos en el orden en que se declararon. Para los valores de un backed enum usa array_column(Status::cases(), 'value'), y para los nombres array_column(Status::cases(), 'name').
¿Cuál es la diferencia entre un enum puro y un backed enum?
Un enum puro (enum Suit { case Hearts; }) tiene casos que son solo nombres. Un backed enum (enum Suit: string { case Hearts = 'H'; }) le da a cada caso un valor int o string único, que necesitas para guardarlo en una base de datos, meterlo en un formulario o en JSON, y volver a convertirlo con from() o tryFrom().
¿Un enum de PHP puede tener métodos?
Sí. Los enums pueden tener métodos, métodos estáticos y constantes, y pueden implementar interfaces. Dentro de un método, $this es el caso actual, así que return match ($this) { self::Active => 'green', self::Banned => 'red' }; es un patrón habitual. Los enums no pueden tener propiedades.