Skip to content
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
9 changes: 3 additions & 6 deletions docs/guide/en/configuration-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use Psr\Container\ContainerInterface;
use Psr\Log\NullLogger;
use Yiisoft\Queue\Cli\SimpleLoop;
use Yiisoft\Queue\Message\Handler\HandlerResolver;
use Yiisoft\Queue\Middleware\CallableFactory;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareFactory;
use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher;
Expand All @@ -39,20 +38,18 @@ $handlers = [
DownloadFileMessage::TYPE => [FileDownloader::class, 'handle'],
];

$callableFactory = new CallableFactory($container);

// Create middleware dispatchers
$consumeMiddlewareDispatcher = new ConsumeMiddlewareDispatcher(
new ConsumeMiddlewareFactory($container, $callableFactory),
new ConsumeMiddlewareFactory($container),
);

$failureMiddlewareDispatcher = new FailureMiddlewareDispatcher(
new FailureMiddlewareFactory($container, $callableFactory),
new FailureMiddlewareFactory($container),
[],
);

$pushMiddlewareConfig = new PushMiddlewareConfig(
new PushMiddlewareFactory($container, $callableFactory),
new PushMiddlewareFactory($container),
);

// Create worker
Expand Down
10 changes: 1 addition & 9 deletions src/Message/Handler/HandlerResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

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;

Expand All @@ -27,7 +25,6 @@ final class HandlerResolver
*/
private array $cache = [];

private readonly Injector $injector;
private readonly CallableFactory $callableFactory;

/**
Expand All @@ -43,8 +40,7 @@ public function __construct(
private readonly ContainerInterface $container,
?ContainerInterface $callableDependencyContainer = null,
) {
$this->injector = new Injector($callableDependencyContainer ?? $this->container);
$this->callableFactory = new CallableFactory($this->container);
$this->callableFactory = new CallableFactory($this->container, $callableDependencyContainer);
}

/**
Expand Down Expand Up @@ -136,10 +132,6 @@ private function createCallableHandler(string $messageType, mixed $definition):
throw new InvalidHandlerConfigurationException($messageType, $exception->getMessage(), $exception);
}

$callable = function (MessageInterface $message) use ($callable): void {
$this->injector->invoke($callable, [$message]);
};

return new CallableHandler($callable);
}
}
26 changes: 23 additions & 3 deletions src/Middleware/CallableFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,50 @@
use Psr\Container\ContainerInterface;
use ReflectionException;
use ReflectionMethod;
use Yiisoft\Injector\Injector;

use function is_array;
use function is_callable;
use function is_object;
use function is_string;

/**
* @internal Create real callable listener from configuration.
* @internal Create real callable listener from configuration, ready to be invoked with dependency injection.
*/
final class CallableFactory
{
private readonly Injector $injector;

public function __construct(
private readonly ContainerInterface $container,
) {}
?ContainerInterface $dependencyContainer = null,
) {
$this->injector = new Injector($dependencyContainer ?? $container);
Comment thread
vjik marked this conversation as resolved.
}

/**
* Create a real callable listener from definition.
* Create a real callable listener from definition. Calling the returned callable invokes the resolved listener,
* injecting its dependencies by type hinting.
*
* @param mixed $definition Definition to create listener from.
*
* @throws InvalidCallableConfigurationException Failed to create listener.
* @throws ContainerExceptionInterface Error while retrieving the entry from container.
*
* @psalm-return callable(mixed...): mixed
*/
public function create(mixed $definition): callable
{
$callable = $this->resolve($definition);

return fn(mixed ...$params): mixed => $this->injector->invoke($callable, $params);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CallableFactory is @internal, and its only two consumers (HandlerResolver and the *MiddlewareFactory classes) always invoke the resolved callable with positional objects ($message, or $request/$handler) — never with positional scalars. So create('strlen')('foo') wasn't exercising any real usage pattern of this class, just a synthetic case in the unit test.

}

/**
* @throws InvalidCallableConfigurationException Failed to resolve listener.
* @throws ContainerExceptionInterface Error while retrieving the entry from container.
*/
private function resolve(mixed $definition): callable
{
if ($definition === null) {
throw new InvalidCallableConfigurationException();
Expand Down
13 changes: 4 additions & 9 deletions src/Middleware/Consume/ConsumeMiddlewareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace Yiisoft\Queue\Middleware\Consume;

use Psr\Container\ContainerInterface;
use Yiisoft\Injector\Injector;
use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException;
use Yiisoft\Queue\Middleware\MiddlewareFactory;

Expand Down Expand Up @@ -60,20 +58,17 @@ protected function getInterfaceName(): string

protected function wrapMiddleware(callable $callback): ConsumeMiddlewareInterface
{
$container = $this->container;
return new class ($callback, $container) implements ConsumeMiddlewareInterface {
return new class ($callback) implements ConsumeMiddlewareInterface {
private $callback;

public function __construct(
callable $callback,
private readonly ContainerInterface $container,
) {
public function __construct(callable $callback)
{
$this->callback = $callback;
}

public function processConsume(ConsumeRequest $request, ConsumeHandlerInterface $handler): ConsumeRequest
{
$response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
$response = ($this->callback)($request, $handler);
if ($response instanceof ConsumeRequest) {
return $response;
}
Expand Down
13 changes: 4 additions & 9 deletions src/Middleware/FailureHandling/FailureMiddlewareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
namespace Yiisoft\Queue\Middleware\FailureHandling;

use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Yiisoft\Injector\Injector;
use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException;
use Yiisoft\Queue\Middleware\MiddlewareFactory;

Expand Down Expand Up @@ -64,20 +62,17 @@ protected function getInterfaceName(): string

protected function wrapMiddleware(callable $callback): FailureMiddlewareInterface
{
$container = $this->container;
return new class ($callback, $container) implements FailureMiddlewareInterface {
return new class ($callback) implements FailureMiddlewareInterface {
private $callback;

public function __construct(
callable $callback,
private readonly ContainerInterface $container,
) {
public function __construct(callable $callback)
{
$this->callback = $callback;
}

public function processFailure(FailureHandlingRequest $request, FailureHandlerInterface $handler): FailureHandlingRequest
{
$response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
$response = ($this->callback)($request, $handler);
if ($response instanceof FailureHandlingRequest) {
return $response;
}
Expand Down
13 changes: 11 additions & 2 deletions src/Middleware/MiddlewareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,19 @@
*/
abstract class MiddlewareFactory
{
private readonly CallableFactory $callableFactory;

/**
* @param ContainerInterface $container Container used to resolve middleware.
* @param ContainerInterface|null $callableDependencyContainer Container used to resolve callable middleware
* dependencies. If not set, the main container is used.
*/
public function __construct(
protected readonly ContainerInterface $container,
private readonly CallableFactory $callableFactory,
) {}
?ContainerInterface $callableDependencyContainer = null,
) {
$this->callableFactory = new CallableFactory($container, $callableDependencyContainer);
}

/**
* @param callable|array|string $definition Middleware definition in one of the following formats:
Expand Down
12 changes: 4 additions & 8 deletions src/Middleware/Push/PushMiddlewareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace Yiisoft\Queue\Middleware\Push;

use Psr\Container\ContainerInterface;
use Yiisoft\Injector\Injector;
use Yiisoft\Queue\Message\MessageInterface;
use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException;
use Yiisoft\Queue\Middleware\MiddlewareFactory;
Expand Down Expand Up @@ -68,19 +66,17 @@ protected function getInterfaceName(): string

protected function wrapMiddleware(callable $callback): PushMiddlewareInterface
{
return new class ($callback, $this->container) implements PushMiddlewareInterface {
return new class ($callback) implements PushMiddlewareInterface {
private $callback;

public function __construct(
callable $callback,
private readonly ContainerInterface $container,
) {
public function __construct(callable $callback)
{
$this->callback = $callback;
}

public function processPush(MessageInterface $message, PushHandlerInterface $handler): MessageInterface
{
$response = (new Injector($this->container))->invoke($this->callback, [$message, $handler]);
$response = ($this->callback)($message, $handler);
if ($response instanceof MessageInterface) {
return $response;
}
Expand Down
8 changes: 3 additions & 5 deletions tests/Benchmark/QueueBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Yiisoft\Queue\Message\GenericMessage;
use Yiisoft\Queue\Message\Serializer\JsonMessageEncoder;
use Yiisoft\Queue\Message\Serializer\MessageSerializer;
use Yiisoft\Queue\Middleware\CallableFactory;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareFactory;
use Yiisoft\Queue\Middleware\FailureHandling\FailureEnvelope;
Expand All @@ -39,14 +38,13 @@ final class QueueBench
public function __construct()
{
$container = new SimpleContainer();
$callableFactory = new CallableFactory($container);
$logger = new NullLogger();

$worker = new Worker(
$logger,
new ConsumeMiddlewareDispatcher(new ConsumeMiddlewareFactory($container, $callableFactory)),
new ConsumeMiddlewareDispatcher(new ConsumeMiddlewareFactory($container)),
new FailureMiddlewareDispatcher(
new FailureMiddlewareFactory($container, $callableFactory),
new FailureMiddlewareFactory($container),
[],
),
new HandlerResolver(
Expand All @@ -61,7 +59,7 @@ public function __construct()

$this->producer = new AsyncQueueProducer(
$logger,
new PushMiddlewareConfig(new PushMiddlewareFactory($container, $callableFactory)),
new PushMiddlewareConfig(new PushMiddlewareFactory($container)),
$this->adapter,
);
$this->consumer = new QueueConsumer($worker, new SimpleLoop(0), $logger, $this->adapter);
Expand Down
13 changes: 2 additions & 11 deletions tests/Integration/MiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Yiisoft\Test\Support\Log\SimpleLogger;
use Yiisoft\Queue\Message\GenericMessage;
use Yiisoft\Queue\Message\MessageInterface;
use Yiisoft\Queue\Middleware\CallableFactory;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareFactory;
use Yiisoft\Queue\Middleware\FailureHandling\FailureFinalHandler;
Expand Down Expand Up @@ -47,9 +46,6 @@ public function testFullStackPush(): void
$pushMiddlewareConfig = new PushMiddlewareConfig(
new PushMiddlewareFactory(
$this->createMock(ContainerInterface::class),
new CallableFactory(
$this->createMock(ContainerInterface::class),
),
),
[
new TestMiddleware('common 1'),
Expand Down Expand Up @@ -85,21 +81,17 @@ public function testFullStackConsume(): void
'common 2',
];
$container = new SimpleContainer();
$callableFactory = new CallableFactory($container);

$consumeMiddlewareDispatcher = new ConsumeMiddlewareDispatcher(
new ConsumeMiddlewareFactory(
$this->createMock(ContainerInterface::class),
new CallableFactory(
$this->createMock(ContainerInterface::class),
),
),
new TestMiddleware('common 1'),
new TestMiddleware('common 2'),
);

$failureMiddlewareDispatcher = new FailureMiddlewareDispatcher(
new FailureMiddlewareFactory($container, $callableFactory),
new FailureMiddlewareFactory($container),
[],
);

Expand All @@ -125,7 +117,6 @@ public function testFullStackFailure(): void
$queueCallback = static fn(MessageInterface $message): MessageInterface => $message;
$queue = $this->createMock(QueueProducerInterface::class);
$container = new SimpleContainer([SendAgainMiddleware::class => new SendAgainMiddleware('test-container', 1, $queue)]);
$callableFactory = new CallableFactory($container);

$queue->expects(self::exactly(7))->method('push')->willReturnCallback($queueCallback);
$queue->method('getQueueName')->willReturn('simple');
Expand Down Expand Up @@ -154,7 +145,7 @@ public function testFullStackFailure(): void
],
];
$dispatcher = new FailureMiddlewareDispatcher(
new FailureMiddlewareFactory($container, $callableFactory),
new FailureMiddlewareFactory($container),
$middlewares,
);

Expand Down
16 changes: 3 additions & 13 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use Yiisoft\Queue\Cli\LoopInterface;
use Yiisoft\Queue\Cli\SimpleLoop;
use Yiisoft\Queue\DefaultQueue;
use Yiisoft\Queue\Middleware\CallableFactory;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareFactory;
use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher;
Expand Down Expand Up @@ -160,30 +159,21 @@ protected function getMessageHandlers(): array
protected function getPushMiddlewareConfig(): PushMiddlewareConfig
{
return new PushMiddlewareConfig(
new PushMiddlewareFactory(
$this->getContainer(),
new CallableFactory($this->getContainer()),
),
new PushMiddlewareFactory($this->getContainer()),
);
}

protected function getConsumeMiddlewareDispatcher(): ConsumeMiddlewareDispatcher
{
return new ConsumeMiddlewareDispatcher(
new ConsumeMiddlewareFactory(
$this->getContainer(),
new CallableFactory($this->getContainer()),
),
new ConsumeMiddlewareFactory($this->getContainer()),
);
}

protected function getFailureMiddlewareDispatcher(): FailureMiddlewareDispatcher
{
return new FailureMiddlewareDispatcher(
new FailureMiddlewareFactory(
$this->getContainer(),
new CallableFactory($this->getContainer()),
),
new FailureMiddlewareFactory($this->getContainer()),
[],
);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Middleware/CallableFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static function positiveDefinitionsProvider(): iterable

yield 'callable string' => [
'strlen',
['foo'],
['string' => 'foo'],
3,
new SimpleContainer(),
];
Expand Down
Loading
Loading