PHP 8.6: A more expressive, more capable PHP
I look at partial function application, more secure defaults and the runtime improvements planned for PHP 8.6, with examples of how they could reduce boilerplate in everyday application code.
10 min read
PHP has never been a language that stands still. Each release brings incremental improvements, but every so often a collection of changes arrives that feels like a meaningful shift in how we can write applications.
PHP 8.6 looks like one of those releases.
There isn’t one headline feature that completely changes the language. Instead, it brings together several improvements that make PHP more expressive, improve its foundations, and remove some of the friction developers have become accustomed to working around.
The feature I’m particularly excited about is Partial Function Application (PFA). Combined with PHP’s existing first-class callables and the pipe operator introduced in PHP 8.5, it opens up some interesting possibilities for functional programming in everyday PHP.
There are also useful additions to the DateTime and Stream APIs, a handful of quality-of-life improvements, and some changes that make secure configuration the default rather than something developers need to remember to enable.
Let’s take a look at what’s coming.
Partial Function Application: less boilerplate, better composition
If there’s one feature I’d encourage PHP developers to experiment with, it’s Partial Function Application.
PFA allows us to call a function with some arguments supplied and leave placeholders for the arguments we want to provide later. Rather than executing the function immediately, PHP returns a closure that can be invoked when the remaining arguments are available.
Consider a familiar example:
$normalise = fn string $value: string strtolowertrim$valueThere’s nothing wrong with this. Arrow functions are concise, and PHP developers use them constantly when working with callbacks.
But what if we want to reuse existing functions without wrapping them in another closure?
PHP 8.6 allows us to do exactly that:
$trim = trim?)
$lowercase = strtolower?)The ? indicates where an argument will be supplied later. These expressions produce callable closures, effectively giving us the same behaviour as writing the corresponding arrow functions ourselves.
This becomes particularly useful when working with higher-order functions such as array_map():
$messages = array_maptrim?) $messages)Previously, we’d typically write:
$messages = array_map
fn string $message: string trim$message
$messages
It’s a small difference in isolation, but it becomes much more significant when composing several operations or working with functions that accept multiple arguments.
PFA with Result
This is where I think PFA gets genuinely interesting.
I’ve been experimenting with functional programming patterns in PHP using the gosuperscript/monads package. It provides Option, Result, Lazy and Writer types, giving us a nice way to compose operations while keeping error handling explicit.
Imagine we’re processing an email address supplied by a user.
We want to trim whitespace, normalise the casing, and validate the result. If validation fails, we want to return an error rather than continue processing an invalid value.
Using Result, we can express that as a pipeline:
use function SuperscriptMonadsResultOk Err
function validateEmailstring $email: Result
return filter_var$email FILTER_VALIDATE_EMAIL
? Ok$email
: Err'Invalid email address'
$result = Ok$input
->mapfn $email trim$email
->mapfn $email strtolower$email
->andThenfn $email validateEmail$emailEach operation receives the successful value from the previous step. If one returns an Err, subsequent map() and andThen() operations are skipped.
With PHP 8.6, we can remove the wrapper closures:
$result = Ok$input
->maptrim?))
->mapstrtolower?))
->andThenvalidateEmail?))That’s a much nicer expression of intent. We’re not interested in the mechanics of passing a value into trim() or strtolower(); we simply want to apply those functions to the value held by the Result.
This is the kind of improvement that makes functional composition feel much more natural in PHP.
PFA with Lazy
PFA also works particularly well alongside lazy evaluation.
The Lazy type in gosuperscript/monads allows us to defer an expensive computation until its result is actually required. It also memoises the result, so subsequent evaluations don’t repeat the work.
Consider a report-generation function:
function generateMonthlyReport
string $month
int $tenantId
: Report
We could create a lazy computation like this:
use SuperscriptMonadsLazyLazy
$report = Lazy::of
generateMonthlyReport$month $tenantId ...
The trailing ... here is part of PHP’s partial function application syntax. Because we’ve supplied the actual arguments and left the remaining argument list open, PHP creates a zero-argument callable.
Lazy::of() can hold that callable without executing the report generation immediately.
When we eventually need the report:
$data = $report->evaluateThe computation runs, and its result is cached for subsequent calls to evaluate().
This is a useful example of how PHP 8.6’s language features can complement existing libraries. PFA isn’t replacing Lazy or Result; it’s making the functions we pass into them easier to express.
For me, that’s the real value of this feature: less glue code between the abstractions we’re already using.
clamp(): a small addition with everyday value
PHP 8.6 also introduces a native clamp() function.
Clamping a value means ensuring it falls within a specified range. If it’s below the minimum, it returns the minimum. If it’s above the maximum, it returns the maximum. Otherwise, it returns the original value.
Previously, we’d commonly implement this using min() and max():
$quantity = max1 min$quantity 100PHP 8.6 makes the intention much clearer:
$quantity = clamp$quantity 1 100This is useful in all sorts of everyday scenarios: pagination limits, volume controls, percentage calculations, configuration values and form input.
It also provides native validation around the bounds, rather than relying on developers to get the ordering right.
It’s not a transformative feature, but it’s a good example of PHP gradually absorbing common patterns that developers have otherwise had to implement themselves.
Time\Duration: a better way to represent elapsed time
PHP’s DateTime functionality is already one of its strengths, but there’s a distinction worth making between calendar-based intervals and elapsed time.
DateInterval is useful when dealing with calendar concepts: adding a month, moving a date forward by a week, or calculating a period between two dates.
But sometimes we simply want to represent a duration of time.
PHP 8.6 introduces Time\Duration, providing a dedicated representation for durations with nanosecond precision.
That gives developers a more appropriate abstraction for elapsed-time calculations, rather than having to bend calendar-oriented date APIs to fit the problem.
This is particularly relevant when working with performance measurements, timeouts, scheduling, and systems that need to represent precise durations.
It’s also an encouraging sign of continued investment in PHP’s time and date APIs.
Stream API improvements
The Stream API has been part of PHP for a long time. It’s used throughout the language, from file handling to network communication.
However, it’s fair to say that parts of the API have accumulated complexity over the years.
PHP 8.6 brings several improvements that help modernise this area.
Polling API
One of the more interesting additions is a polling API.
Polling provides a mechanism for checking multiple events or resources and determining when they’re ready for further processing. It’s a useful building block for things such as timers, signal handling and asynchronous execution.
This isn’t a new async programming model for PHP applications, and developers shouldn’t mistake it for a complete replacement for existing event-loop libraries.
What it does provide is groundwork within PHP itself for more capable event-driven functionality in the future.
Improved stream error handling
Stream errors haven’t always been particularly pleasant to work with. Depending on the operation, developers have had to deal with warnings, return values and error handling that can feel inconsistent.
PHP 8.6 introduces improvements to stream error handling, including more consistent error modes and exception-based handling.
The changes are opt-in, which is important for compatibility with existing applications.
I expect this to be particularly useful for library authors. Better error handling at the runtime level makes it easier to build predictable abstractions around network and file operations.
TLS session resumption
PHP 8.6 also introduces TLS session resumption improvements.
Establishing a TLS connection involves a handshake, which adds overhead to network communication. Session resumption allows a subsequent connection to reuse previously established session information rather than always performing a full handshake.
For applications making repeated secure network requests, this can reduce connection overhead.
It’s not the sort of feature that will change how most developers write PHP, but it is a worthwhile runtime improvement.
Security-conscious defaults
One of the most welcome changes in PHP 8.6 is the move towards more secure session defaults.
Historically, developers have needed to configure several session settings explicitly to get sensible security behaviour.
PHP 8.6 changes the defaults for three important settings:
Setting | New default |
session.use_strict_mode | 1 |
session.cookie_httponly | 1 |
session.cookie_samesite | Lax |
These changes help protect against session fixation, prevent JavaScript from reading the session cookie, and restrict when browsers send session cookies in cross-site requests.
This is a good direction for PHP.
Secure configuration should be the starting point, not an optional hardening exercise that every application team needs to remember to perform.
There is one important migration consideration: applications relying on cross-site POST requests carrying the session cookie, such as some SSO and SAML flows, may need explicit configuration.
As always, test authentication and payment flows carefully when upgrading.
Smaller improvements worth knowing about
Not every useful change needs its own headline feature.
PHP 8.6 includes a collection of smaller improvements that should make everyday development a little more predictable.
Readonly property defaults
Readonly properties can now have default values. This is particularly useful for immutable classes implementing get-only interface properties, where previously a constructor assignment was needed simply to establish a fixed value.
final readonly class CsvExport
public string $extension = 'csv'
public string $contentType = 'text/csv'
Override attributes on class constants
The #[\Override] attribute can now be used with class constants, making it possible to explicitly identify constants that intentionally override inherited definitions.
SortDirection
A new SortDirection enum provides a more consistent way to represent ascending and descending sort directions in application code.
mysqli_quote_string()
The mysqli extension gains mysqli_quote_string(), providing a native quoting function for SQL string values.
More useful error messages
An opt-in error_include_args setting allows function arguments to be included in error messages. This can make debugging easier, while respecting #[\SensitiveParameter] to avoid exposing sensitive values.
Other behavioural changes
There are also some changes worth checking during an upgrade:
trim(),ltrim()andrtrim()now strip form-feed characters by default.Several functions now perform stricter checks around NULL bytes.
preg_grep()returns false on a PCRE error rather than returning a partial result.Mbregex support is being removed, following the end of maintenance for its underlying Oniguruma dependency.
These aren’t necessarily changes you’ll notice immediately, but they are worth including in an upgrade checklist.
What does PHP 8.6 mean for the future?
PHP 8.6 isn’t a release that attempts to reinvent the language. Instead, it feels like a release that is making PHP more capable at the things modern developers increasingly want to do.
PFA is the standout example. Combined with first-class callables and the pipe operator, it makes functional programming patterns more approachable without requiring developers to abandon the object-oriented approaches that PHP has always supported.
The Stream API improvements are similarly interesting. They don’t deliver a complete asynchronous runtime, but they improve the foundations on which future capabilities can be built.
And the smaller improvements matter too. Better defaults, more useful error messages, more expressive types and less boilerplate all contribute to a better developer experience.
I don’t think PHP needs to become another language to remain relevant. Its strength has always been its flexibility: it can support traditional object-oriented applications, functional programming, and everything in between.
PHP 8.6 continues that evolution.
With general availability currently scheduled for 19 November 2026, there’s still time to experiment with the release candidates, review compatibility, and start identifying where the new features could benefit existing applications.
My recommendation? Start with Partial Function Application. Find a few places where you’re currently writing trivial arrow functions just to adapt an existing function to a callback, and see how much cleaner they become.
It’s a small change in syntax, but it points towards a more expressive future for PHP.
Want foundations like these in your codebase?
An architecture review gives you a written, prioritised plan. A foundations engagement puts it in place with your team, not for them.