[FrameworkBundle] fix ValidatorCacheWarmer: use serializing ArrayAdapter

This commit is contained in:
David Maicher 2017-07-17 22:35:32 +02:00 committed by Fabien Potencier
parent 95c03f2fb3
commit c0556cb204
4 changed files with 142 additions and 144 deletions

View File

@ -0,0 +1,92 @@
<?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\Bundle\FrameworkBundle\CacheWarmer;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
/**
* @internal
*/
abstract class AbstractPhpFileCacheWarmer implements CacheWarmerInterface
{
private $phpArrayFile;
private $fallbackPool;
/**
* @param string $phpArrayFile The PHP file where metadata are cached
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached
*/
public function __construct($phpArrayFile, CacheItemPoolInterface $fallbackPool)
{
$this->phpArrayFile = $phpArrayFile;
if (!$fallbackPool instanceof AdapterInterface) {
$fallbackPool = new ProxyAdapter($fallbackPool);
}
$this->fallbackPool = $fallbackPool;
}
/**
* {@inheritdoc}
*/
public function isOptional()
{
return true;
}
/**
* {@inheritdoc}
*/
public function warmUp($cacheDir)
{
$arrayAdapter = new ArrayAdapter();
spl_autoload_register(array(PhpArrayAdapter::class, 'throwOnRequiredClass'));
try {
if (!$this->doWarmUp($cacheDir, $arrayAdapter)) {
return;
}
} finally {
spl_autoload_unregister(array(PhpArrayAdapter::class, 'throwOnRequiredClass'));
}
// the ArrayAdapter stores the values serialized
// to avoid mutation of the data after it was written to the cache
// 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, $this->fallbackPool), $values);
foreach ($values as $k => $v) {
$item = $this->fallbackPool->getItem($k);
$this->fallbackPool->saveDeferred($item->set($v));
}
$this->fallbackPool->commit();
}
protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values)
{
$phpArrayAdapter->warmUp($values);
}
/**
* @param string $cacheDir
* @param ArrayAdapter $arrayAdapter
*
* @return bool false if there is nothing to warm-up
*/
abstract protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter);
}

View File

@ -15,12 +15,8 @@ use Doctrine\Common\Annotations\AnnotationException;
use Doctrine\Common\Annotations\CachedReader; use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Annotations\Reader;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\DoctrineProvider; use Symfony\Component\Cache\DoctrineProvider;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
/** /**
* Warms up annotation caches for classes found in composer's autoload class map * Warms up annotation caches for classes found in composer's autoload class map
@ -28,11 +24,9 @@ use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
* *
* @author Titouan Galopin <galopintitouan@gmail.com> * @author Titouan Galopin <galopintitouan@gmail.com>
*/ */
class AnnotationsCacheWarmer implements CacheWarmerInterface class AnnotationsCacheWarmer extends AbstractPhpFileCacheWarmer
{ {
private $annotationReader; private $annotationReader;
private $phpArrayFile;
private $fallbackPool;
/** /**
* @param Reader $annotationReader * @param Reader $annotationReader
@ -41,70 +35,41 @@ class AnnotationsCacheWarmer implements CacheWarmerInterface
*/ */
public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool) public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
{ {
parent::__construct($phpArrayFile, $fallbackPool);
$this->annotationReader = $annotationReader; $this->annotationReader = $annotationReader;
$this->phpArrayFile = $phpArrayFile;
if (!$fallbackPool instanceof AdapterInterface) {
$fallbackPool = new ProxyAdapter($fallbackPool);
}
$this->fallbackPool = $fallbackPool;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function warmUp($cacheDir) protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
{ {
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool);
$annotatedClassPatterns = $cacheDir.'/annotations.map'; $annotatedClassPatterns = $cacheDir.'/annotations.map';
if (!is_file($annotatedClassPatterns)) { if (!is_file($annotatedClassPatterns)) {
$adapter->warmUp(array()); return true;
return;
} }
$annotatedClasses = include $annotatedClassPatterns; $annotatedClasses = include $annotatedClassPatterns;
$reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayAdapter));
$arrayPool = new ArrayAdapter(0, false); foreach ($annotatedClasses as $class) {
$reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayPool)); try {
$this->readAllComponents($reader, $class);
spl_autoload_register(array($adapter, 'throwOnRequiredClass')); } catch (\ReflectionException $e) {
try { // ignore failing reflection
foreach ($annotatedClasses as $class) { } catch (AnnotationException $e) {
try { /*
$this->readAllComponents($reader, $class); * Ignore any AnnotationException to not break the cache warming process if an Annotation is badly
} catch (\ReflectionException $e) { * configured or could not be found / read / etc.
// ignore failing reflection *
} catch (AnnotationException $e) { * In particular cases, an Annotation in your code can be used and defined only for a specific
/* * environment but is always added to the annotations.map file by some Symfony default behaviors,
* Ignore any AnnotationException to not break the cache warming process if an Annotation is badly * and you always end up with a not found Annotation.
* configured or could not be found / read / etc. */
*
* In particular cases, an Annotation in your code can be used and defined only for a specific
* environment but is always added to the annotations.map file by some Symfony default behaviors,
* and you always end up with a not found Annotation.
*/
}
} }
} finally {
spl_autoload_unregister(array($adapter, 'throwOnRequiredClass'));
} }
$values = $arrayPool->getValues();
$adapter->warmUp($values);
foreach ($values as $k => $v) {
$item = $this->fallbackPool->getItem($k);
$this->fallbackPool->saveDeferred($item->set($v));
}
$this->fallbackPool->commit();
}
/**
* {@inheritdoc}
*/
public function isOptional()
{
return true; return true;
} }

View File

@ -13,11 +13,7 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer;
use Doctrine\Common\Annotations\AnnotationException; use Doctrine\Common\Annotations\AnnotationException;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\LoaderChain; use Symfony\Component\Serializer\Mapping\Loader\LoaderChain;
@ -30,11 +26,9 @@ use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
* *
* @author Titouan Galopin <galopintitouan@gmail.com> * @author Titouan Galopin <galopintitouan@gmail.com>
*/ */
class SerializerCacheWarmer implements CacheWarmerInterface class SerializerCacheWarmer extends AbstractPhpFileCacheWarmer
{ {
private $loaders; private $loaders;
private $phpArrayFile;
private $fallbackPool;
/** /**
* @param LoaderInterface[] $loaders The serializer metadata loaders * @param LoaderInterface[] $loaders The serializer metadata loaders
@ -43,60 +37,33 @@ class SerializerCacheWarmer implements CacheWarmerInterface
*/ */
public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool) public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
{ {
parent::__construct($phpArrayFile, $fallbackPool);
$this->loaders = $loaders; $this->loaders = $loaders;
$this->phpArrayFile = $phpArrayFile;
if (!$fallbackPool instanceof AdapterInterface) {
$fallbackPool = new ProxyAdapter($fallbackPool);
}
$this->fallbackPool = $fallbackPool;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function warmUp($cacheDir) protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
{ {
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) { if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
return; return false;
} }
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool); $metadataFactory = new CacheClassMetadataFactory(new ClassMetadataFactory(new LoaderChain($this->loaders)), $arrayAdapter);
$arrayPool = new ArrayAdapter(0, false);
$metadataFactory = new CacheClassMetadataFactory(new ClassMetadataFactory(new LoaderChain($this->loaders)), $arrayPool); foreach ($this->extractSupportedLoaders($this->loaders) as $loader) {
foreach ($loader->getMappedClasses() as $mappedClass) {
spl_autoload_register(array($adapter, 'throwOnRequiredClass')); try {
try { $metadataFactory->getMetadataFor($mappedClass);
foreach ($this->extractSupportedLoaders($this->loaders) as $loader) { } catch (\ReflectionException $e) {
foreach ($loader->getMappedClasses() as $mappedClass) { // ignore failing reflection
try { } catch (AnnotationException $e) {
$metadataFactory->getMetadataFor($mappedClass); // ignore failing annotations
} catch (\ReflectionException $e) {
// ignore failing reflection
} catch (AnnotationException $e) {
// ignore failing annotations
}
} }
} }
} finally {
spl_autoload_unregister(array($adapter, 'throwOnRequiredClass'));
} }
$values = $arrayPool->getValues();
$adapter->warmUp($values);
foreach ($values as $k => $v) {
$item = $this->fallbackPool->getItem($k);
$this->fallbackPool->saveDeferred($item->set($v));
}
$this->fallbackPool->commit();
}
/**
* {@inheritdoc}
*/
public function isOptional()
{
return true; return true;
} }

View File

@ -13,11 +13,8 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer;
use Doctrine\Common\Annotations\AnnotationException; use Doctrine\Common\Annotations\AnnotationException;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter; use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\Validator\Mapping\Cache\Psr6Cache; use Symfony\Component\Validator\Mapping\Cache\Psr6Cache;
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
use Symfony\Component\Validator\Mapping\Loader\LoaderChain; use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
@ -31,11 +28,9 @@ use Symfony\Component\Validator\ValidatorBuilderInterface;
* *
* @author Titouan Galopin <galopintitouan@gmail.com> * @author Titouan Galopin <galopintitouan@gmail.com>
*/ */
class ValidatorCacheWarmer implements CacheWarmerInterface class ValidatorCacheWarmer extends AbstractPhpFileCacheWarmer
{ {
private $validatorBuilder; private $validatorBuilder;
private $phpArrayFile;
private $fallbackPool;
/** /**
* @param ValidatorBuilderInterface $validatorBuilder * @param ValidatorBuilderInterface $validatorBuilder
@ -44,64 +39,43 @@ class ValidatorCacheWarmer implements CacheWarmerInterface
*/ */
public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool) public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
{ {
parent::__construct($phpArrayFile, $fallbackPool);
$this->validatorBuilder = $validatorBuilder; $this->validatorBuilder = $validatorBuilder;
$this->phpArrayFile = $phpArrayFile;
if (!$fallbackPool instanceof AdapterInterface) {
$fallbackPool = new ProxyAdapter($fallbackPool);
}
$this->fallbackPool = $fallbackPool;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function warmUp($cacheDir) protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
{ {
if (!method_exists($this->validatorBuilder, 'getLoaders')) { if (!method_exists($this->validatorBuilder, 'getLoaders')) {
return; return false;
} }
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool);
$arrayPool = new ArrayAdapter(0, false);
$loaders = $this->validatorBuilder->getLoaders(); $loaders = $this->validatorBuilder->getLoaders();
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayPool)); $metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayAdapter));
spl_autoload_register(array($adapter, 'throwOnRequiredClass')); foreach ($this->extractSupportedLoaders($loaders) as $loader) {
try { foreach ($loader->getMappedClasses() as $mappedClass) {
foreach ($this->extractSupportedLoaders($loaders) as $loader) { try {
foreach ($loader->getMappedClasses() as $mappedClass) { if ($metadataFactory->hasMetadataFor($mappedClass)) {
try { $metadataFactory->getMetadataFor($mappedClass);
if ($metadataFactory->hasMetadataFor($mappedClass)) {
$metadataFactory->getMetadataFor($mappedClass);
}
} catch (\ReflectionException $e) {
// ignore failing reflection
} catch (AnnotationException $e) {
// ignore failing annotations
} }
} catch (\ReflectionException $e) {
// ignore failing reflection
} catch (AnnotationException $e) {
// ignore failing annotations
} }
} }
} finally {
spl_autoload_unregister(array($adapter, 'throwOnRequiredClass'));
} }
$values = $arrayPool->getValues(); return true;
$adapter->warmUp(array_filter($values));
foreach ($values as $k => $v) {
$item = $this->fallbackPool->getItem($k);
$this->fallbackPool->saveDeferred($item->set($v));
}
$this->fallbackPool->commit();
} }
/** protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values)
* {@inheritdoc}
*/
public function isOptional()
{ {
return true; // make sure we don't cache null values
parent::warmUpPhpArrayAdapter($phpArrayAdapter, array_filter($values));
} }
/** /**