- Fonctionnalités
- Pricing
PHP 8 apporte une multitude d'améliorations, de nouvelles fonctionnalités et des peaufinages généraux pour rendre encore meilleur le langage côté serveur préféré du Web. On va passer en revue ce que tu dois savoir sur PHP 8. C'est une version passionnante, et on ne va même pas pouvoir tout couvrir ! Il y a tellement de choses à dire.
Pour commencer, on va parler des différentes améliorations apportées au 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 constituent l'« union » (un « OU » logique) de deux autres types. Par exemple :
<?php
function mangleUsers(string|array $users): array
{
If (is_string($users)) {
$users = [$users];
}
// ...
}Auparavant, si tu voulais prendre en charge le style de paramètre « un ou plusieurs » comme celui-ci, tu ne pouvais pas spécifier le type du paramètre. Désormais, tu peux spécifier string|array et une variable de type chaîne ou tableau est acceptée. 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 prennent en charge tous les types supportés par PHP : les types primitifs, les objets, les classes définies par l'utilisateur, etc. Bien qu'ils puissent être utilisés pour produire toutes sortes d'API peu élégantes et incohérentes, cela était déjà possible auparavant. Au moins, maintenant, ils peuvent être documentés. 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 aussi bien avec des entiers qu'avec des flottants peut désormais l'indiquer clairement :
<?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 requête PSR-7 et renvoie soit une nouvelle requête, soit une réponse correspondante, ressemblerait à ceci :
<?php
function handleRequest(RequestInterface $req): RequestInterface|ResponseInterface
{
// ...
}En héritage, les types union suivent les mêmes règles de covariance/contravariance que n'importe quel autre type. Autrement dit, les paramètres de méthode d'une sous-classe peuvent accepter une définition de type plus large que celle de sa classe parente (donc ajouter un |Foo est OK, mais pas de le supprimer), tandis qu’une valeur de retour peut spécifier une définition de type plus restreinte que celle de son parent (donc supprimer |Foo est autorisé, mais pas l’ajouter).
L'API de réflexion a également été mise à jour pour prendre en charge l'inspection des types union sur les fonctions et les propriétés.
Tu trouveras plus de détails dans le RFC original. Merci à Nikita Popov pour cette info.
PHP dispose déjà d'un certain nombre de types union spécifiques définis dans le langage. iterable, par exemple, est en fait un type union pour array|\Traversable. Les types nullables, tels que ?Product, sont essentiellement un type union de null|Product.
En fait, il est désormais possible de spécifier null comme type dans une union, mais uniquement dans une union. Ainsi null|RequestInterface|ResponseInterface est une définition de type valide. Le ? indicateur « nullable » est désormais un raccourci pour null| lorsqu’une seule autre valeur est autorisée.
Une autre option réservée aux types union est false. Elle sert principalement à prendre en charge les fonctions internes existantes de PHP qui, à cause d’erreurs de jugement commises dans les années 1990, renvoient « une valeur, ou false en cas d’erreur ». Ces fonctions peuvent désormais être typées comme suit :
<?php
function base64_decode(string $str, bool $strict = false): string|false {}Cette fonctionnalité sert uniquement à prendre en charge le code hérité qui ne peut pas modifier son API pour être moins mauvais. N'utilise pas cette option pour du nouveau code. Tout comme null, false ne peut pas être utilisé comme déclaration de type autonome. De plus, la void déclaration de type ne peut être combinée avec quoi que ce soit, car elle signifie littéralement « rien n’est renvoyé, point », il est donc peu judicieux de la combiner avec d’autres types.
PHP 8 introduit également un nouveau type union intégré. Le nouveau mixed type 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. Omettre le type pourrait signifier que le développeur a simplement oublié, ou que le système de types n’est pas assez robuste pour décrire les valeurs autorisées possibles. Utiliser le mixed type indique explicitement au moteur et aux autres développeurs : « J'accepte n'importe quoi ici, et je le pense vraiment. » Il y a désormais très peu de cas où il n'est pas possible de typiser 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 peuvent depuis longtemps définir une méthode pour être convertis en chaîne, à l'aide de la méthode magique __toString(). Cependant, l' string type hint n'accepte pas les objets convertibles en chaîne en mode strict, ce qui rend cette fonctionnalité moins utile depuis l'introduction des types scalaires en PHP 7.0.
PHP 8 introduit désormais une Stringable interface qui correspond à un objet possédant la __toString() méthode magique. Elle le fait également de manière intelligente et rétrocompatible.
À partir de la version 8.0, une classe peut implémenter une Stringable interface 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. Ça veut dire que n'importe quel objet pouvant être converti en chaîne peut désormais faire l'objet d'un contrôle de type, y compris dans le cadre d'un type union, comme ça :
<?php
function show_message(string|Stringable $message): void
{
// ...
}Et voilà ! __toString est à nouveau utile.
Comme la nouvelle interface est, techniquement, un peu plus stricte que la méthode __toString() (puisqu’elle impose un retour de chaîne plutôt que de simplement le supposer), l’équipe Symfony a inclus un polyfill pour celle-ci dans son paquet symfony/polyfill-php80. Ça permet aux développeurs de commencer à l’utiliser dès maintenant pour s’assurer que leurs types sont corrects et qu’ils seront immédiatement compatibles avec PHP 8.
Remarque, ce n’est pas parce que __toString() soit plus utilisable maintenant ne veut pas dire que tu dois en abuser. La plupart des objets ne devraient pas avoir de __toString() méthode et devraient plutôt disposer de méthodes pertinentes qui renvoient des chaînes de caractères. Là où __toString() est utile, c'est pour les objets de valeur qui n'ont qu'une seule et unique représentation sous forme de chaîne significative, car l'objet n'est en fait qu'une chaîne sophistiquée. Un bon exemple est un IPv4Address objet, qu’il ne serait logique de convertir en chaîne que sous la forme 1.2.3.4 type chaîne.
Merci à Nicolas Grekas pour la proposition de spécification (RFC) sur Stringable.
Enfin, il y a deux autres petites améliorations pour faciliter le codage au quotidien.
Tout d’abord, les objets disposent désormais d’une constante magique qui spécifie leur classe, tout comme le font les noms de classe. $object::class est une chaîne contenant le nom de la classe, par exemple App\Form\FormDef. C'est la même chose que get_class(), mais c'est plus facile à utiliser. Le mérite de ces fonctionnalités revient encore une fois à Nikita Popov.
Enfin, une de mes préférées : les méthodes peuvent désormais avoir un type de retour static. C'est surtout utile pour les interfaces avec des méthodes enchaînées. Ça veut dire que ce qui suit est désormais 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é pour renvoyer une instance de ImportantTask. Auparavant, avec un type de retour self , ça indiquait juste qu’il renvoyait TaskBuilder.
Le mérite de cette amélioration revient une fois de plus à Nikita Popov. (On verra souvent son nom dans cette série.)
Liens utiles :