2 namespace TYPO3\CMS\Core\Package;
119 $this->packagesBasePaths = array(
120 'local' => PATH_typo3conf .
'ext',
121 'global' => PATH_typo3 .
'ext',
122 'sysext' => PATH_typo3 .
'sysext',
124 $this->packageStatesPathAndFilename = PATH_typo3conf .
'PackageStates.php';
154 $loadedFromCache =
false;
157 $loadedFromCache =
true;
158 }
catch (
Exception\PackageManagerCacheUnavailableException $exception) {
164 if (!$loadedFromCache) {
174 if ($this->cacheIdentifier === null) {
175 $mTime = @filemtime($this->packageStatesPathAndFilename);
176 if ($mTime !==
false) {
177 $this->cacheIdentifier = md5($this->packageStatesPathAndFilename . $mTime);
179 $this->cacheIdentifier = null;
200 if ($cacheEntryIdentifier !== null && !$this->coreCache->has($cacheEntryIdentifier)) {
204 $packageCache = array(
205 'packageStatesConfiguration' => $this->packageStatesConfiguration,
206 'packageAliasMap' => $this->packageAliasMap,
207 'packageKeys' => $this->packageKeys,
208 'activePackageKeys' => array_keys($this->activePackages),
209 'requiredPackageKeys' => $this->requiredPackageKeys,
210 'loadedExtArray' =>
$GLOBALS[
'TYPO3_LOADED_EXT'],
211 'packageObjectsCacheEntryIdentifier' => $packageObjectsCacheEntryIdentifier
213 $this->coreCache->set($packageObjectsCacheEntryIdentifier, serialize($this->packages));
214 $this->coreCache->set(
215 $cacheEntryIdentifier,
216 'return ' . PHP_EOL . var_export($packageCache,
true) .
';'
229 if ($cacheEntryIdentifier === null || !$this->coreCache->has($cacheEntryIdentifier) || !($packageCache = $this->coreCache->requireOnce($cacheEntryIdentifier))) {
230 throw new Exception\PackageManagerCacheUnavailableException(
'The package state cache could not be loaded.', 1393883342);
232 $this->packageStatesConfiguration = $packageCache[
'packageStatesConfiguration'];
233 $this->packageAliasMap = $packageCache[
'packageAliasMap'];
234 $this->packageKeys = $packageCache[
'packageKeys'];
235 $this->requiredPackageKeys = $packageCache[
'requiredPackageKeys'];
236 $GLOBALS[
'TYPO3_LOADED_EXT'] = $packageCache[
'loadedExtArray'];
237 $GLOBALS[
'TYPO3_currentPackageManager'] = $this;
239 $packageObjects = substr(substr($this->coreCache->get($packageCache[
'packageObjectsCacheEntryIdentifier']), 6), 0, -2);
240 $this->packages = unserialize($packageObjects);
241 foreach ($packageCache[
'activePackageKeys'] as $activePackageKey) {
242 $this->activePackages[$activePackageKey] = $this->packages[$activePackageKey];
244 unset(
$GLOBALS[
'TYPO3_currentPackageManager']);
256 $this->packageStatesConfiguration = @include($this->packageStatesPathAndFilename) ?: array();
257 if (!isset($this->packageStatesConfiguration[
'version']) || $this->packageStatesConfiguration[
'version'] < 4) {
258 $this->packageStatesConfiguration = array();
260 if ($this->packageStatesConfiguration !== array()) {
263 throw new Exception\PackageStatesUnavailableException(
'The PackageStates.php file is either corrupt or unavailable.', 1381507733);
276 $requiredPackages = array();
277 foreach ($this->packages as $packageKey => $package) {
278 if ($package->isProtected()) {
279 $requiredPackages[$packageKey] = $package;
281 if (isset($this->packageStatesConfiguration[
'packages'][$packageKey][
'state']) && $this->packageStatesConfiguration[
'packages'][$packageKey][
'state'] ===
'active') {
282 $this->activePackages[$packageKey] = $package;
286 $this->activePackages = array_merge($requiredPackages, $this->activePackages);
287 $this->requiredPackageKeys = array_keys($requiredPackages);
289 if ($this->activePackages != $previousActivePackages) {
290 foreach ($this->requiredPackageKeys as $requiredPackageKey) {
291 $this->packageStatesConfiguration[
'packages'][$requiredPackageKey][
'state'] =
'active';
304 $loadedExtObj = new \TYPO3\CMS\Core\Compatibility\LoadedExtensionsArray($this);
305 $GLOBALS[
'TYPO3_LOADED_EXT'] = $loadedExtObj->toArray();
319 if (isset($this->packageStatesConfiguration[
'packages'])) {
320 foreach ($this->packageStatesConfiguration[
'packages'] as $packageKey => $configuration) {
321 if (!@file_exists($this->packagesBasePath . $configuration[
'packagePath'])) {
322 unset($this->packageStatesConfiguration[
'packages'][$packageKey]);
326 $this->packageStatesConfiguration[
'packages'] = array();
331 unset($this->packagesBasePaths[$key]);
335 $packagePaths = $this->scanLegacyExtensions();
340 foreach ($packagePaths as $packagePath) {
342 foreach ($this->packagesBasePaths as $basePath) {
343 if (strpos($packagePath, $basePath) === 0) {
344 $packagesBasePath = $basePath;
351 $this->composerNameToPackageKeyMap[strtolower($composerManifest->name)] = $packageKey;
352 $this->packageStatesConfiguration[
'packages'][$packageKey][
'composerName'] = $composerManifest->name;
353 }
catch (
Exception\MissingPackageManifestException $exception) {
354 $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
355 $packageKey = substr($relativePackagePath, strpos($relativePackagePath,
'/') + 1, -1);
359 }
catch (
Exception\InvalidPackageKeyException $exception) {
362 if (!isset($this->packageStatesConfiguration[
'packages'][$packageKey][
'state'])) {
363 $this->packageStatesConfiguration[
'packages'][$packageKey][
'state'] =
'inactive';
366 $this->packageStatesConfiguration[
'packages'][$packageKey][
'packagePath'] = str_replace($this->packagesBasePath,
'', $packagePath);
369 $registerOnlyNewPackages = !empty($this->packages);
371 if ($this->packageStatesConfiguration != $previousPackageStatesConfiguration) {
372 $this->sortAndsavePackageStates();
382 protected function scanLegacyExtensions(&$collectedExtensionPaths = array())
384 $legacyCmsPackageBasePathTypes = array(
'sysext',
'global',
'local');
385 foreach ($this->packagesBasePaths as $type => $packageBasePath) {
386 if (!in_array($type, $legacyCmsPackageBasePathTypes,
true)) {
390 foreach (
new \DirectoryIterator($packageBasePath) as $fileInfo) {
391 if (!$fileInfo->isDir()) {
398 $currentPath = str_replace(
'\\',
'/', $fileInfo->getPathName()) .
'/';
402 if (file_exists($currentPath .
'ext_emconf.php') && !isset($collectedExtensionPaths[
$filename])) {
403 $collectedExtensionPaths[
$filename] = $currentPath;
408 return $collectedExtensionPaths;
421 if (file_exists($packagePath .
'ext_emconf.php')) {
424 if (file_exists($packagePath .
'composer.json')) {
438 $packageStatesHasChanged =
false;
439 foreach ($this->packageStatesConfiguration[
'packages'] as $packageKey => $stateConfiguration) {
444 if (!isset($stateConfiguration[
'packagePath'])) {
446 $packageStatesHasChanged =
true;
452 $package =
new Package($this, $packageKey, $packagePath);
453 }
catch (
Exception\InvalidPackagePathException $exception) {
455 $packageStatesHasChanged =
true;
457 }
catch (
Exception\InvalidPackageKeyException $exception) {
459 $packageStatesHasChanged =
true;
461 }
catch (
Exception\InvalidPackageManifestException $exception) {
463 $packageStatesHasChanged =
true;
469 $this->packageKeys[strtolower($packageKey)] = $packageKey;
470 if ($stateConfiguration[
'state'] ===
'active') {
471 $this->activePackages[$packageKey] = $this->packages[$packageKey];
474 if ($packageStatesHasChanged) {
492 throw new Exception\InvalidPackageStateException(
'Package "' . $packageKey .
'" is already registered.', 1338996122);
495 $this->packages[$packageKey] = $package;
496 $this->packageStatesConfiguration[
'packages'][$packageKey][
'packagePath'] = str_replace($this->packagesBasePath,
'', $package->
getPackagePath());
498 if ($sortAndSave ===
true) {
504 $this->packageAliasMap[strtolower($packageToReplace)] = $package->
getPackageKey();
521 foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
522 unset($this->packageAliasMap[strtolower($packageToReplace)]);
525 }
catch (
Exception\UnknownPackageException $e) {
527 unset($this->packages[$packageKey]);
528 unset($this->packageKeys[strtolower($packageKey)]);
529 unset($this->packageStatesConfiguration[
'packages'][$packageKey]);
540 if (isset($this->packageAliasMap[$composerName])) {
541 return $this->packageAliasMap[$composerName];
543 if (empty($this->composerNameToPackageKeyMap)) {
544 foreach ($this->packageStatesConfiguration[
'packages'] as $packageKey => $packageStateConfiguration) {
545 $this->composerNameToPackageKeyMap[strtolower($packageStateConfiguration[
'composerName'])] = $packageKey;
549 $this->composerNameToPackageKeyMap[
'typo3/cms-cms'] =
'cms';
551 $lowercasedComposerName = strtolower($composerName);
552 if (!isset($this->composerNameToPackageKeyMap[$lowercasedComposerName])) {
553 return $composerName;
555 return $this->composerNameToPackageKeyMap[$lowercasedComposerName];
569 if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
570 $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
573 throw new Exception\UnknownPackageException(
'Package "' . $packageKey .
'" is not available. Please check if the package exists and that the package key is correct (package keys are case sensitive).', 1166546734);
575 return $this->packages[$packageKey];
588 if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
589 $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
591 return isset($this->packages[$packageKey]);
603 return isset($this->runtimeActivatedPackages[$packageKey]) || isset($this->activePackages[$packageKey]);
618 foreach ($this->packageStatesConfiguration[
'packages'] as $packageStateKey => $packageStateConfiguration) {
619 if ($packageKey === $packageStateKey || empty($packageStateConfiguration[
'dependencies']) || $packageStateConfiguration[
'state'] !==
'active') {
622 if (in_array($packageKey, $packageStateConfiguration[
'dependencies'],
true)) {
632 if ($package->isProtected()) {
633 throw new Exception\ProtectedPackageKeyException(
'The package "' . $packageKey .
'" is protected and cannot be deactivated.', 1308662891);
636 unset($this->activePackages[$packageKey]);
637 $this->packageStatesConfiguration[
'packages'][$packageKey][
'state'] =
'inactive';
653 $this->activePackages[$packageKey] = $package;
654 $this->packageStatesConfiguration[
'packages'][$packageKey][
'state'] =
'active';
655 if (!isset($this->packageStatesConfiguration[
'packages'][$packageKey][
'packagePath'])) {
656 $this->packageStatesConfiguration[
'packages'][$packageKey][
'packagePath'] = str_replace($this->packagesBasePath,
'', $package->getPackagePath());
670 $this->runtimeActivatedPackages[$package->getPackageKey()] = $package;
671 if (!isset(
$GLOBALS[
'TYPO3_LOADED_EXT'][$package->getPackageKey()])) {
673 $GLOBALS[
'TYPO3_LOADED_EXT'][$package->getPackageKey()] = $loadedExtArrayElement->toArray();
687 ClassLoadingInformation::registerTransientClassLoadingInformationForPackage($package);
702 throw new Exception\UnknownPackageException(
'Package "' . $packageKey .
'" is not available and cannot be removed.', 1166543253);
706 if ($package->isProtected()) {
707 throw new Exception\ProtectedPackageKeyException(
'The package "' . $packageKey .
'" is protected and cannot be removed.', 1220722120);
717 $packagePath = $package->getPackagePath();
719 if ($deletion ===
false) {
720 throw new Exception(
'Please check file permissions. The directory "' . $packagePath .
'" for package "' . $packageKey .
'" could not be removed.', 1301491089);
735 return array_merge($this->activePackages, $this->runtimeActivatedPackages);
750 ksort($this->packageStatesConfiguration[
'packages']);
751 $this->packageStatesConfiguration[
'packages'] = $this->dependencyResolver->sortPackageStatesConfigurationByDependency($this->packageStatesConfiguration[
'packages']);
754 $newPackages = array();
755 foreach ($this->packageStatesConfiguration[
'packages'] as $packageKey => $_) {
756 $newPackages[$packageKey] = $this->packages[$packageKey];
758 $this->packages = $newPackages;
770 foreach ($this->packages as $packageKey => $package) {
773 foreach ($this->packages as $packageKey => $package) {
786 if (!isset($this->packages[$packageKey])) {
789 $suggestedPackageKeys = array();
791 foreach ($suggestedPackageConstraints as $constraint) {
792 if ($constraint instanceof
MetaData\PackageConstraint) {
793 $suggestedPackageKey = $constraint->getValue();
794 if (isset($this->packages[$suggestedPackageKey])) {
795 $suggestedPackageKeys[] = $suggestedPackageKey;
799 return array_reverse($suggestedPackageKeys);
812 $this->packageStatesConfiguration[
'version'] = 4;
814 $fileDescription =
"# PackageStates.php\n\n";
815 $fileDescription .=
"# This file is maintained by TYPO3's package management. Although you can edit it\n";
816 $fileDescription .=
"# manually, you should rather use the extension manager for maintaining packages.\n";
817 $fileDescription .=
"# This file will be regenerated automatically if it doesn't exist. Deleting this file\n";
818 $fileDescription .=
"# should, however, never become necessary if you use the package commands.\n";
821 foreach ($this->packageStatesConfiguration[
'packages'] as &$packageConfiguration) {
822 if (isset($packageConfiguration[
'dependencies'])) {
823 unset($packageConfiguration[
'dependencies']);
826 if (!@is_writable($this->packageStatesPathAndFilename)) {
828 $fileHandle = @fopen($this->packageStatesPathAndFilename,
'x');
830 throw new Exception\PackageStatesFileNotWritableException(
831 sprintf(
'We could not update the list of installed packages because the file %s is not writable. Please, check the file system permissions for this file and make sure that the web server can update it.', $this->packageStatesPathAndFilename),
837 $packageStatesCode =
"<?php\n$fileDescription\nreturn " . var_export($this->packageStatesConfiguration,
true) .
"\n ?>";
880 throw new Exception\InvalidPackageStateException(
'Package "' . $packageKey .
'" is not registered.', 1338996142);
894 public function reloadPackageInformation($packageKey)
897 throw new Exception\InvalidPackageStateException(
'Package "' . $packageKey .
'" is not registered.', 1436201329);
901 $package = $this->packages[$packageKey];
902 $packagePath = $package->getPackagePath();
903 $newPackage =
new Package($this, $packageKey, $packagePath);
904 $this->packages[$packageKey] = $newPackage;
919 foreach (
new \DirectoryIterator($startPath) as $fileInfo) {
920 if (!$fileInfo->isDir()) {
925 $currentPath = $fileInfo->getPathName();
928 $collectedPackagePaths[$currentPath] = $currentPath;
932 return $collectedPackagePaths;
945 $composerManifest = null;
946 if (file_exists($manifestPath .
'composer.json')) {
947 $json = file_get_contents($manifestPath .
'composer.json');
948 $composerManifest = json_decode($json);
949 if (!$composerManifest instanceof \stdClass) {
950 throw new Exception\InvalidPackageManifestException(
'The composer.json found for extension "' . basename($manifestPath) .
'" is invalid!', 1439555561);
957 basename($manifestPath),
958 $extensionManagerConfiguration,
959 $composerManifest ?:
new \stdClass()
961 }
catch (
Exception\InvalidPackageManifestException $e) {
962 if ($composerManifest === null) {
967 return $composerManifest;
980 $packageKey = basename($packagePath);
981 $_EXTKEY = $packageKey;
982 $path = $packagePath .
'ext_emconf.php';
984 if (@file_exists($path)) {
990 throw new Exception\InvalidPackageManifestException(
'No valid ext_emconf.php file found for package "' . $packageKey .
'".', 1360403545);
1007 $composerManifest->version = $extensionManagerConfiguration[
'version'];
1008 if (isset($extensionManagerConfiguration[
'constraints'][
'depends']) && is_array($extensionManagerConfiguration[
'constraints'][
'depends'])) {
1009 $composerManifest->require = new \stdClass();
1010 foreach ($extensionManagerConfiguration[
'constraints'][
'depends'] as $requiredPackageKey => $requiredPackageVersion) {
1011 if (!empty($requiredPackageKey)) {
1012 if ($requiredPackageKey ===
'typo3') {
1014 $composerManifest->require->core = $requiredPackageVersion;
1015 }
elseif ($requiredPackageKey !==
'php') {
1017 $composerManifest->require->{$requiredPackageKey} = $requiredPackageVersion;
1020 throw new Exception\InvalidPackageManifestException(sprintf(
'The extension "%s" has invalid version constraints in depends section. Extension key is missing!', $packageKey), 1439552058);
1024 if (isset($extensionManagerConfiguration[
'constraints'][
'conflicts']) && is_array($extensionManagerConfiguration[
'constraints'][
'conflicts'])) {
1025 $composerManifest->conflict = new \stdClass();
1026 foreach ($extensionManagerConfiguration[
'constraints'][
'conflicts'] as $conflictingPackageKey => $conflictingPackageVersion) {
1027 if (!empty($conflictingPackageKey)) {
1028 $composerManifest->conflict->$conflictingPackageKey = $conflictingPackageVersion;
1030 throw new Exception\InvalidPackageManifestException(sprintf(
'The extension "%s" has invalid version constraints in conflicts section. Extension key is missing!', $packageKey), 1439552059);
1034 if (isset($extensionManagerConfiguration[
'constraints'][
'suggests']) && is_array($extensionManagerConfiguration[
'constraints'][
'suggests'])) {
1035 $composerManifest->suggest = new \stdClass();
1036 foreach ($extensionManagerConfiguration[
'constraints'][
'suggests'] as $suggestedPackageKey => $suggestedPackageVersion) {
1037 if (!empty($suggestedPackageKey)) {
1038 $composerManifest->suggest->$suggestedPackageKey = $suggestedPackageVersion;
1040 throw new Exception\InvalidPackageManifestException(sprintf(
'The extension "%s" has invalid version constraints in suggests section. Extension key is missing!', $packageKey), 1439552060);
1044 if (isset($extensionManagerConfiguration[
'autoload'])) {
1045 $composerManifest->autoload = json_decode(json_encode($extensionManagerConfiguration[
'autoload']));
1048 unset($composerManifest->{
'autoload-dev'});
1049 if (isset($extensionManagerConfiguration[
'autoload-dev'])) {
1050 $composerManifest->{
'autoload-dev'} = json_decode(json_encode($extensionManagerConfiguration[
'autoload-dev']));
1053 return $composerManifest;
1062 protected function setComposerManifestValueIfEmpty(\stdClass $manifest, $property, $value)
1064 if (empty($manifest->{$property})) {
1065 $manifest->{$property} = $value;
1082 protected function getDependencyArrayForPackage($packageKey, array &$dependentPackageKeys = array(), array $trace = array())
1084 if (!isset($this->packages[$packageKey])) {
1087 if (in_array($packageKey, $trace,
true) !==
false) {
1088 return $dependentPackageKeys;
1090 $trace[] = $packageKey;
1091 $dependentPackageConstraints = $this->packages[$packageKey]->getPackageMetaData()->getConstraintsByType(MetaData::CONSTRAINT_TYPE_DEPENDS);
1092 foreach ($dependentPackageConstraints as $constraint) {
1093 if ($constraint instanceof
MetaData\PackageConstraint) {
1094 $dependentPackageKey = $constraint->getValue();
1095 if (in_array($dependentPackageKey, $dependentPackageKeys,
true) ===
false && in_array($dependentPackageKey, $trace,
true) ===
false) {
1096 $dependentPackageKeys[] = $dependentPackageKey;
1098 $this->getDependencyArrayForPackage($dependentPackageKey, $dependentPackageKeys, $trace);
1101 return array_reverse($dependentPackageKeys);
1121 protected function getPackageKeyFromManifest($manifest, $packagePath, $packagesBasePath)
1123 if (!is_object($manifest)) {
1124 throw new Exception\InvalidPackageManifestException(
'Invalid composer manifest in package path: ' . $packagePath, 1348146451);
1126 if (isset($manifest->type) && substr($manifest->type, 0, 10) ===
'typo3-cms-') {
1127 $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
1128 $packageKey = substr($relativePackagePath, strpos($relativePackagePath,
'/') + 1, -1);
1129 return preg_replace(
'/[^A-Za-z0-9._-]/',
'', $packageKey);
1131 $packageKey = str_replace(
'/',
'.', $manifest->name);
1132 return preg_replace(
'/[^A-Za-z0-9.]/',
'', $packageKey);