‪TYPO3CMS  10.4
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 
26 
66 {
67 
71  const ‪INHERITANCE_OPERATOR = '__inheritances';
72 
79  protected ‪$referenceConfiguration = [];
80 
87  protected ‪$inheritanceStack = [];
88 
95  protected ‪$inheritancePathToCheck = '';
96 
105  public static function ‪create(array $configuration = []): ‪InheritancesResolverService
106  {
108  $inheritancesResolverService = GeneralUtility::makeInstance(ObjectManager::class)
109  ->get(self::class);
110  $inheritancesResolverService->‪setReferenceConfiguration($configuration);
111  return $inheritancesResolverService;
112  }
113 
121  public function ‪reset()
122  {
123  $this->referenceConfiguration = [];
124  $this->inheritanceStack = [];
125  $this->inheritancePathToCheck = '';
126  return $this;
127  }
128 
137  {
138  $this->referenceConfiguration = ‪$referenceConfiguration;
139  return $this;
140  }
141 
150  public function ‪getResolvedConfiguration(): array
151  {
152  $configuration = $this->‪resolve($this->referenceConfiguration);
153  $configuration = $this->‪removeInheritanceOperatorRecursive($configuration);
154  return $configuration;
155  }
156 
171  protected function ‪resolve(
172  array $configuration,
173  array $pathStack = [],
174  bool $setInheritancePathToCheck = true
175  ): array {
176  foreach ($configuration as $key => $values) {
177  //add current key to pathStack
178  $pathStack[] = $key;
179  $path = implode('.', $pathStack);
180 
181  //check endless loop for current path
182  $this->‪throwExceptionIfCycleInheritances($path, $path);
183 
184  //overwrite service property 'inheritancePathToCheck' with current path
185  if ($setInheritancePathToCheck) {
186  $this->inheritancePathToCheck = $path;
187  }
188 
189  //if value of subnode is an array, perform a deep search iteration step
190  if (is_array($configuration[$key])) {
191  if (isset($configuration[$key][self::INHERITANCE_OPERATOR])) {
192  $inheritances = $this->‪getValueByPath($this->referenceConfiguration, $path . '.' . self::INHERITANCE_OPERATOR);
193 
194  //and replace the __inheritance operator by the respective partial
195  if (is_array($inheritances)) {
196  $deprecatedMixinInheritances = array_filter(
197  $inheritances,
198  function (string $inheritance): bool {
199  return ‪StringUtility::beginsWith($inheritance, 'TYPO3.CMS.Form.mixins.');
200  }
201  );
202 
203  if (!empty($deprecatedMixinInheritances)) {
204  trigger_error(sprintf(
205  'Deprecated form mixins used in "%s": %s',
206  $path,
207  implode(', ', $deprecatedMixinInheritances)
208  ), E_USER_DEPRECATED);
209  }
210 
211  $inheritedConfigurations = $this->‪resolveInheritancesRecursive($inheritances);
212  $configuration[$key] = array_replace_recursive($inheritedConfigurations, $configuration[$key]);
213  }
214 
215  //remove the inheritance operator from configuration
216  unset($configuration[$key][self::INHERITANCE_OPERATOR]);
217  }
218 
219  if (!empty($configuration[$key])) {
220  // resolve subnode of YAML config
221  $configuration[$key] = $this->‪resolve($configuration[$key], $pathStack);
222  }
223  }
224  array_pop($pathStack);
225  }
226 
227  return $configuration;
228  }
229 
239  protected function ‪resolveInheritancesRecursive(array $inheritances): array
240  {
241  ksort($inheritances);
242  $inheritedConfigurations = [];
243  foreach ($inheritances as $inheritancePath) {
244  $this->‪throwExceptionIfCycleInheritances($inheritancePath, $inheritancePath);
245  $inheritedConfiguration = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath);
246 
247  if (
248  isset($inheritedConfiguration[self::INHERITANCE_OPERATOR])
249  && count($inheritedConfiguration) === 1
250  ) {
251  if ($this->inheritancePathToCheck === $inheritancePath) {
252  throw new CycleInheritancesException(
253  $this->inheritancePathToCheck . ' has cycle inheritances',
254  1474900796
255  );
256  }
257 
258  $inheritedConfiguration = $this->‪resolveInheritancesRecursive(
259  $inheritedConfiguration[self::INHERITANCE_OPERATOR]
260  );
261  } else {
262  $pathStack = explode('.', $inheritancePath);
263  $key = array_pop($pathStack);
264  $newConfiguration = [
265  $key => $inheritedConfiguration
266  ];
267  $inheritedConfiguration = $this->‪resolve(
268  $newConfiguration,
269  $pathStack,
270  false
271  );
272  $inheritedConfiguration = $inheritedConfiguration[$key];
273  }
274 
275  if ($inheritedConfiguration === null) {
276  throw new CycleInheritancesException(
277  $inheritancePath . ' does not exist within the configuration',
278  1489260796
279  );
280  }
281 
282  $inheritedConfigurations = array_replace_recursive(
283  $inheritedConfigurations,
284  $inheritedConfiguration
285  );
286  }
287 
288  return $inheritedConfigurations;
289  }
290 
298  protected function ‪throwExceptionIfCycleInheritances(string $path, string $pathToCheck)
299  {
300  $configuration = $this->‪getValueByPath($this->referenceConfiguration, $path);
301 
302  if (isset($configuration[self::INHERITANCE_OPERATOR])) {
303  $inheritances = $this->‪getValueByPath($this->referenceConfiguration, $path . '.' . self::INHERITANCE_OPERATOR);
304 
305  if (is_array($inheritances)) {
306  foreach ($inheritances as $inheritancePath) {
307  $configuration = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath);
308 
309  if (isset($configuration[self::INHERITANCE_OPERATOR])) {
310  $_inheritances = $this->‪getValueByPath($this->referenceConfiguration, $inheritancePath . '.' . self::INHERITANCE_OPERATOR);
311 
312  foreach ($_inheritances as $_inheritancePath) {
313  if (strpos($pathToCheck, $_inheritancePath) === 0) {
314  throw new CycleInheritancesException(
315  $pathToCheck . ' has cycle inheritances',
316  1474900797
317  );
318  }
319  }
320  }
321 
322  if (
323  isset($this->inheritanceStack[$pathToCheck])
324  && is_array($this->inheritanceStack[$pathToCheck])
325  && in_array($inheritancePath, $this->inheritanceStack[$pathToCheck])
326  ) {
327  $this->inheritanceStack[$pathToCheck][] = $inheritancePath;
328  throw new CycleInheritancesException(
329  $pathToCheck . ' has cycle inheritances',
330  1474900799
331  );
332  }
333  $this->inheritanceStack[$pathToCheck][] = $inheritancePath;
334  $this->‪throwExceptionIfCycleInheritances($inheritancePath, $pathToCheck);
335  }
336  $this->inheritanceStack[$pathToCheck] = null;
337  }
338  }
339  }
340 
347  protected function ‪removeInheritanceOperatorRecursive(array $array): array
348  {
349  $result = $array;
350  foreach ($result as $key => $value) {
351  if ($key === self::INHERITANCE_OPERATOR) {
352  unset($result[$key]);
353  continue;
354  }
355 
356  if (is_array($value)) {
357  $result[$key] = $this->‪removeInheritanceOperatorRecursive($value);
358  }
359  }
360  return $result;
361  }
362 
372  protected function ‪getValueByPath(array $config, string $path, string $delimiter = '.')
373  {
374  try {
375  return ‪ArrayUtility::getValueByPath($config, $path, $delimiter);
376  } catch (MissingArrayPathException $exception) {
377  return null;
378  }
379  }
380 }
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\INHERITANCE_OPERATOR
‪const INHERITANCE_OPERATOR
Definition: InheritancesResolverService.php:71
‪TYPO3\CMS\Form\Mvc\Configuration
Definition: ConfigurationManager.php:18
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\reset
‪InheritancesResolverService reset()
Definition: InheritancesResolverService.php:118
‪TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException
Definition: MissingArrayPathException.php:28
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\$inheritanceStack
‪array $inheritanceStack
Definition: InheritancesResolverService.php:85
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService
Definition: InheritancesResolverService.php:66
‪TYPO3\CMS\Core\Utility\StringUtility\beginsWith
‪static bool beginsWith($haystack, $needle)
Definition: StringUtility.php:32
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\getResolvedConfiguration
‪array getResolvedConfiguration()
Definition: InheritancesResolverService.php:147
‪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:344
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\resolveInheritancesRecursive
‪array resolveInheritancesRecursive(array $inheritances)
Definition: InheritancesResolverService.php:236
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\$referenceConfiguration
‪array $referenceConfiguration
Definition: InheritancesResolverService.php:78
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\setReferenceConfiguration
‪InheritancesResolverService setReferenceConfiguration(array $referenceConfiguration)
Definition: InheritancesResolverService.php:133
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\$inheritancePathToCheck
‪string $inheritancePathToCheck
Definition: InheritancesResolverService.php:92
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\throwExceptionIfCycleInheritances
‪throwExceptionIfCycleInheritances(string $path, string $pathToCheck)
Definition: InheritancesResolverService.php:295
‪TYPO3\CMS\Form\Mvc\Configuration\Exception\CycleInheritancesException
Definition: CycleInheritancesException.php:29
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\create
‪static InheritancesResolverService create(array $configuration=[])
Definition: InheritancesResolverService.php:102
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Extbase\Object\ObjectManager
Definition: ObjectManager.php:28
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\resolve
‪array resolve(array $configuration, array $pathStack=[], bool $setInheritancePathToCheck=true)
Definition: InheritancesResolverService.php:168
‪TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService\getValueByPath
‪string array null getValueByPath(array $config, string $path, string $delimiter='.')
Definition: InheritancesResolverService.php:369