[RFC Idea] Generic Collection Notation for Iterables (array<T>)

php.internals

Jordi Kroon

248 days ago
Hi everyone, I would like to propose a new type in PHP: Generic Collection Notation for Iterables (`array<T>`). **The problem** PHP provides several ways to represent sequences of values: - `array` - `iterable` - `Traversable` implementations - `generators` While these constructs are fundamental to PHP, none of them allow the developer to express **what type of values they yield or contain** at the language level. This leads to several issues: - Static analysis relies heavily on PHPDoc annotations. - Runtime type hints communicate structure but not intent. - APIs lack self descriptive type information. - Developers must duplicate type information across signatures and documentation. **Example** ```php class UserService { /** * @return User[] */ public function getAllUsers(): array { return [new User("Alice"), new User("Bob")]; } /** * @param User $user * @param Role[] $roles * @return void */ public function saveRoles($user, array $roles): void { // Some database call } } ``` **The proposal** Introduce parameterized iterable types using generic like syntax: `array<T>` `iterable<T>` `Traversable<T>` `Generator<T>` ```php function processUsers(array<User> $users): void { } function normalize(array<string> $values): array<string> { } ``` Optionally we could also extend the new array syntax with this functionality. ```php function processUsers(User[] $users): void { } function normalize(string[] $values): string[] { } ``` Key points: - The existing `array` type remains valid and unchanged. - `array<T>` means an array whose elements are of type `T`. - The syntax is currently invalid in PHP and therefore does not introduce a backward compatibility break. **Example** ```php class UserService { public function getAllUsers(): array<User> { return [new User("Alice"), new User("Bob")]; } public function saveRoles(User $user, array<Role> $roles): void { // Some database call } public function streamAllUsers(): Generator<User> { yield new User("Alice"); yield new User("Bob"); } public function iterateAllUsers(): ArrayIterator<User> { return new ArrayIterator([new User("Alice"), new User("Bob")]); } public function getUserProvider(): Traversable<User> { return new ArrayIterator([new User("Alice"), new User("Bob")]); } public function normalizeNames(iterable<string> $names): iterable<string> { foreach ($names as $name) { yield trim($name); } } } ``` **Runtime semantics and enforcement** The goal is that `array<T>` and related iterable parameterizations behave like other PHP types: if a value is passed or returned that violates the declared type, a `TypeError` is thrown. The main question is when and how enforcement occurs for eager versus lazy containers. - Checked on function argument receive, property assignment, and return. - Each element value is validated against `T`. There are two approaches, but only one gives strong runtime guarantees. Preferred behavior: - Validate on consumption when the iterable is iterated. - If a yielded element violates `T`, throw `TypeError` at the point of iteration. This mirrors the fact that `iterable` may be lazy and cannot be fully validated up front. **Implementation** I have basic C knowledge but I don't have much experience with Zend Engine / PHP Core. However I am fully committed to: - Write and maintain the RFC document. - Test and write tests for the implementation or draft - Participate in discussions - Collaborate with anyone willing to help with the implementation **Keys and additional parameters** This proposal focuses on element types. Keys are intentionally out of scope for the initial version. Possible follow up work could explore `array<TKey, TValue>` and `Generator<TKey, TValue>`. **Next steps** - I would like to have hear any potential caveats. For example regarding lazy loading. - If the RFC gains internal support, I will officially publish the first initial draft of the RFC. - Whether supporting the shorthand `T[]` should be part of the initial scope or a follow up - Whether supporting `TKey` should be part of the initial scope or a follow up Kind regards, Jordi Kroon

Jordi Kroon

248 days ago
On 27/12/2025 4:26 pm, Jordi Kroon wrote:
> Hi everyone, > > I would like to propose a new type in PHP: Generic Collection Notation > for Iterables (`array<T>`). > > **The problem** > PHP provides several ways to represent sequences of values: > > - `array` > - `iterable` > - `Traversable` implementations > - `generators` > > While these constructs are fundamental to PHP, none of them allow the > developer to express **what type of values they yield or contain** at > the language level. > > > This leads to several issues: > - Static analysis relies heavily on PHPDoc annotations. > - Runtime type hints communicate structure but not intent. > - APIs lack self descriptive type information. > - Developers must duplicate type information across signatures and > documentation. > > **Example** > ```php > class UserService > { >     /** >      * @return User[] >      */ >     public function getAllUsers(): array >     { >         return [new User("Alice"), new User("Bob")]; >     } > >     /** >      * @param User $user >      * @param Role[] $roles >      * @return void >      */ >     public function saveRoles($user, array $roles): void >     { >         // Some database call >     } > } > ``` > > **The proposal** > Introduce parameterized iterable types using generic like syntax: > `array<T>` > `iterable<T>` > `Traversable<T>` > `Generator<T>` > > ```php > function processUsers(array<User> $users): void { } > > function normalize(array<string> $values): array<string> { } > ``` > > Optionally we could also extend the new array syntax with this > functionality. > > ```php > function processUsers(User[] $users): void { } > > function normalize(string[] $values): string[] { } > ``` > > Key points: >  - The existing `array` type remains valid and unchanged. >  - `array<T>` means an array whose elements are of type `T`. >  - The syntax is currently invalid in PHP and therefore does not > introduce a backward compatibility break. > > **Example** > ```php > class UserService > { >     public function getAllUsers(): array<User> >     { >         return [new User("Alice"), new User("Bob")]; >     } > >     public function saveRoles(User $user, array<Role> $roles): void >     { >         // Some database call >     } > >     public function streamAllUsers(): Generator<User> >     { >         yield new User("Alice"); >         yield new User("Bob"); >     } > >     public function iterateAllUsers(): ArrayIterator<User> >     { >         return new ArrayIterator([new User("Alice"), new User("Bob")]); >     } > >     public function getUserProvider(): Traversable<User> >     { >         return new ArrayIterator([new User("Alice"), new User("Bob")]); >     } > >     public function normalizeNames(iterable<string> $names): > iterable<string> >     { >         foreach ($names as $name) { >             yield trim($name); >         } >     } > } > ``` > > **Runtime semantics and enforcement** > The goal is that  `array<T>`  and related iterable parameterizations > behave like other PHP types: if a value is passed or returned that > violates the declared type, a  `TypeError`  is thrown. > > The main question is when and how enforcement occurs for eager versus > lazy containers. > -   Checked on function argument receive, property assignment, and return. > -   Each element value is validated against  `T`. > There are two approaches, but only one gives strong runtime guarantees. > > Preferred behavior: > -   Validate on consumption when the iterable is iterated. > -   If a yielded element violates  `T`, throw  `TypeError`  at the point > of iteration. > > This mirrors the fact that  `iterable`  may be lazy and cannot be fully > validated up front. > > **Implementation** > I have basic C knowledge but I don't have much experience with Zend > Engine / PHP Core. However I am fully committed to: >  - Write and maintain the RFC document. >  - Test and write tests for the implementation or draft >  - Participate in discussions > - Collaborate with anyone willing to help with the implementation > > **Keys and additional parameters** > This proposal focuses on element types. Keys are intentionally out of > scope for the initial version. > > Possible follow up work could explore  `array<TKey, TValue>`  and > `Generator<TKey, TValue>`. > > **Next steps** >  - I would like to have hear any potential caveats. For example > regarding lazy loading. >  - If the RFC gains internal support, I will officially publish the > first initial draft of the RFC. >  - Whether supporting the shorthand `T[]` should be part of the initial > scope or a follow up >  - Whether supporting `TKey` should be part of the initial scope or a > follow up > > Kind regards, > Jordi Kroon
Yes, I am fully aware https://wiki.php.net/rfc/generic-arrays exists. But since it's older date I was not sure if I had to revive the original or start a new. That said since PHP is becoming a more strict language, it became more relevant. And the original RFC doesn't mention anything regarding Traversables or other iterables. Though I would like to give my sincere for the work that has been done for the current RFC proposal.

Ben Ramsey

248 days ago
On Sat, Dec 27, 2025 at 09:49 Jordi Kroon <jordikroon@me.com> wrote:
> On 27/12/2025 4:26 pm, Jordi Kroon wrote: > > Hi everyone, > > > > I would like to propose a new type in PHP: Generic Collection Notation > > for Iterables (`array<T>`). > > > > **The problem** > > PHP provides several ways to represent sequences of values: > > > > - `array` > > - `iterable` > > - `Traversable` implementations > > - `generators` > > > > While these constructs are fundamental to PHP, none of them allow the > > developer to express **what type of values they yield or contain** at > > the language level. > > > > > > This leads to several issues: > > - Static analysis relies heavily on PHPDoc annotations. > > - Runtime type hints communicate structure but not intent. > > - APIs lack self descriptive type information. > > - Developers must duplicate type information across signatures and > > documentation. > > > > **Example** > > ```php > > class UserService > > { > > /** > > * @return User[] > > */ > > public function getAllUsers(): array > > { > > return [new User("Alice"), new User("Bob")]; > > } > > > > /** > > * @param User $user > > * @param Role[] $roles > > * @return void > > */ > > public function saveRoles($user, array $roles): void > > { > > // Some database call > > } > > } > > ``` > > > > **The proposal** > > Introduce parameterized iterable types using generic like syntax: > > `array<T>` > > `iterable<T>` > > `Traversable<T>` > > `Generator<T>` > > > > ```php > > function processUsers(array<User> $users): void { } > > > > function normalize(array<string> $values): array<string> { } > > ``` > > > > Optionally we could also extend the new array syntax with this > > functionality. > > > > ```php > > function processUsers(User[] $users): void { } > > > > function normalize(string[] $values): string[] { } > > ``` > > > > Key points: > > - The existing `array` type remains valid and unchanged. > > - `array<T>` means an array whose elements are of type `T`. > > - The syntax is currently invalid in PHP and therefore does not > > introduce a backward compatibility break. > > > > **Example** > > ```php > > class UserService > > { > > public function getAllUsers(): array<User> > > { > > return [new User("Alice"), new User("Bob")]; > > } > > > > public function saveRoles(User $user, array<Role> $roles): void > > { > > // Some database call > > } > > > > public function streamAllUsers(): Generator<User> > > { > > yield new User("Alice"); > > yield new User("Bob"); > > } > > > > public function iterateAllUsers(): ArrayIterator<User> > > { > > return new ArrayIterator([new User("Alice"), new User("Bob")]); > > } > > > > public function getUserProvider(): Traversable<User> > > { > > return new ArrayIterator([new User("Alice"), new User("Bob")]); > > } > > > > public function normalizeNames(iterable<string> $names): > > iterable<string> > > { > > foreach ($names as $name) { > > yield trim($name); > > } > > } > > } > > ``` > > > > **Runtime semantics and enforcement** > > The goal is that `array<T>` and related iterable parameterizations > > behave like other PHP types: if a value is passed or returned that > > violates the declared type, a `TypeError` is thrown. > > > > The main question is when and how enforcement occurs for eager versus > > lazy containers. > > - Checked on function argument receive, property assignment, and > return. > > - Each element value is validated against `T`. > > There are two approaches, but only one gives strong runtime guarantees. > > > > Preferred behavior: > > - Validate on consumption when the iterable is iterated. > > - If a yielded element violates `T`, throw `TypeError` at the point > > of iteration. > > > > This mirrors the fact that `iterable` may be lazy and cannot be fully > > validated up front. > > > > **Implementation** > > I have basic C knowledge but I don't have much experience with Zend > > Engine / PHP Core. However I am fully committed to: > > - Write and maintain the RFC document. > > - Test and write tests for the implementation or draft > > - Participate in discussions > > - Collaborate with anyone willing to help with the implementation > > > > **Keys and additional parameters** > > This proposal focuses on element types. Keys are intentionally out of > > scope for the initial version. > > > > Possible follow up work could explore `array<TKey, TValue>` and > > `Generator<TKey, TValue>`. > > > > **Next steps** > > - I would like to have hear any potential caveats. For example > > regarding lazy loading. > > - If the RFC gains internal support, I will officially publish the > > first initial draft of the RFC. > > - Whether supporting the shorthand `T[]` should be part of the initial > > scope or a follow up > > - Whether supporting `TKey` should be part of the initial scope or a > > follow up > > > > Kind regards, > > Jordi Kroon > Yes, I am fully aware https://wiki.php.net/rfc/generic-arrays exists. > But since it's older date I was not sure if I had to revive the original > or start a new. That said since PHP is becoming a more strict language, > it became more relevant. And the original RFC doesn't mention anything > regarding Traversables or other iterables. Though I would like to give > my sincere for the work that has been done for the current RFC proposal.
I recommend reading this blog post from August that takes an in-depth look at the problem and potential solutions: https://thephp.foundation/blog/2025/08/05/compile-generics/ Another relevant post is from August 2024, a year before the one linked above: https://thephp.foundation/blog/2024/08/19/state-of-generics-and-collections/ Cheers, Ben

Jakub Zelenka

247 days ago
Hi, On Sat 27. 12. 2025 at 16:28, Jordi Kroon <jordikroon@me.com> wrote:
> Hi everyone, > > I would like to propose a new type in PHP: Generic Collection Notation > for Iterables (`array<T>`). > > **The problem** > PHP provides several ways to represent sequences of values: > > - `array` > - `iterable` > - `Traversable` implementations > - `generators` > > While these constructs are fundamental to PHP, none of them allow the > developer to express **what type of values they yield or contain** at > the language level. > > > This leads to several issues: > - Static analysis relies heavily on PHPDoc annotations. > - Runtime type hints communicate structure but not intent. > - APIs lack self descriptive type information. > - Developers must duplicate type information across signatures and > documentation. > > **Example** > ```php > class UserService > { > /** > * @return User[] > */ > public function getAllUsers(): array > { > return [new User("Alice"), new User("Bob")]; > } > > /** > * @param User $user > * @param Role[] $roles > * @return void > */ > public function saveRoles($user, array $roles): void > { > // Some database call > } > } > ``` > > **The proposal** > Introduce parameterized iterable types using generic like syntax: > `array<T>` > `iterable<T>` > `Traversable<T>` > `Generator<T>` > > ```php > function processUsers(array<User> $users): void { } > > function normalize(array<string> $values): array<string> { } > ``` > > Optionally we could also extend the new array syntax with this > functionality. > > ```php > function processUsers(User[] $users): void { } > > function normalize(string[] $values): string[] { } > ``` > > Key points: > - The existing `array` type remains valid and unchanged. > - `array<T>` means an array whose elements are of type `T`. > - The syntax is currently invalid in PHP and therefore does not > introduce a backward compatibility break. > > **Example** > ```php > class UserService > { > public function getAllUsers(): array<User> > { > return [new User("Alice"), new User("Bob")]; > } > > public function saveRoles(User $user, array<Role> $roles): void > { > // Some database call > } > > public function streamAllUsers(): Generator<User> > { > yield new User("Alice"); > yield new User("Bob"); > } > > public function iterateAllUsers(): ArrayIterator<User> > { > return new ArrayIterator([new User("Alice"), new User("Bob")]); > } > > public function getUserProvider(): Traversable<User> > { > return new ArrayIterator([new User("Alice"), new User("Bob")]); > } > > public function normalizeNames(iterable<string> $names): > iterable<string> > { > foreach ($names as $name) { > yield trim($name); > } > } > } > ``` > > **Runtime semantics and enforcement** > The goal is that `array<T>` and related iterable parameterizations > behave like other PHP types: if a value is passed or returned that > violates the declared type, a `TypeError` is thrown. > > The main question is when and how enforcement occurs for eager versus > lazy containers. > - Checked on function argument receive, property assignment, and return. > - Each element value is validated against `T`. > There are two approaches, but only one gives strong runtime guarantees. > > Preferred behavior: > - Validate on consumption when the iterable is iterated. > - If a yielded element violates `T`, throw `TypeError` at the point > of iteration. > > This mirrors the fact that `iterable` may be lazy and cannot be fully > validated up front. > > **Implementation** > I have basic C knowledge but I don't have much experience with Zend > Engine / PHP Core. However I am fully committed to: > - Write and maintain the RFC document. > - Test and write tests for the implementation or draft > - Participate in discussions > - Collaborate with anyone willing to help with the implementation > > **Keys and additional parameters** > This proposal focuses on element types. Keys are intentionally out of > scope for the initial version. > > Possible follow up work could explore `array<TKey, TValue>` and > `Generator<TKey, TValue>`. > > **Next steps** > - I would like to have hear any potential caveats. For example > regarding lazy loading. > - If the RFC gains internal support, I will officially publish the > first initial draft of the RFC. > - Whether supporting the shorthand `T[]` should be part of the initial > scope or a follow up > - Whether supporting `TKey` should be part of the initial scope or a > follow up
This has been discussed in past and we actually talked about it internally quite recently. IMHO only T[] should be supported to not give partial generics and disallow nested arrays. Arnaud described best the possible issues in https://externals.io/message/125049#125069 . There is really not much point to do RFC without implementation so you will probably need to find someone who is willing and capable to implement it. Kind regards, Jakub