vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Iterator/CachingIterator.php line 169

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ODM\MongoDB\Iterator;
  4. use Generator;
  5. use ReturnTypeWillChange;
  6. use RuntimeException;
  7. use Traversable;
  8. use function current;
  9. use function key;
  10. use function next;
  11. use function reset;
  12. /**
  13.  * Iterator for wrapping a Traversable and caching its results.
  14.  *
  15.  * By caching results, this iterators allows a Traversable to be counted and
  16.  * rewound multiple times, even if the wrapped object does not natively support
  17.  * those operations (e.g. MongoDB\Driver\Cursor).
  18.  *
  19.  * @internal
  20.  *
  21.  * @template TValue
  22.  * @template-implements Iterator<TValue>
  23.  */
  24. final class CachingIterator implements Iterator
  25. {
  26.     /** @var array<mixed, TValue> */
  27.     private $items = [];
  28.     /** @var Generator<mixed, TValue>|null */
  29.     private $iterator;
  30.     /** @var bool */
  31.     private $iteratorAdvanced false;
  32.     /** @var bool */
  33.     private $iteratorExhausted false;
  34.     /**
  35.      * Initialize the iterator and stores the first item in the cache. This
  36.      * effectively rewinds the Traversable and the wrapping Generator, which
  37.      * will execute up to its first yield statement. Additionally, this mimics
  38.      * behavior of the SPL iterators and allows users to omit an explicit call
  39.      * to rewind() before using the other methods.
  40.      *
  41.      * @param Traversable<mixed, TValue> $iterator
  42.      */
  43.     public function __construct(Traversable $iterator)
  44.     {
  45.         $this->iterator $this->wrapTraversable($iterator);
  46.         $this->storeCurrentItem();
  47.     }
  48.     public function __destruct()
  49.     {
  50.         $this->iterator null;
  51.     }
  52.     public function toArray(): array
  53.     {
  54.         $this->exhaustIterator();
  55.         return $this->items;
  56.     }
  57.     /**
  58.      * @return TValue|false
  59.      */
  60.     #[ReturnTypeWillChange]
  61.     public function current()
  62.     {
  63.         return current($this->items);
  64.     }
  65.     /**
  66.      * @return mixed
  67.      */
  68.     #[ReturnTypeWillChange]
  69.     public function key()
  70.     {
  71.         return key($this->items);
  72.     }
  73.     /**
  74.      * @see http://php.net/iterator.next
  75.      */
  76.     public function next(): void
  77.     {
  78.         if (! $this->iteratorExhausted) {
  79.             $this->getIterator()->next();
  80.             $this->storeCurrentItem();
  81.         }
  82.         next($this->items);
  83.     }
  84.     /**
  85.      * @see http://php.net/iterator.rewind
  86.      */
  87.     public function rewind(): void
  88.     {
  89.         /* If the iterator has advanced, exhaust it now so that future iteration
  90.          * can rely on the cache.
  91.          */
  92.         if ($this->iteratorAdvanced) {
  93.             $this->exhaustIterator();
  94.         }
  95.         reset($this->items);
  96.     }
  97.     /**
  98.      * @see http://php.net/iterator.valid
  99.      */
  100.     public function valid(): bool
  101.     {
  102.         return $this->key() !== null;
  103.     }
  104.     /**
  105.      * Ensures that the inner iterator is fully consumed and cached.
  106.      */
  107.     private function exhaustIterator(): void
  108.     {
  109.         while (! $this->iteratorExhausted) {
  110.             $this->next();
  111.         }
  112.         $this->iterator null;
  113.     }
  114.     /**
  115.      * @return Generator<mixed, TValue>
  116.      */
  117.     private function getIterator(): Generator
  118.     {
  119.         if ($this->iterator === null) {
  120.             throw new RuntimeException('Iterator has already been destroyed');
  121.         }
  122.         return $this->iterator;
  123.     }
  124.     /**
  125.      * Stores the current item in the cache.
  126.      */
  127.     private function storeCurrentItem(): void
  128.     {
  129.         $key $this->getIterator()->key();
  130.         if ($key === null) {
  131.             return;
  132.         }
  133.         $this->items[$key] = $this->getIterator()->current();
  134.     }
  135.     /**
  136.      * @param Traversable<mixed, TValue> $traversable
  137.      *
  138.      * @return Generator<mixed, TValue>
  139.      */
  140.     private function wrapTraversable(Traversable $traversable): Generator
  141.     {
  142.         foreach ($traversable as $key => $value) {
  143.             yield $key => $value;
  144.             $this->iteratorAdvanced true;
  145.         }
  146.         $this->iteratorExhausted true;
  147.     }
  148. }