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

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

Скалярный эквивалент перечислений определяют следующим синтаксисом:

<?php

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

?>

Вариант со скалярным эквивалентом называется типизированным (англ. Backed Case), поскольку «поддерживается» упрощённым значением. Перечисление с типизированными вариантами называется «типизированным перечислением» (англ. Backed Enum). Типизированное перечисление допускает только типизированные варианты, а чистое перечисление — только чистые.

Перечисления допускают поддержку единственным типом — int или string, поэтому типы нельзя объяединять: int|string. Для каждого варианта типизированного перечисления потребуется явно определить уникальный скалярный эквивалент. Перечисление не генерирует вариантам скалярные эквиваленты наподобие последовательных целых чисел автоматически. PHP проверит уникальность скалярных значений типизированных вариантов перечисления, поэтому двум вариантам типизированного перечисления нельзя присваивать один и тот же скалярный эквивалент. При этом константам разрешается ссылаться на варианты перечисления, что фактически создаёт псевдоним варианта. Смотрите главу «Константы перечислений».

Значения скалярных эквивалентов указывают как строки или строковые выражения. До PHP 8.2.0 варианты перечислений поддерживали только литералы или литеральные выражения, константы и константные выражения не поддерживались. Поэтому выражения наподобие 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() в типизированных перечислениях приведёт к фатальной ошибке.

Добавить

Примечания пользователей

Пользователи ещё не добавляли примечания для страницы
To Top