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