08ad677091028a05b19a0a71a4e2966f19da1912
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Utility / GeneralUtilityTest.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 org\bovigo\vfs\vfsStream;
18 use org\bovigo\vfs\vfsStreamDirectory;
19 use org\bovigo\vfs\vfsStreamWrapper;
20 use TYPO3\CMS\Core\Package\Package;
21 use TYPO3\CMS\Core\Package\PackageManager;
22 use TYPO3\CMS\Core\Tests\Unit\Utility\AccessibleProxies\ExtensionManagementUtilityAccessibleProxy;
23 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityFilesystemFixture;
24 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityFixture;
25 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityMinifyJavaScriptFixture;
26 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\OriginalClassFixture;
27 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\OtherReplacementClassFixture;
28 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\ReplacementClassFixture;
29 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\TwoParametersConstructorFixture;
30 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32 use TYPO3\TestingFramework\Core\FileStreamWrapper;
33
34 /**
35 * Testcase for class \TYPO3\CMS\Core\Utility\GeneralUtility
36 */
37 class GeneralUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
38 {
39 const NO_FIX_PERMISSIONS_ON_WINDOWS = 'fixPermissions() not available on Windows (method does nothing)';
40 /**
41 * @var array A backup of registered singleton instances
42 */
43 protected $singletonInstances = [];
44
45 /**
46 * @var \TYPO3\CMS\Core\Package\PackageManager
47 */
48 protected $backupPackageManager;
49
50 /**
51 * Set up
52 */
53 protected function setUp()
54 {
55 GeneralUtilityFixture::flushInternalRuntimeCaches();
56 GeneralUtilityFixture::$isAllowedHostHeaderValueCallCount = 0;
57 GeneralUtilityFixture::setAllowHostHeaderValue(false);
58 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
59 $this->singletonInstances = GeneralUtility::getSingletonInstances();
60 $this->backupPackageManager = ExtensionManagementUtilityAccessibleProxy::getPackageManager();
61 }
62
63 /**
64 * Tear down
65 */
66 protected function tearDown()
67 {
68 GeneralUtility::resetSingletonInstances($this->singletonInstances);
69 ExtensionManagementUtilityAccessibleProxy::setPackageManager($this->backupPackageManager);
70 parent::tearDown();
71 }
72
73 /**
74 * Helper method to test for an existing internet connection.
75 * Some tests are skipped if there is no working uplink.
76 *
77 * @return bool $isConnected
78 */
79 public function isConnected()
80 {
81 $isConnected = false;
82 $connected = @fsockopen('typo3.org', 80);
83 if ($connected) {
84 $isConnected = true;
85 fclose($connected);
86 }
87 return $isConnected;
88 }
89
90 /**
91 * Helper method to create a random directory in the virtual file system
92 * and return the path.
93 *
94 * @param string $prefix
95 * @return string
96 */
97 protected function getVirtualTestDir($prefix = 'root_')
98 {
99 $root = vfsStream::setup();
100 $path = $root->url() . '/typo3temp/var/tests/' . $this->getUniqueId($prefix);
101 GeneralUtility::mkdir_deep($path);
102 return $path;
103 }
104
105 ///////////////////////////
106 // Tests concerning _GP
107 ///////////////////////////
108 /**
109 * @test
110 * @dataProvider gpDataProvider
111 */
112 public function canRetrieveValueWithGP($key, $get, $post, $expected)
113 {
114 $_GET = $get;
115 $_POST = $post;
116 $this->assertSame($expected, GeneralUtility::_GP($key));
117 }
118
119 /**
120 * Data provider for canRetrieveValueWithGP.
121 * All test values also check whether slashes are stripped properly.
122 *
123 * @return array
124 */
125 public function gpDataProvider()
126 {
127 return [
128 'No key parameter' => [null, [], [], null],
129 'Key not found' => ['cake', [], [], null],
130 'Value only in GET' => ['cake', ['cake' => 'li\\e'], [], 'li\\e'],
131 'Value only in POST' => ['cake', [], ['cake' => 'l\\ie'], 'l\\ie'],
132 'Value from POST preferred over GET' => ['cake', ['cake' => 'is a'], ['cake' => '\\lie'], '\\lie'],
133 'Value can be an array' => [
134 'cake',
135 ['cake' => ['is a' => 'l\\ie']],
136 [],
137 ['is a' => 'l\\ie']
138 ]
139 ];
140 }
141
142 ///////////////////////////
143 // Tests concerning _GPmerged
144 ///////////////////////////
145 /**
146 * @test
147 * @dataProvider gpMergedDataProvider
148 */
149 public function gpMergedWillMergeArraysFromGetAndPost($get, $post, $expected)
150 {
151 $_POST = $post;
152 $_GET = $get;
153 $this->assertEquals($expected, GeneralUtility::_GPmerged('cake'));
154 }
155
156 /**
157 * Data provider for gpMergedWillMergeArraysFromGetAndPost
158 *
159 * @return array
160 */
161 public function gpMergedDataProvider()
162 {
163 $fullDataArray = ['cake' => ['a' => 'is a', 'b' => 'lie']];
164 $postPartData = ['cake' => ['b' => 'lie']];
165 $getPartData = ['cake' => ['a' => 'is a']];
166 $getPartDataModified = ['cake' => ['a' => 'is not a']];
167 return [
168 'Key doesn\' exist' => [['foo'], ['bar'], []],
169 'No POST data' => [$fullDataArray, [], $fullDataArray['cake']],
170 'No GET data' => [[], $fullDataArray, $fullDataArray['cake']],
171 'POST and GET are merged' => [$getPartData, $postPartData, $fullDataArray['cake']],
172 'POST is preferred over GET' => [$getPartDataModified, $fullDataArray, $fullDataArray['cake']]
173 ];
174 }
175
176 ///////////////////////////////
177 // Tests concerning _GET / _POST
178 ///////////////////////////////
179 /**
180 * Data provider for canRetrieveGlobalInputsThroughGet
181 * and canRetrieveGlobalInputsThroughPost
182 *
183 * @return array
184 */
185 public function getAndPostDataProvider()
186 {
187 return [
188 'Requested input data doesn\'t exist' => ['cake', [], null],
189 'No key will return entire input data' => [null, ['cake' => 'l\\ie'], ['cake' => 'l\\ie']],
190 'Can retrieve specific input' => ['cake', ['cake' => 'l\\ie', 'foo'], 'l\\ie'],
191 'Can retrieve nested input data' => ['cake', ['cake' => ['is a' => 'l\\ie']], ['is a' => 'l\\ie']]
192 ];
193 }
194
195 /**
196 * @test
197 * @dataProvider getAndPostDataProvider
198 */
199 public function canRetrieveGlobalInputsThroughGet($key, $get, $expected)
200 {
201 $_GET = $get;
202 $this->assertSame($expected, GeneralUtility::_GET($key));
203 }
204
205 /**
206 * @test
207 * @dataProvider getAndPostDataProvider
208 */
209 public function canRetrieveGlobalInputsThroughPost($key, $post, $expected)
210 {
211 $_POST = $post;
212 $this->assertSame($expected, GeneralUtility::_POST($key));
213 }
214
215 ///////////////////////////////
216 // Tests concerning _GETset
217 ///////////////////////////////
218 /**
219 * @test
220 * @dataProvider getSetDataProvider
221 */
222 public function canSetNewGetInputValues($input, $key, $expected, $getPreset = [])
223 {
224 $_GET = $getPreset;
225 GeneralUtility::_GETset($input, $key);
226 $this->assertSame($expected, $_GET);
227 }
228
229 /**
230 * Data provider for canSetNewGetInputValues
231 *
232 * @return array
233 */
234 public function getSetDataProvider()
235 {
236 return [
237 'No input data used without target key' => [null, null, []],
238 'No input data used with target key' => ['', 'cake', ['cake' => '']],
239 'No target key used with string input data' => ['data', null, []],
240 'No target key used with array input data' => [['cake' => 'lie'], null, ['cake' => 'lie']],
241 'Target key and string input data' => ['lie', 'cake', ['cake' => 'lie']],
242 'Replace existing GET data' => ['lie', 'cake', ['cake' => 'lie'], ['cake' => 'is a lie']],
243 'Target key pointing to sublevels and string input data' => ['lie', 'cake|is', ['cake' => ['is' => 'lie']]],
244 'Target key pointing to sublevels and array input data' => [['a' => 'lie'], 'cake|is', ['cake' => ['is' => ['a' => 'lie']]]]
245 ];
246 }
247
248 ///////////////////////////
249 // Tests concerning cmpIPv4
250 ///////////////////////////
251 /**
252 * Data provider for cmpIPv4ReturnsTrueForMatchingAddress
253 *
254 * @return array Data sets
255 */
256 public static function cmpIPv4DataProviderMatching()
257 {
258 return [
259 'host with full IP address' => ['127.0.0.1', '127.0.0.1'],
260 'host with two wildcards at the end' => ['127.0.0.1', '127.0.*.*'],
261 'host with wildcard at third octet' => ['127.0.0.1', '127.0.*.1'],
262 'host with wildcard at second octet' => ['127.0.0.1', '127.*.0.1'],
263 '/8 subnet' => ['127.0.0.1', '127.1.1.1/8'],
264 '/32 subnet (match only name)' => ['127.0.0.1', '127.0.0.1/32'],
265 '/30 subnet' => ['10.10.3.1', '10.10.3.3/30'],
266 'host with wildcard in list with IPv4/IPv6 addresses' => ['192.168.1.1', '127.0.0.1, 1234:5678::/126, 192.168.*'],
267 'host in list with IPv4/IPv6 addresses' => ['192.168.1.1', '::1, 1234:5678::/126, 192.168.1.1'],
268 ];
269 }
270
271 /**
272 * @test
273 * @dataProvider cmpIPv4DataProviderMatching
274 */
275 public function cmpIPv4ReturnsTrueForMatchingAddress($ip, $list)
276 {
277 $this->assertTrue(GeneralUtility::cmpIPv4($ip, $list));
278 }
279
280 /**
281 * Data provider for cmpIPv4ReturnsFalseForNotMatchingAddress
282 *
283 * @return array Data sets
284 */
285 public static function cmpIPv4DataProviderNotMatching()
286 {
287 return [
288 'single host' => ['127.0.0.1', '127.0.0.2'],
289 'single host with wildcard' => ['127.0.0.1', '127.*.1.1'],
290 'single host with /32 subnet mask' => ['127.0.0.1', '127.0.0.2/32'],
291 '/31 subnet' => ['127.0.0.1', '127.0.0.2/31'],
292 'list with IPv4/IPv6 addresses' => ['127.0.0.1', '10.0.2.3, 192.168.1.1, ::1'],
293 'list with only IPv6 addresses' => ['10.20.30.40', '::1, 1234:5678::/127']
294 ];
295 }
296
297 /**
298 * @test
299 * @dataProvider cmpIPv4DataProviderNotMatching
300 */
301 public function cmpIPv4ReturnsFalseForNotMatchingAddress($ip, $list)
302 {
303 $this->assertFalse(GeneralUtility::cmpIPv4($ip, $list));
304 }
305
306 ///////////////////////////
307 // Tests concerning cmpIPv6
308 ///////////////////////////
309 /**
310 * Data provider for cmpIPv6ReturnsTrueForMatchingAddress
311 *
312 * @return array Data sets
313 */
314 public static function cmpIPv6DataProviderMatching()
315 {
316 return [
317 'empty address' => ['::', '::'],
318 'empty with netmask in list' => ['::', '::/0'],
319 'empty with netmask 0 and host-bits set in list' => ['::', '::123/0'],
320 'localhost' => ['::1', '::1'],
321 'localhost with leading zero blocks' => ['::1', '0:0::1'],
322 'host with submask /128' => ['::1', '0:0::1/128'],
323 '/16 subnet' => ['1234::1', '1234:5678::/16'],
324 '/126 subnet' => ['1234:5678::3', '1234:5678::/126'],
325 '/126 subnet with host-bits in list set' => ['1234:5678::3', '1234:5678::2/126'],
326 'list with IPv4/IPv6 addresses' => ['1234:5678::3', '::1, 127.0.0.1, 1234:5678::/126, 192.168.1.1']
327 ];
328 }
329
330 /**
331 * @test
332 * @dataProvider cmpIPv6DataProviderMatching
333 */
334 public function cmpIPv6ReturnsTrueForMatchingAddress($ip, $list)
335 {
336 $this->assertTrue(GeneralUtility::cmpIPv6($ip, $list));
337 }
338
339 /**
340 * Data provider for cmpIPv6ReturnsFalseForNotMatchingAddress
341 *
342 * @return array Data sets
343 */
344 public static function cmpIPv6DataProviderNotMatching()
345 {
346 return [
347 'empty against localhost' => ['::', '::1'],
348 'empty against localhost with /128 netmask' => ['::', '::1/128'],
349 'localhost against different host' => ['::1', '::2'],
350 'localhost against host with prior bits set' => ['::1', '::1:1'],
351 'host against different /17 subnet' => ['1234::1', '1234:f678::/17'],
352 'host against different /127 subnet' => ['1234:5678::3', '1234:5678::/127'],
353 'host against IPv4 address list' => ['1234:5678::3', '127.0.0.1, 192.168.1.1'],
354 'host against mixed list with IPv6 host in different subnet' => ['1234:5678::3', '::1, 1234:5678::/127']
355 ];
356 }
357
358 /**
359 * @test
360 * @dataProvider cmpIPv6DataProviderNotMatching
361 */
362 public function cmpIPv6ReturnsFalseForNotMatchingAddress($ip, $list)
363 {
364 $this->assertFalse(GeneralUtility::cmpIPv6($ip, $list));
365 }
366
367 ///////////////////////////////
368 // Tests concerning IPv6Hex2Bin
369 ///////////////////////////////
370 /**
371 * Data provider for IPv6Hex2BinCorrect
372 *
373 * @return array Data sets
374 */
375 public static function IPv6Hex2BinDataProviderCorrect()
376 {
377 return [
378 'empty 1' => ['::', str_pad('', 16, "\x00")],
379 'empty 2, already normalized' => ['0000:0000:0000:0000:0000:0000:0000:0000', str_pad('', 16, "\x00")],
380 'already normalized' => ['0102:0304:0000:0000:0000:0000:0506:0078', "\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78"],
381 'expansion in middle 1' => ['1::2', "\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02"],
382 'expansion in middle 2' => ['beef::fefa', "\xbe\xef" . str_pad('', 12, "\x00") . "\xfe\xfa"],
383 ];
384 }
385
386 /**
387 * @test
388 * @dataProvider IPv6Hex2BinDataProviderCorrect
389 */
390 public function IPv6Hex2BinCorrectlyConvertsAddresses($hex, $binary)
391 {
392 $this->assertTrue(GeneralUtility::IPv6Hex2Bin($hex) === $binary);
393 }
394
395 ///////////////////////////////
396 // Tests concerning IPv6Bin2Hex
397 ///////////////////////////////
398 /**
399 * Data provider for IPv6Bin2HexCorrect
400 *
401 * @return array Data sets
402 */
403 public static function IPv6Bin2HexDataProviderCorrect()
404 {
405 return [
406 'empty' => [str_pad('', 16, "\x00"), '::'],
407 'non-empty front' => ["\x01" . str_pad('', 15, "\x00"), '100::'],
408 'non-empty back' => [str_pad('', 15, "\x00") . "\x01", '::1'],
409 'normalized' => ["\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78", '102:304::506:78'],
410 'expansion in middle 1' => ["\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02", '1::2'],
411 'expansion in middle 2' => ["\xbe\xef" . str_pad('', 12, "\x00") . "\xfe\xfa", 'beef::fefa'],
412 ];
413 }
414
415 /**
416 * @test
417 * @dataProvider IPv6Bin2HexDataProviderCorrect
418 */
419 public function IPv6Bin2HexCorrectlyConvertsAddresses($binary, $hex)
420 {
421 $this->assertEquals(GeneralUtility::IPv6Bin2Hex($binary), $hex);
422 }
423
424 ////////////////////////////////////////////////
425 // Tests concerning normalizeIPv6 / compressIPv6
426 ////////////////////////////////////////////////
427 /**
428 * Data provider for normalizeIPv6ReturnsCorrectlyNormalizedFormat
429 *
430 * @return array Data sets
431 */
432 public static function normalizeCompressIPv6DataProviderCorrect()
433 {
434 return [
435 'empty' => ['::', '0000:0000:0000:0000:0000:0000:0000:0000'],
436 'localhost' => ['::1', '0000:0000:0000:0000:0000:0000:0000:0001'],
437 'expansion in middle 1' => ['1::2', '0001:0000:0000:0000:0000:0000:0000:0002'],
438 'expansion in middle 2' => ['1:2::3', '0001:0002:0000:0000:0000:0000:0000:0003'],
439 'expansion in middle 3' => ['1::2:3', '0001:0000:0000:0000:0000:0000:0002:0003'],
440 'expansion in middle 4' => ['1:2::3:4:5', '0001:0002:0000:0000:0000:0003:0004:0005']
441 ];
442 }
443
444 /**
445 * @test
446 * @dataProvider normalizeCompressIPv6DataProviderCorrect
447 */
448 public function normalizeIPv6CorrectlyNormalizesAddresses($compressed, $normalized)
449 {
450 $this->assertEquals($normalized, GeneralUtility::normalizeIPv6($compressed));
451 }
452
453 /**
454 * @test
455 * @dataProvider normalizeCompressIPv6DataProviderCorrect
456 */
457 public function compressIPv6CorrectlyCompressesAdresses($compressed, $normalized)
458 {
459 $this->assertEquals($compressed, GeneralUtility::compressIPv6($normalized));
460 }
461
462 /**
463 * @test
464 */
465 public function compressIPv6CorrectlyCompressesAdressWithSomeAddressOnRightSide()
466 {
467 if (strtolower(PHP_OS) === 'darwin') {
468 $this->markTestSkipped('This test does not work on OSX / Darwin OS.');
469 }
470 $this->assertEquals('::f0f', GeneralUtility::compressIPv6('0000:0000:0000:0000:0000:0000:0000:0f0f'));
471 }
472
473 ///////////////////////////////
474 // Tests concerning validIP
475 ///////////////////////////////
476 /**
477 * Data provider for checkValidIpReturnsTrueForValidIp
478 *
479 * @return array Data sets
480 */
481 public static function validIpDataProvider()
482 {
483 return [
484 '0.0.0.0' => ['0.0.0.0'],
485 'private IPv4 class C' => ['192.168.0.1'],
486 'private IPv4 class A' => ['10.0.13.1'],
487 'private IPv6' => ['fe80::daa2:5eff:fe8b:7dfb']
488 ];
489 }
490
491 /**
492 * @test
493 * @dataProvider validIpDataProvider
494 */
495 public function validIpReturnsTrueForValidIp($ip)
496 {
497 $this->assertTrue(GeneralUtility::validIP($ip));
498 }
499
500 /**
501 * Data provider for checkValidIpReturnsFalseForInvalidIp
502 *
503 * @return array Data sets
504 */
505 public static function invalidIpDataProvider()
506 {
507 return [
508 'null' => [null],
509 'zero' => [0],
510 'string' => ['test'],
511 'string empty' => [''],
512 'string NULL' => ['NULL'],
513 'out of bounds IPv4' => ['300.300.300.300'],
514 'dotted decimal notation with only two dots' => ['127.0.1']
515 ];
516 }
517
518 /**
519 * @test
520 * @dataProvider invalidIpDataProvider
521 */
522 public function validIpReturnsFalseForInvalidIp($ip)
523 {
524 $this->assertFalse(GeneralUtility::validIP($ip));
525 }
526
527 ///////////////////////////////
528 // Tests concerning cmpFQDN
529 ///////////////////////////////
530 /**
531 * Data provider for cmpFqdnReturnsTrue
532 *
533 * @return array Data sets
534 */
535 public static function cmpFqdnValidDataProvider()
536 {
537 return [
538 'localhost should usually resolve, IPv4' => ['127.0.0.1', '*'],
539 'localhost should usually resolve, IPv6' => ['::1', '*'],
540 // other testcases with resolving not possible since it would
541 // require a working IPv4/IPv6-connectivity
542 'aaa.bbb.ccc.ddd.eee, full' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee'],
543 'aaa.bbb.ccc.ddd.eee, wildcard first' => ['aaa.bbb.ccc.ddd.eee', '*.ccc.ddd.eee'],
544 'aaa.bbb.ccc.ddd.eee, wildcard last' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.*'],
545 'aaa.bbb.ccc.ddd.eee, wildcard middle' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.eee'],
546 'list-matches, 1' => ['aaa.bbb.ccc.ddd.eee', 'xxx, yyy, zzz, aaa.*.eee'],
547 'list-matches, 2' => ['aaa.bbb.ccc.ddd.eee', '127:0:0:1,,aaa.*.eee,::1']
548 ];
549 }
550
551 /**
552 * @test
553 * @dataProvider cmpFqdnValidDataProvider
554 */
555 public function cmpFqdnReturnsTrue($baseHost, $list)
556 {
557 $this->assertTrue(GeneralUtility::cmpFQDN($baseHost, $list));
558 }
559
560 /**
561 * Data provider for cmpFqdnReturnsFalse
562 *
563 * @return array Data sets
564 */
565 public static function cmpFqdnInvalidDataProvider()
566 {
567 return [
568 'num-parts of hostname to check can only be less or equal than hostname, 1' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee.fff'],
569 'num-parts of hostname to check can only be less or equal than hostname, 2' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.bbb.ccc.ddd.eee']
570 ];
571 }
572
573 /**
574 * @test
575 * @dataProvider cmpFqdnInvalidDataProvider
576 */
577 public function cmpFqdnReturnsFalse($baseHost, $list)
578 {
579 $this->assertFalse(GeneralUtility::cmpFQDN($baseHost, $list));
580 }
581
582 ///////////////////////////////
583 // Tests concerning inList
584 ///////////////////////////////
585 /**
586 * @test
587 * @param string $haystack
588 * @dataProvider inListForItemContainedReturnsTrueDataProvider
589 */
590 public function inListForItemContainedReturnsTrue($haystack)
591 {
592 $this->assertTrue(GeneralUtility::inList($haystack, 'findme'));
593 }
594
595 /**
596 * Data provider for inListForItemContainedReturnsTrue.
597 *
598 * @return array
599 */
600 public function inListForItemContainedReturnsTrueDataProvider()
601 {
602 return [
603 'Element as second element of four items' => ['one,findme,three,four'],
604 'Element at beginning of list' => ['findme,one,two'],
605 'Element at end of list' => ['one,two,findme'],
606 'One item list' => ['findme']
607 ];
608 }
609
610 /**
611 * @test
612 * @param string $haystack
613 * @dataProvider inListForItemNotContainedReturnsFalseDataProvider
614 */
615 public function inListForItemNotContainedReturnsFalse($haystack)
616 {
617 $this->assertFalse(GeneralUtility::inList($haystack, 'findme'));
618 }
619
620 /**
621 * Data provider for inListForItemNotContainedReturnsFalse.
622 *
623 * @return array
624 */
625 public function inListForItemNotContainedReturnsFalseDataProvider()
626 {
627 return [
628 'Four item list' => ['one,two,three,four'],
629 'One item list' => ['one'],
630 'Empty list' => ['']
631 ];
632 }
633
634 ///////////////////////////////
635 // Tests concerning rmFromList
636 ///////////////////////////////
637 /**
638 * @test
639 * @param string $initialList
640 * @param string $listWithElementRemoved
641 * @dataProvider rmFromListRemovesElementsFromCommaSeparatedListDataProvider
642 */
643 public function rmFromListRemovesElementsFromCommaSeparatedList($initialList, $listWithElementRemoved)
644 {
645 $this->assertSame($listWithElementRemoved, GeneralUtility::rmFromList('removeme', $initialList));
646 }
647
648 /**
649 * Data provider for rmFromListRemovesElementsFromCommaSeparatedList
650 *
651 * @return array
652 */
653 public function rmFromListRemovesElementsFromCommaSeparatedListDataProvider()
654 {
655 return [
656 'Element as second element of three' => ['one,removeme,two', 'one,two'],
657 'Element at beginning of list' => ['removeme,one,two', 'one,two'],
658 'Element at end of list' => ['one,two,removeme', 'one,two'],
659 'One item list' => ['removeme', ''],
660 'Element not contained in list' => ['one,two,three', 'one,two,three'],
661 'Empty element survives' => ['one,,three,,removeme', 'one,,three,'],
662 'Empty element survives at start' => [',removeme,three,removeme', ',three'],
663 'Empty element survives at end' => ['removeme,three,removeme,', 'three,'],
664 'Empty list' => ['', ''],
665 'List contains removeme multiple times' => ['removeme,notme,removeme,removeme', 'notme'],
666 'List contains removeme multiple times nothing else' => ['removeme,removeme,removeme', ''],
667 'List contains removeme multiple times nothing else 2x' => ['removeme,removeme', ''],
668 'List contains removeme multiple times nothing else 3x' => ['removeme,removeme,removeme', ''],
669 'List contains removeme multiple times nothing else 4x' => ['removeme,removeme,removeme,removeme', ''],
670 'List contains removeme multiple times nothing else 5x' => ['removeme,removeme,removeme,removeme,removeme', ''],
671 ];
672 }
673
674 ///////////////////////////////
675 // Tests concerning expandList
676 ///////////////////////////////
677 /**
678 * @test
679 * @param string $list
680 * @param string $expectation
681 * @dataProvider expandListExpandsIntegerRangesDataProvider
682 */
683 public function expandListExpandsIntegerRanges($list, $expectation)
684 {
685 $this->assertSame($expectation, GeneralUtility::expandList($list));
686 }
687
688 /**
689 * Data provider for expandListExpandsIntegerRangesDataProvider
690 *
691 * @return array
692 */
693 public function expandListExpandsIntegerRangesDataProvider()
694 {
695 return [
696 'Expand for the same number' => ['1,2-2,7', '1,2,7'],
697 'Small range expand with parameters reversed ignores reversed items' => ['1,5-3,7', '1,7'],
698 'Small range expand' => ['1,3-5,7', '1,3,4,5,7'],
699 'Expand at beginning' => ['3-5,1,7', '3,4,5,1,7'],
700 'Expand at end' => ['1,7,3-5', '1,7,3,4,5'],
701 'Multiple small range expands' => ['1,3-5,7-10,12', '1,3,4,5,7,8,9,10,12'],
702 'One item list' => ['1-5', '1,2,3,4,5'],
703 'Nothing to expand' => ['1,2,3,4', '1,2,3,4'],
704 'Empty list' => ['', '']
705 ];
706 }
707
708 /**
709 * @test
710 */
711 public function expandListExpandsForTwoThousandElementsExpandsOnlyToThousandElementsMaximum()
712 {
713 $list = GeneralUtility::expandList('1-2000');
714 $this->assertSame(1000, count(explode(',', $list)));
715 }
716
717 ///////////////////////////////
718 // Tests concerning uniqueList
719 ///////////////////////////////
720 /**
721 * @test
722 * @param string $initialList
723 * @param string $unifiedList
724 * @dataProvider uniqueListUnifiesCommaSeparatedListDataProvider
725 */
726 public function uniqueListUnifiesCommaSeparatedList($initialList, $unifiedList)
727 {
728 $this->assertSame($unifiedList, GeneralUtility::uniqueList($initialList));
729 }
730
731 /**
732 * Data provider for uniqueListUnifiesCommaSeparatedList
733 *
734 * @return array
735 */
736 public function uniqueListUnifiesCommaSeparatedListDataProvider()
737 {
738 return [
739 'List without duplicates' => ['one,two,three', 'one,two,three'],
740 'List with two consecutive duplicates' => ['one,two,two,three,three', 'one,two,three'],
741 'List with non-consecutive duplicates' => ['one,two,three,two,three', 'one,two,three'],
742 'One item list' => ['one', 'one'],
743 'Empty list' => ['', '']
744 ];
745 }
746
747 ///////////////////////////////
748 // Tests concerning isFirstPartOfStr
749 ///////////////////////////////
750 /**
751 * Data provider for isFirstPartOfStrReturnsTrueForMatchingFirstParts
752 *
753 * @return array
754 */
755 public function isFirstPartOfStrReturnsTrueForMatchingFirstPartDataProvider()
756 {
757 return [
758 'match first part of string' => ['hello world', 'hello'],
759 'match whole string' => ['hello', 'hello'],
760 'integer is part of string with same number' => ['24', 24],
761 'string is part of integer with same number' => [24, '24'],
762 'integer is part of string starting with same number' => ['24 beer please', 24]
763 ];
764 }
765
766 /**
767 * @test
768 * @dataProvider isFirstPartOfStrReturnsTrueForMatchingFirstPartDataProvider
769 */
770 public function isFirstPartOfStrReturnsTrueForMatchingFirstPart($string, $part)
771 {
772 $this->assertTrue(GeneralUtility::isFirstPartOfStr($string, $part));
773 }
774
775 /**
776 * Data provider for checkIsFirstPartOfStrReturnsFalseForNotMatchingFirstParts
777 *
778 * @return array
779 */
780 public function isFirstPartOfStrReturnsFalseForNotMatchingFirstPartDataProvider()
781 {
782 return [
783 'no string match' => ['hello', 'bye'],
784 'no case sensitive string match' => ['hello world', 'Hello'],
785 'array is not part of string' => ['string', []],
786 'string is not part of array' => [[], 'string'],
787 'NULL is not part of string' => ['string', null],
788 'string is not part of NULL' => [null, 'string'],
789 'NULL is not part of array' => [[], null],
790 'array is not part of NULL' => [null, []],
791 'empty string is not part of empty string' => ['', ''],
792 'NULL is not part of empty string' => ['', null],
793 'false is not part of empty string' => ['', false],
794 'empty string is not part of NULL' => [null, ''],
795 'empty string is not part of false' => [false, ''],
796 'empty string is not part of zero integer' => [0, ''],
797 'zero integer is not part of NULL' => [null, 0],
798 'zero integer is not part of empty string' => ['', 0]
799 ];
800 }
801
802 /**
803 * @test
804 * @dataProvider isFirstPartOfStrReturnsFalseForNotMatchingFirstPartDataProvider
805 */
806 public function isFirstPartOfStrReturnsFalseForNotMatchingFirstPart($string, $part)
807 {
808 $this->assertFalse(GeneralUtility::isFirstPartOfStr($string, $part));
809 }
810
811 ///////////////////////////////
812 // Tests concerning formatSize
813 ///////////////////////////////
814 /**
815 * @test
816 * @dataProvider formatSizeDataProvider
817 */
818 public function formatSizeTranslatesBytesToHigherOrderRepresentation($size, $labels, $base, $expected)
819 {
820 $this->assertEquals($expected, GeneralUtility::formatSize($size, $labels, $base));
821 }
822
823 /**
824 * Data provider for formatSizeTranslatesBytesToHigherOrderRepresentation
825 *
826 * @return array
827 */
828 public function formatSizeDataProvider()
829 {
830 return [
831 'IEC Bytes stay bytes (min)' => [1, '', 0, '1 '],
832 'IEC Bytes stay bytes (max)' => [921, '', 0, '921 '],
833 'IEC Kilobytes are used (min)' => [922, '', 0, '0.90 Ki'],
834 'IEC Kilobytes are used (max)' => [943718, '', 0, '922 Ki'],
835 'IEC Megabytes are used (min)' => [943719, '', 0, '0.90 Mi'],
836 'IEC Megabytes are used (max)' => [966367641, '', 0, '922 Mi'],
837 'IEC Gigabytes are used (min)' => [966367642, '', 0, '0.90 Gi'],
838 'IEC Gigabytes are used (max)' => [989560464998, '', 0, '922 Gi'],
839 'IEC Decimal is omitted for large kilobytes' => [31080, '', 0, '30 Ki'],
840 'IEC Decimal is omitted for large megabytes' => [31458000, '', 0, '30 Mi'],
841 'IEC Decimal is omitted for large gigabytes' => [32212254720, '', 0, '30 Gi'],
842 'SI Bytes stay bytes (min)' => [1, 'si', 0, '1 '],
843 'SI Bytes stay bytes (max)' => [899, 'si', 0, '899 '],
844 'SI Kilobytes are used (min)' => [901, 'si', 0, '0.90 k'],
845 'SI Kilobytes are used (max)' => [900000, 'si', 0, '900 k'],
846 'SI Megabytes are used (min)' => [900001, 'si', 0, '0.90 M'],
847 'SI Megabytes are used (max)' => [900000000, 'si', 0, '900 M'],
848 'SI Gigabytes are used (min)' => [900000001, 'si', 0, '0.90 G'],
849 'SI Gigabytes are used (max)' => [900000000000, 'si', 0, '900 G'],
850 'SI Decimal is omitted for large kilobytes' => [30000, 'si', 0, '30 k'],
851 'SI Decimal is omitted for large megabytes' => [30000000, 'si', 0, '30 M'],
852 'SI Decimal is omitted for large gigabytes' => [30000000000, 'si', 0, '30 G'],
853 'Label for bytes can be exchanged (binary unit)' => [1, ' Foo|||', 0, '1 Foo'],
854 'Label for kilobytes can be exchanged (binary unit)' => [1024, '| Foo||', 0, '1.00 Foo'],
855 'Label for megabyes can be exchanged (binary unit)' => [1048576, '|| Foo|', 0, '1.00 Foo'],
856 'Label for gigabytes can be exchanged (binary unit)' => [1073741824, '||| Foo', 0, '1.00 Foo'],
857 'Label for bytes can be exchanged (decimal unit)' => [1, ' Foo|||', 1000, '1 Foo'],
858 'Label for kilobytes can be exchanged (decimal unit)' => [1000, '| Foo||', 1000, '1.00 Foo'],
859 'Label for megabyes can be exchanged (decimal unit)' => [1000000, '|| Foo|', 1000, '1.00 Foo'],
860 'Label for gigabytes can be exchanged (decimal unit)' => [1000000000, '||| Foo', 1000, '1.00 Foo'],
861 'IEC Base is ignored' => [1024, 'iec', 1000, '1.00 Ki'],
862 'SI Base is ignored' => [1000, 'si', 1024, '1.00 k'],
863 'Use binary base for unexpected base' => [2048, '| Bar||', 512, '2.00 Bar']
864 ];
865 }
866
867 ///////////////////////////////
868 // Tests concerning splitCalc
869 ///////////////////////////////
870 /**
871 * Data provider for splitCalc
872 *
873 * @return array expected values, arithmetic expression
874 */
875 public function splitCalcDataProvider()
876 {
877 return [
878 'empty string returns empty array' => [
879 [],
880 ''
881 ],
882 'number without operator returns array with plus and number' => [
883 [['+', 42]],
884 '42'
885 ],
886 'two numbers with asterisk return first number with plus and second number with asterisk' => [
887 [['+', 42], ['*', 31]],
888 '42 * 31'
889 ]
890 ];
891 }
892
893 /**
894 * @test
895 * @dataProvider splitCalcDataProvider
896 */
897 public function splitCalcCorrectlySplitsExpression($expected, $expression)
898 {
899 $this->assertEquals($expected, GeneralUtility::splitCalc($expression, '+-*/'));
900 }
901
902 ///////////////////////////////
903 // Tests concerning htmlspecialchars_decode
904 ///////////////////////////////
905 /**
906 * @test
907 */
908 public function htmlspecialcharsDecodeReturnsDecodedString()
909 {
910 $string = '<typo3 version="6.0">&nbsp;</typo3>';
911 $encoded = htmlspecialchars($string);
912 $decoded = htmlspecialchars_decode($encoded);
913 $this->assertEquals($string, $decoded);
914 }
915
916 ///////////////////////////////
917 // Tests concerning deHSCentities
918 ///////////////////////////////
919 /**
920 * @test
921 * @dataProvider deHSCentitiesReturnsDecodedStringDataProvider
922 */
923 public function deHSCentitiesReturnsDecodedString($input, $expected)
924 {
925 $this->assertEquals($expected, GeneralUtility::deHSCentities($input));
926 }
927
928 /**
929 * Data provider for deHSCentitiesReturnsDecodedString
930 *
931 * @return array
932 */
933 public function deHSCentitiesReturnsDecodedStringDataProvider()
934 {
935 return [
936 'Empty string' => ['', ''],
937 'Double encoded &' => ['&amp;amp;', '&amp;'],
938 'Double encoded numeric entity' => ['&amp;#1234;', '&#1234;'],
939 'Double encoded hexadecimal entity' => ['&amp;#x1b;', '&#x1b;'],
940 'Single encoded entities are not touched' => ['&amp; &#1234; &#x1b;', '&amp; &#1234; &#x1b;']
941 ];
942 }
943
944 //////////////////////////////////
945 // Tests concerning slashJS
946 //////////////////////////////////
947 /**
948 * @test
949 * @dataProvider slashJsDataProvider
950 */
951 public function slashJsEscapesSingleQuotesAndSlashes($input, $extended, $expected)
952 {
953 $this->assertEquals($expected, GeneralUtility::slashJS($input, $extended));
954 }
955
956 /**
957 * Data provider for slashJsEscapesSingleQuotesAndSlashes
958 *
959 * @return array
960 */
961 public function slashJsDataProvider()
962 {
963 return [
964 'Empty string is not changed' => ['', false, ''],
965 'Normal string is not changed' => ['The cake is a lie √', false, 'The cake is a lie √'],
966 'String with single quotes' => ['The \'cake\' is a lie', false, 'The \\\'cake\\\' is a lie'],
967 'String with single quotes and backslashes - just escape single quotes' => ['The \\\'cake\\\' is a lie', false, 'The \\\\\'cake\\\\\' is a lie'],
968 'String with single quotes and backslashes - escape both' => ['The \\\'cake\\\' is a lie', true, 'The \\\\\\\'cake\\\\\\\' is a lie']
969 ];
970 }
971
972 //////////////////////////////////
973 // Tests concerning rawUrlEncodeJS
974 //////////////////////////////////
975 /**
976 * @test
977 */
978 public function rawUrlEncodeJsPreservesWhitespaces()
979 {
980 $input = 'Encode \'me\', but leave my spaces √';
981 $expected = 'Encode %27me%27%2C but leave my spaces %E2%88%9A';
982 $this->assertEquals($expected, GeneralUtility::rawUrlEncodeJS($input));
983 }
984
985 //////////////////////////////////
986 // Tests concerning rawUrlEncodeJS
987 //////////////////////////////////
988 /**
989 * @test
990 */
991 public function rawUrlEncodeFpPreservesSlashes()
992 {
993 $input = 'Encode \'me\', but leave my / √';
994 $expected = 'Encode%20%27me%27%2C%20but%20leave%20my%20/%20%E2%88%9A';
995 $this->assertEquals($expected, GeneralUtility::rawUrlEncodeFP($input));
996 }
997
998 //////////////////////////////////
999 // Tests concerning strtoupper / strtolower
1000 //////////////////////////////////
1001 /**
1002 * Data provider for strtoupper and strtolower
1003 *
1004 * @return array
1005 */
1006 public function strtouppperDataProvider()
1007 {
1008 return [
1009 'Empty string' => ['', ''],
1010 'String containing only latin characters' => ['the cake is a lie.', 'THE CAKE IS A LIE.'],
1011 'String with umlauts and accent characters' => ['the càkê is ä lie.', 'THE CàKê IS ä LIE.']
1012 ];
1013 }
1014
1015 /**
1016 * @test
1017 * @dataProvider strtouppperDataProvider
1018 */
1019 public function strtoupperConvertsOnlyLatinCharacters($input, $expected)
1020 {
1021 $this->assertEquals($expected, GeneralUtility::strtoupper($input));
1022 }
1023
1024 /**
1025 * @test
1026 * @dataProvider strtouppperDataProvider
1027 */
1028 public function strtolowerConvertsOnlyLatinCharacters($expected, $input)
1029 {
1030 $this->assertEquals($expected, GeneralUtility::strtolower($input));
1031 }
1032
1033 //////////////////////////////////
1034 // Tests concerning validEmail
1035 //////////////////////////////////
1036 /**
1037 * Data provider for valid validEmail's
1038 *
1039 * @return array Valid email addresses
1040 */
1041 public function validEmailValidDataProvider()
1042 {
1043 return [
1044 'short mail address' => ['a@b.c'],
1045 'simple mail address' => ['test@example.com'],
1046 'uppercase characters' => ['QWERTYUIOPASDFGHJKLZXCVBNM@QWERTYUIOPASDFGHJKLZXCVBNM.NET'],
1047 'equal sign in local part' => ['test=mail@example.com'],
1048 'dash in local part' => ['test-mail@example.com'],
1049 'plus in local part' => ['test+mail@example.com'],
1050 'question mark in local part' => ['test?mail@example.com'],
1051 'slash in local part' => ['foo/bar@example.com'],
1052 'hash in local part' => ['foo#bar@example.com'],
1053 'dot in local part' => ['firstname.lastname@employee.2something.com'],
1054 'dash as local part' => ['-@foo.com'],
1055 'umlauts in domain part' => ['foo@äöüfoo.com']
1056 ];
1057 }
1058
1059 /**
1060 * @test
1061 * @dataProvider validEmailValidDataProvider
1062 */
1063 public function validEmailReturnsTrueForValidMailAddress($address)
1064 {
1065 $this->assertTrue(GeneralUtility::validEmail($address));
1066 }
1067
1068 /**
1069 * Data provider for invalid validEmail's
1070 *
1071 * @return array Invalid email addresses
1072 */
1073 public function validEmailInvalidDataProvider()
1074 {
1075 return [
1076 'empty string' => [''],
1077 'empty array' => [[]],
1078 'integer' => [42],
1079 'float' => [42.23],
1080 'array' => [['foo']],
1081 'object' => [new \stdClass()],
1082 '@ sign only' => ['@'],
1083 'string longer than 320 characters' => [str_repeat('0123456789', 33)],
1084 'duplicate @' => ['test@@example.com'],
1085 'duplicate @ combined with further special characters in local part' => ['test!.!@#$%^&*@example.com'],
1086 'opening parenthesis in local part' => ['foo(bar@example.com'],
1087 'closing parenthesis in local part' => ['foo)bar@example.com'],
1088 'opening square bracket in local part' => ['foo[bar@example.com'],
1089 'closing square bracket as local part' => [']@example.com'],
1090 'top level domain only' => ['test@com'],
1091 'dash as second level domain' => ['foo@-.com'],
1092 'domain part starting with dash' => ['foo@-foo.com'],
1093 'domain part ending with dash' => ['foo@foo-.com'],
1094 'number as top level domain' => ['foo@bar.123'],
1095 'dot at beginning of domain part' => ['test@.com'],
1096 'local part ends with dot' => ['e.x.a.m.p.l.e.@example.com'],
1097 'umlauts in local part' => ['äöüfoo@bar.com'],
1098 'trailing whitespace' => ['test@example.com '],
1099 'trailing carriage return' => ['test@example.com' . CR],
1100 'trailing linefeed' => ['test@example.com' . LF],
1101 'trailing carriage return linefeed' => ['test@example.com' . CRLF],
1102 'trailing tab' => ['test@example.com' . TAB],
1103 'prohibited input characters' => ['“mailto:test@example.com”'],
1104 ];
1105 }
1106
1107 /**
1108 * @test
1109 * @dataProvider validEmailInvalidDataProvider
1110 */
1111 public function validEmailReturnsFalseForInvalidMailAddress($address)
1112 {
1113 $this->assertFalse(GeneralUtility::validEmail($address));
1114 }
1115
1116 //////////////////////////////////
1117 // Tests concerning intExplode
1118 //////////////////////////////////
1119 /**
1120 * @test
1121 */
1122 public function intExplodeConvertsStringsToInteger()
1123 {
1124 $testString = '1,foo,2';
1125 $expectedArray = [1, 0, 2];
1126 $actualArray = GeneralUtility::intExplode(',', $testString);
1127 $this->assertEquals($expectedArray, $actualArray);
1128 }
1129
1130 //////////////////////////////////
1131 // Tests concerning implodeArrayForUrl / explodeUrl2Array
1132 //////////////////////////////////
1133 /**
1134 * Data provider for implodeArrayForUrlBuildsValidParameterString and
1135 * explodeUrl2ArrayTransformsParameterStringToArray
1136 *
1137 * @return array
1138 */
1139 public function implodeArrayForUrlDataProvider()
1140 {
1141 $valueArray = ['one' => '√', 'two' => 2];
1142 return [
1143 'Empty input' => ['foo', [], ''],
1144 'String parameters' => ['foo', $valueArray, '&foo[one]=%E2%88%9A&foo[two]=2'],
1145 'Nested array parameters' => ['foo', [$valueArray], '&foo[0][one]=%E2%88%9A&foo[0][two]=2'],
1146 'Keep blank parameters' => ['foo', ['one' => '√', ''], '&foo[one]=%E2%88%9A&foo[0]=']
1147 ];
1148 }
1149
1150 /**
1151 * @test
1152 * @dataProvider implodeArrayForUrlDataProvider
1153 */
1154 public function implodeArrayForUrlBuildsValidParameterString($name, $input, $expected)
1155 {
1156 $this->assertSame($expected, GeneralUtility::implodeArrayForUrl($name, $input));
1157 }
1158
1159 /**
1160 * @test
1161 */
1162 public function implodeArrayForUrlCanSkipEmptyParameters()
1163 {
1164 $input = ['one' => '√', ''];
1165 $expected = '&foo[one]=%E2%88%9A';
1166 $this->assertSame($expected, GeneralUtility::implodeArrayForUrl('foo', $input, '', true));
1167 }
1168
1169 /**
1170 * @test
1171 */
1172 public function implodeArrayForUrlCanUrlEncodeKeyNames()
1173 {
1174 $input = ['one' => '√', ''];
1175 $expected = '&foo%5Bone%5D=%E2%88%9A&foo%5B0%5D=';
1176 $this->assertSame($expected, GeneralUtility::implodeArrayForUrl('foo', $input, '', false, true));
1177 }
1178
1179 /**
1180 * @test
1181 * @dataProvider implodeArrayForUrlDataProvider
1182 */
1183 public function explodeUrl2ArrayTransformsParameterStringToNestedArray($name, $array, $input)
1184 {
1185 $expected = $array ? [$name => $array] : [];
1186 $this->assertEquals($expected, GeneralUtility::explodeUrl2Array($input, true));
1187 }
1188
1189 /**
1190 * @test
1191 * @dataProvider explodeUrl2ArrayDataProvider
1192 */
1193 public function explodeUrl2ArrayTransformsParameterStringToFlatArray($input, $expected)
1194 {
1195 $this->assertEquals($expected, GeneralUtility::explodeUrl2Array($input, false));
1196 }
1197
1198 /**
1199 * Data provider for explodeUrl2ArrayTransformsParameterStringToFlatArray
1200 *
1201 * @return array
1202 */
1203 public function explodeUrl2ArrayDataProvider()
1204 {
1205 return [
1206 'Empty string' => ['', []],
1207 'Simple parameter string' => ['&one=%E2%88%9A&two=2', ['one' => '√', 'two' => 2]],
1208 'Nested parameter string' => ['&foo[one]=%E2%88%9A&two=2', ['foo[one]' => '√', 'two' => 2]]
1209 ];
1210 }
1211
1212 //////////////////////////////////
1213 // Tests concerning compileSelectedGetVarsFromArray
1214 //////////////////////////////////
1215 /**
1216 * @test
1217 */
1218 public function compileSelectedGetVarsFromArrayFiltersIncomingData()
1219 {
1220 $filter = 'foo,bar';
1221 $getArray = ['foo' => 1, 'cake' => 'lie'];
1222 $expected = ['foo' => 1];
1223 $result = GeneralUtility::compileSelectedGetVarsFromArray($filter, $getArray, false);
1224 $this->assertSame($expected, $result);
1225 }
1226
1227 /**
1228 * @test
1229 */
1230 public function compileSelectedGetVarsFromArrayUsesGetPostDataFallback()
1231 {
1232 $_GET['bar'] = '2';
1233 $filter = 'foo,bar';
1234 $getArray = ['foo' => 1, 'cake' => 'lie'];
1235 $expected = ['foo' => 1, 'bar' => '2'];
1236 $result = GeneralUtility::compileSelectedGetVarsFromArray($filter, $getArray, true);
1237 $this->assertSame($expected, $result);
1238 }
1239
1240 //////////////////////////////////
1241 // Tests concerning revExplode
1242 //////////////////////////////////
1243
1244 /**
1245 * @return array
1246 */
1247 public function revExplodeDataProvider()
1248 {
1249 return [
1250 'limit 0 should return unexploded string' => [
1251 ':',
1252 'my:words:here',
1253 0,
1254 ['my:words:here']
1255 ],
1256 'limit 1 should return unexploded string' => [
1257 ':',
1258 'my:words:here',
1259 1,
1260 ['my:words:here']
1261 ],
1262 'limit 2 should return two pieces' => [
1263 ':',
1264 'my:words:here',
1265 2,
1266 ['my:words', 'here']
1267 ],
1268 'limit 3 should return unexploded string' => [
1269 ':',
1270 'my:words:here',
1271 3,
1272 ['my', 'words', 'here']
1273 ],
1274 'limit 0 should return unexploded string if no delimiter is contained' => [
1275 ':',
1276 'mywordshere',
1277 0,
1278 ['mywordshere']
1279 ],
1280 'limit 1 should return unexploded string if no delimiter is contained' => [
1281 ':',
1282 'mywordshere',
1283 1,
1284 ['mywordshere']
1285 ],
1286 'limit 2 should return unexploded string if no delimiter is contained' => [
1287 ':',
1288 'mywordshere',
1289 2,
1290 ['mywordshere']
1291 ],
1292 'limit 3 should return unexploded string if no delimiter is contained' => [
1293 ':',
1294 'mywordshere',
1295 3,
1296 ['mywordshere']
1297 ],
1298 'multi character delimiter is handled properly with limit 2' => [
1299 '[]',
1300 'a[b][c][d]',
1301 2,
1302 ['a[b][c', 'd]']
1303 ],
1304 'multi character delimiter is handled properly with limit 3' => [
1305 '[]',
1306 'a[b][c][d]',
1307 3,
1308 ['a[b', 'c', 'd]']
1309 ],
1310 ];
1311 }
1312
1313 /**
1314 * @test
1315 * @dataProvider revExplodeDataProvider
1316 */
1317 public function revExplodeCorrectlyExplodesStringForGivenPartsCount($delimiter, $testString, $count, $expectedArray)
1318 {
1319 $actualArray = GeneralUtility::revExplode($delimiter, $testString, $count);
1320 $this->assertEquals($expectedArray, $actualArray);
1321 }
1322
1323 /**
1324 * @test
1325 */
1326 public function revExplodeRespectsLimitThreeWhenExploding()
1327 {
1328 $testString = 'even:more:of:my:words:here';
1329 $expectedArray = ['even:more:of:my', 'words', 'here'];
1330 $actualArray = GeneralUtility::revExplode(':', $testString, 3);
1331 $this->assertEquals($expectedArray, $actualArray);
1332 }
1333
1334 //////////////////////////////////
1335 // Tests concerning trimExplode
1336 //////////////////////////////////
1337 /**
1338 * @test
1339 * @dataProvider trimExplodeReturnsCorrectResultDataProvider
1340 *
1341 * @param string $delimiter
1342 * @param string $testString
1343 * @param bool $removeEmpty
1344 * @param int $limit
1345 * @param array $expectedResult
1346 */
1347 public function trimExplodeReturnsCorrectResult($delimiter, $testString, $removeEmpty, $limit, $expectedResult)
1348 {
1349 $this->assertSame($expectedResult, GeneralUtility::trimExplode($delimiter, $testString, $removeEmpty, $limit));
1350 }
1351
1352 /**
1353 * @return array
1354 */
1355 public function trimExplodeReturnsCorrectResultDataProvider()
1356 {
1357 return [
1358 'spaces at element start and end' => [
1359 ',',
1360 ' a , b , c ,d ,, e,f,',
1361 false,
1362 0,
1363 ['a', 'b', 'c', 'd', '', 'e', 'f', '']
1364 ],
1365 'removes newline' => [
1366 ',',
1367 ' a , b , ' . LF . ' ,d ,, e,f,',
1368 true,
1369 0,
1370 ['a', 'b', 'd', 'e', 'f']
1371 ],
1372 'removes empty elements' => [
1373 ',',
1374 'a , b , c , ,d ,, ,e,f,',
1375 true,
1376 0,
1377 ['a', 'b', 'c', 'd', 'e', 'f']
1378 ],
1379 'keeps remaining results with empty items after reaching limit with positive parameter' => [
1380 ',',
1381 ' a , b , c , , d,, ,e ',
1382 false,
1383 3,
1384 ['a', 'b', 'c , , d,, ,e']
1385 ],
1386 'keeps remaining results without empty items after reaching limit with positive parameter' => [
1387 ',',
1388 ' a , b , c , , d,, ,e ',
1389 true,
1390 3,
1391 ['a', 'b', 'c , d,e']
1392 ],
1393 'keeps remaining results with empty items after reaching limit with negative parameter' => [
1394 ',',
1395 ' a , b , c , d, ,e, f , , ',
1396 false,
1397 -3,
1398 ['a', 'b', 'c', 'd', '', 'e']
1399 ],
1400 'keeps remaining results without empty items after reaching limit with negative parameter' => [
1401 ',',
1402 ' a , b , c , d, ,e, f , , ',
1403 true,
1404 -3,
1405 ['a', 'b', 'c']
1406 ],
1407 'returns exact results without reaching limit with positive parameter' => [
1408 ',',
1409 ' a , b , , c , , , ',
1410 true,
1411 4,
1412 ['a', 'b', 'c']
1413 ],
1414 'keeps zero as string' => [
1415 ',',
1416 'a , b , c , ,d ,, ,e,f, 0 ,',
1417 true,
1418 0,
1419 ['a', 'b', 'c', 'd', 'e', 'f', '0']
1420 ],
1421 'keeps whitespace inside elements' => [
1422 ',',
1423 'a , b , c , ,d ,, ,e,f, g h ,',
1424 true,
1425 0,
1426 ['a', 'b', 'c', 'd', 'e', 'f', 'g h']
1427 ],
1428 'can use internal regex delimiter as explode delimiter' => [
1429 '/',
1430 'a / b / c / /d // /e/f/ g h /',
1431 true,
1432 0,
1433 ['a', 'b', 'c', 'd', 'e', 'f', 'g h']
1434 ],
1435 'can use whitespaces as delimiter' => [
1436 ' ',
1437 '* * * * *',
1438 true,
1439 0,
1440 ['*', '*', '*', '*', '*']
1441 ],
1442 'can use words as delimiter' => [
1443 'All',
1444 'HelloAllTogether',
1445 true,
1446 0,
1447 ['Hello', 'Together']
1448 ],
1449 'can use word with appended and prepended spaces as delimiter' => [
1450 ' all ',
1451 'Hello all together',
1452 true,
1453 0,
1454 ['Hello', 'together']
1455 ],
1456 'can use word with appended and prepended spaces as delimiter and do not remove empty' => [
1457 ' all ',
1458 'Hello all together all there all all are all none',
1459 false,
1460 0,
1461 ['Hello', 'together', 'there', '', 'are', 'none']
1462 ],
1463 'can use word with appended and prepended spaces as delimiter, do not remove empty and limit' => [
1464 ' all ',
1465 'Hello all together all there all all are all none',
1466 false,
1467 5,
1468 ['Hello', 'together', 'there', '', 'are all none']
1469 ],
1470 'can use word with appended and prepended spaces as delimiter, do not remove empty, limit and multiple delimiter in last' => [
1471 ' all ',
1472 'Hello all together all there all all are all none',
1473 false,
1474 4,
1475 ['Hello', 'together', 'there', 'all are all none']
1476 ],
1477 'can use word with appended and prepended spaces as delimiter, remove empty and limit' => [
1478 ' all ',
1479 'Hello all together all there all all are all none',
1480 true,
1481 4,
1482 ['Hello', 'together', 'there', 'are all none']
1483 ],
1484 'can use word with appended and prepended spaces as delimiter, remove empty and limit and multiple delimiter in last' => [
1485 ' all ',
1486 'Hello all together all there all all are all none',
1487 true,
1488 5,
1489 ['Hello', 'together', 'there', 'are' , 'none']
1490 ],
1491 'can use words as delimiter and do not remove empty' => [
1492 'all there',
1493 'Helloall theretogether all there all there are all there none',
1494 false,
1495 0,
1496 ['Hello', 'together', '', 'are', 'none']
1497 ],
1498 'can use words as delimiter, do not remove empty and limit' => [
1499 'all there',
1500 'Helloall theretogether all there all there are all there none',
1501 false,
1502 4,
1503 ['Hello', 'together', '', 'are all there none']
1504 ],
1505 'can use words as delimiter, do not remove empty, limit and multiple delimiter in last' => [
1506 'all there',
1507 'Helloall theretogether all there all there are all there none',
1508 false,
1509 3,
1510 ['Hello', 'together', 'all there are all there none']
1511 ],
1512 'can use words as delimiter, remove empty' => [
1513 'all there',
1514 'Helloall theretogether all there all there are all there none',
1515 true,
1516 0,
1517 ['Hello', 'together', 'are', 'none']
1518 ],
1519 'can use words as delimiter, remove empty and limit' => [
1520 'all there',
1521 'Helloall theretogether all there all there are all there none',
1522 true,
1523 3,
1524 ['Hello', 'together', 'are all there none']
1525 ],
1526 'can use words as delimiter, remove empty and limit and multiple delimiter in last' => [
1527 'all there',
1528 'Helloall theretogether all there all there are all there none',
1529 true,
1530 4,
1531 ['Hello', 'together', 'are' , 'none']
1532 ],
1533 'can use new line as delimiter' => [
1534 LF,
1535 "Hello\nall\ntogether",
1536 true,
1537 0,
1538 ['Hello', 'all', 'together']
1539 ],
1540 'works with whitespace separator' => [
1541 "\t",
1542 " a b \t c \t \t d \t e \t u j \t s",
1543 false,
1544 0,
1545 ['a b', 'c', '', 'd', 'e', 'u j', 's']
1546 ],
1547 'works with whitespace separator and limit' => [
1548 "\t",
1549 " a b \t c \t \t d \t e \t u j \t s",
1550 false,
1551 4,
1552 ['a b', 'c', '', "d \t e \t u j \t s"]
1553 ],
1554 'works with whitespace separator and remove empty' => [
1555 "\t",
1556 " a b \t c \t \t d \t e \t u j \t s",
1557 true,
1558 0,
1559 ['a b', 'c', 'd', 'e', 'u j', 's']
1560 ],
1561 'works with whitespace separator remove empty and limit' => [
1562 "\t",
1563 " a b \t c \t \t d \t e \t u j \t s",
1564 true,
1565 3,
1566 ['a b', 'c', "d \t e \t u j \t s"]
1567 ],
1568 ];
1569 }
1570
1571 //////////////////////////////////
1572 // Tests concerning getBytesFromSizeMeasurement
1573 //////////////////////////////////
1574 /**
1575 * Data provider for getBytesFromSizeMeasurement
1576 *
1577 * @return array expected value, input string
1578 */
1579 public function getBytesFromSizeMeasurementDataProvider()
1580 {
1581 return [
1582 '100 kilo Bytes' => ['102400', '100k'],
1583 '100 mega Bytes' => ['104857600', '100m'],
1584 '100 giga Bytes' => ['107374182400', '100g']
1585 ];
1586 }
1587
1588 /**
1589 * @test
1590 * @dataProvider getBytesFromSizeMeasurementDataProvider
1591 */
1592 public function getBytesFromSizeMeasurementCalculatesCorrectByteValue($expected, $byteString)
1593 {
1594 $this->assertEquals($expected, GeneralUtility::getBytesFromSizeMeasurement($byteString));
1595 }
1596
1597 //////////////////////////////////
1598 // Tests concerning getIndpEnv
1599 //////////////////////////////////
1600 /**
1601 * @test
1602 */
1603 public function getIndpEnvTypo3SitePathReturnNonEmptyString()
1604 {
1605 $this->assertTrue(strlen(GeneralUtility::getIndpEnv('TYPO3_SITE_PATH')) >= 1);
1606 }
1607
1608 /**
1609 * @test
1610 */
1611 public function getIndpEnvTypo3SitePathReturnsStringStartingWithSlash()
1612 {
1613 $_SERVER['SCRIPT_NAME'] = '/typo3/';
1614 $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1615 $this->assertEquals('/', $result[0]);
1616 }
1617
1618 /**
1619 * @test
1620 */
1621 public function getIndpEnvTypo3SitePathReturnsStringStartingWithDrive()
1622 {
1623 if (TYPO3_OS !== 'WIN') {
1624 $this->markTestSkipped('Test available only on Windows OS.');
1625 }
1626 $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1627 $this->assertRegExp('/^[a-z]:\//i', $result);
1628 }
1629
1630 /**
1631 * @test
1632 */
1633 public function getIndpEnvTypo3SitePathReturnsStringEndingWithSlash()
1634 {
1635 $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1636 $this->assertEquals('/', $result[strlen($result) - 1]);
1637 }
1638
1639 /**
1640 * @return array
1641 */
1642 public static function hostnameAndPortDataProvider()
1643 {
1644 return [
1645 'localhost ipv4 without port' => ['127.0.0.1', '127.0.0.1', ''],
1646 'localhost ipv4 with port' => ['127.0.0.1:81', '127.0.0.1', '81'],
1647 'localhost ipv6 without port' => ['[::1]', '[::1]', ''],
1648 'localhost ipv6 with port' => ['[::1]:81', '[::1]', '81'],
1649 'ipv6 without port' => ['[2001:DB8::1]', '[2001:DB8::1]', ''],
1650 'ipv6 with port' => ['[2001:DB8::1]:81', '[2001:DB8::1]', '81'],
1651 'hostname without port' => ['lolli.did.this', 'lolli.did.this', ''],
1652 'hostname with port' => ['lolli.did.this:42', 'lolli.did.this', '42']
1653 ];
1654 }
1655
1656 /**
1657 * @test
1658 * @dataProvider hostnameAndPortDataProvider
1659 */
1660 public function getIndpEnvTypo3HostOnlyParsesHostnamesAndIpAdresses($httpHost, $expectedIp)
1661 {
1662 GeneralUtility::flushInternalRuntimeCaches();
1663 $_SERVER['HTTP_HOST'] = $httpHost;
1664 $this->assertEquals($expectedIp, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
1665 }
1666
1667 /**
1668 * @test
1669 */
1670 public function isAllowedHostHeaderValueReturnsFalseIfTrusedHostsIsNotConfigured()
1671 {
1672 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern']);
1673 $this->assertFalse(GeneralUtilityFixture::isAllowedHostHeaderValue('evil.foo.bar'));
1674 }
1675
1676 /**
1677 * @return array
1678 */
1679 public static function hostnamesMatchingTrustedHostsConfigurationDataProvider()
1680 {
1681 return [
1682 'hostname without port matching' => ['lolli.did.this', '.*\.did\.this'],
1683 'other hostname without port matching' => ['helmut.did.this', '.*\.did\.this'],
1684 'two different hostnames without port matching 1st host' => ['helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1685 'two different hostnames without port matching 2nd host' => ['lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1686 'hostname with port matching' => ['lolli.did.this:42', '.*\.did\.this:42'],
1687 'hostnames are case insensitive 1' => ['lolli.DID.this:42', '.*\.did.this:42'],
1688 'hostnames are case insensitive 2' => ['lolli.did.this:42', '.*\.DID.this:42'],
1689 ];
1690 }
1691
1692 /**
1693 * @return array
1694 */
1695 public static function hostnamesNotMatchingTrustedHostsConfigurationDataProvider()
1696 {
1697 return [
1698 'hostname without port' => ['lolli.did.this', 'helmut\.did\.this'],
1699 'hostname with port, but port not allowed' => ['lolli.did.this:42', 'helmut\.did\.this'],
1700 'two different hostnames in pattern but host header starts with different value #1' => ['sub.helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1701 'two different hostnames in pattern but host header starts with different value #2' => ['sub.lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1702 'two different hostnames in pattern but host header ends with different value #1' => ['helmut.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'],
1703 'two different hostnames in pattern but host header ends with different value #2' => ['lolli.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'],
1704 ];
1705 }
1706
1707 /**
1708 * @param string $httpHost HTTP_HOST string
1709 * @param string $hostNamePattern trusted hosts pattern
1710 * @test
1711 * @dataProvider hostnamesMatchingTrustedHostsConfigurationDataProvider
1712 */
1713 public function isAllowedHostHeaderValueReturnsTrueIfHostValueMatches($httpHost, $hostNamePattern)
1714 {
1715 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1716 $this->assertTrue(GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1717 }
1718
1719 /**
1720 * @param string $httpHost HTTP_HOST string
1721 * @param string $hostNamePattern trusted hosts pattern
1722 * @test
1723 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1724 */
1725 public function isAllowedHostHeaderValueReturnsFalseIfHostValueMatches($httpHost, $hostNamePattern)
1726 {
1727 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1728 $this->assertFalse(GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1729 }
1730
1731 public function serverNamePatternDataProvider()
1732 {
1733 return [
1734 'host value matches server name and server port is default http' => [
1735 'httpHost' => 'secure.web.server',
1736 'serverName' => 'secure.web.server',
1737 'isAllowed' => true,
1738 'serverPort' => '80',
1739 'ssl' => 'Off',
1740 ],
1741 'host value matches server name if compared case insensitive 1' => [
1742 'httpHost' => 'secure.web.server',
1743 'serverName' => 'secure.WEB.server',
1744 'isAllowed' => true,
1745 ],
1746 'host value matches server name if compared case insensitive 2' => [
1747 'httpHost' => 'secure.WEB.server',
1748 'serverName' => 'secure.web.server',
1749 'isAllowed' => true,
1750 ],
1751 'host value matches server name and server port is default https' => [
1752 'httpHost' => 'secure.web.server',
1753 'serverName' => 'secure.web.server',
1754 'isAllowed' => true,
1755 'serverPort' => '443',
1756 'ssl' => 'On',
1757 ],
1758 'host value matches server name and server port' => [
1759 'httpHost' => 'secure.web.server:88',
1760 'serverName' => 'secure.web.server',
1761 'isAllowed' => true,
1762 'serverPort' => '88',
1763 ],
1764 'host value matches server name case insensitive 1 and server port' => [
1765 'httpHost' => 'secure.WEB.server:88',
1766 'serverName' => 'secure.web.server',
1767 'isAllowed' => true,
1768 'serverPort' => '88',
1769 ],
1770 'host value matches server name case insensitive 2 and server port' => [
1771 'httpHost' => 'secure.web.server:88',
1772 'serverName' => 'secure.WEB.server',
1773 'isAllowed' => true,
1774 'serverPort' => '88',
1775 ],
1776 'host value is ipv6 but matches server name and server port' => [
1777 'httpHost' => '[::1]:81',
1778 'serverName' => '[::1]',
1779 'isAllowed' => true,
1780 'serverPort' => '81',
1781 ],
1782 'host value does not match server name' => [
1783 'httpHost' => 'insecure.web.server',
1784 'serverName' => 'secure.web.server',
1785 'isAllowed' => false,
1786 ],
1787 'host value does not match server port' => [
1788 'httpHost' => 'secure.web.server:88',
1789 'serverName' => 'secure.web.server',
1790 'isAllowed' => false,
1791 'serverPort' => '89',
1792 ],
1793 'host value has default port that does not match server port' => [
1794 'httpHost' => 'secure.web.server',
1795 'serverName' => 'secure.web.server',
1796 'isAllowed' => false,
1797 'serverPort' => '81',
1798 'ssl' => 'Off',
1799 ],
1800 'host value has default port that does not match server ssl port' => [
1801 'httpHost' => 'secure.web.server',
1802 'serverName' => 'secure.web.server',
1803 'isAllowed' => false,
1804 'serverPort' => '444',
1805 'ssl' => 'On',
1806 ],
1807 ];
1808 }
1809
1810 /**
1811 * @param string $httpHost
1812 * @param string $serverName
1813 * @param bool $isAllowed
1814 * @param string $serverPort
1815 * @param string $ssl
1816 *
1817 * @test
1818 * @dataProvider serverNamePatternDataProvider
1819 */
1820 public function isAllowedHostHeaderValueWorksCorrectlyWithWithServerNamePattern($httpHost, $serverName, $isAllowed, $serverPort = '80', $ssl = 'Off')
1821 {
1822 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME;
1823 $_SERVER['SERVER_NAME'] = $serverName;
1824 $_SERVER['SERVER_PORT'] = $serverPort;
1825 $_SERVER['HTTPS'] = $ssl;
1826 $this->assertSame($isAllowed, GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1827 }
1828
1829 /**
1830 * @test
1831 */
1832 public function allGetIndpEnvCallsRelatedToHostNamesCallIsAllowedHostHeaderValue()
1833 {
1834 GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1835 GeneralUtility::flushInternalRuntimeCaches();
1836 GeneralUtilityFixture::getIndpEnv('TYPO3_HOST_ONLY');
1837 GeneralUtility::flushInternalRuntimeCaches();
1838 GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_HOST');
1839 GeneralUtility::flushInternalRuntimeCaches();
1840 GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_URL');
1841 $this->assertSame(4, GeneralUtilityFixture::$isAllowedHostHeaderValueCallCount);
1842 }
1843
1844 /**
1845 * @param string $httpHost HTTP_HOST string
1846 * @param string $hostNamePattern trusted hosts pattern
1847 * @test
1848 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1849 */
1850 public function getIndpEnvForHostThrowsExceptionForNotAllowedHostnameValues($httpHost, $hostNamePattern)
1851 {
1852 $this->expectException(\UnexpectedValueException::class);
1853 $this->expectExceptionCode(1396795884);
1854
1855 $_SERVER['HTTP_HOST'] = $httpHost;
1856 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1857 GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1858 }
1859
1860 /**
1861 * @param string $httpHost HTTP_HOST string
1862 * @param string $hostNamePattern trusted hosts pattern (not used in this test currently)
1863 * @test
1864 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1865 */
1866 public function getIndpEnvForHostAllowsAllHostnameValuesIfHostPatternIsSetToAllowAll($httpHost, $hostNamePattern)
1867 {
1868 $_SERVER['HTTP_HOST'] = $httpHost;
1869 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
1870 $this->assertSame($httpHost, GeneralUtility::getIndpEnv('HTTP_HOST'));
1871 }
1872
1873 /**
1874 * @test
1875 * @dataProvider hostnameAndPortDataProvider
1876 */
1877 public function getIndpEnvTypo3PortParsesHostnamesAndIpAdresses($httpHost, $dummy, $expectedPort)
1878 {
1879 $_SERVER['HTTP_HOST'] = $httpHost;
1880 $this->assertEquals($expectedPort, GeneralUtility::getIndpEnv('TYPO3_PORT'));
1881 }
1882
1883 //////////////////////////////////
1884 // Tests concerning underscoredToUpperCamelCase
1885 //////////////////////////////////
1886 /**
1887 * Data provider for underscoredToUpperCamelCase
1888 *
1889 * @return array expected, input string
1890 */
1891 public function underscoredToUpperCamelCaseDataProvider()
1892 {
1893 return [
1894 'single word' => ['Blogexample', 'blogexample'],
1895 'multiple words' => ['BlogExample', 'blog_example']
1896 ];
1897 }
1898
1899 /**
1900 * @test
1901 * @dataProvider underscoredToUpperCamelCaseDataProvider
1902 */
1903 public function underscoredToUpperCamelCase($expected, $inputString)
1904 {
1905 $this->assertEquals($expected, GeneralUtility::underscoredToUpperCamelCase($inputString));
1906 }
1907
1908 //////////////////////////////////
1909 // Tests concerning underscoredToLowerCamelCase
1910 //////////////////////////////////
1911 /**
1912 * Data provider for underscoredToLowerCamelCase
1913 *
1914 * @return array expected, input string
1915 */
1916 public function underscoredToLowerCamelCaseDataProvider()
1917 {
1918 return [
1919 'single word' => ['minimalvalue', 'minimalvalue'],
1920 'multiple words' => ['minimalValue', 'minimal_value']
1921 ];
1922 }
1923
1924 /**
1925 * @test
1926 * @dataProvider underscoredToLowerCamelCaseDataProvider
1927 */
1928 public function underscoredToLowerCamelCase($expected, $inputString)
1929 {
1930 $this->assertEquals($expected, GeneralUtility::underscoredToLowerCamelCase($inputString));
1931 }
1932
1933 //////////////////////////////////
1934 // Tests concerning camelCaseToLowerCaseUnderscored
1935 //////////////////////////////////
1936 /**
1937 * Data provider for camelCaseToLowerCaseUnderscored
1938 *
1939 * @return array expected, input string
1940 */
1941 public function camelCaseToLowerCaseUnderscoredDataProvider()
1942 {
1943 return [
1944 'single word' => ['blogexample', 'blogexample'],
1945 'single word starting upper case' => ['blogexample', 'Blogexample'],
1946 'two words starting lower case' => ['minimal_value', 'minimalValue'],
1947 'two words starting upper case' => ['blog_example', 'BlogExample']
1948 ];
1949 }
1950
1951 /**
1952 * @test
1953 * @dataProvider camelCaseToLowerCaseUnderscoredDataProvider
1954 */
1955 public function camelCaseToLowerCaseUnderscored($expected, $inputString)
1956 {
1957 $this->assertEquals($expected, GeneralUtility::camelCaseToLowerCaseUnderscored($inputString));
1958 }
1959
1960 //////////////////////////////////
1961 // Tests concerning lcFirst
1962 //////////////////////////////////
1963 /**
1964 * Data provider for lcFirst
1965 *
1966 * @return array expected, input string
1967 */
1968 public function lcfirstDataProvider()
1969 {
1970 return [
1971 'single word' => ['blogexample', 'blogexample'],
1972 'single Word starting upper case' => ['blogexample', 'Blogexample'],
1973 'two words' => ['blogExample', 'BlogExample']
1974 ];
1975 }
1976
1977 /**
1978 * @test
1979 * @dataProvider lcfirstDataProvider
1980 */
1981 public function lcFirst($expected, $inputString)
1982 {
1983 $this->assertEquals($expected, GeneralUtility::lcfirst($inputString));
1984 }
1985
1986 //////////////////////////////////
1987 // Tests concerning isValidUrl
1988 //////////////////////////////////
1989 /**
1990 * Data provider for valid isValidUrl's
1991 *
1992 * @return array Valid resource
1993 */
1994 public function validUrlValidResourceDataProvider()
1995 {
1996 return [
1997 'http' => ['http://www.example.org/'],
1998 'http without trailing slash' => ['http://qwe'],
1999 'http directory with trailing slash' => ['http://www.example/img/dir/'],
2000 'http directory without trailing slash' => ['http://www.example/img/dir'],
2001 'http index.html' => ['http://example.com/index.html'],
2002 'http index.php' => ['http://www.example.com/index.php'],
2003 'http test.png' => ['http://www.example/img/test.png'],
2004 'http username password querystring and ancher' => ['https://user:pw@www.example.org:80/path?arg=value#fragment'],
2005 'file' => ['file:///tmp/test.c'],
2006 'file directory' => ['file://foo/bar'],
2007 'ftp directory' => ['ftp://ftp.example.com/tmp/'],
2008 'mailto' => ['mailto:foo@bar.com'],
2009 'news' => ['news:news.php.net'],
2010 'telnet' => ['telnet://192.0.2.16:80/'],
2011 'ldap' => ['ldap://[2001:db8::7]/c=GB?objectClass?one'],
2012 'http punycode domain name' => ['http://www.xn--bb-eka.at'],
2013 'http punicode subdomain' => ['http://xn--h-zfa.oebb.at'],
2014 'http domain-name umlauts' => ['http://www.öbb.at'],
2015 'http subdomain umlauts' => ['http://äh.oebb.at'],
2016 ];
2017 }
2018
2019 /**
2020 * @test
2021 * @dataProvider validUrlValidResourceDataProvider
2022 */
2023 public function validURLReturnsTrueForValidResource($url)
2024 {
2025 $this->assertTrue(GeneralUtility::isValidUrl($url));
2026 }
2027
2028 /**
2029 * Data provider for invalid isValidUrl's
2030 *
2031 * @return array Invalid ressource
2032 */
2033 public function isValidUrlInvalidRessourceDataProvider()
2034 {
2035 return [
2036 'http missing colon' => ['http//www.example/wrong/url/'],
2037 'http missing slash' => ['http:/www.example'],
2038 'hostname only' => ['www.example.org/'],
2039 'file missing protocol specification' => ['/tmp/test.c'],
2040 'slash only' => ['/'],
2041 'string http://' => ['http://'],
2042 'string http:/' => ['http:/'],
2043 'string http:' => ['http:'],
2044 'string http' => ['http'],
2045 'empty string' => [''],
2046 'string -1' => ['-1'],
2047 'string array()' => ['array()'],
2048 'random string' => ['qwe'],
2049 'http directory umlauts' => ['http://www.oebb.at/äöü/'],
2050 'prohibited input characters' => ['https://{$unresolved_constant}'],
2051 ];
2052 }
2053
2054 /**
2055 * @test
2056 * @dataProvider isValidUrlInvalidRessourceDataProvider
2057 */
2058 public function validURLReturnsFalseForInvalidRessoure($url)
2059 {
2060 $this->assertFalse(GeneralUtility::isValidUrl($url));
2061 }
2062
2063 //////////////////////////////////
2064 // Tests concerning isOnCurrentHost
2065 //////////////////////////////////
2066 /**
2067 * @test
2068 */
2069 public function isOnCurrentHostReturnsTrueWithCurrentHost()
2070 {
2071 $testUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
2072 $this->assertTrue(GeneralUtility::isOnCurrentHost($testUrl));
2073 }
2074
2075 /**
2076 * Data provider for invalid isOnCurrentHost's
2077 *
2078 * @return array Invalid Hosts
2079 */
2080 public function checkisOnCurrentHostInvalidHosts()
2081 {
2082 return [
2083 'empty string' => [''],
2084 'arbitrary string' => ['arbitrary string'],
2085 'localhost IP' => ['127.0.0.1'],
2086 'relative path' => ['./relpath/file.txt'],
2087 'absolute path' => ['/abspath/file.txt?arg=value'],
2088 'differnt host' => [GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . '.example.org']
2089 ];
2090 }
2091
2092 ////////////////////////////////////////
2093 // Tests concerning sanitizeLocalUrl
2094 ////////////////////////////////////////
2095 /**
2096 * Data provider for valid sanitizeLocalUrl paths
2097 *
2098 * @return array Valid url
2099 */
2100 public function sanitizeLocalUrlValidPathsDataProvider()
2101 {
2102 return [
2103 'alt_intro.php' => ['alt_intro.php'],
2104 'alt_intro.php?foo=1&bar=2' => ['alt_intro.php?foo=1&bar=2'],
2105 '../index.php' => ['../index.php'],
2106 '../typo3/alt_intro.php' => ['../typo3/alt_intro.php'],
2107 '../~userDirectory/index.php' => ['../~userDirectory/index.php'],
2108 '../typo3/index.php?var1=test-case&var2=~user' => ['../typo3/index.php?var1=test-case&var2=~user'],
2109 PATH_site . 'typo3/alt_intro.php' => [PATH_site . 'typo3/alt_intro.php'],
2110 ];
2111 }
2112
2113 /**
2114 * @test
2115 * @param string $path
2116 * @dataProvider sanitizeLocalUrlValidPathsDataProvider
2117 */
2118 public function sanitizeLocalUrlAcceptsNotEncodedValidPaths($path)
2119 {
2120 $this->assertEquals($path, GeneralUtility::sanitizeLocalUrl($path));
2121 }
2122
2123 /**
2124 * @test
2125 * @param string $path
2126 * @dataProvider sanitizeLocalUrlValidPathsDataProvider
2127 */
2128 public function sanitizeLocalUrlAcceptsEncodedValidPaths($path)
2129 {
2130 $this->assertEquals(rawurlencode($path), GeneralUtility::sanitizeLocalUrl(rawurlencode($path)));
2131 }
2132
2133 /**
2134 * Data provider for valid sanitizeLocalUrl's
2135 *
2136 * @return array Valid url
2137 */
2138 public function sanitizeLocalUrlValidUrlsDataProvider()
2139 {
2140 $host = 'localhost';
2141 $subDirectory = '/cms/';
2142
2143 return [
2144 $subDirectory . 'typo3/alt_intro.php' => [
2145 $subDirectory . 'typo3/alt_intro.php',
2146 $host,
2147 $subDirectory,
2148 ],
2149 $subDirectory . 'index.php' => [
2150 $subDirectory . 'index.php',
2151 $host,
2152 $subDirectory,
2153 ],
2154 'http://' . $host . '/typo3/alt_intro.php' => [
2155 'http://' . $host . '/typo3/alt_intro.php',
2156 $host,
2157 '',
2158 ],
2159 'http://' . $host . $subDirectory . 'typo3/alt_intro.php' => [
2160 'http://' . $host . $subDirectory . 'typo3/alt_intro.php',
2161 $host,
2162 $subDirectory,
2163 ],
2164 ];
2165 }
2166
2167 /**
2168 * @test
2169 * @param string $url
2170 * @param string $host
2171 * @param string $subDirectory
2172 * @dataProvider sanitizeLocalUrlValidUrlsDataProvider
2173 */
2174 public function sanitizeLocalUrlAcceptsNotEncodedValidUrls($url, $host, $subDirectory)
2175 {
2176 $_SERVER['HTTP_HOST'] = $host;
2177 $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2178 GeneralUtility::flushInternalRuntimeCaches();
2179 $this->assertEquals($url, GeneralUtility::sanitizeLocalUrl($url));
2180 }
2181
2182 /**
2183 * @test
2184 * @param string $url
2185 * @param string $host
2186 * @param string $subDirectory
2187 * @dataProvider sanitizeLocalUrlValidUrlsDataProvider
2188 */
2189 public function sanitizeLocalUrlAcceptsEncodedValidUrls($url, $host, $subDirectory)
2190 {
2191 $_SERVER['HTTP_HOST'] = $host;
2192 $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2193 GeneralUtility::flushInternalRuntimeCaches();
2194 $this->assertEquals(rawurlencode($url), GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2195 }
2196
2197 /**
2198 * Data provider for invalid sanitizeLocalUrl's
2199 *
2200 * @return array Valid url
2201 */
2202 public function sanitizeLocalUrlInvalidDataProvider()
2203 {
2204 return [
2205 'empty string' => [''],
2206 'http domain' => ['http://www.google.de/'],
2207 'https domain' => ['https://www.google.de/'],
2208 'XSS attempt' => ['" onmouseover="alert(123)"'],
2209 'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
2210 'invalid URL, HTML break out attempt' => ['" >blabuubb'],
2211 'base64 encoded string' => ['data:%20text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4='],
2212 ];
2213 }
2214
2215 /**
2216 * @test
2217 * @dataProvider sanitizeLocalUrlInvalidDataProvider
2218 */
2219 public function sanitizeLocalUrlDeniesPlainInvalidUrls($url)
2220 {
2221 $this->assertEquals('', GeneralUtility::sanitizeLocalUrl($url));
2222 }
2223
2224 /**
2225 * @test
2226 * @dataProvider sanitizeLocalUrlInvalidDataProvider
2227 */
2228 public function sanitizeLocalUrlDeniesEncodedInvalidUrls($url)
2229 {
2230 $this->assertEquals('', GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2231 }
2232
2233 ////////////////////////////////////////
2234 // Tests concerning unlink_tempfile
2235 ////////////////////////////////////////
2236
2237 /**
2238 * @test
2239 */
2240 public function unlink_tempfileRemovesValidFileInTypo3temp()
2241 {
2242 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2243 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2244 @copy($fixtureFile, $testFilename);
2245 GeneralUtility::unlink_tempfile($testFilename);
2246 $fileExists = file_exists($testFilename);
2247 $this->assertFalse($fileExists);
2248 }
2249
2250 /**
2251 * @test
2252 */
2253 public function unlink_tempfileRemovesHiddenFile()
2254 {
2255 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2256 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('.test_') . '.gif';
2257 @copy($fixtureFile, $testFilename);
2258 GeneralUtility::unlink_tempfile($testFilename);
2259 $fileExists = file_exists($testFilename);
2260 $this->assertFalse($fileExists);
2261 }
2262
2263 /**
2264 * @test
2265 */
2266 public function unlink_tempfileReturnsTrueIfFileWasRemoved()
2267 {
2268 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2269 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2270 @copy($fixtureFile, $testFilename);
2271 $returnValue = GeneralUtility::unlink_tempfile($testFilename);
2272 $this->assertTrue($returnValue);
2273 }
2274
2275 /**
2276 * @test
2277 */
2278 public function unlink_tempfileReturnsNullIfFileDoesNotExist()
2279 {
2280 $returnValue = GeneralUtility::unlink_tempfile(PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('i_do_not_exist'));
2281 $this->assertNull($returnValue);
2282 }
2283
2284 /**
2285 * @test
2286 */
2287 public function unlink_tempfileReturnsNullIfFileIsNowWithinTypo3temp()
2288 {
2289 $returnValue = GeneralUtility::unlink_tempfile('/tmp/typo3-unit-test-unlink_tempfile');
2290 $this->assertNull($returnValue);
2291 }
2292
2293 //////////////////////////////////////
2294 // Tests concerning tempnam
2295 //////////////////////////////////////
2296
2297 /**
2298 * @test
2299 */
2300 public function tempnamReturnsPathStartingWithGivenPrefix()
2301 {
2302 $filePath = GeneralUtility::tempnam('foo');
2303 $this->testFilesToDelete[] = $filePath;
2304 $fileName = basename($filePath);
2305 $this->assertStringStartsWith('foo', $fileName);
2306 }
2307
2308 /**
2309 * @test
2310 */
2311 public function tempnamReturnsPathWithoutBackslashes()
2312 {
2313 $filePath = GeneralUtility::tempnam('foo');
2314 $this->testFilesToDelete[] = $filePath;
2315 $this->assertNotContains('\\', $filePath);
2316 }
2317
2318 /**
2319 * @test
2320 */
2321 public function tempnamReturnsAbsolutePathInsideDocumentRoot()
2322 {
2323 $filePath = GeneralUtility::tempnam('foo');
2324 $this->testFilesToDelete[] = $filePath;
2325 $this->assertStringStartsWith(PATH_site, $filePath);
2326 }
2327
2328 //////////////////////////////////////
2329 // Tests concerning removeDotsFromTS
2330 //////////////////////////////////////
2331 /**
2332 * @test
2333 */
2334 public function removeDotsFromTypoScriptSucceedsWithDottedArray()
2335 {
2336 $typoScript = [
2337 'propertyA.' => [
2338 'keyA.' => [
2339 'valueA' => 1
2340 ],
2341 'keyB' => 2
2342 ],
2343 'propertyB' => 3
2344 ];
2345 $expectedResult = [
2346 'propertyA' => [
2347 'keyA' => [
2348 'valueA' => 1
2349 ],
2350 'keyB' => 2
2351 ],
2352 'propertyB' => 3
2353 ];
2354 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2355 }
2356
2357 /**
2358 * @test
2359 */
2360 public function removeDotsFromTypoScriptOverridesSubArray()
2361 {
2362 $typoScript = [
2363 'propertyA.' => [
2364 'keyA' => 'getsOverridden',
2365 'keyA.' => [
2366 'valueA' => 1
2367 ],
2368 'keyB' => 2
2369 ],
2370 'propertyB' => 3
2371 ];
2372 $expectedResult = [
2373 'propertyA' => [
2374 'keyA' => [
2375 'valueA' => 1
2376 ],
2377 'keyB' => 2
2378 ],
2379 'propertyB' => 3
2380 ];
2381 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2382 }
2383
2384 /**
2385 * @test
2386 */
2387 public function removeDotsFromTypoScriptOverridesWithScalar()
2388 {
2389 $typoScript = [
2390 'propertyA.' => [
2391 'keyA.' => [
2392 'valueA' => 1
2393 ],
2394 'keyA' => 'willOverride',
2395 'keyB' => 2
2396 ],
2397 'propertyB' => 3
2398 ];
2399 $expectedResult = [
2400 'propertyA' => [
2401 'keyA' => 'willOverride',
2402 'keyB' => 2
2403 ],
2404 'propertyB' => 3
2405 ];
2406 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2407 }
2408
2409 //////////////////////////////////////
2410 // Tests concerning get_dirs
2411 //////////////////////////////////////
2412 /**
2413 * @test
2414 */
2415 public function getDirsReturnsArrayOfDirectoriesFromGivenDirectory()
2416 {
2417 $path = PATH_typo3conf;
2418 $directories = GeneralUtility::get_dirs($path);
2419 $this->assertInternalType(\PHPUnit\Framework\Constraint\IsType::TYPE_ARRAY, $directories);
2420 }
2421
2422 /**
2423 * @test
2424 */
2425 public function getDirsReturnsStringErrorOnPathFailure()
2426 {
2427 $path = 'foo';
2428 $result = GeneralUtility::get_dirs($path);
2429 $expectedResult = 'error';
2430 $this->assertEquals($expectedResult, $result);
2431 }
2432
2433 //////////////////////////////////
2434 // Tests concerning hmac
2435 //////////////////////////////////
2436 /**
2437 * @test
2438 */
2439 public function hmacReturnsHashOfProperLength()
2440 {
2441 $hmac = GeneralUtility::hmac('message');
2442 $this->assertTrue(!empty($hmac) && is_string($hmac));
2443 $this->assertTrue(strlen($hmac) == 40);
2444 }
2445
2446 /**
2447 * @test
2448 */
2449 public function hmacReturnsEqualHashesForEqualInput()
2450 {
2451 $msg0 = 'message';
2452 $msg1 = 'message';
2453 $this->assertEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2454 }
2455
2456 /**
2457 * @test
2458 */
2459 public function hmacReturnsNoEqualHashesForNonEqualInput()
2460 {
2461 $msg0 = 'message0';
2462 $msg1 = 'message1';
2463 $this->assertNotEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2464 }
2465
2466 //////////////////////////////////
2467 // Tests concerning quoteJSvalue
2468 //////////////////////////////////
2469 /**
2470 * Data provider for quoteJSvalueTest.
2471 *
2472 * @return array
2473 */
2474 public function quoteJsValueDataProvider()
2475 {
2476 return [
2477 'Immune characters are returned as is' => [
2478 '._,',
2479 '._,'
2480 ],
2481 'Alphanumerical characters are returned as is' => [
2482 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
2483 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
2484 ],
2485 'Angle brackets and ampersand are encoded' => [
2486 '<>&',
2487 '\\u003C\\u003E\\u0026'
2488 ],
2489 'Quotes and backslashes are encoded' => [
2490 '"\'\\',
2491 '\\u0022\\u0027\\u005C'
2492 ],
2493 'Forward slashes are escaped' => [
2494 '</script>',
2495 '\\u003C\\/script\\u003E'
2496 ],
2497 'Empty string stays empty' => [
2498 '',
2499 ''
2500 ],
2501 'Exclamation mark and space are properly encoded' => [
2502 'Hello World!',
2503 'Hello\\u0020World\\u0021'
2504 ],
2505 'Whitespaces are properly encoded' => [
2506 TAB . LF . CR . ' ',
2507 '\\u0009\\u000A\\u000D\\u0020'
2508 ],
2509 'Null byte is properly encoded' => [
2510 chr(0),
2511 '\\u0000'
2512 ],
2513 'Umlauts are properly encoded' => [
2514 'ÜüÖöÄä',
2515 '\\u00dc\\u00fc\\u00d6\\u00f6\\u00c4\\u00e4'
2516 ]
2517 ];
2518 }
2519
2520 /**
2521 * @test
2522 * @param string $input
2523 * @param string $expected
2524 * @dataProvider quoteJsValueDataProvider
2525 */
2526 public function quoteJsValueTest($input, $expected)
2527 {
2528 $this->assertSame('\'' . $expected . '\'', GeneralUtility::quoteJSvalue($input));
2529 }
2530
2531 ///////////////////////////////
2532 // Tests concerning _GETset()
2533 ///////////////////////////////
2534 /**
2535 * @test
2536 */
2537 public function getSetWritesArrayToGetSystemVariable()
2538 {
2539 $_GET = [];
2540 $GLOBALS['HTTP_GET_VARS'] = [];
2541 $getParameters = ['foo' => 'bar'];
2542 GeneralUtility::_GETset($getParameters);
2543 $this->assertSame($getParameters, $_GET);
2544 }
2545
2546 /**
2547 * @test
2548 */
2549 public function getSetWritesArrayToGlobalsHttpGetVars()
2550 {
2551 $_GET = [];
2552 $GLOBALS['HTTP_GET_VARS'] = [];
2553 $getParameters = ['foo' => 'bar'];
2554 GeneralUtility::_GETset($getParameters);
2555 $this->assertSame($getParameters, $GLOBALS['HTTP_GET_VARS']);
2556 }
2557
2558 /**
2559 * @test
2560 */
2561 public function getSetForArrayDropsExistingValues()
2562 {
2563 $_GET = [];
2564 $GLOBALS['HTTP_GET_VARS'] = [];
2565 GeneralUtility::_GETset(['foo' => 'bar']);
2566 GeneralUtility::_GETset(['oneKey' => 'oneValue']);
2567 $this->assertEquals(['oneKey' => 'oneValue'], $GLOBALS['HTTP_GET_VARS']);
2568 }
2569
2570 /**
2571 * @test
2572 */
2573 public function getSetAssignsOneValueToOneKey()
2574 {
2575 $_GET = [];
2576 $GLOBALS['HTTP_GET_VARS'] = [];
2577 GeneralUtility::_GETset('oneValue', 'oneKey');
2578 $this->assertEquals('oneValue', $GLOBALS['HTTP_GET_VARS']['oneKey']);
2579 }
2580
2581 /**
2582 * @test
2583 */
2584 public function getSetForOneValueDoesNotDropUnrelatedValues()
2585 {
2586 $_GET = [];
2587 $GLOBALS['HTTP_GET_VARS'] = [];
2588 GeneralUtility::_GETset(['foo' => 'bar']);
2589 GeneralUtility::_GETset('oneValue', 'oneKey');
2590 $this->assertEquals(['foo' => 'bar', 'oneKey' => 'oneValue'], $GLOBALS['HTTP_GET_VARS']);
2591 }
2592
2593 /**
2594 * @test
2595 */
2596 public function getSetCanAssignsAnArrayToASpecificArrayElement()
2597 {
2598 $_GET = [];
2599 $GLOBALS['HTTP_GET_VARS'] = [];
2600 GeneralUtility::_GETset(['childKey' => 'oneValue'], 'parentKey');
2601 $this->assertEquals(['parentKey' => ['childKey' => 'oneValue']], $GLOBALS['HTTP_GET_VARS']);
2602 }
2603
2604 /**
2605 * @test
2606 */
2607 public function getSetCanAssignAStringValueToASpecificArrayChildElement()
2608 {
2609 $_GET = [];
2610 $GLOBALS['HTTP_GET_VARS'] = [];
2611 GeneralUtility::_GETset('oneValue', 'parentKey|childKey');
2612 $this->assertEquals(['parentKey' => ['childKey' => 'oneValue']], $GLOBALS['HTTP_GET_VARS']);
2613 }
2614
2615 /**
2616 * @test
2617 */
2618 public function getSetCanAssignAnArrayToASpecificArrayChildElement()
2619 {
2620 $_GET = [];
2621 $GLOBALS['HTTP_GET_VARS'] = [];
2622 GeneralUtility::_GETset(['key1' => 'value1', 'key2' => 'value2'], 'parentKey|childKey');
2623 $this->assertEquals([
2624 'parentKey' => [
2625 'childKey' => ['key1' => 'value1', 'key2' => 'value2']
2626 ]
2627 ], $GLOBALS['HTTP_GET_VARS']);
2628 }
2629
2630 ///////////////////////////
2631 // Tests concerning minifyJavaScript
2632 ///////////////////////////
2633 /**
2634 * @test
2635 */
2636 public function minifyJavaScriptReturnsInputStringIfNoHookIsRegistered()
2637 {
2638 unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript']);
2639 $testString = $this->getUniqueId('string');
2640 $this->assertSame($testString, GeneralUtility::minifyJavaScript($testString));
2641 }
2642
2643 /**
2644 * Create an own hook callback class, register as hook, and check
2645 * if given string to compress is given to hook method
2646 *
2647 * @test
2648 */
2649 public function minifyJavaScriptCallsRegisteredHookWithInputString()
2650 {
2651 $hookClassName = $this->getUniqueId('tx_coretest');
2652 $minifyHookMock = $this->getMockBuilder('stdClass')
2653 ->setMethods(['minify'])
2654 ->setMockClassName($hookClassName)
2655 ->getMock();
2656 $functionName = $hookClassName . '->minify';
2657 $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = [];
2658 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2659 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2660 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2661 $minifyHookMock->expects($this->once())->method('minify')->will($this->returnCallback([$this, 'isMinifyJavaScriptHookCalledCallback']));
2662 GeneralUtility::minifyJavaScript('foo');
2663 }
2664
2665 /**
2666 * Callback function used in minifyJavaScriptCallsRegisteredHookWithInputString test
2667 *
2668 * @param array $params
2669 */
2670 public function isMinifyJavaScriptHookCalledCallback(array $params)
2671 {
2672 // We can not throw an exception here, because that would be caught by the
2673 // minifyJavaScript method under test itself. Thus, we just die if the
2674 // input string is not ok.
2675 if ($params['script'] !== 'foo') {
2676 die('broken');
2677 }
2678 }
2679
2680 /**
2681 * Create a hook callback, use callback to throw an exception and check
2682 * if the exception is given as error parameter to the calling method.
2683 *
2684 * @test
2685 */
2686 public function minifyJavaScriptReturnsErrorStringOfHookException()
2687 {
2688 $hookClassName = $this->getUniqueId('tx_coretest');
2689 $minifyHookMock = $this->getMockBuilder('stdClass')
2690 ->setMethods(['minify'])
2691 ->setMockClassName($hookClassName)
2692 ->getMock();
2693 $functionName = '&' . $hookClassName . '->minify';
2694 $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = [];
2695 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2696 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2697 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2698 $minifyHookMock->expects($this->any())->method('minify')->will($this->returnCallback([$this, 'minifyJavaScriptErroneousCallback']));
2699 $error = '';
2700 GeneralUtility::minifyJavaScript('string to compress', $error);
2701 $this->assertSame('Error minifying java script: foo', $error);
2702 }
2703
2704 /**
2705 * Check if the error message that is returned by the hook callback
2706 * is logged to \TYPO3\CMS\Core\GeneralUtility::devLog.
2707 *
2708 * @test
2709 */
2710 public function minifyJavaScriptWritesExceptionMessageToDevLog()
2711 {
2712 $hookClassName = $this->getUniqueId('tx_coretest');
2713 $minifyHookMock = $this->getMockBuilder('stdClass')
2714 ->setMethods(['minify'])
2715 ->setMockClassName($hookClassName)
2716 ->getMock();
2717 $functionName = '&' . $hookClassName . '->minify';
2718 $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = [];
2719 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2720 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2721 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2722 $minifyHookMock->expects($this->any())->method('minify')->will($this->returnCallback([$this, 'minifyJavaScriptErroneousCallback']));
2723 $this->expectException(\RuntimeException::class);
2724 GeneralUtilityMinifyJavaScriptFixture::minifyJavaScript('string to compress');
2725 }
2726
2727 /**
2728 * Callback function used in
2729 * minifyJavaScriptReturnsErrorStringOfHookException and
2730 * minifyJavaScriptWritesExceptionMessageToDevLog
2731 *
2732 * @throws \RuntimeException
2733 */
2734 public function minifyJavaScriptErroneousCallback()
2735 {
2736 throw new \RuntimeException('foo', 1344888548);
2737 }
2738
2739 ///////////////////////////////
2740 // Tests concerning fixPermissions
2741 ///////////////////////////////
2742 /**
2743 * @test
2744 */
2745 public function fixPermissionsSetsGroup()
2746 {
2747 if (TYPO3_OS === 'WIN') {
2748 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2749 }
2750 if (!function_exists('posix_getegid')) {
2751 $this->markTestSkipped('Function posix_getegid() not available, fixPermissionsSetsGroup() tests skipped');
2752 }
2753 if (posix_getegid() === -1) {
2754 $this->markTestSkipped('The fixPermissionsSetsGroup() is not available on Mac OS because posix_getegid() always returns -1 on Mac OS.');
2755 }
2756 // Create and prepare test file
2757 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2758 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2759 $currentGroupId = posix_getegid();
2760 // Set target group and run method
2761 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $currentGroupId;
2762 GeneralUtilityFilesystemFixture::fixPermissions($filename);
2763 clearstatcache();
2764 $this->assertEquals($currentGroupId, filegroup($filename));
2765 }
2766
2767 /**
2768 * @test
2769 */
2770 public function fixPermissionsSetsPermissionsToFile()
2771 {
2772 if (TYPO3_OS === 'WIN') {
2773 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2774 }
2775 // Create and prepare test file
2776 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2777 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2778 chmod($filename, 482);
2779 // Set target permissions and run method
2780 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2781 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2782 clearstatcache();
2783 $this->assertTrue($fixPermissionsResult);
2784 $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2785 }
2786
2787 /**
2788 * @test
2789 */
2790 public function fixPermissionsSetsPermissionsToHiddenFile()
2791 {
2792 if (TYPO3_OS === 'WIN') {
2793 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2794 }
2795 // Create and prepare test file
2796 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2797 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2798 chmod($filename, 482);
2799 // Set target permissions and run method
2800 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2801 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2802 clearstatcache();
2803 $this->assertTrue($fixPermissionsResult);
2804 $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2805 }
2806
2807 /**
2808 * @test
2809 */
2810 public function fixPermissionsSetsPermissionsToDirectory()
2811 {
2812 if (TYPO3_OS === 'WIN') {
2813 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2814 }
2815 // Create and prepare test directory
2816 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2817 GeneralUtilityFilesystemFixture::mkdir($directory);
2818 chmod($directory, 1551);
2819 // Set target permissions and run method
2820 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2821 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2822 clearstatcache();
2823 $this->assertTrue($fixPermissionsResult);
2824 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2825 }
2826
2827 /**
2828 * @test
2829 */
2830 public function fixPermissionsSetsPermissionsToDirectoryWithTrailingSlash()
2831 {
2832 if (TYPO3_OS === 'WIN') {
2833 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2834 }
2835 // Create and prepare test directory
2836 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2837 GeneralUtilityFilesystemFixture::mkdir($directory);
2838 chmod($directory, 1551);
2839 // Set target permissions and run method
2840 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2841 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory . '/');
2842 // Get actual permissions and clean up
2843 clearstatcache();
2844 $this->assertTrue($fixPermissionsResult);
2845 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2846 }
2847
2848 /**
2849 * @test
2850 */
2851 public function fixPermissionsSetsPermissionsToHiddenDirectory()
2852 {
2853 if (TYPO3_OS === 'WIN') {
2854 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2855 }
2856 // Create and prepare test directory
2857 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2858 GeneralUtilityFilesystemFixture::mkdir($directory);
2859 chmod($directory, 1551);
2860 // Set target permissions and run method
2861 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2862 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2863 // Get actual permissions and clean up
2864 clearstatcache();
2865 $this->assertTrue($fixPermissionsResult);
2866 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2867 }
2868
2869 /**
2870 * @test
2871 */
2872 public function fixPermissionsCorrectlySetsPermissionsRecursive()
2873 {
2874 if (TYPO3_OS === 'WIN') {
2875 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2876 }
2877 // Create and prepare test directory and file structure
2878 $baseDirectory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2879 GeneralUtilityFilesystemFixture::mkdir($baseDirectory);
2880 chmod($baseDirectory, 1751);
2881 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/file', '42');
2882 chmod($baseDirectory . '/file', 482);
2883 GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/foo');
2884 chmod($baseDirectory . '/foo', 1751);
2885 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/foo/file', '42');
2886 chmod($baseDirectory . '/foo/file', 482);
2887 GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/.bar');
2888 chmod($baseDirectory . '/.bar', 1751);
2889 // Use this if writeFileToTypo3tempDir is fixed to create hidden files in subdirectories
2890 // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/.file', '42');
2891 // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/..file2', '42');
2892 touch($baseDirectory . '/.bar/.file', '42');
2893 chmod($baseDirectory . '/.bar/.file', 482);
2894 touch($baseDirectory . '/.bar/..file2', '42');
2895 chmod($baseDirectory . '/.bar/..file2', 482);
2896 // Set target permissions and run method
2897 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2898 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2899 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($baseDirectory, true);
2900 // Get actual permissions
2901 clearstatcache();
2902 $resultBaseDirectoryPermissions = substr(decoct(fileperms($baseDirectory)), 1);
2903 $resultBaseFilePermissions = substr(decoct(fileperms($baseDirectory . '/file')), 2);
2904 $resultFooDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/foo')), 1);
2905 $resultFooFilePermissions = substr(decoct(fileperms($baseDirectory . '/foo/file')), 2);
2906 $resultBarDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/.bar')), 1);
2907 $resultBarFilePermissions = substr(decoct(fileperms($baseDirectory . '/.bar/.file')), 2);
2908 $resultBarFile2Permissions = substr(decoct(fileperms($baseDirectory . '/.bar/..file2')), 2);
2909 // Test if everything was ok
2910 $this->assertTrue($fixPermissionsResult);
2911 $this->assertEquals('0770', $resultBaseDirectoryPermissions);
2912 $this->assertEquals('0660', $resultBaseFilePermissions);
2913 $this->assertEquals('0770', $resultFooDirectoryPermissions);
2914 $this->assertEquals('0660', $resultFooFilePermissions);
2915 $this->assertEquals('0770', $resultBarDirectoryPermissions);
2916 $this->assertEquals('0660', $resultBarFilePermissions);
2917 $this->assertEquals('0660', $resultBarFile2Permissions);
2918 }
2919
2920 /**
2921 * @test
2922 */
2923 public function fixPermissionsDoesNotSetPermissionsToNotAllowedPath()
2924 {
2925 if (TYPO3_OS === 'WIN') {
2926 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2927 }
2928 // Create and prepare test file
2929 $filename = PATH_site . 'typo3temp/var/tests/../../../typo3temp/var/tests/' . $this->getUniqueId('test_');
2930 // Set target permissions and run method
2931 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2932 $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2933 $this->assertFalse($fixPermissionsResult);
2934 }
2935
2936 /**
2937 * @test
2938 */
2939 public function fixPermissionsSetsPermissionsWithRelativeFileReference()
2940 {
2941 if (TYPO3_OS === 'WIN') {
2942 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2943 }
2944 $filename = 'typo3temp/var/tests/' . $this->getUniqueId('test_');
2945 GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filename, '42');
2946 $this->testFilesToDelete[] = PATH_site . $filename;
2947 chmod(PATH_site . $filename, 482);
2948 // Set target permissions and run method
2949 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2950 $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2951 clearstatcache();
2952 $this->assertTrue($fixPermissionsResult);
2953 $this->assertEquals('0660', substr(decoct(fileperms(PATH_site . $filename)), 2));
2954 }
2955
2956 /**
2957 * @test
2958 */
2959 public function fixPermissionsSetsDefaultPermissionsToFile()
2960 {
2961 if (TYPO3_OS === 'WIN') {
2962 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2963 }
2964 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2965 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2966 chmod($filename, 482);
2967 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']);
2968 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2969 clearstatcache();
2970 $this->assertTrue($fixPermissionsResult);
2971 $this->assertEquals('0644', substr(decoct(fileperms($filename)), 2));
2972 }
2973
2974 /**
2975 * @test
2976 */
2977 public function fixPermissionsSetsDefaultPermissionsToDirectory()
2978 {
2979 if (TYPO3_OS === 'WIN') {
2980 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2981 }
2982 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2983 GeneralUtilityFilesystemFixture::mkdir($directory);
2984 chmod($directory, 1551);
2985 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2986 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2987 clearstatcache();
2988 $this->assertTrue($fixPermissionsResult);
2989 $this->assertEquals('0755', substr(decoct(fileperms($directory)), 1));
2990 }
2991
2992 ///////////////////////////////
2993 // Tests concerning mkdir
2994 ///////////////////////////////
2995 /**
2996 * @test
2997 */
2998 public function mkdirCreatesDirectory()
2999 {
3000 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3001 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
3002 clearstatcache();
3003 $this->assertTrue($mkdirResult);
3004 $this->assertTrue(is_dir($directory));
3005 }
3006
3007 /**
3008 * @test
3009 */
3010 public function mkdirCreatesHiddenDirectory()
3011 {
3012 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('.test_');
3013 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
3014 clearstatcache();
3015 $this->assertTrue($mkdirResult);
3016 $this->assertTrue(is_dir($directory));
3017 }
3018
3019 /**
3020 * @test
3021 */
3022 public function mkdirCreatesDirectoryWithTrailingSlash()
3023 {
3024 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_') . '/';
3025 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
3026 clearstatcache();
3027 $this->assertTrue($mkdirResult);
3028 $this->assertTrue(is_dir($directory));
3029 }
3030
3031 /**
3032 * @test
3033 */
3034 public function mkdirSetsPermissionsOfCreatedDirectory()
3035 {
3036 if (TYPO3_OS === 'WIN') {
3037 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3038 }
3039 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3040 $oldUmask = umask(19);
3041 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0772';
3042 GeneralUtilityFilesystemFixture::mkdir($directory);
3043 clearstatcache();
3044 $resultDirectoryPermissions = substr(decoct(fileperms($directory)), 1);
3045 umask($oldUmask);
3046 $this->assertEquals($resultDirectoryPermissions, '0772');
3047 }
3048
3049 /**
3050 * @test
3051 */
3052 public function mkdirSetsGroupOwnershipOfCreatedDirectory()
3053 {
3054 $swapGroup = $this->checkGroups(__FUNCTION__);
3055 if ($swapGroup !== false) {
3056 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3057 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('mkdirtest_');
3058 GeneralUtilityFilesystemFixture::mkdir($directory);
3059 clearstatcache();
3060 $resultDirectoryGroup = filegroup($directory);
3061 $this->assertEquals($resultDirectoryGroup, $swapGroup);
3062 }
3063 }
3064
3065 ///////////////////////////////
3066 // Helper function for filesystem ownership tests
3067 ///////////////////////////////
3068 /**
3069 * Check if test on filesystem group ownership can be done in this environment
3070 * If so, return second group of webserver user
3071 *
3072 * @param string $methodName calling method name
3073 * @return mixed FALSE if test cannot be run, int group id of the second group of webserver user
3074 */
3075 private function checkGroups($methodName)
3076 {
3077 if (TYPO3_OS === 'WIN') {
3078 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3079 return false;
3080 }
3081 if (!function_exists('posix_getegid')) {
3082 $this->markTestSkipped('Function posix_getegid() not available, ' . $methodName . '() tests skipped');
3083 return false;
3084 }
3085 if (posix_getegid() === -1) {
3086 $this->markTestSkipped('Function posix_getegid() returns -1, ' . $methodName . '() tests skipped');
3087 return false;
3088 }
3089 if (!function_exists('posix_getgroups')) {
3090 $this->markTestSkipped('Function posix_getgroups() not available, ' . $methodName . '() tests skipped');
3091 return false;
3092 }
3093 $groups = posix_getgroups();
3094 if (count($groups) <= 1) {
3095 $this->markTestSkipped($methodName . '() test cannot be done when the web server user is only member of 1 group.');
3096 return false;
3097 }
3098 $secondaryGroups = array_diff($groups, [posix_getegid()]);
3099 return array_shift($secondaryGroups);
3100 }
3101
3102 ///////////////////////////////
3103 // Tests concerning mkdir_deep
3104 ///////////////////////////////
3105 /**
3106 * @test
3107 */
3108 public function mkdirDeepCreatesDirectory()
3109 {
3110 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3111 GeneralUtility::mkdir_deep($directory);
3112 $this->assertTrue(is_dir($directory));
3113 }
3114
3115 /**
3116 * @test
3117 */
3118 public function mkdirDeepCreatesSubdirectoriesRecursive()
3119 {
3120 $directory = $this->getVirtualTestDir() . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
3121 $subDirectory = $directory . '/foo';
3122 GeneralUtility::mkdir_deep($subDirectory);
3123 $this->assertTrue(is_dir($subDirectory));
3124 }
3125
3126 /**
3127 * Data provider for mkdirDeepCreatesDirectoryWithDoubleSlashes.
3128 * @return array
3129 */
3130 public function mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider()
3131 {
3132 return [
3133 'no double slash if concatenated with PATH_site' => ['fileadmin/testDir1'],
3134 'double slash if concatenated with PATH_site' => ['/fileadmin/testDir2'],
3135 ];
3136 }
3137
3138 /**
3139 * @test
3140 * @dataProvider mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider
3141 */
3142 public function mkdirDeepCreatesDirectoryWithDoubleSlashes($directoryToCreate)
3143 {
3144 vfsStream::