1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Iterator; |
4: | |
5: | use Iterator; |
6: | use LogicException; |
7: | use ReturnTypeWillChange; |
8: | use Traversable; |
9: | |
10: | /** |
11: | * Iterates over the properties of an object or the elements of an array |
12: | * |
13: | * @api |
14: | * |
15: | * @implements Iterator<array-key,mixed> |
16: | */ |
17: | class GraphIterator implements Iterator |
18: | { |
19: | /** @var object|mixed[] */ |
20: | protected $Graph; |
21: | /** @var array<array-key> */ |
22: | protected array $Keys = []; |
23: | protected bool $IsObject = true; |
24: | |
25: | /** |
26: | * @param object|mixed[] $graph |
27: | */ |
28: | public function __construct($graph) |
29: | { |
30: | $this->doConstruct($graph); |
31: | } |
32: | |
33: | /** |
34: | * @param object|mixed[] $graph |
35: | */ |
36: | protected function doConstruct(&$graph): void |
37: | { |
38: | if (is_array($graph)) { |
39: | $this->Graph = &$graph; |
40: | $this->Keys = array_keys($graph); |
41: | $this->IsObject = false; |
42: | return; |
43: | } |
44: | |
45: | if ($graph instanceof Traversable) { |
46: | throw new LogicException('Traversable objects are not supported'); |
47: | } |
48: | |
49: | $this->Graph = $graph; |
50: | // @phpstan-ignore foreach.nonIterable |
51: | foreach ($graph as $key => $value) { |
52: | $this->Keys[] = $key; |
53: | } |
54: | } |
55: | |
56: | /** |
57: | * @return mixed|false |
58: | * @disregard P1038 |
59: | */ |
60: | #[ReturnTypeWillChange] |
61: | public function current() |
62: | { |
63: | $key = current($this->Keys); |
64: | if ($key === false) { |
65: | // @codeCoverageIgnoreStart |
66: | return false; |
67: | // @codeCoverageIgnoreEnd |
68: | } |
69: | |
70: | return $this->IsObject |
71: | ? $this->Graph->{$key} |
72: | // @phpstan-ignore offsetAccess.nonOffsetAccessible |
73: | : $this->Graph[$key]; |
74: | } |
75: | |
76: | /** |
77: | * @return array-key|null |
78: | * @disregard P1038 |
79: | */ |
80: | #[ReturnTypeWillChange] |
81: | public function key() |
82: | { |
83: | $key = current($this->Keys); |
84: | if ($key === false) { |
85: | // @codeCoverageIgnoreStart |
86: | return null; |
87: | // @codeCoverageIgnoreEnd |
88: | } |
89: | return $key; |
90: | } |
91: | |
92: | /** |
93: | * @inheritDoc |
94: | */ |
95: | public function next(): void |
96: | { |
97: | next($this->Keys); |
98: | } |
99: | |
100: | /** |
101: | * @inheritDoc |
102: | */ |
103: | public function rewind(): void |
104: | { |
105: | reset($this->Keys); |
106: | } |
107: | |
108: | /** |
109: | * @inheritDoc |
110: | */ |
111: | public function valid(): bool |
112: | { |
113: | return current($this->Keys) !== false; |
114: | } |
115: | } |
116: |