TYPO3 CMS  TYPO3_8-7
GeneralUtilityTest.php
Go to the documentation of this file.
1 <?php
2 
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
38 
42 class GeneralUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
43 {
44  const NO_FIX_PERMISSIONS_ON_WINDOWS = 'fixPermissions() not available on Windows (method does nothing)';
48  protected $singletonInstances = [];
49 
54 
58  protected function setUp()
59  {
62  $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
63  $this->singletonInstances = GeneralUtility::getSingletonInstances();
65  }
66 
70  protected function tearDown()
71  {
72  GeneralUtility::resetSingletonInstances($this->singletonInstances);
74  if ($this->backupPackageManager) {
76  }
77  parent::tearDown();
78  }
79 
86  public function isConnected()
87  {
88  $isConnected = false;
89  $connected = @fsockopen('typo3.org', 80);
90  if ($connected) {
91  $isConnected = true;
92  fclose($connected);
93  }
94  return $isConnected;
95  }
96 
104  protected function getVirtualTestDir($prefix = 'root_')
105  {
106  $root = vfsStream::setup();
107  $path = $root->url() . '/typo3temp/var/tests/' . $this->getUniqueId($prefix);
109  return $path;
110  }
111 
113  // Tests concerning _GP
115 
119  public function canRetrieveValueWithGP($key, $get, $post, $expected)
120  {
121  $_GET = $get;
122  $_POST = $post;
123  $this->assertSame($expected, GeneralUtility::_GP($key));
124  }
125 
132  public function gpDataProvider()
133  {
134  return [
135  'No key parameter' => [null, [], [], null],
136  'Key not found' => ['cake', [], [], null],
137  'Value only in GET' => ['cake', ['cake' => 'li\\e'], [], 'li\\e'],
138  'Value only in POST' => ['cake', [], ['cake' => 'l\\ie'], 'l\\ie'],
139  'Value from POST preferred over GET' => ['cake', ['cake' => 'is a'], ['cake' => '\\lie'], '\\lie'],
140  'Value can be an array' => [
141  'cake',
142  ['cake' => ['is a' => 'l\\ie']],
143  [],
144  ['is a' => 'l\\ie']
145  ]
146  ];
147  }
148 
150  // Tests concerning _GPmerged
152 
156  public function gpMergedWillMergeArraysFromGetAndPost($get, $post, $expected)
157  {
158  $_POST = $post;
159  $_GET = $get;
160  $this->assertEquals($expected, GeneralUtility::_GPmerged('cake'));
161  }
162 
168  public function gpMergedDataProvider()
169  {
170  $fullDataArray = ['cake' => ['a' => 'is a', 'b' => 'lie']];
171  $postPartData = ['cake' => ['b' => 'lie']];
172  $getPartData = ['cake' => ['a' => 'is a']];
173  $getPartDataModified = ['cake' => ['a' => 'is not a']];
174  return [
175  'Key doesn\' exist' => [['foo'], ['bar'], []],
176  'No POST data' => [$fullDataArray, [], $fullDataArray['cake']],
177  'No GET data' => [[], $fullDataArray, $fullDataArray['cake']],
178  'POST and GET are merged' => [$getPartData, $postPartData, $fullDataArray['cake']],
179  'POST is preferred over GET' => [$getPartDataModified, $fullDataArray, $fullDataArray['cake']]
180  ];
181  }
182 
184  // Tests concerning _GET / _POST
186 
192  public function getAndPostDataProvider()
193  {
194  return [
195  'Requested input data doesn\'t exist' => ['cake', [], null],
196  'No key will return entire input data' => [null, ['cake' => 'l\\ie'], ['cake' => 'l\\ie']],
197  'Can retrieve specific input' => ['cake', ['cake' => 'l\\ie', 'foo'], 'l\\ie'],
198  'Can retrieve nested input data' => ['cake', ['cake' => ['is a' => 'l\\ie']], ['is a' => 'l\\ie']]
199  ];
200  }
201 
206  public function canRetrieveGlobalInputsThroughGet($key, $get, $expected)
207  {
208  $_GET = $get;
209  $this->assertSame($expected, GeneralUtility::_GET($key));
210  }
211 
216  public function canRetrieveGlobalInputsThroughPost($key, $post, $expected)
217  {
218  $_POST = $post;
219  $this->assertSame($expected, GeneralUtility::_POST($key));
220  }
221 
223  // Tests concerning _GETset
225 
229  public function canSetNewGetInputValues($input, $key, $expected, $getPreset = [])
230  {
231  $_GET = $getPreset;
232  GeneralUtility::_GETset($input, $key);
233  $this->assertSame($expected, $_GET);
234  }
235 
241  public function getSetDataProvider()
242  {
243  return [
244  'No input data used without target key' => [null, null, []],
245  'No input data used with target key' => ['', 'cake', ['cake' => '']],
246  'No target key used with string input data' => ['data', null, []],
247  'No target key used with array input data' => [['cake' => 'lie'], null, ['cake' => 'lie']],
248  'Target key and string input data' => ['lie', 'cake', ['cake' => 'lie']],
249  'Replace existing GET data' => ['lie', 'cake', ['cake' => 'lie'], ['cake' => 'is a lie']],
250  'Target key pointing to sublevels and string input data' => ['lie', 'cake|is', ['cake' => ['is' => 'lie']]],
251  'Target key pointing to sublevels and array input data' => [
252  ['a' => 'lie'],
253  'cake|is',
254  ['cake' => ['is' => ['a' => 'lie']]]
255  ]
256  ];
257  }
258 
260  // Tests concerning cmpIPv4
262 
267  public static function cmpIPv4DataProviderMatching()
268  {
269  return [
270  'host with full IP address' => ['127.0.0.1', '127.0.0.1'],
271  'host with two wildcards at the end' => ['127.0.0.1', '127.0.*.*'],
272  'host with wildcard at third octet' => ['127.0.0.1', '127.0.*.1'],
273  'host with wildcard at second octet' => ['127.0.0.1', '127.*.0.1'],
274  '/8 subnet' => ['127.0.0.1', '127.1.1.1/8'],
275  '/32 subnet (match only name)' => ['127.0.0.1', '127.0.0.1/32'],
276  '/30 subnet' => ['10.10.3.1', '10.10.3.3/30'],
277  'host with wildcard in list with IPv4/IPv6 addresses' => [
278  '192.168.1.1',
279  '127.0.0.1, 1234:5678::/126, 192.168.*'
280  ],
281  'host in list with IPv4/IPv6 addresses' => ['192.168.1.1', '::1, 1234:5678::/126, 192.168.1.1'],
282  ];
283  }
284 
289  public function cmpIPv4ReturnsTrueForMatchingAddress($ip, $list)
290  {
291  $this->assertTrue(GeneralUtility::cmpIPv4($ip, $list));
292  }
293 
299  public static function cmpIPv4DataProviderNotMatching()
300  {
301  return [
302  'single host' => ['127.0.0.1', '127.0.0.2'],
303  'single host with wildcard' => ['127.0.0.1', '127.*.1.1'],
304  'single host with /32 subnet mask' => ['127.0.0.1', '127.0.0.2/32'],
305  '/31 subnet' => ['127.0.0.1', '127.0.0.2/31'],
306  'list with IPv4/IPv6 addresses' => ['127.0.0.1', '10.0.2.3, 192.168.1.1, ::1'],
307  'list with only IPv6 addresses' => ['10.20.30.40', '::1, 1234:5678::/127']
308  ];
309  }
310 
315  public function cmpIPv4ReturnsFalseForNotMatchingAddress($ip, $list)
316  {
317  $this->assertFalse(GeneralUtility::cmpIPv4($ip, $list));
318  }
319 
321  // Tests concerning cmpIPv6
323 
328  public static function cmpIPv6DataProviderMatching()
329  {
330  return [
331  'empty address' => ['::', '::'],
332  'empty with netmask in list' => ['::', '::/0'],
333  'empty with netmask 0 and host-bits set in list' => ['::', '::123/0'],
334  'localhost' => ['::1', '::1'],
335  'localhost with leading zero blocks' => ['::1', '0:0::1'],
336  'host with submask /128' => ['::1', '0:0::1/128'],
337  '/16 subnet' => ['1234::1', '1234:5678::/16'],
338  '/126 subnet' => ['1234:5678::3', '1234:5678::/126'],
339  '/126 subnet with host-bits in list set' => ['1234:5678::3', '1234:5678::2/126'],
340  'list with IPv4/IPv6 addresses' => ['1234:5678::3', '::1, 127.0.0.1, 1234:5678::/126, 192.168.1.1']
341  ];
342  }
343 
348  public function cmpIPv6ReturnsTrueForMatchingAddress($ip, $list)
349  {
350  $this->assertTrue(GeneralUtility::cmpIPv6($ip, $list));
351  }
352 
358  public static function cmpIPv6DataProviderNotMatching()
359  {
360  return [
361  'empty against localhost' => ['::', '::1'],
362  'empty against localhost with /128 netmask' => ['::', '::1/128'],
363  'localhost against different host' => ['::1', '::2'],
364  'localhost against host with prior bits set' => ['::1', '::1:1'],
365  'host against different /17 subnet' => ['1234::1', '1234:f678::/17'],
366  'host against different /127 subnet' => ['1234:5678::3', '1234:5678::/127'],
367  'host against IPv4 address list' => ['1234:5678::3', '127.0.0.1, 192.168.1.1'],
368  'host against mixed list with IPv6 host in different subnet' => ['1234:5678::3', '::1, 1234:5678::/127']
369  ];
370  }
371 
376  public function cmpIPv6ReturnsFalseForNotMatchingAddress($ip, $list)
377  {
378  $this->assertFalse(GeneralUtility::cmpIPv6($ip, $list));
379  }
380 
382  // Tests concerning IPv6Hex2Bin
384 
389  public static function IPv6Hex2BinDataProviderCorrect()
390  {
391  return [
392  'empty 1' => ['::', str_pad('', 16, "\x00")],
393  'empty 2, already normalized' => ['0000:0000:0000:0000:0000:0000:0000:0000', str_pad('', 16, "\x00")],
394  'already normalized' => [
395  '0102:0304:0000:0000:0000:0000:0506:0078',
396  "\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78"
397  ],
398  'expansion in middle 1' => ['1::2', "\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02"],
399  'expansion in middle 2' => ['beef::fefa', "\xbe\xef" . str_pad('', 12, "\x00") . "\xfe\xfa"],
400  ];
401  }
402 
407  public function IPv6Hex2BinCorrectlyConvertsAddresses($hex, $binary)
408  {
409  $this->assertTrue(GeneralUtility::IPv6Hex2Bin($hex) === $binary);
410  }
411 
413  // Tests concerning IPv6Bin2Hex
415 
420  public static function IPv6Bin2HexDataProviderCorrect()
421  {
422  return [
423  'empty' => [str_pad('', 16, "\x00"), '::'],
424  'non-empty front' => ["\x01" . str_pad('', 15, "\x00"), '100::'],
425  'non-empty back' => [str_pad('', 15, "\x00") . "\x01", '::1'],
426  'normalized' => ["\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78", '102:304::506:78'],
427  'expansion in middle 1' => ["\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02", '1::2'],
428  'expansion in middle 2' => ["\xbe\xef" . str_pad('', 12, "\x00") . "\xfe\xfa", 'beef::fefa'],
429  ];
430  }
431 
436  public function IPv6Bin2HexCorrectlyConvertsAddresses($binary, $hex)
437  {
438  $this->assertEquals(GeneralUtility::IPv6Bin2Hex($binary), $hex);
439  }
440 
442  // Tests concerning normalizeIPv6 / compressIPv6
444 
450  {
451  return [
452  'empty' => ['::', '0000:0000:0000:0000:0000:0000:0000:0000'],
453  'localhost' => ['::1', '0000:0000:0000:0000:0000:0000:0000:0001'],
454  'expansion in middle 1' => ['1::2', '0001:0000:0000:0000:0000:0000:0000:0002'],
455  'expansion in middle 2' => ['1:2::3', '0001:0002:0000:0000:0000:0000:0000:0003'],
456  'expansion in middle 3' => ['1::2:3', '0001:0000:0000:0000:0000:0000:0002:0003'],
457  'expansion in middle 4' => ['1:2::3:4:5', '0001:0002:0000:0000:0000:0003:0004:0005']
458  ];
459  }
460 
465  public function normalizeIPv6CorrectlyNormalizesAddresses($compressed, $normalized)
466  {
467  $this->assertEquals($normalized, GeneralUtility::normalizeIPv6($compressed));
468  }
469 
474  public function compressIPv6CorrectlyCompressesAdresses($compressed, $normalized)
475  {
476  $this->assertEquals($compressed, GeneralUtility::compressIPv6($normalized));
477  }
478 
483  {
484  if (strtolower(PHP_OS) === 'darwin') {
485  $this->markTestSkipped('This test does not work on OSX / Darwin OS.');
486  }
487  $this->assertEquals('::f0f', GeneralUtility::compressIPv6('0000:0000:0000:0000:0000:0000:0000:0f0f'));
488  }
489 
491  // Tests concerning validIP
493 
498  public static function validIpDataProvider()
499  {
500  return [
501  '0.0.0.0' => ['0.0.0.0'],
502  'private IPv4 class C' => ['192.168.0.1'],
503  'private IPv4 class A' => ['10.0.13.1'],
504  'private IPv6' => ['fe80::daa2:5eff:fe8b:7dfb']
505  ];
506  }
507 
512  public function validIpReturnsTrueForValidIp($ip)
513  {
514  $this->assertTrue(GeneralUtility::validIP($ip));
515  }
516 
522  public static function invalidIpDataProvider()
523  {
524  return [
525  'null' => [null],
526  'zero' => [0],
527  'string' => ['test'],
528  'string empty' => [''],
529  'string NULL' => ['NULL'],
530  'out of bounds IPv4' => ['300.300.300.300'],
531  'dotted decimal notation with only two dots' => ['127.0.1']
532  ];
533  }
534 
539  public function validIpReturnsFalseForInvalidIp($ip)
540  {
541  $this->assertFalse(GeneralUtility::validIP($ip));
542  }
543 
545  // Tests concerning cmpFQDN
547 
552  public static function cmpFqdnValidDataProvider()
553  {
554  return [
555  'localhost should usually resolve, IPv4' => ['127.0.0.1', '*'],
556  'localhost should usually resolve, IPv6' => ['::1', '*'],
557  // other testcases with resolving not possible since it would
558  // require a working IPv4/IPv6-connectivity
559  'aaa.bbb.ccc.ddd.eee, full' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee'],
560  'aaa.bbb.ccc.ddd.eee, wildcard first' => ['aaa.bbb.ccc.ddd.eee', '*.ccc.ddd.eee'],
561  'aaa.bbb.ccc.ddd.eee, wildcard last' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.*'],
562  'aaa.bbb.ccc.ddd.eee, wildcard middle' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.eee'],
563  'list-matches, 1' => ['aaa.bbb.ccc.ddd.eee', 'xxx, yyy, zzz, aaa.*.eee'],
564  'list-matches, 2' => ['aaa.bbb.ccc.ddd.eee', '127:0:0:1,,aaa.*.eee,::1']
565  ];
566  }
567 
572  public function cmpFqdnReturnsTrue($baseHost, $list)
573  {
574  $this->assertTrue(GeneralUtility::cmpFQDN($baseHost, $list));
575  }
576 
582  public static function cmpFqdnInvalidDataProvider()
583  {
584  return [
585  'num-parts of hostname to check can only be less or equal than hostname, 1' => [
586  'aaa.bbb.ccc.ddd.eee',
587  'aaa.bbb.ccc.ddd.eee.fff'
588  ],
589  'num-parts of hostname to check can only be less or equal than hostname, 2' => [
590  'aaa.bbb.ccc.ddd.eee',
591  'aaa.*.bbb.ccc.ddd.eee'
592  ]
593  ];
594  }
595 
600  public function cmpFqdnReturnsFalse($baseHost, $list)
601  {
602  $this->assertFalse(GeneralUtility::cmpFQDN($baseHost, $list));
603  }
604 
606  // Tests concerning inList
608 
613  public function inListForItemContainedReturnsTrue($haystack)
614  {
615  $this->assertTrue(GeneralUtility::inList($haystack, 'findme'));
616  }
617 
624  {
625  return [
626  'Element as second element of four items' => ['one,findme,three,four'],
627  'Element at beginning of list' => ['findme,one,two'],
628  'Element at end of list' => ['one,two,findme'],
629  'One item list' => ['findme']
630  ];
631  }
632 
638  public function inListForItemNotContainedReturnsFalse($haystack)
639  {
640  $this->assertFalse(GeneralUtility::inList($haystack, 'findme'));
641  }
642 
649  {
650  return [
651  'Four item list' => ['one,two,three,four'],
652  'One item list' => ['one'],
653  'Empty list' => ['']
654  ];
655  }
656 
658  // Tests concerning rmFromList
660 
666  public function rmFromListRemovesElementsFromCommaSeparatedList($initialList, $listWithElementRemoved)
667  {
668  $this->assertSame($listWithElementRemoved, GeneralUtility::rmFromList('removeme', $initialList));
669  }
670 
677  {
678  return [
679  'Element as second element of three' => ['one,removeme,two', 'one,two'],
680  'Element at beginning of list' => ['removeme,one,two', 'one,two'],
681  'Element at end of list' => ['one,two,removeme', 'one,two'],
682  'One item list' => ['removeme', ''],
683  'Element not contained in list' => ['one,two,three', 'one,two,three'],
684  'Empty element survives' => ['one,,three,,removeme', 'one,,three,'],
685  'Empty element survives at start' => [',removeme,three,removeme', ',three'],
686  'Empty element survives at end' => ['removeme,three,removeme,', 'three,'],
687  'Empty list' => ['', ''],
688  'List contains removeme multiple times' => ['removeme,notme,removeme,removeme', 'notme'],
689  'List contains removeme multiple times nothing else' => ['removeme,removeme,removeme', ''],
690  'List contains removeme multiple times nothing else 2x' => ['removeme,removeme', ''],
691  'List contains removeme multiple times nothing else 3x' => ['removeme,removeme,removeme', ''],
692  'List contains removeme multiple times nothing else 4x' => ['removeme,removeme,removeme,removeme', ''],
693  'List contains removeme multiple times nothing else 5x' => [
694  'removeme,removeme,removeme,removeme,removeme',
695  ''
696  ],
697  ];
698  }
699 
701  // Tests concerning expandList
703 
709  public function expandListExpandsIntegerRanges($list, $expectation)
710  {
711  $this->assertSame($expectation, GeneralUtility::expandList($list));
712  }
713 
720  {
721  return [
722  'Expand for the same number' => ['1,2-2,7', '1,2,7'],
723  'Small range expand with parameters reversed ignores reversed items' => ['1,5-3,7', '1,7'],
724  'Small range expand' => ['1,3-5,7', '1,3,4,5,7'],
725  'Expand at beginning' => ['3-5,1,7', '3,4,5,1,7'],
726  'Expand at end' => ['1,7,3-5', '1,7,3,4,5'],
727  'Multiple small range expands' => ['1,3-5,7-10,12', '1,3,4,5,7,8,9,10,12'],
728  'One item list' => ['1-5', '1,2,3,4,5'],
729  'Nothing to expand' => ['1,2,3,4', '1,2,3,4'],
730  'Empty list' => ['', '']
731  ];
732  }
733 
738  {
739  $list = GeneralUtility::expandList('1-2000');
740  $this->assertSame(1000, count(explode(',', $list)));
741  }
742 
744  // Tests concerning uniqueList
746 
752  public function uniqueListUnifiesCommaSeparatedList($initialList, $unifiedList)
753  {
754  $this->assertSame($unifiedList, GeneralUtility::uniqueList($initialList));
755  }
756 
763  {
764  return [
765  'List without duplicates' => ['one,two,three', 'one,two,three'],
766  'List with two consecutive duplicates' => ['one,two,two,three,three', 'one,two,three'],
767  'List with non-consecutive duplicates' => ['one,two,three,two,three', 'one,two,three'],
768  'One item list' => ['one', 'one'],
769  'Empty list' => ['', '']
770  ];
771  }
772 
774  // Tests concerning isFirstPartOfStr
776 
782  {
783  return [
784  'match first part of string' => ['hello world', 'hello'],
785  'match whole string' => ['hello', 'hello'],
786  'integer is part of string with same number' => ['24', 24],
787  'string is part of integer with same number' => [24, '24'],
788  'integer is part of string starting with same number' => ['24 beer please', 24]
789  ];
790  }
791 
796  public function isFirstPartOfStrReturnsTrueForMatchingFirstPart($string, $part)
797  {
798  $this->assertTrue(GeneralUtility::isFirstPartOfStr($string, $part));
799  }
800 
807  {
808  return [
809  'no string match' => ['hello', 'bye'],
810  'no case sensitive string match' => ['hello world', 'Hello'],
811  'array is not part of string' => ['string', []],
812  'string is not part of array' => [[], 'string'],
813  'NULL is not part of string' => ['string', null],
814  'string is not part of NULL' => [null, 'string'],
815  'NULL is not part of array' => [[], null],
816  'array is not part of NULL' => [null, []],
817  'empty string is not part of empty string' => ['', ''],
818  'NULL is not part of empty string' => ['', null],
819  'false is not part of empty string' => ['', false],
820  'empty string is not part of NULL' => [null, ''],
821  'empty string is not part of false' => [false, ''],
822  'empty string is not part of zero integer' => [0, ''],
823  'zero integer is not part of NULL' => [null, 0],
824  'zero integer is not part of empty string' => ['', 0]
825  ];
826  }
827 
833  {
834  $this->assertFalse(GeneralUtility::isFirstPartOfStr($string, $part));
835  }
836 
838  // Tests concerning formatSize
840 
844  public function formatSizeTranslatesBytesToHigherOrderRepresentation($size, $labels, $base, $expected)
845  {
846  $this->assertEquals($expected, GeneralUtility::formatSize($size, $labels, $base));
847  }
848 
854  public function formatSizeDataProvider()
855  {
856  return [
857  'IEC Bytes stay bytes (min)' => [1, '', 0, '1 '],
858  'IEC Bytes stay bytes (max)' => [921, '', 0, '921 '],
859  'IEC Kilobytes are used (min)' => [922, '', 0, '0.90 Ki'],
860  'IEC Kilobytes are used (max)' => [943718, '', 0, '922 Ki'],
861  'IEC Megabytes are used (min)' => [943719, '', 0, '0.90 Mi'],
862  'IEC Megabytes are used (max)' => [966367641, '', 0, '922 Mi'],
863  'IEC Gigabytes are used (min)' => [966367642, '', 0, '0.90 Gi'],
864  'IEC Gigabytes are used (max)' => [989560464998, '', 0, '922 Gi'],
865  'IEC Decimal is omitted for large kilobytes' => [31080, '', 0, '30 Ki'],
866  'IEC Decimal is omitted for large megabytes' => [31458000, '', 0, '30 Mi'],
867  'IEC Decimal is omitted for large gigabytes' => [32212254720, '', 0, '30 Gi'],
868  'SI Bytes stay bytes (min)' => [1, 'si', 0, '1 '],
869  'SI Bytes stay bytes (max)' => [899, 'si', 0, '899 '],
870  'SI Kilobytes are used (min)' => [901, 'si', 0, '0.90 k'],
871  'SI Kilobytes are used (max)' => [900000, 'si', 0, '900 k'],
872  'SI Megabytes are used (min)' => [900001, 'si', 0, '0.90 M'],
873  'SI Megabytes are used (max)' => [900000000, 'si', 0, '900 M'],
874  'SI Gigabytes are used (min)' => [900000001, 'si', 0, '0.90 G'],
875  'SI Gigabytes are used (max)' => [900000000000, 'si', 0, '900 G'],
876  'SI Decimal is omitted for large kilobytes' => [30000, 'si', 0, '30 k'],
877  'SI Decimal is omitted for large megabytes' => [30000000, 'si', 0, '30 M'],
878  'SI Decimal is omitted for large gigabytes' => [30000000000, 'si', 0, '30 G'],
879  'Label for bytes can be exchanged (binary unit)' => [1, ' Foo|||', 0, '1 Foo'],
880  'Label for kilobytes can be exchanged (binary unit)' => [1024, '| Foo||', 0, '1.00 Foo'],
881  'Label for megabyes can be exchanged (binary unit)' => [1048576, '|| Foo|', 0, '1.00 Foo'],
882  'Label for gigabytes can be exchanged (binary unit)' => [1073741824, '||| Foo', 0, '1.00 Foo'],
883  'Label for bytes can be exchanged (decimal unit)' => [1, ' Foo|||', 1000, '1 Foo'],
884  'Label for kilobytes can be exchanged (decimal unit)' => [1000, '| Foo||', 1000, '1.00 Foo'],
885  'Label for megabyes can be exchanged (decimal unit)' => [1000000, '|| Foo|', 1000, '1.00 Foo'],
886  'Label for gigabytes can be exchanged (decimal unit)' => [1000000000, '||| Foo', 1000, '1.00 Foo'],
887  'IEC Base is ignored' => [1024, 'iec', 1000, '1.00 Ki'],
888  'SI Base is ignored' => [1000, 'si', 1024, '1.00 k'],
889  'Use binary base for unexpected base' => [2048, '| Bar||', 512, '2.00 Bar']
890  ];
891  }
892 
894  // Tests concerning splitCalc
896 
901  public function splitCalcDataProvider()
902  {
903  return [
904  'empty string returns empty array' => [
905  [],
906  ''
907  ],
908  'number without operator returns array with plus and number' => [
909  [['+', 42]],
910  '42'
911  ],
912  'two numbers with asterisk return first number with plus and second number with asterisk' => [
913  [['+', 42], ['*', 31]],
914  '42 * 31'
915  ]
916  ];
917  }
918 
923  public function splitCalcCorrectlySplitsExpression($expected, $expression)
924  {
925  $this->assertEquals($expected, GeneralUtility::splitCalc($expression, '+-*/'));
926  }
927 
929  // Tests concerning htmlspecialchars_decode
931 
935  {
936  $string = '<typo3 version="6.0">&nbsp;</typo3>';
937  $encoded = htmlspecialchars($string);
938  $decoded = htmlspecialchars_decode($encoded);
939  $this->assertEquals($string, $decoded);
940  }
941 
943  // Tests concerning deHSCentities
945 
949  public function deHSCentitiesReturnsDecodedString($input, $expected)
950  {
951  $this->assertEquals($expected, GeneralUtility::deHSCentities($input));
952  }
953 
960  {
961  return [
962  'Empty string' => ['', ''],
963  'Double encoded &' => ['&amp;amp;', '&amp;'],
964  'Double encoded numeric entity' => ['&amp;#1234;', '&#1234;'],
965  'Double encoded hexadecimal entity' => ['&amp;#x1b;', '&#x1b;'],
966  'Single encoded entities are not touched' => ['&amp; &#1234; &#x1b;', '&amp; &#1234; &#x1b;']
967  ];
968  }
969 
971  // Tests concerning slashJS
973 
977  public function slashJsEscapesSingleQuotesAndSlashes($input, $extended, $expected)
978  {
979  $this->assertEquals($expected, GeneralUtility::slashJS($input, $extended));
980  }
981 
987  public function slashJsDataProvider()
988  {
989  return [
990  'Empty string is not changed' => ['', false, ''],
991  'Normal string is not changed' => ['The cake is a lie √', false, 'The cake is a lie √'],
992  'String with single quotes' => ['The \'cake\' is a lie', false, 'The \\\'cake\\\' is a lie'],
993  'String with single quotes and backslashes - just escape single quotes' => [
994  'The \\\'cake\\\' is a lie',
995  false,
996  'The \\\\\'cake\\\\\' is a lie'
997  ],
998  'String with single quotes and backslashes - escape both' => [
999  'The \\\'cake\\\' is a lie',
1000  true,
1001  'The \\\\\\\'cake\\\\\\\' is a lie'
1002  ]
1003  ];
1004  }
1005 
1007  // Tests concerning rawUrlEncodeJS
1009 
1013  {
1014  $input = 'Encode \'me\', but leave my spaces √';
1015  $expected = 'Encode %27me%27%2C but leave my spaces %E2%88%9A';
1016  $this->assertEquals($expected, GeneralUtility::rawUrlEncodeJS($input));
1017  }
1018 
1020  // Tests concerning rawUrlEncodeJS
1022 
1026  {
1027  $input = 'Encode \'me\', but leave my / √';
1028  $expected = 'Encode%20%27me%27%2C%20but%20leave%20my%20/%20%E2%88%9A';
1029  $this->assertEquals($expected, GeneralUtility::rawUrlEncodeFP($input));
1030  }
1031 
1033  // Tests concerning strtoupper / strtolower
1035 
1040  public function strtouppperDataProvider()
1041  {
1042  return [
1043  'Empty string' => ['', ''],
1044  'String containing only latin characters' => ['the cake is a lie.', 'THE CAKE IS A LIE.'],
1045  'String with umlauts and accent characters' => ['the càkê is ä lie.', 'THE CàKê IS ä LIE.']
1046  ];
1047  }
1048 
1053  public function strtoupperConvertsOnlyLatinCharacters($input, $expected)
1054  {
1055  $this->assertEquals($expected, GeneralUtility::strtoupper($input));
1056  }
1057 
1062  public function strtolowerConvertsOnlyLatinCharacters($expected, $input)
1063  {
1064  $this->assertEquals($expected, GeneralUtility::strtolower($input));
1065  }
1066 
1068  // Tests concerning validEmail
1070 
1076  {
1077  return [
1078  'short mail address' => ['a@b.c'],
1079  'simple mail address' => ['test@example.com'],
1080  'uppercase characters' => ['QWERTYUIOPASDFGHJKLZXCVBNM@QWERTYUIOPASDFGHJKLZXCVBNM.NET'],
1081  'equal sign in local part' => ['test=mail@example.com'],
1082  'dash in local part' => ['test-mail@example.com'],
1083  'plus in local part' => ['test+mail@example.com'],
1084  'question mark in local part' => ['test?mail@example.com'],
1085  'slash in local part' => ['foo/bar@example.com'],
1086  'hash in local part' => ['foo#bar@example.com'],
1087  'dot in local part' => ['firstname.lastname@employee.2something.com'],
1088  'dash as local part' => ['-@foo.com'],
1089  'umlauts in domain part' => ['foo@äöüfoo.com']
1090  ];
1091  }
1092 
1098  {
1099  $this->assertTrue(GeneralUtility::validEmail($address));
1100  }
1101 
1108  {
1109  return [
1110  'empty string' => [''],
1111  'empty array' => [[]],
1112  'integer' => [42],
1113  'float' => [42.23],
1114  'array' => [['foo']],
1115  'object' => [new \stdClass()],
1116  '@ sign only' => ['@'],
1117  'string longer than 320 characters' => [str_repeat('0123456789', 33)],
1118  'duplicate @' => ['test@@example.com'],
1119  'duplicate @ combined with further special characters in local part' => ['test!.!@#$%^&*@example.com'],
1120  'opening parenthesis in local part' => ['foo(bar@example.com'],
1121  'closing parenthesis in local part' => ['foo)bar@example.com'],
1122  'opening square bracket in local part' => ['foo[bar@example.com'],
1123  'closing square bracket as local part' => [']@example.com'],
1124  'top level domain only' => ['test@com'],
1125  'dash as second level domain' => ['foo@-.com'],
1126  'domain part starting with dash' => ['foo@-foo.com'],
1127  'domain part ending with dash' => ['foo@foo-.com'],
1128  'number as top level domain' => ['foo@bar.123'],
1129  'dot at beginning of domain part' => ['test@.com'],
1130  'local part ends with dot' => ['e.x.a.m.p.l.e.@example.com'],
1131  'umlauts in local part' => ['äöüfoo@bar.com'],
1132  'trailing whitespace' => ['test@example.com '],
1133  'trailing carriage return' => ['test@example.com' . CR],
1134  'trailing linefeed' => ['test@example.com' . LF],
1135  'trailing carriage return linefeed' => ['test@example.com' . CRLF],
1136  'trailing tab' => ['test@example.com' . TAB],
1137  'prohibited input characters' => ['“mailto:test@example.com”'],
1138  ];
1139  }
1140 
1146  {
1147  $this->assertFalse(GeneralUtility::validEmail($address));
1148  }
1149 
1151  // Tests concerning intExplode
1153 
1157  {
1158  $testString = '1,foo,2';
1159  $expectedArray = [1, 0, 2];
1160  $actualArray = GeneralUtility::intExplode(',', $testString);
1161  $this->assertEquals($expectedArray, $actualArray);
1162  }
1163 
1165  // Tests concerning implodeArrayForUrl / explodeUrl2Array
1167 
1174  {
1175  $valueArray = ['one' => '√', 'two' => 2];
1176  return [
1177  'Empty input' => ['foo', [], ''],
1178  'String parameters' => ['foo', $valueArray, '&foo[one]=%E2%88%9A&foo[two]=2'],
1179  'Nested array parameters' => ['foo', [$valueArray], '&foo[0][one]=%E2%88%9A&foo[0][two]=2'],
1180  'Keep blank parameters' => ['foo', ['one' => '√', ''], '&foo[one]=%E2%88%9A&foo[0]=']
1181  ];
1182  }
1183 
1188  public function implodeArrayForUrlBuildsValidParameterString($name, $input, $expected)
1189  {
1190  $this->assertSame($expected, GeneralUtility::implodeArrayForUrl($name, $input));
1191  }
1192 
1197  {
1198  $input = ['one' => '√', ''];
1199  $expected = '&foo[one]=%E2%88%9A';
1200  $this->assertSame($expected, GeneralUtility::implodeArrayForUrl('foo', $input, '', true));
1201  }
1202 
1207  {
1208  $input = ['one' => '√', ''];
1209  $expected = '&foo%5Bone%5D=%E2%88%9A&foo%5B0%5D=';
1210  $this->assertSame($expected, GeneralUtility::implodeArrayForUrl('foo', $input, '', false, true));
1211  }
1212 
1217  public function explodeUrl2ArrayTransformsParameterStringToNestedArray($name, $array, $input)
1218  {
1219  $expected = $array ? [$name => $array] : [];
1220  $this->assertEquals($expected, GeneralUtility::explodeUrl2Array($input, true));
1221  }
1222 
1227  public function explodeUrl2ArrayTransformsParameterStringToFlatArray($input, $expected)
1228  {
1229  $this->assertEquals($expected, GeneralUtility::explodeUrl2Array($input, false));
1230  }
1231 
1238  {
1239  return [
1240  'Empty string' => ['', []],
1241  'Simple parameter string' => ['&one=%E2%88%9A&two=2', ['one' => '√', 'two' => 2]],
1242  'Nested parameter string' => ['&foo[one]=%E2%88%9A&two=2', ['foo[one]' => '√', 'two' => 2]]
1243  ];
1244  }
1245 
1247  // Tests concerning compileSelectedGetVarsFromArray
1249 
1253  {
1254  $filter = 'foo,bar';
1255  $getArray = ['foo' => 1, 'cake' => 'lie'];
1256  $expected = ['foo' => 1];
1257  $result = GeneralUtility::compileSelectedGetVarsFromArray($filter, $getArray, false);
1258  $this->assertSame($expected, $result);
1259  }
1260 
1265  {
1266  $_GET['bar'] = '2';
1267  $filter = 'foo,bar';
1268  $getArray = ['foo' => 1, 'cake' => 'lie'];
1269  $expected = ['foo' => 1, 'bar' => '2'];
1270  $result = GeneralUtility::compileSelectedGetVarsFromArray($filter, $getArray, true);
1271  $this->assertSame($expected, $result);
1272  }
1273 
1275  // Tests concerning revExplode
1277 
1281  public function revExplodeDataProvider()
1282  {
1283  return [
1284  'limit 0 should return unexploded string' => [
1285  ':',
1286  'my:words:here',
1287  0,
1288  ['my:words:here']
1289  ],
1290  'limit 1 should return unexploded string' => [
1291  ':',
1292  'my:words:here',
1293  1,
1294  ['my:words:here']
1295  ],
1296  'limit 2 should return two pieces' => [
1297  ':',
1298  'my:words:here',
1299  2,
1300  ['my:words', 'here']
1301  ],
1302  'limit 3 should return unexploded string' => [
1303  ':',
1304  'my:words:here',
1305  3,
1306  ['my', 'words', 'here']
1307  ],
1308  'limit 0 should return unexploded string if no delimiter is contained' => [
1309  ':',
1310  'mywordshere',
1311  0,
1312  ['mywordshere']
1313  ],
1314  'limit 1 should return unexploded string if no delimiter is contained' => [
1315  ':',
1316  'mywordshere',
1317  1,
1318  ['mywordshere']
1319  ],
1320  'limit 2 should return unexploded string if no delimiter is contained' => [
1321  ':',
1322  'mywordshere',
1323  2,
1324  ['mywordshere']
1325  ],
1326  'limit 3 should return unexploded string if no delimiter is contained' => [
1327  ':',
1328  'mywordshere',
1329  3,
1330  ['mywordshere']
1331  ],
1332  'multi character delimiter is handled properly with limit 2' => [
1333  '[]',
1334  'a[b][c][d]',
1335  2,
1336  ['a[b][c', 'd]']
1337  ],
1338  'multi character delimiter is handled properly with limit 3' => [
1339  '[]',
1340  'a[b][c][d]',
1341  3,
1342  ['a[b', 'c', 'd]']
1343  ],
1344  ];
1345  }
1346 
1351  public function revExplodeCorrectlyExplodesStringForGivenPartsCount($delimiter, $testString, $count, $expectedArray)
1352  {
1353  $actualArray = GeneralUtility::revExplode($delimiter, $testString, $count);
1354  $this->assertEquals($expectedArray, $actualArray);
1355  }
1356 
1361  {
1362  $testString = 'even:more:of:my:words:here';
1363  $expectedArray = ['even:more:of:my', 'words', 'here'];
1364  $actualArray = GeneralUtility::revExplode(':', $testString, 3);
1365  $this->assertEquals($expectedArray, $actualArray);
1366  }
1367 
1369  // Tests concerning trimExplode
1371 
1380  public function trimExplodeReturnsCorrectResult($delimiter, $testString, $removeEmpty, $limit, $expectedResult)
1381  {
1382  $this->assertSame($expectedResult, GeneralUtility::trimExplode($delimiter, $testString, $removeEmpty, $limit));
1383  }
1384 
1389  {
1390  return [
1391  'spaces at element start and end' => [
1392  ',',
1393  ' a , b , c ,d ,, e,f,',
1394  false,
1395  0,
1396  ['a', 'b', 'c', 'd', '', 'e', 'f', '']
1397  ],
1398  'removes newline' => [
1399  ',',
1400  ' a , b , ' . LF . ' ,d ,, e,f,',
1401  true,
1402  0,
1403  ['a', 'b', 'd', 'e', 'f']
1404  ],
1405  'removes empty elements' => [
1406  ',',
1407  'a , b , c , ,d ,, ,e,f,',
1408  true,
1409  0,
1410  ['a', 'b', 'c', 'd', 'e', 'f']
1411  ],
1412  'keeps remaining results with empty items after reaching limit with positive parameter' => [
1413  ',',
1414  ' a , b , c , , d,, ,e ',
1415  false,
1416  3,
1417  ['a', 'b', 'c , , d,, ,e']
1418  ],
1419  'keeps remaining results without empty items after reaching limit with positive parameter' => [
1420  ',',
1421  ' a , b , c , , d,, ,e ',
1422  true,
1423  3,
1424  ['a', 'b', 'c , d,e']
1425  ],
1426  'keeps remaining results with empty items after reaching limit with negative parameter' => [
1427  ',',
1428  ' a , b , c , d, ,e, f , , ',
1429  false,
1430  -3,
1431  ['a', 'b', 'c', 'd', '', 'e']
1432  ],
1433  'keeps remaining results without empty items after reaching limit with negative parameter' => [
1434  ',',
1435  ' a , b , c , d, ,e, f , , ',
1436  true,
1437  -3,
1438  ['a', 'b', 'c']
1439  ],
1440  'returns exact results without reaching limit with positive parameter' => [
1441  ',',
1442  ' a , b , , c , , , ',
1443  true,
1444  4,
1445  ['a', 'b', 'c']
1446  ],
1447  'keeps zero as string' => [
1448  ',',
1449  'a , b , c , ,d ,, ,e,f, 0 ,',
1450  true,
1451  0,
1452  ['a', 'b', 'c', 'd', 'e', 'f', '0']
1453  ],
1454  'keeps whitespace inside elements' => [
1455  ',',
1456  'a , b , c , ,d ,, ,e,f, g h ,',
1457  true,
1458  0,
1459  ['a', 'b', 'c', 'd', 'e', 'f', 'g h']
1460  ],
1461  'can use internal regex delimiter as explode delimiter' => [
1462  '/',
1463  'a / b / c / /d // /e/f/ g h /',
1464  true,
1465  0,
1466  ['a', 'b', 'c', 'd', 'e', 'f', 'g h']
1467  ],
1468  'can use whitespaces as delimiter' => [
1469  ' ',
1470  '* * * * *',
1471  true,
1472  0,
1473  ['*', '*', '*', '*', '*']
1474  ],
1475  'can use words as delimiter' => [
1476  'All',
1477  'HelloAllTogether',
1478  true,
1479  0,
1480  ['Hello', 'Together']
1481  ],
1482  'can use word with appended and prepended spaces as delimiter' => [
1483  ' all ',
1484  'Hello all together',
1485  true,
1486  0,
1487  ['Hello', 'together']
1488  ],
1489  'can use word with appended and prepended spaces as delimiter and do not remove empty' => [
1490  ' all ',
1491  'Hello all together all there all all are all none',
1492  false,
1493  0,
1494  ['Hello', 'together', 'there', '', 'are', 'none']
1495  ],
1496  'can use word with appended and prepended spaces as delimiter, do not remove empty and limit' => [
1497  ' all ',
1498  'Hello all together all there all all are all none',
1499  false,
1500  5,
1501  ['Hello', 'together', 'there', '', 'are all none']
1502  ],
1503  'can use word with appended and prepended spaces as delimiter, do not remove empty, limit and multiple delimiter in last' => [
1504  ' all ',
1505  'Hello all together all there all all are all none',
1506  false,
1507  4,
1508  ['Hello', 'together', 'there', 'all are all none']
1509  ],
1510  'can use word with appended and prepended spaces as delimiter, remove empty and limit' => [
1511  ' all ',
1512  'Hello all together all there all all are all none',
1513  true,
1514  4,
1515  ['Hello', 'together', 'there', 'are all none']
1516  ],
1517  'can use word with appended and prepended spaces as delimiter, remove empty and limit and multiple delimiter in last' => [
1518  ' all ',
1519  'Hello all together all there all all are all none',
1520  true,
1521  5,
1522  ['Hello', 'together', 'there', 'are', 'none']
1523  ],
1524  'can use words as delimiter and do not remove empty' => [
1525  'all there',
1526  'Helloall theretogether all there all there are all there none',
1527  false,
1528  0,
1529  ['Hello', 'together', '', 'are', 'none']
1530  ],
1531  'can use words as delimiter, do not remove empty and limit' => [
1532  'all there',
1533  'Helloall theretogether all there all there are all there none',
1534  false,
1535  4,
1536  ['Hello', 'together', '', 'are all there none']
1537  ],
1538  'can use words as delimiter, do not remove empty, limit and multiple delimiter in last' => [
1539  'all there',
1540  'Helloall theretogether all there all there are all there none',
1541  false,
1542  3,
1543  ['Hello', 'together', 'all there are all there none']
1544  ],
1545  'can use words as delimiter, remove empty' => [
1546  'all there',
1547  'Helloall theretogether all there all there are all there none',
1548  true,
1549  0,
1550  ['Hello', 'together', 'are', 'none']
1551  ],
1552  'can use words as delimiter, remove empty and limit' => [
1553  'all there',
1554  'Helloall theretogether all there all there are all there none',
1555  true,
1556  3,
1557  ['Hello', 'together', 'are all there none']
1558  ],
1559  'can use words as delimiter, remove empty and limit and multiple delimiter in last' => [
1560  'all there',
1561  'Helloall theretogether all there all there are all there none',
1562  true,
1563  4,
1564  ['Hello', 'together', 'are', 'none']
1565  ],
1566  'can use new line as delimiter' => [
1567  LF,
1568  "Hello\nall\ntogether",
1569  true,
1570  0,
1571  ['Hello', 'all', 'together']
1572  ],
1573  'works with whitespace separator' => [
1574  "\t",
1575  " a b \t c \t \t d \t e \t u j \t s",
1576  false,
1577  0,
1578  ['a b', 'c', '', 'd', 'e', 'u j', 's']
1579  ],
1580  'works with whitespace separator and limit' => [
1581  "\t",
1582  " a b \t c \t \t d \t e \t u j \t s",
1583  false,
1584  4,
1585  ['a b', 'c', '', "d \t e \t u j \t s"]
1586  ],
1587  'works with whitespace separator and remove empty' => [
1588  "\t",
1589  " a b \t c \t \t d \t e \t u j \t s",
1590  true,
1591  0,
1592  ['a b', 'c', 'd', 'e', 'u j', 's']
1593  ],
1594  'works with whitespace separator remove empty and limit' => [
1595  "\t",
1596  " a b \t c \t \t d \t e \t u j \t s",
1597  true,
1598  3,
1599  ['a b', 'c', "d \t e \t u j \t s"]
1600  ],
1601  ];
1602  }
1603 
1605  // Tests concerning getBytesFromSizeMeasurement
1607 
1613  {
1614  return [
1615  '100 kilo Bytes' => ['102400', '100k'],
1616  '100 mega Bytes' => ['104857600', '100m'],
1617  '100 giga Bytes' => ['107374182400', '100g']
1618  ];
1619  }
1620 
1625  public function getBytesFromSizeMeasurementCalculatesCorrectByteValue($expected, $byteString)
1626  {
1627  $this->assertEquals($expected, GeneralUtility::getBytesFromSizeMeasurement($byteString));
1628  }
1629 
1631  // Tests concerning getIndpEnv
1633 
1637  {
1638  $this->assertTrue(strlen(GeneralUtility::getIndpEnv('TYPO3_SITE_PATH')) >= 1);
1639  }
1640 
1645  {
1646  $_SERVER['SCRIPT_NAME'] = '/typo3/';
1647  $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1648  $this->assertEquals('/', $result[0]);
1649  }
1650 
1655  {
1656  if (TYPO3_OS !== 'WIN') {
1657  $this->markTestSkipped('Test available only on Windows OS.');
1658  }
1659  $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1660  $this->assertRegExp('/^[a-z]:\//i', $result);
1661  }
1662 
1667  {
1668  $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1669  $this->assertEquals('/', $result[strlen($result) - 1]);
1670  }
1671 
1675  public static function hostnameAndPortDataProvider()
1676  {
1677  return [
1678  'localhost ipv4 without port' => ['127.0.0.1', '127.0.0.1', ''],
1679  'localhost ipv4 with port' => ['127.0.0.1:81', '127.0.0.1', '81'],
1680  'localhost ipv6 without port' => ['[::1]', '[::1]', ''],
1681  'localhost ipv6 with port' => ['[::1]:81', '[::1]', '81'],
1682  'ipv6 without port' => ['[2001:DB8::1]', '[2001:DB8::1]', ''],
1683  'ipv6 with port' => ['[2001:DB8::1]:81', '[2001:DB8::1]', '81'],
1684  'hostname without port' => ['lolli.did.this', 'lolli.did.this', ''],
1685  'hostname with port' => ['lolli.did.this:42', 'lolli.did.this', '42']
1686  ];
1687  }
1688 
1693  public function getIndpEnvTypo3HostOnlyParsesHostnamesAndIpAdresses($httpHost, $expectedIp)
1694  {
1695  $_SERVER['HTTP_HOST'] = $httpHost;
1696  $this->assertEquals($expectedIp, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
1697  }
1698 
1703  {
1704  unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern']);
1705  $this->assertFalse(GeneralUtilityFixture::isAllowedHostHeaderValue('evil.foo.bar'));
1706  }
1707 
1712  {
1713  return [
1714  'hostname without port matching' => ['lolli.did.this', '.*\.did\.this'],
1715  'other hostname without port matching' => ['helmut.did.this', '.*\.did\.this'],
1716  'two different hostnames without port matching 1st host' => [
1717  'helmut.is.secure',
1718  '(helmut\.is\.secure|lolli\.is\.secure)'
1719  ],
1720  'two different hostnames without port matching 2nd host' => [
1721  'lolli.is.secure',
1722  '(helmut\.is\.secure|lolli\.is\.secure)'
1723  ],
1724  'hostname with port matching' => ['lolli.did.this:42', '.*\.did\.this:42'],
1725  'hostnames are case insensitive 1' => ['lolli.DID.this:42', '.*\.did.this:42'],
1726  'hostnames are case insensitive 2' => ['lolli.did.this:42', '.*\.DID.this:42'],
1727  ];
1728  }
1729 
1734  {
1735  return [
1736  'hostname without port' => ['lolli.did.this', 'helmut\.did\.this'],
1737  'hostname with port, but port not allowed' => ['lolli.did.this:42', 'helmut\.did\.this'],
1738  'two different hostnames in pattern but host header starts with different value #1' => [
1739  'sub.helmut.is.secure',
1740  '(helmut\.is\.secure|lolli\.is\.secure)'
1741  ],
1742  'two different hostnames in pattern but host header starts with different value #2' => [
1743  'sub.lolli.is.secure',
1744  '(helmut\.is\.secure|lolli\.is\.secure)'
1745  ],
1746  'two different hostnames in pattern but host header ends with different value #1' => [
1747  'helmut.is.secure.tld',
1748  '(helmut\.is\.secure|lolli\.is\.secure)'
1749  ],
1750  'two different hostnames in pattern but host header ends with different value #2' => [
1751  'lolli.is.secure.tld',
1752  '(helmut\.is\.secure|lolli\.is\.secure)'
1753  ],
1754  ];
1755  }
1756 
1763  public function isAllowedHostHeaderValueReturnsTrueIfHostValueMatches($httpHost, $hostNamePattern)
1764  {
1765  $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1766  $this->assertTrue(GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1767  }
1768 
1775  public function isAllowedHostHeaderValueReturnsFalseIfHostValueMatches($httpHost, $hostNamePattern)
1776  {
1777  $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1778  $this->assertFalse(GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1779  }
1780 
1782  {
1783  return [
1784  'host value matches server name and server port is default http' => [
1785  'httpHost' => 'secure.web.server',
1786  'serverName' => 'secure.web.server',
1787  'isAllowed' => true,
1788  'serverPort' => '80',
1789  'ssl' => 'Off',
1790  ],
1791  'host value matches server name if compared case insensitive 1' => [
1792  'httpHost' => 'secure.web.server',
1793  'serverName' => 'secure.WEB.server',
1794  'isAllowed' => true,
1795  ],
1796  'host value matches server name if compared case insensitive 2' => [
1797  'httpHost' => 'secure.WEB.server',
1798  'serverName' => 'secure.web.server',
1799  'isAllowed' => true,
1800  ],
1801  'host value matches server name and server port is default https' => [
1802  'httpHost' => 'secure.web.server',
1803  'serverName' => 'secure.web.server',
1804  'isAllowed' => true,
1805  'serverPort' => '443',
1806  'ssl' => 'On',
1807  ],
1808  'host value matches server name and server port' => [
1809  'httpHost' => 'secure.web.server:88',
1810  'serverName' => 'secure.web.server',
1811  'isAllowed' => true,
1812  'serverPort' => '88',
1813  ],
1814  'host value matches server name case insensitive 1 and server port' => [
1815  'httpHost' => 'secure.WEB.server:88',
1816  'serverName' => 'secure.web.server',
1817  'isAllowed' => true,
1818  'serverPort' => '88',
1819  ],
1820  'host value matches server name case insensitive 2 and server port' => [
1821  'httpHost' => 'secure.web.server:88',
1822  'serverName' => 'secure.WEB.server',
1823  'isAllowed' => true,
1824  'serverPort' => '88',
1825  ],
1826  'host value is ipv6 but matches server name and server port' => [
1827  'httpHost' => '[::1]:81',
1828  'serverName' => '[::1]',
1829  'isAllowed' => true,
1830  'serverPort' => '81',
1831  ],
1832  'host value does not match server name' => [
1833  'httpHost' => 'insecure.web.server',
1834  'serverName' => 'secure.web.server',
1835  'isAllowed' => false,
1836  ],
1837  'host value does not match server port' => [
1838  'httpHost' => 'secure.web.server:88',
1839  'serverName' => 'secure.web.server',
1840  'isAllowed' => false,
1841  'serverPort' => '89',
1842  ],
1843  'host value has default port that does not match server port' => [
1844  'httpHost' => 'secure.web.server',
1845  'serverName' => 'secure.web.server',
1846  'isAllowed' => false,
1847  'serverPort' => '81',
1848  'ssl' => 'Off',
1849  ],
1850  'host value has default port that does not match server ssl port' => [
1851  'httpHost' => 'secure.web.server',
1852  'serverName' => 'secure.web.server',
1853  'isAllowed' => false,
1854  'serverPort' => '444',
1855  'ssl' => 'On',
1856  ],
1857  ];
1858  }
1859 
1870  $httpHost,
1871  $serverName,
1872  $isAllowed,
1873  $serverPort = '80',
1874  $ssl = 'Off'
1875  ) {
1876  $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME;
1877  $_SERVER['SERVER_NAME'] = $serverName;
1878  $_SERVER['SERVER_PORT'] = $serverPort;
1879  $_SERVER['HTTPS'] = $ssl;
1880  $this->assertSame($isAllowed, GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1881  }
1882 
1887  {
1888  GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1890  GeneralUtilityFixture::getIndpEnv('TYPO3_HOST_ONLY');
1892  GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_HOST');
1894  GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_URL');
1896  }
1897 
1904  public function getIndpEnvForHostThrowsExceptionForNotAllowedHostnameValues($httpHost, $hostNamePattern)
1905  {
1906  $this->expectException(\UnexpectedValueException::class);
1907  $this->expectExceptionCode(1396795884);
1908  $_SERVER['HTTP_HOST'] = $httpHost;
1909  $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1910  GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1911  }
1912 
1920  {
1921  $_SERVER['HTTP_HOST'] = $httpHost;
1922  $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
1923  $this->assertSame($httpHost, GeneralUtility::getIndpEnv('HTTP_HOST'));
1924  }
1925 
1930  public function getIndpEnvTypo3PortParsesHostnamesAndIpAdresses($httpHost, $dummy, $expectedPort)
1931  {
1932  $_SERVER['HTTP_HOST'] = $httpHost;
1933  $this->assertEquals($expectedPort, GeneralUtility::getIndpEnv('TYPO3_PORT'));
1934  }
1935 
1937  // Tests concerning underscoredToUpperCamelCase
1939 
1945  {
1946  return [
1947  'single word' => ['Blogexample', 'blogexample'],
1948  'multiple words' => ['BlogExample', 'blog_example']
1949  ];
1950  }
1951 
1956  public function underscoredToUpperCamelCase($expected, $inputString)
1957  {
1958  $this->assertEquals($expected, GeneralUtility::underscoredToUpperCamelCase($inputString));
1959  }
1960 
1962  // Tests concerning underscoredToLowerCamelCase
1964 
1970  {
1971  return [
1972  'single word' => ['minimalvalue', 'minimalvalue'],
1973  'multiple words' => ['minimalValue', 'minimal_value']
1974  ];
1975  }
1976 
1981  public function underscoredToLowerCamelCase($expected, $inputString)
1982  {
1983  $this->assertEquals($expected, GeneralUtility::underscoredToLowerCamelCase($inputString));
1984  }
1985 
1987  // Tests concerning camelCaseToLowerCaseUnderscored
1989 
1995  {
1996  return [
1997  'single word' => ['blogexample', 'blogexample'],
1998  'single word starting upper case' => ['blogexample', 'Blogexample'],
1999  'two words starting lower case' => ['minimal_value', 'minimalValue'],
2000  'two words starting upper case' => ['blog_example', 'BlogExample']
2001  ];
2002  }
2003 
2008  public function camelCaseToLowerCaseUnderscored($expected, $inputString)
2009  {
2010  $this->assertEquals($expected, GeneralUtility::camelCaseToLowerCaseUnderscored($inputString));
2011  }
2012 
2014  // Tests concerning lcFirst
2016 
2021  public function lcfirstDataProvider()
2022  {
2023  return [
2024  'single word' => ['blogexample', 'blogexample'],
2025  'single Word starting upper case' => ['blogexample', 'Blogexample'],
2026  'two words' => ['blogExample', 'BlogExample']
2027  ];
2028  }
2029 
2034  public function lcFirst($expected, $inputString)
2035  {
2036  $this->assertEquals($expected, GeneralUtility::lcfirst($inputString));
2037  }
2038 
2040  // Tests concerning isValidUrl
2042 
2048  {
2049  return [
2050  'http' => ['http://www.example.org/'],
2051  'http without trailing slash' => ['http://qwe'],
2052  'http directory with trailing slash' => ['http://www.example/img/dir/'],
2053  'http directory without trailing slash' => ['http://www.example/img/dir'],
2054  'http index.html' => ['http://example.com/index.html'],
2055  'http index.php' => ['http://www.example.com/index.php'],
2056  'http test.png' => ['http://www.example/img/test.png'],
2057  'http username password querystring and ancher' => ['https://user:pw@www.example.org:80/path?arg=value#fragment'],
2058  'file' => ['file:///tmp/test.c'],
2059  'file directory' => ['file://foo/bar'],
2060  'ftp directory' => ['ftp://ftp.example.com/tmp/'],
2061  'mailto' => ['mailto:foo@bar.com'],
2062  'news' => ['news:news.php.net'],
2063  'telnet' => ['telnet://192.0.2.16:80/'],
2064  'ldap' => ['ldap://[2001:db8::7]/c=GB?objectClass?one'],
2065  'http punycode domain name' => ['http://www.xn--bb-eka.at'],
2066  'http punicode subdomain' => ['http://xn--h-zfa.oebb.at'],
2067  'http domain-name umlauts' => ['http://www.öbb.at'],
2068  'http subdomain umlauts' => ['http://äh.oebb.at'],
2069  ];
2070  }
2071 
2077  {
2078  $this->assertTrue(GeneralUtility::isValidUrl($url));
2079  }
2080 
2087  {
2088  return [
2089  'http missing colon' => ['http//www.example/wrong/url/'],
2090  'http missing slash' => ['http:/www.example'],
2091  'hostname only' => ['www.example.org/'],
2092  'file missing protocol specification' => ['/tmp/test.c'],
2093  'slash only' => ['/'],
2094  'string http://' => ['http://'],
2095  'string http:/' => ['http:/'],
2096  'string http:' => ['http:'],
2097  'string http' => ['http'],
2098  'empty string' => [''],
2099  'string -1' => ['-1'],
2100  'string array()' => ['array()'],
2101  'random string' => ['qwe'],
2102  'http directory umlauts' => ['http://www.oebb.at/äöü/'],
2103  'prohibited input characters' => ['https://{$unresolved_constant}'],
2104  ];
2105  }
2106 
2112  {
2113  $this->assertFalse(GeneralUtility::isValidUrl($url));
2114  }
2115 
2117  // Tests concerning isOnCurrentHost
2119 
2123  {
2124  $testUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
2125  $this->assertTrue(GeneralUtility::isOnCurrentHost($testUrl));
2126  }
2127 
2134  {
2135  return [
2136  'empty string' => [''],
2137  'arbitrary string' => ['arbitrary string'],
2138  'localhost IP' => ['127.0.0.1'],
2139  'relative path' => ['./relpath/file.txt'],
2140  'absolute path' => ['/abspath/file.txt?arg=value'],
2141  'differnt host' => [GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . '.example.org']
2142  ];
2143  }
2144 
2146  // Tests concerning sanitizeLocalUrl
2148 
2154  {
2155  return [
2156  'alt_intro.php' => ['alt_intro.php'],
2157  'alt_intro.php?foo=1&bar=2' => ['alt_intro.php?foo=1&bar=2'],
2158  '../index.php' => ['../index.php'],
2159  '../typo3/alt_intro.php' => ['../typo3/alt_intro.php'],
2160  '../~userDirectory/index.php' => ['../~userDirectory/index.php'],
2161  '../typo3/index.php?var1=test-case&var2=~user' => ['../typo3/index.php?var1=test-case&var2=~user'],
2162  PATH_site . 'typo3/alt_intro.php' => [PATH_site . 'typo3/alt_intro.php'],
2163  ];
2164  }
2165 
2172  {
2173  $this->assertEquals($path, GeneralUtility::sanitizeLocalUrl($path));
2174  }
2175 
2182  {
2183  $this->assertEquals(rawurlencode($path), GeneralUtility::sanitizeLocalUrl(rawurlencode($path)));
2184  }
2185 
2192  {
2193  $host = 'localhost';
2194  $subDirectory = '/cms/';
2195 
2196  return [
2197  $subDirectory . 'typo3/alt_intro.php' => [
2198  $subDirectory . 'typo3/alt_intro.php',
2199  $host,
2200  $subDirectory,
2201  ],
2202  $subDirectory . 'index.php' => [
2203  $subDirectory . 'index.php',
2204  $host,
2205  $subDirectory,
2206  ],
2207  'http://' . $host . '/typo3/alt_intro.php' => [
2208  'http://' . $host . '/typo3/alt_intro.php',
2209  $host,
2210  '',
2211  ],
2212  'http://' . $host . $subDirectory . 'typo3/alt_intro.php' => [
2213  'http://' . $host . $subDirectory . 'typo3/alt_intro.php',
2214  $host,
2215  $subDirectory,
2216  ],
2217  ];
2218  }
2219 
2227  public function sanitizeLocalUrlAcceptsNotEncodedValidUrls($url, $host, $subDirectory)
2228  {
2229  $_SERVER['HTTP_HOST'] = $host;
2230  $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2231  $this->assertEquals($url, GeneralUtility::sanitizeLocalUrl($url));
2232  }
2233 
2241  public function sanitizeLocalUrlAcceptsEncodedValidUrls($url, $host, $subDirectory)
2242  {
2243  $_SERVER['HTTP_HOST'] = $host;
2244  $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2245  $this->assertEquals(rawurlencode($url), GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2246  }
2247 
2254  {
2255  return [
2256  'empty string' => [''],
2257  'http domain' => ['http://www.google.de/'],
2258  'https domain' => ['https://www.google.de/'],
2259  'XSS attempt' => ['" onmouseover="alert(123)"'],
2260  'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
2261  'invalid URL, HTML break out attempt' => ['" >blabuubb'],
2262  'base64 encoded string' => ['data:%20text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4='],
2263  ];
2264  }
2265 
2271  {
2272  $this->assertEquals('', GeneralUtility::sanitizeLocalUrl($url));
2273  }
2274 
2280  {
2281  $this->assertEquals('', GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2282  }
2283 
2285  // Tests concerning unlink_tempfile
2287 
2292  {
2293  $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2294  $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2295  @copy($fixtureFile, $testFilename);
2296  GeneralUtility::unlink_tempfile($testFilename);
2297  $fileExists = file_exists($testFilename);
2298  $this->assertFalse($fileExists);
2299  }
2300 
2305  {
2306  $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2307  $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('.test_') . '.gif';
2308  @copy($fixtureFile, $testFilename);
2309  GeneralUtility::unlink_tempfile($testFilename);
2310  $fileExists = file_exists($testFilename);
2311  $this->assertFalse($fileExists);
2312  }
2313 
2318  {
2319  $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2320  $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2321  @copy($fixtureFile, $testFilename);
2322  $returnValue = GeneralUtility::unlink_tempfile($testFilename);
2323  $this->assertTrue($returnValue);
2324  }
2325 
2330  {
2331  $returnValue = GeneralUtility::unlink_tempfile(
2332  PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('i_do_not_exist')
2333  );
2334  $this->assertNull($returnValue);
2335  }
2336 
2341  {
2342  $returnValue = GeneralUtility::unlink_tempfile('/tmp/typo3-unit-test-unlink_tempfile');
2343  $this->assertNull($returnValue);
2344  }
2345 
2347  // Tests concerning tempnam
2349 
2354  {
2355  $filePath = GeneralUtility::tempnam('foo');
2356  $this->testFilesToDelete[] = $filePath;
2357  $fileName = basename($filePath);
2358  $this->assertStringStartsWith('foo', $fileName);
2359  }
2360 
2365  {
2366  $filePath = GeneralUtility::tempnam('foo');
2367  $this->testFilesToDelete[] = $filePath;
2368  $this->assertNotContains('\\', $filePath);
2369  }
2370 
2375  {
2376  $filePath = GeneralUtility::tempnam('foo');
2377  $this->testFilesToDelete[] = $filePath;
2378  $this->assertStringStartsWith(PATH_site, $filePath);
2379  }
2380 
2382  // Tests concerning removeDotsFromTS
2384 
2388  {
2389  $typoScript = [
2390  'propertyA.' => [
2391  'keyA.' => [
2392  'valueA' => 1
2393  ],
2394  'keyB' => 2
2395  ],
2396  'propertyB' => 3
2397  ];
2398  $expectedResult = [
2399  'propertyA' => [
2400  'keyA' => [
2401  'valueA' => 1
2402  ],
2403  'keyB' => 2
2404  ],
2405  'propertyB' => 3
2406  ];
2407  $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2408  }
2409 
2414  {
2415  $typoScript = [
2416  'propertyA.' => [
2417  'keyA' => 'getsOverridden',
2418  'keyA.' => [
2419  'valueA' => 1
2420  ],
2421  'keyB' => 2
2422  ],
2423  'propertyB' => 3
2424  ];
2425  $expectedResult = [
2426  'propertyA' => [
2427  'keyA' => [
2428  'valueA' => 1
2429  ],
2430  'keyB' => 2
2431  ],
2432  'propertyB' => 3
2433  ];
2434  $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2435  }
2436 
2441  {
2442  $typoScript = [
2443  'propertyA.' => [
2444  'keyA.' => [
2445  'valueA' => 1
2446  ],
2447  'keyA' => 'willOverride',
2448  'keyB' => 2
2449  ],
2450  'propertyB' => 3
2451  ];
2452  $expectedResult = [
2453  'propertyA' => [
2454  'keyA' => 'willOverride',
2455  'keyB' => 2
2456  ],
2457  'propertyB' => 3
2458  ];
2459  $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2460  }
2461 
2463  // Tests concerning get_dirs
2465 
2469  {
2470  $path = PATH_typo3conf;
2471  $directories = GeneralUtility::get_dirs($path);
2472  $this->assertInternalType(\PHPUnit\Framework\Constraint\IsType::TYPE_ARRAY, $directories);
2473  }
2474 
2479  {
2480  $path = 'foo';
2481  $result = GeneralUtility::get_dirs($path);
2482  $expectedResult = 'error';
2483  $this->assertEquals($expectedResult, $result);
2484  }
2485 
2487  // Tests concerning hmac
2489 
2493  {
2494  $hmac = GeneralUtility::hmac('message');
2495  $this->assertTrue(!empty($hmac) && is_string($hmac));
2496  $this->assertTrue(strlen($hmac) == 40);
2497  }
2498 
2503  {
2504  $msg0 = 'message';
2505  $msg1 = 'message';
2506  $this->assertEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2507  }
2508 
2513  {
2514  $msg0 = 'message0';
2515  $msg1 = 'message1';
2516  $this->assertNotEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2517  }
2518 
2520  // Tests concerning quoteJSvalue
2522 
2527  public function quoteJsValueDataProvider()
2528  {
2529  return [
2530  'Immune characters are returned as is' => [
2531  '._,',
2532  '._,'
2533  ],
2534  'Alphanumerical characters are returned as is' => [
2535  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
2536  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
2537  ],
2538  'Angle brackets and ampersand are encoded' => [
2539  '<>&',
2540  '\\u003C\\u003E\\u0026'
2541  ],
2542  'Quotes and backslashes are encoded' => [
2543  '"\'\\',
2544  '\\u0022\\u0027\\u005C'
2545  ],
2546  'Forward slashes are escaped' => [
2547  '</script>',
2548  '\\u003C\\/script\\u003E'
2549  ],
2550  'Empty string stays empty' => [
2551  '',
2552  ''
2553  ],
2554  'Exclamation mark and space are properly encoded' => [
2555  'Hello World!',
2556  'Hello\\u0020World\\u0021'
2557  ],
2558  'Whitespaces are properly encoded' => [
2559  TAB . LF . CR . ' ',
2560  '\\u0009\\u000A\\u000D\\u0020'
2561  ],
2562  'Null byte is properly encoded' => [
2563  chr(0),
2564  '\\u0000'
2565  ],
2566  'Umlauts are properly encoded' => [
2567  'ÜüÖöÄä',
2568  '\\u00dc\\u00fc\\u00d6\\u00f6\\u00c4\\u00e4'
2569  ]
2570  ];
2571  }
2572 
2579  public function quoteJsValueTest($input, $expected)
2580  {
2581  $this->assertSame('\'' . $expected . '\'', GeneralUtility::quoteJSvalue($input));
2582  }
2583 
2585  // Tests concerning _GETset()
2587 
2591  {
2592  $_GET = [];
2593  $GLOBALS['HTTP_GET_VARS'] = [];
2594  $getParameters = ['foo' => 'bar'];
2595  GeneralUtility::_GETset($getParameters);
2596  $this->assertSame($getParameters, $_GET);
2597  }
2598 
2603  {
2604  $_GET = [];
2605  $GLOBALS['HTTP_GET_VARS'] = [];
2606  $getParameters = ['foo' => 'bar'];
2607  GeneralUtility::_GETset($getParameters);
2608  $this->assertSame($getParameters, $GLOBALS['HTTP_GET_VARS']);
2609  }
2610 
2615  {
2616  $_GET = [];
2617  $GLOBALS['HTTP_GET_VARS'] = [];
2618  GeneralUtility::_GETset(['foo' => 'bar']);
2619  GeneralUtility::_GETset(['oneKey' => 'oneValue']);
2620  $this->assertEquals(['oneKey' => 'oneValue'], $GLOBALS['HTTP_GET_VARS']);
2621  }
2622 
2627  {
2628  $_GET = [];
2629  $GLOBALS['HTTP_GET_VARS'] = [];
2630  GeneralUtility::_GETset('oneValue', 'oneKey');
2631  $this->assertEquals('oneValue', $GLOBALS['HTTP_GET_VARS']['oneKey']);
2632  }
2633 
2638  {
2639  $_GET = [];
2640  $GLOBALS['HTTP_GET_VARS'] = [];
2641  GeneralUtility::_GETset(['foo' => 'bar']);
2642  GeneralUtility::_GETset('oneValue', 'oneKey');
2643  $this->assertEquals(['foo' => 'bar', 'oneKey' => 'oneValue'], $GLOBALS['HTTP_GET_VARS']);
2644  }
2645 
2650  {
2651  $_GET = [];
2652  $GLOBALS['HTTP_GET_VARS'] = [];
2653  GeneralUtility::_GETset(['childKey' => 'oneValue'], 'parentKey');
2654  $this->assertEquals(['parentKey' => ['childKey' => 'oneValue']], $GLOBALS['HTTP_GET_VARS']);
2655  }
2656 
2661  {
2662  $_GET = [];
2663  $GLOBALS['HTTP_GET_VARS'] = [];
2664  GeneralUtility::_GETset('oneValue', 'parentKey|childKey');
2665  $this->assertEquals(['parentKey' => ['childKey' => 'oneValue']], $GLOBALS['HTTP_GET_VARS']);
2666  }
2667 
2672  {
2673  $_GET = [];
2674  $GLOBALS['HTTP_GET_VARS'] = [];
2675  GeneralUtility::_GETset(['key1' => 'value1', 'key2' => 'value2'], 'parentKey|childKey');
2676  $this->assertEquals(
2677  [
2678  'parentKey' => [
2679  'childKey' => ['key1' => 'value1', 'key2' => 'value2']
2680  ]
2681  ],
2682  $GLOBALS['HTTP_GET_VARS']
2683  );
2684  }
2685 
2687  // Tests concerning minifyJavaScript
2689 
2693  {
2694  unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript']);
2695  $testString = $this->getUniqueId('string');
2696  $this->assertSame($testString, GeneralUtility::minifyJavaScript($testString));
2697  }
2698 
2706  {
2707  $hookClassName = $this->getUniqueId('tx_coretest');
2708  $minifyHookMock = $this->getMockBuilder('stdClass')
2709  ->setMethods(['minify'])
2710  ->setMockClassName($hookClassName)
2711  ->getMock();
2712  $functionName = $hookClassName . '->minify';
2713  $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = [];
2714  $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2715  $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2716  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2717  $minifyHookMock->expects($this->once())->method('minify')->will(
2718  $this->returnCallback([$this, 'isMinifyJavaScriptHookCalledCallback'])
2719  );
2721  }
2722 
2728  public function isMinifyJavaScriptHookCalledCallback(array $params)
2729  {
2730  // We can not throw an exception here, because that would be caught by the
2731  // minifyJavaScript method under test itself. Thus, we just die if the
2732  // input string is not ok.
2733  if ($params['script'] !== 'foo') {
2734  die('broken');
2735  }
2736  }
2737 
2745  {
2746  $hookClassName = $this->getUniqueId('tx_coretest');
2747  $minifyHookMock = $this->getMockBuilder('stdClass')
2748  ->setMethods(['minify'])
2749  ->setMockClassName($hookClassName)
2750  ->getMock();
2751  $functionName = '&' . $hookClassName . '->minify';
2752  $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = [];
2753  $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2754  $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2755  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2756  $minifyHookMock->expects($this->any())->method('minify')->will(
2757  $this->returnCallback([$this, 'minifyJavaScriptErroneousCallback'])
2758  );
2759  $error = '';
2760  GeneralUtility::minifyJavaScript('string to compress', $error);
2761  $this->assertSame('Error minifying java script: foo', $error);
2762  }
2763 
2771  {
2772  $hookClassName = $this->getUniqueId('tx_coretest');
2773  $minifyHookMock = $this->getMockBuilder('stdClass')
2774  ->setMethods(['minify'])
2775  ->setMockClassName($hookClassName)
2776  ->getMock();
2777  $functionName = '&' . $hookClassName . '->minify';
2778  $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = [];
2779  $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2780  $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2781  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2782  $minifyHookMock->expects($this->any())->method('minify')->will(
2783  $this->returnCallback([$this, 'minifyJavaScriptErroneousCallback'])
2784  );
2785  $this->expectException(\RuntimeException::class);
2787  }
2788 
2797  {
2798  throw new \RuntimeException('foo', 1344888548);
2799  }
2800 
2802  // Tests concerning fixPermissions
2804 
2807  public function fixPermissionsSetsGroup()
2808  {
2809  if (TYPO3_OS === 'WIN') {
2810  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2811  }
2812  if (!function_exists('posix_getegid')) {
2813  $this->markTestSkipped('Function posix_getegid() not available, fixPermissionsSetsGroup() tests skipped');
2814  }
2815  if (posix_getegid() === -1) {
2816  $this->markTestSkipped(
2817  'The fixPermissionsSetsGroup() is not available on Mac OS because posix_getegid() always returns -1 on Mac OS.'
2818  );
2819  }
2820  // Create and prepare test file
2821  $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2823  $currentGroupId = posix_getegid();
2824  // Set target group and run method
2825  $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $currentGroupId;
2827  clearstatcache();
2828  $this->assertEquals($currentGroupId, filegroup($filename));
2829  }
2830 
2835  {
2836  if (TYPO3_OS === 'WIN') {
2837  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2838  }
2839  // Create and prepare test file
2840  $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2842  chmod($filename, 482);
2843  // Set target permissions and run method
2844  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2845  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2846  clearstatcache();
2847  $this->assertTrue($fixPermissionsResult);
2848  $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2849  }
2850 
2855  {
2856  if (TYPO3_OS === 'WIN') {
2857  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2858  }
2859  // Create and prepare test file
2860  $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2862  chmod($filename, 482);
2863  // Set target permissions and run method
2864  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2865  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2866  clearstatcache();
2867  $this->assertTrue($fixPermissionsResult);
2868  $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2869  }
2870 
2875  {
2876  if (TYPO3_OS === 'WIN') {
2877  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2878  }
2879  // Create and prepare test directory
2880  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2882  chmod($directory, 1551);
2883  // Set target permissions and run method
2884  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2885  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2886  clearstatcache();
2887  $this->assertTrue($fixPermissionsResult);
2888  $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2889  }
2890 
2895  {
2896  if (TYPO3_OS === 'WIN') {
2897  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2898  }
2899  // Create and prepare test directory
2900  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2902  chmod($directory, 1551);
2903  // Set target permissions and run method
2904  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2905  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory . '/');
2906  // Get actual permissions and clean up
2907  clearstatcache();
2908  $this->assertTrue($fixPermissionsResult);
2909  $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2910  }
2911 
2916  {
2917  if (TYPO3_OS === 'WIN') {
2918  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2919  }
2920  // Create and prepare test directory
2921  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2923  chmod($directory, 1551);
2924  // Set target permissions and run method
2925  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2926  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2927  // Get actual permissions and clean up
2928  clearstatcache();
2929  $this->assertTrue($fixPermissionsResult);
2930  $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2931  }
2932 
2937  {
2938  if (TYPO3_OS === 'WIN') {
2939  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2940  }
2941  // Create and prepare test directory and file structure
2942  $baseDirectory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2944  chmod($baseDirectory, 1751);
2945  GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/file', '42');
2946  chmod($baseDirectory . '/file', 482);
2947  GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/foo');
2948  chmod($baseDirectory . '/foo', 1751);
2949  GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/foo/file', '42');
2950  chmod($baseDirectory . '/foo/file', 482);
2951  GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/.bar');
2952  chmod($baseDirectory . '/.bar', 1751);
2953  // Use this if writeFileToTypo3tempDir is fixed to create hidden files in subdirectories
2954  // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/.file', '42');
2955  // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/..file2', '42');
2956  touch($baseDirectory . '/.bar/.file', '42');
2957  chmod($baseDirectory . '/.bar/.file', 482);
2958  touch($baseDirectory . '/.bar/..file2', '42');
2959  chmod($baseDirectory . '/.bar/..file2', 482);
2960  // Set target permissions and run method
2961  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2962  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2963  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($baseDirectory, true);
2964  // Get actual permissions
2965  clearstatcache();
2966  $resultBaseDirectoryPermissions = substr(decoct(fileperms($baseDirectory)), 1);
2967  $resultBaseFilePermissions = substr(decoct(fileperms($baseDirectory . '/file')), 2);
2968  $resultFooDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/foo')), 1);
2969  $resultFooFilePermissions = substr(decoct(fileperms($baseDirectory . '/foo/file')), 2);
2970  $resultBarDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/.bar')), 1);
2971  $resultBarFilePermissions = substr(decoct(fileperms($baseDirectory . '/.bar/.file')), 2);
2972  $resultBarFile2Permissions = substr(decoct(fileperms($baseDirectory . '/.bar/..file2')), 2);
2973  // Test if everything was ok
2974  $this->assertTrue($fixPermissionsResult);
2975  $this->assertEquals('0770', $resultBaseDirectoryPermissions);
2976  $this->assertEquals('0660', $resultBaseFilePermissions);
2977  $this->assertEquals('0770', $resultFooDirectoryPermissions);
2978  $this->assertEquals('0660', $resultFooFilePermissions);
2979  $this->assertEquals('0770', $resultBarDirectoryPermissions);
2980  $this->assertEquals('0660', $resultBarFilePermissions);
2981  $this->assertEquals('0660', $resultBarFile2Permissions);
2982  }
2983 
2988  {
2989  if (TYPO3_OS === 'WIN') {
2990  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2991  }
2992  // Create and prepare test file
2993  $filename = PATH_site . 'typo3temp/var/tests/../../../typo3temp/var/tests/' . $this->getUniqueId('test_');
2994  // Set target permissions and run method
2995  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2996  $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2997  $this->assertFalse($fixPermissionsResult);
2998  }
2999 
3004  {
3005  if (TYPO3_OS === 'WIN') {
3006  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3007  }
3008  $filename = 'typo3temp/var/tests/' . $this->getUniqueId('test_');
3009  GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filename, '42');
3010  $this->testFilesToDelete[] = PATH_site . $filename;
3011  chmod(PATH_site . $filename, 482);
3012  // Set target permissions and run method
3013  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
3014  $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
3015  clearstatcache();
3016  $this->assertTrue($fixPermissionsResult);
3017  $this->assertEquals('0660', substr(decoct(fileperms(PATH_site . $filename)), 2));
3018  }
3019 
3024  {
3025  if (TYPO3_OS === 'WIN') {
3026  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3027  }
3028  $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3030  chmod($filename, 482);
3031  unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']);
3032  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
3033  clearstatcache();
3034  $this->assertTrue($fixPermissionsResult);
3035  $this->assertEquals('0644', substr(decoct(fileperms($filename)), 2));
3036  }
3037 
3042  {
3043  if (TYPO3_OS === 'WIN') {
3044  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3045  }
3046  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3048  chmod($directory, 1551);
3049  unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
3050  $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
3051  clearstatcache();
3052  $this->assertTrue($fixPermissionsResult);
3053  $this->assertEquals('0755', substr(decoct(fileperms($directory)), 1));
3054  }
3055 
3057  // Tests concerning mkdir
3059 
3062  public function mkdirCreatesDirectory()
3063  {
3064  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3065  $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
3066  clearstatcache();
3067  $this->assertTrue($mkdirResult);
3068  $this->assertTrue(is_dir($directory));
3069  }
3070 
3075  {
3076  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('.test_');
3077  $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
3078  clearstatcache();
3079  $this->assertTrue($mkdirResult);
3080  $this->assertTrue(is_dir($directory));
3081  }
3082 
3087  {
3088  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_') . '/';
3089  $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
3090  clearstatcache();
3091  $this->assertTrue($mkdirResult);
3092  $this->assertTrue(is_dir($directory));
3093  }
3094 
3099  {
3100  if (TYPO3_OS === 'WIN') {
3101  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3102  }
3103  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3104  $oldUmask = umask(19);
3105  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0772';
3107  clearstatcache();
3108  $resultDirectoryPermissions = substr(decoct(fileperms($directory)), 1);
3109  umask($oldUmask);
3110  $this->assertEquals($resultDirectoryPermissions, '0772');
3111  }
3112 
3117  {
3118  $swapGroup = $this->checkGroups(__FUNCTION__);
3119  if ($swapGroup !== false) {
3120  $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3121  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('mkdirtest_');
3123  clearstatcache();
3124  $resultDirectoryGroup = filegroup($directory);
3125  $this->assertEquals($resultDirectoryGroup, $swapGroup);
3126  }
3127  }
3128 
3130  // Helper function for filesystem ownership tests
3132 
3139  private function checkGroups($methodName)
3140  {
3141  if (TYPO3_OS === 'WIN') {
3142  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3143  return false;
3144  }
3145  if (!function_exists('posix_getegid')) {
3146  $this->markTestSkipped('Function posix_getegid() not available, ' . $methodName . '() tests skipped');
3147  return false;
3148  }
3149  if (posix_getegid() === -1) {
3150  $this->markTestSkipped('Function posix_getegid() returns -1, ' . $methodName . '() tests skipped');
3151  return false;
3152  }
3153  if (!function_exists('posix_getgroups')) {
3154  $this->markTestSkipped('Function posix_getgroups() not available, ' . $methodName . '() tests skipped');
3155  return false;
3156  }
3157  $groups = posix_getgroups();
3158  if (count($groups) <= 1) {
3159  $this->markTestSkipped(
3160  $methodName . '() test cannot be done when the web server user is only member of 1 group.'
3161  );
3162  return false;
3163  }
3164  $secondaryGroups = array_diff($groups, [posix_getegid()]);
3165  return array_shift($secondaryGroups);
3166  }
3167 
3169  // Tests concerning mkdir_deep
3171 
3174  public function mkdirDeepCreatesDirectory()
3175  {
3176  $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
3177  GeneralUtility::mkdir_deep($directory);
3178  $this->assertTrue(is_dir($directory));
3179  }
3180 
3185  {
3186  $directory = $this->getVirtualTestDir() . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
3187  $subDirectory = $directory . '/foo';
3188  GeneralUtility::mkdir_deep($subDirectory);
3189  $this->assertTrue(is_dir($subDirectory));
3190  }
3191 
3198  {
3199  return [
3200  'no double slash if concatenated with PATH_site' => ['fileadmin/testDir1'],
3201  'double slash if concatenated with PATH_site' => ['/fileadmin/testDir2'],
3202  ];
3203  }
3204 
3209  public function mkdirDeepCreatesDirectoryWithDoubleSlashes($directoryToCreate)
3210  {
3211  vfsStream::setup();
3212  // Load fixture files and folders from disk
3213  FileStreamWrapper::init(PATH_site);
3214  FileStreamWrapper::registerOverlayPath('fileadmin', 'vfs://root/fileadmin', true);
3215  GeneralUtility::mkdir_deep(PATH_site, $directoryToCreate);
3216  $this->assertTrue(is_dir(PATH_site . $directoryToCreate));
3217  FileStreamWrapper::destroy();
3218  }
3219 
3224  {
3225  if (TYPO3_OS === 'WIN') {
3226  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3227  }
3228  $directory = $this->getUniqueId('mkdirdeeptest_');
3229  $oldUmask = umask(19);
3230  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
3231  GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $directory);
3232  $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3233  clearstatcache();
3234  umask($oldUmask);
3235  $this->assertEquals('777', substr(decoct(fileperms(PATH_site . 'typo3temp/var/tests/' . $directory)), -3, 3));
3236  }
3237 
3242  {
3243  if (TYPO3_OS === 'WIN') {
3244  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3245  }
3246  $directory = $this->getUniqueId('mkdirdeeptest_');
3247  $subDirectory = $directory . '/bar';
3248  $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
3249  $oldUmask = umask(19);
3250  GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $subDirectory);
3251  $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3252  clearstatcache();
3253  umask($oldUmask);
3254  $this->assertEquals('777', substr(decoct(fileperms(PATH_site . 'typo3temp/var/tests/' . $directory)), -3, 3));
3255  }
3256 
3261  {
3262  if (TYPO3_OS === 'WIN') {
3263  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
3264  }
3265  $baseDirectory = PATH_site . 'typo3temp/var/tests/';
3266  $existingDirectory = $this->getUniqueId('test_existing_') . '/';
3267  $newSubDirectory = $this->getUniqueId('test_new_');
3268  @mkdir(($baseDirectory . $existingDirectory));
3269  $this->testFilesToDelete[] = $baseDirectory . $existingDirectory;
3270  chmod($baseDirectory . $existingDirectory, 482);
3271  GeneralUtility::mkdir_deep($baseDirectory, $existingDirectory . $newSubDirectory);
3272  $this->assertEquals('0742', substr(decoct(fileperms($baseDirectory . $existingDirectory)), 2));
3273  }
3274 
3279  {
3280  $swapGroup = $this->checkGroups(__FUNCTION__);
3281  if ($swapGroup !== false) {
3282  $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3283  $directory = $this->getUniqueId('mkdirdeeptest_');
3284  GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $directory);
3285  $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3286  clearstatcache();
3287  $resultDirectoryGroup = filegroup(PATH_site . 'typo3temp/var/tests/' . $directory);
3288  $this->assertEquals($resultDirectoryGroup, $swapGroup);
3289  }
3290  }
3291 
3296  {
3297  $swapGroup = $this->checkGroups(__FUNCTION__);
3298  if ($swapGroup !== false) {
3299  $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3300  $directory = $this->getUniqueId('mkdirdeeptest_');
3301  $subDirectory = $directory . '/bar';
3302  GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $subDirectory);
3303  $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3304  clearstatcache();
3305  $resultDirectoryGroup = filegroup(PATH_site . 'typo3temp/var/tests/' . $directory);
3306  $this->assertEquals($resultDirectoryGroup, $swapGroup);
3307  }
3308  }
3309 
3314  {
3315  $swapGroup = $this->checkGroups(__FUNCTION__);
3316  if ($swapGroup !== false) {
3317  $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3318  $directory = $this->getUniqueId('mkdirdeeptest_');
3319  $subDirectory = $directory . '/bar';
3320  GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $subDirectory);
3321  $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3322  clearstatcache();
3323  $resultDirectoryGroup = filegroup(PATH_site . 'typo3temp/var/tests/' . $directory);
3324  $this->assertEquals($resultDirectoryGroup, $swapGroup);
3325  }
3326  }
3327 
3332  {
3333  if (!class_exists('org\\bovigo\\vfs\\vfsStreamWrapper')) {
3334  $this->markTestSkipped(
3335  'mkdirDeepCreatesDirectoryInVfsStream() test not available with this phpunit version.'
3336  );
3337  }
3338  vfsStreamWrapper::register();
3339  $baseDirectory = $this->getUniqueId('test_');
3340  vfsStreamWrapper::setRoot(new vfsStreamDirectory($baseDirectory));
3341  GeneralUtility::mkdir_deep('vfs://' . $baseDirectory . '/', 'sub');
3342  $this->assertTrue(is_dir('vfs://' . $baseDirectory . '/sub'));
3343  }
3344 
3349  {
3350  $this->expectException(\RuntimeException::class);
3351  $this->expectExceptionCode(1170251401);
3352 
3353  GeneralUtility::mkdir_deep('http://localhost');
3354  }
3355 
3360  {
3361  $this->expectException(\InvalidArgumentException::class);
3362  $this->expectExceptionCode(1303662955);
3363 
3365  }
3366 
3371  {
3372  $this->expectException(\InvalidArgumentException::class);
3373  $this->expectExceptionCode(1303662956);
3374 
3375  GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/foo', []);
3376  }
3377 
3379  // Tests concerning rmdir
3381 
3385  public function rmdirRemovesFile()
3386  {
3387  $file = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('file_');
3388  touch($file);
3389  GeneralUtility::rmdir($file);
3390  $this->assertFalse(file_exists($file));
3391  }
3392 
3397  {
3398  $file = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('file_');
3399  touch($file);
3400  $this->assertTrue(GeneralUtility::rmdir($file));
3401  }
3402 
3407  {
3408  $file = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('file_');
3409  $this->assertFalse(GeneralUtility::rmdir($file));
3410  }
3411 
3415  public function rmdirRemovesDirectory()
3416  {
3417  $directory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('directory_');
3418  mkdir($directory);
3419  GeneralUtility::rmdir($directory);
3420  $this->assertFalse(file_exists($directory));
3421  }
3422 
3427  {
3428  $directory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('directory_') . '/';
3429  mkdir($directory);
3430  GeneralUtility::rmdir($directory);
3431  $this->assertFalse(file_exists($directory));
3432  }
3433 
3438  {
3439  $directory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('directory_') . '/';
3440  mkdir($directory);
3441  $file = $this->getUniqueId('file_');
3442  touch($directory . $file);
3443  $this->testFilesToDelete[] = $directory;
3444  $return = GeneralUtility::rmdir($directory);
3445  $this->assertTrue(file_exists($directory));
3446  $this->assertTrue(file_exists($directory . $file));
3447  $this->assertFalse($return);
3448  }
3449 
3454  {
3455  $directory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('directory_') . '/';
3456  mkdir($directory);
3457  mkdir($directory . 'sub/');
3458  touch($directory . 'sub/file');
3459  $return = GeneralUtility::rmdir($directory, true);
3460  $this->assertFalse(file_exists($directory));
3461  $this->assertTrue($return);
3462  }
3463 
3468  {
3469  $existingDirectory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('notExists_') . '/';
3470  mkdir($existingDirectory);
3471  $this->testFilesToDelete[] = $existingDirectory;
3472  $symlinkName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('link_');
3473  symlink($existingDirectory, $symlinkName);
3474  GeneralUtility::rmdir($symlinkName, true);
3475  $this->assertFalse(is_link($symlinkName));
3476  }
3477 
3482  {
3483  $notExistingDirectory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('notExists_') . '/';
3484  $symlinkName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('link_');
3485  mkdir($notExistingDirectory);
3486  symlink($notExistingDirectory, $symlinkName);
3487  rmdir($notExistingDirectory);
3488 
3489  GeneralUtility::rmdir($symlinkName, true);
3490  $this->assertFalse(is_link($symlinkName));
3491  }
3492 
3496  public function rmdirRemovesDeadLinkToFile()
3497  {
3498  $notExistingFile = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('notExists_');
3499  $symlinkName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('link_');
3500  touch($notExistingFile);
3501  symlink($notExistingFile, $symlinkName);
3502  unlink($notExistingFile);
3503  GeneralUtility::rmdir($symlinkName, true);
3504  $this->assertFalse(is_link($symlinkName));
3505  }
3506 
3508  // Tests concerning getFilesInDir
3510 
3517  {
3518  if (!class_exists('org\\bovigo\\vfs\\vfsStreamWrapper')) {
3519  $this->markTestSkipped('getFilesInDirCreateTestDirectory() helper method not available without vfsStream.');
3520  }
3521  $structure = [
3522  'subDirectory' => [
3523  'test.php' => 'butter',
3524  'other.php' => 'milk',
3525  'stuff.csv' => 'honey',
3526  ],
3527  'excludeMe.txt' => 'cocoa nibs',
3528  'double.setup.typoscript' => 'cool TS',
3529  'testB.txt' => 'olive oil',
3530  'testA.txt' => 'eggs',
3531  'testC.txt' => 'carrots',
3532  'test.js' => 'oranges',
3533  'test.css' => 'apples',
3534  '.secret.txt' => 'sammon',
3535  ];
3536  vfsStream::setup('test', null, $structure);
3537  $vfsUrl = vfsStream::url('test');
3538 
3539  // set random values for mtime
3540  foreach ($structure as $structureLevel1Key => $structureLevel1Content) {
3541  $newMtime = rand();
3542  if (is_array($structureLevel1Content)) {
3543  foreach ($structureLevel1Content as $structureLevel2Key => $structureLevel2Content) {
3544  touch($vfsUrl . '/' . $structureLevel1Key . '/' . $structureLevel2Key, $newMtime);
3545  }
3546  } else {
3547  touch($vfsUrl . '/' . $structureLevel1Key, $newMtime);
3548  }
3549  }
3550 
3551  return $vfsUrl;
3552  }
3553 
3558  {
3559  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3560  $files = GeneralUtility::getFilesInDir($vfsStreamUrl);
3561  $this->assertContains('testA.txt', $files);
3562  }
3563 
3568  {
3569  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3570  $files = GeneralUtility::getFilesInDir($vfsStreamUrl);
3571  $this->assertContains('.secret.txt', $files);
3572  }
3573 
3579  public function fileExtensionDataProvider()
3580  {
3581  return [
3582  'no space' => [
3583  'setup.typoscript,txt,js,css'
3584  ],
3585  'spaces' => [
3586  'setup.typoscript, txt, js, css'
3587  ],
3588  'mixed' => [
3589  'setup.typoscript , txt,js, css'
3590  ],
3591  'wild' => [
3592  'setup.typoscript, txt, js , css'
3593  ]
3594  ];
3595  }
3596 
3601  public function getFilesInDirByExtensionFindsFiles($fileExtensions)
3602  {
3603  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3604  $files = GeneralUtility::getFilesInDir($vfsStreamUrl, $fileExtensions);
3605  $this->assertContains('double.setup.typoscript', $files);
3606  $this->assertContains('testA.txt', $files);
3607  $this->assertContains('test.js', $files);
3608  $this->assertContains('test.css', $files);
3609  }
3610 
3615  {
3616  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3617  $files = GeneralUtility::getFilesInDir($vfsStreamUrl, 'txt,js');
3618  $this->assertContains('testA.txt', $files);
3619  $this->assertContains('test.js', $files);
3620  $this->assertNotContains('test.css', $files);
3621  }
3622 
3627  {
3628  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3629  $files = GeneralUtility::getFilesInDir($vfsStreamUrl, '', false, '', 'excludeMe.*');
3630  $this->assertContains('test.js', $files);
3631  $this->assertNotContains('excludeMe.txt', $files);
3632  }
3633 
3638  {
3639  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3640  $this->assertContains(
3641  $vfsStreamUrl . '/testA.txt',
3642  GeneralUtility::getFilesInDir($vfsStreamUrl, '', true)
3643  );
3644  }
3645 
3650  {
3651  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3652  $this->assertSame(
3653  array_values(GeneralUtility::getFilesInDir($vfsStreamUrl, '', false)),
3654  [
3655  '.secret.txt',
3656  'double.setup.typoscript',
3657  'excludeMe.txt',
3658  'test.css',
3659  'test.js',
3660  'testA.txt',
3661  'testB.txt',
3662  'testC.txt'
3663  ]
3664  );
3665  }
3666 
3671  {
3672  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3673  $files = [];
3674  $iterator = new \DirectoryIterator($vfsStreamUrl);
3675  foreach ($iterator as $fileinfo) {
3676  if ($fileinfo->isFile()) {
3677  $files[$fileinfo->getFilename()] = $fileinfo->getMTime();
3678  }
3679  }
3680  asort($files);
3681  $this->assertSame(
3682  array_values(GeneralUtility::getFilesInDir($vfsStreamUrl, '', false, 'mtime')),
3683  array_keys($files)
3684  );
3685  }
3686 
3691  {
3692  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3693  $this->assertArrayHasKey(
3694  md5($vfsStreamUrl . '/testA.txt'),
3695  GeneralUtility::getFilesInDir($vfsStreamUrl)
3696  );
3697  }
3698 
3703  {
3704  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3705  $this->assertNotContains(
3706  'subDirectory',
3707  GeneralUtility::getFilesInDir($vfsStreamUrl)
3708  );
3709  }
3710 
3718  {
3719  $vfsStreamUrl = $this->getFilesInDirCreateTestDirectory();
3720  $files = GeneralUtility::getFilesInDir($vfsStreamUrl);
3721  $this->assertNotContains('..', $files);
3722  $this->assertNotContains('.', $files);
3723  }
3724 
3726  // Tests concerning unQuoteFilenames
3728 
3734  {
3735  return [
3736  // Some theoretical tests first
3737  [
3738  '',
3739  [],
3740  []
3741  ],
3742  [
3743  'aa bb "cc" "dd"',
3744  ['aa', 'bb', '"cc"', '"dd"'],
3745  ['aa', 'bb', 'cc', 'dd']
3746  ],
3747  [
3748  'aa bb "cc dd"',
3749  ['aa', 'bb', '"cc dd"'],
3750  ['aa', 'bb', 'cc dd']
3751  ],
3752  [
3753  '\'aa bb\' "cc dd"',
3754  ['\'aa bb\'', '"cc dd"'],
3755  ['aa bb', 'cc dd']
3756  ],
3757  [
3758  '\'aa bb\' cc "dd"',
3759  ['\'aa bb\'', 'cc', '"dd"'],
3760  ['aa bb', 'cc', 'dd']
3761  ],
3762  // Now test against some real world examples
3763  [
3764  '/opt/local/bin/gm.exe convert +profile \'*\' -geometry 170x136! -negate "C:/Users/Someuser.Domain/Documents/Htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]" "C:/Users/Someuser.Domain/Documents/Htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"',
3765  [
3766  '/opt/local/bin/gm.exe',
3767  'convert',
3768  '+profile',
3769  '\'*\'',
3770  '-geometry',
3771  '170x136!',
3772  '-negate',
3773  '"C:/Users/Someuser.Domain/Documents/Htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]"',
3774  '"C:/Users/Someuser.Domain/Documents/Htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"'
3775  ],
3776  [
3777  '/opt/local/bin/gm.exe',
3778  'convert',
3779  '+profile',
3780  '*',
3781  '-geometry',
3782  '170x136!',
3783  '-negate',
3784  'C:/Users/Someuser.Domain/Documents/Htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]',
3785  'C:/Users/Someuser.Domain/Documents/Htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif'
3786  ]
3787  ],
3788  [
3789  'C:/opt/local/bin/gm.exe convert +profile \'*\' -geometry 170x136! -negate "C:/Program Files/Apache2/htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]" "C:/Program Files/Apache2/htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"',
3790  [
3791  'C:/opt/local/bin/gm.exe',
3792  'convert',
3793  '+profile',
3794  '\'*\'',
3795  '-geometry',
3796  '170x136!',
3797  '-negate',
3798  '"C:/Program Files/Apache2/htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]"',
3799  '"C:/Program Files/Apache2/htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"'
3800  ],
3801  [
3802  'C:/opt/local/bin/gm.exe',
3803  'convert',
3804  '+profile',
3805  '*',
3806  '-geometry',
3807  '170x136!',
3808  '-negate',
3809  'C:/Program Files/Apache2/htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]',
3810  'C:/Program Files/Apache2/htdocs/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif'
3811  ]
3812  ],
3813  [
3814  '/usr/bin/gm convert +profile \'*\' -geometry 170x136! -negate "/Shared Items/Data/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]" "/Shared Items/Data/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"',
3815  [
3816  '/usr/bin/gm',
3817  'convert',
3818  '+profile',
3819  '\'*\'',
3820  '-geometry',
3821  '170x136!',
3822  '-negate',
3823  '"/Shared Items/Data/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]"',
3824  '"/Shared Items/Data/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"'
3825  ],
3826  [
3827  '/usr/bin/gm',
3828  'convert',
3829  '+profile',
3830  '*',
3831  '-geometry',
3832  '170x136!',
3833  '-negate',
3834  '/Shared Items/Data/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]',
3835  '/Shared Items/Data/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif'
3836  ]
3837  ],
3838  [
3839  '/usr/bin/gm convert +profile \'*\' -geometry 170x136! -negate "/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]" "/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"',
3840  [
3841  '/usr/bin/gm',
3842  'convert',
3843  '+profile',
3844  '\'*\'',
3845  '-geometry',
3846  '170x136!',
3847  '-negate',
3848  '"/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]"',
3849  '"/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif"'
3850  ],
3851  [
3852  '/usr/bin/gm',
3853  'convert',
3854  '+profile',
3855  '*',
3856  '-geometry',
3857  '170x136!',
3858  '-negate',
3859  '/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]',
3860  '/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif'
3861  ]
3862  ],
3863  [
3864  '/usr/bin/gm convert +profile \'*\' -geometry 170x136! -negate \'/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]\' \'/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif\'',
3865  [
3866  '/usr/bin/gm',
3867  'convert',
3868  '+profile',
3869  '\'*\'',
3870  '-geometry',
3871  '170x136!',
3872  '-negate',
3873  '\'/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]\'',
3874  '\'/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif\''
3875  ],
3876  [
3877  '/usr/bin/gm',
3878  'convert',
3879  '+profile',
3880  '*',
3881  '-geometry',
3882  '170x136!',
3883  '-negate',
3884  '/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif[0]',
3885  '/Network/Servers/server01.internal/Projects/typo3temp/var/transient/61401f5c16c63d58e1d92e8a2449f2fe_maskNT.gif'
3886  ]
3887  ]
3888  ];
3889  }
3890 
3897  public function explodeAndUnquoteImageMagickCommands($source, $expectedQuoted, $expectedUnquoted)
3898  {
3899  $actualQuoted = GeneralUtility::unQuoteFilenames($source);
3900  $actualUnquoted = GeneralUtility::unQuoteFilenames($source, true);
3901  $this->assertEquals($expectedQuoted, $actualQuoted, 'The exploded command does not match the expected');
3902  $this->assertEquals(
3903  $expectedUnquoted,
3904  $actualUnquoted,
3905  'The exploded and unquoted command does not match the expected'
3906  );
3907  }
3908 
3910  // Tests concerning split_fileref
3912 
3916  {
3917  $directoryName = $this->getUniqueId('test_') . '.com';
3918  $directoryPath = PATH_site . 'typo3temp/var/tests/';
3919  $directory = $directoryPath . $directoryName;
3920  mkdir($directory, octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
3921  $fileInfo = GeneralUtility::split_fileref($directory);
3922  $directoryCreated = is_dir($directory);
3923  rmdir($directory);
3924  $this->assertTrue($directoryCreated);
3925  $this->assertInternalType(\PHPUnit\Framework\Constraint\IsType::TYPE_ARRAY, $fileInfo);
3926  $this->assertEquals($directoryPath, $fileInfo['path']);
3927  $this->assertEquals($directoryName, $fileInfo['file']);
3928  $this->assertEquals($directoryName, $fileInfo['filebody']);
3929  $this->assertEquals('', $fileInfo['fileext']);
3930  $this->assertArrayNotHasKey('realFileext', $fileInfo);
3931  }
3932 
3937  {
3938  $testFile = 'fileadmin/media/someFile.png';
3939  $fileInfo = GeneralUtility::split_fileref($testFile);
3940  $this->assertInternalType(\PHPUnit\Framework\Constraint\IsType::TYPE_ARRAY, $fileInfo);
3941  $this->assertEquals('fileadmin/media/', $fileInfo['path']);
3942  $this->assertEquals('someFile.png', $fileInfo['file']);
3943  $this->assertEquals('someFile', $fileInfo['filebody']);
3944  $this->assertEquals('png', $fileInfo['fileext']);
3945  }
3946 
3948  // Tests concerning dirname
3950 
3954  public function dirnameDataProvider()
3955  {
3956  return [
3957  'absolute path with multiple part and file' => ['/dir1/dir2/script.php', '/dir1/dir2'],
3958  'absolute path with one part' => ['/dir1/', '/dir1'],
3959  'absolute path to file without extension' => ['/dir1/something', '/dir1'],
3960  'relative path with one part and file' => ['dir1/script.php', 'dir1'],
3961  'relative one-character path with one part and file' => ['d/script.php', 'd'],
3962  'absolute zero-part path with file' => ['/script.php', ''],
3963  'empty string' => ['', '']
3964  ];
3965  }
3966 
3973  public function dirnameWithDataProvider($input, $expectedValue)
3974  {
3975  $this->assertEquals($expectedValue, GeneralUtility::dirname($input));
3976  }
3977 
3979  // Tests concerning resolveBackPath
3981 
3986  {
3987  return [
3988  'empty path' => ['', ''],
3989  'this directory' => ['./', './'],
3990  'relative directory without ..' => ['dir1/dir2/dir3/', 'dir1/dir2/dir3/'],
3991  'relative path without ..' => ['dir1/dir2/script.php', 'dir1/dir2/script.php'],
3992  'absolute directory without ..' => ['/dir1/dir2/dir3/', '/dir1/dir2/dir3/'],
3993  'absolute path without ..' => ['/dir1/dir2/script.php', '/dir1/dir2/script.php'],
3994  'only one directory upwards without trailing slash' => ['..', '..'],
3995  'only one directory upwards with trailing slash' => ['../', '../'],
3996  'one level with trailing ..' => ['dir1/..', ''],
3997  'one level with trailing ../' => ['dir1/../', ''],
3998  'two levels with trailing ..' => ['dir1/dir2/..', 'dir1'],
3999  'two levels with trailing ../' => ['dir1/dir2/../', 'dir1/'],
4000  'leading ../ without trailing /' => ['../dir1', '../dir1'],
4001  'leading ../ with trailing /' => ['../dir1/', '../dir1/'],
4002  'leading ../ and inside path' => ['../dir1/dir2/../dir3/', '../dir1/dir3/'],
4003  'one times ../ in relative directory' => ['dir1/../dir2/', 'dir2/'],
4004  'one times ../ in absolute directory' => ['/dir1/../dir2/', '/dir2/'],
4005  'one times ../ in relative path' => ['dir1/../dir2/script.php', 'dir2/script.php'],
4006  'one times ../ in absolute path' => ['/dir1/../dir2/script.php', '/dir2/script.php'],
4007  'consecutive ../' => ['dir1/dir2/dir3/../../../dir4', 'dir4'],
4008  'distrubuted ../ with trailing /' => ['dir1/../dir2/dir3/../', 'dir2/'],
4009  'distributed ../ without trailing /' => ['dir1/../dir2/dir3/..', 'dir2'],
4010  'multiple distributed and consecutive ../ together' => [
4011  'dir1/dir2/dir3/dir4/../../dir5/dir6/dir7/../dir8/',
4012  'dir1/dir2/dir5/dir6/dir8/'
4013  ],
4014  'dirname with leading ..' => ['dir1/..dir2/dir3/', 'dir1/..dir2/dir3/'],
4015  'dirname with trailing ..' => ['dir1/dir2../dir3/', 'dir1/dir2../dir3/'],
4016  'more times upwards than downwards in directory' => ['dir1/../../', '../'],
4017  'more times upwards than downwards in path' => ['dir1/../../script.php', '../script.php']
4018  ];
4019  }
4020 
4027  public function resolveBackPathWithDataProvider($input, $expectedValue)
4028  {
4029  $this->assertEquals($expectedValue, GeneralUtility::resolveBackPath($input));
4030  }
4031 
4033  // Tests concerning makeInstance, setSingletonInstance, addInstance, purgeInstances
4035 
4039  {
4040  $this->expectException(\InvalidArgumentException::class);
4041  $this->expectExceptionCode(1288965219);
4042 
4044  }
4045 
4050  {
4051  $this->expectException(\InvalidArgumentException::class);
4052  $this->expectExceptionCode(1288965219);
4053 
4055  }
4056 
4061  {
4062  $this->expectException(\InvalidArgumentException::class);
4063  $this->expectExceptionCode(1288965219);
4064 
4066  }
4067 
4072  {
4073  $this->expectException(\InvalidArgumentException::class);
4074  $this->expectExceptionCode(1288965219);
4075 
4077  }
4078 
4083  {
4084  $this->expectException(\InvalidArgumentException::class);
4085  $this->expectExceptionCode(1288965219);
4086 
4088  }
4089 
4094  {
4095  $this->expectException(\InvalidArgumentException::class);
4096  $this->expectExceptionCode(1420281366);
4097 
4098  GeneralUtility::makeInstance('\\TYPO3\\CMS\\Backend\\Controller\\BackendController');
4099  }
4100 
4105  {
4106  $className = get_class($this->getMockBuilder('foo')->getMock());
4107  $this->assertTrue(GeneralUtility::makeInstance($className) instanceof $className);
4108  }
4109 
4114  {
4115  $instance = GeneralUtility::makeInstance(
4116  TwoParametersConstructorFixture::class,
4117  'one parameter',
4118  'another parameter'
4119  );
4120  $this->assertEquals(
4121  'one parameter',
4122  $instance->constructorParameter1,
4123  'The first constructor parameter has not been set.'
4124  );
4125  $this->assertEquals(
4126  'another parameter',
4127  $instance->constructorParameter2,
4128  'The second constructor parameter has not been set.'
4129  );
4130  }
4131 
4136  {
4138  $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalClassFixture::class] = ['className' => ReplacementClassFixture::class];
4139  $this->assertInstanceOf(
4140  ReplacementClassFixture::class,
4141  GeneralUtility::makeInstance(OriginalClassFixture::class)
4142  );
4143  }
4144 
4149  {
4151  $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalClassFixture::class] = ['className' => ReplacementClassFixture::class];
4152  $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][ReplacementClassFixture::class] = ['className' => OtherReplacementClassFixture::class];
4153  $this->assertInstanceOf(
4154  OtherReplacementClassFixture::class,
4155  GeneralUtility::makeInstance(OriginalClassFixture::class)
4156  );
4157  }
4158 
4163  {
4164  $className = get_class($this->getMockBuilder('foo')->getMock());
4165  $this->assertNotSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
4166  }
4167 
4172  {
4173  $className = get_class($this->createMock(\TYPO3\CMS\Core\SingletonInterface::class));
4174  $this->assertSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
4175  }
4176 
4181  {
4182  $className = get_class($this->createMock(\TYPO3\CMS\Core\SingletonInterface::class));
4183  $instance = GeneralUtility::makeInstance($className);
4185  $this->assertNotSame($instance, GeneralUtility::makeInstance($className));
4186  }
4187 
4192  {
4193  $this->expectException(\InvalidArgumentException::class);
4194  $this->expectExceptionCode(1288967479);
4195 
4196  $instance = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4197  GeneralUtility::setSingletonInstance('', $instance);
4198  }
4199 
4204  {
4205  $this->expectException(\InvalidArgumentException::class);
4206  $this->expectExceptionCode(1288967686);
4207 
4208  $instance = $this->getMockBuilder(\TYPO3\CMS\Core\SingletonInterface::class)
4209  ->setMethods(['foo'])
4210  ->getMock();
4211  $singletonClassName = get_class($this->createMock(\TYPO3\CMS\Core\SingletonInterface::class));
4212  GeneralUtility::setSingletonInstance($singletonClassName, $instance);
4213  }
4214 
4219  {
4220  $instance = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4221  $singletonClassName = get_class($instance);
4222  GeneralUtility::setSingletonInstance($singletonClassName, $instance);
4223  $this->assertSame($instance, GeneralUtility::makeInstance($singletonClassName));
4224  }
4225 
4230  {
4231  $instance1 = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4232  $singletonClassName = get_class($instance1);
4233  $instance2 = new $singletonClassName();
4234  GeneralUtility::setSingletonInstance($singletonClassName, $instance1);
4235  GeneralUtility::setSingletonInstance($singletonClassName, $instance2);
4236  $this->assertSame($instance2, GeneralUtility::makeInstance($singletonClassName));
4237  }
4238 
4243  {
4244  $instance = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4245  $instanceClassName = get_class($instance);
4246  GeneralUtility::setSingletonInstance($instanceClassName, $instance);
4247  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
4248  $this->assertArrayHasKey($instanceClassName, $registeredSingletonInstances);
4249  $this->assertSame($registeredSingletonInstances[$instanceClassName], $instance);
4250  }
4251 
4256  {
4257  $instance = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4258  $instanceClassName = get_class($instance);
4259  GeneralUtility::setSingletonInstance($instanceClassName, $instance);
4261  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
4262  $this->assertArrayNotHasKey($instanceClassName, $registeredSingletonInstances);
4263  }
4264 
4269  {
4270  $instance = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4271  $instanceClassName = get_class($instance);
4273  [$instanceClassName => $instance]
4274  );
4275  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
4276  $this->assertArrayHasKey($instanceClassName, $registeredSingletonInstances);
4277  $this->assertSame($registeredSingletonInstances[$instanceClassName], $instance);
4278  }
4279 
4284  {
4285  $this->expectException(\InvalidArgumentException::class);
4286  $this->expectExceptionCode(1288967479);
4287 
4288  $instance = $this->getMockBuilder('foo')->getMock();
4289  GeneralUtility::addInstance('', $instance);
4290  }
4291 
4296  {
4297  $this->expectException(\InvalidArgumentException::class);
4298  $this->expectExceptionCode(1288967686);
4299 
4300  $instance = $this->getMockBuilder('foo')
4301  ->setMethods(['bar'])
4302  ->getMock();
4303  $singletonClassName = get_class($this->createMock('foo'));
4304  GeneralUtility::addInstance($singletonClassName, $instance);
4305  }
4306 
4311  {
4312  $this->expectException(\InvalidArgumentException::class);
4313  $this->expectExceptionCode(1288969325);
4314 
4315  $instance = $this->createMock(\TYPO3\CMS\Core\SingletonInterface::class);
4316  GeneralUtility::addInstance(get_class($instance), $instance);
4317  }
4318 
4323  {
4324  $instance = $this->createMock('foo');
4325  $className = get_class($instance);
4326  GeneralUtility::addInstance($className, $instance);
4327  $this->assertSame($instance, GeneralUtility::makeInstance($className));
4328  }
4329 
4334  {
4335  $instance = $this->createMock('foo');
4336  $className = get_class($instance);
4337  GeneralUtility::addInstance($className, $instance);
4338  $this->assertNotSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
4339  }
4340 
4345  {
4346  $instance1 = $this->createMock('foo');
4347  $className = get_class($instance1);
4348  GeneralUtility::addInstance($className, $instance1);
4349  $instance2 = new $className();
4350  GeneralUtility::addInstance($className, $instance2);
4351  $this->assertSame(
4352  $instance1,
4353  GeneralUtility::makeInstance($className),
4354  'The first returned instance does not match the first added instance.'
4355  );
4356  $this->assertSame(
4357  $instance2,
4358  GeneralUtility::makeInstance($className),
4359  'The second returned instance does not match the second added instance.'
4360  );
4361  }
4362 
4367  {
4368  $instance = $this->createMock('foo');
4369  $className = get_class($instance);
4370  GeneralUtility::addInstance($className, $instance);
4372  $this->assertNotSame($instance, GeneralUtility::makeInstance($className));
4373  }
4374 
4379  {
4380  return [
4381  'typo3/sysext/core/Resources/Public/Icons/Extension.png' => [
4382  'typo3/sysext/core/Resources/Public/Icons/Extension.png',
4383  PATH_site . 'typo3/sysext/core/Resources/Public/Icons/Extension.png'
4384  ],
4385  'sysext/core/Resources/Public/Icons/Extension.png' => [
4386  'sysext/core/Resources/Public/Icons/Extension.png',
4387  PATH_site . 'sysext/core/Resources/Public/Icons/Extension.png'
4388  ],
4389  './typo3/sysext/core/Resources/Public/Icons/Extension.png' => [
4390  './typo3/sysext/core/Resources/Public/Icons/Extension.png',
4391  PATH_site . './typo3/sysext/core/Resources/Public/Icons/Extension.png'
4392  ],
4393  'fileadmin/foo.txt' => ['fileadmin/foo.txt', PATH_site . 'fileadmin/foo.txt'],
4394  './fileadmin/foo.txt' => ['./fileadmin/foo.txt', PATH_site . './fileadmin/foo.txt'],
4395  '../sysext/core/Resources/Public/Icons/Extension.png' => [
4396  '../sysext/core/Resources/Public/Icons/Extension.png',
4397  ''
4398  ],
4399  '../fileadmin/foo.txt' => ['../fileadmin/foo.txt', ''],
4400  'PATH_site . ../sysext/core/Resources/Public/Icons/Extension.png' => [
4401  PATH_site .
4402  '../sysext/core/Resources/Public/Icons/Extension.png',
4403  ''
4404  ],
4405  'PATH_site . fileadmin/foo.txt' => [PATH_site . 'fileadmin/foo.txt', PATH_site . 'fileadmin/foo.txt'],
4406  'PATH_site . typo3/sysext/core/Resources/Public/Icons/Extension.png' => [
4407  PATH_site .
4408  'typo3/sysext/core/Resources/Public/Icons/Extension.png',
4409  PATH_site . 'typo3/sysext/core/Resources/Public/Icons/Extension.png'
4410  ],
4411  'EXT:foo/Resources/Public/Icons/Extension.png' => [
4412  'EXT:foo/Resources/Public/Icons/Extension.png',
4413  PATH_site . 'typo3/sysext/foo/Resources/Public/Icons/Extension.png'
4414  ]
4415  ];
4416  }
4417 
4424  public function getFileAbsFileNameReturnsCorrectValues($path, $expected)
4425  {
4426 
4427  // build the dummy package "foo" for use in ExtensionManagementUtility::extPath('foo');
4428  $package = $this->getMockBuilder(Package::class)
4429  ->disableOriginalConstructor()
4430  ->setMethods(['getPackagePath'])
4431  ->getMock();
4433  $packageManager = $this->getMockBuilder(PackageManager::class)
4434  ->setMethods(['isPackageActive', 'getPackage'])
4435  ->getMock();
4436  $package->expects($this->any())
4437  ->method('getPackagePath')
4438  ->will($this->returnValue(PATH_site . 'typo3/sysext/foo/'));
4439  $packageManager->expects($this->any())
4440  ->method('isPackageActive')
4441  ->with($this->equalTo('foo'))
4442  ->will($this->returnValue(true));
4443  $packageManager->expects($this->any())
4444  ->method('getPackage')
4445  ->with('foo')
4446  ->will($this->returnValue($package));
4448 
4449  $result = GeneralUtility::getFileAbsFileName($path);
4450  $this->assertEquals($expected, $result);
4451  }
4452 
4459  {
4460  $data = [
4461  'double slash in path' => ['path//path'],
4462  'backslash in path' => ['path\\path'],
4463  'directory up in path' => ['path/../path'],
4464  'directory up at the beginning' => ['../path'],
4465  'NUL character in path' => ['path' . chr(0) . 'path'],
4466  'BS character in path' => ['path' . chr(8) . 'path'],
4467  'invalid UTF-8-sequence' => ["\xc0" . 'path/path'],
4468  'Could be overlong NUL in some UTF-8 implementations, invalid in RFC3629' => ["\xc0\x80" . 'path/path'],
4469  ];
4470 
4471  // Mixing with regular utf-8
4472  $utf8Characters = 'Ссылка/';
4473  foreach ($data as $key => $value) {
4474  $data[$key . ' with UTF-8 characters prepended'] = [$utf8Characters . $value[0]];
4475  $data[$key . ' with UTF-8 characters appended'] = [$value[0] . $utf8Characters];
4476  }
4477 
4478  // Encoding with UTF-16
4479  foreach ($data as $key => $value) {
4480  $data[$key . ' encoded with UTF-16'] = [mb_convert_encoding($value[0], 'UTF-16')];
4481  }
4482 
4483  return $data;
4484  }
4485 
4494  {
4495  $this->assertFalse(GeneralUtility::validPathStr($path));
4496  }
4497 
4501  public function validPathStrDataProvider()
4502  {
4503  $data = [
4504  'normal ascii path' => ['fileadmin/templates/myfile..xml'],
4505  'special character' => ['fileadmin/templates/Ссылка (fce).xml']
4506  ];
4507 
4508  return $data;
4509  }
4510 
4518  {
4519  $this->assertTrue(GeneralUtility::validPathStr($path));
4520  }
4521 
4526  {
4527  return [
4528  'Nul character in file' => ['image' . chr(0) . '.gif'],
4529  'Nul character in file with .php' => ['image.php' . chr(0) . '.gif'],
4530  'Nul character in file with \\0' => ['image' . "\0" . '.gif'],
4531  'Nul character in file with \\0 with .php' => ['image.php' . "\0" . '.gif'],
4532  'Nul character and UTF-8 in file' => ['Ссылка' . "\0" . '.gif'],
4533  'Nul character and Latin-1 in file' => ['ÉÐØ' . "\0" . '.gif'],
4534  ];
4535  }
4536 
4545  {
4546  $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] = '';
4547  $this->assertFalse(GeneralUtility::verifyFilenameAgainstDenyPattern($deniedFile));
4548  }
4549 
4554  {
4555  $data = [
4556  'Nul character in file' => ['image' . "\0", '.gif'],
4557  'Nul character in file with .php' => ['image.php' . "\0", '.gif'],
4558  'Nul character and UTF-8 in file' => ['Ссылка' . "\0", '.gif'],
4559  'Nul character and Latin-1 in file' => ['ÉÐØ' . "\0", '.gif'],
4560  'Lower umlaut .php file' => ['üWithFile', '.php'],
4561  'Upper umlaut .php file' => ['fileWithÜ', '.php'],
4562  'invalid UTF-8-sequence' => ["\xc0" . 'file', '.php'],
4563  'Could be overlong NUL in some UTF-8 implementations, invalid in RFC3629' => ["\xc0\x80" . 'file', '.php'],
4564  'Regular .php file' => ['file' , '.php'],
4565  'Regular .php3 file' => ['file', '.php3'],
4566  'Regular .php5 file' => ['file', '.php5'],
4567  'Regular .php7 file' => ['file', '.php7'],
4568  'Regular .phpsh file' => ['file', '.phpsh'],
4569  'Regular .phtml file' => ['file', '.phtml'],
4570  'Regular .pht file' => ['file', '.pht'],
4571  'Regular .phar file' => ['file', '.phar'],
4572  'Regular .shtml file' => ['file', '.shtml'],
4573  'Regular .cgi file' => ['file', '.cgi'],
4574  'Regular .pl file' => ['file', '.pl'],
4575  'Wrapped .php file ' => ['file', '.php.txt'],
4576  'Wrapped .php3 file' => ['file', '.php3.txt'],
4577  'Wrapped .php5 file' => ['file', '.php5.txt'],
4578  'Wrapped .php7 file' => ['file', '.php7.txt'],
4579  'Wrapped .phpsh file' => ['file', '.phpsh.txt'],
4580  'Wrapped .phtml file' => ['file', '.phtml.txt'],
4581  'Wrapped .pht file' => ['file', '.pht.txt'],
4582  'Wrapped .phar file' => ['file', '.phar.txt'],
4583  'Wrapped .shtml file' => ['file', '.shtml.txt'],
4584  'Wrapped .cgi file' => ['file', '.cgi.txt'],
4585  // allowed "Wrapped .pl file" in order to allow language specific files containing ".pl."
4586  '.htaccess file' => ['', '.htaccess'],
4587  ];
4588 
4589  // Mixing with regular utf-8
4590  $utf8Characters = 'Ссылка';
4591  foreach ($data as $key => $value) {
4592  if ($value[0] === '') {
4593  continue;
4594  }
4595  $data[$key . ' with UTF-8 characters prepended'] = [$utf8Characters . $value[0], $value[1]];
4596  $data[$key . ' with UTF-8 characters appended'] = [$value[0] . $utf8Characters, $value[1]];
4597  }
4598 
4599  // combine to single value
4600  $data = array_map(
4601  function (array $values): array {
4602  return [implode('', $values)];
4603  },
4604  $data
4605  );
4606 
4607  // Encoding with UTF-16
4608  foreach ($data as $key => $value) {
4609  $data[$key . ' encoded with UTF-16'] = [mb_convert_encoding($value[0], 'UTF-16')];
4610  }
4611 
4612  return $data;
4613  }
4614 
4623  {
4624  $this->assertFalse(GeneralUtility::verifyFilenameAgainstDenyPattern($deniedFile));
4625  }
4626 
4630  public function allowedFilesDataProvider(): array
4631  {
4632  return [
4633  'Regular .gif file' => ['image.gif'],
4634  'Regular uppercase .gif file' => ['IMAGE.gif'],
4635  'UTF-8 .gif file' => ['Ссылка.gif'],
4636  'Lower umlaut .jpg file' => ['üWithFile.jpg'],
4637  'Upper umlaut .png file' => ['fileWithÜ.png'],
4638  'Latin-1 .gif file' => ['ÉÐØ.gif'],
4639  'Wrapped .pl file' => ['file.pl.txt'],
4640  ];
4641  }
4642 
4650  public function verifyFilenameAgainstDenyPatternAcceptAllowedFiles(string $allowedFile)
4651  {
4652  $this->assertTrue(GeneralUtility::verifyFilenameAgainstDenyPattern($allowedFile));
4653  }
4654 
4656  // Tests concerning copyDirectory
4658 
4663  {
4664  $sourceDirectory = 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '/';
4665  $absoluteSourceDirectory = PATH_site . $sourceDirectory;
4666  $this->testFilesToDelete[] = $absoluteSourceDirectory;
4667  GeneralUtility::mkdir($absoluteSourceDirectory);
4668 
4669  $targetDirectory = 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '/';
4670  $absoluteTargetDirectory = PATH_site . $targetDirectory;
4671  $this->testFilesToDelete[] = $absoluteTargetDirectory;
4672 
4673  GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'file', '42');
4674  GeneralUtility::mkdir($absoluteSourceDirectory . 'foo');
4675  GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'foo/file', '42');
4676 
4677  GeneralUtility::copyDirectory($sourceDirectory, $targetDirectory);
4678 
4679  $this->assertFileExists($absoluteTargetDirectory . 'file');
4680  $this->assertFileExists($absoluteTargetDirectory . 'foo/file');
4681  }
4682 
4687  {
4688  $sourceDirectory = 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '/';
4689  $absoluteSourceDirectory = PATH_site . $sourceDirectory;
4690  $this->testFilesToDelete[] = $absoluteSourceDirectory;
4691  GeneralUtility::mkdir($absoluteSourceDirectory);
4692 
4693  $targetDirectory = 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '/';
4694  $absoluteTargetDirectory = PATH_site . $targetDirectory;
4695  $this->testFilesToDelete[] = $absoluteTargetDirectory;
4696 
4697  GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'file', '42');
4698  GeneralUtility::mkdir($absoluteSourceDirectory . 'foo');
4699  GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'foo/file', '42');
4700 
4701  GeneralUtility::copyDirectory($absoluteSourceDirectory, $absoluteTargetDirectory);
4702 
4703  $this->assertFileExists($absoluteTargetDirectory . 'file');
4704  $this->assertFileExists($absoluteTargetDirectory . 'foo/file');
4705  }
4706 
4708  // Tests concerning sysLog
4710 
4714  {
4715  if (TYPO3_OS === 'WIN') {
4716  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
4717  }
4718  // Fake all required settings
4719  $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] = 0;
4720  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit'] = true;
4721  unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog']);
4722  $testLogFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.txt';
4723  $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'] = 'file,' . $testLogFilename . ',0';
4724  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0777';
4725  // Call method, get actual permissions and clean up
4726  GeneralUtility::sysLog('testLog', 'test', GeneralUtility::SYSLOG_SEVERITY_NOTICE);
4727  $this->testFilesToDelete[] = $testLogFilename;
4728  clearstatcache();
4729  $this->assertEquals('0777', substr(decoct(fileperms($testLogFilename)), 2));
4730  }
4731 
4736  {
4737  if (TYPO3_OS === 'WIN') {
4738  $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
4739  }
4740  $filePath = PATH_site . GeneralUtilityFixture::DEPRECATION_LOG_PATH;
4741  @mkdir(dirname($filePath));
4742  $this->testFilesToDelete[] = $filePath;
4743  $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'] = true;
4744  $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0777';
4746  clearstatcache();
4747  $resultFilePermissions = substr(decoct(fileperms($filePath)), 2);
4748  $this->assertEquals('0777', $resultFilePermissions);
4749  }
4750 
4752  // Tests concerning callUserFunction
4754 
4760  {
4761  $inputData = ['foo' => 'bar'];
4762  // omit the debug() output
4763  ob_start();
4764  $result = GeneralUtility::callUserFunction($functionName, $inputData, $this, 'user_', 1);
4765  ob_end_clean();
4766  $this->assertFalse($result);
4767  }
4768 
4775  public function callUserFunctionWillThrowExceptionForInvalidParameters($functionName, $expectedException)
4776  {
4777  $this->expectException(\InvalidArgumentException::class);
4778  $this->expectExceptionCode($expectedException);
4779 
4780  $inputData = ['foo' => 'bar'];
4781  GeneralUtility::callUserFunction($functionName, $inputData, $this, 'user_', 2);
4782  }
4783 
4791  {
4792  return [
4793  'Function is not prefixed' => ['t3lib_divTest->calledUserFunction', 1294585866],
4794  'Class doesn\'t exists' => ['t3lib_divTest21345->user_calledUserFunction', 1294585866],
4795  'No method name' => ['t3lib_divTest', 1294585867],
4796  'No class name' => ['->user_calledUserFunction', 1294585866],
4797  'No function name' => ['', 1294585867]
4798  ];
4799  }
4800 
4809  {
4810  $inputData = ['foo' => 'bar'];
4812  function () {
4813  return 'Worked fine';
4814  },
4815  $inputData,
4816  $this,
4817  ''
4818  );
4819  $this->assertEquals('Worked fine', $result);
4820  }
4821 
4826  {
4827  $inputData = ['foo' => 'bar'];
4828  $result = GeneralUtility::callUserFunction(self::class . '->user_calledUserFunction', $inputData, $this);
4829  $this->assertEquals('Worked fine', $result);
4830  }
4831 
4835  public function user_calledUserFunction()
4836  {
4837  return 'Worked fine';
4838  }
4839 
4844  {
4845  $inputData = ['foo' => 'bar'];
4847  'typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php:TYPO3\\CMS\\Core\\Tests\\Unit\\Utility\GeneralUtilityTest->user_calledUserFunction',
4848  $inputData,
4849  $this
4850  );
4851  $this->assertEquals('Worked fine', $result);
4852  }
4853 
4858  {
4859  $inputData = ['called' => []];
4861  '&TYPO3\\CMS\\Core\\Tests\\Unit\\Utility\\GeneralUtilityTest->user_calledUserFunctionCountCallers',
4862  $inputData,
4863  $this
4864  );
4866  '&TYPO3\\CMS\\Core\\Tests\\Unit\\Utility\\GeneralUtilityTest->user_calledUserFunctionCountCallers',
4867  $inputData,
4868  $this
4869  );
4870  $this->assertEquals(1, count($inputData['called']));
4871  }
4872 
4880  public function user_calledUserFunctionCountCallers(&$params)
4881  {
4882  $params['called'][spl_object_hash($this)]++;
4883  }
4884 
4889  {
4890  $inputData = ['foo' => 'bar'];
4891  $closure = function ($parameters, $reference) use ($inputData) {
4892  $reference->assertEquals($inputData, $parameters, 'Passed data doesn\'t match expected output');
4893  return 'Worked fine';
4894  };
4895  $this->assertEquals('Worked fine', GeneralUtility::callUserFunction($closure, $inputData, $this));
4896  }
4897 
4902  {
4903  $inputData = ['foo' => 'bar'];
4905  "\t" . self::class . '->user_calledUserFunction ',
4906  $inputData,
4907  $this
4908  );
4909  $this->assertEquals('Worked fine', $result);
4910  }
4911 
4916  {
4917  $directory = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('directory_');
4918  mkdir($directory);
4919  $filesAndDirectories = GeneralUtility::getAllFilesAndFoldersInPath([], $directory, '', true);
4920  $check = true;
4921  foreach ($filesAndDirectories as $md5 => $path) {
4922  if (!preg_match('/^[a-f0-9]{32}$/', $md5)) {
4923  $check = false;
4924  }
4925  }
4926  GeneralUtility::rmdir($directory);
4927  $this->assertTrue($check);
4928  }
4929 
4937  {
4938  $input = [
4939  'el' => []
4940  ];
4941 
4942  $output = GeneralUtility::array2xml($input);
4943 
4944  $this->assertEquals(
4945  '<phparray>
4946  <el type="array"></el>
4947 </phparray>',
4948  $output
4949  );
4950  }
4951 
4955  public function providerForXml2Array(): array
4956  {
4957  return [
4958  'inputWithoutWhitespaces' => [
4959  '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
4960  <T3FlexForms>
4961  <data>
4962  <field index="settings.persistenceIdentifier">
4963  <value index="vDEF">egon</value>
4964  </field>
4965  </data>
4966  </T3FlexForms>'
4967  ],
4968  'inputWithPrecedingWhitespaces' => [
4969  '
4970  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
4971  <T3FlexForms>
4972  <data>
4973  <field index="settings.persistenceIdentifier">
4974  <value index="vDEF">egon</value>
4975  </field>
4976  </data>
4977  </T3FlexForms>'
4978  ],
4979  'inputWithTrailingWhitespaces' => [
4980  '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
4981  <T3FlexForms>
4982  <data>
4983  <field index="settings.persistenceIdentifier">
4984  <value index="vDEF">egon</value>
4985  </field>
4986  </data>
4987  </T3FlexForms>
4988  '
4989  ],
4990  'inputWithPrecedingAndTrailingWhitespaces' => [
4991  '
4992  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
4993  <T3FlexForms>
4994  <data>
4995  <field index="settings.persistenceIdentifier">
4996  <value index="vDEF">egon</value>
4997  </field>
4998  </data>
4999  </T3FlexForms>
5000  '
5001  ],
5002  ];
5003  }
5004 
5010  public function xml2ArrayDealsProperlyWithWhitespace(string $input)
5011  {
5012  $expected = [
5013  'data' => [
5014  'settings.persistenceIdentifier' => [
5015  'vDEF' => 'egon',
5016  ]
5017  ],
5018  ];
5019  $this->assertSame($expected, GeneralUtility::xml2array($input));
5020  }
5021 
5028  public function idnaEncodeConvertsUnicodeCharsToASCIIString($actual, $expected)
5029  {
5030  $result = GeneralUtility::idnaEncode($actual);
5031  $this->assertSame($expected, $result);
5032  }
5033 
5040  public function idnaEncodeDataProvider()
5041  {
5042  return [
5043  'empty string' => [
5044  '',
5045  ''
5046  ],
5047  'null value' => [
5048  null,
5049  ''
5050  ],
5051  'string with ascii chars' => [
5052  'example',
5053  'example'
5054  ],
5055  'domain (1) with utf8 chars' => [
5056  'dömäin.example',
5057  'xn--dmin-moa0i.example'
5058  ],
5059  'domain (2) with utf8 chars' => [
5060  'äaaa.example',
5061  'xn--aaa-pla.example'
5062  ],
5063  'domain (3) with utf8 chars' => [
5064  'déjà.vu.example',
5065  'xn--dj-kia8a.vu.example'
5066  ],
5067  'domain (4) with utf8 chars' => [
5068  'foo.âbcdéf.example',
5069  'foo.xn--bcdf-9na9b.example'
5070  ],
5071  'domain with utf8 char (german umlaut)' => [
5072  'exömple.com',
5073  'xn--exmple-xxa.com'
5074  ],
5075  'email with utf8 char (german umlaut)' => [
5076  'joe.doe@dömäin.de',
5077  'joe.doe@xn--dmin-moa0i.de'
5078  ]
5079  ];
5080  }
5081 
5082  public function splitHeaderLinesDataProvider(): array
5083  {
5084  return [
5085  'one-line, single header' => [
5086  ['Content-Security-Policy:default-src \'self\'; img-src https://*; child-src \'none\';'],
5087  ['Content-Security-Policy' => 'default-src \'self\'; img-src https://*; child-src \'none\';']
5088  ],
5089  'one-line, multiple headers' => [
5090  [
5091  'Content-Security-Policy:default-src \'self\'; img-src https://*; child-src \'none\';',
5092  'Content-Security-Policy-Report-Only:default-src https:; report-uri /csp-violation-report-endpoint/'
5093  ],
5094  [
5095  'Content-Security-Policy' => 'default-src \'self\'; img-src https://*; child-src \'none\';',
5096  'Content-Security-Policy-Report-Only' => 'default-src https:; report-uri /csp-violation-report-endpoint/'
5097  ]
5098  ],
5099  'multi-line headers' => [
5100  ['Content-Type' => 'multipart/form-data; boundary=something', 'Content-Language' => 'de-DE, en-CA'],
5101  ['Content-Type' => 'multipart/form-data; boundary=something', 'Content-Language' => 'de-DE, en-CA'],
5102  ]
5103  ];
5104  }
5105 
5112  public function splitHeaderLines(array $headers, array $expectedHeaders)
5113  {
5114  $stream = $this->prophesize(StreamInterface::class);
5115  $response = $this->prophesize(ResponseInterface::class);
5116  $response->getBody()->willReturn($stream);
5117  $requestFactory = $this->prophesize(RequestFactory::class);
5118  $requestFactory->request(Argument::cetera())->willReturn($response);
5119 
5120  GeneralUtility::addInstance(RequestFactory::class, $requestFactory->reveal());
5121  GeneralUtility::getUrl('http://example.com', 0, $headers);
5122 
5123  $requestFactory->request(Argument::any(), Argument::any(), ['headers' => $expectedHeaders])->shouldHaveBeenCalled();
5124  }
5125 
5126  public function locationHeaderUrlDataProvider(): array
5127  {
5128  return [
5129  'simple relative path' => [
5130  'foo',
5131  'foo.bar.test',
5132  'http://foo.bar.test/foo'
5133  ],
5134  'path beginning with slash' => [
5135  '/foo',
5136  'foo.bar.test',
5137  'http://foo.bar.test/foo'
5138  ],
5139  'path with full domain and https scheme' => [
5140  'https://example.com/foo',
5141  'foo.bar.test',
5142  'https://example.com/foo'
5143  ],
5144  'path with full domain and http scheme' => [
5145  'http://example.com/foo',
5146  'foo.bar.test',
5147  'http://example.com/foo'
5148  ],
5149  'path with full domain and relative scheme' => [
5150  '//example.com/foo',
5151  'foo.bar.test',
5152  '//example.com/foo'
5153  ],
5154  ];
5155  }
5156 
5165  public function locationHeaderUrl($path, $host, $expected)
5166  {
5167  $_SERVER['HTTP_HOST'] = $host;
5168  $_SERVER['SCRIPT_NAME'] = '/index.php';
5169  $result = GeneralUtility::locationHeaderUrl($path);
5170  self::assertSame($expected, $result);
5171  }
5172 }
canSetNewGetInputValues($input, $key, $expected, $getPreset=[])
static compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt=true)
static minifyJavaScript($script, &$error='')
callUserFunctionWillThrowExceptionForInvalidParameters($functionName, $expectedException)
static unlink_tempfile($uploadedTempFileName)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static mkdir_deep($directory, $deepDirectory='')
static addInstance($className, $instance)
static array2xml(array $array, $NSprefix='', $level=0, $docTag='phparray', $spaceInd=0, array $options=[], array $stackData=[])
static getFilesInDir($path, $extensionList='', $prependPath=false, $order='', $excludePattern='')
static isFirstPartOfStr($str, $partStr)
trimExplodeReturnsCorrectResult($delimiter, $testString, $removeEmpty, $limit, $expectedResult)
revExplodeCorrectlyExplodesStringForGivenPartsCount($delimiter, $testString, $count, $expectedArray)
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
static setSingletonInstance($className, SingletonInterface $instance)
static writeFileToTypo3tempDir($filepath, $content)
rmFromListRemovesElementsFromCommaSeparatedList($initialList, $listWithElementRemoved)
static hmac($input, $additionalSecret='')
static getFileAbsFileName($filename, $_=null, $_2=null)
getIndpEnvForHostThrowsExceptionForNotAllowedHostnameValues($httpHost, $hostNamePattern)
splitHeaderLines(array $headers, array $expectedHeaders)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static verifyFilenameAgainstDenyPattern($filename)
static copyDirectory($source, $destination)
static makeInstance($className,... $constructorArguments)
static _GETset($inputGet, $key='')
formatSizeTranslatesBytesToHigherOrderRepresentation($size, $labels, $base, $expected)
static resetSingletonInstances(array $newSingletonInstances)
static split_fileref($fileNameWithPath)
static unQuoteFilenames($parameters, $unQuote=false)
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static fixPermissions($path, $recursive=false)
getIndpEnvTypo3PortParsesHostnamesAndIpAdresses($httpHost, $dummy, $expectedPort)
static getAllFilesAndFoldersInPath(array $fileArr, $path, $extList='', $regDirs=false, $recursivityLevels=99, $excludePattern='')
static getBytesFromSizeMeasurement($measurement)
static tempnam($filePrefix, $fileSuffix='')
static rmdir($path, $removeNonEmpty=false)
getBytesFromSizeMeasurementCalculatesCorrectByteValue($expected, $byteString)
static explodeUrl2Array($string, $multidim=false)
static splitCalc($string, $operators)
slashJsEscapesSingleQuotesAndSlashes($input, $extended, $expected)
getIndpEnvForHostAllowsAllHostnameValuesIfHostPatternIsSetToAllowAll($httpHost, $hostNamePattern)
sanitizeLocalUrlAcceptsNotEncodedValidUrls($url, $host, $subDirectory)
static xml2array($string, $NSprefix='', $reportDocTag=false)
explodeAndUnquoteImageMagickCommands($source, $expectedQuoted, $expectedUnquoted)
static setPackageManager(PackageManager $packageManager)
isAllowedHostHeaderValueWorksCorrectlyWithWithServerNamePattern( $httpHost, $serverName, $isAllowed, $serverPort='80', $ssl='Off')
static formatSize($sizeInBytes, $labels='', $base=0)
static revExplode($delimiter, $string, $count=0)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static slashJS($string, $extended=false, $char='\'')
isAllowedHostHeaderValueReturnsTrueIfHostValueMatches($httpHost, $hostNamePattern)
isAllowedHostHeaderValueReturnsFalseIfHostValueMatches($httpHost, $hostNamePattern)
static uniqueList($in_list, $secondParameter=null)