‪TYPO3CMS  ‪main
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;
28 
46 class ‪YamlFileLoader implements LoggerAwareInterface
47 {
48  use LoggerAwareTrait;
49 
50  public const ‪PATTERN_PARTS = '%[^(%]+?\‍([\'"]?([^(]*?)[\'"]?\‍)%|%([^%()]*?)%';
51  public const ‪PROCESS_PLACEHOLDERS = 1;
52  public const ‪PROCESS_IMPORTS = 2;
53 
57  private ‪$flags;
58 
66  public function ‪load(string $fileName, int ‪$flags = self::PROCESS_PLACEHOLDERS | self::PROCESS_IMPORTS): array
67  {
68  $this->flags = ‪$flags;
69  return $this->‪loadAndParse($fileName, null);
70  }
71 
79  protected function ‪loadAndParse(string $fileName, ?string $currentFileName): array
80  {
81  $sanitizedFileName = $this->‪getStreamlinedFileName($fileName, $currentFileName);
82  $content = $this->‪getFileContents($sanitizedFileName);
83  $content = Yaml::parse($content);
84 
85  if (!is_array($content)) {
86  throw new ‪YamlParseException(
87  'YAML file "' . $fileName . '" could not be parsed into valid syntax, probably empty?',
88  1497332874
89  );
90  }
91 
92  if ($this->‪hasFlag(self::PROCESS_IMPORTS)) {
93  $content = $this->‪processImports($content, $sanitizedFileName);
94  }
95  if ($this->‪hasFlag(self::PROCESS_PLACEHOLDERS)) {
96  // Check for "%" placeholders
97  $content = $this->‪processPlaceholders($content, $content);
98  }
99  return $content;
100  }
101 
107  protected function ‪getFileContents(string $fileName): string
108  {
109  return is_readable($fileName) ? (string)file_get_contents($fileName) : '';
110  }
111 
120  protected function ‪getStreamlinedFileName(string $fileName, ?string $currentFileName): string
121  {
122  if (!empty($currentFileName)) {
124  $streamlinedFileName = GeneralUtility::getFileAbsFileName($fileName);
125  } else {
126  // Now this path is considered to be relative the current file name
128  $currentFileName,
129  $fileName
130  );
131  if (!GeneralUtility::isAllowedAbsPath($streamlinedFileName)) {
132  throw new YamlFileLoadingException(
133  'Referencing a file which is outside of TYPO3s main folder',
134  1560319866
135  );
136  }
137  }
138  } else {
139  $streamlinedFileName = GeneralUtility::getFileAbsFileName($fileName);
140  }
141  if (!$streamlinedFileName) {
142  throw new YamlFileLoadingException('YAML File "' . $fileName . '" could not be loaded', 1485784246);
143  }
144  return $streamlinedFileName;
145  }
146 
151  protected function ‪processImports(array $content, ?string $fileName): array
152  {
153  if (isset($content['imports']) && is_array($content['imports'])) {
154  // Reverse the order of imports to follow the order of the declarations, see #92100
155  $content['imports'] = array_reverse($content['imports']);
156  foreach ($content['imports'] as $import) {
157  try {
158  $import = $this->‪processPlaceholders($import, $content);
159  $resource = $import['resource'];
160  if ($import['glob'] ?? false) {
161  $resource = $this->‪getStreamlinedFileName($resource, $fileName);
162  foreach (array_reverse(glob($resource)) as $file) {
163  $content = ‪ArrayUtility::replaceAndAppendScalarValuesRecursive($this->‪loadAndParse($file, $fileName), $content);
164  }
165  } else {
166  $importedContent = $this->‪loadAndParse($resource, $fileName);
167  // override the imported content with the one from the current file
168  $content = ‪ArrayUtility::replaceAndAppendScalarValuesRecursive($importedContent, $content);
169  }
170  } catch (ParseException|YamlParseException|YamlFileLoadingException $exception) {
171  $this->logger->error($exception->getMessage(), ['exception' => $exception]);
172  }
173  }
174  unset($content['imports']);
175  }
176  return $content;
177  }
178 
188  protected function ‪processPlaceholders(array $content, array $referenceArray): array
189  {
190  foreach ($content as $k => $v) {
191  if (is_array($v)) {
192  $content[$k] = $this->‪processPlaceholders($v, $referenceArray);
193  } elseif ($this->‪containsPlaceholder($v)) {
194  $content[$k] = $this->‪processPlaceholderLine($v, $referenceArray);
195  }
196  }
197  return $content;
198  }
199 
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 
229  protected function ‪processSinglePlaceholder(string $placeholder, string $value, array $referenceArray)
230  {
231  $processorList = GeneralUtility::makeInstance(
232  PlaceholderProcessorList::class,
233  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['yamlLoader']['placeholderProcessors']
234  );
235  foreach ($processorList->compile() as $processor) {
236  if ($processor->canProcess($placeholder, $referenceArray)) {
237  try {
238  $result = $processor->process($value, $referenceArray);
239  } catch (\UnexpectedValueException $e) {
240  $result = $placeholder;
241  }
242  if (is_array($result)) {
243  $result = $this->‪processPlaceholders($result, $referenceArray);
244  }
245  break;
246  }
247  }
248  return $result ?? $placeholder;
249  }
250 
251  protected function ‪getParts(string $placeholders): array
252  {
253  // find occurrences of placeholders like %some()% and %array.access%.
254  // Only find the innermost ones, so we can nest them.
255  preg_match_all(
256  '/' . self::PATTERN_PARTS . '/',
257  $placeholders,
258  $parts,
259  PREG_UNMATCHED_AS_NULL
260  );
261  $matches = array_filter(
262  array_merge($parts[1], $parts[2])
263  );
264  return array_combine($parts[0], $matches);
265  }
266 
271  protected function ‪containsPlaceholder(mixed $value): bool
272  {
273  return is_string($value) && substr_count($value, '%') >= 2;
274  }
275 
276  protected function ‪hasFlag(int $flag): bool
277  {
278  return ($this->flags & $flag) === $flag;
279  }
280 }
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PROCESS_IMPORTS
‪const PROCESS_IMPORTS
Definition: YamlFileLoader.php:52
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\getStreamlinedFileName
‪string getStreamlinedFileName(string $fileName, ?string $currentFileName)
Definition: YamlFileLoader.php:119
‪TYPO3\CMS\Core\Utility\PathUtility\isExtensionPath
‪static isExtensionPath(string $path)
Definition: PathUtility.php:117
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static isAbsolutePath(string $path)
Definition: PathUtility.php:286
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\load
‪array load(string $fileName, int $flags=self::PROCESS_PLACEHOLDERS|self::PROCESS_IMPORTS)
Definition: YamlFileLoader.php:65
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PROCESS_PLACEHOLDERS
‪const PROCESS_PLACEHOLDERS
Definition: YamlFileLoader.php:51
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processSinglePlaceholder
‪mixed processSinglePlaceholder(string $placeholder, string $value, array $referenceArray)
Definition: YamlFileLoader.php:228
‪TYPO3\CMS\Core\Configuration\Loader\Exception\YamlParseException
Definition: YamlParseException.php:20
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\PATTERN_PARTS
‪const PATTERN_PARTS
Definition: YamlFileLoader.php:50
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processPlaceholders
‪array processPlaceholders(array $content, array $referenceArray)
Definition: YamlFileLoader.php:187
‪TYPO3\CMS\Core\Utility\ArrayUtility\replaceAndAppendScalarValuesRecursive
‪static replaceAndAppendScalarValuesRecursive(array $array1, array $array2)
Definition: ArrayUtility.php:971
‪TYPO3\CMS\Core\Configuration\Loader\Exception\YamlFileLoadingException
Definition: YamlFileLoadingException.php:20
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\getParts
‪getParts(string $placeholders)
Definition: YamlFileLoader.php:250
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\loadAndParse
‪array loadAndParse(string $fileName, ?string $currentFileName)
Definition: YamlFileLoader.php:78
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\containsPlaceholder
‪containsPlaceholder(mixed $value)
Definition: YamlFileLoader.php:270
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsolutePathOfRelativeReferencedFileOrPath
‪static string getAbsolutePathOfRelativeReferencedFileOrPath(string $baseFilenameOrPath, string $includeFileName)
Definition: PathUtility.php:319
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\$flags
‪int $flags
Definition: YamlFileLoader.php:56
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processImports
‪processImports(array $content, ?string $fileName)
Definition: YamlFileLoader.php:150
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\getFileContents
‪string getFileContents(string $fileName)
Definition: YamlFileLoader.php:106
‪TYPO3\CMS\Core\Configuration\Loader
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader
Definition: YamlFileLoader.php:47
‪TYPO3\CMS\Core\Configuration\Processor\PlaceholderProcessorList
Definition: PlaceholderProcessorList.php:26
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\hasFlag
‪hasFlag(int $flag)
Definition: YamlFileLoader.php:275
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\processPlaceholderLine
‪mixed processPlaceholderLine(string $line, array $referenceArray)
Definition: YamlFileLoader.php:202