PHP 8.1 dodaje obsługę wyliczeń. Wyliczenie, w skrócie Enum, jest typem wyliczeniowym, który ma stałą liczbę możliwych wartości.
Popularną analogią dla Enum są garnitury w talii kart do gry. Talia kart do gry ma cztery kolory i są one stałe: trefle, diamenty, serca i pik.
W PHP te kolory można wyliczyć za pomocą Enum:
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
Dzięki Suit
Enum możliwe jest teraz wymuszanie typów podczas akceptowania lub zwracania wartości koloru:
function pick_card(Suit $suit) {}
pick_card(Suit::Clubs);
pick_card(Suit::Diamonds);
pick_card(Suit::Hearts);
pick_card(Suit::Spades);
W przeciwieństwie do używania specjalnych ciągów lub liczb wewnętrznie (np. liczb magicznych) do przechowywania i pracy z parametrami, wyliczenia zwiększają czytelność kodu aplikacji i pozwalają uniknąć nieoczekiwanego stanu aplikacji.
Składnia
wyliczenia PHP 8.1 rezerwuje i używa enum
słowa kluczowego do deklarowania Enums. Składnia jest podobna do składni cechy/klasy/interfejsu:
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
Wyliczenia są deklarowane za pomocą enum
słowa kluczowego, po którym następuje nazwa wyliczenia. Wyliczenie może opcjonalnie deklarować string
wartości lub jako wartościint
zabezpieczone. Wyliczenia mogą również rozszerzać klasę i/lub implementować interfejsy.
Wewnętrznie na poziomie parsera PHP istnieje nowy token o nazwie T_ENUM
z przypisaną wartością369
.
Wyliczenia mogą również zawierać wartość dla każdego przypadku, co czyni je Enumami wspieranymi.
enum HTTPMethods: string {
case GET = 'get';
case POST = 'post';
}
Poniżej przedstawiono przykład Enum, który deklaruje typ wartości wspieranej i implementuje interfejs.
enum RolesClassLikeNamespacedEnum: string implements TestFor {
case Admin = 'Administrator';
case Guest = 'Guest';
case Moderator = 'Moderator';
}
Nowa enum_exists
funkcja
PHP 8.1 dodaje również nową enum_exists
funkcję do sprawdzenia czy dany Enum istnieje.
function enum_exists(string $enum, bool $autoload = true): bool {}
Należy zauważyć, że ze względu na semantykę klas wyliczeń, class_exists
funkcja zwraca również dla true
wyliczenia.
UnitEnum
interfejs
Wyliczenia, które nie są poparte wartością, automatycznie implementują UnitEnum
interfejs.
interface UnitEnum {
public static function cases(): array;
}
Wyliczenia nie mogą jawnie implementować tego interfejsu, ponieważ jest to wykonywane wewnętrznie przez silnik. Ma to jedynie pomóc w określeniu rodzaju danego Enum. Metoda UnitEnum::cases
zwraca tablicę wszystkich przypadków danego wyliczenia.
Klasy PHP nie mogą implementować tego interfejsu i powodują błąd, jeśli zostaną podjęte:Chociaż wyliczenia zezwalają na zadeklarowane na nich metody, deklarowanie metody o nazwie cases
jest niedozwolone:
class FakeEnum implements UnitEnum {}
Fatal error: Non-enum class FakeEnum cannot implement interface UnitEnum in ... on line ...
enum Foo {
public function cases(): array{}
}
Fatal error: Cannot redeclare Foo::cases() in ... on line ...
BackedEnum
interface
Jeśli Enum deklaruje wartości oparte na skalarnie, to Enum automatycznie interfejs wywołuje BackedEnum
. Podobnie jak w przypadku UnitEnum
interfejsu, nie jest możliwe jawne zaimplementowanie BackedEnum
interfejsu.
interface BackedEnum extends UnitEnum {
public static function from(int|string $value): static;
public static function tryFrom(int|string $value): ?static;
}
Zobacz BackedEnum::from
i BackedEnum::tryFrom
dla informacji o ich użyciu.
Klasy standardowe nie mogą implementować tego interfejsu.
class FakeEnum implements BackedEnum {}
Fatal error: Non-enum class FakeEnum cannot implement interface BackedEnum in ... on line ...
Podobnie jak w przypadku ograniczenia dotyczącego niedozwolonego zezwalania na metodę o nazwie cases
jest niedozwolona, żaden wspierany Enum nie może deklarować a from
or tryFrom
method:
enum Foo: int {
public function from() {}
public function tryFrom() {}
}
Fatal error: Cannot redeclare Foo::from() in ... on line ...
Fatal error: Cannot redeclare Foo::tryFrom() in ... on line ...
Declaring Enums
Enums są wewnętrznie implementowane na najwyższych klasach PHP i dziedziczą większość semantyki klas z nałożonymi dodatkowymi ograniczeniami.
Wyliczenia obsługują przestrzenie nazw, automatyczne ładowanie, mogą mieć metody (ale nie właściwości), implementację interfejsów i wiele innych zachowań związanych z klasami PHP.
Podstawowy wyliczenie to po prostu strukturaenum
, w której każdy przypadek jest deklarowany za pomocą słowa kluczowegocase
. Dzięki wyliczeniom obsługiwanym w PHP 8.1, PHP rezerwuje "enum"
teraz jako słowo zastrzeżone i zapobiega tworzeniu jakichkolwiek funkcji, klas, interfejsów itp. za pomocą enum
. Może być częścią przestrzeni nazw ze względu na zmiany w sposobie uwzględniania zarezerwowanych słów kluczowych w wartościach w przestrzeni nazw.
Wyliczenia mogą mieć zero lub więcej przypadków
W ramach enum
struktury może zawierać dowolną liczbę "przypadków", od zera do nieograniczonego. Obie te deklaracje Enum są prawidłowe:
enum ErrorStates {
}
enum HTTPMethods {
case GET;
case POST;
}
Wyliczenia mogą mieć wartości
opcjonalne Możliwe jest przypisanie wartości a string
lub int
do każdego przypadku w Enum. Może to być przydatne podczas serializacji danych, przechowywania ich w bazie danych itp.
Wyliczenia, które posiadają wartość, tj. Enum wspierany, muszą:
-
Declare the scalar type in the Enum declaration. Only
string
orint
is allowed. - Assign values for all cases.
-
Hold values of same scalar type. It is not allowed to store
string
andint
values mixed. - Cases and assigned values are unique
enum Suit: string {
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
Wyliczenie oparte musi zadeklarować typ skalarny Aby wyliczenie mogło kojarzyć wartości dla każdego przypadku (tj. Enum wspierane), musi zadeklarować typ
skalarny w deklaracji Enum.
Niezastosowanie się do tego powoduje błąd:
enum HTTPMethods {
case GET = 'get';
case POST = 'post';
}
Fatal error: Case GET of non-scalar enum HTTPMethods must not have a value in ... on line ...
Wyliczenia tylko obsługują string
i int
typy skalarne. Wszelkie inne typy, w tym bool
, , , null
?string
?int
a nawet string|int
typy unijne są niedozwolone.
enum HTTPMethods: object {
case GET = 'get';
case POST = 'post';
}
Enum backing type must be int or string, object given in ... on line ...
Wyliczenia pomocnicze muszą przypisywać wartości dla wszystkich przypadków
Ta sekcja jest w toku
Jeśli wyliczenie deklaruje wartość typu skalarnego, musi ustawić wartość dla wszystkich przypadków:
enum HTTPMethods: string {
case GET;
case POST;
}
W powyższym fragmencie kodu zadeklarowano, że wyliczenie zawiera string
, HTTPMethods
ale przypadkom nie przypisano wartości. Jest to niedozwolone i powoduje błąd.
Wyliczenia z podparciem muszą zawierać wartości tego samego typu
skalarnego W przypadku wyliczenia wspieranego wartości przypisane do każdego przypadku muszą być tego samego typu, co zadeklarowane w typie.
PHP ściśle to wymusza, nawet jeśli strict_types nie jest włączony.
enum HTTPMethods: int {
case GET = 1;
case POST = '2';
}
Fatal error: Enum case type string does not match enum scalar type int in ... on line ...
Przypadki i wartości Enum Backed muszą być unikalne
Ta sekcja jest w toku
Prawidłowa wyliczenie nie może zawierać zduplikowanych przypadków ani zduplikowanych wartości. Na przykład obie te deklaracje są nieprawidłowe:
enum Test {
case FOO;
case FOO;
}
enum Test: string {
case FOO = 'baz';
case BAR = 'baz';
}
Wartości Enum muszą być również unikatowe, ponieważ
BackedEnum::from
obsługują pobieranie obiektu Enum z danej wartości skalarnej.
Rozróżnianie
wielkości liter Sama nazwa Enum nie rozróżnia wielkości liter i wynika z tego, jak PHP traktuje klasy i funkcje w sposób niewrażliwy na wielkość liter.
W indywidualnych przypadkach w Enum rozróżniana jest wielkość liter.
enum CaseInSenSitive {
case bAR;
case Bar;
}
Semantyka klas w przestrzeniach nazw wyliczeń Wyliczenia obsługują przestrzenie
nazw.
Są one zgodne ze standardową składnią przestrzeni nazw, która jest używana w klasach, cechach, interfejsach, funkcjach itp.
namespace Foo\Bar;
enum HTTPMethods {}
Automatyczne ładowanie
Podobnie jak PHP obsługuje automatyczne ładowanie klas, cech i interfejsów, wyliczenia obsługują również automatyczne ładowanie.
Należy pamiętać, że może to wymagać aktualizacji generatorów map klas, które skanują pliki w poszukiwaniu elementów ładowanych automatycznie.
Stałe magiczne
Wyliczenia w pełni obsługują wszystkie stałe magiczne, które PHP obsługuje dla klas.
-
::class
stała, która odnosi się do nazwy samego Enum. -
__CLASS__
stała magiczna, która odnosi się do nazwy Enum z wnętrza Enum. -
__FUNCTION__
w kontekście metody wyliczenia. -
__METHOD__
w kontekście metody wyliczenia.
Funkcje klas/obiektów i wyliczenia zachowują się podobnie do klas, gdy są używane z funkcjami obsługującymi inspekcję klas i instanceof
obiektów.
Na przykład funkcje , , i get_debug_type
(nowość w PHP 8.0) zachowują się tak, gettype
is_object
is_a
get_class
jakby wyliczenie było standardowym obiektem PHP.
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
gettype(Suit::Clubs); // "object"
is_object(Suit::Spades); // true
is_a(Suit::Clubs, Suit::class); // true
get_class(Suit::Clubs); // "Suit"
get_debug_type(Suit::Clubs); // "Suit"
Suit::Clubs instanceof Suit; // true
Suit::Clubs instanceof UnitEnum; // true
Suit::Clubs instanceof object; // false
Wyliczenia z właściwościami, metodami i cechami
Wyliczenia są wykonane w taki sposób, że można porównać jeden przypadek Enum z innym. Wyliczenia muszą być bezstanowe, ponieważ nie pozwalają na przechowywanie w nich właściwości.
Wyliczenia pozwalają na metody
Wyliczenia mogą zawierać metody. Obsługują również modyfikatory widoczności metod standardowych, a także metody statyczne.
Może to być bardzo przydatne przypadki użycia, takie jak deklarowanie label(): string
metody, która zwraca przyjazną dla użytkownika etykietę.
enum HTTPStatus: int {
case OK = 200;
case ACCESS_DENIED = 403;
case NOT_FOUND = 404;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
HTTPStatus::OK => 'OK',
HTTPStatus::ACCESS_DENIED => 'Access Denied',
HTTPStatus::NOT_FOUND => 'Page Not Found',
};
}
}
echo HTTPStatus::ACCESS_DENIED->label(); // "Access Denied"
echo HTTPStatus::getLabel(HTTPStatus::ACCESS_DENIED); // "Access Denied"
Powyższy fragment kodu używa
match
wyrażeń dodanych w PHP 8.0
Enums może implementować interfejsyEnums może implementować interfejsy
. Wyliczenia muszą spełniać kontrakty interfejsów, tak jak musi to robić klasa standardowa.
interface Foo {}
enum FooEnum implements Foo {}
Wyliczenie nie może zawierać właściwości
Jedną z najważniejszych różnic między Enum a klasą jest to, że Enums nie mogą mieć żadnego stanu. Deklarowanie właściwości lub ustawianie właściwości jest niedozwolone. Właściwości statyczne również nie są dozwolone.
enum Foo {
private string $test;
private static string $test2;
}
Fatal error: Enums may not include properties in ... on line ...
Ponadto dynamiczne ustawianie właściwości również nie jest dozwolone:
enum Foo {
case Bar;
}
$bar = Foo::Bar;
$bar->test = 42;
Error: Error: Cannot create dynamic property Foo::$test in ...:...
Tworzenie instancji za pomocą nie jest dozwolone
Chociaż przypadki wyliczenia same w sobie są obiektami, nie wolno tworzyć ich instancji za pomocą new
new
konstrukcji.
Obie poniższe new
konstrukcje są niedozwolone:
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 "$"
Wyliczenia nie mogą być rozszerzane i nie mogą dziedziczyć Ta sekcja jest w toku
Ta sekcja jest w toku
po innym Enum lub klasie.
enum Foo extends Bar {}
Parse error: syntax error, unexpected token "extends", expecting "{" in ... on line ...
Jeśli klasa próbuje rozszerzyć Enum, spowoduje to również błąd, ponieważ wszystkie Enum są uznane za ostateczne.
enum Foo {}
class Bar extends Foo {}
Fatal error: Class Bar cannot extend final class Foo in ... on line ...
Wyliczenia wspierają cechy
pozbawione właściwości Wyliczenie może use
cechować, o ile cecha nie deklaruje żadnych właściwości.
trait NamedDocumentStatus {
public function getStatusName(): string {}
}
enum DocumentStats {
use NamedDocumentStatus;
case DRAFT;
case PUBLISHED;
}
Jeśli użyte cechy zawierają jakiekolwiek właściwości (statyczne lub inne), użycie tej cechy powoduje błąd
trait NotGood {
public string $value;
}
enum Foo {
use NotGood;
}
Fatal error: Enum "Foo" may not include properties in ... on line ...
krytyczny:Niedozwolone metody
magiczne Aby zapobiec posiadaniu jakiegokolwiek stanu przez obiekty Enum i zapewnić porównywalność dwóch wyliczeń, wyliczenia nie pozwalają na implementację kilku metod magicznych:
-
__get()
: Aby zapobiec utrzymywaniu stanu w obiektach Enum. -
__set()
: Aby zapobiec dynamicznemu przypisywaniu właściwości i utrzymywaniu stanu. -
__construct()
: Wyliczenia nie obsługująnew Foo()
konstrukcji wszystkich. -
__destruct()
: Wyliczenie nie może utrzymywać stanu. -
__clone()
: Wyliczenie jest obiektem nieklonowalnym. -
__sleep()
: Wyliczenia nie obsługują metod cyklu życia. -
__wakeup()
: Wyliczenia nie obsługują metod cyklu życia. -
__set_state()
: Aby zapobiec przymusowemu stanowi obiektów Enum.
Wszystkie poniższe deklaracje metod magicznych są niedozwolone.
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() {}
}
Jeśli są zadeklarowane, spowoduje to błąd krytyczny z komunikatem podobnym do tego:
Fatal error: Enum may not include __get in ... on line ...
Klasy vs Enums
Wyliczenia przechowują stałe wartości, z opcjonalnymi wartościami tła, i nie zezwalają na stan. Służą one wyraźnie różnym celom, ale wyliczenia mają pewną semantykę z klasami.
Classes | Enums | |
---|---|---|
Syntax | class Foo {} |
enum Foo {} |
Properties | ✔ | ✘ |
Static Properties | ✔ | ✘ |
Methods | ✔ | ✔ |
Static Methods | ✔ | ✔ |
Autoloading | ✔ | ✔ |
Instantiating: new Foo() |
✔ | ✘ |
Implement interfaces | ✔ | ✔ |
Inheritance: Foo extends Bar |
✔ | ✘ |
Magic Constants: ::class , __CLASS__ , etc. |
✔ | ✔ |
Object Comparison Foo === Foo
|
Not equal | Equals |
Traits | Supports | Supports without properties |
Korzystanie z Enums Głównym przypadkiem użycia Enums
jest bezpieczeństwo typu. Silnik PHP zapewni, że przekazana lub zwrócona wartość należy do jednej z dozwolonych wartości. Bez konieczności sprawdzania poprawności przekazywanych wartości należących do jednego z dozwolonych typów, silnik PHP wymusza to i pozwala IDE i analizatorom statycznym również na podkreślenie potencjalnych błędów.
Dzięki wyliczeniom opartym możliwe jest przechowywanie wyliczonych wartości w bazie danych lub innym magazynie i przywracanie programowo identycznego obiektu.
Porównywanie wartości
wyliczenia W dowolnym momencie dwa wyliczenia posiadające tę samą wartość są uważane za identyczne, tak jak PHP uważa dwa ciągi za identyczne. Jest to zasadnicza różnica między dwoma obiektami, ponieważ dwa obiekty z instancjonowanej klasy nie są uważane za identyczne, mimo że mają dokładnie te same wartości.
new stdClass() === new stdClass(); // false
Suit::Hearts === Suit::Hearts; // true
Wyliczenia jako parametry, właściwości i typy
zwrotów Wyliczenia mogą być używane jako typy parametrów, zwrotów i właściwości. Jeśli używana jest nazwa Enum, wartości passed/set/return muszą być jedną z wartości wyliczonych.
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
function play(Suit $suit, string $value) {}
Funkcja play
akceptuje wartość wyliczeniową , Suit
a przekazanie dowolnej innej wartości spowoduje wyświetlenie \TypeError
pliku . Może to znacznie poprawić kod, ponieważ PHP sprawdza poprawność przekazywanych wartości bez konieczności pisania dodatkowych walidacji wewnątrz play
funkcji.
play(Suit::Clubs, 'J');
play(Suit::Hearts, 'Q');
Jeśli zostanie przekazana jakakolwiek wartość inna niż Suit
wartość wyliczenia, spowoduje to błąd typu:
play('clubs', 'J');
TypeError: play(): Argument #1 ($suit) must be of type Suit, string given
Nie pozwala również na użycie wartości z innego Enum:
play(UnoColors::Blue, 'J');
TypeError: play(): Argument #1 ($suit) must be of type Suit, UnoColors given
Ponadto, jeśli użyta zostanie niezdefiniowana wartość Enum, spowoduje to powstanie niezdefiniowanej stałej klasy, ponieważ wartości wyliczenia i stałe klas mają tę samą składnię.
Wyliczenie i value
właściwości Każde wyliczenie name
zawiera właściwośćname
, która odwołuje się do nazwy właściwości
. Ta wartość jest tylko do odczytu.
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
echo Suit::Clubs->name; // "Clubs"
W Backed Enum istnieje również value
właściwość wartości backed (albo string
lub int
).
enum Suit: string {
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
echo Suit::Clubs->name; // "Clubs"
echo Suit::Clubs->value; // "♣"
Próba zmiany name
właściwości spowoduje value
błąd:
Suit::Clubs->name = 'Hearts';
Cannot use temporary expression in write context in
Ponadto wyliczenia bez wartości wspieranej (UnitEnum
) nie mają value
właściwości. Tylko Backed Enums (BackedEnum
) mają value
własność. Próba użycia niezdefiniowanej właściwości (w tym ) obecnie powoduje wyświetlenie ostrzeżenia w value
PHP 8.1.
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
echo Suit::Clubs->value;
Warning: Undefined property: Suit::$value in ... on line ...
Uzyskiwanie wszystkich wartości
wyliczenia Zarówno wyliczenia, jak i BackedEnum
wyliczenia obsługują metodę::cases
, która zwraca wszystkie wartości wyliczeniaUnitEnum
.
enum Suit {
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
Suit::cases();
// [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spaces]
Metoda Suit::cases()
zwraca tablicę wszystkich możliwych wyliczeń. Zwracane wartości są samymi obiektami Enum, a nie nazwami lub właściwościami wartości.
Pobieranie wyliczenia przez wartość
wspieraną Wszystkie obsługiwane from
wyliczenia (BackedEnum
) i tryFrom
metody umożliwiające pobieranie instancji z wartości wspieranej.
enum Suit: string {
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
$clubs = Suit::from('♥');
var_dump($clubs); // enum(Suit::Hearts)
echo $clubs->name; // "Hearts";
echo $clubs->value; // "♥"
Jeśli nie ma Enum przez tę wartość, PHP zgłosi ValueError
wyjątek.
Istnieje również tryFrom
metoda, która zwraca null
jeśli Enum nie istnieje przez tę wartość.
enum Suit: string {
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
$clubs = Suit::tryFrom('not-existing');
var_dump($clubs); // null
Serializacja/Unserializacja wyliczeń
Zarówno Backed jak i Unit Enum mogą być serializowane i nieserializowane przy użyciu wbudowanych serialize
funkcji i funkcjiunserialize
.
Formularz serializowany będzie miał identyfikator E
i nazwę wyliczenia.
enum PostStatus {
case DRAFT;
}
serialize(PostStatus::DRAFT);
E:16:"PostStatus:DRAFT";
W przypadku wyliczeń opartych wartości serializowane nadal są nazwą członka; nie wartość wspierana:
enum FooEnum: string {
case Foo = 'bartest';
}
serialize(FooEnum::Foo)
E:11:"FooEnum:Foo";
Zauważ, że składnia unserializacji nie jest zgodna ze starszymi wersjami PHP. Żadnych serializowanych ciągów wyliczeń nie można cofnąć serializacji w starszych wersjach PHP. Próba wykonania tej czynności powoduje wyświetlenie ostrzeżenia (PHP Notice: unserialize(): Error at offset ...
) i unserialize
zwrócenie false
pliku .
Wpływ
zgodności wstecznej Wyliczenia używają nowej składni i nie można ich używać w wersjach PHP wcześniejszych niż 8.1.
Natywne podejścia PHP, takie jak myclabs/php-enum, nie są kompatybilne z PHP 8.1 Enums.
Składnia Enum spowoduje ParserError
powstanie w PHP < 8.1. Co więcej, nie jest możliwe serializowaneunserialize
Enum w starszych wersjach PHP.