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]

Reply via email to