I recently read the goal statement of Rhombus, a new programming language built on Racket. It opens with a question every language designer must answer:
Modern programming languages reflect a consensus on the most important programming concepts, including lexically scoped variables, closures, objects, pattern matching, and type parametricity. Why, then, yet another programming language?
Beyond the basics, there are still more good ideas for programming constructs than can fit in any one language specification. Furthermore, specific domains benefit from language support that is tailored to the domain. Language extensibility helps to balance the competing goals of a manageable language size versus fit-to-purpose for a wide range of tasks.
I found this framing useful: not for evaluating the new language, but for evaluating the one I have used since 1998. PHP is 31 years old. I do not regret choosing it as my primary language, and this article is not a defence of that choice.
Defences concede the frame: they accept that PHP must justify its existence against experiences people had with it twenty years ago, and then spend two thousand words rebutting complaints about PHP 4. Warrick Bayman explained years ago why that genre is exhausted. I will not add to it.
Instead, let us take the checklist above and ask a more interesting question: how does PHP in 2026, with PHP 8.5 released last November and PHP 8.6 due this November, measure up against the consensus concepts of modern language design? And what does its answer to the extensibility question look like?
Rhombus's checklist
Lexically scoped variables and closures
PHP's closures are lexically scoped, with a design decision that still distinguishes it from most languages: capture is explicit. A function () use ($x) closure names what it captures; an arrow function fn () => $x captures automatically, but by value. You always know what crosses the boundary.
Since PHP 8.1, any function or method can be lifted into a closure with first-class callable syntax. PHP 8.5 extended closures into constant expressions, such as attribute arguments as well as property and parameter defaults, and added the pipe operator |>.
PHP 8.6 completes the picture with partial function application: strlen(?) produces a closure with a hole in it, and together with |> this gives PHP genuine function composition. The same release optimises the implementation underneath: closures that do not use $this are inferred as static, and fully stateless closures are cached between uses.
That is the order in which a language takes a concept seriously: first the semantics, then the ergonomics, then the performance.
Objects
The object system is, by some distance, the most complete part of the language. Enumerations, readonly properties, constructor promotion, property hooks and asymmetric visibility (PHP 8.4), clone with and final promoted properties (PHP 8.5), #[\Override] extended to class constants (PHP 8.6).
A class written in modern PHP expresses its intent in the declaration itself, checked by the engine at runtime: what is immutable, what is computed, what may be replaced by a subclass. Anyone whose idea of PHP objects was formed in the days of var $foo would not recognise the language.
Pattern matching
Half a point today, but with a concrete trajectory.
match has been an expression with strict comparison semantics since PHP 8.0, and it eliminated an entire class of switch bugs. But it matches values, not structures: no destructuring, no binding, no exhaustiveness over shapes.
That gap is now being addressed by the Pattern Matching RFC by Larry Garfield and Ilija Tovilo, which is under discussion with a working implementation. It introduces is as a comparison operator over patterns:
- Type patterns:
$value is int|float - Object patterns with property matching and variable binding:
$p is Point(x: 3, y: $y) - Array shapes:
$request is ['action' => string, 'id' => int, ...]
With match ($value) is { ... } we will have a structure that allows us to combine these patterns and shapes.
The RFC is explicitly a stepping stone towards algebraic data types (enumerations with associated values), which would close the distance to the consensus entirely. It missed the PHP 8.6 feature window; I assume it will ship with PHP 8.7 in 2027. Until then, this remains the concept where PHP is furthest behind, and it is worth saying so plainly, while also saying that the remaining distance is now measured in one release cycle, not in principle.
Type parametricity
The honest answer is no, and the interesting answer is more complicated.
PHP has no generics in the language, and the internals community reaffirmed this recently when a proposal for bound-erased generic types was declined for PHP 8.6. It is worth understanding why it was declined, because the reason is not indifference to generics. As Gina Banyard argues, the objection is to erasure, not to parametricity.
Every type declaration PHP has ever gained is checked at runtime, from the class types of PHP 5.0 through the scalar types of PHP 7.0 to today, and introducing native syntax that silently is not checked would break that invariant, because most developers, when shown the syntax, assume it is enforced. Erasure works, for example, in languages such as Java and TypeScript. These languages combine this feature with a mandatory, separate compilation step that checks the types before execution begins.
PHP has no such step, and the premise that "everyone uses static analysis anyway" does not survive contact with reality: the JetBrains Developer Ecosystem Survey puts PHPStan and Psalm adoption at, charitably combined, well under half of PHP developers. Declining erased generics was a vote for coherence.
The path Banyard sketches instead, gradual runtime-checked generics that start with invariant type parameters on interfaces such as ArrayAccess<int, Animal> and for which she has a working prototype, is exactly how PHP's type system has always grown: one checked increment per release, from class types in PHP 5.0 to DNF types in PHP 8.2.
Meanwhile, every non-trivial PHP codebase I encounter already uses generics: list<Order>, array<string, Money>, Collection<T>, template covariance, conditional types. They live in a type language that the PHP interpreter does not define and does not check: the docblock dialect interpreted by PHPStan and Psalm, enforced in CI, understood by every IDE. And for the most common use case, type-safe collections, you do not even have to wait for generics.
Extensibility, accidentally
Which brings me back to Rhombus. Its answer to "why yet another language?" is extensibility, a credible answer from that particular corner of the world, since Rhombus builds on Racket, the most radically extensible language ecosystem in existence: keep the specification small, let users extend it towards their domain. PHP has no macros, no user-defined syntax, no extensible reader. By Rhombus's criteria, it should be stuck choosing between a bloated specification and poor fit-to-purpose.
It escaped through a side door. The static analysis type language is PHP's extensibility mechanism: a full, expressive, user-space type system layered on top of the runtime's gradual types, evolving at the release cadence of tooling rather than the release cadence of a language specification with a backward-compatibility promise.
One can lament that the "real" type system of professional PHP is a convention between third-party tools. One can also notice that this arrangement delivered generics, shape types, and refinement types to millions of developers without a single RFC vote, and without making the engine slower or the language larger.
And the arrangement works precisely because the boundary is honest: a docblock visibly makes no runtime claim, while erased native syntax would make the same non-claim disguised as one, which is the coherence argument on which bound-erased generics foundered.
The two layers succeed by not pretending to be each other. Attributes are the second seam: a sanctioned metaprogramming hook through which frameworks attach domain semantics such as routing, validation, and object-relational mapping without new syntax. The specification stayed manageable; the fit-to-purpose came from outside it.
The domain commitment
Rhombus's goal statement makes a second claim: specific domains benefit from language support tailored to the domain. PHP is the strongest existing evidence for that claim, because it made the opposite trade-off to extensibility: it committed to one domain and baked it into the platform.
The shared-nothing, request-scoped execution model remains PHP's most underrated property. Every request starts from a clean state and ends in oblivion: fault isolation per request, horizontal scaling as the default, no slow accumulation of corrupted state, and memory management that mostly reduces to "the request ends". These are properties other ecosystems assemble from frameworks, conventions, and operational discipline. In PHP they are the floor. And the platform keeps deepening that commitment.
PHP 8.5 shipped a new URI extension with both RFC 3986 and WHATWG URL semantics: a genuinely hard correctness problem for anything that touches user-supplied URLs, now solved in the standard library.
PHP 8.6 follows up on it and hardens session configuration defaults, making the secure configuration the default configuration. The same release shows function arguments in error messages, building on PHP 8.5's fatal error backtraces.
Next to partial function application these sound minor, but for a language whose primary deployment context is production web workloads, error observability and secure defaults are domain support in exactly the sense Rhombus's designers mean.
And the deprecation track quietly pays down the technical debt of 1995: lossy casts such as (int) "123abc" are on their way out, one carefully voted RFC at a time, on an annual release cadence that has held for a decade.
This is what evolution under a backward-compatibility promise looks like: slower than a research language, faster than a standards committee, and predictable enough to plan an upgrade around.
Where this leaves us
Measured against Rhombus's checklist, PHP in 2026 scores four out of five: pattern matching at half credit but on a visible path to full marks, and type parametricity outsourced, for now, to a tooling layer that iterates faster than the language itself ever could, while internals works out how to add generics without erasing them.
Measured against the domain-fit criterion, it scores higher than languages far more admired: gradual typing at exactly the boundary where HTTP input is untyped anyway, an execution model shaped like the request/response structure of the domain, and a standard library that now includes correct URL parsing and secure session defaults out of the box.
None of this required me to argue that PHP is alive. It required only that we look at the language as it is, with the criteria language designers themselves consider essential, and describe what we find.
What we find is a 31-year-old language that adopted most of the consensus, found an unconventional answer to the extensibility question, and never wavered from its domain.
I have been writing PHP for 28 of those 31 years. The language I use today is better than the one I chose, and it is much, much better than the one most people who left remember.