Passed
Pull Request — master (#34)
by Alexander
26:50 queued 11:51
created

ArrayHelperTest::testMergeWithReverseBlock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 21
c 3
b 0
f 1
dl 0
loc 31
rs 9.584
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Yiisoft\Arrays\Tests;
4
5
use ArrayObject;
6
use PHPUnit\Framework\TestCase;
7
use stdClass;
8
use Yiisoft\Arrays\ArrayableInterface;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Arrays\Modifier\RemoveKeys;
11
use Yiisoft\Arrays\Modifier\ReverseBlockMerge;
12
use Yiisoft\Arrays\Modifier\ReplaceValue;
13
use Yiisoft\Arrays\Modifier\UnsetValue;
14
15
final class ArrayHelperTest extends TestCase
16
{
17
    public function testToArray(): void
18
    {
19
        $data = $this->getMockBuilder(ArrayableInterface::class)
20
            ->getMock()
21
            ->method('toArray')
22
            ->willReturn([]);
23
24
        $this->assertEquals([], ArrayHelper::toArray($data));
25
        $this->assertEquals(['foo'], ArrayHelper::toArray('foo'));
26
        $object = new Post1();
27
        $this->assertEquals(get_object_vars($object), ArrayHelper::toArray($object));
28
        $object = new Post2();
29
        $this->assertEquals(get_object_vars($object), ArrayHelper::toArray($object));
30
31
        $object1 = new Post1();
32
        $object2 = new Post2();
33
        $this->assertEquals(
34
            [
35
                get_object_vars($object1),
36
                get_object_vars($object2),
37
            ],
38
            ArrayHelper::toArray(
39
                [
40
                    $object1,
41
                    $object2,
42
                ]
43
            )
44
        );
45
46
        $object = new Post2();
47
        $this->assertEquals(
48
            [
49
                'id' => 123,
50
                'secret' => 's',
51
                '_content' => 'test',
52
                'length' => 4,
53
            ],
54
            ArrayHelper::toArray(
55
                $object,
56
                [
57
                    get_class($object) => [
58
                        'id',
59
                        'secret',
60
                        '_content' => 'content',
61
                        'length' => function ($post) {
62
                            return strlen($post->content);
63
                        },
64
                    ],
65
                ]
66
            )
67
        );
68
69
        $object = new Post3();
70
        $this->assertEquals(get_object_vars($object), ArrayHelper::toArray($object, [], false));
71
        $this->assertEquals(
72
            [
73
                'id' => 33,
74
                'subObject' => [
75
                    'id' => 123,
76
                    'content' => 'test',
77
                ],
78
            ],
79
            ArrayHelper::toArray($object)
80
        );
81
82
        //recursive with attributes of object and subobject
83
        $this->assertEquals(
84
            [
85
                'id' => 33,
86
                'id_plus_1' => 34,
87
                'subObject' => [
88
                    'id' => 123,
89
                    'id_plus_1' => 124,
90
                ],
91
            ],
92
            ArrayHelper::toArray(
93
                $object,
94
                [
95
                    get_class($object) => [
96
                        'id',
97
                        'subObject',
98
                        'id_plus_1' => static function ($post) {
99
                            return $post->id + 1;
100
                        },
101
                    ],
102
                    get_class($object->subObject) => [
103
                        'id',
104
                        'id_plus_1' => static function ($post) {
105
                            return $post->id + 1;
106
                        },
107
                    ],
108
                ]
109
            )
110
        );
111
112
        //recursive with attributes of subobject only
113
        $this->assertEquals(
114
            [
115
                'id' => 33,
116
                'subObject' => [
117
                    'id' => 123,
118
                    'id_plus_1' => 124,
119
                ],
120
            ],
121
            ArrayHelper::toArray(
122
                $object,
123
                [
124
                    get_class($object->subObject) => [
125
                        'id',
126
                        'id_plus_1' => static function ($post) {
127
                            return $post->id + 1;
128
                        },
129
                    ],
130
                ]
131
            )
132
        );
133
    }
134
135
    public function testRemove(): void
136
    {
137
        $array = ['name' => 'b', 'age' => 3];
138
        $name = ArrayHelper::remove($array, 'name');
139
140
        $this->assertEquals('b', $name);
141
        $this->assertEquals(['age' => 3], $array);
142
143
        $default = ArrayHelper::remove($array, 'nonExisting', 'defaultValue');
144
        $this->assertEquals('defaultValue', $default);
145
    }
146
147
    public function testRemoveValueMultiple(): void
148
    {
149
        $array = [
150
            'Bob' => 'Dylan',
151
            'Michael' => 'Jackson',
152
            'Mick' => 'Jagger',
153
            'Janet' => 'Jackson',
154
        ];
155
156
        $removed = ArrayHelper::removeValue($array, 'Jackson');
157
158
        $this->assertEquals(
159
            [
160
                'Bob' => 'Dylan',
161
                'Mick' => 'Jagger',
162
            ],
163
            $array
164
        );
165
        $this->assertEquals(
166
            [
167
                'Michael' => 'Jackson',
168
                'Janet' => 'Jackson',
169
            ],
170
            $removed
171
        );
172
    }
173
174
    public function testRemoveValueNotExisting(): void
175
    {
176
        $array = [
177
            'Bob' => 'Dylan',
178
            'Michael' => 'Jackson',
179
            'Mick' => 'Jagger',
180
            'Janet' => 'Jackson',
181
        ];
182
183
        $removed = ArrayHelper::removeValue($array, 'Marley');
184
185
        $this->assertEquals(
186
            [
187
                'Bob' => 'Dylan',
188
                'Michael' => 'Jackson',
189
                'Mick' => 'Jagger',
190
                'Janet' => 'Jackson',
191
            ],
192
            $array
193
        );
194
        $this->assertEquals([], $removed);
195
    }
196
197
    public function testEmptyMerge(): void
198
    {
199
        $this->assertEquals([], ArrayHelper::merge(...[]));
200
    }
201
202
    public function testMerge(): void
203
    {
204
        $a = [
205
            'name' => 'Yii',
206
            'version' => '1.0',
207
            'options' => [
208
                'namespace' => false,
209
                'unittest' => false,
210
            ],
211
            'features' => [
212
                'mvc',
213
            ],
214
        ];
215
        $b = [
216
            'version' => '1.1',
217
            'options' => [
218
                'unittest' => true,
219
            ],
220
            'features' => [
221
                'gii',
222
            ],
223
        ];
224
        $c = [
225
            'version' => '2.0',
226
            'options' => [
227
                'namespace' => true,
228
            ],
229
            'features' => [
230
                'debug',
231
            ],
232
            'foo',
233
        ];
234
235
        $result = ArrayHelper::merge($a, $b, $c);
236
        $expected = [
237
            'name' => 'Yii',
238
            'version' => '2.0',
239
            'options' => [
240
                'namespace' => true,
241
                'unittest' => true,
242
            ],
243
            'features' => [
244
                'mvc',
245
                'gii',
246
                'debug',
247
            ],
248
            'foo',
249
        ];
250
251
        $this->assertEquals($expected, $result);
252
    }
253
254
    public function testMergeWithUnset(): void
255
    {
256
        $a = [
257
            'name' => 'Yii',
258
            'version' => '1.0',
259
            'options' => [
260
                'namespace' => false,
261
                'unittest' => false,
262
            ],
263
            'features' => [
264
                'mvc',
265
            ],
266
        ];
267
        $b = [
268
            'version' => '1.1',
269
            'options' => new UnsetValue(),
270
            'features' => [
271
                'gii',
272
            ],
273
        ];
274
275
        $result = ArrayHelper::merge($a, $b);
276
        $expected = [
277
            'name' => 'Yii',
278
            'version' => '1.1',
279
            'features' => [
280
                'mvc',
281
                'gii',
282
            ],
283
        ];
284
285
        $this->assertEquals($expected, $result);
286
    }
287
288
    public function testMergeWithReplace(): void
289
    {
290
        $a = [
291
            'name' => 'Yii',
292
            'version' => '1.0',
293
            'options' => [
294
                'namespace' => false,
295
                'unittest' => false,
296
            ],
297
            'features' => [
298
                'mvc',
299
            ],
300
        ];
301
        $b = [
302
            'version' => '1.1',
303
            'options' => [
304
                'unittest' => true,
305
            ],
306
            'features' => new ReplaceValue(
307
                [
308
                    'gii',
309
                ]
310
            ),
311
        ];
312
313
        $result = ArrayHelper::merge($a, $b);
314
        $expected = [
315
            'name' => 'Yii',
316
            'version' => '1.1',
317
            'options' => [
318
                'namespace' => false,
319
                'unittest' => true,
320
            ],
321
            'features' => [
322
                'gii',
323
            ],
324
        ];
325
326
        $this->assertEquals($expected, $result);
327
    }
328
329
    public function testMergeWithRemoveKeys(): void
330
    {
331
        $a = [
332
            'name' => 'Yii',
333
            'version' => '1.0',
334
        ];
335
        $b = [
336
            'version' => '1.1',
337
            'options' => [],
338
            new RemoveKeys(),
339
        ];
340
341
        $result = ArrayHelper::merge($a, $b);
342
        $expected = [
343
            'Yii',
344
            '1.1',
345
            [],
346
        ];
347
348
        $this->assertEquals($expected, $result);
349
    }
350
351
    public function testMergeWithReverseBlock(): void
352
    {
353
        $a = [
354
            'name' => 'Yii',
355
            'options' => [
356
                'option1' => 'valueA',
357
                'option3' => 'valueAA',
358
            ],
359
            'version' => '1.0',
360
        ];
361
        $b = [
362
            'version' => '1.1',
363
            'options' => [
364
                'option1' => 'valueB',
365
                'option2' => 'valueBB',
366
            ],
367
            ReverseBlockMerge::class => new ReverseBlockMerge(),
368
        ];
369
370
        $result = ArrayHelper::merge($a, $b);
371
        $expected = [
372
            'version' => '1.1',
373
            'options' => [
374
                'option1' => 'valueB',
375
                'option2' => 'valueBB',
376
                'option3' => 'valueAA',
377
            ],
378
            'name' => 'Yii',
379
        ];
380
381
        $this->assertSame($expected, $result);
382
    }
383
384
    public function testMergeWithNullValues(): void
385
    {
386
        $a = [
387
            'firstValue',
388
            null,
389
        ];
390
        $b = [
391
            'secondValue',
392
            'thirdValue'
393
        ];
394
395
        $result = ArrayHelper::merge($a, $b);
396
        $expected = [
397
            'firstValue',
398
            null,
399
            'secondValue',
400
            'thirdValue',
401
        ];
402
403
        $this->assertEquals($expected, $result);
404
    }
405
406
407
    public function testMergeIntegerKeyedArraysWithSameValue(): void
408
    {
409
        $a = ['2019-01-25'];
410
        $b = ['2019-01-25'];
411
        $c = ['2019-01-25'];
412
413
        $result = ArrayHelper::merge($a, $b, $c);
414
        $expected = ['2019-01-25'];
415
416
        $this->assertEquals($expected, $result);
417
    }
418
419
    /**
420
     * @see https://github.com/yiisoft/yii2/pull/11549
421
     */
422
    public function testFloatKey(): void
423
    {
424
        $array = [];
425
        $array[1.0] = 'some value';
426
427
        $result = ArrayHelper::getValue($array, 1.0);
428
429
        $this->assertEquals('some value', $result);
430
    }
431
432
    public function testIndex(): void
433
    {
434
        $array = [
435
            ['id' => '123', 'data' => 'abc'],
436
            ['id' => '345', 'data' => 'def'],
437
            ['id' => '345', 'data' => 'ghi'],
438
        ];
439
        $result = ArrayHelper::index($array, 'id');
440
        $this->assertEquals(
441
            [
442
                '123' => ['id' => '123', 'data' => 'abc'],
443
                '345' => ['id' => '345', 'data' => 'ghi'],
444
            ],
445
            $result
446
        );
447
448
        $result = ArrayHelper::index(
449
            $array,
450
            static function ($element) {
451
                return $element['data'];
452
            }
453
        );
454
        $this->assertEquals(
455
            [
456
                'abc' => ['id' => '123', 'data' => 'abc'],
457
                'def' => ['id' => '345', 'data' => 'def'],
458
                'ghi' => ['id' => '345', 'data' => 'ghi'],
459
            ],
460
            $result
461
        );
462
463
        $result = ArrayHelper::index($array, null);
464
        $this->assertEquals([], $result);
465
466
        $result = ArrayHelper::index(
467
            $array,
468
            static function () {
469
                return null;
470
            }
471
        );
472
        $this->assertEquals([], $result);
473
474
        $result = ArrayHelper::index(
475
            $array,
476
            static function ($element) {
477
                return $element['id'] === '345' ? null : $element['id'];
478
            }
479
        );
480
        $this->assertEquals(
481
            [
482
                '123' => ['id' => '123', 'data' => 'abc'],
483
            ],
484
            $result
485
        );
486
    }
487
488
    public function testIndexGroupBy(): void
489
    {
490
        $array = [
491
            ['id' => '123', 'data' => 'abc'],
492
            ['id' => '345', 'data' => 'def'],
493
            ['id' => '345', 'data' => 'ghi'],
494
        ];
495
496
        $expected = [
497
            '123' => [
498
                ['id' => '123', 'data' => 'abc'],
499
            ],
500
            '345' => [
501
                ['id' => '345', 'data' => 'def'],
502
                ['id' => '345', 'data' => 'ghi'],
503
            ],
504
        ];
505
        $result = ArrayHelper::index($array, null, ['id']);
506
        $this->assertEquals($expected, $result);
507
        $result = ArrayHelper::index($array, null, 'id');
508
        $this->assertEquals($expected, $result);
509
510
        $result = ArrayHelper::index($array, null, ['id', 'data']);
511
        $this->assertEquals(
512
            [
513
                '123' => [
514
                    'abc' => [
515
                        ['id' => '123', 'data' => 'abc'],
516
                    ],
517
                ],
518
                '345' => [
519
                    'def' => [
520
                        ['id' => '345', 'data' => 'def'],
521
                    ],
522
                    'ghi' => [
523
                        ['id' => '345', 'data' => 'ghi'],
524
                    ],
525
                ],
526
            ],
527
            $result
528
        );
529
530
        $expected = [
531
            '123' => [
532
                'abc' => ['id' => '123', 'data' => 'abc'],
533
            ],
534
            '345' => [
535
                'def' => ['id' => '345', 'data' => 'def'],
536
                'ghi' => ['id' => '345', 'data' => 'ghi'],
537
            ],
538
        ];
539
        $result = ArrayHelper::index($array, 'data', ['id']);
540
        $this->assertEquals($expected, $result);
541
        $result = ArrayHelper::index($array, 'data', 'id');
542
        $this->assertEquals($expected, $result);
543
        $result = ArrayHelper::index(
544
            $array,
545
            static function ($element) {
546
                return $element['data'];
547
            },
548
            'id'
549
        );
550
        $this->assertEquals($expected, $result);
551
552
        $expected = [
553
            '123' => [
554
                'abc' => [
555
                    'abc' => ['id' => '123', 'data' => 'abc'],
556
                ],
557
            ],
558
            '345' => [
559
                'def' => [
560
                    'def' => ['id' => '345', 'data' => 'def'],
561
                ],
562
                'ghi' => [
563
                    'ghi' => ['id' => '345', 'data' => 'ghi'],
564
                ],
565
            ],
566
        ];
567
        $result = ArrayHelper::index($array, 'data', ['id', 'data']);
568
        $this->assertEquals($expected, $result);
569
        $result = ArrayHelper::index(
570
            $array,
571
            static function ($element) {
572
                return $element['data'];
573
            },
574
            ['id', 'data']
575
        );
576
        $this->assertEquals($expected, $result);
577
    }
578
579
    /**
580
     * @see https://github.com/yiisoft/yii2/issues/11739
581
     */
582
    public function testIndexFloat(): void
583
    {
584
        $array = [
585
            ['id' => 1e6],
586
            ['id' => 1e32],
587
            ['id' => 1e64],
588
            ['id' => 1465540807.522109],
589
        ];
590
591
        $expected = [
592
            '1000000' => ['id' => 1e6],
593
            '1.0E+32' => ['id' => 1e32],
594
            '1.0E+64' => ['id' => 1e64],
595
            '1465540807.5221' => ['id' => 1465540807.522109],
596
        ];
597
598
        $result = ArrayHelper::index($array, 'id');
599
600
        $this->assertEquals($expected, $result);
601
    }
602
603
    public function testGetColumn(): void
604
    {
605
        $array = [
606
            'a' => ['id' => '123', 'data' => 'abc'],
607
            'b' => ['id' => '345', 'data' => 'def'],
608
        ];
609
        $result = ArrayHelper::getColumn($array, 'id');
610
        $this->assertEquals(['a' => '123', 'b' => '345'], $result);
611
        $result = ArrayHelper::getColumn($array, 'id', false);
612
        $this->assertEquals(['123', '345'], $result);
613
614
        $result = ArrayHelper::getColumn(
615
            $array,
616
            static function ($element) {
617
                return $element['data'];
618
            }
619
        );
620
        $this->assertEquals(['a' => 'abc', 'b' => 'def'], $result);
621
        $result = ArrayHelper::getColumn(
622
            $array,
623
            static function ($element) {
624
                return $element['data'];
625
            },
626
            false
627
        );
628
        $this->assertEquals(['abc', 'def'], $result);
629
    }
630
631
    public function testMap(): void
632
    {
633
        $array = [
634
            ['id' => '123', 'name' => 'aaa', 'class' => 'x'],
635
            ['id' => '124', 'name' => 'bbb', 'class' => 'x'],
636
            ['id' => '345', 'name' => 'ccc', 'class' => 'y'],
637
        ];
638
639
        $result = ArrayHelper::map($array, 'id', 'name');
640
        $this->assertEquals(
641
            [
642
                '123' => 'aaa',
643
                '124' => 'bbb',
644
                '345' => 'ccc',
645
            ],
646
            $result
647
        );
648
649
        $result = ArrayHelper::map($array, 'id', 'name', 'class');
650
        $this->assertEquals(
651
            [
652
                'x' => [
653
                    '123' => 'aaa',
654
                    '124' => 'bbb',
655
                ],
656
                'y' => [
657
                    '345' => 'ccc',
658
                ],
659
            ],
660
            $result
661
        );
662
    }
663
664
    public function testKeyExists(): void
665
    {
666
        $array = [
667
            'a' => 1,
668
            'B' => 2,
669
        ];
670
        $this->assertTrue(ArrayHelper::keyExists($array, 'a'));
671
        $this->assertFalse(ArrayHelper::keyExists($array, 'b'));
672
        $this->assertTrue(ArrayHelper::keyExists($array, 'B'));
673
        $this->assertFalse(ArrayHelper::keyExists($array, 'c'));
674
675
        $this->assertTrue(ArrayHelper::keyExists($array, 'a', false));
676
        $this->assertTrue(ArrayHelper::keyExists($array, 'b', false));
677
        $this->assertTrue(ArrayHelper::keyExists($array, 'B', false));
678
        $this->assertFalse(ArrayHelper::keyExists($array, 'c', false));
679
    }
680
681
    public function getValueFromArrayProvider(): array
682
    {
683
        return [
684
            ['name', 'test'],
685
            ['noname', null],
686
            ['noname', 'test', 'test'],
687
            ['post.id', 5],
688
            ['post.id', 5, 'test'],
689
            ['nopost.id', null],
690
            ['nopost.id', 'test', 'test'],
691
            ['post.author.name', 'cebe'],
692
            ['post.author.noname', null],
693
            ['post.author.noname', 'test', 'test'],
694
            ['post.author.profile.title', '1337'],
695
            ['admin.firstname', 'Qiang'],
696
            ['admin.firstname', 'Qiang', 'test'],
697
            ['admin.lastname', 'Xue'],
698
            [
699
                static function ($array, $defaultValue) {
700
                    return $array['date'] . $defaultValue;
701
                },
702
                '31-12-2113test',
703
                'test',
704
            ],
705
            [['version', '1.0', 'status'], 'released'],
706
            [['version', '1.0', 'date'], 'defaultValue', 'defaultValue'],
707
        ];
708
    }
709
710
    /**
711
     * @dataProvider getValueFromArrayProvider
712
     *
713
     * @param $key
714
     * @param $expected
715
     * @param null $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
716
     */
717
    public function testGetValueFromArray($key, $expected, $default = null): void
718
    {
719
        $array = [
720
            'name' => 'test',
721
            'date' => '31-12-2113',
722
            'post' => [
723
                'id' => 5,
724
                'author' => [
725
                    'name' => 'cebe',
726
                    'profile' => [
727
                        'title' => '1337',
728
                    ],
729
                ],
730
            ],
731
            'admin.firstname' => 'Qiang',
732
            'admin.lastname' => 'Xue',
733
            'admin' => [
734
                'lastname' => 'cebe',
735
            ],
736
            'version' => [
737
                '1.0' => [
738
                    'status' => 'released',
739
                ],
740
            ],
741
        ];
742
743
        $this->assertEquals($expected, ArrayHelper::getValue($array, $key, $default));
744
    }
745
746
    /**
747
     * @see https://github.com/yiisoft/arrays/issues/1
748
     */
749
    public function testGetValueConsistentWithSetValue(): void
750
    {
751
        $array = [
752
            'a.b' => [
753
                'c' => 'value1',
754
            ],
755
        ];
756
        $this->assertEquals(null, ArrayHelper::getValue($array, 'a.b.c'));
757
        ArrayHelper::setValue($array, 'a.b.c', 'newValue');
758
        $this->assertEquals('newValue', ArrayHelper::getValue($array, 'a.b.c'));
759
    }
760
761
    public function testGetValueObjects(): void
762
    {
763
        $arrayObject = new ArrayObject(['id' => 23], ArrayObject::ARRAY_AS_PROPS);
764
        $this->assertEquals(23, ArrayHelper::getValue($arrayObject, 'id'));
765
766
        $object = new Post1();
767
        $this->assertEquals(23, ArrayHelper::getValue($object, 'id'));
768
    }
769
770
    public function testGetNestedObjectsValueFromObject(): void
771
    {
772
        $object = new stdClass();
773
        $object->subObject = new stdClass();
774
        $object->subObject->id = 155;
775
776
        $this->assertEquals(155, ArrayHelper::getValue($object, 'subObject.id'));
777
    }
778
779
    public function testGetNestedValueFromObjectThatFromArrayFromObject(): void
780
    {
781
        $subObject = new stdClass();
782
        $subObject->id = 200;
783
784
        $object = new stdClass();
785
        $object->subObject = ['sub' => $subObject];
786
787
        $this->assertEquals(200, ArrayHelper::getValue($object, 'subObject.sub.id'));
788
    }
789
790
    public function testGetNestedValueFromObjectFromArray(): void
791
    {
792
        $stdClass = new stdClass();
793
        $stdClass->id = 250;
794
        $object = ['main' => $stdClass];
795
796
        $this->assertEquals(250, ArrayHelper::getValue($object, 'main.id'));
797
    }
798
799
    /**
800
     * This is expected to result in a PHP error.
801
     */
802
    public function testGetValueNonexistingProperties1(): void
803
    {
804
        $this->expectError();
805
        $object = new Post1();
806
        $this->assertNull(ArrayHelper::getValue($object, 'nonExisting'));
807
    }
808
809
    /**
810
     * This is expected to result in a PHP error.
811
     */
812
    public function testGetValueNonexistingProperties2(): void
813
    {
814
        $this->expectError();
815
        $arrayObject = new ArrayObject(['id' => 23], ArrayObject::ARRAY_AS_PROPS);
816
        $this->assertEquals(23, ArrayHelper::getValue($arrayObject, 'nonExisting'));
817
    }
818
819
    /**
820
     * Data provider for [[testSetValue()]].
821
     * @return array test data
822
     */
823
    public function dataProviderSetValue(): array
824
    {
825
        return [
826
            [
827
                [
828
                    'key1' => 'val1',
829
                    'key2' => 'val2',
830
                ],
831
                'key',
832
                'val',
833
                [
834
                    'key1' => 'val1',
835
                    'key2' => 'val2',
836
                    'key' => 'val',
837
                ],
838
            ],
839
            [
840
                [
841
                    'key1' => 'val1',
842
                    'key2' => 'val2',
843
                ],
844
                'key2',
845
                'val',
846
                [
847
                    'key1' => 'val1',
848
                    'key2' => 'val',
849
                ],
850
            ],
851
852
            [
853
                [
854
                    'key1' => 'val1',
855
                ],
856
                'key.in',
857
                'val',
858
                [
859
                    'key1' => 'val1',
860
                    'key' => ['in' => 'val'],
861
                ],
862
            ],
863
            [
864
                [
865
                    'key' => 'val1',
866
                ],
867
                'key.in',
868
                'val',
869
                [
870
                    'key' => [
871
                        'val1',
872
                        'in' => 'val',
873
                    ],
874
                ],
875
            ],
876
            [
877
                [
878
                    'key' => 'val1',
879
                ],
880
                'key',
881
                ['in' => 'val'],
882
                [
883
                    'key' => ['in' => 'val'],
884
                ],
885
            ],
886
887
            [
888
                [
889
                    'key1' => 'val1',
890
                ],
891
                'key.in.0',
892
                'val',
893
                [
894
                    'key1' => 'val1',
895
                    'key' => [
896
                        'in' => ['val'],
897
                    ],
898
                ],
899
            ],
900
901
            [
902
                [
903
                    'key1' => 'val1',
904
                ],
905
                'key.in.arr',
906
                'val',
907
                [
908
                    'key1' => 'val1',
909
                    'key' => [
910
                        'in' => [
911
                            'arr' => 'val',
912
                        ],
913
                    ],
914
                ],
915
            ],
916
            [
917
                [
918
                    'key1' => 'val1',
919
                ],
920
                'key.in.arr',
921
                ['val'],
922
                [
923
                    'key1' => 'val1',
924
                    'key' => [
925
                        'in' => [
926
                            'arr' => ['val'],
927
                        ],
928
                    ],
929
                ],
930
            ],
931
            [
932
                [
933
                    'key' => [
934
                        'in' => ['val1'],
935
                    ],
936
                ],
937
                'key.in.arr',
938
                'val',
939
                [
940
                    'key' => [
941
                        'in' => [
942
                            'val1',
943
                            'arr' => 'val',
944
                        ],
945
                    ],
946
                ],
947
            ],
948
949
            [
950
                [
951
                    'key' => ['in' => 'val1'],
952
                ],
953
                'key.in.arr',
954
                ['val'],
955
                [
956
                    'key' => [
957
                        'in' => [
958
                            'val1',
959
                            'arr' => ['val'],
960
                        ],
961
                    ],
962
                ],
963
            ],
964
            [
965
                [
966
                    'key' => [
967
                        'in' => [
968
                            'val1',
969
                            'key' => 'val',
970
                        ],
971
                    ],
972
                ],
973
                'key.in.0',
974
                ['arr' => 'val'],
975
                [
976
                    'key' => [
977
                        'in' => [
978
                            ['arr' => 'val'],
979
                            'key' => 'val',
980
                        ],
981
                    ],
982
                ],
983
            ],
984
            [
985
                [
986
                    'key' => [
987
                        'in' => [
988
                            'val1',
989
                            'key' => 'val',
990
                        ],
991
                    ],
992
                ],
993
                'key.in',
994
                ['arr' => 'val'],
995
                [
996
                    'key' => [
997
                        'in' => ['arr' => 'val'],
998
                    ],
999
                ],
1000
            ],
1001
            [
1002
                [
1003
                    'key' => [
1004
                        'in' => [
1005
                            'key' => 'val',
1006
                            'data' => [
1007
                                'attr1',
1008
                                'attr2',
1009
                                'attr3',
1010
                            ],
1011
                        ],
1012
                    ],
1013
                ],
1014
                'key.in.schema',
1015
                'array',
1016
                [
1017
                    'key' => [
1018
                        'in' => [
1019
                            'key' => 'val',
1020
                            'schema' => 'array',
1021
                            'data' => [
1022
                                'attr1',
1023
                                'attr2',
1024
                                'attr3',
1025
                            ],
1026
                        ],
1027
                    ],
1028
                ],
1029
            ],
1030
            [
1031
                [
1032
                    'key' => [
1033
                        'in.array' => [
1034
                            'key' => 'val',
1035
                        ],
1036
                    ],
1037
                ],
1038
                ['key', 'in.array', 'ok.schema'],
1039
                'array',
1040
                [
1041
                    'key' => [
1042
                        'in.array' => [
1043
                            'key' => 'val',
1044
                            'ok.schema' => 'array',
1045
                        ],
1046
                    ],
1047
                ],
1048
            ],
1049
            [
1050
                [
1051
                    'key' => ['val'],
1052
                ],
1053
                null,
1054
                'data',
1055
                'data',
1056
            ],
1057
        ];
1058
    }
1059
1060
    /**
1061
     * @dataProvider dataProviderSetValue
1062
     *
1063
     * @param array $arrayInput
1064
     * @param string|array|null $key
1065
     * @param mixed $value
1066
     * @param mixed $expected
1067
     */
1068
    public function testSetValue(array $arrayInput, $key, $value, $expected): void
1069
    {
1070
        ArrayHelper::setValue($arrayInput, $key, $value);
1071
        $this->assertEquals($expected, $arrayInput);
1072
    }
1073
1074
    public function testIsAssociative(): void
1075
    {
1076
        $this->assertFalse(ArrayHelper::isAssociative([]));
1077
        $this->assertFalse(ArrayHelper::isAssociative([1, 2, 3]));
1078
        $this->assertFalse(ArrayHelper::isAssociative([1], false));
1079
        $this->assertTrue(ArrayHelper::isAssociative(['name' => 1, 'value' => 'test']));
1080
        $this->assertFalse(ArrayHelper::isAssociative(['name' => 1, 'value' => 'test', 3]));
1081
        $this->assertTrue(ArrayHelper::isAssociative(['name' => 1, 'value' => 'test', 3], false));
1082
    }
1083
1084
    public function testIsIndexed(): void
1085
    {
1086
        $this->assertTrue(ArrayHelper::isIndexed([]));
1087
        $this->assertTrue(ArrayHelper::isIndexed([1, 2, 3]));
1088
        $this->assertTrue(ArrayHelper::isIndexed([2 => 'a', 3 => 'b']));
1089
        $this->assertFalse(ArrayHelper::isIndexed([2 => 'a', 3 => 'b'], true));
1090
        $this->assertFalse(ArrayHelper::isIndexed(['a' => 'b'], false));
1091
    }
1092
1093
    public function testHtmlEncode(): void
1094
    {
1095
        $array = [
1096
            'abc' => '123',
1097
            '<' => '>',
1098
            'cde' => false,
1099
            3 => 'blank',
1100
            [
1101
                '<>' => 'a<>b',
1102
                '23' => true,
1103
            ],
1104
            'invalid' => "a\x80b",
1105
        ];
1106
        $this->assertEquals(
1107
            [
1108
                'abc' => '123',
1109
                '<' => '&gt;',
1110
                'cde' => false,
1111
                3 => 'blank',
1112
                [
1113
                    '<>' => 'a&lt;&gt;b',
1114
                    '23' => true,
1115
                ],
1116
                'invalid' => 'a�b',
1117
            ],
1118
            ArrayHelper::htmlEncode($array)
1119
        );
1120
        $this->assertEquals(
1121
            [
1122
                'abc' => '123',
1123
                '&lt;' => '&gt;',
1124
                'cde' => false,
1125
                3 => 'blank',
1126
                [
1127
                    '&lt;&gt;' => 'a&lt;&gt;b',
1128
                    '23' => true,
1129
                ],
1130
                'invalid' => 'a�b',
1131
            ],
1132
            ArrayHelper::htmlEncode($array, false)
1133
        );
1134
    }
1135
1136
    public function testHtmlDecode(): void
1137
    {
1138
        $array = [
1139
            'abc' => '123',
1140
            '&lt;' => '&gt;',
1141
            'cde' => false,
1142
            3 => 'blank',
1143
            [
1144
                '<>' => 'a&lt;&gt;b',
1145
                '23' => true,
1146
            ],
1147
        ];
1148
        $this->assertEquals(
1149
            [
1150
                'abc' => '123',
1151
                '&lt;' => '>',
1152
                'cde' => false,
1153
                3 => 'blank',
1154
                [
1155
                    '<>' => 'a<>b',
1156
                    '23' => true,
1157
                ],
1158
            ],
1159
            ArrayHelper::htmlDecode($array)
1160
        );
1161
        $this->assertEquals(
1162
            [
1163
                'abc' => '123',
1164
                '<' => '>',
1165
                'cde' => false,
1166
                3 => 'blank',
1167
                [
1168
                    '<>' => 'a<>b',
1169
                    '23' => true,
1170
                ],
1171
            ],
1172
            ArrayHelper::htmlDecode($array, false)
1173
        );
1174
    }
1175
1176
    public function testIsIn(): void
1177
    {
1178
        $this->assertTrue(ArrayHelper::isIn('a', new ArrayObject(['a', 'b'])));
1179
        $this->assertTrue(ArrayHelper::isIn('a', ['a', 'b']));
1180
1181
        $this->assertTrue(ArrayHelper::isIn('1', new ArrayObject([1, 'b'])));
1182
        $this->assertTrue(ArrayHelper::isIn('1', [1, 'b']));
1183
1184
        $this->assertFalse(ArrayHelper::isIn('1', new ArrayObject([1, 'b']), true));
1185
        $this->assertFalse(ArrayHelper::isIn('1', [1, 'b'], true));
1186
1187
        $this->assertTrue(ArrayHelper::isIn(['a'], new ArrayObject([['a'], 'b'])));
1188
        $this->assertFalse(ArrayHelper::isIn('a', new ArrayObject([['a'], 'b'])));
1189
        $this->assertFalse(ArrayHelper::isIn('a', [['a'], 'b']));
1190
    }
1191
1192
    public function testIsInStrict(): void
1193
    {
1194
        // strict comparison
1195
        $this->assertTrue(ArrayHelper::isIn(1, new ArrayObject([1, 'a']), true));
1196
        $this->assertTrue(ArrayHelper::isIn(1, [1, 'a'], true));
1197
1198
        $this->assertFalse(ArrayHelper::isIn('1', new ArrayObject([1, 'a']), true));
1199
        $this->assertFalse(ArrayHelper::isIn('1', [1, 'a'], true));
1200
    }
1201
1202
    public function testIsSubset(): void
1203
    {
1204
        $this->assertTrue(ArrayHelper::isSubset(['a'], new ArrayObject(['a', 'b'])));
1205
        $this->assertTrue(ArrayHelper::isSubset(new ArrayObject(['a']), ['a', 'b']));
1206
1207
        $this->assertTrue(ArrayHelper::isSubset([1], new ArrayObject(['1', 'b'])));
1208
        $this->assertTrue(ArrayHelper::isSubset(new ArrayObject([1]), ['1', 'b']));
1209
1210
        $this->assertFalse(ArrayHelper::isSubset([1], new ArrayObject(['1', 'b']), true));
1211
        $this->assertFalse(ArrayHelper::isSubset(new ArrayObject([1]), ['1', 'b'], true));
1212
    }
1213
1214
    public function testIsArray(): void
1215
    {
1216
        $this->assertTrue(ArrayHelper::isTraversable(['a']));
1217
        $this->assertTrue(ArrayHelper::isTraversable(new ArrayObject(['1'])));
1218
        $this->assertFalse(ArrayHelper::isTraversable(new stdClass()));
1219
        $this->assertFalse(ArrayHelper::isTraversable('A,B,C'));
1220
        $this->assertFalse(ArrayHelper::isTraversable(12));
1221
        $this->assertFalse(ArrayHelper::isTraversable(false));
1222
        $this->assertFalse(ArrayHelper::isTraversable(null));
1223
    }
1224
1225
    public function testFilter(): void
1226
    {
1227
        $array = [
1228
            'A' => [
1229
                'B' => 1,
1230
                'C' => 2,
1231
                'D' => [
1232
                    'E' => 1,
1233
                    'F' => 2,
1234
                ],
1235
            ],
1236
            'G' => 1,
1237
        ];
1238
1239
        // Include tests
1240
        $this->assertEquals(
1241
            [
1242
                'A' => [
1243
                    'B' => 1,
1244
                    'C' => 2,
1245
                    'D' => [
1246
                        'E' => 1,
1247
                        'F' => 2,
1248
                    ],
1249
                ],
1250
            ],
1251
            ArrayHelper::filter($array, ['A'])
1252
        );
1253
1254
        $this->assertEquals(
1255
            [
1256
                'A' => [
1257
                    'B' => 1,
1258
                ],
1259
            ],
1260
            ArrayHelper::filter($array, ['A.B'])
1261
        );
1262
1263
        $this->assertEquals(
1264
            [
1265
                'A' => [
1266
                    'D' => [
1267
                        'E' => 1,
1268
                        'F' => 2,
1269
                    ],
1270
                ],
1271
            ],
1272
            ArrayHelper::filter($array, ['A.D'])
1273
        );
1274
1275
        $this->assertEquals(
1276
            [
1277
                'A' => [
1278
                    'D' => [
1279
                        'E' => 1,
1280
                    ],
1281
                ],
1282
            ],
1283
            ArrayHelper::filter($array, ['A.D.E'])
1284
        );
1285
1286
        $this->assertEquals(
1287
            [
1288
                'A' => [
1289
                    'B' => 1,
1290
                    'C' => 2,
1291
                    'D' => [
1292
                        'E' => 1,
1293
                        'F' => 2,
1294
                    ],
1295
                ],
1296
                'G' => 1,
1297
            ],
1298
            ArrayHelper::filter($array, ['A', 'G'])
1299
        );
1300
1301
        $this->assertEquals(
1302
            [
1303
                'A' => [
1304
                    'D' => [
1305
                        'E' => 1,
1306
                    ],
1307
                ],
1308
                'G' => 1,
1309
            ],
1310
            ArrayHelper::filter($array, ['A.D.E', 'G'])
1311
        );
1312
1313
        // Exclude (combined with include) tests
1314
        $this->assertEquals(
1315
            [
1316
                'A' => [
1317
                    'C' => 2,
1318
                    'D' => [
1319
                        'E' => 1,
1320
                        'F' => 2,
1321
                    ],
1322
                ],
1323
            ],
1324
            ArrayHelper::filter($array, ['A', '!A.B'])
1325
        );
1326
1327
        $this->assertEquals(
1328
            [
1329
                'A' => [
1330
                    'C' => 2,
1331
                    'D' => [
1332
                        'E' => 1,
1333
                        'F' => 2,
1334
                    ],
1335
                ],
1336
            ],
1337
            ArrayHelper::filter($array, ['!A.B', 'A'])
1338
        );
1339
1340
        $this->assertEquals(
1341
            [
1342
                'A' => [
1343
                    'B' => 1,
1344
                    'C' => 2,
1345
                    'D' => [
1346
                        'F' => 2,
1347
                    ],
1348
                ],
1349
            ],
1350
            ArrayHelper::filter($array, ['A', '!A.D.E'])
1351
        );
1352
1353
        $this->assertEquals(
1354
            [
1355
                'A' => [
1356
                    'B' => 1,
1357
                    'C' => 2,
1358
                ],
1359
            ],
1360
            ArrayHelper::filter($array, ['A', '!A.D'])
1361
        );
1362
    }
1363
1364
    public function testFilterNonExistingKeys(): void
1365
    {
1366
        $array = [
1367
            'A' => [
1368
                'B' => 1,
1369
                'C' => 2,
1370
                'D' => [
1371
                    'E' => 1,
1372
                    'F' => 2,
1373
                ],
1374
            ],
1375
            'G' => 1,
1376
        ];
1377
1378
        $this->assertEquals([], ArrayHelper::filter($array, ['X']));
1379
        $this->assertEquals([], ArrayHelper::filter($array, ['X.Y']));
1380
        $this->assertEquals([], ArrayHelper::filter($array, ['A.X']));
1381
    }
1382
1383
    /**
1384
     * Values that evaluate to `true` with `empty()` tests
1385
     */
1386
    public function testFilterEvaluatedToEmpty(): void
1387
    {
1388
        $input = [
1389
            'a' => 0,
1390
            'b' => '',
1391
            'c' => false,
1392
            'd' => null,
1393
            'e' => true,
1394
        ];
1395
1396
        $this->assertEquals(ArrayHelper::filter($input, array_keys($input)), $input);
1397
    }
1398
1399
    public function testExistingMagicObjectProperty(): void
1400
    {
1401
        $magic = new Magic(['name' => 'Wilmer']);
1402
        $this->assertEquals('Wilmer', ArrayHelper::getValue($magic, 'name'));
1403
    }
1404
1405
    public function testNonExistingMagicObjectProperty(): void
1406
    {
1407
        $magic = new Magic([]);
1408
1409
        $this->expectException(\InvalidArgumentException::class);
1410
1411
        ArrayHelper::getValue($magic, 'name');
1412
    }
1413
1414
    public function testExistingNestedMagicObjectProperty(): void
1415
    {
1416
        $storage = new stdClass();
1417
        $storage->magic = new Magic(['name' => 'Wilmer']);
1418
        $this->assertEquals('Wilmer', ArrayHelper::getValue($storage, 'magic.name'));
1419
    }
1420
1421
    public function testNonExistingNestedMagicObjectProperty(): void
1422
    {
1423
        $order = new stdClass();
1424
        $order->magic = new Magic([]);
1425
1426
        $this->expectException(\InvalidArgumentException::class);
1427
        ArrayHelper::getValue($order, 'magic.name');
1428
    }
1429
}
1430