Passed
Push — master ( da1161...08d06f )
by Smoren
02:13
created

MapAccess::set()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

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