[FEATURE] Use caching framework in autoloader
authorChristian Kuhn <lolli@schwarzbu.ch>
Sun, 10 Jul 2011 21:18:34 +0000 (23:18 +0200)
committerAndreas Wolf <andreas.wolf@ikt-werk.de>
Tue, 12 Jul 2011 20:58:52 +0000 (22:58 +0200)
The patch moves the initialization of the caching framework to
config_default.php, prior to the autoload registration. In effect the
autoloader can use the phpcode cache to write a full autoload registry
that can be required directly to reduce file access overhead.
As a side effect the cache system is now always enabled for all access
scopes like FE, BE, CLI and so on.

Change-Id: I7c548fbabeb6e60b8bd9c4098803c1171bbdbec5
Resolves: #28063
Reviewed-on: http://review.typo3.org/3254
Reviewed-by: Andreas Wolf
Tested-by: Andreas Wolf
t3lib/class.t3lib_autoloader.php
t3lib/class.t3lib_extmgm.php
t3lib/config_default.php
tests/t3lib/class.t3lib_autoloaderTest.php
typo3/init.php
typo3/sysext/cms/tslib/class.tslib_fe.php

index 3dae1e5..aec4cf7 100644 (file)
  *
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
-/**
- * Contains TYPO3 autoloader
- *
- * @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>
- */
 
 /**
  * This class contains TYPO3 autoloader for classes.
  * It handles:
  * - the core of TYPO3
  * - all extensions with an ext_autoload.php file
+ *
+ * @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>
  */
 class t3lib_autoloader {
 
@@ -49,17 +46,6 @@ class t3lib_autoloader {
        protected static $classNameToFileMapping = array();
 
        /**
-        * Associative array which sets for each extension which was attempted to load if it has an autoload configuration
-        *
-        * Key: extension key
-        * Value: TRUE, if extension has an ext_autoload.php and this is already part of $classNameToFileMapping
-        *                FALSE, if extension has no ext_autoload.php
-        *
-        * @var array
-        */
-       protected static $extensionHasAutoloadConfiguration = array();
-
-       /**
         * The autoloader is static, thus we do not allow instances of this class.
         */
        private function __construct() {
@@ -68,21 +54,20 @@ class t3lib_autoloader {
        /**
         * Installs TYPO3 autoloader, and loads the autoload registry for the core.
         *
-        * @return      boolean TRUE in case of success
+        * @return boolean TRUE in case of success
         */
-       static public function registerAutoloader() {
-               self::loadCoreRegistry();
-               self::$extensionHasAutoloadConfiguration = array();
-               return spl_autoload_register('t3lib_autoloader::autoload');
+       public static function registerAutoloader() {
+               self::loadCoreAndExtensionRegistry();
+               return spl_autoload_register('t3lib_autoloader::autoload', TRUE, TRUE);
        }
 
        /**
         * Uninstalls TYPO3 autoloader. This function is for the sake of completeness.
         * It is never called by the TYPO3 core.
         *
-        * @return      boolean TRUE in case of success
+        * @return boolean TRUE in case of success
         */
-       static public function unregisterAutoloader() {
+       public static function unregisterAutoloader() {
                return spl_autoload_unregister('t3lib_autoloader::autoload');
        }
 
@@ -92,16 +77,14 @@ class t3lib_autoloader {
         * 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 Class name
+        * @return void
         */
-       static public function autoload($className) {
-               $classPath = FALSE;
-
-                       // use core and extension registry
+       public static function autoload($className) {
+                       // Use core and extension registry
                $classPath = self::getClassPathByRegistryLookup($className);
 
-               if ($classPath && file_exists($classPath)) {
+               if ($classPath) {
                        t3lib_div::requireFile($classPath);
                } else {
                        try {
@@ -116,26 +99,50 @@ class t3lib_autoloader {
        }
 
        /**
-        * Load the core registry into $classNameToFileMapping, effectively overriding
-        * the whole contents of $classNameToFileMapping.
+        * Load registry from cache file if available or search
+        * for all loaded extensions and create a cache file
         *
         * @return void
         */
-       static protected function loadCoreRegistry() {
-               self::$classNameToFileMapping = require(PATH_t3lib . 'core_autoload.php');
+       public static function loadCoreAndExtensionRegistry() {
+               $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
+               $autoloadCacheIdentifier = TYPO3_MODE === 'FE' ? 't3lib_autoload_FE' : 't3lib_autoload_BE';
+
+                       // Create autoloader cache file if it does not exist yet
+               if (!$phpCodeCache->has($autoloadCacheIdentifier)) {
+                       $classRegistry = self::createCoreAndExtensionRegistry();
+                       $cachedFileContent = 'return array(';
+                       foreach ($classRegistry as $className => $classLocation) {
+                               $cachedFileContent .= chr(10) . '\'' . $className . '\' => \'' . $classLocation . '\',';
+                       }
+                       $cachedFileContent .= chr(10) . ');';
+                       $phpCodeCache->set($autoloadCacheIdentifier, $cachedFileContent);
+               }
+
+                       // Require calculated cache file
+               $mappingArray = $phpCodeCache->requireOnce($autoloadCacheIdentifier);
+
+                       // This can only happen if the autoloader 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 should only happen in unit tests
+               if (!is_array($mappingArray)) {
+                       $mappingArray = self::createCoreAndExtensionRegistry();
+               }
+
+               self::$classNameToFileMapping = $mappingArray;
        }
 
        /**
-        * Get the full path to a class by looking it up in the registry. If not found, returns NULL.
+        * Get the full path to a class by looking it up in the registry.
+        * If not found, returns NULL.
         *
-        * @param       string  $className      Class name
-        * @return      string  full name of the file where $className is declared, or NULL if no entry found in registry.
+        * @param string $className Class name
+        * @return string Full name of the file where $className is declared, or NULL if no entry found in registry.
         */
-       static protected function getClassPathByRegistryLookup($className) {
+       protected static function getClassPathByRegistryLookup($className) {
                $className = strtolower($className);
-               if (!array_key_exists($className, self::$classNameToFileMapping)) {
-                       self::attemptToLoadRegistryForGivenClassName($className);
-               }
                if (array_key_exists($className, self::$classNameToFileMapping)) {
                        return self::$classNameToFileMapping[$className];
                } else {
@@ -144,30 +151,22 @@ class t3lib_autoloader {
        }
 
        /**
-        * Try to load the entries for a given class name into the registry.
+        * Find all ext_autoload files and merge with core_autoload.
         *
-        * First, figures out the extension the class belongs to.
-        * Then, tries to load the ext_autoload.php file inside the extension directory, and adds its contents to the $classNameToFileMapping.
-        *
-        * @param       string  $className      Class Name
+        * @return void
         */
-       static protected function attemptToLoadRegistryForGivenClassName($className) {
-               $classNameParts = explode('_', $className);
-               $extensionPrefix = array_shift($classNameParts) . '_' . array_shift($classNameParts);
-               $extensionKey = t3lib_extMgm::getExtensionKeyByPrefix($extensionPrefix);
-
-               if (!$extensionKey || array_key_exists($extensionKey, self::$extensionHasAutoloadConfiguration)) {
-                               // extension key could not be determined or we already tried to load the extension's autoload configuration
-                       return;
-               }
-               $possibleAutoloadConfigurationFileName = t3lib_extMgm::extPath($extensionKey) . 'ext_autoload.php';
-               if (file_exists($possibleAutoloadConfigurationFileName)) {
-                       self::$extensionHasAutoloadConfiguration[$extensionKey] = TRUE;
-                       $extensionClassNameToFileMapping = require($possibleAutoloadConfigurationFileName);
-                       self::$classNameToFileMapping = array_merge($extensionClassNameToFileMapping, self::$classNameToFileMapping);
-               } else {
-                       self::$extensionHasAutoloadConfiguration[$extensionKey] = FALSE;
+       protected static function createCoreAndExtensionRegistry() {
+               $classRegistry = require(PATH_t3lib . 'core_autoload.php');
+                       // At this point localconf.php was already initialized
+                       // we have a current extList and extMgm is also known
+               $loadedExtensions = array_unique(t3lib_div::trimExplode(',', t3lib_extMgm::getEnabledExtensionList(), TRUE));
+               foreach ($loadedExtensions as $extensionKey) {
+                       $extensionAutoloadFile = t3lib_extMgm::extPath($extensionKey, 'ext_autoload.php');
+                       if (file_exists($extensionAutoloadFile)) {
+                               $classRegistry = array_merge($classRegistry, require($extensionAutoloadFile));
+                       }
                }
+               return $classRegistry;
        }
 }
 ?>
\ No newline at end of file
index 40bb15b..6a7a13f 100644 (file)
@@ -93,8 +93,8 @@ final class t3lib_extMgm {
 
                        $extensionPath = PATH_site . $GLOBALS['TYPO3_LOADED_EXT'][$key]['siteRelPath'];
                } else {
-                       $extensionList = $GLOBALS['TYPO3_CONF_VARS']['EXT']['requiredExt'] . ',' . $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'];
-                       $loadedExtensions = array_flip(array_unique(t3lib_div::trimExplode(',', $extensionList, 1)));
+                       $extensionList = self::getRequiredExtensionList() . ',' . $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'];
+                       $loadedExtensions = array_flip(array_unique(t3lib_div::trimExplode(',', $extensionList, TRUE)));
 
                        if (!isset($loadedExtensions[$key])) {
                                throw new BadFunctionCallException(
@@ -1684,4 +1684,4 @@ $GLOBALS[\'TYPO3_LOADED_EXT\'] = unserialize(stripslashes(\'' . addslashes(seria
        }
 }
 
-?>
+?>
\ No newline at end of file
index d78be73..13e7c4d 100644 (file)
@@ -683,6 +683,27 @@ if (!@is_file(PATH_typo3conf . 'localconf.php')) {
 require(PATH_typo3conf.'localconf.php');
 
 
+function initializeCachingFramework() {
+       require (PATH_t3lib . 'class.t3lib_cache.php');
+       require (PATH_t3lib . 'cache/class.t3lib_cache_exception.php');
+       require (PATH_t3lib . 'cache/exception/class.t3lib_cache_exception_invaliddata.php');
+       require (PATH_t3lib . 'interfaces/interface.t3lib_singleton.php');
+       require (PATH_t3lib . 'cache/class.t3lib_cache_factory.php');
+       require (PATH_t3lib . 'cache/class.t3lib_cache_manager.php');
+       require (PATH_t3lib . 'cache/frontend/interfaces/interface.t3lib_cache_frontend_frontend.php');
+       require (PATH_t3lib . 'cache/frontend/class.t3lib_cache_frontend_abstractfrontend.php');
+       require (PATH_t3lib . 'cache/frontend/class.t3lib_cache_frontend_stringfrontend.php');
+       require (PATH_t3lib . 'cache/frontend/class.t3lib_cache_frontend_phpfrontend.php');
+       require (PATH_t3lib . 'cache/backend/interfaces/interface.t3lib_cache_backend_backend.php');
+       require (PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_abstractbackend.php');
+       require (PATH_t3lib . 'cache/backend/interfaces/interface.t3lib_cache_backend_phpcapablebackend.php');
+       require (PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_filebackend.php');
+       t3lib_cache::initializeCachingFramework();
+}
+
+initializeCachingFramework();
+
+
 // *********************
 // Autoloader
 // *********************
@@ -921,7 +942,6 @@ define('TYPO3_REQUESTTYPE',
 );
 
 
-
 // Load extensions:
 if (TYPO3_MODE=='FE' && is_object($TT)) $TT->push('Loading localconf.php extensions','');
 $TYPO3_LOADED_EXT = t3lib_extMgm::typo3_loadExtensions();
@@ -938,6 +958,10 @@ if ($TYPO3_LOADED_EXT['_CACHEFILE'])       {
 }
 if (TYPO3_MODE=='FE' && is_object($TT)) $TT->pull();
 
+       // Extensions may register new caches, so we set the
+       // global cache array to the manager again at this point
+$GLOBALS['typo3CacheManager']->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
+
 require_once(t3lib_extMgm::extPath('lang') . 'lang.php');
 
        // Deprecation log since 4.6, can be removed in 4.8. Checks if obsolete pageCacheToExternalFiles is set
@@ -978,4 +1002,4 @@ $SIM_EXEC_TIME = $EXEC_TIME;                       // $SIM_EXEC_TIME is set to $EXEC_TIME but can be
 $ACCESS_TIME = $EXEC_TIME - ($EXEC_TIME % 60);         // $ACCESS_TIME is a common time in minutes for access control
 $SIM_ACCESS_TIME = $ACCESS_TIME;               // if $SIM_EXEC_TIME is changed this value must be set accordingly
 
-?>
+?>
\ No newline at end of file
index 76573bc..cc6b700 100644 (file)
 class t3lib_autoloaderTest extends Tx_Phpunit_TestCase {
 
        /**
+        * @var boolean Enable backup of global and system variables
+        */
+       protected $backupGlobals = TRUE;
+
+       /**
+        * Exclude TYPO3_DB from backup/ restore of $GLOBALS
+        * because resource types cannot be handled during serializing
+        *
+        * @var array
+        */
+       protected $backupGlobalsBlacklist = array('TYPO3_DB');
+
+       /**
         * @var array Register of temporary extensions in typo3temp
         */
        protected $fakedExtensions = array();
@@ -61,6 +74,7 @@ class t3lib_autoloaderTest extends Tx_Phpunit_TestCase {
                $GLOBALS['TYPO3_LOADED_EXT'][$extKey] = array(
                        'siteRelPath' => $relPath
                );
+               $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'] .= ',' . $extKey;
 
                $this->fakedExtensions[] = $extKey;
                t3lib_extMgm::clearExtensionKeyMap();
@@ -71,6 +85,15 @@ class t3lib_autoloaderTest extends Tx_Phpunit_TestCase {
        /**
         * @test
         */
+       public function autoloaderCanBeUnregisteredAndRegisteredAgain() {
+               t3lib_autoloader::unregisterAutoloader();
+               t3lib_autoloader::registerAutoloader();
+               t3lib_div::makeInstance('t3lib_timetracknull');
+       }
+
+       /**
+        * @test
+        */
        public function extensionAutoloadFileIsIncludedIfAvailable() {
                $extKey = $this->createFakeExtension();
                $extPath = PATH_site . "typo3temp/$extKey/";
@@ -82,11 +105,19 @@ class t3lib_autoloaderTest extends Tx_Phpunit_TestCase {
                file_put_contents($file, "<?php\n\nthrow new RuntimeException('', 1310203812);\n\n?>");
                file_put_contents($autoloaderFile, "<?php\n\nreturn array('$class' => '$file');\n\n?>");
 
+                       // Inject a dummy for the core_phpcode cache to force the autoloader
+                       // to re calculate the registry
+               $mockCache = $this->getMock('t3lib_cache_frontend_AbstractFrontend', array('getIdentifier', 'set', 'get', 'getByTag', 'has', 'remove', 'flush', 'flushByTag', 'requireOnce'), array(), '', FALSE);
+               $GLOBALS['typo3CacheManager'] = $this->getMock('t3lib_cache_Manager', 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
+               t3lib_autoloader::unregisterAutoloader();
+               t3lib_autoloader::registerAutoloader();
+
                        // Expect the exception of the file to be thrown
                $this->setExpectedException('RuntimeException', '', 1310203812);
                t3lib_autoloader::autoload($class);
        }
 }
-
-
 ?>
\ No newline at end of file
index 83c5289..4f5ae72 100644 (file)
@@ -224,10 +224,6 @@ $TYPO3_DB->debugOutput = $TYPO3_CONF_VARS['SYS']['sqlDebug'];
 $CLIENT = t3lib_div::clientInfo();                                     // $CLIENT includes information about the browser/user-agent
 $PARSETIME_START = t3lib_div::milliseconds();          // Is set to the system time in milliseconds. This could be used to output script parsetime in the end of the script
 
-// ***********************************
-// Initializing the Caching System
-// ***********************************
-t3lib_cache::initializeCachingFramework();
 
 // *************************
 // CLI dispatch processing
index d5a47cd..b9049bc 100644 (file)
         * @return      void
         */
        protected function initCaches() {
-               $GLOBALS['TT']->push('Initializing the Caching System','');
-               t3lib_cache::initializeCachingFramework();
                $this->pageCache = $GLOBALS['typo3CacheManager']->getCache('cache_pages');
-               $GLOBALS['TT']->pull();
        }
 
        /**