Skip to content

Commit 0816b13

Browse files
committed
[FrameworkBundle][CacheWarmer] Ignore exeptions thrown during reflection classes autoload
1 parent ee5e5de commit 0816b13

File tree

9 files changed

+174
-0
lines changed

9 files changed

+174
-0
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array
8282
$phpArrayAdapter->warmUp($values);
8383
}
8484

85+
protected function ignoreAutoloadException($class, \Exception $exception)
86+
{
87+
if (false === array_search([
88+
'function' => 'spl_autoload_call',
89+
'args' => [$class],
90+
], $exception->getTrace(), true) ||
91+
class_exists($class, false) ||
92+
interface_exists($class, false) ||
93+
trait_exists($class, false)) {
94+
throw $exception;
95+
}
96+
}
97+
8598
/**
8699
* @param string $cacheDir
87100
* @param ArrayAdapter $arrayAdapter

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
7575
* environment but is always added to the annotations.map file by some Symfony default behaviors,
7676
* and you always end up with a not found Annotation.
7777
*/
78+
} catch (\Exception $e) {
79+
$this->ignoreAutoloadException($class, $e);
7880
}
7981
}
8082

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
6060
// ignore failing reflection
6161
} catch (AnnotationException $e) {
6262
// ignore failing annotations
63+
} catch (\Exception $e) {
64+
$this->ignoreAutoloadException($mappedClass, $e);
6365
}
6466
}
6567
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
6565
// ignore failing reflection
6666
} catch (AnnotationException $e) {
6767
// ignore failing annotations
68+
} catch (\Exception $e) {
69+
$this->ignoreAutoloadException($mappedClass, $e);
6870
}
6971
}
7072
}

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,54 @@ public function testAnnotationsCacheWarmerWithDebugEnabled()
8585
$reader->getPropertyAnnotations($refClass->getProperty('cacheDir'));
8686
}
8787

88+
/**
89+
* Test that the cache warming process is not broken if a class loader
90+
* throws an exception (on class / file not found for example).
91+
*/
92+
public function testClassAutoloadException()
93+
{
94+
$this->assertFalse(class_exists($annotatedClass = 'C\C\C', false));
95+
96+
file_put_contents($this->cacheDir.'/annotations.map', sprintf('<?php return %s;', var_export([$annotatedClass], true)));
97+
$warmer = new AnnotationsCacheWarmer(new AnnotationReader(), tempnam($this->cacheDir, __FUNCTION__), new ArrayAdapter());
98+
99+
spl_autoload_register($classloader = function ($class) use ($annotatedClass) {
100+
if ($class === $annotatedClass) {
101+
throw new \DomainException('This exception should be caught by the warmer.');
102+
}
103+
}, true, true);
104+
105+
$warmer->warmUp($this->cacheDir);
106+
107+
spl_autoload_unregister($classloader);
108+
}
109+
110+
/**
111+
* Test that the cache warming process is broken if a class loader throws an
112+
* exception but that is unrelated to the class load.
113+
*/
114+
public function testClassAutoloadExceptionWithUnrelatedException()
115+
{
116+
$this->assertFalse(class_exists($annotatedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_AnnotationsCacheWarmerTest', false));
117+
118+
file_put_contents($this->cacheDir.'/annotations.map', sprintf('<?php return %s;', var_export([$annotatedClass], true)));
119+
$warmer = new AnnotationsCacheWarmer(new AnnotationReader(), tempnam($this->cacheDir, __FUNCTION__), new ArrayAdapter());
120+
121+
$this->expectException(\DomainException::class);
122+
$this->expectExceptionMessage('This exception should not be caught by the warmer.');
123+
124+
spl_autoload_register($classloader = function ($class) use ($annotatedClass) {
125+
if ($class === $annotatedClass) {
126+
eval(sprintf('class '.$annotatedClass.'{}'));
127+
throw new \DomainException('This exception should not be caught by the warmer.');
128+
}
129+
}, true, true);
130+
131+
$warmer->warmUp($this->cacheDir);
132+
133+
spl_autoload_unregister($classloader);
134+
}
135+
88136
/**
89137
* @return \PHPUnit_Framework_MockObject_MockObject|Reader
90138
*/

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,58 @@ public function testWarmUpWithoutLoader()
7777
$this->assertInternalType('array', $values);
7878
$this->assertCount(0, $values);
7979
}
80+
81+
/**
82+
* Test that the cache warming process is not broken if a class loader
83+
* throws an exception (on class / file not found for example).
84+
*/
85+
public function testClassAutoloadException()
86+
{
87+
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
88+
$this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.');
89+
}
90+
91+
$this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_SerializerCacheWarmerTest', false));
92+
93+
$warmer = new SerializerCacheWarmer([new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/does_not_exist.yaml')], tempnam(sys_get_temp_dir(), __FUNCTION__), new ArrayAdapter());
94+
95+
spl_autoload_register($classloader = function ($class) use ($mappedClass) {
96+
if ($class === $mappedClass) {
97+
throw new \DomainException('This exception should be caught by the warmer.');
98+
}
99+
}, true, true);
100+
101+
$warmer->warmUp('foo');
102+
103+
spl_autoload_unregister($classloader);
104+
}
105+
106+
/**
107+
* Test that the cache warming process is broken if a class loader throws an
108+
* exception but that is unrelated to the class load.
109+
*/
110+
public function testClassAutoloadExceptionWithUnrelatedException()
111+
{
112+
if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) {
113+
$this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.');
114+
}
115+
116+
$this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_SerializerCacheWarmerTest', false));
117+
118+
$warmer = new SerializerCacheWarmer([new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/does_not_exist.yaml')], tempnam(sys_get_temp_dir(), __FUNCTION__), new ArrayAdapter());
119+
120+
$this->expectException(\DomainException::class);
121+
$this->expectExceptionMessage('This exception should not be caught by the warmer.');
122+
123+
spl_autoload_register($classloader = function ($class) use ($mappedClass) {
124+
if ($class === $mappedClass) {
125+
eval(sprintf('class '.$mappedClass.'{}'));
126+
throw new \DomainException('This exception should not be caught by the warmer.');
127+
}
128+
}, true, true);
129+
130+
$warmer->warmUp('foo');
131+
132+
spl_autoload_unregister($classloader);
133+
}
80134
}

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,55 @@ public function testWarmUpWithoutLoader()
102102
$this->assertInternalType('array', $values);
103103
$this->assertCount(0, $values);
104104
}
105+
106+
/**
107+
* Test that the cache warming process is not broken if a class loader
108+
* throws an exception (on class / file not found for example).
109+
*/
110+
public function testClassAutoloadException()
111+
{
112+
$this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_ValidatorCacheWarmerTest', false));
113+
114+
$validatorBuilder = new ValidatorBuilder();
115+
$validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/does_not_exist.yaml');
116+
$validatorBuilder->enableAnnotationMapping();
117+
$warmer = new ValidatorCacheWarmer($validatorBuilder, tempnam(sys_get_temp_dir(), __FUNCTION__), new ArrayAdapter());
118+
119+
spl_autoload_register($classloader = function ($class) use ($mappedClass) {
120+
if ($class === $mappedClass) {
121+
throw new \DomainException('This exception should be caught by the warmer.');
122+
}
123+
}, true, true);
124+
125+
$warmer->warmUp('foo');
126+
127+
spl_autoload_unregister($classloader);
128+
}
129+
130+
/**
131+
* Test that the cache warming process is broken if a class loader throws an
132+
* exception but that is unrelated to the class load.
133+
*/
134+
public function testClassAutoloadExceptionWithUnrelatedException()
135+
{
136+
$this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_ValidatorCacheWarmerTest', false));
137+
138+
$validatorBuilder = new ValidatorBuilder();
139+
$validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/does_not_exist.yaml');
140+
$warmer = new ValidatorCacheWarmer($validatorBuilder, tempnam(sys_get_temp_dir(), __FUNCTION__), new ArrayAdapter());
141+
142+
$this->expectException(\DomainException::class);
143+
$this->expectExceptionMessage('This exception should not be caught by the warmer.');
144+
145+
spl_autoload_register($classloader = function ($class) use ($mappedClass) {
146+
if ($class === $mappedClass) {
147+
eval(sprintf('class '.$mappedClass.'{}'));
148+
throw new \DomainException('This exception should not be caught by the warmer.');
149+
}
150+
}, true, true);
151+
152+
$warmer->warmUp('foo');
153+
154+
spl_autoload_unregister($classloader);
155+
}
105156
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AClassThatDoesNotExist_FWB_CacheWarmer_SerializerCacheWarmerTest: ~
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AClassThatDoesNotExist_FWB_CacheWarmer_ValidatorCacheWarmerTest: ~

0 commit comments

Comments
 (0)