BufferedIterator.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Collection\Iterator;
  17. use Cake\Collection\Collection;
  18. use Countable;
  19. use Serializable;
  20. use SplDoublyLinkedList;
  21. /**
  22. * Creates an iterator from another iterator that will keep the results of the inner
  23. * iterator in memory, so that results don't have to be re-calculated.
  24. */
  25. class BufferedIterator extends Collection implements Countable, Serializable
  26. {
  27. /**
  28. * The in-memory cache containing results from previous iterators
  29. *
  30. * @var \SplDoublyLinkedList
  31. */
  32. protected $_buffer;
  33. /**
  34. * Points to the next record number that should be fetched
  35. *
  36. * @var int
  37. */
  38. protected $_index = 0;
  39. /**
  40. * Last record fetched from the inner iterator
  41. *
  42. * @var mixed
  43. */
  44. protected $_current;
  45. /**
  46. * Last key obtained from the inner iterator
  47. *
  48. * @var mixed
  49. */
  50. protected $_key;
  51. /**
  52. * Whether or not the internal iterator's rewind method was already
  53. * called
  54. *
  55. * @var bool
  56. */
  57. protected $_started = false;
  58. /**
  59. * Whether or not the internal iterator has reached its end.
  60. *
  61. * @var bool
  62. */
  63. protected $_finished = false;
  64. /**
  65. * Maintains an in-memory cache of the results yielded by the internal
  66. * iterator.
  67. *
  68. * @param iterable $items The items to be filtered.
  69. */
  70. public function __construct(iterable $items)
  71. {
  72. $this->_buffer = new SplDoublyLinkedList();
  73. parent::__construct($items);
  74. }
  75. /**
  76. * Returns the current key in the iterator
  77. *
  78. * @return mixed
  79. */
  80. public function key()
  81. {
  82. return $this->_key;
  83. }
  84. /**
  85. * Returns the current record in the iterator
  86. *
  87. * @return mixed
  88. */
  89. public function current()
  90. {
  91. return $this->_current;
  92. }
  93. /**
  94. * Rewinds the collection
  95. *
  96. * @return void
  97. */
  98. public function rewind(): void
  99. {
  100. if ($this->_index === 0 && !$this->_started) {
  101. $this->_started = true;
  102. parent::rewind();
  103. return;
  104. }
  105. $this->_index = 0;
  106. }
  107. /**
  108. * Returns whether or not the iterator has more elements
  109. *
  110. * @return bool
  111. */
  112. public function valid(): bool
  113. {
  114. if ($this->_buffer->offsetExists($this->_index)) {
  115. $current = $this->_buffer->offsetGet($this->_index);
  116. $this->_current = $current['value'];
  117. $this->_key = $current['key'];
  118. return true;
  119. }
  120. $valid = parent::valid();
  121. if ($valid) {
  122. $this->_current = parent::current();
  123. $this->_key = parent::key();
  124. $this->_buffer->push([
  125. 'key' => $this->_key,
  126. 'value' => $this->_current,
  127. ]);
  128. }
  129. $this->_finished = !$valid;
  130. return $valid;
  131. }
  132. /**
  133. * Advances the iterator pointer to the next element
  134. *
  135. * @return void
  136. */
  137. public function next(): void
  138. {
  139. $this->_index++;
  140. // Don't move inner iterator if we have more buffer
  141. if ($this->_buffer->offsetExists($this->_index)) {
  142. return;
  143. }
  144. if (!$this->_finished) {
  145. parent::next();
  146. }
  147. }
  148. /**
  149. * Returns the number or items in this collection
  150. *
  151. * @return int
  152. */
  153. public function count(): int
  154. {
  155. if (!$this->_started) {
  156. $this->rewind();
  157. }
  158. while ($this->valid()) {
  159. $this->next();
  160. }
  161. return $this->_buffer->count();
  162. }
  163. /**
  164. * Returns a string representation of this object that can be used
  165. * to reconstruct it
  166. *
  167. * @return string
  168. */
  169. public function serialize(): string
  170. {
  171. if (!$this->_finished) {
  172. $this->count();
  173. }
  174. return serialize($this->_buffer);
  175. }
  176. /**
  177. * Unserializes the passed string and rebuilds the BufferedIterator instance
  178. *
  179. * @param string $buffer The serialized buffer iterator
  180. * @return void
  181. */
  182. public function unserialize($buffer): void
  183. {
  184. $this->__construct([]);
  185. $this->_buffer = unserialize($buffer);
  186. $this->_started = true;
  187. $this->_finished = true;
  188. }
  189. }