‪TYPO3CMS  ‪main
GeneralUtilityTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use Psr\Log\LoggerInterface;
30 use TYPO3\CMS\Core\Package\PackageManager;
45 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
46 
47 final class ‪GeneralUtilityTest extends UnitTestCase
48 {
49  public const ‪NO_FIX_PERMISSIONS_ON_WINDOWS = 'fixPermissions() not available on Windows (method does nothing)';
50 
51  protected bool ‪$resetSingletonInstances = true;
52 
53  protected bool ‪$backupEnvironment = true;
54 
55  protected ?PackageManager ‪$backupPackageManager;
56 
57  protected function ‪setUp(): void
58  {
59  parent::setUp();
61  }
62 
63  protected function ‪tearDown(): void
64  {
65  GeneralUtility::flushInternalRuntimeCaches();
66  if ($this->backupPackageManager) {
68  }
69  parent::tearDown();
70  }
71 
78  public function ‪isConnected(): bool
79  {
80  $isConnected = false;
81  $connected = @fsockopen('typo3.org', 80);
82  if ($connected) {
83  $isConnected = true;
84  fclose($connected);
85  }
86  return $isConnected;
87  }
88 
93  protected function ‪getTestDirectory(string $prefix = 'root_'): string
94  {
95  $path = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId($prefix);
97  $this->testFilesToDelete[] = $path;
98  return $path;
99  }
100 
104  public static function cmpIPv4DataProviderMatching(): array
105  {
106  return [
107  'host with full IP address' => ['127.0.0.1', '127.0.0.1'],
108  'host with two wildcards at the end' => ['127.0.0.1', '127.0.*.*'],
109  'host with wildcard at third octet' => ['127.0.0.1', '127.0.*.1'],
110  'host with wildcard at second octet' => ['127.0.0.1', '127.*.0.1'],
111  '/8 subnet' => ['127.0.0.1', '127.1.1.1/8'],
112  '/32 subnet (match only name)' => ['127.0.0.1', '127.0.0.1/32'],
113  '/30 subnet' => ['10.10.3.1', '10.10.3.3/30'],
114  'host with wildcard in list with IPv4/IPv6 addresses' => ['192.168.1.1', '127.0.0.1, 1234:5678::/126, 192.168.*'],
115  'host in list with IPv4/IPv6 addresses' => ['192.168.1.1', '::1, 1234:5678::/126, 192.168.1.1'],
116  ];
117  }
118 
119  #[DataProvider('cmpIPv4DataProviderMatching')]
120  #[Test]
121  public function ‪cmpIPv4ReturnsTrueForMatchingAddress(string $ip, string $list): void
122  {
123  self::assertTrue(‪GeneralUtility::cmpIPv4($ip, $list));
124  }
125 
131  public static function cmpIPv4DataProviderNotMatching(): array
132  {
133  return [
134  'single host' => ['127.0.0.1', '127.0.0.2'],
135  'single host with wildcard' => ['127.0.0.1', '127.*.1.1'],
136  'single host with /32 subnet mask' => ['127.0.0.1', '127.0.0.2/32'],
137  '/31 subnet' => ['127.0.0.1', '127.0.0.2/31'],
138  'list with IPv4/IPv6 addresses' => ['127.0.0.1', '10.0.2.3, 192.168.1.1, ::1'],
139  'list with only IPv6 addresses' => ['10.20.30.40', '::1, 1234:5678::/127'],
140  ];
141  }
142 
143  #[DataProvider('cmpIPv4DataProviderNotMatching')]
144  #[Test]
145  public function ‪cmpIPv4ReturnsFalseForNotMatchingAddress(string $ip, string $list): void
146  {
147  self::assertFalse(‪GeneralUtility::cmpIPv4($ip, $list));
148  }
149 
151  // Tests concerning cmpIPv6
153 
158  public static function cmpIPv6DataProviderMatching(): array
159  {
160  return [
161  'empty address' => ['::', '::'],
162  'empty with netmask in list' => ['::', '::/0'],
163  'empty with netmask 0 and host-bits set in list' => ['::', '::123/0'],
164  'localhost' => ['::1', '::1'],
165  'localhost with leading zero blocks' => ['::1', '0:0::1'],
166  'host with submask /128' => ['::1', '0:0::1/128'],
167  '/16 subnet' => ['1234::1', '1234:5678::/16'],
168  '/126 subnet' => ['1234:5678::3', '1234:5678::/126'],
169  '/126 subnet with host-bits in list set' => ['1234:5678::3', '1234:5678::2/126'],
170  'list with IPv4/IPv6 addresses' => ['1234:5678::3', '::1, 127.0.0.1, 1234:5678::/126, 192.168.1.1'],
171  ];
172  }
173 
174  #[DataProvider('cmpIPv6DataProviderMatching')]
175  #[Test]
176  public function ‪cmpIPv6ReturnsTrueForMatchingAddress(string $ip, string $list): void
177  {
178  self::assertTrue(‪GeneralUtility::cmpIPv6($ip, $list));
179  }
180 
186  public static function cmpIPv6DataProviderNotMatching(): array
187  {
188  return [
189  'empty against localhost' => ['::', '::1'],
190  'empty against localhost with /128 netmask' => ['::', '::1/128'],
191  'localhost against different host' => ['::1', '::2'],
192  'localhost against host with prior bits set' => ['::1', '::1:1'],
193  'host against different /17 subnet' => ['1234::1', '1234:f678::/17'],
194  'host against different /127 subnet' => ['1234:5678::3', '1234:5678::/127'],
195  'host against IPv4 address list' => ['1234:5678::3', '127.0.0.1, 192.168.1.1'],
196  'host against mixed list with IPv6 host in different subnet' => ['1234:5678::3', '::1, 1234:5678::/127'],
197  ];
198  }
199 
200  #[DataProvider('cmpIPv6DataProviderNotMatching')]
201  #[Test]
202  public function ‪cmpIPv6ReturnsFalseForNotMatchingAddress(string $ip, string $list): void
203  {
204  self::assertFalse(‪GeneralUtility::cmpIPv6($ip, $list));
205  }
206 
208  // Tests concerning normalizeIPv6
210 
215  public static function normalizeCompressIPv6DataProviderCorrect(): array
216  {
217  return [
218  'empty' => ['::', '0000:0000:0000:0000:0000:0000:0000:0000'],
219  'localhost' => ['::1', '0000:0000:0000:0000:0000:0000:0000:0001'],
220  'expansion in middle 1' => ['1::2', '0001:0000:0000:0000:0000:0000:0000:0002'],
221  'expansion in middle 2' => ['1:2::3', '0001:0002:0000:0000:0000:0000:0000:0003'],
222  'expansion in middle 3' => ['1::2:3', '0001:0000:0000:0000:0000:0000:0002:0003'],
223  'expansion in middle 4' => ['1:2::3:4:5', '0001:0002:0000:0000:0000:0003:0004:0005'],
224  ];
225  }
226 
227  #[DataProvider('normalizeCompressIPv6DataProviderCorrect')]
228  #[Test]
229  public function ‪normalizeIPv6CorrectlyNormalizesAddresses(string $compressed, string $normalized): void
230  {
231  self::assertEquals($normalized, ‪GeneralUtility::normalizeIPv6($compressed));
232  }
233 
235  // Tests concerning validIP
237 
242  public static function validIpDataProvider(): array
243  {
244  return [
245  '0.0.0.0' => ['0.0.0.0'],
246  'private IPv4 class C' => ['192.168.0.1'],
247  'private IPv4 class A' => ['10.0.13.1'],
248  'private IPv6' => ['fe80::daa2:5eff:fe8b:7dfb'],
249  ];
250  }
251 
252  #[DataProvider('validIpDataProvider')]
253  #[Test]
254  public function ‪validIpReturnsTrueForValidIp(string $ip): void
255  {
256  self::assertTrue(‪GeneralUtility::validIP($ip));
257  }
258 
264  public static function invalidIpDataProvider(): array
265  {
266  return [
267  'zero string' => ['0'],
268  'string' => ['test'],
269  'string empty' => [''],
270  'string NULL' => ['NULL'],
271  'out of bounds IPv4' => ['300.300.300.300'],
272  'dotted decimal notation with only two dots' => ['127.0.1'],
273  ];
274  }
275 
276  #[DataProvider('invalidIpDataProvider')]
277  #[Test]
278  public function ‪validIpReturnsFalseForInvalidIp(string $ip): void
279  {
280  self::assertFalse(‪GeneralUtility::validIP($ip));
281  }
282 
284  // Tests concerning cmpFQDN
286 
291  public static function cmpFqdnValidDataProvider(): array
292  {
293  return [
294  'localhost should usually resolve, IPv4' => ['127.0.0.1', '*'],
295  'localhost should usually resolve, IPv6' => ['::1', '*'],
296  // other testcases with resolving not possible since it would
297  // require a working IPv4/IPv6-connectivity
298  'aaa.bbb.ccc.ddd.eee, full' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee'],
299  'aaa.bbb.ccc.ddd.eee, wildcard first' => ['aaa.bbb.ccc.ddd.eee', '*.ccc.ddd.eee'],
300  'aaa.bbb.ccc.ddd.eee, wildcard last' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.*'],
301  'aaa.bbb.ccc.ddd.eee, wildcard middle' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.eee'],
302  'list-matches, 1' => ['aaa.bbb.ccc.ddd.eee', 'xxx, yyy, zzz, aaa.*.eee'],
303  'list-matches, 2' => ['aaa.bbb.ccc.ddd.eee', '127:0:0:1,,aaa.*.eee,::1'],
304  ];
305  }
306 
307  #[DataProvider('cmpFqdnValidDataProvider')]
308  #[Test]
309  public function ‪cmpFqdnReturnsTrue(string $baseHost, string $list): void
310  {
311  self::assertTrue(‪GeneralUtility::cmpFQDN($baseHost, $list));
312  }
313 
319  public static function cmpFqdnInvalidDataProvider(): array
320  {
321  return [
322  'num-parts of hostname to check can only be less or equal than hostname, 1' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee.fff'],
323  'num-parts of hostname to check can only be less or equal than hostname, 2' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.bbb.ccc.ddd.eee'],
324  ];
325  }
326 
327  #[DataProvider('cmpFqdnInvalidDataProvider')]
328  #[Test]
329  public function ‪cmpFqdnReturnsFalse(string $baseHost, string $list): void
330  {
331  self::assertFalse(‪GeneralUtility::cmpFQDN($baseHost, $list));
332  }
333 
335  // Tests concerning inList
337  #[DataProvider('inListForItemContainedReturnsTrueDataProvider')]
338  #[Test]
339  public function ‪inListForItemContainedReturnsTrue(string $haystack): void
340  {
341  self::assertTrue(‪GeneralUtility::inList($haystack, 'findme'));
342  }
343 
347  public static function ‪inListForItemContainedReturnsTrueDataProvider(): array
348  {
349  return [
350  'Element as second element of four items' => ['one,findme,three,four'],
351  'Element at beginning of list' => ['findme,one,two'],
352  'Element at end of list' => ['one,two,findme'],
353  'One item list' => ['findme'],
354  ];
355  }
356 
357  #[DataProvider('inListForItemNotContainedReturnsFalseDataProvider')]
358  #[Test]
359  public function ‪inListForItemNotContainedReturnsFalse(string $haystack): void
360  {
361  self::assertFalse(‪GeneralUtility::inList($haystack, 'findme'));
362  }
363 
368  {
369  return [
370  'Four item list' => ['one,two,three,four'],
371  'One item list' => ['one'],
372  'Empty list' => [''],
373  ];
374  }
375 
377  // Tests concerning expandList
379  #[DataProvider('expandListExpandsIntegerRangesDataProvider')]
380  #[Test]
381  public function ‪expandListExpandsIntegerRanges(string $list, string $expectation): void
382  {
383  self::assertSame($expectation, ‪GeneralUtility::expandList($list));
384  }
385 
389  public static function ‪expandListExpandsIntegerRangesDataProvider(): array
390  {
391  return [
392  'Expand for the same number' => ['1,2-2,7', '1,2,7'],
393  'Small range expand with parameters reversed ignores reversed items' => ['1,5-3,7', '1,7'],
394  'Small range expand' => ['1,3-5,7', '1,3,4,5,7'],
395  'Expand at beginning' => ['3-5,1,7', '3,4,5,1,7'],
396  'Expand at end' => ['1,7,3-5', '1,7,3,4,5'],
397  'Multiple small range expands' => ['1,3-5,7-10,12', '1,3,4,5,7,8,9,10,12'],
398  'One item list' => ['1-5', '1,2,3,4,5'],
399  'Nothing to expand' => ['1,2,3,4', '1,2,3,4'],
400  'Empty list' => ['', ''],
401  ];
402  }
403 
404  #[Test]
406  {
407  $list = ‪GeneralUtility::expandList('1-2000');
408  self::assertCount(1000, explode(',', $list));
409  }
410 
412  // Tests concerning formatSize
414  #[DataProvider('formatSizeDataProvider')]
415  #[Test]
416  public function ‪formatSizeTranslatesBytesToHigherOrderRepresentation($size, $labels, $base, $expected): void
417  {
418  self::assertEquals($expected, GeneralUtility::formatSize($size, $labels, $base));
419  }
420 
424  public static function ‪formatSizeDataProvider(): array
425  {
426  return [
427  'IEC Bytes stay bytes (min)' => [1, '', 0, '1 '],
428  'IEC Bytes stay bytes (max)' => [921, '', 0, '921 '],
429  'IEC Kilobytes are used (min)' => [922, '', 0, '0.90 Ki'],
430  'IEC Kilobytes are used (max)' => [943718, '', 0, '922 Ki'],
431  'IEC Megabytes are used (min)' => [943719, '', 0, '0.90 Mi'],
432  'IEC Megabytes are used (max)' => [966367641, '', 0, '922 Mi'],
433  'IEC Gigabytes are used (min)' => [966367642, '', 0, '0.90 Gi'],
434  'IEC Gigabytes are used (max)' => [989560464998, '', 0, '922 Gi'],
435  'IEC Decimal is omitted for large kilobytes' => [31080, '', 0, '30 Ki'],
436  'IEC Decimal is omitted for large megabytes' => [31458000, '', 0, '30 Mi'],
437  'IEC Decimal is omitted for large gigabytes' => [32212254720, '', 0, '30 Gi'],
438  'SI Bytes stay bytes (min)' => [1, 'si', 0, '1 '],
439  'SI Bytes stay bytes (max)' => [899, 'si', 0, '899 '],
440  'SI Kilobytes are used (min)' => [901, 'si', 0, '0.90 k'],
441  'SI Kilobytes are used (max)' => [900000, 'si', 0, '900 k'],
442  'SI Megabytes are used (min)' => [900001, 'si', 0, '0.90 M'],
443  'SI Megabytes are used (max)' => [900000000, 'si', 0, '900 M'],
444  'SI Gigabytes are used (min)' => [900000001, 'si', 0, '0.90 G'],
445  'SI Gigabytes are used (max)' => [900000000000, 'si', 0, '900 G'],
446  'SI Decimal is omitted for large kilobytes' => [30000, 'si', 0, '30 k'],
447  'SI Decimal is omitted for large megabytes' => [30000000, 'si', 0, '30 M'],
448  'SI Decimal is omitted for large gigabytes' => [30000000000, 'si', 0, '30 G'],
449  'Label for bytes can be exchanged (binary unit)' => [1, ' Foo|||', 0, '1 Foo'],
450  'Label for kilobytes can be exchanged (binary unit)' => [1024, '| Foo||', 0, '1.00 Foo'],
451  'Label for megabytes can be exchanged (binary unit)' => [1048576, '|| Foo|', 0, '1.00 Foo'],
452  'Label for gigabytes can be exchanged (binary unit)' => [1073741824, '||| Foo', 0, '1.00 Foo'],
453  'Label for bytes can be exchanged (decimal unit)' => [1, ' Foo|||', 1000, '1 Foo'],
454  'Label for kilobytes can be exchanged (decimal unit)' => [1000, '| Foo||', 1000, '1.00 Foo'],
455  'Label for megabytes can be exchanged (decimal unit)' => [1000000, '|| Foo|', 1000, '1.00 Foo'],
456  'Label for gigabytes can be exchanged (decimal unit)' => [1000000000, '||| Foo', 1000, '1.00 Foo'],
457  'IEC Base is ignored' => [1024, 'iec', 1000, '1.00 Ki'],
458  'SI Base is ignored' => [1000, 'si', 1024, '1.00 k'],
459  'Use binary base for unexpected base' => [2048, '| Bar||', 512, '2.00 Bar'],
460  ];
461  }
462 
464  // Tests concerning splitCalc
466 
471  public static function ‪splitCalcDataProvider(): array
472  {
473  return [
474  'empty string returns empty array' => [
475  [],
476  '',
477  ],
478  'number without operator returns array with plus and number' => [
479  [['+', '42']],
480  '42',
481  ],
482  'two numbers with asterisk return first number with plus and second number with asterisk' => [
483  [['+', '42'], ['*', '31']],
484  '42 * 31',
485  ],
486  ];
487  }
488 
489  #[DataProvider('splitCalcDataProvider')]
490  #[Test]
491  public function ‪splitCalcCorrectlySplitsExpression(array $expected, string $expression): void
492  {
493  self::assertSame($expected, GeneralUtility::splitCalc($expression, '+-*/'));
494  }
495 
497  // Tests concerning validEmail
499 
504  public static function validEmailValidDataProvider(): array
505  {
506  return [
507  'short mail address' => ['a@b.c'],
508  'simple mail address' => ['test@example.com'],
509  'uppercase characters' => ['QWERTYUIOPASDFGHJKLZXCVBNM@QWERTYUIOPASDFGHJKLZXCVBNM.NET'],
510  'equal sign in local part' => ['test=mail@example.com'],
511  'dash in local part' => ['test-mail@example.com'],
512  'plus in local part' => ['test+mail@example.com'],
513  'question mark in local part' => ['test?mail@example.com'],
514  'slash in local part' => ['foo/bar@example.com'],
515  'hash in local part' => ['foo#bar@example.com'],
516  'dot in local part' => ['firstname.lastname@employee.2something.com'],
517  'dash as local part' => ['-@foo.com'],
518  'umlauts in domain part' => ['foo@äöüfoo.com'],
519  'number as top level domain' => ['foo@bar.123'],
520  'top level domain only' => ['test@localhost'],
521  'umlauts in local part' => ['äöüfoo@bar.com'],
522  'quoted @ char' => ['"Abc@def"@example.com'],
523  'space between the quotes' => ['" "@example.com'],
524  'quoted double dot' => ['"john..doe"@example.com'],
525  'bangified host route used for uucp mailers' => ['mailhost!username@example.com'],
526  '% escaped mail route to user@example.com via example.com' => ['user%example.com@example.com'],
527  'local-part ending with non-alphanumeric character from the list of allowed printable characters' => ['user-@example.com'],
528  'ipv4 addresses are allowed instead of domains when in square brackets, but strongly discouraged' => ['postmaster@[123.123.123.123]'],
529  'ipv6 uses a different syntax' => ['postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]'],
530  'spaces in local quoted' => ['"yes spaces are fine"@example.com'],
531  ];
532  }
533 
534  #[DataProvider('validEmailValidDataProvider')]
535  #[Test]
536  public function validEmailReturnsTrueForValidMailAddress(string $address): void
537  {
538  self::assertTrue(GeneralUtility::validEmail($address));
539  }
540 
546  public static function validEmailInvalidDataProvider(): array
547  {
548  return [
549  'empty string' => [''],
550  'integer string' => ['42'],
551  'float string' => ['42.23'],
552  '@ sign only' => ['@'],
553  'string longer than 320 characters' => [str_repeat('0123456789', 33)],
554  'duplicate @' => ['test@@example.com'],
555  'duplicate @ combined with further special characters in local part' => ['test!.!@#$%^&*@example.com'],
556  'opening parenthesis in local part' => ['foo(bar@example.com'],
557  'closing parenthesis in local part' => ['foo)bar@example.com'],
558  'opening square bracket in local part' => ['foo[bar@example.com'],
559  'closing square bracket as local part' => [']@example.com'],
560  'dash as second level domain' => ['foo@-.com'],
561  'domain part starting with dash' => ['foo@-foo.com'],
562  'domain part ending with dash' => ['foo@foo-.com'],
563  'dot at beginning of domain part' => ['test@.com'],
564  'local part ends with dot' => ['e.x.a.m.p.l.e.@example.com'],
565  'trailing whitespace' => ['test@example.com '],
566  'trailing carriage return' => ['test@example.com' . CR],
567  'trailing linefeed' => ['test@example.com' . LF],
568  'trailing carriage return linefeed' => ['test@example.com' . CRLF],
569  'trailing tab' => ['test@example.com' . "\t"],
570  'prohibited input characters' => ['“mailto:test@example.com”'],
571  'escaped @ char' => ['abc\@def@example.com'], // known bug, see https://github.com/egulias/EmailValidator/issues/181
572  'no @ character' => ['Abc.example.com'],
573  'only one @ is allowed outside quotation marks' => ['A@b@c@example.com'],
574  'quoted strings must be dot separated or the only element making up the local-part' => ['just"not"right@example.com'],
575  'local-part is longer than 64 characters' => ['1234567890123456789012345678901234567890123456789012345678901234+x@example.com '],
576  'icon characters' => ['QA[icon]CHOCOLATE[icon]@example.com'],
577  'space in middle of local' => ['te at@example.com'],
578  'leading space(s)' => [' teat@example.com'],
579  'space after @' => ['test@ example.com'],
580  'space in domain' => ['test@ex ample.com'],
581  'space before @' => ['test @example.com'],
582  'unbalanced quotes' => ['unbalanced-quotes"@example.com'],
583  'unbalanced quotes with leading spaces' => [' unbalanced-quotes"@example.com'],
584  ];
585  }
586 
587  #[DataProvider('validEmailInvalidDataProvider')]
588  #[Test]
589  public function validEmailReturnsFalseForInvalidMailAddress(string $address): void
590  {
591  self::assertFalse(GeneralUtility::validEmail($address));
592  }
593 
595  // Tests concerning intExplode
597 
598  public static function intExplodeDataProvider(): array
599  {
600  return [
601  'convertStringToInteger' => [
602  '1,foo,2',
603  false,
604  [1, 0, 2],
605  ],
606  'zerosAreKept' => [
607  '0,1, 0, 2,0',
608  false,
609  [0, 1, 0, 2, 0],
610  ],
611  'emptyValuesAreKept' => [
612  '0,1,, 0, 2,,0',
613  false,
614  [0, 1, 0, 0, 2, 0, 0],
615  ],
616  'emptyValuesAreRemoved' => [
617  '0,1,, 0, 2,,0',
618  true,
619  [0, 1, 0, 2, 0],
620  ],
621  ];
622  }
623 
624  #[DataProvider('intExplodeDataProvider')]
625  #[Test]
626  public function intExplodeReturnsExplodedArray(string $input, bool $removeEmpty, array $expected): void
627  {
628  self::assertSame($expected, ‪GeneralUtility::intExplode(',', $input, $removeEmpty));
629  }
630 
632  // Tests concerning implodeArrayForUrl / explodeUrl2Array
634 
637  public static function implodeArrayForUrlDataProvider(): array
638  {
639  $valueArray = ['one' => '√', 'two' => 2];
640  return [
641  'Empty input' => ['foo', [], ''],
642  'String parameters' => ['foo', $valueArray, '&foo[one]=%E2%88%9A&foo[two]=2'],
643  'Nested array parameters' => ['foo', [$valueArray], '&foo[0][one]=%E2%88%9A&foo[0][two]=2'],
644  'Keep blank parameters' => ['foo', ['one' => '√', ''], '&foo[one]=%E2%88%9A&foo[0]='],
645  ];
646  }
647 
648  #[DataProvider('implodeArrayForUrlDataProvider')]
649  #[Test]
650  public function implodeArrayForUrlBuildsValidParameterString($name, $input, $expected): void
651  {
652  self::assertSame($expected, ‪GeneralUtility::implodeArrayForUrl($name, $input));
653  }
654 
655  #[Test]
656  public function implodeArrayForUrlCanSkipEmptyParameters(): void
657  {
658  $input = ['one' => '√', ''];
659  $expected = '&foo[one]=%E2%88%9A';
660  self::assertSame($expected, ‪GeneralUtility::implodeArrayForUrl('foo', $input, '', true));
661  }
662 
663  #[Test]
664  public function implodeArrayForUrlCanUrlEncodeKeyNames(): void
665  {
666  $input = ['one' => '√', ''];
667  $expected = '&foo%5Bone%5D=%E2%88%9A&foo%5B0%5D=';
668  self::assertSame($expected, ‪GeneralUtility::implodeArrayForUrl('foo', $input, '', false, true));
669  }
670 
671  public static function explodeUrl2ArrayTransformsParameterStringToFlatArrayDataProvider(): array
672  {
673  return [
674  'Empty string' => ['', []],
675  'Simple parameter string' => ['&one=%E2%88%9A&two=2', ['one' => '√', 'two' => 2]],
676  'Nested parameter string' => ['&foo[one]=%E2%88%9A&two=2', ['foo[one]' => '√', 'two' => 2]],
677  'Parameter without value' => ['&one=&two=2', ['one' => '', 'two' => 2]],
678  'Nested parameter without value' => ['&foo[one]=&two=2', ['foo[one]' => '', 'two' => 2]],
679  'Parameter without equals sign' => ['&one&two=2', ['one' => '', 'two' => 2]],
680  'Nested parameter without equals sign' => ['&foo[one]&two=2', ['foo[one]' => '', 'two' => 2]],
681  ];
682  }
683 
684  #[DataProvider('explodeUrl2ArrayTransformsParameterStringToFlatArrayDataProvider')]
685  #[Test]
686  public function explodeUrl2ArrayTransformsParameterStringToFlatArray(string $input, array $expected): void
687  {
688  self::assertEquals($expected, GeneralUtility::explodeUrl2Array($input));
689  }
690 
691  public static function revExplodeDataProvider(): array
692  {
693  return [
694  'limit 0 should return unexploded string' => [
695  ':',
696  'my:words:here',
697  0,
698  ['my:words:here'],
699  ],
700  'limit 1 should return unexploded string' => [
701  ':',
702  'my:words:here',
703  1,
704  ['my:words:here'],
705  ],
706  'limit 2 should return two pieces' => [
707  ':',
708  'my:words:here',
709  2,
710  ['my:words', 'here'],
711  ],
712  'limit 3 should return unexploded string' => [
713  ':',
714  'my:words:here',
715  3,
716  ['my', 'words', 'here'],
717  ],
718  'limit 0 should return unexploded string if no delimiter is contained' => [
719  ':',
720  'mywordshere',
721  0,
722  ['mywordshere'],
723  ],
724  'limit 1 should return unexploded string if no delimiter is contained' => [
725  ':',
726  'mywordshere',
727  1,
728  ['mywordshere'],
729  ],
730  'limit 2 should return unexploded string if no delimiter is contained' => [
731  ':',
732  'mywordshere',
733  2,
734  ['mywordshere'],
735  ],
736  'limit 3 should return unexploded string if no delimiter is contained' => [
737  ':',
738  'mywordshere',
739  3,
740  ['mywordshere'],
741  ],
742  'multi character delimiter is handled properly with limit 2' => [
743  '[]',
744  'a[b][c][d]',
745  2,
746  ['a[b][c', 'd]'],
747  ],
748  'multi character delimiter is handled properly with limit 3' => [
749  '[]',
750  'a[b][c][d]',
751  3,
752  ['a[b', 'c', 'd]'],
753  ],
754  ];
755  }
756 
757  #[DataProvider('revExplodeDataProvider')]
758  #[Test]
759  public function revExplodeCorrectlyExplodesStringForGivenPartsCount($delimiter, $testString, $count, $expectedArray): void
760  {
761  $actualArray = ‪GeneralUtility::revExplode($delimiter, $testString, $count);
762  self::assertEquals($expectedArray, $actualArray);
763  }
764 
765  #[Test]
766  public function revExplodeRespectsLimitThreeWhenExploding(): void
767  {
768  $testString = 'even:more:of:my:words:here';
769  $expectedArray = ['even:more:of:my', 'words', 'here'];
770  $actualArray = ‪GeneralUtility::revExplode(':', $testString, 3);
771  self::assertEquals($expectedArray, $actualArray);
772  }
773 
775  // Tests concerning trimExplode
777  #[DataProvider('trimExplodeReturnsCorrectResultDataProvider')]
778  #[Test]
779  public function trimExplodeReturnsCorrectResult(string $delimiter, string $testString, bool $removeEmpty, int $limit, array $expectedResult): void
780  {
781  self::assertSame($expectedResult, ‪GeneralUtility::trimExplode($delimiter, $testString, $removeEmpty, $limit));
782  }
783 
784  public static function trimExplodeReturnsCorrectResultDataProvider(): array
785  {
786  return [
787  'spaces at element start and end' => [
788  ',',
789  ' a , b , c ,d ,, e,f,',
790  false,
791  0,
792  ['a', 'b', 'c', 'd', '', 'e', 'f', ''],
793  ],
794  'removes newline' => [
795  ',',
796  ' a , b , ' . LF . ' ,d ,, e,f,',
797  true,
798  0,
799  ['a', 'b', 'd', 'e', 'f'],
800  ],
801  'removes empty elements' => [
802  ',',
803  'a , b , c , ,d ,, ,e,f,',
804  true,
805  0,
806  ['a', 'b', 'c', 'd', 'e', 'f'],
807  ],
808  'keeps remaining results with empty items after reaching limit with positive parameter' => [
809  ',',
810  ' a , b , c , , d,, ,e ',
811  false,
812  3,
813  ['a', 'b', 'c , , d,, ,e'],
814  ],
815  'keeps remaining results without empty items after reaching limit with positive parameter' => [
816  ',',
817  ' a , b , c , , d,, ,e ',
818  true,
819  3,
820  ['a', 'b', 'c , d,e'],
821  ],
822  'keeps remaining results with empty items after reaching limit with negative parameter' => [
823  ',',
824  ' a , b , c , d, ,e, f , , ',
825  false,
826  -3,
827  ['a', 'b', 'c', 'd', '', 'e'],
828  ],
829  'keeps remaining results without empty items after reaching limit with negative parameter' => [
830  ',',
831  ' a , b , c , d, ,e, f , , ',
832  true,
833  -3,
834  ['a', 'b', 'c'],
835  ],
836  'returns exact results without reaching limit with positive parameter' => [
837  ',',
838  ' a , b , , c , , , ',
839  true,
840  4,
841  ['a', 'b', 'c'],
842  ],
843  'keeps zero as string' => [
844  ',',
845  'a , b , c , ,d ,, ,e,f, 0 ,',
846  true,
847  0,
848  ['a', 'b', 'c', 'd', 'e', 'f', '0'],
849  ],
850  'keeps whitespace inside elements' => [
851  ',',
852  'a , b , c , ,d ,, ,e,f, g h ,',
853  true,
854  0,
855  ['a', 'b', 'c', 'd', 'e', 'f', 'g h'],
856  ],
857  'can use internal regex delimiter as explode delimiter' => [
858  '/',
859  'a / b / c / /d // /e/f/ g h /',
860  true,
861  0,
862  ['a', 'b', 'c', 'd', 'e', 'f', 'g h'],
863  ],
864  'can use whitespaces as delimiter' => [
865  ' ',
866  '* * * * *',
867  true,
868  0,
869  ['*', '*', '*', '*', '*'],
870  ],
871  'can use words as delimiter' => [
872  'All',
873  'HelloAllTogether',
874  true,
875  0,
876  ['Hello', 'Together'],
877  ],
878  'can use word with appended and prepended spaces as delimiter' => [
879  ' all ',
880  'Hello all together',
881  true,
882  0,
883  ['Hello', 'together'],
884  ],
885  'can use word with appended and prepended spaces as delimiter and do not remove empty' => [
886  ' all ',
887  'Hello all together all there all all are all none',
888  false,
889  0,
890  ['Hello', 'together', 'there', '', 'are', 'none'],
891  ],
892  'can use word with appended and prepended spaces as delimiter, do not remove empty and limit' => [
893  ' all ',
894  'Hello all together all there all all are all none',
895  false,
896  5,
897  ['Hello', 'together', 'there', '', 'are all none'],
898  ],
899  'can use word with appended and prepended spaces as delimiter, do not remove empty, limit and multiple delimiter in last' => [
900  ' all ',
901  'Hello all together all there all all are all none',
902  false,
903  4,
904  ['Hello', 'together', 'there', 'all are all none'],
905  ],
906  'can use word with appended and prepended spaces as delimiter, remove empty and limit' => [
907  ' all ',
908  'Hello all together all there all all are all none',
909  true,
910  4,
911  ['Hello', 'together', 'there', 'are all none'],
912  ],
913  'can use word with appended and prepended spaces as delimiter, remove empty and limit and multiple delimiter in last' => [
914  ' all ',
915  'Hello all together all there all all are all none',
916  true,
917  5,
918  ['Hello', 'together', 'there', 'are' , 'none'],
919  ],
920  'can use words as delimiter and do not remove empty' => [
921  'all there',
922  'Helloall theretogether all there all there are all there none',
923  false,
924  0,
925  ['Hello', 'together', '', 'are', 'none'],
926  ],
927  'can use words as delimiter, do not remove empty and limit' => [
928  'all there',
929  'Helloall theretogether all there all there are all there none',
930  false,
931  4,
932  ['Hello', 'together', '', 'are all there none'],
933  ],
934  'can use words as delimiter, do not remove empty, limit and multiple delimiter in last' => [
935  'all there',
936  'Helloall theretogether all there all there are all there none',
937  false,
938  3,
939  ['Hello', 'together', 'all there are all there none'],
940  ],
941  'can use words as delimiter, remove empty' => [
942  'all there',
943  'Helloall theretogether all there all there are all there none',
944  true,
945  0,
946  ['Hello', 'together', 'are', 'none'],
947  ],
948  'can use words as delimiter, remove empty and limit' => [
949  'all there',
950  'Helloall theretogether all there all there are all there none',
951  true,
952  3,
953  ['Hello', 'together', 'are all there none'],
954  ],
955  'can use words as delimiter, remove empty and limit and multiple delimiter in last' => [
956  'all there',
957  'Helloall theretogether all there all there are all there none',
958  true,
959  4,
960  ['Hello', 'together', 'are' , 'none'],
961  ],
962  'can use new line as delimiter' => [
963  LF,
964  "Hello\nall\ntogether",
965  true,
966  0,
967  ['Hello', 'all', 'together'],
968  ],
969  'works with whitespace separator' => [
970  "\t",
971  " a b \t c \t \t d \t e \t u j \t s",
972  false,
973  0,
974  ['a b', 'c', '', 'd', 'e', 'u j', 's'],
975  ],
976  'works with whitespace separator and limit' => [
977  "\t",
978  " a b \t c \t \t d \t e \t u j \t s",
979  false,
980  4,
981  ['a b', 'c', '', "d \t e \t u j \t s"],
982  ],
983  'works with whitespace separator and remove empty' => [
984  "\t",
985  " a b \t c \t \t d \t e \t u j \t s",
986  true,
987  0,
988  ['a b', 'c', 'd', 'e', 'u j', 's'],
989  ],
990  'works with whitespace separator remove empty and limit' => [
991  "\t",
992  " a b \t c \t \t d \t e \t u j \t s",
993  true,
994  3,
995  ['a b', 'c', "d \t e \t u j \t s"],
996  ],
997  ];
998  }
999 
1001  // Tests concerning getBytesFromSizeMeasurement
1003 
1008  public static function getBytesFromSizeMeasurementDataProvider(): array
1009  {
1010  return [
1011  '100 kilo Bytes' => ['102400', '100k'],
1012  '100 mega Bytes' => ['104857600', '100m'],
1013  '100 giga Bytes' => ['107374182400', '100g'],
1014  ];
1015  }
1016 
1017  #[DataProvider('getBytesFromSizeMeasurementDataProvider')]
1018  #[Test]
1019  public function getBytesFromSizeMeasurementCalculatesCorrectByteValue($expected, $byteString): void
1020  {
1021  self::assertEquals($expected, GeneralUtility::getBytesFromSizeMeasurement($byteString));
1022  }
1023 
1025  // Tests concerning getIndpEnv
1027  #[Test]
1028  public function getIndpEnvTypo3SitePathReturnNonEmptyString(): void
1029  {
1030  self::assertTrue(strlen(GeneralUtility::getIndpEnv('TYPO3_SITE_PATH')) >= 1);
1031  }
1032 
1033  #[Test]
1034  public function getIndpEnvTypo3SitePathReturnsStringEndingWithSlash(): void
1035  {
1036  $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1037  self::assertEquals('/', $result[strlen($result) - 1]);
1038  }
1039 
1040  public static function hostnameAndPortDataProvider(): array
1041  {
1042  return [
1043  'localhost ipv4 without port' => ['127.0.0.1', '127.0.0.1', ''],
1044  'localhost ipv4 with port' => ['127.0.0.1:81', '127.0.0.1', '81'],
1045  'localhost ipv6 without port' => ['[::1]', '[::1]', ''],
1046  'localhost ipv6 with port' => ['[::1]:81', '[::1]', '81'],
1047  'ipv6 without port' => ['[2001:DB8::1]', '[2001:DB8::1]', ''],
1048  'ipv6 with port' => ['[2001:DB8::1]:81', '[2001:DB8::1]', '81'],
1049  'hostname without port' => ['lolli.did.this', 'lolli.did.this', ''],
1050  'hostname with port' => ['lolli.did.this:42', 'lolli.did.this', '42'],
1051  ];
1052  }
1053 
1054  #[DataProvider('hostnameAndPortDataProvider')]
1055  #[Test]
1056  public function getIndpEnvTypo3HostOnlyParsesHostnamesAndIpAddresses($httpHost, $expectedIp): void
1057  {
1058  ‪$_SERVER['HTTP_HOST'] = $httpHost;
1059  self::assertEquals($expectedIp, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
1060  }
1061 
1062  #[DataProvider('hostnameAndPortDataProvider')]
1063  #[Test]
1064  public function getIndpEnvTypo3PortParsesHostnamesAndIpAddresses($httpHost, $dummy, $expectedPort): void
1065  {
1066  ‪$_SERVER['HTTP_HOST'] = $httpHost;
1067  self::assertEquals($expectedPort, GeneralUtility::getIndpEnv('TYPO3_PORT'));
1068  }
1069 
1071  // Tests concerning underscoredToUpperCamelCase
1073 
1078  public static function underscoredToUpperCamelCaseDataProvider(): array
1079  {
1080  return [
1081  'single word' => ['Blogexample', 'blogexample'],
1082  'multiple words' => ['BlogExample', 'blog_example'],
1083  ];
1084  }
1085 
1086  #[DataProvider('underscoredToUpperCamelCaseDataProvider')]
1087  #[Test]
1088  public function underscoredToUpperCamelCase($expected, $inputString): void
1089  {
1090  self::assertEquals($expected, ‪GeneralUtility::underscoredToUpperCamelCase($inputString));
1091  }
1092 
1094  // Tests concerning underscoredToLowerCamelCase
1096 
1101  public static function underscoredToLowerCamelCaseDataProvider(): array
1102  {
1103  return [
1104  'single word' => ['minimalvalue', 'minimalvalue'],
1105  'multiple words' => ['minimalValue', 'minimal_value'],
1106  ];
1107  }
1108 
1109  #[DataProvider('underscoredToLowerCamelCaseDataProvider')]
1110  #[Test]
1111  public function underscoredToLowerCamelCase($expected, $inputString): void
1112  {
1113  self::assertEquals($expected, ‪GeneralUtility::underscoredToLowerCamelCase($inputString));
1114  }
1115 
1117  // Tests concerning camelCaseToLowerCaseUnderscored
1119 
1124  public static function camelCaseToLowerCaseUnderscoredDataProvider(): array
1125  {
1126  return [
1127  'single word' => ['blogexample', 'blogexample'],
1128  'single word starting upper case' => ['blogexample', 'Blogexample'],
1129  'two words starting lower case' => ['minimal_value', 'minimalValue'],
1130  'two words starting upper case' => ['blog_example', 'BlogExample'],
1131  ];
1132  }
1133 
1134  #[DataProvider('camelCaseToLowerCaseUnderscoredDataProvider')]
1135  #[Test]
1136  public function camelCaseToLowerCaseUnderscored($expected, $inputString): void
1137  {
1138  self::assertEquals($expected, ‪GeneralUtility::camelCaseToLowerCaseUnderscored($inputString));
1139  }
1140 
1142  // Tests concerning isValidUrl
1144 
1149  public static function validUrlValidResourceDataProvider(): array
1150  {
1151  return [
1152  'http' => ['http://www.example.org/'],
1153  'http without trailing slash' => ['http://qwe'],
1154  'http directory with trailing slash' => ['http://www.example/img/dir/'],
1155  'http directory without trailing slash' => ['http://www.example/img/dir'],
1156  'http index.html' => ['http://example.com/index.html'],
1157  'http index.php' => ['http://www.example.com/index.php'],
1158  'http test.png' => ['http://www.example/img/test.png'],
1159  'http username password querystring and anchor' => ['https://user:pw@www.example.org:80/path?arg=value#fragment'],
1160  'file' => ['file:///tmp/test.c'],
1161  'file directory' => ['file://foo/bar'],
1162  'ftp directory' => ['ftp://ftp.example.com/tmp/'],
1163  'mailto' => ['mailto:foo@bar.com'],
1164  'news' => ['news:news.php.net'],
1165  'telnet' => ['telnet://192.0.2.16:80/'],
1166  'ldap' => ['ldap://[2001:db8::7]/c=GB?objectClass?one'],
1167  'http punycode domain name' => ['http://www.xn--bb-eka.at'],
1168  'http punicode subdomain' => ['http://xn--h-zfa.oebb.at'],
1169  'http domain-name umlauts' => ['http://www.öbb.at'],
1170  'http subdomain umlauts' => ['http://äh.oebb.at'],
1171  ];
1172  }
1173 
1174  #[DataProvider('validUrlValidResourceDataProvider')]
1175  #[Test]
1176  public function validURLReturnsTrueForValidResource(string ‪$url): void
1177  {
1178  self::assertTrue(‪GeneralUtility::isValidUrl(‪$url));
1179  }
1180 
1186  public static function isValidUrlInvalidResourceDataProvider(): array
1187  {
1188  return [
1189  'http missing colon' => ['http//www.example/wrong/url/'],
1190  'http missing slash' => ['http:/www.example'],
1191  'hostname only' => ['www.example.org/'],
1192  'file missing protocol specification' => ['/tmp/test.c'],
1193  'slash only' => ['/'],
1194  'string http://' => ['http://'],
1195  'string http:/' => ['http:/'],
1196  'string http:' => ['http:'],
1197  'string http' => ['http'],
1198  'empty string' => [''],
1199  'string -1' => ['-1'],
1200  'string array()' => ['array()'],
1201  'random string' => ['qwe'],
1202  'http directory umlauts' => ['http://www.oebb.at/äöü/'],
1203  'prohibited input characters' => ['https://{$unresolved_constant}'],
1204  ];
1205  }
1206 
1207  #[DataProvider('isValidUrlInvalidResourceDataProvider')]
1208  #[Test]
1209  public function validURLReturnsFalseForInvalidResource(string ‪$url): void
1210  {
1211  self::assertFalse(‪GeneralUtility::isValidUrl(‪$url));
1212  }
1213 
1215  // Tests concerning isOnCurrentHost
1217  #[Test]
1218  public function isOnCurrentHostReturnsTrueWithCurrentHost(): void
1219  {
1220  $testUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
1221  self::assertTrue(‪GeneralUtility::isOnCurrentHost($testUrl));
1222  }
1223 
1229  public static function checkisOnCurrentHostInvalidHostsDataProvider(): array
1230  {
1231  return [
1232  'empty string' => [''],
1233  'arbitrary string' => ['arbitrary string'],
1234  'localhost IP' => ['127.0.0.1'],
1235  'relative path' => ['./relpath/file.txt'],
1236  'absolute path' => ['/abspath/file.txt?arg=value'],
1237  'different host' => [GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . '.example.org'],
1238  ];
1239  }
1240 
1241  #[DataProvider('checkisOnCurrentHostInvalidHostsDataProvider')]
1242  #[Test]
1243  public function isOnCurrentHostWithNotCurrentHostReturnsFalse(string $hostCandidate): void
1244  {
1245  self::assertFalse(‪GeneralUtility::isOnCurrentHost($hostCandidate));
1246  }
1247 
1249  // Tests concerning sanitizeLocalUrl
1251 
1256  public static function sanitizeLocalUrlValidPathsDataProvider(): array
1257  {
1258  return [
1259  'alt_intro.php' => ['alt_intro.php'],
1260  'alt_intro.php?foo=1&bar=2' => ['alt_intro.php?foo=1&bar=2'],
1261  '../index.php' => ['../index.php'],
1262  '../typo3/alt_intro.php' => ['../typo3/alt_intro.php'],
1263  '../~userDirectory/index.php' => ['../~userDirectory/index.php'],
1264  '../typo3/index.php?var1=test-case&var2=~user' => ['../typo3/index.php?var1=test-case&var2=~user'],
1265  ‪Environment::getPublicPath() . '/typo3/alt_intro.php' => [‪Environment::getPublicPath() . '/typo3/alt_intro.php'],
1266  ];
1267  }
1268 
1269  #[DataProvider('sanitizeLocalUrlValidPathsDataProvider')]
1270  #[Test]
1271  public function sanitizeLocalUrlAcceptsNotEncodedValidPaths(string $path): void
1272  {
1275  true,
1276  false,
1281  // needs to be a subpath in order to validate ".." references
1282  ‪Environment::getPublicPath() . '/subdir/index.php',
1283  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1284  );
1285  ‪$_SERVER['HTTP_HOST'] = 'localhost';
1286  ‪$_SERVER['SCRIPT_NAME'] = '/subdir/index.php';
1287  self::assertEquals($path, GeneralUtility::sanitizeLocalUrl($path));
1288  }
1289 
1290  #[DataProvider('sanitizeLocalUrlValidPathsDataProvider')]
1291  #[Test]
1292  public function sanitizeLocalUrlAcceptsEncodedValidPaths(string $path): void
1293  {
1296  true,
1297  false,
1302  // needs to be a subpath in order to validate ".." references
1303  ‪Environment::getPublicPath() . '/subdir/index.php',
1304  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1305  );
1306  ‪$_SERVER['HTTP_HOST'] = 'localhost';
1307  ‪$_SERVER['SCRIPT_NAME'] = '/subdir/index.php';
1308  self::assertEquals(rawurlencode($path), GeneralUtility::sanitizeLocalUrl(rawurlencode($path)));
1309  }
1310 
1316  public static function sanitizeLocalUrlValidUrlsDataProvider(): array
1317  {
1318  return [
1319  '/cms/typo3/alt_intro.php' => [
1320  '/cms/typo3/alt_intro.php',
1321  'localhost',
1322  '/cms/',
1323  ],
1324  '/cms/index.php' => [
1325  '/cms/index.php',
1326  'localhost',
1327  '/cms/',
1328  ],
1329  'http://localhost/typo3/alt_intro.php' => [
1330  'http://localhost/typo3/alt_intro.php',
1331  'localhost',
1332  '',
1333  ],
1334  'http://localhost/cms/typo3/alt_intro.php' => [
1335  'http://localhost/cms/typo3/alt_intro.php',
1336  'localhost',
1337  '/cms/',
1338  ],
1339  ];
1340  }
1341 
1342  #[DataProvider('sanitizeLocalUrlValidUrlsDataProvider')]
1343  #[Test]
1344  public function sanitizeLocalUrlAcceptsNotEncodedValidUrls(string ‪$url, string $host, string $subDirectory): void
1345  {
1348  true,
1349  false,
1354  ‪Environment::getPublicPath() . '/index.php',
1355  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1356  );
1357  ‪$_SERVER['HTTP_HOST'] = $host;
1358  ‪$_SERVER['SCRIPT_NAME'] = $subDirectory . 'index.php';
1359  self::assertEquals(‪$url, GeneralUtility::sanitizeLocalUrl(‪$url));
1360  }
1361 
1362  #[DataProvider('sanitizeLocalUrlValidUrlsDataProvider')]
1363  #[Test]
1364  public function sanitizeLocalUrlAcceptsEncodedValidUrls(string ‪$url, string $host, string $subDirectory): void
1365  {
1368  true,
1369  false,
1374  ‪Environment::getPublicPath() . '/index.php',
1375  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1376  );
1377  ‪$_SERVER['HTTP_HOST'] = $host;
1378  ‪$_SERVER['SCRIPT_NAME'] = $subDirectory . 'index.php';
1379  self::assertEquals(rawurlencode(‪$url), GeneralUtility::sanitizeLocalUrl(rawurlencode(‪$url)));
1380  }
1381 
1387  public static function sanitizeLocalUrlInvalidDataProvider(): array
1388  {
1389  return [
1390  'empty string' => [''],
1391  'http domain' => ['http://www.google.de/'],
1392  'https domain' => ['https://www.google.de/'],
1393  'domain without schema' => ['//www.google.de/'],
1394  'XSS attempt' => ['" onmouseover="alert(123)"'],
1395  'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
1396  'invalid URL, HTML break out attempt' => ['" >blabuubb'],
1397  'base64 encoded string' => ['data:%20text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4='],
1398  ];
1399  }
1400 
1401  #[DataProvider('sanitizeLocalUrlInvalidDataProvider')]
1402  #[Test]
1403  public function sanitizeLocalUrlDeniesPlainInvalidUrlsInBackendContext(string ‪$url): void
1404  {
1407  true,
1408  false,
1413  ‪Environment::getPublicPath() . '/typo3/index.php',
1414  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1415  );
1416  ‪$_SERVER['HTTP_HOST'] = 'localhost';
1417  ‪$_SERVER['SCRIPT_NAME'] = 'typo3/index.php';
1418  self::assertEquals('', GeneralUtility::sanitizeLocalUrl(‪$url));
1419  }
1420 
1421  #[DataProvider('sanitizeLocalUrlInvalidDataProvider')]
1422  #[Test]
1423  public function sanitizeLocalUrlDeniesPlainInvalidUrlsInFrontendContext(string ‪$url): void
1424  {
1427  true,
1428  false,
1433  ‪Environment::getPublicPath() . '/index.php',
1434  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1435  );
1436  ‪$_SERVER['HTTP_HOST'] = 'localhost';
1437  ‪$_SERVER['SCRIPT_NAME'] = '/index.php';
1438  self::assertEquals('', GeneralUtility::sanitizeLocalUrl(‪$url));
1439  }
1440 
1441  #[DataProvider('sanitizeLocalUrlInvalidDataProvider')]
1442  #[Test]
1443  public function sanitizeLocalUrlDeniesEncodedInvalidUrls(string ‪$url): void
1444  {
1445  self::assertEquals('', GeneralUtility::sanitizeLocalUrl(rawurlencode(‪$url)));
1446  }
1447 
1449  // Tests concerning unlink_tempfile
1451  #[Test]
1452  public function unlink_tempfileRemovesValidFileInTypo3temp(): void
1453  {
1454  $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
1455  $testFilename = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('test_') . '.gif';
1456  @copy($fixtureFile, $testFilename);
1457  GeneralUtility::unlink_tempfile($testFilename);
1458  $fileExists = file_exists($testFilename);
1459  self::assertFalse($fileExists);
1460  }
1461 
1462  #[Test]
1463  public function unlink_tempfileRemovesHiddenFile(): void
1464  {
1465  $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
1466  $testFilename = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('.test_') . '.gif';
1467  @copy($fixtureFile, $testFilename);
1468  GeneralUtility::unlink_tempfile($testFilename);
1469  $fileExists = file_exists($testFilename);
1470  self::assertFalse($fileExists);
1471  }
1472 
1473  #[Test]
1474  public function unlink_tempfileReturnsTrueIfFileWasRemoved(): void
1475  {
1476  $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
1477  $testFilename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_') . '.gif';
1478  @copy($fixtureFile, $testFilename);
1479  $returnValue = GeneralUtility::unlink_tempfile($testFilename);
1480  self::assertTrue($returnValue);
1481  }
1482 
1483  #[Test]
1484  public function unlink_tempfileReturnsNullIfFileDoesNotExist(): void
1485  {
1486  $returnValue = GeneralUtility::unlink_tempfile(‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('i_do_not_exist'));
1487  self::assertNull($returnValue);
1488  }
1489 
1490  #[Test]
1491  public function unlink_tempfileReturnsNullIfFileIsNowWithinTypo3temp(): void
1492  {
1493  $returnValue = GeneralUtility::unlink_tempfile('/tmp/typo3-unit-test-unlink_tempfile');
1494  self::assertNull($returnValue);
1495  }
1496 
1498  // Tests concerning tempnam
1500  #[Test]
1501  public function tempnamReturnsPathStartingWithGivenPrefix(): void
1502  {
1503  $filePath = GeneralUtility::tempnam('foo');
1504  $this->testFilesToDelete[] = $filePath;
1505  $fileName = basename($filePath);
1506  self::assertStringStartsWith('foo', $fileName);
1507  }
1508 
1509  #[Test]
1510  public function tempnamReturnsPathWithoutBackslashes(): void
1511  {
1512  $filePath = GeneralUtility::tempnam('foo');
1513  $this->testFilesToDelete[] = $filePath;
1514  self::assertStringNotContainsString('\\', $filePath);
1515  }
1516 
1517  #[Test]
1518  public function tempnamReturnsAbsolutePathInVarPath(): void
1519  {
1520  $filePath = GeneralUtility::tempnam('foo');
1521  $this->testFilesToDelete[] = $filePath;
1522  self::assertStringStartsWith(‪Environment::getVarPath() . '/transient/', $filePath);
1523  }
1524 
1526  // Tests concerning removeDotsFromTS
1528  #[Test]
1529  public function removeDotsFromTypoScriptSucceedsWithDottedArray(): void
1530  {
1531  $typoScript = [
1532  'propertyA.' => [
1533  'keyA.' => [
1534  'valueA' => 1,
1535  ],
1536  'keyB' => 2,
1537  ],
1538  'propertyB' => 3,
1539  ];
1540  $expectedResult = [
1541  'propertyA' => [
1542  'keyA' => [
1543  'valueA' => 1,
1544  ],
1545  'keyB' => 2,
1546  ],
1547  'propertyB' => 3,
1548  ];
1549  self::assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
1550  }
1551 
1553  // Tests concerning implodeAttributes
1555 
1556  public static function implodeAttributesDataProvider(): \Iterator
1557  {
1558  yield 'Generic input without xhtml' => [
1559  ['hREf' => 'https://example.com', 'title' => 'above'],
1560  false,
1561  true,
1562  'hREf="https://example.com" title="above"',
1563  ];
1564  yield 'Generic input' => [
1565  ['hREf' => 'https://example.com', 'title' => 'above'],
1566  true,
1567  true,
1568  'href="https://example.com" title="above"',
1569  ];
1570  yield 'Generic input keeping empty values' => [
1571  ['hREf' => 'https://example.com', 'title' => ''],
1572  true,
1573  true, // keep empty values
1574  'href="https://example.com" title=""',
1575  ];
1576  yield 'Generic input removing empty values' => [
1577  ['hREf' => 'https://example.com', 'title' => '', 'nomodule' => null],
1578  true,
1579  false, // do not keep empty values
1580  'href="https://example.com"',
1581  ];
1582  }
1583 
1584  #[DataProvider('implodeAttributesDataProvider')]
1585  #[Test]
1586  public function implodeAttributesEscapesProperly(array $input, bool $xhtmlSafe, bool $keepEmptyValues, string $expected): void
1587  {
1588  self::assertSame($expected, GeneralUtility::implodeAttributes($input, $xhtmlSafe, $keepEmptyValues));
1589  }
1590 
1591  #[Test]
1592  public function removeDotsFromTypoScriptOverridesSubArray(): void
1593  {
1594  $typoScript = [
1595  'propertyA.' => [
1596  'keyA' => 'getsOverridden',
1597  'keyA.' => [
1598  'valueA' => 1,
1599  ],
1600  'keyB' => 2,
1601  ],
1602  'propertyB' => 3,
1603  ];
1604  $expectedResult = [
1605  'propertyA' => [
1606  'keyA' => [
1607  'valueA' => 1,
1608  ],
1609  'keyB' => 2,
1610  ],
1611  'propertyB' => 3,
1612  ];
1613  self::assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
1614  }
1615 
1616  #[Test]
1617  public function removeDotsFromTypoScriptOverridesWithScalar(): void
1618  {
1619  $typoScript = [
1620  'propertyA.' => [
1621  'keyA.' => [
1622  'valueA' => 1,
1623  ],
1624  'keyA' => 'willOverride',
1625  'keyB' => 2,
1626  ],
1627  'propertyB' => 3,
1628  ];
1629  $expectedResult = [
1630  'propertyA' => [
1631  'keyA' => 'willOverride',
1632  'keyB' => 2,
1633  ],
1634  'propertyB' => 3,
1635  ];
1636  self::assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
1637  }
1638 
1640  // Tests concerning get_dirs
1642  #[Test]
1643  public function getDirsReturnsArrayOfDirectoriesFromGivenDirectory(): void
1644  {
1646  self::assertIsArray($directories);
1647  }
1648 
1649  #[Test]
1650  public function getDirsReturnsStringErrorOnPathFailure(): void
1651  {
1652  $path = 'foo';
1653  $result = ‪GeneralUtility::get_dirs($path);
1654  $expectedResult = 'error';
1655  self::assertEquals($expectedResult, $result);
1656  }
1657 
1659  // Tests concerning quoteJSvalue
1661 
1664  public static function quoteJsValueDataProvider(): array
1665  {
1666  return [
1667  'Immune characters are returned as is' => [
1668  '._,',
1669  '._,',
1670  ],
1671  'Alphanumerical characters are returned as is' => [
1672  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
1673  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
1674  ],
1675  'Angle brackets and ampersand are encoded' => [
1676  '<>&',
1677  '\\u003C\\u003E\\u0026',
1678  ],
1679  'Quotes and backslashes are encoded' => [
1680  '"\'\\',
1681  '\\u0022\\u0027\\u005C',
1682  ],
1683  'Forward slashes are escaped' => [
1684  '</script>',
1685  '\\u003C\\/script\\u003E',
1686  ],
1687  'Empty string stays empty' => [
1688  '',
1689  '',
1690  ],
1691  'Exclamation mark and space are properly encoded' => [
1692  'Hello World!',
1693  'Hello\\u0020World\\u0021',
1694  ],
1695  'Whitespaces are properly encoded' => [
1696  "\t" . LF . CR . ' ',
1697  '\\u0009\\u000A\\u000D\\u0020',
1698  ],
1699  'Null byte is properly encoded' => [
1700  "\0",
1701  '\\u0000',
1702  ],
1703  'Umlauts are properly encoded' => [
1704  'ÜüÖöÄä',
1705  '\\u00dc\\u00fc\\u00d6\\u00f6\\u00c4\\u00e4',
1706  ],
1707  ];
1708  }
1709 
1710  #[DataProvider('quoteJsValueDataProvider')]
1711  #[Test]
1712  public function quoteJsValueTest(string $input, string $expected): void
1713  {
1714  self::assertSame('\'' . $expected . '\'', GeneralUtility::quoteJSvalue($input));
1715  }
1716 
1717  public static function jsonEncodeForHtmlAttributeTestDataProvider(): array
1718  {
1719  return [
1720  [
1721  ['html' => '<tag attr="\\Vendor\\Package">value</tag>'],
1722  true,
1723  // cave: `\\\\` (four) actually required for PHP only, will be `\\` (two) in HTML
1724  '{&quot;html&quot;:&quot;\u003Ctag attr=\u0022\\\\Vendor\\\\Package\u0022\u003Evalue\u003C\/tag\u003E&quot;}',
1725  ],
1726  [
1727  ['html' => '<tag attr="\\Vendor\\Package">value</tag>'],
1728  false,
1729  // cave: `\\\\` (four) actually required for PHP only, will be `\\` (two) in HTML
1730  '{"html":"\u003Ctag attr=\u0022\\\\Vendor\\\\Package\u0022\u003Evalue\u003C\/tag\u003E"}',
1731  ],
1732  [
1733  ['spaces' => '|' . chr(9) . '|' . chr(10) . '|' . chr(13) . '|'],
1734  false,
1735  '{"spaces":"|\t|\n|\r|"}',
1736  ],
1737  ];
1738  }
1739 
1740  #[DataProvider('jsonEncodeForHtmlAttributeTestDataProvider')]
1741  #[Test]
1742  public function jsonEncodeForHtmlAttributeTest($value, bool $useHtmlEntities, string $expectation): void
1743  {
1744  self::assertSame($expectation, GeneralUtility::jsonEncodeForHtmlAttribute($value, $useHtmlEntities));
1745  }
1746 
1747  public static function jsonEncodeForJavaScriptTestDataProvider(): array
1748  {
1749  return [
1750  [
1751  ['html' => '<tag attr="\\Vendor\\Package">value</tag>'],
1752  // cave: `\\\\` (four) actually required for PHP only, will be `\\` (two) in JavaScript
1753  '{"html":"\u003Ctag attr=\u0022\\\\u005CVendor\\\\u005CPackage\u0022\u003Evalue\u003C\/tag\u003E"}',
1754  ],
1755  [
1756  ['spaces' => '|' . chr(9) . '|' . chr(10) . '|' . chr(13) . '|'],
1757  '{"spaces":"|\u0009|\u000A|\u000D|"}',
1758  ],
1759  ];
1760  }
1761 
1762  #[DataProvider('jsonEncodeForJavaScriptTestDataProvider')]
1763  #[Test]
1764  public function jsonEncodeForJavaScriptTest($value, string $expectation): void
1765  {
1766  self::assertSame($expectation, GeneralUtility::jsonEncodeForJavaScript($value));
1767  }
1768 
1769  public static function sanitizeCssVariableValueDataProvider(): \Generator
1770  {
1771  yield 'double quotes' => ['url("/my-background.png")', 'url("/my-background.png")'];
1772  yield 'single quotes' => ["url('/my-background.png')", "url('/my-background.png')"];
1773  yield 'newline chars' => ["url('/my-background.png'\r\n\r\n)", "url('/my-background.png')"];
1774  yield 'HTML markup' => ['url(</style>)', 'url(&lt;/style&gt;)'];
1775  }
1776 
1777  #[DataProvider('sanitizeCssVariableValueDataProvider')]
1778  #[Test]
1779  public function sanitizeCssVariableValue(string $value, string $expectation): void
1780  {
1781  self::assertSame($expectation, GeneralUtility::sanitizeCssVariableValue($value));
1782  }
1783 
1784  #[Test]
1785  public function fixPermissionsSetsPermissionsToFile(): void
1786  {
1787  if (‪Environment::isWindows()) {
1788  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1789  }
1790  // Create and prepare test file
1791  $filename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1793  chmod($filename, 482);
1794  // Set target permissions and run method
1795  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1796  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1797  clearstatcache();
1798  self::assertTrue($fixPermissionsResult);
1799  self::assertEquals('0660', substr(decoct(fileperms($filename)), 2));
1800  }
1801 
1802  #[Test]
1803  public function fixPermissionsSetsPermissionsToHiddenFile(): void
1804  {
1805  if (‪Environment::isWindows()) {
1806  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1807  }
1808  // Create and prepare test file
1809  $filename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1811  chmod($filename, 482);
1812  // Set target permissions and run method
1813  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1814  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1815  clearstatcache();
1816  self::assertTrue($fixPermissionsResult);
1817  self::assertEquals('0660', substr(decoct(fileperms($filename)), 2));
1818  }
1819 
1820  #[Test]
1821  public function fixPermissionsSetsPermissionsToDirectory(): void
1822  {
1823  if (‪Environment::isWindows()) {
1824  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1825  }
1826  // Create and prepare test directory
1827  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1828  ‪GeneralUtility::mkdir($directory);
1829  chmod($directory, 1551);
1830  // Set target permissions and run method
1831  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1832  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory);
1833  clearstatcache();
1834  self::assertTrue($fixPermissionsResult);
1835  self::assertEquals('0770', substr(decoct(fileperms($directory)), 1));
1836  }
1837 
1838  #[Test]
1839  public function fixPermissionsSetsPermissionsToDirectoryWithTrailingSlash(): void
1840  {
1841  if (‪Environment::isWindows()) {
1842  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1843  }
1844  // Create and prepare test directory
1845  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1846  ‪GeneralUtility::mkdir($directory);
1847  chmod($directory, 1551);
1848  // Set target permissions and run method
1849  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1850  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory . '/');
1851  // Get actual permissions and clean up
1852  clearstatcache();
1853  self::assertTrue($fixPermissionsResult);
1854  self::assertEquals('0770', substr(decoct(fileperms($directory)), 1));
1855  }
1856 
1857  #[Test]
1858  public function fixPermissionsSetsPermissionsToHiddenDirectory(): void
1859  {
1860  if (‪Environment::isWindows()) {
1861  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1862  }
1863  // Create and prepare test directory
1864  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1865  ‪GeneralUtility::mkdir($directory);
1866  chmod($directory, 1551);
1867  // Set target permissions and run method
1868  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1869  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory);
1870  // Get actual permissions and clean up
1871  clearstatcache();
1872  self::assertTrue($fixPermissionsResult);
1873  self::assertEquals('0770', substr(decoct(fileperms($directory)), 1));
1874  }
1875 
1876  #[Test]
1877  public function fixPermissionsCorrectlySetsPermissionsRecursive(): void
1878  {
1879  if (‪Environment::isWindows()) {
1880  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1881  }
1882  // Create and prepare test directory and file structure
1885  chmod(‪$baseDirectory, 1751);
1887  chmod(‪$baseDirectory . '/file', 482);
1889  chmod(‪$baseDirectory . '/foo', 1751);
1891  chmod(‪$baseDirectory . '/foo/file', 482);
1893  chmod(‪$baseDirectory . '/.bar', 1751);
1894  // Use this if writeFileToTypo3tempDir is fixed to create hidden files in subdirectories
1895  // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/.file', '42');
1896  // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/..file2', '42');
1897  touch(‪$baseDirectory . '/.bar/.file', 42);
1898  chmod(‪$baseDirectory . '/.bar/.file', 482);
1899  touch(‪$baseDirectory . '/.bar/..file2', 42);
1900  chmod(‪$baseDirectory . '/.bar/..file2', 482);
1901  // Set target permissions and run method
1902  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1903  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1904  $fixPermissionsResult = ‪GeneralUtility::fixPermissions(‪$baseDirectory, true);
1905  // Get actual permissions
1906  clearstatcache();
1907  $resultBaseDirectoryPermissions = substr(decoct(fileperms(‪$baseDirectory)), 1);
1908  $resultBaseFilePermissions = substr(decoct(fileperms(‪$baseDirectory . '/file')), 2);
1909  $resultFooDirectoryPermissions = substr(decoct(fileperms(‪$baseDirectory . '/foo')), 1);
1910  $resultFooFilePermissions = substr(decoct(fileperms(‪$baseDirectory . '/foo/file')), 2);
1911  $resultBarDirectoryPermissions = substr(decoct(fileperms(‪$baseDirectory . '/.bar')), 1);
1912  $resultBarFilePermissions = substr(decoct(fileperms(‪$baseDirectory . '/.bar/.file')), 2);
1913  $resultBarFile2Permissions = substr(decoct(fileperms(‪$baseDirectory . '/.bar/..file2')), 2);
1914  // Test if everything was ok
1915  self::assertTrue($fixPermissionsResult);
1916  self::assertEquals('0770', $resultBaseDirectoryPermissions);
1917  self::assertEquals('0660', $resultBaseFilePermissions);
1918  self::assertEquals('0770', $resultFooDirectoryPermissions);
1919  self::assertEquals('0660', $resultFooFilePermissions);
1920  self::assertEquals('0770', $resultBarDirectoryPermissions);
1921  self::assertEquals('0660', $resultBarFilePermissions);
1922  self::assertEquals('0660', $resultBarFile2Permissions);
1923  }
1924 
1925  #[Test]
1926  public function fixPermissionsDoesNotSetPermissionsToNotAllowedPath(): void
1927  {
1928  if (‪Environment::isWindows()) {
1929  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1930  }
1931  // Create and prepare test file
1932  $filename = ‪Environment::getVarPath() . '/tests/../../../typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_');
1933  // Set target permissions and run method
1934  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1935  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1936  self::assertFalse($fixPermissionsResult);
1937  }
1938 
1939  #[Test]
1940  public function fixPermissionsSetsPermissionsWithRelativeFileReference(): void
1941  {
1942  if (‪Environment::isWindows()) {
1943  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1944  }
1945  $filename = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_');
1947  $this->testFilesToDelete[] = ‪Environment::getPublicPath() . '/' . $filename;
1948  chmod(‪Environment::getPublicPath() . '/' . $filename, 482);
1949  // Set target permissions and run method
1950  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1951  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1952  clearstatcache();
1953  self::assertTrue($fixPermissionsResult);
1954  self::assertEquals('0660', substr(decoct(fileperms(‪Environment::getPublicPath() . '/' . $filename)), 2));
1955  }
1956 
1957  #[Test]
1958  public function fixPermissionsSetsDefaultPermissionsToFile(): void
1959  {
1960  if (‪Environment::isWindows()) {
1961  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1962  }
1963  $filename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1965  chmod($filename, 482);
1966  unset(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']);
1967  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1968  clearstatcache();
1969  self::assertTrue($fixPermissionsResult);
1970  self::assertEquals('0644', substr(decoct(fileperms($filename)), 2));
1971  }
1972 
1973  #[Test]
1974  public function fixPermissionsSetsDefaultPermissionsToDirectory(): void
1975  {
1976  if (‪Environment::isWindows()) {
1977  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1978  }
1979  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1980  ‪GeneralUtility::mkdir($directory);
1981  chmod($directory, 1551);
1982  unset(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
1983  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory);
1984  clearstatcache();
1985  self::assertTrue($fixPermissionsResult);
1986  self::assertEquals('0755', substr(decoct(fileperms($directory)), 1));
1987  }
1988 
1990  // Tests concerning mkdir
1992  #[Test]
1993  public function mkdirCreatesDirectory(): void
1994  {
1995  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1996  $mkdirResult = ‪GeneralUtility::mkdir($directory);
1997  clearstatcache();
1998  self::assertTrue($mkdirResult);
1999  self::assertDirectoryExists($directory);
2000  }
2001 
2002  #[Test]
2003  public function mkdirCreatesHiddenDirectory(): void
2004  {
2005  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('.test_');
2006  $mkdirResult = ‪GeneralUtility::mkdir($directory);
2007  clearstatcache();
2008  self::assertTrue($mkdirResult);
2009  self::assertDirectoryExists($directory);
2010  }
2011 
2012  #[Test]
2013  public function mkdirCreatesDirectoryWithTrailingSlash(): void
2014  {
2015  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_') . '/';
2016  $mkdirResult = ‪GeneralUtility::mkdir($directory);
2017  clearstatcache();
2018  self::assertTrue($mkdirResult);
2019  self::assertDirectoryExists($directory);
2020  }
2021 
2022  #[Test]
2023  public function mkdirSetsPermissionsOfCreatedDirectory(): void
2024  {
2025  if (‪Environment::isWindows()) {
2026  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2027  }
2028  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
2029  $oldUmask = umask(19);
2030  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0772';
2031  ‪GeneralUtility::mkdir($directory);
2032  clearstatcache();
2033  $resultDirectoryPermissions = substr(decoct(fileperms($directory)), 1);
2034  umask($oldUmask);
2035  self::assertEquals('0772', $resultDirectoryPermissions);
2036  }
2037 
2039  // Tests concerning writeFileToTypo3tempDir()
2041 
2045  public static function invalidFilePathForTypo3tempDirDataProvider(): array
2046  {
2047  return [
2048  [
2049  ‪Environment::getPublicPath() . '/../path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more',
2050  'Input filepath "' . ‪Environment::getPublicPath() . '/../path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more" was generally invalid!',
2051  '',
2052  ],
2053  [
2054  ‪Environment::getPublicPath() . '/dummy/path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more',
2055  'Input filepath "' . ‪Environment::getPublicPath() . '/dummy/path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more" was generally invalid!',
2056  '',
2057  ],
2058  [
2059  ‪Environment::getPublicPath() . '/dummy/path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more',
2060  'Input filepath "' . ‪Environment::getPublicPath() . '/dummy/path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more" was generally invalid!',
2061  '',
2062  ],
2063  [
2064  '/dummy/path/awesome',
2065  '"/dummy/path/" was not within directory Environment::getPublicPath() + "/typo3temp/"',
2066  '',
2067  ],
2068  [
2070  '"' . ‪Environment::getLegacyConfigPath() . '/" was not within directory Environment::getPublicPath() + "/typo3temp/"',
2071  '',
2072  ],
2073  [
2074  ‪Environment::getPublicPath() . '/typo3temp/táylor/swíft',
2075  'Subdir, "táylor/", was NOT on the form "[[:alnum:]_]/+"',
2076  '',
2077  ],
2078  'Path instead of file given' => [
2079  ‪Environment::getPublicPath() . '/typo3temp/dummy/path/',
2080  'Calculated file location didn\'t match input "' . ‪Environment::getPublicPath() . '/typo3temp/dummy/path/".',
2081  ‪Environment::getPublicPath() . '/typo3temp/dummy/',
2082  ],
2083  ];
2084  }
2085 
2086  #[DataProvider('invalidFilePathForTypo3tempDirDataProvider')]
2087  #[Test]
2088  public function writeFileToTypo3tempDirFailsWithInvalidPath(string $invalidFilePath, string $expectedResult, string $pathToCleanUp): void
2089  {
2090  if ($pathToCleanUp !== '') {
2091  $this->testFilesToDelete[] = $pathToCleanUp;
2092  }
2093  $result = ‪GeneralUtility::writeFileToTypo3tempDir($invalidFilePath, 'dummy content to be written');
2094  self::assertSame($result, $expectedResult);
2095  }
2096 
2101  public static function validFilePathForTypo3tempDirDataProvider(): array
2102  {
2103  return [
2104  'Default text file' => [
2105  ‪Environment::getVarPath() . '/tests/paranoid/android.txt',
2106  ‪Environment::getVarPath() . '/tests/',
2107  ],
2108  'Html file extension' => [
2109  ‪Environment::getVarPath() . '/tests/karma.html',
2110  ‪Environment::getVarPath() . '/tests/',
2111  ],
2112  'No file extension' => [
2113  ‪Environment::getVarPath() . '/tests/no-surprises',
2114  ‪Environment::getVarPath() . '/tests/',
2115  ],
2116  'Deep directory' => [
2117  ‪Environment::getVarPath() . '/tests/climbing/up/the/walls',
2118  ‪Environment::getVarPath() . '/tests/',
2119  ],
2120  'File in typo3temp/var directory' => [
2121  ‪Environment::getPublicPath() . '/typo3temp/var/path/foo.txt',
2122  ‪Environment::getPublicPath() . '/typo3temp/var/path',
2123  ],
2124  ];
2125  }
2126 
2131  #[DataProvider('validFilePathForTypo3tempDirDataProvider')]
2132  #[Test]
2133  public function writeFileToTypo3tempDirWorksWithValidPath(string $filePath, string $pathToCleanUp): void
2134  {
2135  if ($pathToCleanUp !== '') {
2136  $this->testFilesToDelete[] = $pathToCleanUp;
2137  }
2138 
2139  $dummyContent = 'Please could you stop the noise, I\'m trying to get some rest from all the unborn chicken voices in my head.';
2140 
2141  $result = ‪GeneralUtility::writeFileToTypo3tempDir($filePath, $dummyContent);
2142 
2143  self::assertNull($result);
2144  self::assertFileExists($filePath);
2145  self::assertStringEqualsFile($filePath, $dummyContent);
2146  }
2147 
2149  // Tests concerning mkdir_deep
2151  #[Test]
2152  public function mkdirDeepCreatesDirectory(): void
2153  {
2154  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
2155  ‪GeneralUtility::mkdir_deep($directory);
2156  self::assertDirectoryExists($directory);
2157  }
2158 
2159  #[Test]
2160  public function mkdirDeepCreatesSubdirectoriesRecursive(): void
2161  {
2162  $directory = $this->‪getTestDirectory() . '/typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_');
2163  $subDirectory = $directory . '/foo';
2164  ‪GeneralUtility::mkdir_deep($subDirectory);
2165  self::assertDirectoryExists($subDirectory);
2166  }
2167 
2171  public static function mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider(): array
2172  {
2173  return [
2174  'no double slash if concatenated with Environment::getPublicPath()' => ['fileadmin/testDir1'],
2175  'double slash if concatenated with Environment::getPublicPath()' => ['/fileadmin/testDir2'],
2176  ];
2177  }
2178 
2179  #[DataProvider('mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider')]
2180  #[Test]
2181  public function mkdirDeepCreatesDirectoryWithDoubleSlashes($directoryToCreate): void
2182  {
2183  $testRoot = ‪Environment::getVarPath() . '/public/';
2184  $this->testFilesToDelete[] = $testRoot;
2185  $directory = $testRoot . $directoryToCreate;
2186  ‪GeneralUtility::mkdir_deep($directory);
2187  self::assertDirectoryExists($directory);
2188  }
2189 
2190  #[Test]
2191  public function mkdirDeepFixesPermissionsOfCreatedDirectory(): void
2192  {
2193  if (‪Environment::isWindows()) {
2194  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2195  }
2196  $directory = ‪StringUtility::getUniqueId('mkdirdeeptest_');
2197  $oldUmask = umask(19);
2198  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
2199  ‪GeneralUtility::mkdir_deep(‪Environment::getVarPath() . '/tests/' . $directory);
2200  $this->testFilesToDelete[] = ‪Environment::getVarPath() . '/tests/' . $directory;
2201  clearstatcache();
2202  umask($oldUmask);
2203  self::assertEquals('777', substr(decoct(fileperms(‪Environment::getVarPath() . '/tests/' . $directory)), -3, 3));
2204  }
2205 
2206  #[Test]
2207  public function mkdirDeepFixesPermissionsOnNewParentDirectory(): void
2208  {
2209  if (‪Environment::isWindows()) {
2210  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2211  }
2212  $directory = ‪StringUtility::getUniqueId('mkdirdeeptest_');
2213  $subDirectory = $directory . '/bar';
2214  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
2215  $oldUmask = umask(19);
2216  ‪GeneralUtility::mkdir_deep(‪Environment::getVarPath() . '/tests/' . $subDirectory);
2217  $this->testFilesToDelete[] = ‪Environment::getVarPath() . '/tests/' . $directory;
2218  clearstatcache();
2219  umask($oldUmask);
2220  self::assertEquals('777', substr(decoct(fileperms(‪Environment::getVarPath() . '/tests/' . $directory)), -3, 3));
2221  }
2222 
2223  #[Test]
2224  public function mkdirDeepDoesNotChangePermissionsOfExistingSubDirectories(): void
2225  {
2226  if (‪Environment::isWindows()) {
2227  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2228  }
2230  $existingDirectory = ‪StringUtility::getUniqueId('test_existing_') . '/';
2231  $newSubDirectory = ‪StringUtility::getUniqueId('test_new_');
2232  @mkdir(‪$baseDirectory . $existingDirectory);
2233  $this->testFilesToDelete[] = ‪$baseDirectory . $existingDirectory;
2234  chmod(‪$baseDirectory . $existingDirectory, 482);
2235  ‪GeneralUtility::mkdir_deep(‪$baseDirectory . $existingDirectory . $newSubDirectory);
2236  self::assertEquals(742, (int)substr(decoct(fileperms(‪$baseDirectory . $existingDirectory)), 2));
2237  }
2238 
2239  #[Test]
2240  public function mkdirDeepThrowsExceptionIfDirectoryCreationFails(): void
2241  {
2242  $this->expectException(\RuntimeException::class);
2243  $this->expectExceptionCode(1170251401);
2244 
2245  ‪GeneralUtility::mkdir_deep('http://localhost');
2246  }
2247 
2249  // Tests concerning rmdir
2251  #[Test]
2252  public function rmdirRemovesFile(): void
2253  {
2254  $testRoot = ‪Environment::getVarPath() . '/tests/';
2255  $this->testFilesToDelete[] = $testRoot;
2256  ‪GeneralUtility::mkdir_deep($testRoot);
2257  $file = $testRoot . ‪StringUtility::getUniqueId('file_');
2258  touch($file);
2259  ‪GeneralUtility::rmdir($file);
2260  self::assertFileDoesNotExist($file);
2261  }
2262 
2263  #[Test]
2264  public function rmdirReturnTrueIfFileWasRemoved(): void
2265  {
2266  $file = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('file_');
2267  touch($file);
2268  self::assertTrue(‪GeneralUtility::rmdir($file));
2269  }
2270 
2271  #[Test]
2272  public function rmdirReturnFalseIfNoFileWasRemoved(): void
2273  {
2274  $file = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('file_');
2275  self::assertFalse(‪GeneralUtility::rmdir($file));
2276  }
2277 
2278  #[Test]
2279  public function rmdirRemovesDirectory(): void
2280  {
2281  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_');
2282  mkdir($directory);
2283  ‪GeneralUtility::rmdir($directory);
2284  self::assertFileDoesNotExist($directory);
2285  }
2286 
2287  #[Test]
2288  public function rmdirRemovesDirectoryWithTrailingSlash(): void
2289  {
2290  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_') . '/';
2291  ‪GeneralUtility::mkdir_deep($directory);
2292  ‪GeneralUtility::rmdir($directory);
2293  self::assertFileDoesNotExist($directory);
2294  }
2295 
2296  #[Test]
2297  public function rmdirDoesNotRemoveDirectoryWithFilesAndReturnsFalseIfRecursiveDeletionIsOff(): void
2298  {
2299  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_') . '/';
2300  ‪GeneralUtility::mkdir_deep($directory);
2301  $file = ‪StringUtility::getUniqueId('file_');
2302  touch($directory . $file);
2303  $return = ‪GeneralUtility::rmdir($directory);
2304  self::assertFileExists($directory);
2305  self::assertFileExists($directory . $file);
2306  self::assertFalse($return);
2307  }
2308 
2309  #[Test]
2310  public function rmdirRemovesDirectoriesRecursiveAndReturnsTrue(): void
2311  {
2312  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_') . '/';
2313  mkdir($directory);
2314  mkdir($directory . 'sub/');
2315  touch($directory . 'sub/file');
2316  $return = ‪GeneralUtility::rmdir($directory, true);
2317  self::assertFileDoesNotExist($directory);
2318  self::assertTrue($return);
2319  }
2320 
2321  #[Test]
2322  public function rmdirRemovesLinkToDirectory(): void
2323  {
2324  $existingDirectory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('notExists_') . '/';
2325  mkdir($existingDirectory);
2326  $symlinkName = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('link_');
2327  symlink($existingDirectory, $symlinkName);
2328  ‪GeneralUtility::rmdir($symlinkName, true);
2329  self::assertFalse(is_link($symlinkName));
2330  }
2331 
2332  #[Test]
2333  public function rmdirRemovesDeadLinkToDirectory(): void
2334  {
2335  $notExistingDirectory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('notExists_') . '/';
2336  $symlinkName = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('link_');
2337  ‪GeneralUtility::mkdir_deep($notExistingDirectory);
2338  symlink($notExistingDirectory, $symlinkName);
2339  rmdir($notExistingDirectory);
2340 
2341  ‪GeneralUtility::rmdir($symlinkName, true);
2342  self::assertFalse(is_link($symlinkName));
2343  }
2344 
2345  #[Test]
2346  public function rmdirRemovesDeadLinkToFile(): void
2347  {
2348  $testDirectory = $this->‪getTestDirectory() . '/';
2349  $notExistingFile = $testDirectory . ‪StringUtility::getUniqueId('notExists_');
2350  $symlinkName = $testDirectory . ‪StringUtility::getUniqueId('link_');
2351  touch($notExistingFile);
2352  symlink($notExistingFile, $symlinkName);
2353  unlink($notExistingFile);
2354  ‪GeneralUtility::rmdir($symlinkName, true);
2355  self::assertFalse(is_link($symlinkName));
2356  }
2357 
2359  // Tests concerning getFilesInDir
2361 
2366  protected function getFilesInDirCreateTestDirectory(): string
2367  {
2368  $path = ‪Environment::getVarPath() . '/FilesInDirTests';
2369  $this->testFilesToDelete[] = $path;
2370  mkdir($path);
2371  mkdir($path . '/subDirectory');
2372  file_put_contents($path . '/subDirectory/test.php', 'butter');
2373  file_put_contents($path . '/subDirectory/other.php', 'milk');
2374  file_put_contents($path . '/subDirectory/stuff.csv', 'honey');
2375  mkdir($path . '/beStylesheet');
2376  file_put_contents($path . '/beStylesheet/backend.css', '.topbar-header-site { color: red; }');
2377  file_put_contents($path . '/beStylesheet/backend.scss', '.topbar-header-site { color: green; }');
2378  file_put_contents($path . '/excludeMe.txt', 'cocoa nibs');
2379  file_put_contents($path . '/double.setup.typoscript', 'cool TS');
2380  file_put_contents($path . '/testB.txt', 'olive oil');
2381  file_put_contents($path . '/testA.txt', 'eggs');
2382  file_put_contents($path . '/testC.txt', 'carrots');
2383  file_put_contents($path . '/test.js', 'oranges');
2384  file_put_contents($path . '/test.css', 'apples');
2385  file_put_contents($path . '/.secret.txt', 'sammon');
2386  return $path;
2387  }
2388 
2389  #[Test]
2390  public function getFilesInDirFindsRegularFile(): void
2391  {
2392  $path = $this->getFilesInDirCreateTestDirectory();
2393  $files = GeneralUtility::getFilesInDir($path);
2394  self::assertContains('testA.txt', $files);
2395  }
2396 
2397  #[Test]
2398  public function getFilesInDirFindsHiddenFile(): void
2399  {
2400  $path = $this->getFilesInDirCreateTestDirectory();
2401  $files = GeneralUtility::getFilesInDir($path);
2402  self::assertContains('.secret.txt', $files);
2403  }
2404 
2405  #[Test]
2406  public function getFilesInDirOnlyFindWithMatchingExtension(): void
2407  {
2408  $path = $this->getFilesInDirCreateTestDirectory();
2409  $files = GeneralUtility::getFilesInDir($path . '/beStylesheet', 'css');
2410  self::assertContains('backend.css', $files);
2411  self::assertNotContains('backend.scss', $files);
2412  }
2413 
2417  public static function fileExtensionDataProvider(): array
2418  {
2419  return [
2420  'no space' => [
2421  'setup.typoscript,txt,js,css',
2422  ],
2423  'spaces' => [
2424  'setup.typoscript, txt, js, css',
2425  ],
2426  'mixed' => [
2427  'setup.typoscript , txt,js, css',
2428  ],
2429  'wild' => [
2430  'setup.typoscript, txt, js , css',
2431  ],
2432  ];
2433  }
2434 
2435  #[DataProvider('fileExtensionDataProvider')]
2436  #[Test]
2437  public function getFilesInDirByExtensionFindsFiles($fileExtensions): void
2438  {
2439  $path = $this->getFilesInDirCreateTestDirectory();
2440  $files = GeneralUtility::getFilesInDir($path, $fileExtensions);
2441  self::assertContains('double.setup.typoscript', $files);
2442  self::assertContains('testA.txt', $files);
2443  self::assertContains('test.js', $files);
2444  self::assertContains('test.css', $files);
2445  }
2446 
2447  #[Test]
2448  public function getFilesInDirByExtensionDoesNotFindFilesWithOtherExtensions(): void
2449  {
2450  $path = $this->getFilesInDirCreateTestDirectory();
2451  $files = GeneralUtility::getFilesInDir($path, 'txt,js');
2452  self::assertContains('testA.txt', $files);
2453  self::assertContains('test.js', $files);
2454  self::assertNotContains('test.css', $files);
2455  }
2456 
2457  #[Test]
2458  public function getFilesInDirExcludesFilesMatchingPattern(): void
2459  {
2460  $path = $this->getFilesInDirCreateTestDirectory();
2461  $files = GeneralUtility::getFilesInDir($path, '', false, '', 'excludeMe.*');
2462  self::assertContains('test.js', $files);
2463  self::assertNotContains('excludeMe.txt', $files);
2464  }
2465 
2466  #[Test]
2467  public function getFilesInDirCanPrependPath(): void
2468  {
2469  $path = $this->getFilesInDirCreateTestDirectory();
2470  self::assertContains(
2471  $path . '/testA.txt',
2472  GeneralUtility::getFilesInDir($path, '', true)
2473  );
2474  }
2475 
2476  #[Test]
2477  public function getFilesInDirDoesSortAlphabeticallyByDefault(): void
2478  {
2479  $path = $this->getFilesInDirCreateTestDirectory();
2480  self::assertSame(
2481  ['.secret.txt', 'double.setup.typoscript', 'excludeMe.txt', 'test.css', 'test.js', 'testA.txt', 'testB.txt', 'testC.txt'],
2482  array_values(GeneralUtility::getFilesInDir($path))
2483  );
2484  }
2485 
2486  #[Test]
2487  public function getFilesInDirReturnsArrayWithMd5OfElementAndPathAsArrayKey(): void
2488  {
2489  $path = $this->getFilesInDirCreateTestDirectory();
2490  self::assertArrayHasKey(
2491  md5($path . '/testA.txt'),
2492  GeneralUtility::getFilesInDir($path)
2493  );
2494  }
2495 
2496  #[Test]
2497  public function getFilesInDirDoesNotFindDirectories(): void
2498  {
2499  $path = $this->getFilesInDirCreateTestDirectory();
2500  self::assertNotContains(
2501  'subDirectory',
2502  GeneralUtility::getFilesInDir($path)
2503  );
2504  }
2505 
2510  #[Test]
2511  public function getFilesInDirDoesNotFindDotfiles(): void
2512  {
2513  $path = $this->getFilesInDirCreateTestDirectory();
2514  $files = GeneralUtility::getFilesInDir($path);
2515  self::assertNotContains('..', $files);
2516  self::assertNotContains('.', $files);
2517  }
2518 
2520  // Tests concerning split_fileref
2522  #[Test]
2523  public function splitFileRefReturnsFileTypeNotForFolders(): void
2524  {
2525  $directoryName = ‪StringUtility::getUniqueId('test_') . '.com';
2526  $directoryPath = ‪Environment::getVarPath() . '/tests/';
2527  @mkdir($directoryPath, octdec(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2528  $directory = $directoryPath . $directoryName;
2529  mkdir($directory, octdec(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2530  $fileInfo = GeneralUtility::split_fileref($directory);
2531  $directoryCreated = is_dir($directory);
2532  rmdir($directory);
2533  self::assertTrue($directoryCreated);
2534  self::assertIsArray($fileInfo);
2535  self::assertEquals($directoryPath, $fileInfo['path']);
2536  self::assertEquals($directoryName, $fileInfo['file']);
2537  self::assertEquals($directoryName, $fileInfo['filebody']);
2538  self::assertEquals('', $fileInfo['fileext']);
2539  self::assertArrayNotHasKey('realFileext', $fileInfo);
2540  }
2541 
2542  #[Test]
2543  public function splitFileRefReturnsFileTypeForFilesWithoutPathSite(): void
2544  {
2545  $testFile = 'fileadmin/media/someFile.png';
2546  $fileInfo = GeneralUtility::split_fileref($testFile);
2547  self::assertIsArray($fileInfo);
2548  self::assertEquals('fileadmin/media/', $fileInfo['path']);
2549  self::assertEquals('someFile.png', $fileInfo['file']);
2550  self::assertEquals('someFile', $fileInfo['filebody']);
2551  self::assertEquals('png', $fileInfo['fileext']);
2552  }
2553 
2555  // Tests concerning dirname
2557 
2561  public static function dirnameDataProvider(): array
2562  {
2563  return [
2564  'absolute path with multiple part and file' => ['/dir1/dir2/script.php', '/dir1/dir2'],
2565  'absolute path with one part' => ['/dir1/', '/dir1'],
2566  'absolute path to file without extension' => ['/dir1/something', '/dir1'],
2567  'relative path with one part and file' => ['dir1/script.php', 'dir1'],
2568  'relative one-character path with one part and file' => ['d/script.php', 'd'],
2569  'absolute zero-part path with file' => ['/script.php', ''],
2570  'empty string' => ['', ''],
2571  ];
2572  }
2573 
2578  #[DataProvider('dirnameDataProvider')]
2579  #[Test]
2580  public function dirnameWithDataProvider(string $input, string $expectedValue): void
2581  {
2582  self::assertEquals($expectedValue, GeneralUtility::dirname($input));
2583  }
2584 
2586  // Tests concerning resolveBackPath
2588 
2592  public static function resolveBackPathDataProvider(): array
2593  {
2594  return [
2595  'empty path' => ['', ''],
2596  'this directory' => ['./', './'],
2597  'relative directory without ..' => ['dir1/dir2/dir3/', 'dir1/dir2/dir3/'],
2598  'relative path without ..' => ['dir1/dir2/script.php', 'dir1/dir2/script.php'],
2599  'absolute directory without ..' => ['/dir1/dir2/dir3/', '/dir1/dir2/dir3/'],
2600  'absolute path without ..' => ['/dir1/dir2/script.php', '/dir1/dir2/script.php'],
2601  'only one directory upwards without trailing slash' => ['..', '..'],
2602  'only one directory upwards with trailing slash' => ['../', '../'],
2603  'one level with trailing ..' => ['dir1/..', ''],
2604  'one level with trailing ../' => ['dir1/../', ''],
2605  'two levels with trailing ..' => ['dir1/dir2/..', 'dir1'],
2606  'two levels with trailing ../' => ['dir1/dir2/../', 'dir1/'],
2607  'leading ../ without trailing /' => ['../dir1', '../dir1'],
2608  'leading ../ with trailing /' => ['../dir1/', '../dir1/'],
2609  'leading ../ and inside path' => ['../dir1/dir2/../dir3/', '../dir1/dir3/'],
2610  'one times ../ in relative directory' => ['dir1/../dir2/', 'dir2/'],
2611  'one times ../ in absolute directory' => ['/dir1/../dir2/', '/dir2/'],
2612  'one times ../ in relative path' => ['dir1/../dir2/script.php', 'dir2/script.php'],
2613  'one times ../ in absolute path' => ['/dir1/../dir2/script.php', '/dir2/script.php'],
2614  'consecutive ../' => ['dir1/dir2/dir3/../../../dir4', 'dir4'],
2615  'distributed ../ with trailing /' => ['dir1/../dir2/dir3/../', 'dir2/'],
2616  'distributed ../ without trailing /' => ['dir1/../dir2/dir3/..', 'dir2'],
2617  'multiple distributed and consecutive ../ together' => ['dir1/dir2/dir3/dir4/../../dir5/dir6/dir7/../dir8/', 'dir1/dir2/dir5/dir6/dir8/'],
2618  'dirname with leading ..' => ['dir1/..dir2/dir3/', 'dir1/..dir2/dir3/'],
2619  'dirname with trailing ..' => ['dir1/dir2../dir3/', 'dir1/dir2../dir3/'],
2620  'more times upwards than downwards in directory' => ['dir1/../../', '../'],
2621  'more times upwards than downwards in path' => ['dir1/../../script.php', '../script.php'],
2622  ];
2623  }
2624 
2629  #[DataProvider('resolveBackPathDataProvider')]
2630  #[Test]
2631  public function resolveBackPathWithDataProvider(string $input, string $expectedValue): void
2632  {
2633  self::assertEquals($expectedValue, GeneralUtility::resolveBackPath($input));
2634  }
2635 
2637  // Tests concerning makeInstance, setSingletonInstance, addInstance, purgeInstances
2639  #[Test]
2640  public function makeInstanceWithEmptyClassNameThrowsException(): void
2641  {
2642  $this->expectException(\InvalidArgumentException::class);
2643  $this->expectExceptionCode(1288965219);
2644 
2645  // @phpstan-ignore-next-line We're explicitly checking the behavior for a contract violation.
2646  GeneralUtility::makeInstance('');
2647  }
2648 
2649  #[Test]
2650  public function makeInstanceWithBeginningSlashInClassNameThrowsException(): void
2651  {
2652  $this->expectException(\InvalidArgumentException::class);
2653  $this->expectExceptionCode(1420281366);
2654 
2655  GeneralUtility::makeInstance('\\TYPO3\\CMS\\Backend\\Controller\\BackendController');
2656  }
2657 
2658  #[Test]
2659  public function makeInstanceReturnsClassInstance(): void
2660  {
2661  self::assertInstanceOf(\stdClass::class, GeneralUtility::makeInstance(\stdClass::class));
2662  }
2663 
2664  #[Test]
2665  public function makeInstancePassesParametersToConstructor(): void
2666  {
2667  $instance = GeneralUtility::makeInstance(TwoParametersConstructorFixture::class, 'one parameter', 'another parameter');
2668  self::assertEquals('one parameter', $instance->constructorParameter1, 'The first constructor parameter has not been set.');
2669  self::assertEquals('another parameter', $instance->constructorParameter2, 'The second constructor parameter has not been set.');
2670  }
2671 
2672  #[Test]
2673  public function makeInstanceInstanciatesConfiguredImplementation(): void
2674  {
2675  GeneralUtility::flushInternalRuntimeCaches();
2676  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalClassFixture::class] = ['className' => ReplacementClassFixture::class];
2677  self::assertInstanceOf(ReplacementClassFixture::class, GeneralUtility::makeInstance(OriginalClassFixture::class));
2678  }
2679 
2680  #[Test]
2681  public function makeInstanceResolvesConfiguredImplementationsRecursively(): void
2682  {
2683  GeneralUtility::flushInternalRuntimeCaches();
2684  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalClassFixture::class] = ['className' => ReplacementClassFixture::class];
2685  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][ReplacementClassFixture::class] = ['className' => OtherReplacementClassFixture::class];
2686  self::assertInstanceOf(OtherReplacementClassFixture::class, GeneralUtility::makeInstance(OriginalClassFixture::class));
2687  }
2688 
2689  #[Test]
2690  public function makeInstanceCalledTwoTimesForNonSingletonClassReturnsDifferentInstances(): void
2691  {
2692  $className = \stdClass::class;
2693  self::assertNotSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
2694  }
2695 
2696  #[Test]
2697  public function makeInstanceCalledTwoTimesForSingletonClassReturnsSameInstance(): void
2698  {
2699  $className = get_class($this->createMock(SingletonInterface::class));
2700  self::assertSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
2701  }
2702 
2703  #[Test]
2704  public function makeInstanceCalledTwoTimesForSingletonClassWithPurgeInstancesInbetweenReturnsDifferentInstances(): void
2705  {
2706  $className = get_class($this->createMock(SingletonInterface::class));
2707  $instance = GeneralUtility::makeInstance($className);
2708  GeneralUtility::purgeInstances();
2709  self::assertNotSame($instance, GeneralUtility::makeInstance($className));
2710  }
2711 
2712  #[Test]
2713  public function makeInstanceInjectsLogger(): void
2714  {
2715  $instance = GeneralUtility::makeInstance(GeneralUtilityMakeInstanceInjectLoggerFixture::class);
2716  self::assertInstanceOf(LoggerInterface::class, $instance->getLogger());
2717  }
2718 
2719  #[Test]
2720  public function setSingletonInstanceForEmptyClassNameThrowsException(): void
2721  {
2722  $this->expectException(\InvalidArgumentException::class);
2723  $this->expectExceptionCode(1288967479);
2724 
2725  $instance = $this->createMock(SingletonInterface::class);
2726  // @phpstan-ignore-next-line We are explicitly testing with a contract violation here.
2727  GeneralUtility::setSingletonInstance('', $instance);
2728  }
2729 
2730  #[Test]
2731  public function setSingletonInstanceForClassThatIsNoSubclassOfProvidedClassThrowsException(): void
2732  {
2733  $this->expectException(\InvalidArgumentException::class);
2734  $this->expectExceptionCode(1288967686);
2735  $instance = $this->getMockBuilder(SingletonInterface::class)->getMock();
2736  $singletonClassName = get_class($this->createMock(SingletonInterface::class));
2737  GeneralUtility::setSingletonInstance($singletonClassName, $instance);
2738  }
2739 
2740  #[Test]
2741  public function setSingletonInstanceMakesMakeInstanceReturnThatInstance(): void
2742  {
2743  $instance = $this->createMock(SingletonInterface::class);
2744  $singletonClassName = get_class($instance);
2745  GeneralUtility::setSingletonInstance($singletonClassName, $instance);
2746  self::assertSame($instance, GeneralUtility::makeInstance($singletonClassName));
2747  }
2748 
2749  #[Test]
2750  public function setSingletonInstanceCalledTwoTimesMakesMakeInstanceReturnLastSetInstance(): void
2751  {
2752  $instance1 = $this->createMock(SingletonInterface::class);
2753  $singletonClassName = get_class($instance1);
2754  $instance2 = new $singletonClassName();
2755  GeneralUtility::setSingletonInstance($singletonClassName, $instance1);
2756  GeneralUtility::setSingletonInstance($singletonClassName, $instance2);
2757  self::assertSame($instance2, GeneralUtility::makeInstance($singletonClassName));
2758  }
2759 
2760  #[Test]
2761  public function getSingletonInstancesContainsPreviouslySetSingletonInstance(): void
2762  {
2763  $instance = $this->createMock(SingletonInterface::class);
2764  $instanceClassName = get_class($instance);
2765  GeneralUtility::setSingletonInstance($instanceClassName, $instance);
2766  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
2767  self::assertArrayHasKey($instanceClassName, $registeredSingletonInstances);
2768  self::assertSame($registeredSingletonInstances[$instanceClassName], $instance);
2769  }
2770 
2771  #[Test]
2772  public function setSingletonInstanceReturnsFinalClassNameWithOverriddenClass(): void
2773  {
2774  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][SingletonClassFixture::class]['className'] = ExtendedSingletonClassFixture::class;
2775  $anotherInstance = new ExtendedSingletonClassFixture();
2776  GeneralUtility::makeInstance(SingletonClassFixture::class);
2777  GeneralUtility::setSingletonInstance(SingletonClassFixture::class, $anotherInstance);
2778  $result = GeneralUtility::makeInstance(SingletonClassFixture::class);
2779  self::assertSame($anotherInstance, $result);
2780  self::assertEquals(ExtendedSingletonClassFixture::class, get_class($anotherInstance));
2781  }
2782 
2783  #[Test]
2784  public function resetSingletonInstancesResetsPreviouslySetInstance(): void
2785  {
2786  $instance = $this->createMock(SingletonInterface::class);
2787  $instanceClassName = get_class($instance);
2788  GeneralUtility::setSingletonInstance($instanceClassName, $instance);
2789  GeneralUtility::resetSingletonInstances([]);
2790  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
2791  self::assertArrayNotHasKey($instanceClassName, $registeredSingletonInstances);
2792  }
2793 
2794  #[Test]
2795  public function resetSingletonInstancesSetsGivenInstance(): void
2796  {
2797  $instance = $this->createMock(SingletonInterface::class);
2798  $instanceClassName = get_class($instance);
2799  GeneralUtility::resetSingletonInstances(
2800  [$instanceClassName => $instance]
2801  );
2802  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
2803  self::assertArrayHasKey($instanceClassName, $registeredSingletonInstances);
2804  self::assertSame($registeredSingletonInstances[$instanceClassName], $instance);
2805  }
2806 
2807  #[Test]
2808  public function addInstanceForEmptyClassNameThrowsException(): void
2809  {
2810  $this->expectException(\InvalidArgumentException::class);
2811  $this->expectExceptionCode(1288967479);
2812 
2813  // @phpstan-ignore-next-line We are explicitly testing with a contract violation here.
2814  GeneralUtility::addInstance('', new \stdClass());
2815  }
2816 
2817  #[Test]
2818  public function addInstanceForClassThatIsNoSubclassOfProvidedClassThrowsException(): void
2819  {
2820  $this->expectException(\InvalidArgumentException::class);
2821  $this->expectExceptionCode(1288967686);
2822  $instance = $this->getMockBuilder(\stdClass::class)->getMock();
2823  $singletonClassName = get_class($this->createMock(\stdClass::class));
2824  GeneralUtility::addInstance($singletonClassName, $instance);
2825  }
2826 
2827  #[Test]
2828  public function addInstanceWithSingletonInstanceThrowsException(): void
2829  {
2830  $this->expectException(\InvalidArgumentException::class);
2831  $this->expectExceptionCode(1288969325);
2832 
2833  $instance = $this->createMock(SingletonInterface::class);
2834  GeneralUtility::addInstance(get_class($instance), $instance);
2835  }
2836 
2837  #[Test]
2838  public function addInstanceMakesMakeInstanceReturnThatInstance(): void
2839  {
2840  $instance = $this->createMock(\stdClass::class);
2841  $className = get_class($instance);
2842  GeneralUtility::addInstance($className, $instance);
2843  self::assertSame($instance, GeneralUtility::makeInstance($className));
2844  }
2845 
2846  #[Test]
2847  public function makeInstanceCalledTwoTimesAfterAddInstanceReturnTwoDifferentInstances(): void
2848  {
2849  $instance = $this->createMock(\stdClass::class);
2850  $className = get_class($instance);
2851  GeneralUtility::addInstance($className, $instance);
2852  self::assertNotSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
2853  }
2854 
2855  #[Test]
2856  public function addInstanceCalledTwoTimesMakesMakeInstanceReturnBothInstancesInAddingOrder(): void
2857  {
2858  $instance1 = $this->createMock(\stdClass::class);
2859  $className = get_class($instance1);
2860  GeneralUtility::addInstance($className, $instance1);
2861  $instance2 = new $className();
2862  GeneralUtility::addInstance($className, $instance2);
2863  self::assertSame($instance1, GeneralUtility::makeInstance($className), 'The first returned instance does not match the first added instance.');
2864  self::assertSame($instance2, GeneralUtility::makeInstance($className), 'The second returned instance does not match the second added instance.');
2865  }
2866 
2867  #[Test]
2868  public function purgeInstancesDropsAddedInstance(): void
2869  {
2870  $instance = $this->createMock(\stdClass::class);
2871  $className = get_class($instance);
2872  GeneralUtility::addInstance($className, $instance);
2873  GeneralUtility::purgeInstances();
2874  self::assertNotSame($instance, GeneralUtility::makeInstance($className));
2875  }
2876 
2877  public static function getFileAbsFileNameDataProvider(): array
2878  {
2879  return [
2880  'relative path is prefixed with public path' => [
2881  'fileadmin/foo.txt',
2882  ‪Environment::getPublicPath() . '/fileadmin/foo.txt',
2883  ],
2884  'relative path, referencing current directory is prefixed with public path' => [
2885  './fileadmin/foo.txt',
2886  ‪Environment::getPublicPath() . '/./fileadmin/foo.txt',
2887  ],
2888  'relative paths with back paths are not allowed and returned empty' => [
2889  '../fileadmin/foo.txt',
2890  '',
2891  ],
2892  'absolute paths with back paths are not allowed and returned empty' => [
2893  ‪Environment::getPublicPath() . '/../sysext/core/Resources/Public/Icons/Extension.png',
2894  '',
2895  ],
2896  'allowed absolute paths are returned as is' => [
2897  ‪Environment::getPublicPath() . '/fileadmin/foo.txt',
2898  ‪Environment::getPublicPath() . '/fileadmin/foo.txt',
2899  ],
2900  'disallowed absolute paths are returned empty' => [
2901  '/somewhere/fileadmin/foo.txt',
2902  '',
2903  ],
2904  'EXT paths are resolved to absolute paths' => [
2905  'EXT:foo/Resources/Private/Templates/Home.html',
2906  '/path/to/foo/Resources/Private/Templates/Home.html',
2907  ],
2908  ];
2909  }
2910 
2911  #[DataProvider('getFileAbsFileNameDataProvider')]
2912  #[Test]
2913  public function getFileAbsFileNameReturnsCorrectValues(string $path, string $expected): void
2914  {
2915  // build the dummy package "foo" for use in ExtensionManagementUtility::extPath('foo');
2916  $package = $this->getMockBuilder(Package::class)
2917  ->disableOriginalConstructor()
2918  ->onlyMethods(['getPackagePath'])
2919  ->getMock();
2920  $packageManager = $this->getMockBuilder(PackageManager::class)
2921  ->onlyMethods(['isPackageActive', 'getPackage', 'getActivePackages'])
2922  ->disableOriginalConstructor()
2923  ->getMock();
2924  $package
2925  ->method('getPackagePath')
2926  ->willReturn('/path/to/foo/');
2927  $packageManager
2928  ->method('getActivePackages')
2929  ->willReturn(['foo' => $package]);
2930  $packageManager
2931  ->method('isPackageActive')
2932  ->with(self::equalTo('foo'))
2933  ->willReturn(true);
2934  $packageManager
2935  ->method('getPackage')
2936  ->with('foo')
2937  ->willReturn($package);
2939 
2940  $result = GeneralUtility::getFileAbsFileName($path);
2941  self::assertEquals($expected, $result);
2942  }
2943 
2949  public static function validPathStrInvalidCharactersDataProvider(): array
2950  {
2951  $data = [
2952  'double slash in path' => ['path//path'],
2953  'backslash in path' => ['path\\path'],
2954  'directory up in path' => ['path/../path'],
2955  'directory up at the beginning' => ['../path'],
2956  'NUL character in path' => ['path' . "\0" . 'path'],
2957  'BS character in path' => ['path' . chr(8) . 'path'],
2958  'invalid UTF-8-sequence' => ["\xc0" . 'path/path'],
2959  'Could be overlong NUL in some UTF-8 implementations, invalid in RFC3629' => ["\xc0\x80" . 'path/path'],
2960  ];
2961 
2962  // Mixing with regular utf-8
2963  $utf8Characters = 'Ссылка/';
2964  foreach ($data as $key => $value) {
2965  $data[$key . ' with UTF-8 characters prepended'] = [$utf8Characters . $value[0]];
2966  $data[$key . ' with UTF-8 characters appended'] = [$value[0] . $utf8Characters];
2967  }
2968 
2969  // Encoding with UTF-16
2970  foreach ($data as $key => $value) {
2971  $data[$key . ' encoded with UTF-16'] = [mb_convert_encoding($value[0], 'UTF-16')];
2972  }
2973 
2974  return $data;
2975  }
2976 
2980  #[DataProvider('validPathStrInvalidCharactersDataProvider')]
2981  #[Test]
2982  public function validPathStrDetectsInvalidCharacters(string $path): void
2983  {
2984  self::assertFalse(GeneralUtility::validPathStr($path));
2985  }
2986 
2992  public static function validPathStrDataProvider(): array
2993  {
2994  $data = [
2995  'normal ascii path' => ['fileadmin/templates/myfile..xml'],
2996  'special character' => ['fileadmin/templates/Ссылка (fce).xml'],
2997  ];
2998 
2999  return $data;
3000  }
3001 
3005  #[DataProvider('validPathStrDataProvider')]
3006  #[Test]
3007  public function validPathStrWorksWithUnicodeFileNames(string $path): void
3008  {
3009  self::assertTrue(GeneralUtility::validPathStr($path));
3010  }
3011 
3013  // Tests concerning copyDirectory
3015  #[Test]
3016  public function copyDirectoryCopiesFilesAndDirectoriesWithRelativePaths(): void
3017  {
3018  $sourceDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3019  $absoluteSourceDirectory = ‪Environment::getPublicPath() . '/' . $sourceDirectory;
3020  $this->testFilesToDelete[] = $absoluteSourceDirectory;
3021  ‪GeneralUtility::mkdir($absoluteSourceDirectory);
3022 
3023  $targetDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3024  $absoluteTargetDirectory = ‪Environment::getPublicPath() . '/' . $targetDirectory;
3025  $this->testFilesToDelete[] = $absoluteTargetDirectory;
3026 
3027  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'file', '42');
3028  ‪GeneralUtility::mkdir($absoluteSourceDirectory . 'foo');
3029  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'foo/file', '42');
3030 
3031  GeneralUtility::copyDirectory($sourceDirectory, $targetDirectory);
3032 
3033  self::assertFileExists($absoluteTargetDirectory . 'file');
3034  self::assertFileExists($absoluteTargetDirectory . 'foo/file');
3035  }
3036 
3037  #[Test]
3038  public function copyDirectoryCopiesFilesAndDirectoriesWithAbsolutePaths(): void
3039  {
3040  $sourceDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3041  $absoluteSourceDirectory = ‪Environment::getPublicPath() . '/' . $sourceDirectory;
3042  $this->testFilesToDelete[] = $absoluteSourceDirectory;
3043  ‪GeneralUtility::mkdir($absoluteSourceDirectory);
3044 
3045  $targetDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3046  $absoluteTargetDirectory = ‪Environment::getPublicPath() . '/' . $targetDirectory;
3047  $this->testFilesToDelete[] = $absoluteTargetDirectory;
3048 
3049  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'file', '42');
3050  ‪GeneralUtility::mkdir($absoluteSourceDirectory . 'foo');
3051  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'foo/file', '42');
3052 
3053  GeneralUtility::copyDirectory($absoluteSourceDirectory, $absoluteTargetDirectory);
3054 
3055  self::assertFileExists($absoluteTargetDirectory . 'file');
3056  self::assertFileExists($absoluteTargetDirectory . 'foo/file');
3057  }
3058 
3059  public static function callUserFunctionInvalidParameterDataProvider(): array
3060  {
3061  return [
3062  'Method does not exist' => [GeneralUtilityTestClass::class . '->calledUserFunction', 1294585865],
3063  'Class does not exist' => ['t3lib_divTest21345->user_calledUserFunction', 1294585866],
3064  'No method name' => [GeneralUtilityTestClass::class, 1294585867],
3065  'No class name' => ['->user_calledUserFunction', 1294585866],
3066  'No function name' => ['', 1294585867],
3067  ];
3068  }
3069 
3073  #[DataProvider('callUserFunctionInvalidParameterDataProvider')]
3074  #[Test]
3075  public function callUserFunctionWillThrowExceptionForInvalidParameters(string $functionName, int $expectedException): void
3076  {
3077  $this->expectException(\InvalidArgumentException::class);
3078  $this->expectExceptionCode($expectedException);
3079  $inputData = ['foo' => 'bar'];
3080  GeneralUtility::callUserFunction($functionName, $inputData, $this);
3081  }
3082 
3083  #[Test]
3084  public function callUserFunctionCanCallClosure(): void
3085  {
3086  $inputData = ['foo' => 'bar'];
3087  $result = GeneralUtility::callUserFunction(
3088  static fn(): string => 'Worked fine',
3089  $inputData,
3090  $this,
3091  ''
3092  );
3093  self::assertEquals('Worked fine', $result);
3094  }
3095 
3096  #[Test]
3097  public function callUserFunctionCanCallMethod(): void
3098  {
3099  $inputData = ['foo' => 'bar'];
3100  $result = GeneralUtility::callUserFunction(GeneralUtilityTestClass::class . '->user_calledUserFunction', $inputData, $this);
3101  self::assertEquals('Worked fine', $result);
3102  }
3103 
3104  #[Test]
3105  public function callUserFunctionTrimsSpaces(): void
3106  {
3107  $inputData = ['foo' => 'bar'];
3108  $result = GeneralUtility::callUserFunction("\t" . GeneralUtilityTestClass::class . '->user_calledUserFunction ', $inputData, $this);
3109  self::assertEquals('Worked fine', $result);
3110  }
3111 
3112  #[Test]
3113  public function callUserFunctionAcceptsClosures(): void
3114  {
3115  $inputData = ['foo' => 'bar'];
3116  $closure = static function ($parameters, $reference) use ($inputData) {
3117  $reference->assertEquals($inputData, $parameters, 'Passed data does not match expected output');
3118  return 'Worked fine';
3119  };
3120  self::assertEquals('Worked fine', GeneralUtility::callUserFunction($closure, $inputData, $this));
3121  }
3122 
3123  #[Test]
3124  public function getAllFilesAndFoldersInPathReturnsArrayWithMd5Keys(): void
3125  {
3126  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_');
3127  mkdir($directory);
3128  $filesAndDirectories = GeneralUtility::getAllFilesAndFoldersInPath([], $directory, '', true);
3129  $check = true;
3130  foreach ($filesAndDirectories as $md5 => $path) {
3131  if (!preg_match('/^[a-f0-9]{32}$/', $md5)) {
3132  $check = false;
3133  }
3134  }
3135  ‪GeneralUtility::rmdir($directory);
3136  self::assertTrue($check);
3137  }
3138 
3143  #[Test]
3144  public function array2xmlConvertsEmptyArraysToElementWithoutContent(): void
3145  {
3146  $input = [
3147  'el' => [],
3148  ];
3149 
3150  ‪$output = GeneralUtility::array2xml($input);
3151 
3152  self::assertEquals('<phparray>
3153  <el type="array"></el>
3154 </phparray>', ‪$output);
3155  }
3156 
3157  #[Test]
3158  public function xml2arrayUsesCache(): void
3159  {
3160  $cacheMock = $this->createMock(FrontendInterface::class);
3161  $cacheMock->method('getIdentifier')->willReturn('runtime');
3162  $cacheMock->expects(self::atLeastOnce())->method('get')->with('generalUtilityXml2Array')->willReturn(false);
3163  $cacheMock->expects(self::atLeastOnce())->method('set')->with('generalUtilityXml2Array', self::anything());
3164  $cacheManager = new CacheManager();
3165  $cacheManager->registerCache($cacheMock);
3166  GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager);
3167  ‪GeneralUtility::xml2array('<?xml version="1.0" encoding="utf-8" standalone="yes"?>', 'T3:');
3168  }
3169 
3173  public static function xml2arrayProcessHandlesWhitespacesDataProvider(): array
3174  {
3175  $headerVariants = [
3176  'utf-8' => '<?xml version="1.0" encoding="utf-8" standalone="yes"?>',
3177  'UTF-8' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
3178  'no-encoding' => '<?xml version="1.0" standalone="yes"?>',
3179  'iso-8859-1' => '<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>',
3180  'ISO-8859-1' => '<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>',
3181  ];
3182  $data = [];
3183  foreach ($headerVariants as ‪$identifier => $headerVariant) {
3184  $data += [
3185  'inputWithoutWhitespaces-' . ‪$identifier => [
3186  $headerVariant . '<T3FlexForms>
3187  <data>
3188  <field index="settings.persistenceIdentifier">
3189  <value index="vDEF">egon</value>
3190  </field>
3191  </data>
3192  </T3FlexForms>',
3193  ],
3194  'inputWithPrecedingWhitespaces-' . ‪$identifier => [
3195  CR . ' ' . $headerVariant . '<T3FlexForms>
3196  <data>
3197  <field index="settings.persistenceIdentifier">
3198  <value index="vDEF">egon</value>
3199  </field>
3200  </data>
3201  </T3FlexForms>',
3202  ],
3203  'inputWithTrailingWhitespaces-' . ‪$identifier => [
3204  $headerVariant . '<T3FlexForms>
3205  <data>
3206  <field index="settings.persistenceIdentifier">
3207  <value index="vDEF">egon</value>
3208  </field>
3209  </data>
3210  </T3FlexForms>' . CR . ' ',
3211  ],
3212  'inputWithPrecedingAndTrailingWhitespaces-' . ‪$identifier => [
3213  CR . ' ' . $headerVariant . '<T3FlexForms>
3214  <data>
3215  <field index="settings.persistenceIdentifier">
3216  <value index="vDEF">egon</value>
3217  </field>
3218  </data>
3219  </T3FlexForms>' . CR . ' ',
3220  ],
3221  ];
3222  }
3223  return $data;
3224  }
3225 
3226  #[DataProvider('xml2arrayProcessHandlesWhitespacesDataProvider')]
3227  #[Test]
3228  public function xml2arrayProcessHandlesWhitespaces(string $input): void
3229  {
3230  $expected = [
3231  'data' => [
3232  'settings.persistenceIdentifier' => [
3233  'vDEF' => 'egon',
3234  ],
3235  ],
3236  ];
3237  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input));
3238  }
3239 
3243  public static function xml2arrayProcessHandlesTagNamespacesDataProvider(): array
3244  {
3245  return [
3246  'inputWithNameSpaceOnRootLevel' => [
3247  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3248  <T3:T3FlexForms>
3249  <data>
3250  <field index="settings.persistenceIdentifier">
3251  <value index="vDEF">egon</value>
3252  </field>
3253  </data>
3254  </T3:T3FlexForms>',
3255  ],
3256  'inputWithNameSpaceOnNonRootLevel' => [
3257  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3258  <T3FlexForms>
3259  <data>
3260  <T3:field index="settings.persistenceIdentifier">
3261  <value index="vDEF">egon</value>
3262  </T3:field>
3263  </data>
3264  </T3FlexForms>',
3265  ],
3266  'inputWithNameSpaceOnRootAndNonRootLevel' => [
3267  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3268  <T3:T3FlexForms>
3269  <data>
3270  <T3:field index="settings.persistenceIdentifier">
3271  <value index="vDEF">egon</value>
3272  </T3:field>
3273  </data>
3274  </T3:T3FlexForms>',
3275  ],
3276  ];
3277  }
3278 
3279  #[DataProvider('xml2arrayProcessHandlesTagNamespacesDataProvider')]
3280  #[Test]
3281  public function xml2arrayProcessHandlesTagNamespaces(string $input): void
3282  {
3283  $expected = [
3284  'data' => [
3285  'settings.persistenceIdentifier' => [
3286  'vDEF' => 'egon',
3287  ],
3288  ],
3289  ];
3290  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input, 'T3:'));
3291  }
3292 
3296  public static function xml2arrayProcessHandlesDocumentTagDataProvider(): array
3297  {
3298  return [
3299  'input' => [
3300  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3301  <T3FlexForms>
3302  <data>
3303  <field index="settings.persistenceIdentifier">
3304  <value index="vDEF">egon</value>
3305  </field>
3306  </data>
3307  </T3FlexForms>',
3308  'T3FlexForms',
3309  ],
3310  'input-with-root-namespace' => [
3311  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3312  <T3:T3FlexForms>
3313  <data>
3314  <field index="settings.persistenceIdentifier">
3315  <value index="vDEF">egon</value>
3316  </field>
3317  </data>
3318  </T3:T3FlexForms>',
3319  'T3:T3FlexForms',
3320  ],
3321  'input-with-namespace' => [
3322  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3323  <T3FlexForms>
3324  <data>
3325  <T3:field index="settings.persistenceIdentifier">
3326  <value index="vDEF">egon</value>
3327  </T3:field>
3328  </data>
3329  </T3FlexForms>',
3330  'T3FlexForms',
3331  ],
3332  ];
3333  }
3334 
3335  #[DataProvider('xml2arrayProcessHandlesDocumentTagDataProvider')]
3336  #[Test]
3337  public function xml2arrayProcessHandlesDocumentTag(string $input, string $docTag): void
3338  {
3339  $expected = [
3340  'data' => [
3341  'settings.persistenceIdentifier' => [
3342  'vDEF' => 'egon',
3343  ],
3344  ],
3345  '_DOCUMENT_TAG' => $docTag,
3346  ];
3347  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input, '', true));
3348  }
3349 
3353  public static function xml2ArrayProcessHandlesBigXmlContentDataProvider(): array
3354  {
3355  return [
3356  '1mb' => [
3357  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3358  <T3:T3FlexForms>
3359  <data>
3360  <field index="settings.persistenceIdentifier">
3361  <value index="vDEF">' . str_repeat('1', 1024 * 1024) . '</value>
3362  </field>
3363  </data>
3364  </T3:T3FlexForms>',
3365  str_repeat('1', 1024 * 1024),
3366  ],
3367  '5mb' => [
3368  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3369  <T3:T3FlexForms>
3370  <data>
3371  <field index="settings.persistenceIdentifier">
3372  <value index="vDEF">' . str_repeat('1', 5 * 1024 * 1024) . '</value>
3373  </field>
3374  </data>
3375  </T3:T3FlexForms>',
3376  str_repeat('1', 5 * 1024 * 1024),
3377  ],
3378  ];
3379  }
3380 
3381  #[DataProvider('xml2ArrayProcessHandlesBigXmlContentDataProvider')]
3382  #[Test]
3383  public function xml2ArrayProcessHandlesBigXmlContent(string $input, string $testValue): void
3384  {
3385  $expected = [
3386  'data' => [
3387  'settings.persistenceIdentifier' => [
3388  'vDEF' => $testValue,
3389  ],
3390  ],
3391  ];
3392  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input));
3393  }
3394 
3398  public static function xml2ArrayProcessHandlesAttributeTypesDataProvider(): array
3399  {
3400  $prefix = '<?xml version="1.0" encoding="utf-8" standalone="yes"?><T3FlexForms><field index="index">';
3401  $suffix = '</field></T3FlexForms>';
3402  return [
3403  'no-type string' => [
3404  $prefix . '<value index="vDEF">foo bar</value>' . $suffix,
3405  'foo bar',
3406  ],
3407  'no-type integer' => [
3408  $prefix . '<value index="vDEF">123</value>' . $suffix,
3409  '123',
3410  ],
3411  'no-type double' => [
3412  $prefix . '<value index="vDEF">1.23</value>' . $suffix,
3413  '1.23',
3414  ],
3415  'integer integer' => [
3416  $prefix . '<value index="vDEF" type="integer">123</value>' . $suffix,
3417  123,
3418  ],
3419  'integer double' => [
3420  $prefix . '<value index="vDEF" type="integer">1.23</value>' . $suffix,
3421  1,
3422  ],
3423  'double integer' => [
3424  $prefix . '<value index="vDEF" type="double">123</value>' . $suffix,
3425  123.0,
3426  ],
3427  'double double' => [
3428  $prefix . '<value index="vDEF" type="double">1.23</value>' . $suffix,
3429  1.23,
3430  ],
3431  'boolean 0' => [
3432  $prefix . '<value index="vDEF" type="boolean">0</value>' . $suffix,
3433  false,
3434  ],
3435  'boolean 1' => [
3436  $prefix . '<value index="vDEF" type="boolean">1</value>' . $suffix,
3437  true,
3438  ],
3439  'boolean true' => [
3440  $prefix . '<value index="vDEF" type="boolean">true</value>' . $suffix,
3441  true,
3442  ],
3443  'boolean false' => [
3444  $prefix . '<value index="vDEF" type="boolean">false</value>' . $suffix,
3445  true, // sic(!)
3446  ],
3447  'NULL' => [
3448  $prefix . '<value index="vDEF" type="NULL"></value>' . $suffix,
3449  null,
3450  ],
3451  'NULL string' => [
3452  $prefix . '<value index="vDEF" type="NULL">foo bar</value>' . $suffix,
3453  null,
3454  ],
3455  'NULL integer' => [
3456  $prefix . '<value index="vDEF" type="NULL">123</value>' . $suffix,
3457  null,
3458  ],
3459  'NULL double' => [
3460  $prefix . '<value index="vDEF" type="NULL">1.23</value>' . $suffix,
3461  null,
3462  ],
3463  'array' => [
3464  $prefix . '<value index="vDEF" type="array"></value>' . $suffix,
3465  [],
3466  ],
3467  ];
3468  }
3469 
3470  #[DataProvider('xml2ArrayProcessHandlesAttributeTypesDataProvider')]
3471  #[Test]
3472  public function xml2ArrayProcessHandlesAttributeTypes(string $input, mixed $expected): void
3473  {
3474  $result = ‪GeneralUtility::xml2arrayProcess($input);
3475  self::assertSame($expected, $result['index']['vDEF']);
3476  }
3477 
3481  public static function locationHeaderUrlDataProvider(): array
3482  {
3483  return [
3484  'simple relative path' => [
3485  'foo',
3486  'foo.bar.test',
3487  'http://foo.bar.test/foo',
3488  ],
3489  'path beginning with slash' => [
3490  '/foo',
3491  'foo.bar.test',
3492  'http://foo.bar.test/foo',
3493  ],
3494  'path with full domain and https scheme' => [
3495  'https://example.com/foo',
3496  'foo.bar.test',
3497  'https://example.com/foo',
3498  ],
3499  'path with full domain and http scheme' => [
3500  'http://example.com/foo',
3501  'foo.bar.test',
3502  'http://example.com/foo',
3503  ],
3504  'path with full domain and relative scheme' => [
3505  '//example.com/foo',
3506  'foo.bar.test',
3507  '//example.com/foo',
3508  ],
3509  ];
3510  }
3511 
3515  #[DataProvider('locationHeaderUrlDataProvider')]
3516  #[Test]
3517  public function locationHeaderUrl(string $path, string $host, string $expected): void
3518  {
3521  true,
3522  false,
3528  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
3529  );
3530  ‪$_SERVER['HTTP_HOST'] = $host;
3531  ‪$_SERVER['SCRIPT_NAME'] = '/index.php';
3532  $result = GeneralUtility::locationHeaderUrl($path);
3533  self::assertSame($expected, $result);
3534  }
3535 
3536  #[Test]
3537  public function createVersionNumberedFilenameDoesNotResolveBackpathForAbsolutePath(): void
3538  {
3539  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename'] = true;
3540 
3541  $uniqueFilename = ‪StringUtility::getUniqueId() . 'backend';
3542  $testFileDirectory = ‪Environment::getVarPath() . '/tests/';
3543  $testFilepath = $testFileDirectory . $uniqueFilename . '.css';
3544  $this->testFilesToDelete[] = $testFilepath;
3545  ‪GeneralUtility::mkdir_deep($testFileDirectory);
3546  touch($testFilepath);
3547 
3548  $versionedFilename = GeneralUtility::createVersionNumberedFilename($testFilepath);
3549 
3550  self::assertMatchesRegularExpression('/^.*\/tests\/' . $uniqueFilename . '\.[0-9]+\.css/', $versionedFilename);
3551  }
3552 
3553  #[Test]
3554  public function createVersionNumberedFilenameKeepsInvalidAbsolutePathInFrontendAndAddsQueryString(): void
3555  {
3558  true,
3559  false,
3564  ‪Environment::getPublicPath() . '/index.php',
3565  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
3566  );
3567  $request = new ServerRequest('https://www.example.com', 'GET');
3568  ‪$GLOBALS['TYPO3_REQUEST'] = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
3569  $uniqueFilename = ‪StringUtility::getUniqueId('main_');
3570  $testFileDirectory = ‪Environment::getPublicPath() . '/static/';
3571  $testFilepath = $testFileDirectory . $uniqueFilename . '.css';
3572  ‪GeneralUtility::mkdir_deep($testFileDirectory);
3573  touch($testFilepath);
3574 
3575  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'] = false;
3576  $incomingFileName = '/' . ‪PathUtility::stripPathSitePrefix($testFilepath);
3577  $versionedFilename = GeneralUtility::createVersionNumberedFilename($incomingFileName);
3578  self::assertStringContainsString('.css?', $versionedFilename);
3579  self::assertStringStartsWith('/static/main_', $versionedFilename);
3580 
3581  $incomingFileName = ‪PathUtility::stripPathSitePrefix($testFilepath);
3582  $versionedFilename = GeneralUtility::createVersionNumberedFilename($incomingFileName);
3583  self::assertStringContainsString('.css?', $versionedFilename);
3584  self::assertStringStartsWith('static/main_', $versionedFilename);
3585 
3586  ‪GeneralUtility::rmdir($testFileDirectory, true);
3587  }
3588 
3589  #[Test]
3590  public function getMaxUploadFileSizeReturnsPositiveInt(): void
3591  {
3592  $result = GeneralUtility::getMaxUploadFileSize();
3593  self::assertGreaterThan(0, $result);
3594  }
3595 }
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\formatSizeDataProvider
‪static formatSizeDataProvider()
Definition: GeneralUtilityTest.php:424
‪TYPO3\CMS\Core\Utility\GeneralUtility\underscoredToLowerCamelCase
‪static string underscoredToLowerCamelCase($string)
Definition: GeneralUtility.php:671
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir(string $newFolder)
Definition: GeneralUtility.php:1638
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static stripPathSitePrefix(string $path)
Definition: PathUtility.php:428
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityMakeInstanceInjectLoggerFixture
Definition: GeneralUtilityMakeInstanceInjectLoggerFixture.php:28
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\cmpFqdnReturnsTrue
‪static cmpFqdnReturnsTrue(string $baseHost, string $list)
Definition: GeneralUtilityTest.php:309
‪TYPO3\CMS\Core\Utility\GeneralUtility\get_dirs
‪static string[] string null get_dirs(string $path)
Definition: GeneralUtility.php:1759
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\NO_FIX_PERMISSIONS_ON_WINDOWS
‪const NO_FIX_PERMISSIONS_ON_WINDOWS
Definition: GeneralUtilityTest.php:49
‪TYPO3\CMS\Core\Utility\GeneralUtility\implodeArrayForUrl
‪static string implodeArrayForUrl(string $name, array $theArray, string $str='', bool $skipBlank=false, bool $rawurlencodeParamName=false)
Definition: GeneralUtility.php:860
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\SingletonClassFixture
Definition: SingletonClassFixture.php:25
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder
Definition: SystemEnvironmentBuilder.php:41
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪$baseDirectory
‪$baseDirectory
Definition: updateIsoDatabase.php:98
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\ExtendedSingletonClassFixture
Definition: ExtendedSingletonClassFixture.php:23
‪TYPO3\CMS\Core\Tests\Unit\Utility
‪TYPO3\CMS\Core\Core\Environment\getCurrentScript
‪static getCurrentScript()
Definition: Environment.php:220
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\isConnected
‪bool isConnected()
Definition: GeneralUtilityTest.php:78
‪TYPO3\CMS\Core\Utility\GeneralUtility\camelCaseToLowerCaseUnderscored
‪static string camelCaseToLowerCaseUnderscored($string)
Definition: GeneralUtility.php:683
‪TYPO3\CMS\Core\Utility\GeneralUtility\cmpFQDN
‪static bool cmpFQDN(string $baseHost, string $list)
Definition: GeneralUtility.php:341
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2arrayProcess
‪static array string xml2arrayProcess(string $string, string $NSprefix='', bool $reportDocTag=false)
Definition: GeneralUtility.php:1288
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep(string $directory)
Definition: GeneralUtility.php:1654
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\validIpReturnsTrueForValidIp
‪static validIpReturnsTrueForValidIp(string $ip)
Definition: GeneralUtilityTest.php:254
‪TYPO3\CMS\Core\Core\Environment\getLegacyConfigPath
‪static getLegacyConfigPath()
Definition: Environment.php:268
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\TwoParametersConstructorFixture
Definition: TwoParametersConstructorFixture.php:24
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\ReplacementClassFixture
Definition: ReplacementClassFixture.php:23
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\inListForItemNotContainedReturnsFalse
‪inListForItemNotContainedReturnsFalse(string $haystack)
Definition: GeneralUtilityTest.php:359
‪TYPO3\CMS\Core\Core\Environment\getConfigPath
‪static getConfigPath()
Definition: Environment.php:212
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\expandListExpandsIntegerRangesDataProvider
‪static expandListExpandsIntegerRangesDataProvider()
Definition: GeneralUtilityTest.php:389
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:32
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\setUp
‪setUp()
Definition: GeneralUtilityTest.php:57
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\cmpFqdnReturnsFalse
‪static cmpFqdnReturnsFalse(string $baseHost, string $list)
Definition: GeneralUtilityTest.php:329
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: GeneralUtilityTest.php:51
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFileToTypo3tempDir
‪static string null writeFileToTypo3tempDir(string $filepath, string $content)
Definition: GeneralUtility.php:1561
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\cmpIPv6ReturnsFalseForNotMatchingAddress
‪static cmpIPv6ReturnsFalseForNotMatchingAddress(string $ip, string $list)
Definition: GeneralUtilityTest.php:202
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\cmpIPv4ReturnsTrueForMatchingAddress
‪static cmpIPv4ReturnsTrueForMatchingAddress(string $ip, string $list)
Definition: GeneralUtilityTest.php:121
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:160
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\cmpIPv4ReturnsFalseForNotMatchingAddress
‪static cmpIPv4ReturnsFalseForNotMatchingAddress(string $ip, string $list)
Definition: GeneralUtilityTest.php:145
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest
Definition: GeneralUtilityTest.php:48
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\tearDown
‪tearDown()
Definition: GeneralUtilityTest.php:63
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\cmpIPv6ReturnsTrueForMatchingAddress
‪static cmpIPv6ReturnsTrueForMatchingAddress(string $ip, string $list)
Definition: GeneralUtilityTest.php:176
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\setPackageManager
‪static setPackageManager(PackageManager $packageManager)
Definition: ExtensionManagementUtility.php:41
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\normalizeIPv6CorrectlyNormalizesAddresses
‪static normalizeIPv6CorrectlyNormalizesAddresses(string $compressed, string $normalized)
Definition: GeneralUtilityTest.php:229
‪TYPO3\CMS\Core\Utility\GeneralUtility\expandList
‪static string expandList($list)
Definition: GeneralUtility.php:434
‪TYPO3\CMS\Core\Package\Package
Definition: Package.php:30
‪TYPO3\CMS\Core\Utility\GeneralUtility\cmpIPv4
‪static bool cmpIPv4(string $baseIP, string $list)
Definition: GeneralUtility.php:135
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\getTestDirectory
‪getTestDirectory(string $prefix='root_')
Definition: GeneralUtilityTest.php:93
‪TYPO3\CMS\Core\Utility\GeneralUtility\rmdir
‪static bool rmdir(string $path, bool $removeNonEmpty=false)
Definition: GeneralUtility.php:1702
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Core\Core\Environment\initialize
‪static initialize(ApplicationContext $context, bool $cli, bool $composerMode, string $projectPath, string $publicPath, string $varPath, string $configPath, string $currentScript, string $os)
Definition: Environment.php:100
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:39
‪$_SERVER
‪$_SERVER['TYPO3_DEPRECATED_ENTRYPOINT']
Definition: legacy-backend.php:20
‪TYPO3\CMS\Core\Utility\GeneralUtility\cmpIPv6
‪static bool cmpIPv6(string $baseIP, string $list)
Definition: GeneralUtility.php:184
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixPermissions
‪static bool fixPermissions(string $path, bool $recursive=false)
Definition: GeneralUtility.php:1496
‪TYPO3\CMS\Core\Tests\Unit\Utility\AccessibleProxies\ExtensionManagementUtilityAccessibleProxy\getPackageManager
‪static getPackageManager()
Definition: ExtensionManagementUtilityAccessibleProxy.php:28
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\inListForItemContainedReturnsTrue
‪inListForItemContainedReturnsTrue(string $haystack)
Definition: GeneralUtilityTest.php:339
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\OriginalClassFixture
Definition: OriginalClassFixture.php:23
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Core\Utility\GeneralUtility\normalizeIPv6
‪static string normalizeIPv6(string $address)
Definition: GeneralUtility.php:239
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\GeneralUtility\revExplode
‪static list< string > revExplode(string $delimiter, string $string, int $limit=0)
Definition: GeneralUtility.php:787
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\splitCalcDataProvider
‪static array splitCalcDataProvider()
Definition: GeneralUtilityTest.php:471
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\OtherReplacementClassFixture
Definition: OtherReplacementClassFixture.php:23
‪TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityTestClass
Definition: GeneralUtilityTestClass.php:21
‪TYPO3\CMS\Core\Utility\GeneralUtility\isValidUrl
‪static bool isValidUrl(string $url)
Definition: GeneralUtility.php:713
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\$backupEnvironment
‪bool $backupEnvironment
Definition: GeneralUtilityTest.php:53
‪TYPO3\CMS\Core\Utility\GeneralUtility\validIP
‪static bool validIP(string $ip)
Definition: GeneralUtility.php:303
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\splitCalcCorrectlySplitsExpression
‪splitCalcCorrectlySplitsExpression(array $expected, string $expression)
Definition: GeneralUtilityTest.php:491
‪TYPO3\CMS\Core\Tests\Unit\Utility\AccessibleProxies\ExtensionManagementUtilityAccessibleProxy
Definition: ExtensionManagementUtilityAccessibleProxy.php:27
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\$backupPackageManager
‪PackageManager $backupPackageManager
Definition: GeneralUtilityTest.php:55
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\inListForItemContainedReturnsTrueDataProvider
‪static inListForItemContainedReturnsTrueDataProvider()
Definition: GeneralUtilityTest.php:347
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\formatSizeTranslatesBytesToHigherOrderRepresentation
‪formatSizeTranslatesBytesToHigherOrderRepresentation($size, $labels, $base, $expected)
Definition: GeneralUtilityTest.php:416
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder\REQUESTTYPE_FE
‪const REQUESTTYPE_FE
Definition: SystemEnvironmentBuilder.php:43
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\Core\Core\Environment\getContext
‪static getContext()
Definition: Environment.php:128
‪TYPO3\CMS\Core\Utility\GeneralUtility\isOnCurrentHost
‪static bool isOnCurrentHost(string $url)
Definition: GeneralUtility.php:409
‪TYPO3\CMS\Core\Utility\GeneralUtility\underscoredToUpperCamelCase
‪static string underscoredToUpperCamelCase($string)
Definition: GeneralUtility.php:659
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2array
‪static array string xml2array(string $string, string $NSprefix='', bool $reportDocTag=false)
Definition: GeneralUtility.php:1265
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\validIpReturnsFalseForInvalidIp
‪static validIpReturnsFalseForInvalidIp(string $ip)
Definition: GeneralUtilityTest.php:278
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\inListForItemNotContainedReturnsFalseDataProvider
‪static inListForItemNotContainedReturnsFalseDataProvider()
Definition: GeneralUtilityTest.php:367
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\expandListExpandsForTwoThousandElementsExpandsOnlyToThousandElementsMaximum
‪expandListExpandsForTwoThousandElementsExpandsOnlyToThousandElementsMaximum()
Definition: GeneralUtilityTest.php:405
‪TYPO3\CMS\Core\Tests\Unit\Utility\GeneralUtilityTest\expandListExpandsIntegerRanges
‪expandListExpandsIntegerRanges(string $list, string $expectation)
Definition: GeneralUtilityTest.php:381
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static isWindows()
Definition: Environment.php:276