-
-
Notifications
You must be signed in to change notification settings - Fork 31
Introduce handler resolver #327
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
base: master
Are you sure you want to change the base?
Changes from all commits
ab628c9
596c7a6
170acdc
da2d72b
89789bc
947b653
3909635
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,29 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Handler; | ||
|
|
||
| use Yiisoft\Queue\Message\MessageInterface; | ||
|
|
||
| /** | ||
| * Handles a message by invoking the given callable. | ||
| * | ||
| * @internal | ||
| */ | ||
| final class CallableHandler implements HandlerInterface | ||
| { | ||
| /** | ||
| * @param callable $handler Callable invoked to handle a message. | ||
| * | ||
| * @psalm-param callable(MessageInterface): void $handler | ||
| */ | ||
| public function __construct( | ||
| private readonly mixed $handler, | ||
| ) {} | ||
|
|
||
| public function handle(MessageInterface $message): void | ||
| { | ||
| ($this->handler)($message); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Handler; | ||
|
|
||
| use Yiisoft\Queue\Message\MessageInterface; | ||
|
|
||
| /** | ||
| * Handles a message. | ||
| */ | ||
| interface HandlerInterface | ||
| { | ||
| /** | ||
| * Handle the given message. | ||
| */ | ||
| public function handle(MessageInterface $message): void; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Handler; | ||
|
|
||
| use LogicException; | ||
| use Throwable; | ||
|
|
||
| use function sprintf; | ||
|
|
||
| /** | ||
| * Thrown when a handler for the given message type is not found. | ||
| */ | ||
| final class HandlerNotFoundException extends LogicException | ||
| { | ||
| public function __construct(string $messageType, int $code = 0, ?Throwable $previous = null) | ||
| { | ||
| parent::__construct( | ||
| sprintf('Queue handler for message type "%s" does not exist.', $messageType), | ||
| $code, | ||
| $previous, | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,144 @@ | ||||||
| <?php | ||||||
|
|
||||||
| declare(strict_types=1); | ||||||
|
|
||||||
| namespace Yiisoft\Queue\Message\Handler; | ||||||
|
|
||||||
| use LogicException; | ||||||
| use Psr\Container\ContainerExceptionInterface; | ||||||
| use Psr\Container\ContainerInterface; | ||||||
| use Yiisoft\Injector\Injector; | ||||||
| use Yiisoft\Queue\Message\MessageInterface; | ||||||
| use Yiisoft\Queue\Middleware\CallableFactory; | ||||||
| use Yiisoft\Queue\Middleware\InvalidCallableConfigurationException; | ||||||
|
|
||||||
| use function array_key_exists; | ||||||
| use function is_callable; | ||||||
| use function is_string; | ||||||
| use function sprintf; | ||||||
|
|
||||||
| /** | ||||||
| * Resolves message handlers from configuration, a DI container, or a callable factory. | ||||||
| */ | ||||||
| final class HandlerResolver | ||||||
| { | ||||||
| /** | ||||||
| * @var HandlerInterface[] Cache of resolved handlers. | ||||||
| * @psalm-var array<non-empty-string, HandlerInterface> | ||||||
| */ | ||||||
| private array $cache = []; | ||||||
|
|
||||||
| private readonly Injector $injector; | ||||||
| private readonly CallableFactory $callableFactory; | ||||||
|
|
||||||
| /** | ||||||
| * @param (array|callable|HandlerInterface|string)[] $handlers Handler definitions indexed by message type. | ||||||
| * @param ContainerInterface $container Container used to resolve handlers. | ||||||
| * @param ContainerInterface|null $callableDependencyContainer Container used to resolve callable handler | ||||||
| * dependencies. If not set, the main container is used. | ||||||
| * | ||||||
| * @psalm-param array<non-empty-string, array|callable|HandlerInterface|string> $handlers | ||||||
| */ | ||||||
| public function __construct( | ||||||
| private readonly array $handlers, | ||||||
| private readonly ContainerInterface $container, | ||||||
| ?ContainerInterface $callableDependencyContainer = null, | ||||||
| ) { | ||||||
| $this->injector = new Injector($callableDependencyContainer ?? $this->container); | ||||||
| $this->callableFactory = new CallableFactory($this->container); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Get a handler for the given message type. | ||||||
| * | ||||||
| * @param string $messageType Message type. | ||||||
| * | ||||||
| * @throws HandlerNotFoundException If no handler exists for the message type. | ||||||
| * @throws InvalidHandlerConfigurationException If the handler definition is configured incorrectly. | ||||||
| * @throws ContainerExceptionInterface Error while retrieving the entry from container. | ||||||
| */ | ||||||
| public function resolve(string $messageType): HandlerInterface | ||||||
| { | ||||||
| if ($messageType === '') { | ||||||
| throw new LogicException('Message type cannot be empty.'); | ||||||
| } | ||||||
|
|
||||||
| if (array_key_exists($messageType, $this->cache)) { | ||||||
| return $this->cache[$messageType]; | ||||||
| } | ||||||
|
|
||||||
| $this->cache[$messageType] = $this->internalResolve($messageType); | ||||||
|
|
||||||
| return $this->cache[$messageType]; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @throws HandlerNotFoundException | ||||||
| * @throws InvalidHandlerConfigurationException | ||||||
| * @throws ContainerExceptionInterface | ||||||
| */ | ||||||
| private function internalResolve(string $messageType): HandlerInterface | ||||||
| { | ||||||
| $definition = $this->handlers[$messageType] ?? $messageType; | ||||||
|
|
||||||
| if ($definition instanceof HandlerInterface) { | ||||||
| return $definition; | ||||||
| } | ||||||
|
|
||||||
| if (is_string($definition)) { | ||||||
|
Member
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. Previously it was possible to use callables. Now it always looks into container in case of strings. Worth keeping previous behavior?
Suggested change
|
||||||
| return $this->getHandlerFromContainer($messageType, $definition); | ||||||
| } | ||||||
|
Comment on lines
+88
to
+90
Contributor
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. Needs fallback to callable resolver |
||||||
|
|
||||||
| return $this->createCallableHandler($messageType, $definition); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @throws HandlerNotFoundException | ||||||
| * @throws InvalidHandlerConfigurationException | ||||||
| * @throws ContainerExceptionInterface | ||||||
| */ | ||||||
| private function getHandlerFromContainer(string $messageType, string $id): HandlerInterface | ||||||
| { | ||||||
| if (!$this->container->has($id)) { | ||||||
| throw new HandlerNotFoundException($messageType); | ||||||
| } | ||||||
|
|
||||||
| $handler = $this->container->get($id); | ||||||
|
|
||||||
| if ($handler instanceof HandlerInterface) { | ||||||
| return $handler; | ||||||
| } | ||||||
|
|
||||||
| if (is_callable($handler)) { | ||||||
| return $this->createCallableHandler($messageType, $handler); | ||||||
| } | ||||||
|
|
||||||
| throw new InvalidHandlerConfigurationException( | ||||||
| $messageType, | ||||||
| sprintf( | ||||||
| 'Resolved from container handler should be an instance of "%s" or callable, got "%s".', | ||||||
| HandlerInterface::class, | ||||||
| get_debug_type($handler), | ||||||
| ), | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @throws InvalidHandlerConfigurationException | ||||||
| * @throws ContainerExceptionInterface | ||||||
| */ | ||||||
| private function createCallableHandler(string $messageType, mixed $definition): CallableHandler | ||||||
| { | ||||||
| try { | ||||||
| $callable = $this->callableFactory->create($definition); | ||||||
| } catch (InvalidCallableConfigurationException $exception) { | ||||||
| throw new InvalidHandlerConfigurationException($messageType, $exception->getMessage(), $exception); | ||||||
| } | ||||||
|
|
||||||
| $callable = function (MessageInterface $message) use ($callable): void { | ||||||
| $this->injector->invoke($callable, [$message]); | ||||||
| }; | ||||||
|
|
||||||
| return new CallableHandler($callable); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Yiisoft\Queue\Message\Handler; | ||
|
|
||
| use LogicException; | ||
| use Throwable; | ||
|
|
||
| use function sprintf; | ||
|
|
||
| /** | ||
| * Thrown when a handler for the given message type is configured incorrectly. | ||
| */ | ||
| final class InvalidHandlerConfigurationException extends LogicException | ||
| { | ||
| public function __construct(string $messageType, ?string $additionalMessage = null, ?Throwable $previous = null) | ||
| { | ||
| $message = sprintf('Queue handler for message type "%s" is configured incorrectly.', $messageType); | ||
|
|
||
| if ($additionalMessage !== null) { | ||
| $message .= ' ' . $additionalMessage; | ||
| } | ||
|
|
||
| parent::__construct($message, 0, $previous); | ||
| } | ||
| } |
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.