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

По умолчанию у вариантов перечислений нет скалярного эквивалента. Это просто одноэлементные объекты. Однако существует множество случаев, когда варианты перечислений должны иметь возможность обращаться к базе данных или аналогичному хранилищу данных и обратно, поэтому полезно иметь встроенный скалярный (и, следовательно, тривиально сериализуемый) эквивалент, определённый внутренне.

Чтобы определить скалярный эквивалент для перечислений, используйте следующий синтаксис:

<?php

enum Suit: string
{
case
Hearts = 'H';
case
Diamonds = 'D';
case
Clubs = 'C';
case
Spades = 'S';
}
?>

Вариант, имеющий скалярный эквивалент, называется типизированным, поскольку он "поддерживается" более простым значением. Перечисление, у которого все варианты типизированные, называется "типизированным перечислением". Типизированное перечисление может содержать только типизированные варианты. Чистое перечисление может содержать только чистые варианты.

Типизированное перечисление может поддерживаться типами int или string и данное перечисление поддерживает только один тип за раз (то есть не допускается объединение int|string). Если перечисление помечено как имеющее скалярный эквивалент, тогда все варианты должны иметь определённый явно уникальный скалярный эквивалент. Не существует автоматически генерируемых скалярных эквивалентов (например, последовательных целых чисел). Типизированные варианты должны быть уникальными; два варианта типизированного перечисления не могут иметь одного и того же скалярного эквивалента. Однако константа может относиться к варианту, фактически создавая псевдоним. Смотрите Константы перечислений.

Эквивалентные значения должны быть строками или строковыми выражениями. Константы и постоянные выражения не поддерживаются. То есть 1 + 1 разрешено, а 1 + SOME_CONST - нет.

У типизированных вариантов есть дополнительное свойство, доступное только для чтения, value, которое является значением, указанным в определении.

<?php
print Suit::Clubs->value;
// Выведет "C"
?>

Чтобы сделать свойство value доступным только для чтения, нельзя назначить переменную в качестве ссылки на неё. То есть следующий код выдаст ошибку:

<?php
$suit
= Suit::Clubs;
$ref = &$suit->value;
// Error: Cannot acquire reference to property Suit::$value
?>

Типизированные перечисления реализуют внутренний интерфейс BackedEnum, который предоставляет два дополнительных метода:

  • from(int|string): self возьмёт скаляр и вернёт соответствующий вариант перечисления. Если вариант не найден, метод выдаст ValueError. Это полезно в тех случаях, когда входной скаляр является доверенным, а отсутствие значения перечисления следует рассматривать как ошибку остановки приложения.
  • tryFrom(int|string): ?self возьмёт скаляр и вернёт соответствующий вариант перечисления. Если вариант не найден, метод вернёт null. Это полезно в тех случаях, когда входной скаляр не является доверенным и вызывающая функция хочет реализовать свою собственную обработку ошибок или логику значения по умолчанию.

Методы from() и tryFrom() следуют стандартным правилам слабой/строгой типизации. В режиме слабой типизации допустима передача целого числа или строки и система соответствующим образом преобразует значение. Передача числа с плавающей точкой также будет работать с принудительным преобразованием. В режиме строгой типизации передача целого числа в from() в перечислении со строковой типизацией (или наоборот) приведёт к TypeError, как и передача числа с плавающей точкой при любых обстоятельствах. Все остальные типы параметров вызовут ошибку TypeError в обоих режимах.

<?php
$record
= get_stuff_from_database($id);
print
$record['suit'];

$suit = Suit::from($record['suit']);

// Недопустимые данные выдают ошибку ValueError: "X" не является допустимым скалярным значением для перечисления "Suit"
print $suit->value;

$suit = Suit::tryFrom('A') ?? Suit::Spades;

// Недопустимые данные возвращают значение null, поэтому вместо этого используется Suit::Spades.
print $suit->value;
?>

Ручное определение метода from() или tryFrom() в типизированных перечислениях приведёт к фатальной ошибке.