Ça y est, PHP 8.5 est disponible depuis le 20 novembre 2025. Pour fêter ses 30 ans, le langage nous offre enfin des outils qu'on espérait voir intégrés depuis longtemps : le pipe operator, une gestion des URI remise au goût du jour et la syntaxe clone with. Cette mise à jour va permet d'assainir notre code au quotidien.
PHP 8.5 : les nouveautés de la nouvelle version majeure
Pierre Gouedar
Qu'est-ce que PHP ?
PHP est un langage de programmation principalement utilisé pour le développement web. Créé en 1995 par Rasmus Lerdorf, PHP alimente aujourd'hui plus de 75% des sites web dans le monde. Sa simplicité d'apprentissage, sa flexibilité et son écosystème riche en frameworks en font un choix privilégié pour les applications web de toutes tailles.
Les nouveautés et améliorations de la version 8.5 de PHP
Avec la version 8.5, PHP ne se contente pas d'évoluer, il introduit de nouvelles fonctionnalités attendues depuis longtemps par les développeurs. Cette mise à jour se concentre sur trois axes majeurs : la lisibilité du code, la manipulation sécurisée des URLs, et le support amélioré des classes readonly.
Le Pipe Operator (|>)
Le pipe operator est sans doute la fonctionnalité la plus attendue de PHP 8.5. Il permet de chaîner des appels de fonctions de manière fluide, en passant le résultat d'une expression comme premier argument de la fonction suivante.
Avant (approche classique)
<?php
$title = ' PHP 8.5 Released ';
$slug = strtolower(
str_replace('.', '',
str_replace(' ', '-',
trim($title)
)
)
);
echo $slug; // "php-85-released"Le code se lit de l'intérieur vers l'extérieur, ce qui devient rapidement illisible avec plusieurs transformations.
Après (avec le pipe operator)
<?php
$title = ' PHP 8.5 Released ';
$slug = $title
|> trim(...)
|> (fn($str) => str_replace(' ', '-', $str))
|> (fn($str) => str_replace('.', '', $str))
|> strtolower(...);
echo $slug; // "php-85-released"Le code se lit maintenant de gauche à droite, de haut en bas, de manière naturelle. Chaque étape de transformation est clairement visible.
Fonctionnement technique
Le pipe operator fonctionne entièrement au niveau du compilateur. L'expression :
$result = $value |> callable(...);Est transformée en :
$result = callable($value);Cela signifie qu'il n'y a aucun impact sur les performances à l'exécution.
L'extension URI
PHP a été conçu pour le web, et le web fonctionne avec des URLs. Pourtant, la fonction historique parse_url() ne suivait aucun standard et était explicitement documentée comme ne devant pas être utilisée avec des URLs non fiables ou malformées.
PHP 8.5 introduit une nouvelle extension URI qui respecte les standards RFC 3986 et WHATWG URL, utilisée par les bibliothèques comme uriparser et Lexbor.
Avant (avec parse_url)
<?php
$url = 'https://api.example.com:8080/users?id=123&sort=name#profile';
$parts = parse_url($url);
echo $parts['scheme']; // https
echo $parts['host']; // api.example.com
echo $parts['port']; // 8080
echo $parts['path']; // /users
echo $parts['query']; // id=123&sort=name
echo $parts['fragment']; // profileProblèmes : pas de validation, pas de normalisation, comportement incohérent sur les URLs malformées.
Après (avec Uri\Rfc3986\Uri)
<?php
use Uri\Rfc3986\Uri;
$uri = new Uri('https://api.example.com:8080/users?id=123&sort=name#profile');
echo $uri->getScheme(); // https
echo $uri->getHost(); // api.example.com
echo $uri->getPort(); // 8080
echo $uri->getPath(); // /users
echo $uri->getQuery(); // id=123&sort=name
echo $uri->getFragment(); // profileModification fluide et immutable
Les objets Uri sont immutables. Les modifications créent de nouvelles instances :
<?php
use Uri\Rfc3986\Uri;
$uri = new Uri('http://example.com/old-path');
$newUri = $uri
->withScheme('https')
->withPath('/new-path')
->withQuery('updated=true');
echo $newUri->toString();
// https://example.com/new-path?updated=trueDeux standards disponibles
PHP 8.5 propose deux classes selon le standard souhaité :
Uri\Rfc3986\Uri: Suit le RFC 3986 (URIs génériques, validation stricte)Uri\WhatWg\Url: Suit le standard WHATWG URL (comportement des navigateurs, support Unicode/IDNA)
<?php
use Uri\Rfc3986\Uri;
use Uri\WhatWg\Url;
// RFC 3986 - strict
$rfc = Uri::parse("https://example.com/path?x=1");
// WHATWG - comportement navigateur
$whatwg = Url::parse("https://example.com/path?x=1");
// Gestion des erreurs
$errors = [];
$bad = Url::parse(" invalid url", null, $errors);
// $bad est null, $errors contient les détailsClone With
Depuis PHP 8.1 et les propriétés readonly, cloner un objet et modifier ses propriétés était devenu problématique. PHP 8.5 résout élégamment ce problème avec la nouvelle syntaxe clone().
Avant (approche fastidieuse)
<?php
readonly class Color
{
public function __construct(
public int $red,
public int $green,
public int $blue,
public int $alpha = 255,
) {}
public function withAlpha(int $alpha): self
{
// Workaround complexe
$values = get_object_vars($this);
$values['alpha'] = $alpha;
return new self(...$values);
}
}
$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);Après (avec clone with)
<?php
readonly class Color
{
public function __construct(
public int $red,
public int $green,
public int $blue,
public int $alpha = 255,
) {}
public function withAlpha(int $alpha): self
{
return clone($this, [
'alpha' => $alpha,
]);
}
}
$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);
echo $blue->alpha; // 255
echo $transparentBlue->alpha; // 128L'attribut #[\NoDiscard]
Le nouvel attribut #[\NoDiscard] permet de signaler qu'une valeur de retour ne doit pas être ignorée. PHP émettra un warning si le résultat n'est pas utilisé.
<?php
#[\NoDiscard("Le résultat du traitement peut contenir des erreurs")]
function bulk_process(array $items): array
{
// Traitement...
return $results;
}
// Warning: The return value of function bulk_process() should
// either be used or intentionally ignored by casting it as (void)
bulk_process($items);
// OK - résultat utilisé
$results = bulk_process($items);
// OK - ignoré explicitement
(void) bulk_process($items);Cette fonctionnalité améliore la sécurité des APIs en évitant les bugs silencieux où une valeur de retour importante est accidentellement ignorée.
Closures dans les expressions constantes
Les closures statiques et les callables de première classe peuvent maintenant être utilisés dans les expressions constantes, notamment dans les attributs :
<?php
#[SkipDiscovery(static function (Container $container): bool {
return ! $container->get(Application::class) instanceof ConsoleApplication;
})]
final class BlogPostEventHandlers
{
// ...
}Note importante : Ces closures doivent être explicitement marquées comme static et ne peuvent pas accéder à des variables externes avec use.
Nouvelles fonctions pour les tableaux
PHP 8.5 ajoute deux fonctions très attendues pour récupérer le premier et le dernier élément d'un tableau :
<?php
$users = ['Alice', 'Bob', 'Charlie'];
$first = array_first($users); // 'Alice'
$last = array_last($users); // 'Charlie'
// Fonctionne avec les tableaux associatifs
$data = ['name' => 'John', 'age' => 30, 'city' => 'Paris'];
echo array_first($data); // 'John'
echo array_last($data); // 'Paris'
// Retourne null pour les tableaux vides
$empty = [];
var_dump(array_first($empty)); // null
var_dump(array_last($empty)); // null
// Combinaison avec l'opérateur null coalescent
array_first($empty) ?? 'valeur par défaut'; // 'valeur par défaut'Ces fonctions complètent array_key_first() et array_key_last() introduites en PHP 7.3.
Backtraces pour les erreurs fatales
Le debugging devient plus facile avec l'affichage automatique des stack traces pour les erreurs fatales :
Fatal error: Maximum execution time of 1 second exceeded in example.php on line 6
Stack trace:
#0 example.php(6): usleep(100000)
#1 example.php(7): recurse()
#2 example.php(7): recurse()
#3 example.php(7): recurse()
...
#10 example.php(10): recurse()
#11 {main}Cette fonctionnalité est contrôlée par le paramètre INI fatal_error_backtraces (activé par défaut).
Nouvelles fonctions de récupération des handlers
// Récupérer le handler d'erreur actuel
$currentErrorHandler = get_error_handler();
// Récupérer le handler d'exception actuel
$currentExceptionHandler = get_exception_handler();Ces fonctions sont particulièrement utiles pour les frameworks et CMS modulaires qui veulent chaîner ou ajuster leur logique de gestion d'erreurs.
Constante PHP_BUILD_DATE
Une nouvelle constante permet de connaître la date de compilation de PHP :
<?php
echo PHP_BUILD_DATE; // "Nov 20 2025 10:30:45"
// Utile pour le debugging en production
echo 'PHP Version: ' . PHP_VERSION . "\n";
echo 'Build Date: ' . PHP_BUILD_DATE . "\n";Amélioration de cURL
La nouvelle fonction curl_multi_get_handles() facilite la gestion des sessions cURL multiples :
<?php
$multiHandle = curl_multi_init();
$ch1 = curl_init('https://api.example.com/users');
$ch2 = curl_init('https://api.example.com/posts');
curl_multi_add_handle($multiHandle, $ch1);
curl_multi_add_handle($multiHandle, $ch2);
// Nouveau en PHP 8.5 : récupérer tous les handles
$handles = curl_multi_get_handles($multiHandle);
// Retourne: [$ch1, $ch2]
// Exécution et traitement
$running = null;
do {
curl_multi_exec($multiHandle, $running);
} while ($running > 0);
foreach ($handles as $handle) {
$response = curl_multi_getcontent($handle);
curl_multi_remove_handle($multiHandle, $handle);
}De plus, les handles cURL peuvent maintenant être persistés entre plusieurs requêtes PHP, évitant le coût de réinitialisation des connexions.
OPcache toujours compilé
À partir de PHP 8.5, l'extension OPcache est toujours compilée statiquement dans PHP (comme ext/date, ext/hash, ext/pcre). Son activation reste contrôlée par les paramètres INI (opcache.enable).
Option CLI --ini=diff
Une nouvelle option CLI permet d'afficher uniquement les paramètres INI modifiés :
php --ini=diff
# Exemple de sortie :
# memory_limit = 256M (default: 128M)
# max_execution_time = 60 (default: 30)Dépréciations importantes
PHP 8.5 prépare le terrain pour PHP 9.0 avec plusieurs dépréciations :
| Fonctionnalité | Alternative |
|---|---|
__sleep() et __wakeup() |
__serialize() et __unserialize() |
Syntaxe ${var} en interpolation |
{$var} |
| Case avec point-virgule au lieu de deux-points | case X: |
null comme offset de tableau |
Chaîne vide '' |
| Backticks pour l'exécution shell | shell_exec() |
Récapitulatif des différences entre PHP 8.4 et 8.5
| Fonctionnalité | PHP 8.4 | PHP 8.5 |
|---|---|---|
| Pipe Operator | Non disponible | \|> disponible |
| Extension URI | Non disponible | RFC 3986 + WHATWG URL |
| Clone With | Non disponible | clone($obj, [...]) |
#[\NoDiscard] |
Non disponible | Disponible |
| Closures en constantes | Non disponible | Disponible (static) |
array_first() / array_last() |
Non disponible | Disponible |
| Backtraces erreurs fatales | Non disponible | Activé par défaut |
| OPcache | Extension optionnelle | Toujours compilé |
| Property Hooks | Disponible | Disponible |
| Lazy Objects | Disponible | Disponible |
Support et maintenance
| Version | Sortie | Fin support bugs | Fin support sécurité |
|---|---|---|---|
| PHP 8.5 | Nov 2025 | Déc 2027 | Déc 2029 |
| PHP 8.4 | Nov 2024 | Déc 2026 | Déc 2028 |
| PHP 8.3 | Nov 2023 | Déc 2025 | Déc 2027 |
Conseils de migration
Comme à chaque nouvelle version majeure, il est préférable de patienter jusqu'à la première release de correctifs (8.5.1) avant d'envisager une mise en production. Les retours de la communauté dans les premières semaines permettent d'identifier et corriger les problèmes passés entre les mailles du filet.
Avant toute migration, une analyse statique avec PHPStan ou Psalm permet de détecter rapidement les incompatibilités dans votre codebase. Pensez également à consulter la liste des dépréciations : des fonctionnalités courantes comme __sleep() ou la syntaxe ${var} disparaîtront dans PHP 9.
Pour les projets de taille importante, Rector peut automatiser une partie significative des adaptations de code et faire gagner un temps précieux.
Conclusion
Au final, cette version 8.5 est bien plus qu'une étape de transition vers PHP 9. Elle concrétise des attentes de longue date : on pense évidemment au pipe operator pour la fluidité, mais aussi à la gestion enfin standardisée des URL et à la souplesse apportée aux classes readonly via clone with.
Les ajouts comme #[\NoDiscard] ou les fonctions sur les tableaux montrent aussi un souci du détail très appréciable au quotidien. PHP a 30 ans, mais il n'a jamais été aussi agréable à écrire. Si vous n'avez pas encore migré, c'est le moment d'y passer pour gagner en robustesse et en propreté.
Ressources et Documentation
- Page officielle PHP 8.5 : https://www.php.net/releases/8.5/en.php
- Guide de migration : https://www.php.net/manual/en/migration85.php
- RFC Wiki : https://wiki.php.net/rfc
- The PHP Foundation : https://thephp.foundation/
Pierre Gouedar
Partager l'article