Making stdClass iterable

php.internals

Craig Duncan (PHP)

7 years ago
Hi everybody I'd like to propose a change to stdClass so that it can satisfy the iterable parameter/return type. Firstly I acknowledge that this isn't an ideal solution, but I think it gets us to a better state than we're in today. It shouldn't be necessary, but while we have such a common function (json_decode) returning stdClass we should make it easy/unsurprising to work with. https://wiki.php.net/rfc/iterable-stdclass Thanks, Craig

Dan Ackroyd

7 years ago
Hi Duncan, I quite strongly dislike the idea, as it sounds like this solution is a hack for a particular use case, and that use case doesn't seem to be particularly important to me. I say it's not particularly important to me, as you could solve it right now by: i) asking json_decode to return an array rather than an object. ii) converting the result from json_decode into an object that implements traversable. iii) By accepted that PHP's type system isn't powerful enough to make all programs trivially type-safe, and so to not use the iterable parameter type here. This won't make your program any more or less correct, it will just be slightly more annoying to debug. Those seem to be better than special casing StdClass. I say it's a hack as you're forcing the language to fit a particular need with the available tools now, rather than thinking through what best end-goal would be. I think the correct long-term solution for this would be for us to revisit the union type RFC (https://wiki.php.net/rfc/union_types) and so be able to define a type like: type foreachable = object|iterable; The union types RFC would support the problem you're trying to solve, as well as other use cases without introducing special cases. cheers Dan Ack On Sat, 12 Jan 2019 at 19:39, Craig Duncan <php@duncanc.co.uk> wrote:

Markus Fischer

7 years ago
On 12.01.19 21:13, Dan Ackroyd wrote:
> Hi Duncan,
[...] great points I was about to reply to Craig when Dans' email hit the list. I couldn't agree with Dan more. The expanded use of stdClass feels wrong to me, as in: wrong solution to a problem best solved differently. Dan gave ample examples about that. I think one more approach not mentioned is that you could also `(array) $yourStdClassVar`. Of course you didn't ask for it, but nevertheless: stdClass is already a weird thing on its own and I wouldn't want to see it getting more attention to increase the encourage to use it. cheers, - Markus

Craig Duncan (PHP)

7 years ago
On Sat, 12 Jan 2019 at 20:13, Dan Ackroyd <Danack@basereality.com> wrote:
> I think the correct long-term solution for this would be for us to > revisit the union type RFC (https://wiki.php.net/rfc/union_types) and > so be able to define a type like: > > type foreachable = object|iterable; >
This argument makes sense to me in a pre-iterable world, but as we already have an iterable type I don't understand why it shouldn't accept stdClass as well as array. What about an array is more iterable than a stdClass object?

Rowan Collins

7 years ago
On Mon, 14 Jan 2019 at 13:23, Craig Duncan <php@duncanc.co.uk> wrote:
> What about an array is more iterable than a stdClass object? >
An array is the structure PHP provides to represent a list, or an ordered collection of arbitrary key-value pairs; an object, whether stdClass or otherwise, is neither of these things. I would instead ask: what about a stdClass object is more iterable than an object created from a user-defined class? Regards,
-- Rowan Collins [IMSoP]

Nikita Popov

7 years ago
On Sat, Jan 12, 2019 at 8:39 PM Craig Duncan <php@duncanc.co.uk> wrote:
> Hi everybody > > I'd like to propose a change to stdClass so that it can satisfy the > iterable parameter/return type. > > Firstly I acknowledge that this isn't an ideal solution, but I think it > gets us to a better state than we're in today. It shouldn't be necessary, > but while we have such a common function (json_decode) returning stdClass > we should make it easy/unsurprising to work with. > > https://wiki.php.net/rfc/iterable-stdclass > > Thanks,
Craig
>
Rather than implementing Traversable, I think it would be much better to implement IteratorAggregate and provide a proper getIterator() implementation. Objects that only implement Traversable are a PITA to work with and lots of code assumes that anything Traversable will either be Iterator or IteratorAggregate. For userland code this is strictly enforced, but internal code can avoid this. However, the current exceptions to this are rather niche, while stdClass is somewhat commonly used. I'm in favor of the general change here. What I mainly have in mind is that we should stop allowing iteration over arbitrary objects in the future (we did the first step by excluding them from "iterable") and instead require the use of an explicit PropertyIterator, or similar. However, we'd probably still want to allow direct iteration of stdClass and the cleanest way to do that is to make it Traversable (via IteratorAggregate). Nikita

Craig Duncan (PHP)

7 years ago
On Mon, 14 Jan 2019 at 11:57, Nikita Popov <nikita.ppv@gmail.com> wrote:
> Rather than implementing Traversable, I think it would be much better to > implement IteratorAggregate and provide a proper getIterator() > implementation. Objects that only implement Traversable are a PITA to work > with and lots of code assumes that anything Traversable will either be > Iterator or IteratorAggregate. For userland code this is strictly enforced, > but internal code can avoid this. However, the current exceptions to this > are rather niche, while stdClass is somewhat commonly used. >
That makes sense, if the RFC passes I'll pick up these implementation points before proceeding, thanks

Rowan Collins

7 years ago
On Mon, 14 Jan 2019 at 11:57, Nikita Popov <nikita.ppv@gmail.com> wrote:
> Rather than implementing Traversable, I think it would be much better to > implement IteratorAggregate and provide a proper getIterator() > implementation.
I think adding behaviour to stdClass is a precedent we should approach with extreme caution. Currently, it has no methods, and matches no type checks other than itself and "object"; in a sense, it doesn't even have any properties, just the ability to add them dynamically. To me, that makes sense: it's the "classless object", the absolute bare minimum any object could be.
> What I mainly have in mind is that > we should stop allowing iteration over arbitrary objects in the future (we > did the first step by excluding them from "iterable") and instead require > the use of an explicit PropertyIterator, or similar.
I agree that this should be explicit, and it could probably be implemented as a very light wrapper.
> However, we'd probably still want to allow direct iteration of stdClass >
Why? What do you consider the role of stdClass to be, that means having it opt in to this behaviour would always be appropriate? If you want something iterable, you can convert your stdClass object to an array, wrap it an iterator that accepts any object, or convert it however you want into a "proper" object where you can define what behaviour you want. Regards,
-- Rowan Collins [IMSoP]

Rowan Collins

7 years ago
On Mon, 14 Jan 2019 at 14:48, Rowan Collins <rowan.collins@gmail.com> wrote:
> Currently, it has no methods, and matches no type checks other than itself > and "object"; in a sense, it doesn't even have any properties, just the > ability to add them dynamically. To me, that makes sense: it's the > "classless object", the absolute bare minimum any object could be. >
Apologies for the multiple replies, but it just occurred to me that with anonymous classes, we can define something even more minimal: $foo = new class {}; $bar = new stdClass; Given that stdClass is returned as a "default structureless object" by a lot of internal functions, what is the justification for $foo and $bar here behaving differently? Regards,
-- Rowan Collins [IMSoP]

Nikita Popov

7 years ago
On Mon, Jan 14, 2019 at 3:48 PM Rowan Collins <rowan.collins@gmail.com> wrote:
> On Mon, 14 Jan 2019 at 11:57, Nikita Popov <nikita.ppv@gmail.com> wrote: > >> Rather than implementing Traversable, I think it would be much better to >> implement IteratorAggregate and provide a proper getIterator() >> implementation. > > > I think adding behaviour to stdClass is a precedent we should approach > with extreme caution. > > Currently, it has no methods, and matches no type checks other than itself > and "object"; in a sense, it doesn't even have any properties, just the > ability to add them dynamically. To me, that makes sense: it's the > "classless object", the absolute bare minimum any object could be. > > > >> What I mainly have in mind is that >> we should stop allowing iteration over arbitrary objects in the future (we >> did the first step by excluding them from "iterable") and instead require >> the use of an explicit PropertyIterator, or similar. > > > I agree that this should be explicit, and it could probably be implemented > as a very light wrapper. > > > >> However, we'd probably still want to allow direct iteration of stdClass >> > > Why? What do you consider the role of stdClass to be, that means having it > opt in to this behaviour would always be appropriate? >
The way I see it, stdClass is basically an array in object notation (and object passing semantics). It carries data in the form of key value pairs and has no associated logic. The only things you can do with it are access keys (properties) and ... iterate over them. Mind you, stdClass is an anti-pattern. Instead of stdClass you *should* just be using an array. We should never have added stdClass. But now that we do have it, and with the way it is typically being used, I think it's fully appropriate to iterate over it. That is much unlike most other (non-Traversable) objects, where iterating over them is only reasonable in a few rather specific scenarios (say, a serializer, cloner, hydrator or whatever -- something that explicitly is interested in inspecting properties of objects as a matter of representation). Nikita

Rowan Collins

7 years ago
On Mon, 14 Jan 2019 at 15:00, Nikita Popov <nikita.ppv@gmail.com> wrote:
> The way I see it, stdClass is basically an array in object notation (and > object passing semantics). It carries data in the form of key value pairs > and has no associated logic. The only things you can do with it are access > keys (properties) and ... iterate over them. > > [...] But now that we do have it, and with the way it is typically being > used, I think it's fully appropriate to iterate over it. >
I think it's the assumption about "the way it is typically being used" where I differ from you. I generally see stdClass being used where people want to access specific keys using -> notation, and maybe to get the pass-by-pointer semantics. I'm not convinced that iterating over it as an ordered collection is a sufficiently universal use case that it should be an exception to the proposed rule of "objects aren't iterable unless iteration logic has been defined". I can see it might be convenient occasionally, but it feels like an unnecessary special case, on top of a feature which I agree should be discouraged anyway. Regards,
-- Rowan Collins [IMSoP]

Benjamin Morel

7 years ago
> We should never have added stdClass.
Why not deprecate it then, instead of extending it and giving it more legitimacy? This would remove some of the "2 ways of doing the same thing", such as json_decode(). Ben On Mon, 14 Jan 2019 at 16:00, Nikita Popov <nikita.ppv@gmail.com> wrote:

Marco Pivetta

7 years ago
On Mon, Jan 14, 2019 at 4:09 PM Benjamin Morel <benjamin.morel@gmail.com> wrote:
> > We should never have added stdClass. > > Why not deprecate it then, instead of extending it and giving it more > legitimacy? > This would remove some of the "2 ways of doing the same thing", such as > json_decode(). > > Ben >
This. `stdClass` can't die due to a lot of backwards compatibility in existing code (wordpress loves it), but in any code I've written over the past 5 years, the first thing I'd do with it is get rid of it as fast as possible. Making it more powerful will just make the problem bigger. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Benjamin Morel

7 years ago
> `stdClass` can't die due to a lot of backwards compatibility in existing
code (wordpress loves it) I, personally, would LOVE to see PHP break things on each release of a major version! I fail to see how upgrading could be painful, with a good changelog and the right E_DEPRECATED notices for a minor release or two, enough time to make it run in dev and even production, and working on deprecation notices as they appear in the log; this would make the language move much faster towards a better language. But I digress. On Mon, 14 Jan 2019 at 16:18, Marco Pivetta <ocramius@gmail.com> wrote: