‪TYPO3CMS  11.5
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 
102  public static function ‪create(array $configuration = []): ‪InheritancesResolverService
103  {
104  $inheritancesResolverService = GeneralUtility::makeInstance(self::class);
105  $inheritancesResolverService->setReferenceConfiguration($configuration);
106  return $inheritancesResolverService;
107  }
108 
116  public function ‪reset()
117  {
118  $this->referenceConfiguration = [];
119  $this->inheritanceStack = [];
120  $this->inheritancePathToCheck = '';
121  return $this;
122  }
123 
132  {
133  $this->referenceConfiguration = ‪$referenceConfiguration;
134  return $this;
135  }
136 
145  public function ‪getResolvedConfiguration(): array
146  {
147  $configuration = $this->‪resolve($this->referenceConfiguration);
148  $configuration = $this->‪removeInheritanceOperatorRecursive($configuration);
149  return $configuration;
150  }
151 
166  protected function ‪resolve(
167  array $configuration,
168  array $pathStack = [],
169  bool $setInheritancePathToCheck = true
170  ): array {
171  foreach ($configuration as $key => $values) {
172  //add current key to pathStack
173  $pathStack[] = $key;
174  $path = implode('.', $pathStack);
175 
176  //check endless loop for current path
177  $this->‪throwExceptionIfCycleInheritances($path, $path);
178 
179  //overwrite service property 'inheritancePathToCheck' with current path
180  if ($setInheritancePathToCheck) {
181  $this->inheritancePathToCheck = $path;
182  }
183 
184  //if value of subnode is an array, perform a deep search iteration step
185  if (is_array($configuration[$key])) {
186  if (isset($configuration[$key][self::INHERITANCE_OPERATOR])) {
187  $inheritances = $this->‪getValueByPath($this->referenceConfiguration, $path . '.' . self::INHERITANCE_OPERATOR);
188 
189  //and replace the __inheritance operator by the respective partial
190  if (is_array($inheritances)) {
191  $inheritedConfigurations = $this->‪resolveInheritancesRecursive($inheritances);
192  $configuration[$key] = array_replace_recursive($inheritedConfigurations, $configuration[$key]);
193  }
194 
195  //remove the inheritance operator from configuration
196  unset($configuration[$key][self::INHERITANCE_OPERATOR]);
197  }
198 
199  if (!empty($configuration[$key])) {
200  // resolve subnode of YAML config
201  $configuration[$key] = $this->‪resolve($configuration[$key], $pathStack);
202  }
203  }
204  array_pop($pathStack);
205  }
206 
207  return $configuration;
208  }
209 
219  protected function ‪resolveInheritancesRecursive(array $inheritances): array
220  {
221  ksort($inheritances);
222  $inheritedConfigurations = [];
223  foreach ($inheritances as $inheritancePath) {
224  $this->‪throwExceptionIfCycleInheritances($inheritancePath, $inheritancePath);
225  $inheritedConfiguration = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath);
226 
227  if (
228  isset($inheritedConfiguration[self::INHERITANCE_OPERATOR])
229  && count($inheritedConfiguration) === 1
230  ) {
231  if ($this->inheritancePathToCheck === $inheritancePath) {
232  throw new CycleInheritancesException(
233  $this->inheritancePathToCheck . ' has cycle inheritances',
234  1474900796
235  );
236  }
237 
238  $inheritedConfiguration = $this->‪resolveInheritancesRecursive(
239  $inheritedConfiguration[self::INHERITANCE_OPERATOR]
240  );
241  } else {
242  $pathStack = explode('.', $inheritancePath);
243  $key = array_pop($pathStack);
244  $newConfiguration = [
245  $key => $inheritedConfiguration,
246  ];
247  $inheritedConfiguration = $this->‪resolve(
248  $newConfiguration,
249  $pathStack,
250  false
251  );
252  $inheritedConfiguration = $inheritedConfiguration[$key];
253  }
254 
255  if ($inheritedConfiguration === null) {
256  throw new CycleInheritancesException(
257  $inheritancePath . ' does not exist within the configuration',
258  1489260796
259  );
260  }
261 
262  $inheritedConfigurations = array_replace_recursive(
263  $inheritedConfigurations,
264  $inheritedConfiguration
265  );
266  }
267 
268  return $inheritedConfigurations;
269  }
270 
278  protected function ‪throwExceptionIfCycleInheritances(string $path, string $pathToCheck)
279  {
280  $configuration = $this->‪getValueByPath($this->referenceConfiguration, $path);
281 
282  if (isset($configuration[self::INHERITANCE_OPERATOR])) {
283  $inheritances = $this->‪getValueByPath($this->referenceConfiguration, $path . '.' . self::INHERITANCE_OPERATOR);
284 
285  if (is_array($inheritances)) {
286  foreach ($inheritances as $inheritancePath) {
287  $configuration = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath);
288 
289  if (isset($configuration[self::INHERITANCE_OPERATOR])) {
290  $_inheritances = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath . '.' . self::INHERITANCE_OPERATOR);
291 
292  foreach ($_inheritances as $_inheritancePath) {
293  if (strpos($pathToCheck, $_inheritancePath) === 0) {
294  throw new CycleInheritancesException(
295  $pathToCheck . ' has cycle inheritances',
296  1474900797
297  );
298  }
299  }
300  }
301 
302  if (
303  isset($this->inheritanceStack[$pathToCheck])
304  && is_array($this->inheritanceStack[$pathToCheck])
305  && in_array($inheritancePath, $this->inheritanceStack[$pathToCheck])
306  ) {
307  $this->inheritanceStack[$pathToCheck][] = $inheritancePath;
308  throw new CycleInheritancesException(
309  $pathToCheck . ' has cycle inheritances',
310  1474900799
311  );
312  }
313  $this->inheritanceStack[$pathToCheck][] = $inheritancePath;
314  $this->‪throwExceptionIfCycleInheritances($inheritancePath, $pathToCheck);
315  }
316  $this->inheritanceStack[$pathToCheck] = null;
317  }
318  }
319  }
320 
327  protected function ‪removeInheritanceOperatorRecursive(array $array): array
328  {
329  $result = $array;
330  foreach ($result as $key => $value) {
331  if ($key === self::INHERITANCE_OPERATOR) {
332  unset($result[$key]);
333  continue;
334  }
335 
336  if (is_array($value)) {
337  $result[$key] = $this->‪removeInheritanceOperatorRecursive($value);
338  }
339  }
340  return $result;
341  }
342 
352  protected function ‪getValueByPath(array $config, string $path, string $delimiter = '.')
353  {
354  try {
355  return ‪ArrayUtility::getValueByPath($config, $path, $delimiter);
356  } catch (MissingArrayPathException $exception) {
357  return null;
358  }
359  }
360 }
‪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\reset
‪InheritancesResolverService reset()
Definition: InheritancesResolverService.php:113
‪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\Form\Mvc\Configuration\InheritancesResolverService\getResolvedConfiguration
‪array getResolvedConfiguration()
Definition: InheritancesResolverService.php:142
‪TYPO3\CMS\Core\Utility\ArrayUtility\getValueByPath
‪static mixed getValueByPath(array $array, $path, $delimiter='/')
Definition: ArrayUtility.php:180
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\removeInheritanceOperatorRecursive
‪array removeInheritanceOperatorRecursive(array $array)
Definition: InheritancesResolverService.php:324
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\resolveInheritancesRecursive
‪array resolveInheritancesRecursive(array $inheritances)
Definition: InheritancesResolverService.php:216
‪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:128
‪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:275
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\CycleInheritancesException
Definition: CycleInheritancesException.php:28
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\create
‪static InheritancesResolverService create(array $configuration=[])
Definition: InheritancesResolverService.php:99
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\resolve
‪array resolve(array $configuration, array $pathStack=[], bool $setInheritancePathToCheck=true)
Definition: InheritancesResolverService.php:163
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\getValueByPath
‪string array null getValueByPath(array $config, string $path, string $delimiter='.')
Definition: InheritancesResolverService.php:349