PHP 8.5 est sorti le 20 novembre 2025. Cette version continue la dynamique de PHP 8.x : plus de productivité, meilleure DX et un langage qui vous pousse vers les bonnes pratiques sans casser tout votre code.
Dans cet article, on passe en revue les nouveautés majeures, les changements subtils (mais importants) et ce que vous devez vérifier avant de passer votre stack en 8.5.
PHP 8.5 en un coup d’œil
En bref, PHP 8.5 apporte :
- L’opérateur pipe
|>pour chaîner les appels de manière lisible. - Une nouvelle extension URI native pour parser et manipuler les URLs proprement.
clone()amélioré avec un paramètre pour modifier des propriétés pendant le clonage.- L’attribut
#[\NoDiscard]pour signaler les retours à ne pas ignorer. - Closures et first-class callables utilisables dans les expressions constantes.
- Nouvelles fonctions utilitaires comme
array_first(),array_last(),get_error_handler(),get_exception_handler(). - OPcache obligatoire, intégré au binaire PHP.
- Stack trace sur les erreurs fatales et nouvelles options de debug.
- Une salve de dépréciations ciblées (backticks, casts non canoniques, etc.).
L’opérateur pipe |> : fini la salade d’appels imbriqués
C’est la star de PHP 8.5. L’opérateur pipe permet de chaîner des appels de fonctions en lisant le flux de gauche à droite.
Avant : du code illisible
<?php
$title = ' PHP 8.5 Released ';
$slug = strtolower(
str_replace('.', '',
str_replace(' ', '-',
trim($title)
)
)
);Code language: HTML, XML (xml)
Maintenant : des pipelines explicites
<?php
$title = ' PHP 8.5 Released ';
$slug = $title
|> trim(...)
|> (fn (string $str): string => str_replace(' ', '-', $str))
|> (fn (string $str): string => str_replace('.', '', $str))
|> strtolower(...);Code language: HTML, XML (xml)
Quelques points clés :
- La valeur de gauche est passée implicitement comme premier argument des callables de droite.
- Vous pouvez utiliser des fonctions, des closures, des first-class callables (
$obj->method(...),Class::staticMethod(...)). - Le pipe ne remplace pas tout, mais il rend les transformations successives nettement plus lisibles, surtout combiné avec les fonctions pures.
En pratique, vous allez l’adorer pour :
- Le nettoyage de données (normalisation d’input, trimming, filtering).
- Les pipelines sur des collections (combiner
array_map,array_filter,array_values, etc.). - Les transformations de chaînes et les générateurs.
clone() évolue : le “clone with” version PHP
PHP 8.5 introduit un clone() amélioré, capable de cloner un objet et de modifier certaines propriétés en une seule opération.
Avant, pour un objet readonly, il fallait bricoler :
<?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 {
$values = get_object_vars($this);
$values['alpha'] = $alpha;
return new self(...$values);
}
}
$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);Code language: HTML, XML (xml)
Avec PHP 8.5, clone() accepte désormais un second argument $withProperties :
<?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);Code language: HTML, XML (xml)
Ce que ça change pour vous :
- Le pattern with-er devient trivial, même avec des propriétés
readonly. - Vous réduisez le boilerplate autour de l’immutabilité.
- Les objets de configuration, DTO et value objects deviennent plus agréables à manipuler.
URI extension : enfin une API moderne pour les URLs
Après des années à bricoler avec parse_url() et les pièges d’URLs mal formées, PHP 8.5 apporte une extension URI toujours disponible, capable de gérer RFC 3986 et WHATWG URL.
Avant : parse_url() et ses limites
<?php
$components = parse_url('https://php.net/releases/8.5/en.php');
echo $components['host']; // "php.net"Code language: HTML, XML (xml)
Problèmes classiques :
- Comportement parfois surprenant avec des URLs exotiques ou partiellement valides.
- Pas d’objet immuable, pas d’API riche, beaucoup de logique maison pour recomposer une URL.
Maintenant : objets Uri
<?php
use Uri\Rfc3986\Uri;
$uri = new Uri('https://php.net/releases/8.5/en.php');
echo $uri->getHost(); // "php.net"
$newUri = $uri
->withPath('/releases/8.5/fr.php')
->withQuery('ref=skyminds');Code language: HTML, XML (xml)
En pratique :
- Vous gagnez une API objet propre, fluide, immuable.
- Vous pouvez choisir le modèle RFC 3986 ou WHATWG selon le cas d’usage.
- Vous réduisez la surface de bugs de sécurité liés à des parsings approximatifs.
Nouveaux attributs et métaprogrammation
PHP 8.5 fait aussi évoluer le système d’attributs et les expressions constantes.
#[\NoDiscard] : les retours qui comptent
L’attribut #[\NoDiscard] marque une fonction dont le résultat ne doit pas être ignoré :
<?php
#[\NoDiscard]
function saveUser(User $user): bool {
// ...
return true;
}
saveUser($user);
// Warning: The return value of function saveUser() should either be used
// or intentionally ignored by casting it as (void)Code language: HTML, XML (xml)
Vous pouvez indiquer que vous ignorez volontairement le retour :
<?php
(void) saveUser($user); // Pas de warningCode language: HTML, XML (xml)
Intérêt direct :
- Sécuriser les API cruciales (persistance, transactions, opérations sensibles).
- Aider les équipes à repérer les oublis d’appels critiques.
Closures et first-class callables dans les constantes
PHP 8.5 autorise désormais l’usage de closures statiques et de first-class callables dans les expressions constantes : attributs, valeurs par défaut, constantes.
<?php
final class PostsController
{
#[AccessControl(static function (Request $request, Post $post): bool {
return $request->user === $post->getAuthor();
})]
public function update(Request $request, Post $post): Response {
// ...
}
}Code language: HTML, XML (xml)
Cela ouvre la porte à des attributs beaucoup plus expressifs et typés, sans passer par des mini DSL en string.
Autres petits plus côté attributs
- Attributs désormais possibles sur les constantes (
const) — pratique pour marquer une constante comme#[Deprecated]. #[\Override]peut maintenant viser les propriétés, en plus des méthodes.- Asymmetric visibility étendu aux propriétés statiques, pour des API plus fines.
Nouvelles fonctions utilitaires du core
PHP 8.5 apporte plusieurs helpers très pratiques pour le quotidien.
array_first() et array_last()
Fini les reset() / end() ou les array_key_last() un peu verbeux :
<?php
$events = getEvents();
$first = array_first($events);
$last = array_last($events);
// Compose bien avec ??
$last = array_last($events) ?? $fallbackEvent;Code language: HTML, XML (xml)
Si le tableau est vide, ces fonctions retournent null.
Nouveaux helpers d’erreur et de debug
get_error_handler()etget_exception_handler()donnent accès aux handlers courants.- Les erreurs fatales ont maintenant un backtrace de stack utilisable pour le debug.
Les fatal errors ne seront plus de simples messages opaques, vous pouvez enfin savoir “comment” on est arrivé à la catastrophe.
Améliorations CLI et configuration
php --ini=diffmontre les directives INI modifiées par rapport aux valeurs par défaut.- Nouveau directive
max_memory_limitqui définit un plafond àmemory_limitpour éviter des réglages irréalistes en production.
OPcache obligatoire : performance by default
Gros changement structurel : l’extension OPcache devient une partie non optionnelle de PHP. Elle est toujours compilée et chargée.
Concrètement :
- Plus besoin de
docker-php-ext-install opcacheni dezend_extension=opcachedansphp.ini. - Tenter d’ajouter
zend_extension=opcachedéclenche un warning. - Les environnements oublieux n’existent plus : OPcache est là, point.
Les mesures préliminaires annoncent environ 5–10 % de gain sur des applis web classiques, plus dans certains profils CPU-bound.
Extensions : cURL, Intl, DOM, sessions…
PHP 8.5 améliore aussi plusieurs extensions clés.
cURL : partage persistant des connexions
Les share handles peuvent maintenant être persistants entre requêtes PHP :
<?php
$sh = curl_share_init_persistent([
CURL_LOCK_DATA_DNS,
CURL_LOCK_DATA_CONNECT,
]);
$ch = curl_init('https://php.net/');
curl_setopt($ch, CURLOPT_SHARE, $sh);
curl_exec($ch); // Peut réutiliser une connexion précédenteCode language: HTML, XML (xml)
Résultat : moins d’overhead de handshake, meilleur throughput pour des services très bavards.
Intl : formatage de listes
Nouvelle classe IntlListFormatter, très utile pour rendre des listes dans la locale correcte (and/or, unités, etc.).
DOM, EXIF, Standard, Sessions…
Quelques exemples :
Dom\Element::$outerHTML.- Support HEIF/HEIC dans EXIF et
getimagesize(). setcookie()et les sessions supportent le flag"partitioned".mail()remonte enfin les erreurs sendmail plutôt que de les avaler.
Ce sont des petits changements, mais ils simplifient pas mal de choses au quotidien, surtout côté interopérabilité et observabilité.
Dépréciations à traiter dès maintenant
PHP 8.5 déprécie plusieurs comportements historiques. Mieux vaut les corriger avant que ça ne casse en 9.0.
Casts non canoniques
Les casts suivants sont dépréciés :
(boolean)→ utilisez(bool)(integer)→ utilisez(int)(double)→ utilisez(float)(binary)→ utilisez(string)
Backticks
Le fameux :
<?php
$output = `ls -la`;Code language: HTML, XML (xml)
est déprécié en tant qu’alias de shell_exec(). Utilisez explicitement :
<?php
$output = shell_exec('ls -la');Code language: HTML, XML (xml)
C’est plus clair, plus explicite pour les audits de sécurité et future-proof.
case ...; au lieu de case ...:
Terminer un case par un point-virgule plutôt qu’un deux-points est maintenant déprécié. Exemple à bannir :
<?php
switch ($value) {
case 1;
// ...
break;
}Code language: HTML, XML (xml)
Remplacez par :
<?php
switch ($value) {
case 1:
// ...
break;
}Code language: HTML, XML (xml)
Output handler qui émet du output
Si vous produisez du contenu (echo, etc.) dans un output handler utilisateur, c’est déprécié. La déprecation bypassera votre handler pour être visible, ce qui peut surprendre.
Fonctions et constantes dépréciées
D’après le manuel et PHP.Watch :
curl_close()etcurl_share_close()(no-op depuis PHP 8.0).xml_parser_free()(idem).socket_set_timeout()en faveur destream_set_timeout().- Les constantes
MHASH_*. - Alias
mysqli_execute()en faveur demysqli_stmt_execute().
Compatibilité : ce qu’il faut tester avant de migrer
Avant de basculer votre prod en 8.5, je vous recommande au minimum :
- Scanner le code pour les dépréciations
(boolean),(integer),(double),(binary).- Backticks `…`.
caseterminés par;.- Output handlers qui font
echoouprint. - Fonctions dépréciées listées ci-dessus.
- Vérifier les scripts d’init PHP / Docker
- Retirer
docker-php-ext-install opcache. - Supprimer
zend_extension=opcachedansphp.iniou les.inide conf.
- Retirer
- Tester les middlewares et frameworks
- Symfony, Laravel, WordPress & co vont suivre rapidement, mais vérifiez la compatibilité officielle de vos versions.
- Attention aux libs qui se basent sur des comportements très bas niveau (output buffering, extensions cURL/Intl/DOM).
- Activer les logs de dépréciation en préprod
error_reporting = E_ALLdisplay_errors = 0,log_errors = 1- Remontez et corrigez tous les
E_DEPRECATEDavant la mise en production.
Support et calendrier
Selon la page officielle des versions supportées, PHP 8.5 est supporté jusqu’au 31 décembre 2027 (active) puis 31 décembre 2029 (security).
En clair : si vous migrez sur 8.5 dans les prochains mois, vous êtes tranquille plusieurs années sans upgrade majeur forcé.
Installation de PHP 8.5 sous Ubuntu Server
Nous avions déjà écrit le script pour PHP 8.4, je le reprends ci-dessus avec le paquet opcache désactivé puisqu’il est maintenant inclus par défaut :
#!/bin/bash
set -euo pipefail
# Script: Install new PHP version on Ubuntu Server
# (c) 2025, Matt Biscay, skyminds.net
# https://mattbiscay.com
CUR_PHP_VER="8.4"
NEW_PHP_VER="8.5"
# Packages to exclude (suffixes only, i.e. without "phpX.Y-" prefix)
# Example: php8.4-opcache -> "opcache"
# Note: PHP 8.5 includes OPcache by default.
exclude_packages=("xhprof" "opcache")
# Must be run as root (or via sudo)
if [[ "${EUID}" -ne 0 ]]; then
echo "This script must be run as root (try: sudo $0)" >&2
exit 1
fi
echo "Detecting installed php${CUR_PHP_VER} packages..."
# Collect installed phpX.Y-* packages (suffix only)
mapfile -t installed_suffixes < <(
dpkg -l \
| awk '/^ii\s+php'"${CUR_PHP_VER//./\\.}"'[-]/ {print $2}' \
| sed "s/^php${CUR_PHP_VER}-//" \
| grep -v '^php[0-9]' \
| sort -u
)
if [[ ${#installed_suffixes[@]} -eq 0 ]]; then
echo "No php${CUR_PHP_VER}-* packages detected. Nothing to migrate."
exit 0
fi
echo "Found php${CUR_PHP_VER}-* suffixes: ${installed_suffixes[*]}"
# Filter out excluded packages
declare -a to_install_suffixes=()
for suffix in "${installed_suffixes[@]}"; do
skip=false
for excluded in "${exclude_packages[@]}"; do
if [[ "${suffix}" == "${excluded}" ]]; then
echo "Excluding package suffix: ${suffix}"
skip=true
break
fi
done
[[ "${skip}" == true ]] && continue
to_install_suffixes+=("${suffix}")
done
# Build full package list for apt
declare -a apt_packages=("php${NEW_PHP_VER}")
for suffix in "${to_install_suffixes[@]}"; do
apt_packages+=("php${NEW_PHP_VER}-${suffix}")
done
echo
echo "Will install the following packages:"
printf ' %s\n' "${apt_packages[@]}"
echo
read -r -p "Proceed with installation? [y/N] " answer
case "${answer}" in
[Yy]* )
echo "Installing PHP ${NEW_PHP_VER} and extensions..."
apt update
apt install -y "${apt_packages[@]}"
;;
* )
echo "Aborting installation."
exit 0
;;
esac
echo
echo "Copying custom configuration (if present)..."
custom_ini_src="/etc/php/${CUR_PHP_VER}/fpm/conf.d/60-custom.ini"
custom_ini_dst="/etc/php/${NEW_PHP_VER}/fpm/conf.d/60-custom.ini"
pool_conf_src="/etc/php/${CUR_PHP_VER}/fpm/pool.d/zzz-custom-www.conf"
pool_conf_dst="/etc/php/${NEW_PHP_VER}/fpm/pool.d/zzz-custom-www.conf"
if [[ -f "${custom_ini_src}" ]]; then
cp "${custom_ini_src}" "${custom_ini_dst}"
echo "Copied ${custom_ini_src} -> ${custom_ini_dst}"
else
echo "Warning: ${custom_ini_src} not found, skipping."
fi
if [[ -f "${pool_conf_src}" ]]; then
cp "${pool_conf_src}" "${pool_conf_dst}"
echo "Copied ${pool_conf_src} -> ${pool_conf_dst}"
else
echo "Warning: ${pool_conf_src} not found, skipping."
fi
echo
echo "Enabling and starting php${NEW_PHP_VER}-fpm service..."
systemctl enable "php${NEW_PHP_VER}-fpm" --now
echo
echo "PHP ${NEW_PHP_VER} installed, custom configuration copied where available,"
echo "and php${NEW_PHP_VER}-fpm service enabled and started."Code language: PHP (php)
Le script installe tous les paquets qui étaient présents avec PHP 8.4, avec leur équivalent pour PHP 8.5.
Il ne vous reste ensuite qu’à éditer les server blocks NginX pour pointer vers php8.5-fpm.sock puis relancer nginx. On trouve tous les server blocks concernés :
grep -rin "php8.4" /etc/nginx/sites-available/Code language: JavaScript (javascript)
Et on met à jour la directive fastcgi_pass avec la dernière version de PHP-FPM, à la main – histoire de ne pas tout casser:
sudo sed -i 's/php8\.4-fpm/php8.5-fpm/g' /etc/nginx/sites-available/rocketstack.confCode language: JavaScript (javascript)
Si vous êtes vraiment sûr de votre coup (sauvegardez vos fichiers de configuration avant!), on peut automatiser et servir tous les server blocks avec PHP 8.5 :
#!/bin/bash
set -euo pipefail
# Migrate all Nginx server blocks to PHP 8.5
# (c) 2025 Matt Biscay
# https://mattbiscay.com
# Must be run as root (or via sudo)
if [[ "${EUID}" -ne 0 ]]; then
echo "This script must be run as root (try: sudo $0)" >&2
exit 1
fi
conf_dir="/etc/nginx/sites-available"
for file in "${conf_dir}"/*.conf; do
if [[ -f "${file}" ]]; then
# Create a backup before editing
sed -i.bak 's/php8\.4-fpm/php8.5-fpm/g' "${file}"
echo "Updated ${file}"
fi
done
echo "Testing Nginx configuration..."
if nginx -t; then
echo "Nginx configuration is valid. Reloading Nginx..."
systemctl reload nginx
echo "Nginx has been reloaded."
else
echo "Nginx configuration test failed. Check errors and restore *.bak if needed."
exit 1
fiCode language: PHP (php)
Bonne migration !
Vous imaginez un projet WordPress ou WooCommerce ? Je vous accompagne à chaque étape pour concrétiser vos ambitions, avec rigueur et transparence.