Наконец-то они появятся — встроенная поддержка перечислений будет добавлена в PHP 8.1! Кто-то может подумать, что они давно назрели, но вы не слышите, как я жалуюсь; Я рад, что они это сделали! Этот пост посвящен подробному рассмотрению недавно добавленной функции. Если вы хотите быть в курсе таких изменений и новых функций в PHP, обязательно подпишитесь на мою рассылку.
Как обычно с моими постами о функциях PHP, мы начнем с общего обзора того, как выглядят перечисления:Преимущество перечислений заключается в том, что они представляют собой коллекцию константных значений, но, что наиболее важно, эти значения могут быть типизированы, например:
enum Status
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
}
В этом примере создание перечисления и передача его в a BlogPost
выглядит следующим образом:
class BlogPost
{
public function __construct(
public Status $status,
) {}
}
$post = new BlogPost(Status::DRAFT);
Это основы, так как вы можете видеть, что в них нет ничего сложного. Тем не менее, есть много побочных примечаний, давайте подробно рассмотрим перечисления!
# Методы
перечисления Перечисления могут определять методы, как и классы. Это очень мощная функция, особенно в сочетании с операторомmatch
:Методы можно использовать следующим образом:Статические методы также разрешены:И вы также можете использовать self
в перечислении:# Интерфейсы
enum Status
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
public function color(): string
{
return match($this)
{
Status::DRAFT => 'grey',
Status::PUBLISHED => 'green',
Status::ARCHIVED => 'red',
};
}
}
перечисления Перечисления могут реализовывать интерфейсы, как и обычные классы:
$status = Status::ARCHIVED;
$status->color(); // 'red'
enum Status
{
// …
public static function make(): Status
{
// …
}
}
enum Status
{
// …
public function color(): string
{
return match($this)
{
self::DRAFT => 'grey',
self::PUBLISHED => 'green',
self::ARCHIVED => 'red',
};
}
}
interface HasColor
{
public function color(): string;
}
enum Status implements HasColor
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
public function color(): string { /* … */ }
}
# Значения перечисления — также известные как «Поддерживаемые перечисления»
Значения перечисления представлены объектами внутренне, но при необходимости им можно присвоить значение; Это полезно, например. сериализация их в базу данных.
enum Status: string
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
}
Обратите внимание на объявление типа в определении перечисления. Это означает, что все значения перечисления относятся к определенному типу. Вы также можете сделать его .int
Обратите внимание, что только и string
разрешены в int
качестве значений перечисления.
enum Status: int
{
case DRAFT = 1;
case PUBLISHED = 2;
case ARCHIVED = 3;
}
Технический термин для типизированных перечислений называется «поддерживаемыми перечислениями», поскольку они «подкреплены» более простым значением. Если вы решите присвоить значения перечисления, все случаи должны иметь значение. Вы не можете смешивать и сочетать их. Перечисления, которые не являются «подкрепленными», называются «чистыми перечислениями».
# Резервные перечисления с интерфейсами Если
вы объединяете резервные перечисления и интерфейс, тип перечисления должен идти непосредственно после имени перечисления, а не после implements
ключевого слова.
enum Status: string implements HasColor
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
// …
}
# Сериализация резервных перечислений
Если вы присваиваете значения случаям перечисления, вам, вероятно, нужен способ их сериализации и десериализации. Их сериализация означает, что вам нужен способ доступа к значению перечисления. Это делается с помощью общедоступного свойства только для чтения:Восстановление перечисления из значения можно выполнить с помощью Enum::from
:
$value = Status::PUBLISHED->value; // 2
$status = Status::from(2); // Status::PUBLISHED
Также есть значение, которое возвращаетсяnull
, если передается неизвестное значениеtryFrom
. Если бы вы использовалиfrom
, было бы исключение.
$status = Status::from('unknown'); // ValueError
$status = Status::tryFrom('unknown'); // null
Обратите внимание, что вы также можете использовать встроенные serialize
и unserialize
функции в перечислениях. Кроме того, вы можете использовать json_encode
в сочетании с резервными перечислениями, его результатом будет значение перечисления. Это поведение можно переопределить, реализовав JsonSerializable
.
# Перечисление значений
перечисления Вы можете использовать метод staticEnum::cases()
, чтобы получить список всех доступных случаев в перечислении:Обратите внимание, что этот массив содержит фактические объекты перечисления:
Status::cases();
/* [
Status::DRAFT,
Status::PUBLISHED,
Status::ARCHIVED
] */
array_map(
fn(Status $status) => $status->color(),
Status::cases()
);
# Перечисления - это объекты Я уже упоминал, что значения перечислений представлены в виде объектов, на самом деле это одноэлементные объекты
. Это означает, что вы можете сравнивать их следующим образом:
$statusA = Status::PENDING;
$statusB = Status::PENDING;
$statusC = Status::ARCHIVED;
$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true
# Перечисления как ключи массива Поскольку значения перечислений на самом деле являются объектами, в настоящее время их невозможно использовать в качестве ключей
массива. Следующее приведет к ошибке:
$list = [
Status::DRAFT => 'draft',
// …
];
Существует RFC для изменения этого поведения, но он еще не был проголосован.
Это означает, что вы можете использовать перечисления только в качестве ключей в SplObjectStorage
и WeakMaps
.
# Черты
Перечисления могут использовать черты так же, как классы, но с некоторыми дополнительными ограничениями. Вы не можете переопределять встроенные методы перечисления, и они не могут содержать свойства класса — они запрещены для перечислений.
# Отражение и атрибуты
Как и ожидалось, для работы с перечислениями добавлено несколько классов отражения: ReflectionEnum
, ReflectionEnumUnitCase
и ReflectionEnumBackedCase
. Также есть новая enum_exists
функция, которая делает то, что следует из ее названия.
Как и обычные классы и свойства, перечисления и их случаи могут быть аннотированы с помощью атрибутов. Обратите внимание, что TARGET_CLASS
фильтр также будет включать перечисления.
И последнее: у перечислений также есть свойство $enum->name
только для чтения, которое, как упоминается в RFC, является деталью реализации и, вероятно, должно использоваться только для целей отладки. Хотя об этом все же стоит упомянуть.
Заметили тпё? Вы можете отправить PR, чтобы исправить это. Если вы хотите быть в курсе того, что происходит в этом блоге, вы можете подписаться на меня в Твиттере или подпишитесь на мою рассылку:
Вот и все, что можно сказать о перечислениях, я с нетерпением жду возможности использовать их, как только появится PHP 8.1, а также закатить свою собственную пользовательскую реализацию.