bug #10861 [Debug] enhance perf of DebugClassLoader (nicolas-grekas)

This PR was merged into the 2.3-dev branch.

Discussion
----------

[Debug] enhance perf of DebugClassLoader

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

- restricts the check for case mismatch to Windows and Darwin,
- on Darwin, use glob() trick to bypass more extensive checks

Commits
-------

485e047 [Debug] enhance perf of DebugClassLoader
This commit is contained in:
Fabien Potencier 2014-05-08 18:40:26 +02:00
commit ff9f06b978

View File

@ -29,6 +29,7 @@ class DebugClassLoader
private $classLoader;
private $isFinder;
private $wasFinder;
private static $caseCheck;
/**
* Constructor.
@ -49,6 +50,10 @@ class DebugClassLoader
$this->classLoader = $classLoader;
$this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile');
}
if (!isset(self::$caseCheck)) {
self::$caseCheck = false !== stripos(PHP_OS, 'win') ? (false !== stripos(PHP_OS, 'darwin') ? 2 : 1) : 0;
}
}
/**
@ -162,9 +167,13 @@ class DebugClassLoader
$exists = class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false));
if ('\\' === $class[0]) {
$class = substr($class, 1);
}
if ($exists) {
$name = new \ReflectionClass($class);
$name = $name->getName();
$refl = new \ReflectionClass($class);
$name = $refl->getName();
if ($name !== $class) {
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name));
@ -172,30 +181,35 @@ class DebugClassLoader
}
if ($file) {
if ('\\' == $class[0]) {
$class = substr($class, 1);
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));
}
throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
}
if (preg_match('#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#', $file, $tail)) {
if (self::$caseCheck && preg_match('#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#D', $file, $tail)) {
$tail = $tail[0];
$real = realpath($file);
$real = $refl->getFilename();
if (false !== stripos(PHP_OS, 'darwin')) {
// realpath() on MacOSX doesn't normalize the case of characters,
// let's do it ourselves. This is tricky.
if (2 === self::$caseCheck) {
// realpath() on MacOSX doesn't normalize the case of characters
$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;
// glob() patterns are case-sensitive even if the underlying fs is not
if (!in_array($basename, glob($basename.'*', GLOB_NOSORT), true)) {
$real = getcwd().'/';
$h = opendir('.');
while (false !== $f = readdir($h)) {
if (0 === strcasecmp($f, $basename)) {
$real .= $f;
break;
}
}
closedir($h);
}
closedir($h);
chdir($cwd);
}
@ -206,14 +220,6 @@ class DebugClassLoader
}
}
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));
}
throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
}
return true;
}
}