The PHP team just released PHP 8.6.0 RC2, and it's the first release candidate for PHP 8.6. There is no RC1. The release managers tagged one, found a mistake in it, and tagged RC2 instead. Release candidates come after the feature freeze, so from here the work is bug fixes on the way to general availability on November 19.
Here's what's in RC2:
- A fix so
fclose(),file_put_contents(), andcopy()returnfalsewhen flushing the stream fails - A warning from
unserialize(''), which used to returnfalsewith no warning at all - Renamed cases on the new URI host type enums, from
IPv4toIpV4 - A comparison handler and an unserialization fix for
Time\Duration - Unicode 18.0 data tables in MBString, and file 5.48 in Fileinfo
- Memory safety fixes across DOM, Intl, PDO, SPL, SQLite, and Readline
What Happened to RC1
Daniel Scherzer, one of the three PHP 8.6 release managers, explained it on his blog. The first release candidate is when the team creates the PHP-8.6 branch, and part of that step is bumping the internal API numbers (ZEND_MODULE_API_NO, ZEND_EXTENSION_API_NO, and PHP_API_VERSION). Extensions check those numbers to know which PHP they were built for.
The other two release managers, Matteo Beccati and Joe Ferguson, use a Docker container to automate part of the build. The script had a bug that scheduled the bump for the last release candidate instead of the first. So the php-8.6.0RC1 tag went out with the wrong API numbers, and the team tagged a corrected RC2 rather than reuse the number. Scherzer says he'd reviewed the same script and missed the bug too, and that they handled it the way he would have.
Nothing here affects your code. If you went looking for RC1 downloads and couldn't find them, that's why.
Changes Worth Testing
Two fixes change what existing code sees. fclose(), file_put_contents(), and copy() now return false when flushing or closing the stream fails. Before, those failures were silently ignored, and there was no way to tell a failed write from a good one (bug #60110). If your code treats a false from these as impossible, it's worth a look.
unserialize('') still returns false, but now with the same warning any other bad input gets:
unserialize(''); // Warning: unserialize(): Error at offset 0 of 0 bytes
The old behavior made an empty string indistinguishable from serialize(false), which is b:0; (GH-23780).
If you've written code against the betas using the URI extension's getHostType(), the enum cases changed names to follow PHP's naming policy and the RFC (GH-23851):
// Beta 3 Uri\Rfc3986\UriHostType::IPv4; // RC2 Uri\Rfc3986\UriHostType::IpV4;
The same goes for IpV6, IpVFuture, and the matching cases on Uri\WhatWg\UrlHostType. These enums are new in 8.6, so no released code depends on the old names.
The Time\Duration class picked up a comparison handler, so you can compare two durations directly, and a fix for unserializing it.
The Road to GA
Here is the full PHP 8.6 timetable. Releases are tagged on a Tuesday and ship on the Thursday, and the release managers can add or drop releases as development goes.
| Date | Release |
|---|---|
| July 2 | Alpha 1 |
| July 16 | Alpha 2 |
| July 30 | Alpha 3 |
| August 11 | All RFCs targeting 8.6 should be merged |
| August 13 | Beta 1 and soft feature freeze |
| August 27 | Beta 2 |
| September 10 | Beta 3 |
| September 22 | Hard feature freeze |
| September 24 | RC1 (skipped) |
| September 24 | RC2 |
| October 8 | RC3 |
| October 22 | RC4 |
| November 5 | RC5 |
| November 19 | General availability |
This is the point to run your test suite against 8.6 if you haven't already. The team asks you not to run it in production, and to report anything you find on GitHub Issues. For everything landing in 8.6, from partial function application to the new session defaults, see our PHP 8.6 version page.