TYPO3 CMS  TYPO3_7-6
DependencyResolver.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
19 
26 {
30  const SYSEXT_FOLDER = 'typo3/sysext';
31 
36 
41  {
42  $this->dependencyOrderingService = $dependencyOrderingService;
43  }
44 
50  public function sortPackageStatesConfigurationByDependency(array $packageStatesConfiguration)
51  {
52  // We just want to consider active packages
53  $activePackageStatesConfiguration = array_filter($packageStatesConfiguration, function ($packageState) {
54  return isset($packageState['state']) && $packageState['state'] === 'active';
55  });
56  $inactivePackageStatesConfiguration = array_diff_key($packageStatesConfiguration, $activePackageStatesConfiguration);
57 
58  $sortedPackageKeys = $this->dependencyOrderingService->calculateOrder($this->buildDependencyGraph($activePackageStatesConfiguration));
59 
60  // Reorder the package states according to the loading order
61  $newPackageStatesConfiguration = [];
62  foreach ($sortedPackageKeys as $packageKey) {
63  $newPackageStatesConfiguration[$packageKey] = $packageStatesConfiguration[$packageKey];
64  }
65 
66  // Append the inactive configurations again
67  $newPackageStatesConfiguration = array_merge($newPackageStatesConfiguration, $inactivePackageStatesConfiguration);
68 
69  return $newPackageStatesConfiguration;
70  }
71 
82  protected function convertConfigurationForGraph(array $packageStatesConfiguration, array $packageKeys)
83  {
84  $dependencies = [];
85  foreach ($packageKeys as $packageKey) {
86  if (!isset($packageStatesConfiguration[$packageKey]['dependencies']) && !isset($packageStatesConfiguration[$packageKey]['suggestions'])) {
87  continue;
88  }
89  $dependencies[$packageKey] = [
90  'after' => []
91  ];
92  if (isset($packageStatesConfiguration[$packageKey]['dependencies'])) {
93  foreach ($packageStatesConfiguration[$packageKey]['dependencies'] as $dependentPackageKey) {
94  if (!in_array($dependentPackageKey, $packageKeys, true)) {
95  throw new \UnexpectedValueException(
96  'The package "' . $packageKey . '" depends on "'
97  . $dependentPackageKey . '" which is not present in the system.',
98  1382276561);
99  }
100  $dependencies[$packageKey]['after'][] = $dependentPackageKey;
101  }
102  }
103  if (isset($packageStatesConfiguration[$packageKey]['suggestions'])) {
104  foreach ($packageStatesConfiguration[$packageKey]['suggestions'] as $suggestedPackageKey) {
105  // skip suggestions on not existing packages
106  if (in_array($suggestedPackageKey, $packageKeys, true)) {
107  // Suggestions actually have never been meant to influence loading order.
108  // We misuse this currently, as there is no other way to influence the loading order
109  // for not-required packages (soft-dependency).
110  // When considering suggestions for the loading order, we might create a cyclic dependency
111  // if the suggested package already has a real dependency on this package, so the suggestion
112  // has do be dropped in this case and must *not* be taken into account for loading order evaluation.
113  $dependencies[$packageKey]['after-resilient'][] = $suggestedPackageKey;
114  }
115  }
116  }
117  }
118  return $dependencies;
119  }
120 
131  protected function addDependencyToFrameworkToAllExtensions(array $packageStateConfiguration, array $rootPackageKeys)
132  {
133  $frameworkPackageKeys = $this->findFrameworkPackages($packageStateConfiguration);
134  $extensionPackageKeys = array_diff(array_keys($packageStateConfiguration), $frameworkPackageKeys);
135  foreach ($extensionPackageKeys as $packageKey) {
136  // Remove framework packages from list
137  $packageKeysWithoutFramework = array_diff(
138  $packageStateConfiguration[$packageKey]['dependencies'],
139  $frameworkPackageKeys
140  );
141  // The order of the array_merge is crucial here,
142  // we want the framework first
143  $packageStateConfiguration[$packageKey]['dependencies'] = array_merge(
144  $rootPackageKeys, $packageKeysWithoutFramework
145  );
146  }
147  return $packageStateConfiguration;
148  }
149 
159  protected function buildDependencyGraph(array $packageStateConfiguration)
160  {
161  $frameworkPackageKeys = $this->findFrameworkPackages($packageStateConfiguration);
162  $frameworkPackagesDependencyGraph = $this->dependencyOrderingService->buildDependencyGraph($this->convertConfigurationForGraph($packageStateConfiguration, $frameworkPackageKeys));
163  $packageStateConfiguration = $this->addDependencyToFrameworkToAllExtensions($packageStateConfiguration, $this->dependencyOrderingService->findRootIds($frameworkPackagesDependencyGraph));
164 
165  $packageKeys = array_keys($packageStateConfiguration);
166  return $this->dependencyOrderingService->buildDependencyGraph($this->convertConfigurationForGraph($packageStateConfiguration, $packageKeys));
167  }
168 
174  protected function findFrameworkPackages(array $packageStateConfiguration)
175  {
176  $frameworkPackageKeys = [];
178  $packageManager = Bootstrap::getInstance()->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
179  foreach ($packageStateConfiguration as $packageKey => $packageConfiguration) {
181  $package = $packageManager->getPackage($packageKey);
182  if ($package->getValueFromComposerManifest('type') === 'typo3-cms-framework') {
183  $frameworkPackageKeys[] = $packageKey;
184  }
185  }
186 
187  return $frameworkPackageKeys;
188  }
189 }
buildDependencyGraph(array $packageStateConfiguration)
convertConfigurationForGraph(array $packageStatesConfiguration, array $packageKeys)
addDependencyToFrameworkToAllExtensions(array $packageStateConfiguration, array $rootPackageKeys)
sortPackageStatesConfigurationByDependency(array $packageStatesConfiguration)
injectDependencyOrderingService(DependencyOrderingService $dependencyOrderingService)