1: <?php
2:
3: /**
4: * This file is part of the Nette Framework (https://nette.org)
5: * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
6: * @package Nette\Iterators
7: */
8:
9:
10:
11: /**
12: * Smarter caching iterator.
13: *
14: * @author David Grudl
15: *
16: * @property-read bool $first
17: * @property-read bool $last
18: * @property-read bool $empty
19: * @property-read bool $odd
20: * @property-read bool $even
21: * @property-read int $counter
22: * @property-read mixed $nextKey
23: * @property-read mixed $nextValue
24: * @property-read $innerIterator
25: * @property $flags
26: * @property-read $cache
27: * @package Nette\Iterators
28: */
29: class SmartCachingIterator extends CachingIterator implements Countable
30: {
31: /** @var int */
32: private $counter = 0;
33:
34:
35: public function __construct($iterator)
36: {
37: if (is_array($iterator) || $iterator instanceof stdClass) {
38: $iterator = new ArrayIterator($iterator);
39:
40: } elseif ($iterator instanceof IteratorAggregate) {
41: do {
42: $iterator = $iterator->getIterator();
43: } while ($iterator instanceof IteratorAggregate);
44:
45: } elseif ($iterator instanceof Traversable) {
46: if (!$iterator instanceof Iterator) {
47: $iterator = new IteratorIterator($iterator);
48: }
49: } else {
50: throw new InvalidArgumentException(sprintf('Invalid argument passed to foreach resp. %s; array or Traversable expected, %s given.', __CLASS__, is_object($iterator) ? get_class($iterator) : gettype($iterator)));
51: }
52:
53: parent::__construct($iterator, 0);
54: }
55:
56:
57: /**
58: * Is the current element the first one?
59: * @param int grid width
60: * @return bool
61: */
62: public function isFirst($width = NULL)
63: {
64: return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0);
65: }
66:
67:
68: /**
69: * Is the current element the last one?
70: * @param int grid width
71: * @return bool
72: */
73: public function isLast($width = NULL)
74: {
75: return !$this->hasNext() || ($width && ($this->counter % $width) === 0);
76: }
77:
78:
79: /**
80: * Is the iterator empty?
81: * @return bool
82: */
83: public function isEmpty()
84: {
85: return $this->counter === 0;
86: }
87:
88:
89: /**
90: * Is the counter odd?
91: * @return bool
92: */
93: public function isOdd()
94: {
95: return $this->counter % 2 === 1;
96: }
97:
98:
99: /**
100: * Is the counter even?
101: * @return bool
102: */
103: public function isEven()
104: {
105: return $this->counter % 2 === 0;
106: }
107:
108:
109: /**
110: * Returns the counter.
111: * @return int
112: */
113: public function getCounter()
114: {
115: return $this->counter;
116: }
117:
118:
119: /**
120: * Returns the count of elements.
121: * @return int
122: */
123: public function count()
124: {
125: $inner = $this->getInnerIterator();
126: if ($inner instanceof Countable) {
127: return $inner->count();
128:
129: } else {
130: throw new NotSupportedException('Iterator is not countable.');
131: }
132: }
133:
134:
135: /**
136: * Forwards to the next element.
137: * @return void
138: */
139: public function next()
140: {
141: parent::next();
142: if (parent::valid()) {
143: $this->counter++;
144: }
145: }
146:
147:
148: /**
149: * Rewinds the Iterator.
150: * @return void
151: */
152: public function rewind()
153: {
154: parent::rewind();
155: $this->counter = parent::valid() ? 1 : 0;
156: }
157:
158:
159: /**
160: * Returns the next key.
161: * @return mixed
162: */
163: public function getNextKey()
164: {
165: return $this->getInnerIterator()->key();
166: }
167:
168:
169: /**
170: * Returns the next element.
171: * @return mixed
172: */
173: public function getNextValue()
174: {
175: return $this->getInnerIterator()->current();
176: }
177:
178:
179: /********************* Object behaviour ****************d*g**/
180:
181:
182: /**
183: * Call to undefined method.
184: * @param string method name
185: * @param array arguments
186: * @return mixed
187: * @throws MemberAccessException
188: */
189: public function __call($name, $args)
190: {
191: return ObjectMixin::call($this, $name, $args);
192: }
193:
194:
195: /**
196: * Returns property value. Do not call directly.
197: * @param string property name
198: * @return mixed property value
199: * @throws MemberAccessException if the property is not defined.
200: */
201: public function &__get($name)
202: {
203: return ObjectMixin::get($this, $name);
204: }
205:
206:
207: /**
208: * Sets value of a property. Do not call directly.
209: * @param string property name
210: * @param mixed property value
211: * @return void
212: * @throws MemberAccessException if the property is not defined or is read-only
213: */
214: public function __set($name, $value)
215: {
216: ObjectMixin::set($this, $name, $value);
217: }
218:
219:
220: /**
221: * Is property defined?
222: * @param string property name
223: * @return bool
224: */
225: public function __isset($name)
226: {
227: return ObjectMixin::has($this, $name);
228: }
229:
230:
231: /**
232: * Access to undeclared property.
233: * @param string property name
234: * @return void
235: * @throws MemberAccessException
236: */
237: public function __unset($name)
238: {
239: ObjectMixin::remove($this, $name);
240: }
241:
242:
243: }
244: