ManipulationTrait::flatten()   B
last analyzed

Complexity

Conditions 8
Paths 9

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
cc 8
eloc 12
c 2
b 0
f 2
nc 9
nop 2
dl 0
loc 20
rs 8.4444
1
<?php
2
/**
3
 * Framy Framework
4
 *
5
 * @copyright Copyright Framy
6
 * @Author Marco Bier <[email protected]>
7
 */
8
9
namespace app\framework\Component\StdLib\StdObject\ArrayObject;
10
11
use app\framework\Component\StdLib\StdObject\StdObjectWrapper;
12
use app\framework\Component\StdLib\StdObject\StringObject\StringObject;
13
use ErrorException;
14
15
trait ManipulationTrait
16
{
17
    /**
18
     * Return, or update, current standard objects value.
19
     *
20
     * @param null $value If $value is set, value is updated and ArrayObject is returned.
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
21
     * @return mixed
22
     */
23
    abstract public function val($value = null);
24
25
    abstract public function keyExists($key);
26
27
    /**
28
     * (PHP 5 &gt;= 5.1.0)<br/>
29
     * Count elements of an object
30
     *
31
     * @link http://php.net/manual/en/countable.count.php
32
     * @return int The custom count as an integer.
33
     *       </p>
34
     *       <p>
35
     *       The return value is cast to an integer.
36
     */
37
    abstract public function count();
38
39
    /**
40
     * Checks if given value is null.
41
     *
42
     * @param mixed $var Value to check
43
     *
44
     * @return bool
45
     */
46
    abstract protected static function isNull($var);
47
48
    /**
49
     * Check if $instance is of $type.
50
     *
51
     * @param mixed $instance
52
     * @param string $type
53
     *
54
     * @return bool
55
     */
56
    abstract protected static function isInstanceOf($instance, $type);
57
58
    /**
59
     * Checks if given value is an array.
60
     *
61
     * @param $var
62
     *
63
     * @return bool
64
     */
65
    abstract protected static function isArray($var);
66
67
    /**
68
     * Returns the difference between $attributes and $original
69
     * If an value is different the element from $arr1 will be returned
70
     *
71
     * @param $arr
72
     * @return array
73
     */
74
    public function difference($arr)
75
    {
76
        $attributes = [];
77
        foreach ($this->val() as $attKey => $attribute) {
78
            foreach ($arr as $oriKey => $original) {
79
                if ($attKey == $oriKey) {
80
                    if ($attribute !== $original) {
81
                        $attributes[$attKey] = $attribute;
82
                    }
83
                }
84
            }
85
        }
86
87
        return $attributes;
88
    }
89
90
    /**
91
     * Convert the array into a query string.
92
     *
93
     * @return string
94
     */
95
    public function query()
96
    {
97
        return http_build_query($this->val(), null, '&', PHP_QUERY_RFC3986);
98
    }
99
100
    /**
101
     * Flatten a multi-dimensional associative array with dots.
102
     *
103
     * @param  array   $array
104
     * @param  string  $prepend
105
     * @return array
106
     */
107
    public function dot($array = null, $prepend = '')
108
    {
109
110
        if (is_null($array)) {
111
            $array = $this->val();
112
        }
113
114
        $results = [];
115
        foreach ($array as $key => $value) {
116
            if (is_array($value) && ! empty($value)) {
117
                $results = array_merge($results, $this->dot($value, $prepend.$key.'.'));
118
            } else {
119
                $results[$prepend.$key] = $value;
120
            }
121
        }
122
        return $results;
123
    }
124
125
    /**
126
     * Divide an array into two arrays. One with keys and the other with values.
127
     *
128
     * @return array
129
     */
130
    public function divide()
131
    {
132
        return [$this->getKeys(), $this->getOnlyValues()];
133
    }
134
135
    /**
136
     * Returns a array of only the keys
137
     *
138
     * @return array
139
     */
140
    public function getKeys()
141
    {
142
        return array_keys($this->val());
143
    }
144
145
    /**
146
     * Returns a array of only the values
147
     *
148
     * @return array
149
     */
150
    public function getOnlyValues()
151
    {
152
        return array_values($this->val());
153
    }
154
155
    /**
156
     * remove first element of given array
157
     *
158
     * @return $this
159
     */
160
    public function removeFirst()
161
    {
162
        $array = $this->val();
163
164
        array_shift($array);
165
166
        $this->val($array);
167
168
        return $this;
169
    }
170
171
    /**
172
     * remove last element of given array
173
     *
174
     * @return $this
175
     */
176
    public function removeLast()
177
    {
178
        $array = $this->val();
179
180
        array_pop($array);
181
182
        $this->val($array);
183
184
        return $this;
185
    }
186
187
    /**
188
     * Go thru all the entries and remove all with given value
189
     *
190
     * @param $_value
191
     * @return $this
192
     */
193
    public function removeIfValue($_value)
194
    {
195
        $values = $this->val();
196
197
        foreach ($values as $key => $value) {
198
            if ($value === $_value) {
199
                $this->removeKey($key);
200
            }
201
        }
202
203
        return $this;
204
    }
205
206
    /**
207
     * The method joins the items in a collection.
208
     *
209
     * @param string $glue The string you wish to place between the values.
210
     * @param string $key  If the value contains arrays or objects, you should pass the key of the attributes you wish to join
211
     * @return string
212
     */
213
    public function implode(string $glue, string $key = null)
214
    {
215
        if (! function_exists(__NAMESPACE__.'\useGlue')) {
216
            // done this function to avoid writing redundant code
217
            function useGlue(&$i, &$length, &$glue) {
218
                // don't use glue if is last element
219
                if($i < $length)
220
                    return $glue;
221
                else
222
                    return "";
223
            }
224
        }
225
226
        $val    = $this->val();
227
        $length = $this->count();
228
        $result = "";
229
230
        // check if val is key or obj
231
        $i = 1;
232
        foreach ($val as $item) {
233
            if(is_object($item)) {
234
                $item = get_object_vars($this);
0 ignored issues
show
Unused Code introduced by
The assignment to $item is dead and can be removed.
Loading history...
235
                //TODO finnish this case
236
            } elseif(is_array($item)) {
237
                if(!isset($key))
238
                    handle(new \Exception("\$key must be set!"));
239
240
                $result .= $item[$key].useGlue($i, $length, $glue);
241
            } else {
242
                $result .= $item.useGlue($i, $length, $glue);
243
            }
244
            $i++;
245
        }
246
247
        return $result;
248
    }
249
250
    /**
251
     * Get or update the given key inside current array.
252
     *
253
     * @param string|int|StringObject $key Array key
254
     * @param null|mixed              $value If set, the value under current $key will be updated and not returned.
255
     * @param bool                    $setOnlyIfDoesntExist Set the $value only in case if the $key doesn't exist.
256
     *
257
     * @return $this|mixed|StringObject The value of the given key.
258
     */
259
    public function key($key, $value = null, $setOnlyIfDoesntExist = false)
260
    {
261
        $key = StdObjectWrapper::toString($key);
262
        $array = $this->val();
263
264
        if ($setOnlyIfDoesntExist && !$this->keyExists($key)) {
265
            $array[$key] = $value;
266
            $this->val($array);
267
268
            return $value;
269
        } else {
270
            if (!$setOnlyIfDoesntExist && !$this->isNull($value)) {
271
                $array[$key] = $value;
272
                $this->val($array);
273
274
                return $this;
275
            }
276
        }
277
278
        if (!isset($array[$key])) {
279
            return $value;
280
        }
281
282
        return $array[$key];
283
    }
284
285
    /**
286
     * Inserts an element to the end of the array.
287
     * If you set both params, that first param is the key, and second is the value,
288
     * else first param is the value, and the second is ignored.
289
     *
290
     * @param mixed $k
291
     * @param mixed $v
292
     *
293
     * @return $this
294
     */
295
    public function append($k, $v = null)
296
    {
297
        $array = $this->val();
298
299
        if (!$this->isNull($v)) {
300
            $array[$k] = $v;
301
        } else {
302
            $array[] = $k;
303
        }
304
305
        $this->val($array);
306
307
        return $this;
308
    }
309
310
    /**
311
     * Inserts an element at the beginning of the array.
312
     * If you set both params, that first param is the key, and second is the value,
313
     * else first param is the value, and the second is ignored.
314
     *
315
     * @param mixed $k
316
     * @param mixed $v
317
     *
318
     * @return $this
319
     */
320
    public function prepend($k, $v = null)
321
    {
322
        $array = $this->val();
323
324
        if (!$this->isNull($v)) {
325
            $array = array_reverse($array, true);
326
            $array[$k] = $v;
327
            $array = array_reverse($array, true);
328
        } else {
329
            array_unshift($array, $k);
330
        }
331
332
        $this->val($array);
333
334
        return $this;
335
    }
336
337
    /**
338
     * remove key in current array
339
     *
340
     * @param $key
341
     */
342
    public function removeKey($key)
343
    {
344
        if ($this->keyExists($key)) {
345
            $array = $this->val();
346
            unset($array[$key]);
347
348
            $this->val($array);
349
        }
350
    }
351
352
    /**
353
     * The method iterates through the array and passes each value to
354
     * the given callback. The callback is free to modify the item and return it,
355
     * thus forming a new ArrayObject of modified items
356
     *
357
     * @param callable $call
358
     * @return ArrayObject
359
     */
360
    public function map(callable $call)
361
    {
362
        $array  = $this->val();
363
        $result = [];
364
365
        foreach ($array as $key => $item)
366
            $result[] = call_user_func($call, $item, $key);
367
368
        return new ArrayObject($result);
369
    }
370
371
    /**
372
     * Merge given $array with current array.
373
     *
374
     * @param array|ArrayObject $array
375
     *
376
     * @return $this
377
     */
378
    public function merge($array)
379
    {
380
        if($this->isInstanceOf($array, $this)){
0 ignored issues
show
Bug introduced by
$this of type app\framework\Component\...bject\ManipulationTrait is incompatible with the type string expected by parameter $type of app\framework\Component\...onTrait::isInstanceOf(). ( Ignorable by Annotation )

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

380
        if($this->isInstanceOf($array, /** @scrutinizer ignore-type */ $this)){
Loading history...
381
            $array = $array->val();
382
        }
383
384
        $this->val(array_merge($this->val(), $array));
0 ignored issues
show
Bug introduced by
It seems like $array can also be of type app\framework\Component\...ArrayObject\ArrayObject; however, parameter $array2 of array_merge() does only seem to accept array|null, 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

384
        $this->val(array_merge($this->val(), /** @scrutinizer ignore-type */ $array));
Loading history...
385
386
        return $this;
387
    }
388
389
    /**
390
     * Merge given $array with current array.
391
     *
392
     * @param array|ArrayObject $array
393
     *
394
     * @return $this
395
     */
396
    public function mergeRecursive($array)
397
    {
398
        if($this->isInstanceOf($array, $this)){
0 ignored issues
show
Bug introduced by
$this of type app\framework\Component\...bject\ManipulationTrait is incompatible with the type string expected by parameter $type of app\framework\Component\...onTrait::isInstanceOf(). ( Ignorable by Annotation )

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

398
        if($this->isInstanceOf($array, /** @scrutinizer ignore-type */ $this)){
Loading history...
399
            $array = $array->val();
400
        }
401
402
        $this->val(array_merge_recursive($this->val(), $array));
0 ignored issues
show
Bug introduced by
It seems like $array can also be of type app\framework\Component\...ArrayObject\ArrayObject; however, parameter $_ of array_merge_recursive() 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

402
        $this->val(array_merge_recursive($this->val(), /** @scrutinizer ignore-type */ $array));
Loading history...
403
404
        return $this;
405
    }
406
407
    /**
408
     * @param int $num
409
     *
410
     * @return ArrayObject
411
     * @throws ArrayObjectException
412
     */
413
    public function rand($num = 1)
414
    {
415
        try {
416
            $arr = array_rand($this->val(), $num);
417
        } catch (ErrorException $e) {
418
            throw new ArrayObjectException($e->getMessage());
419
        }
420
421
        if (!$this->isArray($arr)) {
422
            $arr = [$arr];
423
        }
424
425
        return new ArrayObject($arr);
426
    }
427
428
    /**
429
     * reverse elements order
430
     *
431
     * @param bool $preserve
432
     *
433
     * @return $this
434
     */
435
    public function reverse($preserve = false)
436
    {
437
        $this->val(array_reverse($this->val(), $preserve));
438
439
        return $this;
440
    }
441
442
    /**
443
     * Shuffle elements in the array.
444
     *
445
     * @return $this
446
     */
447
    public function shuffle()
448
    {
449
        $val = $this->val();
450
        shuffle($val);
451
        $this->val($val);
452
        return $this;
453
    }
454
455
    /**
456
     * Removes duplicate values from an array
457
     *
458
     * @param int $sortFlag  Sorting type flags:<br>
459
     *                       SORT_REGULAR - compare items normally (don't change types)<br>
460
     *                       SORT_NUMERIC - compare items numerically<br>
461
     *                       SORT_STRING - compare items as strings<br>
462
     *                       SORT_LOCALE_STRING - compare items as strings, based on the current locale.<br>
463
     *
464
     * @return $this
465
     */
466
    public function unique($sortFlag = SORT_REGULAR)
467
    {
468
        $this->val(array_unique($this->val(), $sortFlag));
469
470
        return $this;
471
    }
472
473
    /**
474
     * Sort an array by values using a user-defined comparison function<br />
475
     * This function assigns new keys to the elements in array. It will remove any existing keys that may have been assigned, rather than just reordering the keys.<br />
476
     * The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
477
     *
478
     * @param callable $comparisonFunction
479
     *
480
     * @throws ArrayObjectException
481
     *
482
     * @return $this
483
     */
484
    public function sortUsingFunction($comparisonFunction)
485
    {
486
        if (!is_callable($comparisonFunction)) {
487
            throw new ArrayObjectException(ArrayObjectException::MSG_INVALID_ARG, [
488
                '$comparisonFunction',
489
                'callable'
490
            ]);
491
        }
492
493
        $val = $this->val();
494
        usort($val, $comparisonFunction);
495
        $this->val($val);
496
497
        return $this;
498
    }
499
500
    /**
501
     * Flatten a multi-dimensional array into a single level.
502
     *
503
     * @param  array $array
504
     * @param  int   $depth
505
     * @return array
506
     */
507
    public function flatten(array $array = null, $depth = INF)
508
    {
509
        $result = [];
510
        $val    = $array ?: $this->val();
511
512
        foreach ($val as $item) {
513
            $item = $item instanceof ArrayObject ? $item->val() : $item;
514
515
            if (! is_array($item) or $item === []) {
516
                if ($item !== []) {
517
                    $result[] = $item;
518
                }
519
            } elseif ($depth === 1) {
520
                $result = array_merge($result, array_values($item));
521
            } else {
522
                $result = array_merge($result, $this->flatten($item, $depth - 1));
0 ignored issues
show
Bug introduced by
$depth - 1 of type double is incompatible with the type integer expected by parameter $depth of app\framework\Component\...ulationTrait::flatten(). ( Ignorable by Annotation )

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

522
                $result = array_merge($result, $this->flatten($item, /** @scrutinizer ignore-type */ $depth - 1));
Loading history...
523
            }
524
        }
525
526
        return $result;
527
    }
528
}
529