Menu

Перечисления PHP (enum): from(), tryFrom() и cases()

Перечисление PHP (8.1+) это тип с фиксированным списком значений: enum Status: string { case Active = 'active'; }. Чистые и типизированные перечисления, ->value и ->name, преобразование через from() и tryFrom(), список вариантов через cases(), методы, константы, интерфейсы и перечисления в match.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Перечисление PHP (PHP 8.1+) задаёт тип с фиксированным набором возможных значений, его вариантов: enum Status { case Active; case Banned; }. Вариант используется как Status::Active, а параметр с типом Status ничего другого не примет. Типизированное перечисление, enum Status: string, ещё и даёт каждому варианту значение, которое можно сохранить и превратить обратно через Status::from().

До перечислений то же самое делали списком строковых констант, и ничто не мешало опечатке вроде 'actve' попасть в базу данных. С Status в качестве типа параметра передать можно только три варианта.

Чистые и типизированные перечисления

Варианты чистого перечисления это только имена. Типизированное перечисление объявляет тип после имени, int или string, и тогда у каждого варианта должно быть уникальное значение этого типа. ->name есть у каждого варианта; ->value только у вариантов типизированного перечисления.

Выбирайте типизированное перечисление всякий раз, когда значение покидает программу: столбец базы данных, URL, поле формы, JSON. Чистое перечисление подходит для значений, которые существуют только внутри кода, например состояния парсера.

Преобразование значения через from() и tryFrom()

У типизированных перечислений есть два статических метода, чтобы превратить сохранённое значение обратно в вариант. from() выбрасывает ValueError для неизвестного значения; tryFrom() возвращает null, что хорошо сочетается с ?? для значения по умолчанию:

Используйте tryFrom() для всего, что прислал пользователь, и from() для значений, которые записал ваш собственный код, где неизвестное значение означает ошибку, о которой вы хотите узнать. Аргумент подчиняется обычным правилам типов параметров: у перечисления int в режиме по умолчанию Priority::from('5') возвращает Priority::Normal, потому что '5' сначала приводится к 5, Priority::from('5x') это TypeError, а под declare(strict_types=1) любая строка это TypeError. Это касается и tryFrom(): Priority::tryFrom('abc') выбрасывает исключение, а не возвращает null, поэтому ввод из формы для перечисления int сначала проверяйте через filter_var($raw, FILTER_VALIDATE_INT).

Список всех вариантов через cases()

Enum::cases() возвращает массив всех вариантов в порядке объявления. Так строят выпадающий список, проверяют список значений или получают все значения через array_column:

Запустите, выберите тариф и нажмите Choose: тот же скрипт выполнится снова с заполненным $_POST['plan'], и tryFrom() превратит отправленную строку обратно в вариант Plan. Подделанное значение вроде plan=gold даёт null, и ни один пункт не будет выбран.

Методы, константы и интерфейсы перечислений

У перечислений могут быть методы, статические методы и константы, и они могут реализовывать интерфейсы. Внутри метода $this это текущий вариант, поэтому match ($this) естественный способ привязать данные к каждому варианту. Чего у перечисления быть не может, так это свойств: данные для каждого варианта берутся из методов.

Перечисления в match

match сравнивает через ===, а вариант перечисления идентичен только самому себе, поэтому match и перечисления хорошо сочетаются. Если забыть вариант и он дойдёт до match, PHP выбросит UnhandledMatchError, а не вернёт молча пустоту:

Чем match отличается от switch, смотрите в match.

Перечисления в JSON и как строки

json_encode() записывает вариант типизированного перечисления как его значение. Чистое перечисление закодировать нельзя, а echo любого варианта завершается ошибкой, потому что перечисление это объект, а не строка. Выводите ->value или ->name:

echo Role::Admin; выбрасывает Error ("Object of class Role could not be converted to string"), а реализовать __toString() перечисления не могут. На обратном пути JSON даёт обычную строку, поэтому превращайте её через from() или tryFrom(), как показано. Подробнее о кодировании в json_encode.

Типичные ошибки с перечислениями

Три ошибки встречаются снова и снова. Вариант никогда не равен своему значению, вариант не может быть ключом массива, а new с перечислением не работает:

Если нужен словарь с ключами из самих вариантов, используйте SplObjectStorage или WeakMap: они принимают объекты в качестве ключей.

Часто задаваемые вопросы

Как получить значение enum в PHP?

У типизированного перечисления читайте ->value: Status::Active->value равно 'active'. У каждого варианта есть ещё ->name, имя варианта строкой ('Active'). У чистого перечисления (без : string или : int) есть только ->name.

Чем from() отличается от tryFrom() в перечислениях PHP?

Оба превращают значение в вариант типизированного перечисления. Status::from('active') возвращает вариант или выбрасывает ValueError, когда варианта с таким значением нет; Status::tryFrom('nope') вместо этого возвращает null. Используйте tryFrom() для пользовательского ввода, а from() там, где неверное значение означает ошибку в коде.

Как получить все значения enum в PHP?

Status::cases() возвращает все варианты в порядке объявления. Для значений типизированного перечисления используйте array_column(Status::cases(), 'value'), а для имён array_column(Status::cases(), 'name').

Чем чистое перечисление отличается от типизированного?

Варианты чистого перечисления (enum Suit { case Hearts; }) это только имена. Типизированное перечисление (enum Suit: string { case Hearts = 'H'; }) даёт каждому варианту уникальное значение int или string, которое нужно, чтобы хранить его в базе данных, передавать в форме или JSON и превращать обратно через from() или tryFrom().

Могут ли у перечисления PHP быть методы?

Да. У перечислений могут быть методы, статические методы и константы, и они могут реализовывать интерфейсы. Внутри метода $this это текущий вариант, поэтому return match ($this) { self::Active => 'green', self::Banned => 'red' }; частый шаблон. Свойств у перечислений быть не может.

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

НАЧАТЬ