2 namespace TYPO3\CMS\Core\Package
;
5 * This script belongs to the TYPO3 Flow framework. *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License, either version 3 *
9 * of the License, or (at your option) any later version. *
11 * The TYPO3 project - inspiring people to share! *
14 use TYPO3\Flow\Annotations
as Flow
;
17 * The default TYPO3 Package Manager
18 * Adapted from FLOW for TYPO3 CMS
21 * @Flow\Scope("singleton")
23 class PackageManager
extends \TYPO3\Flow\Package\PackageManager
implements \TYPO3\CMS\Core\SingletonInterface
{
26 * @var \TYPO3\CMS\Core\Core\ClassLoader
28 protected $classLoader;
31 * @var \TYPO3\CMS\Core\Package\DependencyResolver
33 protected $dependencyResolver;
36 * @var \TYPO3\CMS\Core\Core\Bootstrap
41 * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
48 protected $cacheIdentifier;
53 protected $extAutoloadClassFiles;
58 protected $packagesBasePaths = array();
63 protected $packageAliasMap = array();
68 protected $requiredPackageKeys = array();
73 protected $runtimeActivatedPackages = array();
76 * Absolute path leading to the various package directories
79 protected $packagesBasePath = PATH_site
;
84 public function __construct() {
85 $this->packagesBasePaths
= array(
86 'local' => PATH_typo3conf
. 'ext',
87 'global' => PATH_typo3
. 'ext',
88 'sysext' => PATH_typo3
. 'sysext',
89 'composer' => PATH_site
. 'Packages',
91 $this->packageStatesPathAndFilename
= PATH_typo3conf
. 'PackageStates.php';
92 $this->packageFactory
= new PackageFactory($this);
96 * @param \TYPO3\CMS\Core\Core\ClassLoader $classLoader
98 public function injectClassLoader(\TYPO3\CMS\Core\Core\ClassLoader
$classLoader) {
99 $this->classLoader
= $classLoader;
103 * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
105 public function injectCoreCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
$coreCache) {
106 $this->coreCache
= $coreCache;
110 * @param DependencyResolver
112 public function injectDependencyResolver(DependencyResolver
$dependencyResolver) {
113 $this->dependencyResolver
= $dependencyResolver;
117 * Initializes the package manager
119 * @param \TYPO3\CMS\Core\Core\Bootstrap|\TYPO3\Flow\Core\Bootstrap $bootstrap The current bootstrap; Flow Bootstrap is here by intention to keep the PackageManager valid to the interface
122 public function initialize(\TYPO3\Flow\Core\Bootstrap
$bootstrap) {
123 $this->bootstrap
= $bootstrap;
125 $loadedFromCache = FALSE;
127 $this->loadPackageManagerStatesFromCache();
128 $loadedFromCache = TRUE;
129 } catch (Exception\PackageManagerCacheUnavailableException
$exception) {
130 $this->loadPackageStates();
131 $this->initializePackageObjects();
132 $this->initializeCompatibilityLoadedExtArray();
135 //@deprecated since 6.2, don't use
136 if (!defined('REQUIRED_EXTENSIONS')) {
137 // List of extensions required to run the core
138 define('REQUIRED_EXTENSIONS', implode(',', $this->requiredPackageKeys
));
141 $cacheIdentifier = $this->getCacheIdentifier();
142 if ($cacheIdentifier === NULL) {
143 // Create an artificial cache identifier if the package states file is not available yet
144 // in order that the class loader and class alias map can cache anyways.
145 $cacheIdentifier = md5(implode('###', array_keys($this->activePackages
)));
147 $this->classLoader
->setCacheIdentifier($cacheIdentifier)->setPackages($this->activePackages
);
149 foreach ($this->activePackages
as $package) {
150 /** @var $package Package */
151 $package->boot($bootstrap);
154 if (!$loadedFromCache) {
155 $this->saveToPackageCache();
160 * Updates the class loader with currently active packages.
161 * This method is currently a slot that monitors the after
162 * extension is installed signal to make the class loader
163 * populate its caches again.
164 * Maybe we find a better solution in the future, but as of now
165 * we have to do this as all caches are flushed after an extension
166 * is installed and the current request might fail otherwise.
168 public function updatePackagesForClassLoader() {
169 $this->classLoader
->setPackages($this->activePackages
);
173 * @return PackageFactory
175 protected function getPackageFactory() {
176 if (!isset($this->packageFactory
)) {
177 $this->packageFactory
= new PackageFactory($this);
179 return $this->packageFactory
;
185 protected function getCacheIdentifier() {
186 if ($this->cacheIdentifier
=== NULL) {
187 if (@file_exists
($this->packageStatesPathAndFilename
)) {
188 $this->cacheIdentifier
= md5_file($this->packageStatesPathAndFilename
);
190 $this->cacheIdentifier
= NULL;
193 return $this->cacheIdentifier
;
199 protected function getCacheEntryIdentifier() {
200 $cacheIdentifier = $this->getCacheIdentifier();
201 return $cacheIdentifier !== NULL ?
'PackageManager_' . $cacheIdentifier : NULL;
207 protected function saveToPackageCache() {
208 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
209 if ($cacheEntryIdentifier !== NULL && !$this->coreCache
->has($cacheEntryIdentifier)) {
210 // Package objects get their own cache entry, so PHP does not have to parse the serialized string
211 $packageObjectsCacheEntryIdentifier = uniqid('PackageObjects_');
213 $packageCache = array(
214 'packageStatesConfiguration' => $this->packageStatesConfiguration
,
215 'packageAliasMap' => $this->packageAliasMap
,
216 'packageKeys' => $this->packageKeys
,
217 'activePackageKeys' => array_keys($this->activePackages
),
218 'requiredPackageKeys' => $this->requiredPackageKeys
,
219 'loadedExtArray' => $GLOBALS['TYPO3_LOADED_EXT'],
220 'packageObjectsCacheEntryIdentifier' => $packageObjectsCacheEntryIdentifier
222 $packageClassSources = array(
223 'typo3\\flow\\package\\package' => NULL,
224 'typo3\\cms\\core\\package\\package' => NULL,
226 foreach ($this->packages
as $package) {
227 $packageClassName = strtolower(get_class($package));
228 if (!isset($packageClassSources[$packageClassName]) ||
$packageClassSources[$packageClassName] === NULL) {
229 $reflectionPackageClass = new \
ReflectionClass($packageClassName);
230 $packageClassSource = file_get_contents($reflectionPackageClass->getFileName());
231 $packageClassSources[$packageClassName] = preg_replace('/<\?php|\?>/i', '', $packageClassSource);
234 $this->coreCache
->set($packageObjectsCacheEntryIdentifier, serialize($this->packages
));
235 $this->coreCache
->set(
236 $cacheEntryIdentifier,
237 implode(PHP_EOL
, $packageClassSources) . PHP_EOL
.
238 'return ' . PHP_EOL
. var_export($packageCache, TRUE) . ';'
244 * Attempts to load the package manager states from cache
246 * @throws Exception\PackageManagerCacheUnavailableException
248 protected function loadPackageManagerStatesFromCache() {
249 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
250 if ($cacheEntryIdentifier === NULL ||
!$this->coreCache
->has($cacheEntryIdentifier) ||
!($packageCache = $this->coreCache
->requireOnce($cacheEntryIdentifier))) {
251 throw new Exception\
PackageManagerCacheUnavailableException('The package state cache could not be loaded.', 1393883342);
253 $this->packageStatesConfiguration
= $packageCache['packageStatesConfiguration'];
254 $this->packageAliasMap
= $packageCache['packageAliasMap'];
255 $this->packageKeys
= $packageCache['packageKeys'];
256 $this->requiredPackageKeys
= $packageCache['requiredPackageKeys'];
257 $GLOBALS['TYPO3_LOADED_EXT'] = $packageCache['loadedExtArray'];
258 $GLOBALS['TYPO3_currentPackageManager'] = $this;
259 // Strip off PHP Tags from Php Cache Frontend
260 $packageObjects = substr(substr($this->coreCache
->get($packageCache['packageObjectsCacheEntryIdentifier']), 6), 0, -2);
261 $this->packages
= unserialize($packageObjects);
262 foreach ($packageCache['activePackageKeys'] as $activePackageKey) {
263 $this->activePackages
[$activePackageKey] = $this->packages
[$activePackageKey];
265 unset($GLOBALS['TYPO3_currentPackageManager']);
269 * Loads the states of available packages from the PackageStates.php file.
270 * The result is stored in $this->packageStatesConfiguration.
272 * @throws Exception\PackageStatesUnavailableException
275 protected function loadPackageStates() {
276 $this->packageStatesConfiguration
= @include
($this->packageStatesPathAndFilename
) ?
: array();
277 if (!isset($this->packageStatesConfiguration
['version']) ||
$this->packageStatesConfiguration
['version'] < 4) {
278 $this->packageStatesConfiguration
= array();
280 if ($this->packageStatesConfiguration
!== array()) {
281 $this->registerPackagesFromConfiguration();
283 throw new Exception\
PackageStatesUnavailableException('The PackageStates.php file is either corrupt or unavailable.', 1381507733);
288 * Initializes activePackages and requiredPackageKeys properties
290 * Saves PackageStates.php if list of required extensions has changed.
294 protected function initializePackageObjects() {
295 $requiredPackages = array();
296 foreach ($this->packages
as $packageKey => $package) {
297 $protected = $package->isProtected();
299 $requiredPackages[$packageKey] = $package;
301 if (isset($this->packageStatesConfiguration
['packages'][$packageKey]['state']) && $this->packageStatesConfiguration
['packages'][$packageKey]['state'] === 'active') {
302 $this->activePackages
[$packageKey] = $package;
305 $previousActivePackage = $this->activePackages
;
306 $this->activePackages
= array_merge($requiredPackages, $this->activePackages
);
307 $this->requiredPackageKeys
= array_keys($requiredPackages);
309 if ($this->activePackages
!= $previousActivePackage) {
310 foreach ($this->requiredPackageKeys
as $requiredPackageKey) {
311 $this->packageStatesConfiguration
['packages'][$requiredPackageKey]['state'] = 'active';
313 $this->sortAndSavePackageStates();
318 * Initializes a backwards compatibility $GLOBALS['TYPO3_LOADED_EXT'] array
322 protected function initializeCompatibilityLoadedExtArray() {
323 $loadedExtObj = new \TYPO3\CMS\Core\Compatibility\
LoadedExtensionsArray($this);
324 $GLOBALS['TYPO3_LOADED_EXT'] = $loadedExtObj->toArray();
329 * Scans all directories in the packages directories for available packages.
330 * For each package a Package object is created and stored in $this->packages.
333 * @throws \TYPO3\Flow\Package\Exception\DuplicatePackageException
335 public function scanAvailablePackages() {
336 $previousPackageStatesConfiguration = $this->packageStatesConfiguration
;
338 if (isset($this->packageStatesConfiguration
['packages'])) {
339 foreach ($this->packageStatesConfiguration
['packages'] as $packageKey => $configuration) {
340 if (!@file_exists
($this->packagesBasePath
. $configuration['packagePath'])) {
341 unset($this->packageStatesConfiguration
['packages'][$packageKey]);
345 $this->packageStatesConfiguration
['packages'] = array();
348 foreach ($this->packagesBasePaths
as $key => $packagesBasePath) {
349 if (!is_dir($packagesBasePath)) {
350 unset($this->packagesBasePaths
[$key]);
354 $packagePaths = $this->scanLegacyExtensions();
355 foreach ($this->packagesBasePaths
as $packagesBasePath) {
356 $this->scanPackagesInPath($packagesBasePath, $packagePaths);
359 foreach ($packagePaths as $packagePath => $composerManifestPath) {
360 $packagesBasePath = PATH_site
;
361 foreach ($this->packagesBasePaths
as $basePath) {
362 if (strpos($packagePath, $basePath) === 0) {
363 $packagesBasePath = $basePath;
368 $composerManifest = self
::getComposerManifest($composerManifestPath);
369 $packageKey = PackageFactory
::getPackageKeyFromManifest($composerManifest, $packagePath, $packagesBasePath);
370 $this->composerNameToPackageKeyMap
[strtolower($composerManifest->name
)] = $packageKey;
371 $this->packageStatesConfiguration
['packages'][$packageKey]['manifestPath'] = substr($composerManifestPath, strlen($packagePath)) ?
: '';
372 $this->packageStatesConfiguration
['packages'][$packageKey]['composerName'] = $composerManifest->name
;
373 } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException
$exception) {
374 $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
375 $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') +
1, -1);
376 if (!$this->isPackageKeyValid($packageKey)) {
379 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageKeyException
$exception) {
382 if (!isset($this->packageStatesConfiguration
['packages'][$packageKey]['state'])) {
383 $this->packageStatesConfiguration
['packages'][$packageKey]['state'] = 'inactive';
386 $this->packageStatesConfiguration
['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath
, '', $packagePath);
388 // Change this to read the target from Composer or any other source
389 $this->packageStatesConfiguration
['packages'][$packageKey]['classesPath'] = Package
::DIRECTORY_CLASSES
;
392 $registerOnlyNewPackages = !empty($this->packages
);
393 $this->registerPackagesFromConfiguration($registerOnlyNewPackages);
394 if ($this->packageStatesConfiguration
!= $previousPackageStatesConfiguration) {
395 $this->sortAndsavePackageStates();
400 * @param array $collectedExtensionPaths
403 protected function scanLegacyExtensions(&$collectedExtensionPaths = array()) {
404 $legacyCmsPackageBasePathTypes = array('sysext', 'global', 'local');
405 foreach ($this->packagesBasePaths
as $type => $packageBasePath) {
406 if (!in_array($type, $legacyCmsPackageBasePathTypes)) {
409 /** @var $fileInfo \SplFileInfo */
410 foreach (new \
DirectoryIterator($packageBasePath) as $fileInfo) {
411 if (!$fileInfo->isDir()) {
414 $filename = $fileInfo->getFilename();
415 if ($filename[0] !== '.') {
416 $currentPath = \TYPO3\Flow\Utility\Files
::getUnixStylePath($fileInfo->getPathName()) . '/';
417 if (file_exists($currentPath . 'ext_emconf.php')) {
418 $collectedExtensionPaths[$currentPath] = $currentPath;
423 return $collectedExtensionPaths;
427 * Looks for composer.json in the given path and returns a path or NULL.
429 * @param string $packagePath
432 protected function findComposerManifestPaths($packagePath) {
433 // If an ext_emconf.php file is found, we don't need to look deeper
434 if (file_exists($packagePath . '/ext_emconf.php')) {
437 return parent
::findComposerManifestPaths($packagePath);
441 * Requires and registers all packages which were defined in packageStatesConfiguration
443 * @param boolean $registerOnlyNewPackages
445 * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
447 protected function registerPackagesFromConfiguration($registerOnlyNewPackages = FALSE) {
448 foreach ($this->packageStatesConfiguration
['packages'] as $packageKey => $stateConfiguration) {
450 if ($registerOnlyNewPackages && $this->isPackageAvailable($packageKey)) {
454 $packagePath = isset($stateConfiguration['packagePath']) ?
$stateConfiguration['packagePath'] : NULL;
455 $classesPath = isset($stateConfiguration['classesPath']) ?
$stateConfiguration['classesPath'] : NULL;
456 $manifestPath = isset($stateConfiguration['manifestPath']) ?
$stateConfiguration['manifestPath'] : NULL;
459 $package = $this->getPackageFactory()->create($this->packagesBasePath
, $packagePath, $packageKey, $classesPath, $manifestPath);
460 } catch (\TYPO3\Flow\Package\Exception\InvalidPackagePathException
$exception) {
461 $this->unregisterPackageByPackageKey($packageKey);
463 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageKeyException
$exception) {
464 $this->unregisterPackageByPackageKey($packageKey);
468 $this->registerPackage($package, FALSE);
470 if (!$this->packages
[$packageKey] instanceof \TYPO3\Flow\Package\PackageInterface
) {
471 throw new \TYPO3\Flow\Package\Exception\
CorruptPackageException(sprintf('The package class in package "%s" does not implement PackageInterface.', $packageKey), 1300782488);
474 $this->packageKeys
[strtolower($packageKey)] = $packageKey;
475 if ($stateConfiguration['state'] === 'active') {
476 $this->activePackages
[$packageKey] = $this->packages
[$packageKey];
482 * Register a native Flow package
484 * @param \TYPO3\Flow\Package\PackageInterface $package The Package to be registered
485 * @param boolean $sortAndSave allows for not saving packagestates when used in loops etc.
486 * @return \TYPO3\Flow\Package\PackageInterface
487 * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
489 public function registerPackage(\TYPO3\Flow\Package\PackageInterface
$package, $sortAndSave = TRUE) {
490 $package = parent
::registerPackage($package, $sortAndSave);
491 if ($package instanceof PackageInterface
) {
492 foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
493 $this->packageAliasMap
[strtolower($packageToReplace)] = $package->getPackageKey();
500 * Unregisters a package from the list of available packages
502 * @param string $packageKey Package Key of the package to be unregistered
505 protected function unregisterPackageByPackageKey($packageKey) {
507 $package = $this->getPackage($packageKey);
508 if ($package instanceof PackageInterface
) {
509 foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
510 unset($this->packageAliasMap
[strtolower($packageToReplace)]);
512 $packageKey = $package->getPackageKey();
514 } catch (\TYPO3\Flow\Package\Exception\UnknownPackageException
$e) {
516 parent
::unregisterPackageByPackageKey($packageKey);
520 * Resolves a Flow package key from a composer package name.
522 * @param string $composerName
524 * @throws \TYPO3\Flow\Package\Exception\InvalidPackageStateException
526 public function getPackageKeyFromComposerName($composerName) {
527 if (isset($this->packageAliasMap
[$composerName])) {
528 return $this->packageAliasMap
[$composerName];
531 return parent
::getPackageKeyFromComposerName($composerName);
532 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageStateException
$exception) {
533 return $composerName;
540 public function getExtAutoloadRegistry() {
541 if (!isset($this->extAutoloadClassFiles
)) {
542 $classRegistry = array();
543 foreach ($this->activePackages
as $packageKey => $packageData) {
545 $extensionAutoloadFile = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::extPath($packageKey, 'ext_autoload.php');
546 if (@file_exists
($extensionAutoloadFile)) {
547 $classRegistry = array_merge($classRegistry, require $extensionAutoloadFile);
549 } catch (\BadFunctionCallException
$e) {
552 $this->extAutoloadClassFiles
= $classRegistry;
554 return $this->extAutoloadClassFiles
;
558 * Returns a PackageInterface object for the specified package.
559 * A package is available, if the package directory contains valid MetaData information.
561 * @param string $packageKey
562 * @return \TYPO3\Flow\Package\PackageInterface The requested package object
563 * @throws \TYPO3\Flow\Package\Exception\UnknownPackageException if the specified package is not known
566 public function getPackage($packageKey) {
567 if (isset($this->packageAliasMap
[$lowercasedPackageKey = strtolower($packageKey)])) {
568 $packageKey = $this->packageAliasMap
[$lowercasedPackageKey];
570 return parent
::getPackage($packageKey);
574 * Returns TRUE if a package is available (the package's files exist in the packages directory)
575 * or FALSE if it's not. If a package is available it doesn't mean necessarily that it's active!
577 * @param string $packageKey The key of the package to check
578 * @return boolean TRUE if the package is available, otherwise FALSE
581 public function isPackageAvailable($packageKey) {
582 if (isset($this->packageAliasMap
[$lowercasedPackageKey = strtolower($packageKey)])) {
583 $packageKey = $this->packageAliasMap
[$lowercasedPackageKey];
585 return parent
::isPackageAvailable($packageKey);
589 * Returns TRUE if a package is activated or FALSE if it's not.
591 * @param string $packageKey The key of the package to check
592 * @return boolean TRUE if package is active, otherwise FALSE
595 public function isPackageActive($packageKey) {
596 if (isset($this->packageAliasMap
[$lowercasedPackageKey = strtolower($packageKey)])) {
597 $packageKey = $this->packageAliasMap
[$lowercasedPackageKey];
599 return isset($this->runtimeActivatedPackages
[$packageKey]) || parent
::isPackageActive($packageKey);
603 * @param string $packageKey
605 public function deactivatePackage($packageKey) {
606 $package = $this->getPackage($packageKey);
607 parent
::deactivatePackage($package->getPackageKey());
611 * @param string $packageKey
613 public function activatePackage($packageKey) {
614 $package = $this->getPackage($packageKey);
615 parent
::activatePackage($package->getPackageKey());
616 $this->classLoader
->addActivePackage($package);
620 * Enables packages during runtime, but no class aliases will be available
622 * @param string $packageKey
625 public function activatePackageDuringRuntime($packageKey) {
626 $package = $this->getPackage($packageKey);
627 $this->runtimeActivatedPackages
[$package->getPackageKey()] = $package;
628 $this->classLoader
->addActivePackage($package);
629 if (!isset($GLOBALS['TYPO3_LOADED_EXT'][$package->getPackageKey()])) {
630 $loadedExtArrayElement = new \TYPO3\CMS\Core\Compatibility\
LoadedExtensionArrayElement($package);
631 $GLOBALS['TYPO3_LOADED_EXT'][$package->getPackageKey()] = $loadedExtArrayElement->toArray();
637 * @param string $packageKey
639 public function deletePackage($packageKey) {
640 $package = $this->getPackage($packageKey);
641 parent
::deletePackage($package->getPackageKey());
646 * @param string $packageKey
648 public function freezePackage($packageKey) {
649 $package = $this->getPackage($packageKey);
650 parent
::freezePackage($package->getPackageKey());
654 * @param string $packageKey
657 public function isPackageFrozen($packageKey) {
658 $package = $this->getPackage($packageKey);
659 return parent
::isPackageFrozen($package->getPackageKey());
663 * @param string $packageKey
665 public function unfreezePackage($packageKey) {
666 $package = $this->getPackage($packageKey);
667 parent
::unfreezePackage($package->getPackageKey());
671 * @param string $packageKey
673 public function refreezePackage($packageKey) {
674 $package = $this->getPackage($packageKey);
675 parent
::refreezePackage($package->getPackageKey());
679 * Returns an array of \TYPO3\Flow\Package objects of all active packages.
680 * A package is active, if it is available and has been activated in the package
681 * manager settings. This method returns runtime activated packages too
683 * @return \TYPO3\Flow\Package\PackageInterface[]
686 public function getActivePackages() {
687 return array_merge(parent
::getActivePackages(), $this->runtimeActivatedPackages
);
691 * Orders all packages by comparing their dependencies. By this, the packages
692 * and package configurations arrays holds all packages in the correct
693 * initialization order.
697 protected function sortAvailablePackagesByDependencies() {
698 $this->resolvePackageDependencies();
700 $this->packageStatesConfiguration
['packages'] = $this->dependencyResolver
->sortPackageStatesConfigurationByDependency($this->packageStatesConfiguration
['packages']);
702 // Reorder the packages according to the loading order
703 $newPackages = array();
704 foreach (array_keys($this->packageStatesConfiguration
['packages']) as $packageKey) {
705 $newPackages[$packageKey] = $this->packages
[$packageKey];
707 $this->packages
= $newPackages;
711 * Resolves the dependent packages from the meta data of all packages recursively. The
712 * resolved direct or indirect dependencies of each package will put into the package
713 * states configuration array.
717 protected function resolvePackageDependencies() {
718 parent
::resolvePackageDependencies();
719 foreach ($this->packages
as $packageKey => $package) {
720 $this->packageStatesConfiguration
['packages'][$packageKey]['suggestions'] = $this->getSuggestionArrayForPackage($packageKey);
725 * Returns an array of suggested package keys for the given package.
727 * @param string $packageKey The package key to fetch the suggestions for
728 * @return array|NULL An array of directly suggested packages
730 protected function getSuggestionArrayForPackage($packageKey) {
731 if (!isset($this->packages
[$packageKey])) {
734 $suggestedPackageKeys = array();
735 $suggestedPackageConstraints = $this->packages
[$packageKey]->getPackageMetaData()->getConstraintsByType(\TYPO3\Flow\Package\MetaDataInterface
::CONSTRAINT_TYPE_SUGGESTS
);
736 foreach ($suggestedPackageConstraints as $constraint) {
737 if ($constraint instanceof \TYPO3\Flow\Package\MetaData\PackageConstraint
) {
738 $suggestedPackageKey = $constraint->getValue();
739 if (isset($this->packages
[$suggestedPackageKey])) {
740 $suggestedPackageKeys[] = $suggestedPackageKey;
744 return array_reverse($suggestedPackageKeys);
748 * Saves the current content of $this->packageStatesConfiguration to the
749 * PackageStates.php file.
753 protected function sortAndSavePackageStates() {
754 parent
::sortAndSavePackageStates();
756 $this->initializeCompatibilityLoadedExtArray();
757 \TYPO3\CMS\Core\Utility\OpcodeCacheUtility
::clearAllActive($this->packageStatesPathAndFilename
);
761 * Check the conformance of the given package key
763 * @param string $packageKey The package key to validate
764 * @return boolean If the package key is valid, returns TRUE otherwise FALSE
767 public function isPackageKeyValid($packageKey) {
768 return parent
::isPackageKeyValid($packageKey) ||
preg_match(\TYPO3\CMS\Core\Package\Package
::PATTERN_MATCH_EXTENSIONKEY
, $packageKey) === 1;