[BUGFIX] Fix wrong usage of $callback in ArrayUtility::filterRecursive
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Utility / ArrayUtilityTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\Utility;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\ArrayUtilityFilterRecursiveCallbackFixture;
19 use TYPO3\CMS\Core\Utility\ArrayUtility;
20 use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
21 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
22
23 /**
24 * Test case
25 */
26 class ArrayUtilityTest extends UnitTestCase
27 {
28 ///////////////////////
29 // Tests concerning filterByValueRecursive
30 ///////////////////////
31 /**
32 * Data provider for filterByValueRecursiveCorrectlyFiltersArray
33 *
34 * Every array splits into:
35 * - String value to search for
36 * - Input array
37 * - Expected result array
38 */
39 public function filterByValueRecursive()
40 {
41 return [
42 'empty search array' => [
43 'banana',
44 [],
45 []
46 ],
47 'empty string as needle' => [
48 '',
49 [
50 '',
51 'apple'
52 ],
53 [
54 ''
55 ]
56 ],
57 'flat array searching for string' => [
58 'banana',
59 [
60 'apple',
61 'banana'
62 ],
63 [
64 1 => 'banana'
65 ]
66 ],
67 'flat array searching for string with two matches' => [
68 'banana',
69 [
70 'foo' => 'apple',
71 'firstbanana' => 'banana',
72 'secondbanana' => 'banana'
73 ],
74 [
75 'firstbanana' => 'banana',
76 'secondbanana' => 'banana'
77 ]
78 ],
79 'multi dimensional array searching for string with multiple matches' => [
80 'banana',
81 [
82 'foo' => 'apple',
83 'firstbanana' => 'banana',
84 'grape' => [
85 'foo2' => 'apple2',
86 'secondbanana' => 'banana',
87 'foo3' => []
88 ],
89 'bar' => 'orange'
90 ],
91 [
92 'firstbanana' => 'banana',
93 'grape' => [
94 'secondbanana' => 'banana'
95 ]
96 ]
97 ],
98 'multi dimensional array searching for integer with multiple matches' => [
99 42,
100 [
101 'foo' => 23,
102 'bar' => 42,
103 [
104 'foo' => 23,
105 'bar' => 42
106 ]
107 ],
108 [
109 'bar' => 42,
110 [
111 'bar' => 42
112 ]
113 ]
114 ],
115 'flat array searching for boolean TRUE' => [
116 true,
117 [
118 23 => false,
119 42 => true
120 ],
121 [
122 42 => true
123 ]
124 ],
125 'multi dimensional array searching for boolean FALSE' => [
126 false,
127 [
128 23 => false,
129 42 => true,
130 'foo' => [
131 23 => false,
132 42 => true
133 ]
134 ],
135 [
136 23 => false,
137 'foo' => [
138 23 => false
139 ]
140 ]
141 ],
142 'flat array searching for array' => [
143 [
144 'foo' => 'bar'
145 ],
146 [
147 'foo' => 'bar',
148 'foobar' => [
149 'foo' => 'bar'
150 ]
151 ],
152 [
153 'foobar' => [
154 'foo' => 'bar'
155 ]
156 ]
157 ]
158 ];
159 }
160
161 /**
162 * @test
163 * @dataProvider filterByValueRecursive
164 * @param array $needle
165 * @param array $haystack
166 * @param array $expectedResult
167 */
168 public function filterByValueRecursiveCorrectlyFiltersArray($needle, $haystack, $expectedResult)
169 {
170 $this->assertEquals(
171 $expectedResult,
172 ArrayUtility::filterByValueRecursive($needle, $haystack)
173 );
174 }
175
176 /**
177 * @test
178 */
179 public function filterByValueRecursiveMatchesReferencesToSameObject()
180 {
181 $instance = new \stdClass();
182 $this->assertEquals(
183 [$instance],
184 ArrayUtility::filterByValueRecursive($instance, [$instance])
185 );
186 }
187
188 /**
189 * @test
190 */
191 public function filterByValueRecursiveDoesNotMatchDifferentInstancesOfSameClass()
192 {
193 $this->assertEquals(
194 [],
195 ArrayUtility::filterByValueRecursive(new \stdClass(), [new \stdClass()])
196 );
197 }
198
199 ///////////////////////
200 // Tests concerning isValidPath
201 ///////////////////////
202 /**
203 * @test
204 */
205 public function isValidPathReturnsTrueIfPathExists()
206 {
207 $this->assertTrue(ArrayUtility::isValidPath(['foo' => 'bar'], 'foo'));
208 }
209
210 /**
211 * @test
212 */
213 public function isValidPathReturnsFalseIfPathDoesNotExist()
214 {
215 $this->assertFalse(ArrayUtility::isValidPath(['foo' => 'bar'], 'bar'));
216 }
217
218 ///////////////////////
219 // Tests concerning getValueByPath
220 ///////////////////////
221 /**
222 * @test
223 */
224 public function getValueByPathThrowsExceptionIfPathIsNotString()
225 {
226 $this->expectException(\InvalidArgumentException::class);
227 $this->expectExceptionCode(1476557628);
228
229 ArrayUtility::getValueByPath([], 123);
230 }
231
232 /**
233 * @test
234 */
235 public function getValueByPathThrowsExceptionIfPathIsEmpty()
236 {
237 $this->expectException(\RuntimeException::class);
238 $this->expectExceptionCode(1341397767);
239
240 ArrayUtility::getValueByPath([], '');
241 }
242
243 /**
244 * @test
245 */
246 public function getValueByPathReturnsFirstIndexIfPathIsZero()
247 {
248 $this->assertSame('foo', ArrayUtility::getValueByPath(['foo'], '0'));
249 }
250
251 /**
252 * @test
253 */
254 public function getValueByPathReturnsFirstIndexIfPathSegmentIsZero()
255 {
256 $this->assertSame('bar', ArrayUtility::getValueByPath(['foo' => ['bar']], 'foo/0'));
257 }
258
259 /**
260 * Data provider for getValueByPathThrowsExceptionIfPathNotExists
261 * Every array splits into:
262 * - Array to get value from
263 * - String path
264 * - Expected result
265 * @return array
266 */
267 public function getValueByPathInvalidPathDataProvider()
268 {
269 return [
270 'not existing index' => [
271 [
272 'foo' => ['foo']
273 ],
274 'foo/1',
275 false
276 ],
277 'not existing path 1' => [
278 [
279 'foo' => []
280 ],
281 'foo/bar/baz',
282 false
283 ],
284 'not existing path 2' => [
285 [
286 'foo' => [
287 'baz' => 42
288 ],
289 'bar' => []
290 ],
291 'foo/bar/baz',
292 false
293 ],
294 // Negative test: This could be improved and the test moved to
295 // the valid data provider if the method supports this
296 'doubletick encapsulated quoted doubletick does not work' => [
297 [
298 '"foo"bar"' => [
299 'baz' => 42
300 ],
301 'bar' => []
302 ],
303 '"foo\\"bar"/baz',
304 42
305 ],
306 // Negative test: Method could be improved here
307 'path with doubletick does not work' => [
308 [
309 'fo"o' => [
310 'bar' => 42
311 ]
312 ],
313 'fo"o/foobar',
314 42
315 ]
316 ];
317 }
318
319 /**
320 * @test
321 * @dataProvider getValueByPathInvalidPathDataProvider
322 * @param array $array
323 * @param string $path
324 */
325 public function getValueByPathThrowsExceptionIfPathNotExists(array $array, $path)
326 {
327 $this->expectException(\RuntimeException::class);
328 $this->expectExceptionCode(1341397869);
329 ArrayUtility::getValueByPath($array, $path);
330 }
331
332 /**
333 * @test
334 * @dataProvider getValueByPathInvalidPathDataProvider
335 * @param array $array
336 * @param string $path
337 */
338 public function getValueByPathThrowsSpecificExceptionIfPathNotExists(array $array, string $path)
339 {
340 $this->expectException(MissingArrayPathException::class);
341 $this->expectExceptionCode(1341397869);
342 ArrayUtility::getValueByPath($array, $path);
343 }
344
345 /**
346 * Data provider for getValueByPathReturnsCorrectValue
347 * Every array splits into:
348 * - Array to get value from
349 * - String path
350 * - Expected result
351 */
352 public function getValueByPathValidDataProvider()
353 {
354 $testObject = new \stdClass();
355 $testObject->foo = 'foo';
356 $testObject->bar = 'bar';
357 return [
358 'integer in multi level array' => [
359 [
360 'foo' => [
361 'bar' => [
362 'baz' => 42
363 ],
364 'bar2' => []
365 ]
366 ],
367 'foo/bar/baz',
368 42
369 ],
370 'zero integer in multi level array' => [
371 [
372 'foo' => [
373 'bar' => [
374 'baz' => 0
375 ]
376 ]
377 ],
378 'foo/bar/baz',
379 0
380 ],
381 'NULL value in multi level array' => [
382 [
383 'foo' => [
384 'baz' => null
385 ]
386 ],
387 'foo/baz',
388 null
389 ],
390 'get string value' => [
391 [
392 'foo' => [
393 'baz' => 'this is a test string'
394 ]
395 ],
396 'foo/baz',
397 'this is a test string'
398 ],
399 'get boolean value: FALSE' => [
400 [
401 'foo' => [
402 'baz' => false
403 ]
404 ],
405 'foo/baz',
406 false
407 ],
408 'get boolean value: TRUE' => [
409 [
410 'foo' => [
411 'baz' => true
412 ]
413 ],
414 'foo/baz',
415 true
416 ],
417 'get object value' => [
418 [
419 'foo' => [
420 'baz' => $testObject
421 ]
422 ],
423 'foo/baz',
424 $testObject
425 ],
426 'enclosed path' => [
427 [
428 'foo/bar' => [
429 'foobar' => 42
430 ]
431 ],
432 '"foo/bar"/foobar',
433 42
434 ]
435 ];
436 }
437
438 /**
439 * @test
440 * @dataProvider getValueByPathValidDataProvider
441 * @param array $array
442 * @param string $path
443 * @param mixed $expectedResult
444 */
445 public function getValueByPathGetsCorrectValue(array $array, $path, $expectedResult)
446 {
447 $this->assertEquals($expectedResult, ArrayUtility::getValueByPath($array, $path));
448 }
449
450 /**
451 * @test
452 */
453 public function getValueByPathAcceptsDifferentDelimiter()
454 {
455 $input = [
456 'foo' => [
457 'bar' => [
458 'baz' => 42
459 ],
460 'bar2' => []
461 ]
462 ];
463 $searchPath = 'foo%bar%baz';
464 $expected = 42;
465 $delimiter = '%';
466 $this->assertEquals(
467 $expected,
468 ArrayUtility::getValueByPath($input, $searchPath, $delimiter)
469 );
470 }
471
472 ///////////////////////
473 // Tests concerning setValueByPath
474 ///////////////////////
475 /**
476 * @test
477 */
478 public function setValueByPathThrowsExceptionIfPathIsEmpty()
479 {
480 $this->expectException(\RuntimeException::class);
481 $this->expectExceptionCode(1341406194);
482
483 ArrayUtility::setValueByPath([], '', null);
484 }
485
486 /**
487 * @test
488 */
489 public function setValueByPathThrowsExceptionIfPathIsNotAString()
490 {
491 $this->expectException(\InvalidArgumentException::class);
492 $this->expectExceptionCode(1478781081);
493
494 ArrayUtility::setValueByPath([], 123, null);
495 }
496
497 /**
498 * @test
499 */
500 public function setValueByPathThrowsExceptionIfPathSegmentIsEmpty()
501 {
502 $this->expectException(\RuntimeException::class);
503 $this->expectExceptionCode(1341406846);
504
505 ArrayUtility::setValueByPath(['foo' => 'bar'], '/foo', 'value');
506 }
507
508 /**
509 * @test
510 */
511 public function setValueByPathCanUseZeroAsPathSegment()
512 {
513 $this->assertSame(['foo' => ['value']], ArrayUtility::setValueByPath(['foo' => []], 'foo/0', 'value'));
514 }
515
516 /**
517 * @test
518 */
519 public function setValueByPathCanUseZeroAsPath()
520 {
521 $this->assertSame(['value', 'bar'], ArrayUtility::setValueByPath(['foo', 'bar'], '0', 'value'));
522 }
523
524 /**
525 * Data provider for setValueByPathSetsCorrectValueDataProvider
526 *
527 * Every array splits into:
528 * - Array to set value in
529 * - String path
530 * - Value to set
531 * - Expected result
532 */
533 public function setValueByPathSetsCorrectValueDataProvider()
534 {
535 $testObject = new \stdClass();
536 $testObject->foo = 'foo';
537 $testObject->bar = 'bar';
538 return [
539 'set integer value: 42' => [
540 [
541 'foo' => [
542 'bar' => [
543 'baz' => 0
544 ]
545 ]
546 ],
547 'foo/bar/baz',
548 42,
549 [
550 'foo' => [
551 'bar' => [
552 'baz' => 42
553 ]
554 ]
555 ]
556 ],
557 'set integer value: 0' => [
558 [
559 'foo' => [
560 'bar' => [
561 'baz' => 42
562 ]
563 ]
564 ],
565 'foo/bar/baz',
566 0,
567 [
568 'foo' => [
569 'bar' => [
570 'baz' => 0
571 ]
572 ]
573 ]
574 ],
575 'set null value' => [
576 [
577 'foo' => [
578 'bar' => [
579 'baz' => 42
580 ]
581 ]
582 ],
583 'foo/bar/baz',
584 null,
585 [
586 'foo' => [
587 'bar' => [
588 'baz' => null
589 ]
590 ]
591 ]
592 ],
593 'set array value' => [
594 [
595 'foo' => [
596 'bar' => [
597 'baz' => 42
598 ]
599 ]
600 ],
601 'foo/bar/baz',
602 [
603 'foo' => 123
604 ],
605 [
606 'foo' => [
607 'bar' => [
608 'baz' => [
609 'foo' => 123
610 ]
611 ]
612 ]
613 ]
614 ],
615 'set boolean value: FALSE' => [
616 [
617 'foo' => [
618 'bar' => [
619 'baz' => true
620 ]
621 ]
622 ],
623 'foo/bar/baz',
624 false,
625 [
626 'foo' => [
627 'bar' => [
628 'baz' => false
629 ]
630 ]
631 ]
632 ],
633 'set boolean value: TRUE' => [
634 [
635 'foo' => [
636 'bar' => [
637 'baz' => null
638 ]
639 ]
640 ],
641 'foo/bar/baz',
642 true,
643 [
644 'foo' => [
645 'bar' => [
646 'baz' => true
647 ]
648 ]
649 ]
650 ],
651 'set object value' => [
652 [
653 'foo' => [
654 'bar' => [
655 'baz' => null
656 ]
657 ]
658 ],
659 'foo/bar/baz',
660 $testObject,
661 [
662 'foo' => [
663 'bar' => [
664 'baz' => $testObject
665 ]
666 ]
667 ]
668 ],
669 'multi keys in array' => [
670 [
671 'foo' => [
672 'bar' => [
673 'baz' => 'value'
674 ],
675 'bar2' => [
676 'baz' => 'value'
677 ]
678 ]
679 ],
680 'foo/bar2/baz',
681 'newValue',
682 [
683 'foo' => [
684 'bar' => [
685 'baz' => 'value'
686 ],
687 'bar2' => [
688 'baz' => 'newValue'
689 ]
690 ]
691 ]
692 ]
693 ];
694 }
695
696 /**
697 * @test
698 * @dataProvider setValueByPathSetsCorrectValueDataProvider
699 * @param array $array
700 * @param string $path
701 * @param string $value
702 * @param array $expectedResult
703 */
704 public function setValueByPathSetsCorrectValue(array $array, $path, $value, $expectedResult)
705 {
706 $this->assertEquals(
707 $expectedResult,
708 ArrayUtility::setValueByPath($array, $path, $value)
709 );
710 }
711
712 /**********************
713 /* Tests concerning removeByPath
714 ***********************/
715
716 /**
717 * @test
718 */
719 public function removeByPathThrowsExceptionIfPathIsEmpty()
720 {
721 $this->expectException(\RuntimeException::class);
722 $this->expectExceptionCode(1371757718);
723
724 ArrayUtility::removeByPath([], '');
725 }
726
727 /**
728 * @test
729 */
730 public function removeByPathThrowsExceptionIfPathIsNotAString()
731 {
732 $this->expectException(\RuntimeException::class);
733 $this->expectExceptionCode(1371757719);
734
735 ArrayUtility::removeByPath([], ['foo']);
736 }
737
738 /**
739 * @test
740 */
741 public function removeByPathThrowsExceptionWithEmptyPathSegment()
742 {
743 $inputArray = [
744 'foo' => [
745 'bar' => 42,
746 ]
747 ];
748
749 $this->expectException(\RuntimeException::class);
750 $this->expectExceptionCode(1371757720);
751
752 ArrayUtility::removeByPath($inputArray, 'foo//bar');
753 }
754
755 /**
756 * @test
757 */
758 public function removeByPathRemovesFirstIndexWithZeroAsPathSegment()
759 {
760 $inputArray = [
761 'foo' => ['bar']
762 ];
763
764 $this->assertSame(['foo' => []], ArrayUtility::removeByPath($inputArray, 'foo/0'));
765 }
766
767 /**
768 * @test
769 */
770 public function removeByPathRemovesFirstIndexWithZeroAsPath()
771 {
772 $inputArray = ['bar'];
773
774 $this->assertSame([], ArrayUtility::removeByPath($inputArray, '0'));
775 }
776
777 /**
778 * @test
779 */
780 public function removeByPathThrowsExceptionIfPathDoesNotExistInArray()
781 {
782 $inputArray = [
783 'foo' => [
784 'bar' => 42,
785 ]
786 ];
787
788 $this->expectException(\RuntimeException::class);
789 $this->expectExceptionCode(1371758436);
790
791 ArrayUtility::removeByPath($inputArray, 'foo/baz');
792 }
793
794 /**
795 * @test
796 */
797 public function removeByPathThrowsSpecificExceptionIfPathDoesNotExistInArray()
798 {
799 $inputArray = [
800 'foo' => [
801 'bar' => 42,
802 ]
803 ];
804
805 $this->expectException(MissingArrayPathException::class);
806 $this->expectExceptionCode(1371758436);
807
808 ArrayUtility::removeByPath($inputArray, 'foo/baz');
809 }
810
811 /**
812 * @test
813 */
814 public function removeByPathAcceptsGivenDelimiter()
815 {
816 $inputArray = [
817 'foo' => [
818 'toRemove' => 42,
819 'keep' => 23
820 ],
821 ];
822 $path = 'foo.toRemove';
823 $expected = [
824 'foo' => [
825 'keep' => 23,
826 ],
827 ];
828 $this->assertEquals(
829 $expected,
830 ArrayUtility::removeByPath($inputArray, $path, '.')
831 );
832 }
833
834 /**
835 * Data provider for removeByPathRemovesCorrectPath
836 */
837 public function removeByPathRemovesCorrectPathDataProvider()
838 {
839 return [
840 'single value' => [
841 [
842 'foo' => [
843 'toRemove' => 42,
844 'keep' => 23
845 ],
846 ],
847 'foo/toRemove',
848 [
849 'foo' => [
850 'keep' => 23,
851 ],
852 ],
853 ],
854 'whole array' => [
855 [
856 'foo' => [
857 'bar' => 42
858 ],
859 ],
860 'foo',
861 [],
862 ],
863 'sub array' => [
864 [
865 'foo' => [
866 'keep' => 23,
867 'toRemove' => [
868 'foo' => 'bar',
869 ],
870 ],
871 ],
872 'foo/toRemove',
873 [
874 'foo' => [
875 'keep' => 23,
876 ],
877 ],
878 ],
879 ];
880 }
881
882 /**
883 * @test
884 * @dataProvider removeByPathRemovesCorrectPathDataProvider
885 * @param array $array
886 * @param string $path
887 * @param array $expectedResult
888 */
889 public function removeByPathRemovesCorrectPath(array $array, $path, $expectedResult)
890 {
891 $this->assertEquals(
892 $expectedResult,
893 ArrayUtility::removeByPath($array, $path)
894 );
895 }
896
897 ///////////////////////
898 // Tests concerning sortByKeyRecursive
899 ///////////////////////
900 /**
901 * @test
902 */
903 public function sortByKeyRecursiveCheckIfSortingIsCorrect()
904 {
905 $unsortedArray = [
906 'z' => null,
907 'a' => null,
908 'd' => [
909 'c' => null,
910 'b' => null,
911 'd' => null,
912 'a' => null
913 ]
914 ];
915 $expectedResult = [
916 'a' => null,
917 'd' => [
918 'a' => null,
919 'b' => null,
920 'c' => null,
921 'd' => null
922 ],
923 'z' => null
924 ];
925 $this->assertSame($expectedResult, ArrayUtility::sortByKeyRecursive($unsortedArray));
926 }
927
928 ///////////////////////
929 // Tests concerning sortArraysByKey
930 ///////////////////////
931 /**
932 * Data provider for sortArraysByKeyCheckIfSortingIsCorrect
933 */
934 public function sortArraysByKeyCheckIfSortingIsCorrectDataProvider()
935 {
936 return [
937 'assoc array index' => [
938 [
939 '22' => [
940 'uid' => '22',
941 'title' => 'c',
942 'dummy' => 2
943 ],
944 '24' => [
945 'uid' => '24',
946 'title' => 'a',
947 'dummy' => 3
948 ],
949 '23' => [
950 'uid' => '23',
951 'title' => 'b',
952 'dummy' => 4
953 ],
954 ],
955 'title',
956 true,
957 [
958 '24' => [
959 'uid' => '24',
960 'title' => 'a',
961 'dummy' => 3
962 ],
963 '23' => [
964 'uid' => '23',
965 'title' => 'b',
966 'dummy' => 4
967 ],
968 '22' => [
969 'uid' => '22',
970 'title' => 'c',
971 'dummy' => 2
972 ],
973 ],
974 ],
975 'numeric array index' => [
976 [
977 22 => [
978 'uid' => '22',
979 'title' => 'c',
980 'dummy' => 2
981 ],
982 24 => [
983 'uid' => '24',
984 'title' => 'a',
985 'dummy' => 3
986 ],
987 23 => [
988 'uid' => '23',
989 'title' => 'b',
990 'dummy' => 4
991 ],
992 ],
993 'title',
994 true,
995 [
996 24 => [
997 'uid' => '24',
998 'title' => 'a',
999 'dummy' => 3
1000 ],
1001 23 => [
1002 'uid' => '23',
1003 'title' => 'b',
1004 'dummy' => 4
1005 ],
1006 22 => [
1007 'uid' => '22',
1008 'title' => 'c',
1009 'dummy' => 2
1010 ],
1011 ],
1012 ],
1013 'numeric array index DESC' => [
1014 [
1015 23 => [
1016 'uid' => '23',
1017 'title' => 'b',
1018 'dummy' => 4
1019 ],
1020 22 => [
1021 'uid' => '22',
1022 'title' => 'c',
1023 'dummy' => 2
1024 ],
1025 24 => [
1026 'uid' => '24',
1027 'title' => 'a',
1028 'dummy' => 3
1029 ],
1030 ],
1031 'title',
1032 false,
1033 [
1034 22 => [
1035 'uid' => '22',
1036 'title' => 'c',
1037 'dummy' => 2
1038 ],
1039 23 => [
1040 'uid' => '23',
1041 'title' => 'b',
1042 'dummy' => 4
1043 ],
1044 24 => [
1045 'uid' => '24',
1046 'title' => 'a',
1047 'dummy' => 3
1048 ],
1049 ],
1050 ],
1051 ];
1052 }
1053
1054 /**
1055 * @test
1056 * @dataProvider sortArraysByKeyCheckIfSortingIsCorrectDataProvider
1057 * @param array $array
1058 * @param string $key
1059 * @param bool $ascending
1060 * @param array $expectedResult
1061 */
1062 public function sortArraysByKeyCheckIfSortingIsCorrect(array $array, $key, $ascending, $expectedResult)
1063 {
1064 $sortedArray = ArrayUtility::sortArraysByKey($array, $key, $ascending);
1065 $this->assertSame($expectedResult, $sortedArray);
1066 }
1067
1068 /**
1069 * @test
1070 */
1071 public function sortArraysByKeyThrowsExceptionForNonExistingKey()
1072 {
1073 $this->expectException(\RuntimeException::class);
1074 $this->expectExceptionCode(1373727309);
1075
1076 ArrayUtility::sortArraysByKey([['a'], ['a']], 'dummy');
1077 }
1078
1079 ///////////////////////
1080 // Tests concerning arrayExport
1081 ///////////////////////
1082 /**
1083 * @test
1084 */
1085 public function arrayExportReturnsFormattedMultidimensionalArray()
1086 {
1087 $array = [
1088 'foo' => [
1089 'bar' => 42,
1090 'bar2' => [
1091 'baz' => 'val\'ue',
1092 'baz2' => true,
1093 'baz3' => false,
1094 'baz4' => []
1095 ]
1096 ],
1097 'baz' => 23,
1098 'foobar' => null,
1099 'qux' => 0.1,
1100 'qux2' => 0.000000001,
1101 ];
1102 $expected =
1103 '[' . LF .
1104 ' \'foo\' => [' . LF .
1105 ' \'bar\' => 42,' . LF .
1106 ' \'bar2\' => [' . LF .
1107 ' \'baz\' => \'val\\\'ue\',' . LF .
1108 ' \'baz2\' => true,' . LF .
1109 ' \'baz3\' => false,' . LF .
1110 ' \'baz4\' => [],' . LF .
1111 ' ],' . LF .
1112 ' ],' . LF .
1113 ' \'baz\' => 23,' . LF .
1114 ' \'foobar\' => null,' . LF .
1115 ' \'qux\' => 0.1,' . LF .
1116 ' \'qux2\' => 1.0E-9,' . LF .
1117 ']';
1118 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1119 }
1120
1121 /**
1122 * @test
1123 */
1124 public function arrayExportThrowsExceptionIfObjectShouldBeExported()
1125 {
1126 $array = [
1127 'foo' => [
1128 'bar' => new \stdClass()
1129 ]
1130 ];
1131
1132 $this->expectException(\RuntimeException::class);
1133 $this->expectExceptionCode(1342294987);
1134
1135 ArrayUtility::arrayExport($array);
1136 }
1137
1138 /**
1139 * @test
1140 */
1141 public function arrayExportReturnsNumericArrayKeys()
1142 {
1143 $array = [
1144 'foo' => 'string key',
1145 23 => 'integer key',
1146 '42' => 'string key representing integer'
1147 ];
1148 $expected =
1149 '[' . LF .
1150 ' \'foo\' => \'string key\',' . LF .
1151 ' 23 => \'integer key\',' . LF .
1152 ' 42 => \'string key representing integer\',' . LF .
1153 ']';
1154 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1155 }
1156
1157 /**
1158 * @test
1159 */
1160 public function arrayExportReturnsNoKeyIndexForConsecutiveCountedArrays()
1161 {
1162 $array = [
1163 0 => 'zero',
1164 1 => 'one',
1165 2 => 'two'
1166 ];
1167 $expected =
1168 '[' . LF .
1169 ' \'zero\',' . LF .
1170 ' \'one\',' . LF .
1171 ' \'two\',' . LF .
1172 ']';
1173 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1174 }
1175
1176 /**
1177 * @test
1178 */
1179 public function arrayExportReturnsKeyIndexForNonConsecutiveCountedArrays()
1180 {
1181 $array = [
1182 0 => 'zero',
1183 1 => 'one',
1184 3 => 'three',
1185 4 => 'four'
1186 ];
1187 $expected =
1188 '[' . LF .
1189 ' 0 => \'zero\',' . LF .
1190 ' 1 => \'one\',' . LF .
1191 ' 3 => \'three\',' . LF .
1192 ' 4 => \'four\',' . LF .
1193 ']';
1194 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1195 }
1196
1197 ///////////////////////
1198 // Tests concerning flatten
1199 ///////////////////////
1200
1201 /**
1202 * @return array
1203 */
1204 public function flattenCalculatesExpectedResultDataProvider()
1205 {
1206 return [
1207 'plain array' => [
1208 [
1209 'first' => 1,
1210 'second' => 2
1211 ],
1212 [
1213 'first' => 1,
1214 'second' => 2
1215 ]
1216 ],
1217 'plain array with faulty dots' => [
1218 [
1219 'first.' => 1,
1220 'second.' => 2
1221 ],
1222 [
1223 'first' => 1,
1224 'second' => 2
1225 ]
1226 ],
1227 'nested array of 2 levels' => [
1228 [
1229 'first.' => [
1230 'firstSub' => 1
1231 ],
1232 'second.' => [
1233 'secondSub' => 2
1234 ]
1235 ],
1236 [
1237 'first.firstSub' => 1,
1238 'second.secondSub' => 2
1239 ]
1240 ],
1241 'nested array of 2 levels with faulty dots' => [
1242 [
1243 'first.' => [
1244 'firstSub.' => 1
1245 ],
1246 'second.' => [
1247 'secondSub.' => 2
1248 ]
1249 ],
1250 [
1251 'first.firstSub' => 1,
1252 'second.secondSub' => 2
1253 ]
1254 ],
1255 'nested array of 3 levels' => [
1256 [
1257 'first.' => [
1258 'firstSub.' => [
1259 'firstSubSub' => 1
1260 ]
1261 ],
1262 'second.' => [
1263 'secondSub.' => [
1264 'secondSubSub' => 2
1265 ]
1266 ]
1267 ],
1268 [
1269 'first.firstSub.firstSubSub' => 1,
1270 'second.secondSub.secondSubSub' => 2
1271 ]
1272 ],
1273 'nested array of 3 levels with faulty dots' => [
1274 [
1275 'first.' => [
1276 'firstSub.' => [
1277 'firstSubSub.' => 1
1278 ]
1279 ],
1280 'second.' => [
1281 'secondSub.' => [
1282 'secondSubSub.' => 2
1283 ]
1284 ]
1285 ],
1286 [
1287 'first.firstSub.firstSubSub' => 1,
1288 'second.secondSub.secondSubSub' => 2
1289 ]
1290 ]
1291 ];
1292 }
1293
1294 /**
1295 * @test
1296 * @param array $array
1297 * @param array $expected
1298 * @dataProvider flattenCalculatesExpectedResultDataProvider
1299 */
1300 public function flattenCalculatesExpectedResult(array $array, array $expected)
1301 {
1302 $this->assertEquals($expected, ArrayUtility::flatten($array));
1303 }
1304
1305 ///////////////////////
1306 // Tests concerning intersectRecursive
1307 ///////////////////////
1308
1309 /**
1310 * @return array
1311 */
1312 public function intersectRecursiveCalculatesExpectedResultDataProvider()
1313 {
1314 $sameObject = new \stdClass();
1315 return [
1316 // array($source, $mask, $expected)
1317 'empty array is returned if source is empty array' => [
1318 [],
1319 [
1320 'foo' => 'bar',
1321 ],
1322 [],
1323 ],
1324 'empty array is returned if mask is empty' => [
1325 [
1326 'foo' => 'bar',
1327 ],
1328 [],
1329 [],
1330 ],
1331 'key is kept on first level if exists in mask' => [
1332 [
1333 'foo' => 42,
1334 ],
1335 [
1336 'foo' => 42,
1337 ],
1338 [
1339 'foo' => 42,
1340 ],
1341 ],
1342 'value of key in source is kept if mask has different value' => [
1343 [
1344 'foo' => 42,
1345 ],
1346 [
1347 'foo' => new \stdClass(),
1348 ],
1349 [
1350 'foo' => 42,
1351 ],
1352 ],
1353 'key is kept on first level if according mask value is NULL' => [
1354 [
1355 'foo' => 42,
1356 ],
1357 [
1358 'foo' => null,
1359 ],
1360 [
1361 'foo' => 42,
1362 ],
1363 ],
1364 'null in source value is kept' => [
1365 [
1366 'foo' => null,
1367 ],
1368 [
1369 'foo' => 'bar',
1370 ],
1371 [
1372 'foo' => null,
1373 ]
1374 ],
1375 'mask does not add new keys' => [
1376 [
1377 'foo' => 42,
1378 ],
1379 [
1380 'foo' => 23,
1381 'bar' => [
1382 4711
1383 ],
1384 ],
1385 [
1386 'foo' => 42,
1387 ],
1388 ],
1389 'mask does not overwrite simple values with arrays' => [
1390 [
1391 'foo' => 42,
1392 ],
1393 [
1394 'foo' => [
1395 'bar' => 23,
1396 ],
1397 ],
1398 [
1399 'foo' => 42,
1400 ],
1401 ],
1402 'key is kept on first level if according mask value is array' => [
1403 [
1404 'foo' => 42,
1405 ],
1406 [
1407 'foo' => [
1408 'bar' => 23
1409 ],
1410 ],
1411 [
1412 'foo' => 42,
1413 ],
1414 ],
1415 'full array is kept if value is array and mask value is simple type' => [
1416 [
1417 'foo' => [
1418 'bar' => 23
1419 ],
1420 ],
1421 [
1422 'foo' => 42,
1423 ],
1424 [
1425 'foo' => [
1426 'bar' => 23
1427 ],
1428 ],
1429 ],
1430 'key handling is type agnostic' => [
1431 [
1432 42 => 'foo',
1433 ],
1434 [
1435 '42' => 'bar',
1436 ],
1437 [
1438 42 => 'foo',
1439 ],
1440 ],
1441 'value is same if value is object' => [
1442 [
1443 'foo' => $sameObject,
1444 ],
1445 [
1446 'foo' => 'something',
1447 ],
1448 [
1449 'foo' => $sameObject,
1450 ],
1451 ],
1452 'mask does not add simple value to result if key does not exist in source' => [
1453 [
1454 'foo' => '42',
1455 ],
1456 [
1457 'foo' => '42',
1458 'bar' => 23
1459 ],
1460 [
1461 'foo' => '42',
1462 ],
1463 ],
1464 'array of source is kept if value of mask key exists but is no array' => [
1465 [
1466 'foo' => '42',
1467 'bar' => [
1468 'baz' => 23
1469 ],
1470 ],
1471 [
1472 'foo' => 'value is not significant',
1473 'bar' => null,
1474 ],
1475 [
1476 'foo' => '42',
1477 'bar' => [
1478 'baz' => 23
1479 ],
1480 ],
1481 ],
1482 'sub arrays are kept if mask has according sub array key and is similar array' => [
1483 [
1484 'first1' => 42,
1485 'first2' => [
1486 'second1' => 23,
1487 'second2' => 4711,
1488 ],
1489 ],
1490 [
1491 'first1' => 42,
1492 'first2' => [
1493 'second1' => 'exists but different',
1494 ],
1495 ],
1496 [
1497 'first1' => 42,
1498 'first2' => [
1499 'second1' => 23,
1500 ],
1501 ],
1502 ],
1503 ];
1504 }
1505
1506 /**
1507 * @test
1508 * @param array $source
1509 * @param array $mask
1510 * @param array $expected
1511 * @dataProvider intersectRecursiveCalculatesExpectedResultDataProvider
1512 */
1513 public function intersectRecursiveCalculatesExpectedResult(array $source, array $mask, array $expected)
1514 {
1515 $this->assertSame($expected, ArrayUtility::intersectRecursive($source, $mask));
1516 }
1517
1518 ///////////////////////
1519 // Tests concerning renumberKeysToAvoidLeapsIfKeysAreAllNumeric
1520 ///////////////////////
1521 /**
1522 * @return array
1523 */
1524 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider()
1525 {
1526 return [
1527 'empty array is returned if source is empty array' => [
1528 [],
1529 []
1530 ],
1531 'returns self if array is already numerically keyed' => [
1532 [1, 2, 3],
1533 [1, 2, 3]
1534 ],
1535 'returns correctly if keys are numeric, but contains a leap' => [
1536 [0 => 'One', 1 => 'Two', 3 => 'Three'],
1537 [0 => 'One', 1 => 'Two', 2 => 'Three'],
1538 ],
1539 'returns correctly even though keys are strings but still numeric' => [
1540 ['0' => 'One', '1' => 'Two', '3' => 'Three'],
1541 [0 => 'One', 1 => 'Two', 2 => 'Three'],
1542 ],
1543 'returns correctly if just a single keys is not numeric' => [
1544 [0 => 'Zero', '1' => 'One', 'Two' => 'Two'],
1545 [0 => 'Zero', '1' => 'One', 'Two' => 'Two'],
1546 ],
1547 'returns unchanged if keys end with a dot' => [
1548 ['2.' => 'Two', '1.' => 'One', '0.' => 'Zero'],
1549 ['2.' => 'Two', '1.' => 'One', '0.' => 'Zero'],
1550 ],
1551 'return self with nested numerically keyed array' => [
1552 [
1553 'One',
1554 'Two',
1555 'Three',
1556 [
1557 'sub.One',
1558 'sub.Two',
1559 ]
1560 ],
1561 [
1562 'One',
1563 'Two',
1564 'Three',
1565 [
1566 'sub.One',
1567 'sub.Two',
1568 ]
1569 ]
1570 ],
1571 'returns correctly with nested numerically keyed array with leaps' => [
1572 [
1573 'One',
1574 'Two',
1575 'Three',
1576 [
1577 0 => 'sub.One',
1578 2 => 'sub.Two',
1579 ]
1580 ],
1581 [
1582 'One',
1583 'Two',
1584 'Three',
1585 [
1586 'sub.One',
1587 'sub.Two',
1588 ]
1589 ]
1590 ],
1591 'returns correctly with nested string-keyed array' => [
1592 [
1593 'One',
1594 'Two',
1595 'Three',
1596 [
1597 'one' => 'sub.One',
1598 'two' => 'sub.Two',
1599 ]
1600 ],
1601 [
1602 'One',
1603 'Two',
1604 'Three',
1605 [
1606 'one' => 'sub.One',
1607 'two' => 'sub.Two',
1608 ]
1609 ]
1610 ],
1611 'returns correctly with deeply nested arrays' => [
1612 [
1613 'One',
1614 'Two',
1615 [
1616 'one' => 1,
1617 'two' => 2,
1618 'three' => [
1619 2 => 'SubSubOne',
1620 5 => 'SubSubTwo',
1621 9 => [0, 1, 2],
1622 []
1623 ]
1624 ]
1625 ],
1626 [
1627 'One',
1628 'Two',
1629 [
1630 'one' => 1,
1631 'two' => 2,
1632 'three' => [
1633 'SubSubOne',
1634 'SubSubTwo',
1635 [0, 1, 2],
1636 []
1637 ]
1638 ]
1639 ]
1640 ]
1641 ];
1642 }
1643
1644 /**
1645 * @test
1646 * @param array $inputArray
1647 * @param array $expected
1648 * @dataProvider renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider
1649 */
1650 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericReturnsExpectedOrder(array $inputArray, array $expected)
1651 {
1652 $this->assertEquals($expected, ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($inputArray));
1653 }
1654
1655 /**
1656 * @return array
1657 */
1658 public function mergeRecursiveWithOverruleCalculatesExpectedResultDataProvider()
1659 {
1660 return [
1661 'Override array can reset string to array' => [
1662 [
1663 'first' => [
1664 'second' => 'foo',
1665 ],
1666 ],
1667 [
1668 'first' => [
1669 'second' => ['third' => 'bar'],
1670 ],
1671 ],
1672 true,
1673 true,
1674 true,
1675 [
1676 'first' => [
1677 'second' => ['third' => 'bar'],
1678 ],
1679 ],
1680 ],
1681 'Override array does not reset array to string (weird!)' => [
1682 [
1683 'first' => [],
1684 ],
1685 [
1686 'first' => 'foo',
1687 ],
1688 true,
1689 true,
1690 true,
1691 [
1692 'first' => [], // This is rather unexpected, naive expectation: first => 'foo'
1693 ],
1694 ],
1695 'Override array does override string with null' => [
1696 [
1697 'first' => 'foo',
1698 ],
1699 [
1700 'first' => null,
1701 ],
1702 true,
1703 true,
1704 true,
1705 [
1706 'first' => null,
1707 ],
1708 ],
1709 'Override array does override null with string' => [
1710 [
1711 'first' => null,
1712 ],
1713 [
1714 'first' => 'foo',
1715 ],
1716 true,
1717 true,
1718 true,
1719 [
1720 'first' => 'foo',
1721 ],
1722 ],
1723 'Override array does override null with empty string' => [
1724 [
1725 'first' => null,
1726 ],
1727 [
1728 'first' => '',
1729 ],
1730 true,
1731 true,
1732 true,
1733 [
1734 'first' => '',
1735 ],
1736 ],
1737 'Override array does not override string with NULL if requested' => [
1738 [
1739 'first' => 'foo',
1740 ],
1741 [
1742 'first' => null,
1743 ],
1744 true,
1745 false, // no include empty values
1746 true,
1747 [
1748 'first' => 'foo',
1749 ],
1750 ],
1751 'Override array does override null with null' => [
1752 [
1753 'first' => null,
1754 ],
1755 [
1756 'first' => null,
1757 ],
1758 true,
1759 true,
1760 true,
1761 [
1762 'first' => '',
1763 ],
1764 ],
1765 'Override array can __UNSET values' => [
1766 [
1767 'first' => [
1768 'second' => 'second',
1769 'third' => 'third',
1770 ],
1771 'fifth' => [],
1772 ],
1773 [
1774 'first' => [
1775 'second' => 'overrule',
1776 'third' => '__UNSET',
1777 'fourth' => 'overrile',
1778 ],
1779 'fifth' => '__UNSET',
1780 ],
1781 true,
1782 true,
1783 true,
1784 [
1785 'first' => [
1786 'second' => 'overrule',
1787 'fourth' => 'overrile',
1788 ],
1789 ],
1790 ],
1791 'Override can add keys' => [
1792 [
1793 'first' => 'foo',
1794 ],
1795 [
1796 'second' => 'bar',
1797 ],
1798 true,
1799 true,
1800 true,
1801 [
1802 'first' => 'foo',
1803 'second' => 'bar',
1804 ],
1805 ],
1806 'Override does not add key if __UNSET' => [
1807 [
1808 'first' => 'foo',
1809 ],
1810 [
1811 'second' => '__UNSET',
1812 ],
1813 true,
1814 true,
1815 true,
1816 [
1817 'first' => 'foo',
1818 ],
1819 ],
1820 'Override does not add key if not requested' => [
1821 [
1822 'first' => 'foo',
1823 ],
1824 [
1825 'second' => 'bar',
1826 ],
1827 false, // no add keys
1828 true,
1829 true,
1830 [
1831 'first' => 'foo',
1832 ],
1833 ],
1834 'Override does not add key if not requested with add include empty values' => [
1835 [
1836 'first' => 'foo',
1837 ],
1838 [
1839 'second' => 'bar',
1840 ],
1841 false, // no add keys
1842 false, // no include empty values
1843 true,
1844 [
1845 'first' => 'foo',
1846 ],
1847 ],
1848 'Override does not override string with empty string if requested' => [
1849 [
1850 'first' => 'foo',
1851 ],
1852 [
1853 'first' => '',
1854 ],
1855 true,
1856 false, // no include empty values
1857 true,
1858 [
1859 'first' => 'foo',
1860 ],
1861 ],
1862 'Override array does merge instead of __UNSET if requested (weird!)' => [
1863 [
1864 'first' => [
1865 'second' => 'second',
1866 'third' => 'third',
1867 ],
1868 'fifth' => [],
1869 ],
1870 [
1871 'first' => [
1872 'second' => 'overrule',
1873 'third' => '__UNSET',
1874 'fourth' => 'overrile',
1875 ],
1876 'fifth' => '__UNSET',
1877 ],
1878 true,
1879 true,
1880 false,
1881 [
1882 'first' => [
1883 'second' => 'overrule',
1884 'third' => '__UNSET', // overruled
1885 'fourth' => 'overrile',
1886 ],
1887 'fifth' => [], // not overruled with string here, naive expectation: 'fifth' => '__UNSET'
1888 ],
1889 ],
1890 ];
1891 }
1892
1893 /**
1894 * @test
1895 * @dataProvider mergeRecursiveWithOverruleCalculatesExpectedResultDataProvider
1896 * @param array $input1 Input 1
1897 * @param array $input2 Input 2
1898 * @param bool $addKeys TRUE if should add keys, else FALSE
1899 * @param bool $includeEmptyValues TRUE if should include empty values, else FALSE
1900 * @param bool $enableUnsetFeature TRUE if should enable unset feature, else FALSE
1901 * @param array $expected expected array
1902 */
1903 public function mergeRecursiveWithOverruleCalculatesExpectedResult($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature, $expected)
1904 {
1905 ArrayUtility::mergeRecursiveWithOverrule($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature);
1906 $this->assertEquals($expected, $input1);
1907 }
1908
1909 //////////////////////////////////
1910 // Tests concerning removeArrayEntryByValue
1911 //////////////////////////////////
1912 /**
1913 * @test
1914 */
1915 public function checkRemoveArrayEntryByValueRemovesEntriesFromOneDimensionalArray()
1916 {
1917 $inputArray = [
1918 '0' => 'test1',
1919 '1' => 'test2',
1920 '2' => 'test3',
1921 '3' => 'test2'
1922 ];
1923 $compareValue = 'test2';
1924 $expectedResult = [
1925 '0' => 'test1',
1926 '2' => 'test3'
1927 ];
1928 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1929 $this->assertEquals($expectedResult, $actualResult);
1930 }
1931
1932 /**
1933 * @test
1934 */
1935 public function checkRemoveArrayEntryByValueRemovesEntriesFromMultiDimensionalArray()
1936 {
1937 $inputArray = [
1938 '0' => 'foo',
1939 '1' => [
1940 '10' => 'bar'
1941 ],
1942 '2' => 'bar'
1943 ];
1944 $compareValue = 'bar';
1945 $expectedResult = [
1946 '0' => 'foo',
1947 '1' => []
1948 ];
1949 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1950 $this->assertEquals($expectedResult, $actualResult);
1951 }
1952
1953 /**
1954 * @test
1955 */
1956 public function checkRemoveArrayEntryByValueRemovesEntryWithEmptyString()
1957 {
1958 $inputArray = [
1959 '0' => 'foo',
1960 '1' => '',
1961 '2' => 'bar'
1962 ];
1963 $compareValue = '';
1964 $expectedResult = [
1965 '0' => 'foo',
1966 '2' => 'bar'
1967 ];
1968 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1969 $this->assertEquals($expectedResult, $actualResult);
1970 }
1971
1972 //////////////////////////////////
1973 // Tests concerning keepItemsInArray
1974 //////////////////////////////////
1975 /**
1976 * @test
1977 * @dataProvider keepItemsInArrayWorksWithOneArgumentDataProvider
1978 * @param mixed $search The items which are allowed/kept in the array
1979 * @param array $array target array
1980 * @param array $expected expected array
1981 */
1982 public function keepItemsInArrayWorksWithOneArgument($search, $array, $expected)
1983 {
1984 $this->assertEquals($expected, ArrayUtility::keepItemsInArray($array, $search));
1985 }
1986
1987 /**
1988 * Data provider for keepItemsInArrayWorksWithOneArgument
1989 *
1990 * @return array
1991 */
1992 public function keepItemsInArrayWorksWithOneArgumentDataProvider()
1993 {
1994 $array = [
1995 0 => 0,
1996 'one' => 'one',
1997 'two' => 'two',
1998 'three' => 'three'
1999 ];
2000 return [
2001 'Empty argument will match "all" elements' => [null, $array, $array],
2002 'No match' => ['four', $array, []],
2003 'One match' => ['two', $array, ['two' => 'two']],
2004 'Multiple matches' => ['two,one', $array, ['one' => 'one', 'two' => 'two']],
2005 'Argument can be an array' => [['three'], $array, ['three' => 'three']]
2006 ];
2007 }
2008
2009 /**
2010 * Shows the example from the doc comment where
2011 * a function is used to reduce the sub arrays to one item which
2012 * is then used for the matching.
2013 *
2014 * @test
2015 */
2016 public function keepItemsInArrayCanUseClosure()
2017 {
2018 $array = [
2019 'aa' => ['first', 'second'],
2020 'bb' => ['third', 'fourth'],
2021 'cc' => ['fifth', 'sixth']
2022 ];
2023 $expected = ['bb' => ['third', 'fourth']];
2024 $keepItems = 'third';
2025 $match = ArrayUtility::keepItemsInArray(
2026 $array,
2027 $keepItems,
2028 function ($value) {
2029 return $value[0];
2030 }
2031 );
2032 $this->assertEquals($expected, $match);
2033 }
2034
2035 //////////////////////////////////
2036 // Tests concerning remapArrayKeys
2037 //////////////////////////////////
2038 /**
2039 * @test
2040 */
2041 public function remapArrayKeysExchangesKeysWithGivenMapping()
2042 {
2043 $array = [
2044 'one' => 'one',
2045 'two' => 'two',
2046 'three' => 'three'
2047 ];
2048 $keyMapping = [
2049 'one' => '1',
2050 'two' => '2'
2051 ];
2052 $expected = [
2053 '1' => 'one',
2054 '2' => 'two',
2055 'three' => 'three'
2056 ];
2057 ArrayUtility::remapArrayKeys($array, $keyMapping);
2058 $this->assertEquals($expected, $array);
2059 }
2060
2061 //////////////////////////////////////
2062 // Tests concerning arrayDiffAssocRecursive
2063 //////////////////////////////////////
2064 /**
2065 * @test
2066 */
2067 public function arrayDiffAssocRecursiveHandlesOneDimensionalArrays()
2068 {
2069 $array1 = [
2070 'key1' => 'value1',
2071 'key2' => 'value2',
2072 'key3' => 'value3'
2073 ];
2074 $array2 = [
2075 'key1' => 'value1',
2076 'key3' => 'value3'
2077 ];
2078 $expectedResult = [
2079 'key2' => 'value2'
2080 ];
2081 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2082 $this->assertEquals($expectedResult, $actualResult);
2083 }
2084
2085 /**
2086 * @test
2087 */
2088 public function arrayDiffAssocRecursiveHandlesMultiDimensionalArrays()
2089 {
2090 $array1 = [
2091 'key1' => 'value1',
2092 'key2' => [
2093 'key21' => 'value21',
2094 'key22' => 'value22',
2095 'key23' => [
2096 'key231' => 'value231',
2097 'key232' => 'value232'
2098 ]
2099 ]
2100 ];
2101 $array2 = [
2102 'key1' => 'valueDoesNotMatter',
2103 'key2' => [
2104 'key21' => 'value21',
2105 'key23' => [
2106 'key231' => 'value231'
2107 ]
2108 ]
2109 ];
2110 $expectedResult = [
2111 'key2' => [
2112 'key22' => 'value22',
2113 'key23' => [
2114 'key232' => 'value232'
2115 ]
2116 ]
2117 ];
2118 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2119 $this->assertEquals($expectedResult, $actualResult);
2120 }
2121
2122 /**
2123 * @test
2124 */
2125 public function arrayDiffAssocRecursiveHandlesMixedArrays()
2126 {
2127 $array1 = [
2128 'key1' => [
2129 'key11' => 'value11',
2130 'key12' => 'value12'
2131 ],
2132 'key2' => 'value2',
2133 'key3' => 'value3'
2134 ];
2135 $array2 = [
2136 'key1' => 'value1',
2137 'key2' => [
2138 'key21' => 'valueDoesNotMatter'
2139 ]
2140 ];
2141 $expectedResult = [
2142 'key3' => 'value3'
2143 ];
2144 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2145 $this->assertEquals($expectedResult, $actualResult);
2146 }
2147
2148 /**
2149 * @test
2150 */
2151 public function arrayDiffAssocRecursiveReturnsEmptyIfEqual()
2152 {
2153 $array1 = [
2154 'key1' => [
2155 'key11' => 'value11',
2156 'key12' => 'value12'
2157 ],
2158 'key2' => 'value2',
2159 'key3' => 'value3'
2160 ];
2161 $array2 = [
2162 'key1' => [
2163 'key11' => 'valueDoesNotMatter',
2164 'key12' => 'value12'
2165 ],
2166 'key2' => 'value2',
2167 'key3' => 'value3'
2168 ];
2169 $expectedResult = [];
2170 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2171 $this->assertEquals($expectedResult, $actualResult);
2172 }
2173
2174 //////////////////////////////////////
2175 // Tests concerning naturalKeySortRecursive
2176 //////////////////////////////////////
2177
2178 /**
2179 * @test
2180 */
2181 public function naturalKeySortRecursiveSortsOneDimensionalArrayByNaturalOrder()
2182 {
2183 $testArray = [
2184 'bb' => 'bb',
2185 'ab' => 'ab',
2186 '123' => '123',
2187 'aaa' => 'aaa',
2188 'abc' => 'abc',
2189 '23' => '23',
2190 'ba' => 'ba',
2191 'bad' => 'bad',
2192 '2' => '2',
2193 'zap' => 'zap',
2194 '210' => '210'
2195 ];
2196 $expectedResult = [
2197 '2',
2198 '23',
2199 '123',
2200 '210',
2201 'aaa',
2202 'ab',
2203 'abc',
2204 'ba',
2205 'bad',
2206 'bb',
2207 'zap'
2208 ];
2209 ArrayUtility::naturalKeySortRecursive($testArray);
2210 $this->assertEquals($expectedResult, array_values($testArray));
2211 }
2212
2213 /**
2214 * @test
2215 */
2216 public function naturalKeySortRecursiveSortsMultiDimensionalArrayByNaturalOrder()
2217 {
2218 $testArray = [
2219 '2' => '2',
2220 'bb' => 'bb',
2221 'ab' => 'ab',
2222 '23' => '23',
2223 'aaa' => [
2224 'bb' => 'bb',
2225 'ab' => 'ab',
2226 '123' => '123',
2227 'aaa' => 'aaa',
2228 '2' => '2',
2229 'abc' => 'abc',
2230 'ba' => 'ba',
2231 '23' => '23',
2232 'bad' => [
2233 'bb' => 'bb',
2234 'ab' => 'ab',
2235 '123' => '123',
2236 'aaa' => 'aaa',
2237 'abc' => 'abc',
2238 '23' => '23',
2239 'ba' => 'ba',
2240 'bad' => 'bad',
2241 '2' => '2',
2242 'zap' => 'zap',
2243 '210' => '210'
2244 ],
2245 '210' => '210',
2246 'zap' => 'zap'
2247 ],
2248 'abc' => 'abc',
2249 'ba' => 'ba',
2250 '210' => '210',
2251 'bad' => 'bad',
2252 '123' => '123',
2253 'zap' => 'zap'
2254 ];
2255 $expectedResult = [
2256 '2',
2257 '23',
2258 '123',
2259 '210',
2260 'aaa',
2261 'ab',
2262 'abc',
2263 'ba',
2264 'bad',
2265 'bb',
2266 'zap'
2267 ];
2268 ArrayUtility::naturalKeySortRecursive($testArray);
2269 $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa']['bad'])));
2270 $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa'])));
2271 $this->assertEquals($expectedResult, array_values(array_keys($testArray)));
2272 }
2273
2274 /**
2275 * Data provider for filterAndSortByNumericKeysBehavesCorrectlyForAcceptAnyKeysIsTrue
2276 *
2277 * @return array
2278 */
2279 public function filterAndSortByNumericKeysWithAcceptAnyKey()
2280 {
2281 return [
2282 'ordered list of plain numeric keys' => [
2283 'input' => [
2284 '10' => 'foo',
2285 '20' => 'bar',
2286 ],
2287 'expected' => [
2288 10,
2289 20,
2290 ],
2291 ],
2292 'unordered list of plain numeric keys' => [
2293 'input' => [
2294 '20' => 'bar',
2295 '10' => 'foo',
2296 ],
2297 'expected' => [
2298 10,
2299 20,
2300 ],
2301 ],
2302 'list of string keys' => [
2303 'input' => [
2304 '10.' => [
2305 'wrap' => 'foo',
2306 ],
2307 '20.' => [
2308 'wrap' => 'bar',
2309 ],
2310 ],
2311 'expected' => [
2312 10,
2313 20,
2314 ],
2315 ],
2316 'list of mixed keys' => [
2317 'input' => [
2318 '10' => 'foo',
2319 '20.' => [
2320 'wrap' => 'bar',
2321 ],
2322 ],
2323 'expected' => [
2324 10,
2325 20,
2326 ],
2327 ],
2328 'list of mixed keys with one not interpreted as integer' => [
2329 'input' => [
2330 '10' => 'foo',
2331 'bla20.' => [
2332 'wrap' => 'bar',
2333 ],
2334 ],
2335 'expected' => [
2336 0,
2337 10,
2338 ],
2339 ],
2340 'list of mixed keys with more than one not interpreted as integer' => [
2341 'input' => [
2342 '10' => 'foo',
2343 'bla20.' => [
2344 'wrap' => 'bar',
2345 ],
2346 'bla21.' => [
2347 'wrap' => 'foobar',
2348 ],
2349 ],
2350 'expected' => [
2351 0,
2352 10,
2353 ],
2354 ],
2355 ];
2356 }
2357
2358 /**
2359 * @test
2360 * @dataProvider filterAndSortByNumericKeysWithAcceptAnyKey
2361 *
2362 * @param array $input
2363 * @param array $expected
2364 */
2365 public function filterAndSortByNumericKeysBehavesCorrectlyForAcceptAnyKeysIsTrue($input, $expected)
2366 {
2367 $result = ArrayUtility::filterAndSortByNumericKeys($input, true);
2368 $this->assertEquals($result, $expected);
2369 }
2370
2371 /**
2372 * Data provider for filterAndSortByNumericKeysBehavesCorrectlyForAcceptAnyKeysIsFalse
2373 *
2374 * @return array
2375 */
2376 public function filterAndSortByNumericKeysWithoutAcceptAnyKey()
2377 {
2378 return [
2379 'ordered list of plain numeric keys' => [
2380 'input' => [
2381 '10' => 'foo',
2382 '20' => 'bar',
2383 ],
2384 'expected' => [
2385 10,
2386 20,
2387 ],
2388 ],
2389 'unordered list of plain numeric keys' => [
2390 'input' => [
2391 '20' => 'bar',
2392 '10' => 'foo',
2393 ],
2394 'expected' => [
2395 10,
2396 20,
2397 ],
2398 ],
2399 'list of string keys' => [
2400 'input' => [
2401 '10.' => [
2402 'wrap' => 'foo',
2403 ],
2404 '20.' => [
2405 'wrap' => 'bar',
2406 ],
2407 ],
2408 'expected' => [],
2409 ],
2410 'list of mixed keys' => [
2411 'input' => [
2412 '10' => 'foo',
2413 '20.' => [
2414 'wrap' => 'bar',
2415 ],
2416 ],
2417 'expected' => [
2418 10,
2419 ],
2420 ],
2421 ];
2422 }
2423
2424 /**
2425 * @test
2426 * @dataProvider filterAndSortByNumericKeysWithoutAcceptAnyKey
2427 *
2428 * @param array $input
2429 * @param array $expected
2430 */
2431 public function filterAndSortByNumericKeysBehavesCorrectlyForAcceptAnyKeysIsFalse($input, $expected)
2432 {
2433 $result = ArrayUtility::filterAndSortByNumericKeys($input);
2434 $this->assertEquals($result, $expected);
2435 }
2436
2437 /**
2438 * dataProvider for sortArrayWithIntegerKeys
2439 *
2440 * @return array
2441 */
2442 public function sortArrayWithIntegerKeysDataProvider()
2443 {
2444 return [
2445 [
2446 [
2447 '20' => 'test1',
2448 '11' => 'test2',
2449 '16' => 'test3',
2450 ],
2451 [
2452 '11' => 'test2',
2453 '16' => 'test3',
2454 '20' => 'test1',
2455 ]
2456 ],
2457 [
2458 [
2459 '20' => 'test1',
2460 '16.5' => 'test2',
2461 '16' => 'test3',
2462 ],
2463 [
2464 '20' => 'test1',
2465 '16.5' => 'test2',
2466 '16' => 'test3',
2467 ]
2468 ],
2469 [
2470 [
2471 '20' => 'test20',
2472 'somestring' => 'teststring',
2473 '16' => 'test16',
2474 ],
2475 [
2476 '20' => 'test20',
2477 'somestring' => 'teststring',
2478 '16' => 'test16',
2479 ]
2480 ],
2481 ];
2482 }
2483
2484 /**
2485 * @test
2486 *
2487 * @param array $arrayToSort
2488 * @param array $expectedArray
2489 *
2490 * @dataProvider sortArrayWithIntegerKeysDataProvider
2491 */
2492 public function sortArrayWithIntegerKeysSortsNumericArrays(array $arrayToSort, array $expectedArray)
2493 {
2494 $sortedArray = ArrayUtility::sortArrayWithIntegerKeys($arrayToSort);
2495 $this->assertSame($sortedArray, $expectedArray);
2496 }
2497
2498 /**
2499 * @test
2500 */
2501 public function assertAllArrayKeysAreValidThrowsExceptionOnNotAllowedArrayKeys()
2502 {
2503 $this->expectException(\InvalidArgumentException::class);
2504 $this->expectExceptionCode(1325697085);
2505
2506 $arrayToTest = [
2507 'roger' => '',
2508 'francine' => '',
2509 'stan' => '',
2510 ];
2511
2512 $allowedArrayKeys = [
2513 'roger',
2514 'francine',
2515 ];
2516
2517 ArrayUtility::assertAllArrayKeysAreValid($arrayToTest, $allowedArrayKeys);
2518 }
2519
2520 /**
2521 * @test
2522 */
2523 public function assertAllArrayKeysAreValidReturnsNullOnAllowedArrayKeys()
2524 {
2525 $arrayToTest = [
2526 'roger' => '',
2527 'francine' => '',
2528 'stan' => '',
2529 ];
2530
2531 $allowedArrayKeys = [
2532 'roger',
2533 'francine',
2534 'stan',
2535 ];
2536
2537 ArrayUtility::assertAllArrayKeysAreValid($arrayToTest, $allowedArrayKeys);
2538 }
2539
2540 /**
2541 * @test
2542 */
2543 public function sortArrayWithIntegerKeysRecursiveExpectSorting()
2544 {
2545 $input = [
2546 20 => 'b',
2547 10 => 'a',
2548 40 => 'd',
2549 30 => 'c',
2550 50 => [
2551 20 => 'a',
2552 10 => 'b',
2553 ],
2554 ];
2555
2556 $expected = [
2557 10 => 'a',
2558 20 => 'b',
2559 30 => 'c',
2560 40 => 'd',
2561 50 => [
2562 10 => 'b',
2563 20 => 'a',
2564 ],
2565 ];
2566
2567 $this->assertSame($expected, ArrayUtility::sortArrayWithIntegerKeysRecursive($input));
2568 }
2569
2570 /**
2571 * @test
2572 */
2573 public function sortArrayWithIntegerKeysRecursiveExpectNoSorting()
2574 {
2575 $input = [
2576 'b' => 'b',
2577 10 => 'a',
2578 40 => 'd',
2579 30 => 'c',
2580 ];
2581
2582 $expected = [
2583 'b' => 'b',
2584 10 => 'a',
2585 40 => 'd',
2586 30 => 'c',
2587 ];
2588
2589 $this->assertSame($expected, ArrayUtility::sortArrayWithIntegerKeysRecursive($input));
2590 }
2591
2592 /**
2593 * @test
2594 */
2595 public function reIndexNumericArrayKeysRecursiveExpectReindexing()
2596 {
2597 $input = [
2598 20 => 'b',
2599 10 => 'a',
2600 40 => 'd',
2601 30 => 'c',
2602 50 => [
2603 20 => 'a',
2604 10 => 'b',
2605 ],
2606 ];
2607
2608 $expected = [
2609 0 => 'b',
2610 1 => 'a',
2611 2 => 'd',
2612 3 => 'c',
2613 4 => [
2614 0 => 'a',
2615 1 => 'b',
2616 ],
2617 ];
2618
2619 $this->assertSame($expected, ArrayUtility::reIndexNumericArrayKeysRecursive($input));
2620 }
2621
2622 /**
2623 * @test
2624 */
2625 public function reIndexNumericArrayKeysRecursiveExpectNoReindexing()
2626 {
2627 $input = [
2628 'a' => 'b',
2629 10 => 'a',
2630 40 => 'd',
2631 30 => 'c',
2632 50 => [
2633 20 => 'a',
2634 10 => 'b',
2635 ],
2636 ];
2637
2638 $expected = [
2639 'a' => 'b',
2640 10 => 'a',
2641 40 => 'd',
2642 30 => 'c',
2643 50 => [
2644 0 => 'a',
2645 1 => 'b',
2646 ],
2647 ];
2648
2649 $this->assertSame($expected, ArrayUtility::reIndexNumericArrayKeysRecursive($input));
2650 }
2651
2652 /**
2653 * @test
2654 */
2655 public function removeNullValuesRecursiveExpectRemoval()
2656 {
2657 $input = [
2658 'a' => 'a',
2659 'b' => [
2660 'c' => null,
2661 'd' => 'd',
2662 ],
2663 ];
2664
2665 $expected = [
2666 'a' => 'a',
2667 'b' => [
2668 'd' => 'd',
2669 ],
2670 ];
2671
2672 $this->assertSame($expected, ArrayUtility::removeNullValuesRecursive($input));
2673 }
2674
2675 /**
2676 * @test
2677 */
2678 public function stripTagsFromValuesRecursiveExpectRemoval()
2679 {
2680 $input = [
2681 'a' => 'a',
2682 'b' => [
2683 'c' => '<b>i am evil</b>',
2684 'd' => 'd',
2685 ],
2686 ];
2687
2688 $expected = [
2689 'a' => 'a',
2690 'b' => [
2691 'c' => 'i am evil',
2692 'd' => 'd',
2693 ],
2694 ];
2695
2696 $this->assertSame($expected, ArrayUtility::stripTagsFromValuesRecursive($input));
2697 }
2698
2699 /**
2700 * @test
2701 */
2702 public function convertBooleanStringsToBooleanRecursiveExpectConverting()
2703 {
2704 $input = [
2705 'a' => 'a',
2706 'b' => [
2707 'c' => 'true',
2708 'd' => 'd',
2709 ],
2710 ];
2711
2712 $expected = [
2713 'a' => 'a',
2714 'b' => [
2715 'c' => true,
2716 'd' => 'd',
2717 ],
2718 ];
2719
2720 $this->assertSame($expected, ArrayUtility::convertBooleanStringsToBooleanRecursive($input));
2721 }
2722
2723 /**
2724 * Data provider for arrayFilterRecursiveFiltersFalseElements
2725 * @return array
2726 */
2727 public function filterRecursiveFiltersFalseElementsDataProvider()
2728 {
2729 return [
2730 'filter all values which will be false when converted to boolean' => [
2731 // input
2732 [
2733 true,
2734 false,
2735 'foo1' => [
2736 'bar' => [
2737 'baz' => [
2738 '1',
2739 null,
2740 '',
2741 ],
2742 '' => 1,
2743 'bbd' => 0,
2744 ]
2745 ],
2746 'foo2' => 'foo',
2747 'foo3' => '',
2748 'foo4' => [
2749 'z' => 'bar',
2750 'bar' => 0,
2751 'baz' => [
2752 'foo' => [
2753 'bar' => '',
2754 'boo' => [],
2755 'bamboo' => 5,
2756 'fooAndBoo' => [0],
2757 ]
2758 ],
2759 ],
2760 ],
2761 // expected
2762 [
2763 true,
2764 'foo1' => [
2765 'bar' => [
2766 'baz' => [
2767 '1',
2768 ],
2769 '' => 1,
2770 ]
2771 ],
2772 'foo2' => 'foo',
2773 'foo4' => [
2774 'z' => 'bar',
2775 'baz' => [
2776 'foo' => [
2777 'bamboo' => 5,
2778 'fooAndBoo' => [],
2779 ]
2780 ],
2781 ],
2782 ],
2783 ],
2784 ];
2785 }
2786
2787 /**
2788 * @test
2789 * @dataProvider filterRecursiveFiltersFalseElementsDataProvider
2790 * @param array $input
2791 * @param array $expectedResult
2792 */
2793 public function filterRecursiveFiltersFalseElements(array $input, array $expectedResult)
2794 {
2795 // If no callback is supplied, all entries of array equal to FALSE (see converting to boolean) will be removed.
2796 $result = ArrayUtility::filterRecursive($input);
2797 $this->assertEquals($expectedResult, $result);
2798 }
2799
2800 /**
2801 * Data provider for filterRecursiveCallbackFiltersEmptyElementsWithoutIntegerByCallback
2802 * @return array
2803 */
2804 public function filterRecursiveCallbackFiltersEmptyElementsWithoutIntegerZeroByCallbackDataProvider()
2805 {
2806 return [
2807 'filter empty values, keep zero integers' => [
2808 // input
2809 [
2810 true,
2811 false,
2812 'foo1' => [
2813 'bar' => [
2814 'baz' => [
2815 '1',
2816 null,
2817 '',
2818 ],
2819 '' => 1,
2820 'bbd' => 0,
2821 ]
2822 ],
2823 'foo2' => 'foo',
2824 'foo3' => '',
2825 'foo4' => [
2826 'z' => 'bar',
2827 'bar' => 0,
2828 'baz' => [
2829 'foo' => [
2830 'bar' => '',
2831 'boo' => [],
2832 'bamboo' => 5,
2833 'fooAndBoo' => [0],
2834 ]
2835 ],
2836 ],
2837 ],
2838 // expected
2839 [
2840 true,
2841 false,
2842 'foo1' => [
2843 'bar' => [
2844 'baz' => [
2845 '1',
2846 ],
2847 '' => 1,
2848 'bbd' => 0,
2849 ]
2850 ],
2851 'foo2' => 'foo',
2852 'foo4' => [
2853 'z' => 'bar',
2854 'bar' => 0,
2855 'baz' => [
2856 'foo' => [
2857 'bamboo' => 5,
2858 'fooAndBoo' => [0],
2859 ]
2860 ],
2861 ],
2862 ],
2863 ],
2864 ];
2865 }
2866
2867 /**
2868 * @test
2869 * @dataProvider filterRecursiveCallbackFiltersEmptyElementsWithoutIntegerZeroByCallbackDataProvider
2870 * @param array $input
2871 * @param array $expectedResult
2872 */
2873 public function filterRecursiveCallbackFiltersEmptyElementsWithoutIntegerByCallback(array $input, array $expectedResult)
2874 {
2875 // callback filters empty strings, array and null but keeps zero integers
2876 $result = ArrayUtility::filterRecursive(
2877 $input,
2878 function ($item) {
2879 return $item !== '' && $item !== [] && $item !== null;
2880 }
2881 );
2882 $this->assertEquals($expectedResult, $result);
2883 }
2884
2885 /**
2886 * Data provider for filterRecursiveSupportsCallableCallback
2887 * @return array
2888 */
2889 public function filterRecursiveSupportsCallableCallbackDataProvider()
2890 {
2891 $input = [
2892 'foo' => 'remove',
2893 'bar' => [
2894 'baz' => 'remove',
2895 'keep1' => 'keep'
2896 ],
2897 'keep2' => 'keep'
2898 ];
2899 $expectedResult = [
2900 'bar' => [
2901 'keep1' => 'keep'
2902 ],
2903 'keep2' => 'keep'
2904 ];
2905
2906 return [
2907 'filter using a closure' => [
2908 $input,
2909 $expectedResult,
2910 function ($value): bool {
2911 return is_array($value) || $value === 'keep';
2912 },
2913 ],
2914 'filter using a callable "static class-method call" as string' => [
2915 $input,
2916 $expectedResult,
2917 ArrayUtilityFilterRecursiveCallbackFixture::class . '::callbackViaStaticMethod',
2918 ],
2919 'filter using a callable "static class-method call" as array' => [
2920 $input,
2921 $expectedResult,
2922 [ArrayUtilityFilterRecursiveCallbackFixture::class, 'callbackViaStaticMethod'],
2923 ],
2924 'filter using a callable "instance-method call" as array' => [
2925 $input,
2926 $expectedResult,
2927 [new ArrayUtilityFilterRecursiveCallbackFixture(), 'callbackViaInstanceMethod'],
2928 ],
2929 ];
2930 }
2931
2932 /**
2933 * @test
2934 * @dataProvider filterRecursiveSupportsCallableCallbackDataProvider
2935 * @param array $input
2936 * @param array $expectedResult
2937 * @param callable $callback
2938 * @see https://forge.typo3.org/issues/84485
2939 */
2940 public function filterRecursiveSupportsCallableCallback(array $input, array $expectedResult, callable $callback)
2941 {
2942 $result = ArrayUtility::filterRecursive($input, $callback);
2943 $this->assertEquals($expectedResult, $result);
2944 }
2945 }