Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

   1  <?php
   2  /*
   3   * Copyright 2017-present 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   *   https://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 Iterator;
  22  use IteratorIterator;
  23  use ReturnTypeWillChange;
  24  use Traversable;
  25  
  26  use function count;
  27  use function current;
  28  use function next;
  29  use function reset;
  30  
  31  /**
  32   * Iterator for wrapping a Traversable and caching its results.
  33   *
  34   * By caching results, this iterators allows a Traversable to be counted and
  35   * rewound multiple times, even if the wrapped object does not natively support
  36   * those operations (e.g. MongoDB\Driver\Cursor).
  37   *
  38   * @internal
  39   */
  40  class CachingIterator implements Countable, Iterator
  41  {
  42      private const FIELD_KEY = 0;
  43      private const FIELD_VALUE = 1;
  44  
  45      /** @var array */
  46      private $items = [];
  47  
  48      /** @var Iterator */
  49      private $iterator;
  50  
  51      /** @var boolean */
  52      private $iteratorAdvanced = false;
  53  
  54      /** @var boolean */
  55      private $iteratorExhausted = false;
  56  
  57      /**
  58       * Initialize the iterator and stores the first item in the cache. This
  59       * effectively rewinds the Traversable and the wrapping IteratorIterator.
  60       * Additionally, this mimics behavior of the SPL iterators and allows users
  61       * to omit an explicit call to rewind() before using the other methods.
  62       *
  63       * @param Traversable $traversable
  64       */
  65      public function __construct(Traversable $traversable)
  66      {
  67          $this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable);
  68  
  69          $this->iterator->rewind();
  70          $this->storeCurrentItem();
  71      }
  72  
  73      /**
  74       * @see https://php.net/countable.count
  75       */
  76      public function count(): int
  77      {
  78          $this->exhaustIterator();
  79  
  80          return count($this->items);
  81      }
  82  
  83      /**
  84       * @see https://php.net/iterator.current
  85       * @return mixed
  86       */
  87      #[ReturnTypeWillChange]
  88      public function current()
  89      {
  90          $currentItem = current($this->items);
  91  
  92          return $currentItem !== false ? $currentItem[self::FIELD_VALUE] : false;
  93      }
  94  
  95      /**
  96       * @see https://php.net/iterator.key
  97       * @return mixed
  98       */
  99      #[ReturnTypeWillChange]
 100      public function key()
 101      {
 102          $currentItem = current($this->items);
 103  
 104          return $currentItem !== false ? $currentItem[self::FIELD_KEY] : null;
 105      }
 106  
 107      /**
 108       * @see https://php.net/iterator.next
 109       */
 110      public function next(): void
 111      {
 112          if (! $this->iteratorExhausted) {
 113              $this->iteratorAdvanced = true;
 114              $this->iterator->next();
 115  
 116              $this->storeCurrentItem();
 117  
 118              $this->iteratorExhausted = ! $this->iterator->valid();
 119          }
 120  
 121          next($this->items);
 122      }
 123  
 124      /**
 125       * @see https://php.net/iterator.rewind
 126       */
 127      public function rewind(): void
 128      {
 129          /* If the iterator has advanced, exhaust it now so that future iteration
 130           * can rely on the cache.
 131           */
 132          if ($this->iteratorAdvanced) {
 133              $this->exhaustIterator();
 134          }
 135  
 136          reset($this->items);
 137      }
 138  
 139      /**
 140       * @see https://php.net/iterator.valid
 141       */
 142      public function valid(): bool
 143      {
 144          return $this->key() !== null;
 145      }
 146  
 147      /**
 148       * Ensures that the inner iterator is fully consumed and cached.
 149       */
 150      private function exhaustIterator(): void
 151      {
 152          while (! $this->iteratorExhausted) {
 153              $this->next();
 154          }
 155      }
 156  
 157      /**
 158       * Stores the current item in the cache.
 159       */
 160      private function storeCurrentItem(): void
 161      {
 162          if (! $this->iterator->valid()) {
 163              return;
 164          }
 165  
 166          // Storing a new item in the internal cache
 167          $this->items[] = [
 168              self::FIELD_KEY => $this->iterator->key(),
 169              self::FIELD_VALUE => $this->iterator->current(),
 170          ];
 171      }
 172  }