Skip to content
Advertisement

Custom Iterator with a callback

For my project, I have a DoctrineIterator created by a repository. This iterator returns an array based on my internal request.

I need

  • another iterator
  • returning an object based on this array
  • a generic solution (I have more than one occurrence of this)

The usage of the second iterator is out of my scope, so a can’t update the value it returns.

I thought a could create a custom Iterator with a callback to update each item

class CallbackIterator extends IteratorIterator
{
    private $callback;

    public function __construct(Traversable $iterator, callable $callback)
    {
        $this->callback = $callback;
        parent::__construct($iterator);
    }

    public function current()
    {
        return call_user_func($this->callback, parent::current());
    }
}

It seems to work, but I’m surprised there isn’t any iterator of this kind in php

I may go in the wrong direction, what I am missing?

Advertisement

Answer

Yes, you are right, you can use custom iterator (OuterIterator) and process cursor on inner iterator but get element with you callable.

See:

<?php

$arrayIterator = new ArrayIterator([
    [
        'id'   => 1,
        'name' => 'John Smith',
    ],
    [
        'id'   => 2,
        'name' => 'Julia Robin',
    ],
]);

class CallableIterator implements Iterator
{
    private Iterator $innerIterator;
    private Closure $callback;

    public function __construct(Iterator $innerIterator, Closure $callback)
    {
        $this->innerIterator = $innerIterator;
        $this->callback = $callback;
    }

    public function current()
    {
        $currentElement = $this->innerIterator->current();

        return call_user_func($this->callback, $currentElement);
    }

    public function next(): void
    {
        $this->innerIterator->next();
    }

    public function key()
    {
        return $this->innerIterator->key();
    }

    public function valid(): bool
    {
        return $this->innerIterator->valid();
    }

    public function rewind(): void
    {
        $this->innerIterator->rewind();
    }
}


$myIterator = new CallableIterator($arrayIterator, static function (array $element) {
    $element['hash'] = md5($element['name']);

    return $element;
});

print_r(iterator_to_array($myIterator));

In this solution we rewrite result element and add hash based on md5 of name. But in you case, you can add any logic to you callable (create new object, remove specific element, add new element to array, etc…).

Iterator in Doctrine implement Iterator interface, as result you can use this solution in you problem.

P.S. If we use inner iterator – the best practice implement OuterIterator in our iterator. Because many system can read inner iterator and you speak for all next code: I am a parent iterator and can change inner result.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement