Skip to content

Treat \stdClass as iterable #3388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

Conversation

duncan3dc
Copy link
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
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
Member Author

duncan3dc commented Jul 14, 2018

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
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
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
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
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
Copy link
Member

nikic commented Jul 14, 2018

@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
Contributor

Majkl578 commented Jul 14, 2018

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.

@nikic nikic added the RFC label Jul 15, 2018
@duncan3dc
Copy link
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
Member

nikic commented Jul 16, 2018

@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
Member Author

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

@Majkl578
Copy link
Contributor

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

Very likely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants