У вариантов перечислений по умолчанию нет скалярного эквивалента. Варианты перечисления — стандартные одноэлементные объекты. Иногда бывает так, что вариант перечисления требуется сохранять и считывать из базы данных или аналогичного хранилища данных, поэтому полезно указывать для перечисления встроенный скалярный — — и поэтому тривиально сериализуемый — эквивалент, который определили внутри.
Скалярный эквивалент перечислений определяют следующим синтаксисом:
<?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
?>
Вариант со скалярным эквивалентом называется типизированным (Backed Case), поскольку он «поддержан» более простым значением. Перечисление, у которого все варианты типизированные, называется «типизированным перечислением» (Backed Enum). Типизированное перечисление может содержать только типизированные варианты. Чистое перечисление может содержать только чистые варианты.
Типизированное перечисление может поддерживаться типами 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" is not a valid scalar value for enum "Suit"
print $suit->value;
$suit = Suit::tryFrom('A') ?? Suit::Spades;
// Недопустимые данные возвращают значение null, поэтому вместо этого будет использовано Suit::Spades.
print $suit->value;
?>
Ручное определение метода from()
или tryFrom()
в типизированных перечислениях
приведёт к фатальной ошибке.