Hi,

On Sat 27. 12. 2025 at 16:28, Jordi Kroon <[email protected]> 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

>

Reply via email to