Aller au contenu principal
La newsletter arrive ! Premier envoi très bientôt : laissez votre adresse pour la recevoir.

PHP 8.2 : la consolidation du système de types et modernisation de l’API.

PHP 8.2 a introduit les classes readonly, une avancée majeure pour la consolidation du typage en PHP. Découvrez son impact sur la fiabilité des applications.

MAJ 6 min de lecture
Sommaire · 7

PHP 8.2 a mis en avant les types DNF, les types autonomes et une extension de génération aléatoire. Sur ce blog, ces trois-là font zéro usage. La fonctionnalité qui porte 408 classes tenait en deux paragraphes de l'annonce.

Ce que 8.2 a laissé, quatre ans après

PHP 8.2 est sortie en décembre 2022. L'annonce mettait en avant trois choses : les types DNF, les types autonomes true, false et null, et une extension Random refaite en objet.

Le relevé sur le src/ de ce blog, qui tourne en PHP 8.5 :

  • types DNF : 0
  • types autonomes : 0
  • extension Random : 0
  • #[AllowDynamicProperties] : 0
  • constantes dans un trait : 1
  • #[\SensitiveParameter] : 1
  • classes entièrement readonly : 408

Une fonctionnalité à 408, deux à 1, quatre à 0. Ce billet regarde pourquoi, parce que l'écart entre ce qu'une version annonce et ce qu'elle laisse dit quelque chose sur la façon dont un langage grandit.

readonly class : la ligne qui a tout pris

En 8.1, readonly s'appliquait propriété par propriété. Il fallait le répéter sur chacune, et une propriété oubliée passait sans bruit. En 8.2, le modificateur monte au niveau de la classe et devient une garantie de structure : plus aucune propriété modifiable, sans exception possible.

Ce qui a rendu cette ligne structurante ici n'a rien à voir avec l'esthétique. Ce blog tourne en FrankenPHP worker mode : le kernel démarre une fois et sert des milliers de requêtes, donc un service partagé conserve entre deux visiteurs tout ce qu'il écrit dans ses propriétés. Une classe entièrement readonly ne peut rien écrire. Elle est sûre par construction, sans revue et sans discipline.

D'où la convention du projet, final readonly class par défaut sur tout service, et le compte : 408 classes, dont 405 sous cette forme exacte. L'absence de readonly sur une classe est devenue un signal, pas un oubli : elle veut dire que la classe doit se justifier, et les rares qui restent portent toutes un reset(). J'ai raconté ce basculement dans Les design patterns supposent que le process va mourir et compté ce que 8.1 avait posé avant dans PHP 8.1.

Deux paragraphes dans l'annonce de sortie. La charpente de l'application quatre ans plus tard.

Les types DNF : zéro, et la raison est simple

Un type DNF combine intersections et unions : (Serializable&Cacheable)|Document. Il complète les unions de 8.0 et les intersections de 8.1.

Usages ici : zéro. Et zéro intersection simple non plus.

La raison tient en une phrase. Quand deux contrats vont ensemble assez souvent pour mériter une signature, on écrit l'interface qui étend les deux et on lui donne un nom. Le nom documente l'intention une fois ; l'intersection anonyme la répète à chaque signature qui la mentionne. Le besoin réel apparaît surtout dans du code de bibliothèque, qui doit accepter des combinaisons qu'il ne contrôle pas. Une application les contrôle toutes.

Une fonctionnalité peu utilisée n'est pas ratée. Elle a un domaine étroit, et l'annonce de sortie ne le dit jamais.

Les types autonomes : zéro, pour une meilleure raison encore

true, false et null comme types de retour à part entière servent à décrire honnêtement des fonctions historiques : celles qui rendent User|false parce que false y signifie l'échec.

Zéro usage ici, et c'est un compliment adressé au code plutôt qu'à la fonctionnalité. User|false est une convention de la bibliothèque standard PHP, pas une bonne pratique. Dans du code applicatif, l'échec s'écrit ?User quand l'absence est normale, et une exception quand elle ne l'est pas. Ces types existent pour typer l'existant sans le réécrire. Ne pas en avoir besoin veut dire qu'on n'a pas cet existant.

Deux fonctionnalités à un seul usage, et elles sont bien placées

Les constantes dans un trait comblent une limitation qui n'avait aucune justification technique. Une seule ici, et elle est exactement à sa place :

PHP
// src/Service/Ai/Concerns/RateLimitedAiServiceTrait.php
trait RateLimitedAiServiceTrait
{
    /** Clé unique partagée par tous les appels Gemini (budget global, #1802). */
    private const string AI_LIMITER_KEY = 'gemini';

La clé du limiteur appartient au comportement porté par le trait, pas aux classes qui l'utilisent. Avant 8.2, il aurait fallu la poser sur chaque classe consommatrice, ou dans une constante globale sans rapport. La constante vit maintenant avec le code qui s'en sert.

#[\SensitiveParameter] est l'autre. Il retire la valeur d'un argument des traces d'exception, et il y en a exactement un dans ce dépôt :

PHP
// src/Entity/Security/User.php
public function setPassword(#[\SensitiveParameter] string $password): static

Un seul usage parce qu'il n'y a qu'un endroit où un mot de passe en clair traverse une signature. Les jetons du reste de l'application sont déjà signés ou hachés avant d'y arriver. Ici encore, le compte de 1 est la bonne réponse et non un manque de rigueur.

Les propriétés dynamiques : la dépréciation qui n'a rien coûté

Le changement le plus discuté de 8.2 est la dépréciation des propriétés dynamiques. Assigner $user->email sur une classe qui ne déclare pas $email émet désormais un avertissement, et deviendra une erreur. Le comportement remonte aux débuts du langage, et il a produit d'innombrables bugs silencieux sur une simple faute de frappe.

L'échappatoire existe, c'est l'attribut #[AllowDynamicProperties]. Usages ici : zéro.

C'est le chiffre le plus satisfaisant du relevé. Une dépréciation dont on n'a jamais eu besoin d'ouvrir la trappe de secours est une dépréciation qui visait juste : elle a supprimé un comportement que le code correct n'utilisait déjà plus. Toutes ne se passent pas ainsi, et c'est bien pour ça que celle-là mérite d'être notée.

Ce que le relevé raconte

Une release se juge à ce qu'elle laisse, pas à ce qu'elle annonce. Et l'écart, ici, est instructif dans les deux sens.

Les fonctionnalités à zéro ne sont pas des échecs. Les types DNF servent aux bibliothèques, les types autonomes servent à typer du code ancien, l'extension Random sert à qui a besoin d'un générateur reproductible. Ce sont des outils à domaine étroit, et une application banale n'entre dans aucun de ces domaines. Le tort n'est pas dans la fonctionnalité, il est dans la façon dont on présente une version : en alignant les nouveautés à plat, comme si chacune allait concerner tout le monde.

Et la fonctionnalité qui a tout pris est celle dont l'annonce parlait le moins, parce que sa portée ne dépendait pas d'elle. readonly class est devenu structurant le jour où le process PHP a cessé de mourir entre deux requêtes. Cette raison-là n'était écrite nulle part en décembre 2022 : elle est arrivée d'ailleurs, avec les serveurs applicatifs persistants.

Alors la prochaine fois qu'une version sort avec sa liste de nouveautés, la question utile n'est peut-être pas laquelle est la plus impressionnante. C'est plutôt : laquelle deviendra structurante à cause de quelque chose qui n'est pas encore arrivé ?

Une coquille, une erreur dans ce billet ? Signale-la-moi.

Ce billet est publié sous licence Creative Commons BY-NC-SA 4.0 (attribution, pas d'usage commercial, partage dans les mêmes conditions).

Vous aimerez aussi

Newsletter

Un mail quand il y a quelque chose à dire

Pas de tracking, pas de pub, pas de "10 astuces pour…". Juste les nouveaux billets et parfois une réflexion en plus.

Activez uniquement ce que vous souhaitez. Vos choix sont conservés 6 mois.

Strictement nécessaires

Indispensables au fonctionnement du site (session, sécurité, préférence d'affichage). Aucune donnée n'est partagée à des tiers et aucun consentement n'est requis.

Toujours actif

Mesure d'audience

Statistiques via Google Analytics (GA4) : pages vues, source du trafic, navigateur et interactions clés. Dépose des cookies de mesure, activés seulement avec votre accord (Consent Mode). Sans publicité ciblée, sans Google Signals, sans partage commercial.

Contenus externes

Affiche les GIF animés hébergés par Giphy (CDN aux États-Unis). À l'affichage d'un GIF, votre adresse IP et votre navigateur sont transmis à Giphy. Sans votre accord, les GIF ne s'affichent pas.