PHP 8 propose une multitude d'améliorations, de nouvelles fonctionnalités et une amélioration générale du langage côté serveur préféré des internautes. Nous allons couvrir ce que vous devez savoir sur PHP 8. C'est une version passionnante, et nous n'allons même pas tout couvrir ! Il y a tellement de choses à faire.
Tout d'abord, nous allons couvrir les différentes améliorations du système de types.
Le plus grand changement apporté au système de types est l'introduction des types union. Les types union sont des types virtuels qui sont l'"union" (un "OU" logique) de deux autres types. Par exemple, les types union sont des types virtuels qui sont l'union (un OU logique) de deux autres types :
<?php function mangleUsers(string|array $users) : array { If (is_string($users)) { $users = [$users] ; } // ... }
Auparavant, si vous vouliez supporter le style de paramètre "un ou plusieurs", vous ne pouviez pas taper le paramètre. Maintenant, vous pouvez spécifier string|array
et une variable de type string ou array est acceptable. Les entiers, les objets, etc. seront toujours rejetés.
Les types Union fonctionnent sur les paramètres, les types de retour et les types de propriété, et ils supportent tous les types supportés par PHP : primitives, objets, classes définies par l'utilisateur, etc. Bien qu'ils puissent être utilisés pour produire toutes sortes d'API bizarres et incohérentes, celles-ci étaient déjà possibles. Au moins, elles peuvent maintenant être documentées. Plus important encore, cela ouvre la voie à des cas d'utilisation intéressants qui étaient auparavant impossibles à documenter dans le système de types lui-même.
Par exemple, une fonction qui nécessite un nombre mais qui peut fonctionner avec des ints ou des floats peut maintenant le dire exactement :
<?php function doMath(int|float) : int|float { // ... }
Il est également possible d'indiquer explicitement qu'une fonction ou une méthode peut renvoyer des objets de types différents. Par exemple, une fonction qui prend un objet de demandePSR-7 et renvoie une nouvelle demande ou une réponse correspondante ressemblerait à ceci :
<?php function handleRequest(RequestInterface $req) : RequestInterface|ResponseInterface { // ... }
En matière d'héritage, les types d'union suivent les mêmes règles de covariance/contravariance que n'importe quel autre type. En d'autres termes, les paramètres des méthodes d'une sous-classe sont autorisés à accepter une définition de type plus large que celle de son parent (l'ajout d'un |Foo
est donc acceptable, mais pas sa suppression), tandis que la valeur de retour est autorisée à spécifier une définition de type plus étroite que celle de son parent (la suppression d'un |Foo
est donc acceptable, mais pas son ajout).
L'API de réflexion a également été mise à jour pour prendre en charge l'inspection des types d'union sur les fonctions et les propriétés.
Plus de détails sont disponibles dans le RFC original. Merci à Nikita Popov pour celui-ci.
PHP a déjà un certain nombre de types d'union définis dans le langage. iterable
, par exemple, est un type d'union pour array|\Traversable
. Les types nullables, comme ?Product
, sont essentiellement un type d'union pour null|Product
.
En fait, il est maintenant possible de spécifier null
comme type dans une union, mais seulement dans une union. Ainsi, null|RequestInterface|ResponseInterface
est une définition de type légale. Le drapeau ?
nullable est maintenant un raccourci pour null|
quand il n'y a qu'une seule autre valeur autorisée.
Une autre option de type union uniquement est false
. C'est principalement pour supporter les fonctions internes de PHP qui, à cause d'erreurs de jugement commises dans les années 1990, retournent "une valeur, ou false en cas d'erreur". Ces fonctions peuvent maintenant être typées comme suit :
<?php function base64_decode(string $str, bool $strict = false) : string|false {}
Cette capacité n'est destinée qu'à prendre en charge les codes hérités qui ne peuvent pas modifier leur API pour qu'elle soit moins mauvaise. S'il vous plaît, ne l'utilisez pas pour du nouveau code. Comme null
, false
ne peut pas être utilisé comme déclaration de type autonome. De plus, la déclaration de type void
ne peut pas être combinée avec quoi que ce soit, car elle signifie littéralement "rien du tout n'est retourné, point final", donc cela n'a pas beaucoup de sens de la combiner avec d'autres types.
PHP 8 introduit également un nouveau type union intégré. Le nouveau type mixte
est équivalent à array|bool|callable|int|float|null|object|resource|string
. Ce n'est pas tout à fait la même chose que d'omettre complètement le type. L'omission du type peut signifier que le développeur a simplement oublié, ou que le système de type n'est pas assez robuste pour décrire les valeurs possibles autorisées. L'utilisation du type mixte
indique explicitement au moteur et aux autres développeurs : "J'accepte n'importe quoi ici, et je le pense vraiment". Il y a maintenant très peu de cas où il n'est pas possible de taper un paramètre, un retour ou une propriété en PHP 8.
Merci à Máté Kocsis et Dan Ackroid pour cette RFC.
Les objets PHP ont longtemps été capables de définir une façon de les convertir en chaîne de caractères, en utilisant la méthode magique __toString(). Cependant, le type string
hint n'accepte pas les objets castables en mode strict, ce qui a rendu cette méthode moins utile depuis que PHP 7.0 a introduit les types scalaires.
PHP 8 introduit maintenant une interface Stringable
qui correspond à un objet ayant la méthode magique __toString()
. Il le fait également d'une manière intelligente, compatible avec le passé.
Depuis la version 8.0, une classe peut implémenter une interface Stringable
qui définit une méthode public function __toString() : string
. Si ce n'est pas le cas, mais qu'elle implémente tout de même cette méthode, le moteur ajoutera automatiquement la méthode et le type de retour. Cela signifie que n'importe quel objet de type chaîne de caractères peut désormais faire l'objet d'une vérification de type, y compris dans le cadre d'un type d'union, comme dans l'exemple suivant :
<?php function show_message(string|Stringable $message) : void { // ... }
Boom ! __toString
est à nouveau utile.
Parce que la nouvelle interface est, techniquement, légèrement plus stricte que la méthode originale __toString()
(puisqu'elle impose un retour de chaîne plutôt que de le supposer), l'équipe Symfony a inclus un polyfill pour elle dans leur package symfony/polyfill-php80. Cela permet aux développeurs de commencer à l'utiliser dès maintenant pour s'assurer que leurs types sont corrects et seront immédiatement compatibles avec PHP 8.
Attention, ce n'est pas parce que __toString()
est plus utilisable maintenant qu'il faut en faire trop. La plupart des objets ne devraient pas avoir de méthode __toString()
et devraient avoir des méthodes significatives qui retournent des chaînes à la place. Là où __toString()
est utile, c'est pour les objets de valeur qui n'ont qu'une et une seule représentation possible de la chaîne de caractères, parce que l'objet n'est essentiellement qu'une chaîne de caractères fantaisiste. Un bon exemple est l'objet IPv4Address
, qui n'aurait de sens que s'il était converti en chaîne de caractères de type 1.2.3.4
.
Merci à Nicolas Grekas pour le RFC Stringable.
Enfin, il y a deux autres petites améliorations pour aider le code de tous les jours.
Tout d'abord, les objets ont maintenant une constante magique qui spécifie leur classe, tout comme les noms de classe. $object::class
est une chaîne de caractères contenant le nom de la classe, comme App\Form\FormDef
. C'est la même chose que get_class()
, mais plus facile à utiliser. Le mérite de cette fonctionnalité revient à Nikita Popov.
Enfin, et c'est ce que je préfère, les méthodes peuvent désormais avoir un type de retour statique
. C'est principalement utile pour les interfaces avec des méthodes enchaînées. Cela signifie que ce qui suit est maintenant possible :
<?php Interface TaskBuilder { public function addStep(Step $s) : static ; } class ImportantTask implements TaskBuilder { public function addStep(Step $s) : static { $this->steps[] = $s ; return $this ; } }
Et maintenant ImportantTask::addStep()
est typée pour renvoyer une instance d'ImportantTask
. Auparavant, avec un type de retour de self
, il indiquait seulement qu'il renvoyait un TaskBuilder
.
Le mérite de cette amélioration revient une fois de plus à Nikita Popov. (Nous verrons souvent son nom dans cette série).
Liens utiles :