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