[BUGFIX] Add missing namespace parts
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Utility / ArrayUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\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 /**
18 * Test case
19 */
20 class ArrayUtilityTest extends \TYPO3\Components\TestingFramework\Core\Unit\UnitTestCase
21 {
22 /**
23 * @test
24 */
25 public function containsMultipleTypesReturnsFalseOnEmptyArray()
26 {
27 $this->assertFalse(\TYPO3\CMS\Extbase\Utility\ArrayUtility::containsMultipleTypes([]));
28 }
29
30 /**
31 * @test
32 */
33 public function containsMultipleTypesReturnsFalseOnArrayWithIntegers()
34 {
35 $this->assertFalse(\TYPO3\CMS\Extbase\Utility\ArrayUtility::containsMultipleTypes([1, 2, 3]));
36 }
37
38 /**
39 * @test
40 */
41 public function containsMultipleTypesReturnsFalseOnArrayWithObjects()
42 {
43 $this->assertFalse(\TYPO3\CMS\Extbase\Utility\ArrayUtility::containsMultipleTypes([new \stdClass(), new \stdClass(), new \stdClass()]));
44 }
45
46 /**
47 * @test
48 */
49 public function containsMultipleTypesReturnsTrueOnMixedArray()
50 {
51 $this->assertTrue(\TYPO3\CMS\Extbase\Utility\ArrayUtility::containsMultipleTypes([1, 'string', 1.25, new \stdClass()]));
52 }
53
54 /**
55 * @test
56 */
57 public function getValueByPathReturnsTheValueOfANestedArrayByFollowingTheGivenSimplePath()
58 {
59 $array = ['Foo' => 'the value'];
60 $this->assertSame('the value', \TYPO3\CMS\Extbase\Utility\ArrayUtility::getValueByPath($array, ['Foo']));
61 }
62
63 /**
64 * @test
65 */
66 public function getValueByPathReturnsTheValueOfANestedArrayByFollowingTheGivenPath()
67 {
68 $array = ['Foo' => ['Bar' => ['Baz' => [2 => 'the value']]]];
69 $this->assertSame('the value', \TYPO3\CMS\Extbase\Utility\ArrayUtility::getValueByPath($array, ['Foo', 'Bar', 'Baz', 2]));
70 }
71
72 /**
73 * @test
74 */
75 public function getValueByPathReturnsTheValueOfANestedArrayByFollowingTheGivenPathIfPathIsString()
76 {
77 $path = 'Foo.Bar.Baz.2';
78 $array = ['Foo' => ['Bar' => ['Baz' => [2 => 'the value']]]];
79 $expectedResult = 'the value';
80 $actualResult = \TYPO3\CMS\Extbase\Utility\ArrayUtility::getValueByPath($array, $path);
81 $this->assertSame($expectedResult, $actualResult);
82 }
83
84 /**
85 * @test
86 */
87 public function getValueByPathThrowsExceptionIfPathIsNoArrayOrString()
88 {
89 $this->expectException(\InvalidArgumentException::class);
90 $this->expectExceptionCode(1304950007);
91 $array = ['Foo' => ['Bar' => ['Baz' => [2 => 'the value']]]];
92 \TYPO3\CMS\Extbase\Utility\ArrayUtility::getValueByPath($array, null);
93 }
94
95 /**
96 * @test
97 */
98 public function getValueByPathReturnsNullIfTheSegementsOfThePathDontExist()
99 {
100 $array = ['Foo' => ['Bar' => ['Baz' => [2 => 'the value']]]];
101 $this->assertNull(\TYPO3\CMS\Extbase\Utility\ArrayUtility::getValueByPath($array, ['Foo', 'Bar', 'Bax', 2]));
102 }
103
104 /**
105 * @test
106 */
107 public function getValueByPathReturnsNullIfThePathHasMoreSegmentsThanTheGivenArray()
108 {
109 $array = ['Foo' => ['Bar' => ['Baz' => 'the value']]];
110 $this->assertNull(\TYPO3\CMS\Extbase\Utility\ArrayUtility::getValueByPath($array, ['Foo', 'Bar', 'Baz', 'Bux']));
111 }
112
113 /**
114 * @test
115 */
116 public function convertObjectToArrayConvertsNestedObjectsToArray()
117 {
118 $object = new \stdClass();
119 $object->a = 'v';
120 $object->b = new \stdClass();
121 $object->b->c = 'w';
122 $object->d = ['i' => 'foo', 'j' => 12, 'k' => true, 'l' => new \stdClass()];
123 $array = \TYPO3\CMS\Extbase\Utility\ArrayUtility::convertObjectToArray($object);
124 $expected = [
125 'a' => 'v',
126 'b' => [
127 'c' => 'w'
128 ],
129 'd' => [
130 'i' => 'foo',
131 'j' => 12,
132 'k' => true,
133 'l' => []
134 ]
135 ];
136 $this->assertSame($expected, $array);
137 }
138
139 /**
140 * @test
141 */
142 public function setValueByPathSetsValueRecursivelyIfPathIsArray()
143 {
144 $array = [];
145 $path = ['foo', 'bar', 'baz'];
146 $expectedValue = ['foo' => ['bar' => ['baz' => 'The Value']]];
147 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($array, $path, 'The Value');
148 $this->assertSame($expectedValue, $actualValue);
149 }
150
151 /**
152 * @test
153 */
154 public function setValueByPathSetsValueRecursivelyIfPathIsString()
155 {
156 $array = [];
157 $path = 'foo.bar.baz';
158 $expectedValue = ['foo' => ['bar' => ['baz' => 'The Value']]];
159 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($array, $path, 'The Value');
160 $this->assertSame($expectedValue, $actualValue);
161 }
162
163 /**
164 * @test
165 */
166 public function setValueByPathRecursivelyMergesAnArray()
167 {
168 $array = ['foo' => ['bar' => 'should be overriden'], 'bar' => 'Baz'];
169 $path = ['foo', 'bar', 'baz'];
170 $expectedValue = ['foo' => ['bar' => ['baz' => 'The Value']], 'bar' => 'Baz'];
171 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($array, $path, 'The Value');
172 $this->assertSame($expectedValue, $actualValue);
173 }
174
175 /**
176 * @test
177 */
178 public function setValueByPathThrowsExceptionIfPathIsNoArrayOrString()
179 {
180 $this->expectException(\InvalidArgumentException::class);
181 $this->expectExceptionCode(1305111499);
182 $array = ['Foo' => ['Bar' => ['Baz' => [2 => 'the value']]]];
183 \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($array, null, 'Some Value');
184 }
185
186 /**
187 * @test
188 */
189 public function setValueByPathThrowsExceptionIfSubjectIsNoArray()
190 {
191 $this->expectException(\InvalidArgumentException::class);
192 $this->expectExceptionCode(1306424308);
193 $subject = 'foobar';
194 \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($subject, 'foo', 'bar');
195 }
196
197 /**
198 * @test
199 */
200 public function setValueByPathThrowsExceptionIfSubjectIsNoArrayAccess()
201 {
202 $this->expectException(\InvalidArgumentException::class);
203 $this->expectExceptionCode(1306424308);
204 $subject = new \stdClass();
205 \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($subject, 'foo', 'bar');
206 }
207
208 /**
209 * @test
210 */
211 public function setValueByLeavesInputArrayUnchanged()
212 {
213 $subject = ($subjectBackup = ['foo' => 'bar']);
214 \TYPO3\CMS\Extbase\Utility\ArrayUtility::setValueByPath($subject, 'foo', 'baz');
215 $this->assertSame($subject, $subjectBackup);
216 }
217
218 /**
219 * @test
220 */
221 public function unsetValueByPathDoesNotModifyAnArrayIfThePathWasNotFound()
222 {
223 $array = ['foo' => ['bar' => ['baz' => 'Some Value']], 'bar' => 'Baz'];
224 $path = ['foo', 'bar', 'nonExistingKey'];
225 $expectedValue = $array;
226 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::unsetValueByPath($array, $path);
227 $this->assertSame($expectedValue, $actualValue);
228 }
229
230 /**
231 * @test
232 */
233 public function unsetValueByPathRemovesSpecifiedKey()
234 {
235 $array = ['foo' => ['bar' => ['baz' => 'Some Value']], 'bar' => 'Baz'];
236 $path = ['foo', 'bar', 'baz'];
237 $expectedValue = ['foo' => ['bar' => []], 'bar' => 'Baz'];
238 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::unsetValueByPath($array, $path);
239 $this->assertSame($expectedValue, $actualValue);
240 }
241
242 /**
243 * @test
244 */
245 public function unsetValueByPathRemovesSpecifiedKeyIfPathIsString()
246 {
247 $array = ['foo' => ['bar' => ['baz' => 'Some Value']], 'bar' => 'Baz'];
248 $path = 'foo.bar.baz';
249 $expectedValue = ['foo' => ['bar' => []], 'bar' => 'Baz'];
250 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::unsetValueByPath($array, $path);
251 $this->assertSame($expectedValue, $actualValue);
252 }
253
254 /**
255 * @test
256 */
257 public function unsetValueByPathRemovesSpecifiedBranch()
258 {
259 $array = ['foo' => ['bar' => ['baz' => 'Some Value']], 'bar' => 'Baz'];
260 $path = ['foo'];
261 $expectedValue = ['bar' => 'Baz'];
262 $actualValue = \TYPO3\CMS\Extbase\Utility\ArrayUtility::unsetValueByPath($array, $path);
263 $this->assertSame($expectedValue, $actualValue);
264 }
265
266 /**
267 * @test
268 */
269 public function unsetValueByPathThrowsExceptionIfPathIsNoArrayOrString()
270 {
271 $this->expectException(\InvalidArgumentException::class);
272 $this->expectExceptionCode(1305111513);
273 $array = ['Foo' => ['Bar' => ['Baz' => [2 => 'the value']]]];
274 \TYPO3\CMS\Extbase\Utility\ArrayUtility::unsetValueByPath($array, null);
275 }
276
277 /**
278 * @test
279 */
280 public function removeEmptyElementsRecursivelyRemovesNullValues()
281 {
282 $array = ['EmptyElement' => null, 'Foo' => ['Bar' => ['Baz' => ['NotNull' => '', 'AnotherEmptyElement' => null]]]];
283 $expectedResult = ['Foo' => ['Bar' => ['Baz' => ['NotNull' => '']]]];
284 $actualResult = \TYPO3\CMS\Extbase\Utility\ArrayUtility::removeEmptyElementsRecursively($array);
285 $this->assertSame($expectedResult, $actualResult);
286 }
287
288 /**
289 * @test
290 */
291 public function removeEmptyElementsRecursivelyRemovesEmptySubArrays()
292 {
293 $array = ['EmptyElement' => [], 'Foo' => ['Bar' => ['Baz' => ['AnotherEmptyElement' => null]]], 'NotNull' => 123];
294 $expectedResult = ['NotNull' => 123];
295 $actualResult = \TYPO3\CMS\Extbase\Utility\ArrayUtility::removeEmptyElementsRecursively($array);
296 $this->assertSame($expectedResult, $actualResult);
297 }
298
299 public function arrayMergeRecursiveOverruleData()
300 {
301 return [
302 'simple usage' => [
303 'inputArray1' => [
304 'k1' => 'v1',
305 'k2' => 'v2'
306 ],
307 'inputArray2' => [
308 'k2' => 'v2a',
309 'k3' => 'v3'
310 ],
311 'dontAddNewKeys' => false,
312 // default
313 'emptyValuesOverride' => true,
314 // default
315 'expected' => [
316 'k1' => 'v1',
317 'k2' => 'v2a',
318 'k3' => 'v3'
319 ]
320 ],
321 'simple usage with recursion' => [
322 'inputArray1' => [
323 'k1' => 'v1',
324 'k2' => [
325 'k2.1' => 'v2.1',
326 'k2.2' => 'v2.2'
327 ]
328 ],
329 'inputArray2' => [
330 'k2' => [
331 'k2.2' => 'v2.2a',
332 'k2.3' => 'v2.3'
333 ],
334 'k3' => 'v3'
335 ],
336 'dontAddNewKeys' => false,
337 // default
338 'emptyValuesOverride' => true,
339 // default
340 'expected' => [
341 'k1' => 'v1',
342 'k2' => [
343 'k2.1' => 'v2.1',
344 'k2.2' => 'v2.2a',
345 'k2.3' => 'v2.3'
346 ],
347 'k3' => 'v3'
348 ]
349 ],
350 'simple type should override array (k2)' => [
351 'inputArray1' => [
352 'k1' => 'v1',
353 'k2' => [
354 'k2.1' => 'v2.1'
355 ]
356 ],
357 'inputArray2' => [
358 'k2' => 'v2a',
359 'k3' => 'v3'
360 ],
361 'dontAddNewKeys' => false,
362 // default
363 'emptyValuesOverride' => true,
364 // default
365 'expected' => [
366 'k1' => 'v1',
367 'k2' => 'v2a',
368 'k3' => 'v3'
369 ]
370 ],
371 'null should override array (k2)' => [
372 'inputArray1' => [
373 'k1' => 'v1',
374 'k2' => [
375 'k2.1' => 'v2.1'
376 ]
377 ],
378 'inputArray2' => [
379 'k2' => null,
380 'k3' => 'v3'
381 ],
382 'dontAddNewKeys' => false,
383 // default
384 'emptyValuesOverride' => true,
385 // default
386 'expected' => [
387 'k1' => 'v1',
388 'k2' => null,
389 'k3' => 'v3'
390 ]
391 ]
392 ];
393 }
394
395 /**
396 * @test
397 *
398 * @param array $inputArray1
399 * @param array $inputArray2
400 * @param bool $dontAddNewKeys
401 * @param bool $emptyValuesOverride
402 * @param array $expected
403 *
404 * @dataProvider arrayMergeRecursiveOverruleData
405 */
406 public function arrayMergeRecursiveOverruleMergesSimpleArrays(array $inputArray1, array $inputArray2, $dontAddNewKeys, $emptyValuesOverride, array $expected)
407 {
408 $this->assertSame($expected, \TYPO3\CMS\Extbase\Utility\ArrayUtility::arrayMergeRecursiveOverrule($inputArray1, $inputArray2, $dontAddNewKeys, $emptyValuesOverride));
409 }
410
411 /**
412 * @test
413 */
414 public function integerExplodeReturnsArrayOfIntegers()
415 {
416 $inputString = '1,2,3,4,5,6';
417 $expected = [1, 2, 3, 4, 5, 6];
418 $this->assertSame($expected, \TYPO3\CMS\Extbase\Utility\ArrayUtility::integerExplode(',', $inputString));
419 }
420
421 /**
422 * @test
423 */
424 public function integerExplodeReturnsZeroForStringValues()
425 {
426 $inputString = '1,abc,3,,5';
427 $expected = [1, 0, 3, 0, 5];
428 $this->assertSame($expected, \TYPO3\CMS\Extbase\Utility\ArrayUtility::integerExplode(',', $inputString));
429 }
430
431 /**
432 * dataProvider for sortArrayWithIntegerKeys
433 *
434 * @return array
435 */
436 public function sortArrayWithIntegerKeysDataProvider()
437 {
438 return [
439 [
440 [
441 '20' => 'test1',
442 '11' => 'test2',
443 '16' => 'test3',
444 ],
445 [
446 '11' => 'test2',
447 '16' => 'test3',
448 '20' => 'test1',
449 ]
450 ],
451 [
452 [
453 '20' => 'test1',
454 '16.5' => 'test2',
455 '16' => 'test3',
456 ],
457 [
458 '20' => 'test1',
459 '16.5' => 'test2',
460 '16' => 'test3',
461 ]
462 ],
463 [
464 [
465 '20' => 'test20',
466 'somestring' => 'teststring',
467 '16' => 'test16',
468 ],
469 [
470 '20' => 'test20',
471 'somestring' => 'teststring',
472 '16' => 'test16',
473 ]
474 ],
475 ];
476 }
477
478 /**
479 * @test
480 *
481 * @param array $arrayToSort
482 * @param array $expectedArray
483 *
484 * @dataProvider sortArrayWithIntegerKeysDataProvider
485 */
486 public function sortArrayWithIntegerKeysSortsNumericArrays(array $arrayToSort, array $expectedArray)
487 {
488 $sortedArray = \TYPO3\CMS\Extbase\Utility\ArrayUtility::sortArrayWithIntegerKeys($arrayToSort);
489 $this->assertSame($sortedArray, $expectedArray);
490 }
491 }