Issues (61)

src/WebServCo/Framework/ArrayStorage.php (4 issues)

Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WebServCo\Framework;
6
7
use WebServCo\Framework\Exceptions\ArrayStorageException;
8
9
final class ArrayStorage
10
{
11
    /**
12
     * Add data to an existing key of a storage array.
13
     *
14
     * @param array<mixed> $storage
15
     * @param mixed $setting Can be an array, a string,
16
     *                          or a special formatted string
17
     *                          (eg 'i18n/lang').
18
     * @param mixed $data
19
     * @return array<mixed>
20
     * @throws \WebServCo\Framework\Exceptions\ArrayStorageException
21
     */
22
    public static function add(array $storage, $setting, $data): array
23
    {
24
        if (!\is_array($storage) || empty($setting)) {
0 ignored issues
show
The condition is_array($storage) is always true.
Loading history...
25
            throw new ArrayStorageException('Invalid parameters specified.');
26
        }
27
        $setting = self::parseSetting($setting);
28
        $newData = [$data];
29
        if (self::has($storage, $setting)) {
30
            $existingData = self::get($storage, $setting);
31
            if (!\is_array($existingData)) {
32
                throw new ArrayStorageException('Invalid existing data type.');
33
            }
34
            $newData = \array_merge($existingData, $newData);
35
        }
36
        return self::set($storage, $setting, $newData);
37
    }
38
39
    /**
40
     * Append data to a storage array.
41
     *
42
     * @param array<mixed> $storage
43
     * @param mixed $data
44
     * @return array<mixed>
45
     * @throws \WebServCo\Framework\Exceptions\ArrayStorageException
46
     */
47
    public static function append(array $storage, $data = []): array
48
    {
49
        if (!\is_array($storage) || !\is_array($data)) {
0 ignored issues
show
The condition is_array($storage) is always true.
Loading history...
50
            throw new ArrayStorageException('Invalid parameters specified.');
51
        }
52
        foreach ($data as $setting => $value) {
53
            $storage[$setting] = \array_key_exists($setting, $storage) &&
54
                \is_array($storage[$setting]) &&
55
                \is_array($value)
56
                 ? self::append($storage[$setting], $value)
57
                 : $value;
58
        }
59
        return $storage;
60
    }
61
62
    /**
63
     * Retrieve a value from a storage array.
64
     *
65
     * @param mixed $storage
66
     * @param mixed $setting Can be an array, a string,
67
     *                          or a special formatted string
68
     *                          (eg 'i18n/lang').
69
     * @param mixed $defaultValue
70
     * @return mixed
71
     */
72
    public static function get($storage, $setting = null, $defaultValue = null)
73
    {
74
        $setting = self::parseSetting($setting);
75
76
        if (!isset($setting) || \is_bool($setting) || empty($storage)) {
77
            // use "!isset" for `$setting` and not "empty" (can be 0)
78
            // `is_bool` check: handle wrong $setting type
79
            //     prevents: "array_key_exists(): The first argument should be either a string or an integer"
80
            return $defaultValue;
81
        }
82
83
        if (!\is_array($storage)) {
84
            return $defaultValue;
85
        }
86
87
        /**
88
         * If $setting is an array, process it recursively.
89
         */
90
        if (\is_array($setting)) {
91
            /**
92
             * Check if we have the first $setting element in the
93
             * configuration data array.
94
             */
95
            if (\array_key_exists(0, $setting) && \array_key_exists($setting[0], $storage)) {
96
                /**
97
                 * Remove first element from $setting.
98
                 */
99
                $key = \array_shift($setting);
100
                /**
101
                 * At the end of the recursion $setting will be
102
                 * an empty array. In this case we simply return the
103
                 * current configuration data.
104
                 */
105
                if (empty($setting)) {
106
                    return false !== $storage[$key]
107
                        ? $storage[$key]
108
                        : $defaultValue;
109
                }
110
                /**
111
                 * Go down one element in the configuration data
112
                 * and call the method again, with the remainig setting.
113
                 */
114
                return self::get($storage[$key], $setting, $defaultValue);
115
            }
116
            /**
117
             * The requested setting doesn't exist in our
118
             * configuration data array.
119
             */
120
            return $defaultValue;
121
        }
122
123
        /**
124
         * If we arrive here, $setting must be a simple string.
125
         */
126
        if (\array_key_exists($setting, $storage)) {
127
            return $storage[$setting];
128
        }
129
130
        /**
131
         * If we got this far, there is no data to return.
132
         */
133
        return $defaultValue;
134
    }
135
136
    /**
137
     * Retrieve a value from a storage array.
138
     *
139
     * Returns $defaultValue if $setting is empty.
140
     *
141
     * @param mixed $storage
142
     * @param mixed $setting Can be an array, a string,
143
     *                          or a special formatted string
144
     *                          (eg 'i18n/lang').
145
     * @param mixed $defaultValue
146
     * @return mixed
147
     */
148
    public static function getElse($storage, $setting = null, $defaultValue = null)
149
    {
150
        $data = self::get($storage, $setting, $defaultValue);
151
        return !empty($data)
152
            ? $data
153
            : $defaultValue;
154
    }
155
156
    /**
157
    * @param mixed $storage
158
    * @param mixed $setting Can be an array, a string,
159
    *                          or a special formatted string
160
    *                          (eg 'i18n/lang').
161
    */
162
    public static function has($storage, $setting): bool
163
    {
164
        $value = 'WSFW_NOEXIST';
165
        $check = self::get($storage, $setting, $value);
166
        return $check !== $value;
167
    }
168
169
    /**
170
     * Removes a setting from a storage array.
171
     *
172
     * @param array<mixed> $storage
173
     * @param mixed $setting Can be an array, a string,
174
     *                          or a special formatted string
175
     *                          (eg 'i18n/lang').
176
     * @return array<mixed> The updated storage array.
177
     * @throws \WebServCo\Framework\Exceptions\ArrayStorageException
178
     */
179
    public static function remove(array $storage, $setting): array
180
    {
181
        if (!\is_array($storage) || empty($setting)) {
0 ignored issues
show
The condition is_array($storage) is always true.
Loading history...
182
            throw new ArrayStorageException('Invalid parameters specified.');
183
        }
184
185
        $setting = self::parseSetting($setting);
186
187
        if (empty($setting)) {
188
            throw new ArrayStorageException('Empty setting.');
189
        }
190
191
        if (\is_array($setting)) {
192
            return self::removeByIndex($storage, $setting);
193
        }
194
        if (!\array_key_exists($setting, $storage)) {
195
            throw new ArrayStorageException(
196
                \sprintf('setting "%s" does not exist in storage object.', $setting),
197
            );
198
        }
199
        unset($storage[$setting]);
200
        return $storage;
201
    }
202
203
    /**
204
     * Sets a value in a storage array.
205
     *
206
     * @param array<mixed> $storage
207
     * @param mixed $setting Can be an array, a string,
208
     *                          or a special formatted string
209
     *                          (eg 'i18n/lang').
210
     * @param mixed $value The value to be stored.
211
     * @return array<mixed> The storage array with new data.
212
     * @throws \WebServCo\Framework\Exceptions\ArrayStorageException
213
     */
214
    public static function set(array $storage, $setting, $value): array
215
    {
216
        if (!\is_array($storage) || empty($setting)) {
0 ignored issues
show
The condition is_array($storage) is always true.
Loading history...
217
            throw new ArrayStorageException('Invalid parameters specified.');
218
        }
219
        $setting = self::parseSetting($setting);
220
        if (\is_array($setting)) {
221
            // phpcs:ignore SlevomatCodingStandard.PHP.DisallowReference.DisallowedAssigningByReference
222
            $reference = &$storage;
223
            foreach ($setting as $item) {
224
                if (!\is_array($reference)) {
225
                    $reference = [];
226
                }
227
                // phpcs:ignore SlevomatCodingStandard.PHP.DisallowReference.DisallowedAssigningByReference
228
                $reference = &$reference[$item];
229
            }
230
            $reference = $value;
231
            unset($reference);
232
            return $storage;
233
        }
234
        $storage[$setting] = $value;
235
        return $storage;
236
    }
237
238
    /**
239
     * Remove index from multi-dimensional array.
240
     *
241
     * https://stackoverflow.com/questions/26661828/
242
     *
243
     * @param array<mixed> $array
244
     *   The array to remove the index from.
245
     * @param array<int,string> $indices
246
     *   Indexed array containing the indices chain up to the index that should be
247
     *   removed.
248
     * @return array<mixed>
249
     *   The array with the index removed.
250
     * @throws \WebServCo\Framework\Exceptions\ArrayStorageException
251
     *   If the index does not exist within the array.
252
     */
253
    protected static function removeByIndex(array $array, array $indices): array
254
    {
255
        // Create a reference to the original array.
256
        // phpcs:ignore SlevomatCodingStandard.PHP.DisallowReference.DisallowedAssigningByReference
257
        $a = &$array;
258
        // Count all passed indices, remove one because arrays are zero based.
259
        $c = \count($indices) - 1;
260
        // Iterate over all passed indices.
261
        // phpcs:ignore SlevomatCodingStandard.Operators.DisallowIncrementAndDecrementOperators.DisallowedPreIncrementOperator
262
        for ($i = 0; $i <= $c; ++$i) {
263
            // Make sure the index to go down for deletion actually exists.
264
            if (!\array_key_exists($indices[$i], $a)) {
265
                throw new ArrayStorageException(
266
                    \sprintf('"%s" does not exist in storage object.', $indices[$i]),
267
                );
268
            }
269
            // This is the target if we reached the last index that was passed.
270
            if ($i === $c) {
271
                unset($a[$indices[$i]]);
272
            } elseif (\is_array($a[$indices[$i]])) {
273
                // Make sure we have an array to go further down.
274
                // phpcs:ignore SlevomatCodingStandard.PHP.DisallowReference.DisallowedAssigningByReference
275
                $a = &$a[$indices[$i]];
276
            } else {
277
                throw new ArrayStorageException(
278
                    \sprintf('"%s" does not exist in storage object.', $indices[$i]),
279
                );
280
            }
281
        }
282
        return $array;
283
    }
284
285
    /**
286
     * Parse the setting key to make sure it's a simple string
287
     * or an array.
288
     *
289
     * @param mixed $setting
290
     * @return mixed
291
     */
292
    private static function parseSetting($setting)
293
    {
294
        if (\is_string($setting) && false !== \strpos($setting, \WebServCo\Framework\Settings::DIVIDER)) {
295
            return \explode(\WebServCo\Framework\Settings::DIVIDER, $setting);
296
        }
297
        return $setting;
298
    }
299
}
300