TYPO3 CMS  TYPO3_8-7
GeneralUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Utility;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
26 
40 {
41  // Severity constants used by \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog()
47 
50 
57  protected static $allowHostHeaderValue = false;
58 
65  protected static $singletonInstances = [];
66 
72  protected static $nonSingletonInstances = [];
73 
79  protected static $finalClassNameCache = [];
80 
86  protected static $applicationContext = null;
87 
93  protected static $idnaStringCache = [];
94 
100  protected static $idnaConverter = null;
101 
107  protected static $supportedCgiServerApis = [
108  'fpm-fcgi',
109  'cgi',
110  'isapi',
111  'cgi-fcgi',
112  'srv', // HHVM with fastcgi
113  ];
114 
118  protected static $indpEnvCache = [];
119 
120  /*************************
121  *
122  * GET/POST Variables
123  *
124  * Background:
125  * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration.
126  * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so.
127  * But the clean solution is that quotes are never escaped and that is what the functions below offers.
128  * Eventually TYPO3 should provide this in the global space as well.
129  * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below.
130  * This functionality was previously needed to normalize between magic quotes logic, which was removed from PHP 5.4,
131  * so these methods are still in use, but not tackle the slash problem anymore.
132  *
133  *************************/
142  public static function _GP($var)
143  {
144  if (empty($var)) {
145  return;
146  }
147  if (isset($_POST[$var])) {
148  $value = $_POST[$var];
149  } elseif (isset($_GET[$var])) {
150  $value = $_GET[$var];
151  } else {
152  $value = null;
153  }
154  // This is there for backwards-compatibility, in order to avoid NULL
155  if (isset($value) && !is_array($value)) {
156  $value = (string)$value;
157  }
158  return $value;
159  }
160 
167  public static function _GPmerged($parameter)
168  {
169  $postParameter = isset($_POST[$parameter]) && is_array($_POST[$parameter]) ? $_POST[$parameter] : [];
170  $getParameter = isset($_GET[$parameter]) && is_array($_GET[$parameter]) ? $_GET[$parameter] : [];
171  $mergedParameters = $getParameter;
172  ArrayUtility::mergeRecursiveWithOverrule($mergedParameters, $postParameter);
173  return $mergedParameters;
174  }
175 
185  public static function _GET($var = null)
186  {
187  $value = $var === null ? $_GET : (empty($var) ? null : $_GET[$var]);
188  // This is there for backwards-compatibility, in order to avoid NULL
189  if (isset($value) && !is_array($value)) {
190  $value = (string)$value;
191  }
192  return $value;
193  }
194 
203  public static function _POST($var = null)
204  {
205  $value = $var === null ? $_POST : (empty($var) ? null : $_POST[$var]);
206  // This is there for backwards-compatibility, in order to avoid NULL
207  if (isset($value) && !is_array($value)) {
208  $value = (string)$value;
209  }
210  return $value;
211  }
212 
219  public static function _GETset($inputGet, $key = '')
220  {
221  if ($key != '') {
222  if (strpos($key, '|') !== false) {
223  $pieces = explode('|', $key);
224  $newGet = [];
225  $pointer = &$newGet;
226  foreach ($pieces as $piece) {
227  $pointer = &$pointer[$piece];
228  }
229  $pointer = $inputGet;
230  $mergedGet = $_GET;
231  ArrayUtility::mergeRecursiveWithOverrule($mergedGet, $newGet);
232  $_GET = $mergedGet;
233  $GLOBALS['HTTP_GET_VARS'] = $mergedGet;
234  } else {
235  $_GET[$key] = $inputGet;
236  $GLOBALS['HTTP_GET_VARS'][$key] = $inputGet;
237  }
238  } elseif (is_array($inputGet)) {
239  $_GET = $inputGet;
240  $GLOBALS['HTTP_GET_VARS'] = $inputGet;
241  }
242  }
243 
254  public static function removeXSS($string)
255  {
256  static::logDeprecatedFunction();
257  return \RemoveXSS::process($string);
258  }
259 
260  /*************************
261  *
262  * IMAGE FUNCTIONS
263  *
264  *************************/
265 
266  /*************************
267  *
268  * STRING FUNCTIONS
269  *
270  *************************/
279  public static function fixed_lgd_cs($string, $chars, $appendString = '...')
280  {
281  if ((int)$chars === 0 || mb_strlen($string, 'utf-8') <= abs($chars)) {
282  return $string;
283  }
284  if ($chars > 0) {
285  $string = mb_substr($string, 0, $chars, 'utf-8') . $appendString;
286  } else {
287  $string = $appendString . mb_substr($string, $chars, mb_strlen($string, 'utf-8'), 'utf-8');
288  }
289  return $string;
290  }
291 
300  public static function cmpIP($baseIP, $list)
301  {
302  $list = trim($list);
303  if ($list === '') {
304  return false;
305  }
306  if ($list === '*') {
307  return true;
308  }
309  if (strpos($baseIP, ':') !== false && self::validIPv6($baseIP)) {
310  return self::cmpIPv6($baseIP, $list);
311  }
312  return self::cmpIPv4($baseIP, $list);
313  }
314 
322  public static function cmpIPv4($baseIP, $list)
323  {
324  $IPpartsReq = explode('.', $baseIP);
325  if (count($IPpartsReq) === 4) {
326  $values = self::trimExplode(',', $list, true);
327  foreach ($values as $test) {
328  $testList = explode('/', $test);
329  if (count($testList) === 2) {
330  list($test, $mask) = $testList;
331  } else {
332  $mask = false;
333  }
334  if ((int)$mask) {
335  // "192.168.3.0/24"
336  $lnet = ip2long($test);
337  $lip = ip2long($baseIP);
338  $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
339  $firstpart = substr($binnet, 0, $mask);
340  $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
341  $firstip = substr($binip, 0, $mask);
342  $yes = $firstpart === $firstip;
343  } else {
344  // "192.168.*.*"
345  $IPparts = explode('.', $test);
346  $yes = 1;
347  foreach ($IPparts as $index => $val) {
348  $val = trim($val);
349  if ($val !== '*' && $IPpartsReq[$index] !== $val) {
350  $yes = 0;
351  }
352  }
353  }
354  if ($yes) {
355  return true;
356  }
357  }
358  }
359  return false;
360  }
361 
369  public static function cmpIPv6($baseIP, $list)
370  {
371  // Policy default: Deny connection
372  $success = false;
373  $baseIP = self::normalizeIPv6($baseIP);
374  $values = self::trimExplode(',', $list, true);
375  foreach ($values as $test) {
376  $testList = explode('/', $test);
377  if (count($testList) === 2) {
378  list($test, $mask) = $testList;
379  } else {
380  $mask = false;
381  }
382  if (self::validIPv6($test)) {
383  $test = self::normalizeIPv6($test);
384  $maskInt = (int)$mask ?: 128;
385  // Special case; /0 is an allowed mask - equals a wildcard
386  if ($mask === '0') {
387  $success = true;
388  } elseif ($maskInt == 128) {
389  $success = $test === $baseIP;
390  } else {
391  $testBin = self::IPv6Hex2Bin($test);
392  $baseIPBin = self::IPv6Hex2Bin($baseIP);
393  $success = true;
394  // Modulo is 0 if this is a 8-bit-boundary
395  $maskIntModulo = $maskInt % 8;
396  $numFullCharactersUntilBoundary = (int)($maskInt / 8);
397  if (substr($testBin, 0, $numFullCharactersUntilBoundary) !== substr($baseIPBin, 0, $numFullCharactersUntilBoundary)) {
398  $success = false;
399  } elseif ($maskIntModulo > 0) {
400  // If not an 8-bit-boundary, check bits of last character
401  $testLastBits = str_pad(decbin(ord(substr($testBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
402  $baseIPLastBits = str_pad(decbin(ord(substr($baseIPBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
403  if (strncmp($testLastBits, $baseIPLastBits, $maskIntModulo) != 0) {
404  $success = false;
405  }
406  }
407  }
408  }
409  if ($success) {
410  return true;
411  }
412  }
413  return false;
414  }
415 
423  public static function IPv6Hex2Bin($hex)
424  {
425  return inet_pton($hex);
426  }
427 
435  public static function IPv6Bin2Hex($bin)
436  {
437  return inet_ntop($bin);
438  }
439 
447  public static function normalizeIPv6($address)
448  {
449  $normalizedAddress = '';
450  $stageOneAddress = '';
451  // According to RFC lowercase-representation is recommended
452  $address = strtolower($address);
453  // Normalized representation has 39 characters (0000:0000:0000:0000:0000:0000:0000:0000)
454  if (strlen($address) === 39) {
455  // Already in full expanded form
456  return $address;
457  }
458  // Count 2 if if address has hidden zero blocks
459  $chunks = explode('::', $address);
460  if (count($chunks) === 2) {
461  $chunksLeft = explode(':', $chunks[0]);
462  $chunksRight = explode(':', $chunks[1]);
463  $left = count($chunksLeft);
464  $right = count($chunksRight);
465  // Special case: leading zero-only blocks count to 1, should be 0
466  if ($left === 1 && strlen($chunksLeft[0]) === 0) {
467  $left = 0;
468  }
469  $hiddenBlocks = 8 - ($left + $right);
470  $hiddenPart = '';
471  $h = 0;
472  while ($h < $hiddenBlocks) {
473  $hiddenPart .= '0000:';
474  $h++;
475  }
476  if ($left === 0) {
477  $stageOneAddress = $hiddenPart . $chunks[1];
478  } else {
479  $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
480  }
481  } else {
482  $stageOneAddress = $address;
483  }
484  // Normalize the blocks:
485  $blocks = explode(':', $stageOneAddress);
486  $divCounter = 0;
487  foreach ($blocks as $block) {
488  $tmpBlock = '';
489  $i = 0;
490  $hiddenZeros = 4 - strlen($block);
491  while ($i < $hiddenZeros) {
492  $tmpBlock .= '0';
493  $i++;
494  }
495  $normalizedAddress .= $tmpBlock . $block;
496  if ($divCounter < 7) {
497  $normalizedAddress .= ':';
498  $divCounter++;
499  }
500  }
501  return $normalizedAddress;
502  }
503 
511  public static function compressIPv6($address)
512  {
513  return inet_ntop(inet_pton($address));
514  }
515 
524  public static function validIP($ip)
525  {
526  return filter_var($ip, FILTER_VALIDATE_IP) !== false;
527  }
528 
537  public static function validIPv4($ip)
538  {
539  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
540  }
541 
550  public static function validIPv6($ip)
551  {
552  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
553  }
554 
562  public static function cmpFQDN($baseHost, $list)
563  {
564  $baseHost = trim($baseHost);
565  if (empty($baseHost)) {
566  return false;
567  }
568  if (self::validIPv4($baseHost) || self::validIPv6($baseHost)) {
569  // Resolve hostname
570  // Note: this is reverse-lookup and can be randomly set as soon as somebody is able to set
571  // the reverse-DNS for his IP (security when for example used with REMOTE_ADDR)
572  $baseHostName = gethostbyaddr($baseHost);
573  if ($baseHostName === $baseHost) {
574  // Unable to resolve hostname
575  return false;
576  }
577  } else {
578  $baseHostName = $baseHost;
579  }
580  $baseHostNameParts = explode('.', $baseHostName);
581  $values = self::trimExplode(',', $list, true);
582  foreach ($values as $test) {
583  $hostNameParts = explode('.', $test);
584  // To match hostNameParts can only be shorter (in case of wildcards) or equal
585  $hostNamePartsCount = count($hostNameParts);
586  $baseHostNamePartsCount = count($baseHostNameParts);
587  if ($hostNamePartsCount > $baseHostNamePartsCount) {
588  continue;
589  }
590  $yes = true;
591  foreach ($hostNameParts as $index => $val) {
592  $val = trim($val);
593  if ($val === '*') {
594  // Wildcard valid for one or more hostname-parts
595  $wildcardStart = $index + 1;
596  // Wildcard as last/only part always matches, otherwise perform recursive checks
597  if ($wildcardStart < $hostNamePartsCount) {
598  $wildcardMatched = false;
599  $tempHostName = implode('.', array_slice($hostNameParts, $index + 1));
600  while ($wildcardStart < $baseHostNamePartsCount && !$wildcardMatched) {
601  $tempBaseHostName = implode('.', array_slice($baseHostNameParts, $wildcardStart));
602  $wildcardMatched = self::cmpFQDN($tempBaseHostName, $tempHostName);
603  $wildcardStart++;
604  }
605  if ($wildcardMatched) {
606  // Match found by recursive compare
607  return true;
608  }
609  $yes = false;
610  }
611  } elseif ($baseHostNameParts[$index] !== $val) {
612  // In case of no match
613  $yes = false;
614  }
615  }
616  if ($yes) {
617  return true;
618  }
619  }
620  return false;
621  }
622 
630  public static function isOnCurrentHost($url)
631  {
632  return stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0;
633  }
634 
643  public static function inList($list, $item)
644  {
645  return strpos(',' . $list . ',', ',' . $item . ',') !== false;
646  }
647 
658  public static function rmFromList($element, $list)
659  {
660  $items = explode(',', $list);
661  foreach ($items as $k => $v) {
662  if ($v == $element) {
663  unset($items[$k]);
664  }
665  }
666  return implode(',', $items);
667  }
668 
676  public static function expandList($list)
677  {
678  $items = explode(',', $list);
679  $list = [];
680  foreach ($items as $item) {
681  $range = explode('-', $item);
682  if (isset($range[1])) {
683  $runAwayBrake = 1000;
684  for ($n = $range[0]; $n <= $range[1]; $n++) {
685  $list[] = $n;
686  $runAwayBrake--;
687  if ($runAwayBrake <= 0) {
688  break;
689  }
690  }
691  } else {
692  $list[] = $item;
693  }
694  }
695  return implode(',', $list);
696  }
697 
707  public static function compat_version($verNumberStr)
708  {
709  static::logDeprecatedFunction();
711  }
712 
719  public static function md5int($str)
720  {
721  return hexdec(substr(md5($str), 0, 7));
722  }
723 
731  public static function shortMD5($input, $len = 10)
732  {
733  return substr(md5($input), 0, $len);
734  }
735 
743  public static function hmac($input, $additionalSecret = '')
744  {
745  $hashAlgorithm = 'sha1';
746  $hashBlocksize = 64;
747  $hmac = '';
748  $secret = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . $additionalSecret;
749  if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) {
750  $hmac = hash_hmac($hashAlgorithm, $input, $secret);
751  } else {
752  // Outer padding
753  $opad = str_repeat(chr(92), $hashBlocksize);
754  // Inner padding
755  $ipad = str_repeat(chr(54), $hashBlocksize);
756  if (strlen($secret) > $hashBlocksize) {
757  // Keys longer than block size are shorten
758  $key = str_pad(pack('H*', call_user_func($hashAlgorithm, $secret)), $hashBlocksize, chr(0));
759  } else {
760  // Keys shorter than block size are zero-padded
761  $key = str_pad($secret, $hashBlocksize, chr(0));
762  }
763  $hmac = call_user_func($hashAlgorithm, ($key ^ $opad) . pack('H*', call_user_func($hashAlgorithm, (($key ^ $ipad) . $input))));
764  }
765  return $hmac;
766  }
767 
776  public static function uniqueList($in_list, $secondParameter = null)
777  {
778  if (is_array($in_list)) {
779  throw new \InvalidArgumentException('TYPO3 Fatal Error: TYPO3\\CMS\\Core\\Utility\\GeneralUtility::uniqueList() does NOT support array arguments anymore! Only string comma lists!', 1270853885);
780  }
781  if (isset($secondParameter)) {
782  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);
783  }
784  return implode(',', array_unique(self::trimExplode(',', $in_list, true)));
785  }
786 
793  public static function split_fileref($fileNameWithPath)
794  {
795  $reg = [];
796  if (preg_match('/(.*\\/)(.*)$/', $fileNameWithPath, $reg)) {
797  $info['path'] = $reg[1];
798  $info['file'] = $reg[2];
799  } else {
800  $info['path'] = '';
801  $info['file'] = $fileNameWithPath;
802  }
803  $reg = '';
804  // If open_basedir is set and the fileName was supplied without a path the is_dir check fails
805  if (!is_dir($fileNameWithPath) && preg_match('/(.*)\\.([^\\.]*$)/', $info['file'], $reg)) {
806  $info['filebody'] = $reg[1];
807  $info['fileext'] = strtolower($reg[2]);
808  $info['realFileext'] = $reg[2];
809  } else {
810  $info['filebody'] = $info['file'];
811  $info['fileext'] = '';
812  }
813  reset($info);
814  return $info;
815  }
816 
832  public static function dirname($path)
833  {
834  $p = self::revExplode('/', $path, 2);
835  return count($p) === 2 ? $p[0] : '';
836  }
837 
845  public static function isFirstPartOfStr($str, $partStr)
846  {
847  return $partStr != '' && strpos((string)$str, (string)$partStr, 0) === 0;
848  }
849 
858  public static function formatSize($sizeInBytes, $labels = '', $base = 0)
859  {
860  $defaultFormats = [
861  'iec' => ['base' => 1024, 'labels' => [' ', ' Ki', ' Mi', ' Gi', ' Ti', ' Pi', ' Ei', ' Zi', ' Yi']],
862  'si' => ['base' => 1000, 'labels' => [' ', ' k', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y']],
863  ];
864  // Set labels and base:
865  if (empty($labels)) {
866  $labels = 'iec';
867  }
868  if (isset($defaultFormats[$labels])) {
869  $base = $defaultFormats[$labels]['base'];
870  $labelArr = $defaultFormats[$labels]['labels'];
871  } else {
872  $base = (int)$base;
873  if ($base !== 1000 && $base !== 1024) {
874  $base = 1024;
875  }
876  $labelArr = explode('|', str_replace('"', '', $labels));
877  }
878  // @todo find out which locale is used for current BE user to cover the BE case as well
879  $oldLocale = setlocale(LC_NUMERIC, 0);
880  $newLocale = isset($GLOBALS['TSFE']) ? $GLOBALS['TSFE']->config['config']['locale_all'] : '';
881  if ($newLocale) {
882  setlocale(LC_NUMERIC, $newLocale);
883  }
884  $localeInfo = localeconv();
885  if ($newLocale) {
886  setlocale(LC_NUMERIC, $oldLocale);
887  }
888  $sizeInBytes = max($sizeInBytes, 0);
889  $multiplier = floor(($sizeInBytes ? log($sizeInBytes) : 0) / log($base));
890  $sizeInUnits = $sizeInBytes / pow($base, $multiplier);
891  if ($sizeInUnits > ($base * .9)) {
892  $multiplier++;
893  }
894  $multiplier = min($multiplier, count($labelArr) - 1);
895  $sizeInUnits = $sizeInBytes / pow($base, $multiplier);
896  return number_format($sizeInUnits, (($multiplier > 0) && ($sizeInUnits < 20)) ? 2 : 0, $localeInfo['decimal_point'], '') . $labelArr[$multiplier];
897  }
898 
906  public static function convertMicrotime($microtime)
907  {
908  static::logDeprecatedFunction();
909  $parts = explode(' ', $microtime);
910  return round(($parts[0] + $parts[1]) * 1000);
911  }
912 
921  public static function splitCalc($string, $operators)
922  {
923  $res = [];
924  $sign = '+';
925  while ($string) {
926  $valueLen = strcspn($string, $operators);
927  $value = substr($string, 0, $valueLen);
928  $res[] = [$sign, trim($value)];
929  $sign = substr($string, $valueLen, 1);
930  $string = substr($string, $valueLen + 1);
931  }
932  reset($res);
933  return $res;
934  }
935 
945  public static function deHSCentities($str)
946  {
947  static::logDeprecatedFunction();
948  return preg_replace('/&amp;([#[:alnum:]]*;)/', '&\\1', $str);
949  }
950 
960  public static function slashJS($string, $extended = false, $char = '\'')
961  {
962  static::logDeprecatedFunction();
963  if ($extended) {
964  $string = str_replace('\\', '\\\\', $string);
965  }
966  return str_replace($char, '\\' . $char, $string);
967  }
968 
977  public static function rawUrlEncodeJS($str)
978  {
979  static::logDeprecatedFunction();
980  return str_replace('%20', ' ', rawurlencode($str));
981  }
982 
991  public static function rawUrlEncodeFP($str)
992  {
993  static::logDeprecatedFunction();
994  return str_replace('%2F', '/', rawurlencode($str));
995  }
996 
1015  public static function validEmail($email)
1016  {
1017  // Early return in case input is not a string
1018  if (!is_string($email)) {
1019  return false;
1020  }
1021  $atPosition = strrpos($email, '@');
1022  if (!$atPosition || $atPosition + 1 === strlen($email)) {
1023  // Return if no @ found or it is placed at the very beginning or end of the email
1024  return false;
1025  }
1026  $domain = substr($email, $atPosition + 1);
1027  $user = substr($email, 0, $atPosition);
1028  if (!preg_match('/^[a-z0-9.\\-]*$/i', $domain)) {
1029  try {
1030  $domain = self::idnaEncode($domain);
1031  } catch (\InvalidArgumentException $exception) {
1032  return false;
1033  }
1034  }
1035  return filter_var($user . '@' . $domain, FILTER_VALIDATE_EMAIL) !== false;
1036  }
1037 
1048  public static function strtoupper($str)
1049  {
1050  self::logDeprecatedFunction();
1051  return strtr((string)$str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
1052  }
1053 
1064  public static function strtolower($str)
1065  {
1066  self::logDeprecatedFunction();
1067  return strtr((string)$str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
1068  }
1069 
1077  public static function generateRandomBytes($bytesToReturn)
1078  {
1079  self::logDeprecatedFunction();
1080  return self::makeInstance(Random::class)->generateRandomBytes($bytesToReturn);
1081  }
1082 
1089  public static function idnaEncode($value)
1090  {
1091  if (isset(self::$idnaStringCache[$value])) {
1092  return self::$idnaStringCache[$value];
1093  }
1094  if (!self::$idnaConverter) {
1095  self::$idnaConverter = new \Mso\IdnaConvert\IdnaConvert(['idn_version' => 2008]);
1096  }
1097  self::$idnaStringCache[$value] = self::$idnaConverter->encode($value);
1098  return self::$idnaStringCache[$value];
1099  }
1100 
1108  public static function getRandomHexString($count)
1109  {
1110  self::logDeprecatedFunction();
1111  return self::makeInstance(Random::class)->generateRandomHexString($count);
1112  }
1113 
1121  public static function underscoredToUpperCamelCase($string)
1122  {
1123  return str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($string))));
1124  }
1125 
1133  public static function underscoredToLowerCamelCase($string)
1134  {
1135  return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($string)))));
1136  }
1137 
1145  public static function camelCaseToLowerCaseUnderscored($string)
1146  {
1147  $value = preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string);
1148  return mb_strtolower($value, 'utf-8');
1149  }
1150 
1159  public static function lcfirst($string)
1160  {
1161  static::logDeprecatedFunction();
1162  return lcfirst($string);
1163  }
1164 
1189  public static function isValidUrl($url)
1190  {
1191  $parsedUrl = parse_url($url);
1192  if (!$parsedUrl || !isset($parsedUrl['scheme'])) {
1193  return false;
1194  }
1195  // HttpUtility::buildUrl() will always build urls with <scheme>://
1196  // our original $url might only contain <scheme>: (e.g. mail:)
1197  // so we convert that to the double-slashed version to ensure
1198  // our check against the $recomposedUrl is proper
1199  if (!self::isFirstPartOfStr($url, $parsedUrl['scheme'] . '://')) {
1200  $url = str_replace($parsedUrl['scheme'] . ':', $parsedUrl['scheme'] . '://', $url);
1201  }
1202  $recomposedUrl = HttpUtility::buildUrl($parsedUrl);
1203  if ($recomposedUrl !== $url) {
1204  // The parse_url() had to modify characters, so the URL is invalid
1205  return false;
1206  }
1207  if (isset($parsedUrl['host']) && !preg_match('/^[a-z0-9.\\-]*$/i', $parsedUrl['host'])) {
1208  try {
1209  $parsedUrl['host'] = self::idnaEncode($parsedUrl['host']);
1210  } catch (\InvalidArgumentException $exception) {
1211  return false;
1212  }
1213  }
1214  return filter_var(HttpUtility::buildUrl($parsedUrl), FILTER_VALIDATE_URL) !== false;
1215  }
1216 
1217  /*************************
1218  *
1219  * ARRAY FUNCTIONS
1220  *
1221  *************************/
1222 
1233  public static function intExplode($delimiter, $string, $removeEmptyValues = false, $limit = 0)
1234  {
1235  $result = explode($delimiter, $string);
1236  foreach ($result as $key => &$value) {
1237  if ($removeEmptyValues && ($value === '' || trim($value) === '')) {
1238  unset($result[$key]);
1239  } else {
1240  $value = (int)$value;
1241  }
1242  }
1243  unset($value);
1244  if ($limit !== 0) {
1245  if ($limit < 0) {
1246  $result = array_slice($result, 0, $limit);
1247  } elseif (count($result) > $limit) {
1248  $lastElements = array_slice($result, $limit - 1);
1249  $result = array_slice($result, 0, $limit - 1);
1250  $result[] = implode($delimiter, $lastElements);
1251  }
1252  }
1253  return $result;
1254  }
1255 
1270  public static function revExplode($delimiter, $string, $count = 0)
1271  {
1272  // 2 is the (currently, as of 2014-02) most-used value for $count in the core, therefore we check it first
1273  if ($count === 2) {
1274  $position = strrpos($string, strrev($delimiter));
1275  if ($position !== false) {
1276  return [substr($string, 0, $position), substr($string, $position + strlen($delimiter))];
1277  }
1278  return [$string];
1279  }
1280  if ($count <= 1) {
1281  return [$string];
1282  }
1283  $explodedValues = explode($delimiter, strrev($string), $count);
1284  $explodedValues = array_map('strrev', $explodedValues);
1285  return array_reverse($explodedValues);
1286  }
1287 
1300  public static function trimExplode($delim, $string, $removeEmptyValues = false, $limit = 0)
1301  {
1302  $result = explode($delim, $string);
1303  if ($removeEmptyValues) {
1304  $temp = [];
1305  foreach ($result as $value) {
1306  if (trim($value) !== '') {
1307  $temp[] = $value;
1308  }
1309  }
1310  $result = $temp;
1311  }
1312  if ($limit > 0 && count($result) > $limit) {
1313  $lastElements = array_splice($result, $limit - 1);
1314  $result[] = implode($delim, $lastElements);
1315  } elseif ($limit < 0) {
1316  $result = array_slice($result, 0, $limit);
1317  }
1318  $result = array_map('trim', $result);
1319  return $result;
1320  }
1321 
1333  public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = false, $rawurlencodeParamName = false)
1334  {
1335  foreach ($theArray as $Akey => $AVal) {
1336  $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey;
1337  if (is_array($AVal)) {
1338  $str = self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName);
1339  } else {
1340  if (!$skipBlank || (string)$AVal !== '') {
1341  $str .= '&' . ($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName) . '=' . rawurlencode($AVal);
1342  }
1343  }
1344  }
1345  return $str;
1346  }
1347 
1356  public static function explodeUrl2Array($string, $multidim = false)
1357  {
1358  $output = [];
1359  if ($multidim) {
1360  parse_str($string, $output);
1361  } else {
1362  $p = explode('&', $string);
1363  foreach ($p as $v) {
1364  if ($v !== '') {
1365  list($pK, $pV) = explode('=', $v, 2);
1366  $output[rawurldecode($pK)] = rawurldecode($pV);
1367  }
1368  }
1369  }
1370  return $output;
1371  }
1372 
1382  public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = true)
1383  {
1384  $keys = self::trimExplode(',', $varList, true);
1385  $outArr = [];
1386  foreach ($keys as $v) {
1387  if (isset($getArray[$v])) {
1388  $outArr[$v] = $getArray[$v];
1389  } elseif ($GPvarAlt) {
1390  $outArr[$v] = self::_GP($v);
1391  }
1392  }
1393  return $outArr;
1394  }
1395 
1405  public static function csvValues(array $row, $delim = ',', $quote = '"')
1406  {
1407  self::logDeprecatedFunction();
1408  return CsvUtility::csvValues($row, $delim, $quote);
1409  }
1410 
1418  public static function removeDotsFromTS(array $ts)
1419  {
1420  $out = [];
1421  foreach ($ts as $key => $value) {
1422  if (is_array($value)) {
1423  $key = rtrim($key, '.');
1424  $out[$key] = self::removeDotsFromTS($value);
1425  } else {
1426  $out[$key] = $value;
1427  }
1428  }
1429  return $out;
1430  }
1431 
1432  /*************************
1433  *
1434  * HTML/XML PROCESSING
1435  *
1436  *************************/
1445  public static function get_tag_attributes($tag)
1446  {
1447  $components = self::split_tag_attributes($tag);
1448  // Attribute name is stored here
1449  $name = '';
1450  $valuemode = false;
1451  $attributes = [];
1452  foreach ($components as $key => $val) {
1453  // 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
1454  if ($val !== '=') {
1455  if ($valuemode) {
1456  if ($name) {
1457  $attributes[$name] = $val;
1458  $name = '';
1459  }
1460  } else {
1461  if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val))) {
1462  $attributes[$key] = '';
1463  $name = $key;
1464  }
1465  }
1466  $valuemode = false;
1467  } else {
1468  $valuemode = true;
1469  }
1470  }
1471  return $attributes;
1472  }
1473 
1481  public static function split_tag_attributes($tag)
1482  {
1483  $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
1484  // Removes any > in the end of the string
1485  $tag_tmp = trim(rtrim($tag_tmp, '>'));
1486  $value = [];
1487  // Compared with empty string instead , 030102
1488  while ($tag_tmp !== '') {
1489  $firstChar = $tag_tmp[0];
1490  if ($firstChar === '"' || $firstChar === '\'') {
1491  $reg = explode($firstChar, $tag_tmp, 3);
1492  $value[] = $reg[1];
1493  $tag_tmp = trim($reg[2]);
1494  } elseif ($firstChar === '=') {
1495  $value[] = '=';
1496  // Removes = chars.
1497  $tag_tmp = trim(substr($tag_tmp, 1));
1498  } else {
1499  // There are '' around the value. We look for the next ' ' or '>'
1500  $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
1501  $value[] = trim($reg[0]);
1502  $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]);
1503  }
1504  }
1505  reset($value);
1506  return $value;
1507  }
1508 
1517  public static function implodeAttributes(array $arr, $xhtmlSafe = false, $dontOmitBlankAttribs = false)
1518  {
1519  if ($xhtmlSafe) {
1520  $newArr = [];
1521  foreach ($arr as $p => $v) {
1522  if (!isset($newArr[strtolower($p)])) {
1523  $newArr[strtolower($p)] = htmlspecialchars($v);
1524  }
1525  }
1526  $arr = $newArr;
1527  }
1528  $list = [];
1529  foreach ($arr as $p => $v) {
1530  if ((string)$v !== '' || $dontOmitBlankAttribs) {
1531  $list[] = $p . '="' . $v . '"';
1532  }
1533  }
1534  return implode(' ', $list);
1535  }
1536 
1546  public static function wrapJS($string, $_ = null)
1547  {
1548  if ($_ !== null) {
1549  self::deprecationLog('Parameter 2 of GeneralUtility::wrapJS is obsolete and can be omitted.');
1550  }
1551 
1552  if (trim($string)) {
1553  // remove nl from the beginning
1554  $string = ltrim($string, LF);
1555  // re-ident to one tab using the first line as reference
1556  $match = [];
1557  if (preg_match('/^(\\t+)/', $string, $match)) {
1558  $string = str_replace($match[1], TAB, $string);
1559  }
1560  return '<script type="text/javascript">
1561 /*<![CDATA[*/
1562 ' . $string . '
1563 /*]]>*/
1564 </script>';
1565  }
1566  return '';
1567  }
1568 
1577  public static function xml2tree($string, $depth = 999, $parserOptions = [])
1578  {
1579  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
1580  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
1581  $parser = xml_parser_create();
1582  $vals = [];
1583  $index = [];
1584  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
1585  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
1586  foreach ($parserOptions as $option => $value) {
1587  xml_parser_set_option($parser, $option, $value);
1588  }
1589  xml_parse_into_struct($parser, $string, $vals, $index);
1590  libxml_disable_entity_loader($previousValueOfEntityLoader);
1591  if (xml_get_error_code($parser)) {
1592  return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
1593  }
1594  xml_parser_free($parser);
1595  $stack = [[]];
1596  $stacktop = 0;
1597  $startPoint = 0;
1598  $tagi = [];
1599  foreach ($vals as $key => $val) {
1600  $type = $val['type'];
1601  // open tag:
1602  if ($type === 'open' || $type === 'complete') {
1603  $stack[$stacktop++] = $tagi;
1604  if ($depth == $stacktop) {
1605  $startPoint = $key;
1606  }
1607  $tagi = ['tag' => $val['tag']];
1608  if (isset($val['attributes'])) {
1609  $tagi['attrs'] = $val['attributes'];
1610  }
1611  if (isset($val['value'])) {
1612  $tagi['values'][] = $val['value'];
1613  }
1614  }
1615  // finish tag:
1616  if ($type === 'complete' || $type === 'close') {
1617  $oldtagi = $tagi;
1618  $tagi = $stack[--$stacktop];
1619  $oldtag = $oldtagi['tag'];
1620  unset($oldtagi['tag']);
1621  if ($depth == $stacktop + 1) {
1622  if ($key - $startPoint > 0) {
1623  $partArray = array_slice($vals, $startPoint + 1, $key - $startPoint - 1);
1624  $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray);
1625  } else {
1626  $oldtagi['XMLvalue'] = $oldtagi['values'][0];
1627  }
1628  }
1629  $tagi['ch'][$oldtag][] = $oldtagi;
1630  unset($oldtagi);
1631  }
1632  // cdata
1633  if ($type === 'cdata') {
1634  $tagi['values'][] = $val['value'];
1635  }
1636  }
1637  return $tagi['ch'];
1638  }
1639 
1651  public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = [], $charset = '')
1652  {
1653  static::logDeprecatedFunction();
1654  // Set default charset unless explicitly specified
1655  $charset = $charset ?: 'utf-8';
1656  // Return XML:
1657  return '<?xml version="1.0" encoding="' . htmlspecialchars($charset) . '" standalone="yes" ?>' . LF . self::array2xml($array, '', 0, $docTag, 0, $options);
1658  }
1659 
1680  public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = [], array $stackData = [])
1681  {
1682  // 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
1683  $binaryChars = chr(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);
1684  // Set indenting mode:
1685  $indentChar = $spaceInd ? ' ' : TAB;
1686  $indentN = $spaceInd > 0 ? $spaceInd : 1;
1687  $nl = $spaceInd >= 0 ? LF : '';
1688  // Init output variable:
1689  $output = '';
1690  // Traverse the input array
1691  foreach ($array as $k => $v) {
1692  $attr = '';
1693  $tagName = $k;
1694  // Construct the tag name.
1695  // Use tag based on grand-parent + parent tag name
1696  if (isset($options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) {
1697  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1698  $tagName = (string)$options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']];
1699  } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && MathUtility::canBeInterpretedAsInteger($tagName)) {
1700  // Use tag based on parent tag name + if current tag is numeric
1701  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1702  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM'];
1703  } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) {
1704  // Use tag based on parent tag name + current tag
1705  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1706  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName];
1707  } elseif (isset($options['parentTagMap'][$stackData['parentTagName']])) {
1708  // Use tag based on parent tag name:
1709  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1710  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName']];
1711  } elseif (MathUtility::canBeInterpretedAsInteger($tagName)) {
1712  // If integer...;
1713  if ($options['useNindex']) {
1714  // If numeric key, prefix "n"
1715  $tagName = 'n' . $tagName;
1716  } else {
1717  // Use special tag for num. keys:
1718  $attr .= ' index="' . $tagName . '"';
1719  $tagName = $options['useIndexTagForNum'] ?: 'numIndex';
1720  }
1721  } elseif ($options['useIndexTagForAssoc']) {
1722  // Use tag for all associative keys:
1723  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
1724  $tagName = $options['useIndexTagForAssoc'];
1725  }
1726  // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
1727  $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100);
1728  // If the value is an array then we will call this function recursively:
1729  if (is_array($v)) {
1730  // Sub elements:
1731  if ($options['alt_options'][$stackData['path'] . '/' . $tagName]) {
1732  $subOptions = $options['alt_options'][$stackData['path'] . '/' . $tagName];
1733  $clearStackPath = $subOptions['clearStackPath'];
1734  } else {
1735  $subOptions = $options;
1736  $clearStackPath = false;
1737  }
1738  if (empty($v)) {
1739  $content = '';
1740  } else {
1741  $content = $nl . self::array2xml($v, $NSprefix, ($level + 1), '', $spaceInd, $subOptions, [
1742  'parentTagName' => $tagName,
1743  'grandParentTagName' => $stackData['parentTagName'],
1744  'path' => ($clearStackPath ? '' : $stackData['path'] . '/' . $tagName)
1745  ]) . ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '');
1746  }
1747  // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
1748  if ((int)$options['disableTypeAttrib'] != 2) {
1749  $attr .= ' type="array"';
1750  }
1751  } else {
1752  // Just a value:
1753  // Look for binary chars:
1754  $vLen = strlen($v);
1755  // Go for base64 encoding if the initial segment NOT matching any binary char has the same length as the whole string!
1756  if ($vLen && strcspn($v, $binaryChars) != $vLen) {
1757  // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation:
1758  $content = $nl . chunk_split(base64_encode($v));
1759  $attr .= ' base64="1"';
1760  } else {
1761  // Otherwise, just htmlspecialchar the stuff:
1762  $content = htmlspecialchars($v);
1763  $dType = gettype($v);
1764  if ($dType === 'string') {
1765  if ($options['useCDATA'] && $content != $v) {
1766  $content = '<![CDATA[' . $v . ']]>';
1767  }
1768  } elseif (!$options['disableTypeAttrib']) {
1769  $attr .= ' type="' . $dType . '"';
1770  }
1771  }
1772  }
1773  if ((string)$tagName !== '') {
1774  // Add the element to the output string:
1775  $output .= ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '')
1776  . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . $nl;
1777  }
1778  }
1779  // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
1780  if (!$level) {
1781  $output = '<' . $docTag . '>' . $nl . $output . '</' . $docTag . '>';
1782  }
1783  return $output;
1784  }
1785 
1797  public static function xml2array($string, $NSprefix = '', $reportDocTag = false)
1798  {
1799  static $firstLevelCache = [];
1800  $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0'));
1801  // Look up in first level cache
1802  if (!empty($firstLevelCache[$identifier])) {
1803  $array = $firstLevelCache[$identifier];
1804  } else {
1805  $array = self::xml2arrayProcess(trim($string), $NSprefix, $reportDocTag);
1806  // Store content in first level cache
1807  $firstLevelCache[$identifier] = $array;
1808  }
1809  return $array;
1810  }
1811 
1822  protected static function xml2arrayProcess($string, $NSprefix = '', $reportDocTag = false)
1823  {
1824  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
1825  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
1826  // Create parser:
1827  $parser = xml_parser_create();
1828  $vals = [];
1829  $index = [];
1830  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
1831  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
1832  // Default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
1833  $match = [];
1834  preg_match('/^[[:space:]]*<\\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match);
1835  $theCharset = $match[1] ?: 'utf-8';
1836  // us-ascii / utf-8 / iso-8859-1
1837  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset);
1838  // Parse content:
1839  xml_parse_into_struct($parser, $string, $vals, $index);
1840  libxml_disable_entity_loader($previousValueOfEntityLoader);
1841  // If error, return error message:
1842  if (xml_get_error_code($parser)) {
1843  return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
1844  }
1845  xml_parser_free($parser);
1846  // Init vars:
1847  $stack = [[]];
1848  $stacktop = 0;
1849  $current = [];
1850  $tagName = '';
1851  $documentTag = '';
1852  // Traverse the parsed XML structure:
1853  foreach ($vals as $key => $val) {
1854  // First, process the tag-name (which is used in both cases, whether "complete" or "close")
1855  $tagName = $val['tag'];
1856  if (!$documentTag) {
1857  $documentTag = $tagName;
1858  }
1859  // Test for name space:
1860  $tagName = $NSprefix && substr($tagName, 0, strlen($NSprefix)) == $NSprefix ? substr($tagName, strlen($NSprefix)) : $tagName;
1861  // Test for numeric tag, encoded on the form "nXXX":
1862  $testNtag = substr($tagName, 1);
1863  // Closing tag.
1864  $tagName = $tagName[0] === 'n' && MathUtility::canBeInterpretedAsInteger($testNtag) ? (int)$testNtag : $tagName;
1865  // Test for alternative index value:
1866  if ((string)($val['attributes']['index'] ?? '') !== '') {
1867  $tagName = $val['attributes']['index'];
1868  }
1869  // Setting tag-values, manage stack:
1870  switch ($val['type']) {
1871  case 'open':
1872  // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
1873  // Setting blank place holder
1874  $current[$tagName] = [];
1875  $stack[$stacktop++] = $current;
1876  $current = [];
1877  break;
1878  case 'close':
1879  // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
1880  $oldCurrent = $current;
1881  $current = $stack[--$stacktop];
1882  // Going to the end of array to get placeholder key, key($current), and fill in array next:
1883  end($current);
1884  $current[key($current)] = $oldCurrent;
1885  unset($oldCurrent);
1886  break;
1887  case 'complete':
1888  // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
1889  if ($val['attributes']['base64']) {
1890  $current[$tagName] = base64_decode($val['value']);
1891  } else {
1892  // Had to cast it as a string - otherwise it would be evaluate FALSE if tested with isset()!!
1893  $current[$tagName] = (string)$val['value'];
1894  // Cast type:
1895  switch ((string)$val['attributes']['type']) {
1896  case 'integer':
1897  $current[$tagName] = (int)$current[$tagName];
1898  break;
1899  case 'double':
1900  $current[$tagName] = (double)$current[$tagName];
1901  break;
1902  case 'boolean':
1903  $current[$tagName] = (bool)$current[$tagName];
1904  break;
1905  case 'NULL':
1906  $current[$tagName] = null;
1907  break;
1908  case 'array':
1909  // 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...
1910  $current[$tagName] = [];
1911  break;
1912  }
1913  }
1914  break;
1915  }
1916  }
1917  if ($reportDocTag) {
1918  $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
1919  }
1920  // Finally return the content of the document tag.
1921  return $current[$tagName];
1922  }
1923 
1930  public static function xmlRecompileFromStructValArray(array $vals)
1931  {
1932  $XMLcontent = '';
1933  foreach ($vals as $val) {
1934  $type = $val['type'];
1935  // Open tag:
1936  if ($type === 'open' || $type === 'complete') {
1937  $XMLcontent .= '<' . $val['tag'];
1938  if (isset($val['attributes'])) {
1939  foreach ($val['attributes'] as $k => $v) {
1940  $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"';
1941  }
1942  }
1943  if ($type === 'complete') {
1944  if (isset($val['value'])) {
1945  $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>';
1946  } else {
1947  $XMLcontent .= '/>';
1948  }
1949  } else {
1950  $XMLcontent .= '>';
1951  }
1952  if ($type === 'open' && isset($val['value'])) {
1953  $XMLcontent .= htmlspecialchars($val['value']);
1954  }
1955  }
1956  // Finish tag:
1957  if ($type === 'close') {
1958  $XMLcontent .= '</' . $val['tag'] . '>';
1959  }
1960  // Cdata
1961  if ($type === 'cdata') {
1962  $XMLcontent .= htmlspecialchars($val['value']);
1963  }
1964  }
1965  return $XMLcontent;
1966  }
1967 
1975  public static function xmlGetHeaderAttribs($xmlData)
1976  {
1977  self::logDeprecatedFunction();
1978  $match = [];
1979  if (preg_match('/^\\s*<\\?xml([^>]*)\\?\\>/', $xmlData, $match)) {
1980  return self::get_tag_attributes($match[1]);
1981  }
1982  }
1983 
1991  public static function minifyJavaScript($script, &$error = '')
1992  {
1993  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'])) {
1994  $fakeThis = false;
1995  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'] as $hookMethod) {
1996  try {
1997  $parameters = ['script' => $script];
1998  $script = static::callUserFunction($hookMethod, $parameters, $fakeThis);
1999  } catch (\Exception $e) {
2000  $errorMessage = 'Error minifying java script: ' . $e->getMessage();
2001  $error .= $errorMessage;
2002  static::devLog($errorMessage, \TYPO3\CMS\Core\Utility\GeneralUtility::class, 2, [
2003  'JavaScript' => $script,
2004  'Stack trace' => $e->getTrace(),
2005  'hook' => $hookMethod
2006  ]);
2007  }
2008  }
2009  }
2010  return $script;
2011  }
2012 
2013  /*************************
2014  *
2015  * FILES FUNCTIONS
2016  *
2017  *************************/
2028  public static function getUrl($url, $includeHeader = 0, $requestHeaders = null, &$report = null)
2029  {
2030  if (isset($report)) {
2031  $report['error'] = 0;
2032  $report['message'] = '';
2033  }
2034  // Looks like it's an external file, use Guzzle by default
2035  if (preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
2037  $requestFactory = static::makeInstance(RequestFactory::class);
2038  if (is_array($requestHeaders)) {
2039  // Check is $requestHeaders is an associative array or not
2040  if (count(array_filter(array_keys($requestHeaders), 'is_string')) === 0) {
2041  // Convert cURL style lines of headers to Guzzle key/value(s) pairs.
2042  $requestHeaders = static::splitHeaderLines($requestHeaders);
2043  }
2044  $configuration = ['headers' => $requestHeaders];
2045  } else {
2046  $configuration = [];
2047  }
2048  $includeHeader = (int)$includeHeader;
2049  $method = $includeHeader === 2 ? 'HEAD' : 'GET';
2050  try {
2051  if (isset($report)) {
2052  $report['lib'] = 'GuzzleHttp';
2053  }
2054  $response = $requestFactory->request($url, $method, $configuration);
2055  } catch (RequestException $exception) {
2056  if (isset($report)) {
2057  $report['error'] = $exception->getCode();
2058  $report['message'] = $exception->getMessage();
2059  $report['exception'] = $exception;
2060  }
2061  return false;
2062  }
2063  $content = '';
2064  // Add the headers to the output
2065  if ($includeHeader) {
2066  $parsedURL = parse_url($url);
2067  $content = $method . ' ' . (isset($parsedURL['path']) ? $parsedURL['path'] : '/')
2068  . ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') . ' HTTP/1.0' . CRLF
2069  . 'Host: ' . $parsedURL['host'] . CRLF
2070  . 'Connection: close' . CRLF;
2071  if (is_array($requestHeaders)) {
2072  $content .= implode(CRLF, $requestHeaders) . CRLF;
2073  }
2074  foreach ($response->getHeaders() as $headerName => $headerValues) {
2075  $content .= $headerName . ': ' . implode(', ', $headerValues) . CRLF;
2076  }
2077  // Headers are separated from the body with two CRLFs
2078  $content .= CRLF;
2079  }
2080 
2081  $content .= $response->getBody()->getContents();
2082 
2083  if (isset($report)) {
2084  if ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
2085  $report['http_code'] = $response->getStatusCode();
2086  $report['content_type'] = $response->getHeaderLine('Content-Type');
2087  $report['error'] = $response->getStatusCode();
2088  $report['message'] = $response->getReasonPhrase();
2089  } elseif (empty($content)) {
2090  $report['error'] = $response->getStatusCode();
2091  $report['message'] = $response->getReasonPhrase();
2092  } elseif ($includeHeader) {
2093  // Set only for $includeHeader to work exactly like PHP variant
2094  $report['http_code'] = $response->getStatusCode();
2095  $report['content_type'] = $response->getHeaderLine('Content-Type');
2096  }
2097  }
2098  } else {
2099  if (isset($report)) {
2100  $report['lib'] = 'file';
2101  }
2102  $content = @file_get_contents($url);
2103  if ($content === false && isset($report)) {
2104  $report['error'] = -1;
2105  $report['message'] = 'Couldn\'t get URL: ' . $url;
2106  }
2107  }
2108  return $content;
2109  }
2110 
2119  protected static function splitHeaderLines(array $headers): array
2120  {
2121  $newHeaders = [];
2122  foreach ($headers as $header) {
2123  $parts = preg_split('/:[ \t]*/', $header, 2, PREG_SPLIT_NO_EMPTY);
2124  if (count($parts) !== 2) {
2125  continue;
2126  }
2127  $key = &$parts[0];
2128  $value = &$parts[1];
2129  if (array_key_exists($key, $newHeaders)) {
2130  if (is_array($newHeaders[$key])) {
2131  $newHeaders[$key][] = $value;
2132  } else {
2133  $prevValue = &$newHeaders[$key];
2134  $newHeaders[$key] = [$prevValue, $value];
2135  }
2136  } else {
2137  $newHeaders[$key] = $value;
2138  }
2139  }
2140  return $newHeaders;
2141  }
2142 
2151  public static function writeFile($file, $content, $changePermissions = false)
2152  {
2153  if (!@is_file($file)) {
2154  $changePermissions = true;
2155  }
2156  if ($fd = fopen($file, 'wb')) {
2157  $res = fwrite($fd, $content);
2158  fclose($fd);
2159  if ($res === false) {
2160  return false;
2161  }
2162  // Change the permissions only if the file has just been created
2163  if ($changePermissions) {
2164  static::fixPermissions($file);
2165  }
2166  return true;
2167  }
2168  return false;
2169  }
2170 
2178  public static function fixPermissions($path, $recursive = false)
2179  {
2180  if (TYPO3_OS === 'WIN') {
2181  return true;
2182  }
2183  $result = false;
2184  // Make path absolute
2185  if (!static::isAbsPath($path)) {
2186  $path = static::getFileAbsFileName($path);
2187  }
2188  if (static::isAllowedAbsPath($path)) {
2189  if (@is_file($path)) {
2190  $targetPermissions = isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'])
2191  ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']
2192  : '0644';
2193  } elseif (@is_dir($path)) {
2194  $targetPermissions = isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'])
2195  ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']
2196  : '0755';
2197  }
2198  if (!empty($targetPermissions)) {
2199  // make sure it's always 4 digits
2200  $targetPermissions = str_pad($targetPermissions, 4, 0, STR_PAD_LEFT);
2201  $targetPermissions = octdec($targetPermissions);
2202  // "@" is there because file is not necessarily OWNED by the user
2203  $result = @chmod($path, $targetPermissions);
2204  }
2205  // Set createGroup if not empty
2206  if (
2207  isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'])
2208  && $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] !== ''
2209  ) {
2210  // "@" is there because file is not necessarily OWNED by the user
2211  $changeGroupResult = @chgrp($path, $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup']);
2212  $result = $changeGroupResult ? $result : false;
2213  }
2214  // Call recursive if recursive flag if set and $path is directory
2215  if ($recursive && @is_dir($path)) {
2216  $handle = opendir($path);
2217  if (is_resource($handle)) {
2218  while (($file = readdir($handle)) !== false) {
2219  $recursionResult = null;
2220  if ($file !== '.' && $file !== '..') {
2221  if (@is_file(($path . '/' . $file))) {
2222  $recursionResult = static::fixPermissions($path . '/' . $file);
2223  } elseif (@is_dir(($path . '/' . $file))) {
2224  $recursionResult = static::fixPermissions($path . '/' . $file, true);
2225  }
2226  if (isset($recursionResult) && !$recursionResult) {
2227  $result = false;
2228  }
2229  }
2230  }
2231  closedir($handle);
2232  }
2233  }
2234  }
2235  return $result;
2236  }
2237 
2246  public static function writeFileToTypo3tempDir($filepath, $content)
2247  {
2248  // Parse filepath into directory and basename:
2249  $fI = pathinfo($filepath);
2250  $fI['dirname'] .= '/';
2251  // Check parts:
2252  if (!static::validPathStr($filepath) || !$fI['basename'] || strlen($fI['basename']) >= 60) {
2253  return 'Input filepath "' . $filepath . '" was generally invalid!';
2254  }
2255  // Setting main temporary directory name (standard)
2256  $dirName = PATH_site . 'typo3temp/';
2257  if (!@is_dir($dirName)) {
2258  return 'PATH_site + "typo3temp/" was not a directory!';
2259  }
2260  if (!static::isFirstPartOfStr($fI['dirname'], $dirName)) {
2261  return '"' . $fI['dirname'] . '" was not within directory PATH_site + "typo3temp/"';
2262  }
2263  // Checking if the "subdir" is found:
2264  $subdir = substr($fI['dirname'], strlen($dirName));
2265  if ($subdir) {
2266  if (preg_match('#^(?:[[:alnum:]_]+/)+$#', $subdir)) {
2267  $dirName .= $subdir;
2268  if (!@is_dir($dirName)) {
2269  static::mkdir_deep(PATH_site . 'typo3temp/', $subdir);
2270  }
2271  } else {
2272  return 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/+"';
2273  }
2274  }
2275  // Checking dir-name again (sub-dir might have been created):
2276  if (@is_dir($dirName)) {
2277  if ($filepath === $dirName . $fI['basename']) {
2278  static::writeFile($filepath, $content);
2279  if (!@is_file($filepath)) {
2280  return 'The file was not written to the disk. Please, check that you have write permissions to the typo3temp/ directory.';
2281  }
2282  } else {
2283  return 'Calculated file location didn\'t match input "' . $filepath . '".';
2284  }
2285  } else {
2286  return '"' . $dirName . '" is not a directory!';
2287  }
2288  return null;
2289  }
2290 
2299  public static function mkdir($newFolder)
2300  {
2301  $result = @mkdir($newFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2302  if ($result) {
2303  static::fixPermissions($newFolder);
2304  }
2305  return $result;
2306  }
2307 
2317  public static function mkdir_deep($directory, $deepDirectory = '')
2318  {
2319  if (!is_string($directory)) {
2320  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($directory) . '" but a string is expected.', 1303662955);
2321  }
2322  if (!is_string($deepDirectory)) {
2323  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($deepDirectory) . '" but a string is expected.', 1303662956);
2324  }
2325  // Ensure there is only one slash
2326  $fullPath = rtrim($directory, '/') . '/' . ltrim($deepDirectory, '/');
2327  if ($fullPath !== '/' && !is_dir($fullPath)) {
2328  $firstCreatedPath = static::createDirectoryPath($fullPath);
2329  if ($firstCreatedPath !== '') {
2330  static::fixPermissions($firstCreatedPath, true);
2331  }
2332  }
2333  }
2334 
2346  protected static function createDirectoryPath($fullDirectoryPath)
2347  {
2348  $currentPath = $fullDirectoryPath;
2349  $firstCreatedPath = '';
2350  $permissionMask = octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2351  if (!@is_dir($currentPath)) {
2352  do {
2353  $firstCreatedPath = $currentPath;
2354  $separatorPosition = strrpos($currentPath, DIRECTORY_SEPARATOR);
2355  $currentPath = substr($currentPath, 0, $separatorPosition);
2356  } while (!is_dir($currentPath) && $separatorPosition !== false);
2357  $result = @mkdir($fullDirectoryPath, $permissionMask, true);
2358  // Check existence of directory again to avoid race condition. Directory could have get created by another process between previous is_dir() and mkdir()
2359  if (!$result && !@is_dir($fullDirectoryPath)) {
2360  throw new \RuntimeException('Could not create directory "' . $fullDirectoryPath . '"!', 1170251401);
2361  }
2362  }
2363  return $firstCreatedPath;
2364  }
2365 
2373  public static function rmdir($path, $removeNonEmpty = false)
2374  {
2375  $OK = false;
2376  // Remove trailing slash
2377  $path = preg_replace('|/$|', '', $path);
2378  if (file_exists($path)) {
2379  $OK = true;
2380  if (!is_link($path) && is_dir($path)) {
2381  if ($removeNonEmpty == true && ($handle = @opendir($path))) {
2382  while ($OK && false !== ($file = readdir($handle))) {
2383  if ($file === '.' || $file === '..') {
2384  continue;
2385  }
2386  $OK = static::rmdir($path . '/' . $file, $removeNonEmpty);
2387  }
2388  closedir($handle);
2389  }
2390  if ($OK) {
2391  $OK = @rmdir($path);
2392  }
2393  } elseif (is_link($path) && is_dir($path) && TYPO3_OS === 'WIN') {
2394  $OK = @rmdir($path);
2395  } else {
2396  // If $path is a file, simply remove it
2397  $OK = @unlink($path);
2398  }
2399  clearstatcache();
2400  } elseif (is_link($path)) {
2401  $OK = @unlink($path);
2402  if (!$OK && TYPO3_OS === 'WIN') {
2403  // Try to delete dead folder links on Windows systems
2404  $OK = @rmdir($path);
2405  }
2406  clearstatcache();
2407  }
2408  return $OK;
2409  }
2410 
2421  public static function flushDirectory($directory, $keepOriginalDirectory = false, $flushOpcodeCache = false)
2422  {
2423  $result = false;
2424 
2425  if (is_link($directory)) {
2426  // Avoid attempting to rename the symlink see #87367
2427  $directory = realpath($directory);
2428  }
2429 
2430  if (is_dir($directory)) {
2431  $temporaryDirectory = rtrim($directory, '/') . '.' . StringUtility::getUniqueId('remove');
2432  if (rename($directory, $temporaryDirectory)) {
2433  if ($flushOpcodeCache) {
2434  self::makeInstance(OpcodeCacheService::class)->clearAllActive($directory);
2435  }
2436  if ($keepOriginalDirectory) {
2437  static::mkdir($directory);
2438  }
2439  clearstatcache();
2440  $result = static::rmdir($temporaryDirectory, true);
2441  }
2442  }
2443 
2444  return $result;
2445  }
2446 
2455  public static function get_dirs($path)
2456  {
2457  $dirs = null;
2458  if ($path) {
2459  if (is_dir($path)) {
2460  $dir = scandir($path);
2461  $dirs = [];
2462  foreach ($dir as $entry) {
2463  if (is_dir($path . '/' . $entry) && $entry !== '..' && $entry !== '.') {
2464  $dirs[] = $entry;
2465  }
2466  }
2467  } else {
2468  $dirs = 'error';
2469  }
2470  }
2471  return $dirs;
2472  }
2473 
2486  public static function getFilesInDir($path, $extensionList = '', $prependPath = false, $order = '', $excludePattern = '')
2487  {
2488  $excludePattern = (string)$excludePattern;
2489  $path = rtrim($path, '/');
2490  if (!@is_dir($path)) {
2491  return [];
2492  }
2493 
2494  $rawFileList = scandir($path);
2495  if ($rawFileList === false) {
2496  return 'error opening path: "' . $path . '"';
2497  }
2498 
2499  $pathPrefix = $path . '/';
2500  $allowedFileExtensionArray = self::trimExplode(',', $extensionList);
2501  $extensionList = ',' . str_replace(' ', '', $extensionList) . ',';
2502  $files = [];
2503  foreach ($rawFileList as $entry) {
2504  $completePathToEntry = $pathPrefix . $entry;
2505  if (!@is_file($completePathToEntry)) {
2506  continue;
2507  }
2508 
2509  foreach ($allowedFileExtensionArray as $allowedFileExtension) {
2510  if (
2511  ($extensionList === ',,' || stripos($extensionList, ',' . substr($entry, strlen($allowedFileExtension)*-1, strlen($allowedFileExtension)) . ',') !== false)
2512  && ($excludePattern === '' || !preg_match(('/^' . $excludePattern . '$/'), $entry))
2513  ) {
2514  if ($order !== 'mtime') {
2515  $files[] = $entry;
2516  } else {
2517  // Store the value in the key so we can do a fast asort later.
2518  $files[$entry] = filemtime($completePathToEntry);
2519  }
2520  }
2521  }
2522  }
2523 
2524  $valueName = 'value';
2525  if ($order === 'mtime') {
2526  asort($files);
2527  $valueName = 'key';
2528  }
2529 
2530  $valuePathPrefix = $prependPath ? $pathPrefix : '';
2531  $foundFiles = [];
2532  foreach ($files as $key => $value) {
2533  // Don't change this ever - extensions may depend on the fact that the hash is an md5 of the path! (import/export extension)
2534  $foundFiles[md5($pathPrefix . ${$valueName})] = $valuePathPrefix . ${$valueName};
2535  }
2536 
2537  return $foundFiles;
2538  }
2539 
2551  public static function getAllFilesAndFoldersInPath(array $fileArr, $path, $extList = '', $regDirs = false, $recursivityLevels = 99, $excludePattern = '')
2552  {
2553  if ($regDirs) {
2554  $fileArr[md5($path)] = $path;
2555  }
2556  $fileArr = array_merge($fileArr, self::getFilesInDir($path, $extList, 1, 1, $excludePattern));
2557  $dirs = self::get_dirs($path);
2558  if ($recursivityLevels > 0 && is_array($dirs)) {
2559  foreach ($dirs as $subdirs) {
2560  if ((string)$subdirs !== '' && ($excludePattern === '' || !preg_match(('/^' . $excludePattern . '$/'), $subdirs))) {
2561  $fileArr = self::getAllFilesAndFoldersInPath($fileArr, $path . $subdirs . '/', $extList, $regDirs, $recursivityLevels - 1, $excludePattern);
2562  }
2563  }
2564  }
2565  return $fileArr;
2566  }
2567 
2575  public static function removePrefixPathFromList(array $fileArr, $prefixToRemove)
2576  {
2577  foreach ($fileArr as $k => &$absFileRef) {
2578  if (self::isFirstPartOfStr($absFileRef, $prefixToRemove)) {
2579  $absFileRef = substr($absFileRef, strlen($prefixToRemove));
2580  } else {
2581  return 'ERROR: One or more of the files was NOT prefixed with the prefix-path!';
2582  }
2583  }
2584  unset($absFileRef);
2585  return $fileArr;
2586  }
2587 
2594  public static function fixWindowsFilePath($theFile)
2595  {
2596  return str_replace(['\\', '//'], '/', $theFile);
2597  }
2598 
2606  public static function resolveBackPath($pathStr)
2607  {
2608  if (strpos($pathStr, '..') === false) {
2609  return $pathStr;
2610  }
2611  $parts = explode('/', $pathStr);
2612  $output = [];
2613  $c = 0;
2614  foreach ($parts as $part) {
2615  if ($part === '..') {
2616  if ($c) {
2617  array_pop($output);
2618  --$c;
2619  } else {
2620  $output[] = $part;
2621  }
2622  } else {
2623  ++$c;
2624  $output[] = $part;
2625  }
2626  }
2627  return implode('/', $output);
2628  }
2629 
2639  public static function locationHeaderUrl($path)
2640  {
2641  if (strpos($path, '//') === 0) {
2642  return $path;
2643  }
2644 
2645  // relative to HOST
2646  if (strpos($path, '/') === 0) {
2647  return self::getIndpEnv('TYPO3_REQUEST_HOST') . $path;
2648  }
2649 
2650  $urlComponents = parse_url($path);
2651  if (!($urlComponents['scheme'] ?? false)) {
2652  // No scheme either
2653  return self::getIndpEnv('TYPO3_REQUEST_DIR') . $path;
2654  }
2655 
2656  return $path;
2657  }
2658 
2666  public static function getMaxUploadFileSize()
2667  {
2668  // Check for PHP restrictions of the maximum size of one of the $_FILES
2669  $phpUploadLimit = self::getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
2670  // Check for PHP restrictions of the maximum $_POST size
2671  $phpPostLimit = self::getBytesFromSizeMeasurement(ini_get('post_max_size'));
2672  // If the total amount of post data is smaller (!) than the upload_max_filesize directive,
2673  // then this is the real limit in PHP
2674  $phpUploadLimit = $phpPostLimit > 0 && $phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit;
2675  return floor(($phpUploadLimit)) / 1024;
2676  }
2677 
2684  public static function getBytesFromSizeMeasurement($measurement)
2685  {
2686  $bytes = (float)$measurement;
2687  if (stripos($measurement, 'G')) {
2688  $bytes *= 1024 * 1024 * 1024;
2689  } elseif (stripos($measurement, 'M')) {
2690  $bytes *= 1024 * 1024;
2691  } elseif (stripos($measurement, 'K')) {
2692  $bytes *= 1024;
2693  }
2694  return $bytes;
2695  }
2696 
2703  public static function getMaximumPathLength()
2704  {
2705  static::logDeprecatedFunction();
2706  return PHP_MAXPATHLEN;
2707  }
2708 
2725  public static function createVersionNumberedFilename($file)
2726  {
2727  $lookupFile = explode('?', $file);
2728  $path = self::resolveBackPath(self::dirname(PATH_thisScript) . '/' . $lookupFile[0]);
2729 
2730  $doNothing = false;
2731  if (TYPO3_MODE === 'FE') {
2732  $mode = strtolower($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']);
2733  if ($mode === 'embed') {
2734  $mode = true;
2735  } else {
2736  if ($mode === 'querystring') {
2737  $mode = false;
2738  } else {
2739  $doNothing = true;
2740  }
2741  }
2742  } else {
2743  $mode = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename'];
2744  }
2745  if ($doNothing || !file_exists($path)) {
2746  // File not found, return filename unaltered
2747  $fullName = $file;
2748  } else {
2749  if (!$mode) {
2750  // If use of .htaccess rule is not configured,
2751  // we use the default query-string method
2752  if ($lookupFile[1]) {
2753  $separator = '&';
2754  } else {
2755  $separator = '?';
2756  }
2757  $fullName = $file . $separator . filemtime($path);
2758  } else {
2759  // Change the filename
2760  $name = explode('.', $lookupFile[0]);
2761  $extension = array_pop($name);
2762  array_push($name, filemtime($path), $extension);
2763  $fullName = implode('.', $name);
2764  // Append potential query string
2765  $fullName .= $lookupFile[1] ? '?' . $lookupFile[1] : '';
2766  }
2767  }
2768  return $fullName;
2769  }
2770 
2771  /*************************
2772  *
2773  * SYSTEM INFORMATION
2774  *
2775  *************************/
2776 
2785  public static function linkThisScript(array $getParams = [])
2786  {
2787  $parts = self::getIndpEnv('SCRIPT_NAME');
2788  $params = self::_GET();
2789  foreach ($getParams as $key => $value) {
2790  if ($value !== '') {
2791  $params[$key] = $value;
2792  } else {
2793  unset($params[$key]);
2794  }
2795  }
2796  $pString = self::implodeArrayForUrl('', $params);
2797  return $pString ? $parts . '?' . ltrim($pString, '&') : $parts;
2798  }
2799 
2808  public static function linkThisUrl($url, array $getParams = [])
2809  {
2810  $parts = parse_url($url);
2811  $getP = [];
2812  if ($parts['query']) {
2813  parse_str($parts['query'], $getP);
2814  }
2815  ArrayUtility::mergeRecursiveWithOverrule($getP, $getParams);
2816  $uP = explode('?', $url);
2817  $params = self::implodeArrayForUrl('', $getP);
2818  $outurl = $uP[0] . ($params ? '?' . substr($params, 1) : '');
2819  return $outurl;
2820  }
2821 
2830  public static function getIndpEnv($getEnvName)
2831  {
2832  if (array_key_exists($getEnvName, self::$indpEnvCache)) {
2833  return self::$indpEnvCache[$getEnvName];
2834  }
2835 
2836  /*
2837  Conventions:
2838  output from parse_url():
2839  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
2840  [scheme] => 'http'
2841  [user] => 'username'
2842  [pass] => 'password'
2843  [host] => '192.168.1.4'
2844  [port] => '8080'
2845  [path] => '/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/'
2846  [query] => 'arg1,arg2,arg3&p1=parameter1&p2[key]=value'
2847  [fragment] => 'link1'Further definition: [path_script] = '/typo3/32/temp/phpcheck/index.php'
2848  [path_dir] = '/typo3/32/temp/phpcheck/'
2849  [path_info] = '/arg1/arg2/arg3/'
2850  [path] = [path_script/path_dir][path_info]Keys supported:URI______:
2851  REQUEST_URI = [path]?[query] = /typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
2852  HTTP_HOST = [host][:[port]] = 192.168.1.4:8080
2853  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')!
2854  PATH_INFO = [path_info] = /arg1/arg2/arg3/
2855  QUERY_STRING = [query] = arg1,arg2,arg3&p1=parameter1&p2[key]=value
2856  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
2857  (Notice: NO username/password + NO fragment)CLIENT____:
2858  REMOTE_ADDR = (client IP)
2859  REMOTE_HOST = (client host)
2860  HTTP_USER_AGENT = (client user agent)
2861  HTTP_ACCEPT_LANGUAGE = (client accept language)SERVER____:
2862  SCRIPT_FILENAME = Absolute filename of script (Differs between windows/unix). On windows 'C:\\blabla\\blabl\\' will be converted to 'C:/blabla/blabl/'Special extras:
2863  TYPO3_HOST_ONLY = [host] = 192.168.1.4
2864  TYPO3_PORT = [port] = 8080 (blank if 80, taken from host value)
2865  TYPO3_REQUEST_HOST = [scheme]://[host][:[port]]
2866  TYPO3_REQUEST_URL = [scheme]://[host][:[port]][path]?[query] (scheme will by default be "http" until we can detect something different)
2867  TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script]
2868  TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir]
2869  TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend
2870  TYPO3_SITE_PATH = [path_dir] of the TYPO3 website frontend
2871  TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website
2872  TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically)
2873  TYPO3_SSL = Returns TRUE if this session uses SSL/TLS (https)
2874  TYPO3_PROXY = Returns TRUE if this session runs over a well known proxyNotice: [fragment] is apparently NEVER available to the script!Testing suggestions:
2875  - Output all the values.
2876  - 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
2877  - ALSO TRY the script from the ROOT of a site (like 'http://www.mytest.com/' and not 'http://www.mytest.com/test/' !!)
2878  */
2879  $retVal = '';
2880  switch ((string)$getEnvName) {
2881  case 'SCRIPT_NAME':
2882  $retVal = self::isRunningOnCgiServerApi()
2883  && ($_SERVER['ORIG_PATH_INFO'] ?: $_SERVER['PATH_INFO'])
2884  ? ($_SERVER['ORIG_PATH_INFO'] ?: $_SERVER['PATH_INFO'])
2885  : ($_SERVER['ORIG_SCRIPT_NAME'] ?: $_SERVER['SCRIPT_NAME']);
2886  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
2887  if (self::cmpIP($_SERVER['REMOTE_ADDR'] ?? '', $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'] ?? '')) {
2888  if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
2889  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
2890  } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
2891  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
2892  }
2893  }
2894  break;
2895  case 'SCRIPT_FILENAME':
2896  $retVal = PATH_thisScript;
2897  break;
2898  case 'REQUEST_URI':
2899  // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REQUEST_URI'))
2900  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']) {
2901  // This is for URL rewriters that store the original URI in a server variable (eg ISAPI_Rewriter for IIS: HTTP_X_REWRITE_URL)
2902  list($v, $n) = explode('|', $GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']);
2903  $retVal = $GLOBALS[$v][$n];
2904  } elseif (!$_SERVER['REQUEST_URI']) {
2905  // This is for ISS/CGI which does not have the REQUEST_URI available.
2906  $retVal = '/' . ltrim(self::getIndpEnv('SCRIPT_NAME'), '/') . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : '');
2907  } else {
2908  $retVal = '/' . ltrim($_SERVER['REQUEST_URI'], '/');
2909  }
2910  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
2911  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
2912  if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
2913  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
2914  } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
2915  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
2916  }
2917  }
2918  break;
2919  case 'PATH_INFO':
2920  // $_SERVER['PATH_INFO'] != $_SERVER['SCRIPT_NAME'] is necessary because some servers (Windows/CGI)
2921  // are seen to set PATH_INFO equal to script_name
2922  // Further, there must be at least one '/' in the path - else the PATH_INFO value does not make sense.
2923  // IF 'PATH_INFO' never works for our purpose in TYPO3 with CGI-servers,
2924  // then 'PHP_SAPI=='cgi'' might be a better check.
2925  // Right now strcmp($_SERVER['PATH_INFO'], GeneralUtility::getIndpEnv('SCRIPT_NAME')) will always
2926  // return FALSE for CGI-versions, but that is only as long as SCRIPT_NAME is set equal to PATH_INFO
2927  // because of PHP_SAPI=='cgi' (see above)
2928  if (!self::isRunningOnCgiServerApi()) {
2929  $retVal = $_SERVER['PATH_INFO'];
2930  }
2931  break;
2932  case 'TYPO3_REV_PROXY':
2933  $retVal = self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']);
2934  break;
2935  case 'REMOTE_ADDR':
2936  $retVal = $_SERVER['REMOTE_ADDR'];
2937  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
2938  $ip = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2939  // Choose which IP in list to use
2940  if (!empty($ip)) {
2941  switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
2942  case 'last':
2943  $ip = array_pop($ip);
2944  break;
2945  case 'first':
2946  $ip = array_shift($ip);
2947  break;
2948  case 'none':
2949 
2950  default:
2951  $ip = '';
2952  }
2953  }
2954  if (self::validIP($ip)) {
2955  $retVal = $ip;
2956  }
2957  }
2958  break;
2959  case 'HTTP_HOST':
2960  // if it is not set we're most likely on the cli
2961  $retVal = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
2962  if (isset($_SERVER['REMOTE_ADDR']) && static::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
2963  $host = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
2964  // Choose which host in list to use
2965  if (!empty($host)) {
2966  switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
2967  case 'last':
2968  $host = array_pop($host);
2969  break;
2970  case 'first':
2971  $host = array_shift($host);
2972  break;
2973  case 'none':
2974 
2975  default:
2976  $host = '';
2977  }
2978  }
2979  if ($host) {
2980  $retVal = $host;
2981  }
2982  }
2983  if (!static::isAllowedHostHeaderValue($retVal)) {
2984  throw new \UnexpectedValueException(
2985  'The current host header value does not match the configured trusted hosts pattern! Check the pattern defined in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'trustedHostsPattern\'] and adapt it, if you want to allow the current host header \'' . $retVal . '\' for your installation.',
2986  1396795884
2987  );
2988  }
2989  break;
2990  case 'HTTP_REFERER':
2991 
2992  case 'HTTP_USER_AGENT':
2993 
2994  case 'HTTP_ACCEPT_ENCODING':
2995 
2996  case 'HTTP_ACCEPT_LANGUAGE':
2997 
2998  case 'REMOTE_HOST':
2999 
3000  case 'QUERY_STRING':
3001  if (isset($_SERVER[$getEnvName])) {
3002  $retVal = $_SERVER[$getEnvName];
3003  }
3004  break;
3005  case 'TYPO3_DOCUMENT_ROOT':
3006  // Get the web root (it is not the root of the TYPO3 installation)
3007  // The absolute path of the script can be calculated with TYPO3_DOCUMENT_ROOT + SCRIPT_FILENAME
3008  // 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.
3009  // Therefore the DOCUMENT_ROOT is now always calculated as the SCRIPT_FILENAME minus the end part shared with SCRIPT_NAME.
3010  $SFN = self::getIndpEnv('SCRIPT_FILENAME');
3011  $SN_A = explode('/', strrev(self::getIndpEnv('SCRIPT_NAME')));
3012  $SFN_A = explode('/', strrev($SFN));
3013  $acc = [];
3014  foreach ($SN_A as $kk => $vv) {
3015  if ((string)$SFN_A[$kk] === (string)$vv) {
3016  $acc[] = $vv;
3017  } else {
3018  break;
3019  }
3020  }
3021  $commonEnd = strrev(implode('/', $acc));
3022  if ((string)$commonEnd !== '') {
3023  $retVal = substr($SFN, 0, -(strlen($commonEnd) + 1));
3024  }
3025  break;
3026  case 'TYPO3_HOST_ONLY':
3027  $httpHost = self::getIndpEnv('HTTP_HOST');
3028  $httpHostBracketPosition = strpos($httpHost, ']');
3029  $httpHostParts = explode(':', $httpHost);
3030  $retVal = $httpHostBracketPosition !== false ? substr($httpHost, 0, $httpHostBracketPosition + 1) : array_shift($httpHostParts);
3031  break;
3032  case 'TYPO3_PORT':
3033  $httpHost = self::getIndpEnv('HTTP_HOST');
3034  $httpHostOnly = self::getIndpEnv('TYPO3_HOST_ONLY');
3035  $retVal = strlen($httpHost) > strlen($httpHostOnly) ? substr($httpHost, strlen($httpHostOnly) + 1) : '';
3036  break;
3037  case 'TYPO3_REQUEST_HOST':
3038  $retVal = (self::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://') . self::getIndpEnv('HTTP_HOST');
3039  break;
3040  case 'TYPO3_REQUEST_URL':
3041  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('REQUEST_URI');
3042  break;
3043  case 'TYPO3_REQUEST_SCRIPT':
3044  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('SCRIPT_NAME');
3045  break;
3046  case 'TYPO3_REQUEST_DIR':
3047  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/';
3048  break;
3049  case 'TYPO3_SITE_URL':
3050  $url = self::getIndpEnv('TYPO3_REQUEST_DIR');
3051  // This can only be set by external entry scripts
3052  if (defined('TYPO3_PATH_WEB')) {
3053  $retVal = $url;
3054  } elseif (defined('PATH_thisScript') && defined('PATH_site')) {
3055  $lPath = PathUtility::stripPathSitePrefix(dirname(PATH_thisScript)) . '/';
3056  $siteUrl = substr($url, 0, -strlen($lPath));
3057  if (substr($siteUrl, -1) !== '/') {
3058  $siteUrl .= '/';
3059  }
3060  $retVal = $siteUrl;
3061  }
3062  break;
3063  case 'TYPO3_SITE_PATH':
3064  $retVal = substr(self::getIndpEnv('TYPO3_SITE_URL'), strlen(self::getIndpEnv('TYPO3_REQUEST_HOST')));
3065  break;
3066  case 'TYPO3_SITE_SCRIPT':
3067  $retVal = substr(self::getIndpEnv('TYPO3_REQUEST_URL'), strlen(self::getIndpEnv('TYPO3_SITE_URL')));
3068  break;
3069  case 'TYPO3_SSL':
3070  $proxySSL = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL'] ?? null);
3071  if ($proxySSL === '*') {
3072  $proxySSL = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'];
3073  }
3074  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $proxySSL)) {
3075  $retVal = true;
3076  } else {
3077  // https://secure.php.net/manual/en/reserved.variables.server.php
3078  // "Set to a non-empty value if the script was queried through the HTTPS protocol."
3079  $retVal = $_SERVER['SSL_SESSION_ID']
3080  || (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off');
3081  }
3082  break;
3083  case '_ARRAY':
3084  $out = [];
3085  // Here, list ALL possible keys to this function for debug display.
3086  $envTestVars = [
3087  'HTTP_HOST',
3088  'TYPO3_HOST_ONLY',
3089  'TYPO3_PORT',
3090  'PATH_INFO',
3091  'QUERY_STRING',
3092  'REQUEST_URI',
3093  'HTTP_REFERER',
3094  'TYPO3_REQUEST_HOST',
3095  'TYPO3_REQUEST_URL',
3096  'TYPO3_REQUEST_SCRIPT',
3097  'TYPO3_REQUEST_DIR',
3098  'TYPO3_SITE_URL',
3099  'TYPO3_SITE_SCRIPT',
3100  'TYPO3_SSL',
3101  'TYPO3_REV_PROXY',
3102  'SCRIPT_NAME',
3103  'TYPO3_DOCUMENT_ROOT',
3104  'SCRIPT_FILENAME',
3105  'REMOTE_ADDR',
3106  'REMOTE_HOST',
3107  'HTTP_USER_AGENT',
3108  'HTTP_ACCEPT_LANGUAGE'
3109  ];
3110  foreach ($envTestVars as $v) {
3111  $out[$v] = self::getIndpEnv($v);
3112  }
3113  reset($out);
3114  $retVal = $out;
3115  break;
3116  }
3117  self::$indpEnvCache[$getEnvName] = $retVal;
3118  return $retVal;
3119  }
3120 
3129  public static function isAllowedHostHeaderValue($hostHeaderValue)
3130  {
3131  if (static::$allowHostHeaderValue === true) {
3132  return true;
3133  }
3134 
3135  if (static::isInternalRequestType()) {
3136  return static::$allowHostHeaderValue = true;
3137  }
3138 
3139  // Deny the value if trusted host patterns is empty, which means we are early in the bootstrap
3140  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'])) {
3141  return false;
3142  }
3143 
3144  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
3145  static::$allowHostHeaderValue = true;
3146  } else {
3147  static::$allowHostHeaderValue = static::hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue);
3148  }
3149 
3150  return static::$allowHostHeaderValue;
3151  }
3152 
3160  public static function hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue)
3161  {
3162  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME) {
3163  // Allow values that equal the server name
3164  // Note that this is only secure if name base virtual host are configured correctly in the webserver
3165  $defaultPort = self::getIndpEnv('TYPO3_SSL') ? '443' : '80';
3166  $parsedHostValue = parse_url('http://' . $hostHeaderValue);
3167  if (isset($parsedHostValue['port'])) {
3168  $hostMatch = (strtolower($parsedHostValue['host']) === strtolower($_SERVER['SERVER_NAME']) && (string)$parsedHostValue['port'] === $_SERVER['SERVER_PORT']);
3169  } else {
3170  $hostMatch = (strtolower($hostHeaderValue) === strtolower($_SERVER['SERVER_NAME']) && $defaultPort === $_SERVER['SERVER_PORT']);
3171  }
3172  } else {
3173  // In case name based virtual hosts are not possible, we allow setting a trusted host pattern
3174  // See https://typo3.org/teams/security/security-bulletins/typo3-core/typo3-core-sa-2014-001/ for further details
3175  $hostMatch = (bool)preg_match('/^' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] . '$/i', $hostHeaderValue);
3176  }
3177 
3178  return $hostMatch;
3179  }
3180 
3191  protected static function isInternalRequestType()
3192  {
3193  return !defined('TYPO3_REQUESTTYPE') || (defined('TYPO3_REQUESTTYPE') && TYPO3_REQUESTTYPE & (TYPO3_REQUESTTYPE_INSTALL | TYPO3_REQUESTTYPE_CLI));
3194  }
3195 
3201  public static function milliseconds()
3202  {
3203  return round(microtime(true) * 1000);
3204  }
3205 
3212  public static function clientInfo($useragent = '')
3213  {
3214  if (!$useragent) {
3215  $useragent = self::getIndpEnv('HTTP_USER_AGENT');
3216  }
3217  $bInfo = [];
3218  // Which browser?
3219  if (strpos($useragent, 'Konqueror') !== false) {
3220  $bInfo['BROWSER'] = 'konqu';
3221  } elseif (strpos($useragent, 'Opera') !== false) {
3222  $bInfo['BROWSER'] = 'opera';
3223  } elseif (strpos($useragent, 'MSIE') !== false) {
3224  $bInfo['BROWSER'] = 'msie';
3225  } elseif (strpos($useragent, 'Mozilla') !== false) {
3226  $bInfo['BROWSER'] = 'net';
3227  } elseif (strpos($useragent, 'Flash') !== false) {
3228  $bInfo['BROWSER'] = 'flash';
3229  }
3230  if (isset($bInfo['BROWSER'])) {
3231  // Browser version
3232  switch ($bInfo['BROWSER']) {
3233  case 'net':
3234  $bInfo['VERSION'] = (float)substr($useragent, 8);
3235  if (strpos($useragent, 'Netscape6/') !== false) {
3236  $bInfo['VERSION'] = (float)substr(strstr($useragent, 'Netscape6/'), 10);
3237  }
3238  // Will we ever know if this was a typo or intention...?! :-(
3239  if (strpos($useragent, 'Netscape/6') !== false) {
3240  $bInfo['VERSION'] = (float)substr(strstr($useragent, 'Netscape/6'), 10);
3241  }
3242  if (strpos($useragent, 'Netscape/7') !== false) {
3243  $bInfo['VERSION'] = (float)substr(strstr($useragent, 'Netscape/7'), 9);
3244  }
3245  break;
3246  case 'msie':
3247  $tmp = strstr($useragent, 'MSIE');
3248  $bInfo['VERSION'] = (float)preg_replace('/^[^0-9]*/', '', substr($tmp, 4));
3249  break;
3250  case 'opera':
3251  $tmp = strstr($useragent, 'Opera');
3252  $bInfo['VERSION'] = (float)preg_replace('/^[^0-9]*/', '', substr($tmp, 5));
3253  break;
3254  case 'konqu':
3255  $tmp = strstr($useragent, 'Konqueror/');
3256  $bInfo['VERSION'] = (float)substr($tmp, 10);
3257  break;
3258  }
3259  // Client system
3260  if (strpos($useragent, 'Win') !== false) {
3261  $bInfo['SYSTEM'] = 'win';
3262  } elseif (strpos($useragent, 'Mac') !== false) {
3263  $bInfo['SYSTEM'] = 'mac';
3264  } elseif (strpos($useragent, 'Linux') !== false || strpos($useragent, 'X11') !== false || strpos($useragent, 'SGI') !== false || strpos($useragent, ' SunOS ') !== false || strpos($useragent, ' HP-UX ') !== false) {
3265  $bInfo['SYSTEM'] = 'unix';
3266  }
3267  }
3268  return $bInfo;
3269  }
3270 
3277  public static function getHostname($requestHost = true)
3278  {
3279  $host = '';
3280  // If not called from the command-line, resolve on getIndpEnv()
3281  if ($requestHost && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
3282  $host = self::getIndpEnv('HTTP_HOST');
3283  }
3284  if (!$host) {
3285  // will fail for PHP 4.1 and 4.2
3286  $host = @php_uname('n');
3287  // 'n' is ignored in broken installations
3288  if (strpos($host, ' ')) {
3289  $host = '';
3290  }
3291  }
3292  // We have not found a FQDN yet
3293  if ($host && strpos($host, '.') === false) {
3294  $ip = gethostbyname($host);
3295  // We got an IP address
3296  if ($ip != $host) {
3297  $fqdn = gethostbyaddr($ip);
3298  if ($ip != $fqdn) {
3299  $host = $fqdn;
3300  }
3301  }
3302  }
3303  if (!$host) {
3304  $host = 'localhost.localdomain';
3305  }
3306  return $host;
3307  }
3308 
3309  /*************************
3310  *
3311  * TYPO3 SPECIFIC FUNCTIONS
3312  *
3313  *************************/
3325  public static function getFileAbsFileName($filename, $_ = null, $_2 = null)
3326  {
3327  if ((string)$filename === '') {
3328  return '';
3329  }
3330  if ($_ !== null) {
3331  self::deprecationLog('Parameter 2 of GeneralUtility::getFileAbsFileName is obsolete and can be omitted.');
3332  }
3333  if ($_2 !== null) {
3334  self::deprecationLog('Parameter 3 of GeneralUtility::getFileAbsFileName is obsolete and can be omitted.');
3335  }
3336 
3337  // Extension
3338  if (strpos($filename, 'EXT:') === 0) {
3339  list($extKey, $local) = explode('/', substr($filename, 4), 2);
3340  $filename = '';
3341  if ((string)$extKey !== '' && ExtensionManagementUtility::isLoaded($extKey) && (string)$local !== '') {
3342  $filename = ExtensionManagementUtility::extPath($extKey) . $local;
3343  }
3344  } elseif (!static::isAbsPath($filename)) {
3345  // is relative. Prepended with PATH_site
3346  $filename = PATH_site . $filename;
3347  } elseif (!static::isFirstPartOfStr($filename, PATH_site)) {
3348  // absolute, but set to blank if not allowed
3349  $filename = '';
3350  }
3351  if ((string)$filename !== '' && static::validPathStr($filename)) {
3352  // checks backpath.
3353  return $filename;
3354  }
3355  return '';
3356  }
3357 
3369  public static function validPathStr($theFile)
3370  {
3371  return strpos($theFile, '//') === false && strpos($theFile, '\\') === false
3372  && preg_match('#(?:^\\.\\.|/\\.\\./|[[:cntrl:]])#u', $theFile) === 0;
3373  }
3374 
3381  public static function isAbsPath($path)
3382  {
3383  if (substr($path, 0, 6) === 'vfs://') {
3384  return true;
3385  }
3386  return $path[0] === '/' || TYPO3_OS === 'WIN' && (strpos($path, ':/') === 1 || strpos($path, ':\\') === 1);
3387  }
3388 
3395  public static function isAllowedAbsPath($path)
3396  {
3397  if (substr($path, 0, 6) === 'vfs://') {
3398  return true;
3399  }
3400  $lockRootPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'];
3401  return static::isAbsPath($path) && static::validPathStr($path)
3402  && (static::isFirstPartOfStr($path, PATH_site)
3403  || $lockRootPath && static::isFirstPartOfStr($path, $lockRootPath));
3404  }
3405 
3415  public static function verifyFilenameAgainstDenyPattern($filename)
3416  {
3417  $pattern = '/[[:cntrl:]]/';
3418  if ((string)$filename !== '' && (string)$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] !== '') {
3419  $pattern = '/(?:[[:cntrl:]]|' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] . ')/iu';
3420  }
3421  return preg_match($pattern, $filename) === 0;
3422  }
3423 
3430  public static function copyDirectory($source, $destination)
3431  {
3432  if (strpos($source, PATH_site) === false) {
3433  $source = PATH_site . $source;
3434  }
3435  if (strpos($destination, PATH_site) === false) {
3436  $destination = PATH_site . $destination;
3437  }
3438  if (static::isAllowedAbsPath($source) && static::isAllowedAbsPath($destination)) {
3439  static::mkdir_deep($destination);
3440  $iterator = new \RecursiveIteratorIterator(
3441  new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
3442  \RecursiveIteratorIterator::SELF_FIRST
3443  );
3444  foreach ($iterator as $item) {
3445  $target = $destination . '/' . $iterator->getSubPathName();
3446  if ($item->isDir()) {
3447  static::mkdir($target);
3448  } else {
3449  static::upload_copy_move($item, $target);
3450  }
3451  }
3452  }
3453  }
3454 
3465  public static function sanitizeLocalUrl($url = '')
3466  {
3467  $sanitizedUrl = '';
3468  if (!empty($url)) {
3469  $decodedUrl = rawurldecode($url);
3470  $parsedUrl = parse_url($decodedUrl);
3471  $testAbsoluteUrl = self::resolveBackPath($decodedUrl);
3472  $testRelativeUrl = self::resolveBackPath(self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/' . $decodedUrl);
3473  // Pass if URL is on the current host:
3474  if (self::isValidUrl($decodedUrl)) {
3475  if (self::isOnCurrentHost($decodedUrl) && strpos($decodedUrl, self::getIndpEnv('TYPO3_SITE_URL')) === 0) {
3476  $sanitizedUrl = $url;
3477  }
3478  } elseif (self::isAbsPath($decodedUrl) && self::isAllowedAbsPath($decodedUrl)) {
3479  $sanitizedUrl = $url;
3480  } elseif (strpos($testAbsoluteUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && $decodedUrl[0] === '/') {
3481  $sanitizedUrl = $url;
3482  } elseif (empty($parsedUrl['scheme']) && strpos($testRelativeUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0
3483  && $decodedUrl[0] !== '/' && strpbrk($decodedUrl, '*:|"<>') === false && strpos($decodedUrl, '\\\\') === false
3484  ) {
3485  $sanitizedUrl = $url;
3486  }
3487  }
3488  if (!empty($url) && empty($sanitizedUrl)) {
3489  self::sysLog('The URL "' . $url . '" is not considered to be local and was denied.', 'core', self::SYSLOG_SEVERITY_NOTICE);
3490  }
3491  return $sanitizedUrl;
3492  }
3493 
3502  public static function upload_copy_move($source, $destination)
3503  {
3504  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'] ?? null)) {
3505  $params = ['source' => $source, 'destination' => $destination, 'method' => 'upload_copy_move'];
3506  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'] as $hookMethod) {
3507  $fakeThis = false;
3508  self::callUserFunction($hookMethod, $params, $fakeThis);
3509  }
3510  }
3511 
3512  $result = false;
3513  if (is_uploaded_file($source)) {
3514  // Return the value of move_uploaded_file, and if FALSE the temporary $source is still
3515  // around so the user can use unlink to delete it:
3516  $result = move_uploaded_file($source, $destination);
3517  } else {
3518  @copy($source, $destination);
3519  }
3520  // Change the permissions of the file
3521  self::fixPermissions($destination);
3522  // If here the file is copied and the temporary $source is still around,
3523  // so when returning FALSE the user can try unlink to delete the $source
3524  return $result;
3525  }
3526 
3536  public static function upload_to_tempfile($uploadedFileName)
3537  {
3538  if (is_uploaded_file($uploadedFileName)) {
3539  $tempFile = self::tempnam('upload_temp_');
3540  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'] ?? null)) {
3541  $params = ['source' => $uploadedFileName, 'destination' => $tempFile, 'method' => 'upload_to_tempfile'];
3542  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'] as $hookMethod) {
3543  $fakeThis = false;
3544  self::callUserFunction($hookMethod, $params, $fakeThis);
3545  }
3546  }
3547 
3548  move_uploaded_file($uploadedFileName, $tempFile);
3549  return @is_file($tempFile) ? $tempFile : '';
3550  }
3551  }
3552 
3562  public static function unlink_tempfile($uploadedTempFileName)
3563  {
3564  if ($uploadedTempFileName) {
3565  $uploadedTempFileName = self::fixWindowsFilePath($uploadedTempFileName);
3566  if (
3567  self::validPathStr($uploadedTempFileName)
3568  && self::isFirstPartOfStr($uploadedTempFileName, PATH_site . 'typo3temp/')
3569  && @is_file($uploadedTempFileName)
3570  ) {
3571  if (unlink($uploadedTempFileName)) {
3572  return true;
3573  }
3574  }
3575  }
3576  }
3577 
3588  public static function tempnam($filePrefix, $fileSuffix = '')
3589  {
3590  $temporaryPath = PATH_site . 'typo3temp/var/transient/';
3591  if (!is_dir($temporaryPath)) {
3592  self::mkdir_deep($temporaryPath);
3593  }
3594  if ($fileSuffix === '') {
3595  $tempFileName = $temporaryPath . basename(tempnam($temporaryPath, $filePrefix));
3596  } else {
3597  do {
3598  $tempFileName = $temporaryPath . $filePrefix . mt_rand(1, PHP_INT_MAX) . $fileSuffix;
3599  } while (file_exists($tempFileName));
3600  touch($tempFileName);
3601  clearstatcache(null, $tempFileName);
3602  }
3603  return $tempFileName;
3604  }
3605 
3614  public static function stdAuthCode($uid_or_record, $fields = '', $codeLength = 8)
3615  {
3616  if (is_array($uid_or_record)) {
3617  $recCopy_temp = [];
3618  if ($fields) {
3619  $fieldArr = self::trimExplode(',', $fields, true);
3620  foreach ($fieldArr as $k => $v) {
3621  $recCopy_temp[$k] = $uid_or_record[$v];
3622  }
3623  } else {
3624  $recCopy_temp = $uid_or_record;
3625  }
3626  $preKey = implode('|', $recCopy_temp);
3627  } else {
3628  $preKey = $uid_or_record;
3629  }
3630  $authCode = $preKey . '||' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
3631  $authCode = substr(md5($authCode), 0, $codeLength);
3632  return $authCode;
3633  }
3634 
3641  public static function hideIfNotTranslated($l18n_cfg_fieldValue)
3642  {
3643  return $GLOBALS['TYPO3_CONF_VARS']['FE']['hidePagesIfNotTranslatedByDefault'] xor ($l18n_cfg_fieldValue & 2);
3644  }
3645 
3653  public static function hideIfDefaultLanguage($localizationConfiguration)
3654  {
3655  return (bool)($localizationConfiguration & 1);
3656  }
3657 
3666  public static function llXmlAutoFileName($fileRef, $language, $sameLocation = false)
3667  {
3668  // If $fileRef is already prefixed with "[language key]" then we should return it as is
3669  $fileName = basename($fileRef);
3670  if (self::isFirstPartOfStr($fileName, $language . '.')) {
3671  return $fileRef;
3672  }
3673 
3674  if ($sameLocation) {
3675  return str_replace($fileName, $language . '.' . $fileName, $fileRef);
3676  }
3677 
3678  // Analyse file reference:
3679  // Is system:
3680  if (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'sysext/')) {
3681  $validatedPrefix = PATH_typo3 . 'sysext/';
3682  } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'ext/')) {
3683  // Is global:
3684  $validatedPrefix = PATH_typo3 . 'ext/';
3685  } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3conf . 'ext/')) {
3686  // Is local:
3687  $validatedPrefix = PATH_typo3conf . 'ext/';
3688  } else {
3689  $validatedPrefix = '';
3690  }
3691  if ($validatedPrefix) {
3692  // Divide file reference into extension key, directory (if any) and base name:
3693  list($file_extKey, $file_extPath) = explode('/', substr($fileRef, strlen($validatedPrefix)), 2);
3694  $temp = self::revExplode('/', $file_extPath, 2);
3695  if (count($temp) === 1) {
3696  array_unshift($temp, '');
3697  }
3698  // Add empty first-entry if not there.
3699  list($file_extPath, $file_fileName) = $temp;
3700  // The filename is prefixed with "[language key]." because it prevents the llxmltranslate tool from detecting it.
3701  $location = 'typo3conf/l10n/' . $language . '/' . $file_extKey . '/' . ($file_extPath ? $file_extPath . '/' : '');
3702  return $location . $language . '.' . $file_fileName;
3703  }
3704  return null;
3705  }
3706 
3716  public static function resolveSheetDefInDS($dataStructArray, $sheet = 'sDEF')
3717  {
3718  self::logDeprecatedFunction();
3719  if (!is_array($dataStructArray)) {
3720  return 'Data structure must be an array';
3721  }
3722  if (is_array($dataStructArray['sheets'])) {
3723  $singleSheet = false;
3724  if (!isset($dataStructArray['sheets'][$sheet])) {
3725  $sheet = 'sDEF';
3726  }
3727  $dataStruct = $dataStructArray['sheets'][$sheet];
3728  // If not an array, but still set, then regard it as a relative reference to a file:
3729  if ($dataStruct && !is_array($dataStruct)) {
3730  $file = self::getFileAbsFileName($dataStruct);
3731  if ($file && @is_file($file)) {
3732  $dataStruct = self::xml2array(file_get_contents($file));
3733  }
3734  }
3735  } else {
3736  $singleSheet = true;
3737  $dataStruct = $dataStructArray;
3738  if (isset($dataStruct['meta'])) {
3739  unset($dataStruct['meta']);
3740  }
3741  // Meta data should not appear there.
3742  // Default sheet
3743  $sheet = 'sDEF';
3744  }
3745  return [$dataStruct, $sheet, $singleSheet];
3746  }
3747 
3756  public static function resolveAllSheetsInDS(array $dataStructArray)
3757  {
3758  self::logDeprecatedFunction();
3759  if (is_array($dataStructArray['sheets'])) {
3760  $out = ['sheets' => []];
3761  foreach ($dataStructArray['sheets'] as $sheetId => $sDat) {
3762  list($ds, $aS) = self::resolveSheetDefInDS($dataStructArray, $sheetId);
3763  if ($sheetId == $aS) {
3764  $out['sheets'][$aS] = $ds;
3765  }
3766  }
3767  } else {
3768  list($ds) = self::resolveSheetDefInDS($dataStructArray);
3769  $out = ['sheets' => ['sDEF' => $ds]];
3770  }
3771  return $out;
3772  }
3773 
3786  public static function callUserFunction($funcName, &$params, &$ref, $_ = '', $errorMode = 0)
3787  {
3788  $content = false;
3789  // Check if we're using a closure and invoke it directly.
3790  if (is_object($funcName) && is_a($funcName, 'Closure')) {
3791  return call_user_func_array($funcName, [&$params, &$ref]);
3792  }
3793  $funcName = trim($funcName);
3794  // Check persistent object and if found, call directly and exit.
3795  if (isset($GLOBALS['T3_VAR']['callUserFunction'][$funcName]) && is_array($GLOBALS['T3_VAR']['callUserFunction'][$funcName])) {
3796  return call_user_func_array([
3797  &$GLOBALS['T3_VAR']['callUserFunction'][$funcName]['obj'],
3798  $GLOBALS['T3_VAR']['callUserFunction'][$funcName]['method']
3799  ], [&$params, &$ref]);
3800  }
3801  // Check file-reference prefix; if found, require_once() the file (should be library of code)
3802  if (strpos($funcName, ':') !== false) {
3803  // @deprecated since TYPO3 v8, will be removed in v9
3804  self::deprecationLog('Using file references to resolve "' . $funcName . '" has been deprecated in TYPO3 v8 '
3805  . 'when calling GeneralUtility::callUserFunction(), make sure the class is available via the class loader. '
3806  . 'This functionality will be removed in TYPO3 v9.');
3807  list($file, $funcRef) = self::revExplode(':', $funcName, 2);
3808  $requireFile = self::getFileAbsFileName($file);
3809  if ($requireFile) {
3810  require_once $requireFile;
3811  }
3812  } else {
3813  $funcRef = $funcName;
3814  }
3815  // Check for persistent object token, "&"
3816  if ($funcRef[0] === '&') {
3817  self::deprecationLog('Using the persistent object token "&" when resolving "' . $funcRef . '" for '
3818  . 'GeneralUtility::callUserFunc() is deprecated since TYPO3 v8. Make sure to implement '
3819  . 'SingletonInterface to achieve the same functionality. This functionality will be removed in TYPO3 v9 '
3820  . 'and will then result in a fatal PHP error.');
3821  $funcRef = substr($funcRef, 1);
3822  $storePersistentObject = true;
3823  } else {
3824  $storePersistentObject = false;
3825  }
3826  // Call function or method:
3827  $parts = explode('->', $funcRef);
3828  if (count($parts) === 2) {
3829  // Class
3830  // Check if class/method exists:
3831  if (class_exists($parts[0])) {
3832  // Get/Create object of class:
3833  if ($storePersistentObject) {
3834  // Get reference to current instance of class:
3835  if (!is_object($GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]])) {
3836  $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]] = self::makeInstance($parts[0]);
3837  }
3838  $classObj = $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]];
3839  } else {
3840  // Create new object:
3841  $classObj = self::makeInstance($parts[0]);
3842  }
3843  if (method_exists($classObj, $parts[1])) {
3844  // If persistent object should be created, set reference:
3845  if ($storePersistentObject) {
3846  $GLOBALS['T3_VAR']['callUserFunction'][$funcName] = [
3847  'method' => $parts[1],
3848  'obj' => &$classObj
3849  ];
3850  }
3851  // Call method:
3852  $content = call_user_func_array([&$classObj, $parts[1]], [&$params, &$ref]);
3853  } else {
3854  $errorMsg = 'No method name \'' . $parts[1] . '\' in class ' . $parts[0];
3855  if ($errorMode == 2) {
3856  throw new \InvalidArgumentException($errorMsg, 1294585865);
3857  }
3858  if (!$errorMode) {
3859  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
3860  }
3861  }
3862  } else {
3863  $errorMsg = 'No class named ' . $parts[0];
3864  if ($errorMode == 2) {
3865  throw new \InvalidArgumentException($errorMsg, 1294585866);
3866  }
3867  if (!$errorMode) {
3868  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
3869  }
3870  }
3871  } else {
3872  // Function
3873  if (function_exists($funcRef)) {
3874  $content = call_user_func_array($funcRef, [&$params, &$ref]);
3875  } else {
3876  $errorMsg = 'No function named: ' . $funcRef;
3877  if ($errorMode == 2) {
3878  throw new \InvalidArgumentException($errorMsg, 1294585867);
3879  }
3880  if (!$errorMode) {
3881  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
3882  }
3883  }
3884  }
3885  return $content;
3886  }
3887 
3905  public static function getUserObj($classRef)
3906  {
3907  // Check file-reference prefix; if found, require_once() the file (should be library of code)
3908  if (strpos($classRef, ':') !== false) {
3909  // @deprecated since TYPO3 v8, will be removed in v9
3910  self::deprecationLog('Using file references to resolve "' . $classRef . '" has been deprecated in TYPO3 v8 '
3911  . 'when calling GeneralUtility::getUserObj(), make sure the class is available via the class loader. '
3912  . 'This functionality will be removed in TYPO3 v9.');
3913  list($file, $classRef) = self::revExplode(':', $classRef, 2);
3914  $requireFile = self::getFileAbsFileName($file);
3915  if ($requireFile) {
3916  require_once $requireFile;
3917  }
3918  }
3919 
3920  // Check if class exists:
3921  if (class_exists($classRef)) {
3922  return self::makeInstance($classRef);
3923  }
3924  }
3925 
3945  public static function makeInstance($className, ...$constructorArguments)
3946  {
3947  if (!is_string($className) || empty($className)) {
3948  throw new \InvalidArgumentException('$className must be a non empty string.', 1288965219);
3949  }
3950  // Never instantiate with a beginning backslash, otherwise things like singletons won't work.
3951  if ($className[0] === '\\') {
3952  throw new \InvalidArgumentException(
3953  '$className "' . $className . '" must not start with a backslash.',
3954  1420281366
3955  );
3956  }
3957  if (isset(static::$finalClassNameCache[$className])) {
3958  $finalClassName = static::$finalClassNameCache[$className];
3959  } else {
3960  $finalClassName = self::getClassName($className);
3961  static::$finalClassNameCache[$className] = $finalClassName;
3962  }
3963  // Return singleton instance if it is already registered
3964  if (isset(self::$singletonInstances[$finalClassName])) {
3965  return self::$singletonInstances[$finalClassName];
3966  }
3967  // Return instance if it has been injected by addInstance()
3968  if (
3969  isset(self::$nonSingletonInstances[$finalClassName])
3970  && !empty(self::$nonSingletonInstances[$finalClassName])
3971  ) {
3972  return array_shift(self::$nonSingletonInstances[$finalClassName]);
3973  }
3974  // Create new instance and call constructor with parameters
3975  $instance = new $finalClassName(...$constructorArguments);
3976  // Register new singleton instance
3977  if ($instance instanceof SingletonInterface) {
3978  self::$singletonInstances[$finalClassName] = $instance;
3979  }
3980  return $instance;
3981  }
3982 
3990  protected static function getClassName($className)
3991  {
3992  if (class_exists($className)) {
3993  while (static::classHasImplementation($className)) {
3994  $className = static::getImplementationForClass($className);
3995  }
3996  }
3998  }
3999 
4006  protected static function getImplementationForClass($className)
4007  {
4008  return $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className'];
4009  }
4010 
4017  protected static function classHasImplementation($className)
4018  {
4019  // If we are early in the bootstrap, the configuration might not yet be present
4020  if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'])) {
4021  return false;
4022  }
4023 
4024  return isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className])
4025  && is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className])
4026  && !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className']);
4027  }
4028 
4046  public static function setSingletonInstance($className, SingletonInterface $instance)
4047  {
4048  self::checkInstanceClassName($className, $instance);
4049  self::$singletonInstances[$className] = $instance;
4050  }
4051 
4067  public static function removeSingletonInstance($className, SingletonInterface $instance)
4068  {
4069  self::checkInstanceClassName($className, $instance);
4070  if (!isset(self::$singletonInstances[$className])) {
4071  throw new \InvalidArgumentException('No Instance registered for ' . $className . '.', 1394099179);
4072  }
4073  if ($instance !== self::$singletonInstances[$className]) {
4074  throw new \InvalidArgumentException('The instance you are trying to remove has not been registered before.', 1394099256);
4075  }
4076  unset(self::$singletonInstances[$className]);
4077  }
4078 
4092  public static function resetSingletonInstances(array $newSingletonInstances)
4093  {
4094  static::$singletonInstances = [];
4095  foreach ($newSingletonInstances as $className => $instance) {
4096  static::setSingletonInstance($className, $instance);
4097  }
4098  }
4099 
4112  public static function getSingletonInstances()
4113  {
4114  return static::$singletonInstances;
4115  }
4116 
4131  public static function addInstance($className, $instance)
4132  {
4133  self::checkInstanceClassName($className, $instance);
4134  if ($instance instanceof SingletonInterface) {
4135  throw new \InvalidArgumentException('$instance must not be an instance of TYPO3\\CMS\\Core\\SingletonInterface. ' . 'For setting singletons, please use setSingletonInstance.', 1288969325);
4136  }
4137  if (!isset(self::$nonSingletonInstances[$className])) {
4138  self::$nonSingletonInstances[$className] = [];
4139  }
4140  self::$nonSingletonInstances[$className][] = $instance;
4141  }
4142 
4151  protected static function checkInstanceClassName($className, $instance)
4152  {
4153  if ($className === '') {
4154  throw new \InvalidArgumentException('$className must not be empty.', 1288967479);
4155  }
4156  if (!$instance instanceof $className) {
4157  throw new \InvalidArgumentException('$instance must be an instance of ' . $className . ', but actually is an instance of ' . get_class($instance) . '.', 1288967686);
4158  }
4159  }
4160 
4171  public static function purgeInstances()
4172  {
4173  self::$singletonInstances = [];
4174  self::$nonSingletonInstances = [];
4175  }
4176 
4184  public static function flushInternalRuntimeCaches()
4185  {
4186  self::$indpEnvCache = [];
4187  self::$idnaStringCache = [];
4188  }
4189 
4199  public static function makeInstanceService($serviceType, $serviceSubType = '', $excludeServiceKeys = [])
4200  {
4201  $error = false;
4202  if (!is_array($excludeServiceKeys)) {
4203  $excludeServiceKeys = self::trimExplode(',', $excludeServiceKeys, true);
4204  }
4205  $requestInfo = [
4206  'requestedServiceType' => $serviceType,
4207  'requestedServiceSubType' => $serviceSubType,
4208  'requestedExcludeServiceKeys' => $excludeServiceKeys
4209  ];
4210  while ($info = ExtensionManagementUtility::findService($serviceType, $serviceSubType, $excludeServiceKeys)) {
4211  // provide information about requested service to service object
4212  $info = array_merge($info, $requestInfo);
4213  // Check persistent object and if found, call directly and exit.
4214  if (is_object($GLOBALS['T3_VAR']['makeInstanceService'][$info['className']])) {
4215  // update request info in persistent object
4216  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]->info = $info;
4217  // reset service and return object
4218  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]->reset();
4219  return $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']];
4220  }
4221  $obj = self::makeInstance($info['className']);
4222  if (is_object($obj)) {
4223  if (!@is_callable([$obj, 'init'])) {
4224  // use silent logging??? I don't think so.
4225  die('Broken service:' . DebugUtility::viewArray($info));
4226  }
4227  $obj->info = $info;
4228  // service available?
4229  if ($obj->init()) {
4230  // create persistent object
4231  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']] = $obj;
4232  return $obj;
4233  }
4234  $error = $obj->getLastErrorArray();
4235  unset($obj);
4236  }
4237 
4238  // deactivate the service
4239  ExtensionManagementUtility::deactivateService($info['serviceType'], $info['serviceKey']);
4240  }
4241  return $error;
4242  }
4243 
4251  public static function requireOnce($requireFile)
4252  {
4253  self::logDeprecatedFunction();
4254  // Needed for require_once
4255  global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
4256  require_once $requireFile;
4257  }
4258 
4267  public static function requireFile($requireFile)
4268  {
4269  self::logDeprecatedFunction();
4270  // Needed for require
4271  global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
4272  require $requireFile;
4273  }
4274 
4283  public static function makeRedirectUrl($inUrl, $l = 0, $index_script_url = '')
4284  {
4285  if (strlen($inUrl) > $l) {
4286  $md5 = substr(md5($inUrl), 0, 20);
4287  $connection = self::makeInstance(ConnectionPool::class)->getConnectionForTable('cache_md5params');
4288  $count = $connection->count(
4289  '*',
4290  'cache_md5params',
4291  ['md5hash' => $md5]
4292  );
4293  if (!$count) {
4294  $connection->insert(
4295  'cache_md5params',
4296  [
4297  'md5hash' => $md5,
4298  'tstamp' => $GLOBALS['EXEC_TIME'],
4299  'type' => 2,
4300  'params' => $inUrl
4301  ]
4302  );
4303  }
4304  $inUrl = ($index_script_url ?: self::getIndpEnv('TYPO3_REQUEST_DIR') . 'index.php') . '?RDCT=' . $md5;
4305  }
4306  return $inUrl;
4307  }
4308 
4317  public static function freetypeDpiComp($fontSize)
4318  {
4319  self::logDeprecatedFunction();
4320  // FreeType 2 always has 96 dpi.
4321  $dpi = 96.0;
4322  return $fontSize / $dpi * 72;
4323  }
4324 
4330  public static function initSysLog()
4331  {
4332  // For CLI logging name is <fqdn-hostname>:<TYPO3-path>
4333  if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
4334  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getHostname() . ':' . PATH_site;
4335  } else {
4336  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getIndpEnv('TYPO3_SITE_URL');
4337  }
4338  // Init custom logging
4339  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) {
4340  $params = ['initLog' => true];
4341  $fakeThis = false;
4342  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) {
4343  self::callUserFunction($hookMethod, $params, $fakeThis);
4344  }
4345  }
4346  // Init TYPO3 logging
4347  foreach (explode(';', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'], 2) as $log) {
4348  list($type, $destination) = explode(',', $log, 3);
4349  if ($type === 'syslog') {
4350  if (TYPO3_OS === 'WIN') {
4351  $facility = LOG_USER;
4352  } else {
4353  $facility = constant('LOG_' . strtoupper($destination));
4354  }
4355  openlog($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'], LOG_ODELAY, $facility);
4356  }
4357  }
4358  $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] = MathUtility::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'], 0, 4);
4359  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit'] = true;
4360  }
4361 
4372  public static function sysLog($msg, $extKey, $severity = 0)
4373  {
4374  $severity = MathUtility::forceIntegerInRange($severity, 0, 4);
4375  // Is message worth logging?
4376  if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] > $severity) {
4377  return;
4378  }
4379  // Initialize logging
4380  if (!$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit']) {
4381  self::initSysLog();
4382  }
4383  // Do custom logging
4384  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) {
4385  $params = ['msg' => $msg, 'extKey' => $extKey, 'backTrace' => debug_backtrace(), 'severity' => $severity];
4386  $fakeThis = false;
4387  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) {
4388  self::callUserFunction($hookMethod, $params, $fakeThis);
4389  }
4390  }
4391  // TYPO3 logging enabled?
4392  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog']) {
4393  return;
4394  }
4395  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
4396  $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
4397  // Use all configured logging options
4398  foreach (explode(';', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'], 2) as $log) {
4399  list($type, $destination, $level) = explode(',', $log, 4);
4400  // Is message worth logging for this log type?
4401  if ((int)$level > $severity) {
4402  continue;
4403  }
4404  $msgLine = ' - ' . $extKey . ': ' . $msg;
4405  // Write message to a file
4406  if ($type === 'file') {
4407  $file = fopen($destination, 'a');
4408  if ($file) {
4409  fwrite($file, date(($dateFormat . ' ' . $timeFormat)) . $msgLine . LF);
4410  fclose($file);
4411  self::fixPermissions($destination);
4412  }
4413  } elseif ($type === 'mail') {
4414  list($to, $from) = explode('/', $destination);
4415  if (!self::validEmail($from)) {
4416  $from = MailUtility::getSystemFrom();
4417  }
4419  $mail = self::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
4420  $mail->setTo($to)->setFrom($from)->setSubject('Warning - error in TYPO3 installation')->setBody('Host: ' . $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . LF . 'Extension: ' . $extKey . LF . 'Severity: ' . $severity . LF . LF . $msg);
4421  $mail->send();
4422  } elseif ($type === 'error_log') {
4423  error_log($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . $msgLine, 0);
4424  } elseif ($type === 'syslog') {
4425  $priority = [LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT];
4426  syslog($priority[(int)$severity], $msgLine);
4427  }
4428  }
4429  }
4430 
4444  public static function devLog($msg, $extKey, $severity = 0, $dataVar = false)
4445  {
4446  if ((bool)$GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_DLOG'] && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'])) {
4447  $params = ['msg' => $msg, 'extKey' => $extKey, 'severity' => $severity, 'dataVar' => $dataVar];
4448  $fakeThis = false;
4449  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'] as $hookMethod) {
4450  self::callUserFunction($hookMethod, $params, $fakeThis);
4451  }
4452  }
4453  }
4454 
4460  public static function deprecationLog($msg)
4461  {
4462  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) {
4463  return;
4464  }
4465  // Legacy values (no strict comparison, $log can be boolean, string or int)
4466  $log = $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'];
4467  if ($log === true || $log == '1') {
4468  $log = ['file'];
4469  } else {
4470  $log = self::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'], true);
4471  }
4472  $date = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ': ');
4473  if (in_array('file', $log) !== false) {
4474  // Write a longer message to the deprecation log
4475  $destination = static::getDeprecationLogFileName();
4476  $file = @fopen($destination, 'a');
4477  if ($file) {
4478  @fwrite($file, ($date . $msg . LF));
4479  @fclose($file);
4480  self::fixPermissions($destination);
4481  }
4482  }
4483  if (in_array('devlog', $log) !== false) {
4484  // Copy message also to the developer log
4485  self::devLog($msg, 'Core', self::SYSLOG_SEVERITY_WARNING);
4486  }
4487  // Do not use console in login screen
4488  if (in_array('console', $log) !== false && isset($GLOBALS['BE_USER']->user['uid'])) {
4489  DebugUtility::debug($msg, $date, 'Deprecation Log');
4490  }
4491  }
4492 
4517  public static function logDeprecatedViewHelperAttribute(string $property, RenderingContextInterface $renderingContext, string $additionalMessage = '')
4518  {
4519  $template = $renderingContext->getTemplatePaths()->resolveTemplateFileForControllerAndActionAndFormat(
4520  $renderingContext->getControllerName(),
4521  $renderingContext->getControllerAction()
4522  );
4523  $template = str_replace(PATH_site, '', $template);
4524  $message = [];
4525  $message[] = '[' . $template . ']';
4526  $message[] = 'The property "' . $property . '" has been marked as deprecated.';
4527  $message[] = $additionalMessage;
4528  $message[] = 'Please check also your partial and layout files of this template';
4529  self::deprecationLog(implode(' ', $message));
4530  }
4531 
4537  public static function getDeprecationLogFileName()
4538  {
4539  return PATH_typo3conf . 'deprecation_' . self::shortMD5((PATH_site . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) . '.log';
4540  }
4541 
4546  public static function logDeprecatedFunction()
4547  {
4548  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) {
4549  return;
4550  }
4551  $trail = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
4552  if ($trail[1]['type']) {
4553  $function = new \ReflectionMethod($trail[1]['class'], $trail[1]['function']);
4554  } else {
4555  $function = new \ReflectionFunction($trail[1]['function']);
4556  }
4557  $msg = '';
4558  if (preg_match('/@deprecated\\s+(.*)/', $function->getDocComment(), $match)) {
4559  $msg = $match[1];
4560  }
4561  // Write a longer message to the deprecation log: <function> <annotion> - <trace> (<source>)
4562  $logMsg = $trail[1]['class'] . $trail[1]['type'] . $trail[1]['function'];
4563  $logMsg .= '() - ' . $msg . ' - ' . DebugUtility::debugTrail();
4564  $logMsg .= ' (' . PathUtility::stripPathSitePrefix($function->getFileName()) . '#' . $function->getStartLine() . ')';
4565  self::deprecationLog($logMsg);
4566  }
4567 
4577  public static function arrayToLogString(array $arr, $valueList = [], $valueLength = 20)
4578  {
4579  $str = '';
4580  if (!is_array($valueList)) {
4581  $valueList = self::trimExplode(',', $valueList, true);
4582  }
4583  $valListCnt = count($valueList);
4584  foreach ($arr as $key => $value) {
4585  if (!$valListCnt || in_array($key, $valueList)) {
4586  $str .= (string)$key . trim(': ' . self::fixed_lgd_cs(str_replace(LF, '|', (string)$value), $valueLength)) . '; ';
4587  }
4588  }
4589  return $str;
4590  }
4591 
4601  public static function imageMagickCommand($command, $parameters, $path = '')
4602  {
4603  self::logDeprecatedFunction();
4604  return CommandUtility::imageMagickCommand($command, $parameters, $path);
4605  }
4606 
4616  public static function unQuoteFilenames($parameters, $unQuote = false)
4617  {
4618  $paramsArr = explode(' ', trim($parameters));
4619  // Whenever a quote character (") is found, $quoteActive is set to the element number inside of $params. A value of -1 means that there are not open quotes at the current position.
4620  $quoteActive = -1;
4621  foreach ($paramsArr as $k => $v) {
4622  if ($quoteActive > -1) {
4623  $paramsArr[$quoteActive] .= ' ' . $v;
4624  unset($paramsArr[$k]);
4625  if (substr($v, -1) === $paramsArr[$quoteActive][0]) {
4626  $quoteActive = -1;
4627  }
4628  } elseif (!trim($v)) {
4629  // Remove empty elements
4630  unset($paramsArr[$k]);
4631  } elseif (preg_match('/^(["\'])/', $v) && substr($v, -1) !== $v[0]) {
4632  $quoteActive = $k;
4633  }
4634  }
4635  if ($unQuote) {
4636  foreach ($paramsArr as $key => &$val) {
4637  $val = preg_replace('/(?:^"|"$)/', '', $val);
4638  $val = preg_replace('/(?:^\'|\'$)/', '', $val);
4639  }
4640  unset($val);
4641  }
4642  // Return reindexed array
4643  return array_values($paramsArr);
4644  }
4645 
4652  public static function quoteJSvalue($value)
4653  {
4654  return strtr(
4655  json_encode((string)$value, JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_TAG),
4656  [
4657  '"' => '\'',
4658  '\\\\' => '\\u005C',
4659  ' ' => '\\u0020',
4660  '!' => '\\u0021',
4661  '\\t' => '\\u0009',
4662  '\\n' => '\\u000A',
4663  '\\r' => '\\u000D'
4664  ]
4665  );
4666  }
4667 
4673  public static function flushOutputBuffers()
4674  {
4675  self::logDeprecatedFunction();
4676  $obContent = '';
4677  while ($content = ob_get_clean()) {
4678  $obContent .= $content;
4679  }
4680  // If previously a "Content-Encoding: whatever" has been set, we have to unset it
4681  if (!headers_sent()) {
4682  $headersList = headers_list();
4683  foreach ($headersList as $header) {
4684  // Split it up at the :
4685  list($key, $value) = self::trimExplode(':', $header, true);
4686  // Check if we have a Content-Encoding other than 'None'
4687  if (strtolower($key) === 'content-encoding' && strtolower($value) !== 'none') {
4688  header('Content-Encoding: None');
4689  break;
4690  }
4691  }
4692  }
4693  echo $obContent;
4694  }
4695 
4707  {
4708  if (is_null(static::$applicationContext)) {
4709  static::$applicationContext = $applicationContext;
4710  } else {
4711  throw new \RuntimeException('Trying to override applicationContext which has already been defined!', 1376084316);
4712  }
4713  }
4714 
4720  public static function getApplicationContext()
4721  {
4722  return static::$applicationContext;
4723  }
4724 
4729  public static function isRunningOnCgiServerApi()
4730  {
4731  return in_array(PHP_SAPI, self::$supportedCgiServerApis, true);
4732  }
4733 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
static createDirectoryPath($fullDirectoryPath)
static logDeprecatedViewHelperAttribute(string $property, RenderingContextInterface $renderingContext, string $additionalMessage='')
static compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt=true)
static imageMagickCommand($command, $parameters, $path='')
static minifyJavaScript($script, &$error='')
static unlink_tempfile($uploadedTempFileName)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static mkdir_deep($directory, $deepDirectory='')
static removeSingletonInstance($className, SingletonInterface $instance)
static arrayToLogString(array $arr, $valueList=[], $valueLength=20)
static addInstance($className, $instance)
static array2xml(array $array, $NSprefix='', $level=0, $docTag='phparray', $spaceInd=0, array $options=[], array $stackData=[])
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
static getFilesInDir($path, $extensionList='', $prependPath=false, $order='', $excludePattern='')
static flushDirectory($directory, $keepOriginalDirectory=false, $flushOpcodeCache=false)
static stdAuthCode($uid_or_record, $fields='', $codeLength=8)
static array2xml_cs(array $array, $docTag='phparray', array $options=[], $charset='')
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel='E_DEBUG')
static isFirstPartOfStr($str, $partStr)
static csvValues(array $row, $delim=',', $quote='"')
Definition: CsvUtility.php:79
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
static setSingletonInstance($className, SingletonInterface $instance)
static csvValues(array $row, $delim=',', $quote='"')
static resolveAllSheetsInDS(array $dataStructArray)
static writeFileToTypo3tempDir($filepath, $content)
static hmac($input, $additionalSecret='')
static getFileAbsFileName($filename, $_=null, $_2=null)
static generateRandomBytes($bytesToReturn)
static isAllowedHostHeaderValue($hostHeaderValue)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static hideIfDefaultLanguage($localizationConfiguration)
static llXmlAutoFileName($fileRef, $language, $sameLocation=false)
static verifyFilenameAgainstDenyPattern($filename)
static copyDirectory($source, $destination)
static linkThisScript(array $getParams=[])
static makeInstance($className,... $constructorArguments)
$fields
Definition: pages.php:4
static hideIfNotTranslated($l18n_cfg_fieldValue)
static _GETset($inputGet, $key='')
static imageMagickCommand($command, $parameters, $path='')
static resetSingletonInstances(array $newSingletonInstances)
static makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=[])
static debug($var='', $header='Debug', $group='Debug')
static getHostname($requestHost=true)
static split_fileref($fileNameWithPath)
static unQuoteFilenames($parameters, $unQuote=false)
static resolveSheetDefInDS($dataStructArray, $sheet='sDEF')
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static fixPermissions($path, $recursive=false)
static getAllFilesAndFoldersInPath(array $fileArr, $path, $extList='', $regDirs=false, $recursivityLevels=99, $excludePattern='')
static buildUrl(array $urlParts)
static getBytesFromSizeMeasurement($measurement)
static linkThisUrl($url, array $getParams=[])
static debugTrail($prependFileNames=false)
static upload_to_tempfile($uploadedFileName)
static tempnam($filePrefix, $fileSuffix='')
static hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue)
static xml2tree($string, $depth=999, $parserOptions=[])
static rmdir($path, $removeNonEmpty=false)
static explodeUrl2Array($string, $multidim=false)
static splitCalc($string, $operators)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static xml2array($string, $NSprefix='', $reportDocTag=false)
static fixed_lgd_cs($string, $chars, $appendString='...')
static findService($serviceType, $serviceSubType='', $excludeServiceKeys=[])
static formatSize($sizeInBytes, $labels='', $base=0)
static revExplode($delimiter, $string, $count=0)
static checkInstanceClassName($className, $instance)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static removePrefixPathFromList(array $fileArr, $prefixToRemove)
static xml2arrayProcess($string, $NSprefix='', $reportDocTag=false)
static slashJS($string, $extended=false, $char='\'')
static xmlRecompileFromStructValArray(array $vals)
static writeFile($file, $content, $changePermissions=false)
static makeRedirectUrl($inUrl, $l=0, $index_script_url='')
static uniqueList($in_list, $secondParameter=null)
static upload_copy_move($source, $destination)
static presetApplicationContext(ApplicationContext $applicationContext)