‪TYPO3CMS  ‪main
InheritancesResolverService.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 
64 {
68  public const ‪INHERITANCE_OPERATOR = '__inheritances';
69 
76  protected ‪$referenceConfiguration = [];
77 
84  protected ‪$inheritanceStack = [];
85 
92  protected ‪$inheritancePathToCheck = '';
93 
100  public static function ‪create(array $configuration = []): ‪InheritancesResolverService
101  {
102  $inheritancesResolverService = GeneralUtility::makeInstance(self::class);
103  $inheritancesResolverService->setReferenceConfiguration($configuration);
104  return $inheritancesResolverService;
105  }
106 
114  public function ‪reset()
115  {
116  $this->referenceConfiguration = [];
117  $this->inheritanceStack = [];
118  $this->inheritancePathToCheck = '';
119  return $this;
120  }
121 
129  {
130  $this->referenceConfiguration = ‪$referenceConfiguration;
131  return $this;
132  }
133 
141  public function ‪getResolvedConfiguration(): array
142  {
143  $configuration = $this->‪resolve($this->referenceConfiguration);
144  $configuration = $this->‪removeInheritanceOperatorRecursive($configuration);
145  return $configuration;
146  }
147 
161  protected function ‪resolve(
162  array $configuration,
163  array $pathStack = [],
164  bool $setInheritancePathToCheck = true
165  ): array {
166  foreach ($configuration as $key => $values) {
167  //add current key to pathStack
168  $pathStack[] = $key;
169  $path = implode('.', $pathStack);
170 
171  //check endless loop for current path
172  $this->‪throwExceptionIfCycleInheritances($path, $path);
173 
174  //overwrite service property 'inheritancePathToCheck' with current path
175  if ($setInheritancePathToCheck) {
176  $this->inheritancePathToCheck = $path;
177  }
178 
179  //if value of subnode is an array, perform a deep search iteration step
180  if (is_array($configuration[$key])) {
181  if (isset($configuration[$key][self::INHERITANCE_OPERATOR])) {
182  $inheritances = $this->‪getValueByPath($this->referenceConfiguration, $path . '.' . self::INHERITANCE_OPERATOR);
183 
184  //and replace the __inheritance operator by the respective partial
185  if (is_array($inheritances)) {
186  $inheritedConfigurations = $this->‪resolveInheritancesRecursive($inheritances);
187  $configuration[$key] = array_replace_recursive($inheritedConfigurations, $configuration[$key]);
188  }
189 
190  //remove the inheritance operator from configuration
191  unset($configuration[$key][self::INHERITANCE_OPERATOR]);
192  }
193 
194  if (!empty($configuration[$key])) {
195  // resolve subnode of YAML config
196  $configuration[$key] = $this->‪resolve($configuration[$key], $pathStack);
197  }
198  }
199  array_pop($pathStack);
200  }
201 
202  return $configuration;
203  }
204 
213  protected function ‪resolveInheritancesRecursive(array $inheritances): array
214  {
215  ksort($inheritances);
216  $inheritedConfigurations = [];
217  foreach ($inheritances as $inheritancePath) {
218  $inheritancePath = $this->‪removeVendorNamespaceFromInheritancePath($inheritancePath);
219  $this->‪throwExceptionIfCycleInheritances($inheritancePath, $inheritancePath);
220  $inheritedConfiguration = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath);
221 
222  if (
223  isset($inheritedConfiguration[self::INHERITANCE_OPERATOR])
224  && count($inheritedConfiguration) === 1
225  ) {
226  if ($this->inheritancePathToCheck === $inheritancePath) {
227  throw new CycleInheritancesException(
228  $this->inheritancePathToCheck . ' has cycle inheritances',
229  1474900796
230  );
231  }
232 
233  $inheritedConfiguration = $this->‪resolveInheritancesRecursive(
234  $inheritedConfiguration[self::INHERITANCE_OPERATOR]
235  );
236  } else {
237  $pathStack = explode('.', $inheritancePath);
238  $key = array_pop($pathStack);
239  $newConfiguration = [
240  $key => $inheritedConfiguration,
241  ];
242  $inheritedConfiguration = $this->‪resolve(
243  $newConfiguration,
244  $pathStack,
245  false
246  );
247  $inheritedConfiguration = $inheritedConfiguration[$key];
248  }
249 
250  if ($inheritedConfiguration === null) {
251  throw new CycleInheritancesException(
252  $inheritancePath . ' does not exist within the configuration',
253  1489260796
254  );
255  }
256 
257  $inheritedConfigurations = array_replace_recursive(
258  $inheritedConfigurations,
259  $inheritedConfiguration
260  );
261  }
262 
263  return $inheritedConfigurations;
264  }
265 
271  protected function ‪throwExceptionIfCycleInheritances(string $path, string $pathToCheck)
272  {
273  $configuration = $this->‪getValueByPath($this->referenceConfiguration, $path);
274 
275  if (isset($configuration[self::INHERITANCE_OPERATOR])) {
276  $inheritances = $this->‪getValueByPath($this->referenceConfiguration, $path . '.' . self::INHERITANCE_OPERATOR);
277 
278  if (is_array($inheritances)) {
279  foreach ($inheritances as $inheritancePath) {
280  $inheritancePath = $this->‪removeVendorNamespaceFromInheritancePath($inheritancePath);
281  $configuration = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath);
282 
283  if (isset($configuration[self::INHERITANCE_OPERATOR])) {
284  $_inheritances = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath . '.' . self::INHERITANCE_OPERATOR);
285 
286  foreach ($_inheritances as $_inheritancePath) {
287  $_inheritancePath = $this->‪removeVendorNamespaceFromInheritancePath($_inheritancePath);
288  if (str_starts_with($pathToCheck, $_inheritancePath)) {
289  throw new CycleInheritancesException(
290  $pathToCheck . ' has cycle inheritances',
291  1474900797
292  );
293  }
294  }
295  }
296 
297  if (
298  isset($this->inheritanceStack[$pathToCheck])
299  && is_array($this->inheritanceStack[$pathToCheck])
300  && in_array($inheritancePath, $this->inheritanceStack[$pathToCheck])
301  ) {
302  $this->inheritanceStack[$pathToCheck][] = $inheritancePath;
303  throw new CycleInheritancesException(
304  $pathToCheck . ' has cycle inheritances',
305  1474900799
306  );
307  }
308  $this->inheritanceStack[$pathToCheck][] = $inheritancePath;
309  $this->‪throwExceptionIfCycleInheritances($inheritancePath, $pathToCheck);
310  }
311  $this->inheritanceStack[$pathToCheck] = null;
312  }
313  }
314  }
315 
321  protected function ‪removeInheritanceOperatorRecursive(array $array): array
322  {
323  $result = $array;
324  foreach ($result as $key => $value) {
325  if ($key === self::INHERITANCE_OPERATOR) {
326  unset($result[$key]);
327  continue;
328  }
329 
330  if (is_array($value)) {
331  $result[$key] = $this->‪removeInheritanceOperatorRecursive($value);
332  }
333  }
334  return $result;
335  }
336 
343  protected function ‪getValueByPath(array $config, string $path, string $delimiter = '.')
344  {
345  try {
346  return ‪ArrayUtility::getValueByPath($config, $path, $delimiter);
347  } catch (MissingArrayPathException $exception) {
348  return null;
349  }
350  }
351 
352  protected function ‪removeVendorNamespaceFromInheritancePath(string $inheritancePath): string
353  {
354  return str_starts_with($inheritancePath, 'TYPO3.CMS.Form.')
355  ? substr($inheritancePath, 15)
356  : $inheritancePath;
357  }
358 }
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\INHERITANCE_OPERATOR
‪const INHERITANCE_OPERATOR
Definition: InheritancesResolverService.php:68
‪TYPO3\CMS\Form\Mvc\Configuration
Definition: ConfigurationManager.php:18
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\removeVendorNamespaceFromInheritancePath
‪removeVendorNamespaceFromInheritancePath(string $inheritancePath)
Definition: InheritancesResolverService.php:349
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\reset
‪InheritancesResolverService reset()
Definition: InheritancesResolverService.php:111
‪TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException
Definition: MissingArrayPathException.php:27
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\$inheritanceStack
‪array $inheritanceStack
Definition: InheritancesResolverService.php:82
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService
Definition: InheritancesResolverService.php:64
‪TYPO3\CMS\Core\Utility\ArrayUtility\getValueByPath
‪static getValueByPath(array $array, array|string $path, string $delimiter='/')
Definition: ArrayUtility.php:176
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\removeInheritanceOperatorRecursive
‪array removeInheritanceOperatorRecursive(array $array)
Definition: InheritancesResolverService.php:318
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\$referenceConfiguration
‪array $referenceConfiguration
Definition: InheritancesResolverService.php:75
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\setReferenceConfiguration
‪InheritancesResolverService setReferenceConfiguration(array $referenceConfiguration)
Definition: InheritancesResolverService.php:125
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\$inheritancePathToCheck
‪string $inheritancePathToCheck
Definition: InheritancesResolverService.php:89
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\throwExceptionIfCycleInheritances
‪throwExceptionIfCycleInheritances(string $path, string $pathToCheck)
Definition: InheritancesResolverService.php:268
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\create
‪static create(array $configuration=[])
Definition: InheritancesResolverService.php:97
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\CycleInheritancesException
Definition: CycleInheritancesException.php:28
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\resolveInheritancesRecursive
‪resolveInheritancesRecursive(array $inheritances)
Definition: InheritancesResolverService.php:210
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\getResolvedConfiguration
‪getResolvedConfiguration()
Definition: InheritancesResolverService.php:138
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\resolve
‪resolve(array $configuration, array $pathStack=[], bool $setInheritancePathToCheck=true)
Definition: InheritancesResolverService.php:158
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\getValueByPath
‪string array null getValueByPath(array $config, string $path, string $delimiter='.')
Definition: InheritancesResolverService.php:340