TYPO3 CMS  TYPO3_8-7
ArrayUtilityTest.php
Go to the documentation of this file.
1 <?php
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 
18 
22 class ArrayUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
23 {
25  // Tests concerning filterByValueRecursive
27 
35  public function filterByValueRecursive()
36  {
37  return [
38  'empty search array' => [
39  'banana',
40  [],
41  []
42  ],
43  'empty string as needle' => [
44  '',
45  [
46  '',
47  'apple'
48  ],
49  [
50  ''
51  ]
52  ],
53  'flat array searching for string' => [
54  'banana',
55  [
56  'apple',
57  'banana'
58  ],
59  [
60  1 => 'banana'
61  ]
62  ],
63  'flat array searching for string with two matches' => [
64  'banana',
65  [
66  'foo' => 'apple',
67  'firstbanana' => 'banana',
68  'secondbanana' => 'banana'
69  ],
70  [
71  'firstbanana' => 'banana',
72  'secondbanana' => 'banana'
73  ]
74  ],
75  'multi dimensional array searching for string with multiple matches' => [
76  'banana',
77  [
78  'foo' => 'apple',
79  'firstbanana' => 'banana',
80  'grape' => [
81  'foo2' => 'apple2',
82  'secondbanana' => 'banana',
83  'foo3' => []
84  ],
85  'bar' => 'orange'
86  ],
87  [
88  'firstbanana' => 'banana',
89  'grape' => [
90  'secondbanana' => 'banana'
91  ]
92  ]
93  ],
94  'multi dimensional array searching for integer with multiple matches' => [
95  42,
96  [
97  'foo' => 23,
98  'bar' => 42,
99  [
100  'foo' => 23,
101  'bar' => 42
102  ]
103  ],
104  [
105  'bar' => 42,
106  [
107  'bar' => 42
108  ]
109  ]
110  ],
111  'flat array searching for boolean TRUE' => [
112  true,
113  [
114  23 => false,
115  42 => true
116  ],
117  [
118  42 => true
119  ]
120  ],
121  'multi dimensional array searching for boolean FALSE' => [
122  false,
123  [
124  23 => false,
125  42 => true,
126  'foo' => [
127  23 => false,
128  42 => true
129  ]
130  ],
131  [
132  23 => false,
133  'foo' => [
134  23 => false
135  ]
136  ]
137  ],
138  'flat array searching for array' => [
139  [
140  'foo' => 'bar'
141  ],
142  [
143  'foo' => 'bar',
144  'foobar' => [
145  'foo' => 'bar'
146  ]
147  ],
148  [
149  'foobar' => [
150  'foo' => 'bar'
151  ]
152  ]
153  ]
154  ];
155  }
156 
164  public function filterByValueRecursiveCorrectlyFiltersArray($needle, $haystack, $expectedResult)
165  {
166  $this->assertEquals(
167  $expectedResult,
168  ArrayUtility::filterByValueRecursive($needle, $haystack)
169  );
170  }
171 
176  {
177  $instance = new \stdClass();
178  $this->assertEquals(
179  [$instance],
180  ArrayUtility::filterByValueRecursive($instance, [$instance])
181  );
182  }
183 
188  {
189  $this->assertEquals(
190  [],
191  ArrayUtility::filterByValueRecursive(new \stdClass(), [new \stdClass()])
192  );
193  }
194 
196  // Tests concerning isValidPath
198 
207  {
208  $this->assertTrue(ArrayUtility::isValidPath(['foo' => 'bar'], 'foo'));
209  }
210 
215  {
216  $this->assertFalse(ArrayUtility::isValidPath(['foo' => 'bar'], 'bar'));
217  }
218 
220  // Tests concerning getValueByPath
222 
226  {
227  $this->expectException(\InvalidArgumentException::class);
228  $this->expectExceptionCode(1476557628);
229 
231  }
232 
237  {
238  $this->expectException(\RuntimeException::class);
239  $this->expectExceptionCode(1341397767);
240 
242  }
243 
248  {
249  $this->assertSame('foo', ArrayUtility::getValueByPath(['foo'], '0'));
250  }
251 
256  {
257  $this->assertSame('bar', ArrayUtility::getValueByPath(['foo' => ['bar']], 'foo/0'));
258  }
259 
269  {
270  return [
271  'not existing index' => [
272  [
273  'foo' => ['foo']
274  ],
275  'foo/1',
276  false
277  ],
278  'not existing path 1' => [
279  [
280  'foo' => []
281  ],
282  'foo/bar/baz',
283  false
284  ],
285  'not existing path 2' => [
286  [
287  'foo' => [
288  'baz' => 42
289  ],
290  'bar' => []
291  ],
292  'foo/bar/baz',
293  false
294  ],
295  'last segment is not an array' => [
296  [
297  'foo' => [
298  'baz' => 42
299  ],
300  ],
301  'foo/baz/baz',
302  false
303  ],
304  // Negative test: This could be improved and the test moved to
305  // the valid data provider if the method supports this
306  'doubletick encapsulated quoted doubletick does not work' => [
307  [
308  '"foo"bar"' => [
309  'baz' => 42
310  ],
311  'bar' => []
312  ],
313  '"foo\\"bar"/baz',
314  42
315  ],
316  // Negative test: Method could be improved here
317  'path with doubletick does not work' => [
318  [
319  'fo"o' => [
320  'bar' => 42
321  ]
322  ],
323  'fo"o/foobar',
324  42
325  ]
326  ];
327  }
328 
335  public function getValueByPathThrowsExceptionIfPathNotExists(array $array, $path)
336  {
337  $this->expectException(\RuntimeException::class);
338  $this->expectExceptionCode(1341397869);
339 
340  ArrayUtility::getValueByPath($array, $path);
341  }
342 
351  {
352  $testObject = new \stdClass();
353  $testObject->foo = 'foo';
354  $testObject->bar = 'bar';
355  return [
356  'integer in multi level array' => [
357  [
358  'foo' => [
359  'bar' => [
360  'baz' => 42
361  ],
362  'bar2' => []
363  ]
364  ],
365  'foo/bar/baz',
366  42
367  ],
368  'zero integer in multi level array' => [
369  [
370  'foo' => [
371  'bar' => [
372  'baz' => 0
373  ]
374  ]
375  ],
376  'foo/bar/baz',
377  0
378  ],
379  'NULL value in multi level array' => [
380  [
381  'foo' => [
382  'baz' => null
383  ]
384  ],
385  'foo/baz',
386  null
387  ],
388  'get string value' => [
389  [
390  'foo' => [
391  'baz' => 'this is a test string'
392  ]
393  ],
394  'foo/baz',
395  'this is a test string'
396  ],
397  'get boolean value: FALSE' => [
398  [
399  'foo' => [
400  'baz' => false
401  ]
402  ],
403  'foo/baz',
404  false
405  ],
406  'get boolean value: TRUE' => [
407  [
408  'foo' => [
409  'baz' => true
410  ]
411  ],
412  'foo/baz',
413  true
414  ],
415  'get object value' => [
416  [
417  'foo' => [
418  'baz' => $testObject
419  ]
420  ],
421  'foo/baz',
422  $testObject
423  ],
424  'enclosed path' => [
425  [
426  'foo/bar' => [
427  'foobar' => 42
428  ]
429  ],
430  '"foo/bar"/foobar',
431  42
432  ]
433  ];
434  }
435 
443  public function getValueByPathGetsCorrectValue(array $array, $path, $expectedResult)
444  {
445  $this->assertEquals($expectedResult, ArrayUtility::getValueByPath($array, $path));
446  }
447 
452  {
453  $input = [
454  'foo' => [
455  'bar' => [
456  'baz' => 42
457  ],
458  'bar2' => []
459  ]
460  ];
461  $searchPath = 'foo%bar%baz';
462  $expected = 42;
463  $delimiter = '%';
464  $this->assertEquals(
465  $expected,
466  ArrayUtility::getValueByPath($input, $searchPath, $delimiter)
467  );
468  }
469 
471  // Tests concerning setValueByPath
473 
477  {
478  $this->expectException(\RuntimeException::class);
479  $this->expectExceptionCode(1341406194);
480 
481  ArrayUtility::setValueByPath([], '', null);
482  }
483 
488  {
489  $this->expectException(\InvalidArgumentException::class);
490  $this->expectExceptionCode(1478781081);
491 
492  ArrayUtility::setValueByPath([], 123, null);
493  }
494 
499  {
500  $this->expectException(\RuntimeException::class);
501  $this->expectExceptionCode(1341406846);
502 
503  ArrayUtility::setValueByPath(['foo' => 'bar'], '/foo', 'value');
504  }
505 
510  {
511  $this->assertSame(['foo' => ['value']], ArrayUtility::setValueByPath(['foo' => []], 'foo/0', 'value'));
512  }
513 
518  {
519  $this->assertSame(['value', 'bar'], ArrayUtility::setValueByPath(['foo', 'bar'], '0', 'value'));
520  }
521 
532  {
533  $testObject = new \stdClass();
534  $testObject->foo = 'foo';
535  $testObject->bar = 'bar';
536  return [
537  'set integer value: 42' => [
538  [
539  'foo' => [
540  'bar' => [
541  'baz' => 0
542  ]
543  ]
544  ],
545  'foo/bar/baz',
546  42,
547  [
548  'foo' => [
549  'bar' => [
550  'baz' => 42
551  ]
552  ]
553  ]
554  ],
555  'set integer value: 0' => [
556  [
557  'foo' => [
558  'bar' => [
559  'baz' => 42
560  ]
561  ]
562  ],
563  'foo/bar/baz',
564  0,
565  [
566  'foo' => [
567  'bar' => [
568  'baz' => 0
569  ]
570  ]
571  ]
572  ],
573  'set null value' => [
574  [
575  'foo' => [
576  'bar' => [
577  'baz' => 42
578  ]
579  ]
580  ],
581  'foo/bar/baz',
582  null,
583  [
584  'foo' => [
585  'bar' => [
586  'baz' => null
587  ]
588  ]
589  ]
590  ],
591  'set array value' => [
592  [
593  'foo' => [
594  'bar' => [
595  'baz' => 42
596  ]
597  ]
598  ],
599  'foo/bar/baz',
600  [
601  'foo' => 123
602  ],
603  [
604  'foo' => [
605  'bar' => [
606  'baz' => [
607  'foo' => 123
608  ]
609  ]
610  ]
611  ]
612  ],
613  'set boolean value: FALSE' => [
614  [
615  'foo' => [
616  'bar' => [
617  'baz' => true
618  ]
619  ]
620  ],
621  'foo/bar/baz',
622  false,
623  [
624  'foo' => [
625  'bar' => [
626  'baz' => false
627  ]
628  ]
629  ]
630  ],
631  'set boolean value: TRUE' => [
632  [
633  'foo' => [
634  'bar' => [
635  'baz' => null
636  ]
637  ]
638  ],
639  'foo/bar/baz',
640  true,
641  [
642  'foo' => [
643  'bar' => [
644  'baz' => true
645  ]
646  ]
647  ]
648  ],
649  'set object value' => [
650  [
651  'foo' => [
652  'bar' => [
653  'baz' => null
654  ]
655  ]
656  ],
657  'foo/bar/baz',
658  $testObject,
659  [
660  'foo' => [
661  'bar' => [
662  'baz' => $testObject
663  ]
664  ]
665  ]
666  ],
667  'multi keys in array' => [
668  [
669  'foo' => [
670  'bar' => [
671  'baz' => 'value'
672  ],
673  'bar2' => [
674  'baz' => 'value'
675  ]
676  ]
677  ],
678  'foo/bar2/baz',
679  'newValue',
680  [
681  'foo' => [
682  'bar' => [
683  'baz' => 'value'
684  ],
685  'bar2' => [
686  'baz' => 'newValue'
687  ]
688  ]
689  ]
690  ]
691  ];
692  }
693 
702  public function setValueByPathSetsCorrectValue(array $array, $path, $value, $expectedResult)
703  {
704  $this->assertEquals(
705  $expectedResult,
706  ArrayUtility::setValueByPath($array, $path, $value)
707  );
708  }
709 
710  /**********************
711  /* Tests concerning removeByPath
712  ***********************/
713 
718  {
719  $this->expectException(\RuntimeException::class);
720  $this->expectExceptionCode(1371757718);
721 
723  }
724 
729  {
730  $this->expectException(\RuntimeException::class);
731  $this->expectExceptionCode(1371757719);
732 
733  ArrayUtility::removeByPath([], ['foo']);
734  }
735 
740  {
741  $inputArray = [
742  'foo' => [
743  'bar' => 42,
744  ]
745  ];
746 
747  $this->expectException(\RuntimeException::class);
748  $this->expectExceptionCode(1371757720);
749 
750  ArrayUtility::removeByPath($inputArray, 'foo//bar');
751  }
752 
757  {
758  $inputArray = [
759  'foo' => ['bar']
760  ];
761 
762  $this->assertSame(['foo' => []], ArrayUtility::removeByPath($inputArray, 'foo/0'));
763  }
764 
769  {
770  $inputArray = ['bar'];
771 
772  $this->assertSame([], ArrayUtility::removeByPath($inputArray, '0'));
773  }
774 
779  {
780  $inputArray = [
781  'foo' => [
782  'bar' => 42,
783  ]
784  ];
785 
786  $this->expectException(\RuntimeException::class);
787  $this->expectExceptionCode(1371758436);
788 
789  ArrayUtility::removeByPath($inputArray, 'foo/baz');
790  }
791 
796  {
797  $inputArray = [
798  'foo' => [
799  'toRemove' => 42,
800  'keep' => 23
801  ],
802  ];
803  $path = 'foo.toRemove';
804  $expected = [
805  'foo' => [
806  'keep' => 23,
807  ],
808  ];
809  $this->assertEquals(
810  $expected,
811  ArrayUtility::removeByPath($inputArray, $path, '.')
812  );
813  }
814 
819  {
820  return [
821  'single value' => [
822  [
823  'foo' => [
824  'toRemove' => 42,
825  'keep' => 23
826  ],
827  ],
828  'foo/toRemove',
829  [
830  'foo' => [
831  'keep' => 23,
832  ],
833  ],
834  ],
835  'whole array' => [
836  [
837  'foo' => [
838  'bar' => 42
839  ],
840  ],
841  'foo',
842  [],
843  ],
844  'sub array' => [
845  [
846  'foo' => [
847  'keep' => 23,
848  'toRemove' => [
849  'foo' => 'bar',
850  ],
851  ],
852  ],
853  'foo/toRemove',
854  [
855  'foo' => [
856  'keep' => 23,
857  ],
858  ],
859  ],
860  ];
861  }
862 
870  public function removeByPathRemovesCorrectPath(array $array, $path, $expectedResult)
871  {
872  $this->assertEquals(
873  $expectedResult,
874  ArrayUtility::removeByPath($array, $path)
875  );
876  }
877 
879  // Tests concerning sortByKeyRecursive
881 
885  {
886  $unsortedArray = [
887  'z' => null,
888  'a' => null,
889  'd' => [
890  'c' => null,
891  'b' => null,
892  'd' => null,
893  'a' => null
894  ]
895  ];
896  $expectedResult = [
897  'a' => null,
898  'd' => [
899  'a' => null,
900  'b' => null,
901  'c' => null,
902  'd' => null
903  ],
904  'z' => null
905  ];
906  $this->assertSame($expectedResult, ArrayUtility::sortByKeyRecursive($unsortedArray));
907  }
908 
910  // Tests concerning sortArraysByKey
912 
916  {
917  return [
918  'assoc array index' => [
919  [
920  '22' => [
921  'uid' => '22',
922  'title' => 'c',
923  'dummy' => 2
924  ],
925  '24' => [
926  'uid' => '24',
927  'title' => 'a',
928  'dummy' => 3
929  ],
930  '23' => [
931  'uid' => '23',
932  'title' => 'b',
933  'dummy' => 4
934  ],
935  ],
936  'title',
937  true,
938  [
939  '24' => [
940  'uid' => '24',
941  'title' => 'a',
942  'dummy' => 3
943  ],
944  '23' => [
945  'uid' => '23',
946  'title' => 'b',
947  'dummy' => 4
948  ],
949  '22' => [
950  'uid' => '22',
951  'title' => 'c',
952  'dummy' => 2
953  ],
954  ],
955  ],
956  'numeric array index' => [
957  [
958  22 => [
959  'uid' => '22',
960  'title' => 'c',
961  'dummy' => 2
962  ],
963  24 => [
964  'uid' => '24',
965  'title' => 'a',
966  'dummy' => 3
967  ],
968  23 => [
969  'uid' => '23',
970  'title' => 'b',
971  'dummy' => 4
972  ],
973  ],
974  'title',
975  true,
976  [
977  24 => [
978  'uid' => '24',
979  'title' => 'a',
980  'dummy' => 3
981  ],
982  23 => [
983  'uid' => '23',
984  'title' => 'b',
985  'dummy' => 4
986  ],
987  22 => [
988  'uid' => '22',
989  'title' => 'c',
990  'dummy' => 2
991  ],
992  ],
993  ],
994  'numeric array index DESC' => [
995  [
996  23 => [
997  'uid' => '23',
998  'title' => 'b',
999  'dummy' => 4
1000  ],
1001  22 => [
1002  'uid' => '22',
1003  'title' => 'c',
1004  'dummy' => 2
1005  ],
1006  24 => [
1007  'uid' => '24',
1008  'title' => 'a',
1009  'dummy' => 3
1010  ],
1011  ],
1012  'title',
1013  false,
1014  [
1015  22 => [
1016  'uid' => '22',
1017  'title' => 'c',
1018  'dummy' => 2
1019  ],
1020  23 => [
1021  'uid' => '23',
1022  'title' => 'b',
1023  'dummy' => 4
1024  ],
1025  24 => [
1026  'uid' => '24',
1027  'title' => 'a',
1028  'dummy' => 3
1029  ],
1030  ],
1031  ],
1032  ];
1033  }
1034 
1043  public function sortArraysByKeyCheckIfSortingIsCorrect(array $array, $key, $ascending, $expectedResult)
1044  {
1045  $sortedArray = ArrayUtility::sortArraysByKey($array, $key, $ascending);
1046  $this->assertSame($expectedResult, $sortedArray);
1047  }
1048 
1053  {
1054  $this->expectException(\RuntimeException::class);
1055  $this->expectExceptionCode(1373727309);
1056 
1057  ArrayUtility::sortArraysByKey([['a'], ['a']], 'dummy');
1058  }
1059 
1061  // Tests concerning arrayExport
1063 
1067  {
1068  $array = [
1069  'foo' => [
1070  'bar' => 42,
1071  'bar2' => [
1072  'baz' => 'val\'ue',
1073  'baz2' => true,
1074  'baz3' => false,
1075  'baz4' => []
1076  ]
1077  ],
1078  'baz' => 23,
1079  'foobar' => null,
1080  'qux' => 0.1,
1081  'qux2' => 0.000000001,
1082  ];
1083  $expected =
1084  '[' . LF .
1085  ' \'foo\' => [' . LF .
1086  ' \'bar\' => 42,' . LF .
1087  ' \'bar2\' => [' . LF .
1088  ' \'baz\' => \'val\\\'ue\',' . LF .
1089  ' \'baz2\' => true,' . LF .
1090  ' \'baz3\' => false,' . LF .
1091  ' \'baz4\' => [],' . LF .
1092  ' ],' . LF .
1093  ' ],' . LF .
1094  ' \'baz\' => 23,' . LF .
1095  ' \'foobar\' => null,' . LF .
1096  ' \'qux\' => 0.1,' . LF .
1097  ' \'qux2\' => 1.0E-9,' . LF .
1098  ']';
1099  $this->assertSame($expected, ArrayUtility::arrayExport($array));
1100  }
1101 
1106  {
1107  $array = [
1108  'foo' => [
1109  'bar' => new \stdClass()
1110  ]
1111  ];
1112 
1113  $this->expectException(\RuntimeException::class);
1114  $this->expectExceptionCode(1342294987);
1115 
1116  ArrayUtility::arrayExport($array);
1117  }
1118 
1123  {
1124  $array = [
1125  'foo' => 'string key',
1126  23 => 'integer key',
1127  '42' => 'string key representing integer'
1128  ];
1129  $expected =
1130  '[' . LF .
1131  ' \'foo\' => \'string key\',' . LF .
1132  ' 23 => \'integer key\',' . LF .
1133  ' 42 => \'string key representing integer\',' . LF .
1134  ']';
1135  $this->assertSame($expected, ArrayUtility::arrayExport($array));
1136  }
1137 
1142  {
1143  $array = [
1144  0 => 'zero',
1145  1 => 'one',
1146  2 => 'two'
1147  ];
1148  $expected =
1149  '[' . LF .
1150  ' \'zero\',' . LF .
1151  ' \'one\',' . LF .
1152  ' \'two\',' . LF .
1153  ']';
1154  $this->assertSame($expected, ArrayUtility::arrayExport($array));
1155  }
1156 
1161  {
1162  $array = [
1163  0 => 'zero',
1164  1 => 'one',
1165  3 => 'three',
1166  4 => 'four'
1167  ];
1168  $expected =
1169  '[' . LF .
1170  ' 0 => \'zero\',' . LF .
1171  ' 1 => \'one\',' . LF .
1172  ' 3 => \'three\',' . LF .
1173  ' 4 => \'four\',' . LF .
1174  ']';
1175  $this->assertSame($expected, ArrayUtility::arrayExport($array));
1176  }
1177 
1179  // Tests concerning flatten
1181 
1186  {
1187  return [
1188  'plain array' => [
1189  [
1190  'first' => 1,
1191  'second' => 2
1192  ],
1193  [
1194  'first' => 1,
1195  'second' => 2
1196  ]
1197  ],
1198  'plain array with faulty dots' => [
1199  [
1200  'first.' => 1,
1201  'second.' => 2
1202  ],
1203  [
1204  'first' => 1,
1205  'second' => 2
1206  ]
1207  ],
1208  'nested array of 2 levels' => [
1209  [
1210  'first.' => [
1211  'firstSub' => 1
1212  ],
1213  'second.' => [
1214  'secondSub' => 2
1215  ]
1216  ],
1217  [
1218  'first.firstSub' => 1,
1219  'second.secondSub' => 2
1220  ]
1221  ],
1222  'nested array of 2 levels with faulty dots' => [
1223  [
1224  'first.' => [
1225  'firstSub.' => 1
1226  ],
1227  'second.' => [
1228  'secondSub.' => 2
1229  ]
1230  ],
1231  [
1232  'first.firstSub' => 1,
1233  'second.secondSub' => 2
1234  ]
1235  ],
1236  'nested array of 3 levels' => [
1237  [
1238  'first.' => [
1239  'firstSub.' => [
1240  'firstSubSub' => 1
1241  ]
1242  ],
1243  'second.' => [
1244  'secondSub.' => [
1245  'secondSubSub' => 2
1246  ]
1247  ]
1248  ],
1249  [
1250  'first.firstSub.firstSubSub' => 1,
1251  'second.secondSub.secondSubSub' => 2
1252  ]
1253  ],
1254  'nested array of 3 levels with faulty dots' => [
1255  [
1256  'first.' => [
1257  'firstSub.' => [
1258  'firstSubSub.' => 1
1259  ]
1260  ],
1261  'second.' => [
1262  'secondSub.' => [
1263  'secondSubSub.' => 2
1264  ]
1265  ]
1266  ],
1267  [
1268  'first.firstSub.firstSubSub' => 1,
1269  'second.secondSub.secondSubSub' => 2
1270  ]
1271  ]
1272  ];
1273  }
1274 
1281  public function flattenCalculatesExpectedResult(array $array, array $expected)
1282  {
1283  $this->assertEquals($expected, ArrayUtility::flatten($array));
1284  }
1285 
1287  // Tests concerning intersectRecursive
1289 
1294  {
1295  $sameObject = new \stdClass();
1296  return [
1297  // array($source, $mask, $expected)
1298  'empty array is returned if source is empty array' => [
1299  [],
1300  [
1301  'foo' => 'bar',
1302  ],
1303  [],
1304  ],
1305  'empty array is returned if mask is empty' => [
1306  [
1307  'foo' => 'bar',
1308  ],
1309  [],
1310  [],
1311  ],
1312  'key is kept on first level if exists in mask' => [
1313  [
1314  'foo' => 42,
1315  ],
1316  [
1317  'foo' => 42,
1318  ],
1319  [
1320  'foo' => 42,
1321  ],
1322  ],
1323  'value of key in source is kept if mask has different value' => [
1324  [
1325  'foo' => 42,
1326  ],
1327  [
1328  'foo' => new \stdClass(),
1329  ],
1330  [
1331  'foo' => 42,
1332  ],
1333  ],
1334  'key is kept on first level if according mask value is NULL' => [
1335  [
1336  'foo' => 42,
1337  ],
1338  [
1339  'foo' => null,
1340  ],
1341  [
1342  'foo' => 42,
1343  ],
1344  ],
1345  'null in source value is kept' => [
1346  [
1347  'foo' => null,
1348  ],
1349  [
1350  'foo' => 'bar',
1351  ],
1352  [
1353  'foo' => null,
1354  ]
1355  ],
1356  'mask does not add new keys' => [
1357  [
1358  'foo' => 42,
1359  ],
1360  [
1361  'foo' => 23,
1362  'bar' => [
1363  4711
1364  ],
1365  ],
1366  [
1367  'foo' => 42,
1368  ],
1369  ],
1370  'mask does not overwrite simple values with arrays' => [
1371  [
1372  'foo' => 42,
1373  ],
1374  [
1375  'foo' => [
1376  'bar' => 23,
1377  ],
1378  ],
1379  [
1380  'foo' => 42,
1381  ],
1382  ],
1383  'key is kept on first level if according mask value is array' => [
1384  [
1385  'foo' => 42,
1386  ],
1387  [
1388  'foo' => [
1389  'bar' => 23
1390  ],
1391  ],
1392  [
1393  'foo' => 42,
1394  ],
1395  ],
1396  'full array is kept if value is array and mask value is simple type' => [
1397  [
1398  'foo' => [
1399  'bar' => 23
1400  ],
1401  ],
1402  [
1403  'foo' => 42,
1404  ],
1405  [
1406  'foo' => [
1407  'bar' => 23
1408  ],
1409  ],
1410  ],
1411  'key handling is type agnostic' => [
1412  [
1413  42 => 'foo',
1414  ],
1415  [
1416  '42' => 'bar',
1417  ],
1418  [
1419  42 => 'foo',
1420  ],
1421  ],
1422  'value is same if value is object' => [
1423  [
1424  'foo' => $sameObject,
1425  ],
1426  [
1427  'foo' => 'something',
1428  ],
1429  [
1430  'foo' => $sameObject,
1431  ],
1432  ],
1433  'mask does not add simple value to result if key does not exist in source' => [
1434  [
1435  'foo' => '42',
1436  ],
1437  [
1438  'foo' => '42',
1439  'bar' => 23
1440  ],
1441  [
1442  'foo' => '42',
1443  ],
1444  ],
1445  'array of source is kept if value of mask key exists but is no array' => [
1446  [
1447  'foo' => '42',
1448  'bar' => [
1449  'baz' => 23
1450  ],
1451  ],
1452  [
1453  'foo' => 'value is not significant',
1454  'bar' => null,
1455  ],
1456  [
1457  'foo' => '42',
1458  'bar' => [
1459  'baz' => 23
1460  ],
1461  ],
1462  ],
1463  'sub arrays are kept if mask has according sub array key and is similar array' => [
1464  [
1465  'first1' => 42,
1466  'first2' => [
1467  'second1' => 23,
1468  'second2' => 4711,
1469  ],
1470  ],
1471  [
1472  'first1' => 42,
1473  'first2' => [
1474  'second1' => 'exists but different',
1475  ],
1476  ],
1477  [
1478  'first1' => 42,
1479  'first2' => [
1480  'second1' => 23,
1481  ],
1482  ],
1483  ],
1484  ];
1485  }
1486 
1494  public function intersectRecursiveCalculatesExpectedResult(array $source, array $mask, array $expected)
1495  {
1496  $this->assertSame($expected, ArrayUtility::intersectRecursive($source, $mask));
1497  }
1498 
1500  // Tests concerning renumberKeysToAvoidLeapsIfKeysAreAllNumeric
1502 
1506  {
1507  return [
1508  'empty array is returned if source is empty array' => [
1509  [],
1510  []
1511  ],
1512  'returns self if array is already numerically keyed' => [
1513  [1, 2, 3],
1514  [1, 2, 3]
1515  ],
1516  'returns correctly if keys are numeric, but contains a leap' => [
1517  [0 => 'One', 1 => 'Two', 3 => 'Three'],
1518  [0 => 'One', 1 => 'Two', 2 => 'Three'],
1519  ],
1520  'returns correctly even though keys are strings but still numeric' => [
1521  ['0' => 'One', '1' => 'Two', '3' => 'Three'],
1522  [0 => 'One', 1 => 'Two', 2 => 'Three'],
1523  ],
1524  'returns correctly if just a single keys is not numeric' => [
1525  [0 => 'Zero', '1' => 'One', 'Two' => 'Two'],
1526  [0 => 'Zero', '1' => 'One', 'Two' => 'Two'],
1527  ],
1528  'return self with nested numerically keyed array' => [
1529  [
1530  'One',
1531  'Two',
1532  'Three',
1533  [
1534  'sub.One',
1535  'sub.Two',
1536  ]
1537  ],
1538  [
1539  'One',
1540  'Two',
1541  'Three',
1542  [
1543  'sub.One',
1544  'sub.Two',
1545  ]
1546  ]
1547  ],
1548  'returns correctly with nested numerically keyed array with leaps' => [
1549  [
1550  'One',
1551  'Two',
1552  'Three',
1553  [
1554  0 => 'sub.One',
1555  2 => 'sub.Two',
1556  ]
1557  ],
1558  [
1559  'One',
1560  'Two',
1561  'Three',
1562  [
1563  'sub.One',
1564  'sub.Two',
1565  ]
1566  ]
1567  ],
1568  'returns correctly with nested string-keyed array' => [
1569  [
1570  'One',
1571  'Two',
1572  'Three',
1573  [
1574  'one' => 'sub.One',
1575  'two' => 'sub.Two',
1576  ]
1577  ],
1578  [
1579  'One',
1580  'Two',
1581  'Three',
1582  [
1583  'one' => 'sub.One',
1584  'two' => 'sub.Two',
1585  ]
1586  ]
1587  ],
1588  'returns correctly with deeply nested arrays' => [
1589  [
1590  'One',
1591  'Two',
1592  [
1593  'one' => 1,
1594  'two' => 2,
1595  'three' => [
1596  2 => 'SubSubOne',
1597  5 => 'SubSubTwo',
1598  9 => [0, 1, 2],
1599  []
1600  ]
1601  ]
1602  ],
1603  [
1604  'One',
1605  'Two',
1606  [
1607  'one' => 1,
1608  'two' => 2,
1609  'three' => [
1610  'SubSubOne',
1611  'SubSubTwo',
1612  [0, 1, 2],
1613  []
1614  ]
1615  ]
1616  ]
1617  ]
1618  ];
1619  }
1620 
1627  public function renumberKeysToAvoidLeapsIfKeysAreAllNumericReturnsExpectedOrder(array $inputArray, array $expected)
1628  {
1629  $this->assertEquals($expected, ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($inputArray));
1630  }
1631 
1636  {
1637  return [
1638  'Override array can reset string to array' => [
1639  [
1640  'first' => [
1641  'second' => 'foo',
1642  ],
1643  ],
1644  [
1645  'first' => [
1646  'second' => ['third' => 'bar'],
1647  ],
1648  ],
1649  true,
1650  true,
1651  true,
1652  [
1653  'first' => [
1654  'second' => ['third' => 'bar'],
1655  ],
1656  ],
1657  ],
1658  'Override array does not reset array to string (weird!)' => [
1659  [
1660  'first' => [],
1661  ],
1662  [
1663  'first' => 'foo',
1664  ],
1665  true,
1666  true,
1667  true,
1668  [
1669  'first' => [], // This is rather unexpected, naive expectation: first => 'foo'
1670  ],
1671  ],
1672  'Override array does override string with null' => [
1673  [
1674  'first' => 'foo',
1675  ],
1676  [
1677  'first' => null,
1678  ],
1679  true,
1680  true,
1681  true,
1682  [
1683  'first' => null,
1684  ],
1685  ],
1686  'Override array does override null with string' => [
1687  [
1688  'first' => null,
1689  ],
1690  [
1691  'first' => 'foo',
1692  ],
1693  true,
1694  true,
1695  true,
1696  [
1697  'first' => 'foo',
1698  ],
1699  ],
1700  'Override array does override null with empty string' => [
1701  [
1702  'first' => null,
1703  ],
1704  [
1705  'first' => '',
1706  ],
1707  true,
1708  true,
1709  true,
1710  [
1711  'first' => '',
1712  ],
1713  ],
1714  'Override array does not override string with NULL if requested' => [
1715  [
1716  'first' => 'foo',
1717  ],
1718  [
1719  'first' => null,
1720  ],
1721  true,
1722  false, // no include empty values
1723  true,
1724  [
1725  'first' => 'foo',
1726  ],
1727  ],
1728  'Override array does override null with null' => [
1729  [
1730  'first' => null,
1731  ],
1732  [
1733  'first' => null,
1734  ],
1735  true,
1736  true,
1737  true,
1738  [
1739  'first' => '',
1740  ],
1741  ],
1742  'Override array can __UNSET values' => [
1743  [
1744  'first' => [
1745  'second' => 'second',
1746  'third' => 'third',
1747  ],
1748  'fifth' => [],
1749  ],
1750  [
1751  'first' => [
1752  'second' => 'overrule',
1753  'third' => '__UNSET',
1754  'fourth' => 'overrile',
1755  ],
1756  'fifth' => '__UNSET',
1757  ],
1758  true,
1759  true,
1760  true,
1761  [
1762  'first' => [
1763  'second' => 'overrule',
1764  'fourth' => 'overrile',
1765  ],
1766  ],
1767  ],
1768  'Override can add keys' => [
1769  [
1770  'first' => 'foo',
1771  ],
1772  [
1773  'second' => 'bar',
1774  ],
1775  true,
1776  true,
1777  true,
1778  [
1779  'first' => 'foo',
1780  'second' => 'bar',
1781  ],
1782  ],
1783  'Override does not add key if __UNSET' => [
1784  [
1785  'first' => 'foo',
1786  ],
1787  [
1788  'second' => '__UNSET',
1789  ],
1790  true,
1791  true,
1792  true,
1793  [
1794  'first' => 'foo',
1795  ],
1796  ],
1797  'Override does not add key if not requested' => [
1798  [
1799  'first' => 'foo',
1800  ],
1801  [
1802  'second' => 'bar',
1803  ],
1804  false, // no add keys
1805  true,
1806  true,
1807  [
1808  'first' => 'foo',
1809  ],
1810  ],
1811  'Override does not add key if not requested with add include empty values' => [
1812  [
1813  'first' => 'foo',
1814  ],
1815  [
1816  'second' => 'bar',
1817  ],
1818  false, // no add keys
1819  false, // no include empty values
1820  true,
1821  [
1822  'first' => 'foo',
1823  ],
1824  ],
1825  'Override does not override string with empty string if requested' => [
1826  [
1827  'first' => 'foo',
1828  ],
1829  [
1830  'first' => '',
1831  ],
1832  true,
1833  false, // no include empty values
1834  true,
1835  [
1836  'first' => 'foo',
1837  ],
1838  ],
1839  'Override array does merge instead of __UNSET if requested (weird!)' => [
1840  [
1841  'first' => [
1842  'second' => 'second',
1843  'third' => 'third',
1844  ],
1845  'fifth' => [],
1846  ],
1847  [
1848  'first' => [
1849  'second' => 'overrule',
1850  'third' => '__UNSET',
1851  'fourth' => 'overrile',
1852  ],
1853  'fifth' => '__UNSET',
1854  ],
1855  true,
1856  true,
1857  false,
1858  [
1859  'first' => [
1860  'second' => 'overrule',
1861  'third' => '__UNSET', // overruled
1862  'fourth' => 'overrile',
1863  ],
1864  'fifth' => [], // not overruled with string here, naive expectation: 'fifth' => '__UNSET'
1865  ],
1866  ],
1867  ];
1868  }
1869 
1880  public function mergeRecursiveWithOverruleCalculatesExpectedResult($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature, $expected)
1881  {
1882  ArrayUtility::mergeRecursiveWithOverrule($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature);
1883  $this->assertEquals($expected, $input1);
1884  }
1885 
1887  // Tests concerning inArray
1889 
1896  public function inArrayChecksStringExistenceWithinArray($array, $item, $expected)
1897  {
1898  $this->assertEquals($expected, ArrayUtility::inArray($array, $item));
1899  }
1900 
1906  public function inArrayDataProvider()
1907  {
1908  return [
1909  'Empty array' => [[], 'search', false],
1910  'One item array no match' => [['one'], 'two', false],
1911  'One item array match' => [['one'], 'one', true],
1912  'Multiple items array no match' => [['one', 2, 'three', 4], 'four', false],
1913  'Multiple items array match' => [['one', 2, 'three', 4], 'three', true],
1914  'Integer search items can match string values' => [['0', '1', '2'], 1, true],
1915  'Search item is not casted to integer for a match' => [[4], '4a', false],
1916  'Empty item won\'t match - in contrast to the php-builtin ' => [[0, 1, 2], '', false]
1917  ];
1918  }
1919 
1921  // Tests concerning removeArrayEntryByValue
1923 
1927  {
1928  $inputArray = [
1929  '0' => 'test1',
1930  '1' => 'test2',
1931  '2' => 'test3',
1932  '3' => 'test2'
1933  ];
1934  $compareValue = 'test2';
1935  $expectedResult = [
1936  '0' => 'test1',
1937  '2' => 'test3'
1938  ];
1939  $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1940  $this->assertEquals($expectedResult, $actualResult);
1941  }
1942 
1947  {
1948  $inputArray = [
1949  '0' => 'foo',
1950  '1' => [
1951  '10' => 'bar'
1952  ],
1953  '2' => 'bar'
1954  ];
1955  $compareValue = 'bar';
1956  $expectedResult = [
1957  '0' => 'foo',
1958  '1' => []
1959  ];
1960  $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1961  $this->assertEquals($expectedResult, $actualResult);
1962  }
1963 
1968  {
1969  $inputArray = [
1970  '0' => 'foo',
1971  '1' => '',
1972  '2' => 'bar'
1973  ];
1974  $compareValue = '';
1975  $expectedResult = [
1976  '0' => 'foo',
1977  '2' => 'bar'
1978  ];
1979  $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1980  $this->assertEquals($expectedResult, $actualResult);
1981  }
1982 
1984  // Tests concerning keepItemsInArray
1986 
1993  public function keepItemsInArrayWorksWithOneArgument($search, $array, $expected)
1994  {
1995  $this->assertEquals($expected, ArrayUtility::keepItemsInArray($array, $search));
1996  }
1997 
2004  {
2005  $array = [
2006  0 => 0,
2007  'one' => 'one',
2008  'two' => 'two',
2009  'three' => 'three'
2010  ];
2011  return [
2012  'Empty argument will match "all" elements' => [null, $array, $array],
2013  'No match' => ['four', $array, []],
2014  'One match' => ['two', $array, ['two' => 'two']],
2015  'Multiple matches' => ['two,one', $array, ['one' => 'one', 'two' => 'two']],
2016  'Argument can be an array' => [['three'], $array, ['three' => 'three']]
2017  ];
2018  }
2019 
2028  {
2029  $array = [
2030  'aa' => ['first', 'second'],
2031  'bb' => ['third', 'fourth'],
2032  'cc' => ['fifth', 'sixth']
2033  ];
2034  $expected = ['bb' => ['third', 'fourth']];
2035  $keepItems = 'third';
2037  $array,
2038  $keepItems,
2039  function ($value) {
2040  return $value[0];
2041  }
2042  );
2043  $this->assertEquals($expected, $match);
2044  }
2045 
2047  // Tests concerning remapArrayKeys
2049 
2053  {
2054  $array = [
2055  'one' => 'one',
2056  'two' => 'two',
2057  'three' => 'three'
2058  ];
2059  $keyMapping = [
2060  'one' => '1',
2061  'two' => '2'
2062  ];
2063  $expected = [
2064  '1' => 'one',
2065  '2' => 'two',
2066  'three' => 'three'
2067  ];
2068  ArrayUtility::remapArrayKeys($array, $keyMapping);
2069  $this->assertEquals($expected, $array);
2070  }
2071 
2073  // Tests concerning arrayDiffAssocRecursive
2075 
2079  {
2080  $array1 = [
2081  'key1' => 'value1',
2082  'key2' => 'value2',
2083  'key3' => 'value3'
2084  ];
2085  $array2 = [
2086  'key1' => 'value1',
2087  'key3' => 'value3'
2088  ];
2089  $expectedResult = [
2090  'key2' => 'value2'
2091  ];
2092  $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2093  $this->assertEquals($expectedResult, $actualResult);
2094  }
2095 
2100  {
2101  $array1 = [
2102  'key1' => 'value1',
2103  'key2' => [
2104  'key21' => 'value21',
2105  'key22' => 'value22',
2106  'key23' => [
2107  'key231' => 'value231',
2108  'key232' => 'value232'
2109  ]
2110  ]
2111  ];
2112  $array2 = [
2113  'key1' => 'valueDoesNotMatter',
2114  'key2' => [
2115  'key21' => 'value21',
2116  'key23' => [
2117  'key231' => 'value231'
2118  ]
2119  ]
2120  ];
2121  $expectedResult = [
2122  'key2' => [
2123  'key22' => 'value22',
2124  'key23' => [
2125  'key232' => 'value232'
2126  ]
2127  ]
2128  ];
2129  $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2130  $this->assertEquals($expectedResult, $actualResult);
2131  }
2132 
2137  {
2138  $array1 = [
2139  'key1' => [
2140  'key11' => 'value11',
2141  'key12' => 'value12'
2142  ],
2143  'key2' => 'value2',
2144  'key3' => 'value3'
2145  ];
2146  $array2 = [
2147  'key1' => 'value1',
2148  'key2' => [
2149  'key21' => 'valueDoesNotMatter'
2150  ]
2151  ];
2152  $expectedResult = [
2153  'key3' => 'value3'
2154  ];
2155  $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2156  $this->assertEquals($expectedResult, $actualResult);
2157  }
2158 
2163  {
2164  $array1 = [
2165  'key1' => [
2166  'key11' => 'value11',
2167  'key12' => 'value12'
2168  ],
2169  'key2' => 'value2',
2170  'key3' => 'value3'
2171  ];
2172  $array2 = [
2173  'key1' => [
2174  'key11' => 'valueDoesNotMatter',
2175  'key12' => 'value12'
2176  ],
2177  'key2' => 'value2',
2178  'key3' => 'value3'
2179  ];
2180  $expectedResult = [];
2181  $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2182  $this->assertEquals($expectedResult, $actualResult);
2183  }
2184 
2186  // Tests concerning naturalKeySortRecursive
2188 
2193  {
2194  $testArray = [
2195  'bb' => 'bb',
2196  'ab' => 'ab',
2197  '123' => '123',
2198  'aaa' => 'aaa',
2199  'abc' => 'abc',
2200  '23' => '23',
2201  'ba' => 'ba',
2202  'bad' => 'bad',
2203  '2' => '2',
2204  'zap' => 'zap',
2205  '210' => '210'
2206  ];
2207  $expectedResult = [
2208  '2',
2209  '23',
2210  '123',
2211  '210',
2212  'aaa',
2213  'ab',
2214  'abc',
2215  'ba',
2216  'bad',
2217  'bb',
2218  'zap'
2219  ];
2221  $this->assertEquals($expectedResult, array_values($testArray));
2222  }
2223 
2228  {
2229  $testArray = [
2230  '2' => '2',
2231  'bb' => 'bb',
2232  'ab' => 'ab',
2233  '23' => '23',
2234  'aaa' => [
2235  'bb' => 'bb',
2236  'ab' => 'ab',
2237  '123' => '123',
2238  'aaa' => 'aaa',
2239  '2' => '2',
2240  'abc' => 'abc',
2241  'ba' => 'ba',
2242  '23' => '23',
2243  'bad' => [
2244  'bb' => 'bb',
2245  'ab' => 'ab',
2246  '123' => '123',
2247  'aaa' => 'aaa',
2248  'abc' => 'abc',
2249  '23' => '23',
2250  'ba' => 'ba',
2251  'bad' => 'bad',
2252  '2' => '2',
2253  'zap' => 'zap',
2254  '210' => '210'
2255  ],
2256  '210' => '210',
2257  'zap' => 'zap'
2258  ],
2259  'abc' => 'abc',
2260  'ba' => 'ba',
2261  '210' => '210',
2262  'bad' => 'bad',
2263  '123' => '123',
2264  'zap' => 'zap'
2265  ];
2266  $expectedResult = [
2267  '2',
2268  '23',
2269  '123',
2270  '210',
2271  'aaa',
2272  'ab',
2273  'abc',
2274  'ba',
2275  'bad',
2276  'bb',
2277  'zap'
2278  ];
2280  $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa']['bad'])));
2281  $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa'])));
2282  $this->assertEquals($expectedResult, array_values(array_keys($testArray)));
2283  }
2284 
2291  {
2292  return [
2293  'ordered list of plain numeric keys' => [
2294  'input' => [
2295  '10' => 'foo',
2296  '20' => 'bar',
2297  ],
2298  'expected' => [
2299  10,
2300  20,
2301  ],
2302  ],
2303  'unordered list of plain numeric keys' => [
2304  'input' => [
2305  '20' => 'bar',
2306  '10' => 'foo',
2307  ],
2308  'expected' => [
2309  10,
2310  20,
2311  ],
2312  ],
2313  'list of string keys' => [
2314  'input' => [
2315  '10.' => [
2316  'wrap' => 'foo',
2317  ],
2318  '20.' => [
2319  'wrap' => 'bar',
2320  ],
2321  ],
2322  'expected' => [
2323  10,
2324  20,
2325  ],
2326  ],
2327  'list of mixed keys' => [
2328  'input' => [
2329  '10' => 'foo',
2330  '20.' => [
2331  'wrap' => 'bar',
2332  ],
2333  ],
2334  'expected' => [
2335  10,
2336  20,
2337  ],
2338  ],
2339  'list of mixed keys with one not interpreted as integer' => [
2340  'input' => [
2341  '10' => 'foo',
2342  'bla20.' => [
2343  'wrap' => 'bar',
2344  ],
2345  ],
2346  'expected' => [
2347  0,
2348  10,
2349  ],
2350  ],
2351  'list of mixed keys with more than one not interpreted as integer' => [
2352  'input' => [
2353  '10' => 'foo',
2354  'bla20.' => [
2355  'wrap' => 'bar',
2356  ],
2357  'bla21.' => [
2358  'wrap' => 'foobar',
2359  ],
2360  ],
2361  'expected' => [
2362  0,
2363  10,
2364  ],
2365  ],
2366  ];
2367  }
2368 
2377  {
2378  $result = ArrayUtility::filterAndSortByNumericKeys($input, true);
2379  $this->assertEquals($result, $expected);
2380  }
2381 
2388  {
2389  return [
2390  'ordered list of plain numeric keys' => [
2391  'input' => [
2392  '10' => 'foo',
2393  '20' => 'bar',
2394  ],
2395  'expected' => [
2396  10,
2397  20,
2398  ],
2399  ],
2400  'unordered list of plain numeric keys' => [
2401  'input' => [
2402  '20' => 'bar',
2403  '10' => 'foo',
2404  ],
2405  'expected' => [
2406  10,
2407  20,
2408  ],
2409  ],
2410  'list of string keys' => [
2411  'input' => [
2412  '10.' => [
2413  'wrap' => 'foo',
2414  ],
2415  '20.' => [
2416  'wrap' => 'bar',
2417  ],
2418  ],
2419  'expected' => [],
2420  ],
2421  'list of mixed keys' => [
2422  'input' => [
2423  '10' => 'foo',
2424  '20.' => [
2425  'wrap' => 'bar',
2426  ],
2427  ],
2428  'expected' => [
2429  10,
2430  ],
2431  ],
2432  ];
2433  }
2434 
2443  {
2444  $result = ArrayUtility::filterAndSortByNumericKeys($input);
2445  $this->assertEquals($result, $expected);
2446  }
2447 
2454  {
2455  return [
2456  [
2457  [
2458  '20' => 'test1',
2459  '11' => 'test2',
2460  '16' => 'test3',
2461  ],
2462  [
2463  '11' => 'test2',
2464  '16' => 'test3',
2465  '20' => 'test1',
2466  ]
2467  ],
2468  [
2469  [
2470  '20' => 'test1',
2471  '16.5' => 'test2',
2472  '16' => 'test3',
2473  ],
2474  [
2475  '20' => 'test1',
2476  '16.5' => 'test2',
2477  '16' => 'test3',
2478  ]
2479  ],
2480  [
2481  [
2482  '20' => 'test20',
2483  'somestring' => 'teststring',
2484  '16' => 'test16',
2485  ],
2486  [
2487  '20' => 'test20',
2488  'somestring' => 'teststring',
2489  '16' => 'test16',
2490  ]
2491  ],
2492  ];
2493  }
2494 
2503  public function sortArrayWithIntegerKeysSortsNumericArrays(array $arrayToSort, array $expectedArray)
2504  {
2505  $sortedArray = ArrayUtility::sortArrayWithIntegerKeys($arrayToSort);
2506  $this->assertSame($sortedArray, $expectedArray);
2507  }
2508 
2513  {
2514  $this->expectException(\InvalidArgumentException::class);
2515  $this->expectExceptionCode(1325697085);
2516 
2517  $arrayToTest = [
2518  'roger' => '',
2519  'francine' => '',
2520  'stan' => '',
2521  ];
2522 
2523  $allowedArrayKeys = [
2524  'roger',
2525  'francine',
2526  ];
2527 
2528  ArrayUtility::assertAllArrayKeysAreValid($arrayToTest, $allowedArrayKeys);
2529  }
2530 
2535  {
2536  $arrayToTest = [
2537  'roger' => '',
2538  'francine' => '',
2539  'stan' => '',
2540  ];
2541 
2542  $allowedArrayKeys = [
2543  'roger',
2544  'francine',
2545  'stan',
2546  ];
2547 
2548  ArrayUtility::assertAllArrayKeysAreValid($arrayToTest, $allowedArrayKeys);
2549  }
2550 
2555  {
2556  $input = [
2557  20 => 'b',
2558  10 => 'a',
2559  40 => 'd',
2560  30 => 'c',
2561  50 => [
2562  20 => 'a',
2563  10 => 'b',
2564  ],
2565  ];
2566 
2567  $expected = [
2568  10 => 'a',
2569  20 => 'b',
2570  30 => 'c',
2571  40 => 'd',
2572  50 => [
2573  10 => 'b',
2574  20 => 'a',
2575  ],
2576  ];
2577 
2578  $this->assertSame($expected, ArrayUtility::sortArrayWithIntegerKeysRecursive($input));
2579  }
2580 
2585  {
2586  $input = [
2587  'b' => 'b',
2588  10 => 'a',
2589  40 => 'd',
2590  30 => 'c',
2591  ];
2592 
2593  $expected = [
2594  'b' => 'b',
2595  10 => 'a',
2596  40 => 'd',
2597  30 => 'c',
2598  ];
2599 
2600  $this->assertSame($expected, ArrayUtility::sortArrayWithIntegerKeysRecursive($input));
2601  }
2602 
2607  {
2608  $input = [
2609  20 => 'b',
2610  10 => 'a',
2611  40 => 'd',
2612  30 => 'c',
2613  50 => [
2614  20 => 'a',
2615  10 => 'b',
2616  ],
2617  ];
2618 
2619  $expected = [
2620  0 => 'b',
2621  1 => 'a',
2622  2 => 'd',
2623  3 => 'c',
2624  4 => [
2625  0 => 'a',
2626  1 => 'b',
2627  ],
2628  ];
2629 
2630  $this->assertSame($expected, ArrayUtility::reIndexNumericArrayKeysRecursive($input));
2631  }
2632 
2637  {
2638  $input = [
2639  'a' => 'b',
2640  10 => 'a',
2641  40 => 'd',
2642  30 => 'c',
2643  50 => [
2644  20 => 'a',
2645  10 => 'b',
2646  ],
2647  ];
2648 
2649  $expected = [
2650  'a' => 'b',
2651  10 => 'a',
2652  40 => 'd',
2653  30 => 'c',
2654  50 => [
2655  0 => 'a',
2656  1 => 'b',
2657  ],
2658  ];
2659 
2660  $this->assertSame($expected, ArrayUtility::reIndexNumericArrayKeysRecursive($input));
2661  }
2662 
2667  {
2668  $input = [
2669  'a' => 'a',
2670  'b' => [
2671  'c' => null,
2672  'd' => 'd',
2673  ],
2674  ];
2675 
2676  $expected = [
2677  'a' => 'a',
2678  'b' => [
2679  'd' => 'd',
2680  ],
2681  ];
2682 
2683  $this->assertSame($expected, ArrayUtility::removeNullValuesRecursive($input));
2684  }
2685 
2690  {
2691  $input = [
2692  'a' => 'a',
2693  'b' => [
2694  'c' => '<b>i am evil</b>',
2695  'd' => 'd',
2696  ],
2697  ];
2698 
2699  $expected = [
2700  'a' => 'a',
2701  'b' => [
2702  'c' => 'i am evil',
2703  'd' => 'd',
2704  ],
2705  ];
2706 
2707  $this->assertSame($expected, ArrayUtility::stripTagsFromValuesRecursive($input));
2708  }
2709 
2714  {
2715  $testObject = new \stdClass();
2716 
2717  $input = [
2718  'stringWithTags' => '<b>i am evil</b>',
2719  'boolean' => true,
2720  'integer' => 1,
2721  'float' => 1.9,
2722  'object' => $testObject,
2723  'objectWithStringConversion' => new class {
2727  public function __toString()
2728  {
2729  return 'i am evil <b>too</b>';
2730  }
2731  },
2732  ];
2733 
2734  $expected = [
2735  'stringWithTags' => 'i am evil',
2736  'boolean' => true,
2737  'integer' => 1,
2738  'float' => 1.9,
2739  'object' => $testObject,
2740  'objectWithStringConversion' => 'i am evil too',
2741  ];
2742 
2743  $this->assertSame($expected, ArrayUtility::stripTagsFromValuesRecursive($input));
2744  }
2745 
2750  {
2751  $input = [
2752  'a' => 'a',
2753  'b' => [
2754  'c' => 'true',
2755  'd' => 'd',
2756  ],
2757  ];
2758 
2759  $expected = [
2760  'a' => 'a',
2761  'b' => [
2762  'c' => true,
2763  'd' => 'd',
2764  ],
2765  ];
2766 
2767  $this->assertSame($expected, ArrayUtility::convertBooleanStringsToBooleanRecursive($input));
2768  }
2769 }
sortArraysByKeyCheckIfSortingIsCorrect(array $array, $key, $ascending, $expectedResult)
setValueByPathSetsCorrectValue(array $array, $path, $value, $expectedResult)
renumberKeysToAvoidLeapsIfKeysAreAllNumericReturnsExpectedOrder(array $inputArray, array $expected)
static filterByValueRecursive($needle='', array $haystack=[])
static setValueByPath(array $array, $path, $value, $delimiter='/')
static getValueByPath(array $array, $path, $delimiter='/')
inArrayChecksStringExistenceWithinArray($array, $item, $expected)
static sortArrayWithIntegerKeysRecursive(array $array)
static convertBooleanStringsToBooleanRecursive(array $array)
static intersectRecursive(array $source, array $mask=[])
intersectRecursiveCalculatesExpectedResult(array $source, array $mask, array $expected)
static assertAllArrayKeysAreValid(array $arrayToTest, array $allowedArrayKeys)
filterAndSortByNumericKeysBehavesCorrectlyForAcceptAnyKeysIsTrue($input, $expected)
static reIndexNumericArrayKeysRecursive(array $array)
static flatten(array $array, $prefix='')
static remapArrayKeys(array &$array, array $mappingTable)
keepItemsInArrayWorksWithOneArgument($search, $array, $expected)
removeByPathRemovesCorrectPath(array $array, $path, $expectedResult)
getValueByPathGetsCorrectValue(array $array, $path, $expectedResult)
static arrayExport(array $array=[], $level=0)
static naturalKeySortRecursive(array &$array)
static inArray(array $in_array, $item)
static removeByPath(array $array, $path, $delimiter='/')
static sortByKeyRecursive(array $array)
static renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array=[], $level=0)
mergeRecursiveWithOverruleCalculatesExpectedResult($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature, $expected)
static isValidPath(array $array, $path, $delimiter='/')
static filterAndSortByNumericKeys($setupArr, $acceptAnyKeys=false)
static removeArrayEntryByValue(array $array, $cmpValue)
static keepItemsInArray(array $array, $keepItems, $getValueFunc=null)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
filterAndSortByNumericKeysBehavesCorrectlyForAcceptAnyKeysIsFalse($input, $expected)
sortArrayWithIntegerKeysSortsNumericArrays(array $arrayToSort, array $expectedArray)
static sortArrayWithIntegerKeys(array $array)
static removeNullValuesRecursive(array $array)
flattenCalculatesExpectedResult(array $array, array $expected)
static sortArraysByKey(array $arrays, $key, $ascending=true)
static arrayDiffAssocRecursive(array $array1, array $array2)
filterByValueRecursiveCorrectlyFiltersArray($needle, $haystack, $expectedResult)
static stripTagsFromValuesRecursive(array $array)