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

nette / latte / 22836865504

09 Mar 2026 03:15AM UTC coverage: 94.972% (+0.2%) from 94.76%
22836865504

push

github

dg
github actions: code coverage job is non-blocking

5610 of 5907 relevant lines covered (94.97%)

0.95 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
 * @extends \CachingIterator<mixed, mixed, \Iterator<mixed, mixed>>
27
 * @internal
28
 */
29
class CachingIterator extends \CachingIterator implements \Countable
30
{
31
        private int $counter = 0;
32
        private ?self $parent = null;
33

34

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

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

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

56

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

65

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

74

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

83

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

92

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

101

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

110

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

119

120
        /**
121
         * Decrements the counter (minimum 0) to skip the current iteration in counting.
122
         */
123
        public function skipRound(): void
124
        {
125
                $this->counter = max($this->counter - 1, 0);
1✔
126
        }
1✔
127

128

129
        public function __toString(): string
130
        {
131
                return (string) $this->counter;
1✔
132
        }
133

134

135
        public function count(): int
136
        {
137
                $inner = $this->getInnerIterator();
×
138
                if ($inner instanceof \Countable) {
×
139
                        return $inner->count();
×
140

141
                } else {
142
                        throw new \LogicException('Iterator is not countable.');
×
143
                }
144
        }
145

146

147
        /**
148
         * Forwards to the next element.
149
         */
150
        public function next(): void
151
        {
152
                parent::next();
1✔
153
                if (parent::valid()) {
1✔
154
                        $this->counter++;
1✔
155
                }
156
        }
1✔
157

158

159
        /**
160
         * Rewinds the Iterator.
161
         */
162
        public function rewind(): void
163
        {
164
                parent::rewind();
1✔
165
                $this->counter = parent::valid() ? 1 : 0;
1✔
166
        }
1✔
167

168

169
        /**
170
         * Returns the next key or null if position is not valid.
171
         */
172
        public function getNextKey(): mixed
173
        {
174
                $iterator = $this->getInnerIterator();
1✔
175
                return $iterator->valid() ? $iterator->key() : null;
1✔
176
        }
177

178

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

188

189
        /**
190
         * Returns the iterator surrounding the current one.
191
         */
192
        public function getParent(): ?self
193
        {
194
                return $this->parent;
1✔
195
        }
196

197

198
        /********************* property accessor ****************d*g**/
199

200

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

211
                throw new \LogicException('Attempt to read undeclared property ' . static::class . "::\$$name.");
×
212
        }
213

214

215
        /**
216
         * Is property defined?
217
         */
218
        public function __isset(string $name): bool
219
        {
220
                return method_exists($this, 'get' . $name) || method_exists($this, 'is' . $name);
×
221
        }
222
}
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