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 4b521c5

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

File tree

Expand file treeCollapse file tree

3 files changed

+238
-1
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+238
-1
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+25-1Lines changed: 25 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,29 @@ 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+
$constructor = $reflectionClass->getConstructor();
63+
$constructorParams = $constructor ? $constructor->getParameters() : array();
64+
$constructorArgumentsForResource = array();
65+
foreach ($constructorParams as $parameter) {
66+
$constructorArgumentsForResource[] = $parameter->getClass();
67+
}
68+
69+
return new AutowireServiceResource(
70+
$reflectionClass->name,
71+
$reflectionClass->getFileName(),
72+
$constructorArgumentsForResource
73+
);
74+
}
75+
5276
/**
5377
* Wires the given definition.
5478
*
@@ -63,7 +87,7 @@ private function completeDefinition($id, Definition $definition)
6387
return;
6488
}
6589

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

6892
if (!$constructor = $reflectionClass->getConstructor()) {
6993
return;
+91Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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 $constructorArguments;
22+
private $createResourceCallback;
23+
24+
public function __construct($class, $path)
25+
{
26+
$this->class = $class;
27+
$this->filePath = $path;
28+
}
29+
30+
/**
31+
* An array of arguments: 0 => type-hinted class (or null of there is no type-hint).
32+
*
33+
* @param array $constructorArguments
34+
*/
35+
public function setConstructorArguments(array $constructorArguments)
36+
{
37+
$this->constructorArguments = $constructorArguments;
38+
}
39+
40+
public function isFresh($timestamp)
41+
{
42+
if (!file_exists($this->filePath)) {
43+
return false;
44+
}
45+
46+
// has the file *not* been modified? Definitely fresh
47+
if (@filemtime($this->filePath) <= $timestamp) {
48+
return true;
49+
}
50+
51+
if ($this->createResourceCallback) {
52+
// this override is used for testing
53+
$newResource = call_user_func($this->createResourceCallback, new \ReflectionClass($this->class));
54+
} else {
55+
$newResource = AutowirePass::createResourceForClass(new \ReflectionClass($this->class));
56+
}
57+
58+
return $newResource == $this;
59+
}
60+
61+
/**
62+
* @internal
63+
*/
64+
public function setCreateResourceCallback($createResourceCallback)
65+
{
66+
$this->createResourceCallback = $createResourceCallback;
67+
}
68+
69+
public function __toString()
70+
{
71+
return 'service.autowire.'.$this->class;
72+
}
73+
74+
public function serialize()
75+
{
76+
return serialize(array(
77+
$this->class,
78+
$this->filePath,
79+
$this->constructorArguments,
80+
));
81+
}
82+
83+
public function unserialize($serialized)
84+
{
85+
list(
86+
$this->class,
87+
$this->filePath,
88+
$this->constructorArguments
89+
) = unserialize($serialized);
90+
}
91+
}
+122Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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\Config\AutowireServiceResource;
15+
16+
class AutowireServiceResourceTest extends \PHPUnit_Framework_TestCase
17+
{
18+
/**
19+
* @var AutowireServiceResource
20+
*/
21+
private $resource;
22+
private $file;
23+
private $class;
24+
private $time;
25+
private $constructorArgs;
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 = 'Symfony\Component\DependencyInjection\Tests\Config\FooReplicator';
34+
$this->resource = new AutowireServiceResource(
35+
$this->class,
36+
$this->file
37+
);
38+
$this->constructorArgs = array(
39+
null, // no type-hint on this argument
40+
'AppBundle\Service\Foo',
41+
);
42+
$this->resource->setConstructorArguments($this->constructorArgs);
43+
}
44+
45+
public function testToString()
46+
{
47+
$this->assertSame('service.autowire.'.$this->class, (string) $this->resource);
48+
}
49+
50+
public function testSerializeUnserialize()
51+
{
52+
$unserialized = unserialize(serialize($this->resource));
53+
54+
$this->assertEquals($this->resource, $unserialized);
55+
}
56+
57+
public function testIsFresh()
58+
{
59+
$this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second');
60+
$this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed');
61+
$this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated');
62+
}
63+
64+
public function testIsFreshForDeletedResources()
65+
{
66+
unlink($this->file);
67+
68+
$this->assertFalse($this->resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist');
69+
}
70+
71+
public function testIsFreshChangedConstructorArgs()
72+
{
73+
$newResource = new AutowireServiceResource(
74+
$this->class,
75+
$this->file
76+
);
77+
// a new 3rd argument was added!
78+
$args = $this->constructorArgs;
79+
$args[] = 'AppBundle\Service\Bar';
80+
$newResource->setConstructorArguments($args);
81+
82+
// "fake" the return value
83+
$callback = function(\ReflectionClass $reflectionClass) use ($newResource) {
84+
return $newResource;
85+
};
86+
$this->resource->setCreateResourceCallback($callback);
87+
88+
$this->assertFalse($this->resource->isFresh($this->time - 10), '->isFresh() returns false if the constructor arguments have changed');
89+
}
90+
91+
public function testIsFreshSameConstructorArgs()
92+
{
93+
$newResource = new AutowireServiceResource(
94+
$this->class,
95+
$this->file
96+
);
97+
98+
$newResource->setConstructorArguments($this->constructorArgs);
99+
100+
// "fake" the return value
101+
$callback = function(\ReflectionClass $reflectionClass) use ($newResource) {
102+
return $newResource;
103+
};
104+
105+
$this->resource->setCreateResourceCallback($callback);
106+
$this->assertFalse($this->resource->isFresh($this->time - 10), '->isFresh() returns false if the constructor arguments have changed');
107+
}
108+
109+
protected function tearDown()
110+
{
111+
if (!file_exists($this->file)) {
112+
return;
113+
}
114+
115+
unlink($this->file);
116+
}
117+
}
118+
119+
// exists just so that some reflection in the tests doesn't fail
120+
class FooReplicator
121+
{
122+
}

0 commit comments

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