- Удобная и долгожданная: поддержка enum в PHP 8.1
- Методы enum
- Магические константы
- Запрещённые магические методы
- Enum интерфейсы
- Значения перечислений
- Типизированные enum с интерфейсами
- Сериализация типизированных перечислений
- Вывод вариантов перечисления
- Перечисления — это объекты
- Перечисления как ключи массива
- Трейты
- Reflection и атрибуты
- Php enum to array
- Casting
- User Contributed Notes 1 note
- Saved searches
- Use saved searches to filter your results more quickly
- fatih-irday-01/php-enum-methods
- Name already in use
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
Удобная и долгожданная: поддержка enum в PHP 8.1
В PHP 8.1 наконец-то добавили встроенную поддержку перечислений — enum. Под катом — перевод статьи блогера и PHP разработчика Брента с обзором новых возможностей, дополнениям и комментариями разработчиков о том, что они думают о поддержке перечислений в PHP 8.1.
Enum в PHP ждали — и новая функция оправдывает ожидания. Сергей Пешалов, инженер-программист из Eggheads Solutions, оценил нововведения так:
Если раньше фактически писался свой класс или просто использовали массив, то сейчас для этого есть enum. Смысл в том, что в БД поле enum было, а в php нет, поэтому для реализации использовали каждый во что горазд. Сейчас эту проблему решили. Реализация интересная. Фактически перечислители работают как классы, даже с методами. Очень удобно.
Раз удобно, давайте разберёмся подробнее в том, как работают перечисления.
Так выглядят перечисления:
Преимущество enum в том, что это набор постоянных значений, которые можно вводить так:
В этом примере мы создаём enum и передаём его BlogPost объекту так:
$post = new BlogPost(Status::DRAFT);
Хотя кейсы enum сами по себе являются объектами, их нельзя создавать с помощью new. Кроме того, перечисления имеют фиксированное количество возможных значений.
Методы enum
Enum могут определять методы, как и обычные классы. Это очень мощная функция, особенно в сочетании с оператором match:
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'
Также разрешены статические методы:
Self в перечислениях тоже применяют:
enum Status < // … public function color(): string < return match($this) < self::DRAFT =>'grey', self::PUBLISHED => 'green', self::ARCHIVED => 'red', >; >
Одно из наиболее важных различий между enum и классом заключается в том, что enums не могут иметь состояния. Объявление свойств для перечислений запрещены, также не допускаются статические свойства.
Магические константы
Перечисления полностью поддерживают все магические константы, которые PHP поддерживает для классов. Константа класса, которая ссылается на имя самого enum:
- __CLASS__ —магическая константа, которая ссылается на имя enum из enum.
- __FUNCTION__ —в контексте метода enum.
- __METHOD__ — в контексте метода enum.
Запрещённые магические методы
Чтобы объекты enum не имели какого-либо состояния и чтобы два enum были сопоставимы, enums запрещает реализацию нескольких магических методов:
- __get (): чтобы предотвратить сохранение состояния в объектах enum.
- __set (): чтобы предотвратить присвоение динамических свойств и поддержания состояния.
- __construct (): перечисления не поддерживают все конструкции new Foo ().
- __destruct (): перечисления не должны поддерживать состояние.
- __clone (): перечисления — это неклонируемые объекты.
- __sleep (): перечисления не поддерживают методы жизненного цикла.
- __wakeup (): перечисления не поддерживают методы жизненного цикла.
- __set_state (): чтобы предотвратить принуждения состояния к объектам enum.
Enum интерфейсы
Enum могут реализовывать интерфейсы, как и обычные классы:
interface HasColor < public function color(): string; >enum Status implements HasColor < case DRAFT; case PUBLISHED; case ARCHIVED; public function color(): string < /* … */ >>
Перечисления, не поддерживаемые значением, автоматически реализуют интерфейс UnitEnum.
Перечисления не могут явно реализовать этот интерфейс, поскольку это делается внутри движка. Это делается только для помощи в определении типа данных enum. Метод UnitEnum :: cases возвращает массив всех случаев данного enum.
Значения перечислений
Хотя enum — это объект, вы можете присвоить им значения, чтобы сохранить их в базе данных.
Перечисления поддерживают пространства имён, автозагрузку, методы, но не свойства, реализующие интерфейсы и другое поведение PHP-классов.
Обратите внимание на объявление типа в определении перечислений. Так мы указываем, что все значения enum относятся к заданному типу. Аналогичным образом работает int. В качестве значений enum можно использовать только int и string.
Любой другой тип, включая типы bool, null, String, Int, не допускается.
Если вы решите присвоить значения перечислениям, это необходимо сделать для каждого перечисления, также типы нельзя смешивать и совмещать.
PHP резервирует enum и предотвращает создание любых функций, классов, интерфейсов и так далее с именем enum. PHP учитывает зарезервированные ключевые слова в значениях пространства имён. Синтаксис аналогичен синтаксису трейта/класса/интерфейса. Само имя enum нечувствительно к регистру и соответствует тому, как PHP обрабатывает классы и функции без учёта регистра.
Максим Епихин, руководитель отдела поддержки и тестирования программного обеспечения АСУ, ФГАНУ ЦИТиС:
Enum — это отличная система организации списков. Вот такой класс я писал для этого раньше, когда перечислений не было. Он который имитирует часть enum. Сейчас я могу от него полностью избавиться. То есть все статичные данные, которые я храню классами и наследованием и интерфейсами, просто заменились на нормальный и лаконичный enum. Теперь можно было бы хранить все enum не классами (как костыль), а средствами языка.
С одной стороны, количество файлов не изменится, а просто заменятся классы на enum. С другой стороны, зачем хранить файлы, если enum можно объявлять в одном файле. Тут другая проблема: enum будут в одном файле, а это неудобно для изменения и поиска.
Главная фича в том, что мы избавляемся от своих костылей в пользу ядра движка с готовыми функциями, но получаем энтропию организации enum.
Enum нельзя наследовать от другого enum или его класса. Если класс пытается расширить enum, это также приведёт к ошибке, потому что все enum объявлены окончательными.
Типизированные enum с интерфейсами
Если вы комбинируете типизированные enum с интерфейсами, тип перечисления нужно ставить сразу после имени перечисления, перед ключевым словом implements.
enum Status: string implements HasColor < case DRAFT = 'draft'; case PUBLISHED = 'published'; case ARCHIVED = 'archived'; // … >
Сериализация типизированных перечислений
Если вы присваиваете значения вариантам перечислений, вам понадобится способ их сериализации и десериализации. Сериализация означает, что вам нужен способ получить значение перечисления. Это делается с помощью общедоступного свойства value — только для чтения:
$value = Status::PUBLISHED->value; // 2
Получить значение перечисления можно с помощью метода :Enum::from:
$status = Status::from(2); // Status::PUBLISHED
Также можно использовать метод tryFrom, который вернёт null, если передано неизвестное значение. При использовании from будет выведено исключение:
$status = Status::from('unknown'); // ValueError $status = Status::tryFrom('unknown'); // null
Обратите внимание, при работе с перечислениями можно использовать встроенную функцию serialize и unserialize. Кроме того, можно использовать json_encode в сочетании с типизированными перечислениями, результатом функции и будет значение enum. Это поведение можно изменить, реализовав JsonSerializable.
Вывод вариантов перечисления
Чтобы получить все доступные варианты перечислений, воспользуйтесь статичным методом :Enum::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.
Трейты
Перечисления могут использовать трейты аналогично классам, но с некоторыми ограничениями. Вы не можете переопределять встроенные методы перечислений, а также они не могут содержать свойства класса, что запрещено в перечислениях.
PHP поддерживает автозагрузку для классов, трейтов и интерфейсов, enums также поддерживает автозагрузку. Обратите внимание, что для этого могут потребоваться обновления генераторов карт классов, которые анализируют файлы на предмет автоматически загружаемых элементов.
Reflection и атрибуты
Для работы с enum добавили несколько Reflection классов: ReflectionEnum, ReflectionEnumUnitCase и ReflectionEnumBackedCase. Также добавили новую функцию enum_exists — функция, название которой говорит само за себя. Обратите внимание, что из-за семантики классов функция class_exists также возвращает true для enum.
Как и обычные классы и свойства, enum и их случаи можно аннотировать с помощью атрибутов. Обратите внимание, enum включат в фильтр TARGET_CLASS.
И последнее: у перечислений также есть readonly свойство: $enum->name, которое в RFC упоминают как деталь реализации и, вероятно, его можно использовать только для отладки.
Епихин Максим Николаевич, руководитель отдел поддержки и тестирования программного обеспечения АСУ, ФГАНУ ЦИТиС:
Очень крутая и долгожданная вещь в PHP. Минусы и нюансы только в добавлении элементов. Это, насколько мне известно, не такая «дешёвая» операция. Несколько вариантов сравнения, которые работают как бы одинаково, но кто-то умудрится запороть всё и там. Пока протестировать их не удалось, но думаю, что именно в этом будут проблемы.
Php enum to array
Перечисления — это ограничивающий слой над классами и константами классов, предназначенный для предоставления способа определения закрытого набора возможных значений для типа.
enum Suit
case Hearts ;
case Diamonds ;
case Clubs ;
case Spades ;
>
?php
Полное описание смотрите в главе о перечислениях.
Casting
Если перечисление ( enum ) преобразуется в объект ( object ), оно не изменяется. Если перечисление ( enum ) преобразуется в массив ( array ), то создаётся массив с одним ключом name (для простых перечислений) или массив с двумя ключами name и value (для типизированных перечислений). Все остальные приведения типов приведут к ошибке.
User Contributed Notes 1 note
/**
* This is a sample
* How to use Enum to create a custom exception cases
* PHP 8.1^
*/
enum MyExceptionCase case InvalidMethod ;
case InvalidProperty ;
case Timeout ;
>
class MyException extends Exception function __construct (private MyExceptionCase $case ) match ( $case ) MyExceptionCase :: InvalidMethod => parent :: __construct ( «Bad Request — Invalid Method» , 400 ),
MyExceptionCase :: InvalidProperty => parent :: __construct ( «Bad Request — Invalid Property» , 400 ),
MyExceptionCase :: Timeout => parent :: __construct ( «Bad Request — Timeout» , 400 )
>;
>
>
// Testing my custom exception class
try throw new MyException ( MyExceptionCase :: InvalidMethod );
> catch ( MyException $myE ) echo $myE -> getMessage (); // Bad Request — Invalid Method
>
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
fatih-irday-01/php-enum-methods
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
composer require fatihirday/enummethods
use Fatihirday\EnumMethods\EnumMethods; enum HttpMethods: string < use EnumMethods; case GET = 'get'; case POST = 'post'; >
HttpMethods::names(); // Array // ( // [0] => GET // [1] => POST // ) HttpMethods::names(operator: ', '); // GET, POST
HttpMethods::values(); // Array // ( // [0] => get // [1] => post // ) HttpMethods::values(operator: ', '); // get, post
HttpMethods::toArray(); // Array // ( // [GET] => get // [POST] => post // ) HttpMethods::toArray(reverse: true); // Array // ( // [get] => GET // [post] => POST // )
HttpMethods::toObject(); // Object // ( // get => GET // post => POST // ) HttpMethods::toObject(reverse: true); // Object // ( // get => GET // post => POST // )
HttpMethods::getValueByName('GET'); // get HttpMethods::getNameByValue('get'); // GET
HttpMethods::tryFromName('GET'); // True