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