1<?php 2 3/** 4 * This file is part of the Nette Framework (https://nette.org) 5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com) 6 */ 7 8declare(strict_types=1); 9 10namespace Nette\Iterators; 11 12use Nette; 13 14 15/** 16 * Smarter caching iterator. 17 * 18 * @property-read bool $first 19 * @property-read bool $last 20 * @property-read bool $empty 21 * @property-read bool $odd 22 * @property-read bool $even 23 * @property-read int $counter 24 * @property-read mixed $nextKey 25 * @property-read mixed $nextValue 26 */ 27class CachingIterator extends \CachingIterator implements \Countable 28{ 29 use Nette\SmartObject; 30 31 private int $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 assert($iterator instanceof \Iterator); 45 46 } elseif ($iterator instanceof \Iterator) { 47 } elseif ($iterator instanceof \Traversable) { 48 $iterator = new \IteratorIterator($iterator); 49 } else { 50 throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, get_debug_type($iterator))); 51 } 52 53 parent::__construct($iterator, 0); 54 } 55 56 57 /** 58 * Is the current element the first one? 59 */ 60 public function isFirst(?int $gridWidth = null): bool 61 { 62 return $this->counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0); 63 } 64 65 66 /** 67 * Is the current element the last one? 68 */ 69 public function isLast(?int $gridWidth = null): bool 70 { 71 return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0); 72 } 73 74 75 /** 76 * Is the iterator empty? 77 */ 78 public function isEmpty(): bool 79 { 80 return $this->counter === 0; 81 } 82 83 84 /** 85 * Is the counter odd? 86 */ 87 public function isOdd(): bool 88 { 89 return $this->counter % 2 === 1; 90 } 91 92 93 /** 94 * Is the counter even? 95 */ 96 public function isEven(): bool 97 { 98 return $this->counter % 2 === 0; 99 } 100 101 102 /** 103 * Returns the counter. 104 */ 105 public function getCounter(): int 106 { 107 return $this->counter; 108 } 109 110 111 /** 112 * Returns the count of elements. 113 */ 114 public function count(): int 115 { 116 $inner = $this->getInnerIterator(); 117 if ($inner instanceof \Countable) { 118 return $inner->count(); 119 120 } else { 121 throw new Nette\NotSupportedException('Iterator is not countable.'); 122 } 123 } 124 125 126 /** 127 * Forwards to the next element. 128 */ 129 public function next(): void 130 { 131 parent::next(); 132 if (parent::valid()) { 133 $this->counter++; 134 } 135 } 136 137 138 /** 139 * Rewinds the Iterator. 140 */ 141 public function rewind(): void 142 { 143 parent::rewind(); 144 $this->counter = parent::valid() ? 1 : 0; 145 } 146 147 148 /** 149 * Returns the next key. 150 */ 151 public function getNextKey(): mixed 152 { 153 return $this->getInnerIterator()->key(); 154 } 155 156 157 /** 158 * Returns the next element. 159 */ 160 public function getNextValue(): mixed 161 { 162 return $this->getInnerIterator()->current(); 163 } 164} 165