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: * The in-memory cache containing results from previous iterators
30: *
31: * @var \SplDoublyLinkedList
32: */
33: protected $_buffer;
34:
35: /**
36: * Points to the next record number that should be fetched
37: *
38: * @var int
39: */
40: protected $_index = 0;
41:
42: /**
43: * Last record fetched from the inner iterator
44: *
45: * @var mixed
46: */
47: protected $_current;
48:
49: /**
50: * Last key obtained from the inner iterator
51: *
52: * @var mixed
53: */
54: protected $_key;
55:
56: /**
57: * Whether or not the internal iterator's rewind method was already
58: * called
59: *
60: * @var bool
61: */
62: protected $_started = false;
63:
64: /**
65: * Whether or not the internal iterator has reached its end.
66: *
67: * @var bool
68: */
69: protected $_finished = false;
70:
71: /**
72: * Maintains an in-memory cache of the results yielded by the internal
73: * iterator.
74: *
75: * @param array|\Traversable $items The items to be filtered.
76: */
77: public function __construct($items)
78: {
79: $this->_buffer = new SplDoublyLinkedList();
80: parent::__construct($items);
81: }
82:
83: /**
84: * Returns the current key in the iterator
85: *
86: * @return mixed
87: */
88: public function key()
89: {
90: return $this->_key;
91: }
92:
93: /**
94: * Returns the current record in the iterator
95: *
96: * @return mixed
97: */
98: public function current()
99: {
100: return $this->_current;
101: }
102:
103: /**
104: * Rewinds the collection
105: *
106: * @return void
107: */
108: public function rewind()
109: {
110: if ($this->_index === 0 && !$this->_started) {
111: $this->_started = true;
112: parent::rewind();
113:
114: return;
115: }
116:
117: $this->_index = 0;
118: }
119:
120: /**
121: * Returns whether or not the iterator has more elements
122: *
123: * @return bool
124: */
125: public function valid()
126: {
127: if ($this->_buffer->offsetExists($this->_index)) {
128: $current = $this->_buffer->offsetGet($this->_index);
129: $this->_current = $current['value'];
130: $this->_key = $current['key'];
131:
132: return true;
133: }
134:
135: $valid = parent::valid();
136:
137: if ($valid) {
138: $this->_current = parent::current();
139: $this->_key = parent::key();
140: $this->_buffer->push([
141: 'key' => $this->_key,
142: 'value' => $this->_current
143: ]);
144: }
145:
146: $this->_finished = !$valid;
147:
148: return $valid;
149: }
150:
151: /**
152: * Advances the iterator pointer to the next element
153: *
154: * @return void
155: */
156: public function next()
157: {
158: $this->_index++;
159:
160: if (!$this->_finished) {
161: parent::next();
162: }
163: }
164:
165: /**
166: * Returns the number or items in this collection
167: *
168: * @return int
169: */
170: public function count()
171: {
172: if (!$this->_started) {
173: $this->rewind();
174: }
175:
176: while ($this->valid()) {
177: $this->next();
178: }
179:
180: return $this->_buffer->count();
181: }
182:
183: /**
184: * Returns a string representation of this object that can be used
185: * to reconstruct it
186: *
187: * @return string
188: */
189: public function serialize()
190: {
191: if (!$this->_finished) {
192: $this->count();
193: }
194:
195: return serialize($this->_buffer);
196: }
197:
198: /**
199: * Unserializes the passed string and rebuilds the BufferedIterator instance
200: *
201: * @param string $buffer The serialized buffer iterator
202: * @return void
203: */
204: public function unserialize($buffer)
205: {
206: $this->__construct([]);
207: $this->_buffer = unserialize($buffer);
208: $this->_started = true;
209: $this->_finished = true;
210: }
211: }
212: