Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401]

   1  <?php
   2  /*
   3   * Copyright 2017 MongoDB, Inc.
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *   http://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   */
  17  
  18  namespace MongoDB\Model;
  19  
  20  use Countable;
  21  use Generator;
  22  use Iterator;
  23  use Traversable;
  24  use function count;
  25  use function current;
  26  use function key;
  27  use function next;
  28  use function reset;
  29  
  30  /**
  31   * Iterator for wrapping a Traversable and caching its results.
  32   *
  33   * By caching results, this iterators allows a Traversable to be counted and
  34   * rewound multiple times, even if the wrapped object does not natively support
  35   * those operations (e.g. MongoDB\Driver\Cursor).
  36   *
  37   * @internal
  38   */
  39  class CachingIterator implements Countable, Iterator
  40  {
  41      /** @var array */
  42      private $items = [];
  43  
  44      /** @var Generator */
  45      private $iterator;
  46  
  47      /** @var boolean */
  48      private $iteratorAdvanced = false;
  49  
  50      /** @var boolean */
  51      private $iteratorExhausted = false;
  52  
  53      /**
  54       * Initialize the iterator and stores the first item in the cache. This
  55       * effectively rewinds the Traversable and the wrapping Generator, which
  56       * will execute up to its first yield statement. Additionally, this mimics
  57       * behavior of the SPL iterators and allows users to omit an explicit call
  58       * to rewind() before using the other methods.
  59       *
  60       * @param Traversable $traversable
  61       */
  62      public function __construct(Traversable $traversable)
  63      {
  64          $this->iterator = $this->wrapTraversable($traversable);
  65          $this->storeCurrentItem();
  66      }
  67  
  68      /**
  69       * @see http://php.net/countable.count
  70       * @return integer
  71       */
  72      public function count()
  73      {
  74          $this->exhaustIterator();
  75  
  76          return count($this->items);
  77      }
  78  
  79      /**
  80       * @see http://php.net/iterator.current
  81       * @return mixed
  82       */
  83      public function current()
  84      {
  85          return current($this->items);
  86      }
  87  
  88      /**
  89       * @see http://php.net/iterator.key
  90       * @return mixed
  91       */
  92      public function key()
  93      {
  94          return key($this->items);
  95      }
  96  
  97      /**
  98       * @see http://php.net/iterator.next
  99       * @return void
 100       */
 101      public function next()
 102      {
 103          if (! $this->iteratorExhausted) {
 104              $this->iterator->next();
 105              $this->storeCurrentItem();
 106          }
 107  
 108          next($this->items);
 109      }
 110  
 111      /**
 112       * @see http://php.net/iterator.rewind
 113       * @return void
 114       */
 115      public function rewind()
 116      {
 117          /* If the iterator has advanced, exhaust it now so that future iteration
 118           * can rely on the cache.
 119           */
 120          if ($this->iteratorAdvanced) {
 121              $this->exhaustIterator();
 122          }
 123  
 124          reset($this->items);
 125      }
 126  
 127      /**
 128       * @see http://php.net/iterator.valid
 129       * @return boolean
 130       */
 131      public function valid()
 132      {
 133          return $this->key() !== null;
 134      }
 135  
 136      /**
 137       * Ensures that the inner iterator is fully consumed and cached.
 138       */
 139      private function exhaustIterator()
 140      {
 141          while (! $this->iteratorExhausted) {
 142              $this->next();
 143          }
 144      }
 145  
 146      /**
 147       * Stores the current item in the cache.
 148       */
 149      private function storeCurrentItem()
 150      {
 151          $key = $this->iterator->key();
 152  
 153          if ($key === null) {
 154              return;
 155          }
 156  
 157          $this->items[$key] = $this->iterator->current();
 158      }
 159  
 160      /**
 161       * Wraps the Traversable with a Generator.
 162       *
 163       * @param Traversable $traversable
 164       * @return Generator
 165       */
 166      private function wrapTraversable(Traversable $traversable)
 167      {
 168          foreach ($traversable as $key => $value) {
 169              yield $key => $value;
 170              $this->iteratorAdvanced = true;
 171          }
 172  
 173          $this->iteratorExhausted = true;
 174      }
 175  }