[DependencyInjection] Autoconfigurable attributes
This commit is contained in:
parent
f50e6afd7d
commit
2ab3caf080
@ -58,6 +58,7 @@ use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\EventDispatcher\Attribute\EventListener;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
@ -549,6 +550,10 @@ class FrameworkExtension extends Extension
|
||||
$container->registerForAutoconfiguration(LoggerAwareInterface::class)
|
||||
->addMethodCall('setLogger', [new Reference('logger')]);
|
||||
|
||||
$container->registerAttributeForAutoconfiguration(EventListener::class, static function (ChildDefinition $definition, EventListener $attribute): void {
|
||||
$definition->addTag('kernel.event_listener', get_object_vars($attribute));
|
||||
});
|
||||
|
||||
if (!$container->getParameter('kernel.debug')) {
|
||||
// remove tagged iterator argument for resource checkers
|
||||
$container->getDefinition('config_cache_factory')->setArguments([]);
|
||||
|
@ -7,6 +7,7 @@ CHANGELOG
|
||||
* Add `ServicesConfigurator::remove()` in the PHP-DSL
|
||||
* Add `%env(not:...)%` processor to negate boolean values
|
||||
* Add support for loading autoconfiguration rules via the `#[Autoconfigure]` and `#[AutoconfigureTag]` attributes on PHP 8
|
||||
* Add autoconfigurable attributes
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?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\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*/
|
||||
final class AttributeAutoconfigurationPass implements CompilerPassInterface
|
||||
{
|
||||
private $ignoreAttributesTag;
|
||||
|
||||
public function __construct(string $ignoreAttributesTag = 'container.ignore_attributes')
|
||||
{
|
||||
$this->ignoreAttributesTag = $ignoreAttributesTag;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
$autoconfiguredAttributes = $container->getAutoconfiguredAttributes();
|
||||
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
if (!$definition->isAutoconfigured()
|
||||
|| $definition->isAbstract()
|
||||
|| $definition->hasTag($this->ignoreAttributesTag)
|
||||
|| !($reflector = $container->getReflectionClass($definition->getClass(), false))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$instanceof = $definition->getInstanceofConditionals();
|
||||
$conditionals = $instanceof[$reflector->getName()] ?? new ChildDefinition('');
|
||||
foreach ($reflector->getAttributes() as $attribute) {
|
||||
if ($configurator = $autoconfiguredAttributes[$attribute->getName()] ?? null) {
|
||||
$configurator($conditionals, $attribute->newInstance(), $reflector);
|
||||
}
|
||||
}
|
||||
$instanceof[$reflector->getName()] = $conditionals;
|
||||
$definition->setInstanceofConditionals($instanceof);
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,7 @@ class PassConfig
|
||||
100 => [
|
||||
new ResolveClassPass(),
|
||||
new RegisterAutoconfigureAttributesPass(),
|
||||
new AttributeAutoconfigurationPass(),
|
||||
new ResolveInstanceofConditionalsPass(),
|
||||
new RegisterEnvVarProcessorsPass(),
|
||||
],
|
||||
|
@ -123,6 +123,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
|
||||
private $autoconfiguredInstanceof = [];
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
private $autoconfiguredAttributes = [];
|
||||
|
||||
private $removedIds = [];
|
||||
|
||||
private $removedBindingIds = [];
|
||||
@ -671,6 +676,14 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
|
||||
$this->autoconfiguredInstanceof[$interface] = $childDefinition;
|
||||
}
|
||||
|
||||
foreach ($container->getAutoconfiguredAttributes() as $attribute => $configurator) {
|
||||
if (isset($this->autoconfiguredAttributes[$attribute])) {
|
||||
throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same attribute.', $attribute));
|
||||
}
|
||||
|
||||
$this->autoconfiguredAttributes[$attribute] = $configurator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1309,6 +1322,16 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
return $this->autoconfiguredInstanceof[$interface];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an attribute that will be used for autoconfiguring annotated classes.
|
||||
*
|
||||
* The configurator will receive a Definition instance and an instance of the attribute, in that order.
|
||||
*/
|
||||
public function registerAttributeForAutoconfiguration(string $attributeClass, callable $configurator): void
|
||||
{
|
||||
$this->autoconfiguredAttributes[$attributeClass] = $configurator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an autowiring alias that only binds to a specific argument name.
|
||||
*
|
||||
@ -1338,6 +1361,14 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
return $this->autoconfiguredInstanceof;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getAutoconfiguredAttributes(): array
|
||||
{
|
||||
return $this->autoconfiguredAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves env parameter placeholders in a string or an array.
|
||||
*
|
||||
|
@ -16,14 +16,22 @@ use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomAutoconfiguration;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedForDefaultPriorityClass;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooTagClass;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService1;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService2;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService3;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService3Configurator;
|
||||
use Symfony\Contracts\Service\ServiceProviderInterface;
|
||||
use Symfony\Contracts\Service\ServiceSubscriberInterface;
|
||||
|
||||
@ -506,6 +514,109 @@ class IntegrationTest extends TestCase
|
||||
];
|
||||
$this->assertSame($expected, ['baz' => $serviceLocator->get('baz')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8
|
||||
*/
|
||||
public function testTagsViaAttribute()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->registerAttributeForAutoconfiguration(
|
||||
CustomAutoconfiguration::class,
|
||||
static function (ChildDefinition $definition, CustomAutoconfiguration $attribute, \ReflectionClass $reflector) {
|
||||
$definition->addTag('app.custom_tag', get_object_vars($attribute) + ['class' => $reflector->getName()]);
|
||||
}
|
||||
);
|
||||
|
||||
$container->register('one', TaggedService1::class)
|
||||
->setPublic(true)
|
||||
->setAutoconfigured(true);
|
||||
$container->register('two', TaggedService2::class)
|
||||
->addTag('app.custom_tag', ['info' => 'This tag is not autoconfigured'])
|
||||
->setPublic(true)
|
||||
->setAutoconfigured(true);
|
||||
|
||||
$collector = new TagCollector();
|
||||
$container->addCompilerPass($collector);
|
||||
|
||||
$container->compile();
|
||||
|
||||
self::assertSame([
|
||||
'one' => [
|
||||
['someAttribute' => 'one', 'priority' => 0, 'class' => TaggedService1::class],
|
||||
['someAttribute' => 'two', 'priority' => 0, 'class' => TaggedService1::class],
|
||||
],
|
||||
'two' => [
|
||||
['info' => 'This tag is not autoconfigured'],
|
||||
['someAttribute' => 'prio 100', 'priority' => 100, 'class' => TaggedService2::class],
|
||||
],
|
||||
], $collector->collectedTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8
|
||||
*/
|
||||
public function testAttributesAreIgnored()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->registerAttributeForAutoconfiguration(
|
||||
CustomAutoconfiguration::class,
|
||||
static function (Definition $definition, CustomAutoconfiguration $attribute) {
|
||||
$definition->addTag('app.custom_tag', get_object_vars($attribute));
|
||||
}
|
||||
);
|
||||
|
||||
$container->register('one', TaggedService1::class)
|
||||
->setPublic(true)
|
||||
->addTag('container.ignore_attributes')
|
||||
->setAutoconfigured(true);
|
||||
$container->register('two', TaggedService2::class)
|
||||
->setPublic(true)
|
||||
->setAutoconfigured(true);
|
||||
|
||||
$collector = new TagCollector();
|
||||
$container->addCompilerPass($collector);
|
||||
|
||||
$container->compile();
|
||||
|
||||
self::assertSame([
|
||||
'two' => [
|
||||
['someAttribute' => 'prio 100', 'priority' => 100],
|
||||
],
|
||||
], $collector->collectedTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8
|
||||
*/
|
||||
public function testAutoconfigureViaAttribute()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->registerAttributeForAutoconfiguration(
|
||||
CustomAutoconfiguration::class,
|
||||
static function (ChildDefinition $definition) {
|
||||
$definition
|
||||
->addMethodCall('doSomething', [1, 2, 3])
|
||||
->setBindings(['string $foo' => 'bar'])
|
||||
->setConfigurator(new Reference('my_configurator'))
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$container->register('my_configurator', TaggedService3Configurator::class);
|
||||
$container->register('three', TaggedService3::class)
|
||||
->setPublic(true)
|
||||
->setAutoconfigured(true);
|
||||
|
||||
$container->compile();
|
||||
|
||||
/** @var TaggedService3 $service */
|
||||
$service = $container->get('three');
|
||||
|
||||
self::assertSame('bar', $service->foo);
|
||||
self::assertSame(6, $service->sum);
|
||||
self::assertTrue($service->hasBeenConfigured);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceSubscriberStub implements ServiceSubscriberInterface
|
||||
@ -566,3 +677,13 @@ class IntegrationTestStubParent
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
final class TagCollector implements CompilerPassInterface
|
||||
{
|
||||
public $collectedTags;
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$this->collectedTags = $container->findTaggedServiceIds('app.custom_tag');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
<?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\Fixtures\Attribute;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
|
||||
final class CustomAutoconfiguration
|
||||
{
|
||||
public function __construct(
|
||||
public string $someAttribute,
|
||||
public int $priority = 0,
|
||||
) {
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?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\Fixtures;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomAutoconfiguration;
|
||||
|
||||
#[CustomAutoconfiguration(someAttribute: 'one')]
|
||||
#[CustomAutoconfiguration(someAttribute: 'two')]
|
||||
final class TaggedService1
|
||||
{
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?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\Fixtures;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomAutoconfiguration;
|
||||
|
||||
#[CustomAutoconfiguration(someAttribute: 'prio 100', priority: 100)]
|
||||
final class TaggedService2
|
||||
{
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?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\Fixtures;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomAutoconfiguration;
|
||||
|
||||
#[CustomAutoconfiguration(someAttribute: 'three')]
|
||||
final class TaggedService3
|
||||
{
|
||||
public int $sum = 0;
|
||||
public bool $hasBeenConfigured = false;
|
||||
|
||||
public function __construct(
|
||||
public string $foo,
|
||||
) {
|
||||
}
|
||||
|
||||
public function doSomething(int $a, int $b, int $c): void
|
||||
{
|
||||
$this->sum = $a + $b + $c;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?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\Fixtures;
|
||||
|
||||
final class TaggedService3Configurator
|
||||
{
|
||||
public function __invoke(TaggedService3 $service)
|
||||
{
|
||||
$service->hasBeenConfigured = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?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\EventDispatcher\Attribute;
|
||||
|
||||
/**
|
||||
* Service tag to autoconfigure event listeners.
|
||||
*
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
|
||||
class EventListener
|
||||
{
|
||||
public function __construct(
|
||||
public ?string $event = null,
|
||||
public ?string $method = null,
|
||||
public int $priority = 0
|
||||
) {
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* Add `EventListener` attribute for declaring listeners on PHP 8.
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
|
@ -13,12 +13,19 @@ namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\AttributeAutoconfigurationPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\EventDispatcher\Attribute\EventListener;
|
||||
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
|
||||
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\EventDispatcher\Tests\Fixtures\CustomEvent;
|
||||
use Symfony\Component\EventDispatcher\Tests\Fixtures\TaggedInvokableListener;
|
||||
use Symfony\Component\EventDispatcher\Tests\Fixtures\TaggedMultiListener;
|
||||
|
||||
class RegisterListenersPassTest extends TestCase
|
||||
{
|
||||
@ -231,6 +238,90 @@ class RegisterListenersPassTest extends TestCase
|
||||
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8
|
||||
*/
|
||||
public function testTaggedInvokableEventListener()
|
||||
{
|
||||
if (!class_exists(AttributeAutoconfigurationPass::class)) {
|
||||
self::markTestSkipped('This test requires Symfony DependencyInjection >= 5.3');
|
||||
}
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->registerAttributeForAutoconfiguration(EventListener::class, static function (ChildDefinition $definition, EventListener $attribute): void {
|
||||
$definition->addTag('kernel.event_listener', get_object_vars($attribute));
|
||||
});
|
||||
$container->register('foo', TaggedInvokableListener::class)->setAutoconfigured(true);
|
||||
$container->register('event_dispatcher', \stdClass::class);
|
||||
|
||||
(new AttributeAutoconfigurationPass())->process($container);
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new RegisterListenersPass())->process($container);
|
||||
|
||||
$definition = $container->getDefinition('event_dispatcher');
|
||||
$expectedCalls = [
|
||||
[
|
||||
'addListener',
|
||||
[
|
||||
CustomEvent::class,
|
||||
[new ServiceClosureArgument(new Reference('foo')), '__invoke'],
|
||||
0,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8
|
||||
*/
|
||||
public function testTaggedMultiEventListener()
|
||||
{
|
||||
if (!class_exists(AttributeAutoconfigurationPass::class)) {
|
||||
self::markTestSkipped('This test requires Symfony DependencyInjection >= 5.3');
|
||||
}
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->registerAttributeForAutoconfiguration(EventListener::class, static function (ChildDefinition $definition, EventListener $attribute): void {
|
||||
$definition->addTag('kernel.event_listener', get_object_vars($attribute));
|
||||
});
|
||||
$container->register('foo', TaggedMultiListener::class)->setAutoconfigured(true);
|
||||
$container->register('event_dispatcher', \stdClass::class);
|
||||
|
||||
(new AttributeAutoconfigurationPass())->process($container);
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new RegisterListenersPass())->process($container);
|
||||
|
||||
$definition = $container->getDefinition('event_dispatcher');
|
||||
$expectedCalls = [
|
||||
[
|
||||
'addListener',
|
||||
[
|
||||
CustomEvent::class,
|
||||
[new ServiceClosureArgument(new Reference('foo')), 'onCustomEvent'],
|
||||
0,
|
||||
],
|
||||
],
|
||||
[
|
||||
'addListener',
|
||||
[
|
||||
'foo',
|
||||
[new ServiceClosureArgument(new Reference('foo')), 'onFoo'],
|
||||
42,
|
||||
],
|
||||
],
|
||||
[
|
||||
'addListener',
|
||||
[
|
||||
'bar',
|
||||
[new ServiceClosureArgument(new Reference('foo')), 'onBarEvent'],
|
||||
0,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testAliasedEventListener()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
@ -416,10 +507,6 @@ final class AliasedEvent
|
||||
{
|
||||
}
|
||||
|
||||
final class CustomEvent
|
||||
{
|
||||
}
|
||||
|
||||
final class TypedListener
|
||||
{
|
||||
public function __invoke(AliasedEvent $event): void
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?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\EventDispatcher\Tests\Fixtures;
|
||||
|
||||
final class CustomEvent
|
||||
{
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?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\EventDispatcher\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Attribute\EventListener;
|
||||
|
||||
#[EventListener]
|
||||
final class TaggedInvokableListener
|
||||
{
|
||||
public function __invoke(CustomEvent $event): void
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?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\EventDispatcher\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Attribute\EventListener;
|
||||
|
||||
#[EventListener(event: CustomEvent::class, method: 'onCustomEvent')]
|
||||
#[EventListener(event: 'foo', priority: 42)]
|
||||
#[EventListener(event: 'bar', method: 'onBarEvent')]
|
||||
final class TaggedMultiListener
|
||||
{
|
||||
public function onCustomEvent(CustomEvent $event): void
|
||||
{
|
||||
}
|
||||
|
||||
public function onFoo(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function onBarEvent(): void
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user