Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d0b600a

Browse filesBrowse files
committed
Smart caching system for autowiring to only clear container cache when it's *actually* needed
1 parent c5c63dc commit d0b600a
Copy full SHA for d0b600a

File tree

4 files changed

+297
-1
lines changed
Filter options

4 files changed

+297
-1
lines changed

‎src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+45-1Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516
use Symfony\Component\DependencyInjection\Definition;
1617
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -49,6 +50,39 @@ public function process(ContainerBuilder $container)
4950
$this->ambiguousServiceTypes = array();
5051
}
5152

53+
/**
54+
* Creates a resource to help know if this service has changed.
55+
*
56+
* @param \ReflectionClass $reflectionClass
57+
*
58+
* @return AutowireServiceResource
59+
*/
60+
public static function createResourceForClass(\ReflectionClass $reflectionClass)
61+
{
62+
$metadata = array();
63+
64+
if ($constructor = $reflectionClass->getConstructor()) {
65+
$metadata['__construct'] = self::getResourceMetadataForMethod($constructor);
66+
}
67+
68+
// todo - when #17608 is merged, could refactor to private function to remove duplication
69+
// of determining valid "setter" methods
70+
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
71+
$name = $reflectionMethod->getName();
72+
if (isset($methodsCalled[$name]) || $reflectionMethod->isStatic() || 1 !== $reflectionMethod->getNumberOfParameters() || 0 !== strpos($name, 'set')) {
73+
continue;
74+
}
75+
76+
$metadata[$name] = self::getResourceMetadataForMethod($reflectionMethod);
77+
}
78+
79+
return new AutowireServiceResource(
80+
$reflectionClass->name,
81+
$reflectionClass->getFileName(),
82+
$metadata
83+
);
84+
}
85+
5286
/**
5387
* Wires the given definition.
5488
*
@@ -63,7 +97,7 @@ private function completeDefinition($id, Definition $definition)
6397
return;
6498
}
6599

66-
$this->container->addClassResource($reflectionClass);
100+
$this->container->addResource(static::createResourceForClass($reflectionClass));
67101

68102
if (!$constructor = $reflectionClass->getConstructor()) {
69103
return;
@@ -278,4 +312,14 @@ private function addServiceToAmbiguousType($id, $type)
278312
}
279313
$this->ambiguousServiceTypes[$type][] = $id;
280314
}
315+
316+
static private function getResourceMetadataForMethod(\ReflectionMethod $method)
317+
{
318+
$methodArgumentsMetadata = array();
319+
foreach ($method->getParameters() as $parameter) {
320+
$methodArgumentsMetadata[] = (string) $parameter;
321+
}
322+
323+
return $methodArgumentsMetadata;
324+
}
281325
}
+76Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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\Component\DependencyInjection\Config;
13+
14+
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
15+
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
16+
17+
class AutowireServiceResource implements SelfCheckingResourceInterface, \Serializable
18+
{
19+
private $class;
20+
private $filePath;
21+
private $autowiringMetadata = array();
22+
23+
public function __construct($class, $path, array $autowiringMetadata)
24+
{
25+
$this->class = $class;
26+
$this->filePath = $path;
27+
$this->autowiringMetadata = $autowiringMetadata;
28+
}
29+
30+
public function isFresh($timestamp)
31+
{
32+
if (!file_exists($this->filePath)) {
33+
return false;
34+
}
35+
36+
// has the file *not* been modified? Definitely fresh
37+
if (@filemtime($this->filePath) <= $timestamp) {
38+
return true;
39+
}
40+
41+
try {
42+
$reflectionClass = new \ReflectionClass($this->class);
43+
} catch (\ReflectionException $e) {
44+
// the class does not exist anymore!
45+
46+
return false;
47+
}
48+
49+
$newResource = AutowirePass::createResourceForClass($reflectionClass);
50+
51+
return $newResource == $this;
52+
}
53+
54+
public function __toString()
55+
{
56+
return 'service.autowire.'.$this->class;
57+
}
58+
59+
public function serialize()
60+
{
61+
return serialize(array(
62+
$this->class,
63+
$this->filePath,
64+
$this->autowiringMetadata,
65+
));
66+
}
67+
68+
public function unserialize($serialized)
69+
{
70+
list(
71+
$this->class,
72+
$this->filePath,
73+
$this->autowiringMetadata
74+
) = unserialize($serialized);
75+
}
76+
}

‎src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,39 @@ public function testOptionalScalarArgsNotPassedIfLast()
413413
$definition->getArguments()
414414
);
415415
}
416+
417+
/**
418+
* @dataProvider getCreateResourceTests
419+
*/
420+
public function testCreateResourceForClass($className, $isEqual)
421+
{
422+
$startingResource = AutowirePass::createResourceForClass(
423+
new \ReflectionClass(__NAMESPACE__.'\ClassForResource')
424+
);
425+
$newResource = AutowirePass::createResourceForClass(
426+
new \ReflectionClass(__NAMESPACE__.'\\'.$className)
427+
);
428+
429+
// hack so the objects don't differ by the class name
430+
$startingReflObject = new \ReflectionObject($startingResource);
431+
$reflProp = $startingReflObject->getProperty('class');
432+
$reflProp->setAccessible(true);
433+
$reflProp->setValue($startingResource, __NAMESPACE__.'\\'.$className);
434+
435+
if ($isEqual) {
436+
$this->assertEquals($startingResource, $newResource);
437+
} else {
438+
$this->assertNotEquals($startingResource, $newResource);
439+
}
440+
}
441+
442+
public function getCreateResourceTests()
443+
{
444+
return array(
445+
['IdenticalClassResource', true],
446+
['ClassChangedConstructorArgs', false],
447+
);
448+
}
416449
}
417450

418451
class Foo
@@ -562,3 +595,26 @@ public function __construct(A $a, $foo = 'default_val', Lille $lille)
562595
{
563596
}
564597
}
598+
599+
/*
600+
* Classes used for testing createResourceForClass
601+
*/
602+
class ClassForResource
603+
{
604+
public function __construct($foo, Bar $bar = null)
605+
{
606+
}
607+
608+
public function setBar(Bar $bar)
609+
{
610+
}
611+
}
612+
class IdenticalClassResource extends ClassForResource
613+
{
614+
}
615+
class ClassChangedConstructorArgs extends ClassForResource
616+
{
617+
public function __construct($foo, Bar $bar, $baz)
618+
{
619+
}
620+
}
+120Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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\Component\DependencyInjection\Tests\Config;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
15+
use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
16+
17+
class AutowireServiceResourceTest extends \PHPUnit_Framework_TestCase
18+
{
19+
/**
20+
* @var AutowireServiceResource
21+
*/
22+
private $resource;
23+
private $file;
24+
private $class;
25+
private $time;
26+
27+
protected function setUp()
28+
{
29+
$this->file = realpath(sys_get_temp_dir()).'/tmp.php';
30+
$this->time = time();
31+
touch($this->file, $this->time);
32+
33+
$this->class = __NAMESPACE__.'\Foo';
34+
$this->resource = new AutowireServiceResource(
35+
$this->class,
36+
$this->file,
37+
array()
38+
);
39+
}
40+
41+
public function testToString()
42+
{
43+
$this->assertSame('service.autowire.'.$this->class, (string) $this->resource);
44+
}
45+
46+
public function testSerializeUnserialize()
47+
{
48+
$unserialized = unserialize(serialize($this->resource));
49+
50+
$this->assertEquals($this->resource, $unserialized);
51+
}
52+
53+
public function testIsFresh()
54+
{
55+
$this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second');
56+
$this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed');
57+
$this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated');
58+
}
59+
60+
public function testIsFreshForDeletedResources()
61+
{
62+
unlink($this->file);
63+
64+
$this->assertFalse($this->resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the resource does not exist');
65+
}
66+
67+
public function testIsNotFreshChangedResource()
68+
{
69+
$oldResource = new AutowireServiceResource(
70+
$this->class,
71+
$this->file,
72+
array('will_be_different')
73+
);
74+
75+
// test with a stale file *and* a resource that *will* be different than the actual
76+
$this->assertFalse($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed');
77+
}
78+
79+
public function testIsFreshSameConstructorArgs()
80+
{
81+
$oldResource = AutowirePass::createResourceForClass(
82+
new \ReflectionClass(__NAMESPACE__.'\Foo')
83+
);
84+
85+
// test with a stale file *but* the resource will not be changed
86+
$this->assertTrue($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed');
87+
}
88+
89+
public function testNotFreshIfClassNotFound()
90+
{
91+
$resource = new AutowireServiceResource(
92+
'Some\Non\Existent\Class',
93+
$this->file,
94+
array()
95+
);
96+
97+
$this->assertFalse($resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the class no longer exists');
98+
}
99+
100+
protected function tearDown()
101+
{
102+
if (!file_exists($this->file)) {
103+
return;
104+
}
105+
106+
unlink($this->file);
107+
}
108+
109+
private function getStaleFileTime()
110+
{
111+
return $this->time - 10;
112+
}
113+
}
114+
115+
class Foo
116+
{
117+
public function __construct($foo)
118+
{
119+
}
120+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.