---
title: 'PHP 8.6 : les nouveautés à connaître avant la sortie du 19 novembre'
description: 'PHP 8.6 arrive en GA le 19 novembre 2026 : application partielle de fonction, valeurs par défaut des propriétés readonly, Time\Duration et dépréciations à anticiper.'
pubDate: 2026-11-10
family: 'php-symfony'
tags: []
faq:
  - question: 'Quand PHP 8.6 sort-il en version stable ?'
    answer: 'PHP 8.6.0 RC5 est sortie le 5 novembre 2026 et php.net indique que la prochaine version sera la première GA, planifiée pour le 19 novembre 2026. Le calendrier peut encore bouger, la page News de php.net reste la référence.'
  - question: 'Peut-on déjà utiliser PHP 8.6 en production ?'
    answer: 'Non. Les release candidates servent au test : php.net demande explicitement de ne pas les utiliser en production. Attends la GA du 19 novembre 2026, puis vérifie la compatibilité de tes extensions et de ton framework avant de déployer.'
  - question: 'Une propriété readonly avec valeur par défaut reste-t-elle modifiable dans le constructeur ?'
    answer: "Non. La valeur par défaut compte comme l'assignation d'initialisation : la propriété est déjà initialisée avant le corps du constructeur, et toute assignation ultérieure échoue avec une erreur de modification d'une propriété readonly. La RFC readonly_property_defaults documente ce comportement."
---

PHP 8.6 est en fin de cycle. La cinquième release candidate est sortie le 5 novembre 2026, et php.net annonce que la prochaine version sera la première GA, prévue le 19 novembre 2026 ([PHP News Archive](https://www.php.net/archive/2026.php)). Il te reste donc une dizaine de jours pour tester sur une branche, pas pour migrer ta production.

Un détail à ne pas rater : la RC4 du 21 octobre corrigeait deux vulnérabilités critiques permettant une exécution de code à distance non authentifiée. php.net recommande de mettre à jour immédiatement toute build 8.6 antérieure ([annonce RC4](https://www.php.net/archive/2026.php)). Si tu fais tourner une alpha ou une bêta en intégration, c'est le premier chantier. Le contexte de ces failles est détaillé dans [l'article sur les RCE critiques corrigées](/blog/failles-rce-critiques-php-versions-corrigees-et-mitigations).

Pour un projet Symfony ou PHP classique, la GA ne t'oblige à rien : elle ouvre surtout une fenêtre pour repérer les dépréciations et vérifier que tes extensions suivent. Voici ce qui mérite ton attention, avec les sources primaires à côté de chaque point.

> Les extraits de code ci-dessous proviennent des RFC et du fichier `UPGRADING` de la branche PHP-8.6. Ils sont documentés mais n'ont pas été exécutés pour cet article.

## L'application partielle de fonction

C'est la nouveauté de syntaxe la plus visible. Une application partielle de fonction (PFA) appelle une fonction en laissant des trous, et renvoie une closure qui attend les arguments manquants. Le trou s'écrit `?` pour exactement un argument, ou `...` pour « tout ce qui reste ».

```php
$slugify = str_replace(' ', '-', ?);

$slugs = array_map($slugify, ['PHP 8.6', 'Release candidate']);
// Résultat attendu d'après la RFC : ['PHP-8.6', 'Release-candidate']
```

La closure obtenue conserve le nom, les types, les valeurs par défaut, les paramètres par référence et les attributs `#[NoDiscard]` et `#[SensitiveParameter]` de la fonction d'origine. Elle est donc inspectable via la réflexion comme n'importe quelle closure ([RFC partial_function_application_v2](https://wiki.php.net/rfc/partial_function_application_v2)).

La variante `...` est pratique pour préremplir le début d'un appel :

```php
$reportError = $logger->error('paiement: ', ...);

$reportError('montant invalide');
```

Deux limites à garder en tête. Les arguments nommés restent permis, mais un `?` nommé réordonne les paramètres dans la closure générée. Et `func_get_args()` voit les arguments transmis à la closure, pas ceux déjà fixés. Pour le reste, la RFC ne signale aucune rupture de compatibilité : c'est de la syntaxe nouvelle, pas un changement de comportement.

## Les valeurs par défaut des propriétés readonly

Depuis PHP 8.1, une propriété readonly ne pouvait pas déclarer de valeur par défaut. PHP 8.6 lève cette restriction, ce qui rend enfin utilisables les propriétés d'interface introduites en 8.4 : tu peux exprimer un contrat en lecture seule et fournir la valeur directement dans la classe.

```php
interface Source
{
    public string $label { get; }
}

final readonly class ApiSource implements Source
{
    public string $label = 'api';
}
```

Le piège est dans la sémantique : la valeur par défaut compte comme l'assignation d'initialisation. La propriété est donc déjà initialisée quand le corps du constructeur commence, et une assignation dans le constructeur échoue avec `Cannot modify readonly property` ([RFC readonly_property_defaults](https://wiki.php.net/rfc/readonly_property_defaults)). Si tu as l'habitude d'initialiser toutes tes readonly dans le constructeur, ce cas ne te concerne pas ; si tu migres des constantes de classe vers des propriétés d'interface, il te concernera.

Autre point : une propriété readonly, même avec une valeur par défaut, satisfait une exigence `{ get; }` mais pas `{ get; set; }`.

## Time\Duration pour les durées

PHP n'avait pas de type natif pour une durée de type chronomètre : on passait des entiers en secondes, en millisecondes ou en microsecondes, avec le risque d'unité qui va avec. PHP 8.6 ajoute `Time\Duration`, une classe `final readonly` à précision nanoseconde, indépendante des fuseaux horaires et des calendriers ([RFC duration_class](https://wiki.php.net/rfc/duration_class)).

```php
use Time\Duration;

$connect = Duration::fromMilliseconds(500);
$backoff = Duration::fromMilliseconds(200)->multiplyBy(4);
$total = $connect->add($backoff);

if ($total > Duration::fromSeconds(1)) {
    // ...
}
```

Les constructeurs couvrent les nanosecondes, microsecondes, millisecondes, secondes, minutes, heures et les chaînes ISO 8601 sans composant de date. Les opérations passent par `add()`, `sub()`, `multiplyBy()`, `divideBy()`, `negate()` et `absolute()`, et les opérateurs de comparaison fonctionnent directement. La plage maximale est d'environ 292 ans, et un dépassement lève `Time\TimeException`.

Le lien avec le reste de PHP 8.6 est concret : la nouvelle API de polling `Io\Poll\Context::wait()` prend un `?Time\Duration` en timeout ([stub io_poll](https://github.com/php/php-src/blob/PHP-8.6/ext/standard/io_poll.stub.php)).

## #[\Override] sur les constantes de classe

`#[\Override]` existait pour les méthodes depuis 8.3, puis pour les propriétés en 8.5. PHP 8.6 l'étend aux constantes de classe. L'intérêt est le même : rendre explicite une intention d'écrasement, et faire échouer le code quand il n'y a rien à écraser.

```php
interface HasLabel
{
    public const LABEL = 'unknown';
}

final class Report implements HasLabel
{
    #[\Override]
    public const LABEL = 'report';
}
```

L'attribut vérifie qu'une constante publique ou protégée existe bien dans un parent ou une interface implémentée ; une constante privée ne suffit pas. Il fonctionne aussi sur les cases d'énumération, qui doivent alors écraser une constante d'interface ([RFC override_constants](https://wiki.php.net/rfc/override_constants)). C'est le genre de garde-fou qui évite une constante « orpheline » après un renommage.

## Des erreurs de flux structurées

Jusqu'ici, une erreur de flux se manifestait par un warning, une notice, ou rien selon le wrapper. PHP 8.6 ajoute un mode d'erreur configurable par contexte, avec des objets exploitables ([RFC stream_errors](https://wiki.php.net/rfc/stream_errors)).

```php
$context = stream_context_create([
    'stream' => [
        'error_mode' => StreamErrorMode::Exception,
        'error_store' => StreamErrorStore::All,
    ],
]);

try {
    $handle = fopen('/chemin/absent.txt', 'rb', false, $context);
} catch (StreamException $e) {
    foreach ($e->getErrors() as $error) {
        echo $error->code->name, ': ', $error->message;
    }
}
```

`error_mode` accepte `Error` (comportement historique), `Exception` ou `Silent`. `stream_last_errors()` renvoie les erreurs stockées et `stream_clear_errors()` les vide. Deux restrictions documentées : ces options ne peuvent pas être posées sur le contexte par défaut via `stream_context_set_default()`, et seules les erreurs terminantes lèvent une exception en mode `Exception`. Dans le même secteur, `filter.max_filter_count` limite le nombre de filtres d'une URL `php://filter` ([RFC](https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains)).

## JSON : la position de l'erreur dans le message

Les messages de `json_last_error_msg()` et des `JsonException` incluent désormais la ligne et la colonne de l'erreur. Le format est vérifié dans la suite de tests de php-src : `Syntax error near location 1:10` ([tests json_last_error_msg_error_location](https://github.com/php/php-src/blob/PHP-8.6/ext/json/tests/json_last_error_msg_error_location_001.phpt)).

```php
json_decode('{"name": "value}');

echo json_last_error_msg();
// Sortie documentée par les tests : Syntax error near location 1:10
```

Sur un payload de webhook ou un fichier de configuration, tu passes d'un « Syntax error » nu à un endroit précis à corriger. Le code d'erreur, lui, ne change pas.

## Aussi au programme

Quelques ajouts plus ciblés, tous listés dans le fichier [UPGRADING de la branche PHP-8.6](https://github.com/php/php-src/blob/PHP-8.6/UPGRADING) :

- `Uri\Rfc3986\UriBuilder` et `Uri\WhatWg\UrlBuilder`, plus `getUriType()`, `getHostType()` et `url_percent_encode()` ([RFC uri_followup](https://wiki.php.net/rfc/uri_followup)).
- Reprise de session TLS, PSK externes et early data TLS 1.3 en 0-RTT pour les flux OpenSSL ([RFC tls_session_resumption](https://wiki.php.net/rfc/tls_session_resumption)).
- `Pdo\Pgsql::ATTR_CHUNK_SIZE` pour le fetch par lots, avec libpq 17 ou plus récent.
- `size_delivered` dans `curl_getinfo()` et `CURLOPT_SEEKFUNCTION` pour rejouer un corps de requête après redirection.
- Mutation directe d'un objet stocké dans une constante via `OBJ->prop = $val`, et affectation par référence sur `WeakMap` sans clé préexistante.
- `__debugInfo()` autorisé sur les énumérations.

## Les dépréciations à anticiper

Elles ne cassent rien en 8.6, mais elles préparent PHP 9. Active `error_reporting=E_ALL` en CI pour les voir passer.

- `return` dans un bloc `finally` : sors le `return` du bloc, son comportement était déjà piégeux.
- Valeur de retour dans `__construct()` ou `__destruct()` : supprime le `return` avec valeur.
- Type de retour `array|null` ou `?array` pour `__debugInfo()` : déclare `array`.
- `__construct()` ou `__destruct()` déclaré comme générateur : extrais la génération dans une méthode dédiée.
- Fonction nommée `readonly`, identifiants `let` ou `is`, constante nommée `_` : renomme.
- Troisième argument de `define()` : il n'a plus de raison d'être, retire-le.
- `is_a()` ou `is_subclass_of()` avec une chaîne en premier argument alors que `$allow_string` vaut `false` : passe un objet.
- mbregex : l'API est dépréciée parce que la bibliothèque Oniguruma n'est plus maintenue ([RFC eol-oniguruma](https://wiki.php.net/rfc/eol-oniguruma)). Prévois une migration vers PCRE.
- Côté SPL, `spl_object_hash()` laisse place à `spl_object_id()`, `spl_classes()` à `ReflectionExtension::getClassNames()`, et les méthodes CSV de `SplFileObject` sont dépréciées.

La liste complète et les liens vers chaque RFC de dépréciation sont dans la section 4 du fichier `UPGRADING`.

## Comment tester la RC

Le point d'entrée est la [page de téléchargements de php.net](https://www.php.net/downloads.php), qui publie les archives de la RC5. Au moment d'écrire, aucune image Docker officielle `php:8.6.0RC5-cli` n'est publiée sur Docker Hub : la vérification de l'API renvoie 404. Je ne te recommande donc pas de passer par Docker pour cette RC, et je ne fabrique pas de commande `docker run` qui échouerait.

Si tu préfères compiler, la procédure classique depuis l'archive source est documentée dans le fichier `INSTALL`. Elle ressemble à ceci, et n'a pas été exécutée pour cet article :

```bash
./configure --prefix=/opt/php86
make -j"$(nproc)"
make install
```

Quel que soit le canal choisi, php.net répète la même consigne : ne pas utiliser une RC en production. Le test a de la valeur en CI, sur une branche, ou dans un conteneur jetable.

## Faut-il migrer tout de suite ?

Pour la production, non. Attends la GA du 19 novembre 2026, puis laisse passer quelques jours pour les correctifs de sortie. Une RC n'est pas un socle de déploiement, et la RC4 a rappelé que des correctifs de sécurité critiques peuvent encore arriver dans le cycle.

Pour la préparation, oui, il y a du travail utile dès maintenant :

1. Lance ta suite de tests avec `error_reporting=E_ALL` sur la RC pour inventorier les dépréciations.
2. Vérifie que chaque extension tierce dont tu dépends publie une build 8.6, en particulier celles qui ne sont pas dans le cœur.
3. Contrôle ta chaîne d'outils : image de base, OPcache, analyseur statique et outillage de formatage doivent connaître la nouvelle syntaxe.
4. Isole les usages de mbregex et des fonctions SPL dépréciées, ce sont les migrations les plus probables dans un projet Symfony ancien.

Si tu veux d'abord voir à quoi ressemblaient les cycles précédents, les articles sur [les nouveautés de PHP 8.4](/blog/quatre-nouveautes-incontournables-de-php-8-4) et [celles de PHP 8.3](/blog/decouverte-des-nouveautes-de-php-8-3) donnent le recul.

## Sources

- [PHP News Archive 2026](https://www.php.net/archive/2026.php) : annonces RC4 et RC5, date de GA.
- [UPGRADING de la branche PHP-8.6](https://github.com/php/php-src/blob/PHP-8.6/UPGRADING) : nouveautés et dépréciations.
- [NEWS de la branche PHP-8.6](https://github.com/php/php-src/blob/PHP-8.6/NEWS) : détail des correctifs par RC.
- [Page de téléchargements](https://www.php.net/downloads.php) : archives officielles.

## Questions fréquentes

**Quand PHP 8.6 sort-il en version stable ?**

PHP 8.6.0 RC5 est sortie le 5 novembre 2026 et php.net indique que la prochaine version sera la première GA, planifiée pour le 19 novembre 2026. Le calendrier peut encore bouger, la page News de php.net reste la référence.

**Peut-on déjà utiliser PHP 8.6 en production ?**

Non. Les release candidates servent au test : php.net demande explicitement de ne pas les utiliser en production. Attends la GA du 19 novembre 2026, puis vérifie la compatibilité de tes extensions et de ton framework avant de déployer.

**Une propriété readonly avec valeur par défaut reste-t-elle modifiable dans le constructeur ?**

Non. La valeur par défaut compte comme l'assignation d'initialisation : la propriété est déjà initialisée avant le corps du constructeur, et toute assignation ultérieure échoue avec une erreur de modification d'une propriété readonly. La RFC readonly_property_defaults documente ce comportement.
