8000 [FrameworkBundle] Introduce a cache warmer for Validator based on Php… · symfony/symfony@90616f5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 90616f5

Browse files
committed
[FrameworkBundle] Introduce a cache warmer for Validator based on PhpArrayAdapter
1 parent 983b560 commit 90616f5

File tree

9 files changed

+385
-38
lines changed

9 files changed

+385
-38
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\CacheWarmer;
13+
14+
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Bundle\FrameworkBundle\Validator\Loader\OverEagerFileLoaderInterface;
16+
use Symfony\Bundle\FrameworkBundle\Validator\Loader\OverEagerXmlFileLoader;
17+
use Symfony\Bundle\FrameworkBundle\Validator\Loader\OverEagerYamlFileLoader;
18+
use Symfony\Bundle\FrameworkBundle\Validator\ValidatorMappingFilesFinder;
19+
use Symfony\Component\Cache\Adapter\AdapterInterface;
20+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
21+
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
22+
use Symfony\Component\Cache\Adapter\ProxyAdapter;
23+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
24+
use Symfony\Component\Validator\Mapping\Cache\Psr6Cache;
25+
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
26+
use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
27+
28+
/**
29+
* Warms up XML and YAML validator metadata.
30+
*
31+
* @author Titouan Galopin <galopintitouan@gmail.com>
32+
*/
33+
class ValidatorCacheWarmer implements CacheWarmerInterface
34+
{
35+
private $bundles;
36+
private $phpArrayFile;
37+
private $fallbackPool;
38+
39+
/**
40+
* @param array $bundles The application bundles to find validation metadata in.
41+
* @param string $phpArrayFile The PHP file where metadata are cached.
42+
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached.
43+
*/
44+
public function __construct(array $bundles, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
45+
{
46+
$this->bundles = $bundles;
47+
$this->phpArrayFile = $phpArrayFile;
48+
if (!$fallbackPool instanceof AdapterInterface) {
49+
$fallbackPool = new ProxyAdapter($fallbackPool);
50+
}
51+
$this->fallbackPool = $fallbackPool;
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function warmUp($cacheDir)
58+
{
59+
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool);
60+
61+
$mappingFinder = new ValidatorMappingFilesFinder();
62+
$mappingFiles = $mappingFinder->getValidatorMappingFiles($this->bundles);
63+
64+
$arrayPool = new ArrayAdapter(0, false);
65+
66+
$metadataLoaders = $this->createMetadataLoaders($mappingFiles);
67+
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($metadataLoaders), new Psr6Cache($arrayPool));
68+
69+
foreach ($metadataLoaders as $metadataLoader) {
70+
foreach ($metadataLoader->getClasses() as $class) {
71+
$metadataFactory->getMetadataFor($class);
72+
}
73+
}
74+
75+
$values = $arrayPool->getValues();
76+
$adapter->warmUp($values);
77+
78+
foreach ($values as $k => $v) {
79+
$item = $this->fallbackPool->getItem($k);
80+
$this->fallbackPool->saveDeferred($item->set($v));
81+
}
82+
$this->fallbackPool->commit();
83+
}
84+
85+
/**
86+
* {@inheritdoc}
87+
*/
88+
public function isOptional()
89+
{
90+
return true;
91+
}
92+
93+
/**
94+
* @param array $mappingFiles
95+
*
96+
* @return OverEagerFileLoaderInterface[]
97+
*/
98+
private function createMetadataLoaders($mappingFiles)
99+
{
100+
$loaders = array();
101+
102+
if (count($mappingFiles['xml']) > 0) {
103+
foreach ($mappingFiles['xml'] as $xmlFile) {
104+
$loaders[] = new OverEagerXmlFileLoader($xmlFile);
105+
}
106+
}
107+
108+
if (count($mappingFiles['yml']) > 0) {
109+
foreach ($mappingFiles['yml'] as $ymlFile) {
110+
$loaders[] = new OverEagerYamlFileLoader($ymlFile);
111+
}
112+
}
113+
114+
return $loaders;
115+
}
116+
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode)
567567
->info('validation configuration')
568568
->canBeEnabled()
569569
->children()
570-
->scalarNode('cache')->defaultValue('validator.mapping.cache.symfony')->end()
570+
->scalarNode('cache')->end()
571571
->booleanNode('enable_annotations')->defaultFalse()->end()
572572
->arrayNode('static_method')
573573
->defaultValue(array('loadValidatorMetadata'))

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
1313

1414
use Doctrine\Common\Annotations\Reader;
15+
use Symfony\Bundle\FrameworkBundle\Validator\ValidatorMappingFilesFinder;
1516
use Symfony\Component\Cache\Adapter\AdapterInterface;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -836,13 +837,15 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
836837

837838
$container->setParameter('validator.translation_domain', $config['translation_domain']);
838839

839-
list($xmlMappings, $yamlMappings) = $this->getValidatorMappingFiles($container);
840-
if (count($xmlMappings) > 0) {
841-
$validatorBuilder->addMethodCall('addXmlMappings', array($xmlMappings));
842-
}
840+
if ($container->getParameter('kernel.debug') || !file_exists($container->getParameter('validator.mapping.cache.file'))) {
841+
$mappingFiles = $this->getValidatorMappingFiles($container);
842+
if (count($mappingFiles['xml']) > 0) {
843+
$validatorBuilder->addMethodCall('addXmlMappings', array($mappingFiles['xml']));
844+
}
843845

844-
if (count($yamlMappings) > 0) {
845-
$validatorBuilder->addMethodCall('addYamlMappings', array($yamlMappings));
846+
if (count($mappingFiles['yml']) > 0) {
847+
$validatorBuilder->addMethodCall('addYamlMappings', array($mappingFiles['yml']));
848+
}
846849
}
847850

848851
$definition = $container->findDefinition('validator.email');
@@ -858,54 +861,41 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
858861
}
859862
}
860863

861-
if (!$container->getParameter('kernel.debug')) {
864+
if (isset($config['cache']) && $config['cache']) {
865+
@trigger_error('The "framework.validator.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);
866+
862867
$container->setParameter(
863868
'validator.mapping.cache.prefix',
864869
'validator_'.$this->getKernelRootHash($container)
865870
);
866871

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

871878
private function getValidatorMappingFiles(ContainerBuilder $container)
872879
{
873-
$files = array(array(), array());
874-
875-
if (interface_exists('Symfony\Component\Form\FormInterface')) {
876-
$reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface');
877-
$files[0][] = dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
878-
$container->addResource(new FileResource($files[0][0]));
879-
}
880-
880+
$mappingFinder = new ValidatorMappingFilesFinder();
881881
$bundles = $container->getParameter('kernel.bundles');
882-
foreach ($bundles as $bundle) {
883-
$reflection = new \ReflectionClass($bundle);
884-
$dirname = dirname($reflection->getFileName());
882+
$mappingFiles = $mappingFinder->getValidatorMappingFiles($bundles);
885883

886-
if (is_file($file = $dirname. 10000 '/Resources/config/validation.xml')) {
887-
$files[0][] = realpath($file);
888-
$container->addResource(new FileResource($file));
889-
}
884+
foreach ($mappingFiles as $type => $files) {
885+
if ($type === 'directory') {
886+
foreach ($files as $dir) {
887+
$container->addResource(new DirectoryResource($dir));
888+
}
890889

891-
if (is_file($file = $dirname.'/Resources/config/validation.yml')) {
892-
$files[1][] = realpath($file);
893-
$container->addResource(new FileResource($file));
890+
continue;
894891
}
895892

896-
if (is_dir($dir = $dirname.'/Resources/config/validation')) {
897-
foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) {
898-
$files[0][] = $file->getRealPath();
899-
}
900-
foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) {
901-
$files[1][] = $file->getRealPath();
902-
}
903-
904-
$container->addResource(new DirectoryResource($dir));
893+
foreach ($files as $file) {
894+
$container->addResource(new FileResource($file));
905895
}
906896
}
907897

908-
return $files;
898+
return $mappingFiles;
909899
}
910900

911901
private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, $loader)

src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
<parameters>
88
<parameter key="validator.mapping.cache.prefix" />
9+
<parameter key="validator.mapping.cache.file">%kernel.cache_dir%/validation.php</parameter>
910
</parameters>
1011

1112
<services>
@@ -28,8 +29,21 @@
2829

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

31-
<service id="validator.mapping.cache.symfony" class="Symfony\Component\Validator\Mapping\Cache\Psr6Cache" public="false">
32+
<service id="validator.mapping.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\ValidatorCacheWarmer" public="false">
33+
<argument>%kernel.bundles%</argument>
34+
<argument>%validator.mapping.cache.file%</argument>
3235
<argument type="service" id="cache.validator" />
36+
<tag name="kernel.cache_warmer" />
37+
</service>
38+
39+
<service id="validator.mapping.cache.symfony" class="Symfony\Component\Validator\Mapping\Cache\Psr6Cache" public="false">
40+
<argument type="service">
41+
<service class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
42+
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
43+
<argument>%validator.mapping.cache.file%</argument>
44+
<argument type="service" id="cache.validator" />
45+
</service>
46+
</argument>
3347
</service>
3448

3549
<service id="validator.mapping.cache.doctrine.apc" class="Symfony\Component\Validator\Mapping\Cache\DoctrineCache" public="false">

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ protected static function getBundleDefaultConfig()
211211
'static_method' => array('loadValidatorMetadata'),
212212
'translation_domain' => 'validators',
213213
'strict_email' => false,
214-
'cache' => 'validator.mapping.cache.symfony',
215214
),
216215
'annotations' => array(
217216
'cache' => 'php_array',
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Validator\Loader;
13+
14+
/**
15+
* Over-eager loaders load all the classes metadata at boot time.
16+
*
17+
* @author Titouan Galopin <galopintitouan@gmail.com>
18+
*/
19+
interface OverEagerFileLoaderInterface
20+
{
21+
/**
22+
* Returns the list of classes present in this loader file.
23+
*
24+
* @return array
25+
*/
26+
public function getClasses();
27+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Validator\Loader;
13+
14+
use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader;
15+
16+
/**
17+
* Over-eager version of the XmlFileLoader to load all the classes metadata at boot time.
18+
*
19+
* @author Titouan Galopin <galopintitouan@gmail.com>
20+
*/
21+
class OverEagerXmlFileLoader extends XmlFileLoader implements OverEagerFileLoaderInterface
22+
{
23+
public function __construct($file)
24+
{
25+
parent::__construct($file);
26+
27+
$this->initialize();
28+
}
29+
30+
/**
31+
* Returns the list of classes present in this loader file.
32+
*
33+
* @return array
34+
*/
35+
public function getClasses()
36+
{
37+
return array_keys($this->classes);
38+
}
39+
40+
/**
41+
* Initialize the classes list.
42+
*/
43+
private function initialize()
44+
{
45+
$xml = $this->parseFile($this->file);
46+
47+
$this->classes = array();
48+
49+
foreach ($xml->namespace as $namespace) {
50+
$this->addNamespaceAlias((string) $namespace['prefix'], trim((string) $namespace));
51+
}
52+
53+
foreach ($xml->class as $class) {
54+
$this->classes[(string) $class['name']] = $class;
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)
0