浏览代码

added a cache warmer sub-framework

Cache warmer will come in the next commits.

To warm up the cache on a production server, you can use
the cache:warmup command:

./app/console_prod cache:warmup
Fabien Potencier 14 年之前
父节点
当前提交
d0b4bfc8f6

+ 52 - 0
src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.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\Bundle\FrameworkBundle\Command;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Warmup the cache.
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class CacheWarmupCommand extends Command
+{
+    /**
+     * @see Command
+     */
+    protected function configure()
+    {
+        $this
+            ->setName('cache:warmup')
+            ->setDescription('Warms up an empty cache')
+            ->setHelp(<<<EOF
+The <info>cache:warmup</info> command warms up the cache.
+
+Before running this command, the cache must be empty.
+EOF
+            )
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $output->writeln('Warming up the cache');
+
+        $warmer = $this->container->get('cache_warmer');
+        $warmer->enableOptionalWarmers();
+        $warmer->warmUp($this->container->getParameter('kernel.cache_dir'));
+    }
+}

+ 45 - 0
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+/*
+ * 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.
+ */
+
+/**
+ * Registers the cache warmers.
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class AddCacheWarmerPass implements CompilerPassInterface
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition('cache_warmer')) {
+            return;
+        }
+
+        $warmers = array();
+        foreach ($container->findTaggedServiceIds('kernel.cache_warmer') as $id => $attributes) {
+            $warmers[] = new Reference($id);
+        }
+
+        $container->getDefinition('cache_warmer')->setArgument(0, $warmers);
+
+        if ('full' === $container->getParameter('kernel.cache_warmup')) {
+            $container->getDefinition('cache_warmer')->addMethodCall('enableOptionalWarmers', array());
+        }
+    }
+}

+ 7 - 1
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

@@ -157,6 +157,13 @@ class FrameworkExtension extends Extension
             $this->registerEsiConfiguration($config, $container);
             $this->registerEsiConfiguration($config, $container);
         }
         }
 
 
+        if (isset($config['cache-warmer'])) {
+            $config['cache_warmer'] = $config['cache-warmer'];
+        }
+
+        $warmer = isset($config['cache_warmer']) ? $config['cache_warmer'] : !$container->getParameter('kernel.debug');
+        $container->setParameter('kernel.cache_warmup', $warmer);
+
         $this->addClassesToCompile(array(
         $this->addClassesToCompile(array(
             'Symfony\\Component\\HttpFoundation\\ParameterBag',
             'Symfony\\Component\\HttpFoundation\\ParameterBag',
             'Symfony\\Component\\HttpFoundation\\HeaderBag',
             'Symfony\\Component\\HttpFoundation\\HeaderBag',
@@ -276,7 +283,6 @@ class FrameworkExtension extends Extension
 
 
         // compilation
         // compilation
         $this->addClassesToCompile(array(
         $this->addClassesToCompile(array(
-            'Symfony\\Component\\Templating\\DelegatingEngine',
             'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface',
             'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface',
             'Symfony\\Component\\Templating\\EngineInterface',
             'Symfony\\Component\\Templating\\EngineInterface',
         ));
         ));

+ 2 - 0
src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

@@ -21,6 +21,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverP
 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\TranslatorPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
+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;
@@ -82,6 +83,7 @@ class FrameworkBundle extends Bundle
         $container->addCompilerPass(new AddFieldFactoryGuessersPass());
         $container->addCompilerPass(new AddFieldFactoryGuessersPass());
         $container->addCompilerPass(new AddClassesToCachePass());
         $container->addCompilerPass(new AddClassesToCachePass());
         $container->addCompilerPass(new TranslatorPass());
         $container->addCompilerPass(new TranslatorPass());
+        $container->addCompilerPass(new AddCacheWarmerPass());
     }
     }
 
 
     /**
     /**

+ 9 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

@@ -23,8 +23,17 @@
         <xsd:attribute name="ide" type="xsd:string" />
         <xsd:attribute name="ide" type="xsd:string" />
         <xsd:attribute name="charset" type="xsd:string" />
         <xsd:attribute name="charset" type="xsd:string" />
         <xsd:attribute name="error-handler" type="xsd:string" />
         <xsd:attribute name="error-handler" type="xsd:string" />
+        <xsd:attribute name="cache-warmer" type="cache_warmer" />
     </xsd:complexType>
     </xsd:complexType>
 
 
+    <xsd:simpleType name="cache_warmer">
+        <xsd:restriction base="xsd:string">
+            <xsd:enumeration value="false" />
+            <xsd:enumeration value="true" />
+            <xsd:enumeration value="full" />
+        </xsd:restriction>
+    </xsd:simpleType>
+
     <xsd:complexType name="profiler">
     <xsd:complexType name="profiler">
         <xsd:all>
         <xsd:all>
             <xsd:element name="matcher" type="profiler_matcher" minOccurs="0" maxOccurs="1" />
             <xsd:element name="matcher" type="profiler_matcher" minOccurs="0" maxOccurs="1" />

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

@@ -11,6 +11,7 @@
         <parameter key="error_handler.class">Symfony\Component\HttpKernel\Debug\ErrorHandler</parameter>
         <parameter key="error_handler.class">Symfony\Component\HttpKernel\Debug\ErrorHandler</parameter>
         <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>
     </parameters>
     </parameters>
 
 
     <services>
     <services>
@@ -28,6 +29,10 @@
             <call method="setEventDispatcher"><argument type="service" id="event_dispatcher" /></call>
             <call method="setEventDispatcher"><argument type="service" id="event_dispatcher" /></call>
         </service>
         </service>
 
 
+        <service id="cache_warmer" class="%cache_warmer.class%">
+            <argument type="collection" />
+        </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

+ 31 - 0
src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php

@@ -0,0 +1,31 @@
+<?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\HttpKernel\CacheWarmer;
+
+/**
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.org>
+ */
+abstract class CacheWarmer implements CacheWarmerInterface
+{
+    protected function writeCacheFile($file, $content)
+    {
+        $tmpFile = tempnam(dirname($file), basename($file));
+        if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
+            chmod($file, 0644);
+
+            return;
+        }
+
+        throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
+    }
+}

+ 72 - 0
src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php

@@ -0,0 +1,72 @@
+<?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\HttpKernel\CacheWarmer;
+
+/**
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.org>
+ */
+class CacheWarmerAggregate implements CacheWarmerInterface
+{
+    protected $warmers;
+    protected $optionalsEnabled;
+
+    public function __construct(array $warmers = array())
+    {
+        $this->setWarmers($warmers);
+        $this->optionalsEnabled = false;
+    }
+
+    public function enableOptionalWarmers()
+    {
+        $this->optionalsEnabled = true;
+    }
+
+    /**
+     * Warms up the cache.
+     *
+     * @param string $cacheDir The cache directory
+     */
+    public function warmUp($cacheDir)
+    {
+        foreach ($this->warmers as $warmer) {
+            if (!$this->optionalsEnabled && $warmer->isOptional()) {
+                continue;
+            }
+
+            $warmer->warmUp($cacheDir);
+        }
+    }
+
+    /**
+     * Checks whether this warmer is optional or not.
+     *
+     * @return Boolean always true
+     */
+    public function isOptional()
+    {
+        return false;
+    }
+
+    public function setWarmers(array $warmers)
+    {
+        $this->warmers = array();
+        foreach ($warmers as $warmer) {
+            $this->add($warmer);
+        }
+    }
+
+    public function add(CacheWarmerInterface $warmer)
+    {
+        $this->warmers[] = $warmer;
+    }
+}

+ 38 - 0
src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php

@@ -0,0 +1,38 @@
+<?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\HttpKernel\CacheWarmer;
+
+/**
+ *
+ * @author Fabien Potencier <fabien.potencier@symfony-project.org>
+ */
+interface CacheWarmerInterface
+{
+    /**
+     * Warms up the cache.
+     *
+     * @param string $cacheDir The cache directory
+     */
+    function warmUp($cacheDir);
+
+    /**
+     * Checks whether this warmer is optional or not.
+     *
+     * Optional warmers can be ignored on certain conditions.
+     *
+     * A warmer should return true if the cache can be
+     * generated incrementally and on-demand.
+     *
+     * @return Boolean true if the warmer is optional, false otherwise
+     */
+    function isOptional();
+}

+ 7 - 0
src/Symfony/Component/HttpKernel/Kernel.php

@@ -427,15 +427,22 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
         $location = $this->getCacheDir().'/'.$class;
         $location = $this->getCacheDir().'/'.$class;
         $reload = $this->debug ? $this->needsReload($class, $location) : false;
         $reload = $this->debug ? $this->needsReload($class, $location) : false;
 
 
+        $fresh = false;
         if ($reload || !file_exists($location.'.php')) {
         if ($reload || !file_exists($location.'.php')) {
             $container = $this->buildContainer();
             $container = $this->buildContainer();
             $this->dumpContainer($container, $class, $location.'.php');
             $this->dumpContainer($container, $class, $location.'.php');
+
+            $fresh = true;
         }
         }
 
 
         require_once $location.'.php';
         require_once $location.'.php';
 
 
         $this->container = new $class();
         $this->container = new $class();
         $this->container->set('kernel', $this);
         $this->container->set('kernel', $this);
+
+        if ($fresh && 'cli' !== php_sapi_name()) {
+            $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
+        }
     }
     }
 
 
     public function getKernelParameters()
     public function getKernelParameters()