From d0b600aee2c9e43545233d797d0cc7d4d6f95a07 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 12 Mar 2016 15:10:13 -0500 Subject: [PATCH 1/5] Smart caching system for autowiring to only clear container cache when it's *actually* needed --- .../Compiler/AutowirePass.php | 46 ++++++- .../Config/AutowireServiceResource.php | 76 +++++++++++ .../Tests/Compiler/AutowirePassTest.php | 56 ++++++++ .../Config/AutowireServiceResourceTest.php | 120 ++++++++++++++++++ 4 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 03ef374e5d930..d9f3014518bed 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; @@ -49,6 +50,39 @@ public function process(ContainerBuilder $container) $this->ambiguousServiceTypes = array(); } + /** + * Creates a resource to help know if this service has changed. + * + * @param \ReflectionClass $reflectionClass + * + * @return AutowireServiceResource + */ + public static function createResourceForClass(\ReflectionClass $reflectionClass) + { + $metadata = array(); + + if ($constructor = $reflectionClass->getConstructor()) { + $metadata['__construct'] = self::getResourceMetadataForMethod($constructor); + } + + // todo - when #17608 is merged, could refactor to private function to remove duplication + // of determining valid "setter" methods + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { + $name = $reflectionMethod->getName(); + if (isset($methodsCalled[$name]) || $reflectionMethod->isStatic() || 1 !== $reflectionMethod->getNumberOfParameters() || 0 !== strpos($name, 'set')) { + continue; + } + + $metadata[$name] = self::getResourceMetadataForMethod($reflectionMethod); + } + + return new AutowireServiceResource( + $reflectionClass->name, + $reflectionClass->getFileName(), + $metadata + ); + } + /** * Wires the given definition. * @@ -63,7 +97,7 @@ private function completeDefinition($id, Definition $definition) return; } - $this->container->addClassResource($reflectionClass); + $this->container->addResource(static::createResourceForClass($reflectionClass)); if (!$constructor = $reflectionClass->getConstructor()) { return; @@ -278,4 +312,14 @@ private function addServiceToAmbiguousType($id, $type) } $this->ambiguousServiceTypes[$type][] = $id; } + + static private function getResourceMetadataForMethod(\ReflectionMethod $method) + { + $methodArgumentsMetadata = array(); + foreach ($method->getParameters() as $parameter) { + $methodArgumentsMetadata[] = (string) $parameter; + } + + return $methodArgumentsMetadata; + } } diff --git a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php new file mode 100644 index 0000000000000..58e4e8823812c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; +use Symfony\Component\DependencyInjection\Compiler\AutowirePass; + +class AutowireServiceResource implements SelfCheckingResourceInterface, \Serializable +{ + private $class; + private $filePath; + private $autowiringMetadata = array(); + + public function __construct($class, $path, array $autowiringMetadata) + { + $this->class = $class; + $this->filePath = $path; + $this->autowiringMetadata = $autowiringMetadata; + } + + public function isFresh($timestamp) + { + if (!file_exists($this->filePath)) { + return false; + } + + // has the file *not* been modified? Definitely fresh + if (@filemtime($this->filePath) <= $timestamp) { + return true; + } + + try { + $reflectionClass = new \ReflectionClass($this->class); + } catch (\ReflectionException $e) { + // the class does not exist anymore! + + return false; + } + + $newResource = AutowirePass::createResourceForClass($reflectionClass); + + return $newResource == $this; + } + + public function __toString() + { + return 'service.autowire.'.$this->class; + } + + public function serialize() + { + return serialize(array( + $this->class, + $this->filePath, + $this->autowiringMetadata, + )); + } + + public function unserialize($serialized) + { + list( + $this->class, + $this->filePath, + $this->autowiringMetadata + ) = unserialize($serialized); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 00dbaef3b722e..7e784231ca4b1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -413,6 +413,39 @@ public function testOptionalScalarArgsNotPassedIfLast() $definition->getArguments() ); } + + /** + * @dataProvider getCreateResourceTests + */ + public function testCreateResourceForClass($className, $isEqual) + { + $startingResource = AutowirePass::createResourceForClass( + new \ReflectionClass(__NAMESPACE__.'\ClassForResource') + ); + $newResource = AutowirePass::createResourceForClass( + new \ReflectionClass(__NAMESPACE__.'\\'.$className) + ); + + // hack so the objects don't differ by the class name + $startingReflObject = new \ReflectionObject($startingResource); + $reflProp = $startingReflObject->getProperty('class'); + $reflProp->setAccessible(true); + $reflProp->setValue($startingResource, __NAMESPACE__.'\\'.$className); + + if ($isEqual) { + $this->assertEquals($startingResource, $newResource); + } else { + $this->assertNotEquals($startingResource, $newResource); + } + } + + public function getCreateResourceTests() + { + return array( + ['IdenticalClassResource', true], + ['ClassChangedConstructorArgs', false], + ); + } } class Foo @@ -562,3 +595,26 @@ public function __construct(A $a, $foo = 'default_val', Lille $lille) { } } + +/* + * Classes used for testing createResourceForClass + */ +class ClassForResource +{ + public function __construct($foo, Bar $bar = null) + { + } + + public function setBar(Bar $bar) + { + } +} +class IdenticalClassResource extends ClassForResource +{ +} +class ClassChangedConstructorArgs extends ClassForResource +{ + public function __construct($foo, Bar $bar, $baz) + { + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php b/src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php new file mode 100644 index 0000000000000..5c2704db23958 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Config/AutowireServiceResourceTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Config; + +use Symfony\Component\DependencyInjection\Compiler\AutowirePass; +use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; + +class AutowireServiceResourceTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AutowireServiceResource + */ + private $resource; + private $file; + private $class; + private $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.php'; + $this->time = time(); + touch($this->file, $this->time); + + $this->class = __NAMESPACE__.'\Foo'; + $this->resource = new AutowireServiceResource( + $this->class, + $this->file, + array() + ); + } + + public function testToString() + { + $this->assertSame('service.autowire.'.$this->class, (string) $this->resource); + } + + public function testSerializeUnserialize() + { + $unserialized = unserialize(serialize($this->resource)); + + $this->assertEquals($this->resource, $unserialized); + } + + public function testIsFresh() + { + $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); + $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); + } + + public function testIsFreshForDeletedResources() + { + unlink($this->file); + + $this->assertFalse($this->resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the resource does not exist'); + } + + public function testIsNotFreshChangedResource() + { + $oldResource = new AutowireServiceResource( + $this->class, + $this->file, + array('will_be_different') + ); + + // test with a stale file *and* a resource that *will* be different than the actual + $this->assertFalse($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed'); + } + + public function testIsFreshSameConstructorArgs() + { + $oldResource = AutowirePass::createResourceForClass( + new \ReflectionClass(__NAMESPACE__.'\Foo') + ); + + // test with a stale file *but* the resource will not be changed + $this->assertTrue($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed'); + } + + public function testNotFreshIfClassNotFound() + { + $resource = new AutowireServiceResource( + 'Some\Non\Existent\Class', + $this->file, + array() + ); + + $this->assertFalse($resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the class no longer exists'); + } + + protected function tearDown() + { + if (!file_exists($this->file)) { + return; + } + + unlink($this->file); + } + + private function getStaleFileTime() + { + return $this->time - 10; + } +} + +class Foo +{ + public function __construct($foo) + { + } +} From c706c04359f0af604de94f30100fb10f5161eef5 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 29 Mar 2016 08:47:08 -0400 Subject: [PATCH 2/5] Fixes thanks to Stof --- .../Component/DependencyInjection/Compiler/AutowirePass.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index d9f3014518bed..26f437941020d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -69,7 +69,7 @@ public static function createResourceForClass(\ReflectionClass $reflectionClass) // of determining valid "setter" methods foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { $name = $reflectionMethod->getName(); - if (isset($methodsCalled[$name]) || $reflectionMethod->isStatic() || 1 !== $reflectionMethod->getNumberOfParameters() || 0 !== strpos($name, 'set')) { + if ($reflectionMethod->isStatic() || 1 !== $reflectionMethod->getNumberOfParameters() || 0 !== strpos($name, 'set')) { continue; } @@ -97,7 +97,9 @@ private function completeDefinition($id, Definition $definition) return; } - $this->container->addResource(static::createResourceForClass($reflectionClass)); + if ($this->container->isTrackingResources()) { + $this->container->addResource(static::createResourceForClass($reflectionClass)); + } if (!$constructor = $reflectionClass->getConstructor()) { return; From 011e20351675eda2ac7c5fbdfaba57a62789f0f3 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 29 Mar 2016 08:51:29 -0400 Subject: [PATCH 3/5] Implementing a method on symfony/config 2.8's ResourceInterface for 2.8 compat --- .../Config/AutowireServiceResource.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php index 58e4e8823812c..b74eff48d0000 100644 --- a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php +++ b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php @@ -73,4 +73,12 @@ public function unserialize($serialized) $this->autowiringMetadata ) = unserialize($serialized); } + + /** + * @deprecated Implemented for compatibility with Symfony 2.8 + */ + public function getResource() + { + return $this->filePath; + } } From 49271e86f999ad84bb4f8787b844973f7c22f4fc Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 1 Apr 2016 11:20:33 -0400 Subject: [PATCH 4/5] Coding standards changes - via fabpot --- .../Compiler/AutowirePass.php | 6 +----- .../Config/AutowireServiceResource.php | 16 +++------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 26f437941020d..1d4d15e6aa37c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -76,11 +76,7 @@ public static function createResourceForClass(\ReflectionClass $reflectionClass) $metadata[$name] = self::getResourceMetadataForMethod($reflectionMethod); } - return new AutowireServiceResource( - $reflectionClass->name, - $reflectionClass->getFileName(), - $metadata - ); + return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata); } /** diff --git a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php index b74eff48d0000..0d8cc640b7912 100644 --- a/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php +++ b/src/Symfony/Component/DependencyInjection/Config/AutowireServiceResource.php @@ -46,9 +46,7 @@ public function isFresh($timestamp) return false; } - $newResource = AutowirePass::createResourceForClass($reflectionClass); - - return $newResource == $this; + return AutowirePass::createResourceForClass($reflectionClass); } public function __toString() @@ -58,20 +56,12 @@ public function __toString() public function serialize() { - return serialize(array( - $this->class, - $this->filePath, - $this->autowiringMetadata, - )); + return serialize(array($this->class, $this->filePath, $this->autowiringMetadata)); } public function unserialize($serialized) { - list( - $this->class, - $this->filePath, - $this->autowiringMetadata - ) = unserialize($serialized); + list($this->class, $this->filePath, $this->autowiringMetadata) = unserialize($serialized); } /** From 56b4bf9f1f11f2ab45fb4b6c0b4852fa2bd1b7fb Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 1 Apr 2016 11:33:11 -0400 Subject: [PATCH 5/5] Trying to get more fine-grained over the actual information we need --- .../DependencyInjection/Compiler/AutowirePass.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 1d4d15e6aa37c..dc548afed3387 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -315,7 +315,18 @@ static private function getResourceMetadataForMethod(\ReflectionMethod $method) { $methodArgumentsMetadata = array(); foreach ($method->getParameters() as $parameter) { - $methodArgumentsMetadata[] = (string) $parameter; + try { + $class = $parameter->getClass(); + } catch (\ReflectionException $e) { + // type-hint is against a non-existent class + $class = false; + } + + $methodArgumentsMetadata[] = array( + 'class' => $class, + 'isOptional' => $parameter->isOptional(), + 'defaultValue' => $parameter->isOptional() ? $parameter->getDefaultValue() : null, + ); } return $methodArgumentsMetadata; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy