Blog

PHP 8.6 komt op 19 november: dit is er nieuw en dit moet je testen

PHP 8.6 verschijnt op 19 november. Partial function application, clamp(), veiligere sessie-instellingen en een paar wijzigingen die je eerst moet testen.

Over twee weken is PHP 8.6 er. De release staat gepland voor donderdag 19 november 2026. RC4 kwam uit op 21 oktober en RC5 staat voor 5 november op de planning. De features liggen sinds de feature freeze van 22 september vast, dus je kunt nu al zien wat er verandert.

Ik zet hieronder op een rij wat er nieuw is, wat stuk kan gaan en hoe je je upgrade voorbereidt.

Wat er nieuw is #

Partial function application #

Dit is de grootste wijziging aan de taal. Zet je bij een functieaanroep een ? of ... neer in plaats van een argument, dan roept PHP de functie niet aan maar krijg je een closure terug. ? staat voor precies één argument, ... voor alle overige argumenten.

$makeSlug = str_replace(' ', '-', ?);

$makeSlug('Hello World'); // Hello-World

$titles = array_map(strtolower(?), $titles);

Tot nu toe schreef je daarvoor een arrow function als fn ($s) => str_replace(' ', '-', $s). De closure die je nu krijgt neemt de parameternamen en types van de oorspronkelijke functie over.

Er zijn twee dingen die afwijken van een arrow function:

  • De argumenten die je al invult worden direct geëvalueerd, op het moment dat de partial wordt gemaakt, en niet pas bij de aanroep. Zet je er een functieaanroep in, dan draait die meteen.
  • Een vervolg-RFC regelt dat elke ? een verplichte parameter wordt, ook als de originele parameter optioneel was. Optionele parameters houd je alleen via ....

Op new werkt het niet: new Foo(1, ?) is een compile error. Een statische factory als Foo::make(1, ?) mag wel.

clamp() #

Eindelijk een ingebouwde manier om een waarde tussen twee grenzen te houden, zonder max($min, min($max, $value)):

clamp(10, min: 0, max: 100);  // 10
clamp(101, min: 0, max: 100); // 100
clamp(-1, min: 0, max: 100);  // 0

Is $min groter dan $max, of is een van de grenzen NAN, dan krijg je een ValueError.

Readonly properties met een standaardwaarde #

Tot nu toe gaf een default op een readonly property een compile error. In 8.6 mag het:

final class RuleA implements Rule
{
    public readonly string $className = SomeParser::class;
}

Let op: zo'n property kun je daarna niet meer in de constructor zetten. Dat geeft Cannot modify readonly property.

Kleinere toevoegingen #

  • Time\Duration: een readonly klasse voor tijdsduren met nanoseconde-precisie, met optellen, delen en vergelijken. Handig voor retry-logica, bijvoorbeeld Duration::fromMilliseconds(100)->multiplyBy(2 ** $attempt).
  • Polling API (Io\Poll): één interface boven epoll, kqueue en WSAPoll, als vervanger van stream_select() in event loops.
  • Betere foutafhandeling voor streams: met de context-optie error_mode kun je streams exceptions laten gooien in plaats van warnings.
  • #[\Override] op class constants, zodat je fouten ziet als een constante in de parent wordt hernoemd.
  • Enums mogen __debugInfo() hebben, voor leesbaardere var_dump()-output.
  • Doc comments op parameters, uit te lezen met ReflectionParameter::getDocComment().
  • enum SortDirection met Ascending en Descending. Er is nog niets in de core dat hem gebruikt, maar frameworks kunnen hem nu als gedeelde type aannemen.
  • grapheme_strrev() keert een string om zonder emoji en samengestelde tekens kapot te maken.

Wat stuk kan gaan #

Sessies zijn standaard veiliger #

Drie standaardwaarden in ext/session veranderen:

Instelling Was Wordt
session.use_strict_mode 0 1
session.cookie_httponly 0 1
session.cookie_samesite leeg Lax

Voor de meeste sites is dit gewoon winst. Problemen kun je verwachten als:

  • je JavaScript de sessiecookie leest via document.cookie;
  • je een cross-site POST met sessie nodig hebt, zoals SAML-login die vanuit je eigen site start of een oud formulier dat vanaf een ander domein post. Die requests sturen de sessiecookie niet meer mee;
  • je zelf een sessie-ID van buitenaf meegeeft, bijvoorbeeld om een sessie tussen subdomeinen over te dragen.

Staan deze waarden expliciet in je php.ini, dan wint die. Laravel beheert zijn eigen sessiecookies en heeft hier dus weinig last van. Dit gaat vooral over code die session_start() direct gebruikt.

Deprecations en kleine gedragswijzigingen #

  • Een waarde returnen uit __construct() of __destruct() geeft een deprecation. Een kale return; mag nog wel. In de volgende major wordt het een compile error.
  • trim(), ltrim() en rtrim() halen nu standaard ook form feed (\f) weg.
  • array_filter() gooit een ValueError bij een ongeldige $mode.
  • De mbregex-functies (mb_ereg*, mb_split() en verwante) zijn deprecated en verdwijnen in PHP 9.0, omdat de onderliggende Oniguruma-bibliotheek niet meer wordt onderhouden. Gebruik je ze, dan is dit een goed moment om naar preg_* over te stappen.

Hoe je je upgrade voorbereidt #

Wacht op je framework. Laravel 13 ondersteunt officieel PHP 8.3 tot en met 8.5. 8.6 staat nog niet in de supporttabel. Upgrade productie pas als je framework en je belangrijkste packages 8.6 ondersteunen.

Kijk welke packages je tegenhouden. Composer kan je vertellen welke dependencies PHP 8.6 blokkeren:

composer why-not php 8.6.0

Test nu al in een aparte omgeving. Draai je testsuite tegen de RC in een losse container of een extra CI-job, en zet daar deprecations aan (error_reporting=E_ALL). Zoek in je code naar plekken die geraakt worden:

grep -rn "document.cookie" resources/ public/
grep -rn "mb_ereg" app/ src/

Upgrade daarna in kleine stappen: eerst lokaal en in CI, dan acceptatie, dan productie. Houd de vorige PHP-versie als image of pakket beschikbaar, zodat je snel terug kunt.

PHP 8.6 is geen release waar je voor moet rennen. Je hoeft niets te herschrijven, en wat er nieuw is maakt alledaagse code korter. Het enige waar je echt op moet letten zijn de sessie-instellingen als je oudere PHP-code zonder framework draait.

Bronnen #