10000 Smart caching system for autowiring to only clear container cache whe… · symfony/symfony@4b521c5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4b521c5

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

File tree

3 files changed

+238
-1
lines changed

3 files changed

+238
-1
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespac 10000 e 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;
Lines 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+
}
Lines 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