1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Core; |
4: | |
5: | use Salient\Contract\Core\GraphInterface; |
6: | use Salient\Utility\Exception\InvalidArgumentTypeException; |
7: | use OutOfRangeException; |
8: | use ReturnTypeWillChange; |
9: | |
10: | |
11: | |
12: | |
13: | class Graph implements GraphInterface |
14: | { |
15: | protected object $Object; |
16: | |
17: | protected array $Array; |
18: | protected bool $IsObject; |
19: | protected bool $AddMissingProperties; |
20: | protected bool $AddMissingKeys; |
21: | |
22: | protected array $Path = []; |
23: | |
24: | |
25: | |
26: | |
27: | public function __construct( |
28: | &$value = [], |
29: | bool $addMissingKeys = false, |
30: | bool $addMissingProperties = false |
31: | ) { |
32: | if (is_object($value)) { |
33: | $this->Object = $value; |
34: | $this->IsObject = true; |
35: | } elseif (is_array($value)) { |
36: | $this->Array = &$value; |
37: | $this->IsObject = false; |
38: | } else { |
39: | throw new InvalidArgumentTypeException(1, 'value', 'mixed[]|object', $value); |
40: | } |
41: | $this->AddMissingProperties = $addMissingProperties; |
42: | $this->AddMissingKeys = $addMissingKeys; |
43: | } |
44: | |
45: | |
46: | |
47: | |
48: | public function getValue() |
49: | { |
50: | return $this->IsObject ? $this->Object : $this->Array; |
51: | } |
52: | |
53: | |
54: | |
55: | |
56: | public function getPath(): array |
57: | { |
58: | return $this->Path; |
59: | } |
60: | |
61: | |
62: | |
63: | |
64: | public function offsetExists($offset): bool |
65: | { |
66: | return $this->IsObject |
67: | ? property_exists($this->Object, (string) $offset) |
68: | : array_key_exists($offset, $this->Array); |
69: | } |
70: | |
71: | |
72: | |
73: | |
74: | |
75: | #[ReturnTypeWillChange] |
76: | public function offsetGet($offset) |
77: | { |
78: | if (!$this->offsetExists($offset)) { |
79: | if ( |
80: | ($this->IsObject && !$this->AddMissingProperties) |
81: | || (!$this->IsObject && !$this->AddMissingKeys) |
82: | ) { |
83: | throw new OutOfRangeException(sprintf('Offset not found: %s', $offset)); |
84: | } |
85: | if ($this->IsObject) { |
86: | $this->Object->$offset = []; |
87: | } else { |
88: | $this->Array[$offset] = []; |
89: | } |
90: | } |
91: | |
92: | $value = $this->IsObject |
93: | ? $this->Object->$offset |
94: | : $this->Array[$offset]; |
95: | |
96: | if (!is_object($value) && !is_array($value)) { |
97: | return $value; |
98: | } |
99: | |
100: | $value = $this->IsObject |
101: | ? new static($this->Object->$offset) |
102: | : new static($this->Array[$offset]); |
103: | |
104: | $value->AddMissingProperties = $this->AddMissingProperties; |
105: | $value->AddMissingKeys = $this->AddMissingKeys; |
106: | $value->Path = $this->Path; |
107: | $value->Path[] = $offset; |
108: | return $value; |
109: | } |
110: | |
111: | |
112: | |
113: | |
114: | public function offsetSet($offset, $value): void |
115: | { |
116: | if ($this->IsObject) { |
117: | if ($offset === null) { |
118: | throw new OutOfRangeException('Invalid offset'); |
119: | } |
120: | $this->Object->$offset = $value; |
121: | } elseif ($offset === null) { |
122: | $this->Array[] = $value; |
123: | } else { |
124: | $this->Array[$offset] = $value; |
125: | } |
126: | } |
127: | |
128: | |
129: | |
130: | |
131: | public function offsetUnset($offset): void |
132: | { |
133: | if ($this->IsObject) { |
134: | unset($this->Object->$offset); |
135: | } else { |
136: | unset($this->Array[$offset]); |
137: | } |
138: | } |
139: | } |
140: | |