1: <?php
2:
3: /**
4: * This file is part of the Latte (https://latte.nette.org)
5: * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6: */
7:
8: namespace Latte\Runtime;
9:
10: use Latte;
11:
12:
13: /**
14: * Smarter caching iterator.
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: * @internal
25: */
26: class CachingIterator extends \CachingIterator implements \Countable
27: {
28: use Latte\Strict;
29:
30: /** @var int */
31: private $counter = 0;
32:
33:
34: public function __construct($iterator)
35: {
36: if (is_array($iterator) || $iterator instanceof \stdClass) {
37: $iterator = new \ArrayIterator($iterator);
38:
39: } elseif ($iterator instanceof \IteratorAggregate) {
40: do {
41: $iterator = $iterator->getIterator();
42: } while ($iterator instanceof \IteratorAggregate);
43:
44: } elseif ($iterator instanceof \Traversable) {
45: if (!$iterator instanceof \Iterator) {
46: $iterator = new \IteratorIterator($iterator);
47: }
48: } else {
49: throw new \InvalidArgumentException(sprintf('Invalid argument passed to foreach; array or Traversable expected, %s given.', is_object($iterator) ? get_class($iterator) : gettype($iterator)));
50: }
51:
52: parent::__construct($iterator, 0);
53: }
54:
55:
56: /**
57: * Is the current element the first one?
58: * @param int grid width
59: * @return bool
60: */
61: public function isFirst($width = null)
62: {
63: return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0);
64: }
65:
66:
67: /**
68: * Is the current element the last one?
69: * @param int grid width
70: * @return bool
71: */
72: public function isLast($width = null)
73: {
74: return !$this->hasNext() || ($width && ($this->counter % $width) === 0);
75: }
76:
77:
78: /**
79: * Is the iterator empty?
80: * @return bool
81: */
82: public function isEmpty()
83: {
84: return $this->counter === 0;
85: }
86:
87:
88: /**
89: * Is the counter odd?
90: * @return bool
91: */
92: public function isOdd()
93: {
94: return $this->counter % 2 === 1;
95: }
96:
97:
98: /**
99: * Is the counter even?
100: * @return bool
101: */
102: public function isEven()
103: {
104: return $this->counter % 2 === 0;
105: }
106:
107:
108: /**
109: * Returns the counter.
110: * @return int
111: */
112: public function getCounter()
113: {
114: return $this->counter;
115: }
116:
117:
118: /**
119: * Returns the count of elements.
120: * @return int
121: */
122: public function count()
123: {
124: $inner = $this->getInnerIterator();
125: if ($inner instanceof \Countable) {
126: return $inner->count();
127:
128: } else {
129: throw new \LogicException('Iterator is not countable.');
130: }
131: }
132:
133:
134: /**
135: * Forwards to the next element.
136: * @return void
137: */
138: public function next()
139: {
140: parent::next();
141: if (parent::valid()) {
142: $this->counter++;
143: }
144: }
145:
146:
147: /**
148: * Rewinds the Iterator.
149: * @return void
150: */
151: public function rewind()
152: {
153: parent::rewind();
154: $this->counter = parent::valid() ? 1 : 0;
155: }
156:
157:
158: /**
159: * Returns the next key.
160: * @return mixed
161: */
162: public function getNextKey()
163: {
164: return $this->getInnerIterator()->key();
165: }
166:
167:
168: /**
169: * Returns the next element.
170: * @return mixed
171: */
172: public function getNextValue()
173: {
174: return $this->getInnerIterator()->current();
175: }
176:
177:
178: /********************* property accessor ****************d*g**/
179:
180:
181: /**
182: * Returns property value.
183: * @throws \LogicException if the property is not defined.
184: */
185: public function &__get($name)
186: {
187: if (method_exists($this, $m = 'get' . $name) || method_exists($this, $m = 'is' . $name)) {
188: $ret = $this->$m();
189: return $ret;
190: }
191: throw new \LogicException('Attempt to read undeclared property ' . get_class($this) . "::\$$name.");
192: }
193:
194:
195: /**
196: * Is property defined?
197: * @return bool
198: */
199: public function __isset($name)
200: {
201: return method_exists($this, 'get' . $name) || method_exists($this, 'is' . $name);
202: }
203: }
204: