Engineering / /

PHP 8.6 release: what ships Nov 19 and how to upgrade

PHP 8.6 release day is Nov 19, and the clock is louder than the feature list. PHP 8.2 loses security support on Dec 31, so about one in five PHP sites is roughly ten weeks from running unpatched. Our web development team breaks down what ships, what breaks, and the upgrade checklist to run before RC4 lands on Oct 22.

TL;DR

  • PHP 8.6 GA lands Nov 19 (RC4 Oct 22): partial function application, clamp(), Time\Duration, and safer session defaults.
  • New session defaults (SameSite=Lax, HttpOnly, strict mode) can break logins, SSO, and payment callbacks. Test those by hand first.
  • is_integer() and spl_object_hash() are deprecated. Grep your codebase before GA.
  • PHP 8.2 security ends Dec 31. Target 8.3 or 8.4 this quarter, with 8.6 in CI now.

The timetable: RC4 ships Thursday

The PHP 8.6.0 release candidate cycle is nearly over. RC3 was tagged on Oct 8, RC4 is scheduled for Oct 22, RC5 follows on Nov 5, and general availability is set for Nov 19. The team has asked testers not to run release candidates in production and to report what they find on GitHub Issues.

Two details of the numbering trip people up. RC1 was skipped entirely: a packaging mistake meant RC2 shipped on Sept 24 in its place, and the RC2 announcement opened with an apology for the typo. The feature set locked on Sept 22, so everything landing from here on is a bug fix. Daniel Scherzer runs the release with Matteo Beccati and Joe Ferguson, and the managers can still add or drop a release candidate if the testing needs it.

The practical message for teams is plain. Test suites should be running against 8.6 now, in CI, as a non-blocking job. RC4 on Thursday is the build to point your pipeline at: late enough to be close to GA, early enough that a report you file can still change the release. On Ubuntu or Debian, the RC3 builds are already installable from Ondrej Sury's repositories, and the official Docker images are tracking the candidates.

The ecosystem is doing its part of the work in public. Drupal's PHP 8.6 compatibility tracker is working through upstream library deprecations found while testing RC3 builds. Drupal 12 requires PHP 8.5, and nobody is claiming 8.6 production support yet, but the tracker is the right kind of signal: frameworks are shaking out RC bugs before Thursday, not after GA.

What ships in 8.6

The most visible addition is partial function application. A call with a ? placeholder for one missing argument, or ... for the rest, produces a Closure. The strlen(...) first-class callable syntax is the same feature with no bound arguments, and the full RFC walkthrough has the examples. In practice it shortens the code you already write:

$lower = array_map(strtolower(?), $titles);
$log = printf("request %d took %0.3fs\n", ...);

A few semantics are worth knowing before you lean on it. Bound arguments are evaluated when the partial application is created, not each time it runs, so a bound date('H:i:s') is a snapshot. Every ? becomes a required parameter even when the original had a default. And when array_map() receives a first-class callable or a partial application, the engine compiles the loop instead of dispatching per call, so the shorter code is not slower.

The new clamp() function is the kind of small addition you will use within a week. It takes a value, a minimum, and a maximum, and returns the nearest bound when the value falls outside the range. It uses normal PHP comparisons, so strings and DateTimeInterface objects work too, and it throws a ValueError when the lower bound exceeds the upper bound or a bound is NAN:

$page = clamp((int) $_GET['page'], 1, $totalPages);

Time\Duration is a final readonly class for exact nanosecond-precision durations. Named factories build instances from seconds, minutes, and ISO 8601 strings, and RC3 added a comparison handler so two durations compare directly. There is no integration with DateTime or DateInterval, and sleep() does not accept one, so its job today is typed timeout configuration and the new polling API. Readonly properties can now declare default values, including in readonly classes, which closes a small gap that pushed configuration into constructors.

Two additions matter for infrastructure code. Io\Poll adds an object-oriented event-loop primitive with automatic backend selection across epoll, kqueue, poll, event ports, and WSAPoll, and wait() takes a Time\Duration timeout. The URI extension gains fluent Uri\Rfc3986\UriBuilder and Uri\WhatWg\UrlBuilder classes aimed at redirects, signed URLs, and SSRF checks. Performance work includes stateless closure caching, compiled printf() and array_map() calls, faster JSON encoding, and a faster tail-call VM on Windows. Treat the speedups as incremental and workload-dependent, and benchmark your own app.

Table of three PHP 8.6 session default changes: use_strict_mode 0 to 1, cookie_httponly 0 to 1, cookie_samesite unset to Lax

The session defaults that can break your logins

Features get the headlines, but session defaults carry the largest immediate migration risk. Three INI settings change: session.use_strict_mode goes from 0 to 1, session.cookie_httponly goes from 0 to 1, and session.cookie_samesite goes from unset to Lax.

Each change is a security fix for a real class of bug, and each one can break a flow that was quietly depending on the old behavior. Strict mode makes PHP reject session IDs it did not create. HttpOnly takes session cookies away from JavaScript through document.cookie. SameSite=Lax means the cookie no longer accompanies cross-site POST requests. The places that feel it first are the ones that cross a boundary: custom session handlers, sessions shared across subdomains or services, SAML POST bindings, and payment-gateway callbacks that rely on the session surviving the round trip.

If your checkout posts to a gateway and returns to a session-scoped cart, test that flow by hand on the RC before anything else. Login, SSO, and cart-to-checkout are the risky paths; the new defaults are safe for most apps, but "most" is not the bet a store takes blind. Applications that genuinely need the old values should set them explicitly in php.ini or at runtime. Pinning the old behavior on purpose, in a file you control, is a decision. Inheriting it because nobody read the UPGRADING file is an accident.

The deprecations you should grep for this week

PHP 8.6 is a quiet release for removals, but the deprecation list has items that show up in real codebases. Most of them are preparation for PHP 9, which means the warnings you see in the RC are the errors you will hit in two years.

The easy greps first. The scalar aliases is_integer(), is_long(), is_double(), and doubleval() are deprecated in favor of is_int(), is_float(), and (float). spl_object_hash() is deprecated in favor of spl_object_id(), which returns an integer instead of a string — a difference that matters for serialized data and cache keys. The third argument of define(), spl_classes(), and mysqli_get_charset() is deprecated. Returning from finally, returning values from construct() or destruct(), and calling is_a() or is_subclass_of() with strings when allow_string is false need an intent review.

The strategic item is mbregex. The Oniguruma library behind it is unmaintained, so the whole mb_ereg(), mb_eregi(), mb_ereg_replace(), mb_split(), and mb_regex_encoding() family is deprecated, with removal planned for PHP 9.0. Migration to PCRE with the u modifier is the common path, but the dialects differ, so it needs tests, not a find-and-replace. Custom session handlers without create_sid() and validateId() are in the list too, and so are several ArrayIterator sorting and serialization methods.

Stricter argument validation lands alongside the deprecations. Invalid modes and flags now affect array_filter(), array_change_key_case(), pathinfo(), and scandir(). Out-of-range values can make number_format(), sleep(), and usleep() throw ValueError. NUL bytes in getenv(), putenv(), parse_str(), setlocale(), and filename functions raise instead of being truncated or quietly rejected. trim(), ltrim(), and rtrim() now remove form feed by default. None of these break clean code. They break code that was already wrong and getting away with it.

One thing the list does not contain: list(). The proposed list() deprecation failed its vote 23 to 23, the pipe-assign |>= proposal failed 14 to 12, and pattern matching is not in 8.6 at all. If an old checklist tells you to grep for those, it is describing a different RFC cycle. Our Next.js 16 Cache Components breakdown took the same vote-by-vote approach to another framework's upgrade, and the habit pays off here: check what actually merged, not what was proposed.

The other clock: PHP 8.2 end of life

Six weeks after 8.6 ships, PHP 8.2 leaves support. Security fixes for the 8.2 branch end on Dec 31, 2026, and on the same day 8.4 leaves active support (its security window runs to Dec 31, 2028). PHP 8.5 gets security fixes until Dec 31, 2029.

Roughly one in five PHP-powered sites runs a version that stops receiving security fixes within about three months, per MonsterMegs' end-of-life analysis. Upgrade inertia runs deep in this ecosystem: about 18 percent of WordPress sites still run PHP 7.4, a version whose security support ended years ago. An unpatched runtime is not a theoretical risk. It is the part of your stack that every scanner on the internet knows how to read.

The sane target this quarter is 8.3 or 8.4, not 8.6 on day one. Let the new branch get its first patch release, get your suite green against the RCs now, and move production to a supported branch before the Dec 31 deadline. The teams in real trouble are not the ones behind one version. They are the ones who will discover on Jan 2 that their last upgrade plan was written for PHP 7.

The upgrade playbook: test Thursday's RC like GA depends on it

A PHP upgrade is a migration with a deadline, so run it like one. We put every framework migration through the same four steps we use on client builds: Discovery and Architecture, Design and Prototyping, Engineering and Build, Deployment and Scaling. For a runtime upgrade, the steps compress into a checklist you can start on Thursday.

First, add PHP 8.6 to CI as a non-blocking job against RC4. Package authors and anyone with extensions should go first: lower-level changes need more lead time than application code. Turn on E_DEPRECATED logging in the test environment so the deprecations above show up as failures you can count, not noise you scroll past.

Second, audit your effective session settings and compare them against the new defaults. If your app needs the old values, set them explicitly now and file the follow-up to remove them. Then test the risky flows by hand: login, SSO, cart, checkout, and every payment callback. These are the paths the session changes touch first, and they are the paths your revenue walks.

Third, grep the deprecations and trace user input. Search for the scalar aliases, spl_object_hash(), the mb_ereg family, and return in finally blocks. Then follow user input into filesystem, environment, and locale functions, where NUL bytes now raise exceptions. This is the same discipline we apply on client platform rebuilds like Eight Square's custom CRM: know where the data goes before you change what the runtime allows.

Fourth, stage it like production. Push the RC build to staging, send production-like traffic at it, and watch the error logs and exit codes. Two new failure modes to know: the error_include_args INI setting adds function arguments to error messages and is off by default, so decide whether your logs should see it, and hard out-of-memory failures now call abort() with exit code 134, which changes what your supervisor and alerts should watch for.

Six-step PHP 8.6 upgrade checklist: add 8.6 to CI, log E_DEPRECATED, audit sessions, test by hand, grep deprecations, stage it

Still running PHP 8.2 with December on the calendar?

We audit PHP apps, map the deprecations and session risks, and move production to a supported branch without breaking logins or checkout.

Get a PHP upgrade audit

Frequently asked questions

When is the PHP 8.6 release date? General availability is scheduled for Nov 19, 2026, following RC4 on Oct 22 and RC5 on Nov 5. The dates are planned milestones and can still move, so the safe move is to run your test suite against the release candidates now instead of waiting for the final tag.

Can I run a PHP 8.6 release candidate in production? No. The PHP team labels release candidates as early test versions and asks testers to report issues on GitHub instead of deploying them. Use Docker, a local setup, or a staging server. Release candidates exist so your bug reports can still change the release. Your users should never be the test suite.

Will PHP 8.6 break my WordPress or Laravel site? Most code runs unchanged, but three things bite real apps: the new session defaults can break login and payment-callback flows, deprecated functions raise E_DEPRECATED warnings, and stricter argument validation turns silently-wrong calls into errors. Clone the site, run it on the RC, and test login, cart, and checkout by hand.

What changed with PHP sessions in 8.6? Three defaults flipped: strict mode is now on, session cookies are HttpOnly, and SameSite defaults to Lax. PHP rejects session IDs it did not create, JavaScript loses session-cookie access, and cookies stop accompanying cross-site POST requests. Apps that need the old behavior should set the values explicitly.

What should I upgrade to if I am still on PHP 8.2? Move production to PHP 8.3 or 8.4 this quarter, before 8.2 security support ends on Dec 31, 2026. Add PHP 8.6 to CI now and plan the 8.6 move after GA plus its first patch release, once your suite is green and your dependencies declare support.

Do hosting providers offer PHP 8.6 right away? No. Hosts add new PHP versions to their panels on their own schedules, so do not expect 8.6 in your version selector on Nov 19. Fix compatibility now against the RC builds, and the version switch later is a two-minute change instead of a migration project.

More from Buzzin Tech Hub

Have a project in mind?

Tell us what you're working on and we'll tell you honestly what it'll take to build it right.

Start Your Project →
VOLT // QUICK SCOPE Need a 60-second stack or growth audit?