php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80010 No error notification or throw when returning iterator from iterator method
Submitted: 2020-08-23 10:07 UTC Modified: 2020-08-24 13:55 UTC
From: petr dot vejchoda at seznam dot cz Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 7.3.21 OS: Win10
Private report: No CVE-ID: None
 [2020-08-23 10:07 UTC] petr dot vejchoda at seznam dot cz
Description:
------------
When writing iterator method, complier lets me return Iterator variable, but it doesn't pass it. I am actually at version 7.3.20 and I don't care to update. I read changelog.
Note value in $e in supplied code.

Test script:
---------------
class test
{
    private $whatever = false;

    public function __construct()
    {
        $a = iterator_to_array($this->firstIterator());
        $b = iterator_to_array($this->secondIterator($this->firstIterator()));
        $c = iterator_to_array($this->thirdIterator($this->firstIterator()));

        $this->whatever = true;
        $d = iterator_to_array($this->firstIterator());
        $e = iterator_to_array($this->secondIterator($this->firstIterator()));
        $f = iterator_to_array($this->thirdIterator($this->firstIterator()));

        var_dump([$a, $b, $c, $d, $e, $f]);
    }

    public function firstIterator(): \Iterator
    {
        yield 'a'=> 1;
        yield 'b'=> 2;
        yield 'c'=> 3;
    }

    public function secondIterator(\Iterator $first): \Iterator
    {
        if ($this->whatever === true)
        {
            return $first;
        }

        foreach($first as $key=>$value)
        {
            yield $key => $value * $value;
        }
    }

    public function thirdIterator(\Iterator $first): \Iterator
    {
        if ($this->whatever === true)
        {
            return $first;
        }

        return $this->thirdSubIterator($first);
    }

    public function thirdSubIterator(\Iterator $first): \Iterator
    {
        foreach($first as $key=>$value)
        {
            yield $key => $value * $value;
        }
    }
}

Expected result:
----------------
Returned Iterator should be either passed regularly or iterator method should not be accepted at all as an iterator method, throwing exception


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-08-23 21:17 UTC] requinix@php.net
-Status: Open +Status: Not a bug -Package: *Compile Issues +Package: Class/Object related
 [2020-08-23 21:17 UTC] requinix@php.net
https://3v4l.org/bq2df

You didn't say it, but I assume what you're reporting is how the [4]=> output (the one for $e) is an empty array.

    public function secondIterator(\Iterator $first): \Iterator
    {
        if ($this->whatever === true)
        {
            return $first;
        }

        foreach($first as $key=>$value)
        {
            yield $key => $value * $value;
        }
    }

Any function that contains a yield will always return a Generator. Always. Even if it returns before it yields. The return value from a Generator function is kept separate from the yielded values.

https://www.php.net/manual/en/language.generators.syntax.php
https://www.php.net/manual/en/generator.getreturn.php

$e contains a Generator with no yielded items, and its getReturn() will give the passed iterator.

https://3v4l.org/bQJkN
 [2020-08-24 13:49 UTC] petr dot vejchoda at seznam dot cz
Ok, then tell me, what will happen in case I add foreach ($e as $value) { xy }; Which one will be actually iterated through? How do I check whether function is iterator method or whether it just returns Generator. In my case I wrongly typed everything as Iterator, but If I typed it as Generator, it would be the same. I tried that.
This definitely leads to some very inconsistent behaviour. Or maybe consistently confusing.
This is actually the first time I hear, that methods in PHP return two values. Why in hell would you allow such a thing with things as confusing as iterator functions.
 [2020-08-24 13:55 UTC] nikic@php.net
I have a hard time understanding what this bug report is about, but at a guess, you are probably looking for "yield from", which is how you delegate to a different iterator.

Using "return" inside generators is an advanced feature that is only relevant when using generators as coroutines.
 [2020-08-24 14:56 UTC] petr dot vejchoda at seznam dot cz
Ok, I get it now. So why do generators need return value? Because yeah, in Unity coroutines are poorly designed and yielded values from coroutines could only be used for passing execution information. Is that the case in PHP too? Did you poorly designed coroutines too and thus made this whole syntax up? I mean I never used coroutines in PHP. But wouldn't just common method finished() and current() suffice? If you need to pass execution information together with value, you can have type for that class XZ { $value; $nextExecution; }. Why do generators have to have a one more unnecessary state? If using return is advance feature, why doesn't the advanced feature have some advanced language syntax, instead of pure simple passing different generator from one method to another having complex syntax? Shouldn't coroutines maybe have some additional functionality instead of putting that into poor poor generators?
I get it ... still I don't like it.
 [2020-08-24 15:02 UTC] petr dot vejchoda at seznam dot cz
I mean seriously. It's just some sugar for stuff that nobody needs. Croutines returning values could as well be classes that have getReturn() method and getIterator() method; Tadaa! Why the hell do we need this stuff?? Seriously. Making mess in the language.
 [2020-08-24 15:06 UTC] petr dot vejchoda at seznam dot cz
Instead, do generics finally. That shit would be useful.
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Wed Apr 21 20:01:24 2021 UTC