From af0b8eb26caac493cd1d69ea915691ef76ab7796 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 3 Aug 2015 21:57:20 +0200 Subject: [PATCH] [Debug] Enhance DebugClassLoader performance on MacOSX --- .../Component/Debug/DebugClassLoader.php | 75 ++++++++++++++----- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index a2532d3038..c8e5d1a83f 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -32,6 +32,7 @@ class DebugClassLoader private static $caseCheck; private static $deprecated = array(); private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null'); + private static $darwinCache = array('/' => array('/', array())); /** * Constructor. @@ -220,33 +221,73 @@ class DebugClassLoader 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 (self::$caseCheck && preg_match('#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#D', $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]; + $tailLen = strlen($tail); $real = $refl->getFileName(); 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); - // 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; + + $i = 1 + strrpos($real, '/'); + $file = substr($real, $i); + $real = substr($real, 0, $i); + + if (isset(self::$darwinCache[$real])) { + $kDir = $real; + } else { + $kDir = strtolower($real); + + if (isset(self::$darwinCache[$kDir])) { + $real = self::$darwinCache[$kDir][0]; + } else { + $dir = getcwd(); + chdir($real); + $real = getcwd().'/'; + chdir($dir); + + $dir = $real; + $k = $kDir; + $i = strlen($dir) - 1; + while (!isset(self::$darwinCache[$k])) { + self::$darwinCache[$k] = array($dir, array()); + self::$darwinCache[$dir] = &self::$darwinCache[$k]; + + while ('/' !== $dir[--$i]) { + } + $k = substr($k, 0, ++$i); + $dir = substr($dir, 0, $i--); } } - closedir($h); } - chdir($cwd); + + $dirFiles = self::$darwinCache[$kDir][1]; + + if (isset($dirFiles[$file])) { + $kFile = $file; + } else { + $kFile = strtolower($file); + + if (!isset($dirFiles[$kFile])) { + foreach (scandir($real, 2) as $f) { + if ('.' !== $f[0]) { + $dirFiles[$f] = $f; + if ($f === $file) { + $kFile = $k = $file; + } elseif ($f !== $k = strtolower($f)) { + $dirFiles[$k] = $f; + } + } + } + self::$darwinCache[$kDir][1] = $dirFiles; + } + } + + $real .= $dirFiles[$kFile]; } - if (0 === substr_compare($real, $tail, -strlen($tail), strlen($tail), true) - && 0 !== substr_compare($real, $tail, -strlen($tail), strlen($tail), false) + if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) + && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) ) { throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $real)); }