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