[TASK] Removes extra empty lines
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Utility / ArrayUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Utility;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Tests\UnitTestCase;
18 use TYPO3\CMS\Core\Utility\ArrayUtility;
19
20 /**
21 * Test case
22 */
23 class ArrayUtilityTest extends UnitTestCase
24 {
25 ///////////////////////
26 // Tests concerning filterByValueRecursive
27 ///////////////////////
28 /**
29 * Data provider for filterByValueRecursiveCorrectlyFiltersArray
30 *
31 * Every array splits into:
32 * - String value to search for
33 * - Input array
34 * - Expected result array
35 */
36 public function filterByValueRecursive()
37 {
38 return array(
39 'empty search array' => array(
40 'banana',
41 array(),
42 array()
43 ),
44 'empty string as needle' => array(
45 '',
46 array(
47 '',
48 'apple'
49 ),
50 array(
51 ''
52 )
53 ),
54 'flat array searching for string' => array(
55 'banana',
56 array(
57 'apple',
58 'banana'
59 ),
60 array(
61 1 => 'banana'
62 )
63 ),
64 'flat array searching for string with two matches' => array(
65 'banana',
66 array(
67 'foo' => 'apple',
68 'firstbanana' => 'banana',
69 'secondbanana' => 'banana'
70 ),
71 array(
72 'firstbanana' => 'banana',
73 'secondbanana' => 'banana'
74 )
75 ),
76 'multi dimensional array searching for string with multiple matches' => array(
77 'banana',
78 array(
79 'foo' => 'apple',
80 'firstbanana' => 'banana',
81 'grape' => array(
82 'foo2' => 'apple2',
83 'secondbanana' => 'banana',
84 'foo3' => array()
85 ),
86 'bar' => 'orange'
87 ),
88 array(
89 'firstbanana' => 'banana',
90 'grape' => array(
91 'secondbanana' => 'banana'
92 )
93 )
94 ),
95 'multi dimensional array searching for integer with multiple matches' => array(
96 42,
97 array(
98 'foo' => 23,
99 'bar' => 42,
100 array(
101 'foo' => 23,
102 'bar' => 42
103 )
104 ),
105 array(
106 'bar' => 42,
107 array(
108 'bar' => 42
109 )
110 )
111 ),
112 'flat array searching for boolean TRUE' => array(
113 true,
114 array(
115 23 => false,
116 42 => true
117 ),
118 array(
119 42 => true
120 )
121 ),
122 'multi dimensional array searching for boolean FALSE' => array(
123 false,
124 array(
125 23 => false,
126 42 => true,
127 'foo' => array(
128 23 => false,
129 42 => true
130 )
131 ),
132 array(
133 23 => false,
134 'foo' => array(
135 23 => false
136 )
137 )
138 ),
139 'flat array searching for array' => array(
140 array(
141 'foo' => 'bar'
142 ),
143 array(
144 'foo' => 'bar',
145 'foobar' => array(
146 'foo' => 'bar'
147 )
148 ),
149 array(
150 'foobar' => array(
151 'foo' => 'bar'
152 )
153 )
154 )
155 );
156 }
157
158 /**
159 * @test
160 * @dataProvider filterByValueRecursive
161 * @param array $needle
162 * @param array $haystack
163 * @param array $expectedResult
164 */
165 public function filterByValueRecursiveCorrectlyFiltersArray($needle, $haystack, $expectedResult)
166 {
167 $this->assertEquals(
168 $expectedResult,
169 ArrayUtility::filterByValueRecursive($needle, $haystack)
170 );
171 }
172
173 /**
174 * @test
175 */
176 public function filterByValueRecursiveMatchesReferencesToSameObject()
177 {
178 $instance = new \stdClass();
179 $this->assertEquals(
180 array($instance),
181 ArrayUtility::filterByValueRecursive($instance, array($instance))
182 );
183 }
184
185 /**
186 * @test
187 */
188 public function filterByValueRecursiveDoesNotMatchDifferentInstancesOfSameClass()
189 {
190 $this->assertEquals(
191 array(),
192 ArrayUtility::filterByValueRecursive(new \stdClass(), array(new \stdClass()))
193 );
194 }
195
196 ///////////////////////
197 // Tests concerning isValidPath
198 ///////////////////////
199 /**
200 * Mock the class under test, isValidPath() (method under test), calls
201 * static getValuePath() internally, which is mocked here to return a specific
202 * result. This works because of 'static' keyword' instead of 'self'
203 * for getValueByPath() call, using late static binding in PHP 5.3
204 *
205 * @test
206 */
207 public function isValidPathReturnsTrueIfPathExists()
208 {
209 $this->assertTrue(ArrayUtility::isValidPath(array('foo' => 'bar'), 'foo'));
210 }
211
212 /**
213 * @test
214 */
215 public function isValidPathReturnsFalseIfPathDoesNotExist()
216 {
217 $this->assertFalse(ArrayUtility::isValidPath(array('foo' => 'bar'), 'bar'));
218 }
219
220 ///////////////////////
221 // Tests concerning getValueByPath
222 ///////////////////////
223 /**
224 * @test
225 * @expectedException \RuntimeException
226 */
227 public function getValueByPathThrowsExceptionIfPathIsEmpty()
228 {
229 ArrayUtility::getValueByPath(array(), '');
230 }
231
232 /**
233 * Data provider for getValueByPathThrowsExceptionIfPathNotExists
234 * Every array splits into:
235 * - Array to get value from
236 * - String path
237 * - Expected result
238 * @return array
239 */
240 public function getValueByPathInvalidPathDataProvider()
241 {
242 return array(
243 'not existing path 1' => array(
244 array(
245 'foo' => array()
246 ),
247 'foo/bar/baz',
248 false
249 ),
250 'not existing path 2' => array(
251 array(
252 'foo' => array(
253 'baz' => 42
254 ),
255 'bar' => array()
256 ),
257 'foo/bar/baz',
258 false
259 ),
260 // Negative test: This could be improved and the test moved to
261 // the valid data provider if the method supports this
262 'doubletick encapsulated quoted doubletick does not work' => array(
263 array(
264 '"foo"bar"' => array(
265 'baz' => 42
266 ),
267 'bar' => array()
268 ),
269 '"foo\\"bar"/baz',
270 42
271 ),
272 // Negative test: Method could be improved here
273 'path with doubletick does not work' => array(
274 array(
275 'fo"o' => array(
276 'bar' => 42
277 )
278 ),
279 'fo"o/foobar',
280 42
281 )
282 );
283 }
284
285 /**
286 * @test
287 * @dataProvider getValueByPathInvalidPathDataProvider
288 * @expectedException \RuntimeException
289 * @param array $array
290 * @param string $path
291 */
292 public function getValueByPathThrowsExceptionIfPathNotExists(array $array, $path)
293 {
294 ArrayUtility::getValueByPath($array, $path);
295 }
296
297 /**
298 * Data provider for getValueByPathReturnsCorrectValue
299 * Every array splits into:
300 * - Array to get value from
301 * - String path
302 * - Expected result
303 */
304 public function getValueByPathValidDataProvider()
305 {
306 $testObject = new \StdClass();
307 $testObject->foo = 'foo';
308 $testObject->bar = 'bar';
309 return array(
310 'integer in multi level array' => array(
311 array(
312 'foo' => array(
313 'bar' => array(
314 'baz' => 42
315 ),
316 'bar2' => array()
317 )
318 ),
319 'foo/bar/baz',
320 42
321 ),
322 'zero integer in multi level array' => array(
323 array(
324 'foo' => array(
325 'bar' => array(
326 'baz' => 0
327 )
328 )
329 ),
330 'foo/bar/baz',
331 0
332 ),
333 'NULL value in multi level array' => array(
334 array(
335 'foo' => array(
336 'baz' => null
337 )
338 ),
339 'foo/baz',
340 null
341 ),
342 'get string value' => array(
343 array(
344 'foo' => array(
345 'baz' => 'this is a test string'
346 )
347 ),
348 'foo/baz',
349 'this is a test string'
350 ),
351 'get boolean value: FALSE' => array(
352 array(
353 'foo' => array(
354 'baz' => false
355 )
356 ),
357 'foo/baz',
358 false
359 ),
360 'get boolean value: TRUE' => array(
361 array(
362 'foo' => array(
363 'baz' => true
364 )
365 ),
366 'foo/baz',
367 true
368 ),
369 'get object value' => array(
370 array(
371 'foo' => array(
372 'baz' => $testObject
373 )
374 ),
375 'foo/baz',
376 $testObject
377 ),
378 'enclosed path' => array(
379 array(
380 'foo/bar' => array(
381 'foobar' => 42
382 )
383 ),
384 '"foo/bar"/foobar',
385 42
386 )
387 );
388 }
389
390 /**
391 * @test
392 * @dataProvider getValueByPathValidDataProvider
393 * @param array $array
394 * @param string $path
395 * @param mixed $expectedResult
396 */
397 public function getValueByPathGetsCorrectValue(array $array, $path, $expectedResult)
398 {
399 $this->assertEquals($expectedResult, ArrayUtility::getValueByPath($array, $path));
400 }
401
402 /**
403 * @test
404 */
405 public function getValueByPathAcceptsDifferentDelimiter()
406 {
407 $input = array(
408 'foo' => array(
409 'bar' => array(
410 'baz' => 42
411 ),
412 'bar2' => array()
413 )
414 );
415 $searchPath = 'foo%bar%baz';
416 $expected = 42;
417 $delimiter = '%';
418 $this->assertEquals(
419 $expected,
420 ArrayUtility::getValueByPath($input, $searchPath, $delimiter)
421 );
422 }
423
424 ///////////////////////
425 // Tests concerning setValueByPath
426 ///////////////////////
427 /**
428 * @test
429 * @expectedException \RuntimeException
430 */
431 public function setValueByPathThrowsExceptionIfPathIsEmpty()
432 {
433 ArrayUtility::setValueByPath(array(), '', null);
434 }
435
436 /**
437 * @test
438 * @expectedException \RuntimeException
439 */
440 public function setValueByPathThrowsExceptionIfPathIsNotAString()
441 {
442 ArrayUtility::setValueByPath(array(), array('foo'), null);
443 }
444
445 /**
446 * Data provider for setValueByPathSetsCorrectValueDataProvider
447 *
448 * Every array splits into:
449 * - Array to set value in
450 * - String path
451 * - Value to set
452 * - Expected result
453 */
454 public function setValueByPathSetsCorrectValueDataProvider()
455 {
456 $testObject = new \StdClass();
457 $testObject->foo = 'foo';
458 $testObject->bar = 'bar';
459 return array(
460 'set integer value: 42' => array(
461 array(
462 'foo' => array(
463 'bar' => array(
464 'baz' => 0
465 )
466 )
467 ),
468 'foo/bar/baz',
469 42,
470 array(
471 'foo' => array(
472 'bar' => array(
473 'baz' => 42
474 )
475 )
476 )
477 ),
478 'set integer value: 0' => array(
479 array(
480 'foo' => array(
481 'bar' => array(
482 'baz' => 42
483 )
484 )
485 ),
486 'foo/bar/baz',
487 0,
488 array(
489 'foo' => array(
490 'bar' => array(
491 'baz' => 0
492 )
493 )
494 )
495 ),
496 'set null value' => array(
497 array(
498 'foo' => array(
499 'bar' => array(
500 'baz' => 42
501 )
502 )
503 ),
504 'foo/bar/baz',
505 null,
506 array(
507 'foo' => array(
508 'bar' => array(
509 'baz' => null
510 )
511 )
512 )
513 ),
514 'set array value' => array(
515 array(
516 'foo' => array(
517 'bar' => array(
518 'baz' => 42
519 )
520 )
521 ),
522 'foo/bar/baz',
523 array(
524 'foo' => 123
525 ),
526 array(
527 'foo' => array(
528 'bar' => array(
529 'baz' => array(
530 'foo' => 123
531 )
532 )
533 )
534 )
535 ),
536 'set boolean value: FALSE' => array(
537 array(
538 'foo' => array(
539 'bar' => array(
540 'baz' => true
541 )
542 )
543 ),
544 'foo/bar/baz',
545 false,
546 array(
547 'foo' => array(
548 'bar' => array(
549 'baz' => false
550 )
551 )
552 )
553 ),
554 'set boolean value: TRUE' => array(
555 array(
556 'foo' => array(
557 'bar' => array(
558 'baz' => null
559 )
560 )
561 ),
562 'foo/bar/baz',
563 true,
564 array(
565 'foo' => array(
566 'bar' => array(
567 'baz' => true
568 )
569 )
570 )
571 ),
572 'set object value' => array(
573 array(
574 'foo' => array(
575 'bar' => array(
576 'baz' => null
577 )
578 )
579 ),
580 'foo/bar/baz',
581 $testObject,
582 array(
583 'foo' => array(
584 'bar' => array(
585 'baz' => $testObject
586 )
587 )
588 )
589 ),
590 'multi keys in array' => array(
591 array(
592 'foo' => array(
593 'bar' => array(
594 'baz' => 'value'
595 ),
596 'bar2' => array(
597 'baz' => 'value'
598 )
599 )
600 ),
601 'foo/bar2/baz',
602 'newValue',
603 array(
604 'foo' => array(
605 'bar' => array(
606 'baz' => 'value'
607 ),
608 'bar2' => array(
609 'baz' => 'newValue'
610 )
611 )
612 )
613 )
614 );
615 }
616
617 /**
618 * @test
619 * @dataProvider setValueByPathSetsCorrectValueDataProvider
620 * @param array $array
621 * @param string $path
622 * @param string $value
623 * @param array $expectedResult
624 */
625 public function setValueByPathSetsCorrectValue(array $array, $path, $value, $expectedResult)
626 {
627 $this->assertEquals(
628 $expectedResult,
629 ArrayUtility::setValueByPath($array, $path, $value)
630 );
631 }
632
633 /**********************
634 /* Tests concerning removeByPath
635 ***********************/
636
637 /**
638 * @test
639 * @expectedException \RuntimeException
640 */
641 public function removeByPathThrowsExceptionIfPathIsEmpty()
642 {
643 ArrayUtility::removeByPath(array(), '');
644 }
645
646 /**
647 * @test
648 * @expectedException \RuntimeException
649 */
650 public function removeByPathThrowsExceptionIfPathIsNotAString()
651 {
652 ArrayUtility::removeByPath(array(), array('foo'));
653 }
654
655 /**
656 * @test
657 * @expectedException \RuntimeException
658 */
659 public function removeByPathThrowsExceptionWithEmptyPathSegment()
660 {
661 $inputArray = array(
662 'foo' => array(
663 'bar' => 42,
664 ),
665 );
666 ArrayUtility::removeByPath($inputArray, 'foo//bar');
667 }
668
669 /**
670 * @test
671 * @expectedException \RuntimeException
672 */
673 public function removeByPathThrowsExceptionIfPathDoesNotExistInArray()
674 {
675 $inputArray = array(
676 'foo' => array(
677 'bar' => 42,
678 ),
679 );
680 ArrayUtility::removeByPath($inputArray, 'foo/baz');
681 }
682
683 /**
684 * @test
685 */
686 public function removeByPathAcceptsGivenDelimiter()
687 {
688 $inputArray = array(
689 'foo' => array(
690 'toRemove' => 42,
691 'keep' => 23
692 ),
693 );
694 $path = 'foo.toRemove';
695 $expected = array(
696 'foo' => array(
697 'keep' => 23,
698 ),
699 );
700 $this->assertEquals(
701 $expected,
702 ArrayUtility::removeByPath($inputArray, $path, '.')
703 );
704 }
705
706 /**
707 * Data provider for removeByPathRemovesCorrectPath
708 */
709 public function removeByPathRemovesCorrectPathDataProvider()
710 {
711 return array(
712 'single value' => array(
713 array(
714 'foo' => array(
715 'toRemove' => 42,
716 'keep' => 23
717 ),
718 ),
719 'foo/toRemove',
720 array(
721 'foo' => array(
722 'keep' => 23,
723 ),
724 ),
725 ),
726 'whole array' => array(
727 array(
728 'foo' => array(
729 'bar' => 42
730 ),
731 ),
732 'foo',
733 array(),
734 ),
735 'sub array' => array(
736 array(
737 'foo' => array(
738 'keep' => 23,
739 'toRemove' => array(
740 'foo' => 'bar',
741 ),
742 ),
743 ),
744 'foo/toRemove',
745 array(
746 'foo' => array(
747 'keep' => 23,
748 ),
749 ),
750 ),
751 );
752 }
753
754 /**
755 * @test
756 * @dataProvider removeByPathRemovesCorrectPathDataProvider
757 * @param array $array
758 * @param string $path
759 * @param array $expectedResult
760 */
761 public function removeByPathRemovesCorrectPath(array $array, $path, $expectedResult)
762 {
763 $this->assertEquals(
764 $expectedResult,
765 ArrayUtility::removeByPath($array, $path)
766 );
767 }
768
769 ///////////////////////
770 // Tests concerning sortByKeyRecursive
771 ///////////////////////
772 /**
773 * @test
774 */
775 public function sortByKeyRecursiveCheckIfSortingIsCorrect()
776 {
777 $unsortedArray = array(
778 'z' => null,
779 'a' => null,
780 'd' => array(
781 'c' => null,
782 'b' => null,
783 'd' => null,
784 'a' => null
785 )
786 );
787 $expectedResult = array(
788 'a' => null,
789 'd' => array(
790 'a' => null,
791 'b' => null,
792 'c' => null,
793 'd' => null
794 ),
795 'z' => null
796 );
797 $this->assertSame($expectedResult, ArrayUtility::sortByKeyRecursive($unsortedArray));
798 }
799
800 ///////////////////////
801 // Tests concerning sortArraysByKey
802 ///////////////////////
803 /**
804 * Data provider for sortArraysByKeyCheckIfSortingIsCorrect
805 */
806 public function sortArraysByKeyCheckIfSortingIsCorrectDataProvider()
807 {
808 return array(
809 'assoc array index' => array(
810 array(
811 '22' => array(
812 'uid' => '22',
813 'title' => 'c',
814 'dummy' => 2
815 ),
816 '24' => array(
817 'uid' => '24',
818 'title' => 'a',
819 'dummy' => 3
820 ),
821 '23' => array(
822 'uid' => '23',
823 'title' => 'b',
824 'dummy' => 4
825 ),
826 ),
827 'title',
828 true,
829 array(
830 '24' => array(
831 'uid' => '24',
832 'title' => 'a',
833 'dummy' => 3
834 ),
835 '23' => array(
836 'uid' => '23',
837 'title' => 'b',
838 'dummy' => 4
839 ),
840 '22' => array(
841 'uid' => '22',
842 'title' => 'c',
843 'dummy' => 2
844 ),
845 ),
846 ),
847 'numeric array index' => array(
848 array(
849 22 => array(
850 'uid' => '22',
851 'title' => 'c',
852 'dummy' => 2
853 ),
854 24 => array(
855 'uid' => '24',
856 'title' => 'a',
857 'dummy' => 3
858 ),
859 23 => array(
860 'uid' => '23',
861 'title' => 'b',
862 'dummy' => 4
863 ),
864 ),
865 'title',
866 true,
867 array(
868 24 => array(
869 'uid' => '24',
870 'title' => 'a',
871 'dummy' => 3
872 ),
873 23 => array(
874 'uid' => '23',
875 'title' => 'b',
876 'dummy' => 4
877 ),
878 22 => array(
879 'uid' => '22',
880 'title' => 'c',
881 'dummy' => 2
882 ),
883 ),
884 ),
885 'numeric array index DESC' => array(
886 array(
887 23 => array(
888 'uid' => '23',
889 'title' => 'b',
890 'dummy' => 4
891 ),
892 22 => array(
893 'uid' => '22',
894 'title' => 'c',
895 'dummy' => 2
896 ),
897 24 => array(
898 'uid' => '24',
899 'title' => 'a',
900 'dummy' => 3
901 ),
902 ),
903 'title',
904 false,
905 array(
906 22 => array(
907 'uid' => '22',
908 'title' => 'c',
909 'dummy' => 2
910 ),
911 23 => array(
912 'uid' => '23',
913 'title' => 'b',
914 'dummy' => 4
915 ),
916 24 => array(
917 'uid' => '24',
918 'title' => 'a',
919 'dummy' => 3
920 ),
921 ),
922 ),
923 );
924 }
925
926 /**
927 * @test
928 * @dataProvider sortArraysByKeyCheckIfSortingIsCorrectDataProvider
929 * @param array $array
930 * @param string $key
931 * @param bool $ascending
932 * @param array $expectedResult
933 */
934 public function sortArraysByKeyCheckIfSortingIsCorrect(array $array, $key, $ascending, $expectedResult)
935 {
936 $sortedArray = ArrayUtility::sortArraysByKey($array, $key, $ascending);
937 $this->assertSame($expectedResult, $sortedArray);
938 }
939
940 /**
941 * @test
942 * @expectedException \RuntimeException
943 */
944 public function sortArraysByKeyThrowsExceptionForNonExistingKey()
945 {
946 ArrayUtility::sortArraysByKey(array(array('a'), array('a')), 'dummy');
947 }
948
949 ///////////////////////
950 // Tests concerning arrayExport
951 ///////////////////////
952 /**
953 * @test
954 */
955 public function arrayExportReturnsFormattedMultidimensionalArray()
956 {
957 $array = array(
958 'foo' => array(
959 'bar' => 42,
960 'bar2' => array(
961 'baz' => 'val\'ue',
962 'baz2' => true,
963 'baz3' => false,
964 'baz4' => array()
965 )
966 ),
967 'baz' => 23,
968 'foobar' => null,
969 'qux' => 0.1,
970 'qux2' => 0.000000001,
971 );
972 $expected =
973 '[' . LF .
974 ' \'foo\' => [' . LF .
975 ' \'bar\' => 42,' . LF .
976 ' \'bar2\' => [' . LF .
977 ' \'baz\' => \'val\\\'ue\',' . LF .
978 ' \'baz2\' => true,' . LF .
979 ' \'baz3\' => false,' . LF .
980 ' \'baz4\' => [],' . LF .
981 ' ],' . LF .
982 ' ],' . LF .
983 ' \'baz\' => 23,' . LF .
984 ' \'foobar\' => null,' . LF .
985 ' \'qux\' => 0.1,' . LF .
986 ' \'qux2\' => 1.0E-9,' . LF .
987 ']';
988 $this->assertSame($expected, ArrayUtility::arrayExport($array));
989 }
990
991 /**
992 * @test
993 * @expectedException \RuntimeException
994 */
995 public function arrayExportThrowsExceptionIfObjectShouldBeExported()
996 {
997 $array = array(
998 'foo' => array(
999 'bar' => new \stdClass()
1000 )
1001 );
1002 ArrayUtility::arrayExport($array);
1003 }
1004
1005 /**
1006 * @test
1007 */
1008 public function arrayExportReturnsNumericArrayKeys()
1009 {
1010 $array = array(
1011 'foo' => 'string key',
1012 23 => 'integer key',
1013 '42' => 'string key representing integer'
1014 );
1015 $expected =
1016 '[' . LF .
1017 ' \'foo\' => \'string key\',' . LF .
1018 ' 23 => \'integer key\',' . LF .
1019 ' 42 => \'string key representing integer\',' . LF .
1020 ']';
1021 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1022 }
1023
1024 /**
1025 * @test
1026 */
1027 public function arrayExportReturnsNoKeyIndexForConsecutiveCountedArrays()
1028 {
1029 $array = array(
1030 0 => 'zero',
1031 1 => 'one',
1032 2 => 'two'
1033 );
1034 $expected =
1035 '[' . LF .
1036 ' \'zero\',' . LF .
1037 ' \'one\',' . LF .
1038 ' \'two\',' . LF .
1039 ']';
1040 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1041 }
1042
1043 /**
1044 * @test
1045 */
1046 public function arrayExportReturnsKeyIndexForNonConsecutiveCountedArrays()
1047 {
1048 $array = array(
1049 0 => 'zero',
1050 1 => 'one',
1051 3 => 'three',
1052 4 => 'four'
1053 );
1054 $expected =
1055 '[' . LF .
1056 ' 0 => \'zero\',' . LF .
1057 ' 1 => \'one\',' . LF .
1058 ' 3 => \'three\',' . LF .
1059 ' 4 => \'four\',' . LF .
1060 ']';
1061 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1062 }
1063
1064 ///////////////////////
1065 // Tests concerning flatten
1066 ///////////////////////
1067
1068 /**
1069 * @return array
1070 */
1071 public function flattenCalculatesExpectedResultDataProvider()
1072 {
1073 return array(
1074 'plain array' => array(
1075 array(
1076 'first' => 1,
1077 'second' => 2
1078 ),
1079 array(
1080 'first' => 1,
1081 'second' => 2
1082 )
1083 ),
1084 'plain array with faulty dots' => array(
1085 array(
1086 'first.' => 1,
1087 'second.' => 2
1088 ),
1089 array(
1090 'first' => 1,
1091 'second' => 2
1092 )
1093 ),
1094 'nested array of 2 levels' => array(
1095 array(
1096 'first.' => array(
1097 'firstSub' => 1
1098 ),
1099 'second.' => array(
1100 'secondSub' => 2
1101 )
1102 ),
1103 array(
1104 'first.firstSub' => 1,
1105 'second.secondSub' => 2
1106 )
1107 ),
1108 'nested array of 2 levels with faulty dots' => array(
1109 array(
1110 'first.' => array(
1111 'firstSub.' => 1
1112 ),
1113 'second.' => array(
1114 'secondSub.' => 2
1115 )
1116 ),
1117 array(
1118 'first.firstSub' => 1,
1119 'second.secondSub' => 2
1120 )
1121 ),
1122 'nested array of 3 levels' => array(
1123 array(
1124 'first.' => array(
1125 'firstSub.' => array(
1126 'firstSubSub' => 1
1127 )
1128 ),
1129 'second.' => array(
1130 'secondSub.' => array(
1131 'secondSubSub' => 2
1132 )
1133 )
1134 ),
1135 array(
1136 'first.firstSub.firstSubSub' => 1,
1137 'second.secondSub.secondSubSub' => 2
1138 )
1139 ),
1140 'nested array of 3 levels with faulty dots' => array(
1141 array(
1142 'first.' => array(
1143 'firstSub.' => array(
1144 'firstSubSub.' => 1
1145 )
1146 ),
1147 'second.' => array(
1148 'secondSub.' => array(
1149 'secondSubSub.' => 2
1150 )
1151 )
1152 ),
1153 array(
1154 'first.firstSub.firstSubSub' => 1,
1155 'second.secondSub.secondSubSub' => 2
1156 )
1157 )
1158 );
1159 }
1160
1161 /**
1162 * @test
1163 * @param array $array
1164 * @param array $expected
1165 * @dataProvider flattenCalculatesExpectedResultDataProvider
1166 */
1167 public function flattenCalculatesExpectedResult(array $array, array $expected)
1168 {
1169 $this->assertEquals($expected, ArrayUtility::flatten($array));
1170 }
1171
1172 ///////////////////////
1173 // Tests concerning intersectRecursive
1174 ///////////////////////
1175
1176 /**
1177 * @return array
1178 */
1179 public function intersectRecursiveCalculatesExpectedResultDataProvider()
1180 {
1181 $sameObject = new \stdClass();
1182 return array(
1183 // array($source, $mask, $expected)
1184 'empty array is returned if source is empty array' => array(
1185 array(),
1186 array(
1187 'foo' => 'bar',
1188 ),
1189 array(),
1190 ),
1191 'empty array is returned if mask is empty' => array(
1192 array(
1193 'foo' => 'bar',
1194 ),
1195 array(),
1196 array(),
1197 ),
1198 'key is kept on first level if exists in mask' => array(
1199 array(
1200 'foo' => 42,
1201 ),
1202 array(
1203 'foo' => 42,
1204 ),
1205 array(
1206 'foo' => 42,
1207 ),
1208 ),
1209 'value of key in source is kept if mask has different value' => array(
1210 array(
1211 'foo' => 42,
1212 ),
1213 array(
1214 'foo' => new \stdClass(),
1215 ),
1216 array(
1217 'foo' => 42,
1218 ),
1219 ),
1220 'key is kept on first level if according mask value is NULL' => array(
1221 array(
1222 'foo' => 42,
1223 ),
1224 array(
1225 'foo' => null,
1226 ),
1227 array(
1228 'foo' => 42,
1229 ),
1230 ),
1231 'null in source value is kept' => array(
1232 array(
1233 'foo' => null,
1234 ),
1235 array(
1236 'foo' => 'bar',
1237 ),
1238 array(
1239 'foo' => null,
1240 )
1241 ),
1242 'mask does not add new keys' => array(
1243 array(
1244 'foo' => 42,
1245 ),
1246 array(
1247 'foo' => 23,
1248 'bar' => array(
1249 4711
1250 ),
1251 ),
1252 array(
1253 'foo' => 42,
1254 ),
1255 ),
1256 'mask does not overwrite simple values with arrays' => array(
1257 array(
1258 'foo' => 42,
1259 ),
1260 array(
1261 'foo' => array(
1262 'bar' => 23,
1263 ),
1264 ),
1265 array(
1266 'foo' => 42,
1267 ),
1268 ),
1269 'key is kept on first level if according mask value is array' => array(
1270 array(
1271 'foo' => 42,
1272 ),
1273 array(
1274 'foo' => array(
1275 'bar' => 23
1276 ),
1277 ),
1278 array(
1279 'foo' => 42,
1280 ),
1281 ),
1282 'full array is kept if value is array and mask value is simple type' => array(
1283 array(
1284 'foo' => array(
1285 'bar' => 23
1286 ),
1287 ),
1288 array(
1289 'foo' => 42,
1290 ),
1291 array(
1292 'foo' => array(
1293 'bar' => 23
1294 ),
1295 ),
1296 ),
1297 'key handling is type agnostic' => array(
1298 array(
1299 42 => 'foo',
1300 ),
1301 array(
1302 '42' => 'bar',
1303 ),
1304 array(
1305 42 => 'foo',
1306 ),
1307 ),
1308 'value is same if value is object' => array(
1309 array(
1310 'foo' => $sameObject,
1311 ),
1312 array(
1313 'foo' => 'something',
1314 ),
1315 array(
1316 'foo' => $sameObject,
1317 ),
1318 ),
1319 'mask does not add simple value to result if key does not exist in source' => array(
1320 array(
1321 'foo' => '42',
1322 ),
1323 array(
1324 'foo' => '42',
1325 'bar' => 23
1326 ),
1327 array(
1328 'foo' => '42',
1329 ),
1330 ),
1331 'array of source is kept if value of mask key exists but is no array' => array(
1332 array(
1333 'foo' => '42',
1334 'bar' => array(
1335 'baz' => 23
1336 ),
1337 ),
1338 array(
1339 'foo' => 'value is not significant',
1340 'bar' => null,
1341 ),
1342 array(
1343 'foo' => '42',
1344 'bar' => array(
1345 'baz' => 23
1346 ),
1347 ),
1348 ),
1349 'sub arrays are kept if mask has according sub array key and is similar array' => array(
1350 array(
1351 'first1' => 42,
1352 'first2' => array(
1353 'second1' => 23,
1354 'second2' => 4711,
1355 ),
1356 ),
1357 array(
1358 'first1' => 42,
1359 'first2' => array(
1360 'second1' => 'exists but different',
1361 ),
1362 ),
1363 array(
1364 'first1' => 42,
1365 'first2' => array(
1366 'second1' => 23,
1367 ),
1368 ),
1369 ),
1370 );
1371 }
1372
1373 /**
1374 * @test
1375 * @param array $source
1376 * @param array $mask
1377 * @param array $expected
1378 * @dataProvider intersectRecursiveCalculatesExpectedResultDataProvider
1379 */
1380 public function intersectRecursiveCalculatesExpectedResult(array $source, array $mask, array $expected)
1381 {
1382 $this->assertSame($expected, ArrayUtility::intersectRecursive($source, $mask));
1383 }
1384
1385 ///////////////////////
1386 // Tests concerning renumberKeysToAvoidLeapsIfKeysAreAllNumeric
1387 ///////////////////////
1388 /**
1389 * @return array
1390 */
1391 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider()
1392 {
1393 return array(
1394 'empty array is returned if source is empty array' => array(
1395 array(),
1396 array()
1397 ),
1398 'returns self if array is already numerically keyed' => array(
1399 array(1,2,3),
1400 array(1,2,3)
1401 ),
1402 'returns correctly if keys are numeric, but contains a leap' => array(
1403 array(0 => 'One', 1 => 'Two', 3 => 'Three'),
1404 array(0 => 'One', 1 => 'Two', 2 => 'Three'),
1405 ),
1406 'returns correctly even though keys are strings but still numeric' => array(
1407 array('0' => 'One', '1' => 'Two', '3' => 'Three'),
1408 array(0 => 'One', 1 => 'Two', 2 => 'Three'),
1409 ),
1410 'returns correctly if just a single keys is not numeric' => array(
1411 array(0 => 'Zero', '1' => 'One', 'Two' => 'Two'),
1412 array(0 => 'Zero', '1' => 'One', 'Two' => 'Two'),
1413 ),
1414 'return self with nested numerically keyed array' => array(
1415 array(
1416 'One',
1417 'Two',
1418 'Three',
1419 array(
1420 'sub.One',
1421 'sub.Two',
1422 )
1423 ),
1424 array(
1425 'One',
1426 'Two',
1427 'Three',
1428 array(
1429 'sub.One',
1430 'sub.Two',
1431 )
1432 )
1433 ),
1434 'returns correctly with nested numerically keyed array with leaps' => array(
1435 array(
1436 'One',
1437 'Two',
1438 'Three',
1439 array(
1440 0 => 'sub.One',
1441 2 => 'sub.Two',
1442 )
1443 ),
1444 array(
1445 'One',
1446 'Two',
1447 'Three',
1448 array(
1449 'sub.One',
1450 'sub.Two',
1451 )
1452 )
1453 ),
1454 'returns correctly with nested string-keyed array' => array(
1455 array(
1456 'One',
1457 'Two',
1458 'Three',
1459 array(
1460 'one' => 'sub.One',
1461 'two' => 'sub.Two',
1462 )
1463 ),
1464 array(
1465 'One',
1466 'Two',
1467 'Three',
1468 array(
1469 'one' => 'sub.One',
1470 'two' => 'sub.Two',
1471 )
1472 )
1473 ),
1474 'returns correctly with deeply nested arrays' => array(
1475 array(
1476 'One',
1477 'Two',
1478 array(
1479 'one' => 1,
1480 'two' => 2,
1481 'three' => array(
1482 2 => 'SubSubOne',
1483 5 => 'SubSubTwo',
1484 9 => array(0,1,2),
1485 array()
1486 )
1487 )
1488 ),
1489 array(
1490 'One',
1491 'Two',
1492 array(
1493 'one' => 1,
1494 'two' => 2,
1495 'three' => array(
1496 'SubSubOne',
1497 'SubSubTwo',
1498 array(0,1,2),
1499 array()
1500 )
1501 )
1502 )
1503 )
1504 );
1505 }
1506
1507 /**
1508 * @test
1509 * @param array $inputArray
1510 * @param array $expected
1511 * @dataProvider renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider
1512 */
1513 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericReturnsExpectedOrder(array $inputArray, array $expected)
1514 {
1515 $this->assertEquals($expected, ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($inputArray));
1516 }
1517
1518 /**
1519 * @return array
1520 */
1521 public function mergeRecursiveWithOverruleCalculatesExpectedResultDataProvider()
1522 {
1523 return array(
1524 'Override array can reset string to array' => array(
1525 array(
1526 'first' => array(
1527 'second' => 'foo',
1528 ),
1529 ),
1530 array(
1531 'first' => array(
1532 'second' => array('third' => 'bar'),
1533 ),
1534 ),
1535 true,
1536 true,
1537 true,
1538 array(
1539 'first' => array(
1540 'second' => array('third' => 'bar'),
1541 ),
1542 ),
1543 ),
1544 'Override array does not reset array to string (weird!)' => array(
1545 array(
1546 'first' => array(),
1547 ),
1548 array(
1549 'first' => 'foo',
1550 ),
1551 true,
1552 true,
1553 true,
1554 array(
1555 'first' => array(), // This is rather unexpected, naive expectation: first => 'foo'
1556 ),
1557 ),
1558 'Override array does override string with null' => array(
1559 array(
1560 'first' => 'foo',
1561 ),
1562 array(
1563 'first' => null,
1564 ),
1565 true,
1566 true,
1567 true,
1568 array(
1569 'first' => null,
1570 ),
1571 ),
1572 'Override array does override null with string' => array(
1573 array(
1574 'first' => null,
1575 ),
1576 array(
1577 'first' => 'foo',
1578 ),
1579 true,
1580 true,
1581 true,
1582 array(
1583 'first' => 'foo',
1584 ),
1585 ),
1586 'Override array does override null with empty string' => array(
1587 array(
1588 'first' => null,
1589 ),
1590 array(
1591 'first' => '',
1592 ),
1593 true,
1594 true,
1595 true,
1596 array(
1597 'first' => '',
1598 ),
1599 ),
1600 'Override array does not override string with NULL if requested' => array(
1601 array(
1602 'first' => 'foo',
1603 ),
1604 array(
1605 'first' => null,
1606 ),
1607 true,
1608 false, // no include empty values
1609 true,
1610 array(
1611 'first' => 'foo',
1612 ),
1613 ),
1614 'Override array does override null with null' => array(
1615 array(
1616 'first' => null,
1617 ),
1618 array(
1619 'first' => null,
1620 ),
1621 true,
1622 true,
1623 true,
1624 array(
1625 'first' => '',
1626 ),
1627 ),
1628 'Override array can __UNSET values' => array(
1629 array(
1630 'first' => array(
1631 'second' => 'second',
1632 'third' => 'third',
1633 ),
1634 'fifth' => array(),
1635 ),
1636 array(
1637 'first' => array(
1638 'second' => 'overrule',
1639 'third' => '__UNSET',
1640 'fourth' => 'overrile',
1641 ),
1642 'fifth' => '__UNSET',
1643 ),
1644 true,
1645 true,
1646 true,
1647 array(
1648 'first' => array(
1649 'second' => 'overrule',
1650 'fourth' => 'overrile',
1651 ),
1652 ),
1653 ),
1654 'Override can add keys' => array(
1655 array(
1656 'first' => 'foo',
1657 ),
1658 array(
1659 'second' => 'bar',
1660 ),
1661 true,
1662 true,
1663 true,
1664 array(
1665 'first' => 'foo',
1666 'second' => 'bar',
1667 ),
1668 ),
1669 'Override does not add key if __UNSET' => array(
1670 array(
1671 'first' => 'foo',
1672 ),
1673 array(
1674 'second' => '__UNSET',
1675 ),
1676 true,
1677 true,
1678 true,
1679 array(
1680 'first' => 'foo',
1681 ),
1682 ),
1683 'Override does not add key if not requested' => array(
1684 array(
1685 'first' => 'foo',
1686 ),
1687 array(
1688 'second' => 'bar',
1689 ),
1690 false, // no add keys
1691 true,
1692 true,
1693 array(
1694 'first' => 'foo',
1695 ),
1696 ),
1697 'Override does not add key if not requested with add include empty values' => array(
1698 array(
1699 'first' => 'foo',
1700 ),
1701 array(
1702 'second' => 'bar',
1703 ),
1704 false, // no add keys
1705 false, // no include empty values
1706 true,
1707 array(
1708 'first' => 'foo',
1709 ),
1710 ),
1711 'Override does not override string with empty string if requested' => array(
1712 array(
1713 'first' => 'foo',
1714 ),
1715 array(
1716 'first' => '',
1717 ),
1718 true,
1719 false, // no include empty values
1720 true,
1721 array(
1722 'first' => 'foo',
1723 ),
1724 ),
1725 'Override array does merge instead of __UNSET if requested (weird!)' => array(
1726 array(
1727 'first' => array(
1728 'second' => 'second',
1729 'third' => 'third',
1730 ),
1731 'fifth' => array(),
1732 ),
1733 array(
1734 'first' => array(
1735 'second' => 'overrule',
1736 'third' => '__UNSET',
1737 'fourth' => 'overrile',
1738 ),
1739 'fifth' => '__UNSET',
1740 ),
1741 true,
1742 true,
1743 false,
1744 array(
1745 'first' => array(
1746 'second' => 'overrule',
1747 'third' => '__UNSET', // overruled
1748 'fourth' => 'overrile',
1749 ),
1750 'fifth' => array(), // not overruled with string here, naive expectation: 'fifth' => '__UNSET'
1751 ),
1752 ),
1753 );
1754 }
1755
1756 /**
1757 * @test
1758 * @dataProvider mergeRecursiveWithOverruleCalculatesExpectedResultDataProvider
1759 * @param array $input1 Input 1
1760 * @param array $input2 Input 2
1761 * @param bool $addKeys TRUE if should add keys, else FALSE
1762 * @param bool $includeEmptyValues TRUE if should include empty values, else FALSE
1763 * @param bool $enableUnsetFeature TRUE if should enable unset feature, else FALSE
1764 * @param array $expected expected array
1765 */
1766 public function mergeRecursiveWithOverruleCalculatesExpectedResult($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature, $expected)
1767 {
1768 ArrayUtility::mergeRecursiveWithOverrule($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature);
1769 $this->assertEquals($expected, $input1);
1770 }
1771
1772 //////////////////////////////////
1773 // Tests concerning inArray
1774 //////////////////////////////////
1775 /**
1776 * @test
1777 * @dataProvider inArrayDataProvider
1778 * @param array $array target array
1779 * @param string $item search string
1780 * @param bool $expected expected value
1781 */
1782 public function inArrayChecksStringExistenceWithinArray($array, $item, $expected)
1783 {
1784 $this->assertEquals($expected, ArrayUtility::inArray($array, $item));
1785 }
1786
1787 /**
1788 * Data provider for inArrayChecksStringExistenceWithinArray
1789 *
1790 * @return array
1791 */
1792 public function inArrayDataProvider()
1793 {
1794 return array(
1795 'Empty array' => array(array(), 'search', false),
1796 'One item array no match' => array(array('one'), 'two', false),
1797 'One item array match' => array(array('one'), 'one', true),
1798 'Multiple items array no match' => array(array('one', 2, 'three', 4), 'four', false),
1799 'Multiple items array match' => array(array('one', 2, 'three', 4), 'three', true),
1800 'Integer search items can match string values' => array(array('0', '1', '2'), 1, true),
1801 'Search item is not casted to integer for a match' => array(array(4), '4a', false),
1802 'Empty item won\'t match - in contrast to the php-builtin ' => array(array(0, 1, 2), '', false)
1803 );
1804 }
1805
1806 //////////////////////////////////
1807 // Tests concerning removeArrayEntryByValue
1808 //////////////////////////////////
1809 /**
1810 * @test
1811 */
1812 public function checkRemoveArrayEntryByValueRemovesEntriesFromOneDimensionalArray()
1813 {
1814 $inputArray = array(
1815 '0' => 'test1',
1816 '1' => 'test2',
1817 '2' => 'test3',
1818 '3' => 'test2'
1819 );
1820 $compareValue = 'test2';
1821 $expectedResult = array(
1822 '0' => 'test1',
1823 '2' => 'test3'
1824 );
1825 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1826 $this->assertEquals($expectedResult, $actualResult);
1827 }
1828
1829 /**
1830 * @test
1831 */
1832 public function checkRemoveArrayEntryByValueRemovesEntriesFromMultiDimensionalArray()
1833 {
1834 $inputArray = array(
1835 '0' => 'foo',
1836 '1' => array(
1837 '10' => 'bar'
1838 ),
1839 '2' => 'bar'
1840 );
1841 $compareValue = 'bar';
1842 $expectedResult = array(
1843 '0' => 'foo',
1844 '1' => array()
1845 );
1846 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1847 $this->assertEquals($expectedResult, $actualResult);
1848 }
1849
1850 /**
1851 * @test
1852 */
1853 public function checkRemoveArrayEntryByValueRemovesEntryWithEmptyString()
1854 {
1855 $inputArray = array(
1856 '0' => 'foo',
1857 '1' => '',
1858 '2' => 'bar'
1859 );
1860 $compareValue = '';
1861 $expectedResult = array(
1862 '0' => 'foo',
1863 '2' => 'bar'
1864 );
1865 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1866 $this->assertEquals($expectedResult, $actualResult);
1867 }
1868
1869 //////////////////////////////////
1870 // Tests concerning keepItemsInArray
1871 //////////////////////////////////
1872 /**
1873 * @test
1874 * @dataProvider keepItemsInArrayWorksWithOneArgumentDataProvider
1875 * @param mixed $search The items which are allowed/kept in the array
1876 * @param array $array target array
1877 * @param array $expected expected array
1878 */
1879 public function keepItemsInArrayWorksWithOneArgument($search, $array, $expected)
1880 {
1881 $this->assertEquals($expected, ArrayUtility::keepItemsInArray($array, $search));
1882 }
1883
1884 /**
1885 * Data provider for keepItemsInArrayWorksWithOneArgument
1886 *
1887 * @return array
1888 */
1889 public function keepItemsInArrayWorksWithOneArgumentDataProvider()
1890 {
1891 $array = array(
1892 'one' => 'one',
1893 'two' => 'two',
1894 'three' => 'three'
1895 );
1896 return array(
1897 'Empty argument will match "all" elements' => array(null, $array, $array),
1898 'No match' => array('four', $array, array()),
1899 'One match' => array('two', $array, array('two' => 'two')),
1900 'Multiple matches' => array('two,one', $array, array('one' => 'one', 'two' => 'two')),
1901 'Argument can be an array' => array(array('three'), $array, array('three' => 'three'))
1902 );
1903 }
1904
1905 /**
1906 * Shows the example from the doc comment where
1907 * a function is used to reduce the sub arrays to one item which
1908 * is then used for the matching.
1909 *
1910 * @test
1911 */
1912 public function keepItemsInArrayCanUseClosure()
1913 {
1914 $array = array(
1915 'aa' => array('first', 'second'),
1916 'bb' => array('third', 'fourth'),
1917 'cc' => array('fifth', 'sixth')
1918 );
1919 $expected = array('bb' => array('third', 'fourth'));
1920 $keepItems = 'third';
1921 $match = ArrayUtility::keepItemsInArray(
1922 $array,
1923 $keepItems,
1924 function ($value) {
1925 return $value[0];
1926 }
1927 );
1928 $this->assertEquals($expected, $match);
1929 }
1930
1931 //////////////////////////////////
1932 // Tests concerning remapArrayKeys
1933 //////////////////////////////////
1934 /**
1935 * @test
1936 */
1937 public function remapArrayKeysExchangesKeysWithGivenMapping()
1938 {
1939 $array = array(
1940 'one' => 'one',
1941 'two' => 'two',
1942 'three' => 'three'
1943 );
1944 $keyMapping = array(
1945 'one' => '1',
1946 'two' => '2'
1947 );
1948 $expected = array(
1949 '1' => 'one',
1950 '2' => 'two',
1951 'three' => 'three'
1952 );
1953 ArrayUtility::remapArrayKeys($array, $keyMapping);
1954 $this->assertEquals($expected, $array);
1955 }
1956
1957 //////////////////////////////////////
1958 // Tests concerning arrayDiffAssocRecursive
1959 //////////////////////////////////////
1960 /**
1961 * @test
1962 */
1963 public function arrayDiffAssocRecursiveHandlesOneDimensionalArrays()
1964 {
1965 $array1 = array(
1966 'key1' => 'value1',
1967 'key2' => 'value2',
1968 'key3' => 'value3'
1969 );
1970 $array2 = array(
1971 'key1' => 'value1',
1972 'key3' => 'value3'
1973 );
1974 $expectedResult = array(
1975 'key2' => 'value2'
1976 );
1977 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
1978 $this->assertEquals($expectedResult, $actualResult);
1979 }
1980
1981 /**
1982 * @test
1983 */
1984 public function arrayDiffAssocRecursiveHandlesMultiDimensionalArrays()
1985 {
1986 $array1 = array(
1987 'key1' => 'value1',
1988 'key2' => array(
1989 'key21' => 'value21',
1990 'key22' => 'value22',
1991 'key23' => array(
1992 'key231' => 'value231',
1993 'key232' => 'value232'
1994 )
1995 )
1996 );
1997 $array2 = array(
1998 'key1' => 'value1',
1999 'key2' => array(
2000 'key21' => 'value21',
2001 'key23' => array(
2002 'key231' => 'value231'
2003 )
2004 )
2005 );
2006 $expectedResult = array(
2007 'key2' => array(
2008 'key22' => 'value22',
2009 'key23' => array(
2010 'key232' => 'value232'
2011 )
2012 )
2013 );
2014 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2015 $this->assertEquals($expectedResult, $actualResult);
2016 }
2017
2018 /**
2019 * @test
2020 */
2021 public function arrayDiffAssocRecursiveHandlesMixedArrays()
2022 {
2023 $array1 = array(
2024 'key1' => array(
2025 'key11' => 'value11',
2026 'key12' => 'value12'
2027 ),
2028 'key2' => 'value2',
2029 'key3' => 'value3'
2030 );
2031 $array2 = array(
2032 'key1' => 'value1',
2033 'key2' => array(
2034 'key21' => 'value21'
2035 )
2036 );
2037 $expectedResult = array(
2038 'key3' => 'value3'
2039 );
2040 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2041 $this->assertEquals($expectedResult, $actualResult);
2042 }
2043
2044 //////////////////////////////////////
2045 // Tests concerning naturalKeySortRecursive
2046 //////////////////////////////////////
2047
2048 /**
2049 * @test
2050 */
2051 public function naturalKeySortRecursiveSortsOneDimensionalArrayByNaturalOrder()
2052 {
2053 $testArray = array(
2054 'bb' => 'bb',
2055 'ab' => 'ab',
2056 '123' => '123',
2057 'aaa' => 'aaa',
2058 'abc' => 'abc',
2059 '23' => '23',
2060 'ba' => 'ba',
2061 'bad' => 'bad',
2062 '2' => '2',
2063 'zap' => 'zap',
2064 '210' => '210'
2065 );
2066 $expectedResult = array(
2067 '2',
2068 '23',
2069 '123',
2070 '210',
2071 'aaa',
2072 'ab',
2073 'abc',
2074 'ba',
2075 'bad',
2076 'bb',
2077 'zap'
2078 );
2079 ArrayUtility::naturalKeySortRecursive($testArray);
2080 $this->assertEquals($expectedResult, array_values($testArray));
2081 }
2082
2083 /**
2084 * @test
2085 */
2086 public function naturalKeySortRecursiveSortsMultiDimensionalArrayByNaturalOrder()
2087 {
2088 $testArray = array(
2089 '2' => '2',
2090 'bb' => 'bb',
2091 'ab' => 'ab',
2092 '23' => '23',
2093 'aaa' => array(
2094 'bb' => 'bb',
2095 'ab' => 'ab',
2096 '123' => '123',
2097 'aaa' => 'aaa',
2098 '2' => '2',
2099 'abc' => 'abc',
2100 'ba' => 'ba',
2101 '23' => '23',
2102 'bad' => array(
2103 'bb' => 'bb',
2104 'ab' => 'ab',
2105 '123' => '123',
2106 'aaa' => 'aaa',
2107 'abc' => 'abc',
2108 '23' => '23',
2109 'ba' => 'ba',
2110 'bad' => 'bad',
2111 '2' => '2',
2112 'zap' => 'zap',
2113 '210' => '210'
2114 ),
2115 '210' => '210',
2116 'zap' => 'zap'
2117 ),
2118 'abc' => 'abc',
2119 'ba' => 'ba',
2120 '210' => '210',
2121 'bad' => 'bad',
2122 '123' => '123',
2123 'zap' => 'zap'
2124 );
2125 $expectedResult = array(
2126 '2',
2127 '23',
2128 '123',
2129 '210',
2130 'aaa',
2131 'ab',
2132 'abc',
2133 'ba',
2134 'bad',
2135 'bb',
2136 'zap'
2137 );
2138 ArrayUtility::naturalKeySortRecursive($testArray);
2139 $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa']['bad'])));
2140 $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa'])));
2141 $this->assertEquals($expectedResult, array_values(array_keys($testArray)));
2142 }
2143 }