diff --git a/res/composer-schema.json b/res/composer-schema.json index 62d6be0ed..308b0d9ba 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -128,6 +128,10 @@ "type": "object", "description": "This is a hash of namespaces (keys) and the directories they can be found into (values) by the autoloader.", "additionalProperties": true + }, + "classmap": { + "type": "array", + "description": "This is an array of directories that contain classes to be included in the class-map generation process." } } }, diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index e33c8ff99..38e44ca2a 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -44,6 +44,11 @@ return call_user_func(function() { $loader->add($namespace, $path); } + $classMap = require __DIR__.'/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + $loader->register(); return $loader; @@ -107,9 +112,15 @@ EOF; } } } - $namespacesFile .= ");\n"; + if (isset($autoloads['classmap'])) { + $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); + ClassMapGenerator::dump(iterator_to_array($it), $targetDir.'/autoload_classmap.php'); + } else { + file_put_contents($targetDir.'/autoload_classmap.php', 'fallbackDirs; } + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + /** * Registers a set of classes * @@ -142,6 +160,10 @@ class ClassLoader */ public function findFile($class) { + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ('\\' == $class[0]) { $class = substr($class, 1); } diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php new file mode 100644 index 000000000..dad422929 --- /dev/null +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @license MIT + */ + +namespace Composer\Autoload; + +/** + * ClassMapGenerator + * + * @author Gyula Sallai + */ +class ClassMapGenerator +{ + /** + * Generate a class map file + * + * @param array|string $dirs Directories or a single path to search in + * @param string $file The name of the class map file + */ + static public function dump($dirs, $file) + { + $dirs = (array) $dirs; + $maps = array(); + + foreach ($dirs as $dir) { + $maps = array_merge($maps, static::createMap($dir)); + } + + file_put_contents($file, sprintf('isFile()) { + continue; + } + + $path = $file->getRealPath(); + + if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { + continue; + } + + $classes = self::findClasses($path); + + foreach ($classes as $class) { + $map[$class] = $path; + } + + } + + return $map; + } + + /** + * Extract the classes in the given file + * + * @param string $path The file to check + * + * @return array The found classes + */ + static private function findClasses($path) + { + $contents = file_get_contents($path); + $tokens = token_get_all($contents); + + $classes = array(); + + $namespace = ''; + for ($i = 0, $max = count($tokens); $i < $max; $i++) { + $token = $tokens[$i]; + + if (is_string($token)) { + continue; + } + + $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: + // 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; + } + } + + if (empty($namespace)) { + $classes[] = $class; + } else { + $classes[] = $namespace . $class; + } + break; + default: + break; + } + } + + return $classes; + } +} +