On 18/05/2026 09:33, Seifeddine Gmati wrote:
> Behind the jargon, [...] All of the rest is just "a syntactic tier
that PHP parses but ignores".
Two pieces missing from that framing:
Reflection metadata. The pre-erasure form is exposed as introspectable
data. Frameworks, DI containers, ORMs, serializers can read generic
information directly from the engine rather than reparsing source.
It's first-class structured data that the language exposes.
I'd vaguely included this under "provides syntax for SA tools to hang
their own semantics onto".
However, this reflection visibility is exactly the part I was thinking
about how to provide with a dedicated syntax.
The bound-as-runtime-type substitution. `class UserCollection extends
Collection<User>` substitutes T = User into every T-typed parameter
and return; those positions become User-typed at runtime, enforced by
the engine. That's real type-checking work.
Gotcha. Am I right in thinking this part is effectively the same as the
"monomorphized generics" in Larry & Gina's blog post?
The `~~Type` proposal raises two questions I don't see clean answers to:
1. *Lexical grammar inside `~~`?* Whitespace breaks conditional types
(`$var is Foo ? A : B`). `$` breaks variable-using expressions. `{`
breaks array shapes. `<` breaks generics. Every plausible stopping
rule breaks a class of type expressions SA tools currently support.
I did say this was straw man syntax :) However, I don't think there's
any problem with nesting brackets - I'm not aware of any restriction on
using [] array literals inside #[] attributes, for instance. Perhaps
this is really the same as the next question...
2. *AST shape?* Either an opaque string (tools still parse it
themselves, still disagree, gain over docblocks is just location) or
structured (PHP has committed to an AST for type expressions it
doesn't enforce, which is the architectural pattern you wanted to avoid).
My thinking was that the general syntax would be completely parsed, and
put into a suitable structure in reflection, but without any semantic rules.
The minimum needed for generics would be:
- all existing types, including namespace and alias resolution
- an additional name resolution rule for generic type parameters, so
that "T" inside "class Foo<T> {" doesn't get expanded to a
namespace-qualified name
- a rule for type<type list>, allowing any valid type both inside and
outside
Parse that into a general-purpose reflection structure, suitable not
only for generics like "Foo<T>", but also things like "array<int>"
Add a few more rules, and you can cover maybe 90% of the extra types
PHPStan lists here: https://phpstan.org/writing-php-code/phpdoc-types
- string and numeric literals
- constant and class constant references
- unexpanded pseudo-types, which could be "any name with at least one
hyphen", similar to Custom HTML Elements
That would parse, for example, "non-empty-list<negative-number>",
"int<1,100>", "class-string<T>", and "value-of<Type::ARRAY_CONST>"
The next obvious thing to add would be array shapes - again, just
standardising and parsing the syntax, and creating a stable reflection
API which tools could use.
The syntax marker would just be to alert users that this was a different
category of type information, which couldn't be relied on without
external tooling.
--
Rowan Tommins
[IMSoP]