Licence CC BY-NC-ND

Sortie de PHP 8

Tous les ans, les développeurs de PHP travaillent d’arrache pied pour nous sortir une nouvelle version de PHP en fin d’année. Et à chaque fois c’est beaucoup de nouvelles fonctionnalités très intéressantes qui sont intégrées au langage. Découvrons ensemble les nouvelles fonctionnalités de cette nouvelle version majeure.

Une nouvelle année de PHP

Tous les ans les équipes de PHP nous préparent une nouvelle version de PHP. Depuis quelques années PHP arbore une version 7. Cette dernière a marqué une nouvelle ère de performances car elle a intégré PHP NG, une modification profonde de PHP.

Ce travail était à la base réalisé dans l’idée de changer la structure de PHP pour y intégrer du JIT (Just In Time). Mais le résultat a été si performant et le travail était encore si long pour arriver à une version JIT que les équipes ont décidé de la sortir quand même.

Mais qu’à cela ne tienne, le travail acharné paie: et PHP 8.0 est cette version qui nous amène le JIT, mais pas que ! La communauté a été très active sur PHP cette dernière année et beaucoup de nouvelles fonctionnalités sont arrivées en même temps.

Détaillons ensemble les nouvelles fonctionnalités majeures du langage.

Nouvelles fonctionnalités

Changements dans les casts de nombres

Commençons ce tour des nouveautés en douceur :) .

Lorsque vous passez une chaîne de caractères en paramètre d’une fonction qui attend un nombre, PHP tente de tout convertir en nombre. En PHP 7, si cette chaîne n’était pas convertible, la fonction recevait 0.

Avec PHP 8 si la chaîne de caractère est effectivement un nombre, elle est convertie, sinon vous obtiendrez une exception.

function foo(int $i) {

}

foo('2 et autre chose'); // throw TypeError
foo('2'); // fonctionne toujours si vous n'avez pas spécifié le strict types

Les unions de types

Clairement l’une des nouvelles fonctionnalités qui me plaît le plus ! Dans les dernières versions de PHP nous avons vu arriver le typage strict, mais lorsqu’on prend plusieurs types potentiels en paramètres il nous faut toujours re-vérifier le type car on ne peut pas réclamer cela à PHP. Dans cette nouvelle version de PHP on pourra donc faire des unions de type et s’éviter cette tâche.

Voici un exemple d’interface fonctionnant avec l’union de types :

interface ContainsNumberInterface
{
    public function setNumber(int|float $number);
    public function getNumber(): int|float;
}

Le type mixed

C’est un nouveau type qui vient s’ajouter aux types natifs existant dans PHP. Il a fait longtemps débat car d’un côté inutile, puisqu’il n’oblige à rien lors de l’utilisation d’une fonction ou d’un attribut. Mais PHP 8 rajoute ce type. Voyez là l’obligation de tout typer explicitement ;) .

class Something
{
    public mixed $userData;
}

Note: il a été décidé d’utiliser mixed et non pas any comme on peut voir dans d’autres langages car la communauté PHP entière s’accorde à utiliser ce wording pour spécifier un "multi-type" en documentation.

Les expressions match

Plus rapide à écrire, mais aussi plus rapide à exécuter qu’un switch, les expressions match ressemblent tout de même grandement à un switch :

  • Elles retournent directement quelque chose
  • Elles font une comparaison stricte contrairement au switch

En voici un exemple illustratif :

// En utilisant un switch
switch(8.0) {
  case "8.0":
    $result = "Oh non !";
    break;
  case 8.0:
    $result = "Ce que j'espère";
    break;
}
echo $result; // Affiche "Oh non !"

echo match (8.0) {
    "8.0" => "Oh non !",
    8.0 => "Ce que j'espère",
}; // Affiche "Ce que j'espère"

Les arguments nommés

Certaines fonctions ont une longue liste d’arguments optionnels, et devoir spécifier les arguments intermédiaires n’est pas toujours utile ou peut même porter à confusion ! PHP 8 nous propose donc cette nouvelle fonctionnalité : les arguments nommés.

Voici un exemple :

<?php
$items = [1, 2, 'foo'];
array_filter(array: $items, callback: function ($item) { return is_int($item); });

Les attributs

A partir de maintenant on s’accordera pour dire "propriété" de classe et "attribut" pour ce qui va suivre d’accord ? ;)

En PHP on a l’habitude d’avoir des commentaires contenant des annotations. Heureusement l’API de réflexion de PHP nous permet de simplifier le processus de parsing des annotations en commentaires… Mais ça n’était pas assez au goût général, et ça fait d’ailleurs depuis PHP 5.4 (environ) que les discussions sont ouvertes au sujet de faire quelque chose de "plus intégré à PHP". (pour les annotations Symfony et la plupart des frameworks utilisent un package nommé doctrine/annotations)

Nous y voici donc, on peut à présent utiliser les attributs pour remplacer les annotations. Voyons comment on peut déclarer un attribut.

#[Attribute]
class Column
{
    public string $name;

    public function __construct(string $name = null)
    {
        $this->name = $name;
    }
}

Voici un exemple d’utilisation de notre attribut. (Bientôt nos entités doctrine ressembleront à cela !)

final class User
{
    #[ORM\Id()]
    #[ORM\Column("id")]
    private int $id;
}

Et pour finir on utilise l’API de réflexion de PHP:

$reflectionClass = new ReflectionClass(User::class);
$attributes = $reflectionClass->getMethods()[0]->getAttributes(Column::class); // ✨

Note : Les attributs doivent être supportées par votre librairie, PHP ne fera pas la conversion automatiquement entre annotation et attributs.

Nouveautés sur le throw

Dans un premier temps, la clause throw est devenue une expression. C’est tout bête, mais ça permet par exemple de rendre le code suivant valide:

$value = $nullableValue ?? throw new InvalidArgumentException();

Et une petite modification a été faite sur le catch: le nom de variable est maintenant optionnel si vous n’avez pas besoin de cette dernière.

try {
    throw new TypeError();
} catch (TypeError) {
    echo 'oups erreur de type';
}

L’opérateur « Null Safe »

C’est l’une des fonctionnalités qui m’attire le plus: elle permet de sortir de ce qu’on appelle communément le « null hell ».

Concrètement, vous allez pouvoir remplacer ce code:

$country =  null;
 
if ($session !== null) {
    $user = $session->user;
 
    if ($user !== null) {
        $address = $user->getAddress();
 
        if ($address !== null) {
            $country = $address->country;
        }
    }
}

Par celui-ci:

$country = $session?->user?->getAddress()?->country;

N’oubliez cependant pas que moins vous aurez de null, mieux vous vous porterez psychologiquement !

Promotion des propriétés du constructeur

Cette fonctionnalité est en réalité une nouvelle façon de déclarer les propriétés de classe: PHP nous permet de les déclarer directement dans le constructeur, cela dans le but d’éviter le code redondant que l’on voit habituellement dans les classes.

Voici un exemple avant la nouvelle fonctionnalité :

class User
{
    private string $name;
    private string $email;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
}

Et après :

class User
{
    public function __construct(
        private string $name,
        private string $email
    ) {}
}

Plutôt cool non ?

PHP 8 is JIT

PHP et le JIT, une longue histoire

En réalité ça fait plusieurs années que l’équipe de PHP, et plus particulièrement Dmitry Stogov qui s’est acharné à faire fonctionner PHP en JIT. Et ça n’a pas été sans problème. Le tout a été recodé plusieurs fois, l’une d’entre elles a d’ailleurs donné naissance à PHP 7 ! Si vous avez entendu parler de php ng, il s’agit en réalité d’une tentative de clean pour faire fonctionner PHP en JIT. Mais l’avancée a été telle qu’on a pu sortir une nouvelle version qui boost les perfs sans même passer en JIT.

Bref, nous y sommes. PHP 8 est JIT (Just In Time).

JIT, donc perf, n’est-ce pas ?

Et bien non. C’est une nouveauté qui a donné beaucoup de fil à retordre à l’équipe de PHP mais qui n’a qu’assez peu d’impact sur les performances de PHP au final. Par exemple si vous avez une API qui répond en 10ms, et bien il y a peu de chances que PHP8 vous aide: php a toujours été très optimisé pour cela, et PHP 7.4 avec le preloading a encore poussé la vitesse de chargement de PHP. Pas vraiment d’amélioration, mais PHP est déjà très rapide.

Là où vous aurez les meilleurs gains de performances c’est sur les process longs. Par exemple si vous voulez faire des traitements mathématiques, ou du machine learning. Bref, des processus longs. En voici d’ailleurs la preuve.

Démonstration des gains de performances sur des calculs mathématiques longs

Cependant, il faut reconnaître qu’il y a une légère amélioration de performances tout de même dans le cas général.

Fun fact

Le typage généralisé arrivé ces dernières années avait un coût en terme de perf, car ça ajoutait un check supplémentaire nécessaire. Alors qu’avec PHP 8 et JIT, c’est une optimisation.

Ne cherchez pas à typer pour la perf: c’est marginal. Mais c’est rigolo.


Une belle nouvelle version de PHP, malgré la petite taille de la core team de PHP, ces derniers nous sortent encore de belles nouvelles version, et c’est agréable !

Avant de vous laisser j’aimerais vous glisser un dernier mot : récemment nous avons également eu droit à une nouvelle version de composer. Le gestionnaire de paquet de PHP est à présent plus rapide, et il utilise moins de mémoire.

24 commentaires

Et bien non. C’est une nouveauté qui a donné beaucoup de fil à retordre à l’équipe de PHP mais qui n’a qu’assez peu d’impact sur les performances de PHP au final. Par exemple si vous avez une API qui répond en 10ms, et bien il y a peu de chances que PHP8 vous aide: php a toujours été très optimisé pour cela, et PHP 7.4 avec le preloading a encore poussé la vitesse de chargement de PHP. Pas vraiment d’amélioration, mais PHP est déjà très rapide.

Il y a quelques années, dans une ancienne boîte, j’ai eu pour mission de migrer une base de code de PHP5 à PHP7. Je me souviens encore à quel point j’ai été subjugué par les nouvelles performances ! Je suis un peu déçu, mais c’est sans grande surprise que j’apprends que le JIT n’est pas aussi époustouflant. C’est quand même très bien et j’espère que PHP pourra faire office de leader sur ce sujet, donnant peut-être une impulsion à d’autres grands langages (je pense à Python) pour suivre le pas.

Je ne pratique plus PHP depuis quelques temps déjà, mais je suis heureux d’apprendre qu’il évolue bien. Merci pour cet article.

+4 -0

Un truc que d’aucuns attendaient avec PHP 8.0 : les erreurs que PDO rencontre avec le serveur SQL sont désormais activées par défaut. Je n’ai cependant pas l’impression que l’équivalent ait été fait pour mysqli, dommage…

+1 -0
// En utilisant un switch
switch(8.0) {
  case "8.0":
    $result = "Oh non !";
    break;
  case 8.0:
    $result = "Ce que j'espère";
    break;
}
echo $result; // Affiche "Oh non !"

Je n’y connais pas grand chose en PHP, mais c’est pas censé afficher "Ce que j’espère" ? ^^

+0 -0

En même temps mysqli n’a pas grand intérêt une fois que tu as PDO.

artragis

Certes. Je suis donc le seul à me demander pourquoi on active pour une interface et pas pour l’autre, même si l’argument de l’utilisation se tient ?

Ymox

Il est tout à fait possible que ça soit pour des raisons de rétro-compatibilité. Cela-dit je n’ai pas les détails. Mais ça ne m’étonnerait pas que l’argument "ne pas casser wordpress" ait été choc dans le débat.

Il y a aussi le problème du fait que PDO n’a pas vraiment le même but et la même conception que MySQLi. MySQLi est vraiment fait pour s’intégrer dans un développement "proche" de MySQL et de ses API traditionnelle où l’erreur est une variable statique partagée (à la C donc). PDO est un vrai mapping objet qui va avoir un vrai workflow objet.

Il est tout à fait possible que ça soit pour des raisons de rétro-compatibilité. Cela-dit je n’ai pas les détails. Mais ça ne m’étonnerait pas que l’argument "ne pas casser wordpress" ait été choc dans le débat.

Nek

Il y a aussi le problème du fait que PDO n’a pas vraiment le même but et la même conception que MySQLi. MySQLi est vraiment fait pour s’intégrer dans un développement "proche" de MySQL et de ses API traditionnelle où l’erreur est une variable statique partagée (à la C donc). PDO est un vrai mapping objet qui va avoir un vrai workflow objet.

artragis

Je ne connais pas assez le code sous-jacent aux deux alternatives et ne connais pas non plus assez WordPress pour savoir que c’était mysqli qui était utilisé. Je pense cependant qu’il y a aussi des solutions existantes avec PDO pour lesquelles l’activation par défaut des messages d’erreur pourrait poser problème, j’imagine. La rétro-compatibilité, quand on passe à une version majeure, c’est plutôt attendu (sinon annoncé) qu’on ait des choses qui ne fonctionnent plus comme avant.

Si on finit par dire que c’est juste à cause de comment ("pas correctement")/par qui ("gros acteurs du web") chacune des APIs est utilisée, je persiste à trouver ça dommage.

Ils ont changé une valeur par défaut pour PDO, je pensais naïvement que c’était pas beaucoup plus compliqué pour mysqli.

Edit

Ah, alors en plus WordPress semble avoir besoin de GD qui n’est pas encore compatible PHP 8, l’argument « "ne pas casser wordpress" » ne tient pas vraiment  :D

+0 -0

C’est un nouveau type qui vient s’ajouter aux types natifs existant dans PHP. Il a fait longtemps débat car d’un côté inutile, puisqu’il n’oblige à rien lors de l’utilisation d’une fonction ou d’un attribut. Mais PHP 8 rajoute ce type. Voyez là l’obligation de tout typer explicitement

Veux-tu dire qu’en PHP 8, le typage devient systématiquement obligatoire ? Ce qui signifierait du même coup une incompatibilité définitive avec du code PHP 5 ?

+0 -0

C’est un nouveau type qui vient s’ajouter aux types natifs existant dans PHP. Il a fait longtemps débat car d’un côté inutile, puisqu’il n’oblige à rien lors de l’utilisation d’une fonction ou d’un attribut. Mais PHP 8 rajoute ce type. Voyez là l’obligation de tout typer explicitement

Veux-tu dire qu’en PHP 8, le typage devient systématiquement obligatoire ? Ce qui signifierait du même coup une incompatibilité définitive avec du code PHP 5 ?

QuentinC

Aucune incompatibilité complète n’est au programme :) . Au contraire c’est un peu la marque de fabrique de PHP… Et en partie quelque chose qu’on lui repproche. L’idée de PHP++ (qui aurait cassé entièrement la compatibilité avec les très vieilles versions) a été lancée mais à part les créateurs de PHP (ironiquement), tout le monde a rejeté la l’idée en bloc.

Je débarque un peu tard sur cet article (très bien détaillé), mais j’avais trouvé à l’époque de l’annonce des nouveautés, que la promotion des propriétés du constructeur était créé pour la 'flemme' et qu’il nuit pas mal à la compréhension et la lecture du code. J’espère que peu de personnes l’utiliseront au profit de quelque chose de plus clair et conventionnel : la version 'je déclare tout'

Si on doit programmer et que seul l’IDE nous aide à la compréhension, c’est que de base c’est pas clair… Je sais qu’il faut utiliser les outils modernes, mais quelqu’un qui code avec un Atom sans plugin, pour X raisons, s’y perdra à force. On cherche a faire toujours plus court, plus léger, mais c’est justement ce qui perd le développeur. Si j’écrivais une phrase avec un mot sur trois, ce serait plus léger, mais pas compréhensible dans son entiereté et avec les nuances. Après, l’ajout de cette fonctionnalité est légitime je ne dis pas le contraire, mais je doute quant à son utilisation sur des classes qui ont des responsabilités lourdes et un haut besoin de compréhension.

J’ajoute aussi qu’on défini la portée de la variable dans les arguments d’une fonction, ce qui peut porter à confusion. En plus de ça, il n’est pas possible de déclarer la variable, puis d’utiliser ce type de constructeur (logique vu la mécanique, mais c’est à noter). On ne peux pas utiliser le mot clé new, et on peut quand meme mixer avec une syntaxe "à l’ancienne". Il faudra, je pense, avoir des conventions pour l’utilisation de ce type de constructeur.

EDIT : Et il est apparemment possible aussi de faire ceci, même si avec le typage ça n’a que peu d’intéret maintenant.

class MyClass 
{
    public function __construct(
        /** @var string */
        public $a,
    ) {}
}
+0 -0

Ce qui fait la force de PHP est justement sa facilité d’apprentissage. Si tu dis qu’il faut absolument faire des trucs lourds, c’est aller à contre-courant de cette philosophie.

Y’a pas de bonne manière de coder, y’a que des mauvaises et des moins mauvaises.

En entreprise c’est difficile de se passer d’outils performants comme un IDE (ou au moins un bon éditeur avec quelques plugins adaptés) si tu veux bosser efficacement. Ça permet par exemple d’intégrer des linters qui vont aider aussi à mettre en place des guides de style pour avoir une base de code cohérente. Les fameuses conventions dont tu parles, qui peuvent être répandues mais dont on peut sortir si besoin (parce qu’il y aura parfois des cas où ça devient nécessaire).

Les éditeurs sans plugin c’est bien pour découvrir un langage (auquel cas on s’en fout un peu de l’utilisation des dernières nouveautés), mais pour un usage professionnel faut savoir s’équiper convenablement. Perso je ferais pas confiance à un plaquiste qui vient avec un simple tournevis pour installer des plaques de placo dans une maison. J’ai pas envie de travailler avec un mec sous Notepad++ pour la même raison.

Je ne dis pas qu’il faille faire des trucs lourds, non. Je suis d’accord avec toi à 100% sur l’IDE et j’aime bien l’image du plaquiste ! Ce que je dis c’est qu’une syntaxe comme ça, on en avait pas forcement besoin, et que c’est du sucre gâché et qui rajoute potentiellement des problèmes de compréhension du code. Et je parle même pas si on fait de l’héritage avec ce genre de syntaxe.

Cependant, ils ont du l’inclure suite à des conversation et un vote auquel pas mal de noms participent. Je vais retrouver cette RFC et peut-etre que j’editerai mon avis un peu tranché sur la question :)

EDIT : Ok je viens de lire la RFC concernant ce changement https://wiki.php.net/rfc/constructor_promotion?s[]=promoted . Il est bien explicité dedans :

If constructor property promotion is used, it is recommended that the constructor be placed as the first method in the class, and directly following any explicit property declarations. This ensures that all declared properties are grouped together and visible at a glance. Coding standards that currently require static methods to be placed first should be adjusted to place the class constructor first.

C’est ce que je soulignait dans mon message et qui est "argumenté" par ce paragraphe. Si tel est la convention, alors oui, ça peut être très sympa, mais dans des cas (finalement) définis. Il y a une section Desugaring dans la RFC aussi qui est intéressante. Je suis beaucoup moins sceptique ducoup.

+0 -0

Perso je préfère cette syntaxe. Comme dit dans ma première réponse ça se rapproche du TypeScript, qui est un langage que j’apprécie beaucoup. On retrouve un code relativement facile à lire (même si c’est bizarre d’avoir potentiellement des déclarations à deux endroits, je suis d’accord).

Mais comme il n’y a pas d’obligation d’utiliser l’une ou l’autre des méthodes, ça peut permettre des usages au cas par cas, ce qui peut alléger certaines classes qui ne font pas vraiment de traitements dans le constructeur par exemple.

J’ai édité mon message précédent et tu as raison sur le fait que ça peut alléger certaines classes. Ce que je disais c’est que ça peut aussi en alourdir certaines et il va falloir faire super attention. Certains développeurs se prennent d’amour pour certaines méthodes et les utilisent à tout va sans réfléchir à leur utilité/pertinence (c’est la meme chose avec les design pattern par exemple). Je suis peut-etre trop pessimiste pour le coup…

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte