8000 [DependencyInjection] Add `SubscribedService` attribute, deprecate current `ServiceSubscriberTrait` usage by kbond · Pull Request #42238 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DependencyInjection] Add SubscribedService attribute, deprecate current ServiceSubscriberTrait usage #42238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\LegacyTestServiceSubscriberChild;
use Symfony\Component\DependencyInjection\Tests\Fixtures\LegacyTestServiceSubscriberParent;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition2;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition3;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberChild;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberParent;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Contracts\Service\Attribute\SubscribedService;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;

Expand Down Expand Up @@ -143,11 +146,14 @@ public function testExtraServiceSubscriber()
$container->compile();
}

/**
* @group legacy
*/
public function testServiceSubscriberTrait()
{
$container = new ContainerBuilder();

$container->register('foo', TestServiceSubscriberChild::class)
$container->register('foo', LegacyTestServiceSubscriberChild::class)
->addMethodCall('setContainer', [new Reference(PsrContainerInterface::class)])
->addTag('container.service_subscriber')
;
Expand All @@ -159,15 +165,18 @@ public function testServiceSubscriberTrait()
$locator = $container->getDefinition((string) $foo->getMethodCalls()[0][1][0]);

$expected = [
TestServiceSubscriberChild::class.'::invalidDefinition' => new ServiceClosureArgument(new TypedReference('Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', 'Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
TestServiceSubscriberChild::class.'::testDefinition2' => new ServiceClosureArgument(new TypedReference(TestDefinition2::class, TestDefinition2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
TestServiceSubscriberChild::class.'::testDefinition3' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
TestServiceSubscriberParent::class.'::testDefinition1' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
LegacyTestServiceSubscriberChild::class.'::invalidDefinition' => new ServiceClosureArgument(new TypedReference('Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', 'Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
LegacyTestServiceSubscriberChild::class.'::testDefinition2' => new ServiceClosureArgument(new TypedReference(TestDefinition2::class, TestDefinition2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
LegacyTestServiceSubscriberChild::class.'::testDefinition3' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
LegacyTestServiceSubscriberParent::class.'::testDefinition1' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
];

$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
}

/**
* @group legacy
*/
public function testServiceSubscriberTraitWithGetter()
{
$container = new ContainerBuilder();
Expand Down Expand Up @@ -195,6 +204,108 @@ public function getFoo(): \stdClass
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
}

/**
* @requires PHP 8
*/
public function testServiceSubscriberTraitWithSubscribedServiceAttribute()
{
if (!class_exists(SubscribedService::class)) {
$this->markTestSkipped('SubscribedService attribute not available.');
}

$container = new ContainerBuilder();

$container->register('foo', TestServiceSubscriberChild::class)
->addMethodCall('setContainer', [new Reference(PsrContainerInterface::class)])
->addTag('container.service_subscriber')
;

(new RegisterServiceSubscribersPass())->process($container);
(new ResolveServiceSubscribersPass())->process($container);

$foo = $container->getDefinition('foo');
$locator = $container->getDefinition((string) $foo->getMethodCalls()[0][1][0]);

$expected = [
TestServiceSubscriberChild::class.'::invalidDefinition' => new ServiceClosureArgument(new TypedReference('Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', 'Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition')),
TestServiceSubscriberChild::class.'::testDefinition2' => new ServiceClosureArgument(new TypedReference(TestDefinition2::class, TestDefinition2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
TestServiceSubscriberChild::class.'::testDefinition4' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class)),
TestServiceSubscriberParent::class.'::testDefinition1' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class)),
'custom_name' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'custom_name')),
];

$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
}

/**
* @requires PHP 8
*/
public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnStaticMethod()
{
if (!class_exists(SubscribedService::class)) {
$this->markTestSkipped('SubscribedService attribute not available.');
}

$subscriber = new class() implements ServiceSubscriberInterface {
use ServiceSubscriberTrait;

#[SubscribedService]
public static function method(): TestDefinition1
{
}
};

$this->expectException(\LogicException::class);

$subscriber::getSubscribedServices();
}

/**
* @requires PHP 8
*/
public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnMethodWithRequiredParameters()
{
if (!class_exists(SubscribedService::class)) {
$this->markTestSkipped('SubscribedService attribute not available.');
}

$subscriber = new class() implements ServiceSubscriberInterface {
use ServiceSubscriberTrait;

#[SubscribedService]
public function method($param1, $param2 = null): TestDefinition1
{
}
};

$this->expectException(\LogicException::class);

$subscriber::getSubscribedServices();
}

/**
* @requires PHP 8
*/
public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnMethodMissingReturnType()
{
if (!class_exists(SubscribedService::class)) {
$this->markTestSkipped('SubscribedService attribute not available.');
}

$subscriber = new class() implements ServiceSubscriberInterface {
use ServiceSubscriberTrait;

#[SubscribedService]
public function method()
{
}
};

$this->expectException(\LogicException::class);

$subscriber::getSubscribedServices();
}

public function testServiceSubscriberWithSemanticId()
{
$container = new ContainerBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Contracts\Service\ServiceSubscriberTrait;

class LegacyTestServiceSubscriberChild extends LegacyTestServiceSubscriberParent
{
use ServiceSubscriberTrait;
use LegacyTestServiceSubscriberTrait;

private function testDefinition2(): TestDefinition2
{
return $this->container->get(__METHOD__);
}

private function invalidDefinition(): InvalidDefinition
{
return $this->container->get(__METHOD__);
}

private function privateFunction1(): string
{
}

private function privateFunction2(): string
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;

class LegacyTestServiceSubscriberParent implements ServiceSubscriberInterface
{
use ServiceSubscriberTrait;

private function testDefinition1(): TestDefinition1
{
return $this->container->get(__METHOD__);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

trait LegacyTestServiceSubscriberTrait
{
private function testDefinition3(): TestDefinition3
{
return $this->container->get(__CLASS__.'::'.__FUNCTION__);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Contracts\Service\Attribute\SubscribedService;
use Symfony\Contracts\Service\ServiceSubscriberTrait;

class TestServiceSubscriberChild extends TestServiceSubscriberParent
{
use ServiceSubscriberTrait;
use TestServiceSubscriberTrait;

private function testDefinition2(): TestDefinition2
#[SubscribedService]
private function testDefinition2(): ?TestDefinition2
{
return $this->container->get(__METHOD__);
}

#[SubscribedService('custom_name')]
private function testDefinition3(): TestDefinition3
{
return $this->container->get('custom_name');
}

#[SubscribedService]
private function invalidDefinition(): InvalidDefinition
{
return $this->container->get(__METHOD__);
Expand All @@ -26,4 +35,8 @@ private function privateFunction1(): string
private function privateFunction2(): string
{
}

private function privateFunction3(): AnotherClass
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Contracts\Service\Attribute\SubscribedService;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;

class TestServiceSubscriberParent implements ServiceSubscriberInterface
{
use ServiceSubscriberTrait;

public function publicFunction1(): SomeClass
{
}

#[SubscribedService]
private function testDefinition1(): TestDefinition1
{
return $this->container->get(__METHOD__);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Contracts\Service\Attribute\SubscribedService;

trait TestServiceSubscriberTrait
{
private function testDefinition3(): TestDefinition3
protected function protectedFunction1(): SomeClass
{
}

#[SubscribedService]
private function testDefinition4(): TestDefinition3
{
return $this->container->get(__CLASS__.'::'.__FUNCTION__);
}
Expand Down
33 changes: 33 additions & 0 deletions src/Symfony/Contracts/Service/Attribute/SubscribedService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?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\Contracts\Service\Attribute;

use Symfony\Contracts\Service\ServiceSubscriberTrait;

/**
* Use with {@see ServiceSubscriberTrait} to mark a method's return type
* as a subscribed service.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
final class SubscribedService
{
/**
* @param string|null $key The key to use for the service
* If null, use "ClassName::methodName"
*/
public function __construct(
public ?string $key = null
) {
}
}
Loading
0