[BUGFIX] Update $GLOBALS['TYPO3_LOADED_EXT'] on package change
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Package / PackageManager.php
1 <?php
2 namespace TYPO3\CMS\Core\Package;
3
4 /* *
5 * This script belongs to the TYPO3 Flow framework. *
6 * *
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. *
10 * *
11 * The TYPO3 project - inspiring people to share! *
12 * */
13
14 use TYPO3\Flow\Annotations as Flow;
15
16 /**
17 * The default TYPO3 Package Manager
18 * Adapted from FLOW for TYPO3 CMS
19 *
20 * @api
21 * @Flow\Scope("singleton")
22 */
23 class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO3\CMS\Core\SingletonInterface {
24
25 /**
26 * @var \TYPO3\CMS\Core\Core\ClassLoader
27 */
28 protected $classLoader;
29
30 /**
31 * @var \TYPO3\CMS\Core\Package\DependencyResolver
32 */
33 protected $dependencyResolver;
34
35 /**
36 * @var \TYPO3\CMS\Core\Core\Bootstrap
37 */
38 protected $bootstrap;
39
40 /**
41 * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
42 */
43 protected $coreCache;
44
45 /**
46 * @var string
47 */
48 protected $cacheIdentifier;
49
50 /**
51 * @var array
52 */
53 protected $extAutoloadClassFiles;
54
55 /**
56 * @var array
57 */
58 protected $packagesBasePaths = array();
59
60 /**
61 * @var array
62 */
63 protected $packageAliasMap = array();
64
65 /**
66 * @var array
67 */
68 protected $requiredPackageKeys = array();
69
70 /**
71 * @var array
72 */
73 protected $runtimeActivatedPackages = array();
74
75 /**
76 * Absolute path leading to the various package directories
77 * @var string
78 */
79 protected $packagesBasePath = PATH_site;
80
81 /**
82 * Constructor
83 */
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',
90 );
91 }
92
93 /**
94 * @param \TYPO3\CMS\Core\Core\ClassLoader $classLoader
95 */
96 public function injectClassLoader(\TYPO3\CMS\Core\Core\ClassLoader $classLoader) {
97 $this->classLoader = $classLoader;
98 }
99
100 /**
101 * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
102 */
103 public function injectCoreCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache) {
104 $this->coreCache = $coreCache;
105 }
106
107 /**
108 * @param DependencyResolver
109 */
110 public function injectDependencyResolver(DependencyResolver $dependencyResolver) {
111 $this->dependencyResolver = $dependencyResolver;
112 }
113
114 /**
115 * Initializes the package manager
116 *
117 * @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
118 * @return void
119 */
120 public function initialize(\TYPO3\Flow\Core\Bootstrap $bootstrap) {
121 $this->bootstrap = $bootstrap;
122 $this->packageStatesPathAndFilename = PATH_typo3conf . 'PackageStates.php';
123 $this->packageFactory = new PackageFactory($this);
124
125 $loadedFromCache = FALSE;
126 try {
127 $this->loadPackageManagerStatesFromCache();
128 $loadedFromCache = TRUE;
129 } catch (Exception\PackageManagerCacheUnavailableException $exception) {
130 $this->loadPackageStates();
131 $this->initializePackageObjects();
132 $this->initializeCompatibilityLoadedExtArray();
133 }
134
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));
139 }
140
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)));
146 }
147 $this->classLoader->setCacheIdentifier($cacheIdentifier)->setPackages($this->activePackages);
148
149 foreach ($this->activePackages as $package) {
150 /** @var $package Package */
151 $package->boot($bootstrap);
152 }
153
154 if (!$loadedFromCache) {
155 $this->saveToPackageCache();
156 }
157 }
158
159 /**
160 * @return PackageFactory
161 */
162 protected function getPackageFactory() {
163 if (!isset($this->packageFactory)) {
164 $this->packageFactory = new PackageFactory($this);
165 }
166 return $this->packageFactory;
167 }
168
169 /**
170 * @return string
171 */
172 protected function getCacheIdentifier() {
173 if ($this->cacheIdentifier === NULL) {
174 if (@file_exists($this->packageStatesPathAndFilename)) {
175 $this->cacheIdentifier = md5_file($this->packageStatesPathAndFilename);
176 } else {
177 $this->cacheIdentifier = NULL;
178 }
179 }
180 return $this->cacheIdentifier;
181 }
182
183 /**
184 * @return string
185 */
186 protected function getCacheEntryIdentifier() {
187 $cacheIdentifier = $this->getCacheIdentifier();
188 return $cacheIdentifier !== NULL ? 'PackageManager_' . $cacheIdentifier : NULL;
189 }
190
191 /**
192 *
193 */
194 protected function saveToPackageCache() {
195 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
196 if ($cacheEntryIdentifier !== NULL && !$this->coreCache->has($cacheEntryIdentifier)) {
197 // Package objects get their own cache entry, so PHP does not have to parse the serialized string
198 $packageObjectsCacheEntryIdentifier = uniqid('PackageObjects_');
199 // Build cache file
200 $packageCache = array(
201 'packageStatesConfiguration' => $this->packageStatesConfiguration,
202 'packageAliasMap' => $this->packageAliasMap,
203 'packageKeys' => $this->packageKeys,
204 'activePackageKeys' => array_keys($this->activePackages),
205 'requiredPackageKeys' => $this->requiredPackageKeys,
206 'loadedExtArray' => $GLOBALS['TYPO3_LOADED_EXT'],
207 'packageObjectsCacheEntryIdentifier' => $packageObjectsCacheEntryIdentifier
208 );
209 $packageClassSources = array(
210 'typo3\\flow\\package\\package' => NULL,
211 'typo3\\cms\\core\\package\\package' => NULL,
212 );
213 foreach ($this->packages as $package) {
214 $packageClassName = strtolower(get_class($package));
215 if (!isset($packageClassSources[$packageClassName]) || $packageClassSources[$packageClassName] === NULL) {
216 $reflectionPackageClass = new \ReflectionClass($packageClassName);
217 $packageClassSource = file_get_contents($reflectionPackageClass->getFileName());
218 $packageClassSources[$packageClassName] = preg_replace('/<\?php|\?>/i', '', $packageClassSource);
219 }
220 }
221 $this->coreCache->set($packageObjectsCacheEntryIdentifier, serialize($this->packages));
222 $this->coreCache->set(
223 $cacheEntryIdentifier,
224 implode(PHP_EOL, $packageClassSources) . PHP_EOL .
225 'return ' . PHP_EOL . var_export($packageCache, TRUE) . ';'
226 );
227 }
228 }
229
230 /**
231 * Attempts to load the package manager states from cache
232 *
233 * @throws Exception\PackageManagerCacheUnavailableException
234 */
235 protected function loadPackageManagerStatesFromCache() {
236 $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
237 if ($cacheEntryIdentifier === NULL || !$this->coreCache->has($cacheEntryIdentifier) || !($packageCache = $this->coreCache->requireOnce($cacheEntryIdentifier))) {
238 throw new Exception\PackageManagerCacheUnavailableException('The package state cache could not be loaded.', 1393883342);
239 }
240 $this->packageStatesConfiguration = $packageCache['packageStatesConfiguration'];
241 $this->packageAliasMap = $packageCache['packageAliasMap'];
242 $this->packageKeys = $packageCache['packageKeys'];
243 $this->requiredPackageKeys = $packageCache['requiredPackageKeys'];
244 $GLOBALS['TYPO3_LOADED_EXT'] = $packageCache['loadedExtArray'];
245 $GLOBALS['TYPO3_currentPackageManager'] = $this;
246 // Strip off PHP Tags from Php Cache Frontend
247 $packageObjects = substr(substr($this->coreCache->get($packageCache['packageObjectsCacheEntryIdentifier']), 6), 0, -2);
248 $this->packages = unserialize($packageObjects);
249 foreach ($packageCache['activePackageKeys'] as $activePackageKey) {
250 $this->activePackages[$activePackageKey] = $this->packages[$activePackageKey];
251 }
252 unset($GLOBALS['TYPO3_currentPackageManager']);
253 }
254
255 /**
256 * Loads the states of available packages from the PackageStates.php file.
257 * The result is stored in $this->packageStatesConfiguration.
258 *
259 * @throws Exception\PackageStatesUnavailableException
260 * @return void
261 */
262 protected function loadPackageStates() {
263 $this->packageStatesConfiguration = @include($this->packageStatesPathAndFilename) ?: array();
264 if (!isset($this->packageStatesConfiguration['version']) || $this->packageStatesConfiguration['version'] < 4) {
265 $this->packageStatesConfiguration = array();
266 }
267 if ($this->packageStatesConfiguration !== array()) {
268 $this->registerPackagesFromConfiguration();
269 } else {
270 throw new Exception\PackageStatesUnavailableException('The PackageStates.php file is either corrupt or unavailable.', 1381507733);
271 }
272 }
273
274 /**
275 * Initializes activePackages and requiredPackageKeys properties
276 *
277 * Saves PackageStates.php if list of required extensions has changed.
278 *
279 * @return void
280 */
281 protected function initializePackageObjects() {
282 $requiredPackages = array();
283 foreach ($this->packages as $packageKey => $package) {
284 $protected = $package->isProtected();
285 if ($protected) {
286 $requiredPackages[$packageKey] = $package;
287 }
288 if (isset($this->packageStatesConfiguration['packages'][$packageKey]['state']) && $this->packageStatesConfiguration['packages'][$packageKey]['state'] === 'active') {
289 $this->activePackages[$packageKey] = $package;
290 }
291 }
292 $previousActivePackage = $this->activePackages;
293 $this->activePackages = array_merge($requiredPackages, $this->activePackages);
294 $this->requiredPackageKeys = array_keys($requiredPackages);
295
296 if ($this->activePackages != $previousActivePackage) {
297 foreach ($this->requiredPackageKeys as $requiredPackageKey) {
298 $this->packageStatesConfiguration['packages'][$requiredPackageKey]['state'] = 'active';
299 }
300 $this->sortAndSavePackageStates();
301 }
302 }
303
304 /**
305 * Initializes a backwards compatibility $GLOBALS['TYPO3_LOADED_EXT'] array
306 *
307 * @return void
308 */
309 protected function initializeCompatibilityLoadedExtArray() {
310 $loadedExtObj = new \TYPO3\CMS\Core\Compatibility\LoadedExtensionsArray($this);
311 $GLOBALS['TYPO3_LOADED_EXT'] = $loadedExtObj->toArray();
312 }
313
314
315 /**
316 * Scans all directories in the packages directories for available packages.
317 * For each package a Package object is created and stored in $this->packages.
318 *
319 * @return void
320 * @throws \TYPO3\Flow\Package\Exception\DuplicatePackageException
321 */
322 public function scanAvailablePackages() {
323 $previousPackageStatesConfiguration = $this->packageStatesConfiguration;
324
325 if (isset($this->packageStatesConfiguration['packages'])) {
326 foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $configuration) {
327 if (!@file_exists($this->packagesBasePath . $configuration['packagePath'])) {
328 unset($this->packageStatesConfiguration['packages'][$packageKey]);
329 }
330 }
331 } else {
332 $this->packageStatesConfiguration['packages'] = array();
333 }
334
335 foreach ($this->packagesBasePaths as $key => $packagesBasePath) {
336 if (!is_dir($packagesBasePath)) {
337 unset($this->packagesBasePaths[$key]);
338 }
339 }
340
341 $packagePaths = $this->scanLegacyExtensions();
342 foreach ($this->packagesBasePaths as $packagesBasePath) {
343 $this->scanPackagesInPath($packagesBasePath, $packagePaths);
344 }
345
346 foreach ($packagePaths as $packagePath => $composerManifestPath) {
347 $packagesBasePath = PATH_site;
348 foreach ($this->packagesBasePaths as $basePath) {
349 if (strpos($packagePath, $basePath) === 0) {
350 $packagesBasePath = $basePath;
351 break;
352 }
353 }
354 try {
355 $composerManifest = self::getComposerManifest($composerManifestPath);
356 $packageKey = PackageFactory::getPackageKeyFromManifest($composerManifest, $packagePath, $packagesBasePath);
357 $this->composerNameToPackageKeyMap[strtolower($composerManifest->name)] = $packageKey;
358 $this->packageStatesConfiguration['packages'][$packageKey]['manifestPath'] = substr($composerManifestPath, strlen($packagePath)) ? : '';
359 $this->packageStatesConfiguration['packages'][$packageKey]['composerName'] = $composerManifest->name;
360 } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) {
361 $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
362 $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') + 1, -1);
363 if (!$this->isPackageKeyValid($packageKey)) {
364 continue;
365 }
366 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageKeyException $exception) {
367 continue;
368 }
369 if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['state'])) {
370 $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'inactive';
371 }
372
373 $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $packagePath);
374
375 // Change this to read the target from Composer or any other source
376 $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = Package::DIRECTORY_CLASSES;
377 }
378
379 $registerOnlyNewPackages = !empty($this->packages);
380 $this->registerPackagesFromConfiguration($registerOnlyNewPackages);
381 if ($this->packageStatesConfiguration != $previousPackageStatesConfiguration) {
382 $this->sortAndsavePackageStates();
383 }
384 }
385
386 /**
387 * @param array $collectedExtensionPaths
388 * @return array
389 */
390 protected function scanLegacyExtensions(&$collectedExtensionPaths = array()) {
391 $legacyCmsPackageBasePathTypes = array('sysext', 'global', 'local');
392 foreach ($this->packagesBasePaths as $type => $packageBasePath) {
393 if (!in_array($type, $legacyCmsPackageBasePathTypes)) {
394 continue;
395 }
396 /** @var $fileInfo \SplFileInfo */
397 foreach (new \DirectoryIterator($packageBasePath) as $fileInfo) {
398 if (!$fileInfo->isDir()) {
399 continue;
400 }
401 $filename = $fileInfo->getFilename();
402 if ($filename[0] !== '.') {
403 $currentPath = \TYPO3\Flow\Utility\Files::getUnixStylePath($fileInfo->getPathName()) . '/';
404 if (file_exists($currentPath . 'ext_emconf.php')) {
405 $collectedExtensionPaths[$currentPath] = $currentPath;
406 }
407 }
408 }
409 }
410 return $collectedExtensionPaths;
411 }
412
413 /**
414 * Looks for composer.json in the given path and returns a path or NULL.
415 *
416 * @param string $packagePath
417 * @return array
418 */
419 protected function findComposerManifestPaths($packagePath) {
420 // If an ext_emconf.php file is found, we don't need to look deeper
421 if (file_exists($packagePath . '/ext_emconf.php')) {
422 return array();
423 }
424 return parent::findComposerManifestPaths($packagePath);
425 }
426
427 /**
428 * Requires and registers all packages which were defined in packageStatesConfiguration
429 *
430 * @param boolean $registerOnlyNewPackages
431 * @return void
432 * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
433 */
434 protected function registerPackagesFromConfiguration($registerOnlyNewPackages = FALSE) {
435 foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $stateConfiguration) {
436
437 if ($registerOnlyNewPackages && $this->isPackageAvailable($packageKey)) {
438 continue;
439 }
440
441 $packagePath = isset($stateConfiguration['packagePath']) ? $stateConfiguration['packagePath'] : NULL;
442 $classesPath = isset($stateConfiguration['classesPath']) ? $stateConfiguration['classesPath'] : NULL;
443 $manifestPath = isset($stateConfiguration['manifestPath']) ? $stateConfiguration['manifestPath'] : NULL;
444
445 try {
446 $package = $this->getPackageFactory()->create($this->packagesBasePath, $packagePath, $packageKey, $classesPath, $manifestPath);
447 } catch (\TYPO3\Flow\Package\Exception\InvalidPackagePathException $exception) {
448 $this->unregisterPackageByPackageKey($packageKey);
449 continue;
450 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageKeyException $exception) {
451 $this->unregisterPackageByPackageKey($packageKey);
452 continue;
453 }
454
455 $this->registerPackage($package, FALSE);
456
457 if (!$this->packages[$packageKey] instanceof \TYPO3\Flow\Package\PackageInterface) {
458 throw new \TYPO3\Flow\Package\Exception\CorruptPackageException(sprintf('The package class in package "%s" does not implement PackageInterface.', $packageKey), 1300782488);
459 }
460
461 $this->packageKeys[strtolower($packageKey)] = $packageKey;
462 if ($stateConfiguration['state'] === 'active') {
463 $this->activePackages[$packageKey] = $this->packages[$packageKey];
464 }
465 }
466 }
467
468 /**
469 * Register a native Flow package
470 *
471 * @param \TYPO3\Flow\Package\PackageInterface $package The Package to be registered
472 * @param boolean $sortAndSave allows for not saving packagestates when used in loops etc.
473 * @return \TYPO3\Flow\Package\PackageInterface
474 * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
475 */
476 public function registerPackage(\TYPO3\Flow\Package\PackageInterface $package, $sortAndSave = TRUE) {
477 $package = parent::registerPackage($package, $sortAndSave);
478 if ($package instanceof PackageInterface) {
479 foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
480 $this->packageAliasMap[strtolower($packageToReplace)] = $package->getPackageKey();
481 }
482 }
483 return $package;
484 }
485
486 /**
487 * Unregisters a package from the list of available packages
488 *
489 * @param string $packageKey Package Key of the package to be unregistered
490 * @return void
491 */
492 protected function unregisterPackageByPackageKey($packageKey) {
493 try {
494 $package = $this->getPackage($packageKey);
495 if ($package instanceof PackageInterface) {
496 foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
497 unset($this->packageAliasMap[strtolower($packageToReplace)]);
498 }
499 $packageKey = $package->getPackageKey();
500 }
501 } catch (\TYPO3\Flow\Package\Exception\UnknownPackageException $e) {
502 }
503 parent::unregisterPackageByPackageKey($packageKey);
504 }
505
506 /**
507 * Resolves a Flow package key from a composer package name.
508 *
509 * @param string $composerName
510 * @return string
511 * @throws \TYPO3\Flow\Package\Exception\InvalidPackageStateException
512 */
513 public function getPackageKeyFromComposerName($composerName) {
514 if (isset($this->packageAliasMap[$composerName])) {
515 return $this->packageAliasMap[$composerName];
516 }
517 try {
518 return parent::getPackageKeyFromComposerName($composerName);
519 } catch (\TYPO3\Flow\Package\Exception\InvalidPackageStateException $exception) {
520 return $composerName;
521 }
522 }
523
524 /**
525 * @return array
526 */
527 public function getExtAutoloadRegistry() {
528 if (!isset($this->extAutoloadClassFiles)) {
529 $classRegistry = array();
530 foreach ($this->activePackages as $packageKey => $packageData) {
531 try {
532 $extensionAutoloadFile = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($packageKey, 'ext_autoload.php');
533 if (@file_exists($extensionAutoloadFile)) {
534 $classRegistry = array_merge($classRegistry, require $extensionAutoloadFile);
535 }
536 } catch (\BadFunctionCallException $e) {
537 }
538 }
539 $this->extAutoloadClassFiles = $classRegistry;
540 }
541 return $this->extAutoloadClassFiles;
542 }
543
544 /**
545 * Returns a PackageInterface object for the specified package.
546 * A package is available, if the package directory contains valid MetaData information.
547 *
548 * @param string $packageKey
549 * @return \TYPO3\Flow\Package\PackageInterface The requested package object
550 * @throws \TYPO3\Flow\Package\Exception\UnknownPackageException if the specified package is not known
551 * @api
552 */
553 public function getPackage($packageKey) {
554 if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
555 $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
556 }
557 return parent::getPackage($packageKey);
558 }
559
560 /**
561 * Returns TRUE if a package is available (the package's files exist in the packages directory)
562 * or FALSE if it's not. If a package is available it doesn't mean necessarily that it's active!
563 *
564 * @param string $packageKey The key of the package to check
565 * @return boolean TRUE if the package is available, otherwise FALSE
566 * @api
567 */
568 public function isPackageAvailable($packageKey) {
569 if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
570 $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
571 }
572 return parent::isPackageAvailable($packageKey);
573 }
574
575 /**
576 * Returns TRUE if a package is activated or FALSE if it's not.
577 *
578 * @param string $packageKey The key of the package to check
579 * @return boolean TRUE if package is active, otherwise FALSE
580 * @api
581 */
582 public function isPackageActive($packageKey) {
583 if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
584 $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
585 }
586 return isset($this->runtimeActivatedPackages[$packageKey]) || parent::isPackageActive($packageKey);
587 }
588
589 /**
590 * @param string $packageKey
591 */
592 public function deactivatePackage($packageKey) {
593 $package = $this->getPackage($packageKey);
594 parent::deactivatePackage($package->getPackageKey());
595 }
596
597 /**
598 * @param string $packageKey
599 */
600 public function activatePackage($packageKey) {
601 $package = $this->getPackage($packageKey);
602 parent::activatePackage($package->getPackageKey());
603 $this->classLoader->addActivePackage($package);
604 }
605
606 /**
607 * Enables packages during runtime, but no class aliases will be available
608 *
609 * @param string $packageKey
610 * @api
611 */
612 public function activatePackageDuringRuntime($packageKey) {
613 $package = $this->getPackage($packageKey);
614 $this->runtimeActivatedPackages[$package->getPackageKey()] = $package;
615 $this->classLoader->addActivePackage($package);
616 if (!isset($GLOBALS['TYPO3_LOADED_EXT'][$package->getPackageKey()])) {
617 $loadedExtArrayElement = new \TYPO3\CMS\Core\Compatibility\LoadedExtensionArrayElement($package);
618 $GLOBALS['TYPO3_LOADED_EXT'][$package->getPackageKey()] = $loadedExtArrayElement->toArray();
619 }
620 }
621
622
623 /**
624 * @param string $packageKey
625 */
626 public function deletePackage($packageKey) {
627 $package = $this->getPackage($packageKey);
628 parent::deletePackage($package->getPackageKey());
629 }
630
631
632 /**
633 * @param string $packageKey
634 */
635 public function freezePackage($packageKey) {
636 $package = $this->getPackage($packageKey);
637 parent::freezePackage($package->getPackageKey());
638 }
639
640 /**
641 * @param string $packageKey
642 * @return bool
643 */
644 public function isPackageFrozen($packageKey) {
645 $package = $this->getPackage($packageKey);
646 return parent::isPackageFrozen($package->getPackageKey());
647 }
648
649 /**
650 * @param string $packageKey
651 */
652 public function unfreezePackage($packageKey) {
653 $package = $this->getPackage($packageKey);
654 parent::unfreezePackage($package->getPackageKey());
655 }
656
657 /**
658 * @param string $packageKey
659 */
660 public function refreezePackage($packageKey) {
661 $package = $this->getPackage($packageKey);
662 parent::refreezePackage($package->getPackageKey());
663 }
664
665 /**
666 * Returns an array of \TYPO3\Flow\Package objects of all active packages.
667 * A package is active, if it is available and has been activated in the package
668 * manager settings. This method returns runtime activated packages too
669 *
670 * @return array Array of \TYPO3\Flow\Package\PackageInterface
671 * @api
672 */
673 public function getActivePackages() {
674 return array_merge(parent::getActivePackages(), $this->runtimeActivatedPackages);
675 }
676
677 /**
678 * Orders all packages by comparing their dependencies. By this, the packages
679 * and package configurations arrays holds all packages in the correct
680 * initialization order.
681 *
682 * @return void
683 */
684 protected function sortAvailablePackagesByDependencies() {
685 $this->resolvePackageDependencies();
686
687 $this->packageStatesConfiguration['packages'] = $this->dependencyResolver->sortPackageStatesConfigurationByDependency($this->packageStatesConfiguration['packages']);
688
689 // Reorder the packages according to the loading order
690 $newPackages = array();
691 foreach (array_keys($this->packageStatesConfiguration['packages']) as $packageKey) {
692 $newPackages[$packageKey] = $this->packages[$packageKey];
693 }
694 $this->packages = $newPackages;
695 }
696
697 /**
698 * Saves the current content of $this->packageStatesConfiguration to the
699 * PackageStates.php file.
700 *
701 * @return void
702 */
703 protected function sortAndSavePackageStates() {
704 parent::sortAndSavePackageStates();
705
706 $this->initializeCompatibilityLoadedExtArray();
707 \TYPO3\CMS\Core\Utility\OpcodeCacheUtility::clearAllActive($this->packageStatesPathAndFilename);
708 }
709
710 /**
711 * Check the conformance of the given package key
712 *
713 * @param string $packageKey The package key to validate
714 * @return boolean If the package key is valid, returns TRUE otherwise FALSE
715 * @api
716 */
717 public function isPackageKeyValid($packageKey) {
718 return parent::isPackageKeyValid($packageKey) || preg_match(\TYPO3\CMS\Core\Package\Package::PATTERN_MATCH_EXTENSIONKEY, $packageKey) === 1;
719 }
720 }