Completed
Push — master ( 062b37...2b6d7b )
by Michael
04:25
created

Arrgh::keepChain()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 34 and the first side effect is on line 876.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
namespace Arrgh;
4
5
use \Closure;
6
use \Exception;
7
use \InvalidArgumentException;
8
9
/**
10
 * A chainable array API or a set of static functions, or both.
11
 *
12
 * Note: arr_* global functions are defined at the end of the file
13
 *
14
 * @method string getString()
15
 * @method void setInteger(integer $integer)
16
 * @method setString(integer $integer)
17
 * @method array collapse(array $input)
18
 * @method bool contains(array $haystack, string $needle, string $key)
19
 * @method array except(array $input, array|string $keys)
20
 * @method array only(array $input, array|string $keys)
21
 * @method array map_assoc(array $input, Closure $callable)
22
 * @method array sort_by(array $input, string $key)
23
 * @method integer depth(array $input)
24
 * @method array even(array $input)
25
 * @method mixed first(array $input)
26
 * @method array get(array, $input, array|string, $path, bool $collapse)
27
 * @method mixed head(array $input)
28
 * @method bool is_collection(array $input)
29
 * @method mixed last(array $input)
30
 * @method array odd(array $input)
31
 * @method array partition(array $input, Closure $callable)
32
 * @method array tail(array $input)
33
 */
34
class Arrgh implements \ArrayAccess, \Iterator
35
{
36
    const PHP_SORT_DIRECTION_56 = 1;
37
    const PHP_SORT_DIRECTION_7 = -1;
38
39
    private $array;
40
    private $array_position;
41
    private $original_array;
42
    private $terminate;
43
    private $keep_once;
44
    private $last_value;
45
46
    private static $php_version;
47
    private static $php_sort_direction;
48
49
    /* Creates a new arrgh array */
50
    public function __construct($array = [])
51
    {
52
        $this->array = $array;
53
        $this->array_position = 0;
54
        if ($array instanceof Arrgh) {
55
            $this->array = $array->toArray();
56
        }
57
        $this->original_array = $this->array;
58
        $this->terminate = true;
59
    }
60
61
    /* Starts object calls */
62
    public function __call($method, $args)
63
    {
64
        return self::invoke($method, $args, $this);
65
    }
66
67
    /* Returns an array */
68
    public function toArray()
69
    {
70
        $array = array_map(function($item) {
71
            if ($item instanceof Arrgh) {
72
                return $item->toArray();
73
            }
74
            return $item;
75
        }, $this->array);
76
        return $array;
77
    }
78
79
    public function keep()
80
    {
81
        return $this->keepChain(true);
82
    }
83
    public function keepOnce()
84
    {
85
        return $this->keepChain(true, true);
86
    }
87
    public function keepChain($value = true, $keep_once = false)
88
    {
89
        $this->terminate = !$value;
90
        $this->keep_once = $keep_once;
91
        return $this;
92
    }
93
    public function breakChain()
94
    {
95
        return $this->keepChain(false);
96
    }
97
98
    /* ArrayAccess */
99
    public function offsetExists($offset)
100
    {
101
        return isset($this->array[$offset]);
102
    }
103
104
    /* ArrayAccess */
105
    public function offsetGet($offset)
106
    {
107
        return isset($this->array[$offset]) ? $this->array[$offset] : null;
108
    }
109
110
    /* ArrayAccess */
111
    public function offsetSet($offset, $value)
112
    {
113
        if (is_null($offset)) {
114
            $this->array[] = $value;
115
        } else {
116
            $this->array[$offset] = $value;
117
        }
118
    }
119
120
    /* ArrayAccess */
121
    public function offsetUnset($offset)
122
    {
123
        unset($this->array[$offset]);
124
    }
125
126
    /* Iterator */
127
    public function current()
128
    {
129
        $value = $this->array[$this->array_position];
130
        if (is_array($value)) {
131
            return new Arrgh($value);
132
        }
133
        return $value;
134
    }
135
136
    /* Iterator */
137
    public function key()
138
    {
139
        return $this->array_position;
140
    }
141
142
    /* Iterator */
143
    public function next()
144
    {
145
        ++$this->array_position;
146
    }
147
148
    /* Iterator */
149
    public function rewind()
150
    {
151
        $this->array_position = 0;
152
    }
153
154
    /* Iterator */
155
    public function valid()
156
    {
157
        return isset($this->array[$this->array_position]);
158
    }
159
160
    /* Creates a new arr array. Synonym for: chain() */
161
    public static function arr($array = [])
162
    {
163
        return self::chain($array);
164
    }
165
166
    /* Creates a new arrgh array. Synonym for: arr() */
167
    public static function chain($array = [])
168
    {
169
        return new self($array);
170
    }
171
172
    /* Starts object calls */
173
    public static function __callStatic($method, $args)
174
    {
175
        if ($method[0] === "_") {
176
            $method = substr($method, 1);
177
            $_args = $args;
178
            $first_argument = array_shift($args);
179
            if (is_array($first_argument)) {
180
                return self::chain($first_argument)->$method(...$args);
181
            }
182
            return self::chain()->$method(...$_args);
183
        }
184
        return self::invoke($method, $args);
185
    }
186
187
    public static function allFunctions()
188
    {
189
        return [
190
            "_arrgh"        => self::$arr_functions,
191
            "_call"         => self::$simple_functions,
192
            "_rotateRight"  => self::$reverse_functions,
193
            "_swapTwoFirst" => self::$swapped_functions,
194
            "_copy"         => self::$mutable_functions,
195
            "_copyMultiple" => self::$mutable_functions_multiple,
196
            "_copyValue"    => self::$mutable_value_functions,
197
        ];
198
    }
199
200
    public static function getSortDirection($direction = null)
201
    {
202
        if (self::$php_version === null) {
203
            self::$php_version = explode(".", phpversion());
204
            self::$php_sort_direction = self::$php_version[0] >= 7 ? self::PHP_SORT_DIRECTION_7 : self::PHP_SORT_DIRECTION_56;
205
        }
206
        if ($direction === null || $direction === 0) {
207
            return self::$php_sort_direction;
208
        }
209
        return $direction;
210
    }
211
212
    /* Wraps a callable with the purpose of fixing bad PHP sort implementations */
213
    private static function wrapCallable($callable)
214
    {
215
        $direction = self::getSortDirection();
216
        return function($a, $b) use ($direction, $callable) {
217
            $result = $callable($a, $b);
218
            if ($result === 0) return $direction;
219
            return $result;
220
        };
221
    }
222
223
    /* Based on input method finds handler, function and post handler */
224
    private static function findFunction($method)
225
    {
226
        $snake = strtolower(preg_replace('/\B([A-Z])/', '_\1', $method));
227
        $function_name = $snake;
228
        $function_name_prefixed = stripos($method, "array_") === 0 ? $snake : "array_" . $snake;
229
230
        $all_function_names = [$function_name, $function_name_prefixed];
231
        $all_functions      = self::allFunctions();
232
233
        $matching_handler  = null;
234
        $matching_function = null;
235
        $post_handler      = null;
236
        foreach ($all_functions as $handler => $functions) {
237
            foreach ($all_function_names as $function) {
238
                if (in_array($function, $functions)) {
239
                    $matching_handler  = $handler;
240
                    $matching_function = $function;
241
                    break 2;
242
                }
243
            }
244
        }
245
246
        if ($matching_function === null) {
247
            throw new InvalidArgumentException("Method {$method} doesn't exist");
248
        }
249
        return [$matching_handler, $matching_function, $post_handler];
250
    }
251
252
    /* Transforms the incoming calls to native calls */
253
    private static function invoke($method, $args, $object = null)
254
    {
255
        self::getSortDirection();
256
257
        list($matching_handler, $matching_function, $post_handler) = self::findFunction($method);
258
259
        switch ($matching_function) {
260
            case "asort":
261
                self::handleCaseAsort($matching_handler, $matching_function, $post_handler, $args);
262
                break;
263
            case "array_column":
264
                self::handleCaseArrayColumn($matching_handler, $matching_function, $post_handler, $args);
265
                break;
266
            default:
267
                break;
268
        }
269
270
        // If chain unshift array onto argument stack
271
        if ($object && !in_array($matching_function, self::$starters)) {
272
            array_unshift($args, $object->array);
273
        }
274
275
        // If some arrays are Arrghs map to array or if callable, wrap it in
276
        // new callable with info about sort direction.
277
        $args = array_map(function($arg) use ($matching_function) {
278
            if ($arg instanceof Arrgh) {
279
                return $arg->array;
280
            } else if ($arg instanceof Closure) {
281
                if (in_array($matching_function, self::$reverse_result_functions) && self::$php_version[0] < 7) {
282
                    return self::wrapCallable($arg);
283
                }
284
            }
285
            return $arg;
286
        }, $args);
287
288
        // Invoke handler
289
        $result = self::$matching_handler($matching_function, $args, $object);
290
        // If a post handler is registered let it modify the result
291
        if ($post_handler) {
292
            $result = $post_handler($result);
293
        }
294
295
        if ($object) {
296
            if (in_array($matching_function, self::$terminators)) {
297
                if ($object->terminate) {
298
                    if (is_array($result)) {
299
                        return new Arrgh($result);
300
                    }
301
                    return $result;
302
                }
303
                if ($object->keep_once) {
304
                    $object->terminate = true;
305
                    $object->keep_once = false;
306
                }
307
                $object->last_value = $result;
308
                return $object;
309
            }
310
            $object->array = $result;
311
            return $object;
312
        }
313
        return $result;
314
    }
315
316
    /* Handles special case: asort - In PHP5 reverses equals ("arsort" doen't mess up for some reason) */
317
    private static function handleCaseAsort(&$matching_handler, &$matching_function, &$post_handler, &$args)
2 ignored issues
show
Unused Code introduced by
The parameter $matching_handler is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $post_handler is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
318
    {
319
        $matching_function = "uasort";
320
        array_push($args, function($a, $b) { return strcasecmp($a, $b); });
321
    }
322
323
    /* Handles special case: array_column - Native array_column filters away null values.
324
     * That means you cannot use array_column for multisort since array size no longer matches.
325
     * This version of array_column returns null if the column is missing. */
326
    private static function handleCaseArrayColumn(&$matching_handler, &$matching_function, &$post_handler, &$args)
327
    {
328
        $matching_handler  = "_rotateRight";
329
        $matching_function = "array_map";
330
        $column_id    =  null;
0 ignored issues
show
Unused Code introduced by
$column_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
331
        $column_array = $args[0];
332
        $column_key   = $args[1];
333
        if (count($args) === 3) {
334
            $column_id = $args[2];
335
            $column_ids_new = array_map(function($item) use ($column_id) {
336
                return isset($item[$column_id]) ? $item[$column_id] : null;
337
            }, $column_array);
338
            $post_handler = function($result) use ($column_ids_new) {
339
                return array_combine($column_ids_new, $result);
340
            };
341
        }
342
        $args = [$column_array];
343
        array_push($args, function($item) use ($column_key) {
344
            return isset($item[$column_key]) ? $item[$column_key] : null;
345
        });
346
    }
347
348
    /* Calls the native function directly */
349
    private static function _call($function, $args)
350
    {
351
        return $function(...$args);
352
    }
353
354
    /* Shifts of the first argument (callable) and pushes it to the end */
355
    private static function _rotateRight($function, $args)
356
    {
357
        $first_argument = array_pop($args);
358
        array_unshift($args, $first_argument);
359
        return $function(...$args);
360
    }
361
362
    /* Swaps the first two args */
363
    private static function _swapTwoFirst($function, $args)
364
    {
365
        $first_argument = array_shift($args);
366
        $second_argument = array_shift($args);
367
        array_unshift($args, $first_argument);
368
        array_unshift($args, $second_argument);
369
        return $function(...$args);
370
    }
371
372
    /* Makes a copy of the array and returns it after invoking function */
373
    private static function _copy($function, $args)
374
    {
375
        $array = array_shift($args);
376
        $function($array, ...$args);
377
        return $array;
378
    }
379
380
    /* If multiple arrays are passed as arguments mulitple will be returned. Otherwise _copy is used */
381
    private static function _copyMultiple($function, $args)
382
    {
383
        $function(...$args);
384
        $arrays = [];
385
        foreach ($args as $arg) {
386
            if (is_array($arg)) {
387
                $arrays[] = $arg;
388
            }
389
        }
390
        if (count($arrays) === 1) {
391
            return $arrays[0];
392
        }
393
        return $arrays;
394
    }
395
396
    /* Makes a copy of the array and returns it after invoking function */
397
    private static function _copyValue($function, $args, $object = null)
398
    {
399
        $array = array_shift($args);
400
        $result = $function($array, ...$args);
401
        if ($object) {
402
            $object->array = $array;
403
        }
404
        return $result;
405
    }
406
407
    private static function _arrgh($function, $args, $object = null)
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
408
    {
409
        $function = "arr_" . $function;
410
        return self::$function(...$args);
411
    }
412
413
    private static function arr_map_assoc($array, $callable)
414
    {
415
        $keys = array_keys($array);
416
        return array_combine($keys, array_map($callable, $keys, $array));
417
    }
418
419
    /**
420
     * Sort an array of associative arrays by key. It checks the first two values for type
421
     * either sorts by number or using strcmp. If a key is missing entries are moved to the top
422
     * (or bottom depending on $direction)
423
     */
424
    private static function arr_sort_by($array, $key, $direction = "ASC")
425
    {
426
        $direction_int = strtoupper($direction) === "ASC" ? 1 : -1;
427
428
        if ($key instanceof Closure) {
429
            usort($array, self::wrapCallable($key));
430
            if ($direction_int === -1) {
431
                return array_reverse($array);
432
            }
433
            return $array;
434
        }
435
436
        $column = array_map(function($item) use ($key) {
437
            return isset($item[$key]) ? $item[$key] : null;
438
        }, $array);
439
        array_multisort($column, ($direction_int === 1 ? SORT_ASC : SORT_DESC), $array);
440
        return $array;
441
    }
442
443
    private static function arr_collapse($array)
444
    {
445
        return array_reduce($array, function($merged, $item) {
446
            if (is_array($item)) {
447
                return array_merge($merged, $item);
448
            }
449
            $merged[] = $item;
450
            return $merged;
451
        }, []);
452
    }
453
454
    private static function arr_contains($array, $search, $key = null)
455
    {
456
        $haystack = null;
1 ignored issue
show
Unused Code introduced by
$haystack is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
457
        if ($key) {
458
            $haystack = array_column($array, $key);
459
        } else {
460
            $haystack = array_reduce($array, function($merged, $item) {
461
                return array_merge($merged, array_values($item));
462
            }, []);
463
        }
464
        return array_search($search, $haystack) !== false;
465
    }
466
467
    private static function arr_except($array, $except)
468
    {
469
        if (is_string($except)) {
470
            $except = [$except];
471
        }
472
473
        $is_collection = self::arr_is_collection($array);
474
        $array = $is_collection ? $array : [ $array ];
475
476
        $result = array_map(function($item) use ($except) {
477
            foreach ($except as $key) {
478
                unset($item[$key]);
479
            }
480
            return $item;
481
        }, $array);
482
483
        if ($is_collection) {
484
            return $result;
485
        }
486
        return $result[0];
487
    }
488
489
    private static function arr_only($array, $only)
490
    {
491
        if (is_string($only)) {
492
            $only = [$only];
493
        }
494
495
        $is_collection = self::arr_is_collection($array);
496
        $array = $is_collection ? $array : [ $array ];
497
498
        $result = array_map(function($item) use ($only) {
499
            foreach ($item as $key => $value) {
500
                if (!in_array($key, $only)) {
501
                    unset($item[$key]);
502
                }
503
            }
504
            return $item;
505
        }, $array);
506
507
        if ($is_collection) {
508
            return $result;
509
        }
510
        return $result[0];
511
    }
512
513
    /**
514
     *  Get for multi-dimensional arrays
515
     *
516
     *  @param array      An array to query on
517
     *  @param path|array A string representing the path to traverse.
518
     *                    Optionally pass as [ $path, ...$functions ] if `!$` is used
519
     *  @param bool       Collapse resulting data-set
520
     */
521
    private static function arr_get($array, $path, $collapse = false)
522
    {
523
        $path_string = $path;
524
        if (is_array($path)) {
525
            $path_string = array_shift($path);
526
        }
527
        $path_segments = explode(".", $path_string);
528
        $result = self::_arr_get_traverse($array, $path_segments, $collapse, /* functions */ $path);
529
        return $result;
530
    }
531
532
    private static function _arr_get_traverse($data, $path, $collapse = false, $functions = [])
533
    {
534
        $next_key      = array_shift($path);
535
        $plug_index    = is_numeric($next_key) ? (int) $next_key : null;
536
        $is_collection = self::isCollection($data);
537
538
        $next_node = null;
539
540
        // Apply custom function
541
        if ($next_key === '!$') {
542
            if ($is_collection) {
543
                $function  = array_shift($functions);
544
                $data      = array_values(array_filter($data, $function, ARRAY_FILTER_USE_BOTH));
545
                $next_key  = array_shift($path);
546
            } else {
547
                throw new Exception("Invalid path trying to invoke function on non-collection");
548
            }
549
        }
550
551
        // Select data either by index or key
552
        if ($plug_index !== null) {
553
            $count = count($data);
554
            if ($is_collection) {
555
                // Adjust negative index
556
                if ($plug_index < 0) {
557
                    $plug_index = $count === 1 ? 0 : $count + ($plug_index % $count);
558
                }
559
                // Plug data
560
                if (isset($data[$plug_index])) {
561
                    $next_node = $data[$plug_index];
562
                }
563
            } else {
564
                throw new Exception("Invalid path trying to plug item but data is not a collection");
565
            }
566
        } else {
567
            if ($next_key === null) {
568
                $next_node = $data;
569
            } else {
570
                if ($is_collection) {
571
                    $next_node = array_map(function($item) use ($next_key) {
572
                        if ($item !== null && array_key_exists($next_key, $item)) {
573
                            return $item[$next_key];
574
                        }
575
                        return null;
576
                    }, $data);
577
                } else if (is_array($data)) {
578
                    if (array_key_exists($next_key, $data)) {
579
                        $next_node = $data[$next_key];
580
                    }
581
                }
582
            }
583
        }
584
585
        // If nothing matched break path and return
586
        if (empty($next_node)) {
587
            return null;
588
        }
589
590
        // If path is at the end return
591
        if (count($path) === 0) {
592
            if (is_array($next_node) && $collapse) {
593
                return array_filter($next_node);
594
            }
595
            return $next_node;
596
        }
597
598
        // If path is not completed
599
        if (is_array($next_node)) {
600
601
            // Recurse
602
            $node_is_collection = self::arr_is_collection($next_node);
603
            $node_depth = self::arr_depth($next_node);
604
605
            if ($node_is_collection) {
606
                // Collapse collections
607
                if ($collapse                  // if enabled
608
                    && !is_numeric($path[0])   // if next path segment is not an index
609
                    && $path[0] !== "!$"       // if not the result of a custom function
610
                    && $node_depth > 0         // if array of arrays
611
                ) {
612
                    $next_node = self::arr_collapse($next_node);
613
                }
614
615
                if (is_numeric($path[0]) && $node_depth < 1) {
616
                    $result = self::_arr_get_traverse($next_node, $path, $collapse, $functions);
617
                } else {
618
                    // Collect data from sub-tree
619
                    $result = [];
620
                    foreach ($next_node as $node) {
621
                        if ($node === null) {
622
                            $result[] = null;
623
                        } else {
624
                            $partial = self::_arr_get_traverse($node, $path, $collapse, $functions);
625
                            if ($collapse) {
626
                                $result[] = $partial;
627
                            } else {
628
                                $result[] = [$partial];
629
                            }
630
                        }
631
                    }
632
                }
633
634
                // Since collection functions inject an array segment we must collapse the result
635
                if ($path[0] === "!$") {
636
                    $result = self::arr_collapse($result);
637
                }
638
            } else {
639
                $result = self::_arr_get_traverse($next_node, $path, $collapse, $functions);
640
            }
641
            if (is_array($result)) {
642
                // Collapse collections greater than 1
643
                if (self::arr_depth($result) > 1) {
644
                    $result = self::arr_collapse($result);
645
                }
646
                return array_filter($result);
647
            }
648
            return $result;
649
        }
650
        throw new Exception("Next node in path is not an array");
651
    }
652
653
    private static function arr_is_collection($mixed)
654
    {
655
        return is_array($mixed) && array_values($mixed) === $mixed;
656
    }
657
658
    /**
659
     * Return the depth of a collection hiearchy. Zero based.
660
     *
661
     * @param array A collection
662
     * @return int `null` if $array is not a collection.
663
     */
664
    private static function arr_depth($array)
665
    {
666
        if (empty($array) && is_array($array)) return 0;
667
        if (!self::arr_is_collection($array)) return null;
668
669
        $depth = 0;
670
        $child = array_shift($array);
671
        while (self::arr_is_collection($child)) {
672
            $depth += 1;
673
            $child = array_shift($child);
674
        }
675
        return $depth;
676
    }
677
678
    /**
679
     * Partion the input based on the result of the callback function.
680
     *
681
     * @param array    $array    A collection
682
     * @param Closeure $callable A callable returning true or false depending on which way to partion the element—left or right.
683
     * @return array An array with two arrays—left and right: [left, right]
684
     */
685
    private static function arr_partition($array, $callable)
686
    {
687
        $left = [];
688
        $right = [];
689
        array_walk($array, function($item, $key) use (&$left, &$right, $callable) {
690
            if ($callable($item, $key)) {
691
                $left[] = $item;
692
            } else {
693
                $right[] = $item;
694
            }
695
        });
696
        return [$left, $right];
697
    }
698
699
    private static function arr_even($array)
700
    {
701
        return self::arr_partition($array, function($item, $key) { return $key % 2 === 0; })[0];
0 ignored issues
show
Documentation introduced by
function ($item, $key) {...eturn $key % 2 === 0; } is of type object<Closure>, but the function expects a object<Arrgh\Closeure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
702
    }
703
704
    private static function arr_odd($array)
705
    {
706
        return self::arr_partition($array, function($item, $key) { return $key % 2 === 1; })[0];
0 ignored issues
show
Documentation introduced by
function ($item, $key) {...eturn $key % 2 === 1; } is of type object<Closure>, but the function expects a object<Arrgh\Closeure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
707
    }
708
709
    /* Synonym of shift */
710
    private static function arr_head($array)
711
    {
712
        return self::shift($array);
713
    }
714
715
    /* Synonym of shift */
716
    private static function arr_first($array)
717
    {
718
        return self::shift($array);
719
    }
720
721
    /* Synonym of pop */
722
    private static function arr_last($array)
723
    {
724
        return self::pop($array);
725
    }
726
727
    private static function arr_tail($array)
728
    {
729
        return self::chain($array)->keep()->shift()->toArray();
0 ignored issues
show
Documentation Bug introduced by
The method shift does not exist on object<Arrgh\Arrgh>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
730
    }
731
732
    // _arrgh
733
    static private $arr_functions = [
734
        "collapse",
735
        "contains",
736
        "except",
737
        "map_assoc",
738
        "only",
739
        "sort_by",
740
        'depth',
741
        'even',
742
        'first',
743
        'get',
744
        'head',
745
        'is_collection',
746
        'last',
747
        'odd',
748
        'partition',
749
        'tail',
750
    ];
751
752
    // _call
753
    static private $simple_functions = [
754
        "array_change_key_case",
755
        "array_chunk",
756
        "array_column",
757
        "array_combine",
758
        "array_count_values",
759
        "array_diff",
760
        "array_diff_assoc",
761
        "array_diff_key",
762
        "array_diff_uassoc",
763
        "array_diff_ukey",
764
        "array_fill",
765
        "array_fill_keys",
766
        "array_filter",
767
        "array_flip",
768
        "array_intersect",
769
        "array_intersect_assoc",
770
        "array_intersect_key",
771
        "array_intersect_uassoc",
772
        "array_intersect_ukey",
773
        "array_keys",
774
        "array_merge",
775
        "array_merge_recursive",
776
        "array_pad",
777
        "array_product",
778
        "array_rand",
779
        "array_reduce",
780
        "array_replace",
781
        "array_replace_recursive",
782
        "array_reverse",
783
        "array_slice",
784
        "array_sum",
785
        "array_udiff",
786
        "array_udiff_assoc",
787
        "array_udiff_uassoc",
788
        "array_uintersect",
789
        "array_uintersect_assoc",
790
        "array_uintersect_uassoc",
791
        "array_unique",
792
        "array_values",
793
        "count",
794
        "max",
795
        "min",
796
        "range",
797
        "sizeof",
798
    ];
799
800
    // _copy
801
    static private $mutable_functions = [
802
        "array_push",
803
        "array_splice",
804
        "array_unshift",
805
        "array_walk",
806
        "array_walk_recursive",
807
        "arsort",
808
        "asort",
809
        "krsort",
810
        "ksort",
811
        "natcasesort",
812
        "natsort",
813
        "rsort",
814
        "shuffle",
815
        "sort",
816
        "uasort",
817
        "uksort",
818
        "usort",
819
    ];
820
821
    // _copyMultiple
822
    static private $mutable_functions_multiple = [
823
        "array_multisort",
824
    ];
825
826
    // _copyValue
827
    static private $mutable_value_functions = [
828
        "array_pop",
829
        "array_shift",
830
        "end",
831
    ];
832
833
    // _rotateRight
834
    static private $reverse_functions = [
835
        "array_map",
836
    ];
837
838
    // _swapTwoFirst
839
    static private $swapped_functions = [
840
        "array_key_exists",
841
        "array_search",
842
        "implode",
843
        "in_array",
844
        "join",
845
    ];
846
847
    static private $starters = [
848
        "array_fill",
849
        "array_fill_keys",
850
        "range",
851
    ];
852
853
    static private $terminators = [
854
        "array_pop",
855
        "array_shift",
856
        "array_sum",
857
        "count",
858
        "first",
859
        "head",
860
        "join",
861
        "last",
862
        "max",
863
        "min",
864
        "sizeof",
865
    ];
866
867
    static private $reverse_result_functions = [
868
        "uasort",
869
        "uksort",
870
        "usort",
871
        "asort",
872
    ];
873
}
874
875
if (defined("ARRGH")) {
876
    require __DIR__ . '/arrgh_functions.php';
877
}
878