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