diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php index 0d943f46151e2..fbaef1ce09e5d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -43,8 +43,8 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberUnionWithTrait; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Contracts\Service\Attribute\SubscribedService; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; use Symfony\Contracts\Service\ServiceSubscriberInterface; -use Symfony\Contracts\Service\ServiceSubscriberTrait; require_once __DIR__.'/../Fixtures/includes/classes.php'; @@ -221,7 +221,7 @@ public function testExtraServiceSubscriber() $container->compile(); } - public function testServiceSubscriberTraitWithSubscribedServiceAttribute() + public function testServiceMethodsSubscriberTraitWithSubscribedServiceAttribute() { if (!class_exists(SubscribedService::class)) { $this->markTestSkipped('SubscribedService attribute not available.'); @@ -251,14 +251,14 @@ public function testServiceSubscriberTraitWithSubscribedServiceAttribute() $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); } - public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnStaticMethod() + public function testServiceMethodsSubscriberTraitWithSubscribedServiceAttributeOnStaticMethod() { if (!class_exists(SubscribedService::class)) { $this->markTestSkipped('SubscribedService attribute not available.'); } $subscriber = new class() implements ServiceSubscriberInterface { - use ServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; #[SubscribedService] public static function method(): TestDefinition1 @@ -271,14 +271,14 @@ public static function method(): TestDefinition1 $subscriber::getSubscribedServices(); } - public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnMethodWithRequiredParameters() + public function testServiceMethodsSubscriberTraitWithSubscribedServiceAttributeOnMethodWithRequiredParameters() { if (!class_exists(SubscribedService::class)) { $this->markTestSkipped('SubscribedService attribute not available.'); } $subscriber = new class() implements ServiceSubscriberInterface { - use ServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; #[SubscribedService] public function method($param1, $param2 = null): TestDefinition1 @@ -291,14 +291,14 @@ public function method($param1, $param2 = null): TestDefinition1 $subscriber::getSubscribedServices(); } - public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnMethodMissingReturnType() + public function testServiceMethodsSubscriberTraitWithSubscribedServiceAttributeOnMethodMissingReturnType() { if (!class_exists(SubscribedService::class)) { $this->markTestSkipped('SubscribedService attribute not available.'); } $subscriber = new class() implements ServiceSubscriberInterface { - use ServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; #[SubscribedService] public function method() @@ -311,7 +311,7 @@ public function method() $subscriber::getSubscribedServices(); } - public function testServiceSubscriberTraitWithUnionReturnType() + public function testServiceMethodsSubscriberTraitWithUnionReturnType() { if (!class_exists(SubscribedService::class)) { $this->markTestSkipped('SubscribedService attribute not available.'); @@ -338,7 +338,7 @@ public function testServiceSubscriberTraitWithUnionReturnType() $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); } - public function testServiceSubscriberTraitWithIntersectionReturnType() + public function testServiceMethodsSubscriberTraitWithIntersectionReturnType() { if (!class_exists(SubscribedService::class)) { $this->markTestSkipped('SubscribedService attribute not available.'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberTrait.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceMethodsSubscriberTrait.php similarity index 90% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberTrait.php rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceMethodsSubscriberTrait.php index 52e946ff1f0bc..a3d84dda9ab17 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberTrait.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceMethodsSubscriberTrait.php @@ -4,7 +4,7 @@ use Symfony\Contracts\Service\Attribute\SubscribedService; -trait TestServiceSubscriberTrait +trait TestServiceMethodsSubscriberTrait { protected function protectedFunction1(): SomeClass { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberChild.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberChild.php index ee2df273996b6..07f3cdecf5bb3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberChild.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberChild.php @@ -3,12 +3,12 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures; use Symfony\Contracts\Service\Attribute\SubscribedService; -use Symfony\Contracts\Service\ServiceSubscriberTrait; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; class TestServiceSubscriberChild extends TestServiceSubscriberParent { - use ServiceSubscriberTrait; - use TestServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; + use TestServiceMethodsSubscriberTrait; #[SubscribedService] private function testDefinition2(): ?TestDefinition2 diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberIntersectionWithTrait.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberIntersectionWithTrait.php index 8dcacd49dd302..09ecaef3d3e96 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberIntersectionWithTrait.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberIntersectionWithTrait.php @@ -3,12 +3,12 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures; use Symfony\Contracts\Service\Attribute\SubscribedService; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; use Symfony\Contracts\Service\ServiceSubscriberInterface; -use Symfony\Contracts\Service\ServiceSubscriberTrait; class TestServiceSubscriberIntersectionWithTrait implements ServiceSubscriberInterface { - use ServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; #[SubscribedService] private function method1(): TestDefinition1&TestDefinition2 diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php index 95fbfd35218c6..a82123af8bd84 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php @@ -3,12 +3,12 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures; use Symfony\Contracts\Service\Attribute\SubscribedService; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; use Symfony\Contracts\Service\ServiceSubscriberInterface; -use Symfony\Contracts\Service\ServiceSubscriberTrait; class TestServiceSubscriberParent implements ServiceSubscriberInterface { - use ServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; public function publicFunction1(): SomeClass { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberUnionWithTrait.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberUnionWithTrait.php index d00333fe042d7..6d8e0cba417d4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberUnionWithTrait.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberUnionWithTrait.php @@ -3,12 +3,12 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures; use Symfony\Contracts\Service\Attribute\SubscribedService; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; use Symfony\Contracts\Service\ServiceSubscriberInterface; -use Symfony\Contracts\Service\ServiceSubscriberTrait; class TestServiceSubscriberUnionWithTrait implements ServiceSubscriberInterface { - use ServiceSubscriberTrait; + use ServiceMethodsSubscriberTrait; #[SubscribedService] private function method1(): TestDefinition1|TestDefinition2|null diff --git a/src/Symfony/Contracts/CHANGELOG.md b/src/Symfony/Contracts/CHANGELOG.md index 02c91564f7032..42fe6fd1c59fa 100644 --- a/src/Symfony/Contracts/CHANGELOG.md +++ b/src/Symfony/Contracts/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add `ServiceCollectionInterface` + * Deprecate `ServiceSubscriberTrait`, use `ServiceMethodsSubscriberTrait` instead 3.4 --- diff --git a/src/Symfony/Contracts/Service/Attribute/SubscribedService.php b/src/Symfony/Contracts/Service/Attribute/SubscribedService.php index d98e1dfdbbeb1..f850b8401f7e3 100644 --- a/src/Symfony/Contracts/Service/Attribute/SubscribedService.php +++ b/src/Symfony/Contracts/Service/Attribute/SubscribedService.php @@ -11,15 +11,15 @@ namespace Symfony\Contracts\Service\Attribute; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; use Symfony\Contracts\Service\ServiceSubscriberInterface; -use Symfony\Contracts\Service\ServiceSubscriberTrait; /** * For use as the return value for {@see ServiceSubscriberInterface}. * * @example new SubscribedService('http_client', HttpClientInterface::class, false, new Target('githubApi')) * - * Use with {@see ServiceSubscriberTrait} to mark a method's return type + * Use with {@see ServiceMethodsSubscriberTrait} to mark a method's return type * as a subscribed service. * * @author Kevin Bond diff --git a/src/Symfony/Contracts/Service/ServiceMethodsSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceMethodsSubscriberTrait.php new file mode 100644 index 0000000000000..0d89d9f25efd7 --- /dev/null +++ b/src/Symfony/Contracts/Service/ServiceMethodsSubscriberTrait.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\Attribute\Required; +use Symfony\Contracts\Service\Attribute\SubscribedService; + +/** + * Implementation of ServiceSubscriberInterface that determines subscribed services + * from methods that have the #[SubscribedService] attribute. + * + * Service ids are available as "ClassName::methodName" so that the implementation + * of subscriber methods can be just `return $this->container->get(__METHOD__);`. + * + * @author Kevin Bond + */ +trait ServiceMethodsSubscriberTrait +{ + protected ContainerInterface $container; + + public static function getSubscribedServices(): array + { + $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; + + foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { + if (self::class !== $method->getDeclaringClass()->name) { + continue; + } + + if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) { + continue; + } + + if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { + throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); + } + + if (!$returnType = $method->getReturnType()) { + throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); + } + + /* @var SubscribedService $attribute */ + $attribute = $attribute->newInstance(); + $attribute->key ??= self::class.'::'.$method->name; + $attribute->type ??= $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; + $attribute->nullable = $returnType->allowsNull(); + + if ($attribute->attributes) { + $services[] = $attribute; + } else { + $services[$attribute->key] = ($attribute->nullable ? '?' : '').$attribute->type; + } + } + + return $services; + } + + #[Required] + public function setContainer(ContainerInterface $container): ?ContainerInterface + { + $ret = null; + if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { + $ret = parent::setContainer($container); + } + + $this->container = $container; + + return $ret; + } +} diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php index f3b450cd6caaa..cc3bc321aa54f 100644 --- a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php +++ b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php @@ -15,17 +15,23 @@ use Symfony\Contracts\Service\Attribute\Required; use Symfony\Contracts\Service\Attribute\SubscribedService; +trigger_deprecation('symfony/contracts', 'v3.5', '"%s" is deprecated, use "ServiceMethodsSubscriberTrait" instead.', ServiceSubscriberTrait::class); + /** - * Implementation of ServiceSubscriberInterface that determines subscribed services from - * method return types. Service ids are available as "ClassName::methodName". + * Implementation of ServiceSubscriberInterface that determines subscribed services + * from methods that have the #[SubscribedService] attribute. + * + * Service ids are available as "ClassName::methodName" so that the implementation + * of subscriber methods can be just `return $this->container->get(__METHOD__);`. + * + * @property ContainerInterface $container * * @author Kevin Bond + * + * @deprecated since symfony/contracts v3.5, use ServiceMethodsSubscriberTrait instead */ trait ServiceSubscriberTrait { - /** @var ContainerInterface */ - protected $container; - public static function getSubscribedServices(): array { $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; diff --git a/src/Symfony/Contracts/Service/composer.json b/src/Symfony/Contracts/Service/composer.json index 061561c91b7d8..fc8674a7274f2 100644 --- a/src/Symfony/Contracts/Service/composer.json +++ b/src/Symfony/Contracts/Service/composer.json @@ -17,7 +17,8 @@ ], "require": { "php": ">=8.1", - "psr/container": "^1.1|^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" diff --git a/src/Symfony/Contracts/Tests/Service/LegacyTestService.php b/src/Symfony/Contracts/Tests/Service/LegacyTestService.php new file mode 100644 index 0000000000000..9d55b9edcc500 --- /dev/null +++ b/src/Symfony/Contracts/Tests/Service/LegacyTestService.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Tests\Service; + +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\Attribute\Required; +use Symfony\Contracts\Service\Attribute\SubscribedService; +use Symfony\Contracts\Service\ServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberTrait; + +class LegacyParentTestService +{ + public function aParentService(): Service1 + { + } + + public function setContainer(ContainerInterface $container): ?ContainerInterface + { + return $container; + } +} + +class LegacyTestService extends LegacyParentTestService implements ServiceSubscriberInterface +{ + use ServiceSubscriberTrait; + + #[SubscribedService] + public function aService(): Service2 + { + return $this->container->get(__METHOD__); + } + + #[SubscribedService] + public function nullableService(): ?Service2 + { + return $this->container->get(__METHOD__); + } + + #[SubscribedService(attributes: new Required())] + public function withAttribute(): ?Service2 + { + return $this->container->get(__METHOD__); + } +} + +class LegacyChildTestService extends LegacyTestService +{ + #[SubscribedService()] + public function aChildService(): LegacyService3 + { + return $this->container->get(__METHOD__); + } +} + +class LegacyParentWithMagicCall +{ + public function __call($method, $args) + { + throw new \BadMethodCallException('Should not be called.'); + } + + public static function __callStatic($method, $args) + { + throw new \BadMethodCallException('Should not be called.'); + } +} + +class LegacyService3 +{ +} + +class LegacyParentTestService2 +{ + /** @var ContainerInterface */ + protected $container; + + public function setContainer(ContainerInterface $container) + { + $previous = $this->container ?? null; + $this->container = $container; + + return $previous; + } +} diff --git a/src/Symfony/Contracts/Tests/Service/ServiceMethodsSubscriberTraitTest.php b/src/Symfony/Contracts/Tests/Service/ServiceMethodsSubscriberTraitTest.php new file mode 100644 index 0000000000000..396ca7f5ad956 --- /dev/null +++ b/src/Symfony/Contracts/Tests/Service/ServiceMethodsSubscriberTraitTest.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Tests\Service; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\Attribute\Required; +use Symfony\Contracts\Service\Attribute\SubscribedService; +use Symfony\Contracts\Service\ServiceLocatorTrait; +use Symfony\Contracts\Service\ServiceMethodsSubscriberTrait; +use Symfony\Contracts\Service\ServiceSubscriberInterface; + +class ServiceMethodsSubscriberTraitTest extends TestCase +{ + public function testMethodsOnParentsAndChildrenAreIgnoredInGetSubscribedServices() + { + $expected = [ + TestService::class.'::aService' => Service2::class, + TestService::class.'::nullableService' => '?'.Service2::class, + new SubscribedService(TestService::class.'::withAttribute', Service2::class, true, new Required()), + ]; + + $this->assertEquals($expected, ChildTestService::getSubscribedServices()); + } + + public function testSetContainerIsCalledOnParent() + { + $container = new class([]) implements ContainerInterface { + use ServiceLocatorTrait; + }; + + $this->assertSame($container, (new TestService())->setContainer($container)); + } + + public function testParentNotCalledIfHasMagicCall() + { + $container = new class([]) implements ContainerInterface { + use ServiceLocatorTrait; + }; + $service = new class() extends ParentWithMagicCall { + use ServiceMethodsSubscriberTrait; + }; + + $this->assertNull($service->setContainer($container)); + $this->assertSame([], $service::getSubscribedServices()); + } + + public function testParentNotCalledIfNoParent() + { + $container = new class([]) implements ContainerInterface { + use ServiceLocatorTrait; + }; + $service = new class() { + use ServiceMethodsSubscriberTrait; + }; + + $this->assertNull($service->setContainer($container)); + $this->assertSame([], $service::getSubscribedServices()); + } + + public function testSetContainerCalledFirstOnParent() + { + $container1 = new class([]) implements ContainerInterface { + use ServiceLocatorTrait; + }; + $container2 = clone $container1; + + $testService = new TestService2(); + $this->assertNull($testService->setContainer($container1)); + $this->assertSame($container1, $testService->setContainer($container2)); + } +} + +class ParentTestService +{ + public function aParentService(): Service1 + { + } + + public function setContainer(ContainerInterface $container): ?ContainerInterface + { + return $container; + } +} + +class TestService extends ParentTestService implements ServiceSubscriberInterface +{ + use ServiceMethodsSubscriberTrait; + + protected ContainerInterface $container; + + #[SubscribedService] + public function aService(): Service2 + { + return $this->container->get(__METHOD__); + } + + #[SubscribedService] + public function nullableService(): ?Service2 + { + return $this->container->get(__METHOD__); + } + + #[SubscribedService(attributes: new Required())] + public function withAttribute(): ?Service2 + { + return $this->container->get(__METHOD__); + } +} + +class ChildTestService extends TestService +{ + #[SubscribedService] + public function aChildService(): Service3 + { + return $this->container->get(__METHOD__); + } +} + +class ParentWithMagicCall +{ + public function __call($method, $args) + { + throw new \BadMethodCallException('Should not be called.'); + } + + public static function __callStatic($method, $args) + { + throw new \BadMethodCallException('Should not be called.'); + } +} + +class Service1 +{ +} + +class Service2 +{ +} + +class Service3 +{ +} + +class ParentTestService2 +{ + protected ContainerInterface $container; + + public function setContainer(ContainerInterface $container) + { + $previous = $this->container ?? null; + $this->container = $container; + + return $previous; + } +} + +class TestService2 extends ParentTestService2 implements ServiceSubscriberInterface +{ + use ServiceMethodsSubscriberTrait; +} diff --git a/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php b/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php index ba370265bac85..184d92d9c4807 100644 --- a/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php +++ b/src/Symfony/Contracts/Tests/Service/ServiceSubscriberTraitTest.php @@ -13,25 +13,31 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Component1\Dir1\Service1; -use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Component1\Dir2\Service2; use Symfony\Contracts\Service\Attribute\Required; use Symfony\Contracts\Service\Attribute\SubscribedService; use Symfony\Contracts\Service\ServiceLocatorTrait; use Symfony\Contracts\Service\ServiceSubscriberInterface; use Symfony\Contracts\Service\ServiceSubscriberTrait; +/** + * @group legacy + */ class ServiceSubscriberTraitTest extends TestCase { + public static function setUpBeforeClass(): void + { + class_exists(LegacyTestService::class); + } + public function testMethodsOnParentsAndChildrenAreIgnoredInGetSubscribedServices() { $expected = [ - TestService::class.'::aService' => Service2::class, - TestService::class.'::nullableService' => '?'.Service2::class, - new SubscribedService(TestService::class.'::withAttribute', Service2::class, true, new Required()), + LegacyTestService::class.'::aService' => Service2::class, + LegacyTestService::class.'::nullableService' => '?'.Service2::class, + new SubscribedService(LegacyTestService::class.'::withAttribute', Service2::class, true, new Required()), ]; - $this->assertEquals($expected, ChildTestService::getSubscribedServices()); + $this->assertEquals($expected, LegacyChildTestService::getSubscribedServices()); } public function testSetContainerIsCalledOnParent() @@ -40,7 +46,7 @@ public function testSetContainerIsCalledOnParent() use ServiceLocatorTrait; }; - $this->assertSame($container, (new TestService())->setContainer($container)); + $this->assertSame($container, (new LegacyTestService())->setContainer($container)); } public function testParentNotCalledIfHasMagicCall() @@ -76,84 +82,10 @@ public function testSetContainerCalledFirstOnParent() }; $container2 = clone $container1; - $testService = new TestService2(); + $testService = new class() extends LegacyParentTestService2 implements ServiceSubscriberInterface { + use ServiceSubscriberTrait; + }; $this->assertNull($testService->setContainer($container1)); $this->assertSame($container1, $testService->setContainer($container2)); } } - -class ParentTestService -{ - public function aParentService(): Service1 - { - } - - public function setContainer(ContainerInterface $container): ?ContainerInterface - { - return $container; - } -} - -class TestService extends ParentTestService implements ServiceSubscriberInterface -{ - use ServiceSubscriberTrait; - - #[SubscribedService] - public function aService(): Service2 - { - } - - #[SubscribedService] - public function nullableService(): ?Service2 - { - } - - #[SubscribedService(attributes: new Required())] - public function withAttribute(): ?Service2 - { - } -} - -class ChildTestService extends TestService -{ - #[SubscribedService] - public function aChildService(): Service3 - { - } -} - -class ParentWithMagicCall -{ - public function __call($method, $args) - { - throw new \BadMethodCallException('Should not be called.'); - } - - public static function __callStatic($method, $args) - { - throw new \BadMethodCallException('Should not be called.'); - } -} - -class Service3 -{ -} - -class ParentTestService2 -{ - /** @var ContainerInterface */ - protected $container; - - public function setContainer(ContainerInterface $container) - { - $previous = $this->container ?? null; - $this->container = $container; - - return $previous; - } -} - -class TestService2 extends ParentTestService2 implements ServiceSubscriberInterface -{ - use ServiceSubscriberTrait; -}