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