8000 WIP Smart caching system to only clear container cache when it's *act… · symfony/symfony@d40c146 · GitHub
[go: up one dir, main page]

Skip to content

Commit d40c146

Browse files
committed
WIP Smart caching system to only clear container cache when it's *actually* needed
1 parent b868feb commit d40c146

File tree

6 files changed

+338
-1
lines changed

6 files changed

+338
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,9 @@
5454
<tag name="config_cache.resource_checker" priority="-990" />
5555
</service>
5656

57+
<service class="Symfony\Component\DependencyInjection\Config\AutowireResourceChecker" public="false">
58+
<tag name="config_cache.resource_checker" />
59+
</service>
60+
5761
</services>
5862
</container>

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

Lines changed: 27 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;
@@ -23,6 +24,9 @@
2324
*/
2425
class AutowirePass implements CompilerPassInterface
2526
{
27+
/**
28+
* @var ContainerBuilder
29+
*/
2630
private $container;
2731
private $reflectionClasses = array();
2832
private $definedTypes = array();
@@ -49,6 +53,28 @@ public function process(ContainerBuilder $container)
4953
$this->ambiguousServiceTypes = array();
5054
}
5155

56+
/**
57+
* Creates a resource to help know if this service has changed.
58+
*
59+
* @param \ReflectionClass $reflectionClass
60+
* @return AutowireServiceResource
61+
*/
62+
public function createResourceForClass(\ReflectionClass $reflectionClass)
63+
{
64+
$constructor = $reflectionClass->getConstructor();
65+
$constructorParams = $constructor ? $constructor->getParameters() : array();
66+
$constructorArgumentsForResource = array();
67+
foreach ($constructorParams as $parameter) {
68+
$constructorArgumentsForResource[] = $parameter->getClass();
69+
}
70+
71+
return new AutowireServiceResource(
72+
$reflectionClass->name,
73+
$reflectionClass->getFileName(),
74+
$constructorArgumentsForResource
75+
);
76+
}
77+
5278
/**
5379
* Wires the given definition.
5480
*
@@ -63,7 +89,7 @@ private function completeDefinition($id, Definition $definition)
6389
return;
6490
}
6591

66-
$this->container->addClassResource($reflectionClass);
92+
$this->container->addResource($this->createResourceForClass($reflectionClass));
6793

6894
if (!$constructor = $reflectionClass->getConstructor()) {
6995
return;
Lines changed: 54 additions & 0 deletions
10000
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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\ResourceInterface;
15+
use Symfony\Component\Config\ResourceCheckerInterface;
16+
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
17+
18+
class AutowireResourceChecker implements ResourceCheckerInterface
19+
{
20+
private $autowirePass;
21+
22+
public function supports(ResourceInterface $metadata)
23+
{
24+
var_dump($metadata);
25+
return $metadata instanceof AutowireServiceResource;
26+
}
27+
28+
public function isFresh(ResourceInterface $resource, $timestamp)
29+
{
30+
/** @var AutowireServiceResource $resource */
31+
$path = $resource->getFilePath();
32+
33+
if (!file_exists($path)) {
34+
return false;
35+
}
36+
37+
// has the file *not* been modified? Definitely fresh
38+
if (@filemtime($path) <= $timestamp) {
39+
return true;
40+
}
41+
42+
43+
$pass = $this->autowirePass ? $this->autowirePass : new AutowirePass();
44+
$newResource = $pass->createResourceForClass(new \ReflectionClass($resource->getClass()));
45+
46+
47+
return $newResource == $resource;
48+
}
49+
50+
public function setAutowirePass(AutowirePass $autowirePass)
51+
{
52+
$this->autowirePass = $autowirePass;
53+
}
54+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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\ResourceInterface;
15+
16+
class AutowireServiceResource implements ResourceInterface, \Serializable
17+
{
18+
private $class;
19+
private $filePath;
20+
private $constructorArguments;
21+
22+
public function __construct($class, $path)
23+
{
24+
$this->class = $class;
25+
$this->filePath = $path;
26+
}
27+
28+
/**
29+
* An array of arguments: 0 => type-hinted class (or null of there is no type-hint)
30+
*
31+
* @param array $constructorArguments
32+
*/
33+
public function setConstructorArguments(array $constructorArguments)
34+
{
35+
$this->constructorArguments = $constructorArguments;
36+
}
37+
38+
public function getFilePath()
39+
{
40+
return $this->filePath;
41+
}
42+
43+
public function getClass()
44+
{
45+
return $this->class;
46+
}
47+
48+
public function __toString()
49+
{
50+
return 'service.autowire.'.$this->class;
51+
}
52+
53+
public function serialize()
54+
{
55+
return serialize(array(
56+
$this->class,
57+
$this->filePath,
58+
$this->constructorArguments
59+
));
60+
}
61+
62+
public function unserialize($serialized)
63+
{
64+
list(
65+
$this->class,
66+
$this->filePath,
67+
$this->constructorArguments
68+
) = unserialize($serialized);
69+
}
70+
71+
// class name
72+
// file name (to see if it's even been modified?)
73+
// construct arguments + classes
74+
// all setters with arguments + classes
75+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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\AutowireResourceChecker;
15+
use Symfony\Component\DependencyInjection\Config\AutowireServiceResource;
16+
17+
class AutowireResourceCheckerTest extends \PHPUnit_Framework_TestCase
18+
{
19+
/**
20+
* @var AutowireServiceResource
21+
*/
22+
private $resource;
23+
private $file;
24+
private $class;
25+
private $time;
26+
private $constructorArgs;
27+
28+
protected function setUp()
29+
{
30+
$this->file = realpath(sys_get_temp_dir()).'/tmp.php';
31+
$this->time = time();
32+
touch($this->file, $this->time);
33+
34+
$this->class = __NAMESPACE__.'\FooReplicator';
35+
$this->resource = new AutowireServiceResource(
36+
$this->class,
37+
$this->file
38+
);
39+
$this->constructorArgs = array(
40+
null, // no type-hint on this argument
41+
'AppBundle\Entity\Foo'
42+
);
43+
$this->resource->setConstructorArguments($this->constructorArgs);
44+
}
45+
46+
protected function tearDown()
47+
{
48+
if (!file_exists($this->file)) {
49+
return;
50+
}
51+
52+
unlink($this->file);
53+
}
54+
55+
public function testIsFresh()
56+
{
57+
$checker = new AutowireResourceChecker();
58+
59+
$this->assertTrue($checker->isFresh($this->resource, $this->time), '->isFresh() returns true if the resource has not changed in same second');
60+
$this->assertTrue($checker->isFresh($this->resource, $this->time + 10), '->isFresh() returns true if the resource has not changed');
61+
$this->assertFalse($checker->isFresh($this->resource, $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+
$checker = new AutowireResourceChecker();
69+
$this->assertFalse($checker->isFresh($this->resource, $this->time), '->isFresh() returns false if the resource does not exist');
70+
}
71+
72+
public function testIsFreshChangedConstructorArgs()
73+
{
74+
$newResource = new AutowireServiceResource(
75+
$this->class,
76+
$this->file
77+
);
78+
// a new 3rd argument was added!
79+
$args = $this->constructorArgs;
80+
$args[] = 'AppBundle\Entity\Bar';
81+
$newResource->setConstructorArguments($args);
82+
83+
// mock so that this is returned as the "new" resource
84+
$autowirePass = $this->getMock('Symfony\Component\DependencyInjection\Compiler\AutowirePass');
85+
$autowirePass->expects($this->once())
86+
->method('createResourceForClass')
87+
->will($this->returnValue($newResource));
88+
89+
$checker = new AutowireResourceChecker();
90+
$checker->setAutowirePass($autowirePass);
91+
$this->assertFalse($checker->isFresh($this->resource, $this->time-10), '->isFresh() returns false if the constructor arguments have changed');
92+
}
93+
94+
public function testIsFreshSameConstructorArgs()
95+
{
96+
$newResource = new AutowireServiceResource(
97+
$this->class,
98+
$this->file
99+
);
100+
101+
$newResource->setConstructorArguments($this->constructorArgs);
102+
103+
// mock so that this is returned as the "new" resource
104+
$autowirePass = $this->getMock('Symfony\Component\DependencyInjection\Compiler\AutowirePass');
105+
$autowirePass->expects($this->once())
106+
->method('createResourceForClass')
107+
->will($this->returnValue($newResource));
108+
109+
$checker = new AutowireResourceChecker();
110+
$checker->setAutowirePass($autowirePass);
111+
$this->assertFalse($checker->isFresh($this->resource, $this->time-10), '->isFresh() returns false if the constructor arguments have changed');
112+
}
113+
}
114+
115+
// exists just so that some reflection in the tests doesn't fail
116+
class FooReplicator
117+
{
118+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
25+
protected function setUp()
26+
{
27+
$this->file = '/tmp/foo';
28+
$this->class = 'AppBundle\Service\FooReplicator';
29+
$this->resource = new AutowireServiceResource(
30+
$this->class,
31+
$this->file
32+
);
33+
$this->resource->setConstructorArguments(array(
34+
null, // no type-hint on this argument
35+
'AppBundle\Entity\Foo'
36+
));
37+
}
38+
39+
public function testGetFilePath()
40+
{
41+
$this->assertSame($this->file, $this->resource->getFilePath());
42+
}
43+
44+
public function testGetClass()
45+
{
46+
$this->assertSame($this->class, $this->resource->getClass());
47+
}
48+
49+
public function testToString()
50+
{
51+
$this->assertSame('service.autowire.'.$this->class, (string) $this->resource);
52+
}
53+
54+
public function testSerializeUnserialize()
55+
{
56+
$unserialized = unserialize(serialize($this->resource));
57+
58+
$this->assertEquals($this->resource, $unserialized);
59+
}
60+
}

0 commit comments

Comments
 (0)
0