[HttpKernel][FrameworkBundle] Add RebootableInterface, fix and un-deprecate cache:clear with warmup

This commit is contained in:
Nicolas Grekas 2017-08-04 14:41:57 +02:00
parent f751ac9ac1
commit a4fc49294e
6 changed files with 98 additions and 33 deletions

View File

@ -143,6 +143,9 @@ HttpKernel
tags: ['console.command'] tags: ['console.command']
``` ```
* The `getCacheDir()` method of your kernel should not be called while building the container.
Use the `%kernel.cache_dir%` parameter instead. Not doing so may break the `cache:clear` command.
Process Process
------- -------

View File

@ -510,6 +510,9 @@ HttpKernel
by Symfony. Use the `%env()%` syntax to get the value of any environment by Symfony. Use the `%env()%` syntax to get the value of any environment
variable from configuration files instead. variable from configuration files instead.
* The `getCacheDir()` method of your kernel should not be called while building the container.
Use the `%kernel.cache_dir%` parameter instead. Not doing so may break the `cache:clear` command.
Ldap Ldap
---- ----

View File

@ -19,6 +19,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\RebootableInterface;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
/** /**
@ -33,6 +34,7 @@ class CacheClearCommand extends ContainerAwareCommand
{ {
private $cacheClearer; private $cacheClearer;
private $filesystem; private $filesystem;
private $warning;
/** /**
* @param CacheClearerInterface $cacheClearer * @param CacheClearerInterface $cacheClearer
@ -112,6 +114,12 @@ EOF
$this->filesystem->rename($realCacheDir, $oldCacheDir); $this->filesystem->rename($realCacheDir, $oldCacheDir);
} else { } else {
$this->warmupCache($input, $output, $realCacheDir, $oldCacheDir); $this->warmupCache($input, $output, $realCacheDir, $oldCacheDir);
if ($this->warning) {
@trigger_error($this->warning, E_USER_DEPRECATED);
$io->warning($this->warning);
$this->warning = null;
}
} }
if ($output->isVerbose()) { if ($output->isVerbose()) {
@ -167,17 +175,23 @@ EOF
{ {
// create a temporary kernel // create a temporary kernel
$realKernel = $this->getApplication()->getKernel(); $realKernel = $this->getApplication()->getKernel();
$realKernelClass = get_class($realKernel); if ($realKernel instanceof RebootableInterface) {
$namespace = ''; $realKernel->reboot($warmupDir);
if (false !== $pos = strrpos($realKernelClass, '\\')) { $tempKernel = $realKernel;
$namespace = substr($realKernelClass, 0, $pos); } else {
$realKernelClass = substr($realKernelClass, $pos + 1); $this->warning = 'Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is deprecated since version 3.4 and will be unsupported in 4.0.';
} $realKernelClass = get_class($realKernel);
$tempKernel = $this->getTempKernel($realKernel, $namespace, $realKernelClass, $warmupDir); $namespace = '';
$tempKernel->boot(); if (false !== $pos = strrpos($realKernelClass, '\\')) {
$namespace = substr($realKernelClass, 0, $pos);
$realKernelClass = substr($realKernelClass, $pos + 1);
}
$tempKernel = $this->getTempKernel($realKernel, $namespace, $realKernelClass, $warmupDir);
$tempKernel->boot();
$tempKernelReflection = new \ReflectionObject($tempKernel); $tempKernelReflection = new \ReflectionObject($tempKernel);
$tempKernelFile = $tempKernelReflection->getFileName(); $tempKernelFile = $tempKernelReflection->getFileName();
}
// warmup temporary dir // warmup temporary dir
$warmer = $tempKernel->getContainer()->get('cache_warmer'); $warmer = $tempKernel->getContainer()->get('cache_warmer');
@ -186,6 +200,20 @@ EOF
} }
$warmer->warmUp($warmupDir); $warmer->warmUp($warmupDir);
// fix references to cached files with the real cache directory name
$search = array($warmupDir, str_replace('\\', '\\\\', $warmupDir));
$replace = str_replace('\\', '/', $realCacheDir);
foreach (Finder::create()->files()->in($warmupDir) as $file) {
$content = str_replace($search, $replace, file_get_contents($file), $count);
if ($count) {
file_put_contents($file, $content);
}
}
if ($realKernel instanceof RebootableInterface) {
return;
}
// fix references to the Kernel in .meta files // fix references to the Kernel in .meta files
$safeTempKernel = str_replace('\\', '\\\\', get_class($tempKernel)); $safeTempKernel = str_replace('\\', '\\\\', get_class($tempKernel));
$realKernelFQN = get_class($realKernel); $realKernelFQN = get_class($realKernel);
@ -198,16 +226,6 @@ EOF
)); ));
} }
// fix references to cached files with the real cache directory name
$search = array($warmupDir, str_replace('\\', '\\\\', $warmupDir));
$replace = str_replace('\\', '/', $realCacheDir);
foreach (Finder::create()->files()->in($warmupDir) as $file) {
$content = str_replace($search, $replace, file_get_contents($file), $count);
if ($count) {
file_put_contents($file, $content);
}
}
// fix references to container's class // fix references to container's class
$tempContainerClass = $tempKernel->getContainerClass(); $tempContainerClass = $tempKernel->getContainerClass();
$realContainerClass = $tempKernel->getRealContainerClass(); $realContainerClass = $tempKernel->getRealContainerClass();

View File

@ -4,6 +4,7 @@ CHANGELOG
3.4.0 3.4.0
----- -----
* added `RebootableInterface` and implemented it in `Kernel`
* deprecated commands auto registration * deprecated commands auto registration
* added `AddCacheClearerPass` * added `AddCacheClearerPass`
* added `AddCacheWarmerPass` * added `AddCacheWarmerPass`

View File

@ -43,7 +43,7 @@ use Symfony\Component\ClassLoader\ClassCollectionLoader;
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
abstract class Kernel implements KernelInterface, TerminableInterface abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface
{ {
/** /**
* @var BundleInterface[] * @var BundleInterface[]
@ -61,6 +61,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $loadClassCache; protected $loadClassCache;
private $projectDir; private $projectDir;
private $warmupDir;
const VERSION = '3.4.0-DEV'; const VERSION = '3.4.0-DEV';
const VERSION_ID = 30400; const VERSION_ID = 30400;
@ -127,6 +128,16 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$this->booted = true; $this->booted = true;
} }
/**
* {@inheritdoc}
*/
public function reboot($warmupDir)
{
$this->shutdown();
$this->warmupDir = $warmupDir;
$this->boot();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -373,7 +384,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
@trigger_error(__METHOD__.'() is deprecated since version 3.3, to be removed in 4.0.', E_USER_DEPRECATED); @trigger_error(__METHOD__.'() is deprecated since version 3.3, to be removed in 4.0.', E_USER_DEPRECATED);
} }
file_put_contents($this->getCacheDir().'/classes.map', sprintf('<?php return %s;', var_export($classes, true))); file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/classes.map', sprintf('<?php return %s;', var_export($classes, true)));
} }
/** /**
@ -381,7 +392,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/ */
public function setAnnotatedClassCache(array $annotatedClasses) public function setAnnotatedClassCache(array $annotatedClasses)
{ {
file_put_contents($this->getCacheDir().'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true))); file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
} }
/** /**
@ -424,9 +435,10 @@ abstract class Kernel implements KernelInterface, TerminableInterface
if (\PHP_VERSION_ID >= 70000) { if (\PHP_VERSION_ID >= 70000) {
@trigger_error(__METHOD__.'() is deprecated since version 3.3, to be removed in 4.0.', E_USER_DEPRECATED); @trigger_error(__METHOD__.'() is deprecated since version 3.3, to be removed in 4.0.', E_USER_DEPRECATED);
} }
$cacheDir = $this->warmupDir ?: $this->getCacheDir();
if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) { if (!$this->booted && is_file($cacheDir.'/classes.map')) {
ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension); ClassCollectionLoader::load(include($cacheDir.'/classes.map'), $cacheDir, $name, $this->debug, false, $extension);
} }
} }
@ -536,7 +548,8 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected function initializeContainer() protected function initializeContainer()
{ {
$class = $this->getContainerClass(); $class = $this->getContainerClass();
$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); $cacheDir = $this->warmupDir ?: $this->getCacheDir();
$cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug);
$fresh = true; $fresh = true;
if (!$cache->isFresh()) { if (!$cache->isFresh()) {
if ($this->debug) { if ($this->debug) {
@ -580,8 +593,8 @@ abstract class Kernel implements KernelInterface, TerminableInterface
if ($this->debug) { if ($this->debug) {
restore_error_handler(); restore_error_handler();
file_put_contents($this->getCacheDir().'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
} }
} }
@ -636,7 +649,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
'kernel.environment' => $this->environment, 'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug, 'kernel.debug' => $this->debug,
'kernel.name' => $this->name, 'kernel.name' => $this->name,
'kernel.cache_dir' => realpath($this->getCacheDir()) ?: $this->getCacheDir(), 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir,
'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(),
'kernel.bundles' => $bundles, 'kernel.bundles' => $bundles,
'kernel.bundles_metadata' => $bundlesMetadata, 'kernel.bundles_metadata' => $bundlesMetadata,
@ -682,7 +695,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/ */
protected function buildContainer() protected function buildContainer()
{ {
foreach (array('cache' => $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { foreach (array('cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) {
if (!is_dir($dir)) { if (!is_dir($dir)) {
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir));
@ -786,9 +799,6 @@ abstract class Kernel implements KernelInterface, TerminableInterface
@chmod($dir.$file, 0666 & ~umask()); @chmod($dir.$file, 0666 & ~umask());
} }
// track changes made to the container directory
$container->fileExists(dirname($dir.$file));
$cache->write($rootCode, $container->getResources()); $cache->write($rootCode, $container->getResources());
} }

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of 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.
*/
namespace Symfony\Component\HttpKernel;
/**
* Allows the Kernel to be rebooted using a temporary cache directory.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface RebootableInterface
{
/**
* Reboots a kernel.
*
* The getCacheDir() method of a rebootable kernel should not be called
* while building the container. Use the %kernel.cache_dir% parameter instead.
*
* @param string|null $warmupDir pass null to reboot in the regular cache directory
*/
public function reboot($warmupDir);
}