Passed
Pull Request — master (#36)
by
unknown
21:22 queued 06:24
created

ArrayHelperTest::testMergeWithReverseValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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