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: | * Creates a new GraphIterator object |
27: | * |
28: | * @param object|mixed[] $graph |
29: | */ |
30: | public function __construct($graph) |
31: | { |
32: | $this->doConstruct($graph); |
33: | } |
34: | |
35: | /** |
36: | * @param object|mixed[] $graph |
37: | */ |
38: | protected function doConstruct(&$graph): void |
39: | { |
40: | if (is_array($graph)) { |
41: | $this->Graph = &$graph; |
42: | $this->Keys = array_keys($graph); |
43: | $this->IsObject = false; |
44: | return; |
45: | } |
46: | |
47: | if ($graph instanceof Traversable) { |
48: | throw new LogicException('Traversable objects are not supported'); |
49: | } |
50: | |
51: | $this->Graph = $graph; |
52: | // @phpstan-ignore-next-line |
53: | foreach ($graph as $key => $value) { |
54: | $this->Keys[] = $key; |
55: | } |
56: | } |
57: | |
58: | /** |
59: | * @return mixed|false |
60: | * @disregard P1038 |
61: | */ |
62: | #[ReturnTypeWillChange] |
63: | public function current() |
64: | { |
65: | $key = current($this->Keys); |
66: | if ($key === false) { |
67: | // @codeCoverageIgnoreStart |
68: | return false; |
69: | // @codeCoverageIgnoreEnd |
70: | } |
71: | |
72: | return $this->IsObject |
73: | ? $this->Graph->{$key} |
74: | // @phpstan-ignore-next-line |
75: | : $this->Graph[$key]; |
76: | } |
77: | |
78: | /** |
79: | * @return array-key|null |
80: | * @disregard P1038 |
81: | */ |
82: | #[ReturnTypeWillChange] |
83: | public function key() |
84: | { |
85: | $key = current($this->Keys); |
86: | if ($key === false) { |
87: | // @codeCoverageIgnoreStart |
88: | return null; |
89: | // @codeCoverageIgnoreEnd |
90: | } |
91: | return $key; |
92: | } |
93: | |
94: | /** |
95: | * @inheritDoc |
96: | */ |
97: | public function next(): void |
98: | { |
99: | next($this->Keys); |
100: | } |
101: | |
102: | /** |
103: | * @inheritDoc |
104: | */ |
105: | public function rewind(): void |
106: | { |
107: | reset($this->Keys); |
108: | } |
109: | |
110: | /** |
111: | * @inheritDoc |
112: | */ |
113: | public function valid(): bool |
114: | { |
115: | return current($this->Keys) !== false; |
116: | } |
117: | } |
118: |