‪TYPO3CMS  ‪main
DependencyUtility.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 
30 use TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService;
31 
37 {
41  protected ‪$extensionRepository;
42 
46  protected ‪$listUtility;
47 
51  protected ‪$emConfUtility;
52 
57 
61  protected ‪$availableExtensions = [];
62 
66  protected ‪$dependencyErrors = [];
67 
71  protected ‪$skipDependencyCheck = false;
72 
74  {
75  $this->extensionRepository = ‪$extensionRepository;
76  }
77 
79  {
80  $this->listUtility = ‪$listUtility;
81  }
82 
83  public function ‪injectEmConfUtility(EmConfUtility ‪$emConfUtility)
84  {
85  $this->emConfUtility = ‪$emConfUtility;
86  }
87 
88  public function ‪injectManagementService(ExtensionManagementService ‪$managementService)
89  {
90  $this->managementService = ‪$managementService;
91  }
92 
97  protected function ‪setAvailableExtensions()
98  {
99  $this->availableExtensions = $this->listUtility->getAvailableExtensions();
100  }
101 
106  {
107  $this->skipDependencyCheck = ‪$skipDependencyCheck;
108  }
109 
113  public function ‪checkDependencies(‪Extension $extension)
114  {
115  $this->dependencyErrors = [];
116  $dependencies = $extension->‪getDependencies();
117  foreach ($dependencies as $dependency) {
119  ‪$identifier = $dependency->getIdentifier();
120  try {
122  if ($this->skipDependencyCheck) {
123  continue;
124  }
125  if (‪$identifier === 'typo3') {
127  }
128  if (‪$identifier === 'php') {
129  $this->‪checkPhpDependency($dependency, PHP_VERSION);
130  }
131  } elseif ($dependency->getType() === 'depends') {
132  $this->‪checkExtensionDependency($dependency);
133  }
134  } catch (UnresolvedDependencyException $e) {
136  $extensionKey = $extension->‪getExtensionKey();
137  } else {
138  $extensionKey = ‪$identifier;
139  }
140  if (!isset($this->dependencyErrors[$extensionKey])) {
141  $this->dependencyErrors[$extensionKey] = [];
142  }
143  $this->dependencyErrors[$extensionKey][] = [
144  'code' => $e->getCode(),
145  'message' => $e->getMessage(),
146  ];
147  }
148  }
149  }
150 
156  public function ‪hasDependencyErrors()
157  {
158  return !empty($this->dependencyErrors);
159  }
160 
164  public function ‪getDependencyErrors(): array
165  {
167  }
168 
174  protected function ‪checkTypo3Dependency(‪Dependency $dependency, string $version): bool
175  {
176  if ($dependency->‪getIdentifier() === 'typo3') {
177  if (!($dependency->‪getLowestVersion() === '') && version_compare($version, $dependency->‪getLowestVersion()) === -1) {
178  throw new Exception\UnresolvedTypo3DependencyException(
179  'Your TYPO3 version is lower than this extension requires. It requires TYPO3 versions ' . $dependency->‪getLowestVersion() . ' - ' . $dependency->‪getHighestVersion(),
180  1399144499
181  );
182  }
183  if (!($dependency->‪getHighestVersion() === '') && version_compare($dependency->‪getHighestVersion(), $version) === -1) {
184  throw new Exception\UnresolvedTypo3DependencyException(
185  'Your TYPO3 version is higher than this extension requires. It requires TYPO3 versions ' . $dependency->‪getLowestVersion() . ' - ' . $dependency->‪getHighestVersion(),
186  1399144521
187  );
188  }
189  } else {
190  throw new Exception\UnresolvedTypo3DependencyException(
191  'checkTypo3Dependency can only check TYPO3 dependencies. Found dependency with identifier "' . $dependency->‪getIdentifier() . '"',
192  1399144551
193  );
194  }
195  return true;
196  }
197 
203  protected function ‪checkPhpDependency(‪Dependency $dependency, string $version): bool
204  {
205  if ($dependency->‪getIdentifier() === 'php') {
206  if (!($dependency->‪getLowestVersion() === '') && version_compare($version, $dependency->‪getLowestVersion()) === -1) {
207  throw new Exception\UnresolvedPhpDependencyException(
208  'Your PHP version is lower than necessary. You need at least PHP version ' . $dependency->‪getLowestVersion(),
209  1377977857
210  );
211  }
212  if (!($dependency->‪getHighestVersion() === '') && version_compare($dependency->‪getHighestVersion(), $version) === -1) {
213  throw new Exception\UnresolvedPhpDependencyException(
214  'Your PHP version is higher than allowed. You can use PHP versions ' . $dependency->‪getLowestVersion() . ' - ' . $dependency->‪getHighestVersion(),
215  1377977856
216  );
217  }
218  } else {
219  throw new Exception\UnresolvedPhpDependencyException(
220  'checkPhpDependency can only check PHP dependencies. Found dependency with identifier "' . $dependency->‪getIdentifier() . '"',
221  1377977858
222  );
223  }
224  return true;
225  }
226 
238  protected function ‪checkExtensionDependency(Dependency $dependency)
239  {
240  $extensionKey = $dependency->getIdentifier();
241  $extensionIsLoaded = $this->‪isDependentExtensionLoaded($extensionKey);
242  if ($extensionIsLoaded === true) {
243  if ($this->skipDependencyCheck || $this->‪isLoadedVersionCompatible($dependency)) {
244  return true;
245  }
246  $extension = $this->listUtility->getExtension($extensionKey);
247  $loadedVersion = $extension->getPackageMetaData()->‪getVersion();
248  if (version_compare($loadedVersion, $dependency->getHighestVersion()) === -1) {
249  try {
250  $this->‪downloadExtensionFromRemote($extensionKey, $dependency);
251  } catch (UnresolvedDependencyException $e) {
252  throw new MissingVersionDependencyException(
253  'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion
254  . ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
255  1396302624
256  );
257  }
258  } else {
259  throw new MissingVersionDependencyException(
260  'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion .
261  ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
262  1430561927
263  );
264  }
265  } else {
266  $extensionIsAvailable = $this->‪isDependentExtensionAvailable($extensionKey);
267  if ($extensionIsAvailable === true) {
268  $isAvailableVersionCompatible = $this->‪isAvailableVersionCompatible($dependency);
269  if ($isAvailableVersionCompatible) {
270  $unresolvedDependencyErrors = ‪$this->dependencyErrors;
271  $this->managementService->markExtensionForInstallation($extensionKey);
272  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
273  } else {
274  $extension = $this->listUtility->getExtension($extensionKey);
275  $availableVersion = $extension->getPackageMetaData()->‪getVersion();
276  if (version_compare($availableVersion, $dependency->getHighestVersion()) === -1) {
277  try {
278  $this->‪downloadExtensionFromRemote($extensionKey, $dependency);
279  } catch (MissingExtensionDependencyException $e) {
280  if (!$this->skipDependencyCheck) {
281  throw new MissingVersionDependencyException(
282  'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
283  . ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
284  1430560390
285  );
286  }
287  }
288  } else {
289  if (!$this->skipDependencyCheck) {
290  throw new MissingVersionDependencyException(
291  'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
292  . ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
293  1430562374
294  );
295  }
296  // Dependency check is skipped and the local version has to be installed
297  $this->managementService->markExtensionForInstallation($extensionKey);
298  }
299  }
300  } else {
301  $unresolvedDependencyErrors = ‪$this->dependencyErrors;
302  $this->‪downloadExtensionFromRemote($extensionKey, $dependency);
303  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
304  }
305  }
306 
307  return false;
308  }
309 
315  protected function ‪downloadExtensionFromRemote(string $extensionKey, ‪Dependency $dependency)
316  {
317  if (!$this->‪isExtensionDownloadableFromRemote($extensionKey)) {
318  if (!$this->skipDependencyCheck) {
319  if ($this->extensionRepository->countAll() > 0) {
321  'The extension ' . $extensionKey . ' is not available from TER.',
322  1399161266
323  );
324  }
326  'The extension ' . $extensionKey . ' could not be checked. Please update your Extension-List from TYPO3 Extension Repository (TER).',
327  1430580308
328  );
329  }
330  return;
331  }
332 
333  $isDownloadableVersionCompatible = $this->‪isDownloadableVersionCompatible($dependency);
334  if (!$isDownloadableVersionCompatible) {
335  if (!$this->skipDependencyCheck) {
336  throw new MissingVersionDependencyException(
337  'No compatible version found for extension ' . $extensionKey,
338  1399161284
339  );
340  }
341  return;
342  }
343 
344  $latestCompatibleExtensionByDependency = $this->‪getLatestCompatibleExtensionByDependency($dependency);
345  if ($latestCompatibleExtensionByDependency === null) {
346  if (!$this->skipDependencyCheck) {
347  throw new MissingExtensionDependencyException(
348  'Could not resolve dependency for "' . $dependency->‪getIdentifier() . '"',
349  1399161302
350  );
351  }
352  return;
353  }
354 
355  if ($this->‪isDependentExtensionLoaded($extensionKey)) {
356  $this->managementService->markExtensionForUpdate($latestCompatibleExtensionByDependency);
357  } else {
358  $this->managementService->markExtensionForDownload($latestCompatibleExtensionByDependency);
359  }
360  }
361 
366  protected function ‪isDependentExtensionLoaded($extensionKey)
367  {
368  return ‪ExtensionManagementUtility::isLoaded($extensionKey);
369  }
370 
371  protected function ‪isLoadedVersionCompatible(Dependency $dependency): bool
372  {
373  $extensionVersion = ‪ExtensionManagementUtility::getExtensionVersion($dependency->getIdentifier());
374  return $dependency->isVersionCompatible($extensionVersion);
375  }
376 
381  protected function ‪isDependentExtensionAvailable(string $extensionKey): bool
382  {
384  return array_key_exists($extensionKey, $this->availableExtensions);
385  }
386 
390  protected function ‪isAvailableVersionCompatible(‪Dependency $dependency): bool
391  {
392  $this->‪setAvailableExtensions();
393  $extensionData = $this->emConfUtility->includeEmConf(
394  $dependency->‪getIdentifier(),
395  $this->availableExtensions[$dependency->‪getIdentifier()]['packagePath'] ?? ''
396  );
397  return $dependency->‪isVersionCompatible($extensionData['version']);
398  }
399 
403  protected function ‪isExtensionDownloadableFromRemote(string $extensionKey): bool
404  {
405  return $this->extensionRepository->count(['extensionKey' => $extensionKey]) > 0;
406  }
407 
411  protected function ‪isDownloadableVersionCompatible(‪Dependency $dependency): bool
412  {
413  $count = $this->extensionRepository->countByVersionRangeAndExtensionKey(
414  $dependency->‪getIdentifier(),
415  $dependency->‪getLowestVersionAsInteger(),
416  $dependency->‪getHighestVersionAsInteger()
417  );
418  return !empty($count);
419  }
420 
425  protected function ‪getCompatibleExtension(iterable $extensions): ?‪Extension
426  {
427  foreach ($extensions as $extension) {
429  $this->‪checkDependencies($extension);
430  $extensionKey = $extension->getExtensionKey();
431 
432  if (isset($this->dependencyErrors[$extensionKey])) {
433  // reset dependencyErrors and continue with next version
434  unset($this->dependencyErrors[$extensionKey]);
435  continue;
436  }
437 
438  return $extension;
439  }
440 
441  return null;
442  }
443 
449  {
450  $compatibleDataSets = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
451  $dependency->‪getIdentifier(),
452  $dependency->‪getLowestVersionAsInteger(),
453  $dependency->‪getHighestVersionAsInteger()
454  );
455  return $this->‪getCompatibleExtension($compatibleDataSets);
456  }
457 }
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\getHighestVersion
‪getHighestVersion()
Definition: Dependency.php:80
‪TYPO3\CMS\Core\Utility\VersionNumberUtility
Definition: VersionNumberUtility.php:26
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency
Definition: Dependency.php:28
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\injectListUtility
‪injectListUtility(ListUtility $listUtility)
Definition: DependencyUtility.php:71
‪TYPO3\CMS\Extensionmanager\Utility
Definition: DependencyUtility.php:18
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getExtensionVersion
‪static string getExtensionVersion(string $key)
Definition: ExtensionManagementUtility.php:113
‪TYPO3\CMS\Extensionmanager\Domain\Model\Extension
Definition: Extension.php:30
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\getLowestVersion
‪getLowestVersion()
Definition: Dependency.php:90
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\injectEmConfUtility
‪injectEmConfUtility(EmConfUtility $emConfUtility)
Definition: DependencyUtility.php:76
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$extensionRepository
‪ExtensionRepository $extensionRepository
Definition: DependencyUtility.php:40
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\checkExtensionDependency
‪bool checkExtensionDependency(Dependency $dependency)
Definition: DependencyUtility.php:231
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$availableExtensions
‪array $availableExtensions
Definition: DependencyUtility.php:56
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\getHighestVersionAsInteger
‪getHighestVersionAsInteger()
Definition: Dependency.php:126
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$emConfUtility
‪EmConfUtility $emConfUtility
Definition: DependencyUtility.php:48
‪TYPO3\CMS\Extensionmanager\Exception
Definition: ExtensionManagerException.php:18
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$managementService
‪ExtensionManagementService $managementService
Definition: DependencyUtility.php:52
‪TYPO3\CMS\Core\Utility\VersionNumberUtility\getNumericTypo3Version
‪static getNumericTypo3Version()
Definition: VersionNumberUtility.php:51
‪TYPO3\CMS\Extensionmanager\Exception\MissingVersionDependencyException
Definition: MissingVersionDependencyException.php:23
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\isAvailableVersionCompatible
‪isAvailableVersionCompatible(Dependency $dependency)
Definition: DependencyUtility.php:383
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\hasDependencyErrors
‪bool hasDependencyErrors()
Definition: DependencyUtility.php:149
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\isDependentExtensionLoaded
‪bool isDependentExtensionLoaded($extensionKey)
Definition: DependencyUtility.php:359
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\isExtensionDownloadableFromRemote
‪isExtensionDownloadableFromRemote(string $extensionKey)
Definition: DependencyUtility.php:396
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\getIdentifier
‪getIdentifier()
Definition: Dependency.php:85
‪TYPO3\CMS\Extensionmanager\Utility\ListUtility
Definition: ListUtility.php:41
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\setSkipDependencyCheck
‪setSkipDependencyCheck($skipDependencyCheck)
Definition: DependencyUtility.php:98
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\injectManagementService
‪injectManagementService(ExtensionManagementService $managementService)
Definition: DependencyUtility.php:81
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\getDependencyErrors
‪getDependencyErrors()
Definition: DependencyUtility.php:157
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static isLoaded(string $key)
Definition: ExtensionManagementUtility.php:55
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\checkPhpDependency
‪checkPhpDependency(Dependency $dependency, string $version)
Definition: DependencyUtility.php:196
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\injectExtensionRepository
‪injectExtensionRepository(ExtensionRepository $extensionRepository)
Definition: DependencyUtility.php:66
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:32
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$listUtility
‪ListUtility $listUtility
Definition: DependencyUtility.php:44
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility
Definition: DependencyUtility.php:37
‪TYPO3\CMS\Extensionmanager\Exception\MissingExtensionDependencyException
Definition: MissingExtensionDependencyException.php:23
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\checkDependencies
‪checkDependencies(Extension $extension)
Definition: DependencyUtility.php:106
‪TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
Definition: ExtensionRepository.php:37
‪TYPO3\CMS\Extensionmanager\Domain\Model\Extension\getDependencies
‪SplObjectStorage< Dependency > getDependencies()
Definition: Extension.php:337
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\setAvailableExtensions
‪setAvailableExtensions()
Definition: DependencyUtility.php:90
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\getLatestCompatibleExtensionByDependency
‪getLatestCompatibleExtensionByDependency(Dependency $dependency)
Definition: DependencyUtility.php:441
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\$specialDependencies
‪static array $specialDependencies
Definition: Dependency.php:38
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\isLoadedVersionCompatible
‪isLoadedVersionCompatible(Dependency $dependency)
Definition: DependencyUtility.php:364
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Extensionmanager\Domain\Model\Extension\getVersion
‪getVersion()
Definition: Extension.php:267
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\isVersionCompatible
‪bool isVersionCompatible(string $version)
Definition: Dependency.php:107
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$dependencyErrors
‪array $dependencyErrors
Definition: DependencyUtility.php:60
‪TYPO3\CMS\Extensionmanager\Domain\Model\Dependency\getLowestVersionAsInteger
‪getLowestVersionAsInteger()
Definition: Dependency.php:118
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\isDependentExtensionAvailable
‪isDependentExtensionAvailable(string $extensionKey)
Definition: DependencyUtility.php:374
‪TYPO3\CMS\Extensionmanager\Domain\Model\Extension\getExtensionKey
‪getExtensionKey()
Definition: Extension.php:176
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\getCompatibleExtension
‪getCompatibleExtension(iterable $extensions)
Definition: DependencyUtility.php:418
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\$skipDependencyCheck
‪bool $skipDependencyCheck
Definition: DependencyUtility.php:64
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\downloadExtensionFromRemote
‪downloadExtensionFromRemote(string $extensionKey, Dependency $dependency)
Definition: DependencyUtility.php:308
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\isDownloadableVersionCompatible
‪isDownloadableVersionCompatible(Dependency $dependency)
Definition: DependencyUtility.php:404
‪TYPO3\CMS\Extensionmanager\Exception\UnresolvedDependencyException
Definition: UnresolvedDependencyException.php:23
‪TYPO3\CMS\Extensionmanager\Utility\DependencyUtility\checkTypo3Dependency
‪checkTypo3Dependency(Dependency $dependency, string $version)
Definition: DependencyUtility.php:167