1b827b27ad8367735336431201f4c10357193d94
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Utility / DependencyUtility.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2012 Susanne Moog <susanne.moog@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Utility for dealing with dependencies
31 *
32 * @author Susanne Moog <susanne.moog@typo3.org>
33 * @package Extension Manager
34 * @subpackage Utility
35 */
36 class DependencyUtility implements \TYPO3\CMS\Core\SingletonInterface {
37
38 /**
39 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
40 */
41 protected $objectManager;
42
43 /**
44 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
45 */
46 protected $extensionRepository;
47
48 /**
49 * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
50 */
51 protected $listUtility;
52
53 /**
54 * @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility
55 */
56 protected $emConfUtility;
57
58 /**
59 * @var \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService
60 */
61 protected $managementService;
62
63 /**
64 * @var array
65 */
66 protected $availableExtensions = array();
67
68 /**
69 * @var array
70 */
71 protected $errors = array();
72
73 /**
74 * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
75 * @return void
76 */
77 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) {
78 $this->objectManager = $objectManager;
79 }
80
81 /**
82 * @param \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository
83 * @return void
84 */
85 public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository) {
86 $this->extensionRepository = $extensionRepository;
87 }
88
89 /**
90 * @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
91 * @return void
92 */
93 public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility) {
94 $this->listUtility = $listUtility;
95 }
96
97 /**
98 * @param \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility
99 * @return void
100 */
101 public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility) {
102 $this->emConfUtility = $emConfUtility;
103 }
104
105 /**
106 * @param \TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService
107 * @return void
108 */
109 public function injectManagementService(\TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService $managementService) {
110 $this->managementService = $managementService;
111 }
112
113 /**
114 * Setter for available extensions
115 * gets available extensions from list utility if not already done
116 *
117 * @return void
118 */
119 protected function setAvailableExtensions() {
120 $this->availableExtensions = $this->listUtility->getAvailableExtensions();
121 }
122
123 /**
124 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Extension $extension
125 * @return void
126 */
127 public function buildExtensionDependenciesTree($extension) {
128 $dependencies = $extension->getDependencies();
129 $this->checkDependencies($dependencies);
130 }
131
132 /**
133 * @param string $dependencies
134 * @return \SplObjectStorage
135 */
136 public function convertDependenciesToObjects($dependencies) {
137 $unserializedDependencies = unserialize($dependencies);
138 $dependenciesObject = new \SplObjectStorage();
139 foreach ($unserializedDependencies as $dependencyType => $dependencyValues) {
140 foreach ($dependencyValues as $dependency => $versions) {
141 if ($dependencyType && $dependency) {
142 list($highest, $lowest) = \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionsStringToVersionNumbers($versions);
143 /** @var $dependencyObject \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency */
144 $dependencyObject = $this->objectManager->create('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Dependency');
145 $dependencyObject->setType($dependencyType);
146 $dependencyObject->setIdentifier($dependency);
147 $dependencyObject->setLowestVersion($lowest);
148 $dependencyObject->setHighestVersion($highest);
149 $dependenciesObject->attach($dependencyObject);
150 unset($dependencyObject);
151 }
152 }
153 }
154 return $dependenciesObject;
155 }
156
157 /**
158 * Checks dependencies for special cases (currently typo3 and php)
159 *
160 * @param \SplObjectStorage $dependencies
161 * @return boolean
162 */
163 protected function checkDependencies(\SplObjectStorage $dependencies) {
164 $dependenciesToResolve = FALSE;
165 foreach ($dependencies as $dependency) {
166 $identifier = strtolower($dependency->getIdentifier());
167 if (in_array($identifier, \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency::$specialDependencies)) {
168 $methodname = 'check' . ucfirst($identifier) . 'Dependency';
169 try {
170 $this->{$methodname}($dependency);
171 } catch (\TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException $e) {
172 $this->errors[] = array(
173 'identifier' => $identifier,
174 'message' => $e->getMessage()
175 );
176 }
177 } else {
178 if ($dependency->getType() === 'depends') {
179 $dependenciesToResolve = !(bool) $this->checkExtensionDependency($dependency);
180 }
181 }
182 }
183 return $dependenciesToResolve;
184 }
185
186 /**
187 * Returns true if current TYPO3 version fulfills extension requirements
188 *
189 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
190 * @return boolean
191 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
192 */
193 protected function checkTypo3Dependency(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
194 $lowerCaseIdentifier = strtolower($dependency->getIdentifier());
195 if ($lowerCaseIdentifier === 'typo3') {
196 if (!($dependency->getLowestVersion() === '') && version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getNumericTypo3Version(), $dependency->getLowestVersion()) === -1) {
197 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Your TYPO3 version is lower than necessary. You need at least TYPO3 version ' . $dependency->getLowestVersion());
198 }
199 if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), \TYPO3\CMS\Core\Utility\VersionNumberUtility::getNumericTypo3Version()) === -1) {
200 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Your TYPO3 version is higher than allowed. You can use TYPO3 versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion());
201 }
202 } else {
203 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('checkTypo3Dependency can only check TYPO3 dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"');
204 }
205 return TRUE;
206 }
207
208 /**
209 * Returns true if current php version fulfills extension requirements
210 *
211 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
212 * @return boolean
213 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
214 */
215 protected function checkPhpDependency(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
216 $lowerCaseIdentifier = strtolower($dependency->getIdentifier());
217 if ($lowerCaseIdentifier === 'php') {
218 if (!($dependency->getLowestVersion() === '') && version_compare(PHP_VERSION, $dependency->getLowestVersion()) === -1) {
219 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Your PHP version is lower than necessary. You need at least PHP version ' . $dependency->getLowestVersion());
220 }
221 if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), PHP_VERSION) === -1) {
222 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Your PHP version is higher than allowed. You can use PHP versions ' . $dependency->getLowestVersion() . ' - ' . $dependency->getHighestVersion());
223 }
224 } else {
225 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('checkPhpDependency can only check PHP dependencies. Found dependency with identifier "' . $dependency->getIdentifier() . '"');
226 }
227 return TRUE;
228 }
229
230 /**
231 * Main controlling function for checking dependencies
232 * Dependency check is done in the following way:
233 * - installed extension in matching version ? - return true
234 * - available extension in matching version ? - mark for installation
235 * - remote (TER) extension in matching version? - mark for download
236 *
237 * @todo handle exceptions / markForUpload
238 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
239 * @return boolean
240 */
241 protected function checkExtensionDependency(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
242 $extensionKey = $dependency->getIdentifier();
243 $extensionIsLoaded = $this->isDependentExtensionLoaded($extensionKey);
244 if ($extensionIsLoaded === TRUE) {
245 $isLoadedVersionCompatible = $this->isLoadedVersionCompatible($dependency);
246 if ($isLoadedVersionCompatible === TRUE) {
247 return TRUE;
248 } else {
249 $this->getExtensionFromTer($extensionKey, $dependency);
250 }
251 } else {
252 $extensionIsAvailable = $this->isDependentExtensionAvailable($extensionKey);
253 if ($extensionIsAvailable === TRUE) {
254 $isAvailableVersionCompatible = $this->isAvailableVersionCompatible($dependency);
255 if ($isAvailableVersionCompatible) {
256 $this->managementService->markExtensionForInstallation($extensionKey);
257 } else {
258 $this->getExtensionFromTer($extensionKey, $dependency);
259 }
260 } else {
261 $this->getExtensionFromTer($extensionKey, $dependency);
262 }
263 }
264 return FALSE;
265 }
266
267 /**
268 * Handles checks to find a compatible extension version from TER
269 * to fulfill given dependency
270 *
271 * @todo unit tests
272 * @param string $extensionKey
273 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
274 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
275 * @return void
276 */
277 protected function getExtensionFromTer($extensionKey, \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
278 $isExtensionDownloadableFromTer = $this->isExtensionDownloadableFromTer($extensionKey);
279 if ($isExtensionDownloadableFromTer === TRUE) {
280 $isDownloadableVersionCompatible = $this->isDownloadableVersionCompatible($dependency);
281 if ($isDownloadableVersionCompatible === TRUE) {
282 $latestCompatibleExtensionByIntegerVersionDependency = $this->getLatestCompatibleExtensionByIntegerVersionDependency($dependency);
283 if ($latestCompatibleExtensionByIntegerVersionDependency instanceof \TYPO3\CMS\Extensionmanager\Domain\Model\Extension) {
284 if ($this->isDependentExtensionLoaded($extensionKey)) {
285 $this->managementService->markExtensionForUpdate($latestCompatibleExtensionByIntegerVersionDependency);
286 } else {
287 $this->managementService->markExtensionForDownload($latestCompatibleExtensionByIntegerVersionDependency);
288 }
289 } else {
290 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Something went wrong.');
291 }
292 } else {
293 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('No compatible version found for extension ' . $extensionKey);
294 }
295 } else {
296 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('The extension ' . $extensionKey . ' is not available from TER.');
297 }
298 }
299
300 /**
301 * @param string $extensionKey
302 * @return bool
303 */
304 protected function isDependentExtensionLoaded($extensionKey) {
305 return \TYPO3\CMS\Core\Extension\ExtensionManager::isLoaded($extensionKey);
306 }
307
308 /**
309 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
310 * @return boolean
311 */
312 protected function isLoadedVersionCompatible(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
313 $extensionVersion = \TYPO3\CMS\Core\Extension\ExtensionManager::getExtensionVersion($dependency->getIdentifier());
314 return $this->isVersionCompatible($extensionVersion, $dependency);
315 }
316
317 /**
318 * @param string $version
319 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
320 * @return boolean
321 */
322 protected function isVersionCompatible($version, \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
323 if (!($dependency->getLowestVersion() === '') && version_compare($version, $dependency->getLowestVersion()) === -1) {
324 return FALSE;
325 }
326 if (!($dependency->getHighestVersion() === '') && version_compare($dependency->getHighestVersion(), $version) === -1) {
327 return FALSE;
328 }
329 return TRUE;
330 }
331
332 /**
333 * Checks whether the needed extension is available
334 * (not necessarily installed, but present in system)
335 *
336 * @param string $extensionKey
337 * @return boolean
338 */
339 protected function isDependentExtensionAvailable($extensionKey) {
340 $this->setAvailableExtensions();
341 return array_key_exists($extensionKey, $this->availableExtensions);
342 }
343
344 /**
345 * Checks whether the available version is compatible
346 *
347 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
348 * @return boolean
349 */
350 protected function isAvailableVersionCompatible(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
351 $this->setAvailableExtensions();
352 $extensionData = $this->emConfUtility->includeEmConf($this->availableExtensions[$dependency->getIdentifier()]);
353 return $this->isVersionCompatible($extensionData['version'], $dependency);
354 }
355
356 /**
357 * Checks whether a ter extension with $extensionKey exists
358 *
359 * @param string $extensionKey
360 * @return boolean
361 */
362 protected function isExtensionDownloadableFromTer($extensionKey) {
363 return $this->extensionRepository->countByExtensionKey($extensionKey) > 0;
364 }
365
366 /**
367 * Checks whether a compatible version of the extension exists in TER
368 *
369 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
370 * @return boolean
371 */
372 protected function isDownloadableVersionCompatible(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
373 $versions = $this->getLowestAndHighestIntegerVersions($dependency);
374 return count($this->extensionRepository->countByVersionRangeAndExtensionKey($dependency->getIdentifier(), $versions['lowestIntegerVersion'], $versions['highestIntegerVersion'])) > 0;
375 }
376
377 /**
378 * Get the latest compatible version of an extension that
379 * fulfills the given dependency from TER
380 *
381 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
382 * @return \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
383 */
384 protected function getLatestCompatibleExtensionByIntegerVersionDependency(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
385 $versions = $this->getLowestAndHighestIntegerVersions($dependency);
386 $compatibleDataSets = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion($dependency->getIdentifier(), $versions['lowestIntegerVersion'], $versions['highestIntegerVersion']);
387 return $compatibleDataSets->getFirst();
388 }
389
390 /**
391 * Return array of lowest and highest version of dependency as integer
392 *
393 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency
394 * @return array
395 */
396 protected function getLowestAndHighestIntegerVersions(\TYPO3\CMS\Extensionmanager\Domain\Model\Dependency $dependency) {
397 $lowestVersion = $dependency->getLowestVersion();
398 $lowestVersionInteger = $lowestVersion ? \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger($lowestVersion) : 0;
399 $highestVersion = $dependency->getHighestVersion();
400 $highestVersionInteger = $highestVersion ? \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger($highestVersion) : 0;
401 return array(
402 'lowestIntegerVersion' => $lowestVersionInteger,
403 'highestIntegerVersion' => $highestVersionInteger
404 );
405 }
406
407 public function findInstalledExtensionsThatDependOnMe($extensionKey) {
408 $availableExtensions = $this->listUtility->getAvailableExtensions();
409 $availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensions($availableExtensions);
410 $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation($availableAndInstalledExtensions);
411 $dependentExtensions = array();
412 foreach ($availableAndInstalledExtensions as $availableAndInstalledExtensionKey => $availableAndInstalledExtension) {
413 if (isset($availableAndInstalledExtension['installed']) && $availableAndInstalledExtension['installed'] === TRUE) {
414 if (is_array($availableAndInstalledExtension['constraints']) && is_array($availableAndInstalledExtension['constraints']['depends']) && array_key_exists($extensionKey, $availableAndInstalledExtension['constraints']['depends'])) {
415 $dependentExtensions[] = $availableAndInstalledExtensionKey;
416 }
417 }
418 }
419 return $dependentExtensions;
420 }
421
422 }
423
424
425 ?>