Sitemap

PHP Generics Are Back on the Table-And This Time the Discussion Has Changed

9 min readMay 14, 2026

When I published Almost Generics in PHP, the focus was the PHP Foundation’s August 2025 compile-time generics proposal.

That proposal was important because it gave the PHP community something concrete to discuss again: not just “PHP should have generics one day”, but an actual design direction with trade-offs, limitations, and implementation ideas.

Since then, the discussion has moved forward.

As of May 2026, there is a new RFC under discussion: Bound-Erased Generic Types. It is not the same as the earlier compile-time-only proposal. It is broader, more ambitious, and closer to the kind of native generic syntax many PHP developers have wanted for years. The RFC proposes generic type parameters for classes, interfaces, traits, functions, methods, closures, and arrow functions. It also introduces bounds, defaults, variance markers, optional type arguments at call sites, Reflection support, and a runtime model based on bound erasure.

That does not mean PHP has generics today.

It does mean the conversation is no longer theoretical.

Interested in the book? Buy it here: https://dogan-ucar.de/almost-generics/

Almost Generics in PHP: A Guided Journey Through the Compile Time Proposal

Why PHP Developers Keep Asking for Generics

If you have worked on a serious PHP codebase, you already know the problem.

  • You write a repository that returns users.
  • You write a collection that contains products.
  • You write a message bus that handles specific message types.
  • You write a DTO mapper, a validator, a result object, or a service locator.

And sooner or later, PHP’s native type system runs out of vocabulary.

You can say:

public function findAll(): array

But you cannot say, in native PHP today:

public function findAll(): array<User>

You can write:

final class Collection
{
public function first(): mixed
{
// ...
}
}

But PHP itself cannot express:

Collection<User>

So we use PHPDoc.

/**
* @template T
*/
final class Collection
{
/**
* @return T
*/
public function first(): mixed
{
// ...
}
}

This works surprisingly well with tools like PHPStan, Psalm, Phan, Mago, and IDE support. In fact, the new RFC explicitly points out that many major PHP projects already use generic information in PHPDoc, including Laravel, Symfony, Doctrine, API Platform, PHPUnit, and others. The point is not that generics are foreign to PHP. The point is that PHP developers already use them — just not in the language itself.

That is the real tension.

Generics already exist in modern PHP development, but they live in comments.

The Problem With Generics in Comments

PHPDoc generics are useful, but they have structural weaknesses.

A static analyzer can understand:

/**
* @template T of UserInterface
*/
interface UserProvider
{
/**
* @return T
*/
public function loadUser(): UserInterface;
}

But PHP itself only sees:

interface UserProvider
{
public function loadUser(): UserInterface;
}

That means the most important part of the type relationship is invisible to the language.

This creates several problems:

A typo in a docblock may silently weaken your type system.

A refactor may update PHP code but miss generic type references inside comments.

Reflection cannot expose the generic type structure in a first-class way.

Different tools may interpret edge cases differently.

And perhaps most importantly: developers have to read two type systems at once — the native PHP signature and the PHPDoc layer above it.

The RFC makes this point very directly: the ecosystem has converged on generic patterns, but the language has not caught up.

What Changed Since the August 2025 Proposal?

The August 2025 PHP Foundation post explored compile-time generics as a possible direction. The main motivation was clear: generics are heavily requested, but runtime generics in PHP have historically been difficult because of performance and implementation complexity.

That earlier direction was intentionally scoped.

The new May 2026 RFC is different.

It proposes bound-erased generics.

In simple terms: PHP would allow generic syntax, but at runtime the type parameter is erased to its declared bound, or to mixed if there is no bound.

For example:

final readonly class Box<T>
{
public function __construct(
public T $value,
) {}
}

At the source-code level, a developer can write:

$box = new Box::<string>("hello");

But at runtime, Box<string> and Box<int> are not separate generated classes. They are the same class. The generic information is available to tooling and Reflection, while the runtime model avoids the full cost of reified generics.

The RFC describes enforcement as split across compile time, link time, and runtime. Compile time validates syntax and some generic constraints. Link time validates inheritance-related rules. Runtime validates arity and bounds at turbofish sites such as calls and new. The RFC describes the design as mostly enforced at runtime and fully enforced at the static-analysis layer.

  • This is an important distinction.
  • It is not “comments, but prettier”.
  • It is also not “Java generics copied into PHP”.
  • It is a PHP-specific compromise.

Why Bound Erasure Matters

There are three broad ways to think about generics.

One model is reified generics, where type arguments exist at runtime. This gives the runtime full knowledge of Collection<User> versus Collection<Product>, but it also creates complexity and cost.

Another model is monomorphization, where the compiler generates specialized versions of generic code for each type. This can be powerful, but it is not a natural fit for PHP’s traditional runtime model and deployment workflow.

The third model is erasure, where generic type information is used for checking and tooling, but not fully represented as separate runtime types.

The new RFC chooses a bound-erased model.

That means this:

final class Repository<T of Entity>
{
public function find(int $id): ?T
{
// ...
}
}

has meaningful generic information for static analyzers and Reflection, while the runtime still works with the declared bound.

This matters because PHP does not have the same build-step culture as TypeScript or many compiled languages. PHP code is usually the code that runs. You do not normally transpile your application before every deployment just to make the language usable.

That is why userland preprocessor approaches never became the mainstream answer. They can demonstrate syntax, but they cannot integrate cleanly with every IDE, debugger, analyzer, test runner, framework, and deployment process. The RFC spends quite some time explaining why unofficial syntax is not enough for PHP.

The Most Interesting Part: Migration From PHPDoc

The most practical part of the new RFC is not the syntax itself.

It is the migration path.

Today, many libraries already write something like this:

/**
* @template T of object
*/
interface Repository
{
/**
* @return T|null
*/
public function find(int $id): ?object;
}

A native version could become:

interface Repository<T of object>
{
public function find(int $id): ?T;
}

That is easier to read.

It is easier to refactor.

It is visible to the parser.

It is available to Reflection.

And it removes the split between “what PHP sees” and “what the analyzer sees”.

The RFC explicitly states that native generic syntax and @template annotations may coexist in the same codebase, and that tools understanding both forms can read either. That is important for incremental adoption. Nobody wants a flag day where every library and application has to migrate at once.

What About PHP 8.5?

PHP 8.5 has already shipped, and it did not ship generics.

It did, however, continue PHP’s broader direction toward a more expressive language. PHP 8.5 introduced features such as the URI extension, the pipe operator, clone-with syntax, #[\NoDiscard], and support for closures and first-class callables in constant expressions.

That matters because generics are not an isolated request.

They are part of a larger trend: PHP is becoming more explicit, more analyzable, and more comfortable with stronger typing.

The gap is that collections, repositories, factories, processors, handlers, and result objects still cannot express their type relationships natively.

That is where generics fit.

Why This Matters for Real Projects

Generics are sometimes discussed as if they were an academic feature.

They are not.

They matter when you build large systems.

They matter when your codebase has hundreds of services, repositories, DTOs, messages, handlers, and framework integrations.

They matter when a method returns “an array”, but everyone knows it should be “a list of Invoice objects”.

They matter when a factory accepts a class name and returns an instance of exactly that class.

They matter when a pipeline transforms one type into another and you want the type system to preserve that relationship.

Without generics, you either accept mixed, duplicate classes, or rely on PHPDoc.

With generics, you can express the relationship directly:

/**
* Today:
*
* @template T of object
* @param class-string<T> $className
* @return T
*/
public function make(string $className): object
{
// ...
}

Possible native direction:

public function make<T of object>(class-string<T> $className): T
{
// ...
}

The second version says what the code means.

That is the value.

My View: PHP Should Not Wait for the Perfect Generics Model

There is a recurring argument in PHP generics discussions:

If the first version is not perfect, should PHP ship it at all?

I understand the concern.

A bad type-system feature can live forever. PHP has a long memory. Once syntax is accepted, it is difficult to change.

But waiting for the perfect model also has a cost.

PHP developers have already been using generics through static analysis for years. The ecosystem already depends on @template. The question is no longer whether PHP developers want generics. They already voted with their code.

The real question is whether generic type information should remain trapped in comments forever.

I do not think it should.

A pragmatic native model, even if limited, can still be a major improvement if it gives us:

native syntax,

parser validation,

Reflection support,

tooling consistency,

a migration path from PHPDoc,

and a foundation for future improvements.

The current RFC also explicitly leaves room for future work, including body-level monomorphization, opt-in reified generics, where clauses, higher-kinded parameters, and inference improvements.

That is the right framing.

Do not solve every possible generics problem in one RFC.

But do create a solid language-level foundation.

Should You Care Today?

Yes — but with the right expectation.

You should not rewrite your production code for a feature that has not shipped.

You should not assume the current RFC will pass unchanged.

You should not sell your team on native generics as if they are already part of PHP.

But you should understand the direction.

Because if PHP gets native generics, the way we design libraries, repositories, collections, framework contracts, and domain abstractions will change.

Not overnight.

But steadily.

The developers who already understand the trade-offs will be faster at adopting the feature correctly.

The developers who only see the syntax later will have to catch up.

I Wrote a Short Book About This

I wrote Almost Generics in PHP for exactly this reason.

Almost Generics in PHP: A Guided Journey Through the Compile Time Proposal
Almost Generics in PHP: A Guided Journey Through the Official Compile-Time Proposal

The book explains the background, the design space, the trade-offs, and the practical patterns behind PHP generics. It started from the PHP Foundation’s compile-time generics proposal, but the deeper value is broader: it helps PHP developers understand why generics are hard, why they matter, and what kind of code they make possible.

It covers:

  • the history of generics in PHP,
  • why previous attempts struggled,
  • compile-time generics,
  • monomorphization,
  • type bounds,
  • invariance,
  • static analysis,
  • testing generic abstractions,
  • design pattern impact,
  • and the open questions the community still needs to answer.

The topic has moved forward since the first announcement, but that makes the book more relevant, not less. The syntax may evolve. The RFC may change. But the underlying concepts are the same concepts every PHP developer will need to understand if generics finally land.

You can get the PDF here:

Buy Almost Generics in PHP

The PDF is currently available for €17.99 and is delivered by email. There is also an Amazon version for Kindle and paperback readers.

Final Thought

PHP generics are no longer just a wishlist item.

They are an active design discussion with real RFC work, real implementation work, and real ecosystem pressure behind it.

Maybe this RFC passes.

Maybe it changes.

Maybe it becomes the basis for the next iteration.

But the direction is clear: PHP’s type system is still evolving, and generics remain one of the most important missing pieces.

For PHP developers building serious systems, this is the moment to start understanding the topic properly.

Not when generics ship.

Before they ship.

--

--

Doğan Uçar
Doğan Uçar

Written by Doğan Uçar

Software Engineer, PHP/Laminas (Zend), Backend, Cloud, Freelancer & CEO, Open Source Contributor