8000 bug #21243 [FrameworkBundle] Fix class_exists() checks in PhpArrayAda… · symfony/symfony@75de5eb · GitHub
[go: up one dir, main page]

Skip to content

Commit 75de5eb

Browse files
committed
bug #21243 [FrameworkBundle] Fix class_exists() checks in PhpArrayAdapter-related cache warmers (nicolas-grekas, mpajunen)
This PR was merged into the 3.2 branch. Discussion ---------- [FrameworkBundle] Fix class_exists() checks in PhpArrayAdapter-related cache warmers | Q | A | ------------- | --- | Branch? | 3.2 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Follow up of #21102 Commits ------- e09dccc [FrameworkBundle] Add annotated validator cache test case c60009e [FrameworkBundle] Fix class_exists() checks in PhpArrayAdapter-related cache warmers
2 parents 7ba3bc6 + e09dccc commit 75de5eb

File tree

9 files changed

+141
-19
lines changed

9 files changed

+141
-19
lines changed

src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ class AnnotationsCacheWarmer implements CacheWarmerInterface
3636

3737
/**
3838
* @param Reader $annotationReader
39-
* @param string $phpArrayFile The PHP file where annotations are cached.
40-
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached.
39+
* @param string $phpArrayFile The PHP file where annotations are cached
40+
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached
4141
*/
4242
public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
4343
{
@@ -67,9 +67,8 @@ public function warmUp($cacheDir)
6767

6868
$arrayPool = new ArrayAdapter(0, false);
6969
$reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayPool));
70-
$throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); };
71-
spl_autoload_register($throwingAutoloader);
7270

71+
spl_autoload_register(array($adapter, 'throwOnRequiredClass'));
7372
try {
7473
foreach ($annotatedClasses as $class) {
7574
try {
@@ -88,7 +87,7 @@ public function warmUp($cacheDir)
8887
}
8988
}
9089
} finally {
91-
spl_autoload_unregister($throwingAutoloader);
90+
spl_autoload_unregister(array($adapter, 'throwOnRequiredClass'));
9291
}
9392

9493
$values = $arrayPool->getValues();

src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\CacheWarmer;
1313

14+
use Doctrine\Common\Annotations\AnnotationException;
1415
use Psr\Cache\CacheItemPoolInterface;
1516
use Symfony\Component\Cache\Adapter\AdapterInterface;
1617
use Symfony\Component\Cache\Adapter\ArrayAdapter;
@@ -36,9 +37,9 @@ class SerializerCacheWarmer implements CacheWarmerInterface
3637
private $fallbackPool;
3738

3839
/**
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.
40+
* @param LoaderInterface[] $loaders The serializer metadata loaders
41+
* @param string $phpArrayFile The PHP file where metadata are cached
42+
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached
4243
*/
4344
public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
4445
{
@@ -64,10 +65,21 @@ public function warmUp($cacheDir)
6465

6566
$metadataFactory = new CacheClassMetadataFactory(new ClassMetadataFactory(new LoaderChain($this->loaders)), $arrayPool);
6667

67-
foreach ($this->extractSupportedLoaders($this->loaders) as $loader) {
68-
foreach ($loader->getMappedClasses() as $mappedClass) {
69-
$metadataFactory->getMetadataFor($mappedClass);
68+
spl_autoload_register(array($adapter, 'throwOnRequiredClass'));
69+
try {
70+
foreach ($this->extractSupportedLoaders($this->loaders) as $loader) {
71+
foreach ($loader->getMappedClasses() as $mappedClass) {
72+
try {
73+
$metadataFactory->getMetadataFor($mappedClass);
74+
} catch (\ReflectionException $e) {
75+
// ignore failing reflection
76+
} catch (AnnotationException $e) {
77+
// ignore failing annotations
78+
}
79+
}
7080
}
81+
} finally {
82+
spl_autoload_unregister(array($adapter, 'throwOnRequiredClass'));
7183
}
7284

7385
$values = $arrayPool->getValues();

src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\CacheWarmer;
1313

14+
use Doctrine\Common\Annotations\AnnotationException;
1415
use Psr\Cache\CacheItemPoolInterface;
1516
use Symfony\Component\Cache\Adapter\AdapterInterface;
1617
use Symfony\Component\Cache\Adapter\ArrayAdapter;
@@ -38,8 +39,8 @@ class ValidatorCacheWarmer implements CacheWarmerInterface
3839

3940
/**
4041
* @param ValidatorBuilderInterface $validatorBuilder
41-
* @param string $phpArrayFile The PHP file where metadata are cached.
42-
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached.
42+
* @param string $phpArrayFile The PHP file where metadata are cached
43+
* @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached
4344
*/
4445
public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool)
4546
{
@@ -66,9 +67,7 @@ public function warmUp($cacheDir)
6667
$loaders = $this->validatorBuilder->getLoaders();
6768
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayPool));
6869

69-
$throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); };
70-
spl_autoload_register($throwingAutoloader);
71-
70+
spl_autoload_register(array($adapter, 'throwOnRequiredClass'));
7271
try {
7372
foreach ($this->extractSupportedLoaders($loaders) as $loader) {
7473
foreach ($loader->getMappedClasses() as $mappedClass) {
@@ -78,15 +77,17 @@ public function warmUp($cacheDir)
7877
}
7978
} catch (\ReflectionException $e) {
8079
// ignore failing reflection
80+
} catch (AnnotationException $e) {
81+
// ignore failing annotations
8182
}
8283
}
8384
}
8485
} finally {
85-
spl_autoload_unregister($throwingAutoloader);
86+
spl_autoload_unregister(array($adapter, 'throwOnRequiredClass'));
8687
}
8788

8889
$values = $arrayPool->getValues();
89-
$adapter->warmUp($values);
90+
$adapter->warmUp(array_filter($values));
9091

9192
foreach ($values as $k => $v) {
9293
$item = $this->fallbackPool->getItem($k);

src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,39 @@ public function testWarmUp()
5151
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author', $values);
5252
}
5353

54+
public function testWarmUpWithAnnotations()
55+
{
56+
$validatorBuilder = new ValidatorBuilder();
57+
$validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/categories.yml');
58+
$validatorBuilder->enableAnnotationMapping();
59+
60+
$file = sys_get_temp_dir().'/cache-validator-with-annotations.php';
61+
@unlink($file);
62+
63+
$fallbackPool = new ArrayAdapter();
64+
65+
$warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool);
66+
$warmer->warmUp(dirname($file));
67+
68+
$this->assertFileExists($file);
69+
70+
$values = require $file;
71+
72+
$this->assertInternalType('array', $values);
73+
$this->assertCount(1, $values);
74+
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category', $values);
75+
76+
// Simple check to make sure that at least one constraint is actually cached, in this case the "id" property Type.
77+
$this->assertContains('"int"', $values['Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category']);
78+
79+
$values = $fallbackPool->getValues();
80+
81+
$this->assertInternalType('array', $values);
82+
$this->assertCount(2, $values);
83+
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category', $values);
84+
$this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.SubCategory', $values);
85+
}
86+
5487
public function testWarmUpWithoutLoader()
5588
{
5689
$validatorBuilder = new ValidatorBuilder();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation;
4+
5+
use Symfony\Component\Validator\Constraints as Assert;
6+
7+
class Category
8+
{
9+
const NAME_PATTERN = '/\w+/';
10+
11+
public $id;
12+
13+
/**
14+
* @Assert\Type(Category::NAME_PATTERN)
15+
*/
16+
public $name;
17+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation\Category:
2+
properties:
3+
id:
4+
- Type: int
5+
6+
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation\SubCategory:
7+
properties:
8+
id:
9+
- Type: int
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation;
4+
5+
// Missing "use" for Assert\Type is on purpose
6+
7+
class SubCategory extends Category
8+
{
9+
/**
10+
* @Assert\Type(Category::class)
11+
*/
12+
public $main;
13+
}

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=5.5.9",
20-
"symfony/cache": "~3.2",
20+
"symfony/cache": "~3.2.2|~3.3",
2121
"symfony/class-loader": "~3.2",
2222
"symfony/dependency-injection": "~3.2.1|~3.3",
2323
"symfony/config": "~2.8|~3.0",

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,4 +365,42 @@ private function generateItems(array $keys)
365365
}
366366
}
367367
}
368+
369+
/**
370+
* @throws \ReflectionException When $class is not found and is required
371+
*
372+
* @internal
373+
*/
374+
public static function throwOnRequiredClass($class)
375+
{
376+
$e = new \ReflectionException(sprintf('Class %s does not exist', $class));
377+
$trace = $e->getTrace();
378+
$autoloadFrame = array(
379+
'function' => 'spl_autoload_call',
380+
'args' => array($class),
381+
);
382+
$i = array_search($autoloadFrame, $trace);
383+
384+
if (false !== $i++ && isset($trace[$i]['function']) && !isset($trace[$i]['class'])) {
385+
switch ($trace[$i]['function']) {
386+
case 'get_class_methods':
387+
case 'get_class_vars':
388+
case 'get_parent_class':
389+
case 'is_a':
390+
case 'is_subclass_of':
391+
case 'class_exists':
392+
case 'class_implements':
393+
case 'class_parents':
394+
case 'trait_exists':
395+
case 'defined':
396+
case 'interface_exists':
397+
case 'method_exists':
398+
case 'property_exists':
399+
case 'is_callable':
400+
return;
401+
}
402+
}
403+
404+
throw $e;
405+
}
368406
}

0 commit comments

Comments
 (0)
0