Passed
Push — master ( 2a9c77...ac68e5 )
by Jean
02:19
created

ChainableArray_Utils_Trait::mergeRecursiveCustom()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 4
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
namespace JClaveau\Arrays;
3
4
/**
5
 * Custom functions that can be used on arrays.
6
 */
7
trait ChainableArray_Utils_Trait
8
{
9
    /**
10
     * Same as $this->groupByTransformed() without the transformer to
11
     * improve the readability in some cases.
12
     *
13
     * @param  callable $indexGenerator   Can return a scalar or an array.
14
     *         Multiple indexes allow to add a row to multiple groups.
15
     * @param  callable $conflictResolver
16
     *
17
     * @throws Missing conflict resolver
18
     *
19
     * @return array The array containing the grouped rows.
20
     */
21
    public function groupBy( callable $indexGenerator, callable $conflictResolver=null )
22
    {
23
        // todo : this doesn't work
24
        // return $this->groupByTransformed($indexGenerator, null, $conflictResolver);
25
26
        $out = [];
27
        foreach ($this->data as $key => $row) {
28
29
            if (!$row)
30
                continue;
31
32
            $newIndexes     = call_user_func($indexGenerator, $key, $row);
33
            if (!is_array($newIndexes))
34
                $newIndexes = [$newIndexes];
35
36
            foreach ($newIndexes as $newIndex) {
37
                if (!isset($out[$newIndex])) {
38
                    $out[$newIndex] = $row;
39
                }
40
                else {
41
                    if ($conflictResolver === null) {
42
                        self::throwUsageException(
43
                            "A 'group by' provoking a conflict"
44
                            ." has no conflict resolver defined:\n"
45
                            ." + key: ".$key."\n"
46
                            ." + existing: ".var_export($out[$newIndex], true)."\n"
47
                            ." + conflict: ".var_export($row, true)."\n"
48
                        );
49
                    }
50
51
                    $out[$newIndex] = call_user_func(
52
                        $conflictResolver,
53
                        $newIndex,
54
                        $out[$newIndex],
55
                        $row
56
                    );
57
                }
58
            }
59
        }
60
61
        return $this->returnConstant($out);
0 ignored issues
show
Bug introduced by
It seems like returnConstant() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

61
        return $this->/** @scrutinizer ignore-call */ returnConstant($out);
Loading history...
62
    }
63
64
    /**
65
     * Group rows in arrays indexed by the index generated by $indexGenerator
66
     *
67
     * @param  callable $indexGenerator   Can return a scalar or an array.
68
     *         Multiple indexes allow to add a row to multiple groups.
69
     *
70
     * @return array The array containing the grouped rows.
71
     */
72
    public function groupInArrays( callable $indexGenerator )
73
    {
74
        $out = [];
75
        foreach ($this->data as $key => $row) {
76
77
            if (!$row)
78
                continue;
79
80
            $new_keys = call_user_func($indexGenerator, $row, $key);
81
            if (!is_array($new_keys))
82
                $new_keys = [$new_keys];
83
84
            foreach ($new_keys as $new_key) {
85
                if (!isset($out[ $new_key ])) {
86
                    $out[ $new_key ] = [
87
                        $key => $row
88
                    ];
89
                }
90
                else {
91
                    $out[ $new_key ][ $key ] = $row;
92
                }
93
            }
94
        }
95
96
        return $this->returnConstant($out);
97
    }
98
99
    /**
100
     * Parses an array and group it rows by index. This index is generated
101
     * by the first parameter.
102
     * The row corresponding to the new index can be different from the
103
     * grouped ones so the second parameter allows us to transform them.
104
     * Finally, the third parameter is used to resolve the conflict e.g.
105
     * when two rows generate the same index.
106
     *
107
     * @paramb callable $indexGenerator
108
     * @paramb callable $rowTransformer
109
     * @paramb callable $conflictResolver
110
     *
111
     * @return array The array containing the grouped rows.
112
     */
113
    public function groupByTransformed(
114
        callable $indexGenerator,
115
        callable $rowTransformer,      // todo check this behavior
116
        callable $conflictResolver )
117
    {
118
        // The goal here is to remove the second parameter has it makes the
119
        // grouping process too complicated
120
        // if (!$conflictResolver) {
121
            // $conflictResolver = $rowTransformer;
122
            // $rowTransformer   = null;
123
        // }
124
125
        $out = [];
126
        foreach ($this->data as $key => $row) {
127
128
            if (!$row)
129
                continue;
130
131
            $newIndex       = call_user_func($indexGenerator, $key, $row);
132
133
            $transformedRow = $rowTransformer
134
                            ? call_user_func($rowTransformer, $row)
135
                            : $row;
136
137
            if (!isset($out[$newIndex])) {
138
                $out[$newIndex] = $transformedRow;
139
            }
140
            else {
141
                $out[$newIndex] = call_user_func(
142
                    $conflictResolver,
143
                    $newIndex,
144
                    $out[$newIndex],
145
                    $transformedRow,
146
                    $row
147
                );
148
            }
149
        }
150
151
        return $this->returnConstant($out);
152
    }
153
154
    /**
155
     * Merge a table into another one
156
     *
157
     * @param static $otherTable       The table to merge into
158
     * @param callable     $conflictResolver Defines what to do if two
159
     *                                       rows have the same index.
160
     * @return static
161
     */
162
    public function mergeWith( $otherTable, callable $conflictResolver=null )
163
    {
164
        if (is_array($otherTable))
0 ignored issues
show
introduced by
The condition is_array($otherTable) is always false.
Loading history...
165
            $otherTable = new static($otherTable);
166
167
        if (!$otherTable instanceof static) {
0 ignored issues
show
introduced by
$otherTable is always a sub-type of static.
Loading history...
168
            self::throwUsageException(
169
                '$otherTable must be an array or an instance of '.static::class.' instead of: '
170
                .var_export($otherTable, true)
171
            );
172
        }
173
174
        $out = $this->data;
175
        foreach ($otherTable->getArray() as $key => $row) {
0 ignored issues
show
Bug introduced by
It seems like getArray() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

175
        foreach ($otherTable->/** @scrutinizer ignore-call */ getArray() as $key => $row) {
Loading history...
176
177
            if (!isset($out[$key])) {
178
                $out[$key] = $row;
179
            }
180
            else {
181
                if ($conflictResolver === null)
182
                    self::throwUsageException('No conflict resolver for a merge provoking one');
183
184
                $arguments = [
185
                    &$key,
186
                    $out[$key],
187
                    $row
188
                ];
189
190
                $out[$key] = call_user_func_array(
191
                    $conflictResolver,
192
                    $arguments
193
                );
194
            }
195
        }
196
197
        return $this->returnConstant($out);
198
    }
199
200
    /**
201
     * Merge the table $otherTable into the current table.
202
     * (same as self::mergeWith with the other table as $this)
203
     * @return static
204
     */
205
    public function mergeIn( $otherTable, callable $conflictResolver=null )
206
    {
207
        $otherTable->mergeWith($this, $conflictResolver);
208
        return $this;
209
    }
210
211
    /**
212
     * The same as self::mergeWith with an array of tables.
213
     *
214
     * @param array $othersTable array of HelperTable
215
     * @param func  $conflictResolver callback resolver
0 ignored issues
show
Bug introduced by
The type JClaveau\Arrays\func was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
216
     */
217
    public function mergeSeveralWith(array $othersTable, callable $conflictResolver = null)
218
    {
219
        foreach ($othersTable as $otherTable) {
220
            $this->mergeWith($otherTable, $conflictResolver);
221
        }
222
223
        return $this;
224
    }
225
226
    /**
227
     *
228
     */
229
    public function each(callable $rowTransformer)
230
    {
231
        $out  = [];
232
        foreach ($this->data as $key => $row) {
233
            $out[$key] = call_user_func_array(
234
                $rowTransformer,
235
                [$row, &$key, $this->data]
236
            );
237
        }
238
239
        return $this->returnConstant($out);
240
    }
241
242
    /**
243
     * Rename a column on every row.
244
     *
245
     * @todo remove this method and force the usage of $this->renameColumns()?
246
     * @deprecated use $this->renameColumns(Array) instead]
247
     *
248
     */
249
    public function renameColumn($old_name, $new_name)
250
    {
251
        return $this->renameColumns([$old_name => $new_name]);
252
    }
253
254
    /**
255
     * Rename a column on every row.
256
     *
257
     * @return static
258
     */
259
    public function renameColumns(array $old_to_new_names)
260
    {
261
        $out  = [];
262
        foreach ($this->data as $key => $row) {
263
            try {
264
                foreach ($old_to_new_names as $old_name => $new_name) {
265
                    $row[$new_name] = $row[$old_name];
266
                    unset($row[$old_name]);
267
                }
268
            }
269
            catch (\Exception $e) {
270
                self::throwUsageException( $e->getMessage() );
271
            }
272
273
            $out[$key] = $row;
274
        }
275
276
        return $this->returnConstant($out);
277
    }
278
279
    /**
280
     * Limits the size of the array.
281
     *
282
     * @param  int         $max
283
     * @return Heper_Table $this
0 ignored issues
show
Bug introduced by
The type JClaveau\Arrays\Heper_Table was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
284
     *
285
     * @todo implement other parameters for this function like in SQL
286
     */
287
    public function limit()
288
    {
289
        $arguments = func_get_args();
290
        if (count($arguments) == 1 && is_numeric($arguments[0]))
291
            $max = $arguments[0];
292
        else
293
            self::throwUsageException("Bad arguments type and count for limit()");
294
295
        $out   = [];
296
        $count = 0;
297
        foreach ($this->data as $key => $row) {
298
299
            if ($max <= $count)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $max does not seem to be defined for all execution paths leading up to this point.
Loading history...
300
                break;
301
302
            $out[$key] = $row;
303
304
            $count++;
305
        }
306
307
        return $this->returnConstant($out);
308
    }
309
310
    /**
311
     * Appends an array to the current one.
312
     *
313
     * @param  array|static $new_rows to append
314
     * @param  callable           $conflict_resolver to use if a new row as the
315
     *                            same key as an existing row. By default, the new
316
     *                            key will be lost and the row appended as natively.
317
     *
318
     * @throws UsageException     If the $new_rows parameter is neither an array
319
     *                            nor a static.
320
     * @return static       $this
321
     */
322
    public function append($new_rows, callable $conflict_resolver=null)
323
    {
324
        if ($new_rows instanceof static)
325
            $new_rows = $new_rows->getArray();
326
327
        if (!is_array($new_rows)) {
328
            $this->throwUsageException(
0 ignored issues
show
Bug introduced by
It seems like throwUsageException() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

328
            $this->/** @scrutinizer ignore-call */ 
329
                   throwUsageException(
Loading history...
329
                "\$new_rows parameter must be an array or an instance of " . __CLASS__
330
            );
331
        }
332
333
        if (!$conflict_resolver) {
334
            // default conflict resolver: append with numeric key
335
            $conflict_resolver = function (&$data, $existing_row, $confliuct_row, $key) {
336
                $data[] = $confliuct_row;
337
            };
338
        }
339
340
        foreach ($new_rows as $key => $new_row) {
341
            if (isset($this->data[$key])) {
342
                $arguments = [
343
                    &$this->data,
344
                    $existing_row,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $existing_row seems to be never defined.
Loading history...
345
                    $confliuct_row,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $confliuct_row seems to be never defined.
Loading history...
346
                    $key
347
                ];
348
349
                call_user_func_array($conflict_resolver, $arguments);
350
            }
351
            else {
352
                $this->data[$key] = $new_row;
0 ignored issues
show
Bug Best Practice introduced by
The property data does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
353
            }
354
        }
355
356
        return $this;
357
    }
358
359
    /**
360
     * @param $columnNames scalar[] The names of the newly created columns.
361
     * @param $options     array    Unsed presently
362
     *
363
     * @see self::dimensionsAsColumns_recurser()
364
     *
365
     * @return static
366
     */
367
    public function dimensionsAsColumns(array $columnNames, array $options=null)
368
    {
369
        $out = $this->dimensionsAsColumns_recurser($this->data, $columnNames);
370
        return $this->returnConstant($out);
371
    }
372
373
    /**
374
     *
375
     * @todo Fix case of other columns
376
     *
377
     * Example:
378
     *  dimensionsAsColumns_recurser([
379
     *      [
380
     *          0,
381
     *          'me',
382
     *      ],
383
     *      [
384
     *          1,
385
     *          'me_too',
386
     *      ],
387
     *  ],
388
     *  [
389
     *      'id',
390
     *      'name',
391
     *  ]
392
     *
393
     * => [
394
     *      'id:0-name:me'     => [
395
     *          'id'   => 0,
396
     *          'name' => 'me',
397
     *      ],
398
     *      'id:1-name:me_too' => [
399
     *          'id'   => 1,
400
     *          'name' => 'me_too',
401
     *      ],
402
     * ]
403
     */
404
    protected function dimensionsAsColumns_recurser(array $data, $columnNames, $rowIdParts=[])
405
    {
406
        $out = [];
407
        // if (!$columnNames)
408
            // return $data;
409
        $no_more_column = !(bool) $columnNames;
0 ignored issues
show
Unused Code introduced by
The assignment to $no_more_column is dead and can be removed.
Loading history...
410
411
        // If all the names have been given to the dimensions
412
        // we compile the index key of the row at the current level
413
        if (empty($columnNames)) {
414
            // echo json_encode([
415
                // 'columnNames' => $columnNames,
416
                // 'rowIdParts'  => $rowIdParts,
417
                // 'data'        => $data,
418
            // ]);
419
            // exit;
420
421
            $indexParts = [];
422
            foreach ($rowIdParts as $name => $value) {
423
                $indexParts[] = $name.':'.$value;
424
            }
425
            $row_id = implode('-', $indexParts);
426
427
            // If we are at a "leaf" of the tree
428
            foreach ($rowIdParts as $name => $value) {
429
                if (isset($data[$name]) && $data[$name] !== $value) {
430
                    self::throwUsageException(
431
                         "Trying to populate a column '$name' that "
432
                        ."already exists with a different value "
433
                        .var_export($data[$name], true). " => '$value'"
434
                    );
435
                }
436
                $data[$name] = $value;
437
            }
438
439
            $out = [
440
                $row_id => $data,
441
            ];
442
443
            return $out;
444
        }
445
446
        $currentDimensionName = array_shift($columnNames);
447
448
        foreach ($data as $key => $row) {
449
450
            // if (!$no_more_column)
451
                $rowIdParts[$currentDimensionName] = $key;
452
            // else
453
                // $rowIdParts[] = $key;
454
455
456
            if (is_array($row)) {
457
                $rows = $this->dimensionsAsColumns_recurser($row, $columnNames, $rowIdParts);
458
                foreach ($rows as $row_id => $joined_row) {
459
                    $out[$row_id] = $joined_row;
460
                }
461
            }
462
            else {
463
464
                if (!isset($rows)) {
465
                    echo json_encode([
466
                        '$rowIdParts' => $rowIdParts,
467
                        '$row' => $row,
468
                    ]);
469
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
470
                }
471
472
                foreach ($rowIdParts as $rowIdPartName => $rowIdPartValue)
473
                    $row[$rowIdPartName] = $rowIdPartValue;
474
475
                $indexParts = [];
476
                foreach ($rowIdParts as $name => $value) {
477
                    $indexParts[] = $name.':'.$value;
478
                }
479
                $row_id = implode('-', $indexParts);
480
481
                $out[$row_id] = $row;
482
            }
483
484
        }
485
486
        return $out;
487
    }
488
489
    /**
490
     * Generates an id usable in hashes to identify a single grouped row.
491
     *
492
     * @param array $row    The row of the array to group by.
493
     * @param array $groups A list of the different groups. Groups can be
494
     *                      strings describing a column name or a callable
495
     *                      function, an array representing a callable,
496
     *                      a function or an integer representing a column.
497
     *                      If the index of the group is a string, it will
498
     *                      be used as a prefix for the group name.
499
     *                      Example:
500
     *                      [
501
     *                          'column_name',
502
     *                          'function_to_call',
503
     *                          4,  //column_number
504
     *                          'group_prefix'  => function($row){},
505
     *                          'group_prefix2' => [$object, 'method'],
506
     *                      ]
507
     *
508
     * @return string       The unique identifier of the group
509
     */
510
    public static function generateGroupId(array $row, array $groups)
511
    {
512
        $group_parts = [];
513
514
        foreach ($groups as $key => $value) {
515
            $part_name = '';
516
517
            if (is_string($key)) {
518
                $part_name .= $key.'_';
519
            }
520
521
            if (is_string($value) && array_key_exists($value, $row)) {
522
                $part_name  .= $value;
523
                $group_value = $row[ $value ];
524
            }
525
            elseif (is_callable($value)) {
526
527
                if (is_string($value)) {
528
                    $part_name  .= $value;
529
                }
530
                // elseif (is_function($value)) {
531
                elseif (is_object($value) && ($value instanceof Closure)) {
0 ignored issues
show
Bug introduced by
The type JClaveau\Arrays\Closure was not found. Did you mean Closure? If so, make sure to prefix the type with \.
Loading history...
532
                    $part_name .= 'unnamed-closure-'
533
                                . hash('crc32b', var_export($value, true));
534
                }
535
                elseif (is_array($value)) {
536
                    $part_name .= implode('::', $value);
537
                }
538
539
                $group_value = call_user_func_array($value, [
540
                    $row, &$part_name
541
                ]);
542
            }
543
            elseif (is_int($value)) {
544
                $part_name  .= $value ? : '0';
545
                $group_value = $row[ $value ];
546
            }
547
            else {
548
                self::throwUsageException(
549
                    'Bad value provided for groupBy id generation: '
550
                    .var_export($value, true)
551
                    ."\n" . var_export($row, true)
552
                );
553
            }
554
555
            if (!is_null($part_name))
556
                $group_parts[ $part_name ] = $group_value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $group_value does not seem to be defined for all execution paths leading up to this point.
Loading history...
557
        }
558
559
        // sort the groups by names (without it the same group could have multiple ids)
560
        ksort($group_parts);
561
562
        // bidimensional implode
563
        $out = [];
564
        foreach ($group_parts as $group_name => $group_value) {
565
            $out[] = $group_name.':'.$group_value;
566
        }
567
568
        return implode('-', $out);
569
    }
570
571
    /**
572
     * Returns the first element of the array
573
     */
574
    public function first($strict=false)
575
    {
576
        if (!$this->count()) {
0 ignored issues
show
Bug introduced by
It seems like count() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

576
        if (!$this->/** @scrutinizer ignore-call */ count()) {
Loading history...
577
            if ($strict)
578
                throw new \ErrorException("No first element found in this array");
579
            else
580
                $first = null;
581
        }
582
        else {
583
            $key   = key($this->data);
584
            $first = reset($this->data);
585
            $this->move($key);
586
        }
587
588
        return $first;
589
    }
590
591
    /**
592
     * Returns the last element of the array
593
     *
594
     * @todo Preserve the offset
595
     */
596
    public function last($strict=false)
597
    {
598
        if (!$this->count()) {
599
            if ($strict)
600
                throw new \ErrorException("No last element found in this array");
601
            else
602
                $last = null;
603
        }
604
        else {
605
            $key  = key($this->data);
606
            $last = end($this->data);
607
            $this->move($key);
608
        }
609
610
        return $last;
611
    }
612
613
    /**
614
     *
615
     */
616
    public function firstKey($strict=false)
617
    {
618
        if (!$this->count()) {
619
            if ($strict)
620
                throw new \ErrorException("No last element found in this array");
621
            else
622
                $firstKey = null;
623
        }
624
        else {
625
            $key      = key($this->data);
626
            reset($this->data);
627
            $firstKey = key($this->data);
628
            $this->move($key);
629
        }
630
631
        return $firstKey;
632
    }
633
634
    /**
635
     *
636
     */
637
    public function lastKey($strict=false)
638
    {
639
        if (!$this->count()) {
640
            if ($strict)
641
                throw new \ErrorException("No last element found in this array");
642
            else
643
                $lastKey = null;
644
        }
645
        else {
646
            $key  = key($this->data);
647
            end($this->data);
648
            $lastKey = key($this->data);
649
            $this->move($key);
650
        }
651
652
        return $lastKey;
653
    }
654
655
    /**
656
     * Move the internal pointer of the array to the key given as parameter
657
     */
658
    public function move($key, $strict=true)
659
    {
660
        if (array_key_exists($key, $this->data)) {
661
            foreach ($this->data as $i => &$value) {
662
                if ($i === $key) {
663
                    prev($this->data);
664
                    break;
665
                }
666
            }
667
        }
668
        elseif ($strict) {
669
            throw new \ErrorException("Unable to move the internal pointer to a key that doesn't exist.");
670
        }
671
672
        return $this;
673
    }
674
675
    /**
676
     * Chained equivalent of in_array().
677
     * @return bool
678
     */
679
    public function contains($value)
680
    {
681
        return in_array($value, $this->data);
682
    }
683
684
    /**
685
     * Checks if the array is associative or not.
686
     * @return bool
687
     */
688
    public function isAssoc()
689
    {
690
        return Arrays::isAssoc($this->getArray());
0 ignored issues
show
Bug introduced by
The method isAssoc() does not exist on JClaveau\Arrays\Arrays. Did you maybe mean isAssociative()? ( Ignorable by Annotation )

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

690
        return Arrays::/** @scrutinizer ignore-call */ isAssoc($this->getArray());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
691
    }
692
693
    /**
694
     * Checks if the array is empty or not.
695
     * @return bool
696
     */
697
    public function isEmpty()
698
    {
699
        return empty($this->getArray());
700
    }
701
702
    /**
703
     * Computes the weighted mean of the values of a column weighted
704
     * by the values of a second one.
705
     *
706
     * @param  string $valueColumnName
707
     * @param  string $weightColumnName
708
     *
709
     * @return float The calculated weighted mean.
710
     */
711
    public function weightedMean($valueColumnName, $weightColumnName)
712
    {
713
        $values  = array_column($this->data, $valueColumnName);
714
        $weights = array_column($this->data, $weightColumnName);
715
716
        return Arrays::weightedMean($values, $weights);
717
    }
718
719
    /**
720
     * Equivalent of var_dump().
721
     *
722
     * @see http://php.net/manual/fr/function.var-dump.php
723
     * @todo Handle xdebug dump formatting
724
     */
725
    public function dump($exit=false)
726
    {
727
        $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
728
        $caller = $bt[0];
729
730
        var_export([
731
            'location' => $caller['file'] . ':' . $caller['line'],
732
            'data'     => $this->data,
733
        ]);
734
735
        if ($exit)
736
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
737
738
        return $this;
739
    }
740
741
    /**
742
     * Scans the array recursivelly (until the max depthis reached) and replaces
743
     * the entries with the callback;
744
     *
745
     * @todo move it to an Arrays class storing static methods
746
     */
747
    public static function replaceEntries(
748
        array $array, callable $replacer, $max_depth=null
749
    ) {
750
        foreach ($array as $key => &$row) {
751
            $arguments = [&$row, $key];
752
            call_user_func_array($replacer, $arguments);
753
754
            if (is_array($row) && $max_depth !== 0) { // allowing null to have no depth limit
755
                $row = self::replaceEntries(
756
                    $row, $replacer, $max_depth ? $max_depth-1 : $max_depth
757
                );
758
            }
759
        }
760
761
        return $array;
762
    }
763
764
    /**
765
     * Equivalent of ->filter() but removes the matching values
766
     *
767
     * @param  callable|array $callback The filter logic with $value and $key
768
     *                            as parameters.
769
     *
770
     * @return static $this or a new static.
771
     */
772
    public function extract($callback=null)
773
    {
774
        if ($callback) {
775
776
            if (is_array($callback)) {
777
                $callback = new \JClaveau\LogicalFilter\LogicalFilter($callback);
0 ignored issues
show
Bug introduced by
The type JClaveau\LogicalFilter\LogicalFilter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
778
            }
779
780
            if (!is_callable($callback)) {
781
                $this->throwUsageException(
782
                    "\$callback must be a logical filter description array or a callable"
783
                    ." instead of "
784
                    .var_export($callback, true)
785
                );
786
            }
787
788
            $out = [];
789
            foreach ($this->data as $key => $value) {
790
                if ($callback($value, $key)) {
791
                    $out[$key] = $value;
792
                    unset( $this->data[$key] );
793
                }
794
            }
795
        }
796
797
        return new static($out);
0 ignored issues
show
Unused Code introduced by
The call to JClaveau\Arrays\Chainabl...ls_Trait::__construct() has too many arguments starting with $out. ( Ignorable by Annotation )

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

797
        return /** @scrutinizer ignore-call */ new static($out);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Comprehensibility Best Practice introduced by
The variable $out does not seem to be defined for all execution paths leading up to this point.
Loading history...
798
    }
799
800
    /**/
801
}
802