type-tools-php
215 строк · 5.7 Кб
1<?php
2
3declare(strict_types=1);
4
5namespace Smoren\TypeTools;
6
7use ArrayAccess;
8use Smoren\TypeTools\Exceptions\KeyError;
9use stdClass;
10
11/**
12* Tool for map-like accessing of different containers by string keys.
13*
14* Can access:
15* - properties of objects (by name or by getter);
16* - elements of arrays and ArrayAccess objects (by key).
17*/
18class MapAccess
19{
20/**
21* Returns value from the container by key or default value if key does not exist or not accessible.
22*
23* @template T
24*
25* @param array<string, T>|ArrayAccess<string, T>|object|mixed $container
26* @param string $key
27* @param T|null $defaultValue
28*
29* @return T|null
30*
31* @throws KeyError
32*/
33public static function get($container, string $key, $defaultValue = null)
34{
35switch(true) {
36case is_array($container):
37return static::getFromArray($container, $key, $defaultValue);
38case $container instanceof ArrayAccess:
39return static::getFromArrayAccess($container, $key, $defaultValue);
40case is_object($container):
41return static::getFromObject($container, $key, $defaultValue);
42}
43
44return $defaultValue;
45}
46
47/**
48* Sets value to the container by key.
49*
50* @template T
51*
52* @param array<string, T>|ArrayAccess<string, T>|object|mixed $container
53* @param string $key
54* @param T $value
55*
56* @return void
57*
58* @throws KeyError
59*/
60public static function set(&$container, string $key, $value): void
61{
62switch(true) {
63case is_array($container):
64case $container instanceof ArrayAccess:
65$container[$key] = $value;
66break;
67case is_object($container):
68static::setToObject($container, $key, $value);
69break;
70}
71}
72
73/**
74* Returns true if the accessible key exists in the container.
75*
76* @param array<string, mixed>|ArrayAccess<string, mixed>|object|mixed $container
77* @param string $key
78*
79* @return bool
80*/
81public static function exists($container, string $key): bool
82{
83switch(true) {
84case is_array($container):
85return static::existsInArray($container, $key);
86case $container instanceof ArrayAccess:
87return static::existsInArrayAccess($container, $key);
88case is_object($container):
89return static::existsInObject($container, $key);
90}
91return false;
92}
93
94/**
95* Returns value from the array by key or default value if key does not exist.
96*
97* @template T
98*
99* @param array<string, T> $container
100* @param string $key
101* @param T|null $defaultValue
102*
103* @return T|null
104*/
105protected static function getFromArray(array $container, string $key, $defaultValue)
106{
107if(static::existsInArray($container, $key)) {
108return $container[$key];
109}
110
111return $defaultValue ?? null;
112}
113
114/**
115* Returns true if the key exists in the array.
116*
117* @template T
118* @param array<string, T> $container
119* @param string $key
120*
121* @return bool
122*/
123protected static function existsInArray(array $container, string $key): bool
124{
125return array_key_exists($key, $container);
126}
127
128/**
129* Returns value from the ArrayAccess object by key or default value if key does not exist.
130*
131* @template T
132*
133* @param ArrayAccess<string, T> $container
134* @param string $key
135* @param T|null $defaultValue
136*
137* @return T|null
138*/
139protected static function getFromArrayAccess(ArrayAccess $container, string $key, $defaultValue)
140{
141if(static::existsInArrayAccess($container, $key)) {
142return $container[$key];
143}
144
145return $defaultValue ?? null;
146}
147
148/**
149* Returns true if the key exists in the ArrayAccess object.
150*
151* @template T
152*
153* @param ArrayAccess<string, T> $container
154* @param string $key
155*
156* @return bool
157*/
158protected static function existsInArrayAccess(ArrayAccess $container, string $key): bool
159{
160return $container->offsetExists($key);
161}
162
163/**
164* Returns value from the object by key or default value if key does not exist.
165*
166* @param object $container
167* @param string $key
168* @param mixed|null $defaultValue
169*
170* @return mixed|null
171*
172* @throws KeyError
173*/
174protected static function getFromObject(object $container, string $key, $defaultValue)
175{
176if(ObjectAccess::hasReadableProperty($container, $key)) {
177return ObjectAccess::getPropertyValue($container, $key);
178}
179
180return $defaultValue;
181}
182
183/**
184* Sets property value to the object if it is writable by name or by setter.
185*
186* @param object $container
187* @param string $key
188* @param mixed $value
189*
190* @return void
191*
192* @throws KeyError
193*/
194protected static function setToObject(object $container, string $key, $value): void
195{
196if(!ObjectAccess::hasWritableProperty($container, $key) && !($container instanceof stdClass)) {
197throw new KeyError("property ".get_class($container)."::{$key} is not writable");
198}
199
200ObjectAccess::setPropertyValue($container, $key, $value);
201}
202
203/**
204* Returns true if the key exists in the object.
205*
206* @param object $container
207* @param string $key
208*
209* @return bool
210*/
211protected static function existsInObject(object $container, string $key): bool
212{
213return ObjectAccess::hasReadableProperty($container, $key);
214}
215}
216