• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / latte / 22359082277

24 Feb 2026 04:03PM UTC coverage: 93.959% (+0.05%) from 93.907%
22359082277

push

github

dg
fixed operator ! priority

5273 of 5612 relevant lines covered (93.96%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.78
/src/Latte/Essential/CachingIterator.php
1
<?php declare(strict_types=1);
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\Essential;
9

10
use function get_debug_type, is_array, max, method_exists, sprintf;
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 int $counter0
23
 * @property-read mixed $nextKey
24
 * @property-read mixed $nextValue
25
 * @property-read ?self $parent
26
 * @internal
27
 */
28
class CachingIterator extends \CachingIterator implements \Countable
29
{
30
        private int $counter = 0;
31
        private ?self $parent = null;
32

33

34
        public function __construct(mixed $iterator, ?self $parent = null)
1✔
35
        {
36
                if (is_array($iterator) || $iterator instanceof \stdClass) {
1✔
37
                        $iterator = new \ArrayIterator((array) $iterator);
1✔
38

39
                } elseif ($iterator instanceof \IteratorAggregate) {
1✔
40
                        do {
41
                                $iterator = $iterator->getIterator();
1✔
42
                        } while (!$iterator instanceof \Iterator);
1✔
43
                } elseif ($iterator instanceof \Traversable) {
1✔
44
                        if (!$iterator instanceof \Iterator) {
1✔
45
                                $iterator = new \IteratorIterator($iterator);
×
46
                        }
47
                } else {
48
                        throw new \InvalidArgumentException(sprintf('Invalid argument passed to foreach; array or Traversable expected, %s given.', get_debug_type($iterator)));
1✔
49
                }
50

51
                parent::__construct($iterator, 0);
1✔
52
                $this->parent = $parent;
1✔
53
        }
1✔
54

55

56
        /**
57
         * Is the current element the first one?
58
         */
59
        public function isFirst(?int $width = null): bool
1✔
60
        {
61
                return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0);
1✔
62
        }
63

64

65
        /**
66
         * Is the current element the last one?
67
         */
68
        public function isLast(?int $width = null): bool
1✔
69
        {
70
                return !$this->hasNext() || ($width && ($this->counter % $width) === 0);
1✔
71
        }
72

73

74
        /**
75
         * Is the iterator empty?
76
         */
77
        public function isEmpty(): bool
78
        {
79
                return $this->counter === 0;
1✔
80
        }
81

82

83
        /**
84
         * Is the counter odd?
85
         */
86
        public function isOdd(): bool
87
        {
88
                return $this->counter % 2 === 1;
1✔
89
        }
90

91

92
        /**
93
         * Is the counter even?
94
         */
95
        public function isEven(): bool
96
        {
97
                return $this->counter % 2 === 0;
1✔
98
        }
99

100

101
        /**
102
         * Returns the 1-indexed counter.
103
         */
104
        public function getCounter(): int
105
        {
106
                return $this->counter;
1✔
107
        }
108

109

110
        /**
111
         * Returns the 0-indexed counter.
112
         */
113
        public function getCounter0(): int
114
        {
115
                return max(0, $this->counter - 1);
1✔
116
        }
117

118

119
        /**
120
         * Decrements counter.
121
         */
122
        public function skipRound(): void
123
        {
124
                $this->counter = max($this->counter - 1, 0);
1✔
125
        }
1✔
126

127

128
        /**
129
         * Returns the counter as string
130
         */
131
        public function __toString(): string
132
        {
133
                return (string) $this->counter;
1✔
134
        }
135

136

137
        /**
138
         * Returns the count of elements.
139
         */
140
        public function count(): int
141
        {
142
                $inner = $this->getInnerIterator();
×
143
                if ($inner instanceof \Countable) {
×
144
                        return $inner->count();
×
145

146
                } else {
147
                        throw new \LogicException('Iterator is not countable.');
×
148
                }
149
        }
150

151

152
        /**
153
         * Forwards to the next element.
154
         */
155
        public function next(): void
156
        {
157
                parent::next();
1✔
158
                if (parent::valid()) {
1✔
159
                        $this->counter++;
1✔
160
                }
161
        }
1✔
162

163

164
        /**
165
         * Rewinds the Iterator.
166
         */
167
        public function rewind(): void
168
        {
169
                parent::rewind();
1✔
170
                $this->counter = parent::valid() ? 1 : 0;
1✔
171
        }
1✔
172

173

174
        /**
175
         * Returns the next key or null if position is not valid.
176
         */
177
        public function getNextKey(): mixed
178
        {
179
                $iterator = $this->getInnerIterator();
1✔
180
                return $iterator->valid() ? $iterator->key() : null;
1✔
181
        }
182

183

184
        /**
185
         * Returns the next element or null if position is not valid.
186
         */
187
        public function getNextValue(): mixed
188
        {
189
                $iterator = $this->getInnerIterator();
1✔
190
                return $iterator->valid() ? $iterator->current() : null;
1✔
191
        }
192

193

194
        /**
195
         * Returns the iterator surrounding the current one.
196
         */
197
        public function getParent(): ?self
198
        {
199
                return $this->parent;
1✔
200
        }
201

202

203
        /********************* property accessor ****************d*g**/
204

205

206
        /**
207
         * Returns property value.
208
         * @throws \LogicException if the property is not defined.
209
         */
210
        public function __get(string $name): mixed
1✔
211
        {
212
                if (method_exists($this, $m = 'get' . $name) || method_exists($this, $m = 'is' . $name)) {
1✔
213
                        return $this->$m();
1✔
214
                }
215

216
                throw new \LogicException('Attempt to read undeclared property ' . static::class . "::\$$name.");
×
217
        }
218

219

220
        /**
221
         * Is property defined?
222
         */
223
        public function __isset(string $name): bool
224
        {
225
                return method_exists($this, 'get' . $name) || method_exists($this, 'is' . $name);
×
226
        }
227
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc