Skip to content

Commit

Permalink
[FrameworkBundle] Introduce a cache warmer for Validator based on Php…
Browse files Browse the repository at this point in the history
…ArrayAdapter
  • Loading branch information
tgalopin committed Aug 1, 2016
1 parent 983b560 commit 522a48a
Show file tree
Hide file tree
Showing 18 changed files with 349 additions and 76 deletions.
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)
{
$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
Expand Up @@ -567,7 +567,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode)
->info('validation configuration')
->canBeEnabled()
->children()
->scalarNode('cache')->defaultValue('validator.mapping.cache.symfony')->end()
->scalarNode('cache')->end()
->booleanNode('enable_annotations')->defaultFalse()->end()
->arrayNode('static_method')
->defaultValue(array('loadValidatorMetadata'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,13 +858,17 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
}
}

if (!$container->getParameter('kernel.debug')) {
if (isset($config['cache']) && $config['cache']) {
@trigger_error('The "framework.validation.cache" option is deprecated since Symfony 3.2 and will be removed in 4.0. Configure the "cache.validator" service under "framework.cache.pools" instead.', E_USER_DEPRECATED);

$container->setParameter(
'validator.mapping.cache.prefix',
'validator_'.$this->getKernelRootHash($container)
);

$validatorBuilder->addMethodCall('setMetadataCache', array(new Reference($config['cache'])));
} elseif (!$container->getParameter('kernel.debug')) {
$validatorBuilder->addMethodCall('setMetadataCache', array(new Reference('validator.mapping.cache.symfony')));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<parameters>
<parameter key="validator.mapping.cache.prefix" />
<parameter key="validator.mapping.cache.file">%kernel.cache_dir%/validation.php</parameter>
</parameters>

<services>
Expand All @@ -28,8 +29,21 @@

<service id="validator.mapping.class_metadata_factory" alias="validator" public="false" />

<service id="validator.mapping.cache.symfony" class="Symfony\Component\Validator\Mapping\Cache\Psr6Cache" public="false">
<service id="validator.mapping.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\ValidatorCacheWarmer" public="false">
<argument type="service" id="validator.builder" />
<argument>%validator.mapping.cache.file%</argument>
<argument type="service" id="cache.validator" />
<tag name="kernel.cache_warmer" />
</service>

<service id="validator.mapping.cache.symfony" class="Symfony\Component\Validator\Mapping\Cache\Psr6Cache" public="false">
<argument type="service">
<service class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
<argument>%validator.mapping.cache.file%</argument>
<argument type="service" id="cache.validator" />
</service>
</argument>
</service>

<service id="validator.mapping.cache.doctrine.apc" class="Symfony\Component\Validator\Mapping\Cache\DoctrineCache" public="false">
Expand Down
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.Person', $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.Person', $values);
}

public function testWarmUpWithoutLoader()
{
$validatorBuilder = new ValidatorBuilder();

$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(0, $values);

$values = $fallbackPool->getValues();

$this->assertInternalType('array', $values);
$this->assertCount(0, $values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ protected static function getBundleDefaultConfig()
'static_method' => array('loadValidatorMetadata'),
'translation_domain' => 'validators',
'strict_email' => false,
'cache' => 'validator.mapping.cache.symfony',
),
'annotations' => array(
'cache' => 'php_array',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
),
'validation' => array(
'enabled' => true,
'cache' => 'validator.mapping.cache.doctrine.apc',
),
'annotations' => array(
'cache' => 'file',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<framework:translator enabled="true" fallback="fr" logging="true">
<framework:path>%kernel.root_dir%/Fixtures/translations</framework:path>
</framework:translator>
<framework:validation enabled="true" cache="validator.mapping.cache.doctrine.apc" />
<framework:validation enabled="true" />
<framework:annotations cache="file" debug="true" file-cache-dir="%kernel.cache_dir%/annotations" />
<framework:serializer enabled="true" enable-annotations="true" name-converter="serializer.name_converter.camel_case_to_snake_case" />
<framework:property-info />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ framework:
paths: ['%kernel.root_dir%/Fixtures/translations']
validation:
enabled: true
cache: validator.mapping.cache.doctrine.apc
annotations:
cache: file
debug: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ public function testValidation()
$this->assertSame('addMethodMapping', $calls[4][0]);
$this->assertSame(array('loadValidatorMetadata'), $calls[4][1]);
$this->assertSame('setMetadataCache', $calls[5][0]);
$this->assertEquals(array(new Reference('validator.mapping.cache.doctrine.apc')), $calls[5][1]);
$this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[5][1]);
}

public function testValidationService()
Expand Down
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>
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"symfony/expression-language": "~2.8|~3.0",
"symfony/process": "~2.8|~3.0",
"symfony/serializer": "~2.8|^3.0",
"symfony/validator": "~3.1",
"symfony/validator": "~3.2",
"symfony/yaml": "~3.2",
"symfony/property-info": "~2.8|~3.0",
"phpdocumentor/reflection-docblock": "^3.0",
Expand Down
Loading

0 comments on commit 522a48a

Please sign in to comment.