PHP - How to count a generators yields

generator, php, yield

Solution

You should understand, that generator isn't data structure - it's an instance of `Generator` class and, actually, it's special sort of `Iterator`. Thus, you can't count its items directly (to be precise - that's because `Generator` class implements only `Iterator` interface, and not `Countable` interface. To be honest, I can't imagine how can it implement that)

To count values with native generator you'll have to iterate through it. But that can not be done in common sense - because in most cases it's you who'll decide how many items will be yielded. Famous `xrange()` sample from manual:

function xrange($start, $limit, $step = 1) {
    if ($start < $limit) {
        if ($step <= 0) {
            throw new LogicException('Step must be +ve');
        }

        for ($i = $start; $i <= $limit; $i += $step) {
            yield $i;
        }
    } else {
        if ($step >= 0) {
            throw new LogicException('Step must be -ve');
        }

        for ($i = $start; $i >= $limit; $i += $step) {
            yield $i;
        }
    }
}

-as you can see, it's you who must define borders. And final count will depend from that. Iterating through generator will have sense only with static-borders defined generator (i.e. when count of items is always static - for example, defined inside generator strictly). In any other case you'll get parameter-dependent result. For `xrange()`:

function getCount(Generator $functor)
{
   $count = 0;
   foreach($functor as $value)
   {
      $count++;
   }
   return $count;
}

-and usage:

var_dump(getCount(xrange(1, 100, 10)));//10
var_dump(getCount(xrange(1, 100, 1)));//100

-as you can see, "count" will change. Even worse, generator hasn't to be finite. It may yield infinite set of values (and borders are defined in external loop, for example) - and this is one more reason which makes "counting" near senseless.

Problem

Using PHP >= 5.5 if we have a method that yielded values, what would be the best method in counting these values? What I was expecting was to be able to convert a Generator to an array and count that, however it would return an empty array. Count() also does not work as the Generator is reported as empty despite not being empty. I'm baffled with this. If you don't need to count a generators yields then it's a nice feature otherwise I don't see much of a point for it. There is a way to detect if a generator is empty, this is by using the key() method and if it returns NULL then there are either no yields or the generator has already been iterated through which would mean the current pointer is null.

Original source