On Wed, May 13, 2026, at 12:58 PM, Rowan Tommins [IMSoP] wrote:
On 13 May 2026 17:19:21 BST, Seifeddine Gmati <azjezz@carthage.software> wrote:
This is an assumption that a lot of your reasoning seems to be based on, and as I've said already, I think it's a false assumption.
The PHP 7.0 type-declaration rollout is the closest empirical test.
Native scalar types shipped to a community that had been using PHPDoc
@param and @return annotations for years. They didn't suddenly create
a new population of developers who type their code, some popular
projects argued the new types were useless and refused to adopt them.
The audience that gained native syntax was the audience that had
already been typing their code. People who didn't see value in types
before PHP 7.0 mostly didn't see value after, either.
I don't think this is true at all. Users were writing "array", and
class/interface types, in their code for many years before PHP 7.0,
*and having them natively enforced*. Most of those users had never
heard of static analysers, but as soon as static types became
available, using them was entirely natural.
If you survey current code bases, I bet you a drink of your choice that
you will find code bases with some use of scalar types outnumbering
code bases which have been tested with a Static Analyser by an order of
magnitude.
The same dynamic will apply here. People who don't care about generic
type information today won't suddenly care because PHP grew the
syntax. The audience that uses @template in docblocks is the audience
that will use native generics.
Of course they will, because it will suddenly be much more visible.
Every "what's new in PHP" blog post will describe the new syntax, and
people will start playing with it. People who see docblocks as "just
documentation" will see frameworks and libraries putting it in "actual
types" and copy it into their own code.
And this is a good thing! We *want* new language features to be
interesting to more people. But we also want them to be *useful* to
those people, not documented in the manual as "advanced users only; if
you're the target audience for this, you probably don't need this page".
Attributes themselves are a counter-example. They shipped in PHP 8.0
specifically to formalize what frameworks had been doing in docblocks.
Attributes are very explicitly an *abstract extension point* for
tooling to do what it wants with. PHP does not attempt to standardise
their use; it doesn't even validate that attribute names correspond to
valid classes unless you ask it to. PHP provides some attributes out of
the box, but only when it also includes some *behaviour* for those
attributes.
In the same way, PHP provides the ability to define interfaces. It also
provides interfaces to interact with included features, like
SessionHandlerInterface. But it leaves it up to the community to agree
interfaces for things that are not included with the language, like
LoggerInterface or CacheInterface.
That's not the same as what you're proposing.
Beyond that, expressing generics through attributes specifically
doesn't work.
Fair enough. My point stands though: the language should provide
abstract extension points, or working implementations, not empty syntax.
I fully echo Rowan's comments. The assumption that the Venn diagram of "uses generics" and "uses PHPstan/Psalm" is a circle is presented without compelling evidence, and I don't believe it to the be case today. Even if it were the case today, I am quite confident it would not remain so for long with this RFC.
Because this RFC is providing some validation (in practice, I think it is providing most by number; it's just the turbofish that doesn't yell at you natively), users will run into generics validation messages at some point (just as they do any other syntax error). It will be confusing when some things self-validate and others do not, especially when PHP itself starts shipping stdlib code that uses generics.
Consider, if I read the RFC correctly this will (correctly) error at link time:
interface Foo<T: DateTimeInterface> {}
class Bar implements Foo<User> {}
So that part of the type system is enforced. It will therefore be natural for users to expect that
new Baz<User>(new Product $p);
will error on them. I fully get the reasons why that's way harder to do than the former. As I said, I could even be talked into accepting it for now, with appropriate communication around it, as long as there is a clear path toward enforcing it later. There's "enough" enforcement that it's not quite as bad as Python...
And basically everyone who uses PHP is going to encounter generics sooner or later. As I noted above, PHP *will* start shipping stdlib code that uses generics. Gina's work on generics was specifically intended to aid her previous work on Fetch API interfaces, to split ArrayAccess up into properly atomic features. Those interfaces naturally benefit from being generic, and would be implemented as such. So that means literally everyone who uses them in the future would encounter generics, and in that case, it would be enforced (at compile/link time) already. Similarly, if this passes, it will be about 4 seconds before I start working on Seq, Set, and Dict collection classes for stdlib (longer if I have trouble finding a partner for it), and those would unquestionably be generic. And that's just two obvious examples. Generics would become as much a part of the standard vocabulary of PHP developers as scalar types and interfaces. If they don't, then we've failed. 
But count me out for opt-in enforcement. We either enforce it or we don't. The reason strict_types exists is, largely, the billion lines of pre-existing code hat was effectively "loose" already, and people not wanting to have to fight that. Let's not create another of those situations. If call-site enforcement can be made cheap enough to include, it's cheap enough to *always* include. If we want to add a "disable type checks in prod" flag, that would be an ini-setting, not a code-level setting, and should be blanket for all types. It's also a completely separate question from this RFC.
Which is why I previously suggested some sort of AOT first-party checker, as a way to help ensure that whenever we do manage to add automatic enforcement, it's a minor speed bump for existing code, not a massive pseudo-BC-break, like warnings on undefined keys was.
There's also the elephant in the room that the proposal doesn't remove
the need for standardising a docblock or attribute approach anyway,
because it is inevitably going to miss things the SA tools support:
class-string<T>, array<int,Foo>, iterable<Bar>, non-empty-string, ...
That again follows from it not being an abstract extension point like
docblocks and attributes.
Advanced users, who you say are the target audience, will still have to
work with both syntaxes; and will still find differences between tools
which aren't covered by the subset of validation that PHP has taken
ownership of.
This is also a very valid point worth calling out. One of my more common usage patterns today is the Doctrine ORM example in the RFC (with class-string<T>). I'm still now sure how, or if, the RFC syntax would handle that case.
Is there even a viable future way to include such more-complex checks natively in the future? For some, the answer is "add that feature to PHP, duh" (like array<int, Foo>, or preferably Dict<int, Foo>), and I have some ideas for non-empty-string that people probably won't like :-), but I'm not sure what to do with class-string<T> or other more esoteric examples.
I really want to like this RFC, and I really want generics. This is likely the most viable approach that's been put forward to date. But I am still very, very nervous about the land-mines it lays in front of us if we're not careful.
--Larry Garfield