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
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.
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.
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 hintSince 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 futurearrayable objects can be passed to array_* functions in the future
--
To unsubscribe, visit:
http://www.php.net/unsub.php
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.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-----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 hintSince 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 futurearrayable objects can be passed to array_* functions in the future
--
To unsubscribe,
visit:
http://www.php.net/unsub.php
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 futurearrayable objects can be passed to array_* functions in the future
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
Am 04.02.20 um 18:18 schrieb Larry Garfield:
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;
}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.
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 );
}
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 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.
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]
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
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
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
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_typesRowan 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
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:
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
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
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