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