TYPO3  7.6
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 = array();
57 
61  protected $localExtensionStorage = '';
62 
66  protected $dependencyErrors = array();
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 = array();
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] = array();
174  }
175  $this->dependencyErrors[$extensionKey][] = array(
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  }
339  }
340  } else {
341  $unresolvedDependencyErrors = $this->dependencyErrors;
342  $this->getExtensionFromRepository($extensionKey, $dependency);
343  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
344  }
345  }
346 
347  return false;
348  }
349 
359  protected function getExtensionFromRepository($extensionKey, Dependency $dependency)
360  {
361  if (!$this->getExtensionFromInExtensionRepository($extensionKey)) {
362  $this->getExtensionFromTer($extensionKey, $dependency);
363  }
364  }
365 
373  protected function getExtensionFromInExtensionRepository($extensionKey)
374  {
375  if ($this->localExtensionStorage !== '' && is_dir($this->localExtensionStorage)) {
376  $extList = \TYPO3\CMS\Core\Utility\GeneralUtility::get_dirs($this->localExtensionStorage);
377  if (in_array($extensionKey, $extList)) {
378  $this->managementService->markExtensionForCopy($extensionKey, $this->localExtensionStorage);
379  return true;
380  }
381  }
382  return false;
383  }
384 
394  protected function getExtensionFromTer($extensionKey, Dependency $dependency)
395  {
396  $isExtensionDownloadableFromTer = $this->isExtensionDownloadableFromTer($extensionKey);
397  if (!$isExtensionDownloadableFromTer) {
398  if (!$this->skipDependencyCheck) {
399  if ($this->extensionRepository->countAll() > 0) {
401  'The extension ' . $extensionKey . ' is not available from TER.',
402  1399161266
403  );
404  } else {
406  'The extension ' . $extensionKey . ' could not be checked. Please update your Extension-List from TYPO3 Extension Repository (TER).',
407  1430580308
408  );
409  }
410  }
411  return;
412  }
413 
414  $isDownloadableVersionCompatible = $this->isDownloadableVersionCompatible($dependency);
415  if (!$isDownloadableVersionCompatible) {
416  if (!$this->skipDependencyCheck) {
418  'No compatible version found for extension ' . $extensionKey,
419  1399161284
420  );
421  }
422  return;
423  }
424 
425  $latestCompatibleExtensionByIntegerVersionDependency = $this->getLatestCompatibleExtensionByIntegerVersionDependency($dependency);
426  if (!$latestCompatibleExtensionByIntegerVersionDependency instanceof Extension) {
427  if (!$this->skipDependencyCheck) {
429  'Could not resolve dependency for "' . $dependency->getIdentifier() . '"',
430  1399161302
431  );
432  }
433  return;
434  }
435 
436  if ($this->isDependentExtensionLoaded($extensionKey)) {
437  $this->managementService->markExtensionForUpdate($latestCompatibleExtensionByIntegerVersionDependency);
438  } else {
439  $this->managementService->markExtensionForDownload($latestCompatibleExtensionByIntegerVersionDependency);
440  }
441  }
442 
447  protected function isDependentExtensionLoaded($extensionKey)
448  {
449  return ExtensionManagementUtility::isLoaded($extensionKey);
450  }
451 
456  protected function isLoadedVersionCompatible(Dependency $dependency)
457  {
458  $extensionVersion = ExtensionManagementUtility::getExtensionVersion($dependency->getIdentifier());
459  return $this->isVersionCompatible($extensionVersion, $dependency);
460  }
461 
467  protected function isVersionCompatible($version, Dependency $dependency)
468  {
469  if (!($dependency->getLowestVersion() === '') && version_compare($version, $dependency->getLowestVersion()) === -1) {
470  return false;
471  }
472  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), $version) === -1) {
473  return false;
474  }
475  return true;
476  }
477 
485  protected function isDependentExtensionAvailable($extensionKey)
486  {
487  $this->setAvailableExtensions();
488  return array_key_exists($extensionKey, $this->availableExtensions);
489  }
490 
497  protected function isAvailableVersionCompatible(Dependency $dependency)
498  {
499  $this->setAvailableExtensions();
500  $extensionData = $this->emConfUtility->includeEmConf($this->availableExtensions[$dependency->getIdentifier()]);
501  return $this->isVersionCompatible($extensionData['version'], $dependency);
502  }
503 
510  protected function isExtensionDownloadableFromTer($extensionKey)
511  {
512  return $this->extensionRepository->countByExtensionKey($extensionKey) > 0;
513  }
514 
521  protected function isDownloadableVersionCompatible(Dependency $dependency)
522  {
523  $versions = $this->getLowestAndHighestIntegerVersions($dependency);
524  $count = $this->extensionRepository->countByVersionRangeAndExtensionKey(
525  $dependency->getIdentifier(), $versions['lowestIntegerVersion'], $versions['highestIntegerVersion']
526  );
527  return !empty($count);
528  }
529 
538  {
539  $versions = $this->getLowestAndHighestIntegerVersions($dependency);
540  $compatibleDataSets = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
541  $dependency->getIdentifier(),
542  $versions['lowestIntegerVersion'],
543  $versions['highestIntegerVersion']
544  );
545  return $compatibleDataSets->getFirst();
546  }
547 
554  protected function getLowestAndHighestIntegerVersions(Dependency $dependency)
555  {
556  $lowestVersion = $dependency->getLowestVersion();
557  $lowestVersionInteger = $lowestVersion ? VersionNumberUtility::convertVersionNumberToInteger($lowestVersion) : 0;
558  $highestVersion = $dependency->getHighestVersion();
559  $highestVersionInteger = $highestVersion ? VersionNumberUtility::convertVersionNumberToInteger($highestVersion) : 0;
560  return array(
561  'lowestIntegerVersion' => $lowestVersionInteger,
562  'highestIntegerVersion' => $highestVersionInteger
563  );
564  }
565 
570  public function findInstalledExtensionsThatDependOnMe($extensionKey)
571  {
572  $availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
573  $dependentExtensions = array();
574  foreach ($availableAndInstalledExtensions as $availableAndInstalledExtensionKey => $availableAndInstalledExtension) {
575  if (isset($availableAndInstalledExtension['installed']) && $availableAndInstalledExtension['installed'] === true) {
576  if (is_array($availableAndInstalledExtension['constraints']) && is_array($availableAndInstalledExtension['constraints']['depends']) && array_key_exists($extensionKey, $availableAndInstalledExtension['constraints']['depends'])) {
577  $dependentExtensions[] = $availableAndInstalledExtensionKey;
578  }
579  }
580  }
581  return $dependentExtensions;
582  }
583 
590  public function getExtensionsSuitableForTypo3Version($extensions)
591  {
592  $suitableExtensions = array();
594  foreach ($extensions as $extension) {
596  foreach ($extension->getDependencies() as $dependency) {
597  if ($dependency->getIdentifier() === 'typo3') {
598  try {
599  if ($this->checkTypo3Dependency($dependency)) {
600  array_push($suitableExtensions, $extension);
601  }
602  } catch (Exception\UnresolvedTypo3DependencyException $e) {
603  }
604  break;
605  }
606  }
607  }
608  return $suitableExtensions;
609  }
610 }