‪TYPO3CMS  ‪main
TypoScriptParser.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\Http\Message\ServerRequestInterface;
19 use Psr\Log\LoggerInterface;
20 use Psr\Log\LogLevel;
21 use Symfony\Component\Finder\Finder;
31 
38 {
44  public ‪$setup = [];
45 
51  protected ‪$raw;
52 
58  protected ‪$rawP;
59 
65  protected ‪$lastComment = '';
66 
72  protected ‪$commentSet = false;
73 
79  protected ‪$multiLineEnabled = false;
80 
86  protected ‪$multiLineObject = '';
87 
93  protected ‪$multiLineValue = [];
94 
100  protected ‪$inBrace = 0;
101 
108  protected ‪$lastConditionTrue = true;
109 
115  public ‪$sections = [];
116 
122  public ‪$sectionsMatch = [];
123 
129  public ‪$regComments = false;
130 
136  public ‪$regLinenumbers = false;
137 
143  public ‪$errors = [];
144 
150  public ‪$lineNumberOffset = 0;
151 
152  public function ‪__construct()
153  {
154  trigger_error('Class ' . __CLASS__ . ' will be removed with TYPO3 v13.0. Use the new parser structure instead.', E_USER_DEPRECATED);
155  }
156 
163  public function ‪parse($string, $matchObj = '')
164  {
165  $this->raw = explode(LF, $string);
166  $this->rawP = 0;
167  $pre = '[GLOBAL]';
168  while ($pre) {
169  if ($pre === '[]') {
170  $this->‪error('Empty condition is always false, this does not make sense. At line ' . ($this->lineNumberOffset + $this->rawP - 1), LogLevel::WARNING);
171  break;
172  }
173  $preUppercase = strtoupper($pre);
174  if ($pre[0] === '[' &&
175  ($preUppercase === '[GLOBAL]' ||
176  $preUppercase === '[END]' ||
177  !$this->lastConditionTrue && $preUppercase === '[ELSE]')
178  ) {
179  $pre = trim($this->‪parseSub($this->setup));
180  $this->lastConditionTrue = true;
181  } else {
182  // We're in a specific section. Therefore we log this section
183  $specificSection = $preUppercase !== '[ELSE]';
184  if ($specificSection) {
185  $this->sections[md5($pre)] = $pre;
186  }
187  if (is_object($matchObj) && $matchObj->match($pre)) {
188  if ($specificSection) {
189  $this->sectionsMatch[md5($pre)] = $pre;
190  }
191  $pre = trim($this->‪parseSub($this->setup));
192  $this->lastConditionTrue = true;
193  } else {
194  $pre = $this->‪nextDivider();
195  $this->lastConditionTrue = false;
196  }
197  }
198  }
199  if ($this->inBrace) {
200  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': The script is short of ' . $this->inBrace . ' end brace(s)', LogLevel::INFO);
201  }
202  if ($this->multiLineEnabled) {
203  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': A multiline value section is not ended with a parenthesis!', LogLevel::INFO);
204  }
205  $this->lineNumberOffset += count($this->raw) + 1;
206  }
207 
214  protected function ‪nextDivider()
215  {
216  while (isset($this->raw[$this->rawP])) {
217  $line = trim($this->raw[$this->rawP]);
218  $this->rawP++;
219  if ($line && $line[0] === '[') {
220  return $line;
221  }
222  }
223  return '';
224  }
225 
232  protected function ‪parseSub(array &‪$setup)
233  {
234  while (isset($this->raw[$this->rawP])) {
235  $line = ltrim($this->raw[$this->rawP]);
236  $this->rawP++;
237  // Set comment flag?
238  if (!$this->multiLineEnabled && str_starts_with($line, '/*')) {
239  $this->commentSet = true;
240  }
241  // If $this->multiLineEnabled we will go and get the line values here because we know, the first if() will be TRUE.
242  if (!$this->commentSet && ($line || $this->multiLineEnabled)) {
243  // If multiline is enabled. Escape by ')'
244  if ($this->multiLineEnabled) {
245  // Multiline ends...
246  if (!empty($line[0]) && $line[0] === ')') {
247  // Disable multiline
248  $this->multiLineEnabled = false;
249  $theValue = implode(LF, $this->multiLineValue);
250  if (str_contains($this->multiLineObject, '.')) {
251  // Set the value deeper.
252  $this->‪setVal($this->multiLineObject, ‪$setup, [$theValue]);
253  } else {
254  // Set value regularly
256  if ($this->lastComment && $this->regComments) {
257  ‪$setup[$this->multiLineObject . '..'] .= ‪$this->lastComment;
258  }
259  if ($this->regLinenumbers) {
260  ‪$setup[$this->multiLineObject . '.ln..'][] = $this->lineNumberOffset + $this->rawP - 1;
261  }
262  }
263  } else {
264  $this->multiLineValue[] = $this->raw[$this->rawP - 1];
265  }
266  } elseif ($this->inBrace === 0 && $line[0] === '[') {
267  if (substr(trim($line), -1, 1) !== ']') {
268  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': Invalid condition found, any condition must end with "]": ' . $line);
269  return $line;
270  }
271  return $line;
272  } else {
273  // Return if GLOBAL condition is set - no matter what.
274  if ($line[0] === '[' && stripos($line, '[GLOBAL]') !== false) {
275  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': On return to [GLOBAL] scope, the script was short of ' . $this->inBrace . ' end brace(s)', 1);
276  $this->inBrace = 0;
277  return $line;
278  }
279  if ($line[0] !== '}' && $line[0] !== '#' && $line[0] !== '/') {
280  // If not brace-end or comment
281  // Find object name string until we meet an operator
282  $varL = strcspn($line, "\t" . ' {=<>(');
283  // check for special ":=" operator
284  if ($varL > 0 && substr($line, $varL - 1, 2) === ':=') {
285  --$varL;
286  }
287  // also remove tabs after the object string name
288  $objStrName = substr($line, 0, $varL);
289  if ($objStrName !== '') {
290  $r = [];
291  if (preg_match('/[^[:alnum:]\\/_\\\\\\.:-]/i', $objStrName, $r)) {
292  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': Object Name String, "' . htmlspecialchars($objStrName) . '" contains invalid character "' . $r[0] . '". Must be alphanumeric or one of: "_:-/\\."');
293  } else {
294  $line = ltrim(substr($line, $varL));
295  if ($line === '') {
296  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': Object Name String, "' . htmlspecialchars($objStrName) . '" was not followed by any operator, =<>({');
297  } else {
298  // Checking for special TSparser properties (to change TS values at parsetime)
299  $match = [];
300  if ($line[0] === ':' && preg_match('/^:=\\s*([[:alpha:]]+)\\s*\\((.*)\\).*/', $line, $match)) {
301  $tsFunc = $match[1];
302  $tsFuncArg = $match[2];
303  $val = $this->‪getVal($objStrName, ‪$setup);
304  $tsFuncArg = str_replace(['\\\\', '\\n', '\\t'], ['\\', LF, "\t"], $tsFuncArg);
305  $newValue = $this->‪executeValueModifier($tsFunc, $tsFuncArg, $val[0]);
306  if (isset($newValue)) {
307  $line = '= ' . $newValue;
308  } else {
309  continue;
310  }
311  }
312  switch ($line[0]) {
313  case '=':
314  if (str_contains($objStrName, '.')) {
315  $value = [];
316  $value[0] = trim(substr($line, 1));
317  $this->‪setVal($objStrName, ‪$setup, $value);
318  } else {
319  ‪$setup[$objStrName] = trim(substr($line, 1));
320  if ($this->lastComment && $this->regComments) {
321  // Setting comment..
322  $matchingCommentKey = $objStrName . '..';
323  if (isset(‪$setup[$matchingCommentKey])) {
324  ‪$setup[$matchingCommentKey] .= ‪$this->lastComment;
325  } else {
326  ‪$setup[$matchingCommentKey] = ‪$this->lastComment;
327  }
328  }
329  if ($this->regLinenumbers) {
330  ‪$setup[$objStrName . '.ln..'][] = $this->lineNumberOffset + $this->rawP - 1;
331  }
332  }
333  break;
334  case '{':
335  $this->inBrace++;
336  if (str_contains($objStrName, '.')) {
337  $exitSig = $this->‪rollParseSub($objStrName, ‪$setup);
338  if ($exitSig) {
339  return $exitSig;
340  }
341  } else {
342  if (!isset(‪$setup[$objStrName . '.'])) {
343  ‪$setup[$objStrName . '.'] = [];
344  }
345  $exitSig = $this->‪parseSub($setup[$objStrName . '.']);
346  if ($exitSig) {
347  return $exitSig;
348  }
349  }
350  break;
351  case '(':
352  $this->multiLineObject = $objStrName;
353  $this->multiLineEnabled = true;
354  $this->multiLineValue = [];
355  break;
356  case '<':
357  $theVal = trim(substr($line, 1));
358  if (str_starts_with($theVal, '.')) {
359  $res = $this->‪getVal(substr($theVal, 1), ‪$setup);
360  } else {
361  $res = $this->‪getVal($theVal, $this->setup);
362  }
363  if ($res[0] === '') {
364  unset($res[0]);
365  }
366  if ($res[1] === []) {
367  unset($res[1]);
368  }
369  // unserialize(serialize(...)) may look stupid but is needed because of some reference issues.
370  // See forge issue #76919 and functional test hasFlakyReferences()
371  $this->‪setVal($objStrName, ‪$setup, unserialize(serialize($res), ['allowed_classes' => false]), true);
372  break;
373  case '>':
374  $this->‪setVal($objStrName, ‪$setup, 'UNSET');
375  break;
376  default:
377  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': Object Name String, "' . htmlspecialchars($objStrName) . '" was not followed by any operator, =<>({');
378  }
379  }
380  }
381  $this->lastComment = '';
382  }
383  } elseif ($line[0] === '}') {
384  $this->inBrace--;
385  $this->lastComment = '';
386  if ($this->inBrace < 0) {
387  $this->‪error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': An end brace is in excess.', LogLevel::INFO);
388  $this->inBrace = 0;
389  } else {
390  break;
391  }
392  } else {
393  // Comment. The comments are concatenated in this temporary string:
394  if ($this->regComments) {
395  $this->lastComment .= rtrim($line) . LF;
396  }
397  }
398  if (str_starts_with($line, '### ERROR')) {
399  $this->‪error(substr($line, 11));
400  }
401  }
402  }
403  // Unset comment
404  if ($this->commentSet) {
405  if (str_contains($line, '*/')) {
406  $this->commentSet = false;
407  }
408  }
409  }
410  return '';
411  }
412 
422  protected function ‪executeValueModifier($modifierName, $modifierArgument = null, $currentValue = null)
423  {
424  $modifierArgumentAsString = (string)$modifierArgument;
425  $currentValueAsString = (string)$currentValue;
426  $newValue = null;
427  switch ($modifierName) {
428  case 'prependString':
429  $newValue = $modifierArgumentAsString . $currentValueAsString;
430  break;
431  case 'appendString':
432  $newValue = $currentValueAsString . $modifierArgumentAsString;
433  break;
434  case 'removeString':
435  $newValue = str_replace($modifierArgumentAsString, '', $currentValueAsString);
436  break;
437  case 'replaceString':
438  $modifierArgumentArray = explode('|', $modifierArgumentAsString, 2);
439  $fromStr = $modifierArgumentArray[0] ?? '';
440  $toStr = $modifierArgumentArray[1] ?? '';
441  $newValue = str_replace($fromStr, $toStr, $currentValueAsString);
442  break;
443  case 'addToList':
444  $newValue = ($currentValueAsString !== '' ? $currentValueAsString . ',' : '') . $modifierArgumentAsString;
445  break;
446  case 'removeFromList':
447  $existingElements = ‪GeneralUtility::trimExplode(',', $currentValueAsString);
448  $removeElements = ‪GeneralUtility::trimExplode(',', $modifierArgumentAsString);
449  if (!empty($removeElements)) {
450  $newValue = implode(',', array_diff($existingElements, $removeElements));
451  }
452  break;
453  case 'uniqueList':
454  $elements = ‪GeneralUtility::trimExplode(',', $currentValueAsString);
455  $newValue = implode(',', array_unique($elements));
456  break;
457  case 'reverseList':
458  $elements = ‪GeneralUtility::trimExplode(',', $currentValueAsString);
459  $newValue = implode(',', array_reverse($elements));
460  break;
461  case 'sortList':
462  $elements = ‪GeneralUtility::trimExplode(',', $currentValueAsString);
463  $arguments = ‪GeneralUtility::trimExplode(',', $modifierArgumentAsString);
464  $arguments = array_map('strtolower', $arguments);
465  $sort_flags = SORT_REGULAR;
466  if (in_array('numeric', $arguments)) {
467  $sort_flags = SORT_NUMERIC;
468  // If the sorting modifier "numeric" is given, all values
469  // are checked and an exception is thrown if a non-numeric value is given
470  // otherwise there is a different behaviour between PHP7 and PHP 5.x
471  // See also the warning on http://us.php.net/manual/en/function.sort.php
472  foreach ($elements as $element) {
473  if (!is_numeric($element)) {
474  throw new \InvalidArgumentException('The list "' . $currentValueAsString . '" should be sorted numerically but contains a non-numeric value', 1438191758);
475  }
476  }
477  }
478  sort($elements, $sort_flags);
479  if (in_array('descending', $arguments)) {
480  $elements = array_reverse($elements);
481  }
482  $newValue = implode(',', $elements);
483  break;
484  case 'getEnv':
485  $environmentValue = getenv(trim($modifierArgumentAsString));
486  if ($environmentValue !== false) {
487  $newValue = $environmentValue;
488  }
489  break;
490  default:
491  if (isset(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$modifierName])) {
492  $hookMethod = ‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$modifierName];
493  $params = ['currentValue' => $currentValue, 'functionArgument' => $modifierArgument];
494  $fakeThis = null;
495  $newValue = GeneralUtility::callUserFunction($hookMethod, $params, $fakeThis);
496  } else {
497  ‪self::getLogger()->warning('Missing function definition for {modifier_name} on TypoScript', [
498  'modifier_name' => $modifierName,
499  ]);
500  }
501  }
502  return $newValue;
503  }
504 
514  protected function ‪rollParseSub($string, array &‪$setup)
515  {
516  if ((string)$string === '') {
517  return '';
518  }
519 
520  [$key, $remainingKey] = $this->‪parseNextKeySegment($string);
521  $key .= '.';
522  if (!isset(‪$setup[$key])) {
523  ‪$setup[$key] = [];
524  }
525  $exitSig = $remainingKey === ''
526  ? $this->‪parseSub($setup[$key])
527  : $this->‪rollParseSub($remainingKey, ‪$setup[$key]);
528  return $exitSig ?: '';
529  }
530 
539  public function ‪getVal($string, ‪$setup): array
540  {
541  $retArr = [
542  0 => '',
543  1 => [],
544  ];
545  if ((string)$string === '') {
546  return $retArr;
547  }
548 
549  [$key, $remainingKey] = $this->‪parseNextKeySegment($string);
550  $subKey = $key . '.';
551  if ($remainingKey === '') {
552  $retArr[0] = ‪$setup[$key] ?? $retArr[0];
553  $retArr[1] = ‪$setup[$subKey] ?? $retArr[1];
554  return $retArr;
555  }
556  if (isset(‪$setup[$subKey])) {
557  return $this->‪getVal($remainingKey, ‪$setup[$subKey]);
558  }
559 
560  return $retArr;
561  }
562 
571  protected function ‪setVal($string, array &‪$setup, $value, $wipeOut = false)
572  {
573  if ((string)$string === '') {
574  return;
575  }
576 
577  [$key, $remainingKey] = $this->‪parseNextKeySegment($string);
578  $subKey = $key . '.';
579  if ($remainingKey === '') {
580  if ($value === 'UNSET') {
581  unset(‪$setup[$key]);
582  unset(‪$setup[$subKey]);
583  if ($this->regLinenumbers) {
584  ‪$setup[$key . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1) . '>';
585  }
586  } else {
587  $lnRegisDone = 0;
588  if ($wipeOut) {
589  unset(‪$setup[$key]);
590  unset(‪$setup[$subKey]);
591  if ($this->regLinenumbers) {
592  ‪$setup[$key . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1) . '<';
593  $lnRegisDone = 1;
594  }
595  }
596  if (isset($value[0])) {
597  ‪$setup[$key] = $value[0];
598  }
599  if (isset($value[1])) {
600  ‪$setup[$subKey] = $value[1];
601  }
602  if ($this->lastComment && $this->regComments) {
603  ‪$setup[$key . '..'] = ‪$setup[$key . '..'] ?? '' . ‪$this->lastComment;
604  }
605  if ($this->regLinenumbers && !$lnRegisDone) {
606  ‪$setup[$key . '.ln..'][] = $this->lineNumberOffset + $this->rawP - 1;
607  }
608  }
609  } else {
610  if (!isset(‪$setup[$subKey])) {
611  ‪$setup[$subKey] = [];
612  }
613  $this->‪setVal($remainingKey, ‪$setup[$subKey], $value);
614  }
615  }
616 
628  protected function ‪parseNextKeySegment($key)
629  {
630  // if no dot is in the key, nothing to do
631  $dotPosition = strpos($key, '.');
632  if ($dotPosition === false) {
633  return [$key, ''];
634  }
635 
636  if (str_contains($key, '\\')) {
637  // backslashes are in the key, so we do further parsing
638 
639  while ($dotPosition !== false) {
640  if ($dotPosition > 0 && $key[$dotPosition - 1] !== '\\' || $dotPosition > 1 && $key[$dotPosition - 2] === '\\') {
641  break;
642  }
643  // escaped dot found, continue
644  $dotPosition = strpos($key, '.', $dotPosition + 1);
645  }
646 
647  if ($dotPosition === false) {
648  // no regular dot found
649  $keySegment = $key;
650  $remainingKey = '';
651  } else {
652  if ($dotPosition > 1 && $key[$dotPosition - 2] === '\\' && $key[$dotPosition - 1] === '\\') {
653  $keySegment = substr($key, 0, $dotPosition - 1);
654  } else {
655  $keySegment = substr($key, 0, $dotPosition);
656  }
657  $remainingKey = substr($key, $dotPosition + 1);
658  }
659 
660  // fix key segment by removing escape sequences
661  $keySegment = str_replace('\\.', '.', $keySegment);
662  } else {
663  // no backslash in the key, we're fine off
664  [$keySegment, $remainingKey] = explode('.', $key, 2);
665  }
666  return [$keySegment, $remainingKey];
667  }
668 
676  protected function ‪error($message, $logLevel = LogLevel::WARNING)
677  {
678  $this->‪getTimeTracker()->‪setTSlogMessage($message, $logLevel);
679  $this->errors[] = [$message, $logLevel, $this->rawP - 1, ‪$this->lineNumberOffset];
680  }
681 
693  public static function ‪checkIncludeLines($string, $cycle_counter = 1, $returnFiles = false, $parentFilenameOrPath = '')
694  {
695  $includedFiles = [];
696  if ($cycle_counter > 100) {
697  ‪self::getLogger()->warning('It appears like TypoScript code is looping over itself. Check your templates for "<INCLUDE_TYPOSCRIPT: ..." tags');
698  if ($returnFiles) {
699  return [
700  'typoscript' => '',
701  'files' => $includedFiles,
702  ];
703  }
704  return '
705 ###
706 ### ERROR: Recursion!
707 ###
708 ';
709  }
710 
711  // Return early if $string is invalid
712  if (!is_string($string) || empty($string)) {
713  return !$returnFiles
714  ? ''
715  : [
716  'typoscript' => '',
717  'files' => $includedFiles,
718  ]
719  ;
720  }
721 
722  $string = ‪StringUtility::removeByteOrderMark($string);
723 
724  // Checking for @import syntax imported files
725  $string = ‪self::addImportsFromExternalFiles($string, $cycle_counter, $returnFiles, $includedFiles, $parentFilenameOrPath);
726 
727  // If no tags found, no need to do slower preg_split
728  if (str_contains($string, '<INCLUDE_TYPOSCRIPT:')) {
729  $splitRegEx = '/\r?\n\s*<INCLUDE_TYPOSCRIPT:\s*(?i)source\s*=\s*"((?i)file|dir):\s*([^"]*)"(.*)>[\ \t]*/';
730  $parts = preg_split($splitRegEx, LF . $string . LF, -1, PREG_SPLIT_DELIM_CAPTURE);
731  $parts = is_array($parts) ? $parts : [];
732 
733  // First text part goes through
734  $newString = ($parts[0] ?? '') . LF;
735  $partCount = count($parts);
736  for ($i = 1; $i + 3 < $partCount; $i += 4) {
737  // $parts[$i] contains 'FILE' or 'DIR'
738  // $parts[$i+1] contains relative file or directory path to be included
739  // $parts[$i+2] optional properties of the INCLUDE statement
740  // $parts[$i+3] next part of the typoscript string (part in between include-tags)
741  $includeType = $parts[$i];
742  $filename = $parts[$i + 1];
743  $originalFilename = $filename;
744  $optionalProperties = $parts[$i + 2];
745  $tsContentsTillNextInclude = $parts[$i + 3];
746 
747  // Check condition
748  $matches = preg_split('#(?i)condition\\s*=\\s*"((?:\\\\\\\\|\\\\"|[^\\"])*)"(\\s*|>)#', $optionalProperties, 2, PREG_SPLIT_DELIM_CAPTURE);
749  $matches = is_array($matches) ? $matches : [];
750 
751  // If there was a condition
752  if (count($matches) > 1) {
753  // Unescape the condition
754  $condition = trim(stripslashes($matches[1]));
755  // If necessary put condition in square brackets
756  if ($condition[0] !== '[') {
757  $condition = '[' . $condition . ']';
758  }
759 
760  if ((‪$GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
761  && ‪ApplicationType::fromRequest(‪$GLOBALS['TYPO3_REQUEST'])->isFrontend()
762  ) {
763  $conditionMatcher = GeneralUtility::makeInstance(FrontendConditionMatcher::class);
764  } else {
765  $conditionMatcher = GeneralUtility::makeInstance(BackendConditionMatcher::class);
766  }
767 
768  // If it didn't match then proceed to the next include, but prepend next normal (not file) part to output string
769  if (!$conditionMatcher->match($condition)) {
770  $newString .= $tsContentsTillNextInclude . LF;
771  continue;
772  }
773  }
774 
775  // Resolve a possible relative paths if a parent file is given
776  if ($parentFilenameOrPath !== '' && $filename[0] === '.') {
777  $filename = ‪PathUtility::getAbsolutePathOfRelativeReferencedFileOrPath($parentFilenameOrPath, $filename);
778  }
779 
780  // There must be a line-break char after - not sure why this check is necessary, kept it for being 100% backwards compatible
781  // An empty string is also ok (means that the next line is also a valid include_typoscript tag)
782  if (!preg_match('/(^\\s*\\r?\\n|^$)/', $tsContentsTillNextInclude)) {
783  $newString .= ‪self::typoscriptIncludeError('Invalid characters after <INCLUDE_TYPOSCRIPT: source="' . $includeType . ':' . $filename . '">-tag (rest of line must be empty).');
784  } elseif (str_contains('..', $filename)) {
785  $newString .= ‪self::typoscriptIncludeError('Invalid filepath "' . $filename . '" (containing "..").');
786  } else {
787  switch (strtolower($includeType)) {
788  case 'file':
789  ‪self::includeFile($originalFilename, $cycle_counter, $returnFiles, $newString, $includedFiles, $optionalProperties, $parentFilenameOrPath);
790  break;
791  case 'dir':
792  ‪self::includeDirectory($originalFilename, $cycle_counter, $returnFiles, $newString, $includedFiles, $optionalProperties, $parentFilenameOrPath);
793  break;
794  default:
795  $newString .= ‪self::typoscriptIncludeError('No valid option for INCLUDE_TYPOSCRIPT source property (valid options are FILE or DIR)');
796  }
797  }
798  // Prepend next normal (not file) part to output string
799  $newString .= $tsContentsTillNextInclude . LF;
800 
801  // load default TypoScript for content rendering templates like
802  // fluid_styled_content if those have been included through f.e.
803  // <INCLUDE_TYPOSCRIPT: source="FILE:EXT:fluid_styled_content/Configuration/TypoScript/setup.typoscript">
804  if (str_starts_with(strtolower($filename), 'ext:')) {
805  $filePointerPathParts = explode('/', substr($filename, 4));
806 
807  // remove file part, determine whether to load setup or constants
808  [$includeType] = explode('.', (string)array_pop($filePointerPathParts));
809 
810  if (in_array($includeType, ['setup', 'constants'])) {
811  // adapt extension key to required format (no underscores)
812  $filePointerPathParts[0] = str_replace('_', '', $filePointerPathParts[0]);
813 
814  // load default TypoScript
815  $defaultTypoScriptKey = implode('/', $filePointerPathParts) . '/';
816  if (in_array($defaultTypoScriptKey, ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'], true)) {
817  $newString .= ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $includeType . '.']['defaultContentRendering'] ?? '';
818  }
819  }
820  }
821  }
822  // Add a line break before and after the included code in order to make sure that the parser always has a LF.
823  $string = LF . trim($newString) . LF;
824  }
825  // When all included files should get returned, simply return a compound array containing
826  // the TypoScript with all "includes" processed and the files which got included
827  if ($returnFiles) {
828  return [
829  'typoscript' => $string,
830  'files' => $includedFiles,
831  ];
832  }
833  return $string;
834  }
835 
846  protected static function ‪addImportsFromExternalFiles($typoScript, $cycleCounter, $returnFiles, &$includedFiles, &$parentFilenameOrPath)
847  {
848  // Check for new syntax "@import 'EXT:bennilove/Configuration/TypoScript/*'"
849  if (is_string($typoScript) && (str_contains($typoScript, '@import \'') || str_contains($typoScript, '@import "'))) {
850  $splitRegEx = '/\r?\n\s*@import\s[\'"]([^\'"]*)[\'"][\ \t]?/';
851  $parts = preg_split($splitRegEx, LF . $typoScript . LF, -1, PREG_SPLIT_DELIM_CAPTURE);
852  $parts = is_array($parts) ? $parts : [];
853  // First text part goes through
854  $newString = $parts[0] . LF;
855  $partCount = count($parts);
856  for ($i = 1; $i + 2 <= $partCount; $i += 2) {
857  $filename = $parts[$i];
858  $tsContentsTillNextInclude = $parts[$i + 1];
859  // Resolve a possible relative paths if a parent file is given
860  if ($parentFilenameOrPath !== '' && $filename[0] === '.') {
861  $filename = ‪PathUtility::getAbsolutePathOfRelativeReferencedFileOrPath($parentFilenameOrPath, $filename);
862  }
863  $newString .= ‪self::importExternalTypoScriptFile($filename, $cycleCounter, $returnFiles, $includedFiles);
864  // Prepend next normal (not file) part to output string
865  $newString .= $tsContentsTillNextInclude;
866  }
867  // Add a line break before and after the included code in order to make sure that the parser always has a LF.
868  $typoScript = LF . trim($newString) . LF;
869  }
870  return $typoScript;
871  }
872 
883  protected static function ‪importExternalTypoScriptFile($filename, $cycleCounter, $returnFiles, array &$includedFiles)
884  {
885  if (str_contains('..', $filename)) {
886  return ‪self::typoscriptIncludeError('Invalid filepath "' . $filename . '" (containing "..").');
887  }
888 
889  $content = '';
890  $absoluteFileName = GeneralUtility::getFileAbsFileName($filename);
891  if ((string)$absoluteFileName === '') {
892  return ‪self::typoscriptIncludeError('Illegal filepath "' . $filename . '".');
893  }
894 
895  ‪$finder = new Finder();
896  ‪$finder
897  // no recursive mode on purpose
898  ->depth(0)
899  // no directories should be fetched
900  ->files()
901  ->sortByName();
902 
903  // Search all files in the folder
904  if (is_dir($absoluteFileName)) {
905  ‪$finder
906  ->in($absoluteFileName)
907  ->name('*.typoscript');
908  // Used for the TypoScript comments
909  $readableFilePrefix = $filename;
910  } else {
911  try {
912  // Apparently this is not a folder, so the restriction
913  // is the folder so we restrict into this folder
914  ‪$finder->in(‪PathUtility::dirname($absoluteFileName));
915  if (!is_file($absoluteFileName)
916  && !str_contains(‪PathUtility::basename($absoluteFileName), '*')
917  && substr(‪PathUtility::basename($absoluteFileName), -11) !== '.typoscript') {
918  $absoluteFileName .= '*.typoscript';
919  }
920  ‪$finder->name(‪PathUtility::basename($absoluteFileName));
921  $readableFilePrefix = ‪PathUtility::dirname($filename);
922  } catch (\InvalidArgumentException $e) {
923  return ‪self::typoscriptIncludeError($e->getMessage());
924  }
925  }
926 
927  foreach (‪$finder as $fileObject) {
928  // Clean filename output for comments
929  $readableFileName = rtrim($readableFilePrefix, '/') . '/' . $fileObject->getFilename();
930  $content .= LF . '### @import \'' . $readableFileName . '\' begin ###' . LF;
931  // Check for allowed files
932  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($fileObject->getFilename())) {
933  $content .= self::typoscriptIncludeError('File "' . $readableFileName . '" was not included since it is not allowed due to fileDenyPattern.');
934  } else {
935  $includedFiles[] = $fileObject->getPathname();
936  // check for includes in included text
937  $included_text = self::checkIncludeLines($fileObject->getContents(), $cycleCounter++, $returnFiles, $absoluteFileName);
938  // If the method also has to return all included files, merge currently included
939  // files with files included by recursively calling itself
940  if ($returnFiles && is_array($included_text)) {
941  $includedFiles = array_merge($includedFiles, $included_text['files']);
942  $included_text = $included_text['typoscript'];
943  }
944  $content .= $included_text . LF;
945  }
946  $content .= '### @import \'' . $readableFileName . '\' end ###' . LF . LF;
947 
948  // load default TypoScript for content rendering templates like
949  // fluid_styled_content if those have been included through e.g.
950  // @import "fluid_styled_content/Configuration/TypoScript/setup.typoscript"
951  if (PathUtility::isExtensionPath(strtoupper($filename))) {
952  $filePointerPathParts = explode('/', substr($filename, 4));
953  // remove file part, determine whether to load setup or constants
954  [$includeType] = explode('.', (string)array_pop($filePointerPathParts));
955 
956  if (in_array($includeType, ['setup', 'constants'], true)) {
957  // adapt extension key to required format (no underscores)
958  $filePointerPathParts[0] = str_replace('_', '', $filePointerPathParts[0]);
959 
960  // load default TypoScript
961  $defaultTypoScriptKey = implode('/', $filePointerPathParts) . '/';
962  if (in_array($defaultTypoScriptKey, $GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'], true)) {
963  $content .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $includeType . '.']['defaultContentRendering'] ?? '';
964  }
965  }
966  }
967  }
968 
969  if (empty($content)) {
970  return self::typoscriptIncludeError('No file or folder found for importing TypoScript on "' . $filename . '".');
971  }
972  return $content;
973  }
974 
989  public static function includeFile($filename, $cycle_counter = 1, $returnFiles = false, &$newString = '', array &$includedFiles = [], $optionalProperties = '', $parentFilenameOrPath = '')
990  {
991  // Resolve a possible relative paths if a parent file is given
992  if ($parentFilenameOrPath !== '' && $filename[0] === '.') {
993  $absfilename = PathUtility::getAbsolutePathOfRelativeReferencedFileOrPath($parentFilenameOrPath, $filename);
994  } else {
995  $absfilename = $filename;
996  }
997  $absfilename = GeneralUtility::getFileAbsFileName($absfilename);
998 
999  $newString .= LF . '### <INCLUDE_TYPOSCRIPT: source="FILE:' . $filename . '"' . $optionalProperties . '> BEGIN:' . LF;
1000  if ((string)$filename !== '') {
1001  // Must exist and must not contain '..' and must be relative
1002  // Check for allowed files
1003  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($absfilename)) {
1004  $newString .= self::typoscriptIncludeError('File "' . $filename . '" was not included since it is not allowed due to fileDenyPattern.');
1005  } else {
1006  $fileExists = false;
1007  if (@file_exists($absfilename)) {
1008  $fileExists = true;
1009  }
1010 
1011  if ($fileExists) {
1012  $includedFiles[] = $absfilename;
1013  // check for includes in included text
1014  $included_text = self::checkIncludeLines((string)file_get_contents($absfilename), $cycle_counter + 1, $returnFiles, $absfilename);
1015  // If the method also has to return all included files, merge currently included
1016  // files with files included by recursively calling itself
1017  if ($returnFiles && is_array($included_text)) {
1018  $includedFiles = array_merge($includedFiles, $included_text['files']);
1019  $included_text = $included_text['typoscript'];
1020  }
1021  $newString .= $included_text . LF;
1022  } else {
1023  $newString .= self::typoscriptIncludeError('File "' . $filename . '" was not found.');
1024  }
1025  }
1026  }
1027  $newString .= '### <INCLUDE_TYPOSCRIPT: source="FILE:' . $filename . '"' . $optionalProperties . '> END:' . LF . LF;
1028  }
1045  protected static function includeDirectory($dirPath, $cycle_counter = 1, $returnFiles = false, &$newString = '', array &$includedFiles = [], $optionalProperties = '', $parentFilenameOrPath = '')
1046  {
1047  // Extract the value of the property extensions="..."
1048  $matches = preg_split('#(?i)extensions\s*=\s*"([^"]*)"(\s*|>)#', $optionalProperties, 2, PREG_SPLIT_DELIM_CAPTURE);
1049  $matches = is_array($matches) ? $matches : [];
1050  if (count($matches) > 1) {
1051  $includedFileExtensions = $matches[1];
1052  } else {
1053  $includedFileExtensions = '';
1054  }
1055 
1056  // Resolve a possible relative paths if a parent file is given
1057  if ($parentFilenameOrPath !== '' && $dirPath[0] === '.') {
1058  $resolvedDirPath = PathUtility::getAbsolutePathOfRelativeReferencedFileOrPath($parentFilenameOrPath, $dirPath);
1059  } else {
1060  $resolvedDirPath = $dirPath;
1061  }
1062  $absDirPath = GeneralUtility::getFileAbsFileName($resolvedDirPath);
1063  if ($absDirPath) {
1064  $absDirPath = rtrim($absDirPath, '/') . '/';
1065  $newString .= LF . '### <INCLUDE_TYPOSCRIPT: source="DIR:' . $dirPath . '"' . $optionalProperties . '> BEGIN:' . LF;
1066  // Get alphabetically sorted file index in array
1067  $fileIndex = GeneralUtility::getAllFilesAndFoldersInPath([], $absDirPath, $includedFileExtensions);
1068  // Prepend file contents to $newString
1069  foreach ($fileIndex as $absFileRef) {
1070  self::includeFile($absFileRef, $cycle_counter, $returnFiles, $newString, $includedFiles);
1071  }
1072  $newString .= '### <INCLUDE_TYPOSCRIPT: source="DIR:' . $dirPath . '"' . $optionalProperties . '> END:' . LF . LF;
1073  } else {
1074  $newString .= self::typoscriptIncludeError('The path "' . $resolvedDirPath . '" is invalid.');
1075  }
1076  }
1077 
1086  protected static function typoscriptIncludeError($error)
1087  {
1088  self::getLogger()->warning($error);
1089  return "\n###\n### ERROR: " . $error . "\n###\n\n";
1090  }
1091 
1098  public static function checkIncludeLines_array(array $array)
1099  {
1100  foreach ($array as $k => $v) {
1101  $array[$k] = self::checkIncludeLines($array[$k]);
1102  }
1103  return $array;
1104  }
1105 
1119  public static function extractIncludes($string, $cycle_counter = 1, array $extractedFileNames = [], $parentFilenameOrPath = '')
1120  {
1121  if ($cycle_counter > 10) {
1122  self::getLogger()->warning('It appears like TypoScript code is looping over itself. Check your templates for "<INCLUDE_TYPOSCRIPT: ..." tags');
1123  return '
1124 ###
1125 ### ERROR: Recursion!
1126 ###
1127 ';
1128  }
1129  $expectedEndTag = '';
1130  $fileContent = [];
1131  $restContent = [];
1132  $fileName = null;
1133  $inIncludePart = false;
1134  $lines = preg_split("/\r\n|\n|\r/", $string);
1135  $skipNextLineIfEmpty = false;
1136  $openingCommentedIncludeStatement = null;
1137  $optionalProperties = '';
1138  foreach ($lines as $line) {
1139  // \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines inserts
1140  // an additional empty line, remove this again
1141  if ($skipNextLineIfEmpty) {
1142  if (trim($line) === '') {
1143  continue;
1144  }
1145  $skipNextLineIfEmpty = false;
1146  }
1147 
1148  // Outside commented include statements
1149  if (!$inIncludePart) {
1150  // Search for beginning commented include statements
1151  if (preg_match('/###\\s*<INCLUDE_TYPOSCRIPT:\\s*source\\s*=\\s*"\\s*((?i)file|dir)\\s*:\\s*([^"]*)"(.*)>\\s*BEGIN/i', $line, $matches)) {
1152  // Found a commented include statement
1153 
1154  // Save this line in case there is no ending tag
1155  $openingCommentedIncludeStatement = trim($line);
1156  $openingCommentedIncludeStatement = preg_replace('/\\s*### Warning: .*###\\s*/', '', $openingCommentedIncludeStatement);
1157 
1158  // type of match: FILE or DIR
1159  $inIncludePart = strtoupper($matches[1]);
1160  $fileName = $matches[2];
1161  $optionalProperties = $matches[3];
1162 
1163  $expectedEndTag = '### <INCLUDE_TYPOSCRIPT: source="' . $inIncludePart . ':' . $fileName . '"' . $optionalProperties . '> END';
1164  // Strip all whitespace characters to make comparison safer
1165  $expectedEndTag = strtolower(preg_replace('/\s/', '', $expectedEndTag) ?? '');
1166  } else {
1167  // If this is not a beginning commented include statement this line goes into the rest content
1168  $restContent[] = $line;
1169  }
1170  } else {
1171  // Inside commented include statements
1172  // Search for the matching ending commented include statement
1173  $strippedLine = preg_replace('/\s/', '', $line);
1174  if (stripos($strippedLine, $expectedEndTag) !== false) {
1175  // Found the matching ending include statement
1176  $fileContentString = implode(PHP_EOL, $fileContent);
1177 
1178  // Write the content to the file
1179 
1180  // Resolve a possible relative paths if a parent file is given
1181  if ($parentFilenameOrPath !== '' && $fileName[0] === '.') {
1182  $realFileName = PathUtility::getAbsolutePathOfRelativeReferencedFileOrPath($parentFilenameOrPath, $fileName);
1183  } else {
1184  $realFileName = $fileName;
1185  }
1186  $realFileName = GeneralUtility::getFileAbsFileName($realFileName);
1187 
1188  if ($inIncludePart === 'FILE') {
1189  // Some file checks
1190  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($realFileName)) {
1191  throw new \UnexpectedValueException(sprintf('File "%s" was not included since it is not allowed due to fileDenyPattern.', $fileName), 1382651858);
1192  }
1193  if (empty($realFileName)) {
1194  throw new \UnexpectedValueException(sprintf('"%s" is not a valid file location.', $fileName), 1294586441);
1195  }
1196  if (!is_writable($realFileName)) {
1197  throw new \RuntimeException(sprintf('"%s" is not writable.', $fileName), 1294586442);
1198  }
1199  if (in_array($realFileName, $extractedFileNames)) {
1200  throw new \RuntimeException(sprintf('Recursive/multiple inclusion of file "%s"', $realFileName), 1294586443);
1201  }
1202  $extractedFileNames[] = $realFileName;
1203 
1204  // Recursive call to detected nested commented include statements
1205  $fileContentString = self::extractIncludes($fileContentString, $cycle_counter + 1, $extractedFileNames, $realFileName);
1206 
1207  // Write the content to the file
1208  if (!GeneralUtility::writeFile($realFileName, $fileContentString)) {
1209  throw new \RuntimeException(sprintf('Could not write file "%s"', $realFileName), 1294586444);
1210  }
1211  // Insert reference to the file in the rest content
1212  $restContent[] = '<INCLUDE_TYPOSCRIPT: source="FILE:' . $fileName . '"' . $optionalProperties . '>';
1213  } else {
1214  // must be DIR
1215 
1216  // Some file checks
1217  if (empty($realFileName)) {
1218  throw new \UnexpectedValueException(sprintf('"%s" is not a valid location.', $fileName), 1366493602);
1219  }
1220  if (!is_dir($realFileName)) {
1221  throw new \RuntimeException(sprintf('"%s" is not a directory.', $fileName), 1366493603);
1222  }
1223  if (in_array($realFileName, $extractedFileNames)) {
1224  throw new \RuntimeException(sprintf('Recursive/multiple inclusion of directory "%s"', $realFileName), 1366493604);
1225  }
1226  $extractedFileNames[] = $realFileName;
1227 
1228  // Recursive call to detected nested commented include statements
1229  self::extractIncludes($fileContentString, $cycle_counter + 1, $extractedFileNames, $realFileName);
1230 
1231  // just drop content between tags since it should usually just contain individual files from that dir
1232 
1233  // Insert reference to the dir in the rest content
1234  $restContent[] = '<INCLUDE_TYPOSCRIPT: source="DIR:' . $fileName . '"' . $optionalProperties . '>';
1235  }
1236 
1237  // Reset variables (preparing for the next commented include statement)
1238  $fileContent = [];
1239  $fileName = null;
1240  $inIncludePart = false;
1241  $openingCommentedIncludeStatement = null;
1242  // \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines inserts
1243  // an additional empty line, remove this again
1244  $skipNextLineIfEmpty = true;
1245  } else {
1246  // If this is not an ending commented include statement this line goes into the file content
1247  $fileContent[] = $line;
1248  }
1249  }
1250  }
1251  // If we're still inside commented include ‪statements copy the lines back to the rest content
1252  if ($inIncludePart) {
1253  $restContent[] = $openingCommentedIncludeStatement . ' ### Warning: Corresponding end line missing! ###';
1254  $restContent = array_merge($restContent, $fileContent);
1255  }
1256  $restContentString = implode(PHP_EOL, $restContent);
1257  return $restContentString;
1258  }
1259 
1266  public static function ‪extractIncludes_array(array $array)
1267  {
1268  foreach ($array as $k => $v) {
1269  $array[$k] = ‪self::extractIncludes($array[$k]);
1270  }
1271  return $array;
1272  }
1273 
1277  protected function ‪getTimeTracker()
1278  {
1279  return GeneralUtility::makeInstance(TimeTracker::class);
1280  }
1281 
1290  protected static function ‪getLogger()
1291  {
1292  return GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
1293  }
1294 }
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\parseSub
‪string parseSub(array &$setup)
Definition: TypoScriptParser.php:216
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:916
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪$finder
‪if(PHP_SAPI !=='cli') $finder
Definition: header-comment.php:22
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$commentSet
‪bool $commentSet
Definition: TypoScriptParser.php:67
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser
Definition: TypoScriptParser.php:38
‪TYPO3\CMS\Core\TypoScript\Parser
Definition: TypoScriptParser.php:16
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$sections
‪array $sections
Definition: TypoScriptParser.php:104
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\importExternalTypoScriptFile
‪static string importExternalTypoScriptFile($filename, $cycleCounter, $returnFiles, array &$includedFiles)
Definition: TypoScriptParser.php:867
‪TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher
Definition: ConditionMatcher.php:34
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$multiLineObject
‪string $multiLineObject
Definition: TypoScriptParser.php:79
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$sectionsMatch
‪array $sectionsMatch
Definition: TypoScriptParser.php:110
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\typoscriptIncludeError
‪static string typoscriptIncludeError($error)
Definition: TypoScriptParser.php:1070
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$lastConditionTrue
‪bool $lastConditionTrue
Definition: TypoScriptParser.php:98
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\executeValueModifier
‪string null executeValueModifier($modifierName, $modifierArgument=null, $currentValue=null)
Definition: TypoScriptParser.php:406
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$lineNumberOffset
‪int $lineNumberOffset
Definition: TypoScriptParser.php:134
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\includeDirectory
‪static includeDirectory($dirPath, $cycle_counter=1, $returnFiles=false, &$newString='', array &$includedFiles=[], $optionalProperties='', $parentFilenameOrPath='')
Definition: TypoScriptParser.php:1029
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static basename(string $path)
Definition: PathUtility.php:219
‪TYPO3\CMS\Core\Utility\StringUtility\removeByteOrderMark
‪static removeByteOrderMark(string $input)
Definition: StringUtility.php:51
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\getVal
‪array getVal($string, $setup)
Definition: TypoScriptParser.php:523
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsolutePathOfRelativeReferencedFileOrPath
‪static string getAbsolutePathOfRelativeReferencedFileOrPath(string $baseFilenameOrPath, string $includeFileName)
Definition: PathUtility.php:319
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$raw
‪string[] $raw
Definition: TypoScriptParser.php:49
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static dirname(string $path)
Definition: PathUtility.php:243
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\rollParseSub
‪string rollParseSub($string, array &$setup)
Definition: TypoScriptParser.php:498
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$rawP
‪int $rawP
Definition: TypoScriptParser.php:55
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$setup
‪array $setup
Definition: TypoScriptParser.php:43
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$errors
‪array $errors
Definition: TypoScriptParser.php:128
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\__construct
‪__construct()
Definition: TypoScriptParser.php:136
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\checkIncludeLines
‪static string array checkIncludeLines($string, $cycle_counter=1, $returnFiles=false, $parentFilenameOrPath='')
Definition: TypoScriptParser.php:677
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$regLinenumbers
‪bool $regLinenumbers
Definition: TypoScriptParser.php:122
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\extractIncludes
‪static string extractIncludes($string, $cycle_counter=1, array $extractedFileNames=[], $parentFilenameOrPath='')
Definition: TypoScriptParser.php:1103
‪TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher
Definition: ConditionMatcher.php:34
‪statements
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$multiLineValue
‪array $multiLineValue
Definition: TypoScriptParser.php:85
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$lastComment
‪string $lastComment
Definition: TypoScriptParser.php:61
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage(string $content, string $logLevel=LogLevel::INFO)
Definition: TimeTracker.php:173
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\parseNextKeySegment
‪array parseNextKeySegment($key)
Definition: TypoScriptParser.php:612
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\parse
‪parse($string, $matchObj='')
Definition: TypoScriptParser.php:147
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\extractIncludes_array
‪static array extractIncludes_array(array $array)
Definition: TypoScriptParser.php:1250
‪TYPO3\CMS\Core\Http\fromRequest
‪@ fromRequest
Definition: ApplicationType.php:67
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$multiLineEnabled
‪bool $multiLineEnabled
Definition: TypoScriptParser.php:73
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$regComments
‪bool $regComments
Definition: TypoScriptParser.php:116
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\getLogger
‪static LoggerInterface getLogger()
Definition: TypoScriptParser.php:1274
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\nextDivider
‪string nextDivider()
Definition: TypoScriptParser.php:198
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\getTimeTracker
‪TimeTracker getTimeTracker()
Definition: TypoScriptParser.php:1261
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:32
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\setVal
‪setVal($string, array &$setup, $value, $wipeOut=false)
Definition: TypoScriptParser.php:555
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\includeFile
‪static includeFile($filename, $cycle_counter=1, $returnFiles=false, &$newString='', array &$includedFiles=[], $optionalProperties='', $parentFilenameOrPath='')
Definition: TypoScriptParser.php:973
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\addImportsFromExternalFiles
‪static string addImportsFromExternalFiles($typoScript, $cycleCounter, $returnFiles, &$includedFiles, &$parentFilenameOrPath)
Definition: TypoScriptParser.php:830
‪TYPO3\CMS\Core\Http\ApplicationType
‪ApplicationType
Definition: ApplicationType.php:56
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\$inBrace
‪int $inBrace
Definition: TypoScriptParser.php:91
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\error
‪error($message, $logLevel=LogLevel::WARNING)
Definition: TypoScriptParser.php:660