From d4eb1cf4958e30a1251b841a3722159270a4ca5b Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:03:47 +0200
Subject: [PATCH 001/101] Add symfony intergation for kafka transport

---
 Client/RdKafkaDriver.php                      | 166 +++++++++
 Symfony/RdKafkaTransportFactory.php           | 148 ++++++++
 Tests/Client/RdKafkaDriverTest.php            | 340 ++++++++++++++++++
 Tests/RdKafkaConnectionFactoryConfigTest.php  |  92 +++++
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 146 ++++++++
 composer.json                                 |   4 +-
 6 files changed, 895 insertions(+), 1 deletion(-)
 create mode 100644 Client/RdKafkaDriver.php
 create mode 100644 Symfony/RdKafkaTransportFactory.php
 create mode 100644 Tests/Client/RdKafkaDriverTest.php
 create mode 100644 Tests/RdKafkaConnectionFactoryConfigTest.php
 create mode 100644 Tests/Symfony/RdKafkaTransportFactoryTest.php

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
new file mode 100644
index 0000000..e3450b3
--- /dev/null
+++ b/Client/RdKafkaDriver.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace Enqueue\RdKafka\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\RdKafka\RdKafkaContext;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class RdKafkaDriver implements DriverInterface
+{
+    /**
+     * @var RdKafkaContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param RdKafkaContext $context
+     * @param Config $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(RdKafkaContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($message->getProperties());
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        if ($contentType = $message->getHeader('content_type')) {
+            $clientMessage->setContentType($contentType);
+        }
+
+        if ($expiration = $message->getHeader('expiration')) {
+            $clientMessage->setExpire($expiration);
+        }
+
+        if ($delay = $message->getHeader('delay')) {
+            $clientMessage->setDelay($delay);
+        }
+
+        if ($priority = $message->getHeader('priority')) {
+            $clientMessage->setPriority($priority);
+        }
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $topic = $this->createRouterTopic();
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $logger->debug('[RdKasfkaDriver] setup broker');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    private function createRouterTopic()
+    {
+        $topic = $this->context->createTopic(
+            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
+        );
+
+        return $topic;
+    }
+}
\ No newline at end of file
diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
new file mode 100644
index 0000000..26e9046
--- /dev/null
+++ b/Symfony/RdKafkaTransportFactory.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace Enqueue\RdKafka\Symfony;
+
+use Enqueue\RdKafka\Client\RdKafkaDriver;
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\RdKafka\RdKafkaContext;
+use Enqueue\Symfony\DriverFactoryInterface;
+use Enqueue\Symfony\TransportFactoryInterface;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class RdKafkaTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @param string $name
+     */
+    public function __construct($name = 'rdkafka')
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * @param ArrayNodeDefinition $builder
+     */
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        $builder
+            ->beforeNormalization()
+                ->ifString()
+                ->then(function ($v) {
+                    return ['dsn' => $v];
+                })
+            ->end()
+            ->fixXmlConfig('topic')
+            ->children()
+                ->scalarNode('dsn')
+                    ->info('The kafka DSN. Other parameters are ignored if set')
+                ->end()
+                ->arrayNode('global')
+                    ->children()
+                        ->scalarNode('metadata.broker.list')->end()
+                    ->end()
+                ->end()
+                ->arrayNode('topics')
+                    ->prototype('scalar')->end()
+                ->end()
+                ->scalarNode('dr_msq_cb')
+                    ->info('todo')
+                ->end()
+                ->scalarNode('error_cb')
+                    ->info('todo')
+                ->end()
+                ->scalarNode('rebalance_cb')
+                    ->info('todo')
+                ->end()
+                ->enumNode('partitioner')
+                    ->values(['RD_KAFKA_MSG_PARTITIONER_RANDOM', 'RD_KAFKA_MSG_PARTITIONER_CONSISTENT'])
+                    ->info('todo')
+                ->end()
+                ->scalarNode('log_level')
+                    ->info('todo')
+                ->end()
+                ->booleanNode('commit_async')
+                    ->defaultFalse()
+                    ->info('todo')
+                ->end()
+        ;
+    }
+
+    /**
+     * @param ContainerBuilder $container
+     * @param array            $config
+     *
+     * @return string The method must return a factory service id
+     */
+    public function createConnectionFactory(ContainerBuilder $container, array $config)
+    {
+        if (false == empty($config['rdkafka'])) {
+            $config['rdkafka'] = new Reference($config['rdkafka']);
+        }
+
+        $factory = new Definition(RdKafkaConnectionFactory::class);
+        $factory->setArguments([isset($config['dsn']) ? $config['dsn'] : $config]);
+
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $container->setDefinition($factoryId, $factory);
+
+        return $factoryId;
+    }
+
+    /**
+     * @param ContainerBuilder $container
+     * @param array            $config
+     *
+     * @return string The method must return a context service id
+     */
+    public function createContext(ContainerBuilder $container, array $config)
+    {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+
+        $context = new Definition(RdKafkaContext::class);
+        $context->setPublic(true);
+        $context->setFactory([new Reference($factoryId), 'createContext']);
+
+        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
+        $container->setDefinition($contextId, $context);
+
+        return $contextId;
+    }
+
+    /**
+     * @param ContainerBuilder $container
+     * @param array            $config
+     *
+     * @return string The method must return a driver service id
+     */
+    public function createDriver(ContainerBuilder $container, array $config)
+    {
+        $driver = new Definition(RdKafkaDriver::class);
+        $driver->setPublic(true);
+        $driver->setArguments([
+            new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
+            new Reference('enqueue.client.config'),
+            new Reference('enqueue.client.meta.queue_meta_registry'),
+        ]);
+
+        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
+        $container->setDefinition($driverId, $driver);
+
+        return $driverId;
+    }
+
+    /**
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+}
\ No newline at end of file
diff --git a/Tests/Client/RdKafkaDriverTest.php b/Tests/Client/RdKafkaDriverTest.php
new file mode 100644
index 0000000..9c0b9f1
--- /dev/null
+++ b/Tests/Client/RdKafkaDriverTest.php
@@ -0,0 +1,340 @@
+<?php
+
+namespace Enqueue\RdKafka\Tests\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\RdKafka\Client\RdKafkaDriver;
+use Enqueue\RdKafka\RdKafkaContext;
+use Enqueue\RdKafka\RdKafkaMessage;
+use Enqueue\RdKafka\RdKafkaTopic;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+
+class RdKafkaDriverTest extends \PHPUnit_Framework_TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, RdKafkaDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $config,
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new RdKafkaTopic('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new RdKafkaTopic('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new RdKafkaMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+
+        $this->assertNull($clientMessage->getExpire());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new RdKafkaMessage())
+        ;
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(RdKafkaMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => null,
+            'correlation_id' => '',
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new RdKafkaTopic('queue-name');
+        $transportMessage = new RdKafkaMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->with('aprefix.router')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new RdKafkaTopic('queue-name');
+        $transportMessage = new RdKafkaMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $context = $this->createPsrContextMock();
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|RdKafkaContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(RdKafkaContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/RdKafkaConnectionFactoryConfigTest.php b/Tests/RdKafkaConnectionFactoryConfigTest.php
new file mode 100644
index 0000000..1795994
--- /dev/null
+++ b/Tests/RdKafkaConnectionFactoryConfigTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Enqueue\RdKafka\Tests;
+
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * The class contains the factory tests dedicated to configuration.
+ */
+class RdKafkaConnectionFactoryConfigTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testThrowNeitherArrayStringNorNullGivenAsConfig()
+    {
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The config must be either an array of options, a DSN string or null');
+
+        new RdKafkaConnectionFactory(new \stdClass());
+    }
+
+    public function testThrowIfSchemeIsNotSupported()
+    {
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The given DSN scheme "http" is not supported. Could be "kafka" only.');
+
+        new RdKafkaConnectionFactory('http://example.com');
+    }
+
+    /**
+     * @dataProvider provideConfigs
+     *
+     * @param mixed $config
+     * @param mixed $expectedConfig
+     */
+    public function testShouldParseConfigurationAsExpected($config, $expectedConfig)
+    {
+        $factory = new RdKafkaConnectionFactory($config);
+
+        $config = $this->getObjectAttribute($factory, 'config');
+
+        $this->assertNotEmpty($config['global']['group.id']);
+
+        $config['global']['group.id'] = 'group-id';
+        $this->assertSame($expectedConfig, $config);
+    }
+
+    public static function provideConfigs()
+    {
+        yield [
+            null,
+            [
+                'global' => [
+                    'group.id' => 'group-id',
+                    'metadata.broker.list' => 'localhost:9092'
+                ]
+            ],
+        ];
+
+        yield [
+            'kafka:',
+            [
+                'global' => [
+                    'group.id' => 'group-id',
+                    'metadata.broker.list' => 'localhost:9092'
+                ]
+            ],
+        ];
+
+        yield [
+            'kafka://user:pass@host:10000/db',
+            [
+                'global' => [
+                    'group.id' => 'group-id',
+                    'metadata.broker.list' => 'host:10000'
+                ]
+            ],
+        ];
+
+        yield [
+            [],
+            [
+                'global' => [
+                    'group.id' => 'group-id',
+                    'metadata.broker.list' => 'localhost:9092'
+                ]
+            ],
+        ];
+    }
+}
diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
new file mode 100644
index 0000000..e81a125
--- /dev/null
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Enqueue\RdKafka\Tests\Symfony;
+
+use Enqueue\RdKafka\Client\RdKafkaDriver;
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\RdKafka\Symfony\RdKafkaTransportFactory;
+use Enqueue\Symfony\TransportFactoryInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class RdKafkaTransportFactoryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementTransportFactoryInterface()
+    {
+        $this->assertClassImplements(TransportFactoryInterface::class, RdKafkaTransportFactory::class);
+    }
+
+    public function testCouldBeConstructedWithDefaultName()
+    {
+        $transport = new RdKafkaTransportFactory();
+
+        $this->assertEquals('rdkafka', $transport->getName());
+    }
+
+    public function testCouldBeConstructedWithCustomName()
+    {
+        $transport = new RdKafkaTransportFactory('theCustomName');
+
+        $this->assertEquals('theCustomName', $transport->getName());
+    }
+
+    public function testShouldAllowAddConfiguration()
+    {
+        $transport = new RdKafkaTransportFactory();
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [[
+        ]]);
+
+        $this->assertEquals([
+            'topics' => [],
+            'commit_async' => false
+        ], $config);
+    }
+
+    public function testShouldAllowAddConfigurationAsString()
+    {
+        $transport = new RdKafkaTransportFactory();
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), ['fileDSN']);
+
+        $this->assertEquals([
+            'dsn' => 'fileDSN',
+            'topics' => [],
+            'commit_async' => false
+        ], $config);
+    }
+
+    public function testShouldCreateConnectionFactory()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new RdKafkaTransportFactory();
+
+        $serviceId = $transport->createConnectionFactory($container, [
+        ]);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals(RdKafkaConnectionFactory::class, $factory->getClass());
+        $this->assertSame([[
+        ]], $factory->getArguments());
+    }
+
+    public function testShouldCreateConnectionFactoryFromDsnString()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new RdKafkaTransportFactory();
+
+        $serviceId = $transport->createConnectionFactory($container, [
+            'dsn' => 'theFileDSN',
+        ]);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals(RdKafkaConnectionFactory::class, $factory->getClass());
+        $this->assertSame(['theFileDSN'], $factory->getArguments());
+    }
+
+    public function testShouldCreateContext()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new RdKafkaTransportFactory();
+
+        $serviceId = $transport->createContext($container, [
+        ]);
+
+        $this->assertEquals('enqueue.transport.rdkafka.context', $serviceId);
+        $this->assertTrue($container->hasDefinition($serviceId));
+
+        $context = $container->getDefinition('enqueue.transport.rdkafka.context');
+        $this->assertInstanceOf(Reference::class, $context->getFactory()[0]);
+        $this->assertEquals('enqueue.transport.rdkafka.connection_factory', (string) $context->getFactory()[0]);
+        $this->assertEquals('createContext', $context->getFactory()[1]);
+    }
+
+    public function testShouldCreateDriver()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new RdKafkaTransportFactory();
+
+        $serviceId = $transport->createDriver($container, []);
+
+        $this->assertEquals('enqueue.client.rdkafka.driver', $serviceId);
+        $this->assertTrue($container->hasDefinition($serviceId));
+
+        $driver = $container->getDefinition($serviceId);
+        $this->assertSame(RdKafkaDriver::class, $driver->getClass());
+
+        $this->assertInstanceOf(Reference::class, $driver->getArgument(0));
+        $this->assertEquals('enqueue.transport.rdkafka.context', (string) $driver->getArgument(0));
+
+        $this->assertInstanceOf(Reference::class, $driver->getArgument(1));
+        $this->assertEquals('enqueue.client.config', (string) $driver->getArgument(1));
+
+        $this->assertInstanceOf(Reference::class, $driver->getArgument(2));
+        $this->assertEquals('enqueue.client.meta.queue_meta_registry', (string) $driver->getArgument(2));
+    }
+}
diff --git a/composer.json b/composer.json
index 5339cd9..09909b5 100644
--- a/composer.json
+++ b/composer.json
@@ -16,7 +16,9 @@
         "enqueue/enqueue": "^0.8@dev",
         "enqueue/null": "^0.8@dev",
         "queue-interop/queue-spec": "^0.5.3@dev",
-        "kwn/php-rdkafka-stubs": "^1.0.2"
+        "kwn/php-rdkafka-stubs": "^1.0.2",
+        "symfony/dependency-injection": "^2.8|^3|^4",
+        "symfony/config": "^2.8|^3|^4"
     },
     "support": {
         "email": "opensource@forma-pro.com",

From 3840b964fce76a8ba76d4067fb3a11b3fd5f9713 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:07:35 +0200
Subject: [PATCH 002/101] Remove expiration, delay and priority from the
 RdKafka driver

---
 Client/RdKafkaDriver.php | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
index e3450b3..fcb7205 100644
--- a/Client/RdKafkaDriver.php
+++ b/Client/RdKafkaDriver.php
@@ -79,18 +79,6 @@ public function createClientMessage(PsrMessage $message)
             $clientMessage->setContentType($contentType);
         }
 
-        if ($expiration = $message->getHeader('expiration')) {
-            $clientMessage->setExpire($expiration);
-        }
-
-        if ($delay = $message->getHeader('delay')) {
-            $clientMessage->setDelay($delay);
-        }
-
-        if ($priority = $message->getHeader('priority')) {
-            $clientMessage->setPriority($priority);
-        }
-
         return $clientMessage;
     }
 

From 1b3898e96114b075e597710b840f666c00546f08 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:21:15 +0200
Subject: [PATCH 003/101] Fixed a typo

---
 Client/RdKafkaDriver.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
index fcb7205..ffd19fb 100644
--- a/Client/RdKafkaDriver.php
+++ b/Client/RdKafkaDriver.php
@@ -132,7 +132,7 @@ public function createQueue($queueName)
     public function setupBroker(LoggerInterface $logger = null)
     {
         $logger = $logger ?: new NullLogger();
-        $logger->debug('[RdKasfkaDriver] setup broker');
+        $logger->debug('[RdKafkaDriver] setup broker');
     }
 
     /**

From f4516e8a0003125cd5e1d2888edf6ef84c582218 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:24:19 +0200
Subject: [PATCH 004/101] Renamed the dsn in the kafka transport factory test

---
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
index e81a125..802a966 100644
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -61,10 +61,10 @@ public function testShouldAllowAddConfigurationAsString()
 
         $transport->addConfiguration($rootNode);
         $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), ['fileDSN']);
+        $config = $processor->process($tb->buildTree(), ['kafkaDSN']);
 
         $this->assertEquals([
-            'dsn' => 'fileDSN',
+            'dsn' => 'kafkaDSN',
             'topics' => [],
             'commit_async' => false
         ], $config);

From 964e820603639d0c7027ffc6184025e85dba637d Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:24:52 +0200
Subject: [PATCH 005/101] Added a new line to the end of the kafka driver and
 transport factory

---
 Client/RdKafkaDriver.php            | 2 +-
 Symfony/RdKafkaTransportFactory.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
index ffd19fb..4530dc5 100644
--- a/Client/RdKafkaDriver.php
+++ b/Client/RdKafkaDriver.php
@@ -151,4 +151,4 @@ private function createRouterTopic()
 
         return $topic;
     }
-}
\ No newline at end of file
+}
diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
index 26e9046..6a903ae 100644
--- a/Symfony/RdKafkaTransportFactory.php
+++ b/Symfony/RdKafkaTransportFactory.php
@@ -145,4 +145,4 @@ public function getName()
     {
         return $this->name;
     }
-}
\ No newline at end of file
+}

From 4c17dcdd8f6cda686bccafadd1cce501c38a6f15 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:26:14 +0200
Subject: [PATCH 006/101] Renamed another dsn in the kafka transport factory
 test

---
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
index 802a966..a98a514 100644
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -93,13 +93,13 @@ public function testShouldCreateConnectionFactoryFromDsnString()
         $transport = new RdKafkaTransportFactory();
 
         $serviceId = $transport->createConnectionFactory($container, [
-            'dsn' => 'theFileDSN',
+            'dsn' => 'theKafkaDSN',
         ]);
 
         $this->assertTrue($container->hasDefinition($serviceId));
         $factory = $container->getDefinition($serviceId);
         $this->assertEquals(RdKafkaConnectionFactory::class, $factory->getClass());
-        $this->assertSame(['theFileDSN'], $factory->getArguments());
+        $this->assertSame(['theKafkaDSN'], $factory->getArguments());
     }
 
     public function testShouldCreateContext()

From a49dfbcf869772f052429a4b4956b8740db090b5 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:52:09 +0200
Subject: [PATCH 007/101] Use inheritdoc in the kafka transport factory

---
 Symfony/RdKafkaTransportFactory.php | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
index 6a903ae..88088c2 100644
--- a/Symfony/RdKafkaTransportFactory.php
+++ b/Symfony/RdKafkaTransportFactory.php
@@ -28,7 +28,7 @@ public function __construct($name = 'rdkafka')
     }
 
     /**
-     * @param ArrayNodeDefinition $builder
+     * {@inheritdoc}
      */
     public function addConfiguration(ArrayNodeDefinition $builder)
     {
@@ -76,10 +76,7 @@ public function addConfiguration(ArrayNodeDefinition $builder)
     }
 
     /**
-     * @param ContainerBuilder $container
-     * @param array            $config
-     *
-     * @return string The method must return a factory service id
+     * {@inheritdoc}
      */
     public function createConnectionFactory(ContainerBuilder $container, array $config)
     {
@@ -97,10 +94,7 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
     }
 
     /**
-     * @param ContainerBuilder $container
-     * @param array            $config
-     *
-     * @return string The method must return a context service id
+     * {@inheritdoc}
      */
     public function createContext(ContainerBuilder $container, array $config)
     {
@@ -117,10 +111,7 @@ public function createContext(ContainerBuilder $container, array $config)
     }
 
     /**
-     * @param ContainerBuilder $container
-     * @param array            $config
-     *
-     * @return string The method must return a driver service id
+     * {@inheritdoc}
      */
     public function createDriver(ContainerBuilder $container, array $config)
     {

From ccee285b7b82b72c54ac38aaf6676f6c512d0f89 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 16:01:48 +0200
Subject: [PATCH 008/101] Fixed some code styling

---
 Client/RdKafkaDriver.php                      |  4 ++--
 Tests/RdKafkaConnectionFactoryConfigTest.php  | 16 ++++++++--------
 Tests/Symfony/RdKafkaTransportFactoryTest.php |  4 ++--
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
index 4530dc5..7a5fd4a 100644
--- a/Client/RdKafkaDriver.php
+++ b/Client/RdKafkaDriver.php
@@ -29,8 +29,8 @@ class RdKafkaDriver implements DriverInterface
     private $queueMetaRegistry;
 
     /**
-     * @param RdKafkaContext $context
-     * @param Config $config
+     * @param RdKafkaContext    $context
+     * @param Config            $config
      * @param QueueMetaRegistry $queueMetaRegistry
      */
     public function __construct(RdKafkaContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
diff --git a/Tests/RdKafkaConnectionFactoryConfigTest.php b/Tests/RdKafkaConnectionFactoryConfigTest.php
index 1795994..d3a6a5d 100644
--- a/Tests/RdKafkaConnectionFactoryConfigTest.php
+++ b/Tests/RdKafkaConnectionFactoryConfigTest.php
@@ -54,8 +54,8 @@ public static function provideConfigs()
             [
                 'global' => [
                     'group.id' => 'group-id',
-                    'metadata.broker.list' => 'localhost:9092'
-                ]
+                    'metadata.broker.list' => 'localhost:9092',
+                ],
             ],
         ];
 
@@ -64,8 +64,8 @@ public static function provideConfigs()
             [
                 'global' => [
                     'group.id' => 'group-id',
-                    'metadata.broker.list' => 'localhost:9092'
-                ]
+                    'metadata.broker.list' => 'localhost:9092',
+                ],
             ],
         ];
 
@@ -74,8 +74,8 @@ public static function provideConfigs()
             [
                 'global' => [
                     'group.id' => 'group-id',
-                    'metadata.broker.list' => 'host:10000'
-                ]
+                    'metadata.broker.list' => 'host:10000',
+                ],
             ],
         ];
 
@@ -84,8 +84,8 @@ public static function provideConfigs()
             [
                 'global' => [
                     'group.id' => 'group-id',
-                    'metadata.broker.list' => 'localhost:9092'
-                ]
+                    'metadata.broker.list' => 'localhost:9092',
+                ],
             ],
         ];
     }
diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
index a98a514..b49f643 100644
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -49,7 +49,7 @@ public function testShouldAllowAddConfiguration()
 
         $this->assertEquals([
             'topics' => [],
-            'commit_async' => false
+            'commit_async' => false,
         ], $config);
     }
 
@@ -66,7 +66,7 @@ public function testShouldAllowAddConfigurationAsString()
         $this->assertEquals([
             'dsn' => 'kafkaDSN',
             'topics' => [],
-            'commit_async' => false
+            'commit_async' => false,
         ], $config);
     }
 

From 82f1be7a1ccad5ab52a03990504f83b815ab1709 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Fri, 27 Apr 2018 16:45:53 +0200
Subject: [PATCH 009/101] Updated the description on some of the kafka symfony
 configuration nodes

---
 Symfony/RdKafkaTransportFactory.php | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
index 88088c2..260cac3 100644
--- a/Symfony/RdKafkaTransportFactory.php
+++ b/Symfony/RdKafkaTransportFactory.php
@@ -52,25 +52,26 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                 ->arrayNode('topics')
                     ->prototype('scalar')->end()
                 ->end()
-                ->scalarNode('dr_msq_cb')
-                    ->info('todo')
+                ->scalarNode('dr_msg_cb')
+                    ->info('Delivery report callback')
                 ->end()
                 ->scalarNode('error_cb')
-                    ->info('todo')
+                    ->info('Error callback')
                 ->end()
                 ->scalarNode('rebalance_cb')
-                    ->info('todo')
+                    ->info('Called after consumer group has been rebalanced')
                 ->end()
                 ->enumNode('partitioner')
                     ->values(['RD_KAFKA_MSG_PARTITIONER_RANDOM', 'RD_KAFKA_MSG_PARTITIONER_CONSISTENT'])
-                    ->info('todo')
+                    ->info('Which partitioner to use')
                 ->end()
-                ->scalarNode('log_level')
-                    ->info('todo')
+                ->integerNode('log_level')
+                    ->info('Logging level (syslog(3) levels)')
+                    ->min(0)->max(7)
                 ->end()
                 ->booleanNode('commit_async')
                     ->defaultFalse()
-                    ->info('todo')
+                    ->info('Commit asynchronous')
                 ->end()
         ;
     }

From f19c04db606c592feda969a5475cf6a99b142ff9 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Fri, 27 Apr 2018 17:21:00 +0200
Subject: [PATCH 010/101] No need to check as the default return value is null

---
 Client/RdKafkaDriver.php | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
index 7a5fd4a..a881d6d 100644
--- a/Client/RdKafkaDriver.php
+++ b/Client/RdKafkaDriver.php
@@ -70,15 +70,13 @@ public function createClientMessage(PsrMessage $message)
         $clientMessage->setHeaders($message->getHeaders());
         $clientMessage->setProperties($message->getProperties());
 
+        $clientMessage->setContentType($message->getHeader('content_type'));
+
         $clientMessage->setTimestamp($message->getTimestamp());
         $clientMessage->setMessageId($message->getMessageId());
         $clientMessage->setReplyTo($message->getReplyTo());
         $clientMessage->setCorrelationId($message->getCorrelationId());
 
-        if ($contentType = $message->getHeader('content_type')) {
-            $clientMessage->setContentType($contentType);
-        }
-
         return $clientMessage;
     }
 

From 4ad0f40f6e0d35ad1eb9eb180588b706bfe53a5a Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Mon, 30 Apr 2018 09:08:50 +0200
Subject: [PATCH 011/101] Made the rdkafka global config node a variableNode
 with an empty array as default

---
 Symfony/RdKafkaTransportFactory.php           | 6 +-----
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 2 ++
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
index 260cac3..042dfe4 100644
--- a/Symfony/RdKafkaTransportFactory.php
+++ b/Symfony/RdKafkaTransportFactory.php
@@ -44,11 +44,7 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                 ->scalarNode('dsn')
                     ->info('The kafka DSN. Other parameters are ignored if set')
                 ->end()
-                ->arrayNode('global')
-                    ->children()
-                        ->scalarNode('metadata.broker.list')->end()
-                    ->end()
-                ->end()
+                ->variableNode('global')->defaultValue([])->end()
                 ->arrayNode('topics')
                     ->prototype('scalar')->end()
                 ->end()
diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
index b49f643..42e697a 100644
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -50,6 +50,7 @@ public function testShouldAllowAddConfiguration()
         $this->assertEquals([
             'topics' => [],
             'commit_async' => false,
+            'global' => [],
         ], $config);
     }
 
@@ -67,6 +68,7 @@ public function testShouldAllowAddConfigurationAsString()
             'dsn' => 'kafkaDSN',
             'topics' => [],
             'commit_async' => false,
+            'global' => [],
         ], $config);
     }
 

From a5eac82e8741c86d40f622da5282e778485eda3d Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Mon, 30 Apr 2018 18:00:15 +0200
Subject: [PATCH 012/101] Updated the kafka symfony configuration

---
 Symfony/RdKafkaTransportFactory.php | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
index 042dfe4..f03971f 100644
--- a/Symfony/RdKafkaTransportFactory.php
+++ b/Symfony/RdKafkaTransportFactory.php
@@ -39,14 +39,17 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                     return ['dsn' => $v];
                 })
             ->end()
-            ->fixXmlConfig('topic')
             ->children()
                 ->scalarNode('dsn')
                     ->info('The kafka DSN. Other parameters are ignored if set')
                 ->end()
-                ->variableNode('global')->defaultValue([])->end()
-                ->arrayNode('topics')
-                    ->prototype('scalar')->end()
+                ->variableNode('global')
+                    ->defaultValue([])
+                    ->info('The kafka global configuration properties')
+                ->end()
+                ->variableNode('topic')
+                    ->defaultValue([])
+                    ->info('The kafka topic configuration properties')
                 ->end()
                 ->scalarNode('dr_msg_cb')
                     ->info('Delivery report callback')

From 9966437b2e6df667fed272731ca0f7df5dd645fc Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Tue, 1 May 2018 10:21:20 +0200
Subject: [PATCH 013/101] Expect the correct response in rdkafka tests

---
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
index 42e697a..9500342 100644
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -48,7 +48,7 @@ public function testShouldAllowAddConfiguration()
         ]]);
 
         $this->assertEquals([
-            'topics' => [],
+            'topic' => [],
             'commit_async' => false,
             'global' => [],
         ], $config);
@@ -66,7 +66,7 @@ public function testShouldAllowAddConfigurationAsString()
 
         $this->assertEquals([
             'dsn' => 'kafkaDSN',
-            'topics' => [],
+            'topic' => [],
             'commit_async' => false,
             'global' => [],
         ], $config);

From cce3989869eb65677a76fa07c4ad6823d64356b2 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Tue, 1 May 2018 13:36:35 +0200
Subject: [PATCH 014/101] Add the broker setup for the rdkafka driver

---
 Client/RdKafkaDriver.php | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
index a881d6d..4167253 100644
--- a/Client/RdKafkaDriver.php
+++ b/Client/RdKafkaDriver.php
@@ -131,6 +131,21 @@ public function setupBroker(LoggerInterface $logger = null)
     {
         $logger = $logger ?: new NullLogger();
         $logger->debug('[RdKafkaDriver] setup broker');
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[RdKafkaDriver] '.$text, ...$args));
+        };
+
+        // setup router
+        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $log('Create router queue: %s', $routerQueue->getQueueName());
+        $this->context->createConsumer($routerQueue);
+
+        // setup queues
+        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+            $queue = $this->createQueue($meta->getClientName());
+            $log('Create processor queue: %s', $queue->getQueueName());
+            $this->context->createConsumer($queue);
+        }
     }
 
     /**

From 6432397b187e45a6a7a682efb34b5be188cbd35c Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Tue, 1 May 2018 13:55:06 +0200
Subject: [PATCH 015/101] Fix the rdkafka should setupd driver test

---
 Tests/Client/RdKafkaDriverTest.php | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/Tests/Client/RdKafkaDriverTest.php b/Tests/Client/RdKafkaDriverTest.php
index 9c0b9f1..c0cfab6 100644
--- a/Tests/Client/RdKafkaDriverTest.php
+++ b/Tests/Client/RdKafkaDriverTest.php
@@ -290,12 +290,37 @@ public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
 
     public function testShouldSetupBroker()
     {
+        $routerTopic = new RdKafkaTopic('');
+        $routerQueue = new RdKafkaTopic('');
+
+        $processorTopic = new RdKafkaTopic('');
+
         $context = $this->createPsrContextMock();
 
+        $context
+            ->expects($this->at(0))
+            ->method('createQueue')
+            ->willReturn($routerTopic)
+        ;
+        $context
+            ->expects($this->at(1))
+            ->method('createQueue')
+            ->willReturn($routerQueue)
+        ;
+        $context
+            ->expects($this->at(2))
+            ->method('createQueue')
+            ->willReturn($processorTopic)
+        ;
+
+        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
+            'default' => [],
+        ]);
+
         $driver = new RdKafkaDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            $meta
         );
 
         $driver->setupBroker();

From 7b7622c91fbf20f3cdd225b53084a7374401bc3d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 3 May 2018 13:13:08 +0300
Subject: [PATCH 016/101] add group

---
 Tests/Client/RdKafkaDriverTest.php            | 3 +++
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/Tests/Client/RdKafkaDriverTest.php b/Tests/Client/RdKafkaDriverTest.php
index c0cfab6..af9de41 100644
--- a/Tests/Client/RdKafkaDriverTest.php
+++ b/Tests/Client/RdKafkaDriverTest.php
@@ -13,6 +13,9 @@
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\PsrProducer;
 
+/**
+ * @group rdkafka
+ */
 class RdKafkaDriverTest extends \PHPUnit_Framework_TestCase
 {
     use ClassExtensionTrait;
diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
index 9500342..6816e16 100644
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ b/Tests/Symfony/RdKafkaTransportFactoryTest.php
@@ -13,6 +13,9 @@
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
+/**
+ * @group rdkafka
+ */
 class RdKafkaTransportFactoryTest extends TestCase
 {
     use ClassExtensionTrait;

From fdc7fef9d7377ea48608c5448e9fd42f69886c3c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 8 Aug 2018 12:34:35 +0300
Subject: [PATCH 017/101] 0.7@dev to 0.7.x-dev

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index aa20d6c..72f5cdb 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.1.3",
         "ext-rdkafka": "^3.0.3",
-        "queue-interop/queue-interop": "^0.7@dev"
+        "queue-interop/queue-interop": "0.7.x-dev"
     },
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",

From 4ecf68a65b883087319b5052b95e068d0cf55a4c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 8 Aug 2018 17:32:42 +0300
Subject: [PATCH 018/101] fix req.

---
 composer.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/composer.json b/composer.json
index 72f5cdb..5edbf60 100644
--- a/composer.json
+++ b/composer.json
@@ -12,9 +12,9 @@
     },
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",
-        "enqueue/test": "^0.9@dev",
-        "enqueue/enqueue": "^0.9@dev",
-        "enqueue/null": "^0.9@dev",
+        "enqueue/test": "0.9.x-dev",
+        "enqueue/enqueue": "0.9.x-dev",
+        "enqueue/null": "0.9.x-dev",
         "queue-interop/queue-spec": "^0.5.3@dev",
         "kwn/php-rdkafka-stubs": "^1.0.2",
         "symfony/dependency-injection": "^2.8|^3|^4",

From a5836c7b9e2c8d3a560698b18e8c2db3cd8284cb Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 9 Aug 2018 09:51:49 +0300
Subject: [PATCH 019/101] req queue interop ^0.6.2 and queue interop ^0.7.4

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 5edbf60..dd92cc8 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.1.3",
         "ext-rdkafka": "^3.0.3",
-        "queue-interop/queue-interop": "0.7.x-dev"
+        "queue-interop/queue-interop": "^0.6.2"
     },
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",

From 68bb991cb78ab24b7fc9ebcb1a1bd2b6cfcb67eb Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 14 Aug 2018 10:38:52 +0300
Subject: [PATCH 020/101] update pkgs travis files.

---
 .travis.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 658dcab..5556e8f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,8 @@ git:
 language: php
 
 php:
-    - '5.6'
-    - '7.0'
+    - '7.1'
+    - '7.2'
 
 cache:
     directories:

From b7eef6683309486dba6af764485df44917ca50d6 Mon Sep 17 00:00:00 2001
From: Alexander Miehe <alexander.miehe@flaconi.de>
Date: Wed, 15 Aug 2018 15:49:35 +0200
Subject: [PATCH 021/101] Allow either to use assign or subscribe

With `assign` it is not possible to use the rebalancing from Kafka. So if no offset is set we use `subscribe` and rebalancing is possible and otherwise if a offset is set we are using assign.
---
 RdKafkaConsumer.php | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 9d30be2..991ba21 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -55,8 +55,7 @@ public function __construct(KafkaConsumer $consumer, RdKafkaContext $context, Rd
         $this->topic = $topic;
         $this->subscribed = false;
         $this->commitAsync = false;
-        $this->offset = null;
-
+        
         $this->setSerializer($serializer);
     }
 
@@ -99,12 +98,15 @@ public function getQueue()
     public function receive($timeout = 0)
     {
         if (false == $this->subscribed) {
-            $this->consumer->assign([new TopicPartition(
-                $this->getQueue()->getQueueName(),
-                $this->getQueue()->getPartition(),
-                $this->offset
-            )]);
-
+            if ($this->offset === null) {
+                $this->consumer->subscribe([$this->getQueue()->getQueueName()]);
+            } else {
+                $this->consumer->assign([new TopicPartition(
+                    $this->getQueue()->getQueueName(),
+                    $this->getQueue()->getPartition(),
+                    $this->offset
+                ), ]);
+            }
             $this->subscribed = true;
         }
 

From 33715381942955c1419946ba7b5f85c2b7c68239 Mon Sep 17 00:00:00 2001
From: Alexander Miehe <alexander.miehe@flaconi.de>
Date: Thu, 16 Aug 2018 10:02:18 +0200
Subject: [PATCH 022/101] * change existing tests to use subscribe * add test
 that when a offset is set we use assign

---
 RdKafkaConsumer.php           |  8 +++----
 Tests/RdKafkaConsumerTest.php | 40 +++++++++++++++++++++++++++++++----
 2 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 991ba21..c9c9b7e 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -55,7 +55,7 @@ public function __construct(KafkaConsumer $consumer, RdKafkaContext $context, Rd
         $this->topic = $topic;
         $this->subscribed = false;
         $this->commitAsync = false;
-        
+
         $this->setSerializer($serializer);
     }
 
@@ -97,15 +97,15 @@ public function getQueue()
      */
     public function receive($timeout = 0)
     {
-        if (false == $this->subscribed) {
-            if ($this->offset === null) {
+        if (false === $this->subscribed) {
+            if (null === $this->offset) {
                 $this->consumer->subscribe([$this->getQueue()->getQueueName()]);
             } else {
                 $this->consumer->assign([new TopicPartition(
                     $this->getQueue()->getQueueName(),
                     $this->getQueue()->getPartition(),
                     $this->offset
-                ), ]);
+                )]);
             }
             $this->subscribed = true;
         }
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 77a949c..6e0e5d0 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -50,7 +50,7 @@ public function testShouldReceiveFromQueueAndReturnNullIfNoMessageInQueue()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->once())
@@ -79,7 +79,7 @@ public function testShouldPassProperlyConfiguredTopicPartitionOnAssign()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->any())
@@ -106,6 +106,36 @@ public function testShouldSubscribeOnFirstReceiveOnly()
         $kafkaMessage = new Message();
         $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
 
+        $kafkaConsumer = $this->createKafkaConsumerMock();
+        $kafkaConsumer
+            ->expects($this->once())
+            ->method('subscribe')
+        ;
+        $kafkaConsumer
+            ->expects($this->any())
+            ->method('consume')
+            ->willReturn($kafkaMessage)
+        ;
+
+        $consumer = new RdKafkaConsumer(
+            $kafkaConsumer,
+            $this->createContextMock(),
+            $destination,
+            $this->createSerializerMock()
+        );
+
+        $consumer->receive(1000);
+        $consumer->receive(1000);
+        $consumer->receive(1000);
+    }
+
+    public function testShouldAssignWhenOffsetIsSet()
+    {
+        $destination = new RdKafkaTopic('dest');
+
+        $kafkaMessage = new Message();
+        $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
+
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
@@ -124,6 +154,8 @@ public function testShouldSubscribeOnFirstReceiveOnly()
             $this->createSerializerMock()
         );
 
+        $consumer->setOffset(123);
+
         $consumer->receive(1000);
         $consumer->receive(1000);
         $consumer->receive(1000);
@@ -139,7 +171,7 @@ public function testThrowOnOffsetChangeAfterSubscribing()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->any())
@@ -174,7 +206,7 @@ public function testShouldReceiveFromQueueAndReturnMessageIfMessageInQueue()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->once())

From ec1e022b1ceb864dac2977e9c06f7f3f7ae18953 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 21 Aug 2018 20:09:34 +0300
Subject: [PATCH 023/101] [kafka] migrate kafka transport

---
 JsonSerializer.php           |  10 +--
 RdKafkaConnectionFactory.php |  17 +---
 RdKafkaConsumer.php          |  52 ++++-------
 RdKafkaContext.php           |  63 +++++++-------
 RdKafkaMessage.php           | 161 ++++++-----------------------------
 RdKafkaProducer.php          |  42 +++------
 RdKafkaTopic.php             |  45 ++--------
 Serializer.php               |  14 +--
 Tests/RdKafkaContextTest.php |   5 +-
 composer.json                |   2 +-
 10 files changed, 109 insertions(+), 302 deletions(-)

diff --git a/JsonSerializer.php b/JsonSerializer.php
index 763c432..e2f28cc 100644
--- a/JsonSerializer.php
+++ b/JsonSerializer.php
@@ -4,10 +4,7 @@
 
 class JsonSerializer implements Serializer
 {
-    /**
-     * {@inheritdoc}
-     */
-    public function toString(RdKafkaMessage $message)
+    public function toString(RdKafkaMessage $message): string
     {
         $json = json_encode([
             'body' => $message->getBody(),
@@ -26,10 +23,7 @@ public function toString(RdKafkaMessage $message)
         return $json;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function toMessage($string)
+    public function toMessage(string $string): RdKafkaMessage
     {
         $data = json_decode($string, true);
         if (JSON_ERROR_NONE !== json_last_error()) {
diff --git a/RdKafkaConnectionFactory.php b/RdKafkaConnectionFactory.php
index a3fdbc1..1228946 100644
--- a/RdKafkaConnectionFactory.php
+++ b/RdKafkaConnectionFactory.php
@@ -3,6 +3,7 @@
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\PsrContext;
 
 class RdKafkaConnectionFactory implements PsrConnectionFactory
 {
@@ -48,21 +49,14 @@ public function __construct($config = 'kafka:')
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return RdKafkaContext
      */
-    public function createContext()
+    public function createContext(): PsrContext
     {
         return new RdKafkaContext($this->config);
     }
 
-    /**
-     * @param string $dsn
-     *
-     * @return array
-     */
-    private function parseDsn($dsn)
+    private function parseDsn(string $dsn): array
     {
         $dsnConfig = parse_url($dsn);
         if (false === $dsnConfig) {
@@ -98,10 +92,7 @@ private function parseDsn($dsn)
         return array_replace_recursive($this->defaultConfig(), $config);
     }
 
-    /**
-     * @return array
-     */
-    private function defaultConfig()
+    private function defaultConfig(): array
     {
         return [
             'global' => [
diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index c9c9b7e..203d877 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -5,6 +5,7 @@
 use Interop\Queue\InvalidMessageException;
 use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use RdKafka\KafkaConsumer;
 use RdKafka\TopicPartition;
 
@@ -42,12 +43,6 @@ class RdKafkaConsumer implements PsrConsumer
      */
     private $offset;
 
-    /**
-     * @param KafkaConsumer  $consumer
-     * @param RdKafkaContext $context
-     * @param RdKafkaTopic   $topic
-     * @param Serializer     $serializer
-     */
     public function __construct(KafkaConsumer $consumer, RdKafkaContext $context, RdKafkaTopic $topic, Serializer $serializer)
     {
         $this->consumer = $consumer;
@@ -59,23 +54,17 @@ public function __construct(KafkaConsumer $consumer, RdKafkaContext $context, Rd
         $this->setSerializer($serializer);
     }
 
-    /**
-     * @return bool
-     */
-    public function isCommitAsync()
+    public function isCommitAsync(): bool
     {
         return $this->commitAsync;
     }
 
-    /**
-     * @param bool $async
-     */
-    public function setCommitAsync($async)
+    public function setCommitAsync(bool $async): void
     {
-        $this->commitAsync = (bool) $async;
+        $this->commitAsync = $async;
     }
 
-    public function setOffset($offset)
+    public function setOffset(int $offset = null): void
     {
         if ($this->subscribed) {
             throw new \LogicException('The consumer has already subscribed.');
@@ -84,18 +73,15 @@ public function setOffset($offset)
         $this->offset = $offset;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getQueue()
+    public function getQueue(): PsrQueue
     {
         return $this->topic;
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaMessage
      */
-    public function receive($timeout = 0)
+    public function receive(int $timeout = 0): ?PsrMessage
     {
         if (false === $this->subscribed) {
             if (null === $this->offset) {
@@ -107,6 +93,7 @@ public function receive($timeout = 0)
                     $this->offset
                 )]);
             }
+
             $this->subscribed = true;
         }
 
@@ -125,19 +112,17 @@ public function receive($timeout = 0)
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaMessage
      */
-    public function receiveNoWait()
+    public function receiveNoWait(): ?PsrMessage
     {
         throw new \LogicException('Not implemented');
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @param RdKafkaMessage $message
      */
-    public function acknowledge(PsrMessage $message)
+    public function acknowledge(PsrMessage $message): void
     {
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
 
@@ -153,11 +138,9 @@ public function acknowledge(PsrMessage $message)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @param RdKafkaMessage $message
      */
-    public function reject(PsrMessage $message, $requeue = false)
+    public function reject(PsrMessage $message, bool $requeue = false): void
     {
         $this->acknowledge($message);
 
@@ -166,12 +149,7 @@ public function reject(PsrMessage $message, $requeue = false)
         }
     }
 
-    /**
-     * @param int $timeout
-     *
-     * @return RdKafkaMessage|null
-     */
-    private function doReceive($timeout)
+    private function doReceive(int $timeout): ?RdKafkaMessage
     {
         $kafkaMessage = $this->consumer->consume($timeout);
 
@@ -190,5 +168,7 @@ private function doReceive($timeout)
                 throw new \LogicException($kafkaMessage->errstr(), $kafkaMessage->err);
                 break;
         }
+
+        return null;
     }
 }
diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 0a67e59..107d97a 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -3,8 +3,17 @@
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\InvalidDestinationException;
+use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrDestination;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrSubscriptionConsumer;
+use Interop\Queue\PsrTopic;
+use Interop\Queue\PurgeQueueNotSupportedException;
+use Interop\Queue\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\TemporaryQueueNotSupportedException;
 use RdKafka\Conf;
 use RdKafka\KafkaConsumer;
 use RdKafka\Producer;
@@ -46,57 +55,48 @@ public function __construct(array $config)
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaMessage
      */
-    public function createMessage($body = '', array $properties = [], array $headers = [])
+    public function createMessage(string $body = '', array $properties = [], array $headers = []): PsrMessage
     {
         return new RdKafkaMessage($body, $properties, $headers);
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return RdKafkaTopic
      */
-    public function createTopic($topicName)
+    public function createTopic(string $topicName): PsrTopic
     {
         return new RdKafkaTopic($topicName);
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return RdKafkaTopic
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         return new RdKafkaTopic($queueName);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function createTemporaryQueue()
+    public function createTemporaryQueue(): PsrQueue
     {
-        throw new \LogicException('Not implemented');
+        throw TemporaryQueueNotSupportedException::providerDoestNotSupportIt();
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return RdKafkaProducer
      */
-    public function createProducer()
+    public function createProducer(): PsrProducer
     {
         return new RdKafkaProducer($this->getProducer(), $this->getSerializer());
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @param RdKafkaTopic $destination
+     *
+     * @return RdKafkaConsumer
      */
-    public function createConsumer(PsrDestination $destination)
+    public function createConsumer(PsrDestination $destination): PsrConsumer
     {
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
 
@@ -116,10 +116,7 @@ public function createConsumer(PsrDestination $destination)
         return $consumer;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function close()
+    public function close(): void
     {
         $kafkaConsumers = $this->kafkaConsumers;
         $this->kafkaConsumers = [];
@@ -129,10 +126,17 @@ public function close()
         }
     }
 
-    /**
-     * @return Producer
-     */
-    private function getProducer()
+    public function createSubscriptionConsumer(): PsrSubscriptionConsumer
+    {
+        throw SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt();
+    }
+
+    public function purgeQueue(PsrQueue $queue): void
+    {
+        throw PurgeQueueNotSupportedException::providerDoestNotSupportIt();
+    }
+
+    private function getProducer(): Producer
     {
         if (null === $this->producer) {
             $this->producer = new Producer($this->getConf());
@@ -145,10 +149,7 @@ private function getProducer()
         return $this->producer;
     }
 
-    /**
-     * @return Conf
-     */
-    private function getConf()
+    private function getConf(): Conf
     {
         if (null === $this->conf) {
             $topicConf = new TopicConf();
diff --git a/RdKafkaMessage.php b/RdKafkaMessage.php
index 7b5137e..3024852 100644
--- a/RdKafkaMessage.php
+++ b/RdKafkaMessage.php
@@ -5,11 +5,7 @@
 use Interop\Queue\PsrMessage;
 use RdKafka\Message;
 
-/**
- * TODO: \JsonSerializable will be removed in next version (probably 0.8.x)
- * The serialization logic was moved to JsonSerializer.
- */
-class RdKafkaMessage implements PsrMessage, \JsonSerializable
+class RdKafkaMessage implements PsrMessage
 {
     /**
      * @var string
@@ -46,12 +42,7 @@ class RdKafkaMessage implements PsrMessage, \JsonSerializable
      */
     private $kafkaMessage;
 
-    /**
-     * @param string $body
-     * @param array  $properties
-     * @param array  $headers
-     */
-    public function __construct($body = '', array $properties = [], array $headers = [])
+    public function __construct(string $body = '', array $properties = [], array $headers = [])
     {
         $this->body = $body;
         $this->properties = $properties;
@@ -59,231 +50,135 @@ public function __construct($body = '', array $properties = [], array $headers =
         $this->redelivered = false;
     }
 
-    /**
-     * @param string $body
-     */
-    public function setBody($body)
+    public function setBody(string $body): void
     {
         $this->body = $body;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getBody()
+    public function getBody(): string
     {
         return $this->body;
     }
 
-    /**
-     * @param array $properties
-     */
-    public function setProperties(array $properties)
+    public function setProperties(array $properties): void
     {
         $this->properties = $properties;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getProperties()
+    public function getProperties(): array
     {
         return $this->properties;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setProperty($name, $value)
+    public function setProperty(string $name, $value): void
     {
         $this->properties[$name] = $value;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getProperty($name, $default = null)
+    public function getProperty(string $name, $default = null)
     {
         return array_key_exists($name, $this->properties) ? $this->properties[$name] : $default;
     }
 
-    /**
-     * @param array $headers
-     */
-    public function setHeaders(array $headers)
+    public function setHeaders(array $headers): void
     {
         $this->headers = $headers;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getHeaders()
+    public function getHeaders(): array
     {
         return $this->headers;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setHeader($name, $value)
+    public function setHeader(string $name, $value): void
     {
         $this->headers[$name] = $value;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getHeader($name, $default = null)
+    public function getHeader(string $name, $default = null)
     {
         return array_key_exists($name, $this->headers) ? $this->headers[$name] : $default;
     }
 
-    /**
-     * @return bool
-     */
-    public function isRedelivered()
+    public function isRedelivered(): bool
     {
         return $this->redelivered;
     }
 
-    /**
-     * @param bool $redelivered
-     */
-    public function setRedelivered($redelivered)
+    public function setRedelivered(bool $redelivered): void
     {
         $this->redelivered = $redelivered;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setCorrelationId($correlationId)
+    public function setCorrelationId(string $correlationId = null): void
     {
         $this->setHeader('correlation_id', (string) $correlationId);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getCorrelationId()
+    public function getCorrelationId(): ?string
     {
         return $this->getHeader('correlation_id');
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setMessageId($messageId)
+    public function setMessageId(string $messageId = null): void
     {
         $this->setHeader('message_id', (string) $messageId);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getMessageId()
+    public function getMessageId(): ?string
     {
         return $this->getHeader('message_id');
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getTimestamp()
+    public function getTimestamp(): ?int
     {
         $value = $this->getHeader('timestamp');
 
         return null === $value ? null : (int) $value;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setTimestamp($timestamp)
+    public function setTimestamp(int $timestamp = null): void
     {
         $this->setHeader('timestamp', $timestamp);
     }
 
-    /**
-     * @param string|null $replyTo
-     */
-    public function setReplyTo($replyTo)
+    public function setReplyTo(string $replyTo = null): void
     {
         $this->setHeader('reply_to', $replyTo);
     }
 
-    /**
-     * @return string|null
-     */
-    public function getReplyTo()
+    public function getReplyTo(): ?string
     {
         return $this->getHeader('reply_to');
     }
 
-    /**
-     * @return int
-     */
-    public function getPartition()
+    public function getPartition(): ?int
     {
         return $this->partition;
     }
 
-    /**
-     * @param int $partition
-     */
-    public function setPartition($partition)
+    public function setPartition(int $partition = null): void
     {
         $this->partition = $partition;
     }
 
-    /**
-     * @return string|null
-     */
-    public function getKey()
+    public function getKey(): ?string
     {
         return $this->key;
     }
 
-    /**
-     * @param string|null $key
-     */
-    public function setKey($key)
+    public function setKey(string $key = null): void
     {
         $this->key = $key;
     }
 
-    /**
-     * @return Message
-     */
-    public function getKafkaMessage()
+    public function getKafkaMessage(): ?Message
     {
         return $this->kafkaMessage;
     }
 
-    /**
-     * @param Message $message
-     */
-    public function setKafkaMessage(Message $message)
+    public function setKafkaMessage(Message $message = null): void
     {
         $this->kafkaMessage = $message;
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function jsonSerialize()
-    {
-        return (new JsonSerializer())->toString($this);
-    }
-
-    /**
-     * @param string $json
-     *
-     * @return self
-     */
-    public static function jsonUnserialize($json)
-    {
-        return (new JsonSerializer())->toMessage($json);
-    }
 }
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 337ce7b..6e96c7d 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -18,10 +18,6 @@ class RdKafkaProducer implements PsrProducer
      */
     private $producer;
 
-    /**
-     * @param Producer   $producer
-     * @param Serializer $serializer
-     */
     public function __construct(Producer $producer, Serializer $serializer)
     {
         $this->producer = $producer;
@@ -30,12 +26,10 @@ public function __construct(Producer $producer, Serializer $serializer)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @param RdKafkaTopic   $destination
      * @param RdKafkaMessage $message
      */
-    public function send(PsrDestination $destination, PsrMessage $message)
+    public function send(PsrDestination $destination, PsrMessage $message): void
     {
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
@@ -49,61 +43,49 @@ public function send(PsrDestination $destination, PsrMessage $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaProducer
      */
-    public function setDeliveryDelay($deliveryDelay)
+    public function setDeliveryDelay(int $deliveryDelay = null): PsrProducer
     {
         if (null === $deliveryDelay) {
-            return;
+            return $this;
         }
 
         throw new \LogicException('Not implemented');
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getDeliveryDelay()
+    public function getDeliveryDelay(): ?int
     {
         return null;
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaProducer
      */
-    public function setPriority($priority)
+    public function setPriority(int $priority = null): PsrProducer
     {
         if (null === $priority) {
-            return;
+            return $this;
         }
 
         throw new \LogicException('Not implemented');
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getPriority()
+    public function getPriority(): ?int
     {
         return null;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setTimeToLive($timeToLive)
+    public function setTimeToLive(int $timeToLive = null): PsrProducer
     {
         if (null === $timeToLive) {
-            return;
+            return $this;
         }
 
         throw new \LogicException('Not implemented');
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getTimeToLive()
+    public function getTimeToLive(): ?int
     {
         return null;
     }
diff --git a/RdKafkaTopic.php b/RdKafkaTopic.php
index 6faf12f..c4bbbe2 100644
--- a/RdKafkaTopic.php
+++ b/RdKafkaTopic.php
@@ -28,74 +28,47 @@ class RdKafkaTopic implements PsrTopic, PsrQueue
      */
     private $key;
 
-    /**
-     * @param string $name
-     */
-    public function __construct($name)
+    public function __construct(string $name)
     {
         $this->name = $name;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getTopicName()
+    public function getTopicName(): string
     {
         return $this->name;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getQueueName()
+    public function getQueueName(): string
     {
         return $this->name;
     }
 
-    /**
-     * @return TopicConf|null
-     */
-    public function getConf()
+    public function getConf(): ?TopicConf
     {
         return $this->conf;
     }
 
-    /**
-     * @param TopicConf|null $conf
-     */
-    public function setConf(TopicConf $conf = null)
+    public function setConf(TopicConf $conf = null): void
     {
         $this->conf = $conf;
     }
 
-    /**
-     * @return int
-     */
-    public function getPartition()
+    public function getPartition(): ?int
     {
         return $this->partition;
     }
 
-    /**
-     * @param int $partition
-     */
-    public function setPartition($partition)
+    public function setPartition(int $partition = null): void
     {
         $this->partition = $partition;
     }
 
-    /**
-     * @return string|null
-     */
-    public function getKey()
+    public function getKey(): ?string
     {
         return $this->key;
     }
 
-    /**
-     * @param string|null $key
-     */
-    public function setKey($key)
+    public function setKey(string $key = null): void
     {
         $this->key = $key;
     }
diff --git a/Serializer.php b/Serializer.php
index e141415..581c49b 100644
--- a/Serializer.php
+++ b/Serializer.php
@@ -4,17 +4,7 @@
 
 interface Serializer
 {
-    /**
-     * @param RdKafkaMessage $message
-     *
-     * @return string
-     */
-    public function toString(RdKafkaMessage $message);
+    public function toString(RdKafkaMessage $message): string;
 
-    /**
-     * @param string $string
-     *
-     * @return RdKafkaMessage
-     */
-    public function toMessage($string);
+    public function toMessage(string $string): RdKafkaMessage;
 }
diff --git a/Tests/RdKafkaContextTest.php b/Tests/RdKafkaContextTest.php
index f09c5be..72ce311 100644
--- a/Tests/RdKafkaContextTest.php
+++ b/Tests/RdKafkaContextTest.php
@@ -7,6 +7,7 @@
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\RdKafka\Serializer;
 use Interop\Queue\InvalidDestinationException;
+use Interop\Queue\TemporaryQueueNotSupportedException;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -18,8 +19,8 @@ public function testThrowNotImplementedOnCreateTemporaryQueue()
     {
         $context = new RdKafkaContext([]);
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Not implemented');
+        $this->expectException(TemporaryQueueNotSupportedException::class);
+
         $context->createTemporaryQueue();
     }
 
diff --git a/composer.json b/composer.json
index dd92cc8..5edbf60 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.1.3",
         "ext-rdkafka": "^3.0.3",
-        "queue-interop/queue-interop": "^0.6.2"
+        "queue-interop/queue-interop": "0.7.x-dev"
     },
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",

From 8401b3331012252c147ed8837447c50469da08b3 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 22 Aug 2018 15:58:01 +0300
Subject: [PATCH 024/101] upd spec version.

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 5edbf60..6409e74 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
         "enqueue/test": "0.9.x-dev",
         "enqueue/enqueue": "0.9.x-dev",
         "enqueue/null": "0.9.x-dev",
-        "queue-interop/queue-spec": "^0.5.3@dev",
+        "queue-interop/queue-spec": "0.6.x-dev",
         "kwn/php-rdkafka-stubs": "^1.0.2",
         "symfony/dependency-injection": "^2.8|^3|^4",
         "symfony/config": "^2.8|^3|^4"

From 5eaee9cf8720edeeeff7ae73421b386a008d030e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 25 Aug 2018 15:07:30 +0300
Subject: [PATCH 025/101] add strict to rdkafka transport.

---
 JsonSerializer.php            | 2 ++
 RdKafkaConnectionFactory.php  | 2 ++
 RdKafkaConsumer.php           | 2 ++
 RdKafkaContext.php            | 2 ++
 RdKafkaMessage.php            | 2 ++
 RdKafkaProducer.php           | 2 ++
 RdKafkaTopic.php              | 2 ++
 Serializer.php                | 2 ++
 SerializerAwareTrait.php      | 2 ++
 Tests/RdKafkaConsumerTest.php | 1 +
 10 files changed, 19 insertions(+)

diff --git a/JsonSerializer.php b/JsonSerializer.php
index e2f28cc..ae161ca 100644
--- a/JsonSerializer.php
+++ b/JsonSerializer.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 class JsonSerializer implements Serializer
diff --git a/RdKafkaConnectionFactory.php b/RdKafkaConnectionFactory.php
index 1228946..272a40b 100644
--- a/RdKafkaConnectionFactory.php
+++ b/RdKafkaConnectionFactory.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\PsrConnectionFactory;
diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 203d877..a598545 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\InvalidMessageException;
diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 107d97a..fc7a0f7 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\InvalidDestinationException;
diff --git a/RdKafkaMessage.php b/RdKafkaMessage.php
index 3024852..cad2b10 100644
--- a/RdKafkaMessage.php
+++ b/RdKafkaMessage.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\PsrMessage;
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 6e96c7d..b11179c 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\InvalidDestinationException;
diff --git a/RdKafkaTopic.php b/RdKafkaTopic.php
index c4bbbe2..8ee1a77 100644
--- a/RdKafkaTopic.php
+++ b/RdKafkaTopic.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 use Interop\Queue\PsrQueue;
diff --git a/Serializer.php b/Serializer.php
index 581c49b..7e2a116 100644
--- a/Serializer.php
+++ b/Serializer.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 interface Serializer
diff --git a/SerializerAwareTrait.php b/SerializerAwareTrait.php
index 866d303..d640f93 100644
--- a/SerializerAwareTrait.php
+++ b/SerializerAwareTrait.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\RdKafka;
 
 trait SerializerAwareTrait
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 6e0e5d0..e35d97f 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -132,6 +132,7 @@ public function testShouldSubscribeOnFirstReceiveOnly()
     public function testShouldAssignWhenOffsetIsSet()
     {
         $destination = new RdKafkaTopic('dest');
+        $destination->setPartition(1);
 
         $kafkaMessage = new Message();
         $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;

From 181184657ee1ffe4882d4a13f7292a6e836da023 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 4 Sep 2018 17:59:00 +0300
Subject: [PATCH 026/101] Simplify Enqueue configuration.

---
 Client/RdKafkaDriver.php                      | 167 --------
 Symfony/RdKafkaTransportFactory.php           | 139 -------
 Tests/Client/RdKafkaDriverTest.php            | 368 ------------------
 Tests/Symfony/RdKafkaTransportFactoryTest.php | 151 -------
 composer.json                                 |   8 +-
 5 files changed, 1 insertion(+), 832 deletions(-)
 delete mode 100644 Client/RdKafkaDriver.php
 delete mode 100644 Symfony/RdKafkaTransportFactory.php
 delete mode 100644 Tests/Client/RdKafkaDriverTest.php
 delete mode 100644 Tests/Symfony/RdKafkaTransportFactoryTest.php

diff --git a/Client/RdKafkaDriver.php b/Client/RdKafkaDriver.php
deleted file mode 100644
index 4167253..0000000
--- a/Client/RdKafkaDriver.php
+++ /dev/null
@@ -1,167 +0,0 @@
-<?php
-
-namespace Enqueue\RdKafka\Client;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\RdKafka\RdKafkaContext;
-use Interop\Queue\PsrMessage;
-use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
-
-class RdKafkaDriver implements DriverInterface
-{
-    /**
-     * @var RdKafkaContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    /**
-     * @param RdKafkaContext    $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
-    public function __construct(RdKafkaContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createTransportMessage(Message $message)
-    {
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($message->getProperties());
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createClientMessage(PsrMessage $message)
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $topic = $this->createRouterTopic();
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($topic, $transportMessage);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createQueue($queueName)
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
-    {
-        $logger = $logger ?: new NullLogger();
-        $logger->debug('[RdKafkaDriver] setup broker');
-        $log = function ($text, ...$args) use ($logger) {
-            $logger->debug(sprintf('[RdKafkaDriver] '.$text, ...$args));
-        };
-
-        // setup router
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
-        $log('Create router queue: %s', $routerQueue->getQueueName());
-        $this->context->createConsumer($routerQueue);
-
-        // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $queue = $this->createQueue($meta->getClientName());
-            $log('Create processor queue: %s', $queue->getQueueName());
-            $this->context->createConsumer($queue);
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getConfig()
-    {
-        return $this->config;
-    }
-
-    private function createRouterTopic()
-    {
-        $topic = $this->context->createTopic(
-            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
-        );
-
-        return $topic;
-    }
-}
diff --git a/Symfony/RdKafkaTransportFactory.php b/Symfony/RdKafkaTransportFactory.php
deleted file mode 100644
index f03971f..0000000
--- a/Symfony/RdKafkaTransportFactory.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-namespace Enqueue\RdKafka\Symfony;
-
-use Enqueue\RdKafka\Client\RdKafkaDriver;
-use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Enqueue\RdKafka\RdKafkaContext;
-use Enqueue\Symfony\DriverFactoryInterface;
-use Enqueue\Symfony\TransportFactoryInterface;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\Reference;
-
-class RdKafkaTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
-{
-    /**
-     * @var string
-     */
-    private $name;
-
-    /**
-     * @param string $name
-     */
-    public function __construct($name = 'rdkafka')
-    {
-        $this->name = $name;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function addConfiguration(ArrayNodeDefinition $builder)
-    {
-        $builder
-            ->beforeNormalization()
-                ->ifString()
-                ->then(function ($v) {
-                    return ['dsn' => $v];
-                })
-            ->end()
-            ->children()
-                ->scalarNode('dsn')
-                    ->info('The kafka DSN. Other parameters are ignored if set')
-                ->end()
-                ->variableNode('global')
-                    ->defaultValue([])
-                    ->info('The kafka global configuration properties')
-                ->end()
-                ->variableNode('topic')
-                    ->defaultValue([])
-                    ->info('The kafka topic configuration properties')
-                ->end()
-                ->scalarNode('dr_msg_cb')
-                    ->info('Delivery report callback')
-                ->end()
-                ->scalarNode('error_cb')
-                    ->info('Error callback')
-                ->end()
-                ->scalarNode('rebalance_cb')
-                    ->info('Called after consumer group has been rebalanced')
-                ->end()
-                ->enumNode('partitioner')
-                    ->values(['RD_KAFKA_MSG_PARTITIONER_RANDOM', 'RD_KAFKA_MSG_PARTITIONER_CONSISTENT'])
-                    ->info('Which partitioner to use')
-                ->end()
-                ->integerNode('log_level')
-                    ->info('Logging level (syslog(3) levels)')
-                    ->min(0)->max(7)
-                ->end()
-                ->booleanNode('commit_async')
-                    ->defaultFalse()
-                    ->info('Commit asynchronous')
-                ->end()
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createConnectionFactory(ContainerBuilder $container, array $config)
-    {
-        if (false == empty($config['rdkafka'])) {
-            $config['rdkafka'] = new Reference($config['rdkafka']);
-        }
-
-        $factory = new Definition(RdKafkaConnectionFactory::class);
-        $factory->setArguments([isset($config['dsn']) ? $config['dsn'] : $config]);
-
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        $container->setDefinition($factoryId, $factory);
-
-        return $factoryId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createContext(ContainerBuilder $container, array $config)
-    {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-
-        $context = new Definition(RdKafkaContext::class);
-        $context->setPublic(true);
-        $context->setFactory([new Reference($factoryId), 'createContext']);
-
-        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
-        $container->setDefinition($contextId, $context);
-
-        return $contextId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createDriver(ContainerBuilder $container, array $config)
-    {
-        $driver = new Definition(RdKafkaDriver::class);
-        $driver->setPublic(true);
-        $driver->setArguments([
-            new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
-            new Reference('enqueue.client.config'),
-            new Reference('enqueue.client.meta.queue_meta_registry'),
-        ]);
-
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-        $container->setDefinition($driverId, $driver);
-
-        return $driverId;
-    }
-
-    /**
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-}
diff --git a/Tests/Client/RdKafkaDriverTest.php b/Tests/Client/RdKafkaDriverTest.php
deleted file mode 100644
index af9de41..0000000
--- a/Tests/Client/RdKafkaDriverTest.php
+++ /dev/null
@@ -1,368 +0,0 @@
-<?php
-
-namespace Enqueue\RdKafka\Tests\Client;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\RdKafka\Client\RdKafkaDriver;
-use Enqueue\RdKafka\RdKafkaContext;
-use Enqueue\RdKafka\RdKafkaMessage;
-use Enqueue\RdKafka\RdKafkaTopic;
-use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrProducer;
-
-/**
- * @group rdkafka
- */
-class RdKafkaDriverTest extends \PHPUnit_Framework_TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementsDriverInterface()
-    {
-        $this->assertClassImplements(DriverInterface::class, RdKafkaDriver::class);
-    }
-
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $config,
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new RdKafkaTopic('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new RdKafkaTopic('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new RdKafkaMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-
-        $this->assertNull($clientMessage->getExpire());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new RdKafkaMessage())
-        ;
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(RdKafkaMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => null,
-            'correlation_id' => '',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new RdKafkaTopic('queue-name');
-        $transportMessage = new RdKafkaMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->with('aprefix.router')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new RdKafkaTopic('queue-name');
-        $transportMessage = new RdKafkaMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldSetupBroker()
-    {
-        $routerTopic = new RdKafkaTopic('');
-        $routerQueue = new RdKafkaTopic('');
-
-        $processorTopic = new RdKafkaTopic('');
-
-        $context = $this->createPsrContextMock();
-
-        $context
-            ->expects($this->at(0))
-            ->method('createQueue')
-            ->willReturn($routerTopic)
-        ;
-        $context
-            ->expects($this->at(1))
-            ->method('createQueue')
-            ->willReturn($routerQueue)
-        ;
-        $context
-            ->expects($this->at(2))
-            ->method('createQueue')
-            ->willReturn($processorTopic)
-        ;
-
-        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
-            'default' => [],
-        ]);
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $meta
-        );
-
-        $driver->setupBroker();
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|RdKafkaContext
-     */
-    private function createPsrContextMock()
-    {
-        return $this->createMock(RdKafkaContext::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
-     */
-    private function createPsrProducerMock()
-    {
-        return $this->createMock(PsrProducer::class);
-    }
-
-    /**
-     * @return QueueMetaRegistry
-     */
-    private function createDummyQueueMetaRegistry()
-    {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
-
-        return $registry;
-    }
-
-    /**
-     * @return Config
-     */
-    private function createDummyConfig()
-    {
-        return Config::create('aPrefix');
-    }
-}
diff --git a/Tests/Symfony/RdKafkaTransportFactoryTest.php b/Tests/Symfony/RdKafkaTransportFactoryTest.php
deleted file mode 100644
index 6816e16..0000000
--- a/Tests/Symfony/RdKafkaTransportFactoryTest.php
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-
-namespace Enqueue\RdKafka\Tests\Symfony;
-
-use Enqueue\RdKafka\Client\RdKafkaDriver;
-use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Enqueue\RdKafka\Symfony\RdKafkaTransportFactory;
-use Enqueue\Symfony\TransportFactoryInterface;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Config\Definition\Builder\TreeBuilder;
-use Symfony\Component\Config\Definition\Processor;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Reference;
-
-/**
- * @group rdkafka
- */
-class RdKafkaTransportFactoryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementTransportFactoryInterface()
-    {
-        $this->assertClassImplements(TransportFactoryInterface::class, RdKafkaTransportFactory::class);
-    }
-
-    public function testCouldBeConstructedWithDefaultName()
-    {
-        $transport = new RdKafkaTransportFactory();
-
-        $this->assertEquals('rdkafka', $transport->getName());
-    }
-
-    public function testCouldBeConstructedWithCustomName()
-    {
-        $transport = new RdKafkaTransportFactory('theCustomName');
-
-        $this->assertEquals('theCustomName', $transport->getName());
-    }
-
-    public function testShouldAllowAddConfiguration()
-    {
-        $transport = new RdKafkaTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [[
-        ]]);
-
-        $this->assertEquals([
-            'topic' => [],
-            'commit_async' => false,
-            'global' => [],
-        ], $config);
-    }
-
-    public function testShouldAllowAddConfigurationAsString()
-    {
-        $transport = new RdKafkaTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), ['kafkaDSN']);
-
-        $this->assertEquals([
-            'dsn' => 'kafkaDSN',
-            'topic' => [],
-            'commit_async' => false,
-            'global' => [],
-        ], $config);
-    }
-
-    public function testShouldCreateConnectionFactory()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RdKafkaTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, [
-        ]);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(RdKafkaConnectionFactory::class, $factory->getClass());
-        $this->assertSame([[
-        ]], $factory->getArguments());
-    }
-
-    public function testShouldCreateConnectionFactoryFromDsnString()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RdKafkaTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, [
-            'dsn' => 'theKafkaDSN',
-        ]);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(RdKafkaConnectionFactory::class, $factory->getClass());
-        $this->assertSame(['theKafkaDSN'], $factory->getArguments());
-    }
-
-    public function testShouldCreateContext()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RdKafkaTransportFactory();
-
-        $serviceId = $transport->createContext($container, [
-        ]);
-
-        $this->assertEquals('enqueue.transport.rdkafka.context', $serviceId);
-        $this->assertTrue($container->hasDefinition($serviceId));
-
-        $context = $container->getDefinition('enqueue.transport.rdkafka.context');
-        $this->assertInstanceOf(Reference::class, $context->getFactory()[0]);
-        $this->assertEquals('enqueue.transport.rdkafka.connection_factory', (string) $context->getFactory()[0]);
-        $this->assertEquals('createContext', $context->getFactory()[1]);
-    }
-
-    public function testShouldCreateDriver()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RdKafkaTransportFactory();
-
-        $serviceId = $transport->createDriver($container, []);
-
-        $this->assertEquals('enqueue.client.rdkafka.driver', $serviceId);
-        $this->assertTrue($container->hasDefinition($serviceId));
-
-        $driver = $container->getDefinition($serviceId);
-        $this->assertSame(RdKafkaDriver::class, $driver->getClass());
-
-        $this->assertInstanceOf(Reference::class, $driver->getArgument(0));
-        $this->assertEquals('enqueue.transport.rdkafka.context', (string) $driver->getArgument(0));
-
-        $this->assertInstanceOf(Reference::class, $driver->getArgument(1));
-        $this->assertEquals('enqueue.client.config', (string) $driver->getArgument(1));
-
-        $this->assertInstanceOf(Reference::class, $driver->getArgument(2));
-        $this->assertEquals('enqueue.client.meta.queue_meta_registry', (string) $driver->getArgument(2));
-    }
-}
diff --git a/composer.json b/composer.json
index 6409e74..18374e1 100644
--- a/composer.json
+++ b/composer.json
@@ -13,12 +13,9 @@
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",
         "enqueue/test": "0.9.x-dev",
-        "enqueue/enqueue": "0.9.x-dev",
         "enqueue/null": "0.9.x-dev",
         "queue-interop/queue-spec": "0.6.x-dev",
-        "kwn/php-rdkafka-stubs": "^1.0.2",
-        "symfony/dependency-injection": "^2.8|^3|^4",
-        "symfony/config": "^2.8|^3|^4"
+        "kwn/php-rdkafka-stubs": "^1.0.2"
     },
     "support": {
         "email": "opensource@forma-pro.com",
@@ -44,9 +41,6 @@
             "RdKafka\\": "vendor/kwn/php-rdkafka-stubs/stubs/RdKafka"
         }
     },
-    "suggest": {
-        "enqueue/enqueue": "If you'd like to use advanced features like Client abstract layer or Symfony integration features"
-    },
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {

From d96852a6285cdf6c125c8f57344a7493a4eb91ad Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 14:53:56 +0300
Subject: [PATCH 027/101] [rdkafka] Do not use deprecated classes.

---
 README.md                                     |  2 +-
 RdKafkaConnectionFactory.php                  |  8 ++--
 RdKafkaConsumer.php                           | 20 ++++----
 RdKafkaContext.php                            | 48 +++++++++----------
 RdKafkaMessage.php                            | 10 ++--
 RdKafkaProducer.php                           | 28 +++++------
 RdKafkaTopic.php                              |  6 +--
 Tests/RdKafkaContextTest.php                  |  4 +-
 Tests/RdKafkaProducerTest.php                 |  4 +-
 .../RdKafkaSendToAndReceiveFromTopicTest.php  |  4 +-
 10 files changed, 67 insertions(+), 67 deletions(-)

diff --git a/README.md b/README.md
index 7ea76ae..d0e0bed 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![Total Downloads](https://poser.pugx.org/enqueue/rdkafka/d/total.png)](https://packagist.org/packages/enqueue/rdkafka)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/rdkafka/version.png)](https://packagist.org/packages/enqueue/rdkafka)
  
-This is an implementation of PSR specification. It allows you to send and consume message via Kafka protocol.  
+This is an implementation of Queue Interop specification. It allows you to send and consume message via Kafka protocol.  
 
 ## Resources
 
diff --git a/RdKafkaConnectionFactory.php b/RdKafkaConnectionFactory.php
index 272a40b..988473d 100644
--- a/RdKafkaConnectionFactory.php
+++ b/RdKafkaConnectionFactory.php
@@ -4,10 +4,10 @@
 
 namespace Enqueue\RdKafka;
 
-use Interop\Queue\PsrConnectionFactory;
-use Interop\Queue\PsrContext;
+use Interop\Queue\ConnectionFactory;
+use Interop\Queue\Context;
 
-class RdKafkaConnectionFactory implements PsrConnectionFactory
+class RdKafkaConnectionFactory implements ConnectionFactory
 {
     /**
      * @var array
@@ -53,7 +53,7 @@ public function __construct($config = 'kafka:')
     /**
      * @return RdKafkaContext
      */
-    public function createContext(): PsrContext
+    public function createContext(): Context
     {
         return new RdKafkaContext($this->config);
     }
diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index a598545..5e89f3d 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -4,14 +4,14 @@
 
 namespace Enqueue\RdKafka;
 
-use Interop\Queue\InvalidMessageException;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Consumer;
+use Interop\Queue\Exception\InvalidMessageException;
+use Interop\Queue\Message;
+use Interop\Queue\Queue;
 use RdKafka\KafkaConsumer;
 use RdKafka\TopicPartition;
 
-class RdKafkaConsumer implements PsrConsumer
+class RdKafkaConsumer implements Consumer
 {
     use SerializerAwareTrait;
 
@@ -75,7 +75,7 @@ public function setOffset(int $offset = null): void
         $this->offset = $offset;
     }
 
-    public function getQueue(): PsrQueue
+    public function getQueue(): Queue
     {
         return $this->topic;
     }
@@ -83,7 +83,7 @@ public function getQueue(): PsrQueue
     /**
      * @return RdKafkaMessage
      */
-    public function receive(int $timeout = 0): ?PsrMessage
+    public function receive(int $timeout = 0): ?Message
     {
         if (false === $this->subscribed) {
             if (null === $this->offset) {
@@ -116,7 +116,7 @@ public function receive(int $timeout = 0): ?PsrMessage
     /**
      * @return RdKafkaMessage
      */
-    public function receiveNoWait(): ?PsrMessage
+    public function receiveNoWait(): ?Message
     {
         throw new \LogicException('Not implemented');
     }
@@ -124,7 +124,7 @@ public function receiveNoWait(): ?PsrMessage
     /**
      * @param RdKafkaMessage $message
      */
-    public function acknowledge(PsrMessage $message): void
+    public function acknowledge(Message $message): void
     {
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
 
@@ -142,7 +142,7 @@ public function acknowledge(PsrMessage $message): void
     /**
      * @param RdKafkaMessage $message
      */
-    public function reject(PsrMessage $message, bool $requeue = false): void
+    public function reject(Message $message, bool $requeue = false): void
     {
         $this->acknowledge($message);
 
diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index fc7a0f7..cfe8e0e 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -4,24 +4,24 @@
 
 namespace Enqueue\RdKafka;
 
-use Interop\Queue\InvalidDestinationException;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrSubscriptionConsumer;
-use Interop\Queue\PsrTopic;
-use Interop\Queue\PurgeQueueNotSupportedException;
-use Interop\Queue\SubscriptionConsumerNotSupportedException;
-use Interop\Queue\TemporaryQueueNotSupportedException;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context;
+use Interop\Queue\Destination;
+use Interop\Queue\Exception\InvalidDestinationException;
+use Interop\Queue\Exception\PurgeQueueNotSupportedException;
+use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Exception\TemporaryQueueNotSupportedException;
+use Interop\Queue\Message;
+use Interop\Queue\Producer;
+use Interop\Queue\Queue;
+use Interop\Queue\SubscriptionConsumer;
+use Interop\Queue\Topic;
 use RdKafka\Conf;
 use RdKafka\KafkaConsumer;
-use RdKafka\Producer;
+use RdKafka\Producer as VendorProducer;
 use RdKafka\TopicConf;
 
-class RdKafkaContext implements PsrContext
+class RdKafkaContext implements Context
 {
     use SerializerAwareTrait;
 
@@ -59,7 +59,7 @@ public function __construct(array $config)
     /**
      * @return RdKafkaMessage
      */
-    public function createMessage(string $body = '', array $properties = [], array $headers = []): PsrMessage
+    public function createMessage(string $body = '', array $properties = [], array $headers = []): Message
     {
         return new RdKafkaMessage($body, $properties, $headers);
     }
@@ -67,7 +67,7 @@ public function createMessage(string $body = '', array $properties = [], array $
     /**
      * @return RdKafkaTopic
      */
-    public function createTopic(string $topicName): PsrTopic
+    public function createTopic(string $topicName): Topic
     {
         return new RdKafkaTopic($topicName);
     }
@@ -75,12 +75,12 @@ public function createTopic(string $topicName): PsrTopic
     /**
      * @return RdKafkaTopic
      */
-    public function createQueue(string $queueName): PsrQueue
+    public function createQueue(string $queueName): Queue
     {
         return new RdKafkaTopic($queueName);
     }
 
-    public function createTemporaryQueue(): PsrQueue
+    public function createTemporaryQueue(): Queue
     {
         throw TemporaryQueueNotSupportedException::providerDoestNotSupportIt();
     }
@@ -88,7 +88,7 @@ public function createTemporaryQueue(): PsrQueue
     /**
      * @return RdKafkaProducer
      */
-    public function createProducer(): PsrProducer
+    public function createProducer(): Producer
     {
         return new RdKafkaProducer($this->getProducer(), $this->getSerializer());
     }
@@ -98,7 +98,7 @@ public function createProducer(): PsrProducer
      *
      * @return RdKafkaConsumer
      */
-    public function createConsumer(PsrDestination $destination): PsrConsumer
+    public function createConsumer(Destination $destination): Consumer
     {
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
 
@@ -128,20 +128,20 @@ public function close(): void
         }
     }
 
-    public function createSubscriptionConsumer(): PsrSubscriptionConsumer
+    public function createSubscriptionConsumer(): SubscriptionConsumer
     {
         throw SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt();
     }
 
-    public function purgeQueue(PsrQueue $queue): void
+    public function purgeQueue(Queue $queue): void
     {
         throw PurgeQueueNotSupportedException::providerDoestNotSupportIt();
     }
 
-    private function getProducer(): Producer
+    private function getProducer(): VendorProducer
     {
         if (null === $this->producer) {
-            $this->producer = new Producer($this->getConf());
+            $this->producer = new VendorProducer($this->getConf());
 
             if (isset($this->config['log_level'])) {
                 $this->producer->setLogLevel($this->config['log_level']);
diff --git a/RdKafkaMessage.php b/RdKafkaMessage.php
index cad2b10..0785a56 100644
--- a/RdKafkaMessage.php
+++ b/RdKafkaMessage.php
@@ -4,10 +4,10 @@
 
 namespace Enqueue\RdKafka;
 
-use Interop\Queue\PsrMessage;
-use RdKafka\Message;
+use Interop\Queue\Message;
+use RdKafka\Message as VendorMessage;
 
-class RdKafkaMessage implements PsrMessage
+class RdKafkaMessage implements Message
 {
     /**
      * @var string
@@ -174,12 +174,12 @@ public function setKey(string $key = null): void
         $this->key = $key;
     }
 
-    public function getKafkaMessage(): ?Message
+    public function getKafkaMessage(): ?VendorMessage
     {
         return $this->kafkaMessage;
     }
 
-    public function setKafkaMessage(Message $message = null): void
+    public function setKafkaMessage(VendorMessage $message = null): void
     {
         $this->kafkaMessage = $message;
     }
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index b11179c..feb40c4 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -4,23 +4,23 @@
 
 namespace Enqueue\RdKafka;
 
-use Interop\Queue\InvalidDestinationException;
-use Interop\Queue\InvalidMessageException;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
-use RdKafka\Producer;
-
-class RdKafkaProducer implements PsrProducer
+use Interop\Queue\Destination;
+use Interop\Queue\Exception\InvalidDestinationException;
+use Interop\Queue\Exception\InvalidMessageException;
+use Interop\Queue\Message;
+use Interop\Queue\Producer;
+use RdKafka\Producer as VendorProducer;
+
+class RdKafkaProducer implements Producer
 {
     use SerializerAwareTrait;
 
     /**
-     * @var Producer
+     * @var VendorProducer
      */
     private $producer;
 
-    public function __construct(Producer $producer, Serializer $serializer)
+    public function __construct(VendorProducer $producer, Serializer $serializer)
     {
         $this->producer = $producer;
 
@@ -31,7 +31,7 @@ public function __construct(Producer $producer, Serializer $serializer)
      * @param RdKafkaTopic   $destination
      * @param RdKafkaMessage $message
      */
-    public function send(PsrDestination $destination, PsrMessage $message): void
+    public function send(Destination $destination, Message $message): void
     {
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
@@ -47,7 +47,7 @@ public function send(PsrDestination $destination, PsrMessage $message): void
     /**
      * @return RdKafkaProducer
      */
-    public function setDeliveryDelay(int $deliveryDelay = null): PsrProducer
+    public function setDeliveryDelay(int $deliveryDelay = null): Producer
     {
         if (null === $deliveryDelay) {
             return $this;
@@ -64,7 +64,7 @@ public function getDeliveryDelay(): ?int
     /**
      * @return RdKafkaProducer
      */
-    public function setPriority(int $priority = null): PsrProducer
+    public function setPriority(int $priority = null): Producer
     {
         if (null === $priority) {
             return $this;
@@ -78,7 +78,7 @@ public function getPriority(): ?int
         return null;
     }
 
-    public function setTimeToLive(int $timeToLive = null): PsrProducer
+    public function setTimeToLive(int $timeToLive = null): Producer
     {
         if (null === $timeToLive) {
             return $this;
diff --git a/RdKafkaTopic.php b/RdKafkaTopic.php
index 8ee1a77..a7bde10 100644
--- a/RdKafkaTopic.php
+++ b/RdKafkaTopic.php
@@ -4,11 +4,11 @@
 
 namespace Enqueue\RdKafka;
 
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue;
+use Interop\Queue\Topic;
 use RdKafka\TopicConf;
 
-class RdKafkaTopic implements PsrTopic, PsrQueue
+class RdKafkaTopic implements Topic, Queue
 {
     /**
      * @var string
diff --git a/Tests/RdKafkaContextTest.php b/Tests/RdKafkaContextTest.php
index 72ce311..e787efb 100644
--- a/Tests/RdKafkaContextTest.php
+++ b/Tests/RdKafkaContextTest.php
@@ -6,8 +6,8 @@
 use Enqueue\RdKafka\JsonSerializer;
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\RdKafka\Serializer;
-use Interop\Queue\InvalidDestinationException;
-use Interop\Queue\TemporaryQueueNotSupportedException;
+use Interop\Queue\Exception\InvalidDestinationException;
+use Interop\Queue\Exception\TemporaryQueueNotSupportedException;
 use PHPUnit\Framework\TestCase;
 
 /**
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 1c076e4..be5b94a 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -8,8 +8,8 @@
 use Enqueue\RdKafka\RdKafkaProducer;
 use Enqueue\RdKafka\RdKafkaTopic;
 use Enqueue\RdKafka\Serializer;
-use Interop\Queue\InvalidDestinationException;
-use Interop\Queue\InvalidMessageException;
+use Interop\Queue\Exception\InvalidDestinationException;
+use Interop\Queue\Exception\InvalidMessageException;
 use PHPUnit\Framework\TestCase;
 use RdKafka\Producer;
 use RdKafka\ProducerTopic;
diff --git a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
index 05b5abf..6604eb1 100644
--- a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
+++ b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
@@ -3,7 +3,7 @@
 namespace Enqueue\RdKafka\Tests\Spec;
 
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Message;
 use Interop\Queue\Spec\SendToAndReceiveFromTopicSpec;
 
 /**
@@ -27,7 +27,7 @@ public function test()
 
         $message = $consumer->receive(10000); // 10 sec
 
-        $this->assertInstanceOf(PsrMessage::class, $message);
+        $this->assertInstanceOf(Message::class, $message);
         $consumer->acknowledge($message);
 
         $this->assertSame($expectedBody, $message->getBody());

From 5c0260fa7236d20f3dd89181fd928a3568d53e06 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 15:18:46 +0300
Subject: [PATCH 028/101] Remove "Psr" prefix from spec base tests.

---
 Tests/Spec/RdKafkaConnectionFactoryTest.php | 4 ++--
 Tests/Spec/RdKafkaContextTest.php           | 4 ++--
 Tests/Spec/RdKafkaMessageTest.php           | 4 ++--
 Tests/Spec/RdKafkaQueueTest.php             | 4 ++--
 Tests/Spec/RdKafkaTopicTest.php             | 4 ++--
 5 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/Tests/Spec/RdKafkaConnectionFactoryTest.php b/Tests/Spec/RdKafkaConnectionFactoryTest.php
index 1132e39..3327c95 100644
--- a/Tests/Spec/RdKafkaConnectionFactoryTest.php
+++ b/Tests/Spec/RdKafkaConnectionFactoryTest.php
@@ -3,12 +3,12 @@
 namespace Enqueue\RdKafka\Tests\Spec;
 
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Interop\Queue\Spec\PsrConnectionFactorySpec;
+use Interop\Queue\Spec\ConnectionFactorySpec;
 
 /**
  * @group rdkafka
  */
-class RdKafkaConnectionFactoryTest extends PsrConnectionFactorySpec
+class RdKafkaConnectionFactoryTest extends ConnectionFactorySpec
 {
     protected function createConnectionFactory()
     {
diff --git a/Tests/Spec/RdKafkaContextTest.php b/Tests/Spec/RdKafkaContextTest.php
index c99c9f0..fe96256 100644
--- a/Tests/Spec/RdKafkaContextTest.php
+++ b/Tests/Spec/RdKafkaContextTest.php
@@ -3,12 +3,12 @@
 namespace Enqueue\RdKafka\Tests\Spec;
 
 use Enqueue\RdKafka\RdKafkaContext;
-use Interop\Queue\Spec\PsrContextSpec;
+use Interop\Queue\Spec\ContextSpec;
 
 /**
  * @group rdkafka
  */
-class RdKafkaContextTest extends PsrContextSpec
+class RdKafkaContextTest extends ContextSpec
 {
     protected function createContext()
     {
diff --git a/Tests/Spec/RdKafkaMessageTest.php b/Tests/Spec/RdKafkaMessageTest.php
index 6cff666..a29780b 100644
--- a/Tests/Spec/RdKafkaMessageTest.php
+++ b/Tests/Spec/RdKafkaMessageTest.php
@@ -3,12 +3,12 @@
 namespace Enqueue\RdKafka\Tests\Spec;
 
 use Enqueue\RdKafka\RdKafkaMessage;
-use Interop\Queue\Spec\PsrMessageSpec;
+use Interop\Queue\Spec\MessageSpec;
 
 /**
  * @group rdkafka
  */
-class RdKafkaMessageTest extends PsrMessageSpec
+class RdKafkaMessageTest extends MessageSpec
 {
     /**
      * {@inheritdoc}
diff --git a/Tests/Spec/RdKafkaQueueTest.php b/Tests/Spec/RdKafkaQueueTest.php
index e6bf401..93bc7ac 100644
--- a/Tests/Spec/RdKafkaQueueTest.php
+++ b/Tests/Spec/RdKafkaQueueTest.php
@@ -3,12 +3,12 @@
 namespace Enqueue\RdKafka\Tests\Spec;
 
 use Enqueue\RdKafka\RdKafkaTopic;
-use Interop\Queue\Spec\PsrQueueSpec;
+use Interop\Queue\Spec\QueueSpec;
 
 /**
  * @group rdkafka
  */
-class RdKafkaQueueTest extends PsrQueueSpec
+class RdKafkaQueueTest extends QueueSpec
 {
     protected function createQueue()
     {
diff --git a/Tests/Spec/RdKafkaTopicTest.php b/Tests/Spec/RdKafkaTopicTest.php
index a5c61ad..26aacc7 100644
--- a/Tests/Spec/RdKafkaTopicTest.php
+++ b/Tests/Spec/RdKafkaTopicTest.php
@@ -3,12 +3,12 @@
 namespace Enqueue\RdKafka\Tests\Spec;
 
 use Enqueue\RdKafka\RdKafkaTopic;
-use Interop\Queue\Spec\PsrTopicSpec;
+use Interop\Queue\Spec\TopicSpec;
 
 /**
  * @group rdkafka
  */
-class RdKafkaTopicTest extends PsrTopicSpec
+class RdKafkaTopicTest extends TopicSpec
 {
     /**
      * {@inheritdoc}

From 32a691531b2d6bb8860b130cfaf8120ae824b620 Mon Sep 17 00:00:00 2001
From: Alexis DUMAS <adumas37@outlook.com>
Date: Tue, 16 Oct 2018 14:08:10 +0200
Subject: [PATCH 029/101] Fixing Kafka Default configuration merge

---
 RdKafkaConnectionFactory.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/RdKafkaConnectionFactory.php b/RdKafkaConnectionFactory.php
index a3fdbc1..214552d 100644
--- a/RdKafkaConnectionFactory.php
+++ b/RdKafkaConnectionFactory.php
@@ -44,7 +44,7 @@ public function __construct($config = 'kafka:')
             throw new \LogicException('The config must be either an array of options, a DSN string or null');
         }
 
-        $this->config = array_replace($this->defaultConfig(), $config);
+        $this->config = array_replace_recursive($this->defaultConfig(), $config);
     }
 
     /**

From e17743abe4121bf936bff6f86dd48688dd3d1734 Mon Sep 17 00:00:00 2001
From: adumas37 <adumas37@outlook.com>
Date: Tue, 16 Oct 2018 14:44:10 +0200
Subject: [PATCH 030/101] Adding test for recursive configuration merging

---
 Tests/RdKafkaConnectionFactoryTest.php | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Tests/RdKafkaConnectionFactoryTest.php b/Tests/RdKafkaConnectionFactoryTest.php
index 01f653e..47fdc17 100644
--- a/Tests/RdKafkaConnectionFactoryTest.php
+++ b/Tests/RdKafkaConnectionFactoryTest.php
@@ -107,5 +107,19 @@ public static function provideConfigs()
                 ],
             ],
         ];
+
+        yield [
+            [
+                'global' => [
+                    'group.id' => 'group-id',
+                ],
+            ],
+            [
+                'global' => [
+                    'metadata.broker.list' => 'localhost:9092',
+                    'group.id' => 'group-id',
+                ],
+            ],
+        ];
     }
 }

From 3debee61ed6953c8d363435e07c25c7908da1edd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <3183926+Steveb-p@users.noreply.github.com>
Date: Mon, 22 Oct 2018 08:35:47 +0200
Subject: [PATCH 031/101] Backport changes to topic subscription

Allow either to use assign or subscribe

With `assign` it is not possible to use the rebalancing from Kafka. So if no offset is set we use `subscribe` and rebalancing is possible and otherwise if a offset is set we are using assign.

@see commit d2fa17881fcc2c523b8277f3ba30d1d9caeff667
@see https://github.com/php-enqueue/enqueue-dev/issues/570
---
 RdKafkaConsumer.php | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 9d30be2..37bdb8d 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -99,11 +99,15 @@ public function getQueue()
     public function receive($timeout = 0)
     {
         if (false == $this->subscribed) {
-            $this->consumer->assign([new TopicPartition(
-                $this->getQueue()->getQueueName(),
-                $this->getQueue()->getPartition(),
-                $this->offset
-            )]);
+            if (null === $this->offset) {
+                $this->consumer->subscribe([$this->getQueue()->getQueueName()]);
+            } else {
+                $this->consumer->assign([new TopicPartition(
+                    $this->getQueue()->getQueueName(),
+                    $this->getQueue()->getPartition(),
+                    $this->offset
+                )]);
+            }
 
             $this->subscribed = true;
         }

From 1a09fae1aca52d342bee4b4b429192027d0d64db Mon Sep 17 00:00:00 2001
From: webmake <webmake@users.noreply.github.com>
Date: Sat, 27 Oct 2018 10:56:55 +0000
Subject: [PATCH 032/101] Do not export non source files

---
 .gitattributes | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .gitattributes

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bdf2dcb
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+/Tests export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.travis.yml export-ignore
+phpunit.xml.dist export-ignore

From 4e676032b4403b21e52c0cd75845bcda74312ed0 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 2 Nov 2018 09:33:22 +0200
Subject: [PATCH 033/101] fix tests 08

---
 Tests/RdKafkaConsumerTest.php | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 77a949c..e79a51d 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -50,7 +50,7 @@ public function testShouldReceiveFromQueueAndReturnNullIfNoMessageInQueue()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->once())
@@ -94,6 +94,8 @@ public function testShouldPassProperlyConfiguredTopicPartitionOnAssign()
             $this->createSerializerMock()
         );
 
+        $consumer->setOffset(12345);
+
         $consumer->receive(1000);
         $consumer->receive(1000);
         $consumer->receive(1000);
@@ -109,7 +111,7 @@ public function testShouldSubscribeOnFirstReceiveOnly()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->any())
@@ -139,7 +141,7 @@ public function testThrowOnOffsetChangeAfterSubscribing()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->any())
@@ -174,7 +176,7 @@ public function testShouldReceiveFromQueueAndReturnMessageIfMessageInQueue()
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
             ->expects($this->once())
-            ->method('assign')
+            ->method('subscribe')
         ;
         $kafkaConsumer
             ->expects($this->once())

From 8e492f9f629ff4ba87b1f974123e8d0be53e1cfd Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 6 Nov 2018 18:06:13 +0200
Subject: [PATCH 034/101] [doc][skip ci] Add sponsoring section.

---
 README.md | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/README.md b/README.md
index d0e0bed..c21edbb 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,12 @@
+<h2 align="center">Supporting Enqueue</h2>
+
+Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:
+
+- [Become a sponsor](https://www.patreon.com/makasim).
+- [Become our client](http://forma-pro.com/)
+
+---
+
 # RdKafka Transport
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)

From 998b889ac9d21585c1c3da427a5827189a8e2562 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 6 Nov 2018 18:12:08 +0200
Subject: [PATCH 035/101] [docs][skip ci] Add sponsoring to docs.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index c21edbb..cb0f9c4 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:
 
-- [Become a sponsor](https://www.patreon.com/makasim).
+- [Become a sponsor](https://www.patreon.com/makasim)
 - [Become our client](http://forma-pro.com/)
 
 ---

From 77a64242769223bd8b23f5c972a338c2ab317fc1 Mon Sep 17 00:00:00 2001
From: Frank Giesecke <frank.giesecke@final-gene.de>
Date: Thu, 15 Nov 2018 13:27:15 +0100
Subject: [PATCH 036/101] Fix wrong exceptions in transports

The `setPriority` method have to throw a PriorityNotSupportedException
if the feature is not supported.
---
 RdKafkaProducer.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index feb40c4..3f6d192 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -7,6 +7,7 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Exception\InvalidDestinationException;
 use Interop\Queue\Exception\InvalidMessageException;
+use Interop\Queue\Exception\PriorityNotSupportedException;
 use Interop\Queue\Message;
 use Interop\Queue\Producer;
 use RdKafka\Producer as VendorProducer;
@@ -70,7 +71,7 @@ public function setPriority(int $priority = null): Producer
             return $this;
         }
 
-        throw new \LogicException('Not implemented');
+        throw PriorityNotSupportedException::providerDoestNotSupportIt();
     }
 
     public function getPriority(): ?int

From d41b5b59955e92016e6c2c748e29cc1707e2623b Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 16 Nov 2018 13:52:04 +0200
Subject: [PATCH 037/101] multi client configuration

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 18374e1..9ec0d2f 100644
--- a/composer.json
+++ b/composer.json
@@ -8,13 +8,13 @@
     "require": {
         "php": "^7.1.3",
         "ext-rdkafka": "^3.0.3",
-        "queue-interop/queue-interop": "0.7.x-dev"
+        "queue-interop/queue-interop": "^0.7"
     },
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",
         "enqueue/test": "0.9.x-dev",
         "enqueue/null": "0.9.x-dev",
-        "queue-interop/queue-spec": "0.6.x-dev",
+        "queue-interop/queue-spec": "^0.6",
         "kwn/php-rdkafka-stubs": "^1.0.2"
     },
     "support": {

From 0ffa50036de376b4ad6856811a41b826d5402b71 Mon Sep 17 00:00:00 2001
From: webmake <nyrkao@gmail.com>
Date: Sat, 27 Oct 2018 13:56:55 +0300
Subject: [PATCH 038/101] Do not export non source files

(cherry picked from commit 404fae1)
---
 .gitattributes | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .gitattributes

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bdf2dcb
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+/Tests export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.travis.yml export-ignore
+phpunit.xml.dist export-ignore

From 6a21f5169a18def9be3cb3da1fcfe5a4b99fa9d5 Mon Sep 17 00:00:00 2001
From: Kotlyar Maksim <kotlyar.maksim@gmail.com>
Date: Thu, 13 Dec 2018 17:31:01 +0200
Subject: [PATCH 039/101] Allow queue interop 0.8 in packages

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 9ec0d2f..e0d55b3 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.1.3",
         "ext-rdkafka": "^3.0.3",
-        "queue-interop/queue-interop": "^0.7"
+        "queue-interop/queue-interop": "^0.7|^0.8"
     },
     "require-dev": {
         "phpunit/phpunit": "~5.4.0",

From abf4cecbd40857706dbacbfac411c221f31791ac Mon Sep 17 00:00:00 2001
From: "f.kulakov" <f.kulakov@corp.mail.ru>
Date: Wed, 13 Mar 2019 10:42:04 +0300
Subject: [PATCH 040/101] stats_cb support

---
 RdKafkaContext.php | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index cfe8e0e..22d4a8e 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -186,6 +186,10 @@ private function getConf(): Conf
                 $this->conf->setRebalanceCb($this->config['rebalance_cb']);
             }
 
+            if (isset($this->config['stats_cb'])) {
+                $this->conf->setStatsCb($this->config['stats_cb']);
+            }
+
             $this->conf->setDefaultTopicConf($topicConf);
         }
 

From caf88f31acdd465ae8a6513ba5332025f0a25594 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Fri, 29 Mar 2019 00:04:50 +0100
Subject: [PATCH 041/101] Set commit_async as true by default for Kafka

Provide addition configuration documentation for integrating with Symfony bundle

Fixes #580
---
 RdKafkaConsumer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 5e89f3d..9522b32 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -51,7 +51,7 @@ public function __construct(KafkaConsumer $consumer, RdKafkaContext $context, Rd
         $this->context = $context;
         $this->topic = $topic;
         $this->subscribed = false;
-        $this->commitAsync = false;
+        $this->commitAsync = true;
 
         $this->setSerializer($serializer);
     }

From 93db42f6afde70884d91a6894574a611aaec6fae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= <pawel@niedzielski.biz.pl>
Date: Mon, 13 May 2019 16:50:04 +0200
Subject: [PATCH 042/101] Fix consumption errors in kafka against recent
 versions in librdkafka/phprdkafka

Recent versions may no longer return error message when reaching end of partition
(RD_KAFKA_RESP_ERR__PARTITION_EOF - due to change in `enable.partition.eof`).
This is especially true for librdkafka >=1.0.0.
see https://github.com/arnaud-lb/php-rdkafka/issues/220#issuecomment-491162230
---
 RdKafkaConsumer.php | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 9522b32..d7016a9 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -155,6 +155,10 @@ private function doReceive(int $timeout): ?RdKafkaMessage
     {
         $kafkaMessage = $this->consumer->consume($timeout);
 
+        if (null === $kafkaMessage) {
+            return null;
+        }
+
         switch ($kafkaMessage->err) {
             case RD_KAFKA_RESP_ERR__PARTITION_EOF:
             case RD_KAFKA_RESP_ERR__TIMED_OUT:

From 0f97f676074c20c28c339579f4ce1e654b52c857 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= <pawel@niedzielski.biz.pl>
Date: Mon, 27 May 2019 15:12:22 +0200
Subject: [PATCH 043/101] Prefer github pages in packages' readme files

---
 README.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index cb0f9c4..1502f35 100644
--- a/README.md
+++ b/README.md
@@ -13,20 +13,20 @@ Enqueue is an MIT-licensed open source project with its ongoing development made
 [![Build Status](https://travis-ci.org/php-enqueue/rdkafka.png?branch=master)](https://travis-ci.org/php-enqueue/rdkafka)
 [![Total Downloads](https://poser.pugx.org/enqueue/rdkafka/d/total.png)](https://packagist.org/packages/enqueue/rdkafka)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/rdkafka/version.png)](https://packagist.org/packages/enqueue/rdkafka)
- 
-This is an implementation of Queue Interop specification. It allows you to send and consume message via Kafka protocol.  
+
+This is an implementation of Queue Interop specification. It allows you to send and consume message via Kafka protocol.
 
 ## Resources
 
 * [Site](https://enqueue.forma-pro.com/)
-* [Documentation](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/index.md)
+* [Documentation](https://php-enqueue.github.io/transport/kafka/)
 * [Questions](https://gitter.im/php-enqueue/Lobby)
 * [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)
 
 ## Developed by Forma-Pro
 
-Forma-Pro is a full stack development company which interests also spread to open source development. 
-Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience. 
+Forma-Pro is a full stack development company which interests also spread to open source development.
+Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience.
 Our main specialization is Symfony framework based solution, but we are always looking to the technologies that allow us to do our job the best way. We are committed to creating solutions that revolutionize the way how things are developed in aspects of architecture & scalability.
 
 If you have any questions and inquires about our open source development, this product particularly or any other matter feel free to contact at opensource@forma-pro.com

From 563ecc98e01a0ad1ecb10cd4d4d4c4c43f81b9ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Fri, 7 Jun 2019 02:40:15 +0200
Subject: [PATCH 044/101] Allow reading headers from Kafka Message headers

A simpler part of #843, adds just the consumption part. This makes
headers accessible in the consumer in case another application makes
use of those, be it PHP or not.
---
 RdKafkaConsumer.php | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index d7016a9..241ee38 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -169,6 +169,12 @@ private function doReceive(int $timeout): ?RdKafkaMessage
                 $message->setPartition($kafkaMessage->partition);
                 $message->setKafkaMessage($kafkaMessage);
 
+                // Merge headers passed from Kafka with possible earlier serialized payload headers. Prefer Kafka's.
+                // Note: Requires phprdkafka >= 3.1.0
+                if (isset($kafkaMessage->headers)) {
+                    $message->setHeaders(array_merge($message->getHeaders(), $kafkaMessage->headers));
+                }
+
                 return $message;
             default:
                 throw new \LogicException($kafkaMessage->errstr(), $kafkaMessage->err);

From 61a537b786511488b64a23c25b2cccb5816e48af Mon Sep 17 00:00:00 2001
From: Andrea Quintino <andreaquin1990@gmail.com>
Date: Tue, 10 Sep 2019 22:56:31 +0200
Subject: [PATCH 045/101] Create singleton to avoid consumer duplication

---
 RdKafkaContext.php           | 31 +++++++++++++++++++++----------
 Tests/RdKafkaContextTest.php | 23 +++++++++++++++++++++++
 2 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 22d4a8e..ace937c 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -45,6 +45,11 @@ class RdKafkaContext implements Context
      */
     private $kafkaConsumers;
 
+    /**
+     * @var RdKafkaConsumer[]
+     */
+    private $rdKafkaConsumers;
+
     /**
      * @param array $config
      */
@@ -102,20 +107,26 @@ public function createConsumer(Destination $destination): Consumer
     {
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
 
-        $this->kafkaConsumers[] = $kafkaConsumer = new KafkaConsumer($this->getConf());
+        $queueName = $destination->getQueueName();
 
-        $consumer = new RdKafkaConsumer(
-            $kafkaConsumer,
-            $this,
-            $destination,
-            $this->getSerializer()
-        );
+        if (!isset($this->rdKafkaConsumers[$queueName])) {
+            $this->kafkaConsumers[] = $kafkaConsumer = new KafkaConsumer($this->getConf());
+
+            $consumer = new RdKafkaConsumer(
+                $kafkaConsumer,
+                $this,
+                $destination,
+                $this->getSerializer()
+            );
+
+            if (isset($this->config['commit_async'])) {
+                $consumer->setCommitAsync($this->config['commit_async']);
+            }
 
-        if (isset($this->config['commit_async'])) {
-            $consumer->setCommitAsync($this->config['commit_async']);
+            $this->rdKafkaConsumers[$queueName] = $consumer;
         }
 
-        return $consumer;
+        return $this->rdKafkaConsumers[$queueName];
     }
 
     public function close(): void
diff --git a/Tests/RdKafkaContextTest.php b/Tests/RdKafkaContextTest.php
index e787efb..48900f4 100644
--- a/Tests/RdKafkaContextTest.php
+++ b/Tests/RdKafkaContextTest.php
@@ -69,4 +69,27 @@ public function testShouldInjectItsSerializerToConsumer()
 
         $this->assertSame($context->getSerializer(), $producer->getSerializer());
     }
+
+    public function testShouldNotCreateConsumerTwice()
+    {
+        $context = new RdKafkaContext([]);
+        $queue = $context->createQueue('aQueue');
+
+        $consumer = $context->createConsumer($queue);
+        $consumer2 = $context->createConsumer($queue);
+
+        $this->assertSame($consumer, $consumer2);
+    }
+
+    public function testShouldCreateTwoConsumers()
+    {
+        $context = new RdKafkaContext([]);
+        $queueA = $context->createQueue('aQueue');
+        $queueB = $context->createQueue('aQueue');
+
+        $consumer = $context->createConsumer($queueA);
+        $consumer2 = $context->createConsumer($queueB);
+
+        $this->assertNotSame($consumer, $consumer2);
+    }
 }

From b098f054684d0bb7d8da5acc5199384965e5ef2d Mon Sep 17 00:00:00 2001
From: dirk39 <andreaquin1990@gmail.com>
Date: Wed, 11 Sep 2019 11:57:22 +0200
Subject: [PATCH 046/101] update rdKafka test

---
 Tests/RdKafkaContextTest.php | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/Tests/RdKafkaContextTest.php b/Tests/RdKafkaContextTest.php
index 48900f4..227eea7 100644
--- a/Tests/RdKafkaContextTest.php
+++ b/Tests/RdKafkaContextTest.php
@@ -72,7 +72,9 @@ public function testShouldInjectItsSerializerToConsumer()
 
     public function testShouldNotCreateConsumerTwice()
     {
-        $context = new RdKafkaContext([]);
+        $context = new RdKafkaContext(['global' => [
+            'group.id' => uniqid('', true),
+        ]]);
         $queue = $context->createQueue('aQueue');
 
         $consumer = $context->createConsumer($queue);
@@ -83,9 +85,11 @@ public function testShouldNotCreateConsumerTwice()
 
     public function testShouldCreateTwoConsumers()
     {
-        $context = new RdKafkaContext([]);
+        $context = new RdKafkaContext(['global' => [
+            'group.id' => uniqid('', true),
+        ]]);
         $queueA = $context->createQueue('aQueue');
-        $queueB = $context->createQueue('aQueue');
+        $queueB = $context->createQueue('bQueue');
 
         $consumer = $context->createConsumer($queueA);
         $consumer2 = $context->createConsumer($queueB);

From ea1f8c5860d6fa9ca3eab355cafb1f62973810cd Mon Sep 17 00:00:00 2001
From: dirk39 <andreaquin1990@gmail.com>
Date: Thu, 12 Sep 2019 09:31:03 +0200
Subject: [PATCH 047/101] clean rdKafkaConsumers list when context is close

---
 RdKafkaContext.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index ace937c..cd33795 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -133,6 +133,7 @@ public function close(): void
     {
         $kafkaConsumers = $this->kafkaConsumers;
         $this->kafkaConsumers = [];
+        $this->rdKafkaConsumers = [];
 
         foreach ($kafkaConsumers as $kafkaConsumer) {
             $kafkaConsumer->unsubscribe();

From f6052ccc1c1e2a21d5c101f53baafd2c79c598e7 Mon Sep 17 00:00:00 2001
From: dirk39 <andreaquin1990@gmail.com>
Date: Thu, 12 Sep 2019 10:08:00 +0200
Subject: [PATCH 048/101] initialized rdKafkaConsumers attribute

---
 RdKafkaContext.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index cd33795..cda5fc9 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -57,6 +57,7 @@ public function __construct(array $config)
     {
         $this->config = $config;
         $this->kafkaConsumers = [];
+        $this->rdKafkaConsumers = [];
 
         $this->setSerializer(new JsonSerializer());
     }

From e0f9eecd0d3eb5f206c43a0f1dae7064272cae74 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Wed, 25 Sep 2019 08:53:05 +0200
Subject: [PATCH 049/101] Use producev to support headers in kafka

---
 RdKafkaProducer.php | 2 +-
 composer.json       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 3f6d192..facdf88 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -42,7 +42,7 @@ public function send(Destination $destination, Message $message): void
         $key = $message->getKey() ?: $destination->getKey() ?: null;
 
         $topic = $this->producer->newTopic($destination->getTopicName(), $destination->getConf());
-        $topic->produce($partition, 0 /* must be 0 */, $payload, $key);
+        $topic->producev($partition, 0 /* must be 0 */, $payload, $key, $message->getHeaders());
     }
 
     /**
diff --git a/composer.json b/composer.json
index e0d55b3..d82de49 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "ext-rdkafka": "^3.0.3",
+        "ext-rdkafka": "^3.1.0",
         "queue-interop/queue-interop": "^0.7|^0.8"
     },
     "require-dev": {

From cb369b4d4cadb5bc8c1301d2bef95e267bb7e307 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Wed, 25 Sep 2019 09:13:26 +0200
Subject: [PATCH 050/101] Fix tests

---
 Tests/RdKafkaProducerTest.php | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index be5b94a..1f8e7db 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -45,18 +45,20 @@ public function testThrowIfMessageInvalid()
 
     public function testShouldUseSerializerToEncodeMessageAndPutToExpectedTube()
     {
-        $message = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], ['bar' => 'barVal']);
+        $messageHeaders = ['bar' => 'barVal'];
+        $message = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], $messageHeaders);
         $message->setKey('key');
 
         $kafkaTopic = $this->createKafkaTopicMock();
         $kafkaTopic
             ->expects($this->once())
-            ->method('produce')
+            ->method('producev')
             ->with(
                 RD_KAFKA_PARTITION_UA,
                 0,
                 'theSerializedMessage',
-                'key'
+                'key',
+                $messageHeaders
             )
         ;
 
@@ -87,7 +89,7 @@ public function testShouldPassNullAsTopicConfigIfNotSetOnTopic()
         $kafkaTopic = $this->createKafkaTopicMock();
         $kafkaTopic
             ->expects($this->once())
-            ->method('produce')
+            ->method('producev')
         ;
 
         $kafkaProducer = $this->createKafkaProducerMock();
@@ -123,7 +125,7 @@ public function testShouldPassCustomConfAsTopicConfigIfSetOnTopic()
         $kafkaTopic = $this->createKafkaTopicMock();
         $kafkaTopic
             ->expects($this->once())
-            ->method('produce')
+            ->method('producev')
         ;
 
         $kafkaProducer = $this->createKafkaProducerMock();
@@ -165,13 +167,14 @@ public function testShouldAllowGetPreviouslySetSerializer()
 
     public function testShouldAllowSerializersToSerializeKeys()
     {
-        $message = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], ['bar' => 'barVal']);
+        $messageHeaders = ['bar' => 'barVal'];
+        $message = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], $messageHeaders);
         $message->setKey('key');
 
         $kafkaTopic = $this->createKafkaTopicMock();
         $kafkaTopic
             ->expects($this->once())
-            ->method('produce')
+            ->method('producev')
             ->with(
                 RD_KAFKA_PARTITION_UA,
                 0,

From 8a2705a4ea76e6c6d97336d6e7a3ad593493a992 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Wed, 25 Sep 2019 11:15:29 +0200
Subject: [PATCH 051/101] Maintain backward compatibility

---
 RdKafkaContext.php  | 11 +++++++++++
 RdKafkaProducer.php | 19 ++++++++++++++++++-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 22d4a8e..9501252 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -138,6 +138,17 @@ public function purgeQueue(Queue $queue): void
         throw PurgeQueueNotSupportedException::providerDoestNotSupportIt();
     }
 
+    public static function getLibrdKafkaVersion(): string
+    {
+        if (!defined('RD_KAFKA_VERSION')) {
+            throw new \RuntimeException('RD_KAFKA_VERSION constant is not defined. Phprdkafka is probably not installed');
+        }
+        $major = (RD_KAFKA_VERSION & 0xFF000000) >> 24;
+        $minor = (RD_KAFKA_VERSION & 0x00FF0000) >> 16;
+        $patch = (RD_KAFKA_VERSION & 0x0000FF00) >> 8;
+        return "$major.$minor.$patch";
+    }
+
     private function getProducer(): VendorProducer
     {
         if (null === $this->producer) {
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index facdf88..a6135c5 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -42,7 +42,24 @@ public function send(Destination $destination, Message $message): void
         $key = $message->getKey() ?: $destination->getKey() ?: null;
 
         $topic = $this->producer->newTopic($destination->getTopicName(), $destination->getConf());
-        $topic->producev($partition, 0 /* must be 0 */, $payload, $key, $message->getHeaders());
+
+        // Note: Topic::producev method exists in phprdkafka > 3.1.0
+        // Headers in payload are maintained for backwards compatibility with apps that might run on lower phprdkafka version
+        if (method_exists($topic, 'producev')) {
+            // Phprdkafka <= 3.1.0 will fail calling `producev` on librdkafka >= 1.0.0 causing segfault
+            if (version_compare(RdKafkaContext::getLibrdKafkaVersion(), '1.0.0', '>=')
+                && version_compare(phpversion('rdkafka'), '3.1.0', '<=')) {
+                trigger_error(
+                    'Phprdkafka <= 3.1.0 is incompatible with librdkafka 1.0.0 when calling `producev`',
+                    E_USER_WARNING
+                );
+            } else {
+                $topic->producev($partition, 0 /* must be 0 */, $payload, $key, $message->getHeaders());
+                return;
+            }
+        }
+
+        $topic->produce($partition, 0 /* must be 0 */, $payload, $key);
     }
 
     /**

From 264455f0e0d7be184abddb968025875aa1f2e7b8 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Wed, 25 Sep 2019 11:23:09 +0200
Subject: [PATCH 052/101] Update warning message

---
 RdKafkaProducer.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index a6135c5..747c3ce 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -50,7 +50,8 @@ public function send(Destination $destination, Message $message): void
             if (version_compare(RdKafkaContext::getLibrdKafkaVersion(), '1.0.0', '>=')
                 && version_compare(phpversion('rdkafka'), '3.1.0', '<=')) {
                 trigger_error(
-                    'Phprdkafka <= 3.1.0 is incompatible with librdkafka 1.0.0 when calling `producev`',
+                    'Phprdkafka <= 3.1.0 is incompatible with librdkafka 1.0.0 when calling `producev`. ' .
+                    'Falling back to `produce` (without message headers) instead.',
                     E_USER_WARNING
                 );
             } else {

From c6c644289f708e9b44763fe4616d42bde3b72915 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Wed, 25 Sep 2019 11:34:08 +0200
Subject: [PATCH 053/101] =?UTF-8?q?CS=C2=A0fixes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 RdKafkaContext.php  | 1 +
 RdKafkaProducer.php | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 9501252..a82770a 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -146,6 +146,7 @@ public static function getLibrdKafkaVersion(): string
         $major = (RD_KAFKA_VERSION & 0xFF000000) >> 24;
         $minor = (RD_KAFKA_VERSION & 0x00FF0000) >> 16;
         $patch = (RD_KAFKA_VERSION & 0x0000FF00) >> 8;
+
         return "$major.$minor.$patch";
     }
 
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 747c3ce..77ec911 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -50,12 +50,13 @@ public function send(Destination $destination, Message $message): void
             if (version_compare(RdKafkaContext::getLibrdKafkaVersion(), '1.0.0', '>=')
                 && version_compare(phpversion('rdkafka'), '3.1.0', '<=')) {
                 trigger_error(
-                    'Phprdkafka <= 3.1.0 is incompatible with librdkafka 1.0.0 when calling `producev`. ' .
+                    'Phprdkafka <= 3.1.0 is incompatible with librdkafka 1.0.0 when calling `producev`. '.
                     'Falling back to `produce` (without message headers) instead.',
                     E_USER_WARNING
                 );
             } else {
                 $topic->producev($partition, 0 /* must be 0 */, $payload, $key, $message->getHeaders());
+
                 return;
             }
         }

From 57b5a75eceaed5e04323e54055d8db256f8f5a6f Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Wed, 25 Sep 2019 13:33:42 +0200
Subject: [PATCH 054/101] Revert version bump of rdkafka dep

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index d82de49..e0d55b3 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "ext-rdkafka": "^3.1.0",
+        "ext-rdkafka": "^3.0.3",
         "queue-interop/queue-interop": "^0.7|^0.8"
     },
     "require-dev": {

From 84f74654beee86342f65b5b7524c81fdd9c1d733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= <pawel@niedzielski.biz.pl>
Date: Wed, 20 Nov 2019 11:42:01 +0100
Subject: [PATCH 055/101] Prefer early returns in consumer code

---
 RdKafkaConsumer.php | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 241ee38..88b05ad 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -99,18 +99,17 @@ public function receive(int $timeout = 0): ?Message
             $this->subscribed = true;
         }
 
-        $message = null;
         if ($timeout > 0) {
-            $message = $this->doReceive($timeout);
-        } else {
-            while (true) {
-                if ($message = $this->doReceive(500)) {
-                    break;
-                }
+            return $this->doReceive($timeout);
+        }
+
+        while (true) {
+            if ($message = $this->doReceive(500)) {
+                return $message;
             }
         }
 
-        return $message;
+        return null;
     }
 
     /**
@@ -162,7 +161,7 @@ private function doReceive(int $timeout): ?RdKafkaMessage
         switch ($kafkaMessage->err) {
             case RD_KAFKA_RESP_ERR__PARTITION_EOF:
             case RD_KAFKA_RESP_ERR__TIMED_OUT:
-                break;
+                return null;
             case RD_KAFKA_RESP_ERR_NO_ERROR:
                 $message = $this->serializer->toMessage($kafkaMessage->payload);
                 $message->setKey($kafkaMessage->key);
@@ -180,7 +179,5 @@ private function doReceive(int $timeout): ?RdKafkaMessage
                 throw new \LogicException($kafkaMessage->errstr(), $kafkaMessage->err);
                 break;
         }
-
-        return null;
     }
 }

From a59a3a3b06792098b19947befbf96c4c86ce4af5 Mon Sep 17 00:00:00 2001
From: Mathieu Lemoine <mathieu@doctoome.com>
Date: Wed, 11 Dec 2019 16:32:12 +0100
Subject: [PATCH 056/101] Reduced dependency to voryx/Thruway

---
 Tests/RdKafkaConsumerTest.php | 6 +++---
 Tests/RdKafkaProducerTest.php | 6 +++---
 phpunit.xml.dist              | 1 -
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index e35d97f..b40916c 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -272,7 +272,7 @@ public function testShouldAllowGetPreviouslySetSerializer()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|KafkaConsumer
+     * @return \PHPUnit\Framework\MockObject\MockObject|KafkaConsumer
      */
     private function createKafkaConsumerMock()
     {
@@ -280,7 +280,7 @@ private function createKafkaConsumerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|RdKafkaContext
+     * @return \PHPUnit\Framework\MockObject\MockObject|RdKafkaContext
      */
     private function createContextMock()
     {
@@ -288,7 +288,7 @@ private function createContextMock()
     }
 
     /**
-     * @return Serializer|\PHPUnit_Framework_MockObject_MockObject|Serializer
+     * @return Serializer|\PHPUnit\Framework\MockObject\MockObject|Serializer
      */
     private function createSerializerMock()
     {
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 1f8e7db..7cc601a 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -206,7 +206,7 @@ public function testShouldAllowSerializersToSerializeKeys()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerTopic
+     * @return \PHPUnit\Framework\MockObject\MockObject|ProducerTopic
      */
     private function createKafkaTopicMock()
     {
@@ -214,7 +214,7 @@ private function createKafkaTopicMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Producer
+     * @return \PHPUnit\Framework\MockObject\MockObject|Producer
      */
     private function createKafkaProducerMock()
     {
@@ -222,7 +222,7 @@ private function createKafkaProducerMock()
     }
 
     /**
-     * @return Serializer|\PHPUnit_Framework_MockObject_MockObject|Serializer
+     * @return Serializer|\PHPUnit\Framework\MockObject\MockObject|Serializer
      */
     private function createSerializerMock()
     {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index d899fd6..0f9bb33 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,7 +8,6 @@
          convertWarningsToExceptions="true"
          processIsolation="false"
          stopOnFailure="false"
-         syntaxCheck="false"
          bootstrap="./vendor/autoload.php"
         >
 

From 8e0464a1a23ab5a170d18cd54d5aca1929ad1252 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 19 Dec 2019 09:01:09 +0200
Subject: [PATCH 057/101] master is 0.10

---
 composer.json | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/composer.json b/composer.json
index e0d55b3..520c05a 100644
--- a/composer.json
+++ b/composer.json
@@ -8,12 +8,12 @@
     "require": {
         "php": "^7.1.3",
         "ext-rdkafka": "^3.0.3",
-        "queue-interop/queue-interop": "^0.7|^0.8"
+        "queue-interop/queue-interop": "^0.8"
     },
     "require-dev": {
-        "phpunit/phpunit": "~5.4.0",
-        "enqueue/test": "0.9.x-dev",
-        "enqueue/null": "0.9.x-dev",
+        "phpunit/phpunit": "~7.5",
+        "enqueue/test": "0.10.x-dev",
+        "enqueue/null": "0.10.x-dev",
         "queue-interop/queue-spec": "^0.6",
         "kwn/php-rdkafka-stubs": "^1.0.2"
     },
@@ -44,7 +44,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "0.9.x-dev"
+            "dev-master": "0.10.x-dev"
         }
     }
 }

From 8735ca0eca02c154486802e6ed59af2a7022bda3 Mon Sep 17 00:00:00 2001
From: Rudy den Otter <rudy@keepgettingbetter.io>
Date: Fri, 10 Jan 2020 14:36:20 +0100
Subject: [PATCH 058/101] Catch consume error "Local: Broker transport failure"
 and continue consuming

---
 RdKafkaConsumer.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 88b05ad..6ec89aa 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -161,6 +161,7 @@ private function doReceive(int $timeout): ?RdKafkaMessage
         switch ($kafkaMessage->err) {
             case RD_KAFKA_RESP_ERR__PARTITION_EOF:
             case RD_KAFKA_RESP_ERR__TIMED_OUT:
+            case RD_KAFKA_RESP_ERR__TRANSPORT:
                 return null;
             case RD_KAFKA_RESP_ERR_NO_ERROR:
                 $message = $this->serializer->toMessage($kafkaMessage->payload);

From f63b08d35647aab31f8731293718931e08628b57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Mon, 27 Jan 2020 00:45:10 +0100
Subject: [PATCH 059/101] Add schema declaration to phpunit files. Remove
 parameters set to phpunit defaults.

---
 phpunit.xml.dist | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 0f9bb33..a20efef 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,15 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<phpunit backupGlobals="false"
-         backupStaticAttributes="false"
-         colors="true"
-         convertErrorsToExceptions="true"
-         convertNoticesToExceptions="true"
-         convertWarningsToExceptions="true"
-         processIsolation="false"
-         stopOnFailure="false"
-         bootstrap="./vendor/autoload.php"
-        >
+<phpunit
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
+    backupGlobals="false"
+    backupStaticAttributes="false"
+    colors="true"
+    convertDeprecationsToExceptions="false"
+    convertErrorsToExceptions="true"
+    convertNoticesToExceptions="true"
+    convertWarningsToExceptions="true"
+    processIsolation="false"
+    stopOnFailure="false"
+    bootstrap="./vendor/autoload.php"
+>
 
     <testsuites>
         <testsuite name="Message Queue RdKafka Transport Test Suite">

From 3012e9f9cf7fa7608a0ddc3b895f5d6a5cdec0cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Fri, 14 Feb 2020 20:59:18 +0100
Subject: [PATCH 060/101] Call flush method on shutdown / context closing

This allows compatibility with phprdkafka 4.0
---
 RdKafkaConnectionFactory.php                  |  1 +
 RdKafkaContext.php                            | 38 +++++++++++--------
 RdKafkaProducer.php                           |  8 ++++
 .../RdKafkaSendToAndReceiveFromTopicTest.php  | 11 ++++--
 composer.json                                 |  4 +-
 5 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/RdKafkaConnectionFactory.php b/RdKafkaConnectionFactory.php
index 8a634a1..a55c5c6 100644
--- a/RdKafkaConnectionFactory.php
+++ b/RdKafkaConnectionFactory.php
@@ -28,6 +28,7 @@ class RdKafkaConnectionFactory implements ConnectionFactory
      *     'partitioner' => null,                          // https://arnaud-lb.github.io/php-rdkafka/phpdoc/rdkafka-topicconf.setpartitioner.html
      *     'log_level' => null,
      *     'commit_async' => false,
+     *     'shutdown_timeout' => -1,                       // https://github.com/arnaud-lb/php-rdkafka#proper-shutdown
      * ]
      *
      * or
diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 6270b22..97223ba 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -36,7 +36,7 @@ class RdKafkaContext implements Context
     private $conf;
 
     /**
-     * @var Producer
+     * @var RdKafkaProducer
      */
     private $producer;
 
@@ -96,7 +96,23 @@ public function createTemporaryQueue(): Queue
      */
     public function createProducer(): Producer
     {
-        return new RdKafkaProducer($this->getProducer(), $this->getSerializer());
+        if (!isset($this->producer)) {
+            $producer = new VendorProducer($this->getConf());
+
+            if (isset($this->config['log_level'])) {
+                $producer->setLogLevel($this->config['log_level']);
+            }
+
+            $this->producer = new RdKafkaProducer($producer, $this->getSerializer());
+
+            // Once created RdKafkaProducer can store messages internally that need to be delivered before PHP shuts
+            // down. Otherwise, we are bound to lose messages in transit.
+            // Note that it is generally preferable to call "close" method explicitly before shutdown starts, since
+            // otherwise we might not have access to some objects, like database connections.
+            register_shutdown_function([$this->producer, 'flush'], $this->config['shutdown_timeout'] ?? -1);
+        }
+
+        return $this->producer;
     }
 
     /**
@@ -139,6 +155,11 @@ public function close(): void
         foreach ($kafkaConsumers as $kafkaConsumer) {
             $kafkaConsumer->unsubscribe();
         }
+
+        // Compatibility with phprdkafka 4.0.
+        if (isset($this->producer)) {
+            $this->producer->flush($this->config['shutdown_timeout'] ?? -1);
+        }
     }
 
     public function createSubscriptionConsumer(): SubscriptionConsumer
@@ -163,19 +184,6 @@ public static function getLibrdKafkaVersion(): string
         return "$major.$minor.$patch";
     }
 
-    private function getProducer(): VendorProducer
-    {
-        if (null === $this->producer) {
-            $this->producer = new VendorProducer($this->getConf());
-
-            if (isset($this->config['log_level'])) {
-                $this->producer->setLogLevel($this->config['log_level']);
-            }
-        }
-
-        return $this->producer;
-    }
-
     private function getConf(): Conf
     {
         if (null === $this->conf) {
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 77ec911..8464866 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -111,4 +111,12 @@ public function getTimeToLive(): ?int
     {
         return null;
     }
+
+    public function flush(int $timeout): void
+    {
+        // Flush method is exposed in phprdkafka 4.0
+        if (method_exists($this->producer, 'flush')) {
+            $this->producer->flush($timeout);
+        }
+    }
 }
diff --git a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
index 6604eb1..c755b87 100644
--- a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
+++ b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
@@ -19,9 +19,14 @@ public function test()
 
         $topic = $this->createTopic($context, uniqid('', true));
 
-        $consumer = $context->createConsumer($topic);
-
         $expectedBody = __CLASS__.time();
+        $producer = $context->createProducer();
+        $producer->send($topic, $context->createMessage($expectedBody));
+
+        // Calling close causes Producer to flush (wait for messages to be delivered to Kafka)
+        $context->close();
+
+        $consumer = $context->createConsumer($topic);
 
         $context->createProducer()->send($topic, $context->createMessage($expectedBody));
 
@@ -48,8 +53,6 @@ protected function createContext()
 
         $context = (new RdKafkaConnectionFactory($config))->createContext();
 
-        sleep(3);
-
         return $context;
     }
 }
diff --git a/composer.json b/composer.json
index 520c05a..b6b4865 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "ext-rdkafka": "^3.0.3",
+        "ext-rdkafka": "^3.0.3|^4.0",
         "queue-interop/queue-interop": "^0.8"
     },
     "require-dev": {
@@ -15,7 +15,7 @@
         "enqueue/test": "0.10.x-dev",
         "enqueue/null": "0.10.x-dev",
         "queue-interop/queue-spec": "^0.6",
-        "kwn/php-rdkafka-stubs": "^1.0.2"
+        "kwn/php-rdkafka-stubs": "^1.0.2 | ^2.0"
     },
     "support": {
         "email": "opensource@forma-pro.com",

From d9fcfe864dfc2665a7932c8bd71e80586fac22ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Fri, 14 Feb 2020 21:22:33 +0100
Subject: [PATCH 061/101] Fix style

---
 RdKafkaContext.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 97223ba..cc49150 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -50,9 +50,6 @@ class RdKafkaContext implements Context
      */
     private $rdKafkaConsumers;
 
-    /**
-     * @param array $config
-     */
     public function __construct(array $config)
     {
         $this->config = $config;

From 37b0b64eb7ef65523dbc2063d4f4dd5e18f568f5 Mon Sep 17 00:00:00 2001
From: Quentin Dreyer <quentin.dreyer@gmail.com>
Date: Wed, 24 Jun 2020 20:27:05 +0200
Subject: [PATCH 062/101] chore: add typehint to RdKafkaConsumer#getQueue

Prevent the vscode intelephense error : "Undefined method 'getPartition'."
---
 RdKafkaConsumer.php | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 6ec89aa..3aa568e 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -75,6 +75,9 @@ public function setOffset(int $offset = null): void
         $this->offset = $offset;
     }
 
+    /**
+     * @return RdKafkaTopic
+     */
     public function getQueue(): Queue
     {
         return $this->topic;

From 29af1b09ac444bcbb6c3e2bda8e04a57138f9e28 Mon Sep 17 00:00:00 2001
From: Witold Wasiczko <witold@wasiczko.pl>
Date: Thu, 30 Jul 2020 20:38:01 +0200
Subject: [PATCH 063/101] Add php 7.3 and 7.4 travis env to test

---
 .travis.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 5556e8f..8210653 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,8 @@ language: php
 php:
     - '7.1'
     - '7.2'
+    - '7.3'
+    - '7.4'
 
 cache:
     directories:

From e0bd708a914946df5af327a960c41e4a0d2c32dd Mon Sep 17 00:00:00 2001
From: nick <nickjobszh@gmail.com>
Date: Mon, 5 Oct 2020 19:25:45 +0200
Subject: [PATCH 064/101] remove topic conf, deprecated

---
 RdKafkaContext.php | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index cc49150..362037d 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -184,20 +184,18 @@ public static function getLibrdKafkaVersion(): string
     private function getConf(): Conf
     {
         if (null === $this->conf) {
-            $topicConf = new TopicConf();
+            $this->conf = new Conf();
 
             if (isset($this->config['topic']) && is_array($this->config['topic'])) {
                 foreach ($this->config['topic'] as $key => $value) {
-                    $topicConf->set($key, $value);
+                    $this->conf->set($key, $value);
                 }
             }
 
             if (isset($this->config['partitioner'])) {
-                $topicConf->setPartitioner($this->config['partitioner']);
+                $this->conf->set('partitioner', $this->config['partitioner']);
             }
 
-            $this->conf = new Conf();
-
             if (isset($this->config['global']) && is_array($this->config['global'])) {
                 foreach ($this->config['global'] as $key => $value) {
                     $this->conf->set($key, $value);
@@ -219,8 +217,6 @@ private function getConf(): Conf
             if (isset($this->config['stats_cb'])) {
                 $this->conf->setStatsCb($this->config['stats_cb']);
             }
-
-            $this->conf->setDefaultTopicConf($topicConf);
         }
 
         return $this->conf;

From c26eaba856f4755945f02b270c2a4f7a58b95df5 Mon Sep 17 00:00:00 2001
From: nick <nickjobszh@gmail.com>
Date: Mon, 5 Oct 2020 19:41:05 +0200
Subject: [PATCH 065/101] add non-blocking poll call to serve cb's

---
 RdKafkaProducer.php           |  2 ++
 Tests/RdKafkaProducerTest.php | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 8464866..2b987bb 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -56,12 +56,14 @@ public function send(Destination $destination, Message $message): void
                 );
             } else {
                 $topic->producev($partition, 0 /* must be 0 */, $payload, $key, $message->getHeaders());
+                $this->producer->poll(0);
 
                 return;
             }
         }
 
         $topic->produce($partition, 0 /* must be 0 */, $payload, $key);
+        $this->producer->poll(0);
     }
 
     /**
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 7cc601a..467f1a4 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -69,6 +69,11 @@ public function testShouldUseSerializerToEncodeMessageAndPutToExpectedTube()
             ->with('theQueueName')
             ->willReturn($kafkaTopic)
         ;
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('poll')
+            ->with(0)
+        ;
 
         $serializer = $this->createSerializerMock();
         $serializer
@@ -99,6 +104,11 @@ public function testShouldPassNullAsTopicConfigIfNotSetOnTopic()
             ->with('theQueueName', null)
             ->willReturn($kafkaTopic)
         ;
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('poll')
+            ->with(0)
+        ;
 
         $serializer = $this->createSerializerMock();
         $serializer
@@ -135,6 +145,11 @@ public function testShouldPassCustomConfAsTopicConfigIfSetOnTopic()
             ->with('theQueueName', $this->identicalTo($conf))
             ->willReturn($kafkaTopic)
         ;
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('poll')
+            ->with(0)
+        ;
 
         $serializer = $this->createSerializerMock();
         $serializer
@@ -189,6 +204,11 @@ public function testShouldAllowSerializersToSerializeKeys()
             ->method('newTopic')
             ->willReturn($kafkaTopic)
         ;
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('poll')
+            ->with(0)
+        ;
 
         $serializer = $this->createSerializerMock();
         $serializer

From 88c18d0f5c0d8faa7ca9e1bbeb5d8c4721b1b0d3 Mon Sep 17 00:00:00 2001
From: nick <nickjobszh@gmail.com>
Date: Mon, 5 Oct 2020 19:46:40 +0200
Subject: [PATCH 066/101] remove unused import

---
 RdKafkaContext.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 362037d..9930b56 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -19,7 +19,6 @@
 use RdKafka\Conf;
 use RdKafka\KafkaConsumer;
 use RdKafka\Producer as VendorProducer;
-use RdKafka\TopicConf;
 
 class RdKafkaContext implements Context
 {

From a369f9bfa1fe031418049490a6afea98d8466a11 Mon Sep 17 00:00:00 2001
From: nick <nickjobszh@gmail.com>
Date: Mon, 5 Oct 2020 21:14:06 +0200
Subject: [PATCH 067/101] test improvements

---
 Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
index c755b87..d756f23 100644
--- a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
+++ b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
@@ -30,7 +30,8 @@ public function test()
 
         $context->createProducer()->send($topic, $context->createMessage($expectedBody));
 
-        $message = $consumer->receive(10000); // 10 sec
+        // Initial balancing can take some time, so we want to make sure the timeout is high enough
+        $message = $consumer->receive(15000); // 15 sec
 
         $this->assertInstanceOf(Message::class, $message);
         $consumer->acknowledge($message);
@@ -47,7 +48,7 @@ protected function createContext()
                 'enable.auto.commit' => 'false',
             ],
             'topic' => [
-                'auto.offset.reset' => 'beginning',
+                'auto.offset.reset' => 'earliest',
             ],
         ];
 

From 8e5c39638ef48771c36558c1d203dcf9ed6775cf Mon Sep 17 00:00:00 2001
From: nick <nickjobszh@gmail.com>
Date: Thu, 8 Oct 2020 21:35:32 +0200
Subject: [PATCH 068/101] add exception for unsupported librdkafka version

---
 RdKafkaConnectionFactory.php | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/RdKafkaConnectionFactory.php b/RdKafkaConnectionFactory.php
index a55c5c6..24d60c9 100644
--- a/RdKafkaConnectionFactory.php
+++ b/RdKafkaConnectionFactory.php
@@ -39,6 +39,10 @@ class RdKafkaConnectionFactory implements ConnectionFactory
      */
     public function __construct($config = 'kafka:')
     {
+        if (version_compare(RdKafkaContext::getLibrdKafkaVersion(), '1.0.0', '<')) {
+            throw new \RuntimeException('You must install librdkafka:1.0.0 or higher');
+        }
+
         if (empty($config) || 'kafka:' === $config) {
             $config = [];
         } elseif (is_string($config)) {

From 551585c7b4608310dfbfcb0d052e057fc6a0fd5e Mon Sep 17 00:00:00 2001
From: nick <nickjobszh@gmail.com>
Date: Fri, 9 Oct 2020 11:56:41 +0200
Subject: [PATCH 069/101] make check more safe

---
 RdKafkaProducer.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 2b987bb..db21b93 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -47,7 +47,8 @@ public function send(Destination $destination, Message $message): void
         // Headers in payload are maintained for backwards compatibility with apps that might run on lower phprdkafka version
         if (method_exists($topic, 'producev')) {
             // Phprdkafka <= 3.1.0 will fail calling `producev` on librdkafka >= 1.0.0 causing segfault
-            if (version_compare(RdKafkaContext::getLibrdKafkaVersion(), '1.0.0', '>=')
+            // Since we are forcing to use at least librdkafka:1.0.0, no need to check the lib version anymore
+            if (false !== phpversion('rdkafka')
                 && version_compare(phpversion('rdkafka'), '3.1.0', '<=')) {
                 trigger_error(
                     'Phprdkafka <= 3.1.0 is incompatible with librdkafka 1.0.0 when calling `producev`. '.

From 78dbed59464781dd0bbc20eea17685430028f3f5 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 14 Jan 2021 20:00:42 +0200
Subject: [PATCH 070/101] Allow ext-rdkafka 5

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index b6b4865..4cdeb04 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "ext-rdkafka": "^3.0.3|^4.0",
+        "ext-rdkafka": "^3.0.3|^4.0|^5.0",
         "queue-interop/queue-interop": "^0.8"
     },
     "require-dev": {

From 38a20c2118c183398df682c681bfed20a49f639d Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 20:58:12 +0200
Subject: [PATCH 071/101] Fix more attributes; void returns; assert string
 contains; resource;

---
 Tests/JsonSerializerTest.php           | 2 +-
 Tests/RdKafkaConnectionFactoryTest.php | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/Tests/JsonSerializerTest.php b/Tests/JsonSerializerTest.php
index 6513a22..84f09a6 100644
--- a/Tests/JsonSerializerTest.php
+++ b/Tests/JsonSerializerTest.php
@@ -43,7 +43,7 @@ public function testThrowIfFailedToEncodeMessageToJson()
         $resource = fopen(__FILE__, 'r');
 
         //guard
-        $this->assertInternalType('resource', $resource);
+        $this->assertIsResource($resource);
 
         $message = new RdKafkaMessage('theBody', ['aProp' => $resource]);
 
diff --git a/Tests/RdKafkaConnectionFactoryTest.php b/Tests/RdKafkaConnectionFactoryTest.php
index 47fdc17..2950196 100644
--- a/Tests/RdKafkaConnectionFactoryTest.php
+++ b/Tests/RdKafkaConnectionFactoryTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\RdKafka\Tests;
 
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\Test\ReadAttributeTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -10,6 +11,8 @@
  */
 class RdKafkaConnectionFactoryTest extends TestCase
 {
+    use ReadAttributeTrait;
+
     public function testThrowNeitherArrayStringNorNullGivenAsConfig()
     {
         $this->expectException(\LogicException::class);

From a252a5cbf4b4961e064ef165a1aee6178bbe7242 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 21:11:36 +0200
Subject: [PATCH 072/101] Fix more attributes

---
 Tests/RdKafkaConnectionFactoryConfigTest.php | 4 +++-
 Tests/RdKafkaConnectionFactoryTest.php       | 4 ++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Tests/RdKafkaConnectionFactoryConfigTest.php b/Tests/RdKafkaConnectionFactoryConfigTest.php
index d3a6a5d..0a50218 100644
--- a/Tests/RdKafkaConnectionFactoryConfigTest.php
+++ b/Tests/RdKafkaConnectionFactoryConfigTest.php
@@ -4,6 +4,7 @@
 
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
 use Enqueue\Test\ClassExtensionTrait;
+use Enqueue\Test\ReadAttributeTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -12,6 +13,7 @@
 class RdKafkaConnectionFactoryConfigTest extends TestCase
 {
     use ClassExtensionTrait;
+    use ReadAttributeTrait;
 
     public function testThrowNeitherArrayStringNorNullGivenAsConfig()
     {
@@ -39,7 +41,7 @@ public function testShouldParseConfigurationAsExpected($config, $expectedConfig)
     {
         $factory = new RdKafkaConnectionFactory($config);
 
-        $config = $this->getObjectAttribute($factory, 'config');
+        $config = $this->readAttribute($factory, 'config');
 
         $this->assertNotEmpty($config['global']['group.id']);
 
diff --git a/Tests/RdKafkaConnectionFactoryTest.php b/Tests/RdKafkaConnectionFactoryTest.php
index 2950196..8af3e85 100644
--- a/Tests/RdKafkaConnectionFactoryTest.php
+++ b/Tests/RdKafkaConnectionFactoryTest.php
@@ -41,7 +41,7 @@ public function testShouldBeExpectedDefaultConfig()
     {
         $factory = new RdKafkaConnectionFactory(null);
 
-        $config = $this->getObjectAttribute($factory, 'config');
+        $config = $this->readAttribute($factory, 'config');
 
         $this->assertNotEmpty($config['global']['group.id']);
 
@@ -58,7 +58,7 @@ public function testShouldBeExpectedDefaultDsnConfig()
     {
         $factory = new RdKafkaConnectionFactory('kafka:');
 
-        $config = $this->getObjectAttribute($factory, 'config');
+        $config = $this->readAttribute($factory, 'config');
 
         $this->assertNotEmpty($config['global']['group.id']);
 

From cbe6742f51ee6b68c58b1bcfb1bd682de850f95c Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 09:51:30 +0200
Subject: [PATCH 073/101] Update PHP and PHPUnit across all packages; fix
 ext-rdkafka requirement

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 4cdeb04..8db8e9c 100644
--- a/composer.json
+++ b/composer.json
@@ -6,12 +6,12 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.1.3",
+        "php": "^7.3",
         "ext-rdkafka": "^3.0.3|^4.0|^5.0",
         "queue-interop/queue-interop": "^0.8"
     },
     "require-dev": {
-        "phpunit/phpunit": "~7.5",
+        "phpunit/phpunit": "^9.5",
         "enqueue/test": "0.10.x-dev",
         "enqueue/null": "0.10.x-dev",
         "queue-interop/queue-spec": "^0.6",

From 05a65a5b160d677593fbcdc244fc28a4800cff5a Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Tue, 9 Feb 2021 02:14:42 +0200
Subject: [PATCH 074/101] Fix package CI

- Add push to master trigger;
- Replace the clunky cache + composer install steps with an Action;
- Replace Travis with GH WF in packages;
- Add the missing CI to amqp-tools;
- Replace the shields.
---
 .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++
 .travis.yml              | 24 ------------------------
 README.md                |  2 +-
 3 files changed, 30 insertions(+), 25 deletions(-)
 create mode 100644 .github/workflows/ci.yml
 delete mode 100644 .travis.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..24af1bb
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,29 @@
+name: CI
+on:
+  pull_request:
+  push:
+    branches:
+      - master
+jobs:
+  tests:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.3', '7.4']
+
+    name: PHP ${{ matrix.php }} tests
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          coverage: none
+
+      - run: php Tests/fix_composer_json.php
+
+      - uses: "ramsey/composer-install@v1"
+
+      - run: vendor/bin/phpunit --exclude-group=functional
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 8210653..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-sudo: false
-
-git:
-    depth: 10
-
-language: php
-
-php:
-    - '7.1'
-    - '7.2'
-    - '7.3'
-    - '7.4'
-
-cache:
-    directories:
-        - $HOME/.composer/cache
-
-install:
-    - php Tests/fix_composer_json.php
-    - composer self-update
-    - composer install
-
-script:
-    - vendor/bin/phpunit --exclude-group=functional
diff --git a/README.md b/README.md
index 1502f35..2a541eb 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Enqueue is an MIT-licensed open source project with its ongoing development made
 # RdKafka Transport
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)
-[![Build Status](https://travis-ci.org/php-enqueue/rdkafka.png?branch=master)](https://travis-ci.org/php-enqueue/rdkafka)
+[![Build Status](https://img.shields.io/github/workflow/status/php-enqueue/rdkafka/CI)](https://github.com/php-enqueue/rdkafka/actions?query=workflow%3ACI)
 [![Total Downloads](https://poser.pugx.org/enqueue/rdkafka/d/total.png)](https://packagist.org/packages/enqueue/rdkafka)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/rdkafka/version.png)](https://packagist.org/packages/enqueue/rdkafka)
 

From 158395440e3c2b1a7bab314f63ec18dab6e45bd5 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 10:26:59 +0200
Subject: [PATCH 075/101] Allow php8 in all composer.json files

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 8db8e9c..7128a52 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.3",
+        "php": "^7.3|^8.0",
         "ext-rdkafka": "^3.0.3|^4.0|^5.0",
         "queue-interop/queue-interop": "^0.8"
     },

From 3d0c965b2fa0784d8c84863498d6fd073c44f240 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Tue, 9 Feb 2021 14:01:28 +0200
Subject: [PATCH 076/101] Add php 8 to packages' build matrices; normalize
 composer reqs

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 24af1bb..fd96b2a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,7 +10,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        php: ['7.3', '7.4']
+        php: ['7.3', '7.4', '8.0']
 
     name: PHP ${{ matrix.php }} tests
 

From 34fab37836ac95a2acd3d8c710d595687b404be1 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 11 Feb 2021 03:45:55 +0200
Subject: [PATCH 077/101] Bump some minimal deps

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 7128a52..5743a7d 100644
--- a/composer.json
+++ b/composer.json
@@ -14,7 +14,7 @@
         "phpunit/phpunit": "^9.5",
         "enqueue/test": "0.10.x-dev",
         "enqueue/null": "0.10.x-dev",
-        "queue-interop/queue-spec": "^0.6",
+        "queue-interop/queue-spec": "^0.6.2",
         "kwn/php-rdkafka-stubs": "^1.0.2 | ^2.0"
     },
     "support": {

From ccf95802f920221008c1b1e058c2698588589b6d Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 11 Feb 2021 03:52:52 +0200
Subject: [PATCH 078/101] Bump

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 5743a7d..e322a84 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
         "enqueue/test": "0.10.x-dev",
         "enqueue/null": "0.10.x-dev",
         "queue-interop/queue-spec": "^0.6.2",
-        "kwn/php-rdkafka-stubs": "^1.0.2 | ^2.0"
+        "kwn/php-rdkafka-stubs": "^2.0.3"
     },
     "support": {
         "email": "opensource@forma-pro.com",

From 690dfc7c0dbe9b0141c5f497772878b846256005 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 11 Feb 2021 03:56:32 +0200
Subject: [PATCH 079/101] Drop ext-rdkafka 3

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index e322a84..72c5535 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.3|^8.0",
-        "ext-rdkafka": "^3.0.3|^4.0|^5.0",
+        "ext-rdkafka": "^4.0|^5.0",
         "queue-interop/queue-interop": "^0.8"
     },
     "require-dev": {

From 11ece7fc87a78655d62e51985c72bcd720c8f6b3 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Wed, 17 Feb 2021 20:57:43 +0200
Subject: [PATCH 080/101] Bump amqp-interop and queue-interop

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 72c5535..82c5367 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.3|^8.0",
         "ext-rdkafka": "^4.0|^5.0",
-        "queue-interop/queue-interop": "^0.8"
+        "queue-interop/queue-interop": "^0.8.1"
     },
     "require-dev": {
         "phpunit/phpunit": "^9.5",

From 1e71e9ff8c602e753fbfbea42e7b7561188695ee Mon Sep 17 00:00:00 2001
From: DigitVE <etercalm93@gmail.com>
Date: Tue, 13 Jul 2021 23:26:58 +0300
Subject: [PATCH 081/101] Added getter for offset field in RdKafkaConsumer
 class

---
 RdKafkaConsumer.php | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 3aa568e..7fbaef7 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -66,6 +66,11 @@ public function setCommitAsync(bool $async): void
         $this->commitAsync = $async;
     }
 
+    public function getOffset(): ?int
+    {
+        return $this->offset;
+    }
+
     public function setOffset(int $offset = null): void
     {
         if ($this->subscribed) {

From 10de9e26b91756ca60f4d38ed99fabfa137e2019 Mon Sep 17 00:00:00 2001
From: Diego Borges <ferreirabdiego@gmail.com>
Date: Tue, 31 Aug 2021 16:09:54 -0300
Subject: [PATCH 082/101] Changed: Change partition recovery hierachy in
 RdKafkaProducer

---
 RdKafkaProducer.php           | 18 ++++++-
 Tests/RdKafkaProducerTest.php | 98 +++++++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+), 1 deletion(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index db21b93..de8d3af 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -28,6 +28,22 @@ public function __construct(VendorProducer $producer, Serializer $serializer)
         $this->setSerializer($serializer);
     }
 
+
+    /**
+     * @param RdKafkaTopic   $destination
+     * @param RdKafkaMessage $message
+     */
+    private function getPartition(Destination $destination, Message $message): int
+    {
+        if (!is_null($message->getPartition())) {
+            return $message->getPartition();
+        }
+        if (!is_null($destination->getPartition())) {
+            return $destination->getPartition();
+        }
+        return RD_KAFKA_PARTITION_UA;
+    }
+
     /**
      * @param RdKafkaTopic   $destination
      * @param RdKafkaMessage $message
@@ -37,7 +53,7 @@ public function send(Destination $destination, Message $message): void
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
 
-        $partition = $message->getPartition() ?: $destination->getPartition() ?: RD_KAFKA_PARTITION_UA;
+        $partition = $this->getPartition($destination, $message);
         $payload = $this->serializer->toString($message);
         $key = $message->getKey() ?: $destination->getKey() ?: null;
 
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 467f1a4..01c1c1b 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -225,6 +225,104 @@ public function testShouldAllowSerializersToSerializeKeys()
         $producer->send(new RdKafkaTopic('theQueueName'), $message);
     }
 
+    public function testShouldGetPartitionFromMessage(): void
+    {
+        $partition = 1;
+
+        $kafkaTopic = $this->createKafkaTopicMock();
+        $kafkaTopic
+            ->expects($this->once())
+            ->method('producev')
+            ->with(
+                $partition,
+                0,
+                'theSerializedMessage',
+                'theSerializedKey'
+            )
+        ;
+
+        $kafkaProducer = $this->createKafkaProducerMock();
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('newTopic')
+            ->willReturn($kafkaTopic)
+        ;
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('poll')
+            ->with(0)
+        ;
+        $messageHeaders = ['bar' => 'barVal'];
+        $message = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], $messageHeaders);
+        $message->setKey('key');
+        $message->setPartition($partition);
+
+        $serializer = $this->createSerializerMock();
+        $serializer
+            ->expects($this->once())
+            ->method('toString')
+            ->willReturnCallback(function () use ($message) {
+                $message->setKey('theSerializedKey');
+
+                return 'theSerializedMessage';
+            })
+        ;
+
+        $destination = new RdKafkaTopic('theQueueName');
+
+        $producer = new RdKafkaProducer($kafkaProducer, $serializer);
+        $producer->send($destination, $message);
+    }
+
+    public function testShouldGetPartitionFromDestination(): void
+    {
+        $partition = 2;
+
+        $kafkaTopic = $this->createKafkaTopicMock();
+        $kafkaTopic
+            ->expects($this->once())
+            ->method('producev')
+            ->with(
+                $partition,
+                0,
+                'theSerializedMessage',
+                'theSerializedKey'
+            )
+        ;
+
+        $kafkaProducer = $this->createKafkaProducerMock();
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('newTopic')
+            ->willReturn($kafkaTopic)
+        ;
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('poll')
+            ->with(0)
+        ;
+        $messageHeaders = ['bar' => 'barVal'];
+        $message = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], $messageHeaders);
+        $message->setKey('key');
+
+        $serializer = $this->createSerializerMock();
+        $serializer
+            ->expects($this->once())
+            ->method('toString')
+            ->willReturnCallback(function () use ($message) {
+                $message->setKey('theSerializedKey');
+
+                return 'theSerializedMessage';
+            })
+        ;
+
+        $destination = new RdKafkaTopic('theQueueName');
+        $destination->setPartition($partition);
+
+        $producer = new RdKafkaProducer($kafkaProducer, $serializer);
+        $producer->send($destination, $message);
+    }
+
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|ProducerTopic
      */

From 555d740c52123682d4bcf4f9ae6f382f1f95b002 Mon Sep 17 00:00:00 2001
From: maartenderie <maartenderie@gmail.com>
Date: Fri, 29 Oct 2021 15:03:12 +0200
Subject: [PATCH 083/101] RdKafka; Replace composer-modifying for testing with
 --ignore-platform-req argument

---
 .github/workflows/ci.yml    | 4 ++--
 Tests/bootstrap.php         | 3 +++
 Tests/fix_composer_json.php | 9 ---------
 3 files changed, 5 insertions(+), 11 deletions(-)
 delete mode 100644 Tests/fix_composer_json.php

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fd96b2a..5f6516a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,8 +22,8 @@ jobs:
           php-version: ${{ matrix.php }}
           coverage: none
 
-      - run: php Tests/fix_composer_json.php
-
       - uses: "ramsey/composer-install@v1"
+        with: # ext-rdkafka not needed for tests, and a pain to install on CI;
+          composer-options: "--ignore-platform-req=ext-rdkafka"
 
       - run: vendor/bin/phpunit --exclude-group=functional
diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php
index bf11262..1d4816c 100644
--- a/Tests/bootstrap.php
+++ b/Tests/bootstrap.php
@@ -12,5 +12,8 @@
     }
 }
 
+// Pretend that a high version was installed, opposed to 0.8.5 from kwn/php-rdkafka-stubs
+const RD_KAFKA_VERSION = 42424242;
+
 include_once $kafkaStubsDir.'/stubs/constants.php';
 include_once $kafkaStubsDir.'/stubs/functions.php';
diff --git a/Tests/fix_composer_json.php b/Tests/fix_composer_json.php
deleted file mode 100644
index b778f6f..0000000
--- a/Tests/fix_composer_json.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-copy(__DIR__.'/../composer.json', __DIR__.'/../composer.json.bak');
-
-$composerJson = json_decode(file_get_contents(__DIR__.'/../composer.json'), true);
-
-$composerJson['config']['platform']['ext-rdkafka'] = '3.3';
-
-file_put_contents(__DIR__.'/../composer.json', json_encode($composerJson, JSON_PRETTY_PRINT));

From 59b04beae7bb276c39973d69e56affbe0d7ce0c8 Mon Sep 17 00:00:00 2001
From: maartenderie <maartenderie@gmail.com>
Date: Fri, 29 Oct 2021 15:08:27 +0200
Subject: [PATCH 084/101] RdKafka; Replace composer-modifying for testing with
 --ignore-platform-req argument

---
 .github/workflows/ci.yml | 2 ++
 Tests/bootstrap.php      | 3 ---
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5f6516a..58f56b0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,4 +26,6 @@ jobs:
         with: # ext-rdkafka not needed for tests, and a pain to install on CI;
           composer-options: "--ignore-platform-req=ext-rdkafka"
 
+      - run: sed -i 's/525568/16777471/' vendor/kwn/php-rdkafka-stubs/stubs/constants.php
+
       - run: vendor/bin/phpunit --exclude-group=functional
diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php
index 1d4816c..bf11262 100644
--- a/Tests/bootstrap.php
+++ b/Tests/bootstrap.php
@@ -12,8 +12,5 @@
     }
 }
 
-// Pretend that a high version was installed, opposed to 0.8.5 from kwn/php-rdkafka-stubs
-const RD_KAFKA_VERSION = 42424242;
-
 include_once $kafkaStubsDir.'/stubs/constants.php';
 include_once $kafkaStubsDir.'/stubs/functions.php';

From 08666d42a673a7b88108689218919496d5e08fe9 Mon Sep 17 00:00:00 2001
From: Lucas Rivoiro <lucasrivoiro@gmail.com>
Date: Fri, 25 Feb 2022 15:26:56 -0300
Subject: [PATCH 085/101] Upgrade version of rdkafka to 6.0

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 82c5367..2b5ec55 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.3|^8.0",
-        "ext-rdkafka": "^4.0|^5.0",
+        "ext-rdkafka": "^4.0|^5.0|^6.0",
         "queue-interop/queue-interop": "^0.8.1"
     },
     "require-dev": {

From e56debc8b1a31efaa86f97906fecfc2c683d5f34 Mon Sep 17 00:00:00 2001
From: Quentin Dreyer <quentin.dreyer@gmail.com>
Date: Wed, 20 Jul 2022 12:00:16 +0200
Subject: [PATCH 086/101] fix: allow falsy keys

---
 RdKafkaProducer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index d5380b5..59efd69 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -39,7 +39,7 @@ public function send(Destination $destination, Message $message): void
 
         $partition = $this->getPartition($destination, $message);
         $payload = $this->serializer->toString($message);
-        $key = $message->getKey() ?: $destination->getKey() ?: null;
+        $key = $message->getKey() ?? $destination->getKey() ?? null;
 
         $topic = $this->producer->newTopic($destination->getTopicName(), $destination->getConf());
 

From 844c5ffe52458e829d0185970a8718919ccd6c42 Mon Sep 17 00:00:00 2001
From: Quentin Dreyer <quentin.dreyer@gmail.com>
Date: Wed, 20 Jul 2022 12:41:20 +0200
Subject: [PATCH 087/101] test: allow falsy keys

---
 Tests/RdKafkaProducerTest.php | 60 +++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 01c1c1b..512cc9f 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -323,6 +323,66 @@ public function testShouldGetPartitionFromDestination(): void
         $producer->send($destination, $message);
     }
 
+    public function testShouldAllowFalsyKeyFromMessage(): void
+    {
+        $key = 0;
+
+        $kafkaTopic = $this->createKafkaTopicMock();
+        $kafkaTopic
+            ->expects($this->once())
+            ->method('producev')
+            ->with(
+                RD_KAFKA_PARTITION_UA,
+                0,
+                '',
+                $key
+            )
+        ;
+
+        $kafkaProducer = $this->createKafkaProducerMock();
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('newTopic')
+            ->willReturn($kafkaTopic)
+        ;
+
+        $message = new RdKafkaMessage();
+        $message->setKey($key);
+
+        $producer = new RdKafkaProducer($kafkaProducer, $this->createSerializerMock());
+        $producer->send(new RdKafkaTopic(''), $message);
+    }
+
+    public function testShouldAllowFalsyKeyFromDestination(): void
+    {
+        $key = 0;
+
+        $kafkaTopic = $this->createKafkaTopicMock();
+        $kafkaTopic
+            ->expects($this->once())
+            ->method('producev')
+            ->with(
+                RD_KAFKA_PARTITION_UA,
+                0,
+                '',
+                $key
+            )
+        ;
+
+        $kafkaProducer = $this->createKafkaProducerMock();
+        $kafkaProducer
+            ->expects($this->once())
+            ->method('newTopic')
+            ->willReturn($kafkaTopic)
+        ;
+
+        $destination = new RdKafkaTopic('');
+        $destination->setKey($key);
+
+        $producer = new RdKafkaProducer($kafkaProducer, $this->createSerializerMock());
+        $producer->send($destination, new RdKafkaMessage());
+    }
+
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|ProducerTopic
      */

From 3ea26b587f3b8f11bd9a7538d6c1a0307f966e98 Mon Sep 17 00:00:00 2001
From: qkdreyer <quentin.dreyer@rgsystem.com>
Date: Wed, 20 Jul 2022 12:51:11 +0200
Subject: [PATCH 088/101] style: fix code style checks

---
 RdKafkaProducer.php | 19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 59efd69..a3c977f 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -37,7 +37,7 @@ public function send(Destination $destination, Message $message): void
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
 
-        $partition = $this->getPartition($destination, $message);
+        $partition = $message->getPartition() ?? $destination->getPartition() ?? RD_KAFKA_PARTITION_UA;
         $payload = $this->serializer->toString($message);
         $key = $message->getKey() ?? $destination->getKey() ?? null;
 
@@ -122,21 +122,4 @@ public function flush(int $timeout): void
             $this->producer->flush($timeout);
         }
     }
-
-    /**
-     * @param RdKafkaTopic   $destination
-     * @param RdKafkaMessage $message
-     */
-    private function getPartition(Destination $destination, Message $message): int
-    {
-        if (null !== $message->getPartition()) {
-            return $message->getPartition();
-        }
-
-        if (null !== $destination->getPartition()) {
-            return $destination->getPartition();
-        }
-
-        return \RD_KAFKA_PARTITION_UA;
-    }
 }

From 07e42aec51de465bba6c4ba868d9a04ff023b0e5 Mon Sep 17 00:00:00 2001
From: Alex Mayer <amayer5125@gmail.com>
Date: Sat, 25 Feb 2023 22:18:02 -0500
Subject: [PATCH 089/101] Fix Shield URLs in READMEs

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 2a541eb..94f24e5 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Enqueue is an MIT-licensed open source project with its ongoing development made
 # RdKafka Transport
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)
-[![Build Status](https://img.shields.io/github/workflow/status/php-enqueue/rdkafka/CI)](https://github.com/php-enqueue/rdkafka/actions?query=workflow%3ACI)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/php-enqueue/rdkafka/ci.yml?branch=master)](https://github.com/php-enqueue/rdkafka/actions?query=workflow%3ACI)
 [![Total Downloads](https://poser.pugx.org/enqueue/rdkafka/d/total.png)](https://packagist.org/packages/enqueue/rdkafka)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/rdkafka/version.png)](https://packagist.org/packages/enqueue/rdkafka)
 

From c159ae25ad585362bca2479e616cb3e89ce407ae Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 19 Feb 2023 15:13:41 +0200
Subject: [PATCH 090/101] Properly drop old PHP and Symfony versions

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 2b5ec55..068b9d2 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.3|^8.0",
+        "php": "^7.4|^8.0",
         "ext-rdkafka": "^4.0|^5.0|^6.0",
         "queue-interop/queue-interop": "^0.8.1"
     },

From 8d7371154784df848c0ee31e99a37eed1f05de5f Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 20 Mar 2023 16:38:26 +0200
Subject: [PATCH 091/101] Remove special group for rdkafka tests

---
 Tests/JsonSerializerTest.php                        | 3 ---
 Tests/RdKafkaConnectionFactoryTest.php              | 3 ---
 Tests/RdKafkaConsumerTest.php                       | 3 ---
 Tests/RdKafkaContextTest.php                        | 3 ---
 Tests/RdKafkaMessageTest.php                        | 3 ---
 Tests/RdKafkaProducerTest.php                       | 3 ---
 Tests/RdKafkaTopicTest.php                          | 3 ---
 Tests/Spec/RdKafkaConnectionFactoryTest.php         | 3 ---
 Tests/Spec/RdKafkaContextTest.php                   | 3 ---
 Tests/Spec/RdKafkaMessageTest.php                   | 3 ---
 Tests/Spec/RdKafkaQueueTest.php                     | 3 ---
 Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php | 1 -
 Tests/Spec/RdKafkaTopicTest.php                     | 3 ---
 13 files changed, 37 deletions(-)

diff --git a/Tests/JsonSerializerTest.php b/Tests/JsonSerializerTest.php
index 84f09a6..d4db377 100644
--- a/Tests/JsonSerializerTest.php
+++ b/Tests/JsonSerializerTest.php
@@ -8,9 +8,6 @@
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 
-/**
- * @group rdkafka
- */
 class JsonSerializerTest extends TestCase
 {
     use ClassExtensionTrait;
diff --git a/Tests/RdKafkaConnectionFactoryTest.php b/Tests/RdKafkaConnectionFactoryTest.php
index 8af3e85..62ccc76 100644
--- a/Tests/RdKafkaConnectionFactoryTest.php
+++ b/Tests/RdKafkaConnectionFactoryTest.php
@@ -6,9 +6,6 @@
 use Enqueue\Test\ReadAttributeTrait;
 use PHPUnit\Framework\TestCase;
 
-/**
- * @group rdkafka
- */
 class RdKafkaConnectionFactoryTest extends TestCase
 {
     use ReadAttributeTrait;
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index b40916c..6278319 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -11,9 +11,6 @@
 use RdKafka\KafkaConsumer;
 use RdKafka\Message;
 
-/**
- * @group rdkafka
- */
 class RdKafkaConsumerTest extends TestCase
 {
     public function testCouldBeConstructedWithRequiredArguments()
diff --git a/Tests/RdKafkaContextTest.php b/Tests/RdKafkaContextTest.php
index 227eea7..dc1b597 100644
--- a/Tests/RdKafkaContextTest.php
+++ b/Tests/RdKafkaContextTest.php
@@ -10,9 +10,6 @@
 use Interop\Queue\Exception\TemporaryQueueNotSupportedException;
 use PHPUnit\Framework\TestCase;
 
-/**
- * @group rdkafka
- */
 class RdKafkaContextTest extends TestCase
 {
     public function testThrowNotImplementedOnCreateTemporaryQueue()
diff --git a/Tests/RdKafkaMessageTest.php b/Tests/RdKafkaMessageTest.php
index c2e5c22..9bcc346 100644
--- a/Tests/RdKafkaMessageTest.php
+++ b/Tests/RdKafkaMessageTest.php
@@ -6,9 +6,6 @@
 use PHPUnit\Framework\TestCase;
 use RdKafka\Message;
 
-/**
- * @group rdkafka
- */
 class RdKafkaMessageTest extends TestCase
 {
     public function testCouldSetGetPartition()
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 512cc9f..eec4b36 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -15,9 +15,6 @@
 use RdKafka\ProducerTopic;
 use RdKafka\TopicConf;
 
-/**
- * @group rdkafka
- */
 class RdKafkaProducerTest extends TestCase
 {
     public function testCouldBeConstructedWithKafkaProducerAndSerializerAsArguments()
diff --git a/Tests/RdKafkaTopicTest.php b/Tests/RdKafkaTopicTest.php
index 5ed2288..d0bc8cc 100644
--- a/Tests/RdKafkaTopicTest.php
+++ b/Tests/RdKafkaTopicTest.php
@@ -6,9 +6,6 @@
 use PHPUnit\Framework\TestCase;
 use RdKafka\TopicConf;
 
-/**
- * @group rdkafka
- */
 class RdKafkaTopicTest extends TestCase
 {
     public function testCouldSetGetPartition()
diff --git a/Tests/Spec/RdKafkaConnectionFactoryTest.php b/Tests/Spec/RdKafkaConnectionFactoryTest.php
index 3327c95..a582aad 100644
--- a/Tests/Spec/RdKafkaConnectionFactoryTest.php
+++ b/Tests/Spec/RdKafkaConnectionFactoryTest.php
@@ -5,9 +5,6 @@
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
 use Interop\Queue\Spec\ConnectionFactorySpec;
 
-/**
- * @group rdkafka
- */
 class RdKafkaConnectionFactoryTest extends ConnectionFactorySpec
 {
     protected function createConnectionFactory()
diff --git a/Tests/Spec/RdKafkaContextTest.php b/Tests/Spec/RdKafkaContextTest.php
index fe96256..d049ca7 100644
--- a/Tests/Spec/RdKafkaContextTest.php
+++ b/Tests/Spec/RdKafkaContextTest.php
@@ -5,9 +5,6 @@
 use Enqueue\RdKafka\RdKafkaContext;
 use Interop\Queue\Spec\ContextSpec;
 
-/**
- * @group rdkafka
- */
 class RdKafkaContextTest extends ContextSpec
 {
     protected function createContext()
diff --git a/Tests/Spec/RdKafkaMessageTest.php b/Tests/Spec/RdKafkaMessageTest.php
index a29780b..df54fa5 100644
--- a/Tests/Spec/RdKafkaMessageTest.php
+++ b/Tests/Spec/RdKafkaMessageTest.php
@@ -5,9 +5,6 @@
 use Enqueue\RdKafka\RdKafkaMessage;
 use Interop\Queue\Spec\MessageSpec;
 
-/**
- * @group rdkafka
- */
 class RdKafkaMessageTest extends MessageSpec
 {
     /**
diff --git a/Tests/Spec/RdKafkaQueueTest.php b/Tests/Spec/RdKafkaQueueTest.php
index 93bc7ac..863f3e3 100644
--- a/Tests/Spec/RdKafkaQueueTest.php
+++ b/Tests/Spec/RdKafkaQueueTest.php
@@ -5,9 +5,6 @@
 use Enqueue\RdKafka\RdKafkaTopic;
 use Interop\Queue\Spec\QueueSpec;
 
-/**
- * @group rdkafka
- */
 class RdKafkaQueueTest extends QueueSpec
 {
     protected function createQueue()
diff --git a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
index d756f23..afe344a 100644
--- a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
+++ b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
@@ -7,7 +7,6 @@
 use Interop\Queue\Spec\SendToAndReceiveFromTopicSpec;
 
 /**
- * @group rdkafka
  * @group functional
  * @retry 5
  */
diff --git a/Tests/Spec/RdKafkaTopicTest.php b/Tests/Spec/RdKafkaTopicTest.php
index 26aacc7..c5d43ee 100644
--- a/Tests/Spec/RdKafkaTopicTest.php
+++ b/Tests/Spec/RdKafkaTopicTest.php
@@ -5,9 +5,6 @@
 use Enqueue\RdKafka\RdKafkaTopic;
 use Interop\Queue\Spec\TopicSpec;
 
-/**
- * @group rdkafka
- */
 class RdKafkaTopicTest extends TopicSpec
 {
     /**

From 06f45780df2a908b4d03208d07b28cc82a2dfa61 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 20 Mar 2023 16:49:00 +0200
Subject: [PATCH 092/101] Fix CS

---
 Tests/JsonSerializerTest.php                        | 2 +-
 Tests/RdKafkaConsumerTest.php                       | 4 ++--
 Tests/RdKafkaProducerTest.php                       | 2 +-
 Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php | 1 +
 4 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Tests/JsonSerializerTest.php b/Tests/JsonSerializerTest.php
index d4db377..d10479d 100644
--- a/Tests/JsonSerializerTest.php
+++ b/Tests/JsonSerializerTest.php
@@ -39,7 +39,7 @@ public function testThrowIfFailedToEncodeMessageToJson()
 
         $resource = fopen(__FILE__, 'r');
 
-        //guard
+        // guard
         $this->assertIsResource($resource);
 
         $message = new RdKafkaMessage('theBody', ['aProp' => $resource]);
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 6278319..4f2b806 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -195,7 +195,7 @@ public function testShouldReceiveFromQueueAndReturnMessageIfMessageInQueue()
     {
         $destination = new RdKafkaTopic('dest');
 
-        $expectedMessage = new  RdKafkaMessage('theBody', ['foo' => 'fooVal'], ['bar' => 'barVal']);
+        $expectedMessage = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], ['bar' => 'barVal']);
 
         $kafkaMessage = new Message();
         $kafkaMessage->err = RD_KAFKA_RESP_ERR_NO_ERROR;
@@ -260,7 +260,7 @@ public function testShouldAllowGetPreviouslySetSerializer()
 
         $expectedSerializer = $this->createSerializerMock();
 
-        //guard
+        // guard
         $this->assertNotSame($consumer->getSerializer(), $expectedSerializer);
 
         $consumer->setSerializer($expectedSerializer);
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index eec4b36..ecfa4c6 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -169,7 +169,7 @@ public function testShouldAllowGetPreviouslySetSerializer()
 
         $expectedSerializer = $this->createSerializerMock();
 
-        //guard
+        // guard
         $this->assertNotSame($producer->getSerializer(), $expectedSerializer);
 
         $producer->setSerializer($expectedSerializer);
diff --git a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
index afe344a..9a969d4 100644
--- a/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
+++ b/Tests/Spec/RdKafkaSendToAndReceiveFromTopicTest.php
@@ -8,6 +8,7 @@
 
 /**
  * @group functional
+ *
  * @retry 5
  */
 class RdKafkaSendToAndReceiveFromTopicTest extends SendToAndReceiveFromTopicSpec

From b35bcedb005369f82f9c628e47a3aeed9b2568aa Mon Sep 17 00:00:00 2001
From: Witold Wasiczko <witold@wasiczko.pl>
Date: Thu, 23 Mar 2023 10:50:55 +0100
Subject: [PATCH 093/101] Update and fix pkg test with 8.1 and 8.2 PHP support

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 58f56b0..9e0ceb1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,7 +10,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        php: ['7.3', '7.4', '8.0']
+        php: ['7.4', '8.0', '8.1', '8.2']
 
     name: PHP ${{ matrix.php }} tests
 

From d1dfe0422a15f25ef4dfda5eff5cf29633079949 Mon Sep 17 00:00:00 2001
From: Felippe Roberto Bayestorff Duarte <felippeduarte@gmail.com>
Date: Tue, 8 Aug 2023 17:59:25 -0300
Subject: [PATCH 094/101] Add return type to flush function in RdkafkaProducer
 to be compatible to php-rdkafka flush function

---
 RdKafkaProducer.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index a3c977f..64b9ea3 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -115,11 +115,11 @@ public function getTimeToLive(): ?int
         return null;
     }
 
-    public function flush(int $timeout): void
+    public function flush(int $timeout): ?int
     {
         // Flush method is exposed in phprdkafka 4.0
         if (method_exists($this->producer, 'flush')) {
-            $this->producer->flush($timeout);
+            return $this->producer->flush($timeout);
         }
     }
 }

From cf010683fff8252e4ec424dbf81587af2c60d5e2 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Sat, 11 Jan 2025 20:47:49 +0000
Subject: [PATCH 095/101] Fixing risky tests

Makring tests that have no assertions with the annotation to suppress
phpunits warning around risky tests

closes 1370
---
 Tests/JsonSerializerTest.php  | 3 +++
 Tests/RdKafkaConsumerTest.php | 3 +++
 Tests/RdKafkaProducerTest.php | 3 +++
 3 files changed, 9 insertions(+)

diff --git a/Tests/JsonSerializerTest.php b/Tests/JsonSerializerTest.php
index d10479d..32805ca 100644
--- a/Tests/JsonSerializerTest.php
+++ b/Tests/JsonSerializerTest.php
@@ -17,6 +17,9 @@ public function testShouldImplementSerializerInterface()
         $this->assertClassImplements(Serializer::class, JsonSerializer::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new JsonSerializer();
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 4f2b806..471e31a 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -13,6 +13,9 @@
 
 class RdKafkaConsumerTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new RdKafkaConsumer(
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index ecfa4c6..25451f5 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -17,6 +17,9 @@
 
 class RdKafkaProducerTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithKafkaProducerAndSerializerAsArguments()
     {
         new RdKafkaProducer($this->createKafkaProducerMock(), $this->createSerializerMock());

From fd03f9d8d2871de6ec37e4070f9a51fc01fb1023 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Wed, 15 Jan 2025 21:17:06 +0000
Subject: [PATCH 096/101] Removing constructor tests

Removing all tests that creates a new instance but doesn't call any
methods nor preform any assertions.
---
 Tests/JsonSerializerTest.php  |  8 --------
 Tests/RdKafkaConsumerTest.php | 13 -------------
 Tests/RdKafkaProducerTest.php |  8 --------
 3 files changed, 29 deletions(-)

diff --git a/Tests/JsonSerializerTest.php b/Tests/JsonSerializerTest.php
index 32805ca..6c9bbef 100644
--- a/Tests/JsonSerializerTest.php
+++ b/Tests/JsonSerializerTest.php
@@ -17,14 +17,6 @@ public function testShouldImplementSerializerInterface()
         $this->assertClassImplements(Serializer::class, JsonSerializer::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new JsonSerializer();
-    }
-
     public function testShouldConvertMessageToJsonString()
     {
         $serializer = new JsonSerializer();
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 471e31a..74e9295 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -13,19 +13,6 @@
 
 class RdKafkaConsumerTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new RdKafkaConsumer(
-            $this->createKafkaConsumerMock(),
-            $this->createContextMock(),
-            new RdKafkaTopic(''),
-            $this->createSerializerMock()
-        );
-    }
-
     public function testShouldReturnQueueSetInConstructor()
     {
         $destination = new RdKafkaTopic('');
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 25451f5..6082968 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -17,14 +17,6 @@
 
 class RdKafkaProducerTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithKafkaProducerAndSerializerAsArguments()
-    {
-        new RdKafkaProducer($this->createKafkaProducerMock(), $this->createSerializerMock());
-    }
-
     public function testThrowIfDestinationInvalid()
     {
         $producer = new RdKafkaProducer($this->createKafkaProducerMock(), $this->createSerializerMock());

From 4164e1e1e5cbf937c3dfe76a27ab983cc97b3405 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Wed, 15 Jan 2025 22:51:32 +0000
Subject: [PATCH 097/101] Running php-cs-fixer

Running php-cs-fixer to fix CS drift
---
 JsonSerializer.php                           | 16 ++++------------
 RdKafkaConsumer.php                          |  2 +-
 RdKafkaMessage.php                           | 14 +++++++-------
 RdKafkaProducer.php                          |  6 +++---
 RdKafkaTopic.php                             |  6 +++---
 SerializerAwareTrait.php                     |  3 ---
 Tests/RdKafkaConnectionFactoryConfigTest.php |  3 ---
 Tests/RdKafkaConnectionFactoryTest.php       |  3 ---
 Tests/Spec/RdKafkaMessageTest.php            |  3 ---
 Tests/Spec/RdKafkaTopicTest.php              |  3 ---
 Tests/bootstrap.php                          |  2 +-
 11 files changed, 19 insertions(+), 42 deletions(-)

diff --git a/JsonSerializer.php b/JsonSerializer.php
index ae161ca..1d25ea5 100644
--- a/JsonSerializer.php
+++ b/JsonSerializer.php
@@ -14,12 +14,8 @@ public function toString(RdKafkaMessage $message): string
             'headers' => $message->getHeaders(),
         ]);
 
-        if (JSON_ERROR_NONE !== json_last_error()) {
-            throw new \InvalidArgumentException(sprintf(
-                'The malformed json given. Error %s and message %s',
-                json_last_error(),
-                json_last_error_msg()
-            ));
+        if (\JSON_ERROR_NONE !== json_last_error()) {
+            throw new \InvalidArgumentException(sprintf('The malformed json given. Error %s and message %s', json_last_error(), json_last_error_msg()));
         }
 
         return $json;
@@ -28,12 +24,8 @@ public function toString(RdKafkaMessage $message): string
     public function toMessage(string $string): RdKafkaMessage
     {
         $data = json_decode($string, true);
-        if (JSON_ERROR_NONE !== json_last_error()) {
-            throw new \InvalidArgumentException(sprintf(
-                'The malformed json given. Error %s and message %s',
-                json_last_error(),
-                json_last_error_msg()
-            ));
+        if (\JSON_ERROR_NONE !== json_last_error()) {
+            throw new \InvalidArgumentException(sprintf('The malformed json given. Error %s and message %s', json_last_error(), json_last_error_msg()));
         }
 
         return new RdKafkaMessage($data['body'], $data['properties'], $data['headers']);
diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index 7fbaef7..b1e9ad5 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -71,7 +71,7 @@ public function getOffset(): ?int
         return $this->offset;
     }
 
-    public function setOffset(int $offset = null): void
+    public function setOffset(?int $offset = null): void
     {
         if ($this->subscribed) {
             throw new \LogicException('The consumer has already subscribed.');
diff --git a/RdKafkaMessage.php b/RdKafkaMessage.php
index 0785a56..7c6d0d0 100644
--- a/RdKafkaMessage.php
+++ b/RdKafkaMessage.php
@@ -112,7 +112,7 @@ public function setRedelivered(bool $redelivered): void
         $this->redelivered = $redelivered;
     }
 
-    public function setCorrelationId(string $correlationId = null): void
+    public function setCorrelationId(?string $correlationId = null): void
     {
         $this->setHeader('correlation_id', (string) $correlationId);
     }
@@ -122,7 +122,7 @@ public function getCorrelationId(): ?string
         return $this->getHeader('correlation_id');
     }
 
-    public function setMessageId(string $messageId = null): void
+    public function setMessageId(?string $messageId = null): void
     {
         $this->setHeader('message_id', (string) $messageId);
     }
@@ -139,12 +139,12 @@ public function getTimestamp(): ?int
         return null === $value ? null : (int) $value;
     }
 
-    public function setTimestamp(int $timestamp = null): void
+    public function setTimestamp(?int $timestamp = null): void
     {
         $this->setHeader('timestamp', $timestamp);
     }
 
-    public function setReplyTo(string $replyTo = null): void
+    public function setReplyTo(?string $replyTo = null): void
     {
         $this->setHeader('reply_to', $replyTo);
     }
@@ -159,7 +159,7 @@ public function getPartition(): ?int
         return $this->partition;
     }
 
-    public function setPartition(int $partition = null): void
+    public function setPartition(?int $partition = null): void
     {
         $this->partition = $partition;
     }
@@ -169,7 +169,7 @@ public function getKey(): ?string
         return $this->key;
     }
 
-    public function setKey(string $key = null): void
+    public function setKey(?string $key = null): void
     {
         $this->key = $key;
     }
@@ -179,7 +179,7 @@ public function getKafkaMessage(): ?VendorMessage
         return $this->kafkaMessage;
     }
 
-    public function setKafkaMessage(VendorMessage $message = null): void
+    public function setKafkaMessage(?VendorMessage $message = null): void
     {
         $this->kafkaMessage = $message;
     }
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 64b9ea3..8d0056d 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -70,7 +70,7 @@ public function send(Destination $destination, Message $message): void
     /**
      * @return RdKafkaProducer
      */
-    public function setDeliveryDelay(int $deliveryDelay = null): Producer
+    public function setDeliveryDelay(?int $deliveryDelay = null): Producer
     {
         if (null === $deliveryDelay) {
             return $this;
@@ -87,7 +87,7 @@ public function getDeliveryDelay(): ?int
     /**
      * @return RdKafkaProducer
      */
-    public function setPriority(int $priority = null): Producer
+    public function setPriority(?int $priority = null): Producer
     {
         if (null === $priority) {
             return $this;
@@ -101,7 +101,7 @@ public function getPriority(): ?int
         return null;
     }
 
-    public function setTimeToLive(int $timeToLive = null): Producer
+    public function setTimeToLive(?int $timeToLive = null): Producer
     {
         if (null === $timeToLive) {
             return $this;
diff --git a/RdKafkaTopic.php b/RdKafkaTopic.php
index a7bde10..572f4d0 100644
--- a/RdKafkaTopic.php
+++ b/RdKafkaTopic.php
@@ -50,7 +50,7 @@ public function getConf(): ?TopicConf
         return $this->conf;
     }
 
-    public function setConf(TopicConf $conf = null): void
+    public function setConf(?TopicConf $conf = null): void
     {
         $this->conf = $conf;
     }
@@ -60,7 +60,7 @@ public function getPartition(): ?int
         return $this->partition;
     }
 
-    public function setPartition(int $partition = null): void
+    public function setPartition(?int $partition = null): void
     {
         $this->partition = $partition;
     }
@@ -70,7 +70,7 @@ public function getKey(): ?string
         return $this->key;
     }
 
-    public function setKey(string $key = null): void
+    public function setKey(?string $key = null): void
     {
         $this->key = $key;
     }
diff --git a/SerializerAwareTrait.php b/SerializerAwareTrait.php
index d640f93..1cd1fac 100644
--- a/SerializerAwareTrait.php
+++ b/SerializerAwareTrait.php
@@ -11,9 +11,6 @@ trait SerializerAwareTrait
      */
     private $serializer;
 
-    /**
-     * @param Serializer $serializer
-     */
     public function setSerializer(Serializer $serializer)
     {
         $this->serializer = $serializer;
diff --git a/Tests/RdKafkaConnectionFactoryConfigTest.php b/Tests/RdKafkaConnectionFactoryConfigTest.php
index 0a50218..7ecb1bd 100644
--- a/Tests/RdKafkaConnectionFactoryConfigTest.php
+++ b/Tests/RdKafkaConnectionFactoryConfigTest.php
@@ -33,9 +33,6 @@ public function testThrowIfSchemeIsNotSupported()
 
     /**
      * @dataProvider provideConfigs
-     *
-     * @param mixed $config
-     * @param mixed $expectedConfig
      */
     public function testShouldParseConfigurationAsExpected($config, $expectedConfig)
     {
diff --git a/Tests/RdKafkaConnectionFactoryTest.php b/Tests/RdKafkaConnectionFactoryTest.php
index 62ccc76..d7121da 100644
--- a/Tests/RdKafkaConnectionFactoryTest.php
+++ b/Tests/RdKafkaConnectionFactoryTest.php
@@ -70,9 +70,6 @@ public function testShouldBeExpectedDefaultDsnConfig()
 
     /**
      * @dataProvider provideConfigs
-     *
-     * @param mixed $config
-     * @param mixed $expectedConfig
      */
     public function testShouldParseConfigurationAsExpected($config, $expectedConfig)
     {
diff --git a/Tests/Spec/RdKafkaMessageTest.php b/Tests/Spec/RdKafkaMessageTest.php
index df54fa5..9e230d1 100644
--- a/Tests/Spec/RdKafkaMessageTest.php
+++ b/Tests/Spec/RdKafkaMessageTest.php
@@ -7,9 +7,6 @@
 
 class RdKafkaMessageTest extends MessageSpec
 {
-    /**
-     * {@inheritdoc}
-     */
     protected function createMessage()
     {
         return new RdKafkaMessage();
diff --git a/Tests/Spec/RdKafkaTopicTest.php b/Tests/Spec/RdKafkaTopicTest.php
index c5d43ee..08d4278 100644
--- a/Tests/Spec/RdKafkaTopicTest.php
+++ b/Tests/Spec/RdKafkaTopicTest.php
@@ -7,9 +7,6 @@
 
 class RdKafkaTopicTest extends TopicSpec
 {
-    /**
-     * {@inheritdoc}
-     */
     protected function createTopic()
     {
         return new RdKafkaTopic(self::EXPECTED_TOPIC_NAME);
diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php
index bf11262..60f8101 100644
--- a/Tests/bootstrap.php
+++ b/Tests/bootstrap.php
@@ -8,7 +8,7 @@
 if (false == file_exists($kafkaStubsDir)) {
     $kafkaStubsDir = __DIR__.'/../../../vendor/kwn/php-rdkafka-stubs';
     if (false == file_exists($kafkaStubsDir)) {
-        throw new \LogicException('The kafka extension is not loaded and stubs could not be found as well');
+        throw new LogicException('The kafka extension is not loaded and stubs could not be found as well');
     }
 }
 

From 9236342a9f316d4198b995b67f8b9cbb59981853 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Fri, 17 Jan 2025 21:52:03 +0000
Subject: [PATCH 098/101] More CS Fixes

---
 RdKafkaConsumer.php           |  8 ++++----
 RdKafkaContext.php            |  6 +++---
 RdKafkaProducer.php           |  2 +-
 Tests/RdKafkaConsumerTest.php | 12 ++++++------
 Tests/RdKafkaProducerTest.php |  8 ++++----
 5 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/RdKafkaConsumer.php b/RdKafkaConsumer.php
index b1e9ad5..8b6cf12 100644
--- a/RdKafkaConsumer.php
+++ b/RdKafkaConsumer.php
@@ -167,11 +167,11 @@ private function doReceive(int $timeout): ?RdKafkaMessage
         }
 
         switch ($kafkaMessage->err) {
-            case RD_KAFKA_RESP_ERR__PARTITION_EOF:
-            case RD_KAFKA_RESP_ERR__TIMED_OUT:
-            case RD_KAFKA_RESP_ERR__TRANSPORT:
+            case \RD_KAFKA_RESP_ERR__PARTITION_EOF:
+            case \RD_KAFKA_RESP_ERR__TIMED_OUT:
+            case \RD_KAFKA_RESP_ERR__TRANSPORT:
                 return null;
-            case RD_KAFKA_RESP_ERR_NO_ERROR:
+            case \RD_KAFKA_RESP_ERR_NO_ERROR:
                 $message = $this->serializer->toMessage($kafkaMessage->payload);
                 $message->setKey($kafkaMessage->key);
                 $message->setPartition($kafkaMessage->partition);
diff --git a/RdKafkaContext.php b/RdKafkaContext.php
index 9930b56..a252fcf 100644
--- a/RdKafkaContext.php
+++ b/RdKafkaContext.php
@@ -173,9 +173,9 @@ public static function getLibrdKafkaVersion(): string
         if (!defined('RD_KAFKA_VERSION')) {
             throw new \RuntimeException('RD_KAFKA_VERSION constant is not defined. Phprdkafka is probably not installed');
         }
-        $major = (RD_KAFKA_VERSION & 0xFF000000) >> 24;
-        $minor = (RD_KAFKA_VERSION & 0x00FF0000) >> 16;
-        $patch = (RD_KAFKA_VERSION & 0x0000FF00) >> 8;
+        $major = (\RD_KAFKA_VERSION & 0xFF000000) >> 24;
+        $minor = (\RD_KAFKA_VERSION & 0x00FF0000) >> 16;
+        $patch = (\RD_KAFKA_VERSION & 0x0000FF00) >> 8;
 
         return "$major.$minor.$patch";
     }
diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index 8d0056d..c70f1fb 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -37,7 +37,7 @@ public function send(Destination $destination, Message $message): void
         InvalidDestinationException::assertDestinationInstanceOf($destination, RdKafkaTopic::class);
         InvalidMessageException::assertMessageInstanceOf($message, RdKafkaMessage::class);
 
-        $partition = $message->getPartition() ?? $destination->getPartition() ?? RD_KAFKA_PARTITION_UA;
+        $partition = $message->getPartition() ?? $destination->getPartition() ?? \RD_KAFKA_PARTITION_UA;
         $payload = $this->serializer->toString($message);
         $key = $message->getKey() ?? $destination->getKey() ?? null;
 
diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index 74e9295..a0544da 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -32,7 +32,7 @@ public function testShouldReceiveFromQueueAndReturnNullIfNoMessageInQueue()
         $destination = new RdKafkaTopic('dest');
 
         $kafkaMessage = new Message();
-        $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
+        $kafkaMessage->err = \RD_KAFKA_RESP_ERR__TIMED_OUT;
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
@@ -61,7 +61,7 @@ public function testShouldPassProperlyConfiguredTopicPartitionOnAssign()
         $destination = new RdKafkaTopic('dest');
 
         $kafkaMessage = new Message();
-        $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
+        $kafkaMessage->err = \RD_KAFKA_RESP_ERR__TIMED_OUT;
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
@@ -91,7 +91,7 @@ public function testShouldSubscribeOnFirstReceiveOnly()
         $destination = new RdKafkaTopic('dest');
 
         $kafkaMessage = new Message();
-        $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
+        $kafkaMessage->err = \RD_KAFKA_RESP_ERR__TIMED_OUT;
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
@@ -122,7 +122,7 @@ public function testShouldAssignWhenOffsetIsSet()
         $destination->setPartition(1);
 
         $kafkaMessage = new Message();
-        $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
+        $kafkaMessage->err = \RD_KAFKA_RESP_ERR__TIMED_OUT;
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
@@ -154,7 +154,7 @@ public function testThrowOnOffsetChangeAfterSubscribing()
         $destination = new RdKafkaTopic('dest');
 
         $kafkaMessage = new Message();
-        $kafkaMessage->err = RD_KAFKA_RESP_ERR__TIMED_OUT;
+        $kafkaMessage->err = \RD_KAFKA_RESP_ERR__TIMED_OUT;
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
@@ -188,7 +188,7 @@ public function testShouldReceiveFromQueueAndReturnMessageIfMessageInQueue()
         $expectedMessage = new RdKafkaMessage('theBody', ['foo' => 'fooVal'], ['bar' => 'barVal']);
 
         $kafkaMessage = new Message();
-        $kafkaMessage->err = RD_KAFKA_RESP_ERR_NO_ERROR;
+        $kafkaMessage->err = \RD_KAFKA_RESP_ERR_NO_ERROR;
         $kafkaMessage->payload = 'theSerializedMessage';
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
diff --git a/Tests/RdKafkaProducerTest.php b/Tests/RdKafkaProducerTest.php
index 6082968..6295fbc 100644
--- a/Tests/RdKafkaProducerTest.php
+++ b/Tests/RdKafkaProducerTest.php
@@ -46,7 +46,7 @@ public function testShouldUseSerializerToEncodeMessageAndPutToExpectedTube()
             ->expects($this->once())
             ->method('producev')
             ->with(
-                RD_KAFKA_PARTITION_UA,
+                \RD_KAFKA_PARTITION_UA,
                 0,
                 'theSerializedMessage',
                 'key',
@@ -183,7 +183,7 @@ public function testShouldAllowSerializersToSerializeKeys()
             ->expects($this->once())
             ->method('producev')
             ->with(
-                RD_KAFKA_PARTITION_UA,
+                \RD_KAFKA_PARTITION_UA,
                 0,
                 'theSerializedMessage',
                 'theSerializedKey'
@@ -324,7 +324,7 @@ public function testShouldAllowFalsyKeyFromMessage(): void
             ->expects($this->once())
             ->method('producev')
             ->with(
-                RD_KAFKA_PARTITION_UA,
+                \RD_KAFKA_PARTITION_UA,
                 0,
                 '',
                 $key
@@ -354,7 +354,7 @@ public function testShouldAllowFalsyKeyFromDestination(): void
             ->expects($this->once())
             ->method('producev')
             ->with(
-                RD_KAFKA_PARTITION_UA,
+                \RD_KAFKA_PARTITION_UA,
                 0,
                 '',
                 $key

From b42c0d4388416bf20ac7f7e8741e4d923cedf865 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Thu, 16 Jan 2025 22:20:11 +0000
Subject: [PATCH 099/101] Fixing static analysis issues

Fixing issue raised by phpstan
---
 RdKafkaProducer.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/RdKafkaProducer.php b/RdKafkaProducer.php
index c70f1fb..24589b3 100644
--- a/RdKafkaProducer.php
+++ b/RdKafkaProducer.php
@@ -121,5 +121,7 @@ public function flush(int $timeout): ?int
         if (method_exists($this->producer, 'flush')) {
             return $this->producer->flush($timeout);
         }
+
+        return null;
     }
 }

From 593608fa934ee50c0e75b7e350fcb18308291e75 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Sun, 27 Apr 2025 14:30:31 +0100
Subject: [PATCH 100/101] Updating composer

Updating composer.json in pkg directory to correctly reflect the
supported versions of PHP
---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 068b9d2..64afe05 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.4|^8.0",
+        "php": "^8.1",
         "ext-rdkafka": "^4.0|^5.0|^6.0",
         "queue-interop/queue-interop": "^0.8.1"
     },

From 8fd7f62b25c0f9ecbb0585ab65301f451d0fa5ae Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Sat, 19 Apr 2025 00:22:06 +0100
Subject: [PATCH 101/101] Fixing CI

There's an issue with the downstream PPA "ondrej/php" where is cannot find the gearman
package.

Refactoring Dockerfile to use standard base images as a workaround
---
 Tests/RdKafkaConsumerTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Tests/RdKafkaConsumerTest.php b/Tests/RdKafkaConsumerTest.php
index a0544da..e577dfc 100644
--- a/Tests/RdKafkaConsumerTest.php
+++ b/Tests/RdKafkaConsumerTest.php
@@ -190,6 +190,7 @@ public function testShouldReceiveFromQueueAndReturnMessageIfMessageInQueue()
         $kafkaMessage = new Message();
         $kafkaMessage->err = \RD_KAFKA_RESP_ERR_NO_ERROR;
         $kafkaMessage->payload = 'theSerializedMessage';
+        $kafkaMessage->partition = 0;
 
         $kafkaConsumer = $this->createKafkaConsumerMock();
         $kafkaConsumer
