Maison >développement back-end >tutoriel php >Création de tableaux et de collections strictement typés dans PHP

Création de tableaux et de collections strictement typés dans PHP

Christopher Nolan
Christopher Nolanoriginal
2025-02-10 11:20:11131parcourir

Création de tableaux et de collections strictement typés dans PHP

Les plats clés

  • PHP 5.6 a introduit la possibilité de créer des tableaux typés à l'aide du token… qui indique qu'une fonction ou une méthode accepte une durée variable des arguments. Cette fonction peut être combinée avec des indices de type pour s'assurer que seuls certains types d'objets sont acceptés dans un tableau.
  • Une limitation de cette fonctionnalité est qu'un seul tableau tapé peut être défini par méthode. Pour surmonter cela, les tableaux typés peuvent être injectés dans des classes de «collecte», ce qui permet également des types de retour plus spécifiques que le «tableau» sur les méthodes Get.
  • Les objets de valeur peuvent être utilisés pour la validation personnalisée. Par exemple, un objet de valeur de notation peut être créé avec des contraintes pour s'assurer qu'une note se situe toujours entre 0 et 5. Cela fournit une validation supplémentaire des membres de la collection individuels sans avoir à boucler sur chaque objet injecté.
  • Les tableaux et collections strictement typés présentent plusieurs avantages. Ils fournissent une validation de type facile en un seul endroit, s'assurent que les valeurs ont toujours été validées sur la construction, permettent l'ajout d'une logique personnalisée par collection et réduisent les chances de mélanger les arguments dans les signatures de la méthode.
  • Bien qu'il soit possible d'ajouter des méthodes pour faciliter les modifications aux valeurs des collections et de valoriser les objets après la construction initiale, il est plus efficace de les garder immuables et de les convertir en types primitifs lorsque des modifications doivent être apportées. Après avoir apporté des modifications, les collections ou les objets de valeur peuvent être reconstruits avec les valeurs mises à jour, qui seront ensuite validées à nouveau.

Ce post est apparu pour la première fois sur Medium et a été republié ici avec la permission de l'auteur. Nous vous encourageons à suivre Bert sur Medium et à lui donner des likes là-bas!


L'une des fonctionnalités linguistiques annoncées dans PHP 5.6 a été l'ajout du ... jeton pour indiquer qu'une fonction ou une méthode accepte une durée variable des arguments.

quelque chose que je vois rarement mentionné est qu'il est possible de combiner cette fonctionnalité avec des conseils de type pour créer essentiellement des tableaux dactylographiés.

Par exemple, nous pourrions avoir une classe de film avec une méthode pour définir un tableau de dates d'air qui n'acceptent que des objets DateTimeMutable:

<span><span><?php
</span></span><span>
</span><span><span>class Movie {  
</span></span><span>  <span>private $dates = [];
</span></span><span>
</span><span>  <span>public function setAirDates(\DateTimeImmutable ...$dates) {
</span></span><span>    <span>$this->dates = $dates;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAirDates() {
</span></span><span>    <span>return $this->dates;
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

Nous pouvons désormais transmettre un nombre variable d'objets DateTimeMutable distincts à la méthode SetairDates ():

<span><span><?php
</span></span><span>
</span><span><span>$movie = new Movie();
</span></span><span>
</span><span><span>$movie->setAirDates(
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-01-28'),
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-02-22')
</span></span><span><span>);
</span></span>

Si nous devions passer autre chose qu'une datetimetimeimutable, une chaîne par exemple, une erreur fatale serait lancée:

Création de tableaux et de collections strictement typés dans PHP

Si nous avions déjà un tableau d'objets DateTimeMutable que nous voulions passer à setairdates (), nous pourrions à nouveau utiliser le ... jeton, mais cette fois pour les déballer:

<span><span><?php
</span></span><span>
</span><span><span>$dates = [
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-01-28'),
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-02-22'),
</span></span><span><span>];
</span></span><span>
</span><span><span>$movie = new Movie();
</span></span><span><span>$movie->setAirDates(...$dates);
</span></span>

Si le tableau contenait une valeur qui n'est pas du type attendu, nous obtiendrions toujours l'erreur mortelle mentionnée plus tôt.

De plus, nous pouvons utiliser les types scalaires de la même manière à partir de PHP 7. Par exemple, nous pouvons ajouter une méthode pour définir une liste de notes en tant que flotteurs sur notre classe de film:

<span><span><?php
</span></span><span>
</span><span><span>class Movie {  
</span></span><span>  <span>private $dates = [];
</span></span><span>
</span><span>  <span>public function setAirDates(\DateTimeImmutable ...$dates) {
</span></span><span>    <span>$this->dates = $dates;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAirDates() {
</span></span><span>    <span>return $this->dates;
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

Encore une fois, cela garantit que la propriété des notations contiendra toujours des flotteurs sans que nous ayons à boucler sur tout le contenu pour les valider. Alors maintenant, nous pouvons facilement faire des opérations mathématiques sur eux dans getAverAreating (), sans avoir à nous soucier des types non valides.

Problèmes avec ce type de tableaux typés

L'un des inconvénients de l'utilisation de cette fonctionnalité comme tableaux typés est que nous ne pouvons définir qu'un seul tableau par méthode. Supposons que nous voulions avoir une classe de film qui attend une liste de dates d'air avec une liste de notes dans le constructeur, au lieu de les définir plus tard via des méthodes facultatives. Ce serait impossible avec la méthode utilisée ci-dessus.

Un autre problème est que lors de l'utilisation de PHP 7, les types de retour de nos méthodes GET () devraient encore être «tableau», qui est souvent trop générique.

Solution: Classes de collecte

Pour résoudre les deux problèmes, nous pouvons simplement injecter nos tableaux typés à l'intérieur des classes «collection». Cela améliore également notre séparation des préoccupations, car nous pouvons désormais déplacer la méthode de calcul pour la note moyenne à la classe de collecte pertinente:

<span><span><?php
</span></span><span>
</span><span><span>$movie = new Movie();
</span></span><span>
</span><span><span>$movie->setAirDates(
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-01-28'),
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-02-22')
</span></span><span><span>);
</span></span>

Remarquez comment nous utilisons toujours une liste d'arguments typés avec une longueur variable dans notre constructeur, ce qui nous évite de faire du bouclage sur chaque note pour vérifier son type.

Si nous voulions la possibilité d'utiliser cette classe de collecte dans Foreach Loops, nous devons simplement implémenter l'interface IteratorAggregate:

<span><span><?php
</span></span><span>
</span><span><span>$dates = [
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-01-28'),
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-02-22'),
</span></span><span><span>];
</span></span><span>
</span><span><span>$movie = new Movie();
</span></span><span><span>$movie->setAirDates(...$dates);
</span></span>

En continuant, nous pouvons également créer une collection pour notre liste de dates d'air:

<span><span><?php
</span></span><span>
</span><span><span>declare(strict_types=1);
</span></span><span>
</span><span><span>class Movie {
</span></span><span>  <span>private $dates = [];
</span></span><span>  <span>private $ratings = [];
</span></span><span>
</span><span>  <span>public function setAirDates(\DateTimeImmutable ...$dates) { /* ... */ }
</span></span><span>  <span>public function getAirDates() : array { /* ... */ }
</span></span><span>
</span><span>  <span>public function setRatings(float ...$ratings) {
</span></span><span>    <span>$this->ratings = $ratings;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAverageRating() : float {
</span></span><span>    <span>if (empty($this->ratings)) {
</span></span><span>      <span>return 0;
</span></span><span>    <span>}
</span></span><span>
</span><span>    <span>$total = 0;
</span></span><span>
</span><span>    <span>foreach ($this->ratings as $rating) {
</span></span><span>      <span>$total += $rating;
</span></span><span>    <span>}
</span></span><span>
</span><span>    <span>return $total / count($this->ratings);
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

En rassemblant toutes les pièces du puzzle dans la classe de films, nous pouvons maintenant injecter deux collections tapées séparément dans notre constructeur. De plus, nous pouvons définir des types de retour plus spécifiques que «tableau» sur nos méthodes GET:

<span><span><?php
</span></span><span>
</span><span><span>declare(strict_types=1);
</span></span><span>
</span><span><span>class Ratings {
</span></span><span>  <span>private $ratings;
</span></span><span>
</span><span>  <span>public function __construct(float ...$ratings) {
</span></span><span>    <span>$this->ratings = $ratings;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAverage() : float {
</span></span><span>    <span>if (empty($this->ratings)) {
</span></span><span>      <span>return 0;
</span></span><span>    <span>}
</span></span><span>
</span><span>    <span>$total = 0;
</span></span><span>
</span><span>    <span>foreach ($this->ratings as $rating) {
</span></span><span>      <span>$total += $rating;
</span></span><span>    <span>}
</span></span><span>
</span><span>    <span>return $total / count($this->ratings);
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

Utilisation d'objets de valeur pour la validation personnalisée

Si nous voulions ajouter une validation supplémentaire à nos notes, nous pourrions encore aller plus loin et définir un objet de valeur de notation avec certaines contraintes personnalisées. Par exemple, une note pourrait être limitée entre 0 et 5:

<span><span><?php
</span></span><span>
</span><span><span>declare(strict_types=1);
</span></span><span>
</span><span><span>class Ratings implements IteratorAggregate {
</span></span><span>  <span>private $ratings;
</span></span><span>
</span><span>  <span>public function __construct(float ...$ratings) {
</span></span><span>    <span>$this->ratings = $ratings;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAverage() : float { /* ... */ }
</span></span><span>
</span><span>  <span>public function getIterator() {
</span></span><span>     <span>return new ArrayIterator($this->ratings);
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

De retour dans notre classe de collecte de notation, nous n'aurions qu'à effectuer des modifications mineures pour utiliser ces objets de valeur au lieu de flotteurs:

<span><span><?php
</span></span><span>
</span><span><span>class AirDates implements IteratorAggregate {
</span></span><span>  <span>private $dates;
</span></span><span>
</span><span>  <span>public function __construct(\DateTimeImmutable ...$dates) {
</span></span><span>    <span>$this->dates = $dates;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getIterator() {
</span></span><span>     <span>return new ArrayIterator($this->airdates);
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

De cette façon, nous obtenons une validation supplémentaire des membres de la collection individuels, toujours sans avoir à boucler sur chaque objet injecté.

Avantages

taper ces classes de collecte et objet de valeur distinctes peut sembler beaucoup de travail, mais ils ont plusieurs avantages par rapport aux tableaux génériques et aux valeurs scalaires:

  • Validation de type facile en un seul endroit. Nous n'avons jamais à faire un peu manuellement sur un tableau pour valider les types de membres de notre collection;

  • Partout où nous utilisons ces collections et évaluons les objets dans notre application, nous savons que leurs valeurs ont toujours été validées sur la construction. Par exemple, toute note sera toujours comprise entre 0 et 5;

  • Nous pouvons facilement ajouter une logique personnalisée par collection et / ou objet de valeur. Par exemple, la méthode getAverage (), que nous pouvons réutiliser tout au long de notre application;

  • Nous obtenons la possibilité d'injecter plusieurs listes typées dans une seule fonction ou méthode, que nous ne pouvons pas faire en utilisant le jeton ... sans injecter les valeurs dans les classes de collecte en premier;

  • Il y a des chances considérablement réduites de mélange des arguments dans les signatures de la méthode. Par exemple, lorsque nous voulons injecter à la fois une liste des notes et une liste de dates d'air, les deux pourraient facilement se mêler par accident lors de la construction lors de l'utilisation de tableaux génériques;

Qu'en est-il des modifications?

Maintenant, vous vous demandez peut-être comment vous pourriez apporter des modifications aux valeurs de vos collections et à la valeur des objets après la construction initiale.

Bien que nous puissions ajouter des méthodes pour faciliter les modifications, cela deviendrait rapidement lourd car nous devions dupliquer la plupart des méthodes sur chaque collection pour garder l'avantage des indices de type. Par exemple, une méthode ADD () sur les cotes ne doit accepter qu'un objet de notation, tandis qu'une méthode ADD () sur les dates ne doit accepter qu'un objet DateTimeMutable. Cela rend l'interfaçage et / ou la réutilisation de ces méthodes très durement.

Au lieu de cela, nous pouvions simplement garder nos collections et évaluer les objets immuables, et les convertir en types primitifs lorsque nous devons apporter des modifications. Une fois les modifications terminées, nous pouvons simplifier la reconstruction de toutes les collections ou les objets de valeur nécessaires avec les valeurs mises à jour. Lors de (ré) construction, tous les types seraient à nouveau validés, ainsi que toute validation supplémentaire que nous aurions pu définir.

Par exemple, nous pourrions ajouter une méthode ToArray () simple à nos collections et apporter des modifications comme ceci:

<span><span><?php
</span></span><span>
</span><span><span>class Movie {  
</span></span><span>  <span>private $dates = [];
</span></span><span>
</span><span>  <span>public function setAirDates(\DateTimeImmutable ...$dates) {
</span></span><span>    <span>$this->dates = $dates;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAirDates() {
</span></span><span>    <span>return $this->dates;
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

De cette façon, nous pouvons également réutiliser les fonctionnalités existantes du tableau comme array_filter ().

Si nous devions vraiment effectuer des modifications sur les objets de collecte eux-mêmes, nous pourrions ajouter les méthodes nécessaires sur une base de besoin à avoir lieu où qu'elles sont nécessaires. Mais gardez à l'esprit que la plupart d'entre eux devront également faire la validation de type des arguments donnés, il est donc difficile de les réutiliser dans toutes les différentes classes de collecte.

réutiliser des méthodes génériques

Comme vous l'avez peut-être remarqué, nous obtenons toujours une duplication de code dans nos classes de collecte en mettant en œuvre à la fois ToArray () et Getiterator () sur tous. Heureusement, ces méthodes sont suffisamment génériques pour passer à une classe de parent générique, car elles renvoient simplement le tableau injecté:

<span><span><?php
</span></span><span>
</span><span><span>$movie = new Movie();
</span></span><span>
</span><span><span>$movie->setAirDates(
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-01-28'),
</span></span><span>  <span><span>\DateTimeImmutable</span>::createFromFormat('Y-m-d', '2017-02-22')
</span></span><span><span>);
</span></span>

Tout ce qui nous reste dans notre classe de collection serait la validation de type dans le constructeur, et toute logique supplémentaire facultative spécifique à cette collection, comme ceci:

<span><span><?php
</span></span><span>
</span><span><span>class Movie {  
</span></span><span>  <span>private $dates = [];
</span></span><span>
</span><span>  <span>public function setAirDates(\DateTimeImmutable ...$dates) {
</span></span><span>    <span>$this->dates = $dates;
</span></span><span>  <span>}
</span></span><span>
</span><span>  <span>public function getAirDates() {
</span></span><span>    <span>return $this->dates;
</span></span><span>  <span>}
</span></span><span><span>}
</span></span>

éventuellement, nous pourrions rendre notre collection définitive, pour empêcher les classes d'enfants de gâcher la propriété de valeurs d'une manière qui pourrait annuler notre validation de type.

Conclusion

Bien que encore loin d'être parfait, il devenait régulièrement plus facile de travailler avec la validation de type dans les collections et la valeur des objets avec des versions récentes de PHP.

Idéalement, nous obtiendrions une forme de génériques dans une future version de PHP pour faciliter davantage la création de classes de collecte réutilisables.

Une fonctionnalité qui améliorerait considérablement l'utilisation des objets de valeur serait la possibilité de lancer un objet à différents types primitifs, en plus de la chaîne. Cela pourrait facilement être mis en œuvre en ajoutant des méthodes magiques supplémentaires comparables à __ToString (), comme __TOINT (), __Tofloat (), etc.

Heureusement, il y a des RFC en cours pour éventuellement implémenter les deux fonctionnalités dans les versions ultérieures, donc les doigts croisés! ?

  • Génériques: https://wiki.php.net/rfc/generrics

  • Tableaux génériques: https://wiki.php.net/rfc/generic-arrays

  • Casting Objet à Scalar: https://wiki.php.net/rfc/class_casting_to_scalar


Si vous avez trouvé ce tutoriel utile, veuillez visiter le post d'origine sur Medium et lui donner un peu. Si vous avez des commentaires, des questions ou des commentaires, veuillez les laisser ci-dessous ou en réponse sur le post d'origine.

Les questions fréquemment posées (FAQ) sur la création de tableaux et de collections strictement typés dans PHP

Quels sont les avantages de l'utilisation de tableaux strictement typés dans PHP?

Les tableaux typés strictement en PHP fournissent un moyen de s'assurer que tous les éléments d'un tableau sont d'un type spécifique. Cela peut être particulièrement utile dans des applications plus grandes et plus complexes où la cohérence des données est cruciale. En appliquant un type spécifique pour tous les éléments d'un tableau, vous pouvez empêcher les bogues et les erreurs potentiels qui pourraient se produire en raison de types de données inattendus. Cela rend également votre code plus prévisible et plus facile à déboguer, car vous connaissez toujours le type de données avec lesquelles vous travaillez.

Comment puis-je créer un tableau strictement dactylo Ne prend pas en charge nativement les tableaux strictement typés. Cependant, vous pouvez créer une classe qui applique la vérification du type sur les éléments ajoutés au tableau. Cette classe aurait des méthodes pour ajouter et récupérer des éléments, et ces méthodes vérifieraient le type de l'élément avant d'effectuer l'opération. Si le type de l'élément ne correspond pas au type attendu, une erreur serait lancée.

Puis-je utiliser le type de type avec des tableaux dans PHP?

Oui, PHP prend en charge le type de type pour les tableaux. Vous pouvez spécifier qu'une fonction ou une méthode attend un tableau comme argument en ajoutant «tableau» avant le nom de l'argument dans la déclaration de fonction ou de méthode. Cependant, cela garantit seulement que l'argument est un tableau, pas que tous les éléments du tableau sont de type spécifique.

Quelle est la différence entre les tableaux de type librement typés et strictement typés?

Dans un tableau à dactylographie vaguement, les éléments peuvent être de n'importe quel type. Dans un tableau strictement typé, tous les éléments doivent être de type spécifique. Si vous essayez d'ajouter un élément d'un type différent à un tableau strictement typé, une erreur sera lancée.

Comment puis-je appliquer le type de vérification dans PHP?

Vous pouvez appliquer le type de vérification du type Php en utilisant le «Declare (strict_types = 1);» Directive au début de votre fichier PHP. Cela appliquera une vérification stricte de la vérification de tous les appels de fonction et des instructions de retour dans le fichier.

Puis-je créer un tableau d'objets strictement typé dans PHP?

Oui, vous pouvez créer un tableau strictement typé strictement tapé des objets en PHP en créant une classe qui applique la vérification du type sur les objets ajoutés au tableau. La classe aurait des méthodes pour ajouter et récupérer des objets, et ces méthodes vérifieraient le type de l'objet avant d'effectuer l'opération.

Quelles sont les limites des tableaux strictement typés en php?

Le La principale limitation des tableaux strictement typés dans PHP est qu'ils nécessitent un code supplémentaire pour implémenter, car PHP ne les soutient pas nativement. Cela peut rendre votre code plus complexe et plus difficile à maintenir. De plus, les tableaux strictement typés peuvent être moins flexibles que les tableaux tapés de manière lâche, car ils ne permettent pas d'éléments de types différents.

Puis-je utiliser la distinction de type avec des tableaux multidimensionnels en php?

Oui, Vous pouvez utiliser la conduite de type avec des tableaux multidimensionnels en PHP. Cependant, la distinction de type PHP garantit uniquement que l'argument est un tableau, pas que tous les éléments du tableau (ou des sous-terrains) soient de type spécifique.

Comment puis-je gérer les erreurs lors de l'utilisation de tableaux strictement typés dans Php?

Lorsque vous utilisez des tableaux strictement typés dans PHP, vous pouvez gérer les erreurs en utilisant des blocs de capture d'essai. Si une erreur se produit lors de l'ajout d'un élément au tableau (par exemple, si l'élément est du mauvais type), une exception sera lancée. Vous pouvez attraper cette exception et la gérer de manière appropriée.

Puis-je utiliser des tableaux strictement typés avec des fonctions de tableau intégrées de PHP?

Oui, vous pouvez utiliser des tableaux strictement typés avec le tableau intégré de PHP de PHP fonctions. Cependant, vous devez être prudent, car ces fonctions n'appliquent pas la vérification du type. Si vous utilisez une fonction qui modifie le tableau et ajoute un élément du mauvais type, cela pourrait entraîner des erreurs.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn