TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
DependencyUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility;
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 
21 use TYPO3\CMS\Extensionmanager\Exception;
22 
27 {
31  protected $objectManager;
32 
37 
41  protected $listUtility;
42 
46  protected $emConfUtility;
47 
51  protected $managementService;
52 
56  protected $availableExtensions = [];
57 
61  protected $localExtensionStorage = '';
62 
66  protected $dependencyErrors = [];
67 
71  protected $skipDependencyCheck = false;
72 
76  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
77  {
78  $this->objectManager = $objectManager;
79  }
80 
84  public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
85  {
86  $this->extensionRepository = $extensionRepository;
87  }
88 
92  public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
93  {
94  $this->listUtility = $listUtility;
95  }
96 
100  public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
101  {
102  $this->emConfUtility = $emConfUtility;
103  }
104 
108  public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
109  {
110  $this->managementService = $managementService;
111  }
112 
118  {
119  $this->localExtensionStorage = $localExtensionStorage;
120  }
121 
128  protected function setAvailableExtensions()
129  {
130  $this->availableExtensions = $this->listUtility->getAvailableExtensions();
131  }
132 
138  {
139  $this->skipDependencyCheck = $skipDependencyCheck;
140  }
141 
148  public function checkDependencies(Extension $extension)
149  {
150  $this->dependencyErrors = [];
151  $dependencies = $extension->getDependencies();
152  foreach ($dependencies as $dependency) {
154  $identifier = strtolower($dependency->getIdentifier());
155  try {
156  if (in_array($identifier, Dependency::$specialDependencies)) {
157  if (!$this->skipDependencyCheck) {
158  $methodName = 'check' . ucfirst($identifier) . 'Dependency';
159  $this->{$methodName}($dependency);
160  }
161  } else {
162  if ($dependency->getType() === 'depends') {
163  $this->checkExtensionDependency($dependency);
164  }
165  }
166  } catch (Exception\UnresolvedDependencyException $e) {
167  if (in_array($identifier, Dependency::$specialDependencies)) {
168  $extensionKey = $extension->getExtensionKey();
169  } else {
170  $extensionKey = $identifier;
171  }
172  if (!isset($this->dependencyErrors[$extensionKey])) {
173  $this->dependencyErrors[$extensionKey] = [];
174  }
175  $this->dependencyErrors[$extensionKey][] = [
176  'code' => $e->getCode(),
177  'message' => $e->getMessage()
178  ];
179  }
180  }
181  }
182 
188  public function hasDependencyErrors()
189  {
190  return !empty($this->dependencyErrors);
191  }
192 
198  public function getDependencyErrors()
199  {
201  }
202 
210  protected function checkTypo3Dependency(Dependency $dependency)
211  {
212  $lowerCaseIdentifier = strtolower($dependency->getIdentifier());
213  if ($lowerCaseIdentifier === 'typo3') {
214  if (!($dependency->getLowestVersion() === '') && version_compare(VersionNumberUtility::getNumericTypo3Version(), $dependency->getLowestVersion()) === -1) {
216  'Your TYPO3 version is lower than this extension requires. It requires TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
217  1399144499
218  );
219  }
220  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), VersionNumberUtility::getNumericTypo3Version()) === -1) {
222  'Your TYPO3 version is higher than this extension requires. It requires TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
223  1399144521
224  );
225  }
226  } else {
228  'checkTypo3Dependency can only check TYPO3 dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"',
229  1399144551
230  );
231  }
232  return true;
233  }
234 
242  protected function checkPhpDependency(Dependency $dependency)
243  {
244  $lowerCaseIdentifier = strtolower($dependency->getIdentifier());
245  if ($lowerCaseIdentifier === 'php') {
246  if (!($dependency->getLowestVersion() === '') && version_compare(PHP_VERSION, $dependency->getLowestVersion()) === -1) {
248  'Your PHP version is lower than necessary. You need at least PHP version ' . $dependency->getLowestVersion(),
249  1377977857
250  );
251  }
252  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), PHP_VERSION) === -1) {
254  'Your PHP version is higher than allowed. You can use PHP versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
255  1377977856
256  );
257  }
258  } else {
260  'checkPhpDependency can only check PHP dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"',
261  1377977858
262  );
263  }
264  return true;
265  }
266 
279  protected function checkExtensionDependency(Dependency $dependency)
280  {
281  $extensionKey = $dependency->getIdentifier();
282  $extensionIsLoaded = $this->isDependentExtensionLoaded($extensionKey);
283  if ($extensionIsLoaded === true) {
284  $isLoadedVersionCompatible = $this->isLoadedVersionCompatible($dependency);
285  if ($isLoadedVersionCompatible === true || $this->skipDependencyCheck) {
286  return true;
287  }
288  $extension = $this->listUtility->getExtension($extensionKey);
289  $loadedVersion = $extension->getPackageMetaData()->getVersion();
290  if (version_compare($loadedVersion, $dependency->getHighestVersion()) === -1) {
291  try {
292  $this->getExtensionFromRepository($extensionKey, $dependency);
293  } catch (Exception\UnresolvedDependencyException $e) {
295  'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion
296  . ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
297  1396302624
298  );
299  }
300  } else {
302  'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion .
303  ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
304  1430561927
305  );
306  }
307  } else {
308  $extensionIsAvailable = $this->isDependentExtensionAvailable($extensionKey);
309  if ($extensionIsAvailable === true) {
310  $isAvailableVersionCompatible = $this->isAvailableVersionCompatible($dependency);
311  if ($isAvailableVersionCompatible) {
312  $unresolvedDependencyErrors = $this->dependencyErrors;
313  $this->managementService->markExtensionForInstallation($extensionKey);
314  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
315  } else {
316  $extension = $this->listUtility->getExtension($extensionKey);
317  $availableVersion = $extension->getPackageMetaData()->getVersion();
318  if (version_compare($availableVersion, $dependency->getHighestVersion()) === -1) {
319  try {
320  $this->getExtensionFromRepository($extensionKey, $dependency);
321  } catch (Exception\MissingExtensionDependencyException $e) {
322  if (!$this->skipDependencyCheck) {
324  'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
325  . ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
326  1430560390
327  );
328  }
329  }
330  } else {
331  if (!$this->skipDependencyCheck) {
333  'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
334  . ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
335  1430562374
336  );
337  }
338  // Dependency check is skipped and the local version has to be installed
339  $this->managementService->markExtensionForInstallation($extensionKey);
340  }
341  }
342  } else {
343  $unresolvedDependencyErrors = $this->dependencyErrors;
344  $this->getExtensionFromRepository($extensionKey, $dependency);
345  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
346  }
347  }
348 
349  return false;
350  }
351 
361  protected function getExtensionFromRepository($extensionKey, Dependency $dependency)
362  {
363  if (!$this->getExtensionFromInExtensionRepository($extensionKey)) {
364  $this->getExtensionFromTer($extensionKey, $dependency);
365  }
366  }
367 
375  protected function getExtensionFromInExtensionRepository($extensionKey)
376  {
377  if ($this->localExtensionStorage !== '' && is_dir($this->localExtensionStorage)) {
378  $extList = \TYPO3\CMS\Core\Utility\GeneralUtility::get_dirs($this->localExtensionStorage);
379  if (in_array($extensionKey, $extList)) {
380  $this->managementService->markExtensionForCopy($extensionKey, $this->localExtensionStorage);
381  return true;
382  }
383  }
384  return false;
385  }
386 
396  protected function getExtensionFromTer($extensionKey, Dependency $dependency)
397  {
398  $isExtensionDownloadableFromTer = $this->isExtensionDownloadableFromTer($extensionKey);
399  if (!$isExtensionDownloadableFromTer) {
400  if (!$this->skipDependencyCheck) {
401  if ($this->extensionRepository->countAll() > 0) {
403  'The extension ' . $extensionKey . ' is not available from TER.',
404  1399161266
405  );
406  } else {
408  'The extension ' . $extensionKey . ' could not be checked. Please update your Extension-List from TYPO3 Extension Repository (TER).',
409  1430580308
410  );
411  }
412  }
413  return;
414  }
415 
416  $isDownloadableVersionCompatible = $this->isDownloadableVersionCompatible($dependency);
417  if (!$isDownloadableVersionCompatible) {
418  if (!$this->skipDependencyCheck) {
420  'No compatible version found for extension ' . $extensionKey,
421  1399161284
422  );
423  }
424  return;
425  }
426 
427  $latestCompatibleExtensionByIntegerVersionDependency = $this->getLatestCompatibleExtensionByIntegerVersionDependency($dependency);
428  if (!$latestCompatibleExtensionByIntegerVersionDependency instanceof Extension) {
429  if (!$this->skipDependencyCheck) {
431  'Could not resolve dependency for "' . $dependency->getIdentifier() . '"',
432  1399161302
433  );
434  }
435  return;
436  }
437 
438  if ($this->isDependentExtensionLoaded($extensionKey)) {
439  $this->managementService->markExtensionForUpdate($latestCompatibleExtensionByIntegerVersionDependency);
440  } else {
441  $this->managementService->markExtensionForDownload($latestCompatibleExtensionByIntegerVersionDependency);
442  }
443  }
444 
449  protected function isDependentExtensionLoaded($extensionKey)
450  {
451  return ExtensionManagementUtility::isLoaded($extensionKey);
452  }
453 
458  protected function isLoadedVersionCompatible(Dependency $dependency)
459  {
460  $extensionVersion = ExtensionManagementUtility::getExtensionVersion($dependency->getIdentifier());
461  return $this->isVersionCompatible($extensionVersion, $dependency);
462  }
463 
469  protected function isVersionCompatible($version, Dependency $dependency)
470  {
471  if (!($dependency->getLowestVersion() === '') && version_compare($version, $dependency->getLowestVersion()) === -1) {
472  return false;
473  }
474  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), $version) === -1) {
475  return false;
476  }
477  return true;
478  }
479 
487  protected function isDependentExtensionAvailable($extensionKey)
488  {
489  $this->setAvailableExtensions();
490  return array_key_exists($extensionKey, $this->availableExtensions);
491  }
492 
499  protected function isAvailableVersionCompatible(Dependency $dependency)
500  {
501  $this->setAvailableExtensions();
502  $extensionData = $this->emConfUtility->includeEmConf($this->availableExtensions[$dependency->getIdentifier()]);
503  return $this->isVersionCompatible($extensionData['version'], $dependency);
504  }
505 
512  protected function isExtensionDownloadableFromTer($extensionKey)
513  {
514  return $this->extensionRepository->countByExtensionKey($extensionKey) > 0;
515  }
516 
523  protected function isDownloadableVersionCompatible(Dependency $dependency)
524  {
525  $versions = $this->getLowestAndHighestIntegerVersions($dependency);
526  $count = $this->extensionRepository->countByVersionRangeAndExtensionKey(
527  $dependency->getIdentifier(), $versions['lowestIntegerVersion'], $versions['highestIntegerVersion']
528  );
529  return !empty($count);
530  }
531 
540  {
541  $versions = $this->getLowestAndHighestIntegerVersions($dependency);
542  $compatibleDataSets = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
543  $dependency->getIdentifier(),
544  $versions['lowestIntegerVersion'],
545  $versions['highestIntegerVersion']
546  );
547  return $compatibleDataSets->getFirst();
548  }
549 
556  protected function getLowestAndHighestIntegerVersions(Dependency $dependency)
557  {
558  $lowestVersion = $dependency->getLowestVersion();
559  $lowestVersionInteger = $lowestVersion ? VersionNumberUtility::convertVersionNumberToInteger($lowestVersion) : 0;
560  $highestVersion = $dependency->getHighestVersion();
561  $highestVersionInteger = $highestVersion ? VersionNumberUtility::convertVersionNumberToInteger($highestVersion) : 0;
562  return [
563  'lowestIntegerVersion' => $lowestVersionInteger,
564  'highestIntegerVersion' => $highestVersionInteger
565  ];
566  }
567 
572  public function findInstalledExtensionsThatDependOnMe($extensionKey)
573  {
574  $availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
575  $dependentExtensions = [];
576  foreach ($availableAndInstalledExtensions as $availableAndInstalledExtensionKey => $availableAndInstalledExtension) {
577  if (isset($availableAndInstalledExtension['installed']) && $availableAndInstalledExtension['installed'] === true) {
578  if (is_array($availableAndInstalledExtension['constraints']) && is_array($availableAndInstalledExtension['constraints']['depends']) && array_key_exists($extensionKey, $availableAndInstalledExtension['constraints']['depends'])) {
579  $dependentExtensions[] = $availableAndInstalledExtensionKey;
580  }
581  }
582  }
583  return $dependentExtensions;
584  }
585 
592  public function getExtensionsSuitableForTypo3Version($extensions)
593  {
594  $suitableExtensions = [];
596  foreach ($extensions as $extension) {
598  foreach ($extension->getDependencies() as $dependency) {
599  if ($dependency->getIdentifier() === 'typo3') {
600  try {
601  if ($this->checkTypo3Dependency($dependency)) {
602  array_push($suitableExtensions, $extension);
603  }
604  } catch (Exception\UnresolvedTypo3DependencyException $e) {
605  }
606  break;
607  }
608  }
609  }
610  return $suitableExtensions;
611  }
612 }
isVersionCompatible($version, Dependency $dependency)
injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
getExtensionFromRepository($extensionKey, Dependency $dependency)
injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
getExtensionFromTer($extensionKey, Dependency $dependency)
getLatestCompatibleExtensionByIntegerVersionDependency(Dependency $dependency)