[TASK] Replace ClassLoader cache with standard caches 89/25489/9
authorThomas Maroschik <tmaroschik@dfau.de>
Mon, 18 Nov 2013 19:53:36 +0000 (20:53 +0100)
committerThorsten Kahler <thorsten.kahler@typo3.org>
Fri, 22 Nov 2013 14:24:42 +0000 (15:24 +0100)
The class loader can now use all available cache backends
for retrieval of class loading information.

Using it with APC for example brings a good performance
boost.

Resolves: #53744
Releases: 6.2
Change-Id: I55db9686fa2d5b6462b4cb56c452ad2e99e1d2e7
Reviewed-on: https://review.typo3.org/25489
Reviewed-by: Wouter Wolters
Reviewed-by: Markus Klein
Tested-by: Markus Klein
Reviewed-by: Alexander Opitz
Tested-by: Alexander Opitz
Reviewed-by: Thorsten Kahler
Tested-by: Thorsten Kahler
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Classes/Core/ClassAliasMap.php
typo3/sysext/core/Classes/Core/ClassLoader.php
typo3/sysext/core/Classes/Core/SystemEnvironmentBuilder.php
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/core/Tests/Unit/Core/ClassLoaderTest.php
typo3/sysext/core/Tests/Unit/Package/PackageManagerTest.php

index 58c5a87..6c03a21 100644 (file)
@@ -226,7 +226,7 @@ class Bootstrap {
                        ->initializeClassLoader()
                        ->populateLocalConfiguration()
                        ->initializeCachingFramework()
-                       ->initializeClassLoaderCache()
+                       ->initializeClassLoaderCaches()
                        ->initializePackageManagement($packageManagerClassName);
 
                // @TODO dig into this
@@ -259,9 +259,9 @@ class Bootstrap {
         * @return Bootstrap
         */
        protected function initializeClassLoader() {
-               $classLoader = new \TYPO3\CMS\Core\Core\ClassLoader();
+               $classLoader = new ClassLoader($this->applicationContext);
                $this->setEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassLoader', $classLoader);
-               $classLoader->setEarlyClassFileAutoloadRegistry((array) include __DIR__ . '/../../ext_autoload.php');
+               $classLoader->setEarlyClassInformationsFromAutoloadRegistry((array) include __DIR__ . '/../../ext_autoload.php');
                $classAliasMap = new \TYPO3\CMS\Core\Core\ClassAliasMap();
                $classAliasMap->injectClassLoader($classLoader);
                $this->setEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassAliasMap', $classAliasMap);
@@ -274,6 +274,7 @@ class Bootstrap {
         * Reinitializes the class loader during clear cache actions
         * Beware! This is not public API and necessary for edge cases in the install tool
         *
+        * @param string $packageManagerClassName
         * @return void
         */
        public function reinitializeClassLoaderAndCachesAndPackageManagement($packageManagerClassName = 'TYPO3\\CMS\\Core\\Package\\PackageManager') {
@@ -284,7 +285,7 @@ class Bootstrap {
                        ->initializeClassLoader()
                        ->populateLocalConfiguration()
                        ->initializeCachingFramework()
-                       ->initializeClassLoaderCache()
+                       ->initializeClassLoaderCaches()
                        ->initializePackageManagement($packageManagerClassName);
        }
 
@@ -293,9 +294,10 @@ class Bootstrap {
         *
         * @return Bootstrap
         */
-       protected function initializeClassLoaderCache() {
-               /** @var $classLoader \TYPO3\CMS\Core\Core\ClassLoader */
+       protected function initializeClassLoaderCaches() {
+               /** @var $classLoader ClassLoader */
                $classLoader = $this->getEarlyInstance('TYPO3\\CMS\\Core\\Core\\ClassLoader');
+               $classLoader->injectCoreCache($this->getEarlyInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_core'));
                $classLoader->injectClassesCache($this->getEarlyInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_classes'));
                return $this;
        }
@@ -308,6 +310,7 @@ class Bootstrap {
         * @return Bootstrap
         */
        protected function initializePackageManagement($packageManagerClassName) {
+               /** @var \TYPO3\CMS\Core\Package\PackageManager $packageManager */
                $packageManager = new $packageManagerClassName();
                $this->setEarlyInstance('TYPO3\\Flow\\Package\\PackageManager', $packageManager);
                Utility\ExtensionManagementUtility::setPackageManager($packageManager);
@@ -336,7 +339,6 @@ class Bootstrap {
         * Load TYPO3_LOADED_EXT, recreate class loader registry and load ext_localconf
         *
         * @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
         */
@@ -751,7 +753,6 @@ class Bootstrap {
        /**
         * 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
         */
index 450ff7f..cb8cb2b 100644 (file)
@@ -46,12 +46,17 @@ class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
        protected $classNameToAliasMapping = array();
 
        /**
-        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
+        * @var \TYPO3\CMS\Core\Cache\Frontend\StringFrontend
         */
        protected $classesCache;
 
        /**
-        * @var \TYPO3\CMS\Core\Core\ClassLoader
+        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
+        */
+       protected $coreCache;
+
+       /**
+        * @var ClassLoader
         */
        protected $classLoader;
 
@@ -61,21 +66,28 @@ class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
        protected $cacheIdentifier;
 
        /**
-        * @var array<\TYPO3\Flow\Package\Package>
+        * @var \TYPO3\Flow\Package\Package[]
         */
        protected $packages = array();
 
        /**
-        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache
+        * @param \TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache
         */
-       public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache) {
+       public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache) {
                $this->classesCache = $classesCache;
        }
 
        /**
-        * @param \TYPO3\CMS\Core\Core\ClassLoader
+        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
+        */
+       public function injectCoreCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache) {
+               $this->coreCache = $coreCache;
+       }
+
+       /**
+        * @param ClassLoader
         */
-       public function injectClassLoader(\TYPO3\CMS\Core\Core\ClassLoader $classLoader) {
+       public function injectClassLoader(ClassLoader $classLoader) {
                $this->classLoader = $classLoader;
        }
 
@@ -138,8 +150,8 @@ class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
         */
        protected function loadEarlyInstanceMappingFromCache() {
                $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
-               if (!$cacheEntryIdentifier !== NULL && $this->classesCache->has($cacheEntryIdentifier)) {
-                       return (bool) $this->classesCache->requireOnce($cacheEntryIdentifier);
+               if (!$cacheEntryIdentifier !== NULL && $this->coreCache->has($cacheEntryIdentifier)) {
+                       return (bool) $this->coreCache->requireOnce($cacheEntryIdentifier);
                }
                return FALSE;
        }
@@ -151,7 +163,7 @@ class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
         */
        protected function buildMappingAndInitializeEarlyInstanceMapping() {
                $aliasToClassNameMapping = array();
-                       foreach ($this->packages as $package) {
+               foreach ($this->packages as $package) {
                        if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
                                $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $package->getClassAliases());
                        }
@@ -187,35 +199,16 @@ class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
         * @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)
-                               && ($requiredFile = $cacheBackend->getPathOfRequiredFileInCacheEntry($originalClassNameCacheEntryIdentifier))
-                       ) {
-                               $proxyContent = array(
-                                       $this->buildRequireOnceCommand($requiredFile),
-                                       $this->buildClassLoaderCommand(),
-                               );
+                       $classLoadingInformation = $this->classLoader->buildClassLoadingInformation($originalClassName);
+                       if (NULL !== $classLoadingInformation) {
+                               $classLoadingInformation = implode("\xff", array_merge($classLoadingInformation, $aliasClassNames));
+                               $this->classesCache->set($originalClassNameCacheEntryIdentifier, $classLoadingInformation);
                                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);
+                                       $aliasClassNameCacheEntryIdentifier = str_replace('\\', '_', strtolower($aliasClassName));
+                                       $this->classesCache->set($aliasClassNameCacheEntryIdentifier, $classLoadingInformation);
                                }
                        }
                }
@@ -236,8 +229,8 @@ class ClassAliasMap implements \TYPO3\CMS\Core\SingletonInterface {
                        }
                        $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
                        if ($cacheEntryIdentifier !== NULL) {
-                               $this->classesCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
-                               $this->classesCache->requireOnce($cacheEntryIdentifier);
+                               $this->coreCache->set($this->getCacheEntryIdentifier(), implode(LF, $proxyContent));
+                               $this->coreCache->requireOnce($cacheEntryIdentifier);
                        } else {
                                eval(implode(PHP_EOL, $proxyContent));
                        }
index 5341b1d..c29aa33 100644 (file)
@@ -27,8 +27,8 @@ namespace TYPO3\CMS\Core\Core;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use TYPO3\CMS\Core\Package\Package;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 
 /**
  * Class Loader implementation which loads .php files found in the classes
@@ -47,24 +47,34 @@ class ClassLoader {
        static protected $staticAliasMap;
 
        /**
-        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
+        * @var \TYPO3\CMS\Core\Cache\Frontend\StringFrontend
         */
        protected $classesCache;
 
        /**
+        * @var \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
+        */
+       protected $coreCache;
+
+       /**
         * @var string
         */
        protected $cacheIdentifier;
 
        /**
-        * @var array<\TYPO3\Flow\Package\Package>
+        * @var \TYPO3\Flow\Package\Package[]
         */
        protected $packages = array();
 
        /**
+        * @var boolean
+        */
+       protected $isEarlyCache = TRUE;
+
+       /**
         * @var array
         */
-       protected $earlyClassFileAutoloadRegistry = array();
+       protected $earlyClassInformationsFromAutoloadRegistry = array();
 
        /**
         * @var array A list of namespaces this class loader is definitely responsible for
@@ -78,8 +88,8 @@ class ClassLoader {
         */
        protected $packageClassesPaths = array();
 
-       public function __construct() {
-               $this->classesCache = new \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend('cache_classes', new \TYPO3\CMS\Core\Cache\Backend\EarlyClassLoaderBackend());
+       public function __construct(ApplicationContext $context) {
+               $this->classesCache = new \TYPO3\CMS\Core\Cache\Frontend\StringFrontend('cache_classes', new \TYPO3\CMS\Core\Cache\Backend\TransientMemoryBackend($context));
        }
 
        /**
@@ -93,18 +103,29 @@ class ClassLoader {
        }
 
        /**
+        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache
+        */
+       public function injectCoreCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $coreCache) {
+               $this->coreCache = $coreCache;
+               $this->classAliasMap->injectCoreCache($coreCache);
+       }
+
+       /**
         * Get classes cache injected
         *
-        * @param \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache
+        * @param \TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache
         */
-       public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\PhpFrontend $classesCache) {
-               /** @var $earlyClassLoaderBackend \TYPO3\CMS\Core\Cache\Backend\EarlyClassLoaderBackend */
-               $earlyClassLoaderBackend = $this->classesCache->getBackend();
+       public function injectClassesCache(\TYPO3\CMS\Core\Cache\Frontend\StringFrontend $classesCache) {
+               /** @var $earlyClassLoaderBackend \TYPO3\CMS\Core\Cache\Backend\TransientMemoryBackend */
+               $earlyClassesCache = $this->classesCache;
                $this->classesCache = $classesCache;
+               $this->isEarlyCache = FALSE;
                $this->classAliasMap->injectClassesCache($classesCache);
-               foreach ($earlyClassLoaderBackend->getAll() as $cacheEntryIdentifier => $classFilePath) {
+               foreach ($earlyClassesCache->getByTag('early') as $originalClassLoadingInformation) {
+                       $classLoadingInformation = explode("\xff", $originalClassLoadingInformation);
+                       $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $classLoadingInformation[1]));
                        if (!$this->classesCache->has($cacheEntryIdentifier)) {
-                               $this->addClassToCache($classFilePath, $cacheEntryIdentifier);
+                               $this->classesCache->set($cacheEntryIdentifier, $originalClassLoadingInformation);
                        }
                }
        }
@@ -114,107 +135,124 @@ class ClassLoader {
         * a package and specifically registered classes.
         *
         * @param string $className Name of the class/interface to load
-        * @param bool $require TRUE if file should be required
         * @return boolean
         */
-       public function loadClass($className, $require = TRUE) {
+       public function loadClass($className) {
                if ($className[0] === '\\') {
                        $className = substr($className, 1);
                }
 
-               if (!$this->isValidClassname($className)) {
+               if (!$this->isValidClassName($className)) {
                        return FALSE;
                }
 
                $cacheEntryIdentifier = strtolower(str_replace('\\', '_', $className));
-               $cacheEntryCreated = FALSE;
-
-               // Loads any known class via caching framework
-               if ($require) {
-                       if ($this->classesCache->requireOnce($cacheEntryIdentifier) !== FALSE) {
-                               $cacheEntryCreated = TRUE;
+               try {
+                       if ($this->classesCache->has($cacheEntryIdentifier)) {
+                               $classLoadingInformation = explode("\xff", $this->classesCache->get($cacheEntryIdentifier));
+                       } else {
+                               $classLoadingInformation = $this->buildClassLoadingInformation($className);
+                               if ($classLoadingInformation !== NULL) {
+                                       $this->classesCache->set($cacheEntryIdentifier, implode("\xff", $classLoadingInformation), $this->isEarlyCache ? array('early') : array());
+                               }
                        }
+               } catch (\InvalidArgumentException $exception) {
+                       return FALSE;
                }
 
-               if (!$cacheEntryCreated) {
-                       $cacheEntryCreated = $this->createCacheEntryForClassFromCorePackage($className, $cacheEntryIdentifier);
-               }
+               // Class loading information structure
+               // array(
+               //   0 => class file path
+               //   1 => original class name
+               //   2 and following => alias class names
+               // )
 
-               if (!$cacheEntryCreated) {
-                       $cacheEntryCreated = $this->createCacheEntryForClassFromEarlyAutoloadRegistry($className, $cacheEntryIdentifier);
+               $loadingSuccessful = FALSE;
+               if ($classLoadingInformation !== NULL) {
+                       $loadingSuccessful = (bool)require_once $classLoadingInformation[0];
                }
+               if ($loadingSuccessful && count($classLoadingInformation) > 2) {
+                       $originalClassName = $classLoadingInformation[1];
+                       foreach (array_slice($classLoadingInformation, 2) as $aliasClassName) {
+                               $this->setAliasForClassName($aliasClassName, $originalClassName);
+                       }
+               }
+
+               return $loadingSuccessful;
+       }
 
-               if (!$cacheEntryCreated) {
-                       $cacheEntryCreated = $this->createCacheEntryForClassFromRegisteredPackages($className, $cacheEntryIdentifier);
+       /**
+        * @param string $className
+        * @return array|null
+        */
+       public function buildClassLoadingInformation($className) {
+               $classLoadingInformation = $this->buildClassLoadingInformationForClassFromCorePackage($className);
+
+               if ($classLoadingInformation === NULL) {
+                       $classLoadingInformation = $this->buildClassLoadingInformationForClassFromEarlyAutoloadRegistry($className);
                }
 
-               if (!$cacheEntryCreated) {
-                       $cacheEntryCreated = $this->createCacheEntryForClassByNamingConvention($className, $cacheEntryIdentifier);
+               if ($classLoadingInformation === NULL) {
+                       $classLoadingInformation = $this->buildClassLoadingInformationForClassFromRegisteredPackages($className);
                }
 
-               if ($cacheEntryCreated && $require) {
-                       if ($this->classesCache->requireOnce($cacheEntryIdentifier) !== FALSE) {
-                               $cacheEntryCreated = TRUE;
-                       }
+               if ($classLoadingInformation === NULL) {
+                       $classLoadingInformation = $this->buildClassLoadingInformationForClassByNamingConvention($className);
                }
 
-               return $cacheEntryCreated;
+               return $classLoadingInformation;
        }
 
        /**
         * Find out if a class name is valid
         *
         * @param string $className
-        * @return bool
+        * @return boolean
         */
-       protected function isValidClassname($className) {
+       protected function isValidClassName($className) {
                return strpos($className, ' ') === FALSE;
        }
 
        /**
-        * Create cache entry for class from core package
+        * Retrieve class loading information for class from core package
         *
         * @param string $className
-        * @param string $cacheEntryIdentifier
-        * @return boolean TRUE if cache entry exists
+        * @return array|null
         */
-       protected function createCacheEntryForClassFromCorePackage($className, $cacheEntryIdentifier) {
-               if (substr($cacheEntryIdentifier, 0, 14) === 'typo3_cms_core') {
-                       $classesFolder = substr($cacheEntryIdentifier, 15, 5) === 'tests' ? '' : 'Classes/';
+       protected function buildClassLoadingInformationForClassFromCorePackage($className) {
+               if (substr($className, 0, 14) === 'TYPO3\\CMS\\Core') {
+                       $classesFolder = substr($className, 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 array($classFilePath, $className);
                        }
                }
-               return FALSE;
+               return NULL;
        }
 
        /**
-        * Create early class name autoload registry cache
+        * Retrieve class loading information from early class name autoload registry cache
         *
         * @param string $className
-        * @param string $cacheEntryIdentifier
-        * @return boolean TRUE if cache file was created
+        * @return array|null
         */
-       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;
+       protected function buildClassLoadingInformationForClassFromEarlyAutoloadRegistry($className) {
+               $lowercasedClassName = strtolower($className);
+               if (isset($this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName])) {
+                       if (@file_exists($this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName][0])) {
+                               return $this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName];
                        }
                }
-               return FALSE;
+               return NULL;
        }
 
        /**
-        * Create cache entry from registered packages
+        * Retrieve class loading information from registered packages
         *
         * @param string $className
-        * @param string $cacheEntryIdentifier
-        * @return boolean TRUE File was created
+        * @return array|null
         */
-       protected function createCacheEntryForClassFromRegisteredPackages($className, $cacheEntryIdentifier) {;
+       protected function buildClassLoadingInformationForClassFromRegisteredPackages($className) {;
                foreach ($this->packageNamespaces as $packageNamespace => $packageData) {
                        if (substr(str_replace('_', '\\', $className), 0, $packageData['namespaceLength']) === $packageNamespace) {
                                if ($packageData['substituteNamespaceInPath']) {
@@ -238,24 +276,20 @@ class ClassLoader {
                                        $classPathAndFilename = $packageData['classesPath'] . $classPathAndFilename;
                                }
                                if (@file_exists($classPathAndFilename)) {
-                                       $this->addClassToCache($classPathAndFilename, $cacheEntryIdentifier);
-                                       return TRUE;
+                                       return array($classPathAndFilename, $className);
                                }
                        }
                }
-               return FALSE;
+               return NULL;
        }
 
        /**
-        * Try to load a given class name based on 'extbase' naming convention into the registry.
-        * If the file is found it writes an entry to $classNameToFileMapping and re-caches the
-        * array to the file system to save this lookup for next call.
+        * Retrieve class loading information based on 'extbase' naming convention into the registry.
         *
         * @param string $className Class name to find source file of
-        * @param string $classCacheEntryIdentifier
-        * @return boolean TRUE if was created
+        * @return array|null
         */
-       protected function createCacheEntryForClassByNamingConvention($className, $classCacheEntryIdentifier) {
+       protected function buildClassLoadingInformationForClassByNamingConvention($className) {
                $delimiter = '_';
                // To handle namespaced class names, split the class name at the
                // namespace delimiters.
@@ -268,10 +302,12 @@ class ClassLoader {
                // 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) {
-                       return FALSE;
+                       return NULL;
                }
 
-               if (isset($classNameParts[0]) && $classNameParts[0] === 'TYPO3' && (isset($classNameParts[1]) && $classNameParts[1] === 'CMS')) {
+               if (isset($classNameParts[0]) && isset($classNameParts[1])
+                       && $classNameParts[0] === 'TYPO3' && $classNameParts[1] === 'CMS'
+               ) {
                        $extensionKey = GeneralUtility::camelCaseToLowerCaseUnderscored($classNameParts[2]);
                        $classNameWithoutVendorAndProduct = $classNameParts[3];
                } else {
@@ -291,12 +327,11 @@ class ClassLoader {
                        }
                        $classFilePath = $classesPath . strtr($classNameWithoutVendorAndProduct, $delimiter, '/') . '.php';
                        if (@file_exists($classFilePath)) {
-                               $this->addClassToCache($classFilePath, $classCacheEntryIdentifier);
-                               return TRUE;
+                               return array($classFilePath, $className);
                        }
                }
 
-               return FALSE;
+               return NULL;
        }
 
        /**
@@ -311,11 +346,12 @@ class ClassLoader {
        /**
         * Get cache entry identifier
         *
-        * @return string identifier
+        * @return string|null identifier
         */
        protected function getCacheEntryIdentifier() {
-               $cacheIdentifier = $this->getCacheIdentifier();
-               return $cacheIdentifier !== NULL ? 'ClassLoader_' . $this->getCacheIdentifier() : NULL;
+               return $this->getCacheIdentifier() !== NULL
+                       ? 'ClassLoader_' . $this->getCacheIdentifier()
+                       : NULL;
        }
 
        /**
@@ -344,7 +380,7 @@ class ClassLoader {
                        $this->savePackageNamespacesAndClassesPathsToCache();
                        // Rebuild the class alias map too because ext_autoload can contain aliases
                        $classNameToAliasMapping = $this->classAliasMap->setPackagesButDontBuildMappingFilesReturnClassNameToAliasMappingInstead($packages);
-                       $this->buildAutoloadRegistryAndSaveToCache();
+                       $this->buildClassInformationsFromFullAutoloadRegistry();
                        $this->classAliasMap->buildMappingFiles($classNameToAliasMapping);
                } else {
                        $this->classAliasMap->setPackages($packages);
@@ -359,8 +395,8 @@ class ClassLoader {
         */
        protected function loadPackageNamespacesFromCache() {
                $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
-               if ($cacheEntryIdentifier !== NULL && $this->classesCache->has($cacheEntryIdentifier)) {
-                       list($packageNamespaces, $packageClassesPaths) = $this->classesCache->requireOnce($cacheEntryIdentifier);
+               if ($cacheEntryIdentifier !== NULL && $this->coreCache->has($cacheEntryIdentifier)) {
+                       list($packageNamespaces, $packageClassesPaths) = $this->coreCache->requireOnce($cacheEntryIdentifier);
                        if (is_array($packageNamespaces) && is_array($packageClassesPaths)) {
                                $this->packageNamespaces = $packageNamespaces;
                                $this->packageClassesPaths = $packageClassesPaths;
@@ -379,13 +415,13 @@ class ClassLoader {
                /** @var $package \TYPO3\Flow\Package\Package */
                foreach ($this->packages as $package) {
                        $packageNamespace = $package->getNamespace();
-                       // Ignore legacy extensions with unkown vendor name
+                       // Ignore legacy extensions with unknown 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)
+                                       'substituteNamespaceInPath' => ($package instanceof Package)
                                );
                        }
                }
@@ -400,15 +436,15 @@ class ClassLoader {
        }
 
        /**
-        * Build autoload registry
+        * Builds the early autoload registry, also for usage in the class alias map
         *
         * @return void
         */
-       protected function buildAutoloadRegistryAndSaveToCache() {
+       protected function buildClassInformationsFromFullAutoloadRegistry() {
                $classFileAutoloadRegistry = array();
                foreach ($this->packages as $package) {
-                       /** @var $package \TYPO3\CMS\Core\Package\Package */
-                       if ($package instanceof \TYPO3\CMS\Core\Package\Package) {
+                       /** @var $package Package */
+                       if ($package instanceof Package) {
                                $classFilesFromAutoloadRegistry = $package->getClassFilesFromAutoloadRegistry();
                                if (is_array($classFilesFromAutoloadRegistry)) {
                                        $classFileAutoloadRegistry = array_merge($classFileAutoloadRegistry, $classFilesFromAutoloadRegistry);
@@ -416,8 +452,9 @@ class ClassLoader {
                        }
                }
                foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
-                       if (@file_exists($classFilePath)) {
-                               $this->addClassToCache($classFilePath, strtolower(str_replace('\\', '_', $className)));
+                       $lowercasedClassName = strtolower($className);
+                       if (!isset($this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName]) && @file_exists($classFilePath)) {
+                               $this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName] = array($classFilePath, $className);
                        }
                }
        }
@@ -430,9 +467,10 @@ class ClassLoader {
        protected function buildPackageClassesPathsForLegacyExtensions() {
                foreach ($this->packages as $package) {
                        if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface) {
-                               $this->packageClassesPaths[$package->getPackageKey()] = $package->getClassesPath();
+                               $classesPath = $package->getClassesPath();
+                               $this->packageClassesPaths[$package->getPackageKey()] = $classesPath;
                                foreach ($package->getPackageReplacementKeys() as $packageToReplace => $versionConstraint) {
-                                       $this->packageClassesPaths[$packageToReplace] = $package->getClassesPath();
+                                       $this->packageClassesPaths[$packageToReplace] = $classesPath;
                                }
                        }
                }
@@ -446,7 +484,7 @@ class ClassLoader {
        protected function savePackageNamespacesAndClassesPathsToCache() {
                $cacheEntryIdentifier = $this->getCacheEntryIdentifier();
                if ($cacheEntryIdentifier !== NULL) {
-                       $this->classesCache->set(
+                       $this->coreCache->set(
                                $this->getCacheEntryIdentifier(),
                                'return ' . var_export(array($this->packageNamespaces, $this->packageClassesPaths), TRUE) . ';'
                        );
@@ -454,27 +492,18 @@ class ClassLoader {
        }
 
        /**
-        * 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
         *
         * @param array $classFileAutoloadRegistry
+        * @return void
         */
-       public function setEarlyClassFileAutoloadRegistry($classFileAutoloadRegistry) {
-               $this->earlyClassFileAutoloadRegistry = $classFileAutoloadRegistry;
+       public function setEarlyClassInformationsFromAutoloadRegistry($classFileAutoloadRegistry) {
+               foreach ($classFileAutoloadRegistry as $className => $classFilePath) {
+                       $lowercasedClassName = strtolower($className);
+                       if (!isset($this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName])) {
+                               $this->earlyClassInformationsFromAutoloadRegistry[$lowercasedClassName] = array($classFilePath, $className);
+                       }
+               }
        }
 
        /**
@@ -507,7 +536,7 @@ class ClassLoader {
         */
        static public function getAliasForClassName($className) {
                $aliases = static::$staticAliasMap->getAliasesForClassName($className);
-               return (is_array($aliases) && isset($aliases[0])) ? $aliases[0] : NULL;
+               return is_array($aliases) && isset($aliases[0]) ? $aliases[0] : NULL;
        }
 
        /**
index faa3e70..4097631 100644 (file)
@@ -198,10 +198,12 @@ class SystemEnvironmentBuilder {
                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/Frontend/VariableFrontend.php';
                require_once __DIR__ . '/../Cache/Backend/BackendInterface.php';
                require_once __DIR__ . '/../Cache/Backend/PhpCapableBackendInterface.php';
+               require_once __DIR__ . '/../Cache/Backend/TaggableBackendInterface.php';
                require_once __DIR__ . '/../Cache/Backend/AbstractBackend.php';
-               require_once __DIR__ . '/../Cache/Backend/EarlyClassLoaderBackend.php';
+               require_once __DIR__ . '/../Cache/Backend/TransientMemoryBackend.php';
                require_once __DIR__ . '/ClassLoader.php';
                require_once __DIR__ . '/ClassAliasMap.php';
        }
index 58a4bd9..8821f2c 100644 (file)
@@ -134,11 +134,9 @@ return 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',
+                                       'frontend' => 'TYPO3\CMS\Core\Cache\Frontend\StringFrontend',
+                                       'backend' => 'TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend',
                                        'options' => array()
                                ),
                                'cache_hash' => array(
index 5150382..2a150d2 100644 (file)
@@ -81,7 +81,7 @@ class ClassLoaderTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $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 = new \TYPO3\CMS\Core\Core\ClassLoader(\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getApplicationContext());
                $this->classLoader->injectClassAliasMap($mockClassAliasMap);
                $this->classLoader->setPackages(array('Acme.MyApp' => $package1, 'Acme.MyAppAddon' => $package2));
        }
index 9c3c293..8a66549 100644 (file)
@@ -44,7 +44,7 @@ class PackageManagerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                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 = $this->getMock('TYPO3\CMS\Core\Core\ClassLoader', array(), array(\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getApplicationContext()));
                $mockClassLoader->expects($this->any())->method('setCacheIdentifier')->will($this->returnSelf());
 
                $composerNameToPackageKeyMap = array(