From 971ab195623fcf86be2e05ec1182fb6a47ace4c5 Mon Sep 17 00:00:00 2001 From: Martin Parsiegla Date: Sat, 22 Jul 2023 16:01:21 +0200 Subject: [PATCH 1/3] Remove bootstrap file for phpunit The required configuration for phpunit already works out of the box with the composer configuration, thus removing it and directly using the `autoload.php` for bootstraping phpunit. --- composer.json | 5 +++++ phpunit.xml.dist | 2 +- tests/bootstrap.php | 12 ------------ 3 files changed, 6 insertions(+), 13 deletions(-) delete mode 100644 tests/bootstrap.php diff --git a/composer.json b/composer.json index 06d13b3..13c10ee 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,11 @@ "Rebuy\\Amqp\\Consumer\\": "src/Rebuy/Amqp/Consumer" } }, + "autoload-dev": { + "psr-4": { + "Rebuy\\Tests\\": "tests/Rebuy/Tests" + } + }, "suggest": { "psr/log": "required if you'd like to use the LogHandler or LogSubscriber", "league/statsd": "required if you'd like to use the TimingSubscriber", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 018f89b..8d593d5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,7 +8,7 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - bootstrap="./tests/bootstrap.php" + bootstrap="vendor/autoload.php" > diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index 5481784..0000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,12 +0,0 @@ -add('Rebuy\Tests', __DIR__); - -AnnotationRegistry::registerLoader([$loader, 'loadClass']); From 2d3e3a6a4390d9f06a5ccd1d62ee95975ff047e5 Mon Sep 17 00:00:00 2001 From: Martin Parsiegla Date: Sat, 22 Jul 2023 16:04:00 +0200 Subject: [PATCH 2/3] Update dependencies PHP `7.x` has reached its end of life on the first of january 2023 and `8.0` will reach it by the end of 2023, thus bumping the version to at least `8.1`. It also upgrades some of the other dependencies and allows `2.x` for `doctrine/annotations` and `doctrine/collections`. --- composer.json | 8 ++++---- .../Consumer/Annotation/ConsumerContainer.php | 18 ++++++++---------- src/Rebuy/Amqp/Consumer/Annotation/Parser.php | 3 ++- src/Rebuy/Amqp/Consumer/ClientInterface.php | 3 --- .../Subscriber/TimingSubscriberTest.php | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index 13c10ee..d39f8df 100644 --- a/composer.json +++ b/composer.json @@ -12,11 +12,11 @@ } ], "require": { - "php": "^7.2|^8.0", - "php-amqplib/php-amqplib": "^2.12.3|^3.0.0", - "doctrine/annotations": "^1.10", + "php": "^8.1", + "php-amqplib/php-amqplib": "^3.0.0", + "doctrine/annotations": "^1.10|^2.0.1", "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "doctrine/collections": "^1.6" + "doctrine/collections": "^1.6|^2.1.2" }, "require-dev": { "jms/serializer": "^3.15.0", diff --git a/src/Rebuy/Amqp/Consumer/Annotation/ConsumerContainer.php b/src/Rebuy/Amqp/Consumer/Annotation/ConsumerContainer.php index 5dc9bb6..e6c57c1 100644 --- a/src/Rebuy/Amqp/Consumer/Annotation/ConsumerContainer.php +++ b/src/Rebuy/Amqp/Consumer/Annotation/ConsumerContainer.php @@ -51,12 +51,12 @@ public function getBindings() return []; } - $class = $this->method->getParameters()[0]->getClass(); + $class = $this->method->getParameters()[0]->getType()?->getName(); if (null === $class) { return []; } - if (!$class->implementsInterface(MessageInterface::class)) { + if (!is_a($class, MessageInterface::class, true)) { return []; } @@ -76,9 +76,12 @@ public function getConsumerIdentification() */ public function getRoutingKey() { - $class = $this->method->getParameters()[0]->getClass(); + $class = $this->method->getParameters()[0]->getType()?->getName(); + if (!is_a($class, MessageInterface::class, true)) { + return null; + } - return $class->getMethod('getRoutingKey')->invoke(null); + return $class::getRoutingKey(); } /** @@ -86,12 +89,7 @@ public function getRoutingKey() */ public function getMessageClass() { - $class = $this->method->getParameters()[0]->getClass(); - if (null === $class) { - return null; - } - - return $class->getName(); + return $this->method->getParameters()[0]->getType()?->getName(); } /** diff --git a/src/Rebuy/Amqp/Consumer/Annotation/Parser.php b/src/Rebuy/Amqp/Consumer/Annotation/Parser.php index bfd7455..dd63e1f 100644 --- a/src/Rebuy/Amqp/Consumer/Annotation/Parser.php +++ b/src/Rebuy/Amqp/Consumer/Annotation/Parser.php @@ -65,7 +65,8 @@ private function validateMethod(ReflectionMethod $method) } $parameter = $method->getParameters()[0]; - if (null === $parameter->getClass() || !$parameter->getClass()->implementsInterface(MessageInterface::class)) { + $class = $parameter->getType()?->getName(); + if (!is_a($class, MessageInterface::class, true)) { throw new InvalidArgumentException('A @Consumer\'s parameter must implement ' . MessageInterface::class); } } diff --git a/src/Rebuy/Amqp/Consumer/ClientInterface.php b/src/Rebuy/Amqp/Consumer/ClientInterface.php index aa6fe0c..9f8d9a8 100644 --- a/src/Rebuy/Amqp/Consumer/ClientInterface.php +++ b/src/Rebuy/Amqp/Consumer/ClientInterface.php @@ -3,9 +3,6 @@ namespace Rebuy\Amqp\Consumer; use InvalidArgumentException; -use JMS\Serializer\SerializationContext; -use JMS\Serializer\Serializer; -use PhpAmqpLib\Channel\AMQPChannel; use PhpAmqpLib\Message\AMQPMessage; use Rebuy\Amqp\Consumer\Message\MessageInterface; diff --git a/tests/Rebuy/Tests/Amqp/Consumer/Subscriber/TimingSubscriberTest.php b/tests/Rebuy/Tests/Amqp/Consumer/Subscriber/TimingSubscriberTest.php index 83ec37b..5c2f3b3 100644 --- a/tests/Rebuy/Tests/Amqp/Consumer/Subscriber/TimingSubscriberTest.php +++ b/tests/Rebuy/Tests/Amqp/Consumer/Subscriber/TimingSubscriberTest.php @@ -50,7 +50,7 @@ public function preConsume_with_postConsume_should_add_timing_entry() $consumerName = 'consumer'; $eventName = $consumerName . '-' . $deliveryTag; $message = new AMQPMessage('body'); - $message->delivery_info['delivery_tag'] = $deliveryTag; + $message->setDeliveryTag($deliveryTag); $container = $this->prophesize(ConsumerContainer::class); $container->getConsumerName()->willReturn($consumerName); From db333c7fe3c49e2b47c4336902bb51ef694cae62 Mon Sep 17 00:00:00 2001 From: Martin Parsiegla Date: Sat, 22 Jul 2023 17:03:05 +0200 Subject: [PATCH 3/3] Support attribute With this addition, it is now possible to use `#[Rebuy\Amqp\Consumer\Annotation\Consumer]` instead of a doctrine annotation. --- composer.json | 2 +- .../Amqp/Consumer/Annotation/Consumer.php | 21 +++++++------ src/Rebuy/Amqp/Consumer/Annotation/Parser.php | 16 ++++++++-- .../Annotation/ConsumerContainerTest.php | 30 ++++++++++++++----- .../Amqp/Consumer/Annotation/ParserTest.php | 18 ++++++++++- .../Consumer/Stubs/ConsumerWithAttributes.php | 11 +++++++ 6 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 tests/Rebuy/Tests/Amqp/Consumer/Stubs/ConsumerWithAttributes.php diff --git a/composer.json b/composer.json index d39f8df..0beeec9 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "require": { "php": "^8.1", "php-amqplib/php-amqplib": "^3.0.0", - "doctrine/annotations": "^1.10|^2.0.1", + "doctrine/annotations": "^1.13.3|^2.0.1", "symfony/event-dispatcher": "^4.4|^5.0|^6.0", "doctrine/collections": "^1.6|^2.1.2" }, diff --git a/src/Rebuy/Amqp/Consumer/Annotation/Consumer.php b/src/Rebuy/Amqp/Consumer/Annotation/Consumer.php index c5d1e45..2dfe064 100644 --- a/src/Rebuy/Amqp/Consumer/Annotation/Consumer.php +++ b/src/Rebuy/Amqp/Consumer/Annotation/Consumer.php @@ -2,22 +2,21 @@ namespace Rebuy\Amqp\Consumer\Annotation; +use Attribute; + /** * @Annotation * @Target({"METHOD"}) + * @NamedArgumentConstructor */ +#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)] class Consumer { - const DEFAULT_PREFETCH_COUNT = 1; - - /** - * @Required - * @var string - */ - public $name; + private const DEFAULT_PREFETCH_COUNT = 1; - /** - * @var int - */ - public $prefetchCount = self::DEFAULT_PREFETCH_COUNT; + public function __construct( + public string $name, + public int $prefetchCount = self::DEFAULT_PREFETCH_COUNT + ) { + } } diff --git a/src/Rebuy/Amqp/Consumer/Annotation/Parser.php b/src/Rebuy/Amqp/Consumer/Annotation/Parser.php index dd63e1f..0cfe9d7 100644 --- a/src/Rebuy/Amqp/Consumer/Annotation/Parser.php +++ b/src/Rebuy/Amqp/Consumer/Annotation/Parser.php @@ -39,8 +39,7 @@ public function getConsumerMethods($obj) $class = new ReflectionClass($obj); $consumerMethods = []; foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - /** @var Consumer $annotation */ - $annotation = $this->reader->getMethodAnnotation($method, Consumer::class); + $annotation = $this->getConsumerAnnotationOrAttribute($method); if (null === $annotation) { continue; } @@ -70,4 +69,17 @@ private function validateMethod(ReflectionMethod $method) throw new InvalidArgumentException('A @Consumer\'s parameter must implement ' . MessageInterface::class); } } + + private function getConsumerAnnotationOrAttribute(ReflectionMethod $method): ?Consumer + { + $reflectionAttributes = $method->getAttributes(); + foreach ($reflectionAttributes as $attribute) { + if ($attribute->getName() === Consumer::class) { + return $attribute->newInstance(); + } + } + + return $this->reader->getMethodAnnotation($method, Consumer::class); + + } } diff --git a/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ConsumerContainerTest.php b/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ConsumerContainerTest.php index b4f70f8..45ce6ac 100644 --- a/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ConsumerContainerTest.php +++ b/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ConsumerContainerTest.php @@ -33,7 +33,7 @@ public function invoke_should_invoke_reflection() self::TEST_PREFIX, $consumer, $reflectionMethod->reveal(), - new ConsumerAnnotation() + new ConsumerAnnotation('name') ); $container->invoke($payload); } @@ -46,7 +46,7 @@ public function get_bindings_should_return_empty_array_if_interface_is_not_imple $consumer = new ConsumerWithInvalidParameter(); $method = new ReflectionMethod($consumer, 'classWithoutImplementingInterface'); - $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation()); + $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation('name')); $result = $container->getBindings(); verify($result)->empty(); @@ -60,12 +60,26 @@ public function get_bindings_should_return_empty_array_if_parameter_count_is_not $consumer = new ConsumerWithTwoParameters(); $method = new ReflectionMethod($consumer, 'consume'); - $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation()); + $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation('name')); $result = $container->getBindings(); verify($result)->empty(); } + /** + * @test + */ + public function getRoutingKey_should_return_null_if_the_class_does_not_implement_the_MessageInterface() + { + $consumer = new ConsumerWithInvalidParameter(); + $method = new ReflectionMethod($consumer, 'classWithoutImplementingInterface'); + + $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation('name')); + $result = $container->getRoutingKey(); + + verify($result)->empty(); + } + /** * @test */ @@ -74,7 +88,7 @@ public function get_bindings_should_return_empty_array_parameter_is_not_a_class( $consumer = new ConsumerWithInvalidParameter(); $method = new ReflectionMethod($consumer, 'consume'); - $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation()); + $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation('name')); $result = $container->getBindings(); verify($result)->empty(); @@ -88,7 +102,7 @@ public function get_bindings_should_return_array_with_two_bindings() $consumer = new Consumer(); $method = new ReflectionMethod($consumer, 'consume'); - $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation()); + $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, new ConsumerAnnotation('name')); $result = $container->getBindings(); verify($result)->notEmpty(); @@ -103,7 +117,7 @@ public function get_bindings_should_return_correct_bindings() $consumer = new Consumer(); $method = new ReflectionMethod($consumer, 'consume'); - $consumerAnnotation = new ConsumerAnnotation(); + $consumerAnnotation = new ConsumerAnnotation('name'); $consumerAnnotation->name = "consume-method"; $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, $consumerAnnotation); $result = $container->getBindings(); @@ -120,7 +134,7 @@ public function get_consumer_name_should_return_correct_name() $consumer = new Consumer(); $method = new ReflectionMethod($consumer, 'consume'); - $consumerAnnotation = new ConsumerAnnotation(); + $consumerAnnotation = new ConsumerAnnotation('name'); $consumerAnnotation->name = "consume-method"; $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, $consumerAnnotation); $result = $container->getConsumerName(); @@ -136,7 +150,7 @@ public function get_method_name_should_return_class_with_method_name() $consumer = new Consumer(); $method = new ReflectionMethod($consumer, 'consume'); - $consumerAnnotation = new ConsumerAnnotation(); + $consumerAnnotation = new ConsumerAnnotation('name'); $container = new ConsumerContainer(self::TEST_PREFIX, $consumer, $method, $consumerAnnotation); $result = $container->getMethodName(); diff --git a/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ParserTest.php b/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ParserTest.php index fc55913..7564dbe 100644 --- a/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ParserTest.php +++ b/tests/Rebuy/Tests/Amqp/Consumer/Annotation/ParserTest.php @@ -9,6 +9,7 @@ use Rebuy\Amqp\Consumer\Annotation\Consumer as ConsumerAnnotation; use Rebuy\Amqp\Consumer\Annotation\Parser; use Rebuy\Tests\Amqp\Consumer\Stubs\Consumer; +use Rebuy\Tests\Amqp\Consumer\Stubs\ConsumerWithAttributes; use Rebuy\Tests\Amqp\Consumer\Stubs\ConsumerWithInvalidAnnotation; use Rebuy\Tests\Amqp\Consumer\Stubs\ConsumerWithInvalidParameter; use Rebuy\Tests\Amqp\Consumer\Stubs\ConsumerWithPrefetchCount; @@ -49,7 +50,7 @@ public function parser_should_use_default_prefetch_count() } /** - * @tests + * @test */ public function parser_should_use_prefetch_count_from_annotation() { @@ -62,6 +63,21 @@ public function parser_should_use_prefetch_count_from_annotation() verify($consumerMethod->getPrefetchCount())->equals(100); } + /** + * @test + */ + public function parser_should_support_attributes() + { + $parser = new Parser(new AnnotationReader(), 'prefix'); + $consumer = new ConsumerWithAttributes(); + + $consumerMethods = $parser->getConsumerMethods($consumer); + $consumerMethod = $consumerMethods[0]; + + verify($consumerMethod->getConsumerName())->equals('prefix-consume-with-attributes'); + verify($consumerMethod->getPrefetchCount())->equals(100); + } + /** * @test */ diff --git a/tests/Rebuy/Tests/Amqp/Consumer/Stubs/ConsumerWithAttributes.php b/tests/Rebuy/Tests/Amqp/Consumer/Stubs/ConsumerWithAttributes.php new file mode 100644 index 0000000..3c72a87 --- /dev/null +++ b/tests/Rebuy/Tests/Amqp/Consumer/Stubs/ConsumerWithAttributes.php @@ -0,0 +1,11 @@ +