浏览代码

added an autoloader that uses a class map

A class in Symfony2 can be loaded by four different mechanisms:

 * bootstrap.php: This file contains classes that are always required and
   needed very early in the request handling;

 * classes.php: This file contains classes that are always required and
   managed by extensions via addClassesToCompile();

 * MapFileClassLoader: This autoloader uses a map of class/file to load
   classes (classes are managed by extensions via addClassesToAutoloadMap(),
   and should contain often used classes);

 * UniversalAutolaoder: This autoloader loads all other classes (it's the
   slowest one).
Fabien Potencier 14 年之前
父节点
当前提交
2509c9da4b

+ 67 - 0
src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassMapCacheWarmer.php

@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\CacheWarmer;
+
+use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Generates an autoload class map cache.
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class ClassMapCacheWarmer extends CacheWarmer
+{
+    protected $container;
+
+    /**
+     * Constructor.
+     *
+     * @param KernelInterface $kernel  A KernelInterface instance
+     * @param string          $rootDir The directory where global templates can be stored
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Warms up the cache.
+     *
+     * @param string $cacheDir The cache directory
+     */
+    public function warmUp($cacheDir)
+    {
+        if (!$this->container->hasParameter('kernel.autoload_classes')) {
+            return;
+        }
+
+        $classes = array();
+        foreach ($this->container->getParameter('kernel.autoload_classes') as $class) {
+            $r = new \ReflectionClass($class);
+
+            $classes[$class] = $r->getFilename();
+        }
+
+        $this->writeCacheFile($cacheDir.'/autoload.php', sprintf('<?php return %s;', var_export($classes, true)));
+    }
+
+    /**
+     * Checks whether this warmer is optional or not.
+     *
+     * @return Boolean always false
+     */
+    public function isOptional()
+    {
+        return true;
+    }
+}

+ 42 - 0
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddClassesToAutoloadMapPass.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+/**
+ * Sets the class map for the autoloader.
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class AddClassesToAutoloadMapPass implements CompilerPassInterface
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        $classes = array();
+        foreach ($container->getExtensionConfigs() as $name => $configs) {
+            list($namespace, $tag) = explode(':', $name);
+
+            $extension = $container->getExtension($namespace);
+            if ($extension instanceof Extension) {
+                $classes = array_merge($classes, $extension->getAutoloadClassMap());
+            }
+        }
+
+        $container->setParameter('kernel.autoload_classes', array_unique($classes));
+    }
+}

+ 9 - 1
src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

@@ -19,13 +19,15 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConverterManager
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddClassesToCachePass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddClassesToCachePass;
+use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddClassesToAutoloadMapPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 use Symfony\Component\HttpFoundation\File\File;
 use Symfony\Component\HttpFoundation\File\File;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
-use Symfony\Component\HttpKernel\ClassCollectionLoader;
+use Symfony\Component\ClassLoader\ClassCollectionLoader;
+use Symfony\Component\ClassLoader\MapFileClassLoader;
 
 
 /**
 /**
  * Bundle.
  * Bundle.
@@ -57,6 +59,11 @@ class FrameworkBundle extends Bundle
             $this->container->getParameter('kernel.debug'),
             $this->container->getParameter('kernel.debug'),
             true
             true
         );
         );
+
+        if (file_exists($this->container->getParameter('kernel.cache_dir').'/autoload.php')) {
+            $classloader = new MapFileClassLoader($this->container->getParameter('kernel.cache_dir').'/autoload.php');
+            $classloader->register(true);
+        }
     }
     }
 
 
     public function registerExtensions(ContainerBuilder $container)
     public function registerExtensions(ContainerBuilder $container)
@@ -73,6 +80,7 @@ class FrameworkBundle extends Bundle
         $container->addCompilerPass(new AddConstraintValidatorsPass());
         $container->addCompilerPass(new AddConstraintValidatorsPass());
         $container->addCompilerPass(new AddFieldFactoryGuessersPass());
         $container->addCompilerPass(new AddFieldFactoryGuessersPass());
         $container->addCompilerPass(new AddClassesToCachePass());
         $container->addCompilerPass(new AddClassesToCachePass());
+        $container->addCompilerPass(new AddClassesToAutoloadMapPass());
         $container->addCompilerPass(new TranslatorPass());
         $container->addCompilerPass(new TranslatorPass());
         $container->addCompilerPass(new AddCacheWarmerPass());
         $container->addCompilerPass(new AddCacheWarmerPass());
     }
     }

+ 6 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

@@ -12,6 +12,7 @@
         <parameter key="error_handler.level">null</parameter>
         <parameter key="error_handler.level">null</parameter>
         <parameter key="filesystem.class">Symfony\Bundle\FrameworkBundle\Util\Filesystem</parameter>
         <parameter key="filesystem.class">Symfony\Bundle\FrameworkBundle\Util\Filesystem</parameter>
         <parameter key="cache_warmer.class">Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate</parameter>
         <parameter key="cache_warmer.class">Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate</parameter>
+        <parameter key="cache_warmer.autoloader_map.class">Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassMapCacheWarmer</parameter>
     </parameters>
     </parameters>
 
 
     <services>
     <services>
@@ -33,6 +34,11 @@
             <argument type="collection" />
             <argument type="collection" />
         </service>
         </service>
 
 
+        <service id="cache_warmer.autoload_map" class="%cache_warmer.autoloader_map.class%" public="false">
+            <tag name="kernel.cache_warmer" />
+            <argument type="service" id="service_container" />
+        </service>
+
         <!--
         <!--
             If you want to change the Request class, modify the code in
             If you want to change the Request class, modify the code in
             your front controller (app.php) so that it passes an instance of
             your front controller (app.php) so that it passes an instance of

+ 52 - 0
src/Symfony/Component/ClassLoader/MapFileClassLoader.php

@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.org>
+ */
+class MapFileClassLoader
+{
+    protected $map = array();
+
+    public function __construct($file)
+    {
+        $this->map = require $file;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param Boolean $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $class The name of the class
+     */
+    public function loadClass($class)
+    {
+        if ('\\' === $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        if (isset($this->map[$class])) {
+            require $this->map[$class];
+        }
+    }
+}

+ 28 - 2
src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php

@@ -22,19 +22,45 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
 abstract class Extension extends BaseExtension
 abstract class Extension extends BaseExtension
 {
 {
     protected $classes = array();
     protected $classes = array();
+    protected $classMap = array();
 
 
+    /**
+     * Gets the classes to cache.
+     *
+     * @return array An array of classes
+     */
     public function getClassesToCompile()
     public function getClassesToCompile()
     {
     {
         return $this->classes;
         return $this->classes;
     }
     }
 
 
     /**
     /**
-     * Adds classes to be compiled when debug mode is not enabled.
+     * Adds classes to the class cache.
      *
      *
-     * @param array $classes Classes to be compiled
+     * @param array $classes An array of classes
      */
      */
     protected function addClassesToCompile(array $classes)
     protected function addClassesToCompile(array $classes)
     {
     {
         $this->classes = array_merge($this->classes, $classes);
         $this->classes = array_merge($this->classes, $classes);
     }
     }
+
+    /**
+     * Gets the autoload class map.
+     *
+     * @return array An array of classes
+     */
+    public function getAutoloadClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * Adds classes to the autoload class map.
+     *
+     * @param array $classes An array of classes
+     */
+    public function addClassesToAutoloadMap(array $classes)
+    {
+        $this->classMap = array_merge($this->classMap, $classes);
+    }
 }
 }

+ 1 - 0
src/Symfony/Component/HttpKernel/Resources/bin/packager.php

@@ -47,5 +47,6 @@ ClassCollectionLoader::load(array(
 
 
     'Symfony\\Component\\ClassLoader\\ClassCollectionLoader',
     'Symfony\\Component\\ClassLoader\\ClassCollectionLoader',
     'Symfony\\Component\\ClassLoader\\UniversalClassLoader',
     'Symfony\\Component\\ClassLoader\\UniversalClassLoader',
+    'Symfony\\Component\\ClassLoader\\MapFileClassLoader',
 
 
 ), __DIR__.'/../..', 'bootstrap', false);
 ), __DIR__.'/../..', 'bootstrap', false);

+ 24 - 0
src/Symfony/Component/HttpKernel/bootstrap.php

@@ -1948,3 +1948,27 @@ class UniversalClassLoader
     }
     }
 }
 }
 }
 }
+namespace Symfony\Component\ClassLoader
+{
+class MapFileClassLoader
+{
+    protected $map = array();
+    public function __construct($file)
+    {
+        $this->map = require $file;
+    }
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+    public function loadClass($class)
+    {
+        if ('\\' === $class[0]) {
+            $class = substr($class, 1);
+        }
+        if (isset($this->map[$class])) {
+            require $this->map[$class];
+        }
+    }
+}
+}