Aller au contenu principal
Développement web

PHP 8.5 : les nouveautés de la nouvelle version majeure

Pipe operator, extension URI standardisée, syntaxe clone with pour les classes readonly : PHP 8.5 introduit des fonctionnalités attendues depuis longtemps.

Pierre GouedarPierre GouedarPublié le 20 novembre 2025Mis à jour 15 juillet 202610 min
Résumer cet article avec une IA

Ç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.

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']; // profile

Problè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(); // profile

Modification 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=true

Deux 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étails

Clone 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; // 128

L'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


Plus d'articles sur Développement web

Décryptages, retours de terrain et modes d'emploi : ce que l'équipe apprend en livrant des projets, remis au propre.

Symfony Reprise : migrer un projet Symfony de Webpack Encore vers Vite ou Rsbuild
Développement web11 août 202610 min

Symfony Reprise : migrer un projet Symfony de Webpack Encore vers Vite ou Rsbuild

Annoncé le 31 juillet 2026, Symfony Reprise branche Vite ou Rsbuild sur une application Symfony à la place de Webpack Encore. Sur un projet existant : bascule Twig terme à terme, configuration de build qui disparaît en grande partie, pont Symfony UX préservé, rechargement à chaud en développement, et un statut 0.x qui impose de planifier plutôt que de basculer.

Thomas DubreuilThomas Dubreuil
Google va déployer AI Overviews et AI Mode en France cet été 2026
Développement web17 juillet 20269 min

Google va déployer AI Overviews et AI Mode en France cet été 2026

Après un an et demi de retard réglementaire, Google va déployer AI Overviews et AI Mode, ses recherches génératives propulsées par Gemini, en France cet été 2026, au plus tard le 23 septembre. Ce qui change pour votre trafic organique et les premiers réflexes à adopter.

Thomas DubreuilThomas Dubreuil
TypeScript 7 réécrit en Go : le compilateur natif, 8 à 12 fois plus rapide
Développement web13 juillet 20268 min

TypeScript 7 réécrit en Go : le compilateur natif, 8 à 12 fois plus rapide

Microsoft publie la version stable de TypeScript 7.0 : compilateur, service de langage et outillage entièrement réécrits en Go, pour des builds généralement 8 à 12 fois plus rapides. Ce qui change, pourquoi Go, et comment TypeScript 6.0 prépare la migration.

Thomas DubreuilThomas Dubreuil

Combien coûterait votre projet ? Estimation gratuite

Décrivez votre besoin, nous revenons vers vous sous 24h avec une fourchette de budget et de délai.

Réponse sous 24h, sans engagement.

Alexandre Da Silva, Koul, à son poste de travail
Questions fréquentes

Le blog Koul

Ligne éditoriale, sources, usage : ce qui sort sur ce blog et comment vous pouvez vous en servir.

Votre question est plus complexe ?

Un échange court suffit souvent à trancher. Prenez un créneau, on vous répond sur votre cas.

Réserver un créneau