-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[FrameworkBundle] Introduce a cache warmer for Validator based on PhpArrayAdapter #19485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
<?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; | ||
use Symfony\Component\Validator\Mapping\Cache\Psr6Cache; | ||
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; | ||
use Symfony\Component\Validator\Mapping\Loader\LoaderChain; | ||
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface; | ||
use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader; | ||
use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader; | ||
use Symfony\Component\Validator\ValidatorBuilderInterface; | ||
|
||
/** | ||
* Warms up XML and YAML validator metadata. | ||
* | ||
* @author Titouan Galopin <galopintitouan@gmail.com> | ||
*/ | ||
class ValidatorCacheWarmer implements CacheWarmerInterface | ||
{ | ||
private $validatorBuilder; | ||
private $phpArrayFile; | ||
private $fallbackPool; | ||
|
||
/** | ||
* @param ValidatorBuilderInterface $validatorBuilder | ||
* @param string $phpArrayFile The PHP file where metadata are cached. | ||
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached. | ||
*/ | ||
public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only have a last suggestion: shouldn't we inject an instance of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to consider all potential factorizations in the next PR we're talking about just above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok fine for me then :) |
||
{ | ||
$this->validatorBuilder = $validatorBuilder; | ||
$this->phpArrayFile = $phpArrayFile; | ||
if (!$fallbackPool instanceof AdapterInterface) { | ||
$fallbackPool = new ProxyAdapter($fallbackPool); | ||
} | ||
$this->fallbackPool = $fallbackPool; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function warmUp($cacheDir) | ||
{ | ||
if (!method_exists($this->validatorBuilder, 'getLoaders')) { | ||
return; | ||
} | ||
|
||
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool); | ||
$arrayPool = new ArrayAdapter(0, false); | ||
|
||
$loaders = $this->validatorBuilder->getLoaders(); | ||
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayPool)); | ||
|
||
foreach ($this->extractSupportedLoaders($loaders) as $loader) { | ||
foreach ($loader->getMappedClasses() as $mappedClass) { | ||
$metadataFactory->getMetadataFor($mappedClass); | ||
} | ||
} | ||
|
||
$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; | ||
} | ||
|
||
/** | ||
* @param LoaderInterface[] $loaders | ||
* | ||
* @return XmlFileLoader[]|YamlFileLoader[] | ||
*/ | ||
private function extractSupportedLoaders(array $loaders) | ||
{ | ||
$supportedLoaders = array(); | ||
|
||
foreach ($loaders as $loader) { | ||
if ($loader instanceof XmlFileLoader || $loader instanceof YamlFileLoader) { | ||
$supportedLoaders[] = $loader; | ||
} elseif ($loader instanceof LoaderChain) { | ||
$supportedLoaders = array_merge($supportedLoaders, $this->extractSupportedLoaders($loader->getDelegatedLoaders())); | ||
} | ||
} | ||
|
||
return $supportedLoaders; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?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\Tests\CacheWarmer; | ||
|
||
use Symfony\Bundle\FrameworkBundle\CacheWarmer\ValidatorCacheWarmer; | ||
use Symfony\Bundle\FrameworkBundle\Tests\TestCase; | ||
use Symfony\Component\Cache\Adapter\ArrayAdapter; | ||
use Symfony\Component\Validator\ValidatorBuilder; | ||
|
||
class ValidatorCacheWarmerTest extends TestCase | ||
{ | ||
public function testWarmUp() | ||
{ | ||
$validatorBuilder = new ValidatorBuilder(); | ||
$validatorBuilder->addXmlMapping(__DIR__.'/../Fixtures/Validation/Resources/person.xml'); | ||
$validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/author.yml'); | ||
$validatorBuilder->addMethodMapping('loadValidatorMetadata'); | ||
$validatorBuilder->enableAnnotationMapping(); | ||
|
||
$file = sys_get_temp_dir().'/cache-validator.php'; | ||
@unlink($file); | ||
|
||
$fallbackPool = new ArrayAdapter(); | ||
|
||
$warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool); | ||
$warmer->warmUp(dirname($file)); | ||
|
||
$this->assertFileExists($file); | ||
|
||
$values = require $file; | ||
|
||
$this->assertInternalType('array', $values); | ||
$this->assertCount(2, $values); | ||
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Person', $values); | ||
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author', $values); | ||
|
||
$values = $fallbackPool->getValues(); | ||
|
||
$this->assertInternalType('array', $values); | ||
$this->assertCount(2, $values); | ||
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Person', $values); | ||
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author', $values); | ||
} | ||
|
||
public function testWarmUpWithoutLoader() | ||
{ | ||
$validatorBuilder = new ValidatorBuilder(); | ||
|
||
$file = sys_get_temp_dir().'/cache-validator-without-loaders.php'; | ||
@unlink($file); | ||
|
||
$fallbackPool = new ArrayAdapter(); | ||
|
||
$warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool); | ||
$warmer->warmUp(dirname($file)); | ||
|
||
$this->assertFileExists($file); | ||
|
||
$values = require $file; | ||
|
||
$this->assertInternalType('array', $values); | ||
$this->assertCount(0, $values); | ||
|
||
$values = $fallbackPool->getValues(); | ||
|
||
$this->assertInternalType('array', $values); | ||
$this->assertCount(0, $values); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation; | ||
|
||
class Author | ||
{ | ||
public $gender; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation; | ||
|
||
class Person | ||
{ | ||
public $gender; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation\Author: | ||
properties: | ||
gender: | ||
- Choice: { choices: [male, female, other], message: Choose a valid gender. } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> | ||
|
||
<class name="Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation\Person"> | ||
<property name="gender"> | ||
<constraint name="Choice"> | ||
<option name="choices"> | ||
<value>male</value> | ||
<value>female</value> | ||
<value>other</value> | ||
</option> | ||
<option name="message">Choose a valid gender.</option> | ||
</constraint> | ||
</property> | ||
</class> | ||
</constraint-mapping> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cache warmer is very similar to the one you proposed for the serializer, can't we create a new abstract class ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if it seems similar, there is only a few lines that can be shared. I don't think it's worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be used in third party libraries and promote cache warmers by making them much easier to use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's discuss about this in another PR: it's not directly related to this PR and would deserve a dedicated line in the changelog.