PHP 9.0 : nouveautés, changements majeurs et impacts à venir
Par Louis-Arnaud Catoire
PHP 9.0 marque une rupture assumée avec l'héritage permissif du langage. Chaque version majeure depuis PHP 7 a resserré les contraintes de typage, durci la gestion d'erreurs et nettoyé l'API standard. La version 9 pousse cette logique à son terme : les comportements implicites tolérés depuis des années deviennent des erreurs, les fonctions ambiguës sont scindées ou supprimées, et le modèle d'exécution s'ouvre à la concurrence. Ce tour d'horizon couvre les changements concrets, les stratégies de migration et les implications architecturales pour les projets de grande envergure.
Ce qui casse : les suppressions et durcissements à connaître
Opérateurs ++ et -- sur les types non-entiers
PHP autorisait l'incrémentation de chaînes ('a9'++), de null ou de false via une coercion implicite vers un entier. Ce mécanisme produisait des résultats incohérents et difficilement débuggables. PHP 9.0 lève une TypeError dans tous ces cas. La fonction str_increment(), introduite en PHP 8.3, couvre le cas légitime de l'incrémentation alphabétique.
Fin de la promotion implicite de false en tableau
L'écriture $x = false; $x[] = 'value'; transformait silencieusement un booléen en tableau. PHP 9.0 interdit cette coercion et exige une initialisation explicite. Ce changement élimine une source classique de bugs dans les fonctions qui retournent false en cas d'échec et dont le résultat est ensuite utilisé comme tableau sans vérification.
Interpolation de chaînes simplifiée
Les syntaxes "${var}" et "${expr()}" sont supprimées. Seules restent "$var" et "{$var}". La concaténation explicite remplace les expressions complexes dans les chaînes. Ce nettoyage réduit la surface d'ambiguïté syntaxique et aligne PHP sur le comportement attendu par les analyseurs statiques.
Avertissements promus en erreurs fatales
L'accès à une propriété sur null, l'utilisation d'un indice inexistant, la lecture d'une variable non définie : ces situations généraient un warning en PHP 8.x et permettaient au script de continuer dans un état potentiellement corrompu. PHP 9.0 les transforme en erreurs fatales. L'exécution s'arrête immédiatement, ce qui force la correction en amont plutôt que la gestion défensive en aval.
Renforcement de la sécurité et de la cohérence
unserialize() lève une exception dédiée
La fonction unserialize() renvoyait false ou générait un warning en cas de données corrompues. PHP 9.0 introduit UnserializationFailedException, une exception dédiée qui permet un traitement structuré via try/catch :
try {
$data = unserialize($input);
} catch (UnserializationFailedException $e) {
log($e->getMessage());
}
Ce changement est critique pour tout code qui désérialise des données provenant de sources externes (sessions, caches distribués, messages de queue). La surface d'attaque liée à la désérialisation non contrôlée diminue significativement.
Fonctions standard scindées pour lever l'ambiguïté
Des fonctions comme array_keys() changent de comportement selon leurs arguments. PHP 9.0 introduit des variantes explicites (par exemple array_keys_filter()) pour séparer les responsabilités. Ce pattern de scission est préférable à la surcharge sémantique : chaque fonction fait une seule chose, et les signatures deviennent auto-documentées.
Suppression définitive des dépréciations 8.x
L'interface Serializable, les fonctions redondantes et les extensions non maintenues sont supprimées. Tout ce qui a été marqué @deprecated entre PHP 8.1 et 8.4 disparaît. Les méthodes magiques __serialize() et __unserialize() remplacent l'interface historique avec un modèle plus prévisible et compatible avec l'héritage d'objets.
Stratégie de migration : de l'audit au déploiement
Détecter les incompatibilités avant qu'elles ne cassent
La première étape consiste à activer E_DEPRECATED sur tous les environnements, y compris la CI. Sur un projet Symfony ou Laravel, cela implique de configurer le handler d'erreurs pour convertir les dépréciations en exceptions dans les tests :
set_error_handler(function (int $errno, string $errstr) {
if ($errno === E_DEPRECATED) {
throw new \ErrorException($errstr, 0, $errno);
}
return false;
});
Cette approche transforme chaque dépréciation en un test rouge, ce qui rend l'avancement de la migration mesurable et traçable dans la CI.
Rector comme levier d'automatisation
Rector couvre une part significative des transformations requises pour PHP 9.0 : remplacement de Serializable par __serialize()/__unserialize(), correction des interpolations de chaînes, ajout des initialisations explicites de tableaux. Sur un projet de plus de 100 000 lignes, l'approche manuelle n'est pas viable. Rector permet de traiter le code applicatif et les tests en une passe, en générant un diff reviewable.
L'ordre recommandé est le suivant : exécuter Rector sur le code applicatif, valider la suite de tests, puis exécuter Rector sur les tests eux-mêmes. Cette séquence évite de corriger simultanément le code et les assertions qui le validaient.
Gérer les dépendances tierces
Le véritable goulot d'étranglement d'une migration majeure n'est jamais le code applicatif : ce sont les dépendances. Avant de planifier une migration vers PHP 9.0, auditez vos composer.json avec composer outdated et vérifiez la compatibilité déclarée de chaque package. Les packages qui n'ont pas été mis à jour depuis PHP 8.3 devront être remplacés ou forkés. Symfony et Laravel publieront des versions compatibles PHP 9 ; les bundles communautaires moins maintenus représenteront le risque principal.
Performances et concurrence : au-delà du nettoyage
JIT et optimisations du moteur
Le compilateur JIT, introduit en PHP 8.0 et amélioré en 8.1, recevra des optimisations supplémentaires. Les gains les plus significatifs concernent les traitements CPU-bound : calculs mathématiques, manipulations de structures de données volumineuses, transformations d'images. Pour les applications web classiques dominées par les I/O, l'impact restera marginal, mais la tendance est claire : PHP cherche à être compétitif sur des charges de travail historiquement réservées à d'autres langages.
Fibers et concurrence asynchrone
PHP 9.0 pourrait renforcer l'intégration des fibers pour faciliter l'écriture de code concurrent. Les fibers, introduites en PHP 8.1, fournissent la primitive de base pour la suspension et la reprise de contextes d'exécution. Les frameworks comme ReactPHP et Swoole s'appuient déjà dessus. Une meilleure intégration au niveau du langage ouvrirait la voie à des patterns de concurrence structurée sans dépendance à des extensions tierces. Les cas d'usage concernés : appels API parallèles, websockets, traitement de streams en temps réel.
Vision architecturale : ce que PHP 9 signifie pour les grands projets
La trajectoire du langage est désormais prévisible
Depuis PHP 7.0, chaque version majeure suit le même schéma : durcissement du typage, promotion des warnings en erreurs, suppression des dépréciations accumulées. Cette prévisibilité est un atout stratégique. Un architecte peut désormais planifier les migrations sur un horizon de trois à cinq ans avec une confiance raisonnable sur la nature des changements à venir. PHP 10 poursuivra cette trajectoire : les dépréciations introduites en 9.x seront supprimées, et le système de types continuera de se rapprocher d'un modèle statique.
Impact sur l'écosystème des frameworks
Symfony 8.x et Laravel 12.x seront les premières versions à cibler PHP 9.0. Ces frameworks absorbent une part importante des changements du langage dans leurs couches d'abstraction. La migration du framework est généralement transparente pour le code applicatif, à condition que celui-ci respecte les bonnes pratiques : injection de dépendances, typage des arguments et retours, pas de suppression d'erreurs avec @. Les projets qui s'écartent de ces conventions paieront un coût de migration proportionnel à leur dette technique.
Préparer les grandes bases de code
Pour un projet de plusieurs centaines de milliers de lignes, la migration vers PHP 9.0 se planifie en trois phases. D'abord, la mise en conformité sur PHP 8.4 avec zéro dépréciation. Ensuite, la mise à jour des dépendances vers des versions compatibles PHP 9. Enfin, le passage effectif au runtime PHP 9 avec une campagne de tests exhaustive. Chaque phase peut être déployée indépendamment, ce qui permet de répartir l'effort sur plusieurs sprints sans bloquer les livraisons de features.
La clé est de ne pas traiter la migration comme un projet ponctuel mais comme un processus continu. Les équipes qui activent E_DEPRECATED dès aujourd'hui et qui intègrent Rector dans leur pipeline de CI arriveront à PHP 9.0 sans friction. Les autres devront absorber plusieurs années de dette technique en une seule itération.
Pour aller plus loin
- La Fondation PHP fête ses 3 ans — Bilan et perspectives de la Fondation PHP
- Qu'est-ce que les PSRs et à quoi servent-ils ? — Les standards de l'écosystème PHP
- Rector : maîtrisez l'évolution de votre code Symfony — Automatiser la migration de votre code PHP
- PHP.net — Site officiel du langage PHP et documentation des versions
- PHP RFC — Les propositions d'évolution du langage PHP
- The PHP Foundation — L'organisation qui finance le développement de PHP
- PHP sur GitHub — Le code source de PHP