[DI] Add AutowireRequiredMethodsPass to fix bindings for @required methods

This commit is contained in:
Nicolas Grekas 2017-09-24 00:04:06 +02:00
parent e84d2d0943
commit dc55dd2c99
9 changed files with 535 additions and 401 deletions

View File

@ -90,7 +90,7 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
{
if (is_string($factory = $definition->getFactory())) {
if (!function_exists($factory)) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": function "%s" does not exist.', $this->currentId, $factory));
throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory));
}
$r = new \ReflectionFunction($factory);
if (false !== $r->getFileName() && file_exists($r->getFileName())) {
@ -108,7 +108,7 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
$class = $definition->getClass();
}
if ('__construct' === $method) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
}
return $this->getReflectionMethod(new Definition($class), $method);
@ -117,14 +117,14 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
$class = $definition->getClass();
if (!$r = $this->container->getReflectionClass($class)) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class));
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
}
if (!$r = $r->getConstructor()) {
if ($required) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class)));
throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class)));
}
} elseif (!$r->isPublic()) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class)));
throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class)));
}
return $r;
@ -145,20 +145,20 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
}
if (!$class = $definition->getClass()) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": the class is not set.', $this->currentId));
throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId));
}
if (!$r = $this->container->getReflectionClass($class)) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class));
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
}
if (!$r->hasMethod($method)) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
}
$r = $r->getMethod($method);
if (!$r->isPublic()) {
throw new RuntimeException(sprintf('Unable to resolve service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
}
return $r;

View File

@ -130,7 +130,6 @@ class AutowirePass extends AbstractRecursivePass
return $value;
}
$autowiredMethods = $this->getMethodsToAutowire($reflectionClass);
$methodCalls = $value->getMethodCalls();
try {
@ -143,7 +142,7 @@ class AutowirePass extends AbstractRecursivePass
array_unshift($methodCalls, array($constructor, $value->getArguments()));
}
$methodCalls = $this->autowireCalls($reflectionClass, $methodCalls, $autowiredMethods);
$methodCalls = $this->autowireCalls($reflectionClass, $methodCalls);
if ($constructor) {
list(, $arguments) = array_shift($methodCalls);
@ -161,61 +160,18 @@ class AutowirePass extends AbstractRecursivePass
}
/**
* Gets the list of methods to autowire.
*
* @param \ReflectionClass $reflectionClass
*
* @return \ReflectionMethod[]
*/
private function getMethodsToAutowire(\ReflectionClass $reflectionClass)
{
$methodsToAutowire = array();
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
$r = $reflectionMethod;
if ($r->isConstructor()) {
continue;
}
while (true) {
if (false !== $doc = $r->getDocComment()) {
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
$methodsToAutowire[strtolower($reflectionMethod->name)] = $reflectionMethod;
break;
}
if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) {
break;
}
}
try {
$r = $r->getPrototype();
} catch (\ReflectionException $e) {
break; // method has no prototype
}
}
}
return $methodsToAutowire;
}
/**
* @param \ReflectionClass $reflectionClass
* @param array $methodCalls
* @param \ReflectionMethod[] $autowiredMethods
* @param array $methodCalls
*
* @return array
*/
private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls, array $autowiredMethods)
private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls)
{
foreach ($methodCalls as $i => $call) {
list($method, $arguments) = $call;
if ($method instanceof \ReflectionFunctionAbstract) {
$reflectionMethod = $method;
} elseif (isset($autowiredMethods[$lcMethod = strtolower($method)]) && $autowiredMethods[$lcMethod]->isPublic()) {
$reflectionMethod = $autowiredMethods[$lcMethod];
unset($autowiredMethods[$lcMethod]);
} else {
$reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method);
}
@ -227,16 +183,6 @@ class AutowirePass extends AbstractRecursivePass
}
}
foreach ($autowiredMethods as $lcMethod => $reflectionMethod) {
$method = $reflectionMethod->name;
if (!$reflectionMethod->isPublic()) {
$class = $reflectionClass->name;
throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
}
$methodCalls[] = array($method, $this->autowireMethod($reflectionMethod, array()));
}
return $methodCalls;
}

View File

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Definition;
/**
* Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class AutowireRequiredMethodsPass extends AbstractRecursivePass
{
/**
* {@inheritdoc}
*/
protected function processValue($value, $isRoot = false)
{
$value = parent::processValue($value, $isRoot);
if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
return $value;
}
if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
return $value;
}
$alreadyCalledMethods = array();
foreach ($value->getMethodCalls() as list($method)) {
$alreadyCalledMethods[strtolower($method)] = true;
}
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
$r = $reflectionMethod;
if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) {
continue;
}
while (true) {
if (false !== $doc = $r->getDocComment()) {
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
$value->addMethodCall($reflectionMethod->name);
break;
}
if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) {
break;
}
}
try {
$r = $r->getPrototype();
} catch (\ReflectionException $e) {
break; // method has no prototype
}
}
}
return $value;
}
}

View File

@ -58,6 +58,7 @@ class PassConfig
new CheckDefinitionValidityPass(),
new RegisterServiceSubscribersPass(),
new ResolveNamedArgumentsPass(),
new AutowireRequiredMethodsPass(),
new ResolveBindingsPass(),
$autowirePass = new AutowirePass(false),
new ResolveServiceSubscribersPass(),

View File

@ -61,11 +61,11 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass
}
}
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
}
if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) {
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, gettype($argument)));
throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, gettype($argument)));
}
foreach ($parameters as $j => $p) {
@ -76,7 +76,7 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass
}
}
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
}
if ($resolvedArguments !== $call[1]) {

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
use Symfony\Component\DependencyInjection\TypedReference;
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
@ -174,7 +177,7 @@ class AutowirePassTest extends TestCase
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
* @expectedExceptionMessage Unable to resolve service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public.
* @expectedExceptionMessage Invalid service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public.
*/
public function testPrivateConstructorThrowsAutowireException()
{
@ -554,6 +557,7 @@ class AutowirePassTest extends TestCase
;
(new ResolveClassPass())->process($container);
(new AutowireRequiredMethodsPass())->process($container);
(new AutowirePass())->process($container);
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
@ -590,6 +594,7 @@ class AutowirePassTest extends TestCase
;
(new ResolveClassPass())->process($container);
(new AutowireRequiredMethodsPass())->process($container);
(new AutowirePass())->process($container);
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
@ -687,6 +692,8 @@ class AutowirePassTest extends TestCase
$aDefinition = $container->register('setter_injection_collision', SetterInjectionCollision::class);
$aDefinition->setAutowired(true);
(new AutowireRequiredMethodsPass())->process($container);
$pass = new AutowirePass();
$pass->process($container);
}
@ -770,6 +777,7 @@ class AutowirePassTest extends TestCase
}
(new ResolveClassPass())->process($container);
(new AutowireRequiredMethodsPass())->process($container);
(new AutowirePass())->process($container);
}
@ -778,7 +786,7 @@ class AutowirePassTest extends TestCase
return array(
array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class cannot be loaded.'),
array('setDifferentNamespace', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setDifferentNamespace()" references class "stdClass" but no such service exists. It cannot be auto-registered because it is from a different root namespace.'),
array(null, 'Cannot autowire service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'),
array(null, 'Invalid service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'),
);
}
@ -859,333 +867,3 @@ class AutowirePassTest extends TestCase
$pass->process($container);
}
}
class Foo
{
}
class Bar
{
public function __construct(Foo $foo)
{
}
}
interface AInterface
{
}
class A implements AInterface
{
public static function create(Foo $foo)
{
}
}
class B extends A
{
}
class C
{
public function __construct(A $a)
{
}
}
interface DInterface
{
}
interface EInterface extends DInterface
{
}
interface IInterface
{
}
class I implements IInterface
{
}
class F extends I implements EInterface
{
}
class G
{
public function __construct(DInterface $d, EInterface $e, IInterface $i)
{
}
}
class H
{
public function __construct(B $b, DInterface $d)
{
}
}
class D
{
public function __construct(A $a, DInterface $d)
{
}
}
class E
{
public function __construct(D $d = null)
{
}
}
class J
{
public function __construct(I $i)
{
}
}
interface CollisionInterface
{
}
class CollisionA implements CollisionInterface
{
}
class CollisionB implements CollisionInterface
{
}
class CannotBeAutowired
{
public function __construct(CollisionInterface $collision)
{
}
}
class CannotBeAutowiredForwardOrder
{
public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
{
}
}
class CannotBeAutowiredReverseOrder
{
public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
{
}
}
class Lille
{
}
class Dunglas
{
public function __construct(Lille $l)
{
}
}
class LesTilleuls
{
public function __construct(Dunglas $j, Dunglas $k)
{
}
}
class OptionalParameter
{
public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
{
}
}
class BadTypeHintedArgument
{
public function __construct(Dunglas $k, NotARealClass $r)
{
}
}
class BadParentTypeHintedArgument
{
public function __construct(Dunglas $k, OptionalServiceClass $r)
{
}
}
class NotGuessableArgument
{
public function __construct(Foo $k)
{
}
}
class NotGuessableArgumentForSubclass
{
public function __construct(A $k)
{
}
}
class MultipleArguments
{
public function __construct(A $k, $foo, Dunglas $dunglas)
{
}
}
class MultipleArgumentsOptionalScalar
{
public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
{
}
}
class MultipleArgumentsOptionalScalarLast
{
public function __construct(A $a, Lille $lille, $foo = 'some_val')
{
}
}
class MultipleArgumentsOptionalScalarNotReallyOptional
{
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)
{
}
}
class SetterInjection extends SetterInjectionParent
{
/**
* @required
*/
public function setFoo(Foo $foo)
{
// should be called
}
/** @inheritdoc*/
public function setDependencies(Foo $foo, A $a)
{
// should be called
}
/** {@inheritdoc} */
public function setWithCallsConfigured(A $a)
{
// this method has a calls configured on it
}
public function notASetter(A $a)
{
// should be called only when explicitly specified
}
/**
* @required*/
public function setChildMethodWithoutDocBlock(A $a)
{
}
}
class SetterInjectionParent
{
/** @required*/
public function setDependencies(Foo $foo, A $a)
{
// should be called
}
public function notASetter(A $a)
{
// @required should be ignored when the child does not add @inheritdoc
}
/** @required <tab> prefix is on purpose */
public function setWithCallsConfigured(A $a)
{
}
/** @required */
public function setChildMethodWithoutDocBlock(A $a)
{
}
}
class SetterInjectionCollision
{
/**
* @required
*/
public function setMultipleInstancesForOneArg(CollisionInterface $collision)
{
// The CollisionInterface cannot be autowired - there are multiple
// should throw an exception
}
}
class NotWireable
{
public function setNotAutowireable(NotARealClass $n)
{
}
public function setBar()
{
}
public function setOptionalNotAutowireable(NotARealClass $n = null)
{
}
public function setDifferentNamespace(\stdClass $n)
{
}
public function setOptionalNoTypeHint($foo = null)
{
}
public function setOptionalArgNoAutowireable($other = 'default_val')
{
}
/** @required */
protected function setProtectedMethod(A $a)
{
}
}
class PrivateConstructor
{
private function __construct()
{
}
}

View File

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
class AutowireRequiredMethodsPassTest extends TestCase
{
public function testSetterInjection()
{
$container = new ContainerBuilder();
$container->register(Foo::class);
$container->register(A::class);
$container->register(CollisionA::class);
$container->register(CollisionB::class);
// manually configure *one* call, to override autowiring
$container
->register('setter_injection', SetterInjection::class)
->setAutowired(true)
->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2'));
(new ResolveClassPass())->process($container);
(new AutowireRequiredMethodsPass())->process($container);
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
$this->assertEquals(
array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'),
array_column($methodCalls, 0)
);
// test setWithCallsConfigured args
$this->assertEquals(
array('manual_arg1', 'manual_arg2'),
$methodCalls[0][1]
);
// test setFoo args
$this->assertEquals(array(), $methodCalls[1][1]);
}
public function testExplicitMethodInjection()
{
$container = new ContainerBuilder();
$container->register(Foo::class);
$container->register(A::class);
$container->register(CollisionA::class);
$container->register(CollisionB::class);
$container
->register('setter_injection', SetterInjection::class)
->setAutowired(true)
->addMethodCall('notASetter', array());
(new ResolveClassPass())->process($container);
(new AutowireRequiredMethodsPass())->process($container);
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
$this->assertEquals(
array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'),
array_column($methodCalls, 0)
);
$this->assertEquals(array(), $methodCalls[0][1]);
}
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\TypedReference;
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
class ResolveBindingsPassTest extends TestCase
{
public function testProcess()
@ -79,4 +82,17 @@ class ResolveBindingsPassTest extends TestCase
$this->assertEquals(array($typedRef), $container->getDefinition('def1')->getArguments());
$this->assertEquals(array(new Reference('foo')), $container->getDefinition('def2')->getArguments());
}
public function testScalarSetter()
{
$container = new ContainerBuilder();
$definition = $container->autowire('foo', ScalarSetter::class);
$definition->setBindings(array('$defaultLocale' => 'fr'));
(new AutowireRequiredMethodsPass())->process($container);
(new ResolveBindingsPass())->process($container);
$this->assertEquals(array(array('setDefaultLocale', array('fr'))), $definition->getMethodCalls());
}
}

View File

@ -0,0 +1,343 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
class Foo
{
}
class Bar
{
public function __construct(Foo $foo)
{
}
}
interface AInterface
{
}
class A implements AInterface
{
public static function create(Foo $foo)
{
}
}
class B extends A
{
}
class C
{
public function __construct(A $a)
{
}
}
interface DInterface
{
}
interface EInterface extends DInterface
{
}
interface IInterface
{
}
class I implements IInterface
{
}
class F extends I implements EInterface
{
}
class G
{
public function __construct(DInterface $d, EInterface $e, IInterface $i)
{
}
}
class H
{
public function __construct(B $b, DInterface $d)
{
}
}
class D
{
public function __construct(A $a, DInterface $d)
{
}
}
class E
{
public function __construct(D $d = null)
{
}
}
class J
{
public function __construct(I $i)
{
}
}
interface CollisionInterface
{
}
class CollisionA implements CollisionInterface
{
}
class CollisionB implements CollisionInterface
{
}
class CannotBeAutowired
{
public function __construct(CollisionInterface $collision)
{
}
}
class CannotBeAutowiredForwardOrder
{
public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
{
}
}
class CannotBeAutowiredReverseOrder
{
public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
{
}
}
class Lille
{
}
class Dunglas
{
public function __construct(Lille $l)
{
}
}
class LesTilleuls
{
public function __construct(Dunglas $j, Dunglas $k)
{
}
}
class OptionalParameter
{
public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
{
}
}
class BadTypeHintedArgument
{
public function __construct(Dunglas $k, NotARealClass $r)
{
}
}
class BadParentTypeHintedArgument
{
public function __construct(Dunglas $k, OptionalServiceClass $r)
{
}
}
class NotGuessableArgument
{
public function __construct(Foo $k)
{
}
}
class NotGuessableArgumentForSubclass
{
public function __construct(A $k)
{
}
}
class MultipleArguments
{
public function __construct(A $k, $foo, Dunglas $dunglas)
{
}
}
class MultipleArgumentsOptionalScalar
{
public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
{
}
}
class MultipleArgumentsOptionalScalarLast
{
public function __construct(A $a, Lille $lille, $foo = 'some_val')
{
}
}
class MultipleArgumentsOptionalScalarNotReallyOptional
{
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)
{
}
}
class SetterInjectionCollision
{
/**
* @required
*/
public function setMultipleInstancesForOneArg(CollisionInterface $collision)
{
// The CollisionInterface cannot be autowired - there are multiple
// should throw an exception
}
}
class SetterInjection extends SetterInjectionParent
{
/**
* @required
*/
public function setFoo(Foo $foo)
{
// should be called
}
/** @inheritdoc*/ // <- brackets are missing on purpose
public function setDependencies(Foo $foo, A $a)
{
// should be called
}
/** {@inheritdoc} */
public function setWithCallsConfigured(A $a)
{
// this method has a calls configured on it
}
public function notASetter(A $a)
{
// should be called only when explicitly specified
}
/**
* @required*/
public function setChildMethodWithoutDocBlock(A $a)
{
}
}
class SetterInjectionParent
{
/** @required*/
public function setDependencies(Foo $foo, A $a)
{
// should be called
}
public function notASetter(A $a)
{
// @required should be ignored when the child does not add @inheritdoc
}
/** @required <tab> prefix is on purpose */
public function setWithCallsConfigured(A $a)
{
}
/** @required */
public function setChildMethodWithoutDocBlock(A $a)
{
}
}
class NotWireable
{
public function setNotAutowireable(NotARealClass $n)
{
}
public function setBar()
{
}
public function setOptionalNotAutowireable(NotARealClass $n = null)
{
}
public function setDifferentNamespace(\stdClass $n)
{
}
public function setOptionalNoTypeHint($foo = null)
{
}
public function setOptionalArgNoAutowireable($other = 'default_val')
{
}
/** @required */
protected function setProtectedMethod(A $a)
{
}
}
class PrivateConstructor
{
private function __construct()
{
}
}
class ScalarSetter
{
/**
* @required
*/
public function setDefaultLocale($defaultLocale)
{
}
}