Issues (84)

src/ArrayDataCollection.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * @package Application Utils
4
 * @subpackage Collections
5
 * @see \AppUtils\ArrayDataCollection
6
 */
7
8
declare(strict_types=1);
9
10
namespace AppUtils;
11
12
use AppUtils\ConvertHelper\JSONConverter;
13
use AppUtils\ConvertHelper\JSONConverter\JSONConverterException;
14
use DateTime;
15
use Exception;
16
17
/**
18
 * Collection class used to work with associative arrays used to
19
 * store key => value pairs.
20
 *
21
 * Offers strict typed methods to access the available keys, to
22
 * remove the hassle of checking whether keys exist, and whether
23
 * they are of the expected type.
24
 *
25
 * ## Exception-free handling
26
 *
27
 * The collection is not intended to validate any of the stored
28
 * data, this is the purview of the host class. The utility
29
 * methods will only return values that match the expected type.
30
 * Invalid data is ignored, and a matching default value returned.
31
 *
32
 * @package Application Utils
33
 * @subpackage Collections
34
 * @author Sebastian Mordziol <[email protected]>
35
 */
36
class ArrayDataCollection
37
{
38
    /**
39
     * @var array<string,mixed>
40
     */
41
    protected array $data;
42
43
    /**
44
     * @param array<string,mixed> $data
45
     */
46
    public function __construct(array $data=array())
47
    {
48
        $this->data = $data;
49
    }
50
51
    /**
52
     * @param ArrayDataCollection|array<string,mixed>|NULL $data
53
     * @return ArrayDataCollection
54
     */
55
    public static function create($data=array()) : ArrayDataCollection
56
    {
57
        if($data instanceof self) {
58
            return $data;
59
        }
60
61
        return new ArrayDataCollection($data);
0 ignored issues
show
It seems like $data can also be of type null; however, parameter $data of AppUtils\ArrayDataCollection::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

61
        return new ArrayDataCollection(/** @scrutinizer ignore-type */ $data);
Loading history...
62
    }
63
64
    /**
65
     * Creates an array converter from a JSON encoded array.
66
     *
67
     * @param string $json
68
     * @return ArrayDataCollection
69
     * @throws JSONConverterException
70
     */
71
    public static function createFromJSON(string $json) : ArrayDataCollection
72
    {
73
        return self::create(JSONConverter::json2array($json));
74
    }
75
76
    /**
77
     * @return array<string,mixed>
78
     */
79
    public function getData() : array
80
    {
81
        return $this->data;
82
    }
83
84
    /**
85
     * @param array<string,mixed> $data
86
     * @return $this
87
     */
88
    public function setKeys(array $data) : self
89
    {
90
        foreach($data as $key => $value)
91
        {
92
            $this->setKey($key, $value);
93
        }
94
95
        return $this;
96
    }
97
98
    /**
99
     * @param string $name
100
     * @param mixed|NULL $value
101
     * @return $this
102
     */
103
    public function setKey(string $name, $value) : self
104
    {
105
        $this->data[$name] = $value;
106
        return $this;
107
    }
108
109
    /**
110
     * Merges the current collection's data with that of
111
     * the target collection, replacing existing values.
112
     *
113
     * @param ArrayDataCollection $collection
114
     * @return $this
115
     */
116
    public function mergeWith(ArrayDataCollection $collection) : self
117
    {
118
        return $this->setKeys($collection->getData());
119
    }
120
121
    /**
122
     * Combines the current collection's data with the
123
     * target collection, and returns a new collection
124
     * that contains the data of both collections.
125
     *
126
     * NOTE: The source collection's values are overwritten
127
     * by the target collection in the process.
128
     *
129
     * @param ArrayDataCollection $collection
130
     * @return ArrayDataCollection
131
     */
132
    public function combine(ArrayDataCollection $collection) : ArrayDataCollection
133
    {
134
        return self::create($this->data)->setKeys($collection->getData());
135
    }
136
137
    /**
138
     * @param string $name
139
     * @return mixed|null
140
     */
141
    public function getKey(string $name)
142
    {
143
        return $this->data[$name] ?? null;
144
    }
145
146
    /**
147
     * The stored value can be a string or a number.
148
     * All other types and values will return an empty
149
     * string.
150
     *
151
     * @param string $name
152
     * @return string
153
     */
154
    public function getString(string $name) : string
155
    {
156
        $value = $this->getKey($name);
157
158
        if(is_string($value)) {
159
            return $value;
160
        }
161
162
        if(is_numeric($value)) {
163
            return (string)$value;
164
        }
165
166
        return '';
167
    }
168
169
    /**
170
     * The stored value can be an integer or float,
171
     * or a string containing an integer or float.
172
     * All other types and values return <code>0</code>.
173
     *
174
     * @param string $name
175
     * @return int
176
     */
177
    public function getInt(string $name) : int
178
    {
179
        $value = $this->getKey($name);
180
181
        if(is_numeric($value)) {
182
            return (int)$value;
183
        }
184
185
        return 0;
186
    }
187
188
    /**
189
     * Attempts to decode the stored string as JSON.
190
     *
191
     * NOTE: Only _valid JSON_ that decodes into an array is
192
     * accepted. Invalid JSON, booleans or numbers will
193
     * return an empty array.
194
     *
195
     * @param string $name
196
     * @return array<mixed> The decoded array, or an empty array otherwise.
197
     */
198
    public function getJSONArray(string $name) : array
199
    {
200
        $value = $this->getKey($name);
201
202
        // Does not need to be decoded after all
203
        if(is_array($value)) {
204
            return $value;
205
        }
206
207
        if(empty($value) || !is_string($value)) {
208
            return array();
209
        }
210
211
        return JSONConverter::json2arraySilent($value);
212
    }
213
214
    public function getArray(string $name) : array
215
    {
216
        $value = $this->getKey($name);
217
218
        if(is_array($value)) {
219
            return $value;
220
        }
221
222
        return array();
223
    }
224
225
    public function getBool(string $name) : bool
226
    {
227
        $value = $this->getKey($name);
228
229
        if(is_string($value)) {
230
            $value = strtolower($value);
231
        }
232
233
        return
234
            $value === true
235
            ||
236
            $value === 'true'
237
            ||
238
            $value === 'yes'
239
            ||
240
            $value === 1
241
            ||
242
            $value === '1';
243
    }
244
245
    public function getFloat(string $name) : float
246
    {
247
        $value = $this->getKey($name);
248
249
        if(is_numeric($value)) {
250
            return (float)$value;
251
        }
252
253
        return 0.0;
254
    }
255
256
    /**
257
     * Whether the specified key exists in the data set,
258
     * even if its value is <code>NULL</code>.
259
     *
260
     * @param string $name
261
     * @return bool
262
     */
263
    public function keyExists(string $name) : bool
264
    {
265
        return array_key_exists($name, $this->data);
266
    }
267
268
    /**
269
     * Whether the specified key exists in the data set,
270
     * and has a non-<code>NULL</code> value.
271
     *
272
     * @param string $name
273
     * @return bool
274
     */
275
    public function keyHasValue(string $name) : bool
276
    {
277
        return isset($this->data[$name]);
278
    }
279
280
    /**
281
     * Removes the specified key from the data set, if it exists.
282
     *
283
     * @param string $name
284
     * @return $this
285
     */
286
    public function removeKey(string $name) : self
287
    {
288
        unset($this->data[$name]);
289
        return $this;
290
    }
291
292
    /**
293
     * Fetches a {@see DateTime} instance from the stored
294
     * key value, which can be either of the following:
295
     *
296
     * - A timestamp (int|string)
297
     * - A DateTime string
298
     *
299
     * @param string $name
300
     * @return DateTime|null The {@see DateTime} instance, or <code>NULL</code> if empty or invalid.
301
     */
302
    public function getDateTime(string $name) : ?DateTime
303
    {
304
        $value = $this->getString($name);
305
306
        if(empty($value)) {
307
            return null;
308
        }
309
310
        if(is_numeric($value)) {
311
            $date = new DateTime();
312
            $date->setTimestamp((int)$value);
313
            return $date;
314
        }
315
316
        try
317
        {
318
            return new DateTime($value);
319
        }
320
        catch (Exception $e)
321
        {
322
            return null;
323
        }
324
    }
325
326
    /**
327
     * Restores a {@see Microtime} instance from a key previously
328
     * set using {@see ArrayDataCollection::setMicrotime()}.
329
     *
330
     * @param string $name
331
     * @return Microtime|null The {@see Microtime} instance, or <code>NULL</code> if empty or invalid.
332
     */
333
    public function getMicrotime(string $name) : ?Microtime
334
    {
335
        try
336
        {
337
            return Microtime::createFromString($this->getString($name));
338
        }
339
        catch (Exception $e)
340
        {
341
            return null;
342
        }
343
    }
344
345
    /**
346
     * Sets a date and time key, which can be restored later
347
     * using {@see ArrayDataCollection::getDateTime()} or
348
     * {@see ArrayDataCollection::getTimestamp()}.
349
     *
350
     * @param string $name
351
     * @param DateTime $time
352
     * @return $this
353
     */
354
    public function setDateTime(string $name, DateTime $time) : self
355
    {
356
        return $this->setKey($name, $time->format(DATE_W3C));
357
    }
358
359
    /**
360
     * Sets a microtime key: Guarantees that the microseconds
361
     * information will be persisted correctly if restored later
362
     * using {@see ArrayDataCollection::getMicrotime()}.
363
     *
364
     * **NOTE:** Fetching a timestamp from a microtime key with
365
     * {@see ArrayDataCollection::getTimestamp()} will work,
366
     * but the microseconds information will be lost.
367
     *
368
     * @param $name
369
     * @param Microtime $time
370
     * @return $this
371
     */
372
    public function setMicrotime($name, Microtime $time) : self
373
    {
374
        return $this->setKey($name, $time->getISODate());
375
    }
376
377
    /**
378
     * Fetches a stored timestamp. The source value can be
379
     * any of the following:
380
     *
381
     * - An timestamp (int|string)
382
     * - A DateTime key
383
     * - A Microtime key
384
     *
385
     * @param string $name
386
     * @return int The timestamp, or <code>0</code> if none/invalid.
387
     */
388
    public function getTimestamp(string $name) : int
389
    {
390
        $date = $this->getDateTime($name);
391
392
        if($date !== null)
393
        {
394
            return $date->getTimestamp();
395
        }
396
397
        return 0;
398
    }
399
}
400