[FEATURE] Integrate preliminary PackageManager API 05/19605/32
authorThomas Maroschik <tmaroschik@dfau.de>
Mon, 8 Jul 2013 19:22:31 +0000 (21:22 +0200)
committerErnesto Baschny <ernst@cron-it.de>
Sun, 13 Oct 2013 12:48:17 +0000 (14:48 +0200)
The Package Manager from Flow has been backported and integrated as
CMS Package Manager. This enables us to use full vendor namespaces for
extensions and brings us closer to a unified TYPO3 package format.

For more Details see: http://wiki.typo3.org/Blueprints/Packagemanager

Resolves: #47018
Releases: 6.2
Change-Id: I52abedaf470cebddb3553e37581021bf368380e6
Reviewed-on: https://review.typo3.org/19605
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
Reviewed-by: Alexander Opitz
Reviewed-by: Sascha Egerer
Reviewed-by: Thomas Maroschik
Tested-by: Thomas Maroschik
Tested-by: Alexander Opitz
Reviewed-by: Ernesto Baschny
Tested-by: Ernesto Baschny
67 files changed:
index.php
typo3/init.php
typo3/sysext/about/Classes/Domain/Repository/ExtensionRepository.php
typo3/sysext/backend/Classes/Package.php [new file with mode: 0644]
typo3/sysext/cms/Classes/Package.php [new file with mode: 0644]
typo3/sysext/core/Classes/Cache/Backend/ClassLoaderBackend.php [new file with mode: 0755]
typo3/sysext/core/Classes/Cache/Backend/EarlyClassLoaderBackend.php [new file with mode: 0755]
typo3/sysext/core/Classes/Cache/Cache.php
typo3/sysext/core/Classes/Compatibility/LoadedExtensionArrayElement.php [new file with mode: 0644]
typo3/sysext/core/Classes/Compatibility/LoadedExtensionsArray.php [new file with mode: 0644]
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Classes/Core/ClassAliasMap.php [new file with mode: 0644]
typo3/sysext/core/Classes/Core/ClassLoader.php
typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php
typo3/sysext/core/Classes/Package.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/Exception.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/Exception/PackageStatesUnavailableException.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/FailsafePackageManager.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/Package.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/PackageFactory.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/PackageInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Package/PackageManager.php [new file with mode: 0644]
typo3/sysext/core/Classes/TypoScript/TemplateService.php
typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Package.php
typo3/sysext/core/Tests/BaseTestCase.php
typo3/sysext/core/Tests/FunctionalTestCase.php
typo3/sysext/core/Tests/FunctionalTestCaseBootstrapUtility.php
typo3/sysext/core/Tests/Unit/Configuration/TypoScript/ConditionMatching/AbstractConditionMatcherTest.php
typo3/sysext/core/Tests/Unit/Core/ClassLoaderTest.php
typo3/sysext/core/Tests/Unit/Package/PackageManagerTest.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Package/PackageTest.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/TypoScript/TemplateServiceTest.php
typo3/sysext/core/Tests/Unit/Utility/ExtensionManagementUtilityTest.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Utility/ExtensionMangementUtilityTest.php [deleted file]
typo3/sysext/core/Tests/UnitTestCase.php
typo3/sysext/core/ext_autoload.php
typo3/sysext/cshmanual/Classes/Package.php [new file with mode: 0644]
typo3/sysext/dbal/Classes/Database/DatabaseConnection.php
typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionTest.php
typo3/sysext/extbase/Classes/Package.php [new file with mode: 0644]
typo3/sysext/extensionmanager/Classes/Package.php [new file with mode: 0644]
typo3/sysext/extensionmanager/Classes/Utility/ConfigurationUtility.php
typo3/sysext/extensionmanager/Classes/Utility/InstallUtility.php
typo3/sysext/extensionmanager/Tests/Unit/Utility/ConfigurationUtilityTest.php
typo3/sysext/extensionmanager/Tests/Unit/Utility/ListUtilityTest.php
typo3/sysext/fluid/Classes/Package.php [new file with mode: 0644]
typo3/sysext/form/Classes/Utility/FilterUtility.php
typo3/sysext/form/Migrations/Code/ClassAliasMap.php
typo3/sysext/frontend/Classes/Package.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Ajax/ExtensionCompatibilityTester.php
typo3/sysext/install/Classes/Controller/Exception/RedirectLoopException.php
typo3/sysext/install/Classes/Controller/StepController.php
typo3/sysext/install/Classes/FolderStructure/LinkNode.php
typo3/sysext/install/Classes/Package.php [new file with mode: 0644]
typo3/sysext/install/Classes/Service/ClearCacheService.php
typo3/sysext/install/Classes/Service/SqlExpectedSchemaService.php
typo3/sysext/install/Classes/Updates/UpdatePackageManager.php [new file with mode: 0644]
typo3/sysext/install/Start/Install.php
typo3/sysext/install/Tests/Unit/Controller/Action/Ajax/ExtensionCompatibilityTesterTest.php
typo3/sysext/install/Tests/Unit/FolderStructure/LinkNodeTest.php
typo3/sysext/install/ext_localconf.php
typo3/sysext/lang/Classes/Package.php [new file with mode: 0644]
typo3/sysext/recordlist/Classes/Package.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/Classes/Package.php [new file with mode: 0644]
typo3/sysext/sv/Classes/Package.php [new file with mode: 0644]

index 38fdaf6..eef54b2 100644 (file)
--- a/index.php
+++ b/index.php
@@ -36,6 +36,6 @@
 require __DIR__ . '/typo3/sysext/core/Classes/Core/Bootstrap.php';
 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()
        ->baseSetup('')
 require __DIR__ . '/typo3/sysext/core/Classes/Core/Bootstrap.php';
 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()
        ->baseSetup('')
-       ->redirectToInstallerIfLocalConfigurationFileDoesNotExist();
+       ->redirectToInstallerIfEssentialConfigurationDoesNotExist();
 
 require(PATH_tslib . 'index_ts.php');
 
 require(PATH_tslib . 'index_ts.php');
index 39aa167..954c99f 100644 (file)
@@ -56,7 +56,7 @@ require __DIR__ . '/sysext/core/Classes/Core/Bootstrap.php';
 
 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()
        ->baseSetup('typo3/')
 
 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()
        ->baseSetup('typo3/')
-       ->redirectToInstallerIfLocalConfigurationFileDoesNotExist('../')
+       ->redirectToInstallerIfEssentialConfigurationDoesNotExist('../')
        ->startOutputBuffering()
        ->loadConfigurationAndInitialize()
        ->loadTypo3LoadedExtAndExtLocalconf(TRUE)
        ->startOutputBuffering()
        ->loadConfigurationAndInitialize()
        ->loadTypo3LoadedExtAndExtLocalconf(TRUE)
index 9378e73..90067dd 100644 (file)
@@ -52,7 +52,7 @@ class ExtensionRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
                $loadedExtensions = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\ObjectStorage');
                $loadedExtensionsArray = $GLOBALS['TYPO3_LOADED_EXT'];
                foreach ($loadedExtensionsArray as $extensionKey => $extension) {
                $loadedExtensions = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\ObjectStorage');
                $loadedExtensionsArray = $GLOBALS['TYPO3_LOADED_EXT'];
                foreach ($loadedExtensionsArray as $extensionKey => $extension) {
-                       if (is_array($extension) && $extension['type'] != 'S') {
+                       if ((is_array($extension) || $extension instanceof \ArrayAccess) && $extension['type'] != 'S') {
                                $emconfPath = PATH_site . $extension['siteRelPath'] . 'ext_emconf.php';
                                if (file_exists($emconfPath)) {
                                        include $emconfPath;
                                $emconfPath = PATH_site . $extension['siteRelPath'] . 'ext_emconf.php';
                                if (file_exists($emconfPath)) {
                                        include $emconfPath;
diff --git a/typo3/sysext/backend/Classes/Package.php b/typo3/sysext/backend/Classes/Package.php
new file mode 100644 (file)
index 0000000..9dae706
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+namespace TYPO3\CMS\Backend;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Package\Package as BasePackage;
+
+/**
+ * This is the backend package
+ *
+ * @author Thomas Maroschik <tmaroschik@dfau.de>
+ */
+class Package extends BasePackage {
+
+       /**
+        * @var boolean
+        */
+       protected $protected = TRUE;
+
+}
diff --git a/typo3/sysext/cms/Classes/Package.php b/typo3/sysext/cms/Classes/Package.php
new file mode 100644 (file)
index 0000000..64b926d
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+namespace TYPO3\CMS\Cms;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Package\Package as BasePackage;
+
+/**
+ * This is the cms package
+ *
+ * @author Thomas Maroschik <tmaroschik@dfau.de>
+ */
+class Package extends BasePackage {
+
+       /**
+        * @var boolean
+        */
+       protected $protected = TRUE;
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Cache/Backend/ClassLoaderBackend.php b/typo3/sysext/core/Classes/Cache/Backend/ClassLoaderBackend.php
new file mode 100755 (executable)
index 0000000..72b0f6b
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+namespace TYPO3\CMS\Core\Cache\Backend;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\PathUtility;
+
+/**
+ * A caching backend customized explicitly for the class loader.
+ * This backend is NOT public API!
+ *
+ * @internal
+ */
+class ClassLoaderBackend extends SimpleFileBackend {
+
+       /**
+        * Set a class loader cache content
+        *
+        * @TODO: Rename method
+        * @param string $entryIdentifier
+        * @param string $filePath
+        * @throws \InvalidArgumentException
+        * @internal This is not an API method
+        */
+       public function setLinkToPhpFile($entryIdentifier, $filePath) {
+               if ($entryIdentifier === '') {
+                       throw new \InvalidArgumentException('The specified entry identifier must not be empty.', 1364205170);
+               }
+               if (!@file_exists($filePath)) {
+                       throw new \InvalidArgumentException('The specified file path (' . $filePath . ') must exist.', 1364205235);
+               }
+               if (strtolower(substr($filePath, -3)) !== 'php') {
+                       throw new \InvalidArgumentException('The specified file (' . $filePath . ') must be a php file.', 1364205377);
+               }
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException('The specified entry identifier (' . $entryIdentifier . ') must not contain a path segment.', 1364205166);
+               }
+               if ($filePath[0] === '/' && \TYPO3\CMS\Core\Utility\GeneralUtility::isAllowedAbsPath($filePath)) {
+                       // Make relative if absolute to prevent wrong entries if the whole installation is moved or copied
+                       $filePath = \TYPO3\CMS\Core\Utility\PathUtility::getRelativePath($this->cacheDirectory, dirname($filePath)) . basename($filePath);
+               }
+               if ($filePath[0] === '/') {
+                       $this->set($entryIdentifier, '<?php require \'' . $filePath . '\';');
+               } else {
+                       $this->set($entryIdentifier, '<?php require __DIR__ . \'/' . $filePath . '\';');
+               }
+       }
+
+       /**
+        * Used to set alias for class
+        *
+        * @TODO: Rename method
+        * @param string $entryIdentifier
+        * @param string $otherEntryIdentifier
+        * @internal
+        */
+       public function setLinkToOtherCacheEntry($entryIdentifier, $otherEntryIdentifier) {
+               $otherCacheEntryPathAndFilename = $this->cacheDirectory . $otherEntryIdentifier . $this->cacheEntryFileExtension;
+               $this->setLinkToPhpFile($entryIdentifier, $otherCacheEntryPathAndFilename);
+       }
+
+       /**
+        * Loads data from a cache file.
+        *
+        * @param string $entryIdentifier An identifier which describes the cache entry to load
+        * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
+        * @throws \InvalidArgumentException If identifier is invalid
+        * @internal
+        */
+       public function get($entryIdentifier) {
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1334756877);
+               }
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               if (!@file_exists($pathAndFilename)) {
+                       return FALSE;
+               }
+               return file_get_contents($pathAndFilename);
+       }
+
+       /**
+        * Retrieves the target of the a linked cache entry
+        *
+        * @TODO: Rename method
+        * @param string $entryIdentifier
+        * @return bool|string
+        * @internal
+        */
+       public function getTargetOfLinkedCacheEntry($entryIdentifier) {
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               if (@file_exists($pathAndFilename)) {
+                       // If not a link
+                       $fileContent = file_get_contents($pathAndFilename);
+                       $pattern = "!^\<\?php require ((__DIR__) \. )?'([\/\.\_a-z0-9]+)';!i";
+                       $matches = array();
+                       if (preg_match($pattern, $fileContent, $matches) !== FALSE) {
+                               if (!empty($matches[3])) {
+                                       $targetPath = $matches[3];
+                                       if (!empty($matches[2]) && $matches[2] == '__DIR__') {
+                                               $targetPath = dirname($pathAndFilename) . $targetPath;
+                                       }
+                                       return \TYPO3\CMS\Core\Utility\PathUtility::getRelativePath($this->cacheDirectory, dirname($targetPath)) . basename($targetPath);
+                               }
+                       }
+               }
+               return FALSE;
+       }
+
+       /**
+        * Checks if a cache entry with the specified identifier exists.
+        *
+        * @param string $entryIdentifier
+        * @return boolean TRUE if such an entry exists, FALSE if not
+        * @throws \InvalidArgumentException
+        * @internal
+        */
+       public function has($entryIdentifier) {
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1334756878);
+               }
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               return @file_exists($pathAndFilename);
+       }
+
+       /**
+        * Checks if the given cache entry files are still valid or if their
+        * lifetime has exceeded.
+        *
+        * @param string $cacheEntryPathAndFilename
+        * @return boolean
+        * @internal
+        */
+       protected function isCacheFileExpired($cacheEntryPathAndFilename) {
+               return @file_exists($cacheEntryPathAndFilename) === FALSE;
+       }
+
+       /**
+        * Tries to find the cache entry for the specified identifier.
+        *
+        * @TODO: This methods is implemented in simple, file and this backend, but never called?
+        * @param string $entryIdentifier The cache entry identifier
+        * @return mixed The file names (including path) as an array if one or more entries could be found, otherwise FALSE
+        */
+       protected function findCacheFilesByIdentifier($entryIdentifier) {
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               return @file_exists($pathAndFilename) ? array($pathAndFilename) : FALSE;
+       }
+
+       /**
+        * Loads PHP code from the cache and require_onces it right away.
+        *
+        * @param string $entryIdentifier An identifier which describes the cache entry to load
+        * @return mixed Potential return value from the include operation
+        * @throws \InvalidArgumentException
+        * @internal
+        */
+       public function requireOnce($entryIdentifier) {
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073036);
+               }
+               return @file_exists($pathAndFilename) ? require_once $pathAndFilename : FALSE;
+       }
+
+}
diff --git a/typo3/sysext/core/Classes/Cache/Backend/EarlyClassLoaderBackend.php b/typo3/sysext/core/Classes/Cache/Backend/EarlyClassLoaderBackend.php
new file mode 100755 (executable)
index 0000000..a57fb7f
--- /dev/null
@@ -0,0 +1,158 @@
+<?php
+namespace TYPO3\CMS\Core\Cache\Backend;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * A caching backend customized explicitly for the class loader.
+ * This backend is NOT public API
+ *
+ * @internal
+ */
+class EarlyClassLoaderBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implements \TYPO3\CMS\Core\Cache\Backend\PhpCapableBackendInterface {
+
+       /**
+        * @var array Holds cache entries
+        */
+       protected $memoryBackend = array();
+
+       /**
+        * Construct this backend
+        */
+       public function __construct() {
+               parent::__construct('production', array());
+       }
+
+       /**
+        * Saves data in the cache.
+        *
+        * @param string $entryIdentifier An identifier for this specific cache entry
+        * @param string $data The data to be stored
+        * @param array $tags Tags to associate with this cache entry. If the backend does not support tags, this option can be ignored.
+        * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime.
+        * @return void
+        * @throws \TYPO3\CMS\Core\Cache\Exception if no cache frontend has been set.
+        * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException if the data is not a string
+        * @api
+        */
+       public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
+               $this->memoryBackend[$entryIdentifier] = $data;
+       }
+
+       /**
+        * Loads data from the cache.
+        *
+        * @param string $entryIdentifier An identifier which describes the cache entry to load
+        * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
+        * @api
+        */
+       public function get($entryIdentifier) {
+               return isset($this->memoryBackend[$entryIdentifier]) ? $this->memoryBackend[$entryIdentifier] : FALSE;
+       }
+
+       /**
+        * A method that returns all records
+        *
+        * @return array
+        */
+       public function getAll() {
+               return $this->memoryBackend;
+       }
+
+       /**
+        * Checks if a cache entry with the specified identifier exists.
+        *
+        * @param string $entryIdentifier An identifier specifying the cache entry
+        * @return boolean TRUE if such an entry exists, FALSE if not
+        * @api
+        */
+       public function has($entryIdentifier) {
+               return isset($this->memoryBackend[$entryIdentifier]);
+       }
+
+       /**
+        * Removes all cache entries matching the specified identifier.
+        * Usually this only affects one entry but if - for what reason ever -
+        * old entries for the identifier still exist, they are removed as well.
+        *
+        * @param string $entryIdentifier Specifies the cache entry to remove
+        * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
+        * @api
+        */
+       public function remove($entryIdentifier) {
+               if (isset($this->memoryBackend[$entryIdentifier])) {
+                       unset($this->memoryBackend[$entryIdentifier]);
+                       return TRUE;
+               }
+               return FALSE;
+       }
+
+       /**
+        * Removes all cache entries of this cache.
+        *
+        * @return void
+        * @api
+        */
+       public function flush() {
+               $this->memoryBackend = array();
+       }
+
+       /**
+        * Does garbage collection
+        *
+        * @return void
+        * @api
+        */
+       public function collectGarbage() {
+       }
+
+       /**
+        * Loads PHP code from the cache and require_onces it right away.
+        *
+        * @param string $entryIdentifier An identifier which describes the cache entry to load
+        * @return mixed Potential return value from the include operation
+        * @api
+        */
+       public function requireOnce($entryIdentifier) {
+               return require_once ($this->memoryBackend[$entryIdentifier]);
+       }
+
+       /**
+        * Used to set alias for class
+        *
+        * @TODO: Rename method
+        * @param string $entryIdentifier
+        * @param string $filePath
+        * @internal This is not an API method
+        */
+       public function setLinkToPhpFile($entryIdentifier, $filePath) {
+               $this->memoryBackend[$entryIdentifier] = $filePath;
+       }
+
+}
index 463b3c9..c8f314e 100644 (file)
@@ -31,7 +31,7 @@ namespace TYPO3\CMS\Core\Cache;
 class Cache {
 
        /**
 class Cache {
 
        /**
-        * @var         boolean
+        * @var boolean TRUE if caching framework was fully initialized
         */
        static protected $isCachingFrameworkInitialized = FALSE;
 
         */
        static protected $isCachingFrameworkInitialized = FALSE;
 
@@ -68,6 +68,18 @@ class Cache {
        }
 
        /**
        }
 
        /**
+        * Resets the isCachingFrameworkInitialized state
+        * Beware! This is not public API and necessary for edge cases in the install tool.
+        *
+        * @return void
+        */
+       static public function flagCachingFrameworkForReinitialization() {
+               self::$isCachingFrameworkInitialized = FALSE;
+               unset($GLOBALS['typo3CacheManager']);
+               unset($GLOBALS['typo3CacheFactory']);
+       }
+
+       /**
         * Helper method for install tool and extension manager to determine
         * required table structure of all caches that depend on it
         *
         * Helper method for install tool and extension manager to determine
         * required table structure of all caches that depend on it
         *
diff --git a/typo3/sysext/core/Classes/Compatibility/LoadedExtensionArrayElement.php b/typo3/sysext/core/Classes/Compatibility/LoadedExtensionArrayElement.php
new file mode 100644 (file)
index 0000000..f87bd81
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+namespace TYPO3\CMS\Core\Compatibility;
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Class to simulate the "old" extension information array element
+ *
+ * @internal
+ */
+class LoadedExtensionArrayElement implements \IteratorAggregate, \ArrayAccess, \Serializable, \Countable {
+
+       /**
+        * @var \TYPO3\Flow\Package\PackageInterface Instance of package manager
+        */
+       protected $package;
+
+       /**
+        * @var array List of relevant extension files
+        */
+       protected $extensionFilesToCheckFor = array(
+               'ext_localconf.php',
+               'ext_tables.php',
+               'ext_tables.sql',
+               'ext_tables_static+adt.sql',
+               'ext_typoscript_constants.txt',
+               'ext_typoscript_setup.txt'
+       );
+
+       /**
+        * @var array Final extension information
+        */
+       protected $extensionInformation = array();
+
+       /**
+        * Constructor builds compatibility API
+        *
+        * @param \TYPO3\Flow\Package\PackageInterface $package
+        */
+       public function __construct(\TYPO3\Flow\Package\PackageInterface $package) {
+               $this->package = $package;
+               $this->initializeBasicExtensionInformation();
+               $this->initializeExtensionFiles();
+               $this->initializeExtensionIcon();
+       }
+
+       /**
+        * Create main information
+        *
+        * @return void
+        */
+       protected function initializeBasicExtensionInformation() {
+               $pathSite = PATH_site;
+               $pathSiteLength = strlen($pathSite);
+               $absolutePackagePath = $this->package->getPackagePath();
+               if (substr($absolutePackagePath, 0, $pathSiteLength) === $pathSite) {
+                       $relativePackagePathToPathSite = substr($absolutePackagePath, $pathSiteLength);
+                       $relativePackagePathToPathSiteSegments = explode('/', $relativePackagePathToPathSite);
+                       $relativePackagePathToPathTypo3 = NULL;
+                       $packageType = NULL;
+                       // Determine if extension is installed locally, globally or system (in this order)
+                       switch (implode('/', array_slice($relativePackagePathToPathSiteSegments, 0, 2))) {
+                               case 'typo3conf/Packages':
+                                       $packageType = 'C';
+                                       $relativePackagePathToPathTypo3 = '../typo3conf/Packages/' . implode('/', array_slice($relativePackagePathToPathSiteSegments, 2));
+                                       break;
+                               case 'typo3conf/ext':
+                                       $packageType = 'L';
+                                       $relativePackagePathToPathTypo3 = '../typo3conf/ext/' . implode('/', array_slice($relativePackagePathToPathSiteSegments, 2));
+                                       break;
+                               case TYPO3_mainDir . 'ext':
+                                       $packageType = 'G';
+                                       $relativePackagePathToPathTypo3 = 'ext/' . implode('/', array_slice($relativePackagePathToPathSiteSegments, 2));
+                                       break;
+                               case TYPO3_mainDir . 'sysext':
+                                       $packageType = 'S';
+                                       $relativePackagePathToPathTypo3 = 'sysext/' . implode('/', array_slice($relativePackagePathToPathSiteSegments, 2));
+                                       break;
+                               case 'typo3temp/test_ext':
+                                       $packageType = 'T';
+                                       $relativePackagePathToPathTypo3 = '../typo3temp/test_ext/' . implode('/', array_slice($relativePackagePathToPathSiteSegments, 2));
+                                       break;
+                       }
+                       if ($packageType !== NULL && $relativePackagePathToPathSite !== NULL && $relativePackagePathToPathTypo3 !== NULL) {
+                               $this->extensionInformation['type'] = $packageType;
+                               $this->extensionInformation['siteRelPath'] = $relativePackagePathToPathSite;
+                               $this->extensionInformation['typo3RelPath'] = $relativePackagePathToPathTypo3;
+                       }
+               }
+       }
+
+       /**
+        * Initialize extension icon
+        *
+        * @return void
+        */
+       protected function initializeExtensionIcon() {
+               $this->extensionInformation['ext_icon'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getExtensionIcon($this->package->getPackagePath());
+       }
+
+       /**
+        * Register found files in extension array if extension was found
+        *
+        * @param void
+        */
+       protected function initializeExtensionFiles() {
+               foreach ($this->extensionFilesToCheckFor as $fileName) {
+                       $absolutePathToFile = $this->package->getPackagePath() . $fileName;
+                       if (@is_file($absolutePathToFile)) {
+                               $this->extensionInformation[$fileName] = $absolutePathToFile;
+                       }
+               }
+       }
+
+       /**
+        * Retrieve an external iterator
+        *
+        * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
+        * @return \Traversable An instance of an object implementing Iterator or Traversable
+        */
+       public function getIterator() {
+               return new \ArrayIterator($this->extensionInformation);
+       }
+
+       /**
+        * Whether a offset exists
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetexists.php
+        * @param mixed $offset An offset to check for.
+        * @return boolean TRUE on success or FALSE on failure.
+        */
+       public function offsetExists($offset) {
+               return isset($this->extensionInformation[$offset]);
+       }
+
+       /**
+        * Offset to retrieve
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetget.php
+        * @param mixed $offset The offset to retrieve.
+        * @return mixed Can return all value types.
+        */
+       public function offsetGet($offset) {
+               return $this->extensionInformation[$offset];
+       }
+
+       /**
+        * Offset to set
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetset.php
+        * @param mixed $offset The offset to assign the value to.
+        * @param mixed $value The value to set.
+        * @return void
+        * @throws \InvalidArgumentException
+        */
+       public function offsetSet($offset, $value) {
+               throw new \InvalidArgumentException('The array $GLOBALS[\'TYPO3_LOADED_EXT\'] may not be modified.', 1361915115);
+       }
+
+       /**
+        * Offset to unset
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetunset.php
+        * @param mixed $offset The offset to unset.
+        * @return void
+        * @throws \InvalidArgumentException
+        */
+       public function offsetUnset($offset) {
+               throw new \InvalidArgumentException('The array $GLOBALS[\'TYPO3_LOADED_EXT\'] may not be modified.', 1361915206);
+       }
+
+       /**
+        * String representation of object
+        *
+        * @link http://php.net/manual/en/serializable.serialize.php
+        * @return string the string representation of the object or null
+        */
+       public function serialize() {
+               return serialize($this->extensionInformation);
+       }
+
+       /**
+        * Constructs the object
+        *
+        * @link http://php.net/manual/en/serializable.unserialize.php
+        * @param string $serialized The string representation of the object.
+        * @return mixed the original value unserialized.
+        */
+       public function unserialize($serialized) {
+               $this->extensionInformation = unserialize($serialized);
+       }
+
+       /**
+        * Count elements of an object
+        *
+        * @link http://php.net/manual/en/countable.count.php
+        * @return integer The custom count as an integer. The return value is cast to an integer.
+        */
+       public function count() {
+               return count($this->extensionInformation);
+       }
+}
diff --git a/typo3/sysext/core/Classes/Compatibility/LoadedExtensionsArray.php b/typo3/sysext/core/Classes/Compatibility/LoadedExtensionsArray.php
new file mode 100644 (file)
index 0000000..ea74bb1
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+namespace TYPO3\CMS\Core\Compatibility;
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Class to simulate the "old" extension information array
+ *
+ * @internal
+ */
+class LoadedExtensionsArray implements \Iterator, \ArrayAccess, \Serializable, \Countable {
+
+       /**
+        * @var \TYPO3\CMS\Core\Package\PackageManager Instance of package manager
+        */
+       protected $packageManager;
+
+       /**
+        * @var array Loaded element cache
+        */
+       protected $loadedExtensionArrayElementCache = array();
+
+       /**
+        * @var string Pointer to current position
+        */
+       protected $iteratorPosition;
+
+       /**
+        * @param \TYPO3\CMS\Core\Package\PackageManager
+        */
+       public function __construct(\TYPO3\CMS\Core\Package\PackageManager $packageManager) {
+               $this->packageManager = $packageManager;
+       }
+
+       /**
+        * Whether a offset exists
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetexists.php
+        * @param mixed $offset An offset to check for.
+        * @return boolean TRUE on success or FALSE on failure.
+        */
+       public function offsetExists($offset) {
+               return $this->packageManager->isPackageActive($offset);
+       }
+
+       /**
+        * Offset to retrieve
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetget.php
+        * @param mixed $offset The offset to retrieve.
+        * @return mixed Can return all value types.
+        */
+       public function offsetGet($offset) {
+               // Pass it through the package manager, as it resolves package aliases
+               $package = $this->packageManager->getPackage($offset);
+               $packageKey = $package->getPackageKey();
+               if (!isset($this->loadedExtensionArrayElementCache[$packageKey])) {
+                       $this->loadedExtensionArrayElementCache[$packageKey] = new LoadedExtensionArrayElement($package);
+               }
+               return $this->loadedExtensionArrayElementCache[$packageKey];
+       }
+
+       /**
+        * Offset to set
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetset.php
+        * @param mixed $offset The offset to assign the value to.
+        * @param mixed $value The value to set.
+        * @throws \InvalidArgumentException
+        * @return void
+        */
+       public function offsetSet($offset, $value) {
+               throw new \InvalidArgumentException('The array $GLOBALS[\'TYPO3_LOADED_EXT\'] may not be modified.', 1361915596);
+       }
+
+       /**
+        * Offset to unset
+        *
+        * @link http://php.net/manual/en/arrayaccess.offsetunset.php
+        * @param mixed $offset The offset to unset.
+        * @throws \InvalidArgumentException
+        * @return void
+        */
+       public function offsetUnset($offset) {
+               throw new \InvalidArgumentException('The array $GLOBALS[\'TYPO3_LOADED_EXT\'] may not be modified.', 1361915610);
+       }
+
+       /**
+        * String representation of object
+        *
+        * @link http://php.net/manual/en/serializable.serialize.php
+        * @return string the string representation of the object or null
+        */
+       public function serialize() {
+               return serialize($this->loadedExtensionArrayElementCache);
+       }
+
+       /**
+        * Constructs the object
+        *
+        * @link http://php.net/manual/en/serializable.unserialize.php
+        * @param string $serialized The string representation of the object.
+        * @return mixed the original value unserialized.
+        */
+       public function unserialize($serialized) {
+               $this->loadedExtensionArrayElementCache = unserialize($serialized);
+       }
+
+       /**
+        * Count elements of an object
+        *
+        * @link http://php.net/manual/en/countable.count.php
+        * @return integer The custom count as an integer.
+        */
+       public function count() {
+               return count($this->packageManager->getActivePackages());
+       }
+
+
+       /**
+        * Return the current element
+        *
+        * @link http://php.net/manual/en/iterator.current.php
+        * @return mixed Can return any type.
+        */
+       public function current() {
+               return $this->offsetGet($this->iteratorPosition);
+       }
+
+       /**
+        * Move forward to next element
+        *
+        * @link http://php.net/manual/en/iterator.next.php
+        * @return void Any returned value is ignored.
+        */
+       public function next() {
+               $packageKeys = array_keys($this->packageManager->getActivePackages());
+               $position = array_search($this->iteratorPosition, $packageKeys);
+               if (isset($packageKeys[$position + 1])) {
+                       $this->iteratorPosition = $packageKeys[$position + 1];
+               } else {
+                       $this->iteratorPosition = NULL;
+               }
+       }
+
+       /**
+        * Return the key of the current element
+        *
+        * @link http://php.net/manual/en/iterator.key.php
+        * @return mixed scalar on success, or null on failure.
+        */
+       public function key() {
+               return $this->iteratorPosition;
+       }
+
+       /**
+        * Checks if current position is valid
+        *
+        * @link http://php.net/manual/en/iterator.valid.php
+        * @return boolean The return value will be casted to boolean and then evaluated. Returns true on success or false on failure.
+        */
+       public function valid() {
+               return $this->offsetExists($this->iteratorPosition);
+       }
+
+       /**
+        * Rewind the Iterator to the first element
+        *
+        * @link http://php.net/manual/en/iterator.rewind.php
+        * @return void Any returned value is ignored.
+        */
+       public function rewind() {
+               $this->iteratorPosition = array_shift(array_keys($this->packageManager->getActivePackages()));
+       }
+
+       /**
+        * Reset
+        *
+        * @return void
+        */
+       public function reset() {
+               $this->loadedExtensionArrayElementCache = array();
+               $this->rewind();
+       }
+
+       /**
+        * Whether package manager is set in class
+        *
+        * @return boolean TRUE if package manager is set
+        */
+       public function hasPackageManager() {
+               return $this->packageManager !== NULL;
+       }
+}
\ No newline at end of file
index eb4c672..6da84ea 100644 (file)
@@ -27,7 +27,7 @@ namespace TYPO3\CMS\Core\Core;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
-use \TYPO3\CMS\Core\Utility;
+use TYPO3\CMS\Core\Utility;
 
 require __DIR__ . '/SystemEnvironmentBuilder.php';
 
 
 require __DIR__ . '/SystemEnvironmentBuilder.php';
 
@@ -66,6 +66,16 @@ class Bootstrap {
        protected $applicationContext;
 
        /**
        protected $applicationContext;
 
        /**
+        * @var array List of early instances
+        */
+       protected $earlyInstances = array();
+
+       /**
+        * @var string Path to install tool
+        */
+       protected $installToolPath;
+
+       /**
         * Disable direct creation of this object.
         * Set unique requestId and the application context
         *
         * Disable direct creation of this object.
         * Set unique requestId and the application context
         *
@@ -86,16 +96,18 @@ class Bootstrap {
        /**
         * Return 'this' as singleton
         *
        /**
         * Return 'this' as singleton
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        static public function getInstance() {
         * @internal This is not a public API method, do not use in own extensions
         */
        static public function getInstance() {
-               if (is_null(self::$instance)) {
+               if (is_null(static::$instance)) {
                        require_once(__DIR__ . '/ApplicationContext.php');
                        $applicationContext = trim(getenv('TYPO3_CONTEXT'), '"\' ') ? : 'Production';
                        require_once(__DIR__ . '/ApplicationContext.php');
                        $applicationContext = trim(getenv('TYPO3_CONTEXT'), '"\' ') ? : 'Production';
-                       self::$instance = new \TYPO3\CMS\Core\Core\Bootstrap($applicationContext);
+                       self::$instance = new static($applicationContext);
+                       // Establish an alias for Flow/Package interoperability
+                       class_alias(get_class(static::$instance), 'TYPO3\\Flow\\Core\\Bootstrap');
                }
                }
-               return self::$instance;
+               return static::$instance;
        }
 
        /**
        }
 
        /**
@@ -123,7 +135,7 @@ class Bootstrap {
         * Prevent any unwanted output that may corrupt AJAX/compression.
         * This does not interfere with "die()" or "echo"+"exit()" messages!
         *
         * Prevent any unwanted output that may corrupt AJAX/compression.
         * This does not interfere with "die()" or "echo"+"exit()" messages!
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function startOutputBuffering() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function startOutputBuffering() {
@@ -138,7 +150,7 @@ class Bootstrap {
         * Script execution will be aborted if something fails here.
         *
         * @param string $relativePathPart Relative path of the entry script back to document root
         * Script execution will be aborted if something fails here.
         *
         * @param string $relativePathPart Relative path of the entry script back to document root
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function baseSetup($relativePathPart = '') {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function baseSetup($relativePathPart = '') {
@@ -151,13 +163,12 @@ class Bootstrap {
         * Redirect to install tool if LocalConfiguration.php is missing.
         *
         * @param string $pathUpToDocumentRoot Can contain eg. '../' if called from a sub directory
         * Redirect to install tool if LocalConfiguration.php is missing.
         *
         * @param string $pathUpToDocumentRoot Can contain eg. '../' if called from a sub directory
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
         * @internal This is not a public API method, do not use in own extensions
         */
-       public function redirectToInstallerIfLocalConfigurationFileDoesNotExist($pathUpToDocumentRoot = '') {
-               /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
-               $configurationManager = Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
-               if (!file_exists($configurationManager->getLocalConfigurationFileLocation())) {
+       public function redirectToInstallerIfEssentialConfigurationDoesNotExist($pathUpToDocumentRoot = '') {
+               $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager;
+               if (!file_exists($configurationManager->getLocalConfigurationFileLocation()) || !file_exists(PATH_typo3conf . 'PackageStates.php')) {
                        require_once __DIR__ . '/../Utility/HttpUtility.php';
                        Utility\HttpUtility::redirect($pathUpToDocumentRoot . 'typo3/sysext/install/Start/Install.php');
                }
                        require_once __DIR__ . '/../Utility/HttpUtility.php';
                        Utility\HttpUtility::redirect($pathUpToDocumentRoot . 'typo3/sysext/install/Start/Install.php');
                }
@@ -165,27 +176,66 @@ class Bootstrap {
        }
 
        /**
        }
 
        /**
+        * Registers the instance of the specified object for an early boot stage.
+        * On finalizing the Object Manager initialization, all those instances will
+        * be transferred to the Object Manager's registry.
+        *
+        * @param string $objectName Object name, as later used by the Object Manager
+        * @param object $instance The instance to register
+        * @return void
+        */
+       public function setEarlyInstance($objectName, $instance) {
+               $this->earlyInstances[$objectName] = $instance;
+       }
+
+       /**
+        * Returns an instance which was registered earlier through setEarlyInstance()
+        *
+        * @param string $objectName Object name of the registered instance
+        * @return object
+        * @throws \TYPO3\CMS\Core\Exception
+        */
+       public function getEarlyInstance($objectName) {
+               if (!isset($this->earlyInstances[$objectName])) {
+                       throw new \TYPO3\CMS\Core\Exception('Unknown early instance "' . $objectName . '"', 1365167380);
+               }
+               return $this->earlyInstances[$objectName];
+       }
+
+       /**
+        * Returns all registered early instances indexed by object name
+        *
+        * @return array
+        */
+       public function getEarlyInstances() {
+               return $this->earlyInstances;
+       }
+
+       /**
         * Includes LocalConfiguration.php and sets several
         * global settings depending on configuration.
         *
         * @param boolean $allowCaching Whether to allow caching - affects cache_core (autoloader)
         * Includes LocalConfiguration.php and sets several
         * global settings depending on configuration.
         *
         * @param boolean $allowCaching Whether to allow caching - affects cache_core (autoloader)
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer)
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
         * @internal This is not a public API method, do not use in own extensions
         */
-       public function loadConfigurationAndInitialize($allowCaching = TRUE) {
-               $bootstrap = $this->getInstance();
-
-               $bootstrap->populateLocalConfiguration();
+       public function loadConfigurationAndInitialize($allowCaching = TRUE, $packageManagerClassName = 'TYPO3\\CMS\\Core\\Package\\PackageManager') {
+               $this
+                       ->initializeClassLoader()
+                       ->populateLocalConfiguration()
+                       ->initializeCachingFramework()
+                       ->initializeClassLoaderCache()
+                       ->initializePackageManagement($packageManagerClassName);
 
 
+               // @TODO dig into this
                if (!$allowCaching) {
                if (!$allowCaching) {
-                       $bootstrap->setCoreCacheToNullBackend();
+                       $this->setCoreCacheToNullBackend();
                }
 
                }
 
-               $bootstrap->defineDatabaseConstants()
+               $this->defineDatabaseConstants()
                        ->defineUserAgentConstant()
                        ->registerExtDirectComponents()
                        ->defineUserAgentConstant()
                        ->registerExtDirectComponents()
-                       ->initializeCachingFramework()
-                       ->registerAutoloader()
                        ->checkUtf8DatabaseSettingsOrDie()
                        ->transferDeprecatedCurlSettings()
                        ->setCacheHashOptions()
                        ->checkUtf8DatabaseSettingsOrDie()
                        ->transferDeprecatedCurlSettings()
                        ->setCacheHashOptions()
@@ -203,15 +253,79 @@ class Bootstrap {
        }
 
        /**
        }
 
        /**
-        * Load TYPO3_LOADED_EXT and ext_localconf
+        * Initializes the Class Loader
+        *
+        * @return Bootstrap
+        */
+       protected function initializeClassLoader() {
+               $classLoader = new \TYPO3\CMS\Core\Core\ClassLoader();
+               $this->setEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassLoader', $classLoader);
+               $classLoader->setEarlyClassFileAutoloadRegistry((array) include __DIR__ . '/../../ext_autoload.php');
+               $classAliasMap = new \TYPO3\CMS\Core\Core\ClassAliasMap();
+               $classAliasMap->injectClassLoader($classLoader);
+               $this->setEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassAliasMap', $classAliasMap);
+               $classLoader->injectClassAliasMap($classAliasMap);
+               spl_autoload_register(array($classLoader, 'loadClass'), TRUE, TRUE);
+               return $this;
+       }
+
+       /**
+        * Reinitializes the class loader during clear cache actions
+        * Beware! This is not public API and necessary for edge cases in the install tool
+        *
+        * @return void
+        */
+       public function reinitializeClassLoaderAndCachesAndPackageManagement($packageManagerClassName = 'TYPO3\\CMS\\Core\\Package\\PackageManager') {
+               $currentClassLoader = $this->getEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassLoader');
+               spl_autoload_unregister(array($currentClassLoader, 'loadClass'));
+               \TYPO3\CMS\Core\Cache\Cache::flagCachingFrameworkForReinitialization();
+               $this
+                       ->initializeClassLoader()
+                       ->populateLocalConfiguration()
+                       ->initializeCachingFramework()
+                       ->initializeClassLoaderCache()
+                       ->initializePackageManagement($packageManagerClassName);
+       }
+
+       /**
+        * Initialize class loader cache.
+        *
+        * @return Bootstrap
+        */
+       protected function initializeClassLoaderCache() {
+               /** @var $classLoader \TYPO3\CMS\Core\Core\ClassLoader */
+               $classLoader = $this->getEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassLoader');
+               $classLoader->injectClassesCache($this->getEarlyInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_classes'));
+               return $this;
+       }
+
+       /**
+        * Initializes the package system and loads the package configuration and settings
+        * provided by the packages.
+        *
+        * @param string $packageManagerClassName Define an alternative package manager implementation (usually for the installer)
+        * @return Bootstrap
+        */
+       protected function initializePackageManagement($packageManagerClassName) {
+               $packageManager = new $packageManagerClassName();
+               $this->setEarlyInstance('TYPO3\\Flow\\Package\\PackageManager', $packageManager);
+               \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::setPackageManager($packageManager);
+               $packageManager->injectClassLoader($this->getEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassLoader'));
+               $packageManager->injectCoreCache($this->getEarlyInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_core'));
+               $packageManager->initialize($this, PATH_site);
+               $GLOBALS['TYPO3_LOADED_EXT'] = new \TYPO3\CMS\Core\Compatibility\LoadedExtensionsArray($packageManager);
+               return $this;
+       }
+
+       /**
+        * Load ext_localconf of extensions
         *
         * @param boolean $allowCaching
         *
         * @param boolean $allowCaching
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function loadTypo3LoadedExtAndExtLocalconf($allowCaching = TRUE) {
                $this->getInstance()
         * @internal This is not a public API method, do not use in own extensions
         */
        public function loadTypo3LoadedExtAndExtLocalconf($allowCaching = TRUE) {
                $this->getInstance()
-                       ->populateTypo3LoadedExtGlobal($allowCaching)
                        ->loadAdditionalConfigurationFromExtensions($allowCaching);
                return $this;
        }
                        ->loadAdditionalConfigurationFromExtensions($allowCaching);
                return $this;
        }
@@ -219,13 +333,16 @@ class Bootstrap {
        /**
         * Load TYPO3_LOADED_EXT, recreate class loader registry and load ext_localconf
         *
        /**
         * Load TYPO3_LOADED_EXT, recreate class loader registry and load ext_localconf
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @TODO: This method was changed with the package manager patch, do we still need it?
+        * @param boolean $allowCaching
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function reloadTypo3LoadedExtAndClassLoaderAndExtLocalconf() {
                $bootstrap = $this->getInstance();
         * @internal This is not a public API method, do not use in own extensions
         */
        public function reloadTypo3LoadedExtAndClassLoaderAndExtLocalconf() {
                $bootstrap = $this->getInstance();
-               $bootstrap->populateTypo3LoadedExtGlobal(FALSE);
-               \TYPO3\CMS\Core\Core\ClassLoader::loadClassLoaderCache();
+               // Commented out for package management patch, method is still used in extensionmanager
+               //              $bootstrap->populateTypo3LoadedExtGlobal(FALSE);
+               //              \TYPO3\CMS\Core\Core\ClassLoader::loadClassLoaderCache();
                $bootstrap->loadAdditionalConfigurationFromExtensions(FALSE);
                return $this;
        }
                $bootstrap->loadAdditionalConfigurationFromExtensions(FALSE);
                return $this;
        }
@@ -233,7 +350,7 @@ class Bootstrap {
        /**
         * Sets up additional configuration applied in all scopes
         *
        /**
         * Sets up additional configuration applied in all scopes
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function applyAdditionalConfigurationSettings() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function applyAdditionalConfigurationSettings() {
@@ -248,7 +365,7 @@ class Bootstrap {
        /**
         * Throws an exception if no browser could be identified
         *
        /**
         * Throws an exception if no browser could be identified
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @throws \RuntimeException
         * @internal This is not a public API method, do not use in own extensions
         */
         * @throws \RuntimeException
         * @internal This is not a public API method, do not use in own extensions
         */
@@ -261,16 +378,14 @@ class Bootstrap {
        }
 
        /**
        }
 
        /**
-        * Populate the local configuration.
-        * Merge default TYPO3_CONF_VARS with content of typo3conf/LocalConfiguration.php,
-        * execute typo3conf/AdditionalConfiguration.php, define database related constants.
+        * We need an early instance of the configuration manager.
+        * Since makeInstance relies on the object configuration, we create it here with new instead.
         *
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
-        * @internal This is not a public API method, do not use in own extensions
+        * @return Bootstrap
         */
        public function populateLocalConfiguration() {
         */
        public function populateLocalConfiguration() {
-               /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
-               $configurationManager = Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
+               $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager();
+               $this->setEarlyInstance('TYPO3\CMS\Core\Configuration\ConfigurationManager', $configurationManager);
                $configurationManager->exportConfiguration();
                return $this;
        }
                $configurationManager->exportConfiguration();
                return $this;
        }
@@ -317,7 +432,7 @@ class Bootstrap {
        /**
         * Register default ExtDirect components
         *
        /**
         * Register default ExtDirect components
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function registerExtDirectComponents() {
                if (TYPO3_MODE === 'BE') {
         */
        protected function registerExtDirectComponents() {
                if (TYPO3_MODE === 'BE') {
@@ -341,20 +456,12 @@ class Bootstrap {
        /**
         * Initialize caching framework
         *
        /**
         * Initialize caching framework
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function initializeCachingFramework() {
         */
        protected function initializeCachingFramework() {
+               // @todo Please deuglify
                \TYPO3\CMS\Core\Cache\Cache::initializeCachingFramework();
                \TYPO3\CMS\Core\Cache\Cache::initializeCachingFramework();
-               return $this;
-       }
-
-       /**
-        * Register autoloader
-        *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
-        */
-       protected function registerAutoloader() {
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
+               $this->setEarlyInstance('TYPO3\CMS\Core\Cache\CacheManager', $GLOBALS['typo3CacheManager']);
                return $this;
        }
 
                return $this;
        }
 
@@ -366,7 +473,7 @@ class Bootstrap {
         * [SYS][setDBinit] is used to set the DB connection
         * and both settings need to be adjusted for UTF-8 in order to work properly
         *
         * [SYS][setDBinit] is used to set the DB connection
         * and both settings need to be adjusted for UTF-8 in order to work properly
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function checkUtf8DatabaseSettingsOrDie() {
                if (isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit']) &&
         */
        protected function checkUtf8DatabaseSettingsOrDie() {
                if (isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit']) &&
@@ -390,8 +497,8 @@ class Bootstrap {
        /**
         * Parse old curl options and set new http ones instead
         *
        /**
         * Parse old curl options and set new http ones instead
         *
-        * @TODO : This code segment must still be finished
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @TODO: This code segment must still be finished
+        * @return Bootstrap
         */
        protected function transferDeprecatedCurlSettings() {
                if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer'])) {
         */
        protected function transferDeprecatedCurlSettings() {
                if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer'])) {
@@ -410,7 +517,7 @@ class Bootstrap {
        /**
         * Set cacheHash options
         *
        /**
         * Set cacheHash options
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function setCacheHashOptions() {
                $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash'] = array(
         */
        protected function setCacheHashOptions() {
                $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash'] = array(
@@ -429,7 +536,7 @@ class Bootstrap {
        /**
         * Set default timezone
         *
        /**
         * Set default timezone
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function setDefaultTimezone() {
                $timeZone = $GLOBALS['TYPO3_CONF_VARS']['SYS']['phpTimeZone'];
         */
        protected function setDefaultTimezone() {
                $timeZone = $GLOBALS['TYPO3_CONF_VARS']['SYS']['phpTimeZone'];
@@ -450,7 +557,7 @@ class Bootstrap {
        /**
         * Initialize the locales handled by TYPO3
         *
        /**
         * Initialize the locales handled by TYPO3
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function initializeL10nLocales() {
                \TYPO3\CMS\Core\Localization\Locales::initialize();
         */
        protected function initializeL10nLocales() {
                \TYPO3\CMS\Core\Localization\Locales::initialize();
@@ -462,7 +569,7 @@ class Bootstrap {
         * string (e.g. if edited in Install Tool)
         *
         * @TODO : Remove, if the Install Tool handles such data types correctly
         * string (e.g. if edited in Install Tool)
         *
         * @TODO : Remove, if the Install Tool handles such data types correctly
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function convertPageNotFoundHandlingToBoolean() {
                if (!strcasecmp($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'], 'TRUE')) {
         */
        protected function convertPageNotFoundHandlingToBoolean() {
                if (!strcasecmp($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'], 'TRUE')) {
@@ -477,7 +584,7 @@ class Bootstrap {
         * Note: Yes, this is possible in php! xdebug() is then a global function, even
         * if registerGlobalDebugFunctions() is encapsulated in class scope.
         *
         * Note: Yes, this is possible in php! xdebug() is then a global function, even
         * if registerGlobalDebugFunctions() is encapsulated in class scope.
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function registerGlobalDebugFunctions() {
                require_once('GlobalDebugFunctions.php');
         */
        protected function registerGlobalDebugFunctions() {
                require_once('GlobalDebugFunctions.php');
@@ -499,7 +606,7 @@ class Bootstrap {
        /**
         * Configure and set up exception and error handling
         *
        /**
         * Configure and set up exception and error handling
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function configureExceptionHandling() {
                $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['errors']['exceptionHandler'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['productionExceptionHandler'];
         */
        protected function configureExceptionHandling() {
                $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['errors']['exceptionHandler'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['productionExceptionHandler'];
@@ -533,7 +640,7 @@ class Bootstrap {
         * Set PHP memory limit depending on value of
         * $GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit']
         *
         * Set PHP memory limit depending on value of
         * $GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit']
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function setMemoryLimit() {
                if (intval($GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit']) > 16) {
         */
        protected function setMemoryLimit() {
                if (intval($GLOBALS['TYPO3_CONF_VARS']['SYS']['setMemoryLimit']) > 16) {
@@ -546,7 +653,7 @@ class Bootstrap {
         * Define TYPO3_REQUESTTYPE* constants
         * so devs exactly know what type of request it is
         *
         * Define TYPO3_REQUESTTYPE* constants
         * so devs exactly know what type of request it is
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function defineTypo3RequestTypes() {
                define('TYPO3_REQUESTTYPE_FE', 1);
         */
        protected function defineTypo3RequestTypes() {
                define('TYPO3_REQUESTTYPE_FE', 1);
@@ -559,25 +666,13 @@ class Bootstrap {
        }
 
        /**
        }
 
        /**
-        * Set up $GLOBALS['TYPO3_LOADED_EXT'] array with basic information
-        * about extensions.
-        *
-        * @param boolean $allowCaching
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
-        */
-       protected function populateTypo3LoadedExtGlobal($allowCaching = TRUE) {
-               $GLOBALS['TYPO3_LOADED_EXT'] = Utility\ExtensionManagementUtility::loadTypo3LoadedExtensionInformation($allowCaching);
-               return $this;
-       }
-
-       /**
         * Load extension configuration files (ext_localconf.php)
         *
         * The ext_localconf.php files in extensions are meant to make changes
         * to the global $TYPO3_CONF_VARS configuration array.
         *
         * @param boolean $allowCaching
         * Load extension configuration files (ext_localconf.php)
         *
         * The ext_localconf.php files in extensions are meant to make changes
         * to the global $TYPO3_CONF_VARS configuration array.
         *
         * @param boolean $allowCaching
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function loadAdditionalConfigurationFromExtensions($allowCaching = TRUE) {
                Utility\ExtensionManagementUtility::loadExtLocalconf($allowCaching);
         */
        protected function loadAdditionalConfigurationFromExtensions($allowCaching = TRUE) {
                Utility\ExtensionManagementUtility::loadExtLocalconf($allowCaching);
@@ -587,11 +682,11 @@ class Bootstrap {
        /**
         * Initialize exception handling
         *
        /**
         * Initialize exception handling
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function initializeExceptionHandling() {
         */
        protected function initializeExceptionHandling() {
-               if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['errors']['exceptionHandler'] !== '') {
-                       if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandler'] !== '') {
+               if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['errors']['exceptionHandler'])) {
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandler'])) {
                                // Register an error handler for the given errorHandlerErrors
                                $errorHandler = Utility\GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandler'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandlerErrors']);
                                // Set errors which will be converted in an exception
                                // Register an error handler for the given errorHandlerErrors
                                $errorHandler = Utility\GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandler'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['errorHandlerErrors']);
                                // Set errors which will be converted in an exception
@@ -608,7 +703,7 @@ class Bootstrap {
         * Extensions may register new caches, so we set the
         * global cache array to the manager again at this point
         *
         * Extensions may register new caches, so we set the
         * global cache array to the manager again at this point
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function setFinalCachingFrameworkCacheConfiguration() {
                $GLOBALS['typo3CacheManager']->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
         */
        protected function setFinalCachingFrameworkCacheConfiguration() {
                $GLOBALS['typo3CacheManager']->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
@@ -618,7 +713,7 @@ class Bootstrap {
        /**
         * Define logging and exception constants
         *
        /**
         * Define logging and exception constants
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function defineLoggingAndExceptionConstants() {
                define('TYPO3_DLOG', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_DLOG']);
         */
        protected function defineLoggingAndExceptionConstants() {
                define('TYPO3_DLOG', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_DLOG']);
@@ -631,7 +726,7 @@ class Bootstrap {
         * Unsetting reserved global variables:
         * Those are set in "ext:core/ext_tables.php" file:
         *
         * Unsetting reserved global variables:
         * Those are set in "ext:core/ext_tables.php" file:
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         */
        protected function unsetReservedGlobalVariables() {
                unset($GLOBALS['PAGES_TYPES']);
         */
        protected function unsetReservedGlobalVariables() {
                unset($GLOBALS['PAGES_TYPES']);
@@ -654,6 +749,7 @@ class Bootstrap {
        /**
         * Initialize database connection in $GLOBALS and connect if requested
         *
        /**
         * Initialize database connection in $GLOBALS and connect if requested
         *
+        * @param boolean $connect Whether db should be connected
         * @return \TYPO3\CMS\Core\Core\Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
         * @return \TYPO3\CMS\Core\Core\Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
@@ -714,7 +810,7 @@ class Bootstrap {
         * to an URL in file typo3conf/LOCK_BACKEND or exit the script
         *
         * @throws \RuntimeException
         * to an URL in file typo3conf/LOCK_BACKEND or exit the script
         *
         * @throws \RuntimeException
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function checkLockedBackendAndRedirectOrDie() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function checkLockedBackendAndRedirectOrDie() {
@@ -741,7 +837,7 @@ class Bootstrap {
         * Compare client IP with IPmaskList and exit the script run
         * if the client is not allowed to access the backend
         *
         * Compare client IP with IPmaskList and exit the script run
         * if the client is not allowed to access the backend
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function checkBackendIpOrDie() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function checkBackendIpOrDie() {
@@ -762,7 +858,7 @@ class Bootstrap {
         * Check lockSSL configuration variable and redirect
         * to https version of the backend if needed
         *
         * Check lockSSL configuration variable and redirect
         * to https version of the backend if needed
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function checkSslBackendAndRedirectIfNeeded() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function checkSslBackendAndRedirectIfNeeded() {
@@ -808,7 +904,7 @@ class Bootstrap {
         * This way, ext_tables.php ist not executed every time, but $GLOBALS['TCA']
         * is still always there.
         *
         * This way, ext_tables.php ist not executed every time, but $GLOBALS['TCA']
         * is still always there.
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function loadCachedTca() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function loadCachedTca() {
@@ -834,7 +930,7 @@ class Bootstrap {
         * according cache file if exists.
         *
         * @param boolean $allowCaching True, if reading compiled ext_tables file from cache is allowed
         * according cache file if exists.
         *
         * @param boolean $allowCaching True, if reading compiled ext_tables file from cache is allowed
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function loadExtensionTables($allowCaching = TRUE) {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function loadExtensionTables($allowCaching = TRUE) {
@@ -898,7 +994,7 @@ class Bootstrap {
        /**
         * Initialize sprite manager
         *
        /**
         * Initialize sprite manager
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeSpriteManager() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeSpriteManager() {
@@ -909,7 +1005,7 @@ class Bootstrap {
        /**
         * Initialize backend user object in globals
         *
        /**
         * Initialize backend user object in globals
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeBackendUser() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeBackendUser() {
@@ -934,7 +1030,7 @@ class Bootstrap {
        /**
         * Initialize backend user mount points
         *
        /**
         * Initialize backend user mount points
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeBackendUserMounts() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeBackendUserMounts() {
@@ -948,7 +1044,7 @@ class Bootstrap {
        /**
         * Initialize language object
         *
        /**
         * Initialize language object
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeLanguageObject() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeLanguageObject() {
@@ -961,7 +1057,7 @@ class Bootstrap {
        /**
         * Throw away all output that may have happened during bootstrapping by weird extensions
         *
        /**
         * Throw away all output that may have happened during bootstrapping by weird extensions
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function endOutputBufferingAndCleanPreviousOutput() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function endOutputBufferingAndCleanPreviousOutput() {
@@ -972,7 +1068,7 @@ class Bootstrap {
        /**
         * Initialize output compression if configured
         *
        /**
         * Initialize output compression if configured
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeOutputCompression() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeOutputCompression() {
@@ -988,7 +1084,7 @@ class Bootstrap {
        /**
         * Initialize module menu object
         *
        /**
         * Initialize module menu object
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeModuleMenuObject() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeModuleMenuObject() {
@@ -1003,11 +1099,10 @@ class Bootstrap {
         * This method is called in all important scripts for a clean
         * shut down of the system.
         *
         * This method is called in all important scripts for a clean
         * shut down of the system.
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function shutdown() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function shutdown() {
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
                return $this;
        }
 
                return $this;
        }
 
@@ -1015,7 +1110,7 @@ class Bootstrap {
         * Provides an instance of "template" for backend-modules to
         * work with.
         *
         * Provides an instance of "template" for backend-modules to
         * work with.
         *
-        * @return \TYPO3\CMS\Core\Core\Bootstrap
+        * @return Bootstrap
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeBackendTemplate() {
         * @internal This is not a public API method, do not use in own extensions
         */
        public function initializeBackendTemplate() {
diff --git a/typo3/sysext/core/Classes/Core/ClassAliasMap.php b/typo3/sysext/core/Classes/Core/ClassAliasMap.php
new file mode 100644 (file)
index 0000000..8ba8ec5
--- /dev/null
@@ -0,0 +1,312 @@
+<?php
+namespace TYPO3\CMS\Core\Core;
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * This class is responsible for setting and containing class aliases
+ */
+class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Old class name to new class name mapping
+        *
+        * @var array
+        */
+       protected $aliasToClassNameMapping = array();
+
+       /**
+        * New class name to old class name mapping
+        *
+        * @var array
+        */
+       protected $classNameToAliasMapping = array();
+
+       /**
+        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
+        */
+       protected $classesCache;
+
+       /**
+        * @var \TYPO3\CMS\Core\Core\ClassLoader
+        */
+       protected $classLoader;
+
+       /**
+        * @var string Cache identifier
+        */
+       protected $cacheIdentifier;
+
+       /**
+        * @var array<\TYPO3\Flow\Package\Package>
+        */
+       protected $packages = array();
+
+       /**
+        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache
+        */
+       public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache) {
+               $this->classesCache = $classesCache;
+       }
+
+       /**
+        * @param \TYPO3\CMS\Core\Core\ClassLoader
+        */
+       public function injectClassLoader(\TYPO3\CMS\Core\Core\ClassLoader $classLoader) {
+               $this->classLoader = $classLoader;
+       }
+
+       /**
+        * @return string
+        */
+       protected function getCacheIdentifier() {
+               return $this->cacheIdentifier;
+       }
+
+       /**
+        * Set cache identifier
+        *
+        * @param string $cacheIdentifier
+        * @return ClassAliasMap
+        */
+       public function setCacheIdentifier($cacheIdentifier) {
+               $this->cacheIdentifier = $cacheIdentifier;
+               return $this;
+       }
+
+       /**
+        * Get cache identifier
+        *
+        * @return string
+        */
+       protected function getCacheEntryIdentifier() {
+               $cacheIdentifier = $this->getCacheIdentifier();
+               return $cacheIdentifier !== NULL ? 'ClassAliasMap_' . TYPO3_MODE  . '_' . $cacheIdentifier : NULL;
+       }
+
+       /**
+        * Set packages
+        *
+        * @param array $packages
+        */
+       public function setPackages(array $packages) {
+               $this->packages = $packages;
+               if (!$this->loadEarlyInstanceMappingFromCache()) {
+                       $classNameToAliasMapping = $this->buildMappingAndInitializeEarlyInstanceMapping();
+                       $this->buildMappingFiles($classNameToAliasMapping);
+               }
+       }
+
+       /**
+        * Return class name to alias mapping
+        *
+        * @param array $packages
+        * @return array
+        */
+       public function setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead(array $packages) {
+               $this->packages = $packages;
+               return $this->buildMappingAndInitializeEarlyInstanceMapping();
+       }
+
+       /**
+        * Load early instance mapping
+        *
+        * @return boolean
+        */
+       protected function loadEarlyInstanceMappingFromCache() {
+               $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
+               if (!$cacheEntryIdentifier !== NULL && $this->classesCache->has($cacheEntryIdentifier)) {
+                       return (bool) $this->classesCache->requireOnce($cacheEntryIdentifier);
+               }
+               return FALSE;
+       }
+
+       /**
+        * Build mapping for early instances
+        *
+        * @return array
+        */
+       protected function buildMappingAndInitializeEarlyInstanceMapping() {
+               $aliasToClassNameMapping = array();
+                       foreach ($this->packages as $package) {
+                       if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
+                               $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $package->getClassAliases());
+                       }
+               }
+               $lowercasedAliasToClassNameMapping = array();
+               foreach ($aliasToClassNameMapping as $aliasClassName => $className) {
+                       $lowercasedAliasToClassNameMapping[strtolower($aliasClassName)] = $className;
+               }
+               $aliasToClassNameMapping = $lowercasedAliasToClassNameMapping;
+               $classNameToAliasMapping = array();
+               foreach (array_flip($aliasToClassNameMapping) as $className => $aliasClassName) {
+                       $lookUpClassName = strtolower($className);
+                       if (!isset($classNameToAliasMapping[$lookUpClassName])) {
+                               $classNameToAliasMapping[$lookUpClassName] = array();
+                       }
+                       $classNameToAliasMapping[$lookUpClassName][] = $aliasClassName;
+               }
+
+               $this->buildEarlyInstanceMappingAndSaveToCache($aliasToClassNameMapping);
+
+               $classNameToAliasMapping = array();
+               foreach ($aliasToClassNameMapping as $aliasClassName => $originalClassName) {
+                       $classNameToAliasMapping[$originalClassName][$aliasClassName] = $aliasClassName;
+               }
+
+               return $classNameToAliasMapping;
+       }
+
+       /**
+        * Build mapping files
+        *
+        * @param array $classNameToAliasMapping
+        * @return void
+        */
+       public function buildMappingFiles(array $classNameToAliasMapping) {
+               /** @var $cacheBackend \TYPO3\CMS\Core\Cache\Backend\ClassLoaderBackend */
+               $cacheBackend = $this->classesCache->getBackend();
+               foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
+                       $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
+                       // Trigger autoloading for all aliased class names, so a cache entry is created
+                       if ($this->classLoader->loadClass($originalClassName, FALSE) && $originalClassTarget = $cacheBackend->getTargetOfLinkedCacheEntry($originalClassNameCacheEntryIdentifier)) {
+                               $proxyContent = array(
+                                       $this->buildRequireOnceCommand($originalClassTarget),
+                                       $this->buildClassLoaderCommand(),
+                               );
+                               foreach ($aliasClassNames as $aliasClassName) {
+                                       $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
+                               }
+                               $this->classesCache->set($originalClassNameCacheEntryIdentifier, implode(LF, $proxyContent));
+                       }
+               }
+               foreach ($classNameToAliasMapping as $originalClassName => $aliasClassNames) {
+                       foreach ($aliasClassNames as $aliasClassName) {
+                               $aliasClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($aliasClassName));
+                               $originalClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($originalClassName));
+                               if ($this->classesCache->has($aliasClassNameCacheEntryIdentifier)) {
+                                       $this->classesCache->remove($aliasClassNameCacheEntryIdentifier);
+                               }
+                               // Link all aliases to original cache entry
+                               if ($this->classesCache->has($originalClassNameCacheEntryIdentifier)) {
+                                       $cacheBackend->setLinkToOtherCacheEntry($aliasClassNameCacheEntryIdentifier, $originalClassNameCacheEntryIdentifier);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Build and save mapping files to cache
+        *
+        * @param array $aliasToClassNameMapping
+        * @return void
+        */
+       protected function buildEarlyInstanceMappingAndSaveToCache(array $aliasToClassNameMapping) {
+               $classedLoadedPriorToClassLoader = array_intersect($aliasToClassNameMapping, array_merge(get_declared_classes(), get_declared_interfaces()));
+               if (!empty($classedLoadedPriorToClassLoader)) {
+                       $proxyContent = array($this->buildClassLoaderCommand());
+                       foreach ($classedLoadedPriorToClassLoader as $aliasClassName => $originalClassName) {
+                               $proxyContent[] = $this->buildAliasCommand($aliasClassName, $originalClassName);
+                       }
+                       $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
+                       if ($cacheEntryIdentifier !== NULL) {
+                               $this->classesCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
+                               $this->classesCache->requireOnce($cacheEntryIdentifier);
+                       } else {
+                               eval(implode(PHP_EOL, $proxyContent));
+                       }
+               }
+       }
+
+       /**
+        * String command to build class loader
+        *
+        * @return string
+        */
+       protected function buildClassLoaderCommand() {
+               return '$classLoader = \\TYPO3\\CMS\\Core\\Core\\Bootstrap::getInstance()->getEarlyInstance(\'TYPO3\\CMS\\Core\\Core\\ClassLoader\');';
+       }
+
+       /**
+        * String command to build class alias
+        *
+        * @param string $aliasClassName
+        * @param string $originalClassName
+        * @return string
+        */
+       protected function buildAliasCommand($aliasClassName, $originalClassName) {
+               return sprintf('%s->setAliasForClassName(\'%s\', \'%s\');', '$classLoader', $aliasClassName, $originalClassName);
+       }
+
+       /**
+        * String command to build class alias
+        *
+        * @param string $classFilePath
+        * @return string
+        */
+       protected function buildRequireOnceCommand($classFilePath) {
+               return sprintf('require_once __DIR__ . \'/%s\';', $classFilePath);
+       }
+
+       /**
+        * Set an alias for a class name
+        *
+        * @param string $aliasClassName
+        * @param string $originalClassName
+        * @return bool true on success or false on failure
+        */
+       public function setAliasForClassName($aliasClassName, $originalClassName) {
+               if (isset($this->aliasToClassNameMapping[$lowercasedAliasClassName = strtolower($aliasClassName)])) {
+                       return TRUE;
+               }
+               $this->aliasToClassNameMapping[$lowercasedAliasClassName] = $originalClassName;
+               $this->classNameToAliasMapping[strtolower($originalClassName)][$lowercasedAliasClassName] = $aliasClassName;
+               return (\class_exists($aliasClassName, FALSE) || \interface_exists($aliasClassName, FALSE)) ? TRUE : class_alias($originalClassName, $aliasClassName);
+       }
+
+       /**
+        * Get final class name of alias
+        *
+        * @param string $alias
+        * @return mixed
+        */
+       public function getClassNameForAlias($alias) {
+               $lookUpClassName = strtolower($alias);
+               return isset($this->aliasToClassNameMapping[$lookUpClassName]) ? $this->aliasToClassNameMapping[$lookUpClassName] : $alias;
+       }
+
+
+       /**
+        * Get list of aliases for class name
+        *
+        * @param string $className
+        * @return mixed
+        */
+       public function getAliasesForClassName($className) {
+               $lookUpClassName = strtolower($className);
+               return isset($this->classNameToAliasMapping[$lookUpClassName]) ? $this->classNameToAliasMapping[$lookUpClassName] : array($className);
+       }
+}
\ No newline at end of file
index 58d94d5..0a4d3ed 100644 (file)
@@ -1,11 +1,10 @@
 <?php
 namespace TYPO3\CMS\Core\Core;
 <?php
 namespace TYPO3\CMS\Core\Core;
-use \TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /***************************************************************
  *  Copyright notice
  *
 
 /***************************************************************
  *  Copyright notice
  *
- *  (c) 2008-2013 Dmitry Dulepov <dmitry@typo3.org>
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -28,301 +27,225 @@ use \TYPO3\CMS\Core\Utility\GeneralUtility;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+
 /**
 /**
- * This class contains TYPO3 class loader for classes.
- * It handles:
- * - The core of TYPO3
- * - All extensions with an ext_autoload.php file
- * - All extensions that stick to the 'extbase' like naming convention
- *
- * @author Dmitry Dulepov <dmitry@typo3.org>
- * @author Martin Kutschker <masi@typo3.org>
- * @author Oliver Hader <oliver@typo3.org>
- * @author Sebastian Kurfürst <sebastian@typo3.org>
- * @author Christian Kuhn <lolli@schwarzbu.ch>
+ * Class Loader implementation which loads .php files found in the classes
+ * directory of an object.
  */
 class ClassLoader {
 
  */
 class ClassLoader {
 
+       const VALID_CLASSNAME_PATTERN = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9\\\\_\x7f-\xff]*$/';
+
        /**
        /**
-        * Contains the class loaders class name
-        *
-        * @var string
+        * @var ClassAliasMap
         */
         */
-       static protected $className = __CLASS__;
+       protected $classAliasMap;
 
        /**
 
        /**
-        * Class name to file mapping. Key: class name. Value: fully qualified file name.
-        *
-        * @var array
+        * @var ClassAliasMap
         */
         */
-       static protected $classNameToFileMapping = array();
+       static protected $staticAliasMap;
 
        /**
 
        /**
-        * @var boolean TRUE, if old to new and new to old mapping was populated to PHP
+        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
         */
         */
-       static protected $mappingLoaded = FALSE;
+       protected $classesCache;
 
        /**
 
        /**
-        * Old class name to new class name mapping
-        *
-        * @var array
+        * @var string
         */
         */
-       static protected $aliasToClassNameMapping = array();
+       protected $cacheIdentifier;
 
        /**
 
        /**
-        * New class name to old class name mapping
-        *
-        * @var array
+        * @var array<\TYPO3\Flow\Package\Package>
         */
         */
-       static protected $classNameToAliasMapping = array();
+       protected $packages = array();
 
        /**
 
        /**
-        * Name of cache entry identifier in autoload cache
-        *
-        * @var string
+        * @var array
         */
         */
-       static protected $classLoaderCacheIdentifier = NULL;
+       protected $earlyClassFileAutoloadRegistry = array();
 
        /**
 
        /**
-        * Track if the cache file written to disk should be updated.
-        * This is set to TRUE if during script run new classes are
-        * found (for example due to new requested extbase classes)
-        * and is used in unregisterAutoloader() to decide whether or not
-        * the cache file should be re-written.
-        *
-        * @var bool True if mapping changed
+        * @var array A list of namespaces this class loader is definitely responsible for
         */
         */
-       static protected $cacheUpdateRequired = FALSE;
+       protected $packageNamespaces = array(
+               'TYPO3\CMS\Core' => 14
+       );
 
        /**
 
        /**
-        * The class loader is static, thus we do not allow instances of this class.
+        * @var array A list of packages and their replaces pointing to class paths
         */
         */
-       private function __construct() {
+       protected $packageClassesPaths = array();
 
 
+       public function __construct() {
+               $this->classesCache = new \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend('cache_classes', new \TYPO3\CMS\Core\Cache\Backend\EarlyClassLoaderBackend());
        }
 
        /**
        }
 
        /**
-        * Installs TYPO3 class loader, and loads the autoload registry for the core.
+        * Get class alias map list injected
         *
         *
-        * @return boolean TRUE in case of success
+        * @param ClassAliasMap
         */
         */
-       static public function registerAutoloader() {
-               static::loadClassLoaderCache();
-               return spl_autoload_register(static::$className . '::autoload', TRUE, TRUE);
+       public function injectClassAliasMap(ClassAliasMap $classAliasMap) {
+               $this->classAliasMap = $classAliasMap;
+               static::$staticAliasMap = $classAliasMap;
        }
 
        /**
        }
 
        /**
-        * Unload TYPO3 class loader and write any additional classes
-        * found during the script run to the cache file.
-        *
-        * This method is called during shutdown of the framework.
+        * Get classes cache injected
         *
         *
-        * @return boolean TRUE in case of success
+        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache
         */
         */
-       static public function unregisterAutoloader() {
-               if (static::$cacheUpdateRequired) {
-                       static::updateClassLoaderCacheEntry(array(static::$classNameToFileMapping, static::$aliasToClassNameMapping));
-                       static::$cacheUpdateRequired = FALSE;
+       public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache) {
+               /** @var $earlyClassLoaderBackend \TYPO3\CMS\Core\Cache\Backend\EarlyClassLoaderBackend */
+               $earlyClassLoaderBackend = $this->classesCache->getBackend();
+               $this->classesCache = $classesCache;
+               $this->classAliasMap->injectClassesCache($classesCache);
+               foreach ($earlyClassLoaderBackend->getAll() as $cacheEntryIdentifier => $classFilePath) {
+                       if (!$this->classesCache->has($cacheEntryIdentifier)) {
+                               $this->addClassToCache($classFilePath, $cacheEntryIdentifier);
+                       }
                }
                }
-               static::$classNameToFileMapping = array();
-               static::$aliasToClassNameMapping = array();
-               static::$classNameToAliasMapping = array();
-               return spl_autoload_unregister(static::$className . '::autoload');
        }
 
        /**
        }
 
        /**
-        * Autoload function for TYPO3.
+        * Loads php files containing classes or interfaces found in the classes directory of
+        * a package and specifically registered classes.
         *
         *
-        * This method looks up class names in the registry
-        * (which contains extensions and core files)
-        *
-        * @param string $className Class name
-        * @return void
+        * @param string $className Name of the class/interface to load
+        * @param bool $require TRUE if file should be required
+        * @return boolean
         */
         */
-       static public function autoload($className) {
-               $className = ltrim($className, '\\');
-               $realClassName = static::getClassNameForAlias($className);
-               $aliasClassName = static::getAliasForClassName($className);
-               $hasAliasClassName = ($aliasClassName !== $className);
-               $lookUpClassName = ($hasRealClassName = $className !== $realClassName) ? $realClassName : $className;
-               // Use core and extension registry
-               $classPath = static::getClassPathByRegistryLookup($lookUpClassName);
-               if ($classPath && !class_exists($realClassName, FALSE)) {
-                       // Include the required file that holds the class
-                       // Handing over the class name here is only done for the
-                       // compatibility class loader so that it can skip class names
-                       // which do not require rewriting. We can remove this bad
-                       // code smell once we can get rid of the compatibility class loader.
-                       static::requireClassFileOnce($classPath, $className);
-                       try {
-                               // Regular expression for a valid classname taken from
-                               // http://www.php.net/manual/en/language.oop5.basic.php
-                               if (preg_match('/^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*$/', $className)) {
-                                       spl_autoload($className);
-                               }
-                       } catch (\LogicException $exception) {
-
-                       }
+       public function loadClass($className, $require = TRUE) {
+               if ($className[0] === '\\') {
+                       $className = substr($className, 1);
                }
                }
-               if ($hasRealClassName && !class_exists($className, FALSE)) {
-                       class_alias($realClassName, $className);
-               }
-               if ($hasAliasClassName && !class_exists($aliasClassName, FALSE)) {
-                       class_alias($className, $aliasClassName);
+
+               if (!$this->isValidClassname($className)) {
+                       return FALSE;
                }
                }
-       }
 
 
-       /**
-        * Require the class file
-        *
-        * @static
-        * @param string $classPath
-        * @param string $className
-        */
-       static protected function requireClassFileOnce($classPath, $className) {
-               GeneralUtility::requireOnce($classPath);
-       }
+               $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $className));
+               $cacheEntryCreated = FALSE;
 
 
-       /**
-        * Load registry from cache file if available or search
-        * for all loaded extensions and create a cache file
-        *
-        * @return void
-        */
-       static public function loadClassLoaderCache() {
-               $classRegistry = NULL;
-               $aliasToClassNameMapping = NULL;
-               /** @var $phpCodeCache \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend */
-               $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_core');
-               // Create autoload cache file if it does not exist yet
-               if ($phpCodeCache->has(static::getClassLoaderCacheIdentifier())) {
-                       list($classRegistry, $aliasToClassNameMapping) = $phpCodeCache->requireOnce(static::getClassLoaderCacheIdentifier());
+               // Loads any known class via caching framework
+               if ($require) {
+                       if ($this->classesCache->has($cacheEntryIdentifier) && $this->classesCache->requireOnce($cacheEntryIdentifier) !== FALSE) {
+                               $cacheEntryCreated = TRUE;
+                       }
                }
                }
-               // This can only happen if the class loader was already registered
-               // in the same call once, the requireOnce of the cache file then
-               // does not give the cached array back. In this case we just read
-               // all cache entries manually again.
-               // This can happen in unit tests and if the cache backend was
-               // switched to NullBackend for example to simplify development
-               if (!is_array($aliasToClassNameMapping)) {
-                       static::$cacheUpdateRequired = TRUE;
-                       $aliasToClassNameMapping = static::createCoreAndExtensionClassAliasMap();
+
+               if (!$cacheEntryCreated) {
+                       $cacheEntryCreated = $this->createCacheEntryForClassFromCorePackage($className, $cacheEntryIdentifier);
                }
                }
-               static::$aliasToClassNameMapping = $aliasToClassNameMapping;
-               static::$classNameToAliasMapping = array_flip($aliasToClassNameMapping);
-               self::setAliasesForEarlyInstances();
 
 
-               if (!is_array($classRegistry)) {
-                       static::$cacheUpdateRequired = TRUE;
-                       $classRegistry = static::lowerCaseClassRegistry(static::createCoreAndExtensionRegistry());
+               if (!$cacheEntryCreated) {
+                       $cacheEntryCreated = $this->createCacheEntryForClassFromEarlyAutoloadRegistry($className, $cacheEntryIdentifier);
                }
                }
-               static::$classNameToFileMapping = $classRegistry;
-       }
 
 
-       /**
-        * Collects and merges the class alias maps of extensions
-        *
-        * @return array The class alias map
-        */
-       static protected function createCoreAndExtensionClassAliasMap() {
-               $aliasToClassNameMapping = array();
-               foreach (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getLoadedExtensionListArray() as $extensionKey) {
-                       try {
-                               $extensionClassAliasMap = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($extensionKey, 'Migrations/Code/ClassAliasMap.php');
-                               if (@file_exists($extensionClassAliasMap)) {
-                                       $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, (array) require $extensionClassAliasMap);
-                               }
-                       } catch (\BadFunctionCallException $e) {
-                       }
+               if (!$cacheEntryCreated) {
+                       $cacheEntryCreated = $this->createCacheEntryForClassFromRegisteredPackages($className, $cacheEntryIdentifier);
                }
                }
-               foreach ($aliasToClassNameMapping as $oldClassName => $newClassName) {
-                       $aliasToClassNameMapping[GeneralUtility::strtolower($oldClassName)] = $newClassName;
+
+               if (!$cacheEntryCreated) {
+                       $cacheEntryCreated = $this->createCacheEntryForClassByNamingConvention($className, $cacheEntryIdentifier);
                }
                }
-               // Order by key length longest first
-               uksort($aliasToClassNameMapping, function($a, $b) {
-                       return strlen($b) - strlen($a);
-               });
-               return $aliasToClassNameMapping;
-       }
 
 
-       /**
-        * Create aliases for early loaded classes
-        */
-       protected static function setAliasesForEarlyInstances() {
-               $classedLoadedPriorToClassLoader = array_intersect(static::$aliasToClassNameMapping, get_declared_classes());
-               if (!empty($classedLoadedPriorToClassLoader)) {
-                       foreach ($classedLoadedPriorToClassLoader as $oldClassName => $newClassName) {
-                               if (!class_exists($oldClassName, FALSE)) {
-                                       class_alias($newClassName, $oldClassName);
-                               }
+               if ($cacheEntryCreated && $require) {
+                       if ($this->classesCache->has($cacheEntryIdentifier) && $this->classesCache->requireOnce($cacheEntryIdentifier) !== FALSE) {
+                               $cacheEntryCreated = TRUE;
                        }
                }
                        }
                }
+
+               return $cacheEntryCreated;
        }
 
        /**
        }
 
        /**
-        * @param string $alias
-        * @return mixed
+        * Find out if a class name is valid
+        *
+        * @param string $className
+        * @return bool
         */
         */
-       static public function getClassNameForAlias($alias) {
-               $lookUpClassName = GeneralUtility::strtolower($alias);
-               return isset(static::$aliasToClassNameMapping[$lookUpClassName]) ? static::$aliasToClassNameMapping[$lookUpClassName] : $alias;
+       protected function isValidClassname($className) {
+               return (bool) preg_match(self::VALID_CLASSNAME_PATTERN, $className);
        }
 
        }
 
-
        /**
        /**
+        * Create cache entry for class from core package
+        *
         * @param string $className
         * @param string $className
-        * @return mixed
+        * @param string $cacheEntryIdentifier
+        * @return boolean TRUE if cache entry exists
         */
         */
-       static public function getAliasForClassName($className) {
-               return isset(static::$classNameToAliasMapping[$className]) ? static::$classNameToAliasMapping[$className] : $className;
+       protected function createCacheEntryForClassFromCorePackage($className, $cacheEntryIdentifier) {
+               if (substr($cacheEntryIdentifier, 0, 14) === 'typo3_cms_core') {
+                       $classesFolder = substr($cacheEntryIdentifier, 15, 5) === 'tests' ? '' : 'Classes/';
+                       $classFilePath = PATH_typo3 . 'sysext/core/' . $classesFolder . str_replace('\\', '/', substr($className, 15)) . '.php';
+                       if (@file_exists($classFilePath)) {
+                               $this->addClassToCache($classFilePath, $cacheEntryIdentifier);
+                               return TRUE;
+                       }
+               }
+               return FALSE;
        }
 
        /**
        }
 
        /**
-        * Get the full path to a class by looking it up in the registry.
-        * If not found, returns NULL.
+        * Create early class name autoload registry cache
         *
         *
-        * Warning: This method is public as it is needed by \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(),
-        * but it is _not_ part of the public API and should not be used in own extensions!
-        *
-        * @param string $className Class name to find source file of
-        * @return mixed If String: Full name of the file where $className is declared, NULL if no entry is found
-        * @internal
+        * @param string $className
+        * @param string $cacheEntryIdentifier
+        * @return boolean TRUE if cache file was created
         */
         */
-       static public function getClassPathByRegistryLookup($className) {
-               $classPath = NULL;
-               $classNameLower = GeneralUtility::strtolower($className);
-               // Try to resolve extbase naming scheme if class is not already in cache file
-               if (!array_key_exists($classNameLower, static::$classNameToFileMapping)) {
-                       static::attemptToLoadRegistryWithNamingConventionForGivenClassName($className);
-               }
-               // Look up class name in cache file
-               if (array_key_exists($classNameLower, static::$classNameToFileMapping)) {
-                       $classPath = static::$classNameToFileMapping[$classNameLower];
+       protected function createCacheEntryForClassFromEarlyAutoloadRegistry($className, $cacheEntryIdentifier) {
+               if (isset($this->earlyClassFileAutoloadRegistry[$lowercasedClassName = strtolower($className)])) {
+                       if (@file_exists($this->earlyClassFileAutoloadRegistry[$lowercasedClassName])) {
+                               $this->addClassToCache($this->earlyClassFileAutoloadRegistry[$lowercasedClassName], $cacheEntryIdentifier);
+                               return TRUE;
+                       }
                }
                }
-
-               return $classPath;
+               return FALSE;
        }
 
        /**
        }
 
        /**
-        * Find all ext_autoload files and merge with core_autoload.
+        * Create cache entry from registered packages
         *
         *
-        * @return array
+        * @param string $className
+        * @param string $cacheEntryIdentifier
+        * @return boolean TRUE File was created
         */
         */
-       static protected function createCoreAndExtensionRegistry() {
-               $classRegistry = array();
-               // At this point during bootstrap the local configuration is initialized,
-               // ExtensionManagementUtility is ready to get the list of enabled extensions
-               foreach (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getLoadedExtensionListArray() as $extensionKey) {
-                       try {
-                               $extensionAutoloadFile = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($extensionKey, 'ext_autoload.php');
-                               if (@file_exists($extensionAutoloadFile)) {
-                                       $classRegistry = array_merge($classRegistry, (array) require $extensionAutoloadFile);
+       protected function createCacheEntryForClassFromRegisteredPackages($className, $cacheEntryIdentifier) {;
+               foreach ($this->packageNamespaces as $packageNamespace => $packageData) {
+                       if (substr(str_replace('_', '\\', $className), 0, $packageData['namespaceLength']) === $packageNamespace) {
+                               if ($packageData['substituteNamespaceInPath']) {
+                                       // If it's a TYPO3 package, classes don't comply to PSR-0.
+                                       // The namespace part is substituted.
+                                       $classPathAndFilename = '/' . str_replace('\\', '/', ltrim(substr($className, $packageData['namespaceLength']), '\\')) . '.php';
+                               } else {
+                                       // make the classname PSR-0 compliant by replacing underscores only in the classname not in the namespace
+                                       $classPathAndFilename  = '';
+                                       $lastNamespacePosition = strrpos($className, '\\');
+                                       if ($lastNamespacePosition !== FALSE) {
+                                               $namespace = substr($className, 0, $lastNamespacePosition);
+                                               $className = substr($className, $lastNamespacePosition + 1);
+                                               $classPathAndFilename  = str_replace('\\', '/', $namespace) . '/';
+                                       }
+                                       $classPathAndFilename .= str_replace('_', '/', $className) . '.php';
+                               }
+                               if (strtolower(substr($className, $packageData['namespaceLength'], 5)) === 'tests') {
+                                       $classPathAndFilename = $packageData['packagePath'] . $classPathAndFilename;
+                               } else {
+                                       $classPathAndFilename = $packageData['classesPath'] . $classPathAndFilename;
+                               }
+                               if (@file_exists($classPathAndFilename)) {
+                                       $this->addClassToCache($classPathAndFilename, $cacheEntryIdentifier);
+                                       return TRUE;
                                }
                                }
-                       } catch (\BadFunctionCallException $e) {
-
                        }
                }
                        }
                }
-               return $classRegistry;
+               return FALSE;
        }
 
        /**
        }
 
        /**
@@ -331,24 +254,23 @@ class ClassLoader {
         * array to the file system to save this lookup for next call.
         *
         * @param string $className Class name to find source file of
         * array to the file system to save this lookup for next call.
         *
         * @param string $className Class name to find source file of
-        * @return void
+        * @param string $classCacheEntryIdentifier
+        * @return boolean TRUE if was created
         */
         */
-       static protected function attemptToLoadRegistryWithNamingConventionForGivenClassName($className) {
+       protected function createCacheEntryForClassByNamingConvention($className, $classCacheEntryIdentifier) {
                $delimiter = '_';
                $delimiter = '_';
-               $tempClassName = $className;
-               // To handle namespaced class names, get rid of the first backslash
-               // and replace the remaining ones with underscore. This will simulate
-               // a 'usual' "extbase" structure like 'Tx_ExtensionName_Foo_bar'
+               // To handle namespaced class names, split the class name at the
+               // namespace delimiters.
                if (strpos($className, '\\') !== FALSE) {
                if (strpos($className, '\\') !== FALSE) {
-                       $tempClassName = ltrim($className, '\\');
                        $delimiter = '\\';
                }
                        $delimiter = '\\';
                }
-               $classNameParts = explode($delimiter, $tempClassName, 4);
 
 
-               // we only handle classes that follow the convention Vendor\Product\Classname or is longer
+               $classNameParts = explode($delimiter, $className, 4);
+
+               // We only handle classes that follow the convention Vendor\Product\Classname or is longer
                // so we won't deal with class names that only have one or two parts
                if (count($classNameParts) <= 2) {
                // so we won't deal with class names that only have one or two parts
                if (count($classNameParts) <= 2) {
-                       return;
+                       return FALSE;
                }
 
                if (isset($classNameParts[0]) && $classNameParts[0] === 'TYPO3' && (isset($classNameParts[1]) && $classNameParts[1] === 'CMS')) {
                }
 
                if (isset($classNameParts[0]) && $classNameParts[0] === 'TYPO3' && (isset($classNameParts[1]) && $classNameParts[1] === 'CMS')) {
@@ -363,79 +285,241 @@ class ClassLoader {
                        }
                }
 
                        }
                }
 
-               if ($extensionKey) {
-                       try {
-                               // This will throw a BadFunctionCallException if the extension is not loaded
-                               $extensionPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($extensionKey);
-                               $classPath = (substr(strtolower($classNameWithoutVendorAndProduct), 0, 5) === 'tests') ? '' : 'Classes/';
-                               $classFilePathAndName = $extensionPath . $classPath . strtr($classNameWithoutVendorAndProduct, $delimiter, '/') . '.php';
-                               static::addClassToCache($classFilePathAndName, $className);
-                       } catch (\BadFunctionCallException $exception) {
+               if ($extensionKey && isset($this->packageClassesPaths[$extensionKey])) {
+                       if (substr(strtolower($classNameWithoutVendorAndProduct), 0, 5) === 'tests') {
+                               $classesPath = $this->packages[$extensionKey]->getPackagePath();
+                       } else {
+                               $classesPath = $this->packageClassesPaths[$extensionKey];
+                       }
+                       $classFilePath = $classesPath . strtr($classNameWithoutVendorAndProduct, $delimiter, '/') . '.php';
+                       if (@file_exists($classFilePath)) {
+                               $this->addClassToCache($classFilePath, $classCacheEntryIdentifier);
+                               return TRUE;
+                       }
+               }
+
+               return FALSE;
+       }
+
+       /**
+        * Get cache identifier
+        *
+        * @return string identifier
+        */
+       protected function getCacheIdentifier() {
+               return $this->cacheIdentifier;
+       }
+
+       /**
+        * Get cache entry identifier
+        *
+        * @return string identifier
+        */
+       protected function getCacheEntryIdentifier() {
+               $cacheIdentifier = $this->getCacheIdentifier();
+               return $cacheIdentifier !== NULL ? 'ClassLoader_' . $this->getCacheIdentifier() : NULL;
+       }
+
+       /**
+        * Set cache identifier
+        *
+        * @param string $cacheIdentifier Cache identifier
+        * @return ClassLoader
+        */
+       public function setCacheIdentifier($cacheIdentifier) {
+               $this->cacheIdentifier = $cacheIdentifier;
+               $this->classAliasMap->setCacheIdentifier($cacheIdentifier);
+               return $this;
+       }
+
+       /**
+        * Sets the available packages
+        *
+        * @param array $packages An array of \TYPO3\Flow\Package\Package objects
+        * @return ClassLoader
+        */
+       public function setPackages(array $packages) {
+               $this->packages = $packages;
+               if (!$this->loadPackageNamespacesFromCache()) {
+                       $this->buildPackageNamespaces();
+                       $this->buildPackageClassesPathsForLegacyExtensions();
+                       $this->savePackageNamespacesAndClassesPathsToCache();
+                       // Rebuild the class alias map too because ext_autoload can contain aliases
+                       $classNameToAliasMapping = $this->classAliasMap->setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead($packages);
+                       $this->buildAutoloadRegistryAndSaveToCache();
+                       $this->classAliasMap->buildMappingFiles($classNameToAliasMapping);
+               } else {
+                       $this->classAliasMap->setPackages($packages);
+               }
+               return $this;
+       }
 
 
+       /**
+        * Load package namespaces from cache
+        *
+        * @return boolean TRUE if package namespaces were loaded
+        */
+       protected function loadPackageNamespacesFromCache() {
+               $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
+               if ($cacheEntryIdentifier !== NULL && $this->classesCache->has($cacheEntryIdentifier)) {
+                       list($packageNamespaces, $packageClassesPaths) = $this->classesCache->requireOnce($cacheEntryIdentifier);
+                       if (is_array($packageNamespaces) && is_array($packageClassesPaths)) {
+                               $this->packageNamespaces = $packageNamespaces;
+                               $this->packageClassesPaths = $packageClassesPaths;
+                               return TRUE;
                        }
                }
                        }
                }
+               return FALSE;
        }
 
        /**
        }
 
        /**
-        * Adds a single class to class loader cache.
+        * Build package namespaces
         *
         *
-        * @static
-        * @param string $classFilePathAndName Physical path of file containing $className
-        * @param string $className Class name
         * @return void
         */
         * @return void
         */
-       static protected function addClassToCache($classFilePathAndName, $className) {
-               if (file_exists($classFilePathAndName)) {
-                       static::$cacheUpdateRequired = TRUE;
-                       static::$classNameToFileMapping[GeneralUtility::strtolower($className)] = $classFilePathAndName;
+       protected function buildPackageNamespaces() {
+               /** @var $package \TYPO3\Flow\Package\Package */
+               foreach ($this->packages as $package) {
+                       $packageNamespace = $package->getNamespace();
+                       // Ignore legacy extensions with unkown vendor name
+                       if ($packageNamespace[0] !== '*') {
+                               $this->packageNamespaces[$packageNamespace] = array(
+                                       'namespaceLength' => strlen($packageNamespace),
+                                       'classesPath' => $package->getClassesPath(),
+                                       'packagePath' => $package->getPackagePath(),
+                                       'substituteNamespaceInPath' => ($package instanceof \TYPO3\CMS\Core\Package\Package)
+                               );
+                       }
                }
                }
+               // Sort longer package namespaces first, to find specific matches before generic ones
+               $sortPackages = function($a, $b) {
+                       if (($lenA = strlen($a)) === ($lenB = strlen($b))) {
+                               return strcmp($a, $b);
+                       }
+                       return ($lenA > $lenB) ? -1 : 1;
+               };
+               uksort($this->packageNamespaces, $sortPackages);
        }
 
        /**
        }
 
        /**
-        * Set or update class loader cache entry.
-        * It is expected that all class names (keys) are already lowercased!
+        * Build autoload registry
         *
         *
-        * @param array $cacheContent Current class loader cache entries
         * @return void
         */
         * @return void
         */
-       static protected function updateClassLoaderCacheEntry(array $cacheContent) {
-               $cachedFileContent = 'return ' . var_export($cacheContent, TRUE) . ';';
-               $GLOBALS['typo3CacheManager']->getCache('cache_core')->set(static::getClassLoaderCacheIdentifier(), $cachedFileContent);
+       protected function buildAutoloadRegistryAndSaveToCache() {
+               $classFileAutoloadRegistry = array();
+               foreach ($this->packages as $package) {
+                       /** @var $package \TYPO3\CMS\Core\Package\Package */
+                       if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
+                               $classFilesFromAutoloadRegistry = $package->getClassFilesFromAutoloadRegistry();
+                               if (is_array($classFilesFromAutoloadRegistry)) {
+                                       $classFileAutoloadRegistry = array_merge($classFileAutoloadRegistry, $classFilesFromAutoloadRegistry);
+                               }
+                       }
+               }
+               foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
+                       if (@file_exists($classFilePath)) {
+                               $this->addClassToCache($classFilePath, strtolower(str_replace('\\', '_', $className)));
+                       }
+               }
        }
 
        /**
        }
 
        /**
-        * Gets the identifier used for caching the registry files.
-        * The identifier depends on the current TYPO3 version and the
-        * installation path of the TYPO3 site (PATH_site).
+        * Builds the classes paths for legacy extensions with unknown vendor name
         *
         *
-        * In effect, a new registry cache file will be created
-        * when moving to a newer version with possible new core classes
-        * or moving the webroot to another absolute path.
+        * @return void
+        */
+       protected function buildPackageClassesPathsForLegacyExtensions() {
+               foreach ($this->packages as $package) {
+                       if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface) {
+                               $this->packageClassesPaths[$package->getPackageKey()] = $package->getClassesPath();
+                               foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
+                                       $this->packageClassesPaths[$packageToReplace] = $package->getClassesPath();
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Save package namespaces and classes paths to cache
         *
         *
-        * @return string identifier
+        * @return void
         */
         */
-       static protected function getClassLoaderCacheIdentifier() {
-               if (is_null(static::$classLoaderCacheIdentifier)) {
-                       static::$classLoaderCacheIdentifier = 'ClassLoader_' . sha1((TYPO3_version . PATH_site . 'ClassLoader'));
+       protected function savePackageNamespacesAndClassesPathsToCache() {
+               $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
+               if ($cacheEntryIdentifier !== NULL) {
+                       $this->classesCache->set(
+                               $this->getCacheEntryIdentifier(),
+                               'return ' . var_export(array($this->packageNamespaces, $this->packageClassesPaths), TRUE) . ';'
+                       );
                }
                }
-               return static::$classLoaderCacheIdentifier;
        }
 
        /**
        }
 
        /**
-        * Lowercase all keys of the class registry.
+        * Adds a single class to class loader cache.
+        *
+        * @param string $classFilePathAndName Physical path of file containing $className
+        * @param string $classCacheEntryIdentifier
+        */
+       protected function addClassToCache($classFilePathAndName, $classCacheEntryIdentifier) {
+               /** @var $classesCacheBackend \TYPO3\CMS\Core\Cache\Backend\EarlyClassLoaderBackend|\TYPO3\CMS\Core\Cache\Backend\ClassLoaderBackend */
+               $classesCacheBackend = $this->classesCache->getBackend();
+               $classesCacheBackend->setLinkToPhpFile(
+                       $classCacheEntryIdentifier,
+                       $classFilePathAndName
+               );
+       }
+
+       /**
+        * This method is necessary for the early loading of the cores autoload registry
         *
         *
-        * Use the multi byte safe version of strtolower from
-        * GeneralUtility, so array_change_key_case() can not be used
+        * @param array $classFileAutoloadRegistry
+        */
+       public function setEarlyClassFileAutoloadRegistry($classFileAutoloadRegistry) {
+               $this->earlyClassFileAutoloadRegistry = $classFileAutoloadRegistry;
+       }
+
+       /**
+        * Set alias for class name
         *
         *
-        * @param array $registry Given registry entries
-        * @return array with lower cased keys
+        * @param string $aliasClassName
+        * @param string $originalClassName
+        * @return boolean
         */
         */
-       static protected function lowerCaseClassRegistry($registry) {
-               $lowerCasedClassRegistry = array();
-               foreach ($registry as $className => $classFile) {
-                       $lowerCasedClassRegistry[GeneralUtility::strtolower($className)] = $classFile;
-               }
-               return $lowerCasedClassRegistry;
+       public function setAliasForClassName($aliasClassName, $originalClassName) {
+               return $this->classAliasMap->setAliasForClassName($aliasClassName, $originalClassName);
+       }
+
+       /**
+        * Get class name for alias
+        *
+        * @param string $alias
+        * @return mixed
+        */
+       static public function getClassNameForAlias($alias) {
+               return static::$staticAliasMap->getClassNameForAlias($alias);
+       }
+
+       /**
+        * Get alias for class name
+        *
+        * @param string $className
+        * @deprecated since 6.2, use getAliasesForClassName instead. will be removed 2 versions later
+        * @return mixed
+        */
+       static public function getAliasForClassName($className) {
+               $aliases = static::$staticAliasMap->getAliasesForClassName($className);
+               return (is_array($aliases) && isset($aliases[0])) ? $aliases[0] : NULL;
+       }
+
+       /**
+        * Get an aliases for a class name
+        *
+        * @param string $className
+        * @return mixed
+        */
+       static public function getAliasesForClassName($className) {
+               return static::$staticAliasMap->getAliasesForClassName($className);
        }
 
 }
        }
 
 }
index 1a40d28..44e8d56 100644 (file)
@@ -61,7 +61,6 @@ class SystemEnvironmentBuilder {
                self::definePaths($relativePathPart);
                self::checkMainPathsExist();
                self::requireBaseClasses();
                self::definePaths($relativePathPart);
                self::checkMainPathsExist();
                self::requireBaseClasses();
-               self::setupClassAliasForLegacyBaseClasses();
                self::handleMagicQuotesGpc();
                self::addCorePearPathToIncludePath();
                self::initializeGlobalVariables();
                self::handleMagicQuotesGpc();
                self::addCorePearPathToIncludePath();
                self::initializeGlobalVariables();
@@ -107,9 +106,6 @@ class SystemEnvironmentBuilder {
                // Security related constant: List of file extensions that should be registered as php script file extensions
                define('PHP_EXTENSIONS_DEFAULT', 'php,php3,php4,php5,php6,phpsh,inc,phtml');
 
                // Security related constant: List of file extensions that should be registered as php script file extensions
                define('PHP_EXTENSIONS_DEFAULT', 'php,php3,php4,php5,php6,phpsh,inc,phtml');
 
-               // List of extensions required to run the core
-               define('REQUIRED_EXTENSIONS', 'core,backend,frontend,cms,lang,sv,extensionmanager,recordlist,extbase,fluid,cshmanual,install,saltedpasswords');
-
                // Operating system identifier
                // Either "WIN" or empty string
                define('TYPO3_OS', self::getTypo3Os());
                // Operating system identifier
                // Either "WIN" or empty string
                define('TYPO3_OS', self::getTypo3Os());
@@ -191,39 +187,19 @@ class SystemEnvironmentBuilder {
        static protected function requireBaseClasses() {
                require_once __DIR__ . '/../Utility/GeneralUtility.php';
                require_once __DIR__ . '/../Utility/ArrayUtility.php';
        static protected function requireBaseClasses() {
                require_once __DIR__ . '/../Utility/GeneralUtility.php';
                require_once __DIR__ . '/../Utility/ArrayUtility.php';
+               require_once __DIR__ . '/../Utility/PathUtility.php';
                require_once __DIR__ . '/../SingletonInterface.php';
                require_once __DIR__ . '/../Configuration/ConfigurationManager.php';
                require_once __DIR__ . '/../SingletonInterface.php';
                require_once __DIR__ . '/../Configuration/ConfigurationManager.php';
-               require_once __DIR__ . '/../Utility/ExtensionManagementUtility.php';
-               require_once __DIR__ . '/../Cache/Cache.php';
-               require_once __DIR__ . '/../Cache/Exception.php';
-               require_once __DIR__ . '/../Cache/Exception/NoSuchCacheException.php';
-               require_once __DIR__ . '/../Cache/Exception/InvalidDataException.php';
-               require_once __DIR__ . '/../Cache/CacheFactory.php';
-               require_once __DIR__ . '/../Cache/CacheManager.php';
                require_once __DIR__ . '/../Cache/Frontend/FrontendInterface.php';
                require_once __DIR__ . '/../Cache/Frontend/AbstractFrontend.php';
                require_once __DIR__ . '/../Cache/Frontend/StringFrontend.php';
                require_once __DIR__ . '/../Cache/Frontend/PhpFrontend.php';
                require_once __DIR__ . '/../Cache/Backend/BackendInterface.php';
                require_once __DIR__ . '/../Cache/Frontend/FrontendInterface.php';
                require_once __DIR__ . '/../Cache/Frontend/AbstractFrontend.php';
                require_once __DIR__ . '/../Cache/Frontend/StringFrontend.php';
                require_once __DIR__ . '/../Cache/Frontend/PhpFrontend.php';
                require_once __DIR__ . '/../Cache/Backend/BackendInterface.php';
-               require_once __DIR__ . '/../Cache/Backend/TaggableBackendInterface.php';
-               require_once __DIR__ . '/../Cache/Backend/AbstractBackend.php';
                require_once __DIR__ . '/../Cache/Backend/PhpCapableBackendInterface.php';
                require_once __DIR__ . '/../Cache/Backend/PhpCapableBackendInterface.php';
-               require_once __DIR__ . '/../Cache/Backend/SimpleFileBackend.php';
-               require_once __DIR__ . '/../Cache/Backend/NullBackend.php';
-               require_once __DIR__ . '/../Log/LogLevel.php';
-               require_once __DIR__ . '/../Utility/MathUtility.php';
+               require_once __DIR__ . '/../Cache/Backend/AbstractBackend.php';
+               require_once __DIR__ . '/../Cache/Backend/EarlyClassLoaderBackend.php';
                require_once __DIR__ . '/ClassLoader.php';
                require_once __DIR__ . '/ClassLoader.php';
-       }
-
-       /**
-        * Compatibility layer for early t3lib_div or t3lib_extMgm usage
-        *
-        * @return void
-        * @deprecated since 6.0, will be removed in 6.2
-        */
-       static public function setupClassAliasForLegacyBaseClasses() {
-               class_alias('TYPO3\\CMS\\Core\\Utility\\GeneralUtility', 't3lib_div');
-               class_alias('TYPO3\\CMS\\Core\\Utility\\ExtensionManagementUtility', 't3lib_extMgm');
+               require_once __DIR__ . '/ClassAliasMap.php';
        }
 
        /**
        }
 
        /**
diff --git a/typo3/sysext/core/Classes/Package.php b/typo3/sysext/core/Classes/Package.php
new file mode 100644 (file)
index 0000000..3c3bea6
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+namespace TYPO3\CMS\Core;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Package\Package as BasePackage;
+
+/**
+ * This is the core package
+ */
+class Package extends BasePackage {
+
+       /**
+        * @var boolean
+        */
+       protected $protected = TRUE;
+
+}
diff --git a/typo3/sysext/core/Classes/Package/Exception.php b/typo3/sysext/core/Classes/Package/Exception.php
new file mode 100644 (file)
index 0000000..aa33607
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+namespace TYPO3\CMS\Core\Package;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * A package exception
+ */
+class Exception extends \TYPO3\CMS\Core\Exception {
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Package/Exception/PackageStatesUnavailableException.php b/typo3/sysext/core/Classes/Package/Exception/PackageStatesUnavailableException.php
new file mode 100644 (file)
index 0000000..4888d4d
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+namespace TYPO3\CMS\Core\Package\Exception;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * A package unavailable exception
+ */
+class PackageStatesUnavailableException extends \TYPO3\CMS\Core\Package\Exception {
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Package/FailsafePackageManager.php b/typo3/sysext/core/Classes/Package/FailsafePackageManager.php
new file mode 100644 (file)
index 0000000..c52a079
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+namespace TYPO3\CMS\Core\Package;
+
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * This is an intermediate package manager that loads just
+ * the required extensions for the install in case the package
+ * states are unavailable.
+ */
+class FailsafePackageManager extends \TYPO3\CMS\Core\Package\PackageManager {
+
+       /**
+        * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
+        */
+       protected $configurationManager;
+
+       /**
+        * @var boolean TRUE if package manager is in failsafe mode
+        */
+       protected $inFailsafeMode = FALSE;
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               $this->configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager;
+               parent::__construct();
+       }
+
+       /**
+        * Loads the states of available packages from the PackageStates.php file.
+        * The result is stored in $this->packageStatesConfiguration.
+        *
+        * @return void
+        */
+       protected function loadPackageStates() {
+               try {
+                       parent::loadPackageStates();
+               } catch (\TYPO3\CMS\Core\Package\Exception\PackageStatesUnavailableException $exception) {
+                       $this->inFailsafeMode = TRUE;
+                       $this->packageStatesConfiguration = array();
+                       $this->scanAvailablePackages();
+               }
+       }
+
+       /**
+        * Requires and registers all packages which were defined in packageStatesConfiguration
+        *
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
+        */
+       protected function registerPackagesFromConfiguration() {
+               $this->packageStatesConfiguration['packages']['install']['state'] = 'active';
+               parent::registerPackagesFromConfiguration();
+       }
+
+       /**
+        * Sort and save states
+        *
+        * @return void
+        */
+       protected function sortAndSavePackageStates() {
+               // Do not save if in rescue mode
+               if (!$this->inFailsafeMode) {
+                       parent::sortAndSavePackageStates();
+               }
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Package/Package.php b/typo3/sysext/core/Classes/Package/Package.php
new file mode 100644 (file)
index 0000000..c267eb7
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+namespace TYPO3\CMS\Core\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A Package
+ * Adapted from FLOW for TYPO3 CMS
+ *
+ * @api
+ */
+class Package extends \TYPO3\Flow\Package\Package implements PackageInterface {
+
+       const PATTERN_MATCH_EXTENSIONKEY = '/^[0-9a-z_]+$/i';
+
+       /**
+        * @var array
+        */
+       protected $extensionManagerConfiguration = array();
+
+       /**
+        * @var array
+        */
+       protected $classAliases;
+
+       /**
+        * @var bool
+        */
+       protected $objectManagementEnabled = NULL;
+
+       /**
+        * @var array
+        */
+       protected $ignoredClassNames = array();
+
+       /**
+        * Constructor
+        *
+        * @param \TYPO3\Flow\Package\PackageManager $packageManager the package manager which knows this package
+        * @param string $packageKey Key of this package
+        * @param string $packagePath Absolute path to the location of the package's composer manifest
+        * @param string $classesPath Path the classes of the package are in, relative to $packagePath. Optional, read from Composer manifest if not set.
+        * @param string $manifestPath Path the composer manifest of the package, relative to $packagePath. Optional, defaults to ''.
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageKeyException if an invalid package key was passed
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackagePathException if an invalid package path was passed
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageManifestException if no composer manifest file could be found
+        */
+       public function __construct(\TYPO3\Flow\Package\PackageManager $packageManager, $packageKey, $packagePath, $classesPath = NULL, $manifestPath = '') {
+               if (preg_match(self::PATTERN_MATCH_EXTENSIONKEY, $packageKey) !== 1 && preg_match(self::PATTERN_MATCH_PACKAGEKEY, $packageKey) !== 1) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageKeyException('"' . $packageKey . '" is not a valid package key.', 1217959510);
+               }
+               if (!(@is_dir($packagePath) || (\TYPO3\Flow\Utility\Files::is_link($packagePath) && is_dir(\TYPO3\Flow\Utility\Files::getNormalizedPath($packagePath))))) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('Tried to instantiate a package object for package "%s" with a non-existing package path "%s". Either the package does not exist anymore, or the code creating this object contains an error.', $packageKey, $packagePath), 1166631889);
+               }
+               if (substr($packagePath, -1, 1) !== '/') {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package path "%s" provided for package "%s" has no trailing forward slash.', $packagePath, $packageKey), 1166633720);
+               }
+               if (substr($classesPath, 1, 1) === '/') {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package classes path provided for package "%s" has a leading forward slash.', $packageKey), 1334841320);
+               }
+               if (!@file_exists($packagePath . $manifestPath . 'ext_emconf.php')) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageManifestException(sprintf('No ext_emconf file found for package "%s". Please create one at "%sext_emconf.php".', $packageKey, $manifestPath), 1360403545);
+               }
+               $this->packageManager = $packageManager;
+               $this->manifestPath = $manifestPath;
+               $this->packageKey = $packageKey;
+               $this->packagePath = \TYPO3\Flow\Utility\Files::getNormalizedPath($packagePath);
+               $this->classesPath = \TYPO3\Flow\Utility\Files::getNormalizedPath(\TYPO3\Flow\Utility\Files::concatenatePaths(array($this->packagePath, self::DIRECTORY_CLASSES)));
+               try {
+                       $this->getComposerManifest();
+               } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) {
+                       $this->getExtensionEmconf($packageKey, $this->packagePath);
+               }
+               if ($this->objectManagementEnabled === NULL) {
+                       $this->objectManagementEnabled = FALSE;
+               }
+       }
+
+       /**
+        * @return bool
+        */
+       protected function getExtensionEmconf() {
+               $_EXTKEY = $this->packageKey;
+               $path = $this->packagePath . '/ext_emconf.php';
+               $EM_CONF = NULL;
+               if (@file_exists($path)) {
+                       include $path;
+                       if (is_array($EM_CONF[$_EXTKEY])) {
+                               $this->extensionManagerConfiguration = $EM_CONF[$_EXTKEY];
+                               $this->mapExtensionManagerConfigurationToComposerManifest();
+                       }
+               }
+               return FALSE;
+       }
+
+       /**
+        *
+        */
+       protected function mapExtensionManagerConfigurationToComposerManifest() {
+               if (is_array($this->extensionManagerConfiguration)) {
+                       $extensionManagerConfiguration = $this->extensionManagerConfiguration;
+                       $composerManifest = $this->composerManifest = new \stdClass();
+                       $composerManifest->name = $this->getPackageKey();
+                       $composerManifest->type = 'typo3cms-extension';
+                       $composerManifest->description = $extensionManagerConfiguration['title'];
+                       $composerManifest->version = $extensionManagerConfiguration['version'];
+                       if (isset($extensionManagerConfiguration['constraints']['depends']) && is_array($extensionManagerConfiguration['constraints']['depends'])) {
+                               $composerManifest->require = new \stdClass();
+                               foreach ($extensionManagerConfiguration['constraints']['depends'] as $requiredPackageKey => $requiredPackageVersion) {
+                                       if (!empty($requiredPackageKey)) {
+                                               $composerManifest->require->$requiredPackageKey = $requiredPackageVersion;
+                                       } else {
+                                               // TODO: throw meaningful exception or fail silently?
+                                       }
+                               }
+                       }
+                       if (isset($extensionManagerConfiguration['constraints']['conflicts']) && is_array($extensionManagerConfiguration['constraints']['conflicts'])) {
+                               $composerManifest->conflict = new \stdClass();
+                               foreach ($extensionManagerConfiguration['constraints']['conflicts'] as $conflictingPackageKey => $conflictingPackageVersion) {
+                                       $composerManifest->conflict->$conflictingPackageKey = $conflictingPackageVersion;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @return array
+        */
+       public function getPackageReplacementKeys() {
+               return $this->getComposerManifest('replace') ?: array();
+       }
+
+       /**
+        * Returns the PHP namespace of classes in this package.
+        *
+        * @return string
+        * @api
+        */
+       public function getNamespace() {
+               if(!$this->namespace) {
+                       $manifest = $this->getComposerManifest();
+                       if (isset($manifest->autoload->{'psr-0'})) {
+                               $namespaces = $manifest->autoload->{'psr-0'};
+                               if (count($namespaces) === 1) {
+                                       $this->namespace = key($namespaces);
+                               } else {
+                                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageStateException(sprintf('The Composer manifest of package "%s" contains multiple namespace definitions in its autoload section but Flow does only support one namespace per package.', $this->packageKey), 1348053245);
+                               }
+                       } else {
+                               $packageKey = $this->getPackageKey();
+                               if (strpos($packageKey, '.') === FALSE) {
+                                       // Old school with unknown vendor name
+                                       $this->namespace =  '*\\' . \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($packageKey);
+                               } else {
+                                       $this->namespace = str_replace('.', '\\', $packageKey);
+                               }
+                       }
+               }
+               return $this->namespace;
+       }
+
+       /**
+        * @return array
+        */
+       public function getClassFiles() {
+               if (!is_array($this->classFiles)) {
+                       $this->classFiles = $this->filterClassFiles($this->buildArrayOfClassFiles($this->classesPath . '/', $this->namespace . '\\'));
+               }
+               return $this->classFiles;
+       }
+
+       /**
+        * @param array $classFiles
+        * @return array
+        */
+       protected function filterClassFiles(array $classFiles) {
+               $classesNotMatchingClassRule = array_filter(array_keys($classFiles), function($className) {
+                       return preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\\\x7f-\xff]*$/', $className) !== 1;
+               });
+               foreach ($classesNotMatchingClassRule as $forbiddenClassName) {
+                       unset($classFiles[$forbiddenClassName]);
+               }
+               foreach ($this->ignoredClassNames as $ignoredClassName) {
+                       if (isset($classFiles[$ignoredClassName])) {
+                               unset($classFiles[$ignoredClassName]);
+                       }
+               }
+               return $classFiles;
+       }
+
+       /**
+        * @return array
+        */
+       public function getClassFilesFromAutoloadRegistry() {
+               $autoloadRegistryPath = $this->packagePath . 'ext_autoload.php';
+               if (@file_exists($autoloadRegistryPath)) {
+                       return require $autoloadRegistryPath;
+               }
+               return array();
+       }
+
+       /**
+        *
+        */
+       public function getClassAliases() {
+               if (!is_array($this->classAliases)) {
+                       try {
+                               $extensionClassAliasMapPathAndFilename = \TYPO3\Flow\Utility\Files::concatenatePaths(array(
+                                       $this->getPackagePath(),
+                                       'Migrations/Code/ClassAliasMap.php'
+                               ));
+                               if (@file_exists($extensionClassAliasMapPathAndFilename)) {
+                                       $this->classAliases = require $extensionClassAliasMapPathAndFilename;
+                               }
+                       } catch (\BadFunctionCallException $e) {
+                       }
+                       if (!is_array($this->classAliases)) {
+                               $this->classAliases = array();
+                       }
+               }
+               return $this->classAliases;
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Package/PackageFactory.php b/typo3/sysext/core/Classes/Package/PackageFactory.php
new file mode 100644 (file)
index 0000000..4180ca2
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+namespace TYPO3\CMS\Core\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Utility\Files;
+
+/**
+ * Class for building Packages
+ * Adapted from FLOW for TYPO3 CMS
+ */
+class PackageFactory extends \TYPO3\Flow\Package\PackageFactory {
+
+       /**
+        * @var PackageManager
+        */
+       protected $packageManager;
+
+       /**
+        * Constructor
+        *
+        * @param \TYPO3\Flow\Package\PackageManager $packageManager
+        */
+       public function __construct(PackageManager $packageManager) {
+               $this->packageManager = $packageManager;
+       }
+
+       /**
+        * Returns a package instance.
+        *
+        * @param string $packagesBasePath the base install path of packages,
+        * @param string $packagePath path to package, relative to base path
+        * @param string $packageKey key / name of the package
+        * @param string $classesPath path to the classes directory, relative to the package path
+        * @param string $manifestPath path to the package's Composer manifest, relative to package path, defaults to same path
+        * @return \TYPO3\Flow\Package\PackageInterface
+        * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
+        */
+       public function create($packagesBasePath, $packagePath, $packageKey, $classesPath, $manifestPath = '') {
+               $packagePath = Files::getNormalizedPath(Files::concatenatePaths(array($packagesBasePath, $packagePath)));
+               $packageClassPathAndFilename = Files::concatenatePaths(array($packagePath, 'Classes/' . str_replace('.', '/', $packageKey) . '/Package.php'));
+               $alternativeClassPathAndFilename = Files::concatenatePaths(array($packagePath, 'Classes/Package.php'));
+               $packageClassPathAndFilename = @file_exists($alternativeClassPathAndFilename) ? $alternativeClassPathAndFilename : $packageClassPathAndFilename;
+               if (@file_exists($packageClassPathAndFilename)) {
+                       require_once($packageClassPathAndFilename);
+                       if (substr($packagePath, 0, strlen(PATH_typo3)) === PATH_typo3 && strpos($packageKey, '.') === FALSE) {
+                               //TODO Remove this exception once the systextension are renamed to proper Flow naming scheme packages
+                               $packageClassName = 'TYPO3\\CMS\\' . \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($packageKey) . '\Package';
+                       } else {
+                               $packageClassName = str_replace('.', '\\', $packageKey) . '\Package';
+                       }
+                       if (!class_exists($packageClassName, FALSE)) {
+                               throw new \TYPO3\Flow\Package\Exception\CorruptPackageException(sprintf('The package "%s" does not contain a valid package class. Check if the file "%s" really contains a class called "%s".', $packageKey, $packageClassPathAndFilename, $packageClassName), 1327587091);
+                       }
+               } else {
+                       $emConfPath = Files::concatenatePaths(array($packagePath, 'ext_emconf.php'));
+                       $packageClassName = @file_exists($emConfPath) ? 'TYPO3\CMS\Core\Package\Package' : 'TYPO3\Flow\Package\Package';
+               }
+
+               /** @var $package \TYPO3\Flow\Package\PackageInterface */
+               $package = new $packageClassName($this->packageManager, $packageKey, $packagePath, $classesPath, $manifestPath);
+
+               return $package;
+       }
+       /**
+        * Resolves package key from Composer manifest
+        *
+        * If it is a Flow package the name of the containing directory will be used.
+        *
+        * Else if the composer name of the package matches the first part of the lowercased namespace of the package, the mixed
+        * case version of the composer name / namespace will be used, with backslashes replaced by dots.
+        *
+        * Else the composer name will be used with the slash replaced by a dot
+        *
+        * @param object $manifest
+        * @param string $packagesBasePath
+        * @return string
+        */
+       public static function getPackageKeyFromManifest($manifest, $packagePath, $packagesBasePath) {
+               if (!is_object($manifest)) {
+                       throw new  \TYPO3\Flow\Package\Exception\InvalidPackageManifestException('Invalid composer manifest.', 1348146450);
+               }
+               if (isset($manifest->type) && substr($manifest->type, 0, 9) === 'typo3cms-') {
+                       $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
+                       $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') + 1, -1);
+                       /**
+                        * @todo check that manifest name and directory follows convention
+                        */
+                       $packageKey = preg_replace('/[^A-Za-z0-9._]/', '', $packageKey);
+                       return $packageKey;
+               } else {
+                       return parent::getPackageKeyFromManifest($manifest, $packagePath, $packagesBasePath);
+               }
+       }
+
+}
diff --git a/typo3/sysext/core/Classes/Package/PackageInterface.php b/typo3/sysext/core/Classes/Package/PackageInterface.php
new file mode 100644 (file)
index 0000000..b9fadd7
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+namespace TYPO3\CMS\Core\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Interface for a Flow Package class
+ *
+ * @api
+ */
+interface PackageInterface {
+
+       /**
+        * @return array
+        */
+       public function getPackageReplacementKeys();
+
+}
diff --git a/typo3/sysext/core/Classes/Package/PackageManager.php b/typo3/sysext/core/Classes/Package/PackageManager.php
new file mode 100644 (file)
index 0000000..3c76897
--- /dev/null
@@ -0,0 +1,545 @@
+<?php
+namespace TYPO3\CMS\Core\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Annotations as Flow;
+
+/**
+ * The default TYPO3 Package Manager
+ * Adapted from FLOW for TYPO3 CMS
+ *
+ * @api
+ * @Flow\Scope("singleton")
+ */
+class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO3\CMS\Core\SingletonInterface {
+
+
+       /**
+        * @var \TYPO3\CMS\Core\Core\ClassLoader
+        */
+       protected $classLoader;
+
+       /**
+        * @var \TYPO3\CMS\Core\Core\Bootstrap
+        */
+       protected $bootstrap;
+
+       /**
+        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
+        */
+       protected $coreCache;
+
+       /**
+        * @var string
+        */
+       protected $cacheIdentifier;
+
+       /**
+        * @var array
+        */
+       protected $extAutoloadClassFiles;
+
+       /**
+        * @var array
+        */
+       protected $packagesBasePaths = array();
+
+       /**
+        * @var array
+        */
+       protected $packageAliasMap = array();
+
+       /**
+        *
+        */
+       public function __construct() {
+               $this->packagesBasePaths = array(
+                       'local'     => PATH_typo3conf . 'ext',
+                       'global'    => PATH_typo3 . 'ext',
+                       'sysext'    => PATH_typo3 . 'sysext',
+                       'composer'  => PATH_site . 'Packages',
+               );
+       }
+
+       /**
+        * @param \TYPO3\CMS\Core\Core\ClassLoader $classLoader
+        */
+       public function injectClassLoader(\TYPO3\CMS\Core\Core\ClassLoader $classLoader) {
+               $this->classLoader = $classLoader;
+       }
+
+       /**
+        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
+        */
+       public function injectCoreCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache) {
+               $this->coreCache = $coreCache;
+       }
+
+       /**
+        * Initializes the package manager
+        *
+        * @param \TYPO3\CMS\Core\Core\Bootstrap $bootstrap The current bootstrap; Flow Bootstrap is here by intention to keep the PackageManager valid to the interface
+        * @param string $packagesBasePath Absolute path of the Packages directory
+        * @param string $packageStatesPathAndFilename
+        * @return void
+        */
+       public function initialize(\TYPO3\Flow\Core\Bootstrap $bootstrap, $packagesBasePath = PATH_site, $packageStatesPathAndFilename = '') {
+
+               $this->bootstrap = $bootstrap;
+               $this->packagesBasePath = $packagesBasePath;
+               $this->packageStatesPathAndFilename = ($packageStatesPathAndFilename === '') ? PATH_typo3conf . 'PackageStates.php' : $packageStatesPathAndFilename;
+               $this->packageFactory = new PackageFactory($this);
+
+               $this->loadPackageStates();
+
+               $requiredList = array();
+               foreach ($this->packages as $packageKey => $package) {
+                       $protected = $package->isProtected();
+                       if ($protected) {
+                               $requiredList[$packageKey] = $package;
+                       }
+                       if (isset($this->packageStatesConfiguration['packages'][$packageKey]['state']) && $this->packageStatesConfiguration['packages'][$packageKey]['state'] === 'active') {
+                               $this->activePackages[$packageKey] = $package;
+                       }
+               }
+               $previousActivePackage = $this->activePackages;
+               $this->activePackages = array_merge($requiredList, $this->activePackages);
+
+               if ($this->activePackages != $previousActivePackage) {
+                       foreach ($requiredList as $requiredPackageKey => $package) {
+                               $this->packageStatesConfiguration['packages'][$requiredPackageKey]['state'] = 'active';
+                       }
+                       $this->sortAndSavePackageStates();
+               }
+
+               //@deprecated since 6.2, don't use
+               if (!defined('REQUIRED_EXTENSIONS')) {
+                       // List of extensions required to run the core
+                       define('REQUIRED_EXTENSIONS', implode(',', array_keys($requiredList)));
+               }
+
+               $cacheIdentifier = $this->getCacheIdentifier();
+               if ($cacheIdentifier === NULL) {
+                       // Create an artificial cache identifier if the package states file is not available yet
+                       // in order that the class loader and class alias map can cache anyways.
+                       $cacheIdentifier = substr(md5(implode('###', array_keys($this->activePackages))), 0, 13);
+               }
+               $this->classLoader->setCacheIdentifier($cacheIdentifier)->setPackages($this->activePackages);
+
+               foreach ($this->activePackages as $package) {
+                       $package->boot($bootstrap);
+               }
+
+               $this->saveToPackageCache();
+       }
+
+       /**
+        * @return string
+        */
+       protected function getCacheIdentifier() {
+               if ($this->cacheIdentifier === NULL) {
+                       if (@file_exists($this->packageStatesPathAndFilename)) {
+                               $this->cacheIdentifier = substr(md5_file($this->packageStatesPathAndFilename), 0, 13);
+                       } else {
+                               $this->cacheIdentifier = NULL;
+                       }
+               }
+               return $this->cacheIdentifier;
+       }
+
+       /**
+        * @return string
+        */
+       protected function getCacheEntryIdentifier() {
+               $cacheIdentifier = $this->getCacheIdentifier();
+               return $cacheIdentifier !== NULL ? 'PackageManager_' . $cacheIdentifier : NULL;
+       }
+
+       /**
+        *
+        */
+       protected function saveToPackageCache() {
+               $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
+               if ($cacheEntryIdentifier !== NULL && !$this->coreCache->has($cacheEntryIdentifier)) {
+                       $cacheEntryPath = $this->coreCache->getBackend()->getCacheDirectory();
+                       // Package objects get their own cache entry, so PHP does not have to parse the serialized string
+                       $packageObjectsCacheEntryIdentifier = uniqid('PackageObjects_');
+                       // Build cache file
+                       $packageCache = array(
+                               'packageStatesConfiguration'  => $this->packageStatesConfiguration,
+                               'packageAliasMap' => $this->packageAliasMap,
+                               'packageKeys' => $this->packageKeys,
+                               'declaringPackageClassPathsAndFilenames' => array(),
+                               'packageObjectsCacheEntryIdentifier' => $packageObjectsCacheEntryIdentifier
+                       );
+                       foreach ($this->packages as $package) {
+                               if (!isset($packageCache['declaringPackageClassPathsAndFilenames'][$packageClassName = get_class($package)])) {
+                                       $reflectionPackageClass = new \ReflectionClass($packageClassName);
+                                       $packageCache['declaringPackageClassPathsAndFilenames'][$packageClassName] = $reflectionPackageClass->getFileName();
+                               }
+                       }
+                       $this->coreCache->set($packageObjectsCacheEntryIdentifier, serialize($this->packages));
+                       $this->coreCache->set(
+                               $cacheEntryIdentifier,
+                               'return __DIR__ !== \'' . $cacheEntryPath . '\' ? FALSE : ' . PHP_EOL .
+                                       var_export($packageCache, TRUE) . ';'
+                       );
+               }
+       }
+
+       /**
+        * Loads the states of available packages from the PackageStates.php file.
+        * The result is stored in $this->packageStatesConfiguration.
+        *
+        * @return void
+        */
+       protected function loadPackageStates() {
+               $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
+               if ($cacheEntryIdentifier !== NULL && $this->coreCache->has($cacheEntryIdentifier) && $packageCache = $this->coreCache->requireOnce($cacheEntryIdentifier)) {
+                       foreach ($packageCache['declaringPackageClassPathsAndFilenames'] as $packageClassPathAndFilename) {
+                               require_once $packageClassPathAndFilename;
+                       }
+                       $this->packageStatesConfiguration = $packageCache['packageStatesConfiguration'];
+                       $this->packageAliasMap = $packageCache['packageAliasMap'];
+                       $this->packageKeys = $packageCache['packageKeys'];
+                       $GLOBALS['TYPO3_currentPackageManager'] = $this;
+                       // Strip off PHP Tags from Php Cache Frontend
+                       $packageObjects = substr(substr($this->coreCache->get($packageCache['packageObjectsCacheEntryIdentifier']), 6), 0, -2);
+                       $this->packages = unserialize($packageObjects);
+                       unset($GLOBALS['TYPO3_currentPackageManager']);
+               } else {
+                       $this->packageStatesConfiguration = @include($this->packageStatesPathAndFilename) ?: array();
+                       if (!isset($this->packageStatesConfiguration['version']) || $this->packageStatesConfiguration['version'] < 4) {
+                               $this->packageStatesConfiguration = array();
+                       }
+                       if ($this->packageStatesConfiguration !== array()) {
+                               $this->registerPackagesFromConfiguration();
+                       } else {
+                               throw new Exception\PackageStatesUnavailableException('The PackageStates.php file is either corrupt or unavailable.', 1381507733);
+                       }
+               }
+       }
+
+
+       /**
+        * Scans all directories in the packages directories for available packages.
+        * For each package a Package object is created and stored in $this->packages.
+        *
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\DuplicatePackageException
+        */
+       protected function scanAvailablePackages() {
+               $previousPackageStatesConfiguration = $this->packageStatesConfiguration;
+
+               if (isset($this->packageStatesConfiguration['packages'])) {
+                       foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $configuration) {
+                               if (!@file_exists($this->packagesBasePath . $configuration['packagePath'])) {
+                                       unset($this->packageStatesConfiguration['packages'][$packageKey]);
+                               }
+                       }
+               } else {
+                       $this->packageStatesConfiguration['packages'] = array();
+               }
+
+               foreach ($this->packagesBasePaths as $packagesBasePath) {
+                       if (!is_dir($packagesBasePath)) {
+                               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($packagesBasePath);
+                       }
+               }
+
+               $packagePaths = $this->scanLegacyExtensions();
+               foreach ($this->packagesBasePaths as $packagesBasePath) {
+                       $this->scanPackagesInPath($packagesBasePath, $packagePaths);
+               }
+
+               foreach ($packagePaths as $packagePath => $composerManifestPath) {
+                       $packagesBasePath = PATH_site;
+                       foreach ($this->packagesBasePaths as $basePath) {
+                               if (strpos($packagePath, $basePath) === 0) {
+                                       $packagesBasePath = $basePath;
+                                       break;
+                               }
+                       }
+                       try {
+                               $composerManifest = self::getComposerManifest($composerManifestPath);
+                               $packageKey = \TYPO3\CMS\Core\Package\PackageFactory::getPackageKeyFromManifest($composerManifest, $packagePath, $packagesBasePath);
+                               $this->composerNameToPackageKeyMap[strtolower($composerManifest->name)] = $packageKey;
+                               $this->packageStatesConfiguration['packages'][$packageKey]['manifestPath'] = substr($composerManifestPath, strlen($packagePath)) ? : '';
+                               $this->packageStatesConfiguration['packages'][$packageKey]['composerName'] = $composerManifest->name;
+                       }
+                       catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) {
+                               $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
+                               $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') + 1, -1);
+                       }
+                       if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['state'])) {
+                               $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'inactive';
+                       }
+
+                       $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $packagePath);
+
+                       // Change this to read the target from Composer or any other source
+                       $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = \TYPO3\Flow\Package\Package::DIRECTORY_CLASSES;
+               }
+
+               $this->registerPackagesFromConfiguration();
+               if ($this->packageStatesConfiguration != $previousPackageStatesConfiguration) {
+                       $this->sortAndsavePackageStates();
+               }
+       }
+
+       /**
+        * @return array
+        */
+       protected function scanLegacyExtensions(&$collectedExtensionPaths = array()) {
+               $legacyCmsPackageBasePathTypes = array('sysext', 'global', 'local');
+               foreach ($this->packagesBasePaths as $type => $packageBasePath) {
+                       if (!in_array($type, $legacyCmsPackageBasePathTypes)) {
+                               continue;
+                       }
+                       /** @var $fileInfo \SplFileInfo */
+                       foreach (new \DirectoryIterator($packageBasePath) as $fileInfo) {
+                               if (!$fileInfo->isDir()) {
+                                       continue;
+                               }
+                               $filename = $fileInfo->getFilename();
+                               if ($filename[0] !== '.') {
+                                       $currentPath = \TYPO3\Flow\Utility\Files::getUnixStylePath($fileInfo->getPathName()) . '/';
+                                       $collectedExtensionPaths[$currentPath] = $currentPath;
+                               }
+                       }
+               }
+               return $collectedExtensionPaths;
+       }
+
+       /**
+        * Looks for composer.json in the given path and returns a path or NULL.
+        *
+        * @param string $packagePath
+        * @return array
+        */
+       protected function findComposerManifestPaths($packagePath) {
+               // If an ext_emconf.php file is found, we don't need to look deeper
+               if (file_exists($packagePath . '/ext_emconf.php')) {
+                       return array();
+               }
+               return parent::findComposerManifestPaths($packagePath);
+       }
+
+       /**
+        * Requires and registers all packages which were defined in packageStatesConfiguration
+        *
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
+        */
+       protected function registerPackagesFromConfiguration() {
+               foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $stateConfiguration) {
+
+                       $packagePath = isset($stateConfiguration['packagePath']) ? $stateConfiguration['packagePath'] : NULL;
+                       $classesPath = isset($stateConfiguration['classesPath']) ? $stateConfiguration['classesPath'] : NULL;
+                       $manifestPath = isset($stateConfiguration['manifestPath']) ? $stateConfiguration['manifestPath'] : NULL;
+
+                       try {
+                               $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, $packageKey, $classesPath, $manifestPath);
+                       } catch (\TYPO3\Flow\Package\Exception\InvalidPackagePathException $exception) {
+                               $this->unregisterPackageByPackageKey($packageKey);
+                               continue;
+                       }
+
+                       $this->registerPackage($package, FALSE);
+
+                       if (!$this->packages[$packageKey] instanceof \TYPO3\Flow\Package\PackageInterface) {
+                               throw new \TYPO3\Flow\Package\Exception\CorruptPackageException(sprintf('The package class in package "%s" does not implement PackageInterface.', $packageKey), 1300782487);
+                       }
+
+                       $this->packageKeys[strtolower($packageKey)] = $packageKey;
+                       if ($stateConfiguration['state'] === 'active') {
+                               $this->activePackages[$packageKey] = $this->packages[$packageKey];
+                       }
+               }
+       }
+
+       /**
+        * Register a native Flow package
+        *
+        * @param string $packageKey The Package to be registered
+        * @param boolean $sortAndSave allows for not saving packagestates when used in loops etc.
+        * @return \TYPO3\Flow\Package\PackageInterface
+        * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
+        */
+       public function registerPackage(\TYPO3\Flow\Package\PackageInterface $package, $sortAndSave = TRUE) {
+               $package = parent::registerPackage($package, $sortAndSave);
+               if ($package instanceof PackageInterface) {
+                       foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
+                               $this->packageAliasMap[strtolower($packageToReplace)] = $package->getPackageKey();
+                       }
+               }
+               return $package;
+       }
+
+       /**
+        * Unregisters a package from the list of available packages
+        *
+        * @param string $packageKey Package Key of the package to be unregistered
+        * @return void
+        */
+       protected function unregisterPackageByPackageKey($packageKey) {
+               try {
+                       $package = $this->getPackage($packageKey);
+                       if ($package instanceof PackageInterface) {
+                               foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
+                                       unset($this->packageAliasMap[strtolower($packageToReplace)]);
+                               }
+                               $packageKey = $package->getPackageKey();
+                       }
+               } catch (\TYPO3\Flow\Package\Exception\UnknownPackageException $e) {
+               }
+               parent::unregisterPackageByPackageKey($packageKey);
+       }
+
+       /**
+        * Resolves a Flow package key from a composer package name.
+        *
+        * @param string $composerName
+        * @return string
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageStateException
+        */
+       public function getPackageKeyFromComposerName($composerName) {
+               if (isset($this->packageAliasMap[$composerName])) {
+                       return $this->packageAliasMap[$composerName];
+               }
+               return parent::getPackageKeyFromComposerName($composerName);
+       }
+
+       /**
+        * @return array
+        */
+       public function getExtAutoloadRegistry() {
+               if (!isset($this->extAutoloadClassFiles)) {
+                       $classRegistry = array();
+                       foreach ($this->activePackages as $packageKey => $packageData) {
+                               try {
+                                       $extensionAutoloadFile = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($packageKey, 'ext_autoload.php');
+                                       if (@file_exists($extensionAutoloadFile)) {
+                                               $classRegistry = array_merge($classRegistry, require $extensionAutoloadFile);
+                                       }
+                               } catch (\BadFunctionCallException $e) {
+                               }
+                       }
+                       $this->extAutoloadClassFiles = $classRegistry;
+               }
+               return $this->extAutoloadClassFiles;
+       }
+
+       /**
+        * Returns a PackageInterface object for the specified package.
+        * A package is available, if the package directory contains valid MetaData information.
+        *
+        * @param string $packageKey
+        * @return \TYPO3\Flow\Package\PackageInterface The requested package object
+        * @throws \TYPO3\Flow\Package\Exception\UnknownPackageException if the specified package is not known
+        * @api
+        */
+       public function getPackage($packageKey) {
+               if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
+                       $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
+               }
+               return parent::getPackage($packageKey);
+       }
+
+       /**
+        * Returns TRUE if a package is available (the package's files exist in the packages directory)
+        * or FALSE if it's not. If a package is available it doesn't mean necessarily that it's active!
+        *
+        * @param string $packageKey The key of the package to check
+        * @return boolean TRUE if the package is available, otherwise FALSE
+        * @api
+        */
+       public function isPackageAvailable($packageKey) {
+               if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
+                       $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
+               }
+               return parent::isPackageAvailable($packageKey);
+       }
+
+       /**
+        * Returns TRUE if a package is activated or FALSE if it's not.
+        *
+        * @param string $packageKey The key of the package to check
+        * @return boolean TRUE if package is active, otherwise FALSE
+        * @api
+        */
+       public function isPackageActive($packageKey) {
+               if (isset($this->packageAliasMap[$lowercasedPackageKey = strtolower($packageKey)])) {
+                       $packageKey = $this->packageAliasMap[$lowercasedPackageKey];
+               }
+               return parent::isPackageActive($packageKey);
+       }
+
+       /**
+        * @param string $packageKey
+        */
+       public function deactivatePackage($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::deactivatePackage($package->getPackageKey());
+       }
+
+       /**
+        * @param string $packageKey
+        */
+       public function activatePackage($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::activatePackage($package->getPackageKey());
+       }
+
+
+       /**
+        * @param string $packageKey
+        */
+       public function deletePackage($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::deletePackage($package->getPackageKey());
+       }
+
+
+       /**
+        * @param string $packageKey
+        */
+       public function freezePackage($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::freezePackage($package->getPackageKey());
+       }
+
+       /**
+        * @param string $packageKey
+        */
+       public function isPackageFrozen($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::isPackageFrozen($package->getPackageKey());
+       }
+
+       /**
+        * @param string $packageKey
+        */
+       public function unfreezePackage($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::unfreezePackage($package->getPackageKey());
+       }
+
+       /**
+        * @param string $packageKey
+        */
+       public function refreezePackage($packageKey) {
+               $package = $this->getPackage($packageKey);
+               parent::refreezePackage($package->getPackageKey());
+       }
+
+}
index 8d459f0..7d3c1ca 100644 (file)
@@ -836,8 +836,9 @@ class TemplateService {
        public function addExtensionStatics($idList, $templateID, $pid, $row) {
                $this->extensionStaticsProcessed = TRUE;
 
        public function addExtensionStatics($idList, $templateID, $pid, $row) {
                $this->extensionStaticsProcessed = TRUE;
 
+               // @TODO: Change to use new API
                foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extKey => $files) {
                foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extKey => $files) {
-                       if (is_array($files) && ($files['ext_typoscript_constants.txt'] || $files['ext_typoscript_setup.txt'])) {
+                       if ((is_array($files) || $files instanceof \ArrayAccess) && ($files['ext_typoscript_constants.txt'] || $files['ext_typoscript_setup.txt'])) {
                                $mExtKey = str_replace('_', '', $extKey);
                                $subrow = array(
                                        'constants' => $files['ext_typoscript_constants.txt'] ? GeneralUtility::getUrl($files['ext_typoscript_constants.txt']) : '',
                                $mExtKey = str_replace('_', '', $extKey);
                                $subrow = array(
                                        'constants' => $files['ext_typoscript_constants.txt'] ? GeneralUtility::getUrl($files['ext_typoscript_constants.txt']) : '',
index 5c0e8de..3be617e 100644 (file)
@@ -53,6 +53,21 @@ class ExtensionManagementUtility {
         */
        static protected $extTablesWasReadFromCacheOnce = FALSE;
 
         */
        static protected $extTablesWasReadFromCacheOnce = FALSE;
 
+       /**
+        * @var \TYPO3\CMS\Core\Package\PackageManager
+        */
+       static protected $packageManager;
+
+       /**
+        * Sets the package manager for all that backwards compatibility stuff,
+        * so it doesn't have to be fetched through the bootstap
+        *
+        * @param \TYPO3\CMS\Core\Package\PackageManager $packageManager
+        */
+       static public function setPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager) {
+               static::$packageManager = $packageManager;
+       }
+
        /**************************************
         *
         * PATHS and other evaluation
        /**************************************
         *
         * PATHS and other evaluation
@@ -67,7 +82,7 @@ class ExtensionManagementUtility {
         * @throws \BadFunctionCallException
         */
        static public function isLoaded($key, $exitOnError = FALSE) {
         * @throws \BadFunctionCallException
         */
        static public function isLoaded($key, $exitOnError = FALSE) {
-               $isLoaded = in_array($key, static::getLoadedExtensionListArray());
+               $isLoaded = static::$packageManager->isPackageActive($key);
                if ($exitOnError && !$isLoaded) {
                        throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension "' . $key . '" is not loaded!', 1270853910);
                }
                if ($exitOnError && !$isLoaded) {
                        throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension "' . $key . '" is not loaded!', 1270853910);
                }
@@ -85,27 +100,10 @@ class ExtensionManagementUtility {
         * @return string
         */
        static public function extPath($key, $script = '') {
         * @return string
         */
        static public function extPath($key, $script = '') {
-               if (isset($GLOBALS['TYPO3_LOADED_EXT'])) {
-                       if (!isset($GLOBALS['TYPO3_LOADED_EXT'][$key])) {
-                               throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension key "' . $key . '" is NOT loaded!', 1270853878);
-                       }
-                       $extensionPath = PATH_site . $GLOBALS['TYPO3_LOADED_EXT'][$key]['siteRelPath'];
-               } else {
-                       $loadedExtensions = array_flip(static::getLoadedExtensionListArray());
-                       if (!isset($loadedExtensions[$key])) {
-                               throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension key "' . $key . '" is NOT loaded!', 1294430950);
-                       }
-                       if (@is_dir((PATH_typo3conf . 'ext/' . $key . '/'))) {
-                               $extensionPath = PATH_typo3conf . 'ext/' . $key . '/';
-                       } elseif (@is_dir((PATH_typo3 . 'ext/' . $key . '/'))) {
-                               $extensionPath = PATH_typo3 . 'ext/' . $key . '/';
-                       } elseif (@is_dir((PATH_typo3 . 'sysext/' . $key . '/'))) {
-                               $extensionPath = PATH_typo3 . 'sysext/' . $key . '/';
-                       } else {
-                               throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension "' . $key . '" NOT found!', 1294430951);
-                       }
+               if (!static::$packageManager->isPackageActive($key)) {
+                       throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension key "' . $key . '" is NOT loaded!', 1365429656);
                }
                }
-               return $extensionPath . $script;
+               return static::$packageManager->getPackage($key)->getPackagePath() . $script;
        }
 
        /**
        }
 
        /**
@@ -117,10 +115,16 @@ class ExtensionManagementUtility {
         * @return string
         */
        static public function extRelPath($key) {
         * @return string
         */
        static public function extRelPath($key) {
-               if (!isset($GLOBALS['TYPO3_LOADED_EXT'][$key])) {
-                       throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension key "' . $key . '" is NOT loaded!', 1270853879);
+               if (!static::$packageManager->isPackageActive($key)) {
+                       throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension key "' . $key . '" is NOT loaded!', 1365429673);
+               }
+               $relativePathToSiteRoot = self::siteRelPath($key);
+               if (substr($relativePathToSiteRoot, 0, $typo3MainDirLength = strlen(TYPO3_mainDir)) === TYPO3_mainDir) {
+                       $relativePathToSiteRoot = substr($relativePathToSiteRoot, $typo3MainDirLength);
+               } else {
+                       $relativePathToSiteRoot = '../' . $relativePathToSiteRoot;
                }
                }
-               return $GLOBALS['TYPO3_LOADED_EXT'][$key]['typo3RelPath'];
+               return $relativePathToSiteRoot;
        }
 
        /**
        }
 
        /**
@@ -157,9 +161,9 @@ class ExtensionManagementUtility {
                // Build map of short keys referencing to real keys:
                if (!isset(self::$extensionKeyMap)) {
                        self::$extensionKeyMap = array();
                // Build map of short keys referencing to real keys:
                if (!isset(self::$extensionKeyMap)) {
                        self::$extensionKeyMap = array();
-                       foreach (array_keys($GLOBALS['TYPO3_LOADED_EXT']) as $extensionKey) {
-                               $shortKey = str_replace('_', '', $extensionKey);
-                               self::$extensionKeyMap[$shortKey] = $extensionKey;
+                       foreach (static::$packageManager->getActivePackages() as $package) {
+                               $shortKey = str_replace('_', '', $package->getPackageKey());
+                               self::$extensionKeyMap[$shortKey] = $package->getPackageKey();
                        }
                }
                // Lookup by the given short key:
                        }
                }
                // Lookup by the given short key:
@@ -193,16 +197,7 @@ class ExtensionManagementUtility {
                if (!static::isLoaded($key)) {
                        return '';
                }
                if (!static::isLoaded($key)) {
                        return '';
                }
-               $runtimeCache = $GLOBALS['typo3CacheManager']->getCache('cache_runtime');
-               $cacheIdentifier = 'extMgmExtVersion-' . $key;
-               if (!($extensionVersion = $runtimeCache->get($cacheIdentifier))) {
-                       $EM_CONF = array();
-                       $_EXTKEY = $key;
-                       include self::extPath($key) . 'ext_emconf.php';
-                       $extensionVersion = $EM_CONF[$key]['version'];
-                       $runtimeCache->set($cacheIdentifier, $extensionVersion);
-               }
-               return $extensionVersion;
+               return static::$packageManager->getPackage($key)->getPackageMetaData()->getVersion();
        }
 
        /**************************************
        }
 
        /**************************************
@@ -1382,103 +1377,6 @@ tt_content.' . $key . $prefix . ' {
         *
         ***************************************/
        /**
         *
         ***************************************/
        /**
-        * Load the extension information array. This array is set as
-        * $GLOBALS['TYPO3_LOADED_EXT'] in bootstrap. It contains basic information
-        * about every loaded extension.
-        *
-        * This is an internal method. It is only used during bootstrap and
-        * extensions should not use it!
-        *
-        * @param boolean $allowCaching If FALSE, the array will not be read / created from cache
-        * @return array Result array that will be set as $GLOBALS['TYPO3_LOADED_EXT']
-        * @access private
-        * @see createTypo3LoadedExtensionInformationArray
-        */
-       static public function loadTypo3LoadedExtensionInformation($allowCaching = TRUE) {
-               if ($allowCaching) {
-                       $cacheIdentifier = self::getTypo3LoadedExtensionInformationCacheIdentifier();
-                       /** @var $codeCache \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend */
-                       $codeCache = $GLOBALS['typo3CacheManager']->getCache('cache_core');
-                       if ($codeCache->has($cacheIdentifier)) {
-                               $typo3LoadedExtensionArray = $codeCache->requireOnce($cacheIdentifier);
-                       } else {
-                               $typo3LoadedExtensionArray = self::createTypo3LoadedExtensionInformationArray();
-                               $codeCache->set($cacheIdentifier, 'return ' . var_export($typo3LoadedExtensionArray, TRUE) . ';');
-                       }
-               } else {
-                       $typo3LoadedExtensionArray = self::createTypo3LoadedExtensionInformationArray();
-               }
-               return $typo3LoadedExtensionArray;
-       }
-
-       /**
-        * Set up array with basic information about loaded extension:
-        *
-        * array(
-        * 'extensionKey' => array(
-        * 'type' => Either S, L or G, inidicating if the extension is a system, a local or a global extension
-        * 'siteRelPath' => Relative path to the extension from document root
-        * 'typo3RelPath' => Relative path to extension from typo3/ subdirectory
-        * 'ext_localconf.php' => Absolute path to ext_localconf.php file of extension
-        * 'ext_...' => Further absolute path of extension files, see $extensionFilesToCheckFor var for details
-        * ),
-        * );
-        *
-        * @return array Result array that will be set as $GLOBALS['TYPO3_LOADED_EXT']
-        */
-       static protected function createTypo3LoadedExtensionInformationArray() {
-               $loadedExtensions = static::getLoadedExtensionListArray();
-               $loadedExtensionInformation = array();
-               $extensionFilesToCheckFor = array(
-                       'ext_localconf.php',
-                       'ext_tables.php',
-                       'ext_tables.sql',
-                       'ext_tables_static+adt.sql',
-                       'ext_typoscript_constants.txt',
-                       'ext_typoscript_setup.txt'
-               );
-               // Clear file status cache to make sure we get good results from is_dir()
-               clearstatcache();
-               foreach ($loadedExtensions as $extensionKey) {
-                       // Determine if extension is installed locally, globally or system (in this order)
-                       if (@is_dir((PATH_typo3conf . 'ext/' . $extensionKey . '/'))) {
-                               // local
-                               $loadedExtensionInformation[$extensionKey] = array(
-                                       'type' => 'L',
-                                       'siteRelPath' => 'typo3conf/ext/' . $extensionKey . '/',
-                                       'typo3RelPath' => '../typo3conf/ext/' . $extensionKey . '/'
-                               );
-                       } elseif (@is_dir((PATH_typo3 . 'ext/' . $extensionKey . '/'))) {
-                               // global
-                               $loadedExtensionInformation[$extensionKey] = array(
-                                       'type' => 'G',
-                                       'siteRelPath' => TYPO3_mainDir . 'ext/' . $extensionKey . '/',
-                                       'typo3RelPath' => 'ext/' . $extensionKey . '/'
-                               );
-                       } elseif (@is_dir((PATH_typo3 . 'sysext/' . $extensionKey . '/'))) {
-                               // system
-                               $loadedExtensionInformation[$extensionKey] = array(
-                                       'type' => 'S',
-                                       'siteRelPath' => TYPO3_mainDir . 'sysext/' . $extensionKey . '/',
-                                       'typo3RelPath' => 'sysext/' . $extensionKey . '/'
-                               );
-                       }
-                       // Register found files in extension array if extension was found
-                       if (isset($loadedExtensionInformation[$extensionKey])) {
-                               foreach ($extensionFilesToCheckFor as $fileName) {
-                                       $absolutePathToFile = PATH_site . $loadedExtensionInformation[$extensionKey]['siteRelPath'] . $fileName;
-                                       if (@is_file($absolutePathToFile)) {
-                                               $loadedExtensionInformation[$extensionKey][$fileName] = $absolutePathToFile;
-                                       }
-                               }
-                       }
-                       // Register found extension icon
-                       $loadedExtensionInformation[$extensionKey]['ext_icon'] = self::getExtensionIcon(PATH_site . $loadedExtensionInformation[$extensionKey]['siteRelPath']);
-               }
-               return $loadedExtensionInformation;
-       }
-
-       /**
         * Find extension icon
         *
         * @param string $extensionPath Path to extension directory.
         * Find extension icon
         *
         * @param string $extensionPath Path to extension directory.
@@ -1499,15 +1397,6 @@ tt_content.' . $key . $prefix . ' {
        }
 
        /**
        }
 
        /**
-        * Cache identifier of cached Typo3LoadedExtensionInformation array
-        *
-        * @return string
-        */
-       static protected function getTypo3LoadedExtensionInformationCacheIdentifier() {
-               return 'loaded_extensions_' . sha1((TYPO3_version . PATH_site . 'loadedExtensions'));
-       }
-
-       /**
         * Execute all ext_localconf.php files of loaded extensions.
         * The method implements an optionally used caching mechanism that concatenates all
         * ext_localconf.php files in one file.
         * Execute all ext_localconf.php files of loaded extensions.
         * The method implements an optionally used caching mechanism that concatenates all
         * ext_localconf.php files in one file.
@@ -1547,10 +1436,10 @@ tt_content.' . $key . $prefix . ' {
                // Nevertheless we define it here as global for backwards compatibility.
                global $TYPO3_CONF_VARS;
                foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $_EXTKEY => $extensionInformation) {
                // Nevertheless we define it here as global for backwards compatibility.
                global $TYPO3_CONF_VARS;
                foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $_EXTKEY => $extensionInformation) {
-                       if (is_array($extensionInformation) && $extensionInformation['ext_localconf.php']) {
+                       if ((is_array($extensionInformation) || $extensionInformation instanceof \ArrayAccess) && isset($extensionInformation['ext_localconf.php'])) {
                                // $_EXTKEY and $_EXTCONF are available in ext_localconf.php
                                // and are explicitly set in cached file as well
                                // $_EXTKEY and $_EXTCONF are available in ext_localconf.php
                                // and are explicitly set in cached file as well
-                               $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
+                               $_EXTCONF = isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY]) ? $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY] : NULL;
                                require $extensionInformation['ext_localconf.php'];
                        }
                }
                                require $extensionInformation['ext_localconf.php'];
                        }
                }
@@ -1733,7 +1622,7 @@ tt_content.' . $key . $prefix . ' {
                global $_EXTKEY;
                // Load each ext_tables.php file of loaded extensions
                foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $_EXTKEY => $extensionInformation) {
                global $_EXTKEY;
                // Load each ext_tables.php file of loaded extensions
                foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $_EXTKEY => $extensionInformation) {
-                       if (is_array($extensionInformation) && $extensionInformation['ext_tables.php']) {
+                       if ((is_array($extensionInformation) || $extensionInformation instanceof \ArrayAccess) && $extensionInformation['ext_tables.php']) {
                                // $_EXTKEY and $_EXTCONF are available in ext_tables.php
                                // and are explicitly set in cached file as well
                                $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
                                // $_EXTKEY and $_EXTCONF are available in ext_tables.php
                                // and are explicitly set in cached file as well
                                $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
@@ -1876,18 +1765,7 @@ tt_content.' . $key . $prefix . ' {
         * @return array Loaded extensions
         */
        static public function getLoadedExtensionListArray() {
         * @return array Loaded extensions
         */
        static public function getLoadedExtensionListArray() {
-               // Extensions in extListArray
-               if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXT']['extListArray'])) {
-                       $loadedExtensions = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extListArray'];
-               } else {
-                       // Fallback handling if extlist is still a string and not an array
-                       // @deprecated since 6.0, will be removed in 6.2 ... check upgrade process before removal!
-                       $loadedExtensions = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList']);
-               }
-               // Add required extensions
-               $loadedExtensions = array_merge(static::getRequiredExtensionListArray(), $loadedExtensions);
-               $loadedExtensions = array_unique($loadedExtensions);
-               return $loadedExtensions;
+               return array_keys(static::$packageManager->getActivePackages());
        }
 
        /**
        }
 
        /**
@@ -1920,12 +1798,10 @@ tt_content.' . $key . $prefix . ' {
         * @throws \RuntimeException
         */
        static public function loadExtension($extensionKey) {
         * @throws \RuntimeException
         */
        static public function loadExtension($extensionKey) {
-               if (static::isLoaded($extensionKey)) {
+               if (static::$packageManager->isPackageActive($extensionKey)) {
                        throw new \RuntimeException('Extension already loaded', 1342345486);
                }
                        throw new \RuntimeException('Extension already loaded', 1342345486);
                }
-               $extList = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager')->getLocalConfigurationValueByPath('EXT/extListArray');
-               $extList[] = $extensionKey;
-               static::writeNewExtensionList($extList);
+               static::$packageManager->activatePackage($extensionKey);
        }
 
        /**
        }
 
        /**
@@ -1939,29 +1815,24 @@ tt_content.' . $key . $prefix . ' {
         * @throws \RuntimeException
         */
        static public function unloadExtension($extensionKey) {
         * @throws \RuntimeException
         */
        static public function unloadExtension($extensionKey) {
-               if (!static::isLoaded($extensionKey)) {
+               if (!static::$packageManager->isPackageActive($extensionKey)) {
                        throw new \RuntimeException('Extension not loaded', 1342345487);
                }
                        throw new \RuntimeException('Extension not loaded', 1342345487);
                }
-               if (in_array($extensionKey, static::getRequiredExtensionListArray())) {
-                       throw new \RuntimeException('Can not unload required extension', 1342348167);
-               }
-               $extList = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager')->getLocalConfigurationValueByPath('EXT/extListArray');
-               $extList = array_diff($extList, array($extensionKey));
-               static::writeNewExtensionList($extList);
+               static::$packageManager->deactivatePackage($extensionKey);
        }
 
        /**
         * Writes extension list and clear cache files.
         *
        }
 
        /**
         * Writes extension list and clear cache files.
         *
-        * @TODO : This method should be protected, but with current em it is hard to do so,
+        * @TODO: This method should be protected, but with current em it is hard to do so,
+        * @TODO: Find out if we may remove this already
         * @param array Extension array to load, loader order is kept
         * @return void
         * @internal
         * @param array Extension array to load, loader order is kept
         * @return void
         * @internal
+        * @deprecated since 6.2, will be removed two versions later
         */
        static public function writeNewExtensionList(array $newExtensionList) {
         */
        static public function writeNewExtensionList(array $newExtensionList) {
-               $extensionList = array_unique($newExtensionList);
-               GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager')->setLocalConfigurationValueByPath('EXT/extListArray', $extensionList);
-               static::removeCacheFiles();
+               GeneralUtility::logDeprecatedFunction();
        }
 
        /**
        }
 
        /**
index 87ca77d..8389b1a 100644 (file)
@@ -133,6 +133,13 @@ return array(
                                        'backend' => 'TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend',
                                        'options' => array()
                                ),
                                        'backend' => 'TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend',
                                        'options' => array()
                                ),
+                               // The cache_classes cache is for the class loader/class alias map only
+                               // and must not be abused by third party extensions.
+                               'cache_classes' => array(
+                                       'frontend' => 'TYPO3\CMS\Core\Cache\Frontend\PhpFrontend',
+                                       'backend' => 'TYPO3\CMS\Core\Cache\Backend\ClassLoaderBackend',
+                                       'options' => array()
+                               ),
                                'cache_hash' => array(
                                        'frontend' => 'TYPO3\CMS\Core\Cache\Frontend\VariableFrontend',
                                        'backend' => 'TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend',
                                'cache_hash' => array(
                                        'frontend' => 'TYPO3\CMS\Core\Cache\Frontend\VariableFrontend',
                                        'backend' => 'TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend',
index 1cd8946..f70cd68 100644 (file)
@@ -436,12 +436,34 @@ class Package implements PackageInterface {
                return $classFiles;
        }
 
                return $classFiles;
        }
 
+       /**
+        * Added by TYPO3 CMS
+        *
+        * The package caching serializes package objects.
+        * The package manager instance may not be serialized
+        * as a fresh instance is created upon every request.
+        *
+        * This method will be removed once the package is
+        * released of the package manager dependency.
+        *
+        * @return array
+        */
        public function __sleep() {
                $properties = get_class_vars(__CLASS__);
                unset($properties['packageManager']);
                return array_keys($properties);
        }
 
        public function __sleep() {
                $properties = get_class_vars(__CLASS__);
                unset($properties['packageManager']);
                return array_keys($properties);
        }
 
+       /**
+        * Added by TYPO3 CMS
+        *
+        * The package caching deserializes package objects.
+        * A fresh package manager instance has to be set
+        * during bootstrapping.
+        *
+        * This method will be removed once the package is
+        * released of the package manager dependency.
+        */
        public function __wakeup() {
                if (isset($GLOBALS['TYPO3_currentPackageManager'])) {
                        $this->packageManager = $GLOBALS['TYPO3_currentPackageManager'];
        public function __wakeup() {
                if (isset($GLOBALS['TYPO3_currentPackageManager'])) {
                        $this->packageManager = $GLOBALS['TYPO3_currentPackageManager'];
index 374c1f1..020a709 100644 (file)
@@ -210,4 +210,39 @@ abstract class BaseTestCase extends \PHPUnit_Framework_TestCase {
                $reflectionMethod->setAccessible(TRUE);
                return $reflectionMethod->invokeArgs($object, $arguments);
        }
                $reflectionMethod->setAccessible(TRUE);
                return $reflectionMethod->invokeArgs($object, $arguments);
        }
+
+       /**
+        * Injects $dependency into property $name of $target
+        *
+        * This is a convenience method for setting a protected or private property in
+        * a test subject for the purpose of injecting a dependency.
+        *
+        * @param object $target The instance which needs the dependency
+        * @param string $name Name of the property to be injected
+        * @param object $dependency The dependency to inject – usually an object but can also be any other type
+        * @return void
+        * @throws \RuntimeException
+        * @throws \InvalidArgumentException
+        */
+       protected function inject($target, $name, $dependency) {
+               if (!is_object($target)) {
+                       throw new \InvalidArgumentException('Wrong type for argument $target, must be object.');
+               }
+
+               $objectReflection = new \ReflectionObject($target);
+               $methodNamePart = strtoupper($name[0]) . substr($name, 1);
+               if ($objectReflection->hasMethod('set' . $methodNamePart)) {
+                       $methodName = 'set' . $methodNamePart;
+                       $target->$methodName($dependency);
+               } elseif ($objectReflection->hasMethod('inject' . $methodNamePart)) {
+                       $methodName = 'inject' . $methodNamePart;
+                       $target->$methodName($dependency);
+               } elseif ($objectReflection->hasProperty($name)) {
+                       $property = $objectReflection->getProperty($name);
+                       $property->setAccessible(TRUE);
+                       $property->setValue($target, $dependency);
+               } else {
+                       throw new \RuntimeException('Could not inject ' . $name . ' into object of type ' . get_class($target));
+               }
+       }
 }
 }
index a1c8f76..28cc510 100644 (file)
@@ -216,7 +216,7 @@ abstract class FunctionalTestCase extends BaseTestCase {
                                } elseif (isset($column['is-NULL']) && ($column['is-NULL'] === 'yes')) {
                                        $columnValue = NULL;
                                } else {
                                } elseif (isset($column['is-NULL']) && ($column['is-NULL'] === 'yes')) {
                                        $columnValue = NULL;
                                } else {
-                                       $columnValue = $table->$columnName;
+                                       $columnValue = (string) $table->$columnName;
                                }
 
                                $insertArray[$columnName] = $columnValue;
                                }
 
                                $insertArray[$columnName] = $columnValue;
index 5c10522..2bf2093 100644 (file)
@@ -68,7 +68,8 @@ class FunctionalTestCaseBootstrapUtility {
                $this->setUpInstanceDirectories();
                $this->setUpInstanceCoreLinks();
                $this->linkTestExtensionsToInstance($testExtensionsToLoad);
                $this->setUpInstanceDirectories();
                $this->setUpInstanceCoreLinks();
                $this->linkTestExtensionsToInstance($testExtensionsToLoad);
-               $this->setUpLocalConfiguration($coreExtensionsToLoad, $testExtensionsToLoad);
+               $this->setUpLocalConfiguration();
+               $this->setUpPackageStates($coreExtensionsToLoad, $testExtensionsToLoad);
                $this->setUpBasicTypo3Bootstrap();
                $this->setUpTestDatabase();
                $this->createDatabaseStructure();
                $this->setUpBasicTypo3Bootstrap();
                $this->setUpTestDatabase();
                $this->createDatabaseStructure();
@@ -163,7 +164,7 @@ class FunctionalTestCaseBootstrapUtility {
                        ORIGINAL_ROOT . 'index.php' => $this->instancePath . '/index.php'
                );
                foreach ($linksToSet as $from => $to) {
                        ORIGINAL_ROOT . 'index.php' => $this->instancePath . '/index.php'
                );
                foreach ($linksToSet as $from => $to) {
-                       $success = symlink($from,  $to);
+                       $success = symlink($from, $to);
                        if (!$success) {
                                throw new Exception(
                                        'Creating link failed: from ' . $from . ' to: ' . $to,
                        if (!$success) {
                                throw new Exception(
                                        'Creating link failed: from ' . $from . ' to: ' . $to,
@@ -208,7 +209,7 @@ class FunctionalTestCaseBootstrapUtility {
         * @throws Exception
         * @return void
         */
         * @throws Exception
         * @return void
         */
-       protected function setUpLocalConfiguration(array $coreExtensionsToLoad, array $testExtensionPaths) {
+       protected function setUpLocalConfiguration() {
                $originalConfigurationArray = require ORIGINAL_ROOT . 'typo3conf/LocalConfiguration.php';
                // Base of final LocalConfiguration is core factory configuration
                $finalConfigurationArray = require ORIGINAL_ROOT .'typo3/sysext/core/Configuration/FactoryConfiguration.php';
                $originalConfigurationArray = require ORIGINAL_ROOT . 'typo3conf/LocalConfiguration.php';
                // Base of final LocalConfiguration is core factory configuration
                $finalConfigurationArray = require ORIGINAL_ROOT .'typo3/sysext/core/Configuration/FactoryConfiguration.php';
@@ -231,14 +232,6 @@ class FunctionalTestCaseBootstrapUtility {
 
                $finalConfigurationArray['DB']['database'] = $this->databaseName;
 
 
                $finalConfigurationArray['DB']['database'] = $this->databaseName;
 
-               // Determine list of additional extensions to load
-               $extensionNamesOfTestExtensions = array();
-               foreach ($testExtensionPaths as $path) {
-                       $extensionNamesOfTestExtensions[] = basename($path);
-               }
-               $extensionsToLoad = array_merge($coreExtensionsToLoad, $extensionNamesOfTestExtensions);
-               $finalConfigurationArray['EXT']['extListArray'] = $extensionsToLoad;
-
                $result = $this->writeFile(
                        $this->instancePath . '/typo3conf/LocalConfiguration.php',
                        '<?php' . chr(10) .
                $result = $this->writeFile(
                        $this->instancePath . '/typo3conf/LocalConfiguration.php',
                        '<?php' . chr(10) .
@@ -255,6 +248,31 @@ class FunctionalTestCaseBootstrapUtility {
        }
 
        /**
        }
 
        /**
+        * @param array $coreExtensionsToLoad Additional core extensions to load
+        * @param array $testExtensionPaths Paths to extensions relative to document root
+        * @throws Exception
+        * @TODO Figure out what the intention of the upper arguments is
+        */
+       protected function setUpPackageStates(array $coreExtensionsToLoad, array $testExtensionPaths) {
+               $packageStates = require ORIGINAL_ROOT . 'typo3conf/PackageStates.php';
+               $packageStates['packages']['phpunit']['packagePath'] = '../../' . $packageStates['packages']['phpunit']['packagePath'];
+
+               $result = $this->writeFile(
+                       $this->instancePath . '/typo3conf/PackageStates.php',
+                       '<?php' . chr(10) .
+                       'return ' .
+                       $this->arrayExport(
+                               $packageStates
+                       ) .
+                       ';' . chr(10) .
+                       '?>'
+               );
+               if (!$result) {
+                       throw new Exception('Can not write PackageStates', 1381612729);
+               }
+       }
+
+       /**
         * Bootstrap basic TYPO3
         *
         * @return void
         * Bootstrap basic TYPO3
         *
         * @return void
index 18547c8..9a0d2bd 100644 (file)
@@ -24,6 +24,8 @@ namespace TYPO3\CMS\Core\Tests\Unit\Core;
  * This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
  * This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use org\bovigo\vfs\vfsStream;
+
 /**
  * Testcase for TYPO3\CMS\Core\Core\ClassLoader
  *
 /**
  * Testcase for TYPO3\CMS\Core\Core\ClassLoader
  *
@@ -41,34 +43,55 @@ class ClassLoaderTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         */
        protected $fakedExtensions = array();
 
         */
        protected $fakedExtensions = array();
 
+
+       /**
+        * @var \TYPO3\CMS\Core\Core\ClassLoader
+        */
+       protected $classLoader;
+
+       /**
+        * @var \TYPO3\CMS\Core\Core\ClassAliasMap
+        */
+       protected $orinalClassAliasMap;
+
+       /**
+        * Test flag used in in this test case
+        *
+        * @var boolean
+        */
+       public static $testClassWasLoaded = FALSE;
+
        /**
         * Fix a race condition that GeneralUtility is not available
         * during tearDown if fiddling with the autoloader where
         * backupGlobals is not set up again yet
         */
        public function setUp() {
        /**
         * Fix a race condition that GeneralUtility is not available
         * during tearDown if fiddling with the autoloader where
         * backupGlobals is not set up again yet
         */
        public function setUp() {
-               $this->typo3CacheManager = $GLOBALS['typo3CacheManager'];
+               vfsStream::setup('Test');
+
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/composer.json', '{"name": "acme/myapp", "type": "flow-test"}');
+               $package1 = new \TYPO3\Flow\Package\Package($this->getMock('TYPO3\Flow\Package\PackageManager'), 'Acme.MyApp', 'vfs://Test/Packages/Application/Acme.MyApp/', 'Classes');
+
+               mkdir('vfs://Test/Packages/Application/Acme.MyAppAddon/Classes/', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyAppAddon/composer.json', '{"name": "acme/myappaddon", "type": "flow-test"}');
+               $package2 = new \TYPO3\Flow\Package\Package($this->getMock('TYPO3\Flow\Package\PackageManager'), 'Acme.MyAppAddon', 'vfs://Test/Packages/Application/Acme.MyAppAddon/', 'Classes');
+
+               $mockClassAliasMap = $this->getMock('TYPO3\\CMS\\Core\\Core\\ClassAliasMap', array('setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead', 'buildMappingFiles'), array(), '', FALSE);
+               $mockClassAliasMap->expects($this->any())->method('setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead')->will($this->returnValue(array()));
+
+               $this->orinalClassAliasMap = \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassAliasMap');
+               $this->classLoader = new \TYPO3\CMS\Core\Core\ClassLoader();
+               $this->classLoader->injectClassAliasMap($mockClassAliasMap);
+               $this->classLoader->setPackages(array('Acme.MyApp' => $package1, 'Acme.MyAppAddon' => $package2));
        }
 
        /**
        }
 
        /**
-        * Clean up
-        * Warning: Since phpunit itself is php and we are fiddling with php
-        * autoloader code here, the tests are a bit fragile. This tearDown
-        * method ensures that all main classes are available again during
-        * tear down of a testcase.
-        * This construct will fail if the class under test is changed and
-        * not compatible anymore. Make sure to always run the whole test
-        * suite if fiddling with the autoloader unit tests to ensure that
-        * there is no fatal error thrown in other unit test classes triggered
-        * by errors in this one.
+        * The class alias map is kept static in the class loader for legacy reasons
+        * and has to be reset after mocking.
         */
        public function tearDown() {
         */
        public function tearDown() {
-               $GLOBALS['typo3CacheManager'] = $this->typo3CacheManager;
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-               foreach ($this->fakedExtensions as $extension) {
-                       \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir(PATH_site . 'typo3temp/' . $extension, TRUE);
-               }
+               $this->classLoader->injectClassAliasMap($this->orinalClassAliasMap);
        }
 
        /**
        }
 
        /**
@@ -92,262 +115,98 @@ class ClassLoaderTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        }
 
        /**
        }
 
        /**
+        * Checks if the package autoloader loads classes from subdirectories.
+        *
         * @test
         */
         * @test
         */
-       public function unregisterAndRegisterAgainDoesNotFatal() {
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-                       // If this fatals the autoload re registering went wrong
-               \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TimeTracker\\NullTimeTracker');
-       }
+       public function classesFromSubDirectoriesAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/SubDirectory', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/SubDirectory/ClassInSubDirectory.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-       /**
-        * @test
-        */
-       public function unregisterAutoloaderSetsCacheEntryWithT3libNoTags() {
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $mockCache->expects($this->once())->method('set')->with($this->anything(), $this->anything(), array());
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('Acme\MyApp\SubDirectory\ClassInSubDirectory');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
 
        /**
         * @test
        }
 
        /**
         * @test
-        * @expectedException \RuntimeException
         */
         */
-       public function autoloadFindsClassFileDefinedInExtAutoloadFile() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-               $autoloaderFile = $extPath . 'ext_autoload.php';
-               $class = strtolower('tx_{' . $extKey . '}_' . uniqid(''));
-               $file = $extPath . uniqid('') . '.php';
-               file_put_contents($file, '<?php' . LF . 'throw new \\RuntimeException(\'\', 1310203812);' . LF . '?>');
-               file_put_contents($autoloaderFile, '<?php' . LF . 'return array(\'' . $class . '\' => \'' . $file . '\');' . LF . '?>');
-                       // Inject a dummy for the core_phpcode cache to force the autoloader
-                       // to re calculate the registry
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Re-initialize autoloader registry to force it to recognize the new extension
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-                       // Expect the exception of the file to be thrown
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($class);
-       }
+       public function classesFromDeeplyNestedSubDirectoriesAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/SubDirectory/A/B/C/D', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/SubDirectory/A/B/C/D/E.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-       /**
-        * @test
-        */
-       public function unregisterAutoloaderWritesLowerCasedClassFileToCache() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-               $autoloaderFile = $extPath . 'ext_autoload.php';
-                       // A case sensitive key (FooBar) in ext_autoload file
-               $class = 'tx_{' . $extKey . '}_' . uniqid('FooBar');
-               $file = $extPath . uniqid('') . '.php';
-               file_put_contents($autoloaderFile, '<?php' . LF . 'return array(\'' . $class . '\' => \'' . $file . '\');' . LF . '?>');
-                       // Inject a dummy for the core_phpcode cache to force the autoloader
-                       // to re calculate the registry
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Expect that the lower case version of the class name is written to cache
-               $mockCache->expects($this->at(2))->method('set')->with($this->anything(), $this->stringContains(strtolower($class), FALSE));
-                       // Re-initialize autoloader registry to force it to recognize the new extension
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('Acme\MyApp\SubDirectory\A\B\C\D\E');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
 
        /**
        }
 
        /**
+        * Checks if the package autoloader loads classes from packages that match a
+        * substring of another package (e.g. TYPO3CR vs TYPO3).
+        *
         * @test
         * @test
-        * @expectedException \RuntimeException
         */
         */
-       public function autoloadFindsClassFileIfExtAutoloadEntryIsCamelCased() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-                       // A case sensitive key (FooBar) in ext_autoload file
-               $class = 'tx_{' . $extKey . '}_' . uniqid('FooBar');
-               $file = $extPath . uniqid('') . '.php';
-               file_put_contents($file, '<?php' . LF . 'throw new \\RuntimeException(\'\', 1336756850);' . LF . '?>');
-               $extAutoloadFile = $extPath . 'ext_autoload.php';
-               file_put_contents($extAutoloadFile, '<?php' . LF . 'return array(\'' . $class . '\' => \'' . $file . '\');' . LF . '?>');
-                       // Inject cache and return false, so autoloader is forced to read ext_autoloads from extensions
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-               $mockCache->expects($this->any())->method('has')->will($this->returnValue(FALSE));
-                       // Re-initialize autoloader registry to force it to recognize the new extension
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($class);
-       }
+       public function classesFromSubMatchingPackagesAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyAppAddon/Classes/Acme/MyAppAddon', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyAppAddon/Classes/Acme/MyAppAddon/Class.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-       /**
-        * @test
-        * @expectedException \RuntimeException
-        */
-       public function autoloadFindsCamelCasedClassFileIfExtAutoloadEntryIsReadLowerCasedFromCache() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-                       // A case sensitive key (FooBar) in ext_autoload file
-               $class = 'tx_{' . $extKey . '}_' . uniqid('FooBar');
-               $file = $extPath . uniqid('') . '.php';
-               file_put_contents($file, '<?php' . LF . 'throw new \RuntimeException(\'\', 1336756850);' . LF . '?>');
-                       // Inject cache mock and let the cache entry return the lowercased class name as key
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-               $mockCache->expects($this->any())->method('has')->will($this->returnValue(TRUE));
-               $mockCache->expects($this->once())->method('requireOnce')->will($this->returnValue(array(array(strtolower($class) => $file))));
-                       // Re-initialize autoloader registry to force it to recognize the new extension
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($class);
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('Acme\MyAppAddon\Class');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
 
        /**
        }
 
        /**
+        * Checks if the package autoloader loads classes from subdirectories.
+        *
         * @test
         * @test
-        * @expectedException \RuntimeException
         */
         */
-       public function autoloadFindsClassFileThatRespectsExtbaseNamingSchemeWithoutExtAutoloadFile() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-                       // Create a class named Tx_Extension_Foo123_Bar456
-                       // to find file extension/Classes/Foo123/Bar456.php
-               $pathSegment = 'Foo' . uniqid();
-               $fileName = 'Bar' . uniqid();
-               $class = 'Tx_' . ucfirst($extKey) . '_' . $pathSegment . '_' . $fileName;
+       public function classesWithUnderscoresAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/Foo.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-               $file = $extPath . 'Classes/' . $pathSegment . '/' . $fileName . '.php';
-               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($extPath . 'Classes/' . $pathSegment);
-               file_put_contents($file, '<?php' . LF . 'throw new \\RuntimeException(\'\', 1310203813);' . LF . '?>');
-                       // Inject a dummy for the core_phpcode cache to cache
-                       // the calculated cache entry to a dummy cache
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Expect the exception of the file to be thrown
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($class);
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('Acme\MyApp_Foo');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
 
        /**
        }
 
        /**
+        * Checks if the package autoloader loads classes from subdirectories with underscores.
+        *
         * @test
         */
         * @test
         */
-       public function unregisterAutoloaderWritesClassFileThatRespectsExtbaseNamingSchemeToCacheFile() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-               $pathSegment = 'Foo' . uniqid();
-               $fileName = 'Bar' . uniqid();
-               $class = 'Tx_' . $extKey . '_' . $pathSegment . '_' . $fileName;
-               $file = $extPath . 'Classes/' . $pathSegment . '/' . $fileName . '.php';
-               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($extPath . 'Classes/' . $pathSegment);
-               file_put_contents($file, '<?php' . LF . '$foo = \'bar\';' . LF . '?>');
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Expect that an entry to the cache is written containing the newly found class
-               $mockCache->expects($this->once())->method('set')->with($this->anything(), $this->stringContains(strtolower($class), $this->anything()));
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($class);
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-       }
+       public function namespaceWithUnderscoresAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/My_Underscore', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/My_Underscore/Foo.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-       /**
-        * @test
-        */
-       public function unregisterAutoloaderWritesClassFileLocationOfClassRespectingExtbaseNamingSchemeToCacheFile() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-               $pathSegment = 'Foo' . uniqid();
-               $fileName = 'Bar' . uniqid();
-               $class = 'Tx_' . $extKey . '_' . $pathSegment . '_' . $fileName;
-               $file = $extPath . 'Classes/' . $pathSegment . '/' . $fileName . '.php';
-               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($extPath . 'Classes/' . $pathSegment);
-               file_put_contents($file, '<?php' . LF . '$foo = \'bar\';' . LF . '?>');
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Expect that an entry to the cache is written containing the newly found class
-               $mockCache->expects($this->once())->method('set')->with($this->anything(), $this->stringContains(strtolower($file), $this->anything()));
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($class);
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('Acme\MyApp\My_Underscore\Foo');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
 
        /**
        }
 
        /**
+        * Checks if the package autoloader loads classes from subdirectories.
+        *
         * @test
         * @test
-        * @expectedException \RuntimeException
         */
         */
-       public function autoloadFindsClassFileThatRespectsExtbaseNamingSchemeWithNamespace() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-                       // Create a class named \Tx\Extension\Foo123\Bar456
-                       // to find file extension/Classes/Foo123/Bar456.php
-               $pathSegment = 'Foo' . uniqid();
-               $fileName = 'Bar' . uniqid();
-               $namespacedClass = '\\Vendor\\' . ucfirst($extKey) . '\\' . $pathSegment . '\\' . $fileName;
-               $file = $extPath . 'Classes/' . $pathSegment . '/' . $fileName . '.php';
-               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($extPath . 'Classes/' . $pathSegment);
-               file_put_contents($file, '<?php' . LF . 'throw new \\RuntimeException(\'\', 1342800577);' . LF . '?>');
-                       // Re-initialize autoloader registry to force it to recognize the new extension
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-                       // Expect the exception of the file to be thrown
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($namespacedClass);
-       }
+       public function classesWithOnlyUnderscoresAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/UnderscoredOnly.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-       /**
-        * @test
-        */
-       public function unregisterAutoloaderWritesClassFileLocationOfClassRespectingExtbaseNamingSchemeWithNamespaceToCacheFile() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-               $pathSegment = 'Foo' . uniqid();
-               $fileName = 'Bar' . uniqid();
-               $namespacedClass = '\\Tx\\' . $extKey . '\\' . $pathSegment . '\\' . $fileName;
-               $file = $extPath . 'Classes/' . $pathSegment . '/' . $fileName . '.php';
-               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($extPath . 'Classes/' . $pathSegment);
-               file_put_contents($file, "<?php\n\n\$foo = 'bar';\n\n?>");
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Expect that an entry to the cache is written containing the newly found class
-               $mockCache->expects($this->once())->method('set')->with($this->anything(), $this->stringContains(strtolower($file), $this->anything()));
-               \TYPO3\CMS\Core\Core\ClassLoader::autoload($namespacedClass);
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('Acme_MyApp_UnderscoredOnly');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
 
        /**
         * @test
         */
        }
 
        /**
         * @test
         */
-       public function checkClassNamesNotExtbaseSchemePassAutoloaderUntouched() {
-               $class = '\\Symfony\\Foo\\Bar';
-               $this->assertNull(\TYPO3\CMS\Core\Core\ClassLoader::getClassPathByRegistryLookup($class));
-       }
+       public function classesWithLeadingBackslashAreLoaded() {
+               mkdir('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp', 0770, TRUE);
+               file_put_contents('vfs://Test/Packages/Application/Acme.MyApp/Classes/Acme/MyApp/WithLeadingBackslash.php', '<?php ' . __CLASS__ . '::$testClassWasLoaded = TRUE; ?>');
 
 
-       /**
-        * @test
-        */
-       public function checkAutoloaderSetsNamespacedClassnamesInExtAutoloadAreWrittenToCache() {
-               $extKey = $this->createFakeExtension();
-               $extPath = PATH_site . 'typo3temp/' . $extKey . '/';
-               $pathSegment = 'Foo' . uniqid();
-               $fileName = 'Bar' . uniqid();
-               $autoloaderFile = $extPath . 'ext_autoload.php';
-                       // A case sensitive key (FooBar) in ext_autoload file
-               $namespacedClass = '\\Tx\\' . $extKey . '\\' . $pathSegment . '\\' . $fileName;
-               $classFile = 'EXT:someExt/Classes/Foo/bar.php';
-               file_put_contents($autoloaderFile, '<?php' . LF . 'return ' . var_export(array($namespacedClass => $classFile), TRUE) . ';' . LF . '?>');
-                       // Inject a dummy for the core_phpcode cache to force the autoloader
-                       // to re calculate the registry
-               $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
-               $GLOBALS['typo3CacheManager'] = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager', array('getCache'));
-               $GLOBALS['typo3CacheManager']->expects($this->any())->method('getCache')->will($this->returnValue($mockCache));
-                       // Expect that the lower case version of the class name is written to cache
-               $mockCache->expects($this->at(2))->method('set')->with($this->anything(), $this->stringContains(strtolower(addslashes($namespacedClass)), FALSE));
-                       // Re-initialize autoloader registry to force it to recognize the new extension
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::registerAutoloader();
-               \TYPO3\CMS\Core\Core\ClassLoader::unregisterAutoloader();
+               self::$testClassWasLoaded = FALSE;
+               $this->classLoader->loadClass('\Acme\MyApp\WithLeadingBackslash');
+               $this->assertTrue(self::$testClassWasLoaded);
        }
        }
+
 }
 }
diff --git a/typo3/sysext/core/Tests/Unit/Package/PackageManagerTest.php b/typo3/sysext/core/Tests/Unit/Package/PackageManagerTest.php
new file mode 100644 (file)
index 0000000..b537328
--- /dev/null
@@ -0,0 +1,550 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Package\PackageInterface;
+use org\bovigo\vfs\vfsStream;
+
+/**
+ * Testcase for the default package manager
+ *
+ */
+class PackageManagerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @var \TYPO3\Flow\Package\PackageManager
+        */
+       protected $packageManager;
+
+       /**
+        * Sets up this test case
+        *
+        */
+       protected function setUp() {
+               vfsStream::setup('Test');
+               $mockBootstrap = $this->getMock('TYPO3\CMS\Core\Core\Bootstrap', array(), array(), '', FALSE);
+               $mockCache = $this->getMock('TYPO3\CMS\Core\Cache\Frontend\PhpFrontend', array('has', 'set', 'getBackend'), array(), '', FALSE);
+               $mockCacheBackend = $this->getMock('TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend', array('has', 'set', 'getBackend'), array(), '', FALSE);
+               $mockCache->expects($this->any())->method('has')->will($this->returnValue(FALSE));
+               $mockCache->expects($this->any())->method('set')->will($this->returnValue(TRUE));
+               $mockCache->expects($this->any())->method('getBackend')->will($this->returnValue($mockCacheBackend));
+               $mockCacheBackend->expects($this->any())->method('getCacheDirectory')->will($this->returnValue('vfs://Test/Cache'));
+               $this->packageManager = new \TYPO3\CMS\Core\Package\PackageManager();
+
+               mkdir('vfs://Test/Packages/Application', 0700, TRUE);
+               mkdir('vfs://Test/Configuration');
+               file_put_contents('vfs://Test/Configuration/PackageStates.php', "<?php return array ('packages' => array(), 'version' => 4); ");
+
+               $mockClassLoader = $this->getMock('TYPO3\CMS\Core\Core\ClassLoader');
+               $mockClassLoader->expects($this->any())->method('setCacheIdentifier')->will($this->returnSelf());
+
+               $composerNameToPackageKeyMap = array(
+                       'typo3/flow' => 'TYPO3.Flow'
+               );
+
+               $this->packageManager->injectClassLoader($mockClassLoader);
+               $this->packageManager->injectCoreCache($mockCache);
+               $this->inject($this->packageManager, 'composerNameToPackageKeyMap', $composerNameToPackageKeyMap);
+               $this->packageManager->initialize($mockBootstrap, 'vfs://Test/Packages/', 'vfs://Test/Configuration/PackageStates.php');
+       }
+
+       /**
+        * @test
+        */
+       public function getPackageReturnsTheSpecifiedPackage() {
+               $this->packageManager->createPackage('TYPO3.Flow');
+
+               $package = $this->packageManager->getPackage('TYPO3.Flow');
+               $this->assertInstanceOf('TYPO3\Flow\Package\PackageInterface', $package, 'The result of getPackage() was no valid package object.');
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\Flow\Package\Exception\UnknownPackageException
+        */
+       public function getPackageThrowsExceptionOnUnknownPackage() {
+               $this->packageManager->getPackage('PrettyUnlikelyThatThisPackageExists');
+       }
+
+       /**
+        * @test
+        */
+       public function getCaseSensitivePackageKeyReturnsTheUpperCamelCaseVersionOfAGivenPackageKeyIfThePackageIsRegistered() {
+               $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('dummy'));
+               $packageManager->_set('packageKeys', array('acme.testpackage' => 'Acme.TestPackage'));
+               $this->assertEquals('Acme.TestPackage', $packageManager->getCaseSensitivePackageKey('acme.testpackage'));
+       }
+
+       /**
+        * @test
+        */
+       public function scanAvailablePackagesTraversesThePackagesDirectoryAndRegistersPackagesItFinds() {
+               $expectedPackageKeys = array(
+                       'TYPO3.Flow' . md5(uniqid(mt_rand(), TRUE)),
+                       'TYPO3.Flow.Test' . md5(uniqid(mt_rand(), TRUE)),
+                       'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), TRUE)),
+                       'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), TRUE))
+               );
+
+               foreach ($expectedPackageKeys as $packageKey) {
+                       $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
+
+                       mkdir($packagePath, 0770, TRUE);
+                       mkdir($packagePath . 'Classes');
+                       file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "flow-test"}');
+               }
+
+               $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('dummy'));
+               $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
+               $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
+
+               $packageFactory = new \TYPO3\Flow\Package\PackageFactory($packageManager);
+               $this->inject($packageManager, 'packageFactory', $packageFactory);
+
+               $packageManager->_set('packages', array());
+               $packageManager->_call('scanAvailablePackages');
+
+               $packageStates = require('vfs://Test/Configuration/PackageStates.php');
+               $actualPackageKeys = array_keys($packageStates['packages']);
+               $this->assertEquals(sort($expectedPackageKeys), sort($actualPackageKeys));
+       }
+
+       /**
+        * @test
+        */
+       public function scanAvailablePackagesKeepsExistingPackageConfiguration() {
+               $expectedPackageKeys = array(
+                       'TYPO3.Flow' . md5(uniqid(mt_rand(), TRUE)),
+                       'TYPO3.Flow.Test' . md5(uniqid(mt_rand(), TRUE)),
+                       'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), TRUE)),
+                       'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), TRUE))
+               );
+
+               foreach ($expectedPackageKeys as $packageKey) {
+                       $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
+
+                       mkdir($packagePath, 0770, TRUE);
+                       mkdir($packagePath . 'Classes');
+                       file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "flow-test"}');
+               }
+
+               $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('dummy'));
+               $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
+               $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
+
+               $packageFactory = new \TYPO3\Flow\Package\PackageFactory($packageManager);
+               $this->inject($packageManager, 'packageFactory', $packageFactory);
+
+               $packageManager->_set('packageStatesConfiguration', array(
+                       'packages' => array(
+                               $packageKey => array(
+                                       'state' => 'inactive',
+                                       'frozen' => FALSE,
+                                       'packagePath' => 'Application/' . $packageKey . '/',
+                                       'classesPath' => 'Classes/'
+                               )
+                       ),
+                       'version' => 2
+               ));
+               $packageManager->_call('scanAvailablePackages');
+               $packageManager->_call('sortAndsavePackageStates');
+
+               $packageStates = require('vfs://Test/Configuration/PackageStates.php');
+               $this->assertEquals('inactive', $packageStates['packages'][$packageKey]['state']);
+       }
+
+
+       /**
+        * @test
+        */
+       public function packageStatesConfigurationContainsRelativePaths() {
+               $packageKeys = array(
+                       'RobertLemke.Flow.NothingElse' . md5(uniqid(mt_rand(), TRUE)),
+                       'TYPO3.Flow' . md5(uniqid(mt_rand(), TRUE)),
+                       'TYPO3.YetAnotherTestPackage' . md5(uniqid(mt_rand(), TRUE)),
+               );
+
+               foreach ($packageKeys as $packageKey) {
+                       $packagePath = 'vfs://Test/Packages/Application/' . $packageKey . '/';
+
+                       mkdir($packagePath, 0770, TRUE);
+                       mkdir($packagePath . 'Classes');
+                       file_put_contents($packagePath . 'composer.json', '{"name": "' . $packageKey . '", "type": "flow-test"}');
+               }
+
+               $packageManager = $this->getAccessibleMock('TYPO3\Flow\Package\PackageManager', array('updateShortcuts'), array(), '', FALSE);
+               $packageManager->_set('packagesBasePath', 'vfs://Test/Packages/');
+               $packageManager->_set('packageStatesPathAndFilename', 'vfs://Test/Configuration/PackageStates.php');
+
+               $packageFactory = new \TYPO3\Flow\Package\PackageFactory($packageManager);
+               $this->inject($packageManager, 'packageFactory', $packageFactory);
+
+               $packageManager->_set('packages', array());
+               $packageManager->_call('scanAvailablePackages');
+
+               $expectedPackageStatesConfiguration = array();
+               foreach ($packageKeys as $packageKey) {
+                       $expectedPackageStatesConfiguration[$packageKey] = array(
+                               'state' => 'active',
+                               'packagePath' => 'Application/' . $packageKey . '/',
+                               'classesPath' => 'Classes/',
+                               'manifestPath' => '',
+                               'composerName' => $packageKey
+                       );
+               }
+
+               $actualPackageStatesConfiguration = $packageManager->_get('packageStatesConfiguration');
+               $this->assertEquals($expectedPackageStatesConfiguration, $actualPackageStatesConfiguration['packages']);
+       }
+
+       /**
+        * Data Provider returning valid package keys and the corresponding path
+        *
+        * @return array
+        */
+       public function packageKeysAndPaths() {
+               return array(
+                       array('TYPO3.YetAnotherTestPackage', 'vfs://Test/Packages/Application/TYPO3.YetAnotherTestPackage/'),
+                       array('RobertLemke.Flow.NothingElse', 'vfs://Test/Packages/Application/RobertLemke.Flow.NothingElse/')
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider packageKeysAndPaths
+        */
+       public function createPackageCreatesPackageFolderAndReturnsPackage($packageKey, $expectedPackagePath) {
+               $actualPackage = $this->packageManager->createPackage($packageKey);
+               $actualPackagePath = $actualPackage->getPackagePath();
+
+               $this->assertEquals($expectedPackagePath, $actualPackagePath);
+               $this->assertTrue(is_dir($actualPackagePath), 'Package path should exist after createPackage()');
+               $this->assertEquals($packageKey, $actualPackage->getPackageKey());
+               $this->assertTrue($this->packageManager->isPackageAvailable($packageKey));
+       }
+
+       /**
+        * @test
+        */
+       public function createPackageWritesAComposerManifestUsingTheGivenMetaObject() {
+               $metaData = new \TYPO3\Flow\Package\MetaData('Acme.YetAnotherTestPackage');
+               $metaData->setDescription('Yet Another Test Package');
+
+               $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage', $metaData);
+
+               $json = file_get_contents($package->getPackagePath() . '/composer.json');
+               $composerManifest = json_decode($json);
+
+               $this->assertEquals('acme/yetanothertestpackage', $composerManifest->name);
+               $this->assertEquals('Yet Another Test Package', $composerManifest->description);
+       }
+
+       /**
+        * @test
+        */
+       public function createPackageCanChangePackageTypeInComposerManifest() {
+               $metaData = new \TYPO3\Flow\Package\MetaData('Acme.YetAnotherTestPackage2');
+               $metaData->setDescription('Yet Another Test Package');
+               $metaData->setPackageType('flow-custom-package');
+
+               $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage2', $metaData);
+
+               $json = file_get_contents($package->getPackagePath() . '/composer.json');
+               $composerManifest = json_decode($json);
+
+               $this->assertEquals('flow-custom-package', $composerManifest->type);
+       }
+
+       /**
+        * Checks if createPackage() creates the folders for classes, configuration, documentation, resources and tests.
+        *
+        * @test
+        */
+       public function createPackageCreatesCommonFolders() {
+               $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+               $packagePath = $package->getPackagePath();
+
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_CLASSES), "Classes directory was not created");
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_CONFIGURATION), "Configuration directory was not created");
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_DOCUMENTATION), "Documentation directory was not created");
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_RESOURCES), "Resources directory was not created");
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_TESTS_UNIT), "Tests/Unit directory was not created");
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_TESTS_FUNCTIONAL), "Tests/Functional directory was not created");
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA), "Metadata directory was not created");
+       }
+
+       /**
+        * Makes sure that an exception is thrown and no directory is created on passing invalid package keys.
+        *
+        * @test
+        */
+       public function createPackageThrowsExceptionOnInvalidPackageKey() {
+               try {
+                       $this->packageManager->createPackage('Invalid_PackageKey');
+               } catch (\TYPO3\Flow\Package\Exception\InvalidPackageKeyException $exception) {
+               }
+               $this->assertFalse(is_dir('vfs://Test/Packages/Application/Invalid_PackageKey'), 'Package folder with invalid package key was created');
+       }
+
+       /**
+        * Makes sure that duplicate package keys are detected.
+        *
+        * @test
+        * @expectedException \TYPO3\Flow\Package\Exception\PackageKeyAlreadyExistsException
+        */
+       public function createPackageThrowsExceptionForExistingPackageKey() {
+               $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+               $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+       }
+
+       /**
+        * @test
+        */
+       public function createPackageActivatesTheNewlyCreatedPackage() {
+               $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+               $this->assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
+       }
+
+       /**
+        * @test
+        */
+       public function activatePackageAndDeactivatePackageActivateAndDeactivateTheGivenPackage() {
+               $packageKey = 'Acme.YetAnotherTestPackage';
+
+               $this->packageManager->createPackage($packageKey);
+
+               $this->packageManager->deactivatePackage($packageKey);
+               $this->assertFalse($this->packageManager->isPackageActive($packageKey));
+
+               $this->packageManager->activatePackage($packageKey);
+               $this->assertTrue($this->packageManager->isPackageActive($packageKey));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException
+        */
+       public function deactivatePackageThrowsAnExceptionIfPackageIsProtected() {
+               $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+               $package->setProtected(TRUE);
+               $this->packageManager->deactivatePackage('Acme.YetAnotherTestPackage');
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\Flow\Package\Exception\UnknownPackageException
+        */
+       public function deletePackageThrowsErrorIfPackageIsNotAvailable() {
+               $this->packageManager->deletePackage('PrettyUnlikelyThatThisPackageExists');
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException
+        */
+       public function deletePackageThrowsAnExceptionIfPackageIsProtected() {
+               $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+               $package->setProtected(TRUE);
+               $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
+       }
+
+       /**
+        * @test
+        */
+       public function deletePackageRemovesPackageFromAvailableAndActivePackagesAndDeletesThePackageDirectory() {
+               $package = $this->packageManager->createPackage('Acme.YetAnotherTestPackage');
+               $packagePath = $package->getPackagePath();
+
+               $this->assertTrue(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA));
+               $this->assertTrue($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
+               $this->assertTrue($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
+
+               $this->packageManager->deletePackage('Acme.YetAnotherTestPackage');
+
+               $this->assertFalse(is_dir($packagePath . PackageInterface::DIRECTORY_METADATA));
+               $this->assertFalse($this->packageManager->isPackageActive('Acme.YetAnotherTestPackage'));
+               $this->assertFalse($this->packageManager->isPackageAvailable('Acme.YetAnotherTestPackage'));
+       }
+
+       /**
+        * @test
+        */
+       public function getDependencyArrayForPackageReturnsCorrectResult() {
+               $mockFlowMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
+               $mockFlowMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
+                       new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'TYPO3.Fluid'),
+                       new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'Doctrine.ORM')
+               )));
+               $mockFlowPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
+               $mockFlowPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockFlowMetadata));
+
+               $mockFluidMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
+               $mockFluidMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
+                       new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'TYPO3.Flow')
+               )));
+               $mockFluidPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
+               $mockFluidPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockFluidMetadata));
+
+               $mockOrmMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
+               $mockOrmMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
+                       new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'Doctrine.DBAL')
+               )));
+               $mockOrmPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
+               $mockOrmPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockOrmMetadata));
+
+               $mockDbalMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
+               $mockDbalMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array(
+                       new \TYPO3\Flow\Package\MetaData\PackageConstraint('depends', 'Doctrine.Common')
+               )));
+               $mockDbalPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
+               $mockDbalPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockDbalMetadata));
+
+               $mockCommonMetadata = $this->getMock('TYPO3\Flow\Package\MetaDataInterface');
+               $mockCommonMetadata->expects($this->any())->method('getConstraintsByType')->will($this->returnValue(array()));
+               $mockCommonPackage = $this->getMock('TYPO3\Flow\Package\PackageInterface');
+               $mockCommonPackage->expects($this->any())->method('getPackageMetaData')->will($this->returnValue($mockCommonMetadata));
+
+               $packages = array(
+                       'TYPO3.Flow' => $mockFlowPackage,
+                       'TYPO3.Fluid' => $mockFluidPackage,
+                       'Doctrine.ORM' => $mockOrmPackage,
+                       'Doctrine.DBAL' => $mockDbalPackage,
+                       'Doctrine.Common' => $mockCommonPackage
+               );
+
+               $packageManager = $this->getAccessibleMock('\TYPO3\Flow\Package\PackageManager', array('dummy'));
+               $packageManager->_set('packages', $packages);
+               $dependencyArray = $packageManager->_call('getDependencyArrayForPackage', 'TYPO3.Flow');
+
+               $this->assertEquals(array('Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM', 'TYPO3.Fluid'), $dependencyArray);
+       }
+
+       /**
+        * @test
+        */
+       public function sortAvailablePackagesByDependenciesMakesSureThatDependantPackagesAreStandingBeforeAPackageInTheInternalPackagesAndPackagesConfigurationArrays() {
+               $doctrineCommon = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
+               $doctrineCommon->expects($this->any())->method('getPackageKey')->will($this->returnValue('Doctrine.Common'));
+
+               $doctrineDbal = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
+               $doctrineDbal->expects($this->any())->method('getPackageKey')->will($this->returnValue('Doctrine.DBAL'));
+
+               $doctrineOrm = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
+               $doctrineOrm->expects($this->any())->method('getPackageKey')->will($this->returnValue('Doctrine.ORM'));
+
+               $typo3Flow = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
+               $typo3Flow->expects($this->any())->method('getPackageKey')->will($this->returnValue('TYPO3.Flow'));
+
+               $symfonyComponentYaml = $this->getMock('\TYPO3\Flow\Package\PackageInterface');
+               $symfonyComponentYaml->expects($this->any())->method('getPackageKey')->will($this->returnValue('Symfony.Component.Yaml'));
+
+               $unsortedPackageStatesConfiguration = array('packages' =>
+                       array(
+                               'Doctrine.ORM' => array(
+                                       'dependencies' => array('Doctrine.Common', 'Doctrine.DBAL')
+                               ),
+                               'Symfony.Component.Yaml' => array(
+                                       'dependencies' => array()
+                               ),
+                               'TYPO3.Flow' => array(
+                                       'dependencies' => array('Symfony.Component.Yaml', 'Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM')
+                               ),
+                               'Doctrine.Common' => array(
+                                       'dependencies' => array()
+                               ),
+                               'Doctrine.DBAL' => array(
+                                       'dependencies' => array('Doctrine.Common')
+                               )
+                       )
+               );
+
+               $unsortedPackages = array(
+                       'Doctrine.ORM' => $doctrineOrm,
+                       'Symfony.Component.Yaml' => $symfonyComponentYaml,
+                       'TYPO3.Flow' => $typo3Flow,
+                       'Doctrine.Common' => $doctrineCommon,
+                       'Doctrine.DBAL' => $doctrineDbal
+               );