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: