nikic ยท GitHub

@duncan3dc

Copy link Copy Markdown

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

@Majkl578

Copy link Copy Markdown

Contributor

Foreach handles all objects without issue, will all objects be iterable too?

https://3v4l.org/lHTij

Huge ๐Ÿ‘Ž from me on this one.

@duncan3dc

Copy link Copy Markdown

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"

@Majkl578

Copy link Copy Markdown

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.

@duncan3dc

Copy link Copy Markdown

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?

@Majkl578

Copy link Copy Markdown

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)

https://3v4l.org/92r6a

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.

@duncan3dc

Copy link Copy Markdown

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

@nikic

nikic commented

Jul 14, 2018

โ€ข

edited

Loading

Copy link Copy Markdown

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?

@Majkl578

Copy link Copy Markdown

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.

@duncan3dc

Copy link Copy Markdown

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

@nikic

Copy link Copy Markdown

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.

@duncan3dc

Copy link Copy Markdown

Member Author

@nikic I guess I'm alone in not seeing stdClass as a true class ๐Ÿ˜†

@Majkl578

Copy link Copy Markdown

Contributor

I guess I'm alone in not seeing stdClass as a true class

Very likely.

@duncan3dc

@duncan3dc

Read the original on github.com โ†—