Skip to content

Commit

Permalink
[HttpKernel] allow cache warmers to add to the list of preloaded clas…
Browse files Browse the repository at this point in the history
…ses and files
  • Loading branch information
nicolas-grekas committed Mar 25, 2020
1 parent 4dabd00 commit ea52a40
Show file tree
Hide file tree
Showing 24 changed files with 146 additions and 26 deletions.
6 changes: 6 additions & 0 deletions UPGRADE-5.1.md
Expand Up @@ -39,6 +39,12 @@ HttpFoundation
`__construct()` instead)
* Made the Mime component an optional dependency

HttpKernel
----------

* Made `WarmableInterface::warmUp()` return a list of classes or files to preload on PHP 7.4+
not returning an array is deprecated

Mailer
------

Expand Down
5 changes: 5 additions & 0 deletions UPGRADE-6.0.md
Expand Up @@ -36,6 +36,11 @@ HttpFoundation
`RedirectResponse::create()`, and `StreamedResponse::create()` methods (use
`__construct()` instead)

HttpKernel
----------

* Made `WarmableInterface::warmUp()` return a list of classes or files to preload on PHP 7.4+

Messenger
---------

Expand Down
11 changes: 11 additions & 0 deletions src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php
Expand Up @@ -43,9 +43,12 @@ public function isOptional()

/**
* {@inheritdoc}
*
* @return string[] A list of files to preload on PHP 7.4+
*/
public function warmUp(string $cacheDir)
{
$files = [];
foreach ($this->registry->getManagers() as $em) {
// we need the directory no matter the proxy cache generation strategy
if (!is_dir($proxyCacheDir = $em->getConfiguration()->getProxyDir())) {
Expand All @@ -64,6 +67,14 @@ public function warmUp(string $cacheDir)
$classes = $em->getMetadataFactory()->getAllMetadata();

$em->getProxyFactory()->generateProxyClasses($classes);

foreach (scandir($proxyCacheDir) as $file) {
if (!is_dir($file = $proxyCacheDir.'/'.$file)) {
$files[] = $file;
}
}
}

return $files;
}
}
Expand Up @@ -42,6 +42,8 @@ public function isOptional()

/**
* {@inheritdoc}
*
* @return string[] A list of classes to preload on PHP 7.4+
*/
public function warmUp(string $cacheDir)
{
Expand All @@ -61,12 +63,15 @@ public function warmUp(string $cacheDir)
// so here we un-serialize the values first
$values = array_map(function ($val) { return null !== $val ? unserialize($val) : null; }, $arrayAdapter->getValues());

$this->warmUpPhpArrayAdapter(new PhpArrayAdapter($this->phpArrayFile, new NullAdapter()), $values);
return $this->warmUpPhpArrayAdapter(new PhpArrayAdapter($this->phpArrayFile, new NullAdapter()), $values);
}

/**
* @return string[] A list of classes to preload on PHP 7.4+
*/
protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values)
{
$phpArrayAdapter->warmUp($values);
return $phpArrayAdapter->warmUp($values);
}

/**
Expand Down
Expand Up @@ -36,14 +36,18 @@ public function __construct(Psr6CacheClearer $poolClearer, array $pools = [])

/**
* {@inheritdoc}
*
* @return string[]
*/
public function warmUp($cacheDirectory): void
public function warmUp($cacheDirectory): array
{
foreach ($this->pools as $pool) {
if ($this->poolClearer->hasPool($pool)) {
$this->poolClearer->clearPool($pool);
}
}

return [];
}

/**
Expand Down
Expand Up @@ -36,15 +36,15 @@ public function __construct(ContainerInterface $container)

/**
* {@inheritdoc}
*
* @return string[]
*/
public function warmUp(string $cacheDir)
{
$router = $this->container->get('router');

if ($router instanceof WarmableInterface) {
$router->warmUp($cacheDir);

return;
return $router->warmUp($cacheDir);
}

throw new \LogicException(sprintf('The router "%s" cannot be warmed up because it does not implement "%s".', get_debug_type($router), WarmableInterface::class));
Expand Down
Expand Up @@ -35,6 +35,8 @@ public function __construct(ContainerInterface $container)

/**
* {@inheritdoc}
*
* @return string[]
*/
public function warmUp(string $cacheDir)
{
Expand All @@ -43,8 +45,10 @@ public function warmUp(string $cacheDir)
}

if ($this->translator instanceof WarmableInterface) {
$this->translator->warmUp($cacheDir);
return $this->translator->warmUp($cacheDir);
}

return [];
}

/**
Expand Down
Expand Up @@ -68,10 +68,13 @@ protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter)
return true;
}

/**
* @return string[] A list of classes to preload on PHP 7.4+
*/
protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values)
{
// make sure we don't cache null values
parent::warmUpPhpArrayAdapter($phpArrayAdapter, array_filter($values));
return parent::warmUpPhpArrayAdapter($phpArrayAdapter, array_filter($values));
}

/**
Expand Down
Expand Up @@ -17,6 +17,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Dumper\Preloader;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
Expand Down Expand Up @@ -117,7 +118,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$warmer = $kernel->getContainer()->get('cache_warmer');
// non optional warmers already ran during container compilation
$warmer->enableOnlyOptionalWarmers();
$warmer->warmUp($realCacheDir);
$preload = (array) $warmer->warmUp($warmupDir);
Preloader::append($warmupDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php', $preload);
}
} else {
$fs->mkdir($warmupDir);
Expand Down Expand Up @@ -193,7 +195,8 @@ private function warmup(string $warmupDir, string $realCacheDir, bool $enableOpt
$warmer = $kernel->getContainer()->get('cache_warmer');
// non optional warmers already ran during container compilation
$warmer->enableOnlyOptionalWarmers();
$warmer->warmUp($warmupDir);
$preload = (array) $warmer->warmUp($warmupDir);
Preloader::append($warmupDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php', $preload);
}

// fix references to cached files with the real cache directory name
Expand Down
12 changes: 7 additions & 5 deletions src/Symfony/Bundle/FrameworkBundle/Routing/Router.php
Expand Up @@ -21,16 +21,11 @@
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Router as BaseRouter;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

// Help opcache.preload discover always-needed symbols
class_exists(RedirectableCompiledUrlMatcher::class);
class_exists(Route::class);

/**
* This Router creates the Loader only when the cache is empty.
*
Expand Down Expand Up @@ -90,6 +85,8 @@ public function getRouteCollection()

/**
* {@inheritdoc}
*
* @return string[] A list of classes to preload on PHP 7.4+
*/
public function warmUp(string $cacheDir)
{
Expand All @@ -101,6 +98,11 @@ public function warmUp(string $cacheDir)
$this->getGenerator();

$this->setOption('cache_dir', $currentDir);

return [
$this->getOption('generator_class'),
$this->getOption('matcher_class'),
];
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
Expand Up @@ -95,6 +95,8 @@ public function __construct(ContainerInterface $container, MessageFormatterInter

/**
* {@inheritdoc}
*
* @return string[]
*/
public function warmUp(string $cacheDir)
{
Expand All @@ -113,6 +115,8 @@ public function warmUp(string $cacheDir)

$this->loadCatalogue($locale);
}

return [];
}

public function addResource(string $format, $resource, string $locale, string $domain = null)
Expand Down
Expand Up @@ -34,10 +34,15 @@ public function isOptional()
return true;
}

/**
* @return string[]
*/
public function warmUp(string $cacheDir)
{
foreach ($this->expressions as $expression) {
$this->expressionLanguage->parse($expression, ['token', 'user', 'object', 'subject', 'roles', 'request', 'trust_resolver']);
}

return [];
}
}
Expand Up @@ -37,21 +37,31 @@ public function __construct(ContainerInterface $container, iterable $iterator)

/**
* {@inheritdoc}
*
* @return string[] A list of template files to preload on PHP 7.4+
*/
public function warmUp(string $cacheDir)
{
if (null === $this->twig) {
$this->twig = $this->container->get('twig');
}

$files = [];

foreach ($this->iterator as $template) {
try {
$this->twig->load($template);
$template = $this->twig->load($template);

if (\is_callable([$template, 'unwrap'])) {
$files[] = (new \ReflectionClass($template->unwrap()))->getFileName();
}
} catch (Error $e) {
// problem during compilation, give up
// might be a syntax error or a non-Twig template
}
}

return $files;
}

/**
Expand Down
7 changes: 6 additions & 1 deletion src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php
Expand Up @@ -291,6 +291,8 @@ public function clear(string $prefix = '')
* Store an array of cached values.
*
* @param array $values The cached values
*
* @return string[] A list of classes to preload on PHP 7.4+
*/
public function warmUp(array $values)
{
Expand All @@ -314,6 +316,7 @@ public function warmUp(array $values)
}
}

$preload = [];
$dumpedValues = '';
$dumpedMap = [];
$dump = <<<'EOF'
Expand All @@ -334,7 +337,7 @@ public function warmUp(array $values)
$value = "'N;'";
} elseif (\is_object($value) || \is_array($value)) {
try {
$value = VarExporter::export($value, $isStaticValue);
$value = VarExporter::export($value, $isStaticValue, $preload);
} catch (\Exception $e) {
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e);
}
Expand Down Expand Up @@ -376,6 +379,8 @@ public function warmUp(array $values)
unset(self::$valuesCache[$this->file]);

$this->initialize();

return $preload;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@ CHANGELOG
* added support to autowire public typed properties in php 7.4
* added support for defining method calls, a configurator, and property setters in `InlineServiceConfigurator`
* added possibility to define abstract service arguments
* added class `Symfony\Component\DependencyInjection\Dumper\Preloader` to help with preloading on PHP 7.4+

5.0.0
-----
Expand Down
27 changes: 23 additions & 4 deletions src/Symfony/Component/DependencyInjection/Dumper/Preloader.php
Expand Up @@ -13,12 +13,31 @@

/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class Preloader
final class Preloader
{
public static function preload(array $classes)
public function append(string $file, array $list): void
{
if (!file_exists($file)) {
throw new \LogicException(sprintf('File "%s" does not exist.', $file));
}

$cacheDir = \dirname($file);
$classes = [];

foreach ($list as $item) {
if (0 === strpos($item, $cacheDir)) {
file_put_contents($file, sprintf("require __DIR__.%s;\n", var_export(substr($item, \strlen($cacheDir)), true)), FILE_APPEND);
continue;
}

$classes[] = sprintf("\$classes[] = %s;\n", var_export($item, true));
}

file_put_contents($file, sprintf("\n\$classes = [];\n%sPreloader::preload(\$classes);\n", implode($classes)), FILE_APPEND);
}

public static function preload(array $classes): void
{
set_error_handler(function ($t, $m, $f, $l) {
if (error_reporting() & $t) {
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
Expand Up @@ -6,6 +6,8 @@ CHANGELOG

* allowed using public aliases to reference controllers
* added session usage reporting when the `_stateless` attribute of the request is set to `true`
* made `WarmableInterface::warmUp()` return a list of classes or files to preload on PHP 7.4+;
not returning an array is deprecated

5.0.0
-----
Expand Down

0 comments on commit ea52a40

Please sign in to comment.