Watch a demoFree trial
Blog

Amélioration des types de PHP 8.0

PHPfonctionnalitésperformance
30 novembre 2020
Partager
Cet article est également disponible en Anglais.

PHP 8 apporte une multitude d'améliorations, de nouvelles fonctionnalités et un polissage général pour rendre le langage côté serveur préféré du web encore meilleur. Nous allons couvrir ce que vous devez savoir sur PHP 8. C'est une version passionnante, et nous ne couvrirons même pas tout ! Il y a juste tellement de choses qui se passent.

Tout d'abord, nous couvrirons les diverses améliorations apportées au système de types.

Types d'union

Le plus grand changement dans le système de types est l'introduction des types d'union. Les types d'union sont un type virtuel qui est 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 vous vouliez prendre en charge le style de paramètre "un ou plusieurs" comme cela, vous ne pouviez pas typer 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 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 à la fois sur des int ou des float peut maintenant dire exactement cela :

<?php

function doMath(int|float): int|float
{
    // ...
}

Il est également possible de dire explicitement qu'une fonction ou une méthode peut retourner des objets de différents types. Par exemple, une fonction qui prend un objet de requête PSR-7 et qui 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 d'union suivent les mêmes règles de covariance/contravariance que tout autre type. C'est-à-dire que les paramètres de méthode d'une sous-classe sont autorisés à accepter une définition de type plus large que son parent (donc l'ajout d'un |Foo est OK, mais pas sa suppression), tandis qu'une valeur de retour est autorisée à spécifier une définition de type plus étroite que son parent (donc la suppression de |Foo est OK, 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.

Unions spécialisées

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, tels que ?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 de nullable ? est maintenant un raccourci pour null| lorsqu'une seule autre valeur autorisée.

Une autre option réservée aux types d'union est false. Ceci est principalement destiné à prendre en charge les fonctions internes PHP existantes qui, en raison d'erreurs de jugement commises dans les années 1990, renvoient "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 là que pour prendre en charge le code existant qui ne peut pas modifier son API pour être moins mauvaise. Veuillez ne pas l'utiliser pour tout 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 être combinée avec rien, car elle signifie littéralement "rien n'est retourné, point final", il est donc peu logique de la combiner avec d'autres types.

PHP 8 introduit également un nouveau type d'union intégré. Le nouveau type mixed est équivalent à array|bool|callable|int|float|null|object|resource|string. Ce n'est pas tout à fait la même chose que d'omettre entièrement 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 explicite du type mixed indique au moteur et aux autres développeurs : "J'accepte n'importe quoi ici, et je le pense vraiment". Il y a maintenant extrêmement 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.

L'interface Stringable

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, l'indication de type string n'accepte pas les objets convertibles en chaîne 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 et rétrocompatible.

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 toujours cette méthode, le moteur ajoutera automatiquement la méthode et le type de retour. Cela signifie que tout objet stringable peut maintenant être vérifié au niveau du type, y compris dans le cadre d'un type d'union, comme ceci :

<?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() (car elle impose un retour de chaîne de caractères plutôt que de simplement l'assumer), l'équipe Symfony a inclus un polyfill pour celle-ci 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, le fait que __toString() soit plus utilisable maintenant ne signifie pas qu'il faut en abuser. La plupart des objets ne devraient pas avoir de méthode __toString() et devraient avoir des méthodes significatives qui retournent des chaînes de caractères à 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 sous forme 1.2.3.4.

Merci à Nicolas Grekas pour le RFC Stringable.

Petites choses

Enfin, il y a deux autres petites améliorations pour faciliter 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 à nouveau à Nikita Popov.

Enfin, un de mes favoris personnels, les méthodes peuvent maintenant avoir un type de retour static. Cela est principalement utile pour les interfaces avec des méthodes chaî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é pour retourner une instance d'ImportantTask. Auparavant, avec un type de retour de self, cela indiquait seulement qu'il retournait 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 :

Votre meilleur travail
est à l'horizon

Essai gratuit
Discord
© 2025 Platform.sh. All rights reserved.