PHP 11900 ~ 6 мин.

PHP 8.1: Enums (Перечисления)

PHP 8.1: Enums (Перечисления)

Вот и подошли вкусности, Enums точно появятся!

Встроенная поддержка перечислений будет добавлена ​​в PHP 8.1. Кто-то считает, что перечисления не нужны, а если совсем уж приспичило, то можно воспользоваться сторонними пакетами, как например Spatie/Enum , ну а кто-то, в том числе и я, считает, что PHP не хватает нативного функционала по Enums. И я рад, что контрибьютерам PHP удалось наконец-то воплотить это в реальность. 

Итак, Enumeration или для краткости Enum - это перечислимый тип, который имеет фиксированное количество возможных значений. 

Например, масти в колоде игральных карт. В колоде есть четыре масти, и они фиксированы: трефы , бубны , черви и пики. В PHP эти масти можно перечислить с помощью Enum:


enum Suit {
    case Clubs;
    case Diamonds;
    case Hearts;
    case Spades;
}

 С Suit Enum теперь возможно принудительно применять типы при принятии или возврате значения масти:


function pick_card(Suit $suit) {}


pick_card(Suit::Clubs);
pick_card(Suit::Diamonds);
pick_card(Suit::Hearts);
pick_card(Suit::Spades);

Или пример использования, что чаще всего мне как раз и приходиться использовать, статусы заказа в интернет-магазине:


enum Status
{
    case CREATED;
    case COMPLETED;
    case CANCELED;
}

Теперь в модели заказа Order мы можем использовать статусы следующим образом:


class Order
{
    public function __construct(
        public Status $status, 
    ) {}
}


$post = new Order(Status::CREATED);

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

Enum синтаксис

PHP 8.1 резервирует и использует ключевое слово enum для объявления Enums. Синтаксис аналогичен синтаксису трейта/класса/интерфейса:


enum Status
{
    case CREATED;
    case COMPLETED;
    case CANCELED;
    case REFUNDED;
    case FAILED;
}


Чувствительность к регистру

Само имя Enum нечувствительно к регистру , и оно соответствует тому, как PHP обрабатывает классы и функции без учета регистра. Отдельные регистры в Enum нечувствительны к регистру.


enum Status
{
    case created;
    case Completed;
    case canCeleD;
}


Enum методы 

Перечисления могут содержать методы, как и классы. Это очень мощная функция, особенно в сочетании с оператором match:


enum Status
{
    case CREATED;
    case COMPLETED;
    case CANCELED;

    public function color(): string
    {
        return match($this) 
        {
            Status::CREATED => 'grey',   
Status::COMPLETED => 'green',
Status::CANCELED => 'red'
}; } }

 и далее использовать в коде как:


$status = Status::COMPLETED;
$status->color(); // "green"

 Разрешено использовать статические методы. Также обратите внимание, что вы можете использовать в перечислении self:


enum Status
{
    // …
    
    public function color(): string
    {
        return match($this) 
        {
            self::CREATED => 'grey',   
            self::COMPLETED => 'green',   
            self::CANCELED=> 'red',   
        };
    }
}



Enum интерфейсы  

Перечисления могут реализовывать интерфейсы, как и обычные классы:


interface HasColor
{
    public function color(): string;
}


enum Status implements HasColor
{
    case CREATED;
    case COMPLETED;
    case CANCELED;
    
    public function color(): string { /* … */ }
}

Перечисления не должны содержать свойств

Одно из наиболее важных различий между Enum и классом заключается в том, что Enums не могут иметь никакого состояния. Объявление или установка свойств не разрешены. Статические свойства также не допускаются. 


enum Foo {
    private string $test;
    private static string $test2;
}

// Fatal error: Enums may not include member variables in ... on line ...

Кроме того, не допускается динамическая установка свойств:


enum Foo {
    case Bar;
}
$bar = Foo::Bar;
$bar->test = 42;

// Error: Enum properties are immutable in ...:...

 

Enum значения  или "Поддерживаемые перечисления"

Изначально значения Enum представлены внутри объектами, но вы можете присвоить им какое-то значение, например для сериализации их в базу данных.


enum Status: string
{
    case CREATED = 'created';
    case COMPLETED = 'completed';
    case CANCELED = 'canceled';
}

Обратите внимание на объявление типа в определении перечисления. Он указывает, что все значения перечисления имеют данный тип. Вы также можете сделать их целочисленными - int. Обратите внимание, что разрешены только типы int и string, как значения перечисления.  


enum Status: int
{
    case CREATED = 1;
    case COMPLETED = 2;
    case CANCELED = 3;
}

Технический термин для такого ввода перечислений называется «поддерживаемые перечисления», поскольку они «подкреплены» более простым значением. Если вы решили присвоить значения перечисления, тогда все варианты должны иметь какое-то значение. Вы не можете смешивать и сочетать их. Перечисления без "поддержки" называются "чистыми перечислениями". 


Сериализация поддерживаемых перечислений

Если вы присвоите значения enum, вам вероятно понадобится способ их сериализации и десериализации. Их сериализация означает, что вам нужен способ доступа к значению перечисления. Это делается с помощью public свойства value, обратите внимание, что оно только для чтения:


$value = Status::CANCELED->value; // 3

Получить перечисление из значения можно с помощью Enum::from 


$status = Status::from(2); // Status::COMPLETED

Также можно использовать tryFrom для возврата null при неизвестном значении. Если бы вы использовали from, то возникло бы исключение. 


$status = Status::from('unknown'); // ValueError
$status = Status::tryFrom('unknown'); // null

Вы также можете использовать встроенные функции serialize и unserialize на перечислениях. Кроме того, вы можете использовать json_serialize в сочетании с поддерживаемыми перечислениями, ее результатом будет значение перечисления. Это поведение можно изменить, реализовав JsonSerializable. 

Список значений перечисления

Вы можете использовать статический метод cases(), чтобы получить список всех доступных "случаев" в перечислении: Enum::cases()


Status::cases();

// [Status::CREATED, Status::COMPLETED, Status::CANCELED]

Перечисления - это объекты

Как уже выше упоминалось, значения перечислений представлены как объекты, хотя на самом деле это одноэлементные объекты. Это означает, что вы можете сравнивать их так:


$statusA = Status::COMPLETED;
$statusB = Status::COMPLETED;
$statusC = Status::CANCELED;

$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true

Перечисления как ключи массива

Поскольку значения перечислений на самом деле являются объектами, в настоящее время их невозможно использовать в качестве ключей массива. Следующий код приведет к ошибке:


$list = [
    Status::CREATED => 'created',
    // …
];

Существует RFC для изменения этого поведения, но он еще не был принят.

Это означает, что вы можете использовать только перечисления в качестве ключей в SplObjectStorage и WeakMaps. 

Перечисления могут иметь ноль или более значений

Внутри структуры enum может содержаться любое количество case от нуля до бесконечности. Оба этих объявления Enum валидны:


enum ErrorStates {
}


enum HTTPMethods {
    case GET;
    case POST;
}

Создание экземпляров с помощью new запрещено

Хотя кеймы Enum сами по себе являются объектами, их нельзя создавать с помощью конструкции new. Обе следующие new конструкции не допускаются:


enum Foo {
    case Bar;
}

new Foo(); // Fatal error: Uncaught Error: Cannot instantiate enum Foo
new Foo::Bar(); Parse error: syntax error, unexpected identifier "Bar", expecting variable or "$"

Перечисления не могут быть расширены и не должны наследовать

Перечисления объявляются как final, и Enum не может наследоваться от другого Enum или класса.


enum Foo extends Bar {}

// Parse error: syntax error, unexpected token "extends", expecting "{" in ... on line ...


enum Foo {}
class Bar extends Foo {}

// Fatal error: Class Bar may not inherit from final class (Foo) in ... on line ...

Запрещенные магические методы 

Чтобы объекты Enum не имели какого-либо состояния и чтобы два Enum были сопоставимы, Enum запрещает реализацию нескольких магических методов. Все следующие объявления магических методов недопустимы.


enum Foo {
    public function __get() {}
    public function __set() {}
    public function __construct() {}
    public function __destruct() {}
    public function __clone() {}
    public function __sleep() {}
    public function __wakeup() {}
    public function __set_state() {}
}

// Fatal error: Enum may not include __get in ... on line ...

 

Рефлексии и атрибуты

Как и ожидалось, были добавлены несколько классов рефлексии для работы с перечислениями: ReflectionEnum, ReflectionEnumUnitCase и ReflectionEnumBackedCase. Также есть новая функция enum_exists, которая делает то, что предполагает ее название - проверяет является ли переданное значение именем класса Enum.

Как и обычные классы и свойства, перечисления и их случаи можно аннотировать с помощью атрибутов . Обратите внимание, что фильтр TARGET_CLASS также будет включать перечисления.

И последнее: перечисления имеют свойство только для чтения, которое в RFC упоминается как деталь реализации и, вероятно, должно использоваться только для целей отладки. Однако об этом все же стоит упомянуть. $enum->name

Что думаешь?

Категории
  • PHP 66
  • Заметки 15
  • Безопасность 3
  • Флуд 2
  • Nginx 2
  • ИТ новости 2
  • Видео 1
  • Docker 1
  • Roadmap 1
  • Архитектура 0

Хочешь поддержать сайт?

Делаем из мухи слона

sergeymukhin.com

персональный блог о веб-разработке от Сергея Мухина. Блог был основан в 2018 году, и собирался уделять основное внимание последним тенденциям, учебным пособиям, а также советам и рекомендациям, позволяющим начинающим девелоперам встать быстрее на правильную дорогу веб разработки, но что-то пошло не так 😃

Релизы PHP 8.4

Дата Релиз
8 Июня 2024 Альфа 1
20 Июня 2024 Альфа 2
04 Июля 2024 Альфа 3
16 Июля 2024 Feature freeze
18 Июля 2024 Бета 1
01 Августа 2024 Бета 2
15 Августа 2024 Бета 3
29 Августа 2024 RC 1
12 Сентября 2024 RC 2
26 Сентября 2024 RC 3
10 Октября 2024 RC 4
24 Октября 2024 RC 5
07 Ноября 2024 RC 6
21 Ноября 2024 GA

Что нового?