Member
The stdClass is analogous in many ways to an array, foreach handles it without issue, so it is unexpected that the iterable type rejects this structure. This PR adds support for it
function iter(iterable $items) { foreach ($items as $key => $item) { echo "[{$key}] = '{$item}'" . PHP_EOL; } } $result = json_decode('{"one": "1", "two": "2"}'); iter($result);
This replaces #3382, I'll be drafting an RFC once the list is a little quieter
Contributor
Foreach handles all objects without issue, will all objects be iterable too?
Huge ๐ from me on this one.
Member Author
No, all objects will not be iterable too.
I wouldn't say that including some properties and not others, acting differently based on whether interfaces are implemented or not, is "without issue"
Contributor
including some properties and not others
It's not some, it's all visible to current scope, IIRC just like get_object_vars() works.
Regardless, stdClass doesn't implement Traversable, so it can't be iterable.
Member Author
Regardless, stdClass doesn't implement Traversable, so it can't be iterable.
Treating stdClass like a class doesn't make sense though, it's more much akin to an array than it is to an actual class. Do you have a use case or scenario where treating stdClass as iterable would be problematic?
Contributor
That's not how stdClass works. stdClass is a class, it passes is_object() and it passes object typehint.
To quote php.net documentation:
If a value of any other type is converted to an object, a new instance of the stdClass built-in class is created. If the value was NULL, the new instance will be empty. An array converts to an object with properties named by keys and corresponding values. Note that in this case before PHP 7.2.0 numeric keys have been inaccessible unless iterated.
Do you have a use case or scenario where treating stdClass as iterable would be problematic?
Yes. This breaks:
is_array($iterable) ? $iterable : iterator_to_array($iterable)
Because you are breaking the basic invariant of iterable: An iterable is EITHER an array OR an object implementing Traversable. It is also explicitly documented as such on php.net.
Member Author
I see your point on the is_array() usage, but that looks like code that depends on an implementation detail to me. The contract set forth by iterable is:
"Iterable can be used as a parameter type to indicate that a function requires a set of values, but does not care about the form of the value set since it will be used with foreach"
I've taken your comments on board and will include them in the RFC, thanks
Member
@duncan3dc The contract of iterator is array|Traversable. This is a strict guarantee and I don't think the approach proposed in this PR is acceptable. However, why not just stay within the existing framework and actually make stdClass implement Traversable?
Contributor
but that looks like code that depends on an implementation detail to me
Not correct. It depends on the very basic assumption that iterable is either array or Traversable.
This is also how the iterable RFC defined it.
Also with iterable_to_array() being rejected, the invariant of array|Traversable is even more important to not be changed, otherwise you will break user-land code.
Member Author
@nikic I decided against that because then classes that extend \stdClass will be automatically iterable now, but it sounds like that's the lesser BC issue if people are depending on the current implementation of iterable
Member
@duncan3dc I'd argue that if stdClass is iterable, it would be very weird if classes extending stdClass weren't. That would be like a class "unimplementing" an interface during inheritance.
Member Author
@nikic I guess I'm alone in not seeing stdClass as a true class ๐
Contributor
I guess I'm alone in not seeing stdClass as a true class
Very likely.