schemator-php
333 строки · 9.8 Кб
1<?php
2
3declare(strict_types=1);
4
5namespace Smoren\Schemator\Helpers;
6
7use ArrayAccess;
8use Smoren\Schemator\Interfaces\ProxyInterface;
9use Smoren\Schemator\Structs\ObjectPropertyProxy;
10use stdClass;
11
12/**
13* Tool for map-like accessing of different containers by string keys.
14*
15* Can access:
16* - properties of objects (by name or by getter);
17* - elements of arrays and ArrayAccess objects (by key).
18*
19* @template TKey of string|int
20* @template TValue of mixed
21*/
22class ContainerAccessHelper
23{
24/**
25* Returns value from the container by key or default value if key does not exist or not accessible.
26*
27* @param array<TKey, TValue>|ArrayAccess<TKey, TValue>|object|mixed $container
28* @param TKey $key
29* @param TValue|null $defaultValue
30*
31* @return TValue|null
32*
33* @throws \InvalidArgumentException
34*/
35public static function get($container, $key, $defaultValue = null)
36{
37switch (true) {
38case is_array($container):
39return static::getFromArray($container, $key, $defaultValue);
40case $container instanceof ArrayAccess:
41return static::getFromArrayAccess($container, $key, $defaultValue);
42case is_object($container):
43return static::getFromObject($container, $key, $defaultValue);
44}
45
46return $defaultValue;
47}
48
49/**
50* Returns value from the container by key (sets and returns default value if key does not exist).
51*
52* @param array<TKey, TValue>|ArrayAccess<TKey, TValue>|object|mixed $container
53* @param TKey $key
54* @param TValue|null $defaultValue
55*
56* @return TValue|ProxyInterface<TValue>|null
57*
58* @throws \InvalidArgumentException
59*/
60public static function &getRef(&$container, $key, $defaultValue = null)
61{
62switch (true) {
63case is_array($container):
64return static::getRefFromArray($container, $key, $defaultValue);
65case $container instanceof ArrayAccess:
66return static::getRefFromArrayAccess($container, $key, $defaultValue);
67case is_object($container):
68return static::getRefFromObject($container, $key, $defaultValue);
69}
70
71$type = gettype($container);
72throw new \InvalidArgumentException("Cannot get ref to key '{$key}' from container of type '{$type}'");
73}
74
75/**
76* Sets value to the container by key.
77*
78* @param array<TKey, TValue>|ArrayAccess<TKey, TValue>|object $container
79* @param TKey $key
80* @param TValue $value
81*
82* @return void
83*
84* @throws \InvalidArgumentException
85*/
86public static function set(&$container, $key, $value): void
87{
88switch (true) {
89case is_array($container):
90case $container instanceof ArrayAccess:
91$container[$key] = $value;
92break;
93case is_object($container):
94static::setToObject($container, $key, $value);
95break;
96default:
97$type = gettype($container);
98throw new \InvalidArgumentException("Cannot set value to variable of type '{$type}'");
99}
100}
101
102/**
103* Deletes key from the container.
104*
105* @param array<TKey, TValue>|ArrayAccess<TKey, TValue>|object $container
106* @param TKey $key
107*
108* @return void
109*
110* @throws \InvalidArgumentException
111*/
112public static function delete(&$container, $key): void
113{
114switch (true) {
115case is_array($container):
116if (array_key_exists($key, $container)) {
117unset($container[$key]);
118}
119break;
120case $container instanceof ArrayAccess:
121if ($container->offsetExists($key)) {
122$container->offsetUnset($key);
123}
124break;
125case $container instanceof stdClass:
126unset($container->{$key});
127break;
128default:
129$type = gettype($container);
130throw new \InvalidArgumentException("Cannot delete key from variable of type '{$type}'");
131}
132}
133
134/**
135* Returns true if the accessible key exists in the container.
136*
137* @param array<TKey, TValue>|ArrayAccess<TKey, TValue>|object|mixed $container
138* @param TKey $key
139*
140* @return bool
141*/
142public static function exist($container, $key): bool
143{
144switch (true) {
145case is_array($container):
146return static::existsInArray($container, $key);
147case $container instanceof ArrayAccess:
148return static::existsInArrayAccess($container, $key);
149case is_object($container):
150return static::existsInObject($container, $key);
151}
152return false;
153}
154
155/**
156* @param mixed $container
157* @return bool
158*/
159public static function isArrayAccessible($container): bool
160{
161return is_array($container) || ($container instanceof ArrayAccess);
162}
163
164/**
165* Returns value from the array by key or default value if key does not exist.
166*
167* @param array<TKey, TValue> $container
168* @param TKey $key
169* @param TValue|null $defaultValue
170*
171* @return TValue|null
172*/
173protected static function getFromArray(array $container, $key, $defaultValue)
174{
175if (static::existsInArray($container, $key)) {
176return $container[$key];
177}
178
179return $defaultValue ?? null;
180}
181
182/**
183* Returns reference to value from the array by key (sets and returns default value if key does not exist).
184*
185* @param array<TKey, TValue> $container
186* @param TKey $key
187* @param TValue|null $defaultValue
188*
189* @return TValue|null
190*/
191protected static function &getRefFromArray(array &$container, $key, $defaultValue)
192{
193if (!static::existsInArray($container, $key)) {
194$container[$key] = $defaultValue;
195}
196
197return $container[$key];
198}
199
200/**
201* Returns true if the key exists in the array.
202*
203* @param array<TKey, TValue> $container
204* @param TKey $key
205*
206* @return bool
207*/
208protected static function existsInArray(array $container, $key): bool
209{
210return array_key_exists($key, $container);
211}
212
213/**
214* Returns value from the ArrayAccess object by key or default value if key does not exist.
215*
216* @param ArrayAccess<TKey, TValue> $container
217* @param TKey $key
218* @param TValue|null $defaultValue
219*
220* @return TValue|null
221*/
222protected static function getFromArrayAccess(ArrayAccess $container, $key, $defaultValue)
223{
224if (static::existsInArrayAccess($container, $key)) {
225return $container[$key];
226}
227
228return $defaultValue ?? null;
229}
230
231/**
232* Returns reference to value from the ArrayAccess object by key
233* (sets and returns default value if key does not exist).
234*
235* @param ArrayAccess<TKey, TValue> $container
236* @param TKey $key
237* @param TValue|null $defaultValue
238*
239* @return TValue|null
240*/
241protected static function &getRefFromArrayAccess(ArrayAccess &$container, $key, $defaultValue)
242{
243if (!static::existsInArrayAccess($container, $key)) {
244/** @var TValue $defaultValue */
245$container[$key] = $defaultValue;
246}
247
248return $container[$key];
249}
250
251/**
252* Returns true if the key exists in the ArrayAccess object.
253*
254* @param ArrayAccess<TKey, TValue> $container
255* @param TKey $key
256*
257* @return bool
258*/
259protected static function existsInArrayAccess(ArrayAccess $container, $key): bool
260{
261return $container->offsetExists($key);
262}
263
264/**
265* Returns value from the object by key or default value if key does not exist.
266*
267* @param object $container
268* @param TKey $key
269* @param TValue|null $defaultValue
270*
271* @return TValue|null
272*
273* @throws \InvalidArgumentException
274*/
275protected static function getFromObject(object $container, $key, $defaultValue)
276{
277if (ObjectAccessHelper::hasReadableProperty($container, strval($key))) {
278return ObjectAccessHelper::getPropertyValue($container, strval($key));
279}
280
281return $defaultValue;
282}
283
284/**
285* Returns value from the object by key or default value if key does not exist.
286*
287* @param object $container
288* @param TKey $key
289* @param TValue|null $defaultValue
290*
291* @return TValue|ProxyInterface<TValue>|null
292*
293* @throws \InvalidArgumentException
294*/
295protected static function &getRefFromObject(object &$container, $key, $defaultValue)
296{
297return ObjectAccessHelper::getPropertyRef($container, strval($key), $defaultValue);
298}
299
300/**
301* Sets property value to the object if it is writable by name or by setter.
302*
303* @param object $container
304* @param TKey $key
305* @param TValue $value
306*
307* @return void
308*
309* @throws \InvalidArgumentException
310*/
311protected static function setToObject(object $container, $key, $value): void
312{
313if (!ObjectAccessHelper::hasWritableProperty($container, strval($key)) && !($container instanceof stdClass)) {
314$className = get_class($container);
315throw new \InvalidArgumentException("Property '{$className}::{$key}' is not writable");
316}
317
318ObjectAccessHelper::setPropertyValue($container, strval($key), $value);
319}
320
321/**
322* Returns true if the key exists in the object.
323*
324* @param object $container
325* @param TKey $key
326*
327* @return bool
328*/
329protected static function existsInObject(object $container, $key): bool
330{
331return ObjectAccessHelper::hasReadableProperty($container, strval($key));
332}
333}
334