Passed
Pull Request — master (#34)
by Dmitriy
19:36 queued 04:30
created

ArrayHelperTest::testMergeWithReverseBlock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

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