diff --git a/psalm.xml b/psalm.xml index 21cba6bd..849e4c85 100644 --- a/psalm.xml +++ b/psalm.xml @@ -11,8 +11,17 @@ > + + + + + + + + + diff --git a/src/Debug/QueueDecorator.php b/src/Debug/QueueDecorator.php index 908ad064..8bbafca2 100644 --- a/src/Debug/QueueDecorator.php +++ b/src/Debug/QueueDecorator.php @@ -6,6 +6,7 @@ use Yiisoft\Queue\MessageStatus; use Yiisoft\Queue\Message\MessageInterface; +use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; use Yiisoft\Queue\QueueInterface; final class QueueDecorator implements QueueInterface @@ -44,4 +45,14 @@ public function getName(): string { return $this->queue->getName(); } + + public function withMiddlewares(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + { + return new self($this->queue->withMiddlewares(...$middlewareDefinitions), $this->collector); + } + + public function withMiddlewaresAdded(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + { + return new self($this->queue->withMiddlewaresAdded(...$middlewareDefinitions), $this->collector); + } } diff --git a/src/Queue.php b/src/Queue.php index 3b4b39db..20d80d89 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -45,9 +45,12 @@ public function __construct( ) { $this->name = StringNormalizer::normalize($name); $this->middlewareDefinitions = $middlewareDefinitions; - $this->finalPushHandler = $this->isSynchronous() - ? new SynchronousPushHandler($worker, $this) - : new AdapterPushHandler($this->adapter); + $this->finalPushHandler = $this->createFinalPushHandler(); + } + + public function __clone() + { + $this->finalPushHandler = $this->createFinalPushHandler(); } public function getName(): string @@ -189,6 +192,13 @@ public function handlePush(MessageInterface $message): MessageInterface }; } + private function createFinalPushHandler(): MessageHandlerPushInterface + { + return $this->isSynchronous() + ? new SynchronousPushHandler($this->worker, $this) + : new AdapterPushHandler($this->adapter); + } + /** * @psalm-assert-if-false !null $this->adapter */ diff --git a/src/QueueInterface.php b/src/QueueInterface.php index b4ac11e3..781723b1 100644 --- a/src/QueueInterface.php +++ b/src/QueueInterface.php @@ -5,6 +5,7 @@ namespace Yiisoft\Queue; use Yiisoft\Queue\Message\MessageInterface; +use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; interface QueueInterface { @@ -40,4 +41,18 @@ public function status(string|int $id): MessageStatus; * Returns the logical name of the queue. */ public function getName(): string; + + /** + * Creates a new instance with the specified middlewares. All the existing middlewares are replaced. + * + * @param MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions The middleware definitions. + */ + public function withMiddlewares(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self; + + /** + * Creates a new instance with the specified middlewares added after the existing ones. + * + * @param MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions The middleware definitions. + */ + public function withMiddlewaresAdded(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self; } diff --git a/stubs/StubQueue.php b/stubs/StubQueue.php index f34c6d11..353946c8 100644 --- a/stubs/StubQueue.php +++ b/stubs/StubQueue.php @@ -6,6 +6,7 @@ use Yiisoft\Queue\MessageStatus; use Yiisoft\Queue\Message\MessageInterface; +use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; use Yiisoft\Queue\QueueInterface; /** @@ -38,4 +39,14 @@ public function getName(): string { return $this->name; } + + public function withMiddlewares(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + { + return $this; + } + + public function withMiddlewaresAdded(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + { + return $this; + } } diff --git a/tests/App/DummyQueue.php b/tests/App/DummyQueue.php index 325ae3c0..3871243e 100644 --- a/tests/App/DummyQueue.php +++ b/tests/App/DummyQueue.php @@ -7,6 +7,7 @@ use Exception; use Yiisoft\Queue\MessageStatus; use Yiisoft\Queue\Message\MessageInterface; +use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; use Yiisoft\Queue\QueueInterface; final class DummyQueue implements QueueInterface @@ -36,4 +37,14 @@ public function getName(): string { return $this->name; } + + public function withMiddlewares(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + { + throw new Exception('`withMiddlewares()` method is not implemented yet.'); + } + + public function withMiddlewaresAdded(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + { + throw new Exception('`withMiddlewaresAdded()` method is not implemented yet.'); + } } diff --git a/tests/App/FakeAdapter.php b/tests/App/FakeAdapter.php index 46a987d7..683cd47d 100644 --- a/tests/App/FakeAdapter.php +++ b/tests/App/FakeAdapter.php @@ -5,6 +5,7 @@ namespace Yiisoft\Queue\Tests\App; use BackedEnum; +use Exception; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\StringNormalizer; use Yiisoft\Queue\MessageStatus; @@ -24,23 +25,24 @@ public function push(MessageInterface $message): MessageInterface public function runExisting(callable $handlerCallback): void { - //skip + throw new Exception('`runExisting()` method is not implemented yet.'); } public function status(string|int $id): MessageStatus { - //skip + throw new Exception('`status()` method is not implemented yet.'); } public function subscribe(callable $handlerCallback): void { - //skip + throw new Exception('`subscribe()` method is not implemented yet.'); } public function withChannel(string|BackedEnum $channel): self { $new = clone $this; $new->channel = StringNormalizer::normalize($channel); + return $new; } } diff --git a/tests/App/StaticMessageHandler.php b/tests/App/StaticMessageHandler.php index 3825836a..b9dd6e0e 100644 --- a/tests/App/StaticMessageHandler.php +++ b/tests/App/StaticMessageHandler.php @@ -4,7 +4,7 @@ namespace Yiisoft\Queue\Tests\App; -class StaticMessageHandler +final class StaticMessageHandler { public static bool $wasHandled = false; diff --git a/tests/Benchmark/MetadataBench.php b/tests/Benchmark/MetadataBench.php index 12300a7d..e60565f6 100644 --- a/tests/Benchmark/MetadataBench.php +++ b/tests/Benchmark/MetadataBench.php @@ -6,7 +6,6 @@ use Generator; use PhpBench\Attributes\ParamProviders; -use PhpBench\Model\Tag; use Yiisoft\Queue\Message\IdEnvelope; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Message\MessageInterface; @@ -17,7 +16,6 @@ final class MetadataBench /** * Create metadata as an array and read its value from an array. */ - #[Tag('metadata_read')] public function benchArrayRead(): void { $message = new Message('foo', 'bar', ['id' => 1]); @@ -27,7 +25,6 @@ public function benchArrayRead(): void /** * Create metadata as an object and read its value immediately. */ - #[Tag('metadata_read')] public function benchEnvelopeRead(): void { $message = new IdEnvelope(new Message('foo', 'bar'), 1); @@ -37,7 +34,6 @@ public function benchEnvelopeRead(): void /** * Create metadata as an array and read its value from an envelope object. */ - #[Tag('metadata_read')] public function benchEnvelopeReadRestored(): void { $message = IdEnvelope::fromMessage(new Message('foo', 'bar', ['id' => 1])); @@ -63,7 +59,6 @@ public function provideEnvelopeStack(): Generator * @psalm-param array{message: MessageInterface} $params */ #[ParamProviders('provideEnvelopeStack')] - #[Tag('metadata_read')] public function benchEnvelopeReadFromStack(array $params): void { $id = IdEnvelope::fromMessage($params['message'])->getId(); @@ -82,7 +77,6 @@ public function provideEnvelopeStackCounts(): Generator * @psalm-param array{0: int} $params */ #[ParamProviders('provideEnvelopeStackCounts')] - #[Tag('metadata_create')] public function benchEnvelopeStackCreation(array $params): void { $message = new Message('foo', 'bar'); @@ -97,7 +91,6 @@ public function benchEnvelopeStackCreation(array $params): void * @psalm-param array{0: int} $params */ #[ParamProviders('provideEnvelopeStackCounts')] - #[Tag('metadata_create')] public function benchMetadataArrayCreation(array $params): void { $metadata = ['failure-meta' => []]; diff --git a/tests/Benchmark/QueueBench.php b/tests/Benchmark/QueueBench.php index 0f2e73ab..1b79a475 100644 --- a/tests/Benchmark/QueueBench.php +++ b/tests/Benchmark/QueueBench.php @@ -6,7 +6,6 @@ use Generator; use PhpBench\Attributes\ParamProviders; -use PhpBench\Model\Tag; use Psr\Log\NullLogger; use Yiisoft\Injector\Injector; use Yiisoft\Queue\Cli\SimpleLoop; @@ -80,7 +79,6 @@ public function providePush(): Generator } #[ParamProviders('providePush')] - #[Tag('queue_push')] public function benchPush(array $params): void { $this->queue->push($params['message']); @@ -103,7 +101,6 @@ public function provideConsume(): Generator } #[ParamProviders('provideConsume')] - #[Tag('queue_consume')] public function benchConsume(array $params): void { $this->adapter->message = $params['message']; diff --git a/tests/Unit/Cli/SignalLoopTest.php b/tests/Unit/Cli/SignalLoopTest.php index 1b542405..ebf06971 100644 --- a/tests/Unit/Cli/SignalLoopTest.php +++ b/tests/Unit/Cli/SignalLoopTest.php @@ -16,6 +16,10 @@ use const SIGTERM; use const SIGTSTP; +/** + * @psalm-suppress UndefinedConstant + * @psalm-suppress PossiblyFalseArgument + */ #[RequiresPhpExtension('pcntl')] final class SignalLoopTest extends TestCase { diff --git a/tests/Unit/Debug/QueueCollectorTest.php b/tests/Unit/Debug/QueueCollectorTest.php index fe554ea3..5ed42be8 100644 --- a/tests/Unit/Debug/QueueCollectorTest.php +++ b/tests/Unit/Debug/QueueCollectorTest.php @@ -22,7 +22,8 @@ protected function setUp(): void } /** - * @param CollectorInterface|QueueCollector $collector + * @param QueueCollector $collector + * @psalm-suppress MoreSpecificImplementedParamType */ protected function collectTestData(CollectorInterface $collector): void { diff --git a/tests/Unit/Debug/QueueDecoratorTest.php b/tests/Unit/Debug/QueueDecoratorTest.php index ab7a73d0..9d0169c6 100644 --- a/tests/Unit/Debug/QueueDecoratorTest.php +++ b/tests/Unit/Debug/QueueDecoratorTest.php @@ -11,7 +11,7 @@ use Yiisoft\Queue\Message\MessageInterface; use Yiisoft\Queue\QueueInterface; -class QueueDecoratorTest extends TestCase +final class QueueDecoratorTest extends TestCase { public function testStatus(): void { diff --git a/tests/Unit/Debug/QueueProviderInterfaceProxyTest.php b/tests/Unit/Debug/QueueProviderInterfaceProxyTest.php index ffc7b852..2ba2ae42 100644 --- a/tests/Unit/Debug/QueueProviderInterfaceProxyTest.php +++ b/tests/Unit/Debug/QueueProviderInterfaceProxyTest.php @@ -11,7 +11,7 @@ use Yiisoft\Queue\Provider\QueueProviderInterface; use Yiisoft\Queue\QueueInterface; -class QueueProviderInterfaceProxyTest extends TestCase +final class QueueProviderInterfaceProxyTest extends TestCase { public function testGet(): void { diff --git a/tests/Unit/Message/EnvelopeTest.php b/tests/Unit/Message/EnvelopeTest.php index 111b863d..93fdefce 100644 --- a/tests/Unit/Message/EnvelopeTest.php +++ b/tests/Unit/Message/EnvelopeTest.php @@ -29,7 +29,7 @@ public function testFromData(): void public function testNonArrayStackIsNormalized(): void { $base = new Message('handler', 'data', [EnvelopeInterface::ENVELOPE_STACK_KEY => 'oops']); - $wrapped = new DummyEnvelope($base, 'id-1'); + $wrapped = new DummyEnvelope($base); $meta = $wrapped->getMetadata(); self::assertIsArray($meta[EnvelopeInterface::ENVELOPE_STACK_KEY]); diff --git a/tests/Unit/Message/JsonMessageSerializerTest.php b/tests/Unit/Message/JsonMessageSerializerTest.php index e0f2616c..ed511681 100644 --- a/tests/Unit/Message/JsonMessageSerializerTest.php +++ b/tests/Unit/Message/JsonMessageSerializerTest.php @@ -15,6 +15,8 @@ use function sprintf; +use const JSON_THROW_ON_ERROR; + /** * Testing message serialization options */ @@ -31,7 +33,7 @@ public function testTypeFormat(mixed $type): void $this->expectExceptionMessage(sprintf('Message type must be a string. Got %s.', get_debug_type($type))); $this->expectException(InvalidArgumentException::class); - $serializer->unserialize(json_encode($payload)); + $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); } public static function dataUnsupportedTypeFormat(): iterable @@ -53,7 +55,7 @@ public function testDefaultMessageClassFallbackWrongClass(): void ], ]; - $message = $serializer->unserialize(json_encode($payload)); + $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertInstanceOf(Message::class, $message); } @@ -65,7 +67,7 @@ public function testDefaultMessageClassFallbackClassNotSet(): void 'data' => 'test', 'meta' => [], ]; - $message = $serializer->unserialize(json_encode($payload)); + $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertInstanceOf(Message::class, $message); } @@ -76,7 +78,7 @@ public function testPayloadFormat(mixed $payload): void $this->expectExceptionMessage(sprintf('Payload must be array. Got %s.', get_debug_type($payload))); $this->expectException(InvalidArgumentException::class); - $serializer->unserialize(json_encode($payload)); + $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); } public static function dataUnsupportedPayloadFormat(): iterable @@ -95,7 +97,7 @@ public function testMetadataFormat(mixed $meta): void $this->expectExceptionMessage(sprintf('Metadata must be an array. Got %s.', get_debug_type($meta))); $this->expectException(InvalidArgumentException::class); - $serializer->unserialize(json_encode($payload)); + $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); } public static function dataUnsupportedMetadataFormat(): iterable @@ -110,7 +112,7 @@ public function testUnserializeFromData(): void $payload = ['type' => 'handler', 'data' => 'test']; $serializer = $this->createSerializer(); - $message = $serializer->unserialize(json_encode($payload)); + $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertEquals($payload['data'], $message->getData()); $this->assertEquals([EnvelopeInterface::ENVELOPE_STACK_KEY => []], $message->getMetadata()); @@ -121,7 +123,7 @@ public function testUnserializeWithMetadata(): void $payload = ['type' => 'handler', 'data' => 'test', 'meta' => ['int' => 1, 'str' => 'string', 'bool' => true]]; $serializer = $this->createSerializer(); - $message = $serializer->unserialize(json_encode($payload)); + $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertEquals($payload['data'], $message->getData()); $this->assertEquals(['int' => 1, 'str' => 'string', 'bool' => true, EnvelopeInterface::ENVELOPE_STACK_KEY => []], $message->getMetadata()); @@ -141,7 +143,7 @@ public function testUnserializeEnvelopeStack(): void $serializer = $this->createSerializer(); /** @var IdEnvelope $message */ - $message = $serializer->unserialize(json_encode($payload)); + $message = $serializer->unserialize(json_encode($payload, JSON_THROW_ON_ERROR)); $this->assertEquals($payload['data'], $message->getData()); $this->assertEquals([IdEnvelope::class], $message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY]); diff --git a/tests/Unit/Middleware/CallableFactoryTest.php b/tests/Unit/Middleware/CallableFactoryTest.php index 3275278c..f270543b 100644 --- a/tests/Unit/Middleware/CallableFactoryTest.php +++ b/tests/Unit/Middleware/CallableFactoryTest.php @@ -19,6 +19,7 @@ public function testCreatePositive(mixed $definition, array $arguments, mixed $e $factory = new CallableFactory($container); $callable = $factory->create($definition); + /** @psalm-suppress RedundantCondition */ self::assertIsCallable($callable); self::assertSame($expectedResult, $callable(...$arguments)); } diff --git a/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php b/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php index 949646a9..c3ce5b50 100644 --- a/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php +++ b/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php @@ -18,7 +18,7 @@ use const PHP_INT_MAX; -class ExponentialDelayMiddlewareTest extends TestCase +final class ExponentialDelayMiddlewareTest extends TestCase { public static function constructorRequirementsProvider(): array { diff --git a/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php b/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php index 7641ea88..6c1529bf 100644 --- a/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php +++ b/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php @@ -20,7 +20,7 @@ use Yiisoft\Queue\Stubs\StubDelayMiddleware; use Yiisoft\Queue\Tests\TestCase; -class SendAgainMiddlewareTest extends TestCase +final class SendAgainMiddlewareTest extends TestCase { final public const KEY_EXPONENTIAL_ATTEMPTS = ExponentialDelayMiddleware::META_KEY_ATTEMPTS . '-test'; final public const KEY_EXPONENTIAL_DELAY = ExponentialDelayMiddleware::META_KEY_DELAY . '-test'; diff --git a/tests/Unit/Provider/PredefinedQueueProviderTest.php b/tests/Unit/Provider/PredefinedQueueProviderTest.php index 08be260b..83ff06cb 100644 --- a/tests/Unit/Provider/PredefinedQueueProviderTest.php +++ b/tests/Unit/Provider/PredefinedQueueProviderTest.php @@ -65,6 +65,8 @@ public function testInvalidQueueConfig(): void 'stdClass', ), ); + + /** @psalm-suppress InvalidArgument */ new PredefinedQueueProvider([ 'queue1' => new stdClass(), ]); diff --git a/tests/Unit/Stubs/StubAdapterTest.php b/tests/Unit/Stubs/StubAdapterTest.php index dcbd24d8..8f455c6e 100644 --- a/tests/Unit/Stubs/StubAdapterTest.php +++ b/tests/Unit/Stubs/StubAdapterTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use Yiisoft\Queue\MessageStatus; use Yiisoft\Queue\Message\Message; +use Yiisoft\Queue\Message\MessageInterface; use Yiisoft\Queue\Stubs\StubAdapter; final class StubAdapterTest extends TestCase @@ -18,7 +19,7 @@ public function testBase(): void $this->assertSame($message, $adapter->push($message)); $this->assertSame(MessageStatus::DONE, $adapter->status('test')); - $adapter->runExisting(static fn() => null); - $adapter->subscribe(static fn() => null); + $adapter->runExisting(static fn(MessageInterface $message): bool => true); + $adapter->subscribe(static fn(MessageInterface $message): bool => true); } } diff --git a/tests/Unit/WorkerTest.php b/tests/Unit/WorkerTest.php index e8991c2e..22f9aae4 100644 --- a/tests/Unit/WorkerTest.php +++ b/tests/Unit/WorkerTest.php @@ -33,7 +33,7 @@ final class WorkerTest extends TestCase { #[DataProvider('messageHandledDataProvider')] - public function testMessageHandled($handler, array $containerServices): void + public function testMessageHandled(mixed $handler, array $containerServices): void { $message = new Message('simple', ['test-data']); $logger = new SimpleLogger();