-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Messenger] Add Transport attribute to configure message to transport mapping #41179
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
Changes from all commits
8b86e7d
5754874
2d4938c
dec6ddc
8f4ae6c
606e7d0
c8daecd
54784ee
9ada6e6
908e5f0
af5646c
016db38
3638688
9068719
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?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\Messenger\Attribute; | ||
|
||
/** | ||
* @author Maxim Dovydenok <dovydenok.maxim@gmail.com> | ||
*/ | ||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] | ||
class Transport | ||
{ | ||
public function __construct(public readonly string $name) | ||
{ | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\Messenger\Tests\Fixtures; | ||
|
||
use Symfony\Component\Messenger\Attribute\Transport; | ||
|
||
#[Transport('interface_attribute_sender')] | ||
interface DummyMessageInterfaceWithAttribute | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\Messenger\Tests\Fixtures; | ||
|
||
use Symfony\Component\Messenger\Attribute\Transport; | ||
|
||
#[Transport('message_attribute_sender')] | ||
#[CustomTransport] | ||
class DummyMessageWithAttribute implements DummyMessageInterface | ||
{ | ||
private $message; | ||
|
||
public function __construct(string $message) | ||
{ | ||
$this->message = $message; | ||
} | ||
|
||
public function getMessage(): string | ||
{ | ||
return $this->message; | ||
} | ||
} | ||
|
||
#[\Attribute(\Attribute::TARGET_CLASS)] | ||
class CustomTransport extends Transport | ||
{ | ||
public function __construct() | ||
{ | ||
parent::__construct('message_attribute_sender_2'); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\Messenger\Tests\Fixtures; | ||
|
||
use Symfony\Component\Messenger\Attribute\Transport; | ||
|
||
#[Transport('message_attribute_sender')] | ||
class DummyMessageWithAttributeAndInterface implements DummyMessageInterfaceWithAttribute | ||
{ | ||
private $message; | ||
|
||
public function __construct(string $message) | ||
{ | ||
$this->message = $message; | ||
} | ||
|
||
public function getMessage(): string | ||
{ | ||
return $this->message; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
namespace Symfony\Component\Messenger\Transport\Sender; | ||
|
||
use Psr\Container\ContainerInterface; | ||
use Symfony\Component\Messenger\Attribute\Transport; | ||
use Symfony\Component\Messenger\Envelope; | ||
use Symfony\Component\Messenger\Exception\RuntimeException; | ||
use Symfony\Component\Messenger\Handler\HandlersLocator; | ||
|
@@ -41,20 +42,56 @@ public function __construct(array $sendersMap, ContainerInterface $sendersLocato | |
*/ | ||
public function getSenders(Envelope $envelope): iterable | ||
{ | ||
$seen = []; | ||
$senderAliases = []; | ||
|
||
foreach (HandlersLocator::listTypes($envelope) as $type) { | ||
$typeSenderAliases = []; | ||
|
||
foreach ($this->sendersMap[$type] ?? [] as $senderAlias) { | ||
if (!\in_array($senderAlias, $seen, true)) { | ||
if (!$this->sendersLocator->has($senderAlias)) { | ||
throw new RuntimeException(sprintf('Invalid senders configuration: sender "%s" is not in the senders locator.', $senderAlias)); | ||
} | ||
|
||
$seen[] = $senderAlias; | ||
$sender = $this->sendersLocator->get($senderAlias); | ||
yield $senderAlias => $sender; | ||
} | ||
$typeSenderAliases[] = $senderAlias; | ||
} | ||
|
||
if (!$typeSenderAliases) { | ||
$typeSenderAliases = $this->getSendersFromAttributes($type); | ||
} | ||
|
||
$senderAliases = array_merge($senderAliases, $typeSenderAliases); | ||
} | ||
|
||
$senderAliases = array_unique($senderAliases); | ||
|
||
foreach ($senderAliases as $senderAlias) { | ||
if (!$this->sendersLocator->has($senderAlias)) { | ||
throw new RuntimeException(sprintf('Invalid senders configuration: sender "%s" is not in the senders locator.', $senderAlias)); | ||
} | ||
|
||
$sender = $this->sendersLocator->get($senderAlias); | ||
yield $senderAlias => $sender; | ||
} | ||
} | ||
|
||
/** | ||
* @return string[] | ||
*/ | ||
private function getSendersFromAttributes(string $type): array | ||
{ | ||
if (!class_exists($type) && !interface_exists($type)) { | ||
return []; | ||
} | ||
|
||
try { | ||
$reflectionClass = new \ReflectionClass($type); | ||
} catch (\ReflectionException $e) { | ||
return []; | ||
} | ||
|
||
$attributes = $reflectionClass->getAttributes(Transport::class, \ReflectionAttribute::IS_INSTANCEOF); | ||
|
||
return array_map(function (\ReflectionAttribute $attribute): string { | ||
/** @var Transport $attributeInstance */ | ||
$attributeInstance = $attribute->newInstance(); | ||
|
||
return $attributeInstance->name; | ||
}, $attributes); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's unfortunate to need to do this at runtime (and not compile time). But as messages are not services... or tagged in any way, there is no way for us to collect the attribute information at container build time. So 👍 for this approach. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is indeed unfortunate. Any clever ideas? I understand it depends, but is there a non-negligible performance impact to processing the attributes at runtime? |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand it correctly, IF even one real route exists for the message (something in
$this->sendersMap[$type]
), then we do NOT also add the#[Transport]
senders? So basically any config would override the attribute?I think that is... probably the correct behavior. But what about the
*
catch-all route inmessenger.yaml
(or an interface)? I'm wondering if someone will have the use-case where they generally use the attribute, but then they decide to, in addition to the transport in each class, route all messages (or all messages for an interface) to a 2nd transport via the routing config inmessenger.yaml
.So, I'm conflicted on this point.