Bladeren bron

Optimize classmap generation by avoiding the use of token_get_all()

First of all this seems way faster than iterating over all tokens, but
especially it reduces memory usage drastically for very large files
Jordi Boggiano 12 jaren geleden
bovenliggende
commit
8e9678a5ba
1 gewijzigde bestanden met toevoegingen van 21 en 44 verwijderingen
  1. 21 44
      src/Composer/Autoload/ClassMapGenerator.php

+ 21 - 44
src/Composer/Autoload/ClassMapGenerator.php

@@ -92,59 +92,36 @@ class ClassMapGenerator
      */
      */
     private static function findClasses($path)
     private static function findClasses($path)
     {
     {
-        $contents = file_get_contents($path);
+        $contents = php_strip_whitespace($path);
+
         try {
         try {
             if (!preg_match('{\b(?:class|interface|trait)\b}i', $contents)) {
             if (!preg_match('{\b(?:class|interface|trait)\b}i', $contents)) {
                 return array();
                 return array();
             }
             }
-            $tokens   = token_get_all($contents);
-        } catch (\Exception $e) {
-            throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e);
-        }
-        $T_TRAIT  = version_compare(PHP_VERSION, '5.4', '<') ? -1 : T_TRAIT;
 
 
-        $classes = array();
+            // strip heredocs/nowdocs
+            $contents = preg_replace('{<<<\'?(\w+)\'?(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\1(?=\r\n|\n|\r|;)}s', 'null', $contents);
+            // strip strings
+            $contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}', 'null', $contents);
 
 
-        $namespace = '';
-        for ($i = 0, $max = count($tokens); $i < $max; $i++) {
-            $token = $tokens[$i];
+            preg_match_all('{(?:\b(?<![\$:>])(?<type>class|interface|trait)\s+(?<name>\S+)|\b(?<![\$:>])(?<ns>namespace)\s+(?<nsname>[^\s;{}\\\\]+(?:\s*\\\\\s*[^\s;{}\\\\]+)*))}i', $contents, $matches);
+            $classes = array();
 
 
-            if (is_string($token)) {
-                continue;
-            }
+            $namespace = '';
 
 
-            $class = '';
-
-            switch ($token[0]) {
-                case T_NAMESPACE:
-                    $namespace = '';
-                    // If there is a namespace, extract it
-                    while (($t = $tokens[++$i]) && is_array($t)) {
-                        if (in_array($t[0], array(T_STRING, T_NS_SEPARATOR))) {
-                            $namespace .= $t[1];
-                        }
-                    }
-                    $namespace .= '\\';
-                    break;
-                case T_CLASS:
-                case T_INTERFACE:
-                case $T_TRAIT:
-                    // Find the classname
-                    while (($t = $tokens[++$i]) && is_array($t)) {
-                        if (T_STRING === $t[0]) {
-                            $class .= $t[1];
-                        } elseif ($class !== '' && T_WHITESPACE == $t[0]) {
-                            break;
-                        }
-                    }
-
-                    $classes[] = ltrim($namespace . $class, '\\');
-                    break;
-                default:
-                    break;
+            for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
+                $name = $matches['name'][$i];
+
+                if (!empty($matches['ns'][$i])) {
+                    $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\';
+                } else {
+                    $classes[] = ltrim($namespace . $matches['name'][$i], '\\');
+                }
             }
             }
-        }
 
 
-        return $classes;
+            return $classes;
+        } catch (\Exception $e) {
+            throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e);
+        }
     }
     }
 }
 }