Installer PHP 8.5 sous Ubuntu Server

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() et get_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=diff montre les directives INI modifiées par rapport aux valeurs par défaut.
  • Nouveau directive max_memory_limit qui définit un plafond à memory_limit pour é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 opcache ni de zend_extension=opcache dans php.ini.
  • Tenter d’ajouter zend_extension=opcache dé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() et curl_share_close() (no-op depuis PHP 8.0).
  • xml_parser_free() (idem).
  • socket_set_timeout() en faveur de stream_set_timeout().
  • Les constantes MHASH_*.
  • Alias mysqli_execute() en faveur de mysqli_stmt_execute().

Compatibilité : ce qu’il faut tester avant de migrer

Avant de basculer votre prod en 8.5, je vous recommande au minimum :

  1. Scanner le code pour les dépréciations
    • (boolean), (integer), (double), (binary).
    • Backticks `…`.
    • case terminés par ;.
    • Output handlers qui font echo ou print.
    • Fonctions dépréciées listées ci-dessus.
  2. Vérifier les scripts d’init PHP / Docker
    • Retirer docker-php-ext-install opcache.
    • Supprimer zend_extension=opcache dans php.ini ou les .ini de conf.
  3. 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).
  4. Activer les logs de dépréciation en préprod
    • error_reporting = E_ALL
    • display_errors = 0, log_errors = 1
    • Remontez et corrigez tous les E_DEPRECATED avant 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.

Discutons de votre projet ensemble »

Matt

Développeur certifié WordPress & WooCommerce chez Codeable, administrateur système et enseignant-chercheur, je mets mon expertise au service de vos projets web.

Ma priorité : des sites performants, fiables et sécurisés, pensés pour offrir la meilleure expérience utilisateur. J’accompagne chaque client avec écoute et pédagogie, pour transformer vos idées en solutions concrètes et durables.

Profitez de solutions WordPress et WooCommerce sur-mesure, pensées pour optimiser durablement votre site.
Explorez les leviers pour booster l’impact de votre site web.

Opinions