URL: https://linuxfr.org/news/les-nouvelles-fonctionnalites-de-php-8 Title: Les nouvelles fonctionnalités de PHP 8 Authors: MCMic Julien Jorge, Christophe "CHiPs" PETIT, Julien Boudry, Davy Defaud, BenMorel, Ysabeau, Nils Ratusznik, theojouedubanjo, dourouc05, palm123 et ted Date: 2020-10-05T14:16:45+02:00 License: CC By-SA Tags: php8, php et programmation Score: 6 La version 8 de PHP est sortie le 26 novembre 2020, nous allons donc voir ensemble les nouvelles fonctionnalités qui ont été intégrées dans cette version. Pour ne pas faire trop long, on se limitera aux choses nouvelles par rapport à PHP 7.4, et on regardera les nouvelles fonctionnalités principales, pour une liste exhaustive consultez le changelog officiel. ---- [Article anglophone plus complet sur les nouveautés](https://stitcher.io/blog/new-in-php-8) [Annonce officielle](https://www.php.net/releases/8.0/en.php) [Changelog détaillé](https://www.php.net/ChangeLog-8.php) [Guide de migration](https://www.php.net/manual/en/migration80.php) ---- Nouvelles fonctionnalités ======================== Syntaxe ------- ### Les unions de types PHP continue sa route vers un langage fortement typé, en ajoutant le support des unions de types dans les spécificateurs de type de paramètres, de variables ou de retour de fonction. L’exemple de la RFC : ```php class Number { private int|float $number; public function setNumber(int|float $number): void { $this->number = $number; } public function getNumber(): int|float { return $this->number; } } ``` Cela permet de mettre des informations de type à des endroits où on ne pouvait pas en mettre, lorsque plusieurs types différents sont acceptés ou retournés. RFC : https://wiki.php.net/rfc/union_types_v2 ### Type "mixed" Toujours à propos des types, le type `mixed` a été ajouté, qui indique qu’une variable peut être de n’importe quel type supporté par PHP. Ça peut sembler idiot puisque ça revient au même que de ne pas mettre d’indication de type, mais ça permet au développeur de l’indiquer explicitement, et il existe de nombreux cas de figure où il est tout à fait normal d’accepter tous les types (pour une fonction de sérialisation par exemple). Cela peut aussi servir à indiquer qu’une fonction doit retourner quelque chose, c’est donc le contraire de `:void` qui indique l’absence de retour. Aussi le mot-clé `mixed` était déjà largement utilisé dans la documentation de PHP. RFC : https://wiki.php.net/rfc/mixed_type_v2 ### Type de retour «static» Tant qu’on parle de typage, il est maintenant possible d’indiquer « static » comme type de retour pour une méthode. Cela signifie qu’elle renvoie une instance de la classe sur laquelle la méthode est appelée. À noter qu’il existait déjà le type de retour «self» pour indiquer qu’une méthode renvoi une instance de la classe à laquelle elle appartient, ce qui n’est pas la même chose. Voir la notion PHP de « late static binding » : https://www.php.net/manual/fr/language.oop5.late-static-bindings.php Exemple : class Test { public function create(): static { return new static(); } } RFC : https://wiki.php.net/rfc/static_return_type ### Les attributs Largement la plus grosse source de débats internes pour cette version, les attributs ont fait leur apparition. Un attribut, parfois appelé annotation dans d’autres langages, est une métadonnée qui peut être ajoutée sur une fonction, une classe, un paramètre, une propriété… Cette métadonnée est ensuite disponible par réflexion. Après être passé par `<>` et `@@Attribute`, c’est finalement la syntaxe `#[Attribute]` qui a été choisie au bout de 4 RFC, syntaxe déjà utilisée par Rust. La notation `@Attribute` utilisée dans beaucoup d’autres langages n’était pas disponible, puisque l’opérateur `@` existe déjà en PHP et sert à ignorer les erreurs. RFC : - https://wiki.php.net/rfc/attributes - https://wiki.php.net/rfc/attribute_amendments - https://wiki.php.net/rfc/shorter_attribute_syntax - https://wiki.php.net/rfc/shorter_attribute_syntax_change ### Les paramètres nommés Un autre gros morceau de cette version est le support des paramètres nommés, ce qui signifie qu’il est possible d’indiquer la valeur d’un paramètre de fonction par son nom plutôt que par sa position. Exemples tirés de la RFC : ```php htmlspecialchars($string, double_encode: false); // Au lieu de htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false); array_fill(start_index: 0, num: 100, value: 50); // Au lieu de array_fill(0, 100, 50); // L’ordre n’est plus important, cette version est équivalente array_fill(value: 50, num: 100, start_index: 0); ``` Ce changement signifie que les noms des paramètres font maintenant partie de la signature des fonctions, et que renommer un paramètre de fonction peut casser du code qui utilise la fonction. Pour cette raison un gros travail d’amélioration et d’uniformisation des noms de paramètres a été fait pour PHP-8, et dans les versions suivantes un renommage sera considéré comme cassant la compatibilité descendante. RFC : https://wiki.php.net/rfc/named_params ### La promotion de paramètres constructeurs Dans le but de simplifier l’écriture de classes simples servant simplement de schéma pour des objets contenant des données, et pour encourager leur utilisation plutôt que celle de tableaux, il est maintenant possible de déclarer les propriétés d’une classe directement dans les paramètres du constructeur, afin de ne pas avoir à les écrire deux fois. Exemple : ```php // Avant class Point { public float $x; public float $y; public float $z; public function __construct( float $x = 0.0, float $y = 0.0, float $z = 0.0, ) { $this->x = $x; $this->y = $y; $this->z = $z; } } // En PHP>=8.0 class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, ) {} } ``` À noter que cette nouveauté se marie particulièrement bien avec les paramètres nommés : ```php class Circle { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, public float $r = 1.0, public string $name = '', public string $color = 'black', ) {} } $c1 = new Circle(x:12, y:13, r:5, name:'C1'); ``` RFC : https://wiki.php.net/rfc/constructor_promotion ### Opérateur « nullsafe » Un nouvel opérateur `?->` a été ajouté, permettant d’appeler une fonction ou de récupérer une propriété de l’objet contenu dans la variable sans déclencher d’erreur si la variable est nulle. Cela permet par exemple de chainer les appels de fonctions en autorisant une valeur nulle à plusieurs endroits de la chaine. Exemple : ```php // Le code suivant $country = null; if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } } // Peut maintenant être écrit $country = $session?->user?->getAddress()?->country; ``` RFC : https://wiki.php.net/rfc/nullsafe_operator ### Mot-clé `match` Le mot-clé `match` a été ajouté comme alternative au switch quand on cherche à retourner une valeur depuis chaque branche. Exemple : ```php // Avec switch switch ($mot) { case 'un': $nombre = 1; break; case 'deux': $nombre = 2; break; case 'trois': $nombre = 3; break; default: throw new Exception ('Mot invalide'); break; } // Avec match $nombre = match ($mot) { 'un' => 1, 'deux' => 2, 'trois' => 3, default => throw new Exception ('Mot invalide'), }; ``` Lisez attentivement la RFC pour la liste des subtiles différences avec switch. RFC : https://wiki.php.net/rfc/match_expression_v2 ### Nouvelles classes et interfaces La classe `WeakMap` a été ajoutée, permettant de gérer des maps avec des objets en clé, qui n’empêchent pas ces objets d’être détruits par le ramasse-miettes. Quand un objet est détruit, la valeur qui lui est associée est simplement retirée de la `WeakMap`. RFC : https://wiki.php.net/rfc/weak_maps Une nouvelle classe `ValueError` est lancée quand une méthode reçoit un paramètre du bon type mais avec une valeur inappropriée, pour différencier du cas des `TypeError`, qui apparaissent quand le paramètre n’a pas le bon type. Une nouvelle interface `Stringable` permet de reconnaitre les objets qui peuvent être transtypés en `string`. Elle est automatiquement implémentée par les classes qui définissent la méthode magique `__toString()`. Cela permet, avec l’union des types, d’utiliser le groupe `string|Stringable` pour typer un paramètre qui accepte soit une chaîne de caractères soit n’importe quel objet qui peut se transtyper en chaîne de caractères. RFC : https://wiki.php.net/rfc/stringable ### Autres nouveautés - Il est maintenant possible d’obtenir le nom de la classe d’un objet en utilisant `$object::class`. Le résultat est le même que `get_class($object)`. RFC: https://wiki.php.net/rfc/class_name_literal_on_object - `new` et `instanceof` peuvent maintenant être utilisés avec des expressions arbitraires, sous la forme `new (expression)(...$args)` et `$obj instanceof (expression)`. RFC: https://wiki.php.net/rfc/variable_syntax_tweaks - Des changements ont été faits pour rendre la syntaxe des variables plus cohérentes, par exemple il est maintenant autorisé d’écrire `Foo::BAR::$baz`. RFC: https://wiki.php.net/rfc/variable_syntax_tweaks - Les traits peuvent maintenant définir des méthodes privées. RFC: https://wiki.php.net/rfc/abstract_trait_method_validation - `throw` peut désormais être utilisé comme une expression. RFC: https://wiki.php.net/rfc/throw_expression - Une virgule supplémentaire est autorisée à la fin des listes de paramètres, pour faciliter les choses quand on écrit un paramètre par ligne (notamment pour mieux versionner dans ces cas-là). RFC: https://wiki.php.net/rfc/trailing_comma_in_parameter_list - Il est maintenant possible d’écrire `catch (Exception)` pour capturer une exception sans la stocker dans une variable. RFC: https://wiki.php.net/rfc/non-capturing_catches - Les méthodes privées déclarées sur une classe parente ne contraignent plus la signature des méthodes sur la classe fille. (À l’exception des constructeurs finaux privés) RFC: https://wiki.php.net/rfc/inheritance_private_methods - N’importe quel nombre de paramètres de méthode peuvent maintenant être remplacés par un paramètre variadique lors de l’héritage, tant que les types sont compatibles. Ceci est par exemple possible : ```php class A { public function method(int $many, string $parameters, $here) {} } class B extends A { public function method(...$everything) {} } ``` Bibliothèque standard --------------------- ### printf Les fonctions de la famille `printf()` supportent maintenant les spécificateurs de format `%h` et `%H`. Ils font pareil que `%g` et `%G`, mais utilisent toujours le point comme séparateur des décimales au lieu de se fier à la locale `LC_NUMERIC` configurée. Il est également maintenant possible d’utiliser `*` comme largeur de champ ou précision de flottant, auquel cas la largeur/précision est passée comme argument à la fonction. Passer `-1` comme précision pour `%g`, `%G`, `%h` et `%H` permet de reproduire le comportement par défaut de PHP pour les flottants: ```php printf("%.*H", (int) ini_get("precision"), $float); printf("%.*H", (int) ini_get("serialize_precision"), $float); ``` ### proc_open - proc_open() supporte maintenant des descripteurs de pseudo-terminal (PTY). L’exemple suivant attache stdin, stdout et stderr au même PTY : ```php $proc = proc_open($command, [['pty'], ['pty'], ['pty']], $pipes); ``` - proc_open() supporte maintenant les sockets. L’exemple suivant attache une paire de socket distincte pour stdin, stdout et stderr : ```php $proc = proc_open($command, [['socket'], ['socket'], ['socket']], $pipes); ``` Contrairement aux pipes, les sockets n’ont pas de problèmes de blocage d’entrées/sorties sous Windows. Cependant tous les programmes ne fonctionnent pas forcément correctement avec des sockets. ### Fonctions de tri Les différentes fonctions de tri sont maintenant stables, ce qui signifie que les éléments qui sont égaux selon la fonction de comparaison conservent maintenant leur ordre relatif d’avant le tri. Il y a un léger coût en performances mais cela peut éviter les mauvaises surprises. RFC : https://wiki.php.net/rfc/stable_sorting ### Nouvelles fonctions sur les chaînes Les fonctions `str_contains`, `str_starts_with` et `str_ends_with` ont été ajoutées, permettant de déterminer respectivement si une chaîne de caractères contient, commence par et finit par une autre chaîne. Ce sont des opérations très courantes, et s’il n’était pas difficile de les implémenter en PHP, le fait qu’il y ait plusieurs façons de faire pouvait perturber les développeurs. RFC: https://wiki.php.net/rfc/str_contains RFC: https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions ### Opérations sur les tableaux Les fonctions `array_diff()`, `array_intersect()` et leurs variations peuvent maintenant être utilisées avec un seul tableau en paramètre. Cela facilite leur utilisation avec l’opérateur `...` : ```php // Fonctionne même si $excludes est vide array_diff($array, ...$excludes); // Fonctionne même si $arrays contient un seul tableau array_intersect(...$arrays); ``` Extensions ---------- ### Hash Les objets HashContext peuvent maintenant être sérialisés. ### OpenSSL Ajout du support de la RFC 5652, « Cryptographic Message Syntax (CMS) » avec des fonctions pour chiffrement, déchiffrement, signature, vérification et lecture. L’API est similaire à celle pour PKCS #7 avec l’ajout de nouvelles constantes: OPENSSL_ENCODING_DER, OPENSSL_ENCODING_SMIME and OPENSSL_ENCODING_PEM. Performances ------------ ### JIT Un compilateur juste à temps (JIT) a été ajouté dans PHP, permettant dans certains cas d’augmenter les performances. La fonctionnalité est désactivée par défaut, et n’apporte pas grand-chose dans le cadre d’application web classiques. Pour des usages plus gourmands en calcul par contre, par exemple un calcul de fractal qui a été utilisé pour les démos, la JIT apporte un gain de performance très important. L’idée est donc de rendre PHP plus viable pour des usages pour lesquels il était trop lent auparavant.