TYPO3 CMS  TYPO3_8-7
DependencyUtility.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 
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 
117  {
118  $this->localExtensionStorage = $localExtensionStorage;
119  }
120 
125  protected function setAvailableExtensions()
126  {
127  $this->availableExtensions = $this->listUtility->getAvailableExtensions();
128  }
129 
134  {
135  $this->skipDependencyCheck = $skipDependencyCheck;
136  }
137 
143  public function checkDependencies(Extension $extension)
144  {
145  $this->dependencyErrors = [];
146  $dependencies = $extension->getDependencies();
147  foreach ($dependencies as $dependency) {
149  $identifier = strtolower($dependency->getIdentifier());
150  try {
151  if (in_array($identifier, Dependency::$specialDependencies)) {
152  if (!$this->skipDependencyCheck) {
153  $methodName = 'check' . ucfirst($identifier) . 'Dependency';
154  $this->{$methodName}($dependency);
155  }
156  } else {
157  if ($dependency->getType() === 'depends') {
158  $this->checkExtensionDependency($dependency);
159  }
160  }
162  if (in_array($identifier, Dependency::$specialDependencies)) {
163  $extensionKey = $extension->getExtensionKey();
164  } else {
165  $extensionKey = $identifier;
166  }
167  if (!isset($this->dependencyErrors[$extensionKey])) {
168  $this->dependencyErrors[$extensionKey] = [];
169  }
170  $this->dependencyErrors[$extensionKey][] = [
171  'code' => $e->getCode(),
172  'message' => $e->getMessage()
173  ];
174  }
175  }
176  }
177 
183  public function hasDependencyErrors()
184  {
185  return !empty($this->dependencyErrors);
186  }
187 
193  public function getDependencyErrors()
194  {
196  }
197 
205  protected function checkTypo3Dependency(Dependency $dependency)
206  {
207  $lowerCaseIdentifier = strtolower($dependency->getIdentifier());
208  if ($lowerCaseIdentifier === 'typo3') {
209  if (!($dependency->getLowestVersion() === '') && version_compare(VersionNumberUtility::getNumericTypo3Version(), $dependency->getLowestVersion()) === -1) {
210  throw new Exception\UnresolvedTypo3DependencyException(
211  'Your TYPO3 version is lower than this extension requires. It requires TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
212  1399144499
213  );
214  }
215  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), VersionNumberUtility::getNumericTypo3Version()) === -1) {
216  throw new Exception\UnresolvedTypo3DependencyException(
217  'Your TYPO3 version is higher than this extension requires. It requires TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
218  1399144521
219  );
220  }
221  } else {
222  throw new Exception\UnresolvedTypo3DependencyException(
223  'checkTypo3Dependency can only check TYPO3 dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"',
224  1399144551
225  );
226  }
227  return true;
228  }
229 
237  protected function checkPhpDependency(Dependency $dependency)
238  {
239  $lowerCaseIdentifier = strtolower($dependency->getIdentifier());
240  if ($lowerCaseIdentifier === 'php') {
241  if (!($dependency->getLowestVersion() === '') && version_compare(PHP_VERSION, $dependency->getLowestVersion()) === -1) {
242  throw new Exception\UnresolvedPhpDependencyException(
243  'Your PHP version is lower than necessary. You need at least PHP version ' . $dependency->getLowestVersion(),
244  1377977857
245  );
246  }
247  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), PHP_VERSION) === -1) {
248  throw new Exception\UnresolvedPhpDependencyException(
249  'Your PHP version is higher than allowed. You can use PHP versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
250  1377977856
251  );
252  }
253  } else {
254  throw new Exception\UnresolvedPhpDependencyException(
255  'checkPhpDependency can only check PHP dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"',
256  1377977858
257  );
258  }
259  return true;
260  }
261 
274  protected function checkExtensionDependency(Dependency $dependency)
275  {
276  $extensionKey = $dependency->getIdentifier();
277  $extensionIsLoaded = $this->isDependentExtensionLoaded($extensionKey);
278  if ($extensionIsLoaded === true) {
279  $isLoadedVersionCompatible = $this->isLoadedVersionCompatible($dependency);
280  if ($isLoadedVersionCompatible === true || $this->skipDependencyCheck) {
281  return true;
282  }
283  $extension = $this->listUtility->getExtension($extensionKey);
284  $loadedVersion = $extension->getPackageMetaData()->getVersion();
285  if (version_compare($loadedVersion, $dependency->getHighestVersion()) === -1) {
286  try {
287  $this->getExtensionFromRepository($extensionKey, $dependency);
289  throw new Exception\MissingVersionDependencyException(
290  'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion
291  . ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
292  1396302624
293  );
294  }
295  } else {
296  throw new Exception\MissingVersionDependencyException(
297  'The extension ' . $extensionKey . ' is installed in version ' . $loadedVersion .
298  ' but needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
299  1430561927
300  );
301  }
302  } else {
303  $extensionIsAvailable = $this->isDependentExtensionAvailable($extensionKey);
304  if ($extensionIsAvailable === true) {
305  $isAvailableVersionCompatible = $this->isAvailableVersionCompatible($dependency);
306  if ($isAvailableVersionCompatible) {
307  $unresolvedDependencyErrors = $this->dependencyErrors;
308  $this->managementService->markExtensionForInstallation($extensionKey);
309  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
310  } else {
311  $extension = $this->listUtility->getExtension($extensionKey);
312  $availableVersion = $extension->getPackageMetaData()->getVersion();
313  if (version_compare($availableVersion, $dependency->getHighestVersion()) === -1) {
314  try {
315  $this->getExtensionFromRepository($extensionKey, $dependency);
317  if (!$this->skipDependencyCheck) {
318  throw new Exception\MissingVersionDependencyException(
319  'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
320  . ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion() . ' and could not be fetched from TER',
321  1430560390
322  );
323  }
324  }
325  } else {
326  if (!$this->skipDependencyCheck) {
327  throw new Exception\MissingVersionDependencyException(
328  'The extension ' . $extensionKey . ' is available in version ' . $availableVersion
329  . ' but is needed in version ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion(),
330  1430562374
331  );
332  }
333  // Dependency check is skipped and the local version has to be installed
334  $this->managementService->markExtensionForInstallation($extensionKey);
335  }
336  }
337  } else {
338  $unresolvedDependencyErrors = $this->dependencyErrors;
339  $this->getExtensionFromRepository($extensionKey, $dependency);
340  $this->dependencyErrors = array_merge($unresolvedDependencyErrors, $this->dependencyErrors);
341  }
342  }
343 
344  return false;
345  }
346 
355  protected function getExtensionFromRepository($extensionKey, Dependency $dependency)
356  {
357  if (!$this->getExtensionFromInExtensionRepository($extensionKey)) {
358  $this->getExtensionFromTer($extensionKey, $dependency);
359  }
360  }
361 
369  protected function getExtensionFromInExtensionRepository($extensionKey)
370  {
371  if ($this->localExtensionStorage !== '' && is_dir($this->localExtensionStorage)) {
372  $extList = \TYPO3\CMS\Core\Utility\GeneralUtility::get_dirs($this->localExtensionStorage);
373  if (in_array($extensionKey, $extList)) {
374  $this->managementService->markExtensionForCopy($extensionKey, $this->localExtensionStorage);
375  return true;
376  }
377  }
378  return false;
379  }
380 
389  protected function getExtensionFromTer($extensionKey, Dependency $dependency)
390  {
391  $isExtensionDownloadableFromTer = $this->isExtensionDownloadableFromTer($extensionKey);
392  if (!$isExtensionDownloadableFromTer) {
393  if (!$this->skipDependencyCheck) {
394  if ($this->extensionRepository->countAll() > 0) {
395  throw new Exception\MissingExtensionDependencyException(
396  'The extension ' . $extensionKey . ' is not available from TER.',
397  1399161266
398  );
399  }
400  throw new Exception\MissingExtensionDependencyException(
401  'The extension ' . $extensionKey . ' could not be checked. Please update your Extension-List from TYPO3 Extension Repository (TER).',
402  1430580308
403  );
404  }
405  return;
406  }
407 
408  $isDownloadableVersionCompatible = $this->isDownloadableVersionCompatible($dependency);
409  if (!$isDownloadableVersionCompatible) {
410  if (!$this->skipDependencyCheck) {
411  throw new Exception\MissingVersionDependencyException(
412  'No compatible version found for extension ' . $extensionKey,
413  1399161284
414  );
415  }
416  return;
417  }
418 
419  $latestCompatibleExtensionByIntegerVersionDependency = $this->getLatestCompatibleExtensionByIntegerVersionDependency($dependency);
420  if (!$latestCompatibleExtensionByIntegerVersionDependency instanceof Extension) {
421  if (!$this->skipDependencyCheck) {
422  throw new Exception\MissingExtensionDependencyException(
423  'Could not resolve dependency for "' . $dependency->getIdentifier() . '"',
424  1399161302
425  );
426  }
427  return;
428  }
429 
430  if ($this->isDependentExtensionLoaded($extensionKey)) {
431  $this->managementService->markExtensionForUpdate($latestCompatibleExtensionByIntegerVersionDependency);
432  } else {
433  $this->managementService->markExtensionForDownload($latestCompatibleExtensionByIntegerVersionDependency);
434  }
435  }
436 
441  protected function isDependentExtensionLoaded($extensionKey)
442  {
443  return ExtensionManagementUtility::isLoaded($extensionKey);
444  }
445 
450  protected function isLoadedVersionCompatible(Dependency $dependency)
451  {
452  $extensionVersion = ExtensionManagementUtility::getExtensionVersion($dependency->getIdentifier());
453  return $this->isVersionCompatible($extensionVersion, $dependency);
454  }
455 
461  protected function isVersionCompatible($version, Dependency $dependency)
462  {
463  if (!($dependency->getLowestVersion() === '') && version_compare($version, $dependency->getLowestVersion()) === -1) {
464  return false;
465  }
466  if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), $version) === -1) {
467  return false;
468  }
469  return true;
470  }
471 
479  protected function isDependentExtensionAvailable($extensionKey)
480  {
481  $this->setAvailableExtensions();
482  return array_key_exists($extensionKey, $this->availableExtensions);
483  }
484 
491  protected function isAvailableVersionCompatible(Dependency $dependency)
492  {
493  $this->setAvailableExtensions();
494  $extensionData = $this->emConfUtility->includeEmConf($this->availableExtensions[$dependency->getIdentifier()]);
495  return $this->isVersionCompatible($extensionData['version'], $dependency);
496  }
497 
504  protected function isExtensionDownloadableFromTer($extensionKey)
505  {
506  return $this->extensionRepository->countByExtensionKey($extensionKey) > 0;
507  }
508 
515  protected function isDownloadableVersionCompatible(Dependency $dependency)
516  {
517  $versions = $this->getLowestAndHighestIntegerVersions($dependency);
518  $count = $this->extensionRepository->countByVersionRangeAndExtensionKey(
519  $dependency->getIdentifier(),
520  $versions['lowestIntegerVersion'],
521  $versions['highestIntegerVersion']
522  );
523  return !empty($count);
524  }
525 
534  {
535  $versions = $this->getLowestAndHighestIntegerVersions($dependency);
536  $compatibleDataSets = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
537  $dependency->getIdentifier(),
538  $versions['lowestIntegerVersion'],
539  $versions['highestIntegerVersion']
540  );
541  return $compatibleDataSets->getFirst();
542  }
543 
550  protected function getLowestAndHighestIntegerVersions(Dependency $dependency)
551  {
552  $lowestVersion = $dependency->getLowestVersion();
553  $lowestVersionInteger = $lowestVersion ? VersionNumberUtility::convertVersionNumberToInteger($lowestVersion) : 0;
554  $highestVersion = $dependency->getHighestVersion();
555  $highestVersionInteger = $highestVersion ? VersionNumberUtility::convertVersionNumberToInteger($highestVersion) : 0;
556  return [
557  'lowestIntegerVersion' => $lowestVersionInteger,
558  'highestIntegerVersion' => $highestVersionInteger
559  ];
560  }
561 
566  public function findInstalledExtensionsThatDependOnMe($extensionKey)
567  {
568  $availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
569  $dependentExtensions = [];
570  foreach ($availableAndInstalledExtensions as $availableAndInstalledExtensionKey => $availableAndInstalledExtension) {
571  if (isset($availableAndInstalledExtension['installed']) && $availableAndInstalledExtension['installed'] === true) {
572  if (is_array($availableAndInstalledExtension['constraints']) && is_array($availableAndInstalledExtension['constraints']['depends']) && array_key_exists($extensionKey, $availableAndInstalledExtension['constraints']['depends'])) {
573  $dependentExtensions[] = $availableAndInstalledExtensionKey;
574  }
575  }
576  }
577  return $dependentExtensions;
578  }
579 
586  public function getExtensionsSuitableForTypo3Version($extensions)
587  {
588  $suitableExtensions = [];
590  foreach ($extensions as $extension) {
592  foreach ($extension->getDependencies() as $dependency) {
593  if ($dependency->getIdentifier() === 'typo3') {
594  try {
595  if ($this->checkTypo3Dependency($dependency)) {
596  $suitableExtensions[] = $extension;
597  }
599  }
600  break;
601  }
602  }
603  }
604  return $suitableExtensions;
605  }
606 
617  public function filterYoungestVersionOfExtensionList(array $extensions, $showUnsuitable)
618  {
619  if (!$showUnsuitable) {
620  $extensions = $this->getExtensionsSuitableForTypo3Version($extensions);
621  }
622  $filteredExtensions = [];
623  foreach ($extensions as $extension) {
624  $extensionKey = $extension->getExtensionKey();
625  if (!array_key_exists($extensionKey, $filteredExtensions)) {
626  $filteredExtensions[$extensionKey] = $extension;
627  continue;
628  }
629  $currentVersion = $filteredExtensions[$extensionKey]->getVersion();
630  $newVersion = $extension->getVersion();
631  if (version_compare($newVersion, $currentVersion, '>')) {
632  $filteredExtensions[$extensionKey] = $extension;
633  }
634  }
635  return $filteredExtensions;
636  }
637 }
getExtensionFromRepository($extensionKey, Dependency $dependency)
getExtensionFromTer($extensionKey, Dependency $dependency)
filterYoungestVersionOfExtensionList(array $extensions, $showUnsuitable)
injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
isVersionCompatible($version, Dependency $dependency)
injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService)
getLatestCompatibleExtensionByIntegerVersionDependency(Dependency $dependency)
injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)