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