Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 810f469

Browse files
committed
[FrameworkBundle] Introduce a cache warmer for Serializer based on PhpArrayAdapter
1 parent 5cc9ed2 commit 810f469

File tree

12 files changed

+333
-28
lines changed

12 files changed

+333
-28
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Component\Cache\Adapter\AdapterInterface;
16+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17+
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
18+
use Symfony\Component\Cache\Adapter\ProxyAdapter;
19+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
20+
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
21+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
22+
use Symfony\Component\Serializer\Mapping\Loader\LoaderChain;
23+
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
24+
use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
25+
use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
26+
27+
/**
28+
* Warms up XML and YAML serializer metadata.
29+
*
30+
* @author Titouan Galopin <[email protected]>
31+
*/
32+
class SerializerCacheWarmer implements CacheWarmerInterface
33+
{
34+
private $loaders;
35+
private $phpArrayFile;
36+
private $fallbackPool;
37+
38+
/**
39+
* @param LoaderInterface[] $loaders The serializer metadata loaders.
40+
* @param string $phpArrayFile The PHP file where metadata are cached.
41+
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached.
42+
*/
43+
public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
44+
{
45+
$this->loaders = $loaders;
46+
$this->phpArrayFile = $phpArrayFile;
47+
if (!$fallbackPool instanceof AdapterInterface) {
48+
$fallbackPool = new ProxyAdapter($fallbackPool);
49+
}
50+
$this->fallbackPool = $fallbackPool;
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
public function warmUp($cacheDir)
57+
{
58+
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
59+
return;
60+
}
61+
62+
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool);
63+
$arrayPool = new ArrayAdapter(0, false);
64+
65+
$metadataFactory = new CacheClassMetadataFactory(new ClassMetadataFactory(new LoaderChain($this->loaders)), $arrayPool);
66+
67+
foreach ($this->extractSupportedLoaders($this->loaders) as $loader) {
68+
foreach ($loader->getMappedClasses() as $mappedClass) {
69+
$metadataFactory->getMetadataFor($mappedClass);
70+
}
71+
}
72+
73+
$values = $arrayPool->getValues();
74+
$adapter->warmUp($values);
75+
76+
foreach ($values as $k => $v) {
77+
$item = $this->fallbackPool->getItem($k);
78+
$this->fallbackPool->saveDeferred($item->set($v));
79+
}
80+
$this->fallbackPool->commit();
81+
}
82+
83+
/**
84+
* {@inheritdoc}
85+
*/
86+
public function isOptional()
87+
{
88+
return true;
89+
}
90+
91+
/**
92+
* @param LoaderInterface[] $loaders
93+
*
94+
* @return XmlFileLoader[]|YamlFileLoader[]
95+
*/
96+
private function extractSupportedLoaders(array $loaders)
97+
{
98+
$supportedLoaders = array();
99+
100+
foreach ($loaders as $loader) {
101+
if ($loader instanceof XmlFileLoader || $loader instanceof YamlFileLoader) {
102+
$supportedLoaders[] = $loader;
103+
} elseif ($loader instanceof LoaderChain) {
104+
$supportedLoaders = array_merge($supportedLoaders, $this->extractSupportedLoaders($loader->getDelegatedLoaders()));
105+
}
106+
}
107+
108+
return $supportedLoaders;
109+
}
110+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
10671067
}
10681068

10691069
$chainLoader->replaceArgument(0, $serializerLoaders);
1070+
$container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders);
10701071

10711072
if (isset($config['cache']) && $config['cache']) {
10721073
@trigger_error('The "framework.serializer.cache" option is deprecated since Symfony 3.1 and will be removed in 4.0. Configure the "cache.serializer" service under "framework.cache.pools" instead.', E_USER_DEPRECATED);
@@ -1079,12 +1080,12 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
10791080
$container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument(
10801081
1, new Reference($config['cache'])
10811082
);
1082-
} elseif (!$container->getParameter('kernel.debug')) {
1083+
} elseif (!$container->getParameter('kernel.debug') && class_exists(CacheClassMetadataFactory::class)) {
10831084
$cacheMetadataFactory = new Definition(
10841085
CacheClassMetadataFactory::class,
10851086
array(
10861087
new Reference('serializer.mapping.cache_class_metadata_factory.inner'),
1087-
new Reference('cache.serializer'),
1088+
new Reference('serializer.mapping.cache.symfony'),
10881089
)
10891090
);
10901091
$cacheMetadataFactory->setPublic(false);

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
66

77
<parameters>
8+
<parameter key="serializer.mapping.cache.file">%kernel.cache_dir%/serialization.php</parameter>
89
<parameter key="serializer.mapping.cache.prefix" />
910
</parameters>
1011

@@ -39,6 +40,19 @@
3940
</service>
4041

4142
<!-- Cache -->
43+
<service id="serializer.mapping.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\SerializerCacheWarmer" public="false">
44+
<argument type="collection" /><!-- Loaders injected by the extension -->
45+
<argument>%serializer.mapping.cache.file%</argument>
46+
<argument type="service" id="cache.serializer" />
47+
<tag name="kernel.cache_warmer" />
48+
</service>
49+
50+
<service id="serializer.mapping.cache.symfony" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
51+
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
52+
<argument>%serializer.mapping.cache.file%</argument>
53+
<argument type="service" id="cache.serializer" />
54+
</service>
55+
4256
<service id="serializer.mapping.cache.doctrine.apc" class="Doctrine\Common\Cache\ApcCache" public="false">
4357
<call method="setNamespace">
4458
<argument>%serializer.mapping.cache.prefix%</argument>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests\CacheWarmer;
13+
14+
use Symfony\Bundle\FrameworkBundle\CacheWarmer\SerializerCacheWarmer;
15+
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
16+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17+
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
18+
use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
19+
use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
20+
21+
class SerializerCacheWarmerTest extends TestCase
22+
{
23+
public function testWarmUp()
24+
{
25+
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
26+
$this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.');
27+
}
28+
29+
$loaders = array(
30+
new XmlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/person.xml'),
31+
new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/author.yml'),
32+
);
33+
34+
$file = sys_get_temp_dir().'/cache-serializer.php';
35+
@unlink($file);
36+
37+
$fallbackPool = new ArrayAdapter();
38+
39+
$warmer = new SerializerCacheWarmer($loaders, $file, $fallbackPool);
40+
$warmer->warmUp(dirname($file));
41+
42+
$this->assertFileExists($file);
43+
44+
$values = require $file;
45+
46+
$this->assertInternalType('array', $values);
47+
$this->assertCount(2, $values);
48+
$this->assertArrayHasKey('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Person', $values);
49+
$this->assertArrayHasKey('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author', $values);
50+
51+
$values = $fallbackPool->getValues();
52+
53+
$this->assertInternalType('array', $values);
54+
$this->assertCount(2, $values);
55+
$this->assertArrayHasKey('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Person', $values);
56+
$this->assertArrayHasKey('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author', $values);
57+
}
58+
59+
public function testWarmUpWithoutLoader()
60+
{
61+
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
62+
$this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.');
63+
}
64+
65+
$file = sys_get_temp_dir().'/cache-serializer-without-loader.php';
66+
@unlink($file);
67+
68+
$fallbackPool = new ArrayAdapter();
69+
70+
$warmer = new SerializerCacheWarmer(array(), $file, $fallbackPool);
71+
$warmer->warmUp(dirname($file));
72+
73+
$this->assertFileExists($file);
74+
75+
$values = require $file;
76+
77+
$this->assertInternalType('array', $values);
78+
$this->assertCount(0, $values);
79+
80+
$values = $fallbackPool->getValues();
81+
82+
$this->assertInternalType('array', $values);
83+
$this->assertCount(0, $values);
84+
}
85+
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121
use Symfony\Component\Cache\Adapter\ProxyAdapter;
2222
use Symfony\Component\Cache\Adapter\RedisAdapter;
2323
use Symfony\Component\DependencyInjection\ContainerBuilder;
24+
use Symfony\Component\DependencyInjection\Definition;
2425
use Symfony\Component\DependencyInjection\DefinitionDecorator;
2526
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
2627
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
2728
use Symfony\Component\DependencyInjection\Reference;
29+
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
30+
use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
31+
use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
2832
use Symfony\Component\Serializer\Normalizer\DataUriNormalizer;
2933
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
3034
use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
@@ -542,8 +546,16 @@ public function testObjectNormalizerRegistered()
542546

543547
public function testSerializerCacheActivated()
544548
{
549+
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
550+
$this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.');
551+
}
552+
545553
$container = $this->createContainerFromFile('serializer_enabled');
554+
546555
$this->assertTrue($container->hasDefinition('serializer.mapping.cache_class_metadata_factory'));
556+
557+
$cache = $container->getDefinition('serializer.mapping.cache_class_metadata_factory')->getArgument(1);
558+
$this->assertEquals(new Reference('serializer.mapping.cache.symfony'), $cache);
547559
}
548560

549561
public function testSerializerCacheDisabled()
@@ -562,7 +574,10 @@ public function testDeprecatedSerializerCacheOption()
562574
$container = $this->createContainerFromFile('serializer_legacy_cache', array('kernel.debug' => true, 'kernel.container_class' => __CLASS__));
563575

564576
$this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory'));
565-
$this->assertEquals(new Reference('foo'), $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1));
577+
$this->assertTrue($container->hasDefinition('serializer.mapping.class_metadata_factory'));
578+
579+
$cache = $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1);
580+
$this->assertEquals(new Reference('foo'), $cache);
566581
});
567582
}
568583

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serialization;
4+
5+
class Author
6+
{
7+
public $gender;
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serialization;
4+
5+
class Person
6+
{
7+
public $gender;
8+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serialization\Author:
2+
attributes:
3+
gender:
4+
groups: ['group1', 'group2']
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" ?>
2+
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping
5+
http://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
6+
>
7+
<class name="Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serialization\Person">
8+
<attribute name="gender">
9+
<group>group1</group>
10+
<group>group2</group>
11+
</attribute>
12+
</class>
13+
</serializer>

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"symfony/form": "~2.8|~3.0",
4848
"symfony/expression-language": "~2.8|~3.0",
4949
"symfony/process": "~2.8|~3.0",
50-
"symfony/serializer": "~2.8|^3.0",
50+
"symfony/serializer": "~2.8|~3.0",
5151
"symfony/validator": "~3.1",
5252
"symfony/yaml": "~3.2",
5353
"symfony/property-info": "~2.8|~3.0",

src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,11 @@ class XmlFileLoader extends FileLoader
3636
public function loadClassMetadata(ClassMetadataInterface $classMetadata)
3737
{
3838
if (null === $this->classes) {
39-
$this->classes = array();
40-
$xml = $this->parseFile($this->file);
39+
$this->classes = $this->getClassesFromXml();
40+
}
4141

42-
foreach ($xml->class as $class) {
43-
$this->classes[(string) $class['name']] = $class;
44-
}
42+
if (!$this->classes) {
43+
return false;
4544
}
4645

4746
$attributesMetadata = $classMetadata->getAttributesMetadata();
@@ -74,6 +73,20 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
7473
return false;
7574
}
7675

76+
/**
77+
* Return the names of the classes mapped in this file.
78+
*
79+
* @return string[] The classes names
80+
*/
81+
public function getMappedClasses()
82+
{
83+
if (null === $this->classes) {
84+
$this->classes = $this->getClassesFromXml();
85+
}
86+
87+
return array_keys($this->classes);
88+
}
89+
7790
/**
7891
* Parses a XML File.
7992
*
@@ -93,4 +106,16 @@ private function parseFile($file)
93106

94107
return simplexml_import_dom($dom);
95108
}
109+
110+
private function getClassesFromXml()
111+
{
112+
$xml = $this->parseFile($this->file);
113+
$classes = array();
114+
115+
foreach ($xml->class as $class) {
116+
$classes[(string) $class['name']] = $class;
117+
}
118+
119+
return $classes;
120+
}
96121
}

0 commit comments

Comments
 (0)