PHP 8.1 est disponible. Cette version permet d'obtenir une référence à n'importe quelle fonction
Et apporte le type de retour never
Le 2021-11-26 09:12:22, par Stéphane le calme, Chroniqueur Actualités
PHP 8.1 est une mise à jour majeure du langage PHP. Il contient de nombreuses nouvelles fonctionnalités, notamment des énumérations, des propriétés en lecture seule, une syntaxe appelable de première classe, des fibres, des types d'intersection, des améliorations de performances, etc.
Énumérations
Les énumérations ou « Enums » permettent à un développeur de définir un type personnalisé limité à l'une des valeurs possibles parmi un nombre discret. Cela peut être particulièrement utile lors de la définition d'un modèle de domaine, car cela permet de « rendre les états invalides non représentables ».
Les énumérations apparaissent dans de nombreuses langages avec une variété de fonctionnalités différentes. En PHP, les Enums sont un type particulier d'objet. L'Enum lui-même est une classe et ses cas possibles sont tous des objets à instance unique de cette classe. Cela signifie que les cas Enum sont des objets valides et peuvent être utilisés partout où un objet peut être utilisé, y compris les vérifications de type.
L'exemple le plus populaire d'énumérations est le type booléen intégré, qui est un type énuméré avec les valeurs légales true et false. Les énumérations permettent aux développeurs de définir leurs propres énumérations arbitrairement robustes.
Dans PHP 8.1, les énumérations sont limitées aux « énumérations d'unités », c'est-à-dire aux énumérations qui sont elles-mêmes une valeur, plutôt qu'une simple syntaxe sophistiquée pour une constante primitive, et n'incluent pas d'informations associées supplémentaires. Cette capacité offre une prise en charge considérablement étendue de la modélisation des données, des définitions de types personnalisées et du comportement de style monade. Les énumérations permettent la technique de modélisation consistant à « rendre les états invalides non représentables », ce qui conduit à un code plus robuste avec moins de tests exhaustifs.
Les responsables du langage recommande d'utiliser les énumérations au lieu d'un ensemble de constantes et d'obtenir ainsi une validation prête à l'emploi.
Par exemple, avant PHP 8.1, vous pouviez écrire :
Ce code est optimisé en PHP 8.1 avec les énumérations :
Propriétés en lecture seule readonly
Les objets valeur sont souvent immuables : les propriétés sont initialisées une fois dans le constructeur et ne doivent pas être modifiées par la suite. PHP n'a actuellement aucun moyen d'appliquer cette contrainte. L'alternative la plus proche consiste à déclarer la propriété private et à n'exposer qu'un getter public :
Cela ne rend pas réellement la propriété en lecture seule, mais cela resserre la portée où une modification pourrait se produire sur une seule déclaration de classe. Malheureusement, cela nécessite l'utilisation d'un passe-partout getter, ce qui entraîne une moins bonne ergonomie.
La prise en charge des propriétés en lecture seule de première classe vous permet d'exposer directement les propriétés publiques en lecture seule, sans craindre que les invariants de classe puissent être rompus par une modification externe :
Aussi, cette proposition a été retenue : une propriété en lecture seule ne peut être initialisée qu'une seule fois, et uniquement à partir de la portée où elle a été déclarée. Toute autre affectation ou modification de la propriété entraînera une exception d'erreur.
Cette variante n'est pas autorisée, car l'affectation d'initialisation se fait depuis l'extérieur de la classe :
Les modifications ne sont pas nécessairement des affectations simples, tous les éléments suivants entraîneront également une exception d'erreur :
Cependant, les propriétés en lecture seule n'excluent pas la mutabilité intérieure. Les objets (ou ressources) stockés dans des propriétés en lecture seule peuvent toujours être modifiés en interne :
Restrictions
Le modificateur readonly ne peut être appliqué qu'aux propriétés typées. La raison en est que les propriétés non typées ont une valeur par défaut nulle implicite, qui compte comme une affectation d'initialisation et causerait probablement de la confusion.
Grâce à l'introduction du type mixte dans PHP 8.0, une propriété en lecture seule sans contraintes de type peut être créée en utilisant le type mixte :
L'alternative serait de ne pas utiliser de valeur par défaut nulle implicite pour les propriétés en lecture seule non typées. Cependant, cela rendrait les règles pour les valeurs par défaut des propriétés implicites plus complexes et déroutantes. Le simple fait d'en faire une condition d'erreur permet au développeur de s'inscrire explicitement en spécifiant le type mixte.
Syntaxe appelable de première classe
PHP 8.1 introduit une syntaxe appelable de première classe, qui remplace les encodages existants utilisant des chaînes et des tableaux. L'avantage est que la nouvelle syntaxe est accessible à l'analyse statique et respecte la portée au moment où l'appelable est créé.
Dans cet exemple, chaque paire d'expressions est équivalente. La syntaxe strlen(...) crée un Closure qui fait référence à la fonction strlen(), et ainsi de suite.
Le ... peut être vu comme la syntaxe de décompression d'arguments ...$args, avec les arguments réels non encore renseignés :
Aussi, avant PHP 8.1 vous aviez :
À partir de PHP 8.1 vous avez :
Il est désormais possible d'obtenir une référence à n'importe quelle fonction - c'est ce qu'on appelle la syntaxe appelable de première classe.
Expression new dans les initialisateurs
PHP 8.1 autorise l'utilisation des expressions new dans les valeurs par défaut des paramètres, les arguments d'attribut, les initialiseurs de variables statiques et les initialiseurs de constantes globales.
Avant cette version, un code tel que celui-ci n'était pas autorisé :
Au lieu de cela, il était nécessaire d'écrire ainsi :
Cela rend la valeur par défaut réelle moins évidente (du point de vue du contrat d'API) et nécessite l'utilisation d'un argument nullable.
Aussi, les responsables ont accepté la demande d'assouplissement de cette restriction : le langage autorise désormais l'utilisation de new à l'intérieur de certaines expressions d'initialisation.
Les objets peuvent désormais être utilisés comme valeurs de paramètres par défaut, variables statiques et constantes globales, ainsi que dans les arguments d'attribut.
Cela permet effectivement d'utiliser des attributs imbriqués.
Avant PHP 8.1 :
Depuis PHP 8.1 :
Types d'intersection purs
Un « type d'intersection » nécessite une valeur pour satisfaire plusieurs contraintes de type au lieu d'une seule.
Avant PHP 8.1, les types d'intersection n'étaient pas pris en charge nativement par le langage. Au lieu de cela, il fallait soit utiliser des annotations phpdoc, et/ou abuser des propriétés typées comme on peut le voir dans l'exemple suivant :
La prise en charge des types d'intersection dans le langage permet de déplacer davantage d'informations de type de phpdoc vers les signatures de fonction, avec les avantages habituels que cela apporte :
Avant PHP 8.1 :
Depuis PHP 8.1 :
Cette version permet donc d'utiliser des types d'intersection lorsqu'une valeur doit satisfaire plusieurs contraintes de type en même temps.
Il n'est actuellement pas possible de mélanger des types d'intersection et d'union tels que A&B|C.
Le type de retour never
Une fonction ou une méthode déclarée avec le type never indique qu'elle ne retournera pas de valeur et qu'elle lancera une exception ou terminera l'exécution du script avec un appel de die(), exit(), trigger_error() ou quelque chose de similaire.
never est disponible à partir de PHP 8.1.0.
never est, dans le jargon de la théorie des types, le type inférieur. Cela signifie qu'il s'agit du sous-type de tous les autres types et qu'il peut remplacer tout autre type de retour lors de l'héritage.
Avant PHP 8.1 :
Depuis PHP 8.1 :
Source : PHP
Et vous ?
Que pensez-vous des fonctionnalités livrées avec PHP 8.1 ?
Lesquelles vous intéressent le plus ?
Quelles fonctionnalités aimeriez-vous voir apparaître dans une future mise à jour ?
De manière plus globale, comment trouvez vous l'évolution du langage ?
Voir aussi
PHP 8 est disponible et s'accompagne d'optimisations et de nouvelles fonctionnalités, incluant entre autres les arguments nommés, les types d'union, l'opérateur nullsafe, la compilation JIT
Les nouveautés de PHP 8 - Sortie de la version alpha 1 prévue le 25 juin 2020 ! Par Alexandre Tranchant
PHP 7.4.0 est disponible avec de nombreuses améliorations et de nouvelles fonctionnalités, telles que les propriétés typées, un séparateur numérique littéral, et autres
Microsoft annonce qu'il ne va plus offrir de support à PHP sur Windows dès la version 8.0, dont la sortie est prévue pour novembre
Énumérations
Les énumérations ou « Enums » permettent à un développeur de définir un type personnalisé limité à l'une des valeurs possibles parmi un nombre discret. Cela peut être particulièrement utile lors de la définition d'un modèle de domaine, car cela permet de « rendre les états invalides non représentables ».
Les énumérations apparaissent dans de nombreuses langages avec une variété de fonctionnalités différentes. En PHP, les Enums sont un type particulier d'objet. L'Enum lui-même est une classe et ses cas possibles sont tous des objets à instance unique de cette classe. Cela signifie que les cas Enum sont des objets valides et peuvent être utilisés partout où un objet peut être utilisé, y compris les vérifications de type.
L'exemple le plus populaire d'énumérations est le type booléen intégré, qui est un type énuméré avec les valeurs légales true et false. Les énumérations permettent aux développeurs de définir leurs propres énumérations arbitrairement robustes.
Dans PHP 8.1, les énumérations sont limitées aux « énumérations d'unités », c'est-à-dire aux énumérations qui sont elles-mêmes une valeur, plutôt qu'une simple syntaxe sophistiquée pour une constante primitive, et n'incluent pas d'informations associées supplémentaires. Cette capacité offre une prise en charge considérablement étendue de la modélisation des données, des définitions de types personnalisées et du comportement de style monade. Les énumérations permettent la technique de modélisation consistant à « rendre les états invalides non représentables », ce qui conduit à un code plus robuste avec moins de tests exhaustifs.
Les responsables du langage recommande d'utiliser les énumérations au lieu d'un ensemble de constantes et d'obtenir ainsi une validation prête à l'emploi.
Par exemple, avant PHP 8.1, vous pouviez écrire :
Code PHP : |
1 2 3 4 5 6 7 | class Status { const DRAFT = 'draft'; const PUBLISHED = 'published'; const ARCHIVED = 'archived'; } function acceptStatus(string $status) {...} |
Ce code est optimisé en PHP 8.1 avec les énumérations :
Code PHP : |
1 2 3 4 5 6 7 | enum Status { case Draft; case Published; case Archived; } function acceptStatus(Status $status) {...} |
Propriétés en lecture seule readonly
Les objets valeur sont souvent immuables : les propriétés sont initialisées une fois dans le constructeur et ne doivent pas être modifiées par la suite. PHP n'a actuellement aucun moyen d'appliquer cette contrainte. L'alternative la plus proche consiste à déclarer la propriété private et à n'exposer qu'un getter public :
Code PHP : |
1 2 3 4 5 6 7 8 9 | class User { public function __construct( private string $name ) {} public function getName(): string { return $this->name; } } |
Cela ne rend pas réellement la propriété en lecture seule, mais cela resserre la portée où une modification pourrait se produire sur une seule déclaration de classe. Malheureusement, cela nécessite l'utilisation d'un passe-partout getter, ce qui entraîne une moins bonne ergonomie.
La prise en charge des propriétés en lecture seule de première classe vous permet d'exposer directement les propriétés publiques en lecture seule, sans craindre que les invariants de classe puissent être rompus par une modification externe :
Code PHP : |
1 2 3 4 5 | class User { public function __construct( public readonly string $name ) {} } |
Aussi, cette proposition a été retenue : une propriété en lecture seule ne peut être initialisée qu'une seule fois, et uniquement à partir de la portée où elle a été déclarée. Toute autre affectation ou modification de la propriété entraînera une exception d'erreur.
Code PHP : |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Test { public readonly string $prop; public function __construct(string $prop) { // Legal initialization. $this->prop = $prop; } } $test = new Test("foobar"); // Legal read. var_dump($test->prop); // string(6) "foobar" // Illegal reassignment. It does not matter that the assigned value is the same. $test->prop = "foobar"; // Error: Cannot modify readonly property Test::$prop |
Cette variante n'est pas autorisée, car l'affectation d'initialisation se fait depuis l'extérieur de la classe :
Code PHP : |
1 2 3 4 5 6 7 8 | class Test { public readonly string $prop; } $test = new Test; // Illegal initialization outside of private scope. $test->prop = "foobar"; // Error: Cannot initialize readonly property Test::$prop from global scope |
Les modifications ne sont pas nécessairement des affectations simples, tous les éléments suivants entraîneront également une exception d'erreur :
Code PHP : |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Test { public function __construct( public readonly int $i = 0, public readonly array $ary = [], ) {} } $test = new Test; $test->i += 1; $test->i++; ++$test->i; $test->ary[] = 1; $test->ary[0][] = 1; $ref =& $test->i; $test->i =& $ref; byRef($test->i); foreach ($test as &$prop); |
Cependant, les propriétés en lecture seule n'excluent pas la mutabilité intérieure. Les objets (ou ressources) stockés dans des propriétés en lecture seule peuvent toujours être modifiés en interne :
Code PHP : |
1 2 3 4 5 6 7 8 9 | class Test { public function __construct(public readonly object $obj) {} } $test = new Test(new stdClass); // Legal interior mutation. $test->obj->foo = 1; // Illegal reassignment. $test->obj = new stdClass; |
Restrictions
Le modificateur readonly ne peut être appliqué qu'aux propriétés typées. La raison en est que les propriétés non typées ont une valeur par défaut nulle implicite, qui compte comme une affectation d'initialisation et causerait probablement de la confusion.
Grâce à l'introduction du type mixte dans PHP 8.0, une propriété en lecture seule sans contraintes de type peut être créée en utilisant le type mixte :
Code PHP : |
1 2 3 | class Test { public readonly mixed $prop; } |
L'alternative serait de ne pas utiliser de valeur par défaut nulle implicite pour les propriétés en lecture seule non typées. Cependant, cela rendrait les règles pour les valeurs par défaut des propriétés implicites plus complexes et déroutantes. Le simple fait d'en faire une condition d'erreur permet au développeur de s'inscrire explicitement en spécifiant le type mixte.
Syntaxe appelable de première classe
PHP 8.1 introduit une syntaxe appelable de première classe, qui remplace les encodages existants utilisant des chaînes et des tableaux. L'avantage est que la nouvelle syntaxe est accessible à l'analyse statique et respecte la portée au moment où l'appelable est créé.
Code PHP : |
1 2 3 4 5 6 7 8 | $fn = Closure::fromCallable('strlen'); $fn = strlen(...); $fn = Closure::fromCallable([$this, 'method']); $fn = $this->method(...) $fn = Closure::fromCallable([Foo::class, 'method']); $fn = Foo::method(...); |
Dans cet exemple, chaque paire d'expressions est équivalente. La syntaxe strlen(...) crée un Closure qui fait référence à la fonction strlen(), et ainsi de suite.
Le ... peut être vu comme la syntaxe de décompression d'arguments ...$args, avec les arguments réels non encore renseignés :
Code PHP : |
1 2 3 | $fn = Foo::method(...); // Think of it as: $fn = fn(...$args) => Foo::method(...$args); |
Aussi, avant PHP 8.1 vous aviez :
Code PHP : |
1 2 3 | $foo = [$this, 'foo']; $fn = Closure::fromCallable('strlen'); |
À partir de PHP 8.1 vous avez :
Code PHP : |
1 2 3 | $foo = $this->foo(...); $fn = strlen(...); |
Il est désormais possible d'obtenir une référence à n'importe quelle fonction - c'est ce qu'on appelle la syntaxe appelable de première classe.
Expression new dans les initialisateurs
PHP 8.1 autorise l'utilisation des expressions new dans les valeurs par défaut des paramètres, les arguments d'attribut, les initialiseurs de variables statiques et les initialiseurs de constantes globales.
Avant cette version, un code tel que celui-ci n'était pas autorisé :
Code PHP : |
1 2 3 4 5 | class Test { public function __construct( private Logger $logger = new NullLogger, ) {} } |
Au lieu de cela, il était nécessaire d'écrire ainsi :
Code PHP : |
1 2 3 4 5 6 7 8 9 | class Test { private Logger $logger; public function __construct( ?Logger $logger = null, ) { $this->logger = $logger ?? new NullLogger; } } |
Cela rend la valeur par défaut réelle moins évidente (du point de vue du contrat d'API) et nécessite l'utilisation d'un argument nullable.
Aussi, les responsables ont accepté la demande d'assouplissement de cette restriction : le langage autorise désormais l'utilisation de new à l'intérieur de certaines expressions d'initialisation.
Les objets peuvent désormais être utilisés comme valeurs de paramètres par défaut, variables statiques et constantes globales, ainsi que dans les arguments d'attribut.
Cela permet effectivement d'utiliser des attributs imbriqués.
Avant PHP 8.1 :
Code PHP : |
1 2 3 4 5 6 7 8 9 10 | class User { /** * @Assert\All({ * @Assert\NotNull, * @Assert\Length(min=5) * }) */ public string $name = ''; } |
Depuis PHP 8.1 :
Code PHP : |
1 2 3 4 5 6 7 8 | class User { #[\Assert\All( new \Assert\NotNull, new \Assert\Length(min: 6)) ] public string $name = ''; } |
Types d'intersection purs
Un « type d'intersection » nécessite une valeur pour satisfaire plusieurs contraintes de type au lieu d'une seule.
Avant PHP 8.1, les types d'intersection n'étaient pas pris en charge nativement par le langage. Au lieu de cela, il fallait soit utiliser des annotations phpdoc, et/ou abuser des propriétés typées comme on peut le voir dans l'exemple suivant :
Code PHP : |
1 2 3 4 5 6 7 8 9 10 11 12 | class Test { private ?Traversable $traversable = null; private ?Countable $countable = null; /** @var Traversable&Countable */ private $both = null; public function __construct($countableIterator) { $this->traversable =& $this->both; $this->countable =& $this->both; $this->both = $countableIterator; } } |
La prise en charge des types d'intersection dans le langage permet de déplacer davantage d'informations de type de phpdoc vers les signatures de fonction, avec les avantages habituels que cela apporte :
- Les types sont effectivement appliqués, de sorte que les erreurs peuvent être détectées tôt.
- Parce qu'elles sont appliquées, les informations de type sont moins susceptibles de devenir obsolètes ou de manquer des cas limites.
- Les types sont vérifiés lors de l'héritage, appliquant le principe de substitution de Liskov.
- Les types sont disponibles via Reflection.
- La syntaxe est beaucoup moins passe-partout que phpdoc.
Avant PHP 8.1 :
Code PHP : |
1 2 3 4 5 6 7 8 9 10 11 | function count_and_iterate(Iterator $value) { if (!($value instanceof Countable)) { throw new TypeError('value must be Countable'); } foreach ($value as $val) { echo $val; } count($value); } |
Depuis PHP 8.1 :
Code PHP : |
1 2 3 4 5 6 7 | function count_and_iterate(Iterator&Countable $value) { foreach ($value as $val) { echo $val; } count($value); } |
Cette version permet donc d'utiliser des types d'intersection lorsqu'une valeur doit satisfaire plusieurs contraintes de type en même temps.
Il n'est actuellement pas possible de mélanger des types d'intersection et d'union tels que A&B|C.
Le type de retour never
Une fonction ou une méthode déclarée avec le type never indique qu'elle ne retournera pas de valeur et qu'elle lancera une exception ou terminera l'exécution du script avec un appel de die(), exit(), trigger_error() ou quelque chose de similaire.
never est disponible à partir de PHP 8.1.0.
never est, dans le jargon de la théorie des types, le type inférieur. Cela signifie qu'il s'agit du sous-type de tous les autres types et qu'il peut remplacer tout autre type de retour lors de l'héritage.
Avant PHP 8.1 :
Code PHP : |
1 2 3 4 5 6 7 8 9 | function redirect(string $uri) { header('Location: ' . $uri); exit(); } function redirectToLoginPage() { redirect('/login'); echo 'Hello'; // <- dead code } |
Depuis PHP 8.1 :
Code PHP : |
1 2 3 4 5 6 7 8 9 | function redirect(string $uri): never { header('Location: ' . $uri); exit(); } function redirectToLoginPage(): never { redirect('/login'); echo 'Hello'; // <- dead code detected by static analysis } |
Source : PHP
Et vous ?
Voir aussi
-
grunkModérateurL'utilisation de "@" n'a jamais été recommandé , elle ne fait que masquer une erreur , pas la traiter correctement.le 30/11/2021 à 17:28
-
BeezarMembre à l'essaiIl s'agit ici de simplifier la gestion des retours null.
Si tu as besoin d'une gestion fine, tu ne passeras pas par ce système.
Si une exception est levée c'est à toi de l'attraper.
Bref ca ne change rien à la gestion des exceptions.le 01/12/2021 à 9:00 -
grunkModérateurLa syntaxe que tu évoques (opérateur nullsafe) permet juste d'éviter de vérifier l'état null et d'écrire des trucs comme :
Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15if(is_null($foo)) { $result = null; } else { $bar = $foo->findBar(); if(is_null($bar)) { $result = null; } else { $coca = $bar->getCoca(); if(is_null($coca)) { $result = null; } else { $result = $coca->pay(); } } }
Code : 1
2
3
4
5try { $result = $result=$foo?->findBar()?->getCoca()?->pay(); } catch(Exception $e) { $result = null; }
le 01/12/2021 à 9:35 -
GaulouisMembre confirméOk, merci à vous deux, je viens de comprendre.Il s'agit ici de simplifier la gestion des retours null.
Et pour une raison de rétrocompatibilité le choix a été d'ajouter des élément de langage plutôt que de modifier le comportement historique.le 01/12/2021 à 18:24 -
Séb.Expert éminent@ le faisait déjà sur les propriétés mais pas sur les méthodes.
@ est principalement est utile sur les fonctions d'ouverture de flux difficilement prévisibles et qui émettent un warning, comme fopen(),
autrement il vaut mieux l'éviter.le 01/12/2021 à 20:43 -
GaulouisMembre confirmé
Envoyé par PHP Manual > Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?php class A { function findBar() { return $this; } function getCoca() { return null;//throw new Exception("is not catchable by '@' or '?'"); } function pay() { return 2.50; } } $foo = new A;
Code : <?php $result = $foo->usualBar->cocaDrink->price;//Warning: Attempt to read property "cocaDrink" on null
Code : <?php $result = $foo?->findBar()?->getCoca()->pay();//Fatal error: Call to a member function pay() on null
Code : 1
2<?php @$result = $foo->usualBar->cocaDrink->price; var_dump($result);// output null
Code : 1
2<?php $result = $foo?->findBar()?->getCoca()?->pay(); var_dump($result);// output null
Code : 1
2<?php @$result = $foo->usualBar()->cocaDrink()->price();// mute (Fatal error: Call to undefined method A::usualBar ) var_dump($result);// output null
le 01/12/2021 à 22:12 -
Séb.Expert éminentIl s'agit d'une exception, et pas d'une erreur ("Uncaught"
. @ ne s'occupe que des erreurs. Pour les exceptions il y a try/catch/finally le 02/12/2021 à 14:48 -
GaulouisMembre confirméBonjour à tous,
Pour moi, php 8.1 c'est le version de trop
Si tu regarde le site officiel de php il y a 2 liens qui pointe vers oracle. Et oracle c'est pas celui qui possède le compte twitter JavaScript Et combien ont investi Oracle & ses amis pour faire Node.JS ?
Avant:
Code php : @$result=$foo->findBar()->getCoca()->pay();
Aujourd'hui:
Code php : $result=$foo?->findBar()?->getCoca()?->pay();
Envoyé par Proverbe
le 30/11/2021 à 11:34 -
GaulouisMembre confirméL'utilisation de "@" n'a jamais été recommandé , elle ne fait que masquer une erreur , pas la traiter correctement.
En quoi cette nouvelle syntaxe est recommandé ?
Code PHP : $result=$foo?->findBar()?->getCoca()?->pay();
Comment va-t-elle traiter l'erreur "CoronaException : No such bar open." ou "ExtraException: No coca available."
Comment ça se concrétise le "démasquage" d'erreur ?le 30/11/2021 à 20:30