Comment utiliser les énumérations en PHP 8.1 – CloudSavvy IT
PHP 8.1 ajoutera enfin le support du langage pour les énumérations. Les énumérations, abréviation d’énumérations, sont des types qui ne peuvent être instanciés qu’avec des valeurs spécifiques. On les trouve couramment dans d’autres langages orientés objet, mais ils nécessitaient auparavant des solutions de contournement utilisateur pour être implémentées en PHP.
Sommaire
Syntaxe de base
Voici à quoi ressemble une simple énumération :
enum PostStatus { case Published; case InReview; case Draft; }
Les case
mot-clé, faisant auparavant partie de switch
instructions, est utilisé pour délimiter les valeurs spécifiques que l’énumération accepte. Les valeurs sont référencées de la même manière que les constantes de classe :
$published = PostStatus::Published;
Les énumérations se comportent de la même manière que les classes et les interfaces. Ils sont entièrement compatibles avec le système de types, vous pouvez donc indiquer qu’une fonction n’accepte qu’une valeur définie dans une énumération :
class BlogPost { public function __construct( public string $Headline, public string $Content, public PostStatus $Status=PostStatus::Draft) {} }
Voici un exemple d’utilisation du BlogPost
classer:
// OK $post = new BlogPost( "Example Post", "An example", PostStatus::Draft ); // TypeError: Argument #3 ($Status) must be of type PostStatus $post = new BlogPost( "Broken Example", "A broken example", "Submitted" );
La première instance fonctionne parce que son $Status
est une valeur valide de la PostStatus
enum. Dans le second cas, une chaîne simple est passée comme $Status
, ce qui est interdit car la valeur doit être définie dans PostStatus
.
Les cas d’énumération sont représentés sous forme de constantes sur l’objet enum. Cela signifie que vous pouvez les utiliser comme valeurs statiques et dans le cadre d’expressions constantes. Les BlogPost
constructeur montre un cas d’énumération utilisé comme valeur de paramètre par défaut, où $Status
est automatiquement défini sur Brouillon lorsqu’aucune valeur n’est fournie par l’appelant.
Vous pouvez accéder à toutes les valeurs disponibles dans une énumération en utilisant son cases
méthode:
PostStatus::cases(); // [PostStatus::Published, PostStatus::InReview, PostStatus::Draft]
Énumérations pures ou soutenues
Les PostStatus
enum ci-dessus est un pur enum. Il ne contient que case
déclarations, sans données supplémentaires. PHP vous permet également d’attacher une valeur aux cas enum, créant un soutenu enum.
enum PostStatus : string { case Published = "S1"; case InReview = "S2"; case Draft = "S3"; }
Ici le PostStatus
enum a été modifié pour créer un enum sauvegardé. Le typehint dans la définition de l’énumération stipule que chaque cas a une valeur de chaîne qui lui est assignée. Dans cet exemple, nous supposons que chaque statut de publication nommé a un identifiant court associé. Il se peut que cet identifiant soit enregistré dans la base de données lorsque les publications sont persistantes.
Vous pouvez accéder aux valeurs sauvegardées via le value
propriété sur les instances de cas :
class BlogPostRepository { public function save(BlogPost $Post) : void { $this -> insert( "blog_posts", [ "headline" => $Post -> Headline, "content" => $Post -> Content, "status" => $Post -> Status -> value ] ); } } $post = new BlogPost("Example", "Demo", PostStatus::Published); (new BlogPostRepository()) -> save($post);
Cet exemple définirait la valeur de la persistance status
champ à S1
, basé sur la version sauvegardée du PostStatus
enum montré ci-dessus.
Énumérations soutenues seul accepter des chaînes et des entiers comme valeurs. Il n’est pas possible d’utiliser le type union string|int
Soit. De plus, chaque cas a besoin d’une valeur unique – l’exemple suivant n’est pas autorisé :
enum PostStatus : string { case Published = "S1"; case Draft = "S1"; }
PHP fournit une méthode utilitaire sur les énumérations pour créer une instance à partir d’une valeur sauvegardée :
// fetch the blog post from earlier from the database // the "status" field = S1 $status = PostStatus::from($record["status"]);
Les from()
La méthode hydratera les instances à partir des cas de valeur. Dans cet exemple, S1
est mappé sur le Published
case, et votre code reçoit une instance de PostStatus::Published
.
from()
jette un ValueError
si la valeur d’entrée est invalide ; dans les scénarios où vous savez que la valeur peut ne pas être utilisable, l’alternative tryFrom()
méthode peut être utilisée à la place. Cela revient null
quand il n’y a pas de correspondance, au lieu de lancer l’erreur.
Ajout de méthodes aux énumérations
Comme les énumérations sont basées sur des classes, vous pouvez également leur ajouter des méthodes !
enum PostStatus { case Published; case Draft; public function isPubliclyAccessible() : bool { return ($this instanceof self::Published); } }
Cela vous permet de conserver un comportement spécifique à la casse dans votre énumération, au lieu de le dupliquer dans votre base de code.
Les énumérations peuvent également implémenter des interfaces :
enum PostStatus implements PublicAccessGatable { case Published; case Draft; public function isPubliclyAccessible() : bool { return ($this instanceof self::Published); } }
Maintenant, vous pouvez passer un PostStatus
exemple à tout ce qui accepte un PublicAccessGatable
:
class UserAuthenticator { function shouldAllowAccess(PublicAccessGatable $Resource) : bool { return ($this -> User -> isAdmin() || $Resource -> isPubliclyAccessible()); } } $auth = new UserAuthenticator(); // get a blog post from the database if (!$auth -> shouldAllowAccess($post -> Status)) { http_response_code(403); }
Il n’y a aucune restriction sur ce que vous pouvez faire avec les méthodes enum – ce sont des méthodes PHP normales, après tout – mais en général, vous vous attendez à ce qu’elles effectuent une sorte de comparaison avec le cas de l’instance, puis renvoient une valeur statique. Les énumérations peuvent utiliser des traits, vous pouvez donc extraire des méthodes existantes que vous avez également abstraites de cette manière.
Vous pouvez utiliser public
, protected
et private
méthodes dans les énumérations, bien que protected
et private
avoir le même effet. Les énumérations ne peuvent pas s’étendre donc private
est effectivement redondant. Vous ne pouvez pas non plus ajouter de constructeur ou de destructeur. Les méthodes statiques sont prises en charge et peuvent être appelées sur la classe enum ou ses instances case.
Constantes
Les énumérations peuvent également avoir leurs propres constantes, soit en tant que valeurs littérales régulières, soit en tant que référence à un cas d’énumération :
enum PostStatus { case Published; case Draft; public const Live = self::Published; public const PlainConstant = "foobar"; }
Cela peut créer de la confusion car la même syntaxe est utilisée pour accéder aux cas (instances d’énumération) et aux constantes :
$published = PostStatus::Published; $plain = PostStatus::PlainConstant;
Seul $published
satisferait un PostStatus
dactylographie, comme $plain
fait référence à une valeur scalaire simple.
Quand utiliser les énumérations ?
Les énumérations sont destinées aux occasions où vous avez besoin de flexibilité dans la valeur qu’une variable peut prendre, mais uniquement parmi un ensemble prédéterminé de cas possibles.
La classe d’articles de blog qui est présentée dans cet article est un exemple classique. Les messages ne peuvent être que dans l’un des ensembles d’états connus, mais PHP n’avait auparavant aucun moyen simple d’y parvenir.
Dans les anciennes versions, vous avez peut-être utilisé cette approche :
class PostStatus { const Published = 0; const Draft = 1; } class BlogPost { public function __construct( public string $Headline, public int $Status ) {} } $post = new BlogPost("My Headline", PostStatus::Published);
Le problème ici est que $Status
accepte en fait n’importe quel entier, donc l’appel suivant serait parfaitement valide :
$post = new BlogPost("My Headline", 9000);
Par ailleurs, BlogPost
et PostStatus
sont complètement détachés – il n’y a aucun moyen que quelqu’un lise BlogPost
peut apprendre la gamme de valeurs qui $Status
réellement accepte. Bien que ces problèmes puissent être atténués en utilisant des astuces de type docblock appropriées, ou des packages tiers « faux enum », ils ajoutent tous des couches supplémentaires autour d’un concept que d’autres langages de programmation simplifient.
L’ajout d’énumérations natives à PHP est une étape qui permet de compléter le système de types du langage. Vous pouvez enfin saisir les valeurs autorisées de manière à ce que tout le monde soit sur la même longueur d’onde. Si vous transmettez une valeur invalide, vous obtiendrez une erreur d’exécution. Votre IDE peut mieux vous aider à fournir des valeurs correctes, car il saura que $Status
n’accepte que trois options, au lieu de « n’importe quel entier ».
Conclusion
Les énumérations résolvent certains problèmes courants des développeurs lorsqu’ils travaillent en PHP. Ils permettent d’indiquer que les paramètres, les valeurs de retour et les propriétés doivent faire partie d’un ensemble d’options prédéterminées.
Les énumérations sont des entités de base de code flexibles que vous pouvez garder simples sous une forme pure, ou étendre avec des valeurs sauvegardées, des implémentations d’interface et des méthodes personnalisées. Les énumérations se comportent de la même manière que les objets normaux dans la plupart des cas et prennent en charge des fonctionnalités de classe telles que __call()
, __invoke
, et ::class
.
Vous pouvez introspecter les énumérations avec le nouveau enum_exists()
fonction et ReflectionEnum
Classe de réflexion. De plus, les enums implémentent deux nouvelles interfaces, UnitEnum
(dans le cas des énumérations pures), et BackedEnum
(pour les énumérations avec des valeurs sauvegardées). Ceux-ci peuvent être utilisés dans un code-cadre générique qui fonctionne avec tout enum. Les interfaces ne peuvent pas être implémentées manuellement par code utilisateur.
Les énumérations arriveront en PHP dans le cadre de la version 8.1 en novembre 2021. Ils sont déjà disponibles dans les dernières versions bêta. PHP 8.1 embarquera également plusieurs autres fonctionnalités pratiques, y compris les propriétés en lecture seule et les types d’intersection.