‪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 hmac
1661  #[Test]
1662  public function hmacReturnsHashOfProperLength(): void
1663  {
1664  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
1665  $hmac = ‪GeneralUtility::hmac('message');
1666  self::assertTrue(!empty($hmac) && is_string($hmac));
1667  self::assertEquals(strlen($hmac), 40);
1668  }
1669 
1670  #[Test]
1671  public function hmacReturnsEqualHashesForEqualInput(): void
1672  {
1673  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
1674  $msg0 = 'message';
1675  $msg1 = 'message';
1676  self::assertEquals(‪GeneralUtility::hmac($msg0), ‪GeneralUtility::hmac($msg1));
1677  }
1678 
1679  #[Test]
1680  public function hmacReturnsNoEqualHashesForNonEqualInput(): void
1681  {
1682  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
1683  $msg0 = 'message0';
1684  $msg1 = 'message1';
1685  self::assertNotEquals(‪GeneralUtility::hmac($msg0), ‪GeneralUtility::hmac($msg1));
1686  }
1687 
1689  // Tests concerning quoteJSvalue
1691 
1694  public static function quoteJsValueDataProvider(): array
1695  {
1696  return [
1697  'Immune characters are returned as is' => [
1698  '._,',
1699  '._,',
1700  ],
1701  'Alphanumerical characters are returned as is' => [
1702  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
1703  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
1704  ],
1705  'Angle brackets and ampersand are encoded' => [
1706  '<>&',
1707  '\\u003C\\u003E\\u0026',
1708  ],
1709  'Quotes and backslashes are encoded' => [
1710  '"\'\\',
1711  '\\u0022\\u0027\\u005C',
1712  ],
1713  'Forward slashes are escaped' => [
1714  '</script>',
1715  '\\u003C\\/script\\u003E',
1716  ],
1717  'Empty string stays empty' => [
1718  '',
1719  '',
1720  ],
1721  'Exclamation mark and space are properly encoded' => [
1722  'Hello World!',
1723  'Hello\\u0020World\\u0021',
1724  ],
1725  'Whitespaces are properly encoded' => [
1726  "\t" . LF . CR . ' ',
1727  '\\u0009\\u000A\\u000D\\u0020',
1728  ],
1729  'Null byte is properly encoded' => [
1730  "\0",
1731  '\\u0000',
1732  ],
1733  'Umlauts are properly encoded' => [
1734  'ÜüÖöÄä',
1735  '\\u00dc\\u00fc\\u00d6\\u00f6\\u00c4\\u00e4',
1736  ],
1737  ];
1738  }
1739 
1740  #[DataProvider('quoteJsValueDataProvider')]
1741  #[Test]
1742  public function quoteJsValueTest(string $input, string $expected): void
1743  {
1744  self::assertSame('\'' . $expected . '\'', GeneralUtility::quoteJSvalue($input));
1745  }
1746 
1747  public static function jsonEncodeForHtmlAttributeTestDataProvider(): array
1748  {
1749  return [
1750  [
1751  ['html' => '<tag attr="\\Vendor\\Package">value</tag>'],
1752  true,
1753  // cave: `\\\\` (four) actually required for PHP only, will be `\\` (two) in HTML
1754  '{&quot;html&quot;:&quot;\u003Ctag attr=\u0022\\\\Vendor\\\\Package\u0022\u003Evalue\u003C\/tag\u003E&quot;}',
1755  ],
1756  [
1757  ['html' => '<tag attr="\\Vendor\\Package">value</tag>'],
1758  false,
1759  // cave: `\\\\` (four) actually required for PHP only, will be `\\` (two) in HTML
1760  '{"html":"\u003Ctag attr=\u0022\\\\Vendor\\\\Package\u0022\u003Evalue\u003C\/tag\u003E"}',
1761  ],
1762  [
1763  ['spaces' => '|' . chr(9) . '|' . chr(10) . '|' . chr(13) . '|'],
1764  false,
1765  '{"spaces":"|\t|\n|\r|"}',
1766  ],
1767  ];
1768  }
1769 
1770  #[DataProvider('jsonEncodeForHtmlAttributeTestDataProvider')]
1771  #[Test]
1772  public function jsonEncodeForHtmlAttributeTest($value, bool $useHtmlEntities, string $expectation): void
1773  {
1774  self::assertSame($expectation, GeneralUtility::jsonEncodeForHtmlAttribute($value, $useHtmlEntities));
1775  }
1776 
1777  public static function jsonEncodeForJavaScriptTestDataProvider(): array
1778  {
1779  return [
1780  [
1781  ['html' => '<tag attr="\\Vendor\\Package">value</tag>'],
1782  // cave: `\\\\` (four) actually required for PHP only, will be `\\` (two) in JavaScript
1783  '{"html":"\u003Ctag attr=\u0022\\\\u005CVendor\\\\u005CPackage\u0022\u003Evalue\u003C\/tag\u003E"}',
1784  ],
1785  [
1786  ['spaces' => '|' . chr(9) . '|' . chr(10) . '|' . chr(13) . '|'],
1787  '{"spaces":"|\u0009|\u000A|\u000D|"}',
1788  ],
1789  ];
1790  }
1791 
1792  #[DataProvider('jsonEncodeForJavaScriptTestDataProvider')]
1793  #[Test]
1794  public function jsonEncodeForJavaScriptTest($value, string $expectation): void
1795  {
1796  self::assertSame($expectation, GeneralUtility::jsonEncodeForJavaScript($value));
1797  }
1798 
1799  public static function sanitizeCssVariableValueDataProvider(): \Generator
1800  {
1801  yield 'double quotes' => ['url("/my-background.png")', 'url("/my-background.png")'];
1802  yield 'single quotes' => ["url('/my-background.png')", "url('/my-background.png')"];
1803  yield 'newline chars' => ["url('/my-background.png'\r\n\r\n)", "url('/my-background.png')"];
1804  yield 'HTML markup' => ['url(</style>)', 'url(&lt;/style&gt;)'];
1805  }
1806 
1807  #[DataProvider('sanitizeCssVariableValueDataProvider')]
1808  #[Test]
1809  public function sanitizeCssVariableValue(string $value, string $expectation): void
1810  {
1811  self::assertSame($expectation, GeneralUtility::sanitizeCssVariableValue($value));
1812  }
1813 
1814  #[Test]
1815  public function fixPermissionsSetsPermissionsToFile(): void
1816  {
1817  if (‪Environment::isWindows()) {
1818  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1819  }
1820  // Create and prepare test file
1821  $filename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1823  chmod($filename, 482);
1824  // Set target permissions and run method
1825  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1826  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1827  clearstatcache();
1828  self::assertTrue($fixPermissionsResult);
1829  self::assertEquals('0660', substr(decoct(fileperms($filename)), 2));
1830  }
1831 
1832  #[Test]
1833  public function fixPermissionsSetsPermissionsToHiddenFile(): void
1834  {
1835  if (‪Environment::isWindows()) {
1836  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1837  }
1838  // Create and prepare test file
1839  $filename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1841  chmod($filename, 482);
1842  // Set target permissions and run method
1843  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1844  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1845  clearstatcache();
1846  self::assertTrue($fixPermissionsResult);
1847  self::assertEquals('0660', substr(decoct(fileperms($filename)), 2));
1848  }
1849 
1850  #[Test]
1851  public function fixPermissionsSetsPermissionsToDirectory(): void
1852  {
1853  if (‪Environment::isWindows()) {
1854  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1855  }
1856  // Create and prepare test directory
1857  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1858  ‪GeneralUtility::mkdir($directory);
1859  chmod($directory, 1551);
1860  // Set target permissions and run method
1861  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1862  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory);
1863  clearstatcache();
1864  self::assertTrue($fixPermissionsResult);
1865  self::assertEquals('0770', substr(decoct(fileperms($directory)), 1));
1866  }
1867 
1868  #[Test]
1869  public function fixPermissionsSetsPermissionsToDirectoryWithTrailingSlash(): void
1870  {
1871  if (‪Environment::isWindows()) {
1872  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1873  }
1874  // Create and prepare test directory
1875  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1876  ‪GeneralUtility::mkdir($directory);
1877  chmod($directory, 1551);
1878  // Set target permissions and run method
1879  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1880  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory . '/');
1881  // Get actual permissions and clean up
1882  clearstatcache();
1883  self::assertTrue($fixPermissionsResult);
1884  self::assertEquals('0770', substr(decoct(fileperms($directory)), 1));
1885  }
1886 
1887  #[Test]
1888  public function fixPermissionsSetsPermissionsToHiddenDirectory(): void
1889  {
1890  if (‪Environment::isWindows()) {
1891  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1892  }
1893  // Create and prepare test directory
1894  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1895  ‪GeneralUtility::mkdir($directory);
1896  chmod($directory, 1551);
1897  // Set target permissions and run method
1898  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1899  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory);
1900  // Get actual permissions and clean up
1901  clearstatcache();
1902  self::assertTrue($fixPermissionsResult);
1903  self::assertEquals('0770', substr(decoct(fileperms($directory)), 1));
1904  }
1905 
1906  #[Test]
1907  public function fixPermissionsCorrectlySetsPermissionsRecursive(): void
1908  {
1909  if (‪Environment::isWindows()) {
1910  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1911  }
1912  // Create and prepare test directory and file structure
1915  chmod(‪$baseDirectory, 1751);
1917  chmod(‪$baseDirectory . '/file', 482);
1919  chmod(‪$baseDirectory . '/foo', 1751);
1921  chmod(‪$baseDirectory . '/foo/file', 482);
1923  chmod(‪$baseDirectory . '/.bar', 1751);
1924  // Use this if writeFileToTypo3tempDir is fixed to create hidden files in subdirectories
1925  // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/.file', '42');
1926  // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/..file2', '42');
1927  touch(‪$baseDirectory . '/.bar/.file', 42);
1928  chmod(‪$baseDirectory . '/.bar/.file', 482);
1929  touch(‪$baseDirectory . '/.bar/..file2', 42);
1930  chmod(‪$baseDirectory . '/.bar/..file2', 482);
1931  // Set target permissions and run method
1932  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1933  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
1934  $fixPermissionsResult = ‪GeneralUtility::fixPermissions(‪$baseDirectory, true);
1935  // Get actual permissions
1936  clearstatcache();
1937  $resultBaseDirectoryPermissions = substr(decoct(fileperms(‪$baseDirectory)), 1);
1938  $resultBaseFilePermissions = substr(decoct(fileperms(‪$baseDirectory . '/file')), 2);
1939  $resultFooDirectoryPermissions = substr(decoct(fileperms(‪$baseDirectory . '/foo')), 1);
1940  $resultFooFilePermissions = substr(decoct(fileperms(‪$baseDirectory . '/foo/file')), 2);
1941  $resultBarDirectoryPermissions = substr(decoct(fileperms(‪$baseDirectory . '/.bar')), 1);
1942  $resultBarFilePermissions = substr(decoct(fileperms(‪$baseDirectory . '/.bar/.file')), 2);
1943  $resultBarFile2Permissions = substr(decoct(fileperms(‪$baseDirectory . '/.bar/..file2')), 2);
1944  // Test if everything was ok
1945  self::assertTrue($fixPermissionsResult);
1946  self::assertEquals('0770', $resultBaseDirectoryPermissions);
1947  self::assertEquals('0660', $resultBaseFilePermissions);
1948  self::assertEquals('0770', $resultFooDirectoryPermissions);
1949  self::assertEquals('0660', $resultFooFilePermissions);
1950  self::assertEquals('0770', $resultBarDirectoryPermissions);
1951  self::assertEquals('0660', $resultBarFilePermissions);
1952  self::assertEquals('0660', $resultBarFile2Permissions);
1953  }
1954 
1955  #[Test]
1956  public function fixPermissionsDoesNotSetPermissionsToNotAllowedPath(): void
1957  {
1958  if (‪Environment::isWindows()) {
1959  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1960  }
1961  // Create and prepare test file
1962  $filename = ‪Environment::getVarPath() . '/tests/../../../typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_');
1963  // Set target permissions and run method
1964  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1965  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1966  self::assertFalse($fixPermissionsResult);
1967  }
1968 
1969  #[Test]
1970  public function fixPermissionsSetsPermissionsWithRelativeFileReference(): void
1971  {
1972  if (‪Environment::isWindows()) {
1973  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1974  }
1975  $filename = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_');
1977  $this->testFilesToDelete[] = ‪Environment::getPublicPath() . '/' . $filename;
1978  chmod(‪Environment::getPublicPath() . '/' . $filename, 482);
1979  // Set target permissions and run method
1980  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
1981  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1982  clearstatcache();
1983  self::assertTrue($fixPermissionsResult);
1984  self::assertEquals('0660', substr(decoct(fileperms(‪Environment::getPublicPath() . '/' . $filename)), 2));
1985  }
1986 
1987  #[Test]
1988  public function fixPermissionsSetsDefaultPermissionsToFile(): void
1989  {
1990  if (‪Environment::isWindows()) {
1991  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
1992  }
1993  $filename = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
1995  chmod($filename, 482);
1996  unset(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']);
1997  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($filename);
1998  clearstatcache();
1999  self::assertTrue($fixPermissionsResult);
2000  self::assertEquals('0644', substr(decoct(fileperms($filename)), 2));
2001  }
2002 
2003  #[Test]
2004  public function fixPermissionsSetsDefaultPermissionsToDirectory(): void
2005  {
2006  if (‪Environment::isWindows()) {
2007  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2008  }
2009  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
2010  ‪GeneralUtility::mkdir($directory);
2011  chmod($directory, 1551);
2012  unset(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2013  $fixPermissionsResult = ‪GeneralUtility::fixPermissions($directory);
2014  clearstatcache();
2015  self::assertTrue($fixPermissionsResult);
2016  self::assertEquals('0755', substr(decoct(fileperms($directory)), 1));
2017  }
2018 
2020  // Tests concerning mkdir
2022  #[Test]
2023  public function mkdirCreatesDirectory(): void
2024  {
2025  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
2026  $mkdirResult = ‪GeneralUtility::mkdir($directory);
2027  clearstatcache();
2028  self::assertTrue($mkdirResult);
2029  self::assertDirectoryExists($directory);
2030  }
2031 
2032  #[Test]
2033  public function mkdirCreatesHiddenDirectory(): void
2034  {
2035  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('.test_');
2036  $mkdirResult = ‪GeneralUtility::mkdir($directory);
2037  clearstatcache();
2038  self::assertTrue($mkdirResult);
2039  self::assertDirectoryExists($directory);
2040  }
2041 
2042  #[Test]
2043  public function mkdirCreatesDirectoryWithTrailingSlash(): void
2044  {
2045  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_') . '/';
2046  $mkdirResult = ‪GeneralUtility::mkdir($directory);
2047  clearstatcache();
2048  self::assertTrue($mkdirResult);
2049  self::assertDirectoryExists($directory);
2050  }
2051 
2052  #[Test]
2053  public function mkdirSetsPermissionsOfCreatedDirectory(): void
2054  {
2055  if (‪Environment::isWindows()) {
2056  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2057  }
2058  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
2059  $oldUmask = umask(19);
2060  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0772';
2061  ‪GeneralUtility::mkdir($directory);
2062  clearstatcache();
2063  $resultDirectoryPermissions = substr(decoct(fileperms($directory)), 1);
2064  umask($oldUmask);
2065  self::assertEquals('0772', $resultDirectoryPermissions);
2066  }
2067 
2069  // Tests concerning writeFileToTypo3tempDir()
2071 
2075  public static function invalidFilePathForTypo3tempDirDataProvider(): array
2076  {
2077  return [
2078  [
2079  ‪Environment::getPublicPath() . '/../path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more',
2080  'Input filepath "' . ‪Environment::getPublicPath() . '/../path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more" was generally invalid!',
2081  '',
2082  ],
2083  [
2084  ‪Environment::getPublicPath() . '/dummy/path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more',
2085  '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!',
2086  '',
2087  ],
2088  [
2089  ‪Environment::getPublicPath() . '/dummy/path/this-path-has-more-than-60-characters-in-one-base-path-you-can-even-count-more',
2090  '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!',
2091  '',
2092  ],
2093  [
2094  '/dummy/path/awesome',
2095  '"/dummy/path/" was not within directory Environment::getPublicPath() + "/typo3temp/"',
2096  '',
2097  ],
2098  [
2100  '"' . ‪Environment::getLegacyConfigPath() . '/" was not within directory Environment::getPublicPath() + "/typo3temp/"',
2101  '',
2102  ],
2103  [
2104  ‪Environment::getPublicPath() . '/typo3temp/táylor/swíft',
2105  'Subdir, "táylor/", was NOT on the form "[[:alnum:]_]/+"',
2106  '',
2107  ],
2108  'Path instead of file given' => [
2109  ‪Environment::getPublicPath() . '/typo3temp/dummy/path/',
2110  'Calculated file location didn\'t match input "' . ‪Environment::getPublicPath() . '/typo3temp/dummy/path/".',
2111  ‪Environment::getPublicPath() . '/typo3temp/dummy/',
2112  ],
2113  ];
2114  }
2115 
2116  #[DataProvider('invalidFilePathForTypo3tempDirDataProvider')]
2117  #[Test]
2118  public function writeFileToTypo3tempDirFailsWithInvalidPath(string $invalidFilePath, string $expectedResult, string $pathToCleanUp): void
2119  {
2120  if ($pathToCleanUp !== '') {
2121  $this->testFilesToDelete[] = $pathToCleanUp;
2122  }
2123  $result = ‪GeneralUtility::writeFileToTypo3tempDir($invalidFilePath, 'dummy content to be written');
2124  self::assertSame($result, $expectedResult);
2125  }
2126 
2131  public static function validFilePathForTypo3tempDirDataProvider(): array
2132  {
2133  return [
2134  'Default text file' => [
2135  ‪Environment::getVarPath() . '/tests/paranoid/android.txt',
2136  ‪Environment::getVarPath() . '/tests/',
2137  ],
2138  'Html file extension' => [
2139  ‪Environment::getVarPath() . '/tests/karma.html',
2140  ‪Environment::getVarPath() . '/tests/',
2141  ],
2142  'No file extension' => [
2143  ‪Environment::getVarPath() . '/tests/no-surprises',
2144  ‪Environment::getVarPath() . '/tests/',
2145  ],
2146  'Deep directory' => [
2147  ‪Environment::getVarPath() . '/tests/climbing/up/the/walls',
2148  ‪Environment::getVarPath() . '/tests/',
2149  ],
2150  'File in typo3temp/var directory' => [
2151  ‪Environment::getPublicPath() . '/typo3temp/var/path/foo.txt',
2152  ‪Environment::getPublicPath() . '/typo3temp/var/path',
2153  ],
2154  ];
2155  }
2156 
2161  #[DataProvider('validFilePathForTypo3tempDirDataProvider')]
2162  #[Test]
2163  public function writeFileToTypo3tempDirWorksWithValidPath(string $filePath, string $pathToCleanUp): void
2164  {
2165  if ($pathToCleanUp !== '') {
2166  $this->testFilesToDelete[] = $pathToCleanUp;
2167  }
2168 
2169  $dummyContent = 'Please could you stop the noise, I\'m trying to get some rest from all the unborn chicken voices in my head.';
2170 
2171  $result = ‪GeneralUtility::writeFileToTypo3tempDir($filePath, $dummyContent);
2172 
2173  self::assertNull($result);
2174  self::assertFileExists($filePath);
2175  self::assertStringEqualsFile($filePath, $dummyContent);
2176  }
2177 
2179  // Tests concerning mkdir_deep
2181  #[Test]
2182  public function mkdirDeepCreatesDirectory(): void
2183  {
2184  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('test_');
2185  ‪GeneralUtility::mkdir_deep($directory);
2186  self::assertDirectoryExists($directory);
2187  }
2188 
2189  #[Test]
2190  public function mkdirDeepCreatesSubdirectoriesRecursive(): void
2191  {
2192  $directory = $this->‪getTestDirectory() . '/typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_');
2193  $subDirectory = $directory . '/foo';
2194  ‪GeneralUtility::mkdir_deep($subDirectory);
2195  self::assertDirectoryExists($subDirectory);
2196  }
2197 
2201  public static function mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider(): array
2202  {
2203  return [
2204  'no double slash if concatenated with Environment::getPublicPath()' => ['fileadmin/testDir1'],
2205  'double slash if concatenated with Environment::getPublicPath()' => ['/fileadmin/testDir2'],
2206  ];
2207  }
2208 
2209  #[DataProvider('mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider')]
2210  #[Test]
2211  public function mkdirDeepCreatesDirectoryWithDoubleSlashes($directoryToCreate): void
2212  {
2213  $testRoot = ‪Environment::getVarPath() . '/public/';
2214  $this->testFilesToDelete[] = $testRoot;
2215  $directory = $testRoot . $directoryToCreate;
2216  ‪GeneralUtility::mkdir_deep($directory);
2217  self::assertDirectoryExists($directory);
2218  }
2219 
2220  #[Test]
2221  public function mkdirDeepFixesPermissionsOfCreatedDirectory(): void
2222  {
2223  if (‪Environment::isWindows()) {
2224  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2225  }
2226  $directory = ‪StringUtility::getUniqueId('mkdirdeeptest_');
2227  $oldUmask = umask(19);
2228  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
2229  ‪GeneralUtility::mkdir_deep(‪Environment::getVarPath() . '/tests/' . $directory);
2230  $this->testFilesToDelete[] = ‪Environment::getVarPath() . '/tests/' . $directory;
2231  clearstatcache();
2232  umask($oldUmask);
2233  self::assertEquals('777', substr(decoct(fileperms(‪Environment::getVarPath() . '/tests/' . $directory)), -3, 3));
2234  }
2235 
2236  #[Test]
2237  public function mkdirDeepFixesPermissionsOnNewParentDirectory(): void
2238  {
2239  if (‪Environment::isWindows()) {
2240  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2241  }
2242  $directory = ‪StringUtility::getUniqueId('mkdirdeeptest_');
2243  $subDirectory = $directory . '/bar';
2244  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
2245  $oldUmask = umask(19);
2246  ‪GeneralUtility::mkdir_deep(‪Environment::getVarPath() . '/tests/' . $subDirectory);
2247  $this->testFilesToDelete[] = ‪Environment::getVarPath() . '/tests/' . $directory;
2248  clearstatcache();
2249  umask($oldUmask);
2250  self::assertEquals('777', substr(decoct(fileperms(‪Environment::getVarPath() . '/tests/' . $directory)), -3, 3));
2251  }
2252 
2253  #[Test]
2254  public function mkdirDeepDoesNotChangePermissionsOfExistingSubDirectories(): void
2255  {
2256  if (‪Environment::isWindows()) {
2257  self::markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2258  }
2260  $existingDirectory = ‪StringUtility::getUniqueId('test_existing_') . '/';
2261  $newSubDirectory = ‪StringUtility::getUniqueId('test_new_');
2262  @mkdir(‪$baseDirectory . $existingDirectory);
2263  $this->testFilesToDelete[] = ‪$baseDirectory . $existingDirectory;
2264  chmod(‪$baseDirectory . $existingDirectory, 482);
2265  ‪GeneralUtility::mkdir_deep(‪$baseDirectory . $existingDirectory . $newSubDirectory);
2266  self::assertEquals(742, (int)substr(decoct(fileperms(‪$baseDirectory . $existingDirectory)), 2));
2267  }
2268 
2269  #[Test]
2270  public function mkdirDeepThrowsExceptionIfDirectoryCreationFails(): void
2271  {
2272  $this->expectException(\RuntimeException::class);
2273  $this->expectExceptionCode(1170251401);
2274 
2275  ‪GeneralUtility::mkdir_deep('http://localhost');
2276  }
2277 
2279  // Tests concerning rmdir
2281  #[Test]
2282  public function rmdirRemovesFile(): void
2283  {
2284  $testRoot = ‪Environment::getVarPath() . '/tests/';
2285  $this->testFilesToDelete[] = $testRoot;
2286  ‪GeneralUtility::mkdir_deep($testRoot);
2287  $file = $testRoot . ‪StringUtility::getUniqueId('file_');
2288  touch($file);
2289  ‪GeneralUtility::rmdir($file);
2290  self::assertFileDoesNotExist($file);
2291  }
2292 
2293  #[Test]
2294  public function rmdirReturnTrueIfFileWasRemoved(): void
2295  {
2296  $file = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('file_');
2297  touch($file);
2298  self::assertTrue(‪GeneralUtility::rmdir($file));
2299  }
2300 
2301  #[Test]
2302  public function rmdirReturnFalseIfNoFileWasRemoved(): void
2303  {
2304  $file = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('file_');
2305  self::assertFalse(‪GeneralUtility::rmdir($file));
2306  }
2307 
2308  #[Test]
2309  public function rmdirRemovesDirectory(): void
2310  {
2311  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_');
2312  mkdir($directory);
2313  ‪GeneralUtility::rmdir($directory);
2314  self::assertFileDoesNotExist($directory);
2315  }
2316 
2317  #[Test]
2318  public function rmdirRemovesDirectoryWithTrailingSlash(): void
2319  {
2320  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_') . '/';
2321  ‪GeneralUtility::mkdir_deep($directory);
2322  ‪GeneralUtility::rmdir($directory);
2323  self::assertFileDoesNotExist($directory);
2324  }
2325 
2326  #[Test]
2327  public function rmdirDoesNotRemoveDirectoryWithFilesAndReturnsFalseIfRecursiveDeletionIsOff(): void
2328  {
2329  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_') . '/';
2330  ‪GeneralUtility::mkdir_deep($directory);
2331  $file = ‪StringUtility::getUniqueId('file_');
2332  touch($directory . $file);
2333  $return = ‪GeneralUtility::rmdir($directory);
2334  self::assertFileExists($directory);
2335  self::assertFileExists($directory . $file);
2336  self::assertFalse($return);
2337  }
2338 
2339  #[Test]
2340  public function rmdirRemovesDirectoriesRecursiveAndReturnsTrue(): void
2341  {
2342  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_') . '/';
2343  mkdir($directory);
2344  mkdir($directory . 'sub/');
2345  touch($directory . 'sub/file');
2346  $return = ‪GeneralUtility::rmdir($directory, true);
2347  self::assertFileDoesNotExist($directory);
2348  self::assertTrue($return);
2349  }
2350 
2351  #[Test]
2352  public function rmdirRemovesLinkToDirectory(): void
2353  {
2354  $existingDirectory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('notExists_') . '/';
2355  mkdir($existingDirectory);
2356  $symlinkName = ‪Environment::getVarPath() . '/tests/' . ‪StringUtility::getUniqueId('link_');
2357  symlink($existingDirectory, $symlinkName);
2358  ‪GeneralUtility::rmdir($symlinkName, true);
2359  self::assertFalse(is_link($symlinkName));
2360  }
2361 
2362  #[Test]
2363  public function rmdirRemovesDeadLinkToDirectory(): void
2364  {
2365  $notExistingDirectory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('notExists_') . '/';
2366  $symlinkName = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('link_');
2367  ‪GeneralUtility::mkdir_deep($notExistingDirectory);
2368  symlink($notExistingDirectory, $symlinkName);
2369  rmdir($notExistingDirectory);
2370 
2371  ‪GeneralUtility::rmdir($symlinkName, true);
2372  self::assertFalse(is_link($symlinkName));
2373  }
2374 
2375  #[Test]
2376  public function rmdirRemovesDeadLinkToFile(): void
2377  {
2378  $testDirectory = $this->‪getTestDirectory() . '/';
2379  $notExistingFile = $testDirectory . ‪StringUtility::getUniqueId('notExists_');
2380  $symlinkName = $testDirectory . ‪StringUtility::getUniqueId('link_');
2381  touch($notExistingFile);
2382  symlink($notExistingFile, $symlinkName);
2383  unlink($notExistingFile);
2384  ‪GeneralUtility::rmdir($symlinkName, true);
2385  self::assertFalse(is_link($symlinkName));
2386  }
2387 
2389  // Tests concerning getFilesInDir
2391 
2396  protected function getFilesInDirCreateTestDirectory(): string
2397  {
2398  $path = ‪Environment::getVarPath() . '/FilesInDirTests';
2399  $this->testFilesToDelete[] = $path;
2400  mkdir($path);
2401  mkdir($path . '/subDirectory');
2402  file_put_contents($path . '/subDirectory/test.php', 'butter');
2403  file_put_contents($path . '/subDirectory/other.php', 'milk');
2404  file_put_contents($path . '/subDirectory/stuff.csv', 'honey');
2405  mkdir($path . '/beStylesheet');
2406  file_put_contents($path . '/beStylesheet/backend.css', '.topbar-header-site { color: red; }');
2407  file_put_contents($path . '/beStylesheet/backend.scss', '.topbar-header-site { color: green; }');
2408  file_put_contents($path . '/excludeMe.txt', 'cocoa nibs');
2409  file_put_contents($path . '/double.setup.typoscript', 'cool TS');
2410  file_put_contents($path . '/testB.txt', 'olive oil');
2411  file_put_contents($path . '/testA.txt', 'eggs');
2412  file_put_contents($path . '/testC.txt', 'carrots');
2413  file_put_contents($path . '/test.js', 'oranges');
2414  file_put_contents($path . '/test.css', 'apples');
2415  file_put_contents($path . '/.secret.txt', 'sammon');
2416  return $path;
2417  }
2418 
2419  #[Test]
2420  public function getFilesInDirFindsRegularFile(): void
2421  {
2422  $path = $this->getFilesInDirCreateTestDirectory();
2423  $files = GeneralUtility::getFilesInDir($path);
2424  self::assertContains('testA.txt', $files);
2425  }
2426 
2427  #[Test]
2428  public function getFilesInDirFindsHiddenFile(): void
2429  {
2430  $path = $this->getFilesInDirCreateTestDirectory();
2431  $files = GeneralUtility::getFilesInDir($path);
2432  self::assertContains('.secret.txt', $files);
2433  }
2434 
2435  #[Test]
2436  public function getFilesInDirOnlyFindWithMatchingExtension(): void
2437  {
2438  $path = $this->getFilesInDirCreateTestDirectory();
2439  $files = GeneralUtility::getFilesInDir($path . '/beStylesheet', 'css');
2440  self::assertContains('backend.css', $files);
2441  self::assertNotContains('backend.scss', $files);
2442  }
2443 
2447  public static function fileExtensionDataProvider(): array
2448  {
2449  return [
2450  'no space' => [
2451  'setup.typoscript,txt,js,css',
2452  ],
2453  'spaces' => [
2454  'setup.typoscript, txt, js, css',
2455  ],
2456  'mixed' => [
2457  'setup.typoscript , txt,js, css',
2458  ],
2459  'wild' => [
2460  'setup.typoscript, txt, js , css',
2461  ],
2462  ];
2463  }
2464 
2465  #[DataProvider('fileExtensionDataProvider')]
2466  #[Test]
2467  public function getFilesInDirByExtensionFindsFiles($fileExtensions): void
2468  {
2469  $path = $this->getFilesInDirCreateTestDirectory();
2470  $files = GeneralUtility::getFilesInDir($path, $fileExtensions);
2471  self::assertContains('double.setup.typoscript', $files);
2472  self::assertContains('testA.txt', $files);
2473  self::assertContains('test.js', $files);
2474  self::assertContains('test.css', $files);
2475  }
2476 
2477  #[Test]
2478  public function getFilesInDirByExtensionDoesNotFindFilesWithOtherExtensions(): void
2479  {
2480  $path = $this->getFilesInDirCreateTestDirectory();
2481  $files = GeneralUtility::getFilesInDir($path, 'txt,js');
2482  self::assertContains('testA.txt', $files);
2483  self::assertContains('test.js', $files);
2484  self::assertNotContains('test.css', $files);
2485  }
2486 
2487  #[Test]
2488  public function getFilesInDirExcludesFilesMatchingPattern(): void
2489  {
2490  $path = $this->getFilesInDirCreateTestDirectory();
2491  $files = GeneralUtility::getFilesInDir($path, '', false, '', 'excludeMe.*');
2492  self::assertContains('test.js', $files);
2493  self::assertNotContains('excludeMe.txt', $files);
2494  }
2495 
2496  #[Test]
2497  public function getFilesInDirCanPrependPath(): void
2498  {
2499  $path = $this->getFilesInDirCreateTestDirectory();
2500  self::assertContains(
2501  $path . '/testA.txt',
2502  GeneralUtility::getFilesInDir($path, '', true)
2503  );
2504  }
2505 
2506  #[Test]
2507  public function getFilesInDirDoesSortAlphabeticallyByDefault(): void
2508  {
2509  $path = $this->getFilesInDirCreateTestDirectory();
2510  self::assertSame(
2511  ['.secret.txt', 'double.setup.typoscript', 'excludeMe.txt', 'test.css', 'test.js', 'testA.txt', 'testB.txt', 'testC.txt'],
2512  array_values(GeneralUtility::getFilesInDir($path))
2513  );
2514  }
2515 
2516  #[Test]
2517  public function getFilesInDirReturnsArrayWithMd5OfElementAndPathAsArrayKey(): void
2518  {
2519  $path = $this->getFilesInDirCreateTestDirectory();
2520  self::assertArrayHasKey(
2521  md5($path . '/testA.txt'),
2522  GeneralUtility::getFilesInDir($path)
2523  );
2524  }
2525 
2526  #[Test]
2527  public function getFilesInDirDoesNotFindDirectories(): void
2528  {
2529  $path = $this->getFilesInDirCreateTestDirectory();
2530  self::assertNotContains(
2531  'subDirectory',
2532  GeneralUtility::getFilesInDir($path)
2533  );
2534  }
2535 
2540  #[Test]
2541  public function getFilesInDirDoesNotFindDotfiles(): void
2542  {
2543  $path = $this->getFilesInDirCreateTestDirectory();
2544  $files = GeneralUtility::getFilesInDir($path);
2545  self::assertNotContains('..', $files);
2546  self::assertNotContains('.', $files);
2547  }
2548 
2550  // Tests concerning split_fileref
2552  #[Test]
2553  public function splitFileRefReturnsFileTypeNotForFolders(): void
2554  {
2555  $directoryName = ‪StringUtility::getUniqueId('test_') . '.com';
2556  $directoryPath = ‪Environment::getVarPath() . '/tests/';
2557  @mkdir($directoryPath, octdec(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2558  $directory = $directoryPath . $directoryName;
2559  mkdir($directory, octdec(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2560  $fileInfo = GeneralUtility::split_fileref($directory);
2561  $directoryCreated = is_dir($directory);
2562  rmdir($directory);
2563  self::assertTrue($directoryCreated);
2564  self::assertIsArray($fileInfo);
2565  self::assertEquals($directoryPath, $fileInfo['path']);
2566  self::assertEquals($directoryName, $fileInfo['file']);
2567  self::assertEquals($directoryName, $fileInfo['filebody']);
2568  self::assertEquals('', $fileInfo['fileext']);
2569  self::assertArrayNotHasKey('realFileext', $fileInfo);
2570  }
2571 
2572  #[Test]
2573  public function splitFileRefReturnsFileTypeForFilesWithoutPathSite(): void
2574  {
2575  $testFile = 'fileadmin/media/someFile.png';
2576  $fileInfo = GeneralUtility::split_fileref($testFile);
2577  self::assertIsArray($fileInfo);
2578  self::assertEquals('fileadmin/media/', $fileInfo['path']);
2579  self::assertEquals('someFile.png', $fileInfo['file']);
2580  self::assertEquals('someFile', $fileInfo['filebody']);
2581  self::assertEquals('png', $fileInfo['fileext']);
2582  }
2583 
2585  // Tests concerning dirname
2587 
2591  public static function dirnameDataProvider(): array
2592  {
2593  return [
2594  'absolute path with multiple part and file' => ['/dir1/dir2/script.php', '/dir1/dir2'],
2595  'absolute path with one part' => ['/dir1/', '/dir1'],
2596  'absolute path to file without extension' => ['/dir1/something', '/dir1'],
2597  'relative path with one part and file' => ['dir1/script.php', 'dir1'],
2598  'relative one-character path with one part and file' => ['d/script.php', 'd'],
2599  'absolute zero-part path with file' => ['/script.php', ''],
2600  'empty string' => ['', ''],
2601  ];
2602  }
2603 
2608  #[DataProvider('dirnameDataProvider')]
2609  #[Test]
2610  public function dirnameWithDataProvider(string $input, string $expectedValue): void
2611  {
2612  self::assertEquals($expectedValue, GeneralUtility::dirname($input));
2613  }
2614 
2616  // Tests concerning resolveBackPath
2618 
2622  public static function resolveBackPathDataProvider(): array
2623  {
2624  return [
2625  'empty path' => ['', ''],
2626  'this directory' => ['./', './'],
2627  'relative directory without ..' => ['dir1/dir2/dir3/', 'dir1/dir2/dir3/'],
2628  'relative path without ..' => ['dir1/dir2/script.php', 'dir1/dir2/script.php'],
2629  'absolute directory without ..' => ['/dir1/dir2/dir3/', '/dir1/dir2/dir3/'],
2630  'absolute path without ..' => ['/dir1/dir2/script.php', '/dir1/dir2/script.php'],
2631  'only one directory upwards without trailing slash' => ['..', '..'],
2632  'only one directory upwards with trailing slash' => ['../', '../'],
2633  'one level with trailing ..' => ['dir1/..', ''],
2634  'one level with trailing ../' => ['dir1/../', ''],
2635  'two levels with trailing ..' => ['dir1/dir2/..', 'dir1'],
2636  'two levels with trailing ../' => ['dir1/dir2/../', 'dir1/'],
2637  'leading ../ without trailing /' => ['../dir1', '../dir1'],
2638  'leading ../ with trailing /' => ['../dir1/', '../dir1/'],
2639  'leading ../ and inside path' => ['../dir1/dir2/../dir3/', '../dir1/dir3/'],
2640  'one times ../ in relative directory' => ['dir1/../dir2/', 'dir2/'],
2641  'one times ../ in absolute directory' => ['/dir1/../dir2/', '/dir2/'],
2642  'one times ../ in relative path' => ['dir1/../dir2/script.php', 'dir2/script.php'],
2643  'one times ../ in absolute path' => ['/dir1/../dir2/script.php', '/dir2/script.php'],
2644  'consecutive ../' => ['dir1/dir2/dir3/../../../dir4', 'dir4'],
2645  'distributed ../ with trailing /' => ['dir1/../dir2/dir3/../', 'dir2/'],
2646  'distributed ../ without trailing /' => ['dir1/../dir2/dir3/..', 'dir2'],
2647  'multiple distributed and consecutive ../ together' => ['dir1/dir2/dir3/dir4/../../dir5/dir6/dir7/../dir8/', 'dir1/dir2/dir5/dir6/dir8/'],
2648  'dirname with leading ..' => ['dir1/..dir2/dir3/', 'dir1/..dir2/dir3/'],
2649  'dirname with trailing ..' => ['dir1/dir2../dir3/', 'dir1/dir2../dir3/'],
2650  'more times upwards than downwards in directory' => ['dir1/../../', '../'],
2651  'more times upwards than downwards in path' => ['dir1/../../script.php', '../script.php'],
2652  ];
2653  }
2654 
2659  #[DataProvider('resolveBackPathDataProvider')]
2660  #[Test]
2661  public function resolveBackPathWithDataProvider(string $input, string $expectedValue): void
2662  {
2663  self::assertEquals($expectedValue, GeneralUtility::resolveBackPath($input));
2664  }
2665 
2667  // Tests concerning makeInstance, setSingletonInstance, addInstance, purgeInstances
2669  #[Test]
2670  public function makeInstanceWithEmptyClassNameThrowsException(): void
2671  {
2672  $this->expectException(\InvalidArgumentException::class);
2673  $this->expectExceptionCode(1288965219);
2674 
2675  // @phpstan-ignore-next-line We're explicitly checking the behavior for a contract violation.
2676  GeneralUtility::makeInstance('');
2677  }
2678 
2679  #[Test]
2680  public function makeInstanceWithBeginningSlashInClassNameThrowsException(): void
2681  {
2682  $this->expectException(\InvalidArgumentException::class);
2683  $this->expectExceptionCode(1420281366);
2684 
2685  GeneralUtility::makeInstance('\\TYPO3\\CMS\\Backend\\Controller\\BackendController');
2686  }
2687 
2688  #[Test]
2689  public function makeInstanceReturnsClassInstance(): void
2690  {
2691  self::assertInstanceOf(\stdClass::class, GeneralUtility::makeInstance(\stdClass::class));
2692  }
2693 
2694  #[Test]
2695  public function makeInstancePassesParametersToConstructor(): void
2696  {
2697  $instance = GeneralUtility::makeInstance(TwoParametersConstructorFixture::class, 'one parameter', 'another parameter');
2698  self::assertEquals('one parameter', $instance->constructorParameter1, 'The first constructor parameter has not been set.');
2699  self::assertEquals('another parameter', $instance->constructorParameter2, 'The second constructor parameter has not been set.');
2700  }
2701 
2702  #[Test]
2703  public function makeInstanceInstanciatesConfiguredImplementation(): void
2704  {
2705  GeneralUtility::flushInternalRuntimeCaches();
2706  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalClassFixture::class] = ['className' => ReplacementClassFixture::class];
2707  self::assertInstanceOf(ReplacementClassFixture::class, GeneralUtility::makeInstance(OriginalClassFixture::class));
2708  }
2709 
2710  #[Test]
2711  public function makeInstanceResolvesConfiguredImplementationsRecursively(): void
2712  {
2713  GeneralUtility::flushInternalRuntimeCaches();
2714  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][OriginalClassFixture::class] = ['className' => ReplacementClassFixture::class];
2715  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][ReplacementClassFixture::class] = ['className' => OtherReplacementClassFixture::class];
2716  self::assertInstanceOf(OtherReplacementClassFixture::class, GeneralUtility::makeInstance(OriginalClassFixture::class));
2717  }
2718 
2719  #[Test]
2720  public function makeInstanceCalledTwoTimesForNonSingletonClassReturnsDifferentInstances(): void
2721  {
2722  $className = \stdClass::class;
2723  self::assertNotSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
2724  }
2725 
2726  #[Test]
2727  public function makeInstanceCalledTwoTimesForSingletonClassReturnsSameInstance(): void
2728  {
2729  $className = get_class($this->createMock(SingletonInterface::class));
2730  self::assertSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
2731  }
2732 
2733  #[Test]
2734  public function makeInstanceCalledTwoTimesForSingletonClassWithPurgeInstancesInbetweenReturnsDifferentInstances(): void
2735  {
2736  $className = get_class($this->createMock(SingletonInterface::class));
2737  $instance = GeneralUtility::makeInstance($className);
2738  GeneralUtility::purgeInstances();
2739  self::assertNotSame($instance, GeneralUtility::makeInstance($className));
2740  }
2741 
2742  #[Test]
2743  public function makeInstanceInjectsLogger(): void
2744  {
2745  $instance = GeneralUtility::makeInstance(GeneralUtilityMakeInstanceInjectLoggerFixture::class);
2746  self::assertInstanceOf(LoggerInterface::class, $instance->getLogger());
2747  }
2748 
2749  #[Test]
2750  public function setSingletonInstanceForEmptyClassNameThrowsException(): void
2751  {
2752  $this->expectException(\InvalidArgumentException::class);
2753  $this->expectExceptionCode(1288967479);
2754 
2755  $instance = $this->createMock(SingletonInterface::class);
2756  // @phpstan-ignore-next-line We are explicitly testing with a contract violation here.
2757  GeneralUtility::setSingletonInstance('', $instance);
2758  }
2759 
2760  #[Test]
2761  public function setSingletonInstanceForClassThatIsNoSubclassOfProvidedClassThrowsException(): void
2762  {
2763  $this->expectException(\InvalidArgumentException::class);
2764  $this->expectExceptionCode(1288967686);
2765  $instance = $this->getMockBuilder(SingletonInterface::class)->getMock();
2766  $singletonClassName = get_class($this->createMock(SingletonInterface::class));
2767  GeneralUtility::setSingletonInstance($singletonClassName, $instance);
2768  }
2769 
2770  #[Test]
2771  public function setSingletonInstanceMakesMakeInstanceReturnThatInstance(): void
2772  {
2773  $instance = $this->createMock(SingletonInterface::class);
2774  $singletonClassName = get_class($instance);
2775  GeneralUtility::setSingletonInstance($singletonClassName, $instance);
2776  self::assertSame($instance, GeneralUtility::makeInstance($singletonClassName));
2777  }
2778 
2779  #[Test]
2780  public function setSingletonInstanceCalledTwoTimesMakesMakeInstanceReturnLastSetInstance(): void
2781  {
2782  $instance1 = $this->createMock(SingletonInterface::class);
2783  $singletonClassName = get_class($instance1);
2784  $instance2 = new $singletonClassName();
2785  GeneralUtility::setSingletonInstance($singletonClassName, $instance1);
2786  GeneralUtility::setSingletonInstance($singletonClassName, $instance2);
2787  self::assertSame($instance2, GeneralUtility::makeInstance($singletonClassName));
2788  }
2789 
2790  #[Test]
2791  public function getSingletonInstancesContainsPreviouslySetSingletonInstance(): void
2792  {
2793  $instance = $this->createMock(SingletonInterface::class);
2794  $instanceClassName = get_class($instance);
2795  GeneralUtility::setSingletonInstance($instanceClassName, $instance);
2796  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
2797  self::assertArrayHasKey($instanceClassName, $registeredSingletonInstances);
2798  self::assertSame($registeredSingletonInstances[$instanceClassName], $instance);
2799  }
2800 
2801  #[Test]
2802  public function setSingletonInstanceReturnsFinalClassNameWithOverriddenClass(): void
2803  {
2804  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][SingletonClassFixture::class]['className'] = ExtendedSingletonClassFixture::class;
2805  $anotherInstance = new ExtendedSingletonClassFixture();
2806  GeneralUtility::makeInstance(SingletonClassFixture::class);
2807  GeneralUtility::setSingletonInstance(SingletonClassFixture::class, $anotherInstance);
2808  $result = GeneralUtility::makeInstance(SingletonClassFixture::class);
2809  self::assertSame($anotherInstance, $result);
2810  self::assertEquals(ExtendedSingletonClassFixture::class, get_class($anotherInstance));
2811  }
2812 
2813  #[Test]
2814  public function resetSingletonInstancesResetsPreviouslySetInstance(): void
2815  {
2816  $instance = $this->createMock(SingletonInterface::class);
2817  $instanceClassName = get_class($instance);
2818  GeneralUtility::setSingletonInstance($instanceClassName, $instance);
2819  GeneralUtility::resetSingletonInstances([]);
2820  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
2821  self::assertArrayNotHasKey($instanceClassName, $registeredSingletonInstances);
2822  }
2823 
2824  #[Test]
2825  public function resetSingletonInstancesSetsGivenInstance(): void
2826  {
2827  $instance = $this->createMock(SingletonInterface::class);
2828  $instanceClassName = get_class($instance);
2829  GeneralUtility::resetSingletonInstances(
2830  [$instanceClassName => $instance]
2831  );
2832  $registeredSingletonInstances = GeneralUtility::getSingletonInstances();
2833  self::assertArrayHasKey($instanceClassName, $registeredSingletonInstances);
2834  self::assertSame($registeredSingletonInstances[$instanceClassName], $instance);
2835  }
2836 
2837  #[Test]
2838  public function addInstanceForEmptyClassNameThrowsException(): void
2839  {
2840  $this->expectException(\InvalidArgumentException::class);
2841  $this->expectExceptionCode(1288967479);
2842 
2843  // @phpstan-ignore-next-line We are explicitly testing with a contract violation here.
2844  GeneralUtility::addInstance('', new \stdClass());
2845  }
2846 
2847  #[Test]
2848  public function addInstanceForClassThatIsNoSubclassOfProvidedClassThrowsException(): void
2849  {
2850  $this->expectException(\InvalidArgumentException::class);
2851  $this->expectExceptionCode(1288967686);
2852  $instance = $this->getMockBuilder(\stdClass::class)->getMock();
2853  $singletonClassName = get_class($this->createMock(\stdClass::class));
2854  GeneralUtility::addInstance($singletonClassName, $instance);
2855  }
2856 
2857  #[Test]
2858  public function addInstanceWithSingletonInstanceThrowsException(): void
2859  {
2860  $this->expectException(\InvalidArgumentException::class);
2861  $this->expectExceptionCode(1288969325);
2862 
2863  $instance = $this->createMock(SingletonInterface::class);
2864  GeneralUtility::addInstance(get_class($instance), $instance);
2865  }
2866 
2867  #[Test]
2868  public function addInstanceMakesMakeInstanceReturnThatInstance(): void
2869  {
2870  $instance = $this->createMock(\stdClass::class);
2871  $className = get_class($instance);
2872  GeneralUtility::addInstance($className, $instance);
2873  self::assertSame($instance, GeneralUtility::makeInstance($className));
2874  }
2875 
2876  #[Test]
2877  public function makeInstanceCalledTwoTimesAfterAddInstanceReturnTwoDifferentInstances(): void
2878  {
2879  $instance = $this->createMock(\stdClass::class);
2880  $className = get_class($instance);
2881  GeneralUtility::addInstance($className, $instance);
2882  self::assertNotSame(GeneralUtility::makeInstance($className), GeneralUtility::makeInstance($className));
2883  }
2884 
2885  #[Test]
2886  public function addInstanceCalledTwoTimesMakesMakeInstanceReturnBothInstancesInAddingOrder(): void
2887  {
2888  $instance1 = $this->createMock(\stdClass::class);
2889  $className = get_class($instance1);
2890  GeneralUtility::addInstance($className, $instance1);
2891  $instance2 = new $className();
2892  GeneralUtility::addInstance($className, $instance2);
2893  self::assertSame($instance1, GeneralUtility::makeInstance($className), 'The first returned instance does not match the first added instance.');
2894  self::assertSame($instance2, GeneralUtility::makeInstance($className), 'The second returned instance does not match the second added instance.');
2895  }
2896 
2897  #[Test]
2898  public function purgeInstancesDropsAddedInstance(): void
2899  {
2900  $instance = $this->createMock(\stdClass::class);
2901  $className = get_class($instance);
2902  GeneralUtility::addInstance($className, $instance);
2903  GeneralUtility::purgeInstances();
2904  self::assertNotSame($instance, GeneralUtility::makeInstance($className));
2905  }
2906 
2907  public static function getFileAbsFileNameDataProvider(): array
2908  {
2909  return [
2910  'relative path is prefixed with public path' => [
2911  'fileadmin/foo.txt',
2912  ‪Environment::getPublicPath() . '/fileadmin/foo.txt',
2913  ],
2914  'relative path, referencing current directory is prefixed with public path' => [
2915  './fileadmin/foo.txt',
2916  ‪Environment::getPublicPath() . '/./fileadmin/foo.txt',
2917  ],
2918  'relative paths with back paths are not allowed and returned empty' => [
2919  '../fileadmin/foo.txt',
2920  '',
2921  ],
2922  'absolute paths with back paths are not allowed and returned empty' => [
2923  ‪Environment::getPublicPath() . '/../sysext/core/Resources/Public/Icons/Extension.png',
2924  '',
2925  ],
2926  'allowed absolute paths are returned as is' => [
2927  ‪Environment::getPublicPath() . '/fileadmin/foo.txt',
2928  ‪Environment::getPublicPath() . '/fileadmin/foo.txt',
2929  ],
2930  'disallowed absolute paths are returned empty' => [
2931  '/somewhere/fileadmin/foo.txt',
2932  '',
2933  ],
2934  'EXT paths are resolved to absolute paths' => [
2935  'EXT:foo/Resources/Private/Templates/Home.html',
2936  '/path/to/foo/Resources/Private/Templates/Home.html',
2937  ],
2938  ];
2939  }
2940 
2941  #[DataProvider('getFileAbsFileNameDataProvider')]
2942  #[Test]
2943  public function getFileAbsFileNameReturnsCorrectValues(string $path, string $expected): void
2944  {
2945  // build the dummy package "foo" for use in ExtensionManagementUtility::extPath('foo');
2946  $package = $this->getMockBuilder(Package::class)
2947  ->disableOriginalConstructor()
2948  ->onlyMethods(['getPackagePath'])
2949  ->getMock();
2950  $packageManager = $this->getMockBuilder(PackageManager::class)
2951  ->onlyMethods(['isPackageActive', 'getPackage', 'getActivePackages'])
2952  ->disableOriginalConstructor()
2953  ->getMock();
2954  $package
2955  ->method('getPackagePath')
2956  ->willReturn('/path/to/foo/');
2957  $packageManager
2958  ->method('getActivePackages')
2959  ->willReturn(['foo' => $package]);
2960  $packageManager
2961  ->method('isPackageActive')
2962  ->with(self::equalTo('foo'))
2963  ->willReturn(true);
2964  $packageManager
2965  ->method('getPackage')
2966  ->with('foo')
2967  ->willReturn($package);
2969 
2970  $result = GeneralUtility::getFileAbsFileName($path);
2971  self::assertEquals($expected, $result);
2972  }
2973 
2979  public static function validPathStrInvalidCharactersDataProvider(): array
2980  {
2981  $data = [
2982  'double slash in path' => ['path//path'],
2983  'backslash in path' => ['path\\path'],
2984  'directory up in path' => ['path/../path'],
2985  'directory up at the beginning' => ['../path'],
2986  'NUL character in path' => ['path' . "\0" . 'path'],
2987  'BS character in path' => ['path' . chr(8) . 'path'],
2988  'invalid UTF-8-sequence' => ["\xc0" . 'path/path'],
2989  'Could be overlong NUL in some UTF-8 implementations, invalid in RFC3629' => ["\xc0\x80" . 'path/path'],
2990  ];
2991 
2992  // Mixing with regular utf-8
2993  $utf8Characters = 'Ссылка/';
2994  foreach ($data as $key => $value) {
2995  $data[$key . ' with UTF-8 characters prepended'] = [$utf8Characters . $value[0]];
2996  $data[$key . ' with UTF-8 characters appended'] = [$value[0] . $utf8Characters];
2997  }
2998 
2999  // Encoding with UTF-16
3000  foreach ($data as $key => $value) {
3001  $data[$key . ' encoded with UTF-16'] = [mb_convert_encoding($value[0], 'UTF-16')];
3002  }
3003 
3004  return $data;
3005  }
3006 
3010  #[DataProvider('validPathStrInvalidCharactersDataProvider')]
3011  #[Test]
3012  public function validPathStrDetectsInvalidCharacters(string $path): void
3013  {
3014  self::assertFalse(GeneralUtility::validPathStr($path));
3015  }
3016 
3022  public static function validPathStrDataProvider(): array
3023  {
3024  $data = [
3025  'normal ascii path' => ['fileadmin/templates/myfile..xml'],
3026  'special character' => ['fileadmin/templates/Ссылка (fce).xml'],
3027  ];
3028 
3029  return $data;
3030  }
3031 
3035  #[DataProvider('validPathStrDataProvider')]
3036  #[Test]
3037  public function validPathStrWorksWithUnicodeFileNames(string $path): void
3038  {
3039  self::assertTrue(GeneralUtility::validPathStr($path));
3040  }
3041 
3043  // Tests concerning copyDirectory
3045  #[Test]
3046  public function copyDirectoryCopiesFilesAndDirectoriesWithRelativePaths(): void
3047  {
3048  $sourceDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3049  $absoluteSourceDirectory = ‪Environment::getPublicPath() . '/' . $sourceDirectory;
3050  $this->testFilesToDelete[] = $absoluteSourceDirectory;
3051  ‪GeneralUtility::mkdir($absoluteSourceDirectory);
3052 
3053  $targetDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3054  $absoluteTargetDirectory = ‪Environment::getPublicPath() . '/' . $targetDirectory;
3055  $this->testFilesToDelete[] = $absoluteTargetDirectory;
3056 
3057  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'file', '42');
3058  ‪GeneralUtility::mkdir($absoluteSourceDirectory . 'foo');
3059  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'foo/file', '42');
3060 
3061  GeneralUtility::copyDirectory($sourceDirectory, $targetDirectory);
3062 
3063  self::assertFileExists($absoluteTargetDirectory . 'file');
3064  self::assertFileExists($absoluteTargetDirectory . 'foo/file');
3065  }
3066 
3067  #[Test]
3068  public function copyDirectoryCopiesFilesAndDirectoriesWithAbsolutePaths(): void
3069  {
3070  $sourceDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3071  $absoluteSourceDirectory = ‪Environment::getPublicPath() . '/' . $sourceDirectory;
3072  $this->testFilesToDelete[] = $absoluteSourceDirectory;
3073  ‪GeneralUtility::mkdir($absoluteSourceDirectory);
3074 
3075  $targetDirectory = 'typo3temp/var/tests/' . ‪StringUtility::getUniqueId('test_') . '/';
3076  $absoluteTargetDirectory = ‪Environment::getPublicPath() . '/' . $targetDirectory;
3077  $this->testFilesToDelete[] = $absoluteTargetDirectory;
3078 
3079  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'file', '42');
3080  ‪GeneralUtility::mkdir($absoluteSourceDirectory . 'foo');
3081  ‪GeneralUtility::writeFileToTypo3tempDir($absoluteSourceDirectory . 'foo/file', '42');
3082 
3083  GeneralUtility::copyDirectory($absoluteSourceDirectory, $absoluteTargetDirectory);
3084 
3085  self::assertFileExists($absoluteTargetDirectory . 'file');
3086  self::assertFileExists($absoluteTargetDirectory . 'foo/file');
3087  }
3088 
3089  public static function callUserFunctionInvalidParameterDataProvider(): array
3090  {
3091  return [
3092  'Method does not exist' => [GeneralUtilityTestClass::class . '->calledUserFunction', 1294585865],
3093  'Class does not exist' => ['t3lib_divTest21345->user_calledUserFunction', 1294585866],
3094  'No method name' => [GeneralUtilityTestClass::class, 1294585867],
3095  'No class name' => ['->user_calledUserFunction', 1294585866],
3096  'No function name' => ['', 1294585867],
3097  ];
3098  }
3099 
3103  #[DataProvider('callUserFunctionInvalidParameterDataProvider')]
3104  #[Test]
3105  public function callUserFunctionWillThrowExceptionForInvalidParameters(string $functionName, int $expectedException): void
3106  {
3107  $this->expectException(\InvalidArgumentException::class);
3108  $this->expectExceptionCode($expectedException);
3109  $inputData = ['foo' => 'bar'];
3110  GeneralUtility::callUserFunction($functionName, $inputData, $this);
3111  }
3112 
3113  #[Test]
3114  public function callUserFunctionCanCallClosure(): void
3115  {
3116  $inputData = ['foo' => 'bar'];
3117  $result = GeneralUtility::callUserFunction(
3118  static fn(): string => 'Worked fine',
3119  $inputData,
3120  $this,
3121  ''
3122  );
3123  self::assertEquals('Worked fine', $result);
3124  }
3125 
3126  #[Test]
3127  public function callUserFunctionCanCallMethod(): void
3128  {
3129  $inputData = ['foo' => 'bar'];
3130  $result = GeneralUtility::callUserFunction(GeneralUtilityTestClass::class . '->user_calledUserFunction', $inputData, $this);
3131  self::assertEquals('Worked fine', $result);
3132  }
3133 
3134  #[Test]
3135  public function callUserFunctionTrimsSpaces(): void
3136  {
3137  $inputData = ['foo' => 'bar'];
3138  $result = GeneralUtility::callUserFunction("\t" . GeneralUtilityTestClass::class . '->user_calledUserFunction ', $inputData, $this);
3139  self::assertEquals('Worked fine', $result);
3140  }
3141 
3142  #[Test]
3143  public function callUserFunctionAcceptsClosures(): void
3144  {
3145  $inputData = ['foo' => 'bar'];
3146  $closure = static function ($parameters, $reference) use ($inputData) {
3147  $reference->assertEquals($inputData, $parameters, 'Passed data does not match expected output');
3148  return 'Worked fine';
3149  };
3150  self::assertEquals('Worked fine', GeneralUtility::callUserFunction($closure, $inputData, $this));
3151  }
3152 
3153  #[Test]
3154  public function getAllFilesAndFoldersInPathReturnsArrayWithMd5Keys(): void
3155  {
3156  $directory = $this->‪getTestDirectory() . '/' . ‪StringUtility::getUniqueId('directory_');
3157  mkdir($directory);
3158  $filesAndDirectories = GeneralUtility::getAllFilesAndFoldersInPath([], $directory, '', true);
3159  $check = true;
3160  foreach ($filesAndDirectories as $md5 => $path) {
3161  if (!preg_match('/^[a-f0-9]{32}$/', $md5)) {
3162  $check = false;
3163  }
3164  }
3165  ‪GeneralUtility::rmdir($directory);
3166  self::assertTrue($check);
3167  }
3168 
3173  #[Test]
3174  public function array2xmlConvertsEmptyArraysToElementWithoutContent(): void
3175  {
3176  $input = [
3177  'el' => [],
3178  ];
3179 
3180  ‪$output = GeneralUtility::array2xml($input);
3181 
3182  self::assertEquals('<phparray>
3183  <el type="array"></el>
3184 </phparray>', ‪$output);
3185  }
3186 
3187  #[Test]
3188  public function xml2arrayUsesCache(): void
3189  {
3190  $cacheMock = $this->createMock(FrontendInterface::class);
3191  $cacheMock->method('getIdentifier')->willReturn('runtime');
3192  $cacheMock->expects(self::atLeastOnce())->method('get')->with('generalUtilityXml2Array')->willReturn(false);
3193  $cacheMock->expects(self::atLeastOnce())->method('set')->with('generalUtilityXml2Array', self::anything());
3194  $cacheManager = new CacheManager();
3195  $cacheManager->registerCache($cacheMock);
3196  GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager);
3197  ‪GeneralUtility::xml2array('<?xml version="1.0" encoding="utf-8" standalone="yes"?>', 'T3:');
3198  }
3199 
3203  public static function xml2arrayProcessHandlesWhitespacesDataProvider(): array
3204  {
3205  $headerVariants = [
3206  'utf-8' => '<?xml version="1.0" encoding="utf-8" standalone="yes"?>',
3207  'UTF-8' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
3208  'no-encoding' => '<?xml version="1.0" standalone="yes"?>',
3209  'iso-8859-1' => '<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>',
3210  'ISO-8859-1' => '<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>',
3211  ];
3212  $data = [];
3213  foreach ($headerVariants as ‪$identifier => $headerVariant) {
3214  $data += [
3215  'inputWithoutWhitespaces-' . ‪$identifier => [
3216  $headerVariant . '<T3FlexForms>
3217  <data>
3218  <field index="settings.persistenceIdentifier">
3219  <value index="vDEF">egon</value>
3220  </field>
3221  </data>
3222  </T3FlexForms>',
3223  ],
3224  'inputWithPrecedingWhitespaces-' . ‪$identifier => [
3225  CR . ' ' . $headerVariant . '<T3FlexForms>
3226  <data>
3227  <field index="settings.persistenceIdentifier">
3228  <value index="vDEF">egon</value>
3229  </field>
3230  </data>
3231  </T3FlexForms>',
3232  ],
3233  'inputWithTrailingWhitespaces-' . ‪$identifier => [
3234  $headerVariant . '<T3FlexForms>
3235  <data>
3236  <field index="settings.persistenceIdentifier">
3237  <value index="vDEF">egon</value>
3238  </field>
3239  </data>
3240  </T3FlexForms>' . CR . ' ',
3241  ],
3242  'inputWithPrecedingAndTrailingWhitespaces-' . ‪$identifier => [
3243  CR . ' ' . $headerVariant . '<T3FlexForms>
3244  <data>
3245  <field index="settings.persistenceIdentifier">
3246  <value index="vDEF">egon</value>
3247  </field>
3248  </data>
3249  </T3FlexForms>' . CR . ' ',
3250  ],
3251  ];
3252  }
3253  return $data;
3254  }
3255 
3256  #[DataProvider('xml2arrayProcessHandlesWhitespacesDataProvider')]
3257  #[Test]
3258  public function xml2arrayProcessHandlesWhitespaces(string $input): void
3259  {
3260  $expected = [
3261  'data' => [
3262  'settings.persistenceIdentifier' => [
3263  'vDEF' => 'egon',
3264  ],
3265  ],
3266  ];
3267  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input));
3268  }
3269 
3273  public static function xml2arrayProcessHandlesTagNamespacesDataProvider(): array
3274  {
3275  return [
3276  'inputWithNameSpaceOnRootLevel' => [
3277  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3278  <T3:T3FlexForms>
3279  <data>
3280  <field index="settings.persistenceIdentifier">
3281  <value index="vDEF">egon</value>
3282  </field>
3283  </data>
3284  </T3:T3FlexForms>',
3285  ],
3286  'inputWithNameSpaceOnNonRootLevel' => [
3287  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3288  <T3FlexForms>
3289  <data>
3290  <T3:field index="settings.persistenceIdentifier">
3291  <value index="vDEF">egon</value>
3292  </T3:field>
3293  </data>
3294  </T3FlexForms>',
3295  ],
3296  'inputWithNameSpaceOnRootAndNonRootLevel' => [
3297  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3298  <T3:T3FlexForms>
3299  <data>
3300  <T3:field index="settings.persistenceIdentifier">
3301  <value index="vDEF">egon</value>
3302  </T3:field>
3303  </data>
3304  </T3:T3FlexForms>',
3305  ],
3306  ];
3307  }
3308 
3309  #[DataProvider('xml2arrayProcessHandlesTagNamespacesDataProvider')]
3310  #[Test]
3311  public function xml2arrayProcessHandlesTagNamespaces(string $input): void
3312  {
3313  $expected = [
3314  'data' => [
3315  'settings.persistenceIdentifier' => [
3316  'vDEF' => 'egon',
3317  ],
3318  ],
3319  ];
3320  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input, 'T3:'));
3321  }
3322 
3326  public static function xml2arrayProcessHandlesDocumentTagDataProvider(): array
3327  {
3328  return [
3329  'input' => [
3330  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3331  <T3FlexForms>
3332  <data>
3333  <field index="settings.persistenceIdentifier">
3334  <value index="vDEF">egon</value>
3335  </field>
3336  </data>
3337  </T3FlexForms>',
3338  'T3FlexForms',
3339  ],
3340  'input-with-root-namespace' => [
3341  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3342  <T3:T3FlexForms>
3343  <data>
3344  <field index="settings.persistenceIdentifier">
3345  <value index="vDEF">egon</value>
3346  </field>
3347  </data>
3348  </T3:T3FlexForms>',
3349  'T3:T3FlexForms',
3350  ],
3351  'input-with-namespace' => [
3352  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3353  <T3FlexForms>
3354  <data>
3355  <T3:field index="settings.persistenceIdentifier">
3356  <value index="vDEF">egon</value>
3357  </T3:field>
3358  </data>
3359  </T3FlexForms>',
3360  'T3FlexForms',
3361  ],
3362  ];
3363  }
3364 
3365  #[DataProvider('xml2arrayProcessHandlesDocumentTagDataProvider')]
3366  #[Test]
3367  public function xml2arrayProcessHandlesDocumentTag(string $input, string $docTag): void
3368  {
3369  $expected = [
3370  'data' => [
3371  'settings.persistenceIdentifier' => [
3372  'vDEF' => 'egon',
3373  ],
3374  ],
3375  '_DOCUMENT_TAG' => $docTag,
3376  ];
3377  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input, '', true));
3378  }
3379 
3383  public static function xml2ArrayProcessHandlesBigXmlContentDataProvider(): array
3384  {
3385  return [
3386  '1mb' => [
3387  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3388  <T3:T3FlexForms>
3389  <data>
3390  <field index="settings.persistenceIdentifier">
3391  <value index="vDEF">' . str_repeat('1', 1024 * 1024) . '</value>
3392  </field>
3393  </data>
3394  </T3:T3FlexForms>',
3395  str_repeat('1', 1024 * 1024),
3396  ],
3397  '5mb' => [
3398  '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
3399  <T3:T3FlexForms>
3400  <data>
3401  <field index="settings.persistenceIdentifier">
3402  <value index="vDEF">' . str_repeat('1', 5 * 1024 * 1024) . '</value>
3403  </field>
3404  </data>
3405  </T3:T3FlexForms>',
3406  str_repeat('1', 5 * 1024 * 1024),
3407  ],
3408  ];
3409  }
3410 
3411  #[DataProvider('xml2ArrayProcessHandlesBigXmlContentDataProvider')]
3412  #[Test]
3413  public function xml2ArrayProcessHandlesBigXmlContent(string $input, string $testValue): void
3414  {
3415  $expected = [
3416  'data' => [
3417  'settings.persistenceIdentifier' => [
3418  'vDEF' => $testValue,
3419  ],
3420  ],
3421  ];
3422  self::assertSame($expected, ‪GeneralUtility::xml2arrayProcess($input));
3423  }
3424 
3428  public static function xml2ArrayProcessHandlesAttributeTypesDataProvider(): array
3429  {
3430  $prefix = '<?xml version="1.0" encoding="utf-8" standalone="yes"?><T3FlexForms><field index="index">';
3431  $suffix = '</field></T3FlexForms>';
3432  return [
3433  'no-type string' => [
3434  $prefix . '<value index="vDEF">foo bar</value>' . $suffix,
3435  'foo bar',
3436  ],
3437  'no-type integer' => [
3438  $prefix . '<value index="vDEF">123</value>' . $suffix,
3439  '123',
3440  ],
3441  'no-type double' => [
3442  $prefix . '<value index="vDEF">1.23</value>' . $suffix,
3443  '1.23',
3444  ],
3445  'integer integer' => [
3446  $prefix . '<value index="vDEF" type="integer">123</value>' . $suffix,
3447  123,
3448  ],
3449  'integer double' => [
3450  $prefix . '<value index="vDEF" type="integer">1.23</value>' . $suffix,
3451  1,
3452  ],
3453  'double integer' => [
3454  $prefix . '<value index="vDEF" type="double">123</value>' . $suffix,
3455  123.0,
3456  ],
3457  'double double' => [
3458  $prefix . '<value index="vDEF" type="double">1.23</value>' . $suffix,
3459  1.23,
3460  ],
3461  'boolean 0' => [
3462  $prefix . '<value index="vDEF" type="boolean">0</value>' . $suffix,
3463  false,
3464  ],
3465  'boolean 1' => [
3466  $prefix . '<value index="vDEF" type="boolean">1</value>' . $suffix,
3467  true,
3468  ],
3469  'boolean true' => [
3470  $prefix . '<value index="vDEF" type="boolean">true</value>' . $suffix,
3471  true,
3472  ],
3473  'boolean false' => [
3474  $prefix . '<value index="vDEF" type="boolean">false</value>' . $suffix,
3475  true, // sic(!)
3476  ],
3477  'NULL' => [
3478  $prefix . '<value index="vDEF" type="NULL"></value>' . $suffix,
3479  null,
3480  ],
3481  'NULL string' => [
3482  $prefix . '<value index="vDEF" type="NULL">foo bar</value>' . $suffix,
3483  null,
3484  ],
3485  'NULL integer' => [
3486  $prefix . '<value index="vDEF" type="NULL">123</value>' . $suffix,
3487  null,
3488  ],
3489  'NULL double' => [
3490  $prefix . '<value index="vDEF" type="NULL">1.23</value>' . $suffix,
3491  null,
3492  ],
3493  'array' => [
3494  $prefix . '<value index="vDEF" type="array"></value>' . $suffix,
3495  [],
3496  ],
3497  ];
3498  }
3499 
3500  #[DataProvider('xml2ArrayProcessHandlesAttributeTypesDataProvider')]
3501  #[Test]
3502  public function xml2ArrayProcessHandlesAttributeTypes(string $input, mixed $expected): void
3503  {
3504  $result = ‪GeneralUtility::xml2arrayProcess($input);
3505  self::assertSame($expected, $result['index']['vDEF']);
3506  }
3507 
3511  public static function locationHeaderUrlDataProvider(): array
3512  {
3513  return [
3514  'simple relative path' => [
3515  'foo',
3516  'foo.bar.test',
3517  'http://foo.bar.test/foo',
3518  ],
3519  'path beginning with slash' => [
3520  '/foo',
3521  'foo.bar.test',
3522  'http://foo.bar.test/foo',
3523  ],
3524  'path with full domain and https scheme' => [
3525  'https://example.com/foo',
3526  'foo.bar.test',
3527  'https://example.com/foo',
3528  ],
3529  'path with full domain and http scheme' => [
3530  'http://example.com/foo',
3531  'foo.bar.test',
3532  'http://example.com/foo',
3533  ],
3534  'path with full domain and relative scheme' => [
3535  '//example.com/foo',
3536  'foo.bar.test',
3537  '//example.com/foo',
3538  ],
3539  ];
3540  }
3541 
3545  #[DataProvider('locationHeaderUrlDataProvider')]
3546  #[Test]
3547  public function locationHeaderUrl(string $path, string $host, string $expected): void
3548  {
3551  true,
3552  false,
3558  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
3559  );
3560  ‪$_SERVER['HTTP_HOST'] = $host;
3561  ‪$_SERVER['SCRIPT_NAME'] = '/index.php';
3562  $result = GeneralUtility::locationHeaderUrl($path);
3563  self::assertSame($expected, $result);
3564  }
3565 
3566  #[Test]
3567  public function createVersionNumberedFilenameDoesNotResolveBackpathForAbsolutePath(): void
3568  {
3569  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename'] = true;
3570 
3571  $uniqueFilename = ‪StringUtility::getUniqueId() . 'backend';
3572  $testFileDirectory = ‪Environment::getVarPath() . '/tests/';
3573  $testFilepath = $testFileDirectory . $uniqueFilename . '.css';
3574  $this->testFilesToDelete[] = $testFilepath;
3575  ‪GeneralUtility::mkdir_deep($testFileDirectory);
3576  touch($testFilepath);
3577 
3578  $versionedFilename = GeneralUtility::createVersionNumberedFilename($testFilepath);
3579 
3580  self::assertMatchesRegularExpression('/^.*\/tests\/' . $uniqueFilename . '\.[0-9]+\.css/', $versionedFilename);
3581  }
3582 
3583  #[Test]
3584  public function createVersionNumberedFilenameKeepsInvalidAbsolutePathInFrontendAndAddsQueryString(): void
3585  {
3588  true,
3589  false,
3594  ‪Environment::getPublicPath() . '/index.php',
3595  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
3596  );
3597  $request = new ServerRequest('https://www.example.com', 'GET');
3598  ‪$GLOBALS['TYPO3_REQUEST'] = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
3599  $uniqueFilename = ‪StringUtility::getUniqueId('main_');
3600  $testFileDirectory = ‪Environment::getPublicPath() . '/static/';
3601  $testFilepath = $testFileDirectory . $uniqueFilename . '.css';
3602  ‪GeneralUtility::mkdir_deep($testFileDirectory);
3603  touch($testFilepath);
3604 
3605  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'] = false;
3606  $incomingFileName = '/' . ‪PathUtility::stripPathSitePrefix($testFilepath);
3607  $versionedFilename = GeneralUtility::createVersionNumberedFilename($incomingFileName);
3608  self::assertStringContainsString('.css?', $versionedFilename);
3609  self::assertStringStartsWith('/static/main_', $versionedFilename);
3610 
3611  $incomingFileName = ‪PathUtility::stripPathSitePrefix($testFilepath);
3612  $versionedFilename = GeneralUtility::createVersionNumberedFilename($incomingFileName);
3613  self::assertStringContainsString('.css?', $versionedFilename);
3614  self::assertStringStartsWith('static/main_', $versionedFilename);
3615 
3616  ‪GeneralUtility::rmdir($testFileDirectory, true);
3617  }
3618 
3619  #[Test]
3620  public function getMaxUploadFileSizeReturnsPositiveInt(): void
3621  {
3622  $result = GeneralUtility::getMaxUploadFileSize();
3623  self::assertGreaterThan(0, $result);
3624  }
3625 }
‪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:666
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir(string $newFolder)
Definition: GeneralUtility.php:1633
‪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:1754
‪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:855
‪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:678
‪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:1283
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep(string $directory)
Definition: GeneralUtility.php:1649
‪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:1556
‪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\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:474
‪TYPO3\CMS\Core\Utility\GeneralUtility\rmdir
‪static bool rmdir(string $path, bool $removeNonEmpty=false)
Definition: GeneralUtility.php:1697
‪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:1491
‪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:782
‪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:708
‪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:751
‪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:654
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2array
‪static array string xml2array(string $string, string $NSprefix='', bool $reportDocTag=false)
Definition: GeneralUtility.php:1260
‪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:817
‪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