Concept: "arrayable" pseudo type and \Arrayable interface

  107806
November 17, 2019 14:41 norbert@aimeos.com (Aimeos | Norbert Sendetzky)
Since PHP 7.1 there's the "iterable" pseudo type hint that matches
"array" and "Traversable".

PHP frameworks would profit from support of an "arrayable" pseudo type
hint that matches "array" and all objects that implements "Traversable",
"ArrayAccess" and "Countable".

Thus, we could pass arrays or all objects that behave like arrays to
methods and do:

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

It would be useful to implement an Arrayable interface too:

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

Then, we can use array like objects:

class Test implements \Arrayable
{
    // ...
}

$arrayable = new Test();
$arrayable['key'] = $value;
$arrayable = useArrayable( $arrayable );

And if necessary, we can convert them to native arrays:

if( $arrayable instanceof \Arrayable ) {
    $arrayable = $arrayable->toArray();
}
  107807
November 17, 2019 16:10 dohpaz@gmail.com (Ken Stanley)
On Sun, Nov 17, 2019 at 9:42 AM Aimeos | Norbert Sendetzky <
norbert@aimeos.com> wrote:

> Since PHP 7.1 there's the "iterable" pseudo type hint that matches > "array" and "Traversable". > > PHP frameworks would profit from support of an "arrayable" pseudo type > hint that matches "array" and all objects that implements "Traversable", > "ArrayAccess" and "Countable". > > Thus, we could pass arrays or all objects that behave like arrays to > methods and do: > > function useArrayable( arrayable $arg ) : arrayable { > $cnt = count( $arg ); > $value = $arg['key']; > foreach( $arg as $key => $entry ) { ... } > return $arg; > } > > It would be useful to implement an Arrayable interface too: > > interface Arrayable extends \Iterator, \Countable, \ArrayAccess > { > public function toArray() : array; > } > > Then, we can use array like objects: > > class Test implements \Arrayable > { > // ... > } > > $arrayable = new Test(); > $arrayable['key'] = $value; > $arrayable = useArrayable( $arrayable ); > > And if necessary, we can convert them to native arrays: > > if( $arrayable instanceof \Arrayable ) { > $arrayable = $arrayable->toArray(); > } > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php
Also the toArray() should be called when an Arrayable is cast to an array: (array)$arrayable. And shouldn’t the toArray match the same naming semantics as __toString? -- "Do not go gentle into that good night. Rage, rage against the dying of the light." — Dylan Thomas
  107808
November 17, 2019 16:25 stevenwadejr@gmail.com (Steven Wade)
I proposed a while back adding a new magic method __toArray that would automatically cast. I'm halfway through writing the RFC (in draft and unpublished). 

I wonder how that could tie in to this proposal. Or even be a precursor to this. 

Sent from my iPhone

> On Nov 17, 2019, at 11:10 AM, Ken Stanley <dohpaz@gmail.com> wrote: > > On Sun, Nov 17, 2019 at 9:42 AM Aimeos | Norbert Sendetzky < > norbert@aimeos.com> wrote: > >> Since PHP 7.1 there's the "iterable" pseudo type hint that matches >> "array" and "Traversable". >> >> PHP frameworks would profit from support of an "arrayable" pseudo type >> hint that matches "array" and all objects that implements "Traversable", >> "ArrayAccess" and "Countable". >> >> Thus, we could pass arrays or all objects that behave like arrays to >> methods and do: >> >> function useArrayable( arrayable $arg ) : arrayable { >> $cnt = count( $arg ); >> $value = $arg['key']; >> foreach( $arg as $key => $entry ) { ... } >> return $arg; >> } >> >> It would be useful to implement an Arrayable interface too: >> >> interface Arrayable extends \Iterator, \Countable, \ArrayAccess >> { >> public function toArray() : array; >> } >> >> Then, we can use array like objects: >> >> class Test implements \Arrayable >> { >> // ... >> } >> >> $arrayable = new Test(); >> $arrayable['key'] = $value; >> $arrayable = useArrayable( $arrayable ); >> >> And if necessary, we can convert them to native arrays: >> >> if( $arrayable instanceof \Arrayable ) { >> $arrayable = $arrayable->toArray(); >> } >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php > > > Also the toArray() should be called when an Arrayable is cast to an array: > (array)$arrayable. > > And shouldn’t the toArray match the same naming semantics as __toString? > -- > "Do not go gentle into that good night. Rage, rage against the dying of the > light." — Dylan Thomas
  107809
November 17, 2019 16:35 norbert@aimeos.com (Aimeos | Norbert Sendetzky)
>>> And if necessary, we can convert them to native arrays: >>> >>> if( $arrayable instanceof \Arrayable ) { >>> $arrayable = $arrayable->toArray(); >>> } >> >> Also the toArray() should be called when an Arrayable is cast to an array: >> (array)$arrayable. >> >> And shouldn’t the toArray match the same naming semantics as __toString? > > I proposed a while back adding a new magic method __toArray that > would automatically cast. I'm halfway through writing the RFC (in > draft and unpublished). > > I wonder how that could tie in to this proposal. Or even be a > precursor to this.
Thank you for your very good suggestions! @Steven: Can you please post the URL of your RFC?
  107810
November 17, 2019 18:05 rowan.collins@gmail.com (Rowan Tommins)
On 17/11/2019 14:41, Aimeos | Norbert Sendetzky wrote:
> PHP frameworks would profit from support of an "arrayable" pseudo type > hint that matches "array" and all objects that implements "Traversable", > "ArrayAccess" and "Countable".
....
> It would be useful to implement an Arrayable interface too: > > interface Arrayable extends \Iterator, \Countable, \ArrayAccess > { > public function toArray() : array; > }
It feels like there are two alternative suggestions here. Not only do they use the same keyword to mean different things, but "convertible to array", and "usable as array" seem like distinct concepts to me. For the "convertible to array" case, I think __toArray, or an interface specifying just that one method, would make more sense than combining it with the existing interfaces. I'm sceptical of that concept, though, because most objects could be converted to many different arrays in different circumstances, each of which should be given a different and descriptive name. For the "usable as array" case, the big advantage would come if internal array_* functions could take such objects, but that requires a bit of thought on how each function should behave. There could also be some surprising behaviour passing infinite iterators, or objects where the output of ArrayAccess and iteration don't match up. It might make sense to have objects opt into an Arrayable interface, to make clear they're expecting to be used this way; but require no additional methods other than those required by Iterator (or Traversable), Countable, and ArrayAccess. Regards, -- Rowan Tommins (né Collins) [IMSoP]
  107813
November 17, 2019 23:28 mike@newclarity.net (Mike Schinkel)
> On Nov 17, 2019, at 9:41 AM, Aimeos | Norbert Sendetzky <norbert@aimeos.com> wrote: > > Since PHP 7.1 there's the "iterable" pseudo type hint that matches > "array" and "Traversable". > > PHP frameworks would profit from support of an "arrayable" pseudo type > hint that matches "array" and all objects that implements "Traversable", > "ArrayAccess" and "Countable".
If we are going to open up arrays for enhancement in PHP 8 I would ask that we seriously consider addressing the various differences between a built-in array and an instance of ArrayObject and/or the related associated interfaces such that the objects can be used interchangeably with a built-in array, i.e. that ArrayObject and the related associated interfaces can fully replace the use of arrays when refactoring. Specifically I would propose that we provide some type of mechanism that would allow the developer to cause is_array() to return true on an ArrayObject/interfaces. Maybe something like this (I have not considered all the special cases, I am just spitballing in concept here): class MyArray extends ArrayObject { function __AsArray() { return true; } function hello() { echo 'Hi!'; } } echo is_array(new MyArray()) // outputs: true ? 'true' : 'false'; Also, when the object is used by any of the internal PHP functions that expect an array, such as sort() the internal function would treat the object as an array inside the function. This also means returning the object from methods like array_filter() when an ArrayObject with __AsArray()===true is passed. Even though these objects would be treated as arrays when in an array context they should still be able to call their own methods, e.g. $array = new MyArray() $array->hello() // outputs: Hi! ? 'true' : 'false'; This would go a long way to allowing PHP developers to incrementally clean up the gobs of legacy code in the wild, and move to objects where appropriate. -Mike
  107814
November 17, 2019 23:44 ben@benramsey.com (Ben Ramsey)
> On Nov 17, 2019, at 17:28, Mike Schinkel <mike@newclarity.net> wrote: > > If we are going to open up arrays for enhancement in PHP 8 I would ask that we seriously consider addressing the various differences between a built-in array and an instance of ArrayObject and/or the related associated interfaces such that the objects can be used interchangeably with a built-in array
I completely agree. I would love to see anything implementing ArrayAccess able to be used in any of the array functions. This might be a way-out-in-left-field concept, but if we went down this path, perhaps a PHP array could become an object internally (i.e., ArrayObject) without changing the way it works in existing code? Then, if/when generics are introduced, a PHP array would already be a class to which a generic type parameter could be applied. -Ben
  107817
November 18, 2019 08:38 phpmailinglists@gmail.com (Peter Bowyer)
On Sun, 17 Nov 2019 at 23:44, Ben Ramsey <ben@benramsey.com> wrote:

> > > On Nov 17, 2019, at 17:28, Mike Schinkel <mike@newclarity.net> wrote: > > > > If we are going to open up arrays for enhancement in PHP 8 I would ask > that we seriously consider addressing the various differences between a > built-in array and an instance of ArrayObject and/or the related associated > interfaces such that the objects can be used interchangeably with a > built-in array > > I completely agree. I would love to see anything implementing ArrayAccess > able to be used in any of the array functions. > > This might be a way-out-in-left-field concept, but if we went down this > path, perhaps a PHP array could become an object internally (i.e., > ArrayObject) without changing the way it works in existing code? Then, > if/when generics are introduced, a PHP array would already be a class to > which a generic type parameter could be applied.
I agree with Mike & Ben. Another voice in support of this. Peter
  107819
November 18, 2019 10:36 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
pon., 18 lis 2019 o 09:39 Peter Bowyer <phpmailinglists@gmail.com>
napisał(a):

> On Sun, 17 Nov 2019 at 23:44, Ben Ramsey <ben@benramsey.com> wrote: > > > > > > On Nov 17, 2019, at 17:28, Mike Schinkel <mike@newclarity.net> wrote: > > > > > > If we are going to open up arrays for enhancement in PHP 8 I would ask > > that we seriously consider addressing the various differences between a > > built-in array and an instance of ArrayObject and/or the related > associated > > interfaces such that the objects can be used interchangeably with a > > built-in array > > > > I completely agree. I would love to see anything implementing ArrayAccess > > able to be used in any of the array functions. > > > > This might be a way-out-in-left-field concept, but if we went down this > > path, perhaps a PHP array could become an object internally (i.e., > > ArrayObject) without changing the way it works in existing code? Then, > > if/when generics are introduced, a PHP array would already be a class to > > which a generic type parameter could be applied. > > > I agree with Mike & Ben. Another voice in support of this. >
IMO there is a little catch, at least one. Currently, arrays all together with rest of scalar types are value-types which means when they're passed on (unless passed by a reference) their copies are modified but not the original one. That's cause object's in PHP are reference types, when passed they're always passed by reference. Thus making a general ArrayObject class even internally for now should follow value-types semantics. How do you want to reconcile that? Another thing is when shaping an OO API on general array class -> ArrayObject all of the existing array_xxx functions IMO should be deprecated and that's not likely gonna happen. If you ask me why I think like that, well if ArrayObject is going to be automatically served from all arrays keeping array_xxx functions makes no longer a sense, the only thing they could do from that point is only calling an ArrayObject method, which is different from what they do now they return a new value-type a new array with filtered, mapped key-value array elements, when working with objects you expect them to mutate themself if they're mutable or return a brand new instance when they're immutable. Going further if ArrayObject is gonna have for eg. map() method it should return a new instance of self with different key-value pairs collection - this means ArrayObject has to know how to create a new self. This is easy for built-in class but when you go to polymorphism and would like to extend it in MyFooArray you need to implement map method as well cause maybe there is an additional initialization needed besides of key-value pairs only, for eg. additional constructor argument. If that happens for internal array_xxx functions it would be hard to guess how to create a new instance, they'd become a thin wrapper of calling ArrayObject like methods, nothing more. TBH, in general, I think I wouldn't mess up anything. iterable is a pseudo type cause there were reasons for that. iterable accepts arrays, objects implementing iterator or generator. iterable doesn't mean all that accepts is countable (look at generators). speaking of arrayable probably you're thinking of counting it too, but then it would require to behave like an intersection of types arrayable = iterable&countable - going further it would not accept generators anymore - concluding people would still use iterable and not arrayable for traversing using foreach etc. Just my 50cents. Cheers, Michał Brzuchalski
  107818
November 18, 2019 08:54 me@jhdxr.com (=?utf-8?b?Q0hVIFpoYW93ZWk=?=)
The union types RFC passed, so I didn't see much value in the arrayable pseudo except pressing less keys. 

Regarding the Arrayable interface, or toArray method. Since it extends Iterator, iterator_to_array can do the job already. 
So what's the benfits for introducing this new interface? 

I would suggest to find a way to make those built-in array_* functions support the arraylike objects, no matter which interface it requires, existing ArrayAccess, or any new interface.

Regards,
CHU Zhaowei

> -----Original Message----- > From: Aimeos | Norbert Sendetzky <norbert@aimeos.com> > Sent: Sunday, November 17, 2019 10:41 PM > To: internals@lists.php.net > Subject: [PHP-DEV] Concept: "arrayable" pseudo type and \Arrayable > interface > > Since PHP 7.1 there's the "iterable" pseudo type hint that matches "array" > and "Traversable". > > PHP frameworks would profit from support of an "arrayable" pseudo type > hint that matches "array" and all objects that implements "Traversable", > "ArrayAccess" and "Countable". > > Thus, we could pass arrays or all objects that behave like arrays to methods > and do: > > function useArrayable( arrayable $arg ) : arrayable { > $cnt = count( $arg ); > $value = $arg['key']; > foreach( $arg as $key => $entry ) { ... } > return $arg; > } > > It would be useful to implement an Arrayable interface too: > > interface Arrayable extends \Iterator, \Countable, \ArrayAccess { > public function toArray() : array; > } > > Then, we can use array like objects: > > class Test implements \Arrayable > { > // ... > } > > $arrayable = new Test(); > $arrayable['key'] = $value; > $arrayable = useArrayable( $arrayable ); > > And if necessary, we can convert them to native arrays: > > if( $arrayable instanceof \Arrayable ) { > $arrayable = $arrayable->toArray(); > } > > -- > PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: > http://www.php.net/unsub.php >