[BUGFIX] CamelCased class names impossible with empty cache
authorChristian Kuhn <lolli@schwarzbu.ch>
Sat, 23 Jun 2012 18:20:07 +0000 (20:20 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 24 Jun 2012 14:13:02 +0000 (16:13 +0200)
Issue #37110 that aimed to allow case sensitive class name in
ext_autoload files has a bug that any class registered with CamelCased
class name is only found if the autoload entries are read from cache.

The patch now creates a lower cased entry as soon as the core_autoload
and ext_autoload files are read. There is an additional test that fails
if the changes in the autoload class are not applied. The naming of the
existing camelCase test was adapted to show that it is handling the
answer of an successful cache fetch only.

Additionally the current strtolower is changed to t3lib_div::strtolower
to make the whole thing multibyte safe.

Change-Id: I94f7ca5b3e60234f08433c747c00639d8dd219cd
Fixes: #38331
Related: #37110
Releases: 6.0
Reviewed-on: http://review.typo3.org/12310
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
t3lib/class.t3lib_autoloader.php
tests/Unit/t3lib/class.t3lib_autoloaderTest.php

index 9f689d4..e8b5aa5 100644 (file)
@@ -157,7 +157,15 @@ class t3lib_autoloader {
                        $classRegistry = self::createCoreAndExtensionRegistry();
                }
 
-               self::$classNameToFileMapping = $classRegistry;
+                       // Lowercase all keys. We must use the multi byte safe version
+                       // of strtolower from t3lib_div here, so array_change_key_case()
+                       // can not be used
+               $lowerCasedClassRegistry = array();
+               foreach ($classRegistry as $className => $classFile) {
+                       $lowerCasedClassRegistry[t3lib_div::strtolower($className)] = $classFile;
+               }
+
+               self::$classNameToFileMapping = $lowerCasedClassRegistry;
        }
 
        /**
@@ -173,7 +181,7 @@ class t3lib_autoloader {
         */
        public static function getClassPathByRegistryLookup($className) {
                $classPath = NULL;
-               $classNameLower = strtolower($className);
+               $classNameLower = t3lib_div::strtolower($className);
 
                        // Try to resolve extbase naming scheme if class is not already in cache file
                if (!array_key_exists($classNameLower, self::$classNameToFileMapping)) {
@@ -341,12 +349,13 @@ class t3lib_autoloader {
        protected static function addClassToCache($classFilePathAndName, $className) {
                if (file_exists($classFilePathAndName)) {
                        self::$cacheUpdateRequired = TRUE;
-                       self::$classNameToFileMapping[strtolower($className)] = $classFilePathAndName;
+                       self::$classNameToFileMapping[t3lib_div::strtolower($className)] = $classFilePathAndName;
                }
        }
 
        /**
-        * Set or update autoloader cache entry
+        * Set or update autoloader cache entry.
+        * It is expected that all class names (keys) are already lowercased!
         *
         * @param array $registry Current registry entries
         * @return void
@@ -355,7 +364,7 @@ class t3lib_autoloader {
                $cachedFileContent = 'return array(';
                foreach ($registry as $className => $classLocation) {
                        $nullOrLocation = is_string($classLocation) ? '\'' . $classLocation . '\',' : 'NULL,';
-                       $cachedFileContent .= LF . '\'' . strtolower($className) . '\' => ' . $nullOrLocation;
+                       $cachedFileContent .= LF . '\'' . $className . '\' => ' . $nullOrLocation;
                }
                $cachedFileContent .= LF . ');';
                $GLOBALS['typo3CacheManager']->getCache('cache_phpcode')->set(
index 2dac80a..7fdcbdd 100644 (file)
@@ -193,23 +193,49 @@ class t3lib_autoloaderTest extends Tx_Phpunit_TestCase {
         * @test
         * @expectedException RuntimeException
         */
-       public function autoloadFindsCamelCasedClassFileIfExtAutoloadEntryIsLowerCased() {
+       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 = $extPath . uniqid('') . '.php';
                file_put_contents($file, "<?php\n\nthrow new RuntimeException('', 1336756850);\n\n?>");
 
-                       // Inject a dummy for the core_phpcode cache to force the autoloader
-                       // to re calculate the registry
+               $extAutoloadFile = $extPath . 'ext_autoload.php';
+               file_put_contents($extAutoloadFile, "<?php\n\nreturn array('" . $class . "' => '" . $file . "');\n\n?>");
+
+                       // Inject cache and return false, so autoloader is forced to read ext_autoloads from extensions
                $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));
+               $mockCache->expects($this->any())->method('has')->will($this->returnValue(FALSE));
+
+                       // Re-initialize autoloader registry to force it to recognize the new extension
+               t3lib_autoloader::unregisterAutoloader();
+               t3lib_autoloader::registerAutoloader();
+               t3lib_autoloader::autoload($class);
+       }
 
-                       // Let cache access give back lowercased class name
+       /**
+        * @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\n\nthrow new RuntimeException('', 1336756850);\n\n?>");
+
+                       // Inject cache mock and let the cache entry return the lowercased class name as key
+               $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));
                $mockCache->expects($this->any())->method('has')->will($this->returnValue(TRUE));
                $mockCache->expects($this->once())->method('requireOnce')->will($this->returnValue(array(strtolower($class) => $file)));