diff --git a/composer.json b/composer.json index 455a68cf74f7c..33f75b4684e2d 100644 --- a/composer.json +++ b/composer.json @@ -86,7 +86,8 @@ "egulias/email-validator": "~1.2", "symfony/polyfill-apcu": "~1.1", "symfony/security-acl": "~2.8|~3.0", - "phpdocumentor/reflection-docblock": "^3.0" + "phpdocumentor/reflection-docblock": "^3.0", + "goaop/parser-reflection": "^1.0" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0" diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index fa20d04db291b..eda8d3700bddc 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Util\ReflectionHelper; /** * Guesses constructor arguments of services definitions and try to instantiate services if necessary. @@ -29,6 +30,12 @@ class AutowirePass implements CompilerPassInterface private $definedTypes = array(); private $types; private $ambiguousServiceTypes = array(); + private $reflectionHelper; + + public function __construct(ReflectionHelper $reflectionHelper = null) + { + $this->reflectionHelper = $reflectionHelper ?: new ReflectionHelper(); + } /** * {@inheritdoc} @@ -293,11 +300,7 @@ private function getReflectionClass($id, Definition $definition) $class = $this->container->getParameterBag()->resolveValue($class); - try { - return $this->reflectionClasses[$id] = new \ReflectionClass($class); - } catch (\ReflectionException $reflectionException) { - // return null - } + return $this->reflectionClasses[$id] = $this->reflectionHelper->getReflectionClass($class); } private function addServiceToAmbiguousType($id, $type) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 7e784231ca4b1..0008bd52172ab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\Compiler\AutowirePass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Util\ReflectionHelper; /** * @author Kévin Dunglas @@ -446,6 +447,24 @@ public function getCreateResourceTests() ['ClassChangedConstructorArgs', false], ); } + + public function testIgnoreServiceWithClassNotExisting() + { + $container = new ContainerBuilder(); + + $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass'); + + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $oldSetup = ReflectionHelper::preferStaticReflection(true); + + $pass->process($container); + ReflectionHelper::preferStaticReflection($oldSetup); + + $this->assertTrue($container->hasDefinition('bar')); + } } class Foo diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php new file mode 100644 index 0000000000000..7e9238f22301b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Bug\NotExistClass; + +class OptionalServiceClass extends NotExistClass +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Util/ReflectionHelper.php b/src/Symfony/Component/DependencyInjection/Util/ReflectionHelper.php new file mode 100644 index 0000000000000..0348313942b7f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Util/ReflectionHelper.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Util; + +use Go\ParserReflection\ReflectionClass; + +/** + * ReflectionHelper provides method for native or static reflection of class. + * + * @author Martin Hasoň + */ +class ReflectionHelper +{ + private static $staticReflection = false; + private static $reflections = array(); + private $existsGoAopReflection; + + public function __construct() + { + $this->existsGoAopReflection = class_exists(ReflectionClass::class); + } + + public static function preferStaticReflection($use) + { + $oldSetup = self::$staticReflection; + self::$staticReflection = (bool) $use; + + return $oldSetup; + } + + /** + * Returns reflection instance for class. + * + * @param string $class Full name of the class + * + * @return \ReflectionClass|false + */ + public function getReflectionClass($class) + { + if (isset(self::$reflections[$class])) { + return self::$reflections[$class]; + } + + if (!self::$staticReflection || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) { + return $this->getNativeReflection($class); + } + + if ($this->existsGoAopReflection) { + return $this->getGoParserReflection($class); + } + + return $this->getNativeReflection($class); + } + + private function getNativeReflection($class) + { + try { + $reflector = self::$reflections[$class] = new \ReflectionClass($class); + } catch (\ReflectionException $e) { + $reflector = false; + } + + return self::$reflections[$class] = $reflector; + } + + private function getGoParserReflection($class) + { + try { + $reflectionClass = new ReflectionClass($class); + $reflectionClass->__toString(); // checks that reflection is complete and valid + + $reflector = self::$reflections[$class] = $reflectionClass; + } catch (\InvalidArgumentException $e) { + $reflector = false; + } + + return self::$reflections[$class] = $reflector; + } +} diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index 1f4faa8a04998..e8a04c723063c 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -21,12 +21,14 @@ "require-dev": { "symfony/yaml": "~2.8|~3.0", "symfony/config": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0" + "symfony/expression-language": "~2.8|~3.0", + "goaop/parser-reflection": "^1.0" }, "suggest": { "symfony/yaml": "", "symfony/config": "", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "goaop/parser-reflection": "Use static reflection for autowiring" }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" },