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: