The PHP team released PHP 8.6.0 RC5 on 5 November 2026. It is the last planned release candidate. The first general availability release, PHP 8.6.0, is planned for 19 November 2026. That leaves about ten days to find problems in your apps and packages before the final release.
This guide covers what PHP 8.6 adds, what might break, and a step-by-step plan for testing on RC5. As the PHP team stresses, RC5 is a test build: do not use it in production.
What’s new in PHP 8.6
Here are the changes most developers will notice, based on PHP.Watch’s PHP 8.6 overview.
clamp(): keep a value inside a range
PHP finally has a built-in clamp() function, like std::clamp in C++ or Math.clamp in Java. If the value is inside the range, you get it back. Otherwise you get the nearest bound.
clamp(5, 0, 100); // 5
clamp(-5, 0, 100); // 0
clamp(105, 0, 100); // 100
clamp(10.0, 1.6, 4.2); // 4.2
This replaces the familiar max($min, min($max, $value)) pattern, which is easy to get backwards. Real-world examples:
// Pagination: never allow per_page below 1 or above 100
$perPage = clamp((int) ($_GET['per_page'] ?? 20), 1, 100);
// Image quality slider
$quality = clamp($requestedQuality, 10, 95);
// Works with comparable objects too, like dates
$date = clamp(
new DateTimeImmutable('2026-01-08'),
new DateTimeImmutable('2026-01-01'),
new DateTimeImmutable('2026-12-31'),
);
Rules to remember:
- If
$minis greater than$max, it throws aValueError. $minand$maxcannot beNAN.- With mixed types (strings, arrays, booleans), PHP’s normal comparison rules apply, and they can surprise you. Stick to numbers and comparable objects.
Need it on older PHP? PHP.Watch notes that a simple userland polyfill works:
if (!function_exists('clamp')) {
function clamp(mixed $value, mixed $min, mixed $max): mixed {
if ($max < $min) {
throw new ValueError('clamp(): $min must be smaller than or equal to $max');
}
return $value > $max ? $max : ($value < $min ? $min : $value);
}
}
SortDirection: a built-in enum for sort order
PHP 8.6 adds a simple unit enum in the global namespace:
enum SortDirection {
case Ascending;
case Descending;
}
It has no backing values on purpose, so each app can map it to its own format at the boundary. Using it in a type signature removes a whole class of “invalid direction” bugs:
final class ProductRepository
{
public function list(SortDirection $direction = SortDirection::Ascending): array
{
$sql = 'SELECT * FROM products ORDER BY price ' . match ($direction) {
SortDirection::Ascending => 'ASC',
SortDirection::Descending => 'DESC',
};
// ... run the query
return [];
}
}
$repo->list(SortDirection::Descending);
Because match covers every case, there is no need to validate a string such as 'asc' or 'DESC' any more. If you need the same code on PHP 8.1 to 8.5, declare the enum yourself when enum_exists('SortDirection') is false.
grapheme_strrev(): reverse strings without breaking emoji
strrev() reverses bytes, which breaks multibyte text and combined emoji. The Intl extension adds grapheme_strrev(), which reverses by grapheme clusters (what users see as single characters):
grapheme_strrev('ABCDE'); // EDCBA
grapheme_strrev('Apple🍏'); // 🍏elppA
grapheme_strrev('🇨🇳 - 🇳🇨'); // 🇳🇨 - 🇨🇳
It returns false on failure, and it needs the intl extension.
Smaller changes that can affect behaviour
- trim() now strips form feed by default.
trim,ltrim,rtrimandchopadd\f(0x0C) to the default character list. If you depend on keeping a form feed at the edge of a string, pass the$charactersargument explicitly. - array_filter() validates
$mode. Passing an invalid value for the$modeargument now throws aValueErrorinstead of being silently accepted. - Better json_decode() errors. Error and exception messages now say where in the JSON the error happened, which makes broken payloads much easier to debug.
How to test your app on PHP 8.6 RC5
Step 1: Get an RC5 build
Source tarballs are on the php.net pre-release builds page. Most teams find Docker easier. Check Docker Hub for the official PHP image’s 8.6 RC tags, or build from source in a throwaway container.
Step 2: Add PHP 8.6 to your CI matrix
With GitHub Actions and shivammathur/setup-php, add the new version next to your current one (check the action’s README to confirm which pre-release versions it currently provides). Mark it as allowed to fail until GA so it doesn’t block merges:
jobs:
tests:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
matrix:
include:
- php: '8.5'
experimental: false
- php: '8.6'
experimental: true
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- run: composer install --no-progress
- run: vendor/bin/phpunit
Step 3: Check your dependencies
Run composer install on 8.6. Composer will tell you which packages don’t allow PHP 8.6 in their version constraints yet. If one of them is yours, test it and release a version that allows ^8.6.
Step 4: Search for collisions
Since clamp and SortDirection now live in the global namespace, an app that declares its own global clamp() function or SortDirection class will conflict. A quick search finds them:
grep -rn "function clamp(" src/ app/
grep -rn "SortDirection" src/ app/
Step 5: Report what you find
If something breaks and it looks like a PHP bug, the release managers ask you to report it on GitHub Issues. Reports made now can still be fixed before 19 November.
Meanwhile: patch PHP 8.5
If you are on PHP 8.5 in production, make sure you are on 8.5.12 or later. It was released on 21 October 2026 to fix two critical vulnerabilities (CVE-2026-109618 and CVE-2026-109619) that can allow unauthenticated remote code execution. The PHP team says updating is the only safe choice.
Takeaway
PHP 8.6 is a polish release: clamp(), SortDirection and grapheme_strrev() remove small but common annoyances, and the behaviour changes are minor. Add RC5 to CI this week, fix anything that comes up, and you’ll be ready to upgrade soon after 19 November.
Sources: PHP.net news archive 2026 (RC5 announcement) · PHP.Watch: PHP 8.6 What’s New · PHP.Watch: clamp() · PHP.Watch: SortDirection · PHP 8.6 timetable
