Re: Add __toArray() method to objects that would be called on cast to array
| From: | Marcio Almada | Date: | Thu, 16 Mar 2017 14:34:25 +0000 |
| Subject: | Re: Add __toArray() method to objects that would be called on cast to array | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-98568@lists.php.net to get a copy of this message | ||
2017-03-16 11:01 GMT-03:00 Marco Pivetta <ocramius@gmail.com>:
> Since some folks keep banging on "it's not a BC break", I propose a
> challenge in fixing this particular BC break example (reads: find a
> different way to make it work, and no warnings/notices allowed):
>
> I made a very simplistic example of where
__toArray will break
> existing
> API that currently literally accepts any kind of object:
> https://3v4l.org/Tj0GH
>
> For reference, here's the code copy:
>
> <?php
>
> class Foo
> {
> public $bar;
> public $baz;
> public $taz;
> }
>
> // please note that this API has a signature of object $object
> - any
> object allowed.
> function unsetProperties($object) {
> // we ignore inheritance and private properties for simplicity
> return array_diff(
> array_map(
> function (\ReflectionProperty $p) : string {
> return $p->getName();
> },
> (new ReflectionClass($object))->getProperties()
> ),
> array_keys((array) $object)
> );
> }
>
> var_dump(unsetProperties(new Foo));
>
> $foo = new Foo;
>
> unset($foo->baz);
>
> var_dump(unsetProperties($foo));
>
> There are dozens of simpler examples that break (typically, data-mapper
> layers): I picked this particular one because there is no other way to
> detect unset properties without causing side-effects.
>
> If __toArray is implemented on an object, the API defined above
> will
> either report all properties to be unset, or in general produce unreliable
> results.
>
> The current API specification of the (array) operator is as
> following:
>
> * for every SET property produce an array value, with the key being:
> * "\0" . $className . "\0" . $propertyName for private properties
> * "\0*\0" . $propertyName for protected properties
> * $propertyName for public properties
> * $propertyName for dynamically defined properties
>
> This is a **VERY SPECIFIC** behavior that the operator currently
> guarantees. Break that, and you can go on github hunting for
> (array) cast
> usages.
>
> The behavior was frozen in the current suite in
> https://github.com/php/php-
> src/blob/8015daeddb7bb4951c808fa253b97753787fb0ea/tests/classes/
>
> <https://github.com/php/php-%0Asrc/blob/8015daeddb7bb4951c808fa253b97753787fb0ea/tests/classes/>
> array_conversion_keys.phpt (although, now that I notice, doesn't cover
> dynamically defined properties - should add that).
>
> Marco Pivetta
>
> http://twitter.com/Ocramius
>
> http://ocramius.github.com/
>
I kinda agree that (array) being aware of toArray() is not a very good idea
at this point, but...
It's still not a BC break. In order for the output of "(array) $obj" to
change, one would need to change the implementation of the subject $obj
class. Lawyering this as a BC break will just hurt any possibility of
advancing the conversation in any direction.
IMMO, if anyone would like to proceed with this toArray() proposal, a
better object reflection/destructuring API could be provided first.
Thanks,
Márcio.