From e3566c27b7093647b4635a5c0ab6c6a67b78287d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 24 Apr 2014 14:55:37 +0200 Subject: [PATCH] [Debug] fix #10771 DebugClassLoader can't load PSR4 libs --- .../Component/Debug/DebugClassLoader.php | 47 ++++++++++--------- .../Debug/Tests/DebugClassLoaderTest.php | 18 ++++--- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index b7a10d60e5..2d768d37f8 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -176,32 +176,37 @@ class DebugClassLoader $class = substr($class, 1); } - $i = 0; - $tail = DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php'; - $len = strlen($tail); + if (preg_match('#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#', $file, $tail)) { + $tail = $tail[0]; + $real = realpath($file); - do { - $tail = substr($tail, $i); - $len -= $i; - - if (0 === substr_compare($file, $tail, -$len, $len, true)) { - if (0 !== substr_compare($file, $tail, -$len, $len, false)) { - if (method_exists($this->classLoader[0], 'getClassMap')) { - $map = $this->classLoader[0]->getClassMap(); - } else { - $map = array(); - } - - if (! isset($map[$class])) { - throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $file)); + if (false !== stripos(PHP_OS, 'darwin')) { + // realpath() on MacOSX doesn't normalize the case of characters, + // let's do it ourselves. This is tricky. + $cwd = getcwd(); + $basename = strrpos($real, '/'); + chdir(substr($real, 0, $basename)); + $basename = substr($real, $basename + 1); + $real = getcwd().'/'; + $h = opendir('.'); + while (false !== $f = readdir($h)) { + if (0 === strcasecmp($f, $basename)) { + $real .= $f; + break; } } - - break; + closedir($h); + chdir($cwd); } - } while (false !== $i = strpos($tail, DIRECTORY_SEPARATOR, 1)); - if (! $exists) { + if ( 0 === substr_compare($real, $tail, -strlen($tail), strlen($tail), true) + && 0 !== substr_compare($real, $tail, -strlen($tail), strlen($tail), false) + ) { + throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $real)); + } + } + + if (!$exists) { if (false !== strpos($class, '/')) { throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); } diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index fd98a5116e..d50c7adc75 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -27,7 +27,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase { $this->errorReporting = error_reporting(E_ALL | E_STRICT); $this->loader = new ClassLoader(); - spl_autoload_register(array($this->loader, 'loadClass')); + spl_autoload_register(array($this->loader, 'loadClass'), true, true); DebugClassLoader::enable(); } @@ -68,7 +68,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase // See below: this will fail with parse error // but this should not be @-silenced. - @ class_exists(__NAMESPACE__.'\TestingUnsilencing', true); + @class_exists(__NAMESPACE__.'\TestingUnsilencing', true); ini_set('log_errors', $bak[0]); ini_set('display_errors', $bak[1]); @@ -138,6 +138,10 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase */ public function testFileCaseMismatch() { + if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); } @@ -168,7 +172,7 @@ class ClassLoader public function getClassMap() { - return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__ . '/Fixtures/notPsr0Bis.php'); + return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'); } public function findFile($class) @@ -180,13 +184,13 @@ class ClassLoader } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); } elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) { - return __DIR__ . '/Fixtures/casemismatch.php'; + return __DIR__.'/Fixtures/CaseMismatch.php'; } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { - return __DIR__ . '/Fixtures/psr4/Psr4CaseMismatch.php'; + return __DIR__.'/Fixtures/psr4/Psr4CaseMismatch.php'; } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { - return __DIR__ . '/Fixtures/reallyNotPsr0.php'; + return __DIR__.'/Fixtures/reallyNotPsr0.php'; } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { - return __DIR__ . '/Fixtures/notPsr0Bis.php'; + return __DIR__.'/Fixtures/notPsr0Bis.php'; } } }