items = $items; $this->keyField = $keyField; $this->titleField = $titleField; $this->endItemIdx = null; if ($firstItems) { foreach ($firstItems as $k => $v) { $this->firstItems[] = [$k, $v]; $this->excludedItems[] = $k; } } if ($lastItems) { foreach ($lastItems as $k => $v) { $this->lastItems[] = [$k, $v]; $this->excludedItems[] = $k; } } } /** * Rewind the Iterator to the first element. * * @return mixed */ #[\ReturnTypeWillChange] public function rewind() { $this->firstItemIdx = 0; $this->endItemIdx = null; $rewoundItem = $this->items->rewind(); if (isset($this->firstItems[$this->firstItemIdx])) { return $this->firstItems[$this->firstItemIdx][1]; } else { if ($rewoundItem) { return $this->extractValue($rewoundItem, $this->titleField); } else { if (!$this->items->valid() && $this->lastItems) { $this->endItemIdx = 0; return $this->lastItems[0][1]; } } } } /** * Return the current element. * * @return mixed */ #[\ReturnTypeWillChange] public function current() { if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) { return $this->lastItems[$this->endItemIdx][1]; } else { if (isset($this->firstItems[$this->firstItemIdx])) { return $this->firstItems[$this->firstItemIdx][1]; } } return $this->extractValue($this->items->current(), $this->titleField); } /** * Extracts a value from an item in the list, where the item is either an * object or array. * * @param array|object $item * @param string $key * @return mixed */ protected function extractValue($item, $key) { if (is_object($item)) { if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) { return $item->{$key}(); } return $item->{$key}; } else { if (array_key_exists($key, $item ?? [])) { return $item[$key]; } } } /** * Return the key of the current element. * * @return string */ #[\ReturnTypeWillChange] public function key() { if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) { return $this->lastItems[$this->endItemIdx][0]; } else { if (isset($this->firstItems[$this->firstItemIdx])) { return $this->firstItems[$this->firstItemIdx][0]; } else { return $this->extractValue($this->items->current(), $this->keyField); } } } /** * Move forward to next element. * * @return mixed */ #[\ReturnTypeWillChange] public function next() { $this->firstItemIdx++; if (isset($this->firstItems[$this->firstItemIdx])) { return $this->firstItems[$this->firstItemIdx][1]; } else { if (!isset($this->firstItems[$this->firstItemIdx - 1])) { $this->items->next(); } if ($this->excludedItems) { while (($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems ?? [], true)) { $this->items->next(); } } } if (!$this->items->valid()) { // iterator has passed the preface items, off the end of the items // list. Track through the end items to go through to the next if ($this->endItemIdx === null) { $this->endItemIdx = -1; } $this->endItemIdx++; if (isset($this->lastItems[$this->endItemIdx])) { return $this->lastItems[$this->endItemIdx]; } return false; } } /** * Checks if current position is valid. * * @return boolean */ #[\ReturnTypeWillChange] public function valid() { return ( (isset($this->firstItems[$this->firstItemIdx])) || (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) || $this->items->valid() ); } }