Prechádzať zdrojové kódy

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 rokov pred
rodič
commit
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\ProfilerPass;
 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\AddCacheWarmerPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 use Symfony\Component\HttpFoundation\File\File;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
-use Symfony\Component\HttpKernel\ClassCollectionLoader;
+use Symfony\Component\ClassLoader\ClassCollectionLoader;
+use Symfony\Component\ClassLoader\MapFileClassLoader;
 
 /**
  * Bundle.
@@ -57,6 +59,11 @@ class FrameworkBundle extends Bundle
             $this->container->getParameter('kernel.debug'),
             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)
@@ -73,6 +80,7 @@ class FrameworkBundle extends Bundle
         $container->addCompilerPass(new AddConstraintValidatorsPass());
         $container->addCompilerPass(new AddFieldFactoryGuessersPass());
         $container->addCompilerPass(new AddClassesToCachePass());
+        $container->addCompilerPass(new AddClassesToAutoloadMapPass());
         $container->addCompilerPass(new TranslatorPass());
         $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="filesystem.class">Symfony\Bundle\FrameworkBundle\Util\Filesystem</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>
 
     <services>
@@ -33,6 +34,11 @@
             <argument type="collection" />
         </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
             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
 {
     protected $classes = array();
+    protected $classMap = array();
 
+    /**
+     * Gets the classes to cache.
+     *
+     * @return array An array of classes
+     */
     public function getClassesToCompile()
     {
         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)
     {
         $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\\UniversalClassLoader',
+    'Symfony\\Component\\ClassLoader\\MapFileClassLoader',
 
 ), __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];
+        }
+    }
+}
+}