bug #10776 [Debug] fix #10771 DebugClassLoader can't load PSR4 libs (nicolas-grekas)

This PR was merged into the 2.5-dev branch.

Discussion
----------

[Debug] fix #10771 DebugClassLoader can't load PSR4 libs

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #10771
| License       | MIT
| Doc PR        | none

Commits
-------

e3566c2 [Debug] fix #10771 DebugClassLoader can't load PSR4 libs
This commit is contained in:
Fabien Potencier 2014-04-25 09:03:21 +02:00
commit 4c2405f300
2 changed files with 37 additions and 28 deletions

View File

@ -176,32 +176,37 @@ class DebugClassLoader
$class = substr($class, 1); $class = substr($class, 1);
} }
$i = 0; if (preg_match('#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#', $file, $tail)) {
$tail = DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php'; $tail = $tail[0];
$len = strlen($tail); $real = realpath($file);
do { if (false !== stripos(PHP_OS, 'darwin')) {
$tail = substr($tail, $i); // realpath() on MacOSX doesn't normalize the case of characters,
$len -= $i; // let's do it ourselves. This is tricky.
$cwd = getcwd();
if (0 === substr_compare($file, $tail, -$len, $len, true)) { $basename = strrpos($real, '/');
if (0 !== substr_compare($file, $tail, -$len, $len, false)) { chdir(substr($real, 0, $basename));
if (method_exists($this->classLoader[0], 'getClassMap')) { $basename = substr($real, $basename + 1);
$map = $this->classLoader[0]->getClassMap(); $real = getcwd().'/';
} else { $h = opendir('.');
$map = array(); while (false !== $f = readdir($h)) {
} if (0 === strcasecmp($f, $basename)) {
$real .= $f;
if (! isset($map[$class])) { break;
throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $file));
} }
} }
closedir($h);
break; 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, '/')) { 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)); 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));
} }

View File

@ -27,7 +27,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
{ {
$this->errorReporting = error_reporting(E_ALL | E_STRICT); $this->errorReporting = error_reporting(E_ALL | E_STRICT);
$this->loader = new ClassLoader(); $this->loader = new ClassLoader();
spl_autoload_register(array($this->loader, 'loadClass')); spl_autoload_register(array($this->loader, 'loadClass'), true, true);
DebugClassLoader::enable(); DebugClassLoader::enable();
} }
@ -68,7 +68,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
// See below: this will fail with parse error // See below: this will fail with parse error
// but this should not be @-silenced. // but this should not be @-silenced.
@ class_exists(__NAMESPACE__.'\TestingUnsilencing', true); @class_exists(__NAMESPACE__.'\TestingUnsilencing', true);
ini_set('log_errors', $bak[0]); ini_set('log_errors', $bak[0]);
ini_set('display_errors', $bak[1]); ini_set('display_errors', $bak[1]);
@ -138,6 +138,10 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
*/ */
public function testFileCaseMismatch() 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); class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true);
} }
@ -168,7 +172,7 @@ class ClassLoader
public function getClassMap() 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) public function findFile($class)
@ -180,13 +184,13 @@ class ClassLoader
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
} elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) { } elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) {
return __DIR__ . '/Fixtures/casemismatch.php'; return __DIR__.'/Fixtures/CaseMismatch.php';
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
return __DIR__ . '/Fixtures/psr4/Psr4CaseMismatch.php'; return __DIR__.'/Fixtures/psr4/Psr4CaseMismatch.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
return __DIR__ . '/Fixtures/reallyNotPsr0.php'; return __DIR__.'/Fixtures/reallyNotPsr0.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
return __DIR__ . '/Fixtures/notPsr0Bis.php'; return __DIR__.'/Fixtures/notPsr0Bis.php';
} }
} }
} }