# [RFC] "arrayable" pseudo type hint

- Thread: https://externals.io/message/107840
- Original archive: https://news-web.php.net/php.internals/107840
- Started by Aimeos | Norbert Sendetzky on 2019-11-19T22:21:26+00:00
- Messages: 13
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #107840 — Aimeos | Norbert Sendetzky — 2019-11-19T22:21:26+00:00

URL: https://externals.io/message/107840#107840

Since PHP 7.1, there's the "iterable" pseudo type hint that matches
"array" or "Traversable".

PHP frameworks would profit from support of an "arrayable" pseudo type
hint that matches "array" and all objects that can be used like arrays.

For that, "arrayable" objects have to implement these interfaces:
- ArrayAccess
- Countable
- Traversable (i.e. either Iterator or IteratorAggregate)


Implementing "arrayable" pseudo type, we could pass arrays or all
objects that can be used like arrays to methods and do:

function useArrayable( arrayable $arg ) : arrayable {
    $cnt = count( $arg );
    $value = $arg['key'];
    foreach( $arg as $key => $entry );
    return $arg;
}


Best use cases are:

- Laravel Collection
(https://github.com/laravel/framework/blob/6.x/src/Illuminate/Support/Collection.php)

- Aimeos Map (https://github.com/aimeos/map)


No new interface is proposed because we can check if objects implement:

ArrayAccess && Countable && Traversable


Because "array" !== "arrayable", "arrayable objects will not be accepted
by array_* functions and all functions that only use "array" as type
hint for parameters and return types.


This proposal is not obsolete by the implementation of union types (i.e.
array|Traversable) because union data types are types/interfaces
combined by OR. "arrayable" is a combination of OR and AND:

array|ArrayAccess&Countable&Traversable


Also, "arrayable" won't supersede "iterable" because

- "iterable" matches arrays, iterators and generators

- "arrayable" matches arrays and objects that can be used like arrays


"Can be used like arrays" doesn't include support for empty() because it
works for empty arrays but not for "arrayable" objects without elements.
If possible and requested, this may be addressed by another RFC.


As Larry Garfield suggested, this pre-RFC proposes concentrates on the
"arrayable" pseudo type only.

It does NOT discusses that:

- arrayable objects can be converted to arrays (Steven Wade works on an
RFC for a __toArray() magic function)

- ArrayObject or any other arrayable object makes the array_* functions
less relevant in the future

- arrayable objects can be passed to array_* functions in the future

---

## #107853 — Aimeos | Norbert Sendetzky — 2019-11-24T12:53:02+00:00

URL: https://externals.io/message/107840#107853 · In reply to: #107840

> Since PHP 7.1, there's the "iterable" pseudo type hint that matches
> "array" or "Traversable".
> 
> PHP frameworks would profit from support of an "arrayable" pseudo type
> hint that matches "array" and all objects that can be used like arrays.
> 
> For that, "arrayable" objects have to implement these interfaces:
> - ArrayAccess
> - Countable
> - Traversable (i.e. either Iterator or IteratorAggregate)
> 
> 
> Implementing "arrayable" pseudo type, we could pass arrays or all
> objects that can be used like arrays to methods and do:
> 
> function useArrayable( arrayable $arg ) : arrayable {
>     $cnt = count( $arg );
>     $value = $arg['key'];
>     foreach( $arg as $key => $entry );
>     return $arg;
> }

Implementation is pretty straight forward:

https://github.com/aimeos/php-src/commit/8c4f7f1142e20696b9481f4329928a6140eb05fd

It also contains is_arrayable() and ReflectionClass::isArrayable()
methods. Tests haven't been implemented yet.

---

## #107854 — me@jhdxr.com — 2019-11-24T15:54:48+00:00

URL: https://externals.io/message/107840#107854 · In reply to: #107840

> This proposal is not obsolete by the implementation of union types (i.e.
> array|Traversable) because union data types are types/interfaces
> combined by OR. "arrayable" is a combination of OR and AND:
> 
> array|ArrayAccess&Countable&Traversable

True. But it's also easy to implement in userland. Just create a new interface that extends these three interfaces, then union types can work.

```php
interface Arrayable extends ArrayAccess, Countable, Traversable {}

function array_x (array|Arrayable $arr) {}
```

This is not something that userland cannot implement or very difficult to implement. IMO it will make php more complex if we include this in the core. Actually what you suggested has been totally covered by the future scopes of the union types RFC. If you really want to explore this idea, I think they are worth more attention, instead of this specific case. 

Regards,
CHU Zhaowei

> -----Original Message-----
> From: Aimeos | Norbert Sendetzky <norbert@aimeos.com>
> Sent: Wednesday, November 20, 2019 6:21 AM
> To: internals@lists.php.net
> Subject: [PHP-DEV] [RFC] "arrayable" pseudo type hint
> 
> Since PHP 7.1, there's the "iterable" pseudo type hint that matches "array" or
> "Traversable".
> 
> PHP frameworks would profit from support of an "arrayable" pseudo type hint
> that matches "array" and all objects that can be used like arrays.
> 
> For that, "arrayable" objects have to implement these interfaces:
> - ArrayAccess
> - Countable
> - Traversable (i.e. either Iterator or IteratorAggregate)
> 
> 
> Implementing "arrayable" pseudo type, we could pass arrays or all objects that
> can be used like arrays to methods and do:
> 
> function useArrayable( arrayable $arg ) : arrayable {
>     $cnt = count( $arg );
>     $value = $arg['key'];
>     foreach( $arg as $key => $entry );
>     return $arg;
> }
> 
> 
> Best use cases are:
> 
> - Laravel Collection
> (https://github.com/laravel/framework/blob/6.x/src/Illuminate/Support/Collect
> ion.php)
> 
> - Aimeos Map (https://github.com/aimeos/map)
> 
> 
> No new interface is proposed because we can check if objects implement:
> 
> ArrayAccess && Countable && Traversable
> 
> 
> Because "array" !== "arrayable", "arrayable objects will not be accepted by
> array_* functions and all functions that only use "array" as type hint for
> parameters and return types.
> 
> 
> This proposal is not obsolete by the implementation of union types (i.e.
> array|Traversable) because union data types are types/interfaces
> combined by OR. "arrayable" is a combination of OR and AND:
> 
> array|ArrayAccess&Countable&Traversable
> 
> 
> Also, "arrayable" won't supersede "iterable" because
> 
> - "iterable" matches arrays, iterators and generators
> 
> - "arrayable" matches arrays and objects that can be used like arrays
> 
> 
> "Can be used like arrays" doesn't include support for empty() because it works
> for empty arrays but not for "arrayable" objects without elements.
> If possible and requested, this may be addressed by another RFC.
> 
> 
> As Larry Garfield suggested, this pre-RFC proposes concentrates on the
> "arrayable" pseudo type only.
> 
> It does NOT discusses that:
> 
> - arrayable objects can be converted to arrays (Steven Wade works on an RFC
> for a __toArray() magic function)
> 
> - ArrayObject or any other arrayable object makes the array_* functions less
> relevant in the future
> 
> - arrayable objects can be passed to array_* functions in the future
> 
> --
>  To unsubscribe, visit:
> http://www.php.net/unsub.php

---

## #107856 — Andreas Hennings — 2019-11-25T17:33:20+00:00

URL: https://externals.io/message/107840#107856 · In reply to: #107854

> > […]
>
> True. But it's also easy to implement in userland. Just create a new
> interface that extends these three interfaces, then union types can work.
>
> ```php
> interface Arrayable extends ArrayAccess, Countable, Traversable {}
>
> function array_x (array|Arrayable $arr) {}
> ```
>
> This is not something that userland cannot implement or very difficult to
> implement. IMO it will make php more complex if we include this in the
> core. Actually what you suggested has been totally covered by the future
> scopes of the union types RFC.


or "intersection types":
https://wiki.php.net/rfc/intersection_types


> If you really want to explore this idea, I think they are worth more
> attention, instead of this specific case.
>
> Regards,
> CHU Zhaowei
>
> > […]
> "array" or
> > […]
> hint
> > […]
> objects that
> > […]
> https://github.com/laravel/framework/blob/6.x/src/Illuminate/Support/Collect
> > […]
> by
> > […]
> for
> > […]
> works
> > […]
> RFC
> > […]
> less
> > […]
> visit:
> > […]

---

## #108366 — Aimeos | Norbert Sendetzky — 2020-02-04T12:48:21+00:00

URL: https://externals.io/message/107840#108366 · In reply to: #107840

I would like to modify my initial concept of an "arrayable" type because
PHP core developers seems to be in favor of the upcoming union data
types instead of adding a new "arrayable" pseudo type similar to "iterable".

So, I would like to propose an "Arrayable" interface that combines
ArrayAccess, Countable and Traversable interfaces and adds a toArray()
method:

interface Arrayable extends ArrayAccess, Countable, Traversable
{
    public function toArray() : array;
}

Then, methods signatures can support array and Array-like objects:

function useArrayable( array|Arrayable $arg ) : array|Arrayable {
    $cnt = count( $arg );
    $value = $arg['key'];
    foreach( $arg as $key => $entry );
    is_object( $arg ) { $nativeArray = $arg->toArray(); }
    return $arg;
}

Adding a toArray() method also avoids controversy around using a magic
__toArray() method, even if this would be easier for developers in this
case.

If union data types are available and "iterable" is implemented as
alias, an alias "arrayable" for "array|Arrayable" may be added as well.


Am 19.11.19 um 23:21 schrieb Aimeos | Norbert Sendetzky:
> Since PHP 7.1, there's the "iterable" pseudo type hint that matches
> "array" or "Traversable".
> 
> PHP frameworks would profit from support of an "arrayable" pseudo type
> hint that matches "array" and all objects that can be used like arrays.
> 
> For that, "arrayable" objects have to implement these interfaces:
> - ArrayAccess
> - Countable
> - Traversable (i.e. either Iterator or IteratorAggregate)
> 
> 
> Implementing "arrayable" pseudo type, we could pass arrays or all
> objects that can be used like arrays to methods and do:
> 
> function useArrayable( arrayable $arg ) : arrayable {
>     $cnt = count( $arg );
>     $value = $arg['key'];
>     foreach( $arg as $key => $entry );
>     return $arg;
> }
> 
> 
> Best use cases are:
> 
> - Laravel Collection
> (https://github.com/laravel/framework/blob/6.x/src/Illuminate/Support/Collection.php)
> 
> - Aimeos Map (https://github.com/aimeos/map)
> 
> 
> No new interface is proposed because we can check if objects implement:
> 
> ArrayAccess && Countable && Traversable
> 
> 
> Because "array" !== "arrayable", "arrayable objects will not be accepted
> by array_* functions and all functions that only use "array" as type
> hint for parameters and return types.
> 
> 
> This proposal is not obsolete by the implementation of union types (i.e.
> array|Traversable) because union data types are types/interfaces
> combined by OR. "arrayable" is a combination of OR and AND:
> 
> array|ArrayAccess&Countable&Traversable
> 
> 
> Also, "arrayable" won't supersede "iterable" because
> 
> - "iterable" matches arrays, iterators and generators
> 
> - "arrayable" matches arrays and objects that can be used like arrays
> 
> 
> "Can be used like arrays" doesn't include support for empty() because it
> works for empty arrays but not for "arrayable" objects without elements.
> If possible and requested, this may be addressed by another RFC.
> 
> 
> As Larry Garfield suggested, this pre-RFC proposes concentrates on the
> "arrayable" pseudo type only.
> 
> It does NOT discusses that:
> 
> - arrayable objects can be converted to arrays (Steven Wade works on an
> RFC for a __toArray() magic function)
> 
> - ArrayObject or any other arrayable object makes the array_* functions
> less relevant in the future
> 
> - arrayable objects can be passed to array_* functions in the future

---

## #108397 — Larry Garfield — 2020-02-04T17:18:22+00:00

URL: https://externals.io/message/107840#108397 · In reply to: #108366

> I would like to modify my initial concept of an "arrayable" type because
> PHP core developers seems to be in favor of the upcoming union data
> types instead of adding a new "arrayable" pseudo type similar to "iterable".
> 
> So, I would like to propose an "Arrayable" interface that combines
> ArrayAccess, Countable and Traversable interfaces and adds a toArray()
> method:
> 
> interface Arrayable extends ArrayAccess, Countable, Traversable
> {
>     public function toArray() : array;
> }
> 
> Then, methods signatures can support array and Array-like objects:
> 
> function useArrayable( array|Arrayable $arg ) : array|Arrayable {
>     $cnt = count( $arg );
>     $value = $arg['key'];
>     foreach( $arg as $key => $entry );
>     is_object( $arg ) { $nativeArray = $arg->toArray(); }
>     return $arg;
> }
> 
> Adding a toArray() method also avoids controversy around using a magic
> __toArray() method, even if this would be easier for developers in this
> case.
> 
> If union data types are available and "iterable" is implemented as
> alias, an alias "arrayable" for "array|Arrayable" may be added as well.

The more I think on it, the less I like `arrayable`.  PHP arrays are a terrible data structure from a type system point of view, with too much functionality crammed into one variable type.

If you want an object that shoves all the various bits of arrayness into one object, there's already ArrayObject, which you can extend if desired.

Especially with union types, amalgam built in types like this are even less needed.

Aside from iterable, the only other built-in alias I would see is one for array|ArrayAccess.  But... it's now possible to do exactly like that, so I don't know how useful it would be.

--Larry Garfield

---

## #108398 — Aimeos | Norbert Sendetzky — 2020-02-04T18:05:52+00:00

URL: https://externals.io/message/107840#108398 · In reply to: #108397

Am 04.02.20 um 18:18 schrieb Larry Garfield:
> > […]
> 
> The more I think on it, the less I like `arrayable`.  PHP arrays are
> a terrible data structure from a type system point of view, with too
> much functionality crammed into one variable type.

The array internals are outside the scope of this proposal and are not
affected in any way by this proposal.

> If you want an object that shoves all the various bits of arrayness
> into one object, there's already ArrayObject, which you can extend if
> desired.

Tried that already, but other core developers made very clear that
ArrayObject is one of the worst implementations in PHP and that it's not
going to be improved or touched in any way and using it is highly
discouraged.

> Especially with union types, amalgam built in types like this are
> even less needed.

If an "arrayable" pseudo type should be implemented is subject to
discussion. It would be a short form for "array|Arrayable" (native type
or interface for array like objects) only offering a syntactical sugar
for developers.

> Aside from iterable, the only other built-in alias I would see is one
> for array|ArrayAccess.  But... it's now possible to do exactly like
> that, so I don't know how useful it would be.

"array|ArrayAccess" is not sufficient because arrays are also countable
and traversable and this isn't possible with union data types:

interface Arrayable extends ArrayAccess, Countable, Traversable

function useArrayable( array|Arrayable $arg ) {
    $cnt = count( $arg );
    $value = $arg['key'];
    foreach( $arg as $key => $entry );
}

---

## #108399 — Rowan Tommins — 2020-02-04T18:17:40+00:00

URL: https://externals.io/message/107840#108399 · In reply to: #108398

On Tue, 4 Feb 2020 at 18:06, Aimeos | Norbert Sendetzky <norbert@aimeos.com>
wrote:

> Am 04.02.20 um 18:18 schrieb Larry Garfield:
> > […]
>
> The array internals are outside the scope of this proposal and are not
> affected in any way by this proposal.
>


I think Larry's point was that the flexibility of PHP's array type makes it
really hard to pin down whether a given object is "array-like" or not, and
which attributes a particular function actually cares about.

A general "intersection type" system might be more useful, because then you
could require the parts you specifically needed, such as
"traversable&ArrayAccess" or "traversable&countable".

Regards,
-- 
Rowan Tommins
[IMSoP]

---

## #108400 — Aimeos | Norbert Sendetzky — 2020-02-04T18:40:01+00:00

URL: https://externals.io/message/107840#108400 · In reply to: #108399

Am 04.02.20 um 19:17 schrieb Rowan Tommins:
> I think Larry's point was that the flexibility of PHP's array type makes it
> really hard to pin down whether a given object is "array-like" or not, and
> which attributes a particular function actually cares about.

What else besides array access, counting and traversing is possible that
may differ from classes that implement those interfaces?

> A general "intersection type" system might be more useful, because then you
> could require the parts you specifically needed, such as
> "traversable&ArrayAccess" or "traversable&countable".

I think that's too complicated and we should make it as easy as possible
for PHP developers.

Also, there's already an RFC for intersection types but it was never
adopted: https://wiki.php.net/rfc/intersection_types

---

## #108401 — Guilliam Xavier — 2020-02-04T18:51:53+00:00

URL: https://externals.io/message/107840#108401 · In reply to: #108366

On Tue, Feb 4, 2020 at 1:48 PM Aimeos | Norbert Sendetzky
<norbert@aimeos.com> wrote:
>
> [...]
>
> interface Arrayable extends ArrayAccess, Countable, Traversable
> {
>     public function toArray() : array;
> }
>
> [...]
>
> If union data types are available and "iterable" is implemented as
> alias, an alias "arrayable" for "array|Arrayable" may be added as well.

About that last point: it would not be possible to have both the
`Arrayable` interface and the `arrayable` reserved word because PHP is
case-insensitive for those things (the same way you cannot declare an
`interface Iterable` because of the `iterable` reserved word,
<https://3v4l.org/Fhqar>), so you would need another name (if you want
the alias)

-- 
Guilliam Xavier

---

## #108402 — Larry Garfield — 2020-02-04T21:18:47+00:00

URL: https://externals.io/message/107840#108402 · In reply to: #108400

> Am 04.02.20 um 19:17 schrieb Rowan Tommins:
> > […]
> 
> What else besides array access, counting and traversing is possible that
> may differ from classes that implement those interfaces?
> 
> > […]
> 
> I think that's too complicated and we should make it as easy as possible
> for PHP developers.
> 
> Also, there's already an RFC for intersection types but it was never
> adopted: https://wiki.php.net/rfc/intersection_types


Rowan is exactly right and said it better than I did.

The point is that "I can `count()` it", "I can foreach() it" and "I can bracket it" are three different things; in practice, a given function likely only cares about one, maybe two of those at a time.  Adding a type for "an object that mimics all of the dumb things arrays do, but now passes differently" doesn't strike me as useful; it strikes me as the sort of thing I'd reject in a code review if someone tried to do it in user space.

The problem with PHP arrays is that they're not arrays; they're a hash map with poor safety, lame error semantics, and some cheats to make them kinda sorta look like arrays if you don't look too carefully.  In practice, they create more bugs than they fix.

Intersection types would be absolutely delightful and I want them for numerous reasons.

--Larry Garfield

---

## #108403 — Andreas Hennings — 2020-02-04T22:24:36+00:00

URL: https://externals.io/message/107840#108403 · In reply to: #108402

> 
> > […]
> makes it
> > […]
> and
> > […]
> then you
> > […]
>
>
> Rowan is exactly right and said it better than I did.
>
> The point is that "I can `count()` it", "I can foreach() it" and "I can
> bracket it" are three different things; in practice, a given function
> likely only cares about one, maybe two of those at a time.  Adding a type
> for "an object that mimics all of the dumb things arrays do, but now passes
> differently" doesn't strike me as useful; it strikes me as the sort of
> thing I'd reject in a code review if someone tried to do it in user space.
>
> The problem with PHP arrays is that they're not arrays; they're a hash map
> with poor safety, lame error semantics, and some cheats to make them kinda
> sorta look like arrays if you don't look too carefully.  In practice, they
> create more bugs than they fix.
>

There is one good thing about arrays:
They are passed along by-value by default, which gives them similar
qualities as an "immutable" object.
If you pass an array to a function as a parameter which is not
by-reference, you can expect the original array to remain unchanged.
(Objects or referenced variables within that array can still be modified of
course)

A function parameter which allows an object OR an array loses this
advantage.

If I have the choice between \stdClass and array for "unstructured tree of
data", I will always prefer the array.

~~ Andreas


>
> --Larry Garfield

---

## #108411 — Nikita Popov — 2020-02-06T11:59:45+00:00

URL: https://externals.io/message/107840#108411 · In reply to: #108400

On Tue, Feb 4, 2020 at 7:40 PM Aimeos | Norbert Sendetzky <
norbert@aimeos.com> wrote:

> Am 04.02.20 um 19:17 schrieb Rowan Tommins:
> > […]
> it
> > […]
> and
> > […]
>
> What else besides array access, counting and traversing is possible that
> may differ from classes that implement those interfaces?
>
> > […]
> you
> > […]
>
> I think that's too complicated and we should make it as easy as possible
> for PHP developers.
>
> Also, there's already an RFC for intersection types but it was never
> adopted: https://wiki.php.net/rfc/intersection_types
>

FWIW, that's an old RFC that predates the current implementation of union
types in PHP 8 by a few years... A new proposal for intersection types
should stand a better chance now, especially as it can mostly reuse the
(relatively involved) technical underpinnings for the union types
implementation.

Nikita
