• Время чтения ~5 мин
  • 07.03.2023

Запускаете устаревшее приложение Laravel? Получите мгновенные автоматические обновления Laravel с Laravel Shift

Наконец-то они появятся — встроенная поддержка перечислений будет добавлена в 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, а также закатить свою собственную пользовательскую реализацию.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297