‪TYPO3CMS  10.4
YamlFileLoader.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\Log\LoggerAwareInterface;
19 use Psr\Log\LoggerAwareTrait;
20 use Symfony\Component\Yaml\Exception\ParseException;
21 use Symfony\Component\Yaml\Yaml;
29 
47 class ‪YamlFileLoader implements LoggerAwareInterface
48 {
49  use LoggerAwareTrait;
50 
51  public const ‪PATTERN_PARTS = '%[^(%]+?\‍([\'"]?([^(]*?)[\'"]?\‍)%|%([^%()]*?)%';
52  public const ‪PROCESS_PLACEHOLDERS = 1;
53  public const ‪PROCESS_IMPORTS = 2;
54 
58  private ‪$flags;
59 
67  public function ‪load(string $fileName, int ‪$flags = self::PROCESS_PLACEHOLDERS | self::PROCESS_IMPORTS): array
68  {
69  $this->flags = ‪$flags;
70  return $this->‪loadAndParse($fileName, null);
71  }
72 
80  protected function ‪loadAndParse(string $fileName, ?string $currentFileName): array
81  {
82  $sanitizedFileName = $this->‪getStreamlinedFileName($fileName, $currentFileName);
83  $content = $this->‪getFileContents($sanitizedFileName);
84  $content = Yaml::parse($content);
85 
86  if (!is_array($content)) {
87  throw new ‪YamlParseException(
88  'YAML file "' . $fileName . '" could not be parsed into valid syntax, probably empty?',
89  1497332874
90  );
91  }
92 
93  if ($this->‪hasFlag(self::PROCESS_IMPORTS)) {
94  $content = $this->‪processImports($content, $sanitizedFileName);
95  }
96  if ($this->‪hasFlag(self::PROCESS_PLACEHOLDERS)) {
97  // Check for "%" placeholders
98  $content = $this->‪processPlaceholders($content, $content);
99  }
100  return $content;
101  }
102 
109  protected function ‪getFileContents(string $fileName): string
110  {
111  return file_get_contents($fileName);
112  }
113 
122  protected function ‪getStreamlinedFileName(string $fileName, ?string $currentFileName): string
123  {
124  if (!empty($currentFileName)) {
125  if (strpos($fileName, 'EXT:') === 0 || GeneralUtility::isAbsPath($fileName)) {
126  $streamlinedFileName = GeneralUtility::getFileAbsFileName($fileName);
127  } else {
128  // Now this path is considered to be relative the current file name
130  $currentFileName,
131  $fileName
132  );
133  if (!GeneralUtility::isAllowedAbsPath($streamlinedFileName)) {
134  throw new YamlFileLoadingException(
135  'Referencing a file which is outside of TYPO3s main folder',
136  1560319866
137  );
138  }
139  }
140  } else {
141  $streamlinedFileName = GeneralUtility::getFileAbsFileName($fileName);
142  }
143  if (!$streamlinedFileName) {
144  throw new YamlFileLoadingException('YAML File "' . $fileName . '" could not be loaded', 1485784246);
145  }
146  return $streamlinedFileName;
147  }
148 
156  protected function ‪processImports(array $content, ?string $fileName): array
157  {
158  if (isset($content['imports']) && is_array($content['imports'])) {
159  if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('yamlImportsFollowDeclarationOrder')) {
160  $content['imports'] = array_reverse($content['imports']);
161  }
162  foreach ($content['imports'] as $import) {
163  try {
164  $import = $this->‪processPlaceholders($import, $import);
165  $importedContent = $this->‪loadAndParse($import['resource'], $fileName);
166  // override the imported content with the one from the current file
167  $content = ‪ArrayUtility::replaceAndAppendScalarValuesRecursive($importedContent, $content);
168  } catch (ParseException|YamlParseException|YamlFileLoadingException $exception) {
169  $this->logger->error($exception->getMessage(), ['exception' => $exception]);
170  }
171  }
172  unset($content['imports']);
173  }
174  return $content;
175  }
176 
186  protected function ‪processPlaceholders(array $content, array $referenceArray): array
187  {
188  foreach ($content as $k => $v) {
189  if (is_array($v)) {
190  $content[$k] = $this->‪processPlaceholders($v, $referenceArray);
191  } elseif ($this->‪containsPlaceholder($v)) {
192  $content[$k] = $this->‪processPlaceholderLine($v, $referenceArray);
193  }
194  }
195  return $content;
196  }
197 
203  protected function ‪processPlaceholderLine(string $line, array $referenceArray)
204  {
205  $parts = $this->‪getParts($line);
206  foreach ($parts as $partKey => $part) {
207  $result = $this->‪processSinglePlaceholder($partKey, $part, $referenceArray);
208  // Replace whole content if placeholder is the only thing in this line
209  if ($line === $partKey) {
210  $line = $result;
211  } elseif (is_string($result) || is_numeric($result)) {
212  $line = str_replace($partKey, $result, $line);
213  } else {
214  throw new \UnexpectedValueException(
215  'Placeholder can not be substituted if result is not string or numeric',
216  1581502783
217  );
218  }
219  if ($result !== $partKey && $this->‪containsPlaceholder($line)) {
220  $line = $this->‪processPlaceholderLine($line, $referenceArray);
221  }
222  }
223  return $line;
224  }
225 
232  protected function ‪processSinglePlaceholder(string $placeholder, string $value, array $referenceArray)
233  {
234  $processorList = GeneralUtility::makeInstance(
235  PlaceholderProcessorList::class,
236  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['yamlLoader']['placeholderProcessors']
237  );
238  foreach ($processorList->compile() as $processor) {
239  if ($processor->canProcess($placeholder, $referenceArray)) {
240  try {
241  $result = $processor->process($value, $referenceArray);
242  } catch (\UnexpectedValueException $e) {
243  $result = $placeholder;
244  }
245  if (is_array($result)) {
246  $result = $this->‪processPlaceholders($result, $referenceArray);
247  }
248  break;
249  }
250  }
251  return $result ?? $placeholder;
252  }
253 
258  protected function ‪getParts(string $placeholders): array
259  {
260  // find occurrences of placeholders like %some()% and %array.access%.
261  // Only find the innermost ones, so we can nest them.
262  preg_match_all(
263  '/' . self::PATTERN_PARTS . '/',
264  $placeholders,
265  $parts,
266  PREG_UNMATCHED_AS_NULL
267  );
268  $matches = array_filter(
269  array_merge($parts[1], $parts[2])
270  );
271  return array_combine($parts[0], $matches);
272  }
273 
281  protected function ‪containsPlaceholder($value): bool
282  {
283  return is_string($value) && substr_count($value, '%') >= 2;
284  }
285 
286  protected function ‪hasFlag(int $flag): bool
287  {
288  return ($this->flags & $flag) === $flag;
289  }
290 }
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PROCESS_IMPORTS
‪const PROCESS_IMPORTS
Definition: YamlFileLoader.php:53
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\getStreamlinedFileName
‪string getStreamlinedFileName(string $fileName, ?string $currentFileName)
Definition: YamlFileLoader.php:121
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:24
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\load
‪array load(string $fileName, int $flags=self::PROCESS_PLACEHOLDERS|self::PROCESS_IMPORTS)
Definition: YamlFileLoader.php:66
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PROCESS_PLACEHOLDERS
‪const PROCESS_PLACEHOLDERS
Definition: YamlFileLoader.php:52
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processSinglePlaceholder
‪mixed processSinglePlaceholder(string $placeholder, string $value, array $referenceArray)
Definition: YamlFileLoader.php:231
‪TYPO3\CMS\Core\Configuration\Loader\Exception\YamlParseException
Definition: YamlParseException.php:21
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PATTERN_PARTS
‪const PATTERN_PARTS
Definition: YamlFileLoader.php:51
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processPlaceholders
‪array processPlaceholders(array $content, array $referenceArray)
Definition: YamlFileLoader.php:185
‪TYPO3\CMS\Core\Configuration\Loader\Exception\YamlFileLoadingException
Definition: YamlFileLoadingException.php:21
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\loadAndParse
‪array loadAndParse(string $fileName, ?string $currentFileName)
Definition: YamlFileLoader.php:79
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\$flags
‪int $flags
Definition: YamlFileLoader.php:57
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\getFileContents
‪string getFileContents(string $fileName)
Definition: YamlFileLoader.php:108
‪TYPO3\CMS\Core\Configuration\Features
Definition: Features.php:56
‪TYPO3\CMS\Core\Utility\ArrayUtility\replaceAndAppendScalarValuesRecursive
‪static array replaceAndAppendScalarValuesRecursive(array $array1, array $array2)
Definition: ArrayUtility.php:959
‪TYPO3\CMS\Core\Configuration\Loader
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader
Definition: YamlFileLoader.php:48
‪TYPO3\CMS\Core\Configuration\Processor\PlaceholderProcessorList
Definition: PlaceholderProcessorList.php:26
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\hasFlag
‪hasFlag(int $flag)
Definition: YamlFileLoader.php:285
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\getParts
‪array getParts(string $placeholders)
Definition: YamlFileLoader.php:257
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processImports
‪array processImports(array $content, ?string $fileName)
Definition: YamlFileLoader.php:155
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processPlaceholderLine
‪mixed processPlaceholderLine(string $line, array $referenceArray)
Definition: YamlFileLoader.php:202
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsolutePathOfRelativeReferencedFileOrPath
‪static string getAbsolutePathOfRelativeReferencedFileOrPath($baseFilenameOrPath, $includeFileName)
Definition: PathUtility.php:256
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\containsPlaceholder
‪bool containsPlaceholder($value)
Definition: YamlFileLoader.php:280