‪TYPO3CMS  11.5
GeneralUtility.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Egulias\EmailValidator\EmailValidator;
19 use Egulias\EmailValidator\Validation\EmailValidation;
20 use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
21 use Egulias\EmailValidator\Validation\RFCValidation;
22 use GuzzleHttp\Exception\RequestException;
23 use Psr\Container\ContainerInterface;
24 use Psr\Http\Message\ServerRequestInterface;
25 use Psr\Log\LoggerAwareInterface;
26 use Psr\Log\LoggerInterface;
37 
50 {
51  /* @deprecated since v11, will be removed in v12. */
53  /* @deprecated since v11, will be removed in v12. */
54  public const ‪ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME = 'SERVER_NAME';
55 
59  protected static ‪$container;
60 
67  protected static $singletonInstances = [];
68 
74  protected static $nonSingletonInstances = [];
75 
81  protected static $finalClassNameCache = [];
82 
86  protected static $indpEnvCache = [];
87 
88  final private function __construct() {}
89 
90  /*************************
91  *
92  * GET/POST Variables
93  *
94  * Background:
95  * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration.
96  * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so.
97  * But the clean solution is that quotes are never escaped and that is what the functions below offers.
98  * Eventually TYPO3 should provide this in the global space as well.
99  * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below.
100  * This functionality was previously needed to normalize between magic quotes logic, which was removed from PHP 5.4,
101  * so these methods are still in use, but not tackle the slash problem anymore.
102  *
103  *************************/
111  public static function _GP($var)
112  {
113  if (empty($var)) {
114  return;
115  }
116 
117  $value = $_POST[$var] ?? $_GET[$var] ?? null;
118 
119  // This is there for backwards-compatibility, in order to avoid NULL
120  if (isset($value) && !is_array($value)) {
121  $value = (string)$value;
122  }
123  return $value;
124  }
125 
132  public static function _GPmerged($parameter)
133  {
134  $postParameter = isset($_POST[$parameter]) && is_array($_POST[$parameter]) ? $_POST[$parameter] : [];
135  $getParameter = isset($_GET[$parameter]) && is_array($_GET[$parameter]) ? $_GET[$parameter] : [];
136  $mergedParameters = $getParameter;
137  ‪ArrayUtility::mergeRecursiveWithOverrule($mergedParameters, $postParameter);
138  return $mergedParameters;
139  }
140 
150  public static function _GET($var = null)
151  {
152  $value = $var === null
153  ? $_GET
154  : (empty($var) ? null : ($_GET[$var] ?? null));
155  // This is there for backwards-compatibility, in order to avoid NULL
156  if (isset($value) && !is_array($value)) {
157  $value = (string)$value;
158  }
159  return $value;
160  }
161 
170  public static function _POST($var = null)
171  {
172  $value = $var === null ? $_POST : (empty($var) || !isset($_POST[$var]) ? null : $_POST[$var]);
173  // This is there for backwards-compatibility, in order to avoid NULL
174  if (isset($value) && !is_array($value)) {
175  $value = (string)$value;
176  }
177  return $value;
178  }
179 
180  /*************************
181  *
182  * STRING FUNCTIONS
183  *
184  *************************/
194  public static function fixed_lgd_cs($string, $chars, $appendString = '...')
195  {
196  $string = (string)$string;
197  if ((int)$chars === 0 || mb_strlen($string, 'utf-8') <= abs($chars)) {
198  return $string;
199  }
200  if ($chars > 0) {
201  $string = mb_substr($string, 0, $chars, 'utf-8') . $appendString;
202  } else {
203  $string = $appendString . mb_substr($string, $chars, mb_strlen($string, 'utf-8'), 'utf-8');
204  }
205  return $string;
206  }
207 
216  public static function cmpIP($baseIP, $list)
217  {
218  $list = trim($list);
219  if ($list === '') {
220  return false;
221  }
222  if ($list === '*') {
223  return true;
224  }
225  if (str_contains($baseIP, ':') && self::validIPv6($baseIP)) {
226  return self::cmpIPv6($baseIP, $list);
227  }
228  return self::cmpIPv4($baseIP, $list);
229  }
230 
238  public static function cmpIPv4($baseIP, $list)
239  {
240  $IPpartsReq = explode('.', $baseIP);
241  if (count($IPpartsReq) === 4) {
242  $values = ‪self::trimExplode(',', $list, true);
243  foreach ($values as $test) {
244  $testList = explode('/', $test);
245  if (count($testList) === 2) {
246  [$test, $mask] = $testList;
247  } else {
248  $mask = false;
249  }
250  if ((int)$mask) {
251  $mask = (int)$mask;
252  // "192.168.3.0/24"
253  $lnet = (int)ip2long($test);
254  $lip = (int)ip2long($baseIP);
255  $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
256  $firstpart = substr($binnet, 0, $mask);
257  $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
258  $firstip = substr($binip, 0, $mask);
259  $yes = $firstpart === $firstip;
260  } else {
261  // "192.168.*.*"
262  $IPparts = explode('.', $test);
263  $yes = 1;
264  foreach ($IPparts as $index => $val) {
265  $val = trim($val);
266  if ($val !== '*' && $IPpartsReq[$index] !== $val) {
267  $yes = 0;
268  }
269  }
270  }
271  if ($yes) {
272  return true;
273  }
274  }
275  }
276  return false;
277  }
278 
287  public static function cmpIPv6($baseIP, $list)
288  {
289  // Policy default: Deny connection
290  $success = false;
291  $baseIP = self::normalizeIPv6($baseIP);
292  $values = ‪self::trimExplode(',', $list, true);
293  foreach ($values as $test) {
294  $testList = explode('/', $test);
295  if (count($testList) === 2) {
296  [$test, $mask] = $testList;
297  } else {
298  $mask = false;
299  }
300  if (self::validIPv6($test)) {
301  $test = self::normalizeIPv6($test);
302  $maskInt = (int)$mask ?: 128;
303  // Special case; /0 is an allowed mask - equals a wildcard
304  if ($mask === '0') {
305  $success = true;
306  } elseif ($maskInt == 128) {
307  $success = $test === $baseIP;
308  } else {
309  $testBin = (string)inet_pton($test);
310  $baseIPBin = (string)inet_pton($baseIP);
311 
312  $success = true;
313  // Modulo is 0 if this is a 8-bit-boundary
314  $maskIntModulo = $maskInt % 8;
315  $numFullCharactersUntilBoundary = (int)($maskInt / 8);
316  $substring = (string)substr($baseIPBin, 0, $numFullCharactersUntilBoundary);
317  if (strpos($testBin, $substring) !== 0) {
318  $success = false;
319  } elseif ($maskIntModulo > 0) {
320  // If not an 8-bit-boundary, check bits of last character
321  $testLastBits = str_pad(decbin(ord(substr($testBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
322  $baseIPLastBits = str_pad(decbin(ord(substr($baseIPBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
323  if (strncmp($testLastBits, $baseIPLastBits, $maskIntModulo) != 0) {
324  $success = false;
325  }
326  }
327  }
328  }
329  if ($success) {
330  return true;
331  }
332  }
333  return false;
334  }
335 
342  public static function normalizeIPv6($address)
343  {
344  $normalizedAddress = '';
345  // According to RFC lowercase-representation is recommended
346  $address = strtolower($address);
347  // Normalized representation has 39 characters (0000:0000:0000:0000:0000:0000:0000:0000)
348  if (strlen($address) === 39) {
349  // Already in full expanded form
350  return $address;
351  }
352  // Count 2 if if address has hidden zero blocks
353  $chunks = explode('::', $address);
354  if (count($chunks) === 2) {
355  $chunksLeft = explode(':', $chunks[0]);
356  $chunksRight = explode(':', $chunks[1]);
357  $left = count($chunksLeft);
358  $right = count($chunksRight);
359  // Special case: leading zero-only blocks count to 1, should be 0
360  if ($left === 1 && strlen($chunksLeft[0]) === 0) {
361  $left = 0;
362  }
363  $hiddenBlocks = 8 - ($left + $right);
364  $hiddenPart = '';
365  $h = 0;
366  while ($h < $hiddenBlocks) {
367  $hiddenPart .= '0000:';
368  $h++;
369  }
370  if ($left === 0) {
371  $stageOneAddress = $hiddenPart . $chunks[1];
372  } else {
373  $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
374  }
375  } else {
376  $stageOneAddress = $address;
377  }
378  // Normalize the blocks:
379  $blocks = explode(':', $stageOneAddress);
380  $divCounter = 0;
381  foreach ($blocks as $block) {
382  $tmpBlock = '';
383  $i = 0;
384  $hiddenZeros = 4 - strlen($block);
385  while ($i < $hiddenZeros) {
386  $tmpBlock .= '0';
387  $i++;
388  }
389  $normalizedAddress .= $tmpBlock . $block;
390  if ($divCounter < 7) {
391  $normalizedAddress .= ':';
392  $divCounter++;
393  }
394  }
395  return $normalizedAddress;
396  }
397 
406  public static function validIP($ip)
407  {
408  return filter_var($ip, FILTER_VALIDATE_IP) !== false;
409  }
410 
419  public static function validIPv4($ip)
420  {
421  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
422  }
423 
432  public static function validIPv6($ip)
433  {
434  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
435  }
436 
444  public static function cmpFQDN($baseHost, $list)
445  {
446  $baseHost = trim($baseHost);
447  if (empty($baseHost)) {
448  return false;
449  }
450  if (self::validIPv4($baseHost) || self::validIPv6($baseHost)) {
451  // Resolve hostname
452  // Note: this is reverse-lookup and can be randomly set as soon as somebody is able to set
453  // the reverse-DNS for his IP (security when for example used with REMOTE_ADDR)
454  $baseHostName = (string)gethostbyaddr($baseHost);
455  if ($baseHostName === $baseHost) {
456  // Unable to resolve hostname
457  return false;
458  }
459  } else {
460  $baseHostName = $baseHost;
461  }
462  $baseHostNameParts = explode('.', $baseHostName);
463  $values = ‪self::trimExplode(',', $list, true);
464  foreach ($values as $test) {
465  $hostNameParts = explode('.', $test);
466  // To match hostNameParts can only be shorter (in case of wildcards) or equal
467  $hostNamePartsCount = count($hostNameParts);
468  $baseHostNamePartsCount = count($baseHostNameParts);
469  if ($hostNamePartsCount > $baseHostNamePartsCount) {
470  continue;
471  }
472  $yes = true;
473  foreach ($hostNameParts as $index => $val) {
474  $val = trim($val);
475  if ($val === '*') {
476  // Wildcard valid for one or more hostname-parts
477  $wildcardStart = $index + 1;
478  // Wildcard as last/only part always matches, otherwise perform recursive checks
479  if ($wildcardStart < $hostNamePartsCount) {
480  $wildcardMatched = false;
481  $tempHostName = implode('.', array_slice($hostNameParts, $index + 1));
482  while ($wildcardStart < $baseHostNamePartsCount && !$wildcardMatched) {
483  $tempBaseHostName = implode('.', array_slice($baseHostNameParts, $wildcardStart));
484  $wildcardMatched = self::cmpFQDN($tempBaseHostName, $tempHostName);
485  $wildcardStart++;
486  }
487  if ($wildcardMatched) {
488  // Match found by recursive compare
489  return true;
490  }
491  $yes = false;
492  }
493  } elseif ($baseHostNameParts[$index] !== $val) {
494  // In case of no match
495  $yes = false;
496  }
497  }
498  if ($yes) {
499  return true;
500  }
501  }
502  return false;
503  }
504 
512  public static function isOnCurrentHost($url)
513  {
514  return stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0;
515  }
516 
525  public static function inList($list, $item)
526  {
527  return str_contains(',' . $list . ',', ',' . $item . ',');
528  }
529 
541  public static function rmFromList($element, $list)
542  {
543  trigger_error(
544  'GeneralUtility::rmFromList() is deprecated and will be removed in v12.',
545  E_USER_DEPRECATED
546  );
547 
548  $items = explode(',', $list);
549  foreach ($items as $k => $v) {
550  if ($v == $element) {
551  unset($items[$k]);
552  }
553  }
554  return implode(',', $items);
555  }
556 
564  public static function expandList($list)
565  {
566  $items = explode(',', $list);
567  $list = [];
568  foreach ($items as $item) {
569  $range = explode('-', $item);
570  if (isset($range[1])) {
571  $runAwayBrake = 1000;
572  for ($n = $range[0]; $n <= $range[1]; $n++) {
573  $list[] = $n;
574  $runAwayBrake--;
575  if ($runAwayBrake <= 0) {
576  break;
577  }
578  }
579  } else {
580  $list[] = $item;
581  }
582  }
583  return implode(',', $list);
584  }
585 
592  public static function md5int($str)
593  {
594  return hexdec(substr(md5($str), 0, 7));
595  }
596 
605  public static function shortMD5($input, $len = 10)
606  {
607  trigger_error(__METHOD__ . ' will be removed in TYPO3 v12, use md5() instead.', E_USER_DEPRECATED);
608  return substr(md5($input), 0, $len);
609  }
610 
618  public static function hmac($input, $additionalSecret = '')
619  {
620  $hashAlgorithm = 'sha1';
621  $hashBlocksize = 64;
622  $secret = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . $additionalSecret;
623  if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) {
624  $hmac = hash_hmac($hashAlgorithm, $input, $secret);
625  } else {
626  // Outer padding
627  $opad = str_repeat(chr(92), $hashBlocksize);
628  // Inner padding
629  $ipad = str_repeat(chr(54), $hashBlocksize);
630  if (strlen($secret) > $hashBlocksize) {
631  // Keys longer than block size are shorten
632  $key = str_pad(pack('H*', $hashAlgorithm($secret)), $hashBlocksize, "\0");
633  } else {
634  // Keys shorter than block size are zero-padded
635  $key = str_pad($secret, $hashBlocksize, "\0");
636  }
637  $hmac = $hashAlgorithm(($key ^ $opad) . pack('H*', $hashAlgorithm(($key ^ $ipad) . $input)));
638  }
639  return $hmac;
640  }
641 
651  public static function uniqueList($in_list, $secondParameter = null)
652  {
653  trigger_error(
654  'GeneralUtility::uniqueList() is deprecated and will be removed in v12. Use StringUtility::uniqueList() instead.',
655  E_USER_DEPRECATED
656  );
657  if (is_array($in_list)) {
658  throw new \InvalidArgumentException('TYPO3 Fatal Error: TYPO3\\CMS\\Core\\Utility\\GeneralUtility::uniqueList() does NOT support array arguments anymore! Only string comma lists!', 1270853885);
659  }
660  if (isset($secondParameter)) {
661  throw new \InvalidArgumentException('TYPO3 Fatal Error: TYPO3\\CMS\\Core\\Utility\\GeneralUtility::uniqueList() does NOT support more than a single argument value anymore. You have specified more than one!', 1270853886);
662  }
663  return implode(',', array_unique(self::trimExplode(',', $in_list, true)));
664  }
665 
672  public static function split_fileref($fileNameWithPath)
673  {
674  $info = [];
675  $reg = [];
676  if (preg_match('/(.*\\/)(.*)$/', $fileNameWithPath, $reg)) {
677  $info['path'] = $reg[1];
678  $info['file'] = $reg[2];
679  } else {
680  $info['path'] = '';
681  $info['file'] = $fileNameWithPath;
682  }
683  $reg = '';
684  // If open_basedir is set and the fileName was supplied without a path the is_dir check fails
685  if (!is_dir($fileNameWithPath) && preg_match('/(.*)\\.([^\\.]*$)/', $info['file'], $reg)) {
686  $info['filebody'] = $reg[1];
687  $info['fileext'] = strtolower($reg[2]);
688  $info['realFileext'] = $reg[2];
689  } else {
690  $info['filebody'] = $info['file'];
691  $info['fileext'] = '';
692  }
693  reset($info);
694  return $info;
695  }
696 
712  public static function dirname($path)
713  {
714  $p = ‪self::revExplode('/', $path, 2);
715  return count($p) === 2 ? $p[0] : '';
716  }
717 
726  public static function isFirstPartOfStr($str, $partStr)
727  {
728  trigger_error('GeneralUtility::isFirstPartOfStr() will be removed in TYPO3 v12.0. Use PHPs str_starts_with() function instead.', E_USER_DEPRECATED);
729  $str = is_array($str) ? '' : (string)$str;
730  $partStr = is_array($partStr) ? '' : (string)$partStr;
731  return $partStr !== '' && strpos($str, $partStr, 0) === 0;
732  }
733 
742  public static function formatSize(‪$sizeInBytes, $labels = '', $base = 0)
743  {
744  $defaultFormats = [
745  'iec' => ['base' => 1024, 'labels' => [' ', ' Ki', ' Mi', ' Gi', ' Ti', ' Pi', ' Ei', ' Zi', ' Yi']],
746  'si' => ['base' => 1000, 'labels' => [' ', ' k', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y']],
747  ];
748  // Set labels and base:
749  if (empty($labels)) {
750  $labels = 'iec';
751  }
752  if (isset($defaultFormats[$labels])) {
753  $base = $defaultFormats[$labels]['base'];
754  ‪$labelArr = $defaultFormats[$labels]['labels'];
755  } else {
756  $base = (int)$base;
757  if ($base !== 1000 && $base !== 1024) {
758  $base = 1024;
759  }
760  ‪$labelArr = explode('|', str_replace('"', '', $labels));
761  }
762  // This is set via Site Handling and in the Locales class via setlocale()
763  ‪$localeInfo = localeconv();
765  ‪$multiplier = floor((‪$sizeInBytes ? log(‪$sizeInBytes) : 0) / log($base));
767  if (‪$sizeInUnits > ($base * .9)) {
768  ‪$multiplier++;
769  }
770  ‪$multiplier = min(‪$multiplier, count(‪$labelArr) - 1);
772  return number_format(‪$sizeInUnits, ((‪$multiplier > 0) && (‪$sizeInUnits < 20)) ? 2 : 0, ‪$localeInfo['decimal_point'], '') . ‪$labelArr[‪$multiplier];
773  }
774 
784  public static function splitCalc($string, $operators)
785  {
786  $res = [];
787  $sign = '+';
788  while ($string) {
789  $valueLen = strcspn($string, $operators);
790  $value = substr($string, 0, $valueLen);
791  $res[] = [$sign, trim($value)];
792  $sign = substr($string, $valueLen, 1);
793  $string = substr($string, $valueLen + 1);
794  }
795  reset($res);
796  return $res;
797  }
798 
805  public static function validEmail($email)
806  {
807  // Early return in case input is not a string
808  if (!is_string($email)) {
809  return false;
810  }
811  if (trim($email) !== $email) {
812  return false;
813  }
814  if (!str_contains($email, '@')) {
815  return false;
816  }
817  $validators = [];
818  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['MAIL']['validators'] ?? [RFCValidation::class] as $className) {
819  ‪$validator = new $className();
820  if (‪$validator instanceof EmailValidation) {
821  $validators[] = ‪$validator;
822  }
823  }
824  return (new EmailValidator())->isValid($email, new MultipleValidationWithAnd($validators, MultipleValidationWithAnd::STOP_ON_ERROR));
825  }
826 
834  public static function ‪underscoredToUpperCamelCase($string)
835  {
836  return str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($string))));
837  }
838 
846  public static function ‪underscoredToLowerCamelCase($string)
847  {
848  return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($string)))));
849  }
850 
858  public static function ‪camelCaseToLowerCaseUnderscored($string)
859  {
860  $value = preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string) ?? '';
861  return mb_strtolower($value, 'utf-8');
862  }
863 
888  public static function ‪isValidUrl($url)
889  {
890  $parsedUrl = parse_url($url);
891  if (!$parsedUrl || !isset($parsedUrl['scheme'])) {
892  return false;
893  }
894  // HttpUtility::buildUrl() will always build urls with <scheme>://
895  // our original $url might only contain <scheme>: (e.g. mail:)
896  // so we convert that to the double-slashed version to ensure
897  // our check against the $recomposedUrl is proper
898  if (!str_starts_with($url, $parsedUrl['scheme'] . '://')) {
899  $url = str_replace($parsedUrl['scheme'] . ':', $parsedUrl['scheme'] . '://', $url);
900  }
901  $recomposedUrl = ‪HttpUtility::buildUrl($parsedUrl);
902  if ($recomposedUrl !== $url) {
903  // The parse_url() had to modify characters, so the URL is invalid
904  return false;
905  }
906  if (isset($parsedUrl['host']) && !preg_match('/^[a-z0-9.\\-]*$/i', $parsedUrl['host'])) {
907  $host = idn_to_ascii($parsedUrl['host']);
908  if ($host === false) {
909  return false;
910  }
911  $parsedUrl['host'] = $host;
912  }
913  return filter_var(‪HttpUtility::buildUrl($parsedUrl), FILTER_VALIDATE_URL) !== false;
914  }
915 
916  /*************************
917  *
918  * ARRAY FUNCTIONS
919  *
920  *************************/
921 
932  public static function ‪intExplode($delimiter, $string, $removeEmptyValues = false, $limit = 0)
933  {
934  $result = explode($delimiter, $string) ?: [];
935  foreach ($result as $key => &$value) {
936  if ($removeEmptyValues && ($value === '' || trim($value) === '')) {
937  unset($result[$key]);
938  } else {
939  $value = (int)$value;
940  }
941  }
942  unset($value);
943  if ($limit !== 0) {
944  if ($limit < 0) {
945  $result = array_slice($result, 0, $limit);
946  } elseif (count($result) > $limit) {
947  $lastElements = array_slice($result, $limit - 1);
948  $result = array_slice($result, 0, $limit - 1);
949  $result[] = implode($delimiter, $lastElements);
950  }
951  }
952  return $result;
953  }
954 
969  public static function ‪revExplode($delimiter, $string, $count = 0)
970  {
971  // 2 is the (currently, as of 2014-02) most-used value for $count in the core, therefore we check it first
972  if ($count === 2) {
973  $position = strrpos($string, strrev($delimiter));
974  if ($position !== false) {
975  return [substr($string, 0, $position), substr($string, $position + strlen($delimiter))];
976  }
977  return [$string];
978  }
979  if ($count <= 1) {
980  return [$string];
981  }
982  $explodedValues = explode($delimiter, strrev($string), $count) ?: [];
983  $explodedValues = array_map('strrev', $explodedValues);
984  return array_reverse($explodedValues);
985  }
986 
1004  public static function ‪trimExplode($delim, $string, $removeEmptyValues = false, $limit = 0): array
1005  {
1006  $result = explode($delim, (string)$string) ?: [];
1007  if ($removeEmptyValues) {
1008  // Remove items that are just whitespace, but leave whitespace intact for the rest.
1009  $result = array_values(array_filter($result, static fn($item) => trim($item) !== ''));
1010  }
1011 
1012  if ($limit === 0) {
1013  // Return everything.
1014  return array_map('trim', $result);
1015  }
1016 
1017  if ($limit < 0) {
1018  // Trim and return just the first $limit elements and ignore the rest.
1019  return array_map('trim', array_slice($result, 0, $limit));
1020  }
1021 
1022  // Fold the last length - $limit elements into a single trailing item, then trim and return the result.
1023  $tail = array_slice($result, $limit - 1);
1024  $result = array_slice($result, 0, $limit - 1);
1025  if ($tail) {
1026  $result[] = implode($delim, $tail);
1027  }
1028  return array_map('trim', $result);
1029  }
1030 
1042  public static function ‪implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = false, $rawurlencodeParamName = false)
1043  {
1044  foreach ($theArray as $Akey => $AVal) {
1045  $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey;
1046  if (is_array($AVal)) {
1047  $str = ‪self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName);
1048  } else {
1049  $stringValue = (string)$AVal;
1050  if (!$skipBlank || $stringValue !== '') {
1051  $parameterName = $rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName;
1052  $parameterValue = rawurlencode($stringValue);
1053  $str .= '&' . $parameterName . '=' . $parameterValue;
1054  }
1055  }
1056  }
1057  return $str;
1058  }
1059 
1075  public static function explodeUrl2Array($string)
1076  {
1077  ‪$output = [];
1078  $p = explode('&', $string);
1079  foreach ($p as $v) {
1080  if ($v !== '') {
1081  $nameAndValue = explode('=', $v, 2);
1082  ‪$output[rawurldecode($nameAndValue[0])] = isset($nameAndValue[1]) ? rawurldecode($nameAndValue[1]) : '';
1083  }
1084  }
1085  return ‪$output;
1086  }
1087 
1098  public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = true)
1099  {
1100  trigger_error(
1101  'GeneralUtility::compileSelectedGetVarsFromArray() is deprecated and will be removed in v12.',
1102  E_USER_DEPRECATED
1103  );
1104 
1105  $keys = ‪self::trimExplode(',', $varList, true);
1106  $outArr = [];
1107  foreach ($keys as $v) {
1108  if (isset($getArray[$v])) {
1109  $outArr[$v] = $getArray[$v];
1110  } elseif ($GPvarAlt) {
1111  $outArr[$v] = self::_GP($v);
1112  }
1113  }
1114  return $outArr;
1115  }
1116 
1124  public static function removeDotsFromTS(array $ts)
1125  {
1126  $out = [];
1127  foreach ($ts as $key => $value) {
1128  if (is_array($value)) {
1129  $key = rtrim($key, '.');
1130  $out[$key] = self::removeDotsFromTS($value);
1131  } else {
1132  $out[$key] = $value;
1133  }
1134  }
1135  return $out;
1136  }
1137 
1138  /*************************
1139  *
1140  * HTML/XML PROCESSING
1141  *
1142  *************************/
1152  public static function get_tag_attributes($tag, bool $decodeEntities = false)
1153  {
1154  $components = self::split_tag_attributes($tag);
1155  // Attribute name is stored here
1156  $name = '';
1157  $valuemode = false;
1158  $attributes = [];
1159  foreach ($components as $key => $val) {
1160  // Only if $name is set (if there is an attribute, that waits for a value), that valuemode is enabled. This ensures that the attribute is assigned it's value
1161  if ($val !== '=') {
1162  if ($valuemode) {
1163  if ($name) {
1164  $attributes[$name] = $decodeEntities ? htmlspecialchars_decode($val) : $val;
1165  $name = '';
1166  }
1167  } else {
1168  if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val) ?? '')) {
1169  $attributes[$key] = '';
1170  $name = $key;
1171  }
1172  }
1173  $valuemode = false;
1174  } else {
1175  $valuemode = true;
1176  }
1177  }
1178  return $attributes;
1179  }
1180 
1188  public static function split_tag_attributes($tag)
1189  {
1190  $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)) ?? '');
1191  // Removes any > in the end of the string
1192  $tag_tmp = trim(rtrim($tag_tmp, '>'));
1193  $value = [];
1194  // Compared with empty string instead , 030102
1195  while ($tag_tmp !== '') {
1196  $firstChar = $tag_tmp[0];
1197  if ($firstChar === '"' || $firstChar === '\'') {
1198  $reg = explode($firstChar, $tag_tmp, 3);
1199  $value[] = $reg[1];
1200  $tag_tmp = trim($reg[2] ?? '');
1201  } elseif ($firstChar === '=') {
1202  $value[] = '=';
1203  // Removes = chars.
1204  $tag_tmp = trim(substr($tag_tmp, 1));
1205  } else {
1206  // There are '' around the value. We look for the next ' ' or '>'
1207  $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
1208  $value[] = trim($reg[0]);
1209  $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . ($reg[1] ?? ''));
1210  }
1211  }
1212  reset($value);
1213  return $value;
1214  }
1215 
1224  public static function implodeAttributes(array $arr, $xhtmlSafe = false, $keepBlankAttributes = false)
1225  {
1226  if ($xhtmlSafe) {
1227  $newArr = [];
1228  foreach ($arr as $attributeName => $attributeValue) {
1229  $attributeName = strtolower($attributeName);
1230  if (!isset($newArr[$attributeName])) {
1231  $newArr[$attributeName] = htmlspecialchars((string)$attributeValue);
1232  }
1233  }
1234  $arr = $newArr;
1235  }
1236  $list = [];
1237  foreach ($arr as $attributeName => $attributeValue) {
1238  if ((string)$attributeValue !== '' || $keepBlankAttributes) {
1239  $list[] = $attributeName . '="' . $attributeValue . '"';
1240  }
1241  }
1242  return implode(' ', $list);
1243  }
1244 
1253  public static function wrapJS($string)
1254  {
1255  if (trim($string)) {
1256  // remove nl from the beginning
1257  $string = ltrim($string, LF);
1258  // re-ident to one tab using the first line as reference
1259  $match = [];
1260  if (preg_match('/^(\\t+)/', $string, $match)) {
1261  $string = str_replace($match[1], "\t", $string);
1262  }
1263  return '<script>
1264 /*<![CDATA[*/
1265 ' . $string . '
1266 /*]]>*/
1267 </script>';
1268  }
1269  return '';
1270  }
1271 
1280  public static function xml2tree($string, $depth = 999, $parserOptions = [])
1281  {
1282  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
1283  $previousValueOfEntityLoader = null;
1284  if (PHP_MAJOR_VERSION < 8) {
1285  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
1286  }
1287  ‪$parser = xml_parser_create();
1288  $vals = [];
1289  $index = [];
1290  xml_parser_set_option(‪$parser, XML_OPTION_CASE_FOLDING, 0);
1291  xml_parser_set_option(‪$parser, XML_OPTION_SKIP_WHITE, 0);
1292  foreach ($parserOptions as $option => $value) {
1293  xml_parser_set_option(‪$parser, $option, $value);
1294  }
1295  xml_parse_into_struct(‪$parser, $string, $vals, $index);
1296  if (PHP_MAJOR_VERSION < 8) {
1297  libxml_disable_entity_loader($previousValueOfEntityLoader);
1298  }
1299  if (xml_get_error_code(‪$parser)) {
1300  return 'Line ' . xml_get_current_line_number(‪$parser) . ': ' . xml_error_string(xml_get_error_code(‪$parser));
1301  }
1302  xml_parser_free(‪$parser);
1303  $stack = [[]];
1304  $stacktop = 0;
1305  $startPoint = 0;
1306  $tagi = [];
1307  foreach ($vals as $key => $val) {
1308  $type = $val['type'];
1309  // open tag:
1310  if ($type === 'open' || $type === 'complete') {
1311  $stack[$stacktop++] = $tagi;
1312  if ($depth == $stacktop) {
1313  $startPoint = $key;
1314  }
1315  $tagi = ['tag' => $val['tag']];
1316  if (isset($val['attributes'])) {
1317  $tagi['attrs'] = $val['attributes'];
1318  }
1319  if (isset($val['value'])) {
1320  $tagi['values'][] = $val['value'];
1321  }
1322  }
1323  // finish tag:
1324  if ($type === 'complete' || $type === 'close') {
1325  $oldtagi = $tagi;
1326  $tagi = $stack[--$stacktop];
1327  $oldtag = $oldtagi['tag'];
1328  unset($oldtagi['tag']);
1329  if ($depth == $stacktop + 1) {
1330  if ($key - $startPoint > 0) {
1331  $partArray = array_slice($vals, $startPoint + 1, $key - $startPoint - 1);
1332  $oldtagi['XMLvalue'] = ‪self::xmlRecompileFromStructValArray($partArray);
1333  } else {
1334  $oldtagi['XMLvalue'] = $oldtagi['values'][0];
1335  }
1336  }
1337  $tagi['ch'][$oldtag][] = $oldtagi;
1338  unset($oldtagi);
1339  }
1340  // cdata
1341  if ($type === 'cdata') {
1342  $tagi['values'][] = $val['value'];
1343  }
1344  }
1345  return $tagi['ch'];
1346  }
1347 
1368  public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', ‪$spaceInd = 0, array $options = [], array $stackData = [])
1369  {
1370  // The list of byte values which will trigger binary-safe storage. If any value has one of these char values in it, it will be encoded in base64
1371  $binaryChars = "\0" . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . chr(11) . chr(12) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19) . chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29) . chr(30) . chr(31);
1372  // Set indenting mode:
1373  $indentChar = ‪$spaceInd ? ' ' : "\t";
1374  $indentN = ‪$spaceInd > 0 ? ‪$spaceInd : 1;
1375  ‪$nl = ‪$spaceInd >= 0 ? LF : '';
1376  // Init output variable:
1377  ‪$output = '';
1378  // Traverse the input array
1379  foreach ($array as $k => $v) {
1380  $attr = '';
1381  $tagName = (string)$k;
1382  // Construct the tag name.
1383  // Use tag based on grand-parent + parent tag name
1384  if (isset($stackData['grandParentTagName'], $stackData['parentTagName'], $options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) {
1385  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1386  $tagName = (string)$options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']];
1387  } elseif (isset($stackData['parentTagName'], $options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && ‪MathUtility::canBeInterpretedAsInteger($tagName)) {
1388  // Use tag based on parent tag name + if current tag is numeric
1389  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1390  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM'];
1391  } elseif (isset($stackData['parentTagName'], $options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) {
1392  // Use tag based on parent tag name + current tag
1393  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1394  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName];
1395  } elseif (isset($stackData['parentTagName'], $options['parentTagMap'][$stackData['parentTagName']])) {
1396  // Use tag based on parent tag name:
1397  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1398  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName']];
1399  } elseif (‪MathUtility::canBeInterpretedAsInteger($tagName)) {
1400  // If integer...;
1401  if ($options['useNindex'] ?? false) {
1402  // If numeric key, prefix "n"
1403  $tagName = 'n' . $tagName;
1404  } else {
1405  // Use special tag for num. keys:
1406  $attr .= ' index="' . $tagName . '"';
1407  $tagName = ($options['useIndexTagForNum'] ?? false) ?: 'numIndex';
1408  }
1409  } elseif (!empty($options['useIndexTagForAssoc'])) {
1410  // Use tag for all associative keys:
1411  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1412  $tagName = $options['useIndexTagForAssoc'];
1413  }
1414  // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
1415  $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100);
1416  // If the value is an array then we will call this function recursively:
1417  if (is_array($v)) {
1418  // Sub elements:
1419  if (isset($options['alt_options']) && ($options['alt_options'][($stackData['path'] ?? '') . '/' . $tagName] ?? false)) {
1420  $subOptions = $options['alt_options'][($stackData['path'] ?? '') . '/' . $tagName];
1421  $clearStackPath = (bool)($subOptions['clearStackPath'] ?? false);
1422  } else {
1423  $subOptions = $options;
1424  $clearStackPath = false;
1425  }
1426  if (empty($v)) {
1427  $content = '';
1428  } else {
1429  $content = ‪$nl . self::array2xml($v, $NSprefix, $level + 1, '', ‪$spaceInd, $subOptions, [
1430  'parentTagName' => $tagName,
1431  'grandParentTagName' => $stackData['parentTagName'] ?? '',
1432  'path' => $clearStackPath ? '' : ($stackData['path'] ?? '') . '/' . $tagName,
1433  ]) . (‪$spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '');
1434  }
1435  // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
1436  if (!isset($options['disableTypeAttrib']) || (int)$options['disableTypeAttrib'] != 2) {
1437  $attr .= ' type="array"';
1438  }
1439  } else {
1440  $stringValue = (string)$v;
1441  // Just a value:
1442  // Look for binary chars:
1443  $vLen = strlen($stringValue);
1444  // Go for base64 encoding if the initial segment NOT matching any binary char has the same length as the whole string!
1445  if ($vLen && strcspn($stringValue, $binaryChars) != $vLen) {
1446  // If the value contained binary chars then we base64-encode it and set an attribute to notify this situation:
1447  $content = ‪$nl . chunk_split(base64_encode($stringValue));
1448  $attr .= ' base64="1"';
1449  } else {
1450  // Otherwise, just htmlspecialchar the stuff:
1451  $content = htmlspecialchars($stringValue);
1452  $dType = gettype($v);
1453  if ($dType === 'string') {
1454  if (isset($options['useCDATA']) && $options['useCDATA'] && $content != $stringValue) {
1455  $content = '<![CDATA[' . $stringValue . ']]>';
1456  }
1457  } elseif (!($options['disableTypeAttrib'] ?? false)) {
1458  $attr .= ' type="' . $dType . '"';
1459  }
1460  }
1461  }
1462  if ($tagName !== '') {
1463  // Add the element to the output string:
1464  ‪$output .= (‪$spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '')
1465  . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . ‪$nl;
1466  }
1467  }
1468  // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
1469  if (!$level) {
1470  ‪$output = '<' . $docTag . '>' . ‪$nl . ‪$output . '</' . $docTag . '>';
1471  }
1472  return ‪$output;
1473  }
1474 
1487  public static function ‪xml2array($string, $NSprefix = '', $reportDocTag = false)
1488  {
1489  $runtimeCache = static::makeInstance(CacheManager::class)->getCache('runtime');
1490  $firstLevelCache = $runtimeCache->get('generalUtilityXml2Array') ?: [];
1491  $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0'));
1492  // Look up in first level cache
1493  if (empty($firstLevelCache[$identifier])) {
1494  $firstLevelCache[$identifier] = ‪self::xml2arrayProcess($string, $NSprefix, $reportDocTag);
1495  $runtimeCache->set('generalUtilityXml2Array', $firstLevelCache);
1496  }
1497  return $firstLevelCache[$identifier];
1498  }
1499 
1510  public static function ‪xml2arrayProcess($string, $NSprefix = '', $reportDocTag = false)
1511  {
1512  $string = trim((string)$string);
1513  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
1514  $previousValueOfEntityLoader = null;
1515  if (PHP_MAJOR_VERSION < 8) {
1516  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
1517  }
1518  // Create parser:
1519  ‪$parser = xml_parser_create();
1520  $vals = [];
1521  $index = [];
1522  xml_parser_set_option(‪$parser, XML_OPTION_CASE_FOLDING, 0);
1523  xml_parser_set_option(‪$parser, XML_OPTION_SKIP_WHITE, 0);
1524  // Default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
1525  $match = [];
1526  preg_match('/^[[:space:]]*<\\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match);
1527  $theCharset = $match[1] ?? 'utf-8';
1528  // us-ascii / utf-8 / iso-8859-1
1529  xml_parser_set_option(‪$parser, XML_OPTION_TARGET_ENCODING, $theCharset);
1530  // Parse content:
1531  xml_parse_into_struct(‪$parser, $string, $vals, $index);
1532  if (PHP_MAJOR_VERSION < 8) {
1533  libxml_disable_entity_loader($previousValueOfEntityLoader);
1534  }
1535  // If error, return error message:
1536  if (xml_get_error_code(‪$parser)) {
1537  return 'Line ' . xml_get_current_line_number(‪$parser) . ': ' . xml_error_string(xml_get_error_code(‪$parser));
1538  }
1539  xml_parser_free(‪$parser);
1540  // Init vars:
1541  $stack = [[]];
1542  $stacktop = 0;
1543  $current = [];
1544  $tagName = '';
1545  $documentTag = '';
1546  // Traverse the parsed XML structure:
1547  foreach ($vals as $key => $val) {
1548  // First, process the tag-name (which is used in both cases, whether "complete" or "close")
1549  $tagName = $val['tag'];
1550  if (!$documentTag) {
1551  $documentTag = $tagName;
1552  }
1553  // Test for name space:
1554  $tagName = $NSprefix && strpos($tagName, $NSprefix) === 0 ? substr($tagName, strlen($NSprefix)) : $tagName;
1555  // Test for numeric tag, encoded on the form "nXXX":
1556  $testNtag = substr($tagName, 1);
1557  // Closing tag.
1558  $tagName = $tagName[0] === 'n' && ‪MathUtility::canBeInterpretedAsInteger($testNtag) ? (int)$testNtag : $tagName;
1559  // Test for alternative index value:
1560  if ((string)($val['attributes']['index'] ?? '') !== '') {
1561  $tagName = $val['attributes']['index'];
1562  }
1563  // Setting tag-values, manage stack:
1564  switch ($val['type']) {
1565  case 'open':
1566  // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
1567  // Setting blank place holder
1568  $current[$tagName] = [];
1569  $stack[$stacktop++] = $current;
1570  $current = [];
1571  break;
1572  case 'close':
1573  // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
1574  $oldCurrent = $current;
1575  $current = $stack[--$stacktop];
1576  // Going to the end of array to get placeholder key, key($current), and fill in array next:
1577  end($current);
1578  $current[key($current)] = $oldCurrent;
1579  unset($oldCurrent);
1580  break;
1581  case 'complete':
1582  // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
1583  if (!empty($val['attributes']['base64'])) {
1584  $current[$tagName] = base64_decode($val['value']);
1585  } else {
1586  // Had to cast it as a string - otherwise it would be evaluate FALSE if tested with isset()!!
1587  $current[$tagName] = (string)($val['value'] ?? '');
1588  // Cast type:
1589  switch ((string)($val['attributes']['type'] ?? '')) {
1590  case 'integer':
1591  $current[$tagName] = (int)$current[$tagName];
1592  break;
1593  case 'double':
1594  $current[$tagName] = (float)$current[$tagName];
1595  break;
1596  case 'boolean':
1597  $current[$tagName] = (bool)$current[$tagName];
1598  break;
1599  case 'NULL':
1600  $current[$tagName] = null;
1601  break;
1602  case 'array':
1603  // MUST be an empty array since it is processed as a value; Empty arrays would end up here because they would have no tags inside...
1604  $current[$tagName] = [];
1605  break;
1606  }
1607  }
1608  break;
1609  }
1610  }
1611  if ($reportDocTag) {
1612  $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
1613  }
1614  // Finally return the content of the document tag.
1615  return $current[$tagName];
1616  }
1617 
1624  public static function ‪xmlRecompileFromStructValArray(array $vals)
1625  {
1626  $XMLcontent = '';
1627  foreach ($vals as $val) {
1628  $type = $val['type'];
1629  // Open tag:
1630  if ($type === 'open' || $type === 'complete') {
1631  $XMLcontent .= '<' . $val['tag'];
1632  if (isset($val['attributes'])) {
1633  foreach ($val['attributes'] as $k => $v) {
1634  $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"';
1635  }
1636  }
1637  if ($type === 'complete') {
1638  if (isset($val['value'])) {
1639  $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>';
1640  } else {
1641  $XMLcontent .= '/>';
1642  }
1643  } else {
1644  $XMLcontent .= '>';
1645  }
1646  if ($type === 'open' && isset($val['value'])) {
1647  $XMLcontent .= htmlspecialchars($val['value']);
1648  }
1649  }
1650  // Finish tag:
1651  if ($type === 'close') {
1652  $XMLcontent .= '</' . $val['tag'] . '>';
1653  }
1654  // Cdata
1655  if ($type === 'cdata') {
1656  $XMLcontent .= htmlspecialchars($val['value']);
1657  }
1658  }
1659  return $XMLcontent;
1660  }
1661 
1670  public static function ‪minifyJavaScript($script, &$error = '')
1671  {
1672  trigger_error('Calling GeneralUtility::minifyJavaScript directly will be removed in TYPO3 v12.0. Use ResourceCompressor->compressJavaScriptSource() instead.', E_USER_DEPRECATED);
1673  $fakeThis = null;
1674  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'] ?? [] as $hookMethod) {
1675  try {
1676  $parameters = ['script' => $script];
1677  $script = static::callUserFunction($hookMethod, $parameters, $fakeThis);
1678  } catch (\Exception $e) {
1679  $error .= 'Error minifying Javascript: ' . $e->getMessage();
1680  static::getLogger()->warning('Error minifying Javascript: {file}, hook: {hook}', [
1681  'file' => $script,
1682  'hook' => $hookMethod,
1683  'exception' => $e,
1684  ]);
1685  }
1686  }
1687  return $script;
1688  }
1689 
1690  /*************************
1691  *
1692  * FILES FUNCTIONS
1693  *
1694  *************************/
1702  public static function ‪getUrl($url)
1703  {
1704  // Looks like it's an external file, use Guzzle by default
1705  if (preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
1706  $requestFactory = static::makeInstance(RequestFactory::class);
1707  try {
1708  $response = $requestFactory->request($url);
1709  } catch (RequestException $exception) {
1710  return false;
1711  }
1712  $content = $response->getBody()->getContents();
1713  } else {
1714  $content = @file_get_contents($url);
1715  }
1716  return $content;
1717  }
1718 
1727  public static function ‪writeFile($file, $content, $changePermissions = false)
1728  {
1729  if (!@is_file($file)) {
1730  $changePermissions = true;
1731  }
1732  if ($fd = fopen($file, 'wb')) {
1733  $res = fwrite($fd, $content);
1734  fclose($fd);
1735  if ($res === false) {
1736  return false;
1737  }
1738  // Change the permissions only if the file has just been created
1739  if ($changePermissions) {
1740  static::fixPermissions($file);
1741  }
1742  return true;
1743  }
1744  return false;
1745  }
1746 
1754  public static function ‪fixPermissions($path, $recursive = false)
1755  {
1756  $targetPermissions = null;
1757  if (‪Environment::isWindows()) {
1758  return true;
1759  }
1760  $result = false;
1761  // Make path absolute
1762  if (!‪PathUtility::isAbsolutePath($path)) {
1763  $path = static::getFileAbsFileName($path);
1764  }
1765  if (static::isAllowedAbsPath($path)) {
1766  if (@is_file($path)) {
1767  $targetPermissions = (string)(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] ?? '0644');
1768  } elseif (@is_dir($path)) {
1769  $targetPermissions = (string)(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] ?? '0755');
1770  }
1771  if (!empty($targetPermissions)) {
1772  // make sure it's always 4 digits
1773  $targetPermissions = str_pad($targetPermissions, 4, '0', STR_PAD_LEFT);
1774  $targetPermissions = octdec($targetPermissions);
1775  // "@" is there because file is not necessarily OWNED by the user
1776  $result = @chmod($path, (int)$targetPermissions);
1777  }
1778  // Set createGroup if not empty
1779  if (
1780  isset(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'])
1781  && ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] !== ''
1782  ) {
1783  // "@" is there because file is not necessarily OWNED by the user
1784  $changeGroupResult = @chgrp($path, ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup']);
1785  $result = $changeGroupResult ? $result : false;
1786  }
1787  // Call recursive if recursive flag if set and $path is directory
1788  if ($recursive && @is_dir($path)) {
1789  $handle = opendir($path);
1790  if (is_resource($handle)) {
1791  while (($file = readdir($handle)) !== false) {
1792  $recursionResult = null;
1793  if ($file !== '.' && $file !== '..') {
1794  if (@is_file($path . '/' . $file)) {
1795  $recursionResult = static::fixPermissions($path . '/' . $file);
1796  } elseif (@is_dir($path . '/' . $file)) {
1797  $recursionResult = static::fixPermissions($path . '/' . $file, true);
1798  }
1799  if (isset($recursionResult) && !$recursionResult) {
1800  $result = false;
1801  }
1802  }
1803  }
1804  closedir($handle);
1805  }
1806  }
1807  }
1808  return $result;
1809  }
1810 
1819  public static function ‪writeFileToTypo3tempDir($filepath, $content)
1820  {
1821  // Parse filepath into directory and basename:
1822  $fI = pathinfo($filepath);
1823  $fI['dirname'] .= '/';
1824  // Check parts:
1825  if (!static::validPathStr($filepath) || !$fI['basename'] || strlen($fI['basename']) >= 60) {
1826  return 'Input filepath "' . $filepath . '" was generally invalid!';
1827  }
1828 
1829  // Setting main temporary directory name (standard)
1830  $allowedPathPrefixes = [
1831  ‪Environment::getPublicPath() . '/typo3temp' => 'Environment::getPublicPath() + "/typo3temp/"',
1832  ];
1833  // Also allow project-path + /var/
1834  if (‪Environment::getVarPath() !== ‪Environment::getPublicPath() . '/typo3temp/var') {
1835  $relPath = substr(‪Environment::getVarPath(), strlen(‪Environment::getProjectPath()) + 1);
1836  $allowedPathPrefixes[‪Environment::getVarPath()] = 'ProjectPath + ' . $relPath;
1837  }
1838 
1839  $errorMessage = null;
1840  foreach ($allowedPathPrefixes as $pathPrefix => $prefixLabel) {
1841  $dirName = $pathPrefix . '/';
1842  // Invalid file path, let's check for the other path, if it exists
1843  if (!str_starts_with($fI['dirname'], $dirName)) {
1844  if ($errorMessage === null) {
1845  $errorMessage = '"' . $fI['dirname'] . '" was not within directory ' . $prefixLabel;
1846  }
1847  continue;
1848  }
1849  // This resets previous error messages from the first path
1850  $errorMessage = null;
1851 
1852  if (!@is_dir($dirName)) {
1853  $errorMessage = $prefixLabel . ' was not a directory!';
1854  // continue and see if the next iteration resets the errorMessage above
1855  continue;
1856  }
1857  // Checking if the "subdir" is found
1858  $subdir = substr($fI['dirname'], strlen($dirName));
1859  if ($subdir) {
1860  if (preg_match('#^(?:[[:alnum:]_]+/)+$#', $subdir)) {
1861  $dirName .= $subdir;
1862  if (!@is_dir($dirName)) {
1863  static::mkdir_deep($pathPrefix . '/' . $subdir);
1864  }
1865  } else {
1866  $errorMessage = 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/+"';
1867  break;
1868  }
1869  }
1870  // Checking dir-name again (sub-dir might have been created)
1871  if (@is_dir($dirName)) {
1872  if ($filepath === $dirName . $fI['basename']) {
1873  static::writeFile($filepath, $content);
1874  if (!@is_file($filepath)) {
1875  $errorMessage = 'The file was not written to the disk. Please, check that you have write permissions to the ' . $prefixLabel . ' directory.';
1876  }
1877  break;
1878  }
1879  $errorMessage = 'Calculated file location didn\'t match input "' . $filepath . '".';
1880  break;
1881  }
1882  $errorMessage = '"' . $dirName . '" is not a directory!';
1883  break;
1884  }
1885  return $errorMessage;
1886  }
1887 
1896  public static function ‪mkdir($newFolder)
1897  {
1898  $result = @‪mkdir($newFolder, (int)octdec((string)(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] ?? '0')));
1899  if ($result) {
1900  static::fixPermissions($newFolder);
1901  }
1902  return $result;
1903  }
1904 
1913  public static function ‪mkdir_deep($directory)
1914  {
1915  if (!is_string($directory)) {
1916  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($directory) . '" but a string is expected.', 1303662955);
1917  }
1918  // Ensure there is only one slash
1919  $fullPath = rtrim($directory, '/') . '/';
1920  if ($fullPath !== '/' && !is_dir($fullPath)) {
1921  $firstCreatedPath = static::createDirectoryPath($fullPath);
1922  if ($firstCreatedPath !== '') {
1923  static::fixPermissions($firstCreatedPath, true);
1924  }
1925  }
1926  }
1927 
1939  protected static function ‪createDirectoryPath($fullDirectoryPath)
1940  {
1941  $currentPath = $fullDirectoryPath;
1942  $firstCreatedPath = '';
1943  $permissionMask = (int)octdec((string)(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] ?? '0'));
1944  if (!@is_dir($currentPath)) {
1945  do {
1946  $firstCreatedPath = $currentPath;
1947  $separatorPosition = (int)strrpos($currentPath, DIRECTORY_SEPARATOR);
1948  $currentPath = substr($currentPath, 0, $separatorPosition);
1949  } while (!is_dir($currentPath) && $separatorPosition > 0);
1950  $result = @‪mkdir($fullDirectoryPath, $permissionMask, true);
1951  // Check existence of directory again to avoid race condition. Directory could have get created by another process between previous is_dir() and mkdir()
1952  if (!$result && !@is_dir($fullDirectoryPath)) {
1953  throw new \RuntimeException('Could not create directory "' . $fullDirectoryPath . '"!', 1170251401);
1954  }
1955  }
1956  return $firstCreatedPath;
1957  }
1958 
1966  public static function ‪rmdir($path, $removeNonEmpty = false)
1967  {
1968  $OK = false;
1969  // Remove trailing slash
1970  $path = preg_replace('|/$|', '', $path) ?? '';
1971  $isWindows = DIRECTORY_SEPARATOR === '\\';
1972  if (file_exists($path)) {
1973  $OK = true;
1974  if (!is_link($path) && is_dir($path)) {
1975  if ($removeNonEmpty === true && ($handle = @opendir($path))) {
1976  $entries = [];
1977 
1978  while (false !== ($file = readdir($handle))) {
1979  if ($file === '.' || $file === '..') {
1980  continue;
1981  }
1982 
1983  $entries[] = $path . '/' . $file;
1984  }
1985 
1986  closedir($handle);
1987 
1988  foreach ($entries as $entry) {
1989  if (!static::rmdir($entry, $removeNonEmpty)) {
1990  $OK = false;
1991  }
1992  }
1993  }
1994  if ($OK) {
1995  $OK = @‪rmdir($path);
1996  }
1997  } elseif (is_link($path) && is_dir($path) && $isWindows) {
1998  $OK = @‪rmdir($path);
1999  } else {
2000  // If $path is a file, simply remove it
2001  $OK = @unlink($path);
2002  }
2003  clearstatcache();
2004  } elseif (is_link($path)) {
2005  $OK = @unlink($path);
2006  if (!$OK && $isWindows) {
2007  // Try to delete dead folder links on Windows systems
2008  $OK = @‪rmdir($path);
2009  }
2010  clearstatcache();
2011  }
2012  return $OK;
2013  }
2014 
2023  public static function ‪get_dirs($path)
2024  {
2025  $dirs = null;
2026  if ($path) {
2027  if (is_dir($path)) {
2028  ‪$dir = scandir($path);
2029  $dirs = [];
2030  foreach (‪$dir as $entry) {
2031  if (is_dir($path . '/' . $entry) && $entry !== '..' && $entry !== '.') {
2032  $dirs[] = $entry;
2033  }
2034  }
2035  } else {
2036  $dirs = 'error';
2037  }
2038  }
2039  return $dirs;
2040  }
2041 
2054  public static function getFilesInDir($path, $extensionList = '', $prependPath = false, $order = '', $excludePattern = '')
2055  {
2056  $excludePattern = (string)$excludePattern;
2057  $path = rtrim($path, '/');
2058  if (!@is_dir($path)) {
2059  return [];
2060  }
2061 
2062  $rawFileList = scandir($path);
2063  if ($rawFileList === false) {
2064  return 'error opening path: "' . $path . '"';
2065  }
2066 
2067  $pathPrefix = $path . '/';
2068  $allowedFileExtensionArray = ‪self::trimExplode(',', $extensionList);
2069  $extensionList = ',' . str_replace(' ', '', $extensionList) . ',';
2070  $files = [];
2071  foreach ($rawFileList as $entry) {
2072  $completePathToEntry = $pathPrefix . $entry;
2073  if (!@is_file($completePathToEntry)) {
2074  continue;
2075  }
2076 
2077  foreach ($allowedFileExtensionArray as $allowedFileExtension) {
2078  if (
2079  ($extensionList === ',,' || stripos($extensionList, ',' . substr($entry, strlen($allowedFileExtension) * -1, strlen($allowedFileExtension)) . ',') !== false)
2080  && ($excludePattern === '' || !preg_match('/^' . $excludePattern . '$/', $entry))
2081  ) {
2082  if ($order !== 'mtime') {
2083  $files[] = $entry;
2084  } else {
2085  // Store the value in the key so we can do a fast asort later.
2086  $files[$entry] = filemtime($completePathToEntry);
2087  }
2088  }
2089  }
2090  }
2091 
2092  $valueName = 'value';
2093  if ($order === 'mtime') {
2094  asort($files);
2095  $valueName = 'key';
2096  }
2097 
2098  $valuePathPrefix = $prependPath ? $pathPrefix : '';
2099  $foundFiles = [];
2100  foreach ($files as $key => $value) {
2101  // Don't change this ever - extensions may depend on the fact that the hash is an md5 of the path! (import/export extension)
2102  $foundFiles[md5($pathPrefix . ${$valueName})] = $valuePathPrefix . ${$valueName};
2103  }
2104 
2105  return $foundFiles;
2106  }
2107 
2119  public static function getAllFilesAndFoldersInPath(array $fileArr, $path, $extList = '', $regDirs = false, $recursivityLevels = 99, $excludePattern = '')
2120  {
2121  if ($regDirs) {
2122  $fileArr[md5($path)] = $path;
2123  }
2124  $fileArr = array_merge($fileArr, (array)self::getFilesInDir($path, $extList, true, '', $excludePattern));
2125  $dirs = ‪self::get_dirs($path);
2126  if ($recursivityLevels > 0 && is_array($dirs)) {
2127  foreach ($dirs as $subdirs) {
2128  if ((string)$subdirs !== '' && ($excludePattern === '' || !preg_match('/^' . $excludePattern . '$/', $subdirs))) {
2129  $fileArr = self::getAllFilesAndFoldersInPath($fileArr, $path . $subdirs . '/', $extList, $regDirs, $recursivityLevels - 1, $excludePattern);
2130  }
2131  }
2132  }
2133  return $fileArr;
2134  }
2135 
2143  public static function removePrefixPathFromList(array $fileArr, string $prefixToRemove)
2144  {
2145  foreach ($fileArr as &$absFileRef) {
2146  if (str_starts_with($absFileRef, $prefixToRemove)) {
2147  $absFileRef = substr($absFileRef, strlen($prefixToRemove));
2148  } else {
2149  return 'ERROR: One or more of the files was NOT prefixed with the prefix-path!';
2150  }
2151  }
2152  unset($absFileRef);
2153  return $fileArr;
2154  }
2155 
2162  public static function fixWindowsFilePath($theFile)
2163  {
2164  return str_replace(['\\', '//'], '/', $theFile);
2165  }
2166 
2174  public static function resolveBackPath($pathStr)
2175  {
2176  if (!str_contains($pathStr, '..')) {
2177  return $pathStr;
2178  }
2179  $parts = explode('/', $pathStr);
2180  ‪$output = [];
2181  $c = 0;
2182  foreach ($parts as $part) {
2183  if ($part === '..') {
2184  if ($c) {
2185  array_pop(‪$output);
2186  --$c;
2187  } else {
2188  ‪$output[] = $part;
2189  }
2190  } else {
2191  ++$c;
2192  ‪$output[] = $part;
2193  }
2194  }
2195  return implode('/', ‪$output);
2196  }
2197 
2207  public static function locationHeaderUrl($path)
2208  {
2209  if (strpos($path, '//') === 0) {
2210  return $path;
2211  }
2212 
2213  // relative to HOST
2214  if (strpos($path, '/') === 0) {
2215  return self::getIndpEnv('TYPO3_REQUEST_HOST') . $path;
2216  }
2217 
2218  $urlComponents = parse_url($path);
2219  if (!($urlComponents['scheme'] ?? false)) {
2220  // No scheme either
2221  return self::getIndpEnv('TYPO3_REQUEST_DIR') . $path;
2222  }
2223 
2224  return $path;
2225  }
2226 
2234  public static function getMaxUploadFileSize()
2235  {
2236  $uploadMaxFilesize = (string)ini_get('upload_max_filesize');
2237  $postMaxSize = (string)ini_get('post_max_size');
2238  // Check for PHP restrictions of the maximum size of one of the $_FILES
2239  $phpUploadLimit = self::getBytesFromSizeMeasurement($uploadMaxFilesize);
2240  // Check for PHP restrictions of the maximum $_POST size
2241  $phpPostLimit = self::getBytesFromSizeMeasurement($postMaxSize);
2242  // If the total amount of post data is smaller (!) than the upload_max_filesize directive,
2243  // then this is the real limit in PHP
2244  $phpUploadLimit = $phpPostLimit > 0 && $phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit;
2245  return (int)(floor($phpUploadLimit) / 1024);
2246  }
2247 
2254  public static function getBytesFromSizeMeasurement($measurement)
2255  {
2256  $bytes = (float)$measurement;
2257  if (stripos($measurement, 'G')) {
2258  $bytes *= 1024 * 1024 * 1024;
2259  } elseif (stripos($measurement, 'M')) {
2260  $bytes *= 1024 * 1024;
2261  } elseif (stripos($measurement, 'K')) {
2262  $bytes *= 1024;
2263  }
2264  return (int)$bytes;
2265  }
2266 
2283  public static function createVersionNumberedFilename($file)
2284  {
2285  $isFrontend = (‪$GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
2286  && ‪ApplicationType::fromRequest(‪$GLOBALS['TYPO3_REQUEST'])->isFrontend();
2287  $lookupFile = explode('?', $file);
2288  $path = $lookupFile[0];
2289 
2290  // @todo: in v12 this should be resolved by using Environment::getPublicPath() once
2291  if ($isFrontend) {
2292  // Frontend should still allow /static/myfile.css - see #98106
2293  // This should happen regardless of the incoming path is absolute or not
2294  $path = self::resolveBackPath(self::dirname(‪Environment::getCurrentScript()) . '/' . $path);
2295  } elseif (!‪PathUtility::isAbsolutePath($path)) {
2296  // Backend and non-absolute path
2297  $path = self::resolveBackPath(self::dirname(‪Environment::getCurrentScript()) . '/' . $path);
2298  }
2299 
2300  $doNothing = false;
2301 
2302  if ($isFrontend) {
2303  $mode = strtolower(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename']);
2304  if ($mode === 'embed') {
2305  $mode = true;
2306  } else {
2307  if ($mode === 'querystring') {
2308  $mode = false;
2309  } else {
2310  $doNothing = true;
2311  }
2312  }
2313  } else {
2314  $mode = ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename'];
2315  }
2316  try {
2317  $fileExists = file_exists($path);
2318  } catch (\Throwable $e) {
2319  $fileExists = false;
2320  }
2321  if ($doNothing || !$fileExists) {
2322  // File not found, return filename unaltered
2323  $fullName = $file;
2324  } else {
2325  if (!$mode) {
2326  // If use of .htaccess rule is not configured,
2327  // we use the default query-string method
2328  if (!empty($lookupFile[1])) {
2329  $separator = '&';
2330  } else {
2331  $separator = '?';
2332  }
2333  $fullName = $file . $separator . filemtime($path);
2334  } else {
2335  // Change the filename
2336  $name = explode('.', $lookupFile[0]);
2337  $extension = array_pop($name);
2338  array_push($name, filemtime($path), $extension);
2339  $fullName = implode('.', $name);
2340  // Append potential query string
2341  $fullName .= !empty($lookupFile[1]) ? '?' . $lookupFile[1] : '';
2342  }
2343  }
2344  return $fullName;
2345  }
2346 
2354  public static function writeJavaScriptContentToTemporaryFile(string $content)
2355  {
2356  $script = 'typo3temp/assets/js/' . md5($content) . '.js';
2357  if (!@is_file(‪Environment::getPublicPath() . '/' . $script)) {
2359  }
2360  return $script;
2361  }
2362 
2370  public static function writeStyleSheetContentToTemporaryFile(string $content)
2371  {
2372  $script = 'typo3temp/assets/css/' . md5($content) . '.css';
2373  if (!@is_file(‪Environment::getPublicPath() . '/' . $script)) {
2375  }
2376  return $script;
2377  }
2378 
2379  /*************************
2380  *
2381  * SYSTEM INFORMATION
2382  *
2383  *************************/
2384 
2393  public static function linkThisScript(array $getParams = [])
2394  {
2395  $parts = self::getIndpEnv('SCRIPT_NAME');
2396  $params = self::_GET();
2397  foreach ($getParams as $key => $value) {
2398  if ($value !== '') {
2399  $params[$key] = $value;
2400  } else {
2401  unset($params[$key]);
2402  }
2403  }
2404  $pString = ‪self::implodeArrayForUrl('', $params);
2405  return $pString ? $parts . '?' . ltrim($pString, '&') : $parts;
2406  }
2407 
2415  public static function setIndpEnv($envName, $value)
2416  {
2417  self::$indpEnvCache[$envName] = $value;
2418  }
2419 
2428  public static function getIndpEnv($getEnvName)
2429  {
2430  if (array_key_exists($getEnvName, self::$indpEnvCache)) {
2431  return self::$indpEnvCache[$getEnvName];
2432  }
2433 
2434  /*
2435  Conventions:
2436  output from parse_url():
2437  URL: http://username:password@192.168.1.4:8080/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value#link1
2438  [scheme] => 'http'
2439  [user] => 'username'
2440  [pass] => 'password'
2441  [host] => '192.168.1.4'
2442  [port] => '8080'
2443  [path] => '/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/'
2444  [query] => 'arg1,arg2,arg3&p1=parameter1&p2[key]=value'
2445  [fragment] => 'link1'Further definition: [path_script] = '/typo3/32/temp/phpcheck/index.php'
2446  [path_dir] = '/typo3/32/temp/phpcheck/'
2447  [path_info] = '/arg1/arg2/arg3/'
2448  [path] = [path_script/path_dir][path_info]Keys supported:URI______:
2449  REQUEST_URI = [path]?[query] = /typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
2450  HTTP_HOST = [host][:[port]] = 192.168.1.4:8080
2451  SCRIPT_NAME = [path_script]++ = /typo3/32/temp/phpcheck/index.php // NOTICE THAT SCRIPT_NAME will return the php-script name ALSO. [path_script] may not do that (eg. '/somedir/' may result in SCRIPT_NAME '/somedir/index.php')!
2452  PATH_INFO = [path_info] = /arg1/arg2/arg3/
2453  QUERY_STRING = [query] = arg1,arg2,arg3&p1=parameter1&p2[key]=value
2454  HTTP_REFERER = [scheme]://[host][:[port]][path] = http://192.168.1.4:8080/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
2455  (Notice: NO username/password + NO fragment)CLIENT____:
2456  REMOTE_ADDR = (client IP)
2457  REMOTE_HOST = (client host)
2458  HTTP_USER_AGENT = (client user agent)
2459  HTTP_ACCEPT_LANGUAGE = (client accept language)SERVER____:
2460  SCRIPT_FILENAME = Absolute filename of script (Differs between windows/unix). On windows 'C:\\some\\path\\' will be converted to 'C:/some/path/'Special extras:
2461  TYPO3_HOST_ONLY = [host] = 192.168.1.4
2462  TYPO3_PORT = [port] = 8080 (blank if 80, taken from host value)
2463  TYPO3_REQUEST_HOST = [scheme]://[host][:[port]]
2464  TYPO3_REQUEST_URL = [scheme]://[host][:[port]][path]?[query] (scheme will by default be "http" until we can detect something different)
2465  TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script]
2466  TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir]
2467  TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend
2468  TYPO3_SITE_PATH = [path_dir] of the TYPO3 website frontend
2469  TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website
2470  TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically)
2471  TYPO3_SSL = Returns TRUE if this session uses SSL/TLS (https)
2472  TYPO3_PROXY = Returns TRUE if this session runs over a well known proxyNotice: [fragment] is apparently NEVER available to the script!Testing suggestions:
2473  - Output all the values.
2474  - In the script, make a link to the script it self, maybe add some parameters and click the link a few times so HTTP_REFERER is seen
2475  - ALSO TRY the script from the ROOT of a site (like 'http://www.mytest.com/' and not 'http://www.mytest.com/test/' !!)
2476  */
2477  $retVal = '';
2478  switch ((string)$getEnvName) {
2479  case 'SCRIPT_NAME':
2480  $retVal = $_SERVER['SCRIPT_NAME'] ?? '';
2481  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
2482  if (self::cmpIP($_SERVER['REMOTE_ADDR'] ?? '', ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'] ?? '')) {
2483  if (self::getIndpEnv('TYPO3_SSL') && ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
2484  $retVal = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
2485  } elseif (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
2486  $retVal = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
2487  }
2488  }
2489  $retVal = self::encodeFileSystemPathComponentForUrlPath($retVal);
2490  break;
2491  case 'SCRIPT_FILENAME':
2493  break;
2494  case 'REQUEST_URI':
2495  // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REQUEST_URI'))
2496  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar'])) {
2497  // This is for URL rewriters that store the original URI in a server variable (eg ISAPI_Rewriter for IIS: HTTP_X_REWRITE_URL)
2498  [$v, $n] = explode('|', ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']);
2499  $retVal = ‪$GLOBALS[$v][$n];
2500  } elseif (empty($_SERVER['REQUEST_URI'])) {
2501  // This is for ISS/CGI which does not have the REQUEST_URI available.
2502  $retVal = '/' . ltrim(self::getIndpEnv('SCRIPT_NAME'), '/') . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
2503  } else {
2504  $retVal = '/' . ltrim($_SERVER['REQUEST_URI'], '/');
2505  }
2506  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
2507  if (isset($_SERVER['REMOTE_ADDR'], ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])
2508  && self::cmpIP($_SERVER['REMOTE_ADDR'], ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])
2509  ) {
2510  if (self::getIndpEnv('TYPO3_SSL') && ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
2511  $retVal = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
2512  } elseif (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
2513  $retVal = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
2514  }
2515  }
2516  break;
2517  case 'PATH_INFO':
2518  $retVal = $_SERVER['PATH_INFO'] ?? '';
2519  break;
2520  case 'TYPO3_REV_PROXY':
2521  $retVal = self::cmpIP($_SERVER['REMOTE_ADDR'] ?? '', ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']);
2522  break;
2523  case 'REMOTE_ADDR':
2524  $retVal = $_SERVER['REMOTE_ADDR'] ?? '';
2525  if (self::cmpIP($retVal, ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'] ?? '')) {
2526  $ip = ‪self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '');
2527  // Choose which IP in list to use
2528  if (!empty($ip)) {
2529  switch (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
2530  case 'last':
2531  $ip = array_pop($ip);
2532  break;
2533  case 'first':
2534  $ip = array_shift($ip);
2535  break;
2536  case 'none':
2537 
2538  default:
2539  $ip = '';
2540  }
2541  }
2542  if (self::validIP((string)$ip)) {
2543  $retVal = $ip;
2544  }
2545  }
2546  break;
2547  case 'HTTP_HOST':
2548  // if it is not set we're most likely on the cli
2549  $retVal = $_SERVER['HTTP_HOST'] ?? '';
2550  if (isset($_SERVER['REMOTE_ADDR']) && static::cmpIP($_SERVER['REMOTE_ADDR'], ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
2551  $host = ‪self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_HOST'] ?? '');
2552  // Choose which host in list to use
2553  if (!empty($host)) {
2554  switch (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
2555  case 'last':
2556  $host = array_pop($host);
2557  break;
2558  case 'first':
2559  $host = array_shift($host);
2560  break;
2561  case 'none':
2562 
2563  default:
2564  $host = '';
2565  }
2566  }
2567  if ($host) {
2568  $retVal = $host;
2569  }
2570  }
2571  break;
2572  case 'HTTP_REFERER':
2573 
2574  case 'HTTP_USER_AGENT':
2575 
2576  case 'HTTP_ACCEPT_ENCODING':
2577 
2578  case 'HTTP_ACCEPT_LANGUAGE':
2579 
2580  case 'REMOTE_HOST':
2581 
2582  case 'QUERY_STRING':
2583  $retVal = $_SERVER[$getEnvName] ?? '';
2584  break;
2585  case 'TYPO3_DOCUMENT_ROOT':
2586  // Get the web root (it is not the root of the TYPO3 installation)
2587  // The absolute path of the script can be calculated with TYPO3_DOCUMENT_ROOT + SCRIPT_FILENAME
2588  // Some CGI-versions (LA13CGI) and mod-rewrite rules on MODULE versions will deliver a 'wrong' DOCUMENT_ROOT (according to our description). Further various aliases/mod_rewrite rules can disturb this as well.
2589  // Therefore the DOCUMENT_ROOT is now always calculated as the SCRIPT_FILENAME minus the end part shared with SCRIPT_NAME.
2590  $SFN = self::getIndpEnv('SCRIPT_FILENAME');
2591  // Use rawurldecode to reverse the result of self::encodeFileSystemPathComponentForUrlPath()
2592  // which has been applied to getIndpEnv(SCRIPT_NAME) for web URI usage.
2593  // We compare with a file system path (SCRIPT_FILENAME) in here and therefore need to undo the encoding.
2594  $SN_A = array_map('rawurldecode', explode('/', strrev(self::getIndpEnv('SCRIPT_NAME'))));
2595  $SFN_A = explode('/', strrev($SFN));
2596  $acc = [];
2597  foreach ($SN_A as $kk => $vv) {
2598  if ((string)$SFN_A[$kk] === (string)$vv) {
2599  $acc[] = $vv;
2600  } else {
2601  break;
2602  }
2603  }
2604  $commonEnd = strrev(implode('/', $acc));
2605  if ((string)$commonEnd !== '') {
2606  $retVal = substr($SFN, 0, -(strlen($commonEnd) + 1));
2607  }
2608  break;
2609  case 'TYPO3_HOST_ONLY':
2610  $httpHost = self::getIndpEnv('HTTP_HOST');
2611  $httpHostBracketPosition = strpos($httpHost, ']');
2612  $httpHostParts = explode(':', $httpHost);
2613  $retVal = $httpHostBracketPosition !== false ? substr($httpHost, 0, $httpHostBracketPosition + 1) : array_shift($httpHostParts);
2614  break;
2615  case 'TYPO3_PORT':
2616  $httpHost = self::getIndpEnv('HTTP_HOST');
2617  $httpHostOnly = self::getIndpEnv('TYPO3_HOST_ONLY');
2618  $retVal = strlen($httpHost) > strlen($httpHostOnly) ? substr($httpHost, strlen($httpHostOnly) + 1) : '';
2619  break;
2620  case 'TYPO3_REQUEST_HOST':
2621  $retVal = (self::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://') . self::getIndpEnv('HTTP_HOST');
2622  break;
2623  case 'TYPO3_REQUEST_URL':
2624  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('REQUEST_URI');
2625  break;
2626  case 'TYPO3_REQUEST_SCRIPT':
2627  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('SCRIPT_NAME');
2628  break;
2629  case 'TYPO3_REQUEST_DIR':
2630  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/';
2631  break;
2632  case 'TYPO3_SITE_URL':
2635  $url = self::getIndpEnv('TYPO3_REQUEST_DIR');
2636  $siteUrl = substr($url, 0, -strlen($lPath));
2637  if (substr($siteUrl, -1) !== '/') {
2638  $siteUrl .= '/';
2639  }
2640  $retVal = $siteUrl;
2641  }
2642  break;
2643  case 'TYPO3_SITE_PATH':
2644  $retVal = substr(self::getIndpEnv('TYPO3_SITE_URL'), strlen(self::getIndpEnv('TYPO3_REQUEST_HOST')));
2645  break;
2646  case 'TYPO3_SITE_SCRIPT':
2647  $retVal = substr(self::getIndpEnv('TYPO3_REQUEST_URL'), strlen(self::getIndpEnv('TYPO3_SITE_URL')));
2648  break;
2649  case 'TYPO3_SSL':
2650  $proxySSL = trim(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL'] ?? '');
2651  if ($proxySSL === '*') {
2652  $proxySSL = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'];
2653  }
2654  if (self::cmpIP($_SERVER['REMOTE_ADDR'] ?? '', $proxySSL)) {
2655  $retVal = true;
2656  } else {
2657  $retVal = self::webserverUsesHttps();
2658  }
2659  break;
2660  case '_ARRAY':
2661  $out = [];
2662  // Here, list ALL possible keys to this function for debug display.
2663  $envTestVars = [
2664  'HTTP_HOST',
2665  'TYPO3_HOST_ONLY',
2666  'TYPO3_PORT',
2667  'PATH_INFO',
2668  'QUERY_STRING',
2669  'REQUEST_URI',
2670  'HTTP_REFERER',
2671  'TYPO3_REQUEST_HOST',
2672  'TYPO3_REQUEST_URL',
2673  'TYPO3_REQUEST_SCRIPT',
2674  'TYPO3_REQUEST_DIR',
2675  'TYPO3_SITE_URL',
2676  'TYPO3_SITE_SCRIPT',
2677  'TYPO3_SSL',
2678  'TYPO3_REV_PROXY',
2679  'SCRIPT_NAME',
2680  'TYPO3_DOCUMENT_ROOT',
2681  'SCRIPT_FILENAME',
2682  'REMOTE_ADDR',
2683  'REMOTE_HOST',
2684  'HTTP_USER_AGENT',
2685  'HTTP_ACCEPT_LANGUAGE',
2686  ];
2687  foreach ($envTestVars as $v) {
2688  $out[$v] = self::getIndpEnv($v);
2689  }
2690  reset($out);
2691  $retVal = $out;
2692  break;
2693  }
2694  self::$indpEnvCache[$getEnvName] = $retVal;
2695  return $retVal;
2696  }
2697 
2705  public static function isAllowedHostHeaderValue($hostHeaderValue)
2706  {
2707  trigger_error('GeneralUtility::isAllowedHostHeaderValue() will be removed in TYPO3 v12.0. Host header is verified by frontend and backend middlewares.', E_USER_DEPRECATED);
2708 
2709  $verifyHostHeader = new VerifyHostHeader(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] ?? '');
2710  return $verifyHostHeader->isAllowedHostHeaderValue($hostHeaderValue, $_SERVER);
2711  }
2712 
2723  protected static function webserverUsesHttps()
2724  {
2725  if (!empty($_SERVER['SSL_SESSION_ID'])) {
2726  return true;
2727  }
2728 
2729  // https://secure.php.net/manual/en/reserved.variables.server.php
2730  // "Set to a non-empty value if the script was queried through the HTTPS protocol."
2731  return !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off';
2732  }
2733 
2734  protected static function encodeFileSystemPathComponentForUrlPath(string $path): string
2735  {
2736  return implode('/', array_map('rawurlencode', explode('/', $path)));
2737  }
2738 
2739  /*************************
2740  *
2741  * TYPO3 SPECIFIC FUNCTIONS
2742  *
2743  *************************/
2753  public static function getFileAbsFileName($filename)
2754  {
2755  if ((string)$filename === '') {
2756  return '';
2757  }
2758  // Extension
2759  if (‪PathUtility::isExtensionPath($filename)) {
2760  try {
2762  } catch (‪PackageException $e) {
2763  $filename = '';
2764  }
2765  } elseif (!‪PathUtility::isAbsolutePath($filename)) {
2766  // is relative. Prepended with the public web folder
2767  $filename = ‪Environment::getPublicPath() . '/' . $filename;
2768  } elseif (!(
2769  str_starts_with($filename, ‪Environment::getProjectPath())
2770  || str_starts_with($filename, ‪Environment::getPublicPath())
2771  )) {
2772  // absolute, but set to blank if not allowed
2773  $filename = '';
2774  }
2775  if ((string)$filename !== '' && static::validPathStr($filename)) {
2776  // checks backpath.
2777  return $filename;
2778  }
2779  return '';
2780  }
2781 
2793  public static function validPathStr($theFile)
2794  {
2795  return !str_contains($theFile, '//') && !str_contains($theFile, '\\')
2796  && preg_match('#(?:^\\.\\.|/\\.\\./|[[:cntrl:]])#u', $theFile) === 0;
2797  }
2798 
2806  public static function isAbsPath($path)
2807  {
2808  trigger_error('GeneralUtility::isAbsPath() will be removed in TYPO3 v12.0. Use PathUtility::isAbsolutePath() instead.', E_USER_DEPRECATED);
2809  if (substr($path, 0, 6) === 'vfs://') {
2810  return true;
2811  }
2812  return
2813  (isset($path[0]) && $path[0] === '/')
2814  || (‪Environment::isWindows() && (strpos($path, ':/') === 1))
2815  || strpos($path, ':\\') === 1;
2816  }
2817 
2824  public static function isAllowedAbsPath($path)
2825  {
2826  if (substr($path, 0, 6) === 'vfs://') {
2827  return true;
2828  }
2829  return ‪PathUtility::isAbsolutePath($path) && static::validPathStr($path)
2830  && (
2831  str_starts_with($path, ‪Environment::getProjectPath())
2832  || str_starts_with($path, ‪Environment::getPublicPath())
2834  );
2835  }
2836 
2843  public static function copyDirectory($source, $destination)
2844  {
2845  if (!str_contains($source, ‪Environment::getProjectPath() . '/')) {
2846  $source = ‪Environment::getPublicPath() . '/' . $source;
2847  }
2848  if (!str_contains($destination, ‪Environment::getProjectPath() . '/')) {
2849  $destination = ‪Environment::getPublicPath() . '/' . $destination;
2850  }
2851  if (static::isAllowedAbsPath($source) && static::isAllowedAbsPath($destination)) {
2852  static::mkdir_deep($destination);
2853  $iterator = new \RecursiveIteratorIterator(
2854  new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
2855  \RecursiveIteratorIterator::SELF_FIRST
2856  );
2858  foreach ($iterator as $item) {
2859  $target = $destination . '/' . static::fixWindowsFilePath($iterator->getSubPathName());
2860  if ($item->isDir()) {
2861  static::mkdir($target);
2862  } else {
2863  static::upload_copy_move(static::fixWindowsFilePath($item->getPathname()), $target);
2864  }
2865  }
2866  }
2867  }
2868 
2879  public static function sanitizeLocalUrl($url = '')
2880  {
2881  $sanitizedUrl = '';
2882  if (!empty($url)) {
2883  $decodedUrl = rawurldecode($url);
2884  $parsedUrl = parse_url($decodedUrl);
2885  $testAbsoluteUrl = self::resolveBackPath($decodedUrl);
2886  $testRelativeUrl = self::resolveBackPath(self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/' . $decodedUrl);
2887  // Pass if URL is on the current host:
2888  if (self::isValidUrl($decodedUrl)) {
2889  if (self::isOnCurrentHost($decodedUrl) && strpos($decodedUrl, self::getIndpEnv('TYPO3_SITE_URL')) === 0) {
2890  $sanitizedUrl = $url;
2891  }
2892  } elseif (‪PathUtility::isAbsolutePath($decodedUrl) && self::isAllowedAbsPath($decodedUrl)) {
2893  $sanitizedUrl = $url;
2894  } elseif (strpos($testAbsoluteUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && $decodedUrl[0] === '/' &&
2895  substr($decodedUrl, 0, 2) !== '//'
2896  ) {
2897  $sanitizedUrl = $url;
2898  } elseif (empty($parsedUrl['scheme']) && strpos($testRelativeUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0
2899  && $decodedUrl[0] !== '/' && strpbrk($decodedUrl, '*:|"<>') === false && !str_contains($decodedUrl, '\\\\')
2900  ) {
2901  $sanitizedUrl = $url;
2902  }
2903  }
2904  if (!empty($url) && empty($sanitizedUrl)) {
2905  static::getLogger()->notice('The URL "{url}" is not considered to be local and was denied.', ['url' => $url]);
2906  }
2907  return $sanitizedUrl;
2908  }
2909 
2918  public static function upload_copy_move($source, $destination)
2919  {
2920  if (is_array(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\‪TYPO3\CMS\Core\Utility\GeneralUtility::class]['moveUploadedFile'] ?? null)) {
2921  $params = ['source' => $source, 'destination' => $destination, 'method' => 'upload_copy_move'];
2922  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\‪TYPO3\CMS\Core\Utility\GeneralUtility::class]['moveUploadedFile'] as $hookMethod) {
2923  $fakeThis = null;
2924  self::callUserFunction($hookMethod, $params, $fakeThis);
2925  }
2926  }
2927 
2928  $result = false;
2929  if (is_uploaded_file($source)) {
2930  // Return the value of move_uploaded_file, and if FALSE the temporary $source is still
2931  // around so the user can use unlink to delete it:
2932  $result = move_uploaded_file($source, $destination);
2933  } else {
2934  @copy($source, $destination);
2935  }
2936  // Change the permissions of the file
2937  ‪self::fixPermissions($destination);
2938  // If here the file is copied and the temporary $source is still around,
2939  // so when returning FALSE the user can try unlink to delete the $source
2940  return $result;
2941  }
2942 
2953  public static function upload_to_tempfile($uploadedFileName)
2954  {
2955  if (is_uploaded_file($uploadedFileName)) {
2956  $tempFile = self::tempnam('upload_temp_');
2957  if (is_array(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\‪TYPO3\CMS\Core\Utility\GeneralUtility::class]['moveUploadedFile'] ?? null)) {
2958  $params = ['source' => $uploadedFileName, 'destination' => $tempFile, 'method' => 'upload_to_tempfile'];
2959  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\‪TYPO3\CMS\Core\Utility\GeneralUtility::class]['moveUploadedFile'] as $hookMethod) {
2960  $fakeThis = null;
2961  self::callUserFunction($hookMethod, $params, $fakeThis);
2962  }
2963  }
2964 
2965  move_uploaded_file($uploadedFileName, $tempFile);
2966  return @is_file($tempFile) ? $tempFile : '';
2967  }
2968 
2969  return '';
2970  }
2971 
2982  public static function unlink_tempfile($uploadedTempFileName)
2983  {
2984  if ($uploadedTempFileName) {
2985  $uploadedTempFileName = self::fixWindowsFilePath($uploadedTempFileName);
2986  if (
2987  self::validPathStr($uploadedTempFileName)
2988  && (
2989  str_starts_with($uploadedTempFileName, ‪Environment::getPublicPath() . '/typo3temp/')
2990  || str_starts_with($uploadedTempFileName, ‪Environment::getVarPath() . '/')
2991  )
2992  && @is_file($uploadedTempFileName)
2993  ) {
2994  if (unlink($uploadedTempFileName)) {
2995  return true;
2996  }
2997  }
2998  }
2999 
3000  return null;
3001  }
3002 
3014  public static function tempnam($filePrefix, $fileSuffix = '')
3015  {
3016  $temporaryPath = ‪Environment::getVarPath() . '/transient/';
3017  if (!is_dir($temporaryPath)) {
3018  ‪self::mkdir_deep($temporaryPath);
3019  }
3020  if ($fileSuffix === '') {
3021  $path = (string)tempnam($temporaryPath, $filePrefix);
3022  $tempFileName = $temporaryPath . ‪PathUtility::basename($path);
3023  } else {
3024  do {
3025  $tempFileName = $temporaryPath . $filePrefix . random_int(1, PHP_INT_MAX) . $fileSuffix;
3026  } while (file_exists($tempFileName));
3027  touch($tempFileName);
3028  clearstatcache(false, $tempFileName);
3029  }
3030  return $tempFileName;
3031  }
3032 
3042  public static function stdAuthCode($uid_or_record, ‪$fields = '', $codeLength = 8)
3043  {
3044  trigger_error(
3045  'GeneralUtility::stdAuthCode() is deprecated and will be removed in v12.',
3046  E_USER_DEPRECATED
3047  );
3048 
3049  if (is_array($uid_or_record)) {
3050  $recCopy_temp = [];
3051  if (‪$fields) {
3052  $fieldArr = ‪self::trimExplode(',', ‪$fields, true);
3053  foreach ($fieldArr as $k => $v) {
3054  $recCopy_temp[$k] = $uid_or_record[$v];
3055  }
3056  } else {
3057  $recCopy_temp = $uid_or_record;
3058  }
3059  $preKey = implode('|', $recCopy_temp);
3060  } else {
3061  $preKey = $uid_or_record;
3062  }
3063  $authCode = $preKey . '||' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
3064  $authCode = substr(md5($authCode), 0, $codeLength);
3065  return $authCode;
3066  }
3067 
3075  public static function hideIfNotTranslated($l18n_cfg_fieldValue)
3076  {
3077  trigger_error('GeneralUtility::hideIfNotTranslated() will be removed in TYPO3 v12, use the PageTranslationVisibility BitSet API instead.', E_USER_DEPRECATED);
3078  return ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['hidePagesIfNotTranslatedByDefault'] xor ($l18n_cfg_fieldValue & 2);
3079  }
3080 
3089  public static function hideIfDefaultLanguage($localizationConfiguration)
3090  {
3091  trigger_error('GeneralUtility::hideIfDefaultLanguage() will be removed in TYPO3 v12, use the PageTranslationVisibility BitSet API instead.', E_USER_DEPRECATED);
3092  return (bool)($localizationConfiguration & 1);
3093  }
3094 
3105  public static function callUserFunction($funcName, &$params, ?object $ref = null)
3106  {
3107  // Check if we're using a closure and invoke it directly.
3108  if (is_object($funcName) && is_a($funcName, \Closure::class)) {
3109  return call_user_func_array($funcName, [&$params, &$ref]);
3110  }
3111  $funcName = trim($funcName);
3112  $parts = explode('->', $funcName);
3113  // Call function or method
3114  if (count($parts) === 2) {
3115  // It's a class/method
3116  // Check if class/method exists:
3117  if (class_exists($parts[0])) {
3118  // Create object
3119  $classObj = self::makeInstance($parts[0]);
3120  $methodName = (string)$parts[1];
3121  $callable = [$classObj, $methodName];
3122  if (is_callable($callable)) {
3123  // Call method:
3124  $content = call_user_func_array($callable, [&$params, &$ref]);
3125  } else {
3126  throw new \InvalidArgumentException('No method name \'' . $parts[1] . '\' in class ' . $parts[0], 1294585865);
3127  }
3128  } else {
3129  throw new \InvalidArgumentException('No class named ' . $parts[0], 1294585866);
3130  }
3131  } elseif (function_exists($funcName) && is_callable($funcName)) {
3132  // It's a function
3133  $content = call_user_func_array($funcName, [&$params, &$ref]);
3134  } else {
3135  // Usually this will be annotated by static code analysis tools, but there's no native "not empty string" type
3136  throw new \InvalidArgumentException('No function named: ' . $funcName, 1294585867);
3137  }
3138  return $content;
3139  }
3140 
3145  public static function setContainer(ContainerInterface ‪$container): void
3146  {
3147  self::$container = ‪$container;
3148  }
3149 
3154  public static function getContainer(): ContainerInterface
3155  {
3156  if (self::$container === null) {
3157  throw new \LogicException('PSR-11 Container is not available', 1549404144);
3158  }
3159  return ‪self::$container;
3160  }
3161 
3176  public static function makeInstance($className, ...$constructorArguments)
3177  {
3178  if (!is_string($className) || empty($className)) {
3179  throw new \InvalidArgumentException('$className must be a non empty string.', 1288965219);
3180  }
3181  // Never instantiate with a beginning backslash, otherwise things like singletons won't work.
3182  if ($className[0] === '\\') {
3183  throw new \InvalidArgumentException(
3184  '$className "' . $className . '" must not start with a backslash.',
3185  1420281366
3186  );
3187  }
3188  if (isset(static::$finalClassNameCache[$className])) {
3189  $finalClassName = static::$finalClassNameCache[$className];
3190  } else {
3191  $finalClassName = self::getClassName($className);
3192  static::$finalClassNameCache[$className] = $finalClassName;
3193  }
3194  // Return singleton instance if it is already registered
3195  if (isset(self::$singletonInstances[$finalClassName])) {
3196  return self::$singletonInstances[$finalClassName];
3197  }
3198  // Return instance if it has been injected by addInstance()
3199  if (
3200  isset(self::$nonSingletonInstances[$finalClassName])
3201  && !empty(self::$nonSingletonInstances[$finalClassName])
3202  ) {
3203  return array_shift(self::$nonSingletonInstances[$finalClassName]);
3204  }
3205 
3206  // Read service and prototypes from the DI container, this is required to
3207  // support classes that require dependency injection.
3208  // We operate on the original class name on purpose, as class overrides
3209  // are resolved inside the container
3210  if (self::$container !== null && $constructorArguments === [] && self::$container->has($className)) {
3211  return self::$container->get($className);
3212  }
3213 
3214  // Create new instance and call constructor with parameters
3215  $instance = new $finalClassName(...$constructorArguments);
3216  // Register new singleton instance, but only if it is not a known PSR-11 container service
3217  if ($instance instanceof SingletonInterface && !(self::$container !== null && self::$container->has($className))) {
3218  self::$singletonInstances[$finalClassName] = $instance;
3219  }
3220  if ($instance instanceof LoggerAwareInterface) {
3221  $instance->setLogger(static::makeInstance(LogManager::class)->getLogger($className));
3222  }
3223  return $instance;
3224  }
3225 
3238  public static function makeInstanceForDi(string $className, ...$constructorArguments): object
3239  {
3240  $finalClassName = static::$finalClassNameCache[$className] ?? static::$finalClassNameCache[$className] = self::getClassName($className);
3241 
3242  // Return singleton instance if it is already registered (currently required for unit and functional tests)
3243  if (isset(self::$singletonInstances[$finalClassName])) {
3244  return self::$singletonInstances[$finalClassName];
3245  }
3246  // Create new instance and call constructor with parameters
3247  return new $finalClassName(...$constructorArguments);
3248  }
3249 
3257  protected static function getClassName($className)
3258  {
3259  if (class_exists($className)) {
3260  while (static::classHasImplementation($className)) {
3261  $className = static::getImplementationForClass($className);
3262  }
3263  }
3265  }
3266 
3273  protected static function getImplementationForClass($className)
3274  {
3275  return ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className'];
3276  }
3277 
3284  protected static function classHasImplementation($className)
3285  {
3286  return !empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className']);
3287  }
3288 
3306  public static function setSingletonInstance($className, SingletonInterface $instance)
3307  {
3308  self::checkInstanceClassName($className, $instance);
3309  // Check for XCLASS registration (same is done in makeInstance() in order to store the singleton of the final class name)
3310  $finalClassName = self::getClassName($className);
3311  self::$singletonInstances[$finalClassName] = $instance;
3312  }
3313 
3329  public static function removeSingletonInstance($className, SingletonInterface $instance)
3330  {
3331  self::checkInstanceClassName($className, $instance);
3332  if (!isset(self::$singletonInstances[$className])) {
3333  throw new \InvalidArgumentException('No Instance registered for ' . $className . '.', 1394099179);
3334  }
3335  if ($instance !== self::$singletonInstances[$className]) {
3336  throw new \InvalidArgumentException('The instance you are trying to remove has not been registered before.', 1394099256);
3337  }
3338  unset(self::$singletonInstances[$className]);
3339  }
3340 
3354  public static function resetSingletonInstances(array $newSingletonInstances)
3355  {
3356  static::$singletonInstances = [];
3357  foreach ($newSingletonInstances as $className => $instance) {
3358  static::setSingletonInstance($className, $instance);
3359  }
3360  }
3361 
3374  public static function getSingletonInstances()
3375  {
3376  return static::$singletonInstances;
3377  }
3378 
3390  public static function getInstances()
3391  {
3392  return static::$nonSingletonInstances;
3393  }
3394 
3409  public static function addInstance($className, $instance)
3410  {
3411  self::checkInstanceClassName($className, $instance);
3412  if ($instance instanceof SingletonInterface) {
3413  throw new \InvalidArgumentException('$instance must not be an instance of TYPO3\\CMS\\Core\\SingletonInterface. For setting singletons, please use setSingletonInstance.', 1288969325);
3414  }
3415  if (!isset(self::$nonSingletonInstances[$className])) {
3416  self::$nonSingletonInstances[$className] = [];
3417  }
3418  self::$nonSingletonInstances[$className][] = $instance;
3419  }
3420 
3429  protected static function checkInstanceClassName($className, $instance)
3430  {
3431  if ($className === '') {
3432  throw new \InvalidArgumentException('$className must not be empty.', 1288967479);
3433  }
3434  if (!$instance instanceof $className) {
3435  throw new \InvalidArgumentException('$instance must be an instance of ' . $className . ', but actually is an instance of ' . get_class($instance) . '.', 1288967686);
3436  }
3437  }
3438 
3449  public static function purgeInstances()
3450  {
3451  self::$container = null;
3452  self::$singletonInstances = [];
3453  self::$nonSingletonInstances = [];
3454  }
3455 
3465  public static function flushInternalRuntimeCaches()
3466  {
3467  self::$finalClassNameCache = [];
3468  self::$indpEnvCache = [];
3469  }
3470 
3485  public static function makeInstanceService($serviceType, $serviceSubType = '', array $excludeServiceKeys = [])
3486  {
3487  $error = false;
3488  $requestInfo = [
3489  'requestedServiceType' => $serviceType,
3490  'requestedServiceSubType' => $serviceSubType,
3491  'requestedExcludeServiceKeys' => $excludeServiceKeys,
3492  ];
3493  while ($info = ‪ExtensionManagementUtility::findService($serviceType, $serviceSubType, $excludeServiceKeys)) {
3494  // provide information about requested service to service object
3495  $info = array_merge($info, $requestInfo);
3496 
3498  $className = $info['className'];
3500  $obj = self::makeInstance($className);
3501  if (is_object($obj)) {
3502  if (!is_callable([$obj, 'init'])) {
3503  self::getLogger()->error('Requested service {class} has no init() method.', [
3504  'class' => $info['className'],
3505  'service' => $info,
3506  ]);
3507  throw new \RuntimeException('Broken service: ' . $info['className'], 1568119209);
3508  }
3509  $obj->info = $info;
3510  // service available?
3511  if ($obj->init()) {
3512  return $obj;
3513  }
3514  $error = $obj->getLastErrorArray();
3515  unset($obj);
3516  }
3517 
3518  // deactivate the service
3519  ‪ExtensionManagementUtility::deactivateService($info['serviceType'], $info['serviceKey']);
3520  }
3521  return $error;
3522  }
3523 
3530  public static function quoteJSvalue($value)
3531  {
3532  $json = (string)json_encode(
3533  (string)$value,
3534  JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG
3535  );
3536 
3537  return strtr(
3538  $json,
3539  [
3540  '"' => '\'',
3541  '\\\\' => '\\u005C',
3542  ' ' => '\\u0020',
3543  '!' => '\\u0021',
3544  '\\t' => '\\u0009',
3545  '\\n' => '\\u000A',
3546  '\\r' => '\\u000D',
3547  ]
3548  );
3549  }
3550 
3561  public static function jsonEncodeForHtmlAttribute($value, bool $useHtmlEntities = true): string
3562  {
3563  $json = (string)json_encode($value, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG);
3564  return $useHtmlEntities ? htmlspecialchars($json) : $json;
3565  }
3566 
3576  public static function jsonEncodeForJavaScript($value): string
3577  {
3578  $json = (string)json_encode($value, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG);
3579  return strtr(
3580  $json,
3581  [
3582  // comments below refer to JSON-encoded data
3583  '\\\\' => '\\\\u005C', // `"\\Vendor\\Package"` -> `"\\u005CVendor\\u005CPackage"`
3584  '\\t' => '\\u0009', // `"\t"` -> `"\u0009"`
3585  '\\n' => '\\u000A', // `"\n"` -> `"\u000A"`
3586  '\\r' => '\\u000D', // `"\r"` -> `"\u000D"`
3587  ]
3588  );
3589  }
3590 
3594  protected static function getLogger()
3595  {
3596  return static::makeInstance(LogManager::class)->getLogger(__CLASS__);
3597  }
3598 }
‪TYPO3\CMS\Core\Utility\GeneralUtility\underscoredToLowerCamelCase
‪static string underscoredToLowerCamelCase($string)
Definition: GeneralUtility.php:841
‪TYPO3\CMS\Core\Http\ApplicationType\fromRequest
‪static static fromRequest(ServerRequestInterface $request)
Definition: ApplicationType.php:62
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2array
‪static mixed xml2array($string, $NSprefix='', $reportDocTag=false)
Definition: GeneralUtility.php:1482
‪TYPO3\CMS\Core\Utility\GeneralUtility\$nl
‪$nl
Definition: GeneralUtility.php:1370
‪TYPO3\CMS\Core\Utility\GeneralUtility\revExplode
‪static list< string > revExplode($delimiter, $string, $count=0)
Definition: GeneralUtility.php:964
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:206
‪TYPO3\CMS\Core\Utility\GeneralUtility\minifyJavaScript
‪static string minifyJavaScript($script, &$error='')
Definition: GeneralUtility.php:1665
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Utility\PathUtility\isAllowedAdditionalPath
‪static isAllowedAdditionalPath(string $path)
Definition: PathUtility.php:475
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static string stripPathSitePrefix($path)
Definition: PathUtility.php:445
‪TYPO3\CMS\Core\Utility\PathUtility\isExtensionPath
‪static bool isExtensionPath(string $path)
Definition: PathUtility.php:121
‪TYPO3
‪TYPO3\CMS\Core\Utility\GeneralUtility\$localeInfo
‪$localeInfo
Definition: GeneralUtility.php:758
‪TYPO3\CMS\Core\Utility\PathUtility\dirnameDuringBootstrap
‪static string dirnameDuringBootstrap($path)
Definition: PathUtility.php:349
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static bool isWindows()
Definition: Environment.php:318
‪TYPO3\CMS\Core\Utility
Definition: ArrayUtility.php:16
‪TYPO3\CMS\Core\Core\ClassLoadingInformation
Definition: ClassLoadingInformation.php:35
‪TYPO3\CMS\Core\Utility\GeneralUtility\$labelArr
‪if($base !==1000 && $base !==1024) $labelArr
Definition: GeneralUtility.php:755
‪$parser
‪$parser
Definition: annotationChecker.php:110
‪TYPO3\CMS\Core\Core\Environment\getCurrentScript
‪static string getCurrentScript()
Definition: Environment.php:246
‪TYPO3\CMS\Core\Utility\GeneralUtility\$multiplier
‪$multiplier
Definition: GeneralUtility.php:760
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Core\Utility\GeneralUtility\camelCaseToLowerCaseUnderscored
‪static string camelCaseToLowerCaseUnderscored($string)
Definition: GeneralUtility.php:853
‪$dir
‪$dir
Definition: validateRstFiles.php:213
‪TYPO3\CMS\Core\Utility\GeneralUtility\createDirectoryPath
‪static string createDirectoryPath($fullDirectoryPath)
Definition: GeneralUtility.php:1934
‪TYPO3\CMS\Core\Authentication\AbstractAuthenticationService
Definition: AbstractAuthenticationService.php:30
‪TYPO3\CMS\Core\Http\ApplicationType
Definition: ApplicationType.php:52
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:226
‪TYPO3\CMS\Core\Utility\GeneralUtility\implodeArrayForUrl
‪static string implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
Definition: GeneralUtility.php:1037
‪TYPO3\CMS\Core\Utility\GeneralUtility\get_dirs
‪static string[] string null get_dirs($path)
Definition: GeneralUtility.php:2018
‪TYPO3\CMS\Core\Utility\GeneralUtility\getUrl
‪static string false getUrl($url)
Definition: GeneralUtility.php:1697
‪TYPO3\CMS\Core\Core\ClassLoadingInformation\getClassNameForAlias
‪static mixed getClassNameForAlias($alias)
Definition: ClassLoadingInformation.php:205
‪TYPO3\CMS\Core\Utility\GeneralUtility\$sizeInUnits
‪$sizeInUnits
Definition: GeneralUtility.php:761
‪TYPO3\CMS\Core\Utility\HttpUtility\buildUrl
‪static string buildUrl(array $urlParts)
Definition: HttpUtility.php:149
‪TYPO3\CMS\Core\Utility\GeneralUtility\$spaceInd
‪static array< string, function explodeUrl2Array( $string) { $output=[];$p=explode('&', $string);foreach( $p as $v) { if( $v !=='') { $nameAndValue=explode('=', $v, 2);$output[rawurldecode( $nameAndValue[0])]=isset( $nameAndValue[1]) ? rawurldecode( $nameAndValue[1]) :'';} } return $output;} public static array function compileSelectedGetVarsFromArray( $varList, array $getArray, $GPvarAlt=true) { trigger_error('GeneralUtility::compileSelectedGetVarsFromArray() is deprecated and will be removed in v12.', E_USER_DEPRECATED);$keys=self::trimExplode(',', $varList, true);$outArr=[];foreach( $keys as $v) { if(isset( $getArray[ $v])) { $outArr[ $v]=$getArray[ $v];} elseif( $GPvarAlt) { $outArr[ $v]=self::_GP( $v);} } return $outArr;} public static array function removeDotsFromTS(array $ts) { $out=[];foreach( $ts as $key=> $value) { if(is_array( $value)) { $key=rtrim( $key, '.');$out[ $key]=self::removeDotsFromTS( $value);} else { $out[ $key]=$value;} } return $out;} public static array< string, function get_tag_attributes( $tag, bool $decodeEntities=false) { $components=self::split_tag_attributes( $tag);$name='';$valuemode=false;$attributes=[];foreach( $components as $key=> $val) { if( $val !=='=') { if( $valuemode) { if( $name) { $attributes[ $name]=$decodeEntities ? htmlspecialchars_decode( $val) :$val;$name='';} } else { if( $key=strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val) ?? '')) { $attributes[ $key]='';$name=$key;} } $valuemode=false;} else { $valuemode=true;} } return $attributes;} public static string[] function split_tag_attributes( $tag) { $tag_tmp=trim(preg_replace('/^<[^[:space:]] */', '', trim( $tag)) ?? '');$tag_tmp=trim(rtrim( $tag_tmp, '>'));$value=[];while( $tag_tmp !=='') { $firstChar=$tag_tmp[0];if( $firstChar==='"' || $firstChar === '\'') { $reg = explode($firstChar, $tag_tmp, 3); $value[] = $reg[1]; $tag_tmp = trim($reg[2] ?? ''); } elseif ($firstChar === '=') { $value[] = '='; $tag_tmp = trim(substr($tag_tmp, 1)); } else { $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2); $value[] = trim($reg[0]); $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . ($reg[1] ?? '')); } } reset($value); return $value; } public static string function implodeAttributes(array $arr, $xhtmlSafe = false, $keepBlankAttributes = false) { if ($xhtmlSafe) { $newArr = []; foreach ($arr as $attributeName => $attributeValue) { $attributeName = strtolower($attributeName); if (!isset($newArr[$attributeName])) { $newArr[$attributeName] = htmlspecialchars((string)$attributeValue); } } $arr = $newArr; } $list = []; foreach ($arr as $attributeName => $attributeValue) { if ((string)$attributeValue !== '' || $keepBlankAttributes) { $list[] = $attributeName . '="' . $attributeValue . '"'; } } return implode(' ', $list); } public static string function wrapJS($string) { if (trim($string)) { $string = ltrim($string, LF); $match = []; if (preg_match('/^(\\t+)/', $string, $match)) { $string = str_replace($match[1], "\t", $string); } return '<script>' . $string . '</script>'; } return ''; } public static mixed function xml2tree($string, $depth = 999, $parserOptions = []) { $previousValueOfEntityLoader = null; if (PHP_MAJOR_VERSION < 8) { $previousValueOfEntityLoader = libxml_disable_entity_loader(true); } $parser = xml_parser_create(); $vals = []; $index = []; xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0); foreach ($parserOptions as $option => $value) { xml_parser_set_option($parser, $option, $value); } xml_parse_into_struct($parser, $string, $vals, $index); if (PHP_MAJOR_VERSION < 8) { libxml_disable_entity_loader($previousValueOfEntityLoader); } if (xml_get_error_code($parser)) { return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser)); } xml_parser_free($parser); $stack = [[]]; $stacktop = 0; $startPoint = 0; $tagi = []; foreach ($vals as $key => $val) { $type = $val['type']; if ($type === 'open' || $type === 'complete') { $stack[$stacktop++] = $tagi; if ($depth == $stacktop) { $startPoint = $key; } $tagi = ['tag' => $val['tag']]; if (isset($val['attributes'])) { $tagi['attrs'] = $val['attributes']; } if (isset($val['value'])) { $tagi['values'][] = $val['value']; } } if ($type === 'complete' || $type === 'close') { $oldtagi = $tagi; $tagi = $stack[--$stacktop]; $oldtag = $oldtagi['tag']; unset($oldtagi['tag']); if ($depth == $stacktop + 1) { if ($key - $startPoint > 0) { $partArray = array_slice($vals, $startPoint + 1, $key - $startPoint - 1); $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray); } else { $oldtagi['XMLvalue'] = $oldtagi['values'][0]; } } $tagi['ch'][$oldtag][] = $oldtagi; unset($oldtagi); } if ($type === 'cdata') { $tagi['values'][] = $val['value']; } } return $tagi['ch']; } public static string function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = [], array $stackData = []) { $binaryChars = "\0" . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . chr(11) . chr(12) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19) . chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29) . chr(30) . chr(31); $indentChar = $spaceInd ? ' ' : "\t"; $indentN = $spaceInd > $spaceInd
Definition: GeneralUtility.php:1369
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:177
‪TYPO3\CMS\Core\Utility\GeneralUtility\ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL
‪const ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\deactivateService
‪static deactivateService($serviceType, $serviceKey)
Definition: ExtensionManagementUtility.php:1177
‪TYPO3\CMS\Core\Utility\GeneralUtility\$output
‪foreach($array as $k=> $v) if(! $level) return $output
Definition: GeneralUtility.php:1464
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixPermissions
‪static mixed fixPermissions($path, $recursive=false)
Definition: GeneralUtility.php:1749
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:1908
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\resolvePackagePath
‪static string resolvePackagePath(string $path)
Definition: ExtensionManagementUtility.php:129
‪$validator
‪if(isset($args['d'])) $validator
Definition: validateRstFiles.php:218
‪TYPO3\CMS\Core\Utility\GeneralUtility\$container
‪static ContainerInterface null $container
Definition: GeneralUtility.php:58
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2arrayProcess
‪static mixed xml2arrayProcess($string, $NSprefix='', $reportDocTag=false)
Definition: GeneralUtility.php:1505
‪TYPO3\CMS\Core\Utility\GeneralUtility\$sizeInBytes
‪$sizeInBytes
Definition: GeneralUtility.php:759
‪TYPO3\CMS\Core\Http\RequestFactory
Definition: RequestFactory.php:31
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static bool isAbsolutePath($path)
Definition: PathUtility.php:296
‪TYPO3\CMS\Core\Utility\GeneralUtility\ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME
‪const ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME
Definition: GeneralUtility.php:54
‪TYPO3\CMS\Core\Utility\GeneralUtility\$output
‪$output
Definition: GeneralUtility.php:1372
‪TYPO3\CMS\Core\Utility\GeneralUtility\isValidUrl
‪static bool isValidUrl($url)
Definition: GeneralUtility.php:883
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:43
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:927
‪TYPO3\CMS\Core\Utility\GeneralUtility\rmdir
‪static bool rmdir($path, $removeNonEmpty=false)
Definition: GeneralUtility.php:1961
‪TYPO3\CMS\Core\Utility\GeneralUtility\xmlRecompileFromStructValArray
‪static string xmlRecompileFromStructValArray(array $vals)
Definition: GeneralUtility.php:1619
‪TYPO3\CMS\Core\Middleware\VerifyHostHeader
Definition: VerifyHostHeader.php:31
‪TYPO3\CMS\Core\Package\Exception
Definition: InvalidPackageKeyException.php:16
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir($newFolder)
Definition: GeneralUtility.php:1891
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1722
‪TYPO3\CMS\Core\Utility\GeneralUtility\underscoredToUpperCamelCase
‪static string underscoredToUpperCamelCase($string)
Definition: GeneralUtility.php:829
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\findService
‪static mixed findService($serviceType, $serviceSubType='', array $excludeServiceKeys=[])
Definition: ExtensionManagementUtility.php:1077
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:218
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFileToTypo3tempDir
‪static string null writeFileToTypo3tempDir($filepath, $content)
Definition: GeneralUtility.php:1814