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

Skip to content

Commit 35b0ab9

Browse files
committed
feature #18533 [FrameworkBundle] Wire PhpArrayAdapter with a new cache warmer for annotations (tgalopin)
This PR was squashed before being merged into the 3.2-dev branch (closes #18533). Discussion ---------- [FrameworkBundle] Wire PhpArrayAdapter with a new cache warmer for annotations | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | WIP | Fixed tickets | - | License | MIT | Doc PR | - Depends on #18825 and #18823 This PR implements the usage of the new OpCacheAdapter in the annotations caching system. The idea to use this adapter as much as possible in Symfony (validator, serializer, ...). These other implementations will be the object of different PRs. Commits ------- f950a2b [FrameworkBundle] Wire PhpArrayAdapter with a new cache warmer for annotations
2 parents f76d050 + f950a2b commit 35b0ab9

File tree

13 files changed

+427
-15
lines changed

13 files changed

+427
-15
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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 Doctrine\Common\Annotations\CachedReader;
15+
use Doctrine\Common\Annotations\Reader;
16+
use Psr\Cache\CacheItemPoolInterface;
17+
use Symfony\Component\Cache\Adapter\AdapterInterface;
18+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
19+
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
20+
use Symfony\Component\Cache\Adapter\ProxyAdapter;
21+
use Symfony\Component\Cache\DoctrineProvider;
22+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
23+
24+
/**
25+
* Warms up annotation caches for classes found in composer's autoload class map
26+
* and declared in DI bundle extensions using the addAnnotatedClassesToCache method.
27+
*
28+
* @author Titouan Galopin <[email protected]>
29+
*/
30+
class AnnotationsCacheWarmer implements CacheWarmerInterface
31+
{
32+
private $annotationReader;
33+
private $phpArrayFile;
34+
private $fallbackPool;
35+
36+
/**
37+
* @param Reader $annotationReader
38+
* @param string $phpArrayFile The PHP file where annotations are cached.
39+
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached.
40+
*/
41+
public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
42+
{
43+
$this->annotationReader = $annotationReader;
44+
$this->phpArrayFile = $phpArrayFile;
45+
if (!$fallbackPool instanceof AdapterInterface) {
46+
$fallbackPool = new ProxyAdapter($fallbackPool);
47+
}
48+
$this->fallbackPool = $fallbackPool;
49+
}
50+
51+
/**
52+
* {@inheritdoc}
53+
*/
54+
public function warmUp($cacheDir)
55+
{
56+
$adapter = new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool);
57+
$annotatedClassPatterns = $cacheDir.'/annotations.map';
58+
59+
if (!is_file($annotatedClassPatterns)) {
60+
$adapter->warmUp(array());
61+
62+
return;
63+
}
64+
65+
$annotatedClasses = include $annotatedClassPatterns;
66+
67+
$arrayPool = new ArrayAdapter(0, false);
68+
$reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayPool));
69+
70+
foreach ($annotatedClasses as $class) {
71+
$this->readAllComponents($reader, $class);
72+
}
73+
74+
$values = $arrayPool->getValues();
75+
$adapter->warmUp($values);
76+
77+
foreach ($values as $k => $v) {
78+
$item = $this->fallbackPool->getItem($k);
79+
$this->fallbackPool->saveDeferred($item->set($v));
80+
}
81+
$this->fallbackPool->commit();
82+
}
83+
84+
/**
85+
* {@inheritdoc}
86+
*/
87+
public function isOptional()
88+
{
89+
return true;
90+
}
91+
92+
private function readAllComponents(Reader $reader, $class)
93+
{
94+
$reflectionClass = new \ReflectionClass($class);
95+
$reader->getClassAnnotations($reflectionClass);
96+
97+
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
98+
$reader->getMethodAnnotations($reflectionMethod);
99+
}
100+
101+
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
102+
$reader->getPropertyAnnotations($reflectionProperty);
103+
}
104+
}
105+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ private function addAnnotationsSection(ArrayNodeDefinition $rootNode)
594594
->info('annotation configuration')
595595
->addDefaultsIfNotSet()
596596
->children()
597-
->scalarNode('cache')->defaultValue('file')->end()
597+
->scalarNode('cache')->defaultValue('php_array')->end()
598598
->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end()
599599
->booleanNode('debug')->defaultValue($this->debug)->end()
600600
->end()

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,14 @@ public function load(array $configs, ContainerBuilder $container)
168168
$definition->replaceArgument(1, null);
169169
}
170170

171+
$this->addAnnotatedClassesToCompile(array(
172+
'**Bundle\\Controller\\',
173+
'**Bundle\\Entity\\',
174+
175+
// Added explicitly so that we don't rely on the class map being dumped to make it work
176+
'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
177+
));
178+
171179
$this->addClassesToCompile(array(
172180
'Symfony\\Component\\Config\\ConfigCache',
173181
'Symfony\\Component\\Config\\FileLocator',
@@ -905,8 +913,22 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde
905913
$loader->load('annotations.xml');
906914

907915
if ('none' !== $config['cache']) {
908-
if ('file' === $config['cache']) {
916+
$cacheService = $config['cache'];
917+
918+
if ('php_array' === $config['cache']) {
919+
$cacheService = 'annotations.cache';
920+
921+
// Enable warmer only if PHP array is used for cache
922+
$definition = $container->findDefinition('annotations.cache_warmer');
923+
$definition->addTag('kernel.cache_warmer');
924+
925+
$this->addClassesToCompile(array(
926+
'Symfony\Component\Cache\Adapter\PhpArrayAdapter',
927+
'Symfony\Component\Cache\DoctrineProvider',
928+
));
929+
} elseif ('file' === $config['cache']) {
909930
$cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']);
931+
910932
if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
911933
throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir));
912934
}
@@ -915,11 +937,13 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde
915937
->getDefinition('annotations.filesystem_cache')
916938
->replaceArgument(0, $cacheDir)
917939
;
940+
941+
$cacheService = 'annotations.filesystem_cache';
918942
}
919943

920944
$container
921945
->getDefinition('annotations.cached_reader')
922-
->replaceArgument(1, new Reference('file' !== $config['cache'] ? $config['cache'] : 'annotations.filesystem_cache'))
946+
->replaceArgument(1, new Reference($cacheService))
923947
->replaceArgument(2, $config['debug'])
924948
->addAutowiringType(Reader::class)
925949
;
@@ -1129,10 +1153,8 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
11291153
}
11301154

11311155
$this->addClassesToCompile(array(
1132-
'Psr\Cache\CacheItemInterface',
1133-
'Psr\Cache\CacheItemPoolInterface',
1134-
'Symfony\Component\Cache\Adapter\AdapterInterface',
1135-
'Symfony\Component\Cache\Adapter\AbstractAdapter',
1156+
'Symfony\Component\Cache\Adapter\ApcuAdapter',
1157+
'Symfony\Component\Cache\Adapter\FilesystemAdapter',
11361158
'Symfony\Component\Cache\CacheItem',
11371159
));
11381160
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@
1919
<argument /><!-- Cache-Directory -->
2020
</service>
2121

22+
<service id="annotations.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer" public="false">
23+
<argument type="service" id="annotations.reader" />
24+
<argument>%kernel.cache_dir%/annotations.php</argument>
25+
<argument type="service" id="cache.annotations" />
26+
</service>
27+
28+
<service id="annotations.cache" class="Symfony\Component\Cache\DoctrineProvider" public="false">
29+
<argument type="service">
30+
<service class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
31+
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
32+
<argument>%kernel.cache_dir%/annotations.php</argument>
33+
<argument type="service" id="cache.annotations" />
34+
</service>
35+
</argument>
36+
</service>
37+
2238
<service id="annotation_reader" alias="annotations.reader" />
2339
</services>
2440
</container>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
<tag name="cache.pool" />
2323
</service>
2424

25+
<service id="cache.annotations" parent="cache.system" public="false">
26+
<tag name="cache.pool" />
27+
</service>
28+
2529
<service id="cache.adapter.system" class="Symfony\Component\Cache\Adapter\AdapterInterface" abstract="true">
2630
<factory class="Symfony\Component\Cache\Adapter\AbstractAdapter" method="createSystemCache" />
2731
<tag name="cache.pool" clearer="cache.default_clearer" />

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ protected static function getBundleDefaultConfig()
214214
'cache' => 'validator.mapping.cache.symfony',
215215
),
216216
'annotations' => array(
217-
'cache' => 'file',
217+
'cache' => 'php_array',
218218
'file_cache_dir' => '%kernel.cache_dir%/annotations',
219219
'debug' => true,
220220
),

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
"require": {
1919
"php": ">=5.5.9",
2020
"symfony/asset": "~2.8|~3.0",
21-
"symfony/cache": "~3.1",
21+
"symfony/cache": "~3.2",
2222
"symfony/class-loader": "~3.2",
2323
"symfony/dependency-injection": "~3.2",
2424
"symfony/config": "~2.8|~3.0",
2525
"symfony/event-dispatcher": "~2.8|~3.0",
2626
"symfony/http-foundation": "~3.1",
27-
"symfony/http-kernel": "~3.1.2|~3.2",
27+
"symfony/http-kernel": "~3.2",
2828
"symfony/polyfill-mbstring": "~1.0",
2929
"symfony/filesystem": "~2.8|~3.0",
3030
"symfony/finder": "~2.8|~3.0",

src/Symfony/Component/Cache/Adapter/ArrayAdapter.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function ($key, $value, $isHit) use ($defaultLifetime) {
5656
public function getItem($key)
5757
{
5858
if (!$isHit = $this->hasItem($key)) {
59-
$value = null;
59+
$this->values[$key] = $value = null;
6060
} elseif ($this->storeSerialized) {
6161
$value = unserialize($this->values[$key]);
6262
} else {
@@ -79,6 +79,16 @@ public function getItems(array $keys = array())
7979
return $this->generateItems($keys, time());
8080
}
8181

82+
/**
83+
* Returns all cached values, with cache miss as null.
84+
*
85+
* @return array
86+
*/
87+
public function getValues()
88+
{
89+
return $this->values;
90+
}
91+
8292
/**
8393
* {@inheritdoc}
8494
*/
@@ -183,7 +193,7 @@ private function generateItems(array $keys, $now)
183193

184194
foreach ($keys as $key) {
185195
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] >= $now || !$this->deleteItem($key))) {
186-
$value = null;
196+
$this->values[$key] = $value = null;
187197
} elseif ($this->storeSerialized) {
188198
$value = unserialize($this->values[$key]);
189199
} else {

src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,30 @@ public function createCachePool($defaultLifetime = 0)
2727
{
2828
return new ArrayAdapter($defaultLifetime);
2929
}
30+
31+
public function testGetValuesHitAndMiss()
32+
{
33+
/** @var ArrayAdapter $cache */
34+
$cache = $this->createCachePool();
35+
36+
// Hit
37+
$item = $cache->getItem('foo');
38+
$item->set('4711');
39+
$cache->save($item);
40+
41+
$fooItem = $cache->getItem('foo');
42+
$this->assertTrue($fooItem->isHit());
43+
$this->assertEquals('4711', $fooItem->get());
44+
45+
// Miss (should be present as NULL in $values)
46+
$cache->getItem('bar');
47+
48+
$values = $cache->getValues();
49+
50+
$this->assertCount(2, $values);
51+
$this->assertArrayHasKey('foo', $values);
52+
$this->assertSame(serialize('4711'), $values['foo']);
53+
$this->assertArrayHasKey('bar', $values);
54+
$this->assertNull($values['bar']);
55+
}
3056
}

0 commit comments

Comments
 (0)