Browse Source

Add support for class-map generation.

Benjamin Eberlei 13 years ago
parent
commit
1af3604303

+ 4 - 0
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."
                 }
             }
         },

+ 12 - 1
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', '<?php return array();');
+        }
+
         file_put_contents($targetDir.'/autoload.php', $autoloadFile);
         file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
         copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');

+ 22 - 0
src/Composer/Autoload/ClassLoader.php

@@ -45,6 +45,7 @@ class ClassLoader
     private $prefixes = array();
     private $fallbackDirs = array();
     private $useIncludePath = false;
+    private $classMap = array();
 
     public function getPrefixes()
     {
@@ -56,6 +57,23 @@ class ClassLoader
         return $this->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);
         }

+ 138 - 0
src/Composer/Autoload/ClassMapGenerator.php

@@ -0,0 +1,138 @@
+<?php
+
+/*
+ * This file is copied from the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * 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 <salla016@gmail.com>
+ */
+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('<?php return %s;', var_export($maps, true)));
+    }
+
+    /**
+     * Iterate over all files in the given directory searching for classes
+     *
+     * @param Iterator|string $dir The directory to search in or an iterator
+     *
+     * @return array A class map array
+     */
+    static public function createMap($dir)
+    {
+        if (is_string($dir)) {
+            $dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
+        }
+
+        $map = array();
+
+        foreach ($dir as $file) {
+            if (!$file->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;
+    }
+}
+