├── RabbitMq
├── Exception
│ ├── QueueNotFoundException.php
│ ├── AckStopConsumerException.php
│ └── StopConsumerException.php
├── DequeuerAwareInterface.php
├── Fallback.php
├── BatchConsumerInterface.php
├── ProducerInterface.php
├── AmqpPartsHolder.php
├── DequeuerInterface.php
├── AnonConsumer.php
├── ConsumerInterface.php
├── AMQPLoggedChannel.php
├── RpcServer.php
├── DynamicConsumer.php
├── Producer.php
├── Binding.php
├── MultipleConsumer.php
├── RpcClient.php
├── BaseConsumer.php
├── AMQPConnectionFactory.php
├── BaseAmqp.php
├── Consumer.php
└── BatchConsumer.php
├── Event
├── AbstractAMQPEvent.php
├── OnConsumeEvent.php
├── AfterProcessingMessageEvent.php
├── BeforeProcessingMessageEvent.php
├── OnIdleEvent.php
├── AfterProducerPublishMessageEvent.php
├── BeforeProducerPublishMessageEvent.php
└── AMQPEvent.php
├── CODEOWNERS
├── SECURITY.md
├── MemoryChecker
├── NativeMemoryUsageProvider.php
└── MemoryConsumptionChecker.php
├── Command
├── ConsumerCommand.php
├── AnonConsumerCommand.php
├── BaseRabbitMqCommand.php
├── MultipleConsumerCommand.php
├── DynamicConsumerCommand.php
├── SetupFabricCommand.php
├── RpcServerCommand.php
├── DeleteCommand.php
├── PurgeConsumerCommand.php
├── StdInProducerCommand.php
├── BaseConsumerCommand.php
└── BatchConsumerCommand.php
├── Provider
├── QueueOptionsProviderInterface.php
├── QueuesProviderInterface.php
└── ConnectionParametersProviderInterface.php
├── DependencyInjection
├── Compiler
│ ├── ServiceContainerPass.php
│ ├── InjectEventDispatcherPass.php
│ └── RegisterPartsPass.php
├── Configuration.php
└── OldSoundRabbitMqExtension.php
├── LICENSE
├── DataCollector
└── MessageDataCollector.php
├── OldSoundRabbitMqBundle.php
├── phpstan.neon.dist
├── composer.json
├── CHANGELOG
└── Resources
├── views
└── Collector
│ └── collector.html.twig
└── config
└── rabbitmq.xml
/RabbitMq/Exception/QueueNotFoundException.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class NativeMemoryUsageProvider
11 | {
12 | /**
13 | * @return int
14 | */
15 | public function getMemoryUsage()
16 | {
17 | return memory_get_usage(true);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Command/ConsumerCommand.php:
--------------------------------------------------------------------------------
1 | setDescription('Executes a consumer');
11 | $this->setName('rabbitmq:consumer');
12 | }
13 |
14 | protected function getConsumerService()
15 | {
16 | return 'old_sound_rabbit_mq.%s_consumer';
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/RabbitMq/AmqpPartsHolder.php:
--------------------------------------------------------------------------------
1 | parts = [];
12 | }
13 |
14 | public function addPart($type, BaseAmqp $part)
15 | {
16 | $this->parts[$type][] = $part;
17 | }
18 |
19 | public function getParts($type)
20 | {
21 | $type = (string) $type;
22 | return $this->parts[$type] ?? [];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Event/OnConsumeEvent.php:
--------------------------------------------------------------------------------
1 | setConsumer($consumer);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Provider/QueueOptionsProviderInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | interface QueueOptionsProviderInterface
11 | {
12 | /**
13 | * Return queue options
14 | *
15 | * Example:
16 | * array(
17 | * 'name' => 'example_context',
18 | * 'durable' => true,
19 | * 'routing_keys' => array('key.*')
20 | * )
21 | *
22 | * @return array
23 | *
24 | */
25 | public function getQueueOptions($context = null);
26 | }
27 |
--------------------------------------------------------------------------------
/RabbitMq/DequeuerInterface.php:
--------------------------------------------------------------------------------
1 | setName('rabbitmq:anon-consumer');
12 | $this->setDescription('Executes an anonymous consumer');
13 | $this->getDefinition()->getOption('messages')->setDefault('1');
14 | $this->getDefinition()->getOption('route')->setDefault('#');
15 | }
16 |
17 | protected function getConsumerService()
18 | {
19 | return 'old_sound_rabbit_mq.%s_anon';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/RabbitMq/AnonConsumer.php:
--------------------------------------------------------------------------------
1 | setQueueOptions([
14 | 'name' => '',
15 | 'passive' => false,
16 | 'durable' => false,
17 | 'exclusive' => true,
18 | 'auto_delete' => true,
19 | 'nowait' => false,
20 | 'arguments' => null,
21 | 'ticket' => null,
22 | ]);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RabbitMq/Exception/StopConsumerException.php:
--------------------------------------------------------------------------------
1 | container = $container;
18 | }
19 |
20 | /**
21 | * @return ContainerInterface
22 | */
23 | public function getContainer()
24 | {
25 | return $this->container;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Event/AfterProcessingMessageEvent.php:
--------------------------------------------------------------------------------
1 | setConsumer($consumer);
25 | $this->setAMQPMessage($AMQPMessage);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Event/BeforeProcessingMessageEvent.php:
--------------------------------------------------------------------------------
1 | setConsumer($consumer);
25 | $this->setAMQPMessage($AMQPMessage);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Provider/QueuesProviderInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | interface QueuesProviderInterface
11 | {
12 | /**
13 | * Return array of queues
14 | *
15 | * Example:
16 | * array(
17 | * 'queue_name' => array(
18 | * 'durable' => false,
19 | * 'exclusive' => false,
20 | * 'passive' => false,
21 | * 'nowait' => false,
22 | * 'auto_delete' => false,
23 | * 'routing_keys' => array('key.1', 'key.2'),
24 | * 'arguments' => array(),
25 | * 'ticket' => '',
26 | * 'callback' => array($callback, 'execute')
27 | * )
28 | * );
29 | * @return array
30 | *
31 | */
32 | public function getQueues();
33 | }
34 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/ServiceContainerPass.php:
--------------------------------------------------------------------------------
1 | findTaggedServiceIds('console.command') as $id => $attributes) {
15 | $command = $container->findDefinition($id);
16 | if (is_a($command->getClass(), BaseRabbitMqCommand::class, true)) {
17 | $command->addMethodCall('setContainer', [new Reference('service_container')]);
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Command/MultipleConsumerCommand.php:
--------------------------------------------------------------------------------
1 | setDescription('Executes a consumer that uses multiple queues')
15 | ->setName('rabbitmq:multiple-consumer')
16 | ->addArgument('context', InputArgument::OPTIONAL, 'Context the consumer runs in')
17 | ;
18 | }
19 |
20 | protected function getConsumerService()
21 | {
22 | return 'old_sound_rabbit_mq.%s_multiple';
23 | }
24 |
25 | protected function initConsumer(InputInterface $input)
26 | {
27 | parent::initConsumer($input);
28 | $this->consumer->setContext($input->getArgument('context'));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/RabbitMq/ConsumerInterface.php:
--------------------------------------------------------------------------------
1 | setConsumer($consumer);
29 |
30 | $this->forceStop = true;
31 | }
32 |
33 | /**
34 | * @return boolean
35 | */
36 | public function isForceStop()
37 | {
38 | return $this->forceStop;
39 | }
40 |
41 | /**
42 | * @param boolean $forceStop
43 | */
44 | public function setForceStop($forceStop)
45 | {
46 | $this->forceStop = $forceStop;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/RabbitMq/AMQPLoggedChannel.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class AMQPLoggedChannel extends AMQPChannel
13 | {
14 | private $basicPublishLog = [];
15 |
16 | public function basic_publish($msg, $exchange = '', $routingKey = '', $mandatory = false, $immediate = false, $ticket = null)
17 | {
18 | $this->basicPublishLog[] = [
19 | 'msg' => $msg,
20 | 'exchange' => $exchange,
21 | 'routing_key' => $routingKey,
22 | 'mandatory' => $mandatory,
23 | 'immediate' => $immediate,
24 | 'ticket' => $ticket,
25 | ];
26 |
27 | parent::basic_publish($msg, $exchange, $routingKey, $mandatory, $immediate, $ticket);
28 | }
29 |
30 | public function getBasicPublishLog()
31 | {
32 | return $this->basicPublishLog;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Event/AfterProducerPublishMessageEvent.php:
--------------------------------------------------------------------------------
1 | setProducer($producer);
30 | $this->setAMQPMessage($AMQPMessage);
31 | $this->routingKey = $routingKey;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getRoutingKey()
38 | {
39 | return $this->routingKey;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Event/BeforeProducerPublishMessageEvent.php:
--------------------------------------------------------------------------------
1 | setProducer($producer);
30 | $this->setAMQPMessage($AMQPMessage);
31 | $this->routingKey = $routingKey;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getRoutingKey()
38 | {
39 | return $this->routingKey;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Provider/ConnectionParametersProviderInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | interface ConnectionParametersProviderInterface
11 | {
12 | /**
13 | * Return connection parameters.
14 | *
15 | * Example:
16 | * array(
17 | * 'host' => 'localhost',
18 | * 'port' => 5672,
19 | * 'user' => 'guest',
20 | * 'password' => 'guest',
21 | * 'vhost' => '/',
22 | * 'lazy' => false,
23 | * 'connection_timeout' => 3,
24 | * 'read_write_timeout' => 3,
25 | * 'keepalive' => false,
26 | * 'heartbeat' => 0,
27 | * 'use_socket' => true,
28 | * 'constructor_args' => array(...)
29 | * )
30 | *
31 | * If constructor_args is present, all the other parameters are ignored; constructor_args are passes as constructor
32 | * arguments.
33 | *
34 | * @return array
35 | */
36 | public function getConnectionParameters();
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2010 Alvaro Videla
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Command/DynamicConsumerCommand.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 |
12 | namespace OldSound\RabbitMqBundle\Command;
13 |
14 | use Symfony\Component\Console\Input\InputArgument;
15 | use Symfony\Component\Console\Input\InputInterface;
16 |
17 | class DynamicConsumerCommand extends BaseConsumerCommand
18 | {
19 | protected function configure(): void
20 | {
21 | parent::configure();
22 |
23 | $this
24 | ->setName('rabbitmq:dynamic-consumer')
25 | ->setDescription('Executes context-aware consumer')
26 | ->addArgument('context', InputArgument::REQUIRED, 'Context the consumer runs in')
27 | ;
28 | }
29 |
30 | protected function getConsumerService()
31 | {
32 | return 'old_sound_rabbit_mq.%s_dynamic';
33 | }
34 |
35 | protected function initConsumer(InputInterface $input)
36 | {
37 | parent::initConsumer($input);
38 | $this->consumer->setContext($input->getArgument('context'));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/DataCollector/MessageDataCollector.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class MessageDataCollector extends DataCollector
15 | {
16 | private $channels;
17 |
18 | public function __construct($channels)
19 | {
20 | $this->channels = $channels;
21 | $this->data = [];
22 | }
23 |
24 | public function collect(Request $request, Response $response, ?\Throwable $exception = null)
25 | {
26 | foreach ($this->channels as $channel) {
27 | foreach ($channel->getBasicPublishLog() as $log) {
28 | $this->data[] = $log;
29 | }
30 | }
31 | }
32 |
33 | public function getName()
34 | {
35 | return 'rabbit_mq';
36 | }
37 |
38 | public function getPublishedMessagesCount()
39 | {
40 | return count($this->data);
41 | }
42 |
43 | public function getPublishedMessagesLog()
44 | {
45 | return $this->data;
46 | }
47 |
48 | public function reset()
49 | {
50 | $this->data = [];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/InjectEventDispatcherPass.php:
--------------------------------------------------------------------------------
1 | has(self::EVENT_DISPATCHER_SERVICE_ID)) {
22 | return;
23 | }
24 | $taggedConsumers = $container->findTaggedServiceIds('old_sound_rabbit_mq.base_amqp');
25 |
26 | foreach ($taggedConsumers as $id => $tag) {
27 | $definition = $container->getDefinition($id);
28 | $definition->addMethodCall(
29 | 'setEventDispatcher',
30 | [
31 | new Reference(self::EVENT_DISPATCHER_SERVICE_ID, ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
32 | ]
33 | );
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/OldSoundRabbitMqBundle.php:
--------------------------------------------------------------------------------
1 | addCompilerPass(new RegisterPartsPass());
18 | $container->addCompilerPass(new InjectEventDispatcherPass());
19 | $container->addCompilerPass(new ServiceContainerPass());
20 | }
21 |
22 | /**
23 | * {@inheritDoc}
24 | */
25 | public function shutdown(): void
26 | {
27 | parent::shutdown();
28 | if (!$this->container->hasParameter('old_sound_rabbit_mq.base_amqp')) {
29 | return;
30 | }
31 | $connections = $this->container->getParameter('old_sound_rabbit_mq.base_amqp');
32 | foreach ($connections as $connection) {
33 | if ($this->container->initialized($connection)) {
34 | $this->container->get($connection)->close();
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/RabbitMq/RpcServer.php:
--------------------------------------------------------------------------------
1 | setExchangeOptions(['name' => $name, 'type' => 'direct']);
14 | $this->setQueueOptions(['name' => $name . '-queue']);
15 | }
16 |
17 | public function processMessage(AMQPMessage $msg)
18 | {
19 | try {
20 | $msg->ack();
21 | $result = call_user_func($this->callback, $msg);
22 | $result = call_user_func($this->serializer, $result);
23 | $this->sendReply($result, $msg->get('reply_to'), $msg->get('correlation_id'));
24 | $this->consumed++;
25 | $this->maybeStopConsumer();
26 | } catch (\Exception $e) {
27 | $this->sendReply('error: ' . $e->getMessage(), $msg->get('reply_to'), $msg->get('correlation_id'));
28 | }
29 | }
30 |
31 | protected function sendReply($result, $client, $correlationId)
32 | {
33 | $reply = new AMQPMessage($result, ['content_type' => 'text/plain', 'correlation_id' => $correlationId]);
34 | $this->getChannel()->basic_publish($reply, '', $client);
35 | }
36 |
37 | public function setSerializer($serializer)
38 | {
39 | $this->serializer = $serializer;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/phpstan.neon.dist:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/phpstan/phpstan-phpunit/extension.neon
3 | - vendor/phpstan/phpstan-phpunit/rules.neon
4 | parameters:
5 | level: 5
6 | reportUnmatchedIgnoredErrors: false
7 | paths:
8 | - Command
9 | - DataCollector
10 | - DependencyInjection
11 | - Event
12 | - MemoryChecker
13 | - Provider
14 | - RabbitMq
15 | - Resources
16 | - Tests
17 | - OldSoundRabbitMqBundle.php
18 | ignoreErrors:
19 | - '#Call to an undefined method Symfony\\Component\\DependencyInjection\\Definition::((setFactoryService)|(setFactoryMethod))\(\)\.#'
20 | - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::((children)|(append))\(\)\.#'
21 | - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::((booleanNode)|(scalarNode))\(\)#'
22 | - '#Parameter \#1 \$node of method OldSound\\RabbitMqBundle\\DependencyInjection\\Configuration::addQueueNodeConfiguration\(\) expects Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition, Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition given\.#'
23 | - '#Method Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface::dispatch\(\) invoked with 2 parameters, 1 required\.#'
24 | - "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#"
--------------------------------------------------------------------------------
/RabbitMq/DynamicConsumer.php:
--------------------------------------------------------------------------------
1 | queueOptionsProvider = $queueOptionsProvider;
33 | return $this;
34 | }
35 |
36 | public function setContext($context)
37 | {
38 | $this->context = $context;
39 | }
40 |
41 |
42 | protected function setupConsumer()
43 | {
44 | $this->mergeQueueOptions();
45 | parent::setupConsumer();
46 | }
47 |
48 | protected function mergeQueueOptions()
49 | {
50 | if (null === $this->queueOptionsProvider) {
51 | return;
52 | }
53 | $this->queueOptions = array_merge($this->queueOptions, $this->queueOptionsProvider->getQueueOptions($this->context));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Command/SetupFabricCommand.php:
--------------------------------------------------------------------------------
1 | setName('rabbitmq:setup-fabric')
16 | ->setDescription('Sets up the Rabbit MQ fabric')
17 | ->addOption('debug', 'd', InputOption::VALUE_NONE, 'Enable Debugging')
18 | ;
19 | }
20 |
21 | protected function execute(InputInterface $input, OutputInterface $output): int
22 | {
23 | if (defined('AMQP_DEBUG') === false) {
24 | define('AMQP_DEBUG', (bool) $input->getOption('debug'));
25 | }
26 |
27 | $output->writeln('Setting up the Rabbit MQ fabric');
28 |
29 | $partsHolder = $this->getContainer()->get('old_sound_rabbit_mq.parts_holder');
30 |
31 | foreach (['base_amqp', 'binding'] as $key) {
32 | foreach ($partsHolder->getParts('old_sound_rabbit_mq.' . $key) as $baseAmqp) {
33 | if ($baseAmqp instanceof DynamicConsumer) {
34 | continue;
35 | }
36 | $baseAmqp->setupFabric();
37 | }
38 | }
39 |
40 | return 0;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/RegisterPartsPass.php:
--------------------------------------------------------------------------------
1 | findTaggedServiceIds('old_sound_rabbit_mq.base_amqp');
14 | $container->setParameter('old_sound_rabbit_mq.base_amqp', array_keys($services));
15 | if (!$container->hasDefinition('old_sound_rabbit_mq.parts_holder')) {
16 | return;
17 | }
18 |
19 | $definition = $container->getDefinition('old_sound_rabbit_mq.parts_holder');
20 |
21 | $tags = [
22 | 'old_sound_rabbit_mq.base_amqp',
23 | 'old_sound_rabbit_mq.binding',
24 | 'old_sound_rabbit_mq.producer',
25 | 'old_sound_rabbit_mq.consumer',
26 | 'old_sound_rabbit_mq.multi_consumer',
27 | 'old_sound_rabbit_mq.anon_consumer',
28 | 'old_sound_rabbit_mq.batch_consumer',
29 | 'old_sound_rabbit_mq.rpc_client',
30 | 'old_sound_rabbit_mq.rpc_server',
31 | ];
32 |
33 | foreach ($tags as $tag) {
34 | foreach ($container->findTaggedServiceIds($tag) as $id => $attributes) {
35 | $definition->addMethodCall('addPart', [$tag, new Reference($id)]);
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Command/RpcServerCommand.php:
--------------------------------------------------------------------------------
1 | setName('rabbitmq:rpc-server')
18 | ->setDescription('Start an RPC server')
19 | ->addArgument('name', InputArgument::REQUIRED, 'Server Name')
20 | ->addOption('messages', 'm', InputOption::VALUE_OPTIONAL, 'Messages to consume', '0')
21 | ->addOption('debug', 'd', InputOption::VALUE_OPTIONAL, 'Debug mode', false)
22 | ;
23 | }
24 |
25 | /**
26 | * Executes the current command.
27 | *
28 | * @param InputInterface $input An InputInterface instance
29 | * @param OutputInterface $output An OutputInterface instance
30 | *
31 | * @return integer 0 if everything went fine, or an error code
32 | *
33 | * @throws \InvalidArgumentException When the number of messages to consume is less than 0
34 | */
35 | protected function execute(InputInterface $input, OutputInterface $output): int
36 | {
37 | define('AMQP_DEBUG', (bool) $input->getOption('debug'));
38 | $amount = (int)$input->getOption('messages');
39 |
40 | if (0 > $amount) {
41 | throw new \InvalidArgumentException("The -m option should be null or greater than 0");
42 | }
43 |
44 | $this->getContainer()
45 | ->get(sprintf('old_sound_rabbit_mq.%s_server', $input->getArgument('name')))
46 | ->start($amount);
47 |
48 | return 0;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/MemoryChecker/MemoryConsumptionChecker.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class MemoryConsumptionChecker
11 | {
12 | /** @var NativeMemoryUsageProvider */
13 | private $memoryUsageProvider;
14 |
15 | /**
16 | * MemoryManager constructor.
17 | *
18 | * @param NativeMemoryUsageProvider $memoryUsageProvider
19 | */
20 | public function __construct(NativeMemoryUsageProvider $memoryUsageProvider)
21 | {
22 | $this->memoryUsageProvider = $memoryUsageProvider;
23 | }
24 |
25 | /**
26 | * @param int|string $allowedConsumptionUntil
27 | * @param int|string $maxConsumptionAllowed
28 | *
29 | * @return bool
30 | */
31 | public function isRamAlmostOverloaded($maxConsumptionAllowed, $allowedConsumptionUntil = 0)
32 | {
33 | $allowedConsumptionUntil = $this->convertHumanUnitToNumerical($allowedConsumptionUntil);
34 | $maxConsumptionAllowed = $this->convertHumanUnitToNumerical($maxConsumptionAllowed);
35 | $currentUsage = $this->convertHumanUnitToNumerical($this->memoryUsageProvider->getMemoryUsage());
36 |
37 | return $currentUsage > ($maxConsumptionAllowed - $allowedConsumptionUntil);
38 | }
39 |
40 | /**
41 | * @param int|string $humanUnit
42 | *
43 | * @return int
44 | */
45 | private function convertHumanUnitToNumerical($humanUnit)
46 | {
47 | $numerical = $humanUnit;
48 | if (!is_numeric($humanUnit)) {
49 | $numerical = (int) substr($numerical, 0, -1);
50 | switch (substr($humanUnit, -1)) {
51 | case 'G':
52 | $numerical *= pow(1024, 3);
53 | break;
54 | case 'M':
55 | $numerical *= pow(1024, 2);
56 | break;
57 | case 'K':
58 | $numerical *= 1024;
59 | break;
60 | }
61 | }
62 |
63 | return (int)$numerical;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php-amqplib/rabbitmq-bundle",
3 | "type": "symfony-bundle",
4 | "description": "Integrates php-amqplib with Symfony & RabbitMq. Formerly emag-tech-labs/rabbitmq-bundle, oldsound/rabbitmq-bundle.",
5 | "keywords": ["symfony", "symfony4", "symfony5", "rabbitmq", "message", "queue", "amqp"],
6 | "license": "MIT",
7 | "authors": [{
8 | "name" : "Alvaro Videla"
9 | }],
10 | "require": {
11 | "php": "^7.4|^8.0",
12 |
13 | "symfony/dependency-injection": "^4.4|^5.3|^6.0|^7.0",
14 | "symfony/event-dispatcher": "^4.4|^5.3|^6.0|^7.0",
15 | "symfony/config": "^4.4|^5.3|^6.0|^7.0",
16 | "symfony/yaml": "^4.4|^5.3|^6.0|^7.0",
17 | "symfony/console": "^4.4|^5.3|^6.0|^7.0",
18 | "php-amqplib/php-amqplib": "^2.12.2|^3.0",
19 | "psr/log": "^1.0 || ^2.0 || ^3.0",
20 | "symfony/http-kernel": "^4.4|^5.3|^6.0|^7.0",
21 | "symfony/framework-bundle": "^4.4|^5.3|^6.0|^7.0"
22 | },
23 | "require-dev": {
24 | "symfony/serializer": "^4.4|^5.3|^6.0|^7.0",
25 | "phpunit/phpunit": "^9.5",
26 | "phpstan/phpstan": "^1.2",
27 | "phpstan/phpstan-phpunit": "^1.0"
28 | },
29 | "replace": {
30 | "oldsound/rabbitmq-bundle": "self.version",
31 | "emag-tech-labs/rabbitmq-bundle": "self.version"
32 | },
33 | "suggest": {
34 | "ext-pcntl": "*",
35 | "symfony/framework-bundle": "To use this lib as a full Symfony Bundle and to use the profiler data collector"
36 | },
37 | "extra": {
38 | "branch-alias": {
39 | "dev-master": "1.10.x-dev"
40 | }
41 | },
42 | "autoload": {
43 | "psr-4": {
44 | "OldSound\\RabbitMqBundle\\": ""
45 | },
46 | "exclude-from-classmap": [
47 | "/Tests/"
48 | ]
49 | },
50 | "autoload-dev": {
51 | "psr-4": {
52 | "OldSound\\RabbitMqBundle\\Tests\\": "Tests/"
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Command/DeleteCommand.php:
--------------------------------------------------------------------------------
1 | addArgument('name', InputArgument::REQUIRED, 'Consumer Name')
19 | ->setDescription('Delete a consumer\'s queue')
20 | ->addOption('no-confirmation', null, InputOption::VALUE_NONE, 'Whether it must be confirmed before deleting');
21 |
22 | $this->setName('rabbitmq:delete');
23 | }
24 |
25 | protected function initialize(InputInterface $input, OutputInterface $output): void
26 | {
27 | // nothing to initialize here as BaseConsumerCommand initializes on option that is not available here
28 | }
29 |
30 | /**
31 | * @param InputInterface $input
32 | * @param OutputInterface $output
33 | *
34 | * @return int
35 | */
36 | protected function execute(InputInterface $input, OutputInterface $output): int
37 | {
38 | $noConfirmation = (bool) $input->getOption('no-confirmation');
39 |
40 | if (!$noConfirmation && $input->isInteractive()) {
41 | $question = new ConfirmationQuestion(
42 | sprintf(
43 | 'Are you sure you wish to delete "%s" consumer\'s queue? (y/n)',
44 | $input->getArgument('name')
45 | ),
46 | false
47 | );
48 |
49 | if (!$this->getHelper('question')->ask($input, $output, $question)) {
50 | $output->writeln('Deletion cancelled!');
51 |
52 | return 1;
53 | }
54 | }
55 |
56 | $this->consumer = $this->getContainer()
57 | ->get(sprintf($this->getConsumerService(), $input->getArgument('name')));
58 | $this->consumer->delete();
59 |
60 | return 0;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Command/PurgeConsumerCommand.php:
--------------------------------------------------------------------------------
1 | addArgument('name', InputArgument::REQUIRED, 'Consumer Name')
19 | ->setDescription('Purge a consumer\'s queue')
20 | ->addOption('no-confirmation', null, InputOption::VALUE_NONE, 'Whether it must be confirmed before purging');
21 |
22 | $this->setName('rabbitmq:purge');
23 | }
24 |
25 | protected function initialize(InputInterface $input, OutputInterface $output): void
26 | {
27 | // nothing to initialize here as BaseConsumerCommand initializes on option that is not available here
28 | }
29 |
30 | /**
31 | * @param InputInterface $input
32 | * @param OutputInterface $output
33 | *
34 | * @return int
35 | */
36 | protected function execute(InputInterface $input, OutputInterface $output): int
37 | {
38 | $noConfirmation = (bool) $input->getOption('no-confirmation');
39 |
40 | if (!$noConfirmation && $input->isInteractive()) {
41 | $question = new ConfirmationQuestion(
42 | sprintf(
43 | 'Are you sure you wish to purge "%s" queue? (y/n)',
44 | $input->getArgument('name')
45 | ),
46 | false
47 | );
48 |
49 | if (!$this->getHelper('question')->ask($input, $output, $question)) {
50 | $output->writeln('Purging cancelled!');
51 |
52 | return 1;
53 | }
54 | }
55 |
56 | $this->consumer = $this->getContainer()
57 | ->get(sprintf($this->getConsumerService(), $input->getArgument('name')));
58 | $this->consumer->purge($input->getArgument('name'));
59 |
60 | return 0;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Event/AMQPEvent.php:
--------------------------------------------------------------------------------
1 | AMQPMessage;
45 | }
46 |
47 | /**
48 | * @param AMQPMessage $AMQPMessage
49 | *
50 | * @return AMQPEvent
51 | */
52 | public function setAMQPMessage(AMQPMessage $AMQPMessage)
53 | {
54 | $this->AMQPMessage = $AMQPMessage;
55 |
56 | return $this;
57 | }
58 |
59 | /**
60 | * @return Consumer
61 | */
62 | public function getConsumer()
63 | {
64 | return $this->consumer;
65 | }
66 |
67 | /**
68 | * @param Consumer $consumer
69 | *
70 | * @return AMQPEvent
71 | */
72 | public function setConsumer(Consumer $consumer)
73 | {
74 | $this->consumer = $consumer;
75 |
76 | return $this;
77 | }
78 |
79 | /**
80 | * @return Producer
81 | */
82 | public function getProducer()
83 | {
84 | return $this->producer;
85 | }
86 |
87 | /**
88 | * @param Producer $producer
89 | *
90 | * @return AMQPEvent
91 | */
92 | public function setProducer(Producer $producer)
93 | {
94 | $this->producer = $producer;
95 |
96 | return $this;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Command/StdInProducerCommand.php:
--------------------------------------------------------------------------------
1 | setName('rabbitmq:stdin-producer')
21 | ->addArgument('name', InputArgument::REQUIRED, 'Producer Name')
22 | ->setDescription('Executes a producer that reads data from STDIN')
23 | ->addOption('route', 'r', InputOption::VALUE_OPTIONAL, 'Routing Key', '')
24 | ->addOption('format', 'f', InputOption::VALUE_OPTIONAL, 'Payload Format', self::FORMAT_PHP)
25 | ->addOption('debug', 'd', InputOption::VALUE_OPTIONAL, 'Enable Debugging', false)
26 | ;
27 | }
28 |
29 | /**
30 | * Executes the current command.
31 | *
32 | * @param InputInterface $input An InputInterface instance
33 | * @param OutputInterface $output An OutputInterface instance
34 | *
35 | * @return integer 0 if everything went fine, or an error code
36 | */
37 | protected function execute(InputInterface $input, OutputInterface $output): int
38 | {
39 | define('AMQP_DEBUG', (bool) $input->getOption('debug'));
40 |
41 | $producer = $this->getContainer()->get(sprintf('old_sound_rabbit_mq.%s_producer', $input->getArgument('name')));
42 |
43 | $data = '';
44 | while (!feof(STDIN)) {
45 | $data .= fread(STDIN, 8192);
46 | }
47 |
48 | $route = $input->getOption('route');
49 | $format = $input->getOption('format');
50 |
51 | switch ($format) {
52 | case self::FORMAT_RAW:
53 | break; // data as is
54 | case self::FORMAT_PHP:
55 | $data = serialize($data);
56 | break;
57 | default:
58 | throw new \InvalidArgumentException(sprintf(
59 | 'Invalid payload format "%s", expecting one of: %s.',
60 | $format,
61 | implode(', ', [self::FORMAT_PHP, self::FORMAT_RAW])
62 | ));
63 | }
64 |
65 | $producer->publish($data, $route);
66 |
67 | return 0;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/RabbitMq/Producer.php:
--------------------------------------------------------------------------------
1 | contentType = $contentType;
23 |
24 | return $this;
25 | }
26 |
27 | public function setDeliveryMode($deliveryMode)
28 | {
29 | $this->deliveryMode = $deliveryMode;
30 |
31 | return $this;
32 | }
33 |
34 | public function setDefaultRoutingKey($defaultRoutingKey)
35 | {
36 | $this->defaultRoutingKey = $defaultRoutingKey;
37 |
38 | return $this;
39 | }
40 |
41 | protected function getBasicProperties()
42 | {
43 | return ['content_type' => $this->contentType, 'delivery_mode' => $this->deliveryMode];
44 | }
45 |
46 | /**
47 | * Publishes the message and merges additional properties with basic properties
48 | *
49 | * @param string $msgBody
50 | * @param string $routingKey
51 | * @param array $additionalProperties
52 | * @param array $headers
53 | */
54 | public function publish($msgBody, $routingKey = null, $additionalProperties = [], ?array $headers = null)
55 | {
56 | if ($this->autoSetupFabric) {
57 | $this->setupFabric();
58 | }
59 |
60 | $msg = new AMQPMessage((string) $msgBody, array_merge($this->getBasicProperties(), $additionalProperties));
61 |
62 | if (!empty($headers)) {
63 | $headersTable = new AMQPTable($headers);
64 | $msg->set('application_headers', $headersTable);
65 | }
66 |
67 | $real_routingKey = $routingKey !== null ? $routingKey : $this->defaultRoutingKey;
68 |
69 | $this->dispatchEvent(
70 | BeforeProducerPublishMessageEvent::NAME,
71 | new BeforeProducerPublishMessageEvent($this, $msg, $real_routingKey)
72 | );
73 |
74 | $this->getChannel()->basic_publish($msg, $this->exchangeOptions['name'], (string)$real_routingKey);
75 | $this->logger->debug('AMQP message published', [
76 | 'amqp' => [
77 | 'body' => $msgBody,
78 | 'routingkey' => $real_routingKey,
79 | 'properties' => $additionalProperties,
80 | 'headers' => $headers,
81 | ],
82 | ]);
83 |
84 | $this->dispatchEvent(
85 | AfterProducerPublishMessageEvent::NAME,
86 | new AfterProducerPublishMessageEvent($this, $msg, $real_routingKey)
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | - 2024-03-18
2 | * Add bundle configuration for `login_method` for RabbitMQ connections, this allow to configure other values than default AMQPLAIN (PLAIN or EXTERNAL), see https://www.rabbitmq.com/docs/access-control#mechanisms
3 |
4 | - 2023-11-07
5 | * Add consumer option `no_ack`
6 |
7 | - 2021-05-15
8 | * Add possibility to use multiple RabbitMQ hosts
9 |
10 | - 2017-01-22
11 | * Add `graceful_max_execution_timeout`
12 |
13 | - 2015-02-07
14 | * Added possibility to set serialize/unserialize function for rpc servers/clients
15 |
16 | - 2014-11-27
17 | * Added interface `OldSound\RabbitMqBundle\Provider\QueuesProviderInterface`
18 | * Added `queues_provider` configuration for multiple consumer
19 |
20 | - 2014-07-21
21 | * Added `reconnect` method into `OldSound\RabbitMqBundle\RabbitMq\BaseAmqp`
22 |
23 | - 2014-02-24
24 | * Add a parameter to RPC client configuration to disable auto unserialize when adding call results to results array.
25 |
26 | - 2013-01-18
27 | * adds an an optional parameter for the AMQP Message Properties for the publish method of the Producer, so they can be set as well. For example, seeting the application_headers is now possible.
28 |
29 | - 2012-06-04
30 | * Revert PR #46. It is still possible to override parameter classes but in a proper way.
31 | * Some default options for exchanges declared in the "producers" config section
32 | have changed to match the defaults of exchanges declared in the "consumers" section.
33 | The affected settings are:
34 | * `durable` was changed from `false` to `true`,
35 | * `auto_delete` was changed from `true` to `false`.
36 | * Adds basic_reject functionality to consumers. A message can be rejected by returning `false` from a callback.
37 |
38 | - 2012-05-29
39 | * Merged PR #46 adding compiler passes for the configuration. Now the parameter classes can be overriden.
40 | * Treats queue arguments as a variableNode in the configuration to allow declaring HA Queues.
41 |
42 | - 2012-01-03
43 | * Merged PR #14 Now instances of `PhpAmqpLib\Message\AMQPMessage` are passed to consumers instead of just the message body.
44 | The message body can be obtained then by writing `$msg->body` inside the `execute` method.
45 | * Merged PR #13 removing the need for the ContainerAwareInterface on consumers.
46 | * `rabbitmq:consumer` now takes a `--route` argument that can be used to specify the routing key.
47 |
48 | - 2011-11-25:
49 | * Fixed autoload configuration example
50 | * Adds a producer that can publish data coming from STDIN. The use case will be to use it in combination with unix pipes.
51 |
52 | - 2011-11-24:
53 | * The rabbitmq:consumer command consumes messages forever unless the -m option is provided.
54 | * The -m option for the rabbitmq:consumer command must be greater null or greater than 0.
55 | * Fixed issues #2 #7 #11 and #12.
56 | * Updated the bundle to use the latest version from videlalvaro/php-amqplib.
57 | * Updated installation/setup instructions.
58 |
--------------------------------------------------------------------------------
/RabbitMq/Binding.php:
--------------------------------------------------------------------------------
1 | exchange;
43 | }
44 |
45 | /**
46 | * @param string $exchange
47 | */
48 | public function setExchange($exchange)
49 | {
50 | $this->exchange = $exchange;
51 | }
52 |
53 | /**
54 | * @return string
55 | */
56 | public function getDestination()
57 | {
58 | return $this->destination;
59 | }
60 |
61 | /**
62 | * @param string $destination
63 | */
64 | public function setDestination($destination)
65 | {
66 | $this->destination = $destination;
67 | }
68 |
69 | /**
70 | * @return bool
71 | */
72 | public function getDestinationIsExchange()
73 | {
74 | return $this->destinationIsExchange;
75 | }
76 |
77 | /**
78 | * @param bool $destinationIsExchange
79 | */
80 | public function setDestinationIsExchange($destinationIsExchange)
81 | {
82 | $this->destinationIsExchange = $destinationIsExchange;
83 | }
84 |
85 | /**
86 | * @return string
87 | */
88 | public function getRoutingKey()
89 | {
90 | return $this->routingKey;
91 | }
92 |
93 | /**
94 | * @param string $routingKey
95 | */
96 | public function setRoutingKey($routingKey)
97 | {
98 | $this->routingKey = $routingKey;
99 | }
100 |
101 | /**
102 | * @return boolean
103 | */
104 | public function isNowait()
105 | {
106 | return $this->nowait;
107 | }
108 |
109 | /**
110 | * @param boolean $nowait
111 | */
112 | public function setNowait($nowait)
113 | {
114 | $this->nowait = $nowait;
115 | }
116 |
117 | /**
118 | * @return array
119 | */
120 | public function getArguments()
121 | {
122 | return $this->arguments;
123 | }
124 |
125 | /**
126 | * @param array $arguments
127 | */
128 | public function setArguments($arguments)
129 | {
130 | $this->arguments = $arguments;
131 | }
132 |
133 |
134 | /**
135 | * create bindings
136 | *
137 | * @return void
138 | */
139 | public function setupFabric()
140 | {
141 | $method = ($this->destinationIsExchange) ? 'exchange_bind' : 'queue_bind';
142 | $channel = $this->getChannel();
143 | call_user_func(
144 | [$channel, $method],
145 | $this->destination,
146 | $this->exchange,
147 | $this->routingKey,
148 | $this->nowait,
149 | $this->arguments
150 | );
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/Resources/views/Collector/collector.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'WebProfilerBundle:Profiler:layout.html.twig' %}
2 |
3 | {% block toolbar %}
4 | {% if collector.publishedMessagesCount %}
5 | {% set icon %}
6 |
7 | {{ collector.publishedMessagesCount }}
8 | {% endset %}
9 | {% set text %}
10 |
11 | Messages
12 | {{ collector.publishedMessagesCount }}
13 |
14 | {% endset %}
15 | {% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %}
16 | {% endif %}
17 | {% endblock %}
18 |
19 | {% block menu %}
20 |
21 |
22 | RabbitMQ
23 |
24 | {{ collector.publishedMessagesCount }}
25 |
26 |
27 | {% endblock %}
28 |
29 | {% block panel %}
30 | Messages
31 | {% if collector.publishedMessagesCount %}
32 |
33 |
34 |
35 | | Exchange |
36 | Message body |
37 |
38 |
39 |
40 | {% for log in collector.publishedMessagesLog %}
41 |
42 | | {{ log.exchange }} |
43 | {{ log.msg.body }} |
44 |
45 | {% endfor %}
46 |
47 |
48 | {% else %}
49 |
50 | No messages were sent.
51 |
52 | {% endif %}
53 | {% endblock %}
54 |
--------------------------------------------------------------------------------
/RabbitMq/MultipleConsumer.php:
--------------------------------------------------------------------------------
1 | queuesProvider = $queuesProvider;
37 | return $this;
38 | }
39 |
40 | public function getQueueConsumerTag($queue)
41 | {
42 | return sprintf('%s-%s', $this->getConsumerTag(), $queue);
43 | }
44 |
45 | public function setQueues(array $queues)
46 | {
47 | $this->queues = $queues;
48 | }
49 |
50 | public function setContext($context)
51 | {
52 | $this->context = $context;
53 | }
54 |
55 | protected function setupConsumer()
56 | {
57 | $this->mergeQueues();
58 |
59 | if ($this->autoSetupFabric) {
60 | $this->setupFabric();
61 | }
62 |
63 | foreach ($this->queues as $name => $options) {
64 | //PHP 5.3 Compliant
65 | $currentObject = $this;
66 |
67 | $this->getChannel()->basic_consume($name, $this->getQueueConsumerTag($name), false, $this->consumerOptions['no_ack'], false, false, function (AMQPMessage $msg) use ($currentObject, $name) {
68 | $currentObject->processQueueMessage($name, $msg);
69 | });
70 | }
71 | }
72 |
73 | protected function queueDeclare()
74 | {
75 | foreach ($this->queues as $name => $options) {
76 | [$queueName, , ] = $this->getChannel()->queue_declare(
77 | $name,
78 | $options['passive'],
79 | $options['durable'],
80 | $options['exclusive'],
81 | $options['auto_delete'],
82 | $options['nowait'],
83 | $options['arguments'],
84 | $options['ticket']
85 | );
86 |
87 | if (isset($options['routing_keys']) && count($options['routing_keys']) > 0) {
88 | foreach ($options['routing_keys'] as $routingKey) {
89 | $this->queueBind($queueName, $this->exchangeOptions['name'], $routingKey, $options['arguments'] ?? []);
90 | }
91 | } else {
92 | $this->queueBind($queueName, $this->exchangeOptions['name'], $this->routingKey, $options['arguments'] ?? []);
93 | }
94 | }
95 |
96 | $this->queueDeclared = true;
97 | }
98 |
99 | public function processQueueMessage($queueName, AMQPMessage $msg)
100 | {
101 | if (!isset($this->queues[$queueName])) {
102 | throw new QueueNotFoundException();
103 | }
104 |
105 | $this->processMessageQueueCallback($msg, $queueName, $this->queues[$queueName]['callback']);
106 | }
107 |
108 | public function stopConsuming()
109 | {
110 | foreach ($this->queues as $name => $options) {
111 | $this->getChannel()->basic_cancel($this->getQueueConsumerTag($name), false, true);
112 | }
113 | }
114 |
115 | /**
116 | * Merges static and provided queues into one array
117 | */
118 | protected function mergeQueues()
119 | {
120 | if ($this->queuesProvider) {
121 | $this->queues = array_merge(
122 | $this->queues,
123 | $this->queuesProvider->getQueues()
124 | );
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/Command/BaseConsumerCommand.php:
--------------------------------------------------------------------------------
1 | consumer instanceof Consumer) {
24 | // Process current message, then halt consumer
25 | $this->consumer->forceStopConsumer();
26 |
27 | // Halt consumer if waiting for a new message from the queue
28 | try {
29 | $this->consumer->stopConsuming();
30 | } catch (AMQPTimeoutException $e) {
31 | }
32 | }
33 | }
34 |
35 | public function restartConsumer()
36 | {
37 | // TODO: Implement restarting of consumer
38 | }
39 |
40 | protected function configure(): void
41 | {
42 | parent::configure();
43 |
44 | $this
45 | ->addArgument('name', InputArgument::REQUIRED, 'Consumer Name')
46 | ->addOption('messages', 'm', InputOption::VALUE_OPTIONAL, 'Messages to consume', '0')
47 | ->addOption('route', 'r', InputOption::VALUE_OPTIONAL, 'Routing Key', '')
48 | ->addOption('memory-limit', 'l', InputOption::VALUE_OPTIONAL, 'Allowed memory for this process (MB)')
49 | ->addOption('debug', 'd', InputOption::VALUE_NONE, 'Enable Debugging')
50 | ->addOption('without-signals', 'w', InputOption::VALUE_NONE, 'Disable catching of system signals')
51 | ;
52 | }
53 |
54 | protected function initialize(InputInterface $input, OutputInterface $output): void
55 | {
56 | $this->amount = (int)$input->getOption('messages');
57 | if (0 > $this->amount) {
58 | throw new \InvalidArgumentException("The -m option should be null or greater than 0");
59 | }
60 | }
61 |
62 | /**
63 | * Executes the current command.
64 | *
65 | * @param InputInterface $input An InputInterface instance
66 | * @param OutputInterface $output An OutputInterface instance
67 | *
68 | * @return integer 0 if everything went fine, or an error code
69 | *
70 | * @throws \InvalidArgumentException When the number of messages to consume is less than 0
71 | * @throws \BadFunctionCallException When the pcntl is not installed and option -s is true
72 | */
73 | protected function execute(InputInterface $input, OutputInterface $output): int
74 | {
75 | if (defined('AMQP_WITHOUT_SIGNALS') === false) {
76 | define('AMQP_WITHOUT_SIGNALS', $input->getOption('without-signals'));
77 | }
78 |
79 | if (!AMQP_WITHOUT_SIGNALS && extension_loaded('pcntl')) {
80 | if (!function_exists('pcntl_signal')) {
81 | throw new \BadFunctionCallException("Function 'pcntl_signal' is referenced in the php.ini 'disable_functions' and can't be called.");
82 | }
83 |
84 | pcntl_signal(SIGTERM, [&$this, 'stopConsumer']);
85 | pcntl_signal(SIGINT, [&$this, 'stopConsumer']);
86 | pcntl_signal(SIGQUIT, [&$this, 'stopConsumer']);
87 | pcntl_signal(SIGHUP, [&$this, 'restartConsumer']);
88 | }
89 |
90 | if (defined('AMQP_DEBUG') === false) {
91 | define('AMQP_DEBUG', (bool) $input->getOption('debug'));
92 | }
93 |
94 | $this->initConsumer($input);
95 |
96 | return $this->consumer->consume($this->amount);
97 | }
98 |
99 | protected function initConsumer(InputInterface $input)
100 | {
101 | $this->consumer = $this->getContainer()
102 | ->get(sprintf($this->getConsumerService(), $input->getArgument('name')));
103 |
104 | if ($input->hasOption('memory-limit')) {
105 | $memoryLimit = (int)$input->getOption('memory-limit');
106 | if ($memoryLimit > 0) {
107 | $this->consumer->setMemoryLimit($memoryLimit);
108 | }
109 | }
110 | $this->consumer->setRoutingKey($input->getOption('route'));
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/RabbitMq/RpcClient.php:
--------------------------------------------------------------------------------
1 | expectSerializedResponse = $expectSerializedResponse;
23 | }
24 |
25 | public function addRequest($msgBody, $server, $requestId = null, $routingKey = '', $expiration = 0)
26 | {
27 | if (empty($requestId)) {
28 | throw new \InvalidArgumentException('You must provide a $requestId');
29 | }
30 |
31 | if (0 == $this->requests) {
32 | // On first addRequest() call, clear all replies
33 | $this->replies = [];
34 |
35 | if ($this->directReplyTo) {
36 | // On direct reply-to mode, make initial consume call
37 | $this->directConsumerTag = $this->getChannel()->basic_consume('amq.rabbitmq.reply-to', '', false, true, false, false, [$this, 'processMessage']);
38 | }
39 | }
40 |
41 | $msg = new AMQPMessage($msgBody, ['content_type' => 'text/plain',
42 | 'reply_to' => $this->directReplyTo
43 | ? 'amq.rabbitmq.reply-to' // On direct reply-to mode, use predefined queue name
44 | : $this->getQueueName(),
45 | 'delivery_mode' => 1, // non durable
46 | 'expiration' => $expiration * 1000,
47 | 'correlation_id' => $requestId, ]);
48 |
49 | $this->getChannel()->basic_publish($msg, $server, $routingKey);
50 |
51 | $this->requests++;
52 |
53 | if ($expiration > $this->timeout) {
54 | $this->timeout = $expiration;
55 | }
56 | }
57 |
58 | public function getReplies()
59 | {
60 | if ($this->directReplyTo) {
61 | $consumer_tag = $this->directConsumerTag;
62 | } else {
63 | $consumer_tag = $this->getChannel()->basic_consume($this->getQueueName(), '', false, true, false, false, [$this, 'processMessage']);
64 | }
65 |
66 | try {
67 | while (count($this->replies) < $this->requests) {
68 | $this->getChannel()->wait(null, false, $this->timeout);
69 | }
70 | } finally {
71 | $this->getChannel()->basic_cancel($consumer_tag);
72 | }
73 |
74 | $this->directConsumerTag = null;
75 | $this->requests = 0;
76 | $this->timeout = 0;
77 |
78 | return $this->replies;
79 | }
80 |
81 | public function processMessage(AMQPMessage $msg)
82 | {
83 | $messageBody = $msg->body;
84 | if ($this->expectSerializedResponse) {
85 | $messageBody = call_user_func($this->unserializer, $messageBody);
86 | }
87 | if ($this->notifyCallback !== null) {
88 | call_user_func($this->notifyCallback, $messageBody);
89 | }
90 |
91 | $this->replies[$msg->get('correlation_id')] = $messageBody;
92 | }
93 |
94 | protected function getQueueName()
95 | {
96 | if (null === $this->queueName) {
97 | [$this->queueName, , ] = $this->getChannel()->queue_declare("", false, false, true, false);
98 | }
99 |
100 | return $this->queueName;
101 | }
102 |
103 | public function setUnserializer($unserializer)
104 | {
105 | $this->unserializer = $unserializer;
106 | }
107 |
108 | public function notify($callback)
109 | {
110 | if (is_callable($callback)) {
111 | $this->notifyCallback = $callback;
112 | } else {
113 | throw new \InvalidArgumentException('First parameter expects to be callable');
114 | }
115 | }
116 |
117 | public function setDirectReplyTo($directReplyTo)
118 | {
119 | $this->directReplyTo = $directReplyTo;
120 | }
121 |
122 | public function reset()
123 | {
124 | $this->replies = [];
125 | $this->requests = 0;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/Command/BatchConsumerCommand.php:
--------------------------------------------------------------------------------
1 | consumer instanceof BatchConsumer) {
22 | // Process current message, then halt consumer
23 | $this->consumer->forceStopConsumer();
24 |
25 | // Halt consumer if waiting for a new message from the queue
26 | try {
27 | $this->consumer->stopConsuming();
28 | } catch (AMQPTimeoutException $e) {
29 | }
30 | }
31 | }
32 |
33 | protected function configure(): void
34 | {
35 | parent::configure();
36 |
37 | $this
38 | ->setName('rabbitmq:batch:consumer')
39 | ->addArgument('name', InputArgument::REQUIRED, 'Consumer Name')
40 | ->addOption('batches', 'b', InputOption::VALUE_OPTIONAL, 'Number of batches to consume', '0')
41 | ->addOption('route', 'r', InputOption::VALUE_OPTIONAL, 'Routing Key', '')
42 | ->addOption('memory-limit', 'l', InputOption::VALUE_OPTIONAL, 'Allowed memory for this process')
43 | ->addOption('debug', 'd', InputOption::VALUE_NONE, 'Enable Debugging')
44 | ->addOption('without-signals', 'w', InputOption::VALUE_NONE, 'Disable catching of system signals')
45 | ->setDescription('Executes a Batch Consumer');
46 | }
47 |
48 | /**
49 | * Executes the current command.
50 | *
51 | * @param InputInterface $input An InputInterface instance
52 | * @param OutputInterface $output An OutputInterface instance
53 | *
54 | * @return integer 0 if everything went fine, or an error code
55 | *
56 | * @throws \InvalidArgumentException When the number of batches to consume is less than 0
57 | * @throws \BadFunctionCallException When the pcntl is not installed and option -s is true
58 | */
59 | protected function execute(InputInterface $input, OutputInterface $output): int
60 | {
61 | if (defined('AMQP_WITHOUT_SIGNALS') === false) {
62 | define('AMQP_WITHOUT_SIGNALS', $input->getOption('without-signals'));
63 | }
64 |
65 | if (!AMQP_WITHOUT_SIGNALS && extension_loaded('pcntl')) {
66 | if (!function_exists('pcntl_signal')) {
67 | throw new \BadFunctionCallException("Function 'pcntl_signal' is referenced in the php.ini 'disable_functions' and can't be called.");
68 | }
69 |
70 | pcntl_signal(SIGTERM, [&$this, 'stopConsumer']);
71 | pcntl_signal(SIGINT, [&$this, 'stopConsumer']);
72 | pcntl_signal(SIGQUIT, [&$this, 'stopConsumer']);
73 | }
74 |
75 | if (defined('AMQP_DEBUG') === false) {
76 | define('AMQP_DEBUG', (bool) $input->getOption('debug'));
77 | }
78 |
79 | $batchAmountTarget = (int)$input->getOption('batches');
80 | if (0 > $batchAmountTarget) {
81 | throw new \InvalidArgumentException("The -b option should be greater than 0");
82 | }
83 |
84 | $this->initConsumer($input);
85 |
86 | return $this->consumer->consume($batchAmountTarget);
87 | }
88 |
89 | /**
90 | * @param InputInterface $input
91 | */
92 | protected function initConsumer(InputInterface $input)
93 | {
94 | $this->consumer = $this->getContainer()
95 | ->get(sprintf($this->getConsumerService(), $input->getArgument('name')));
96 |
97 | if (null !== $input->getOption('memory-limit') &&
98 | ctype_digit((string) $input->getOption('memory-limit')) &&
99 | (int) $input->getOption('memory-limit') > 0
100 | ) {
101 | $this->consumer->setMemoryLimit($input->getOption('memory-limit'));
102 | }
103 | $this->consumer->setRoutingKey($input->getOption('route'));
104 | }
105 |
106 | /**
107 | * @return string
108 | */
109 | protected function getConsumerService()
110 | {
111 | return 'old_sound_rabbit_mq.%s_batch';
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/RabbitMq/BaseConsumer.php:
--------------------------------------------------------------------------------
1 | callback = $callback;
30 | }
31 |
32 | /**
33 | * @return callable
34 | */
35 | public function getCallback()
36 | {
37 | return $this->callback;
38 | }
39 |
40 | /**
41 | * @param int $msgAmount
42 | * @throws \ErrorException
43 | */
44 | public function start($msgAmount = 0)
45 | {
46 | $this->target = $msgAmount;
47 |
48 | $this->setupConsumer();
49 |
50 | while ($this->getChannel()->is_consuming()) {
51 | $this->getChannel()->wait();
52 | }
53 | }
54 |
55 | /**
56 | * Tell the server you are going to stop consuming.
57 | *
58 | * It will finish up the last message and not send you any more.
59 | */
60 | public function stopConsuming()
61 | {
62 | // This gets stuck and will not exit without the last two parameters set.
63 | $this->getChannel()->basic_cancel($this->getConsumerTag(), false, true);
64 | }
65 |
66 | protected function setupConsumer()
67 | {
68 | if ($this->autoSetupFabric) {
69 | $this->setupFabric();
70 | }
71 | $this->getChannel()->basic_consume($this->queueOptions['name'], $this->getConsumerTag(), false, $this->consumerOptions['no_ack'], false, false, [$this, 'processMessage']);
72 | }
73 |
74 | public function processMessage(AMQPMessage $msg)
75 | {
76 | //To be implemented by descendant classes
77 | }
78 |
79 | protected function maybeStopConsumer()
80 | {
81 | if (extension_loaded('pcntl') && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true)) {
82 | if (!function_exists('pcntl_signal_dispatch')) {
83 | throw new \BadFunctionCallException("Function 'pcntl_signal_dispatch' is referenced in the php.ini 'disable_functions' and can't be called.");
84 | }
85 |
86 | pcntl_signal_dispatch();
87 | }
88 |
89 | if ($this->forceStop || ($this->consumed == $this->target && $this->target > 0)) {
90 | $this->stopConsuming();
91 | }
92 | }
93 |
94 | public function setConsumerTag($tag)
95 | {
96 | $this->consumerTag = $tag;
97 | }
98 |
99 | public function getConsumerTag()
100 | {
101 | return $this->consumerTag;
102 | }
103 |
104 | public function forceStopConsumer()
105 | {
106 | $this->forceStop = true;
107 | }
108 |
109 | /**
110 | * Sets the qos settings for the current channel
111 | * Consider that prefetchSize and global do not work with rabbitMQ version <= 8.0
112 | *
113 | * @param int $prefetchSize
114 | * @param int $prefetchCount
115 | * @param bool $global
116 | */
117 | public function setQosOptions($prefetchSize = 0, $prefetchCount = 0, $global = false)
118 | {
119 | $this->getChannel()->basic_qos($prefetchSize, $prefetchCount, $global);
120 | }
121 |
122 | public function setIdleTimeout($idleTimeout)
123 | {
124 | $this->idleTimeout = $idleTimeout;
125 | }
126 |
127 | /**
128 | * Set exit code to be returned when there is a timeout exception
129 | *
130 | * @param int|null $idleTimeoutExitCode
131 | */
132 | public function setIdleTimeoutExitCode($idleTimeoutExitCode)
133 | {
134 | $this->idleTimeoutExitCode = $idleTimeoutExitCode;
135 | }
136 |
137 | public function getIdleTimeout()
138 | {
139 | return $this->idleTimeout;
140 | }
141 |
142 | /**
143 | * Get exit code to be returned when there is a timeout exception
144 | *
145 | * @return int|null
146 | */
147 | public function getIdleTimeoutExitCode()
148 | {
149 | return $this->idleTimeoutExitCode;
150 | }
151 |
152 | /**
153 | * Resets the consumed property.
154 | * Use when you want to call start() or consume() multiple times.
155 | */
156 | public function resetConsumed()
157 | {
158 | $this->consumed = 0;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/Resources/config/rabbitmq.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | PhpAmqpLib\Connection\AMQPStreamConnection
8 | PhpAmqpLib\Connection\AMQPSocketConnection
9 | PhpAmqpLib\Connection\AMQPLazyConnection
10 | PhpAmqpLib\Connection\AMQPLazySocketConnection
11 | OldSound\RabbitMqBundle\RabbitMq\AMQPConnectionFactory
12 | OldSound\RabbitMqBundle\RabbitMq\Binding
13 | OldSound\RabbitMqBundle\RabbitMq\Producer
14 | OldSound\RabbitMqBundle\RabbitMq\Consumer
15 | OldSound\RabbitMqBundle\RabbitMq\MultipleConsumer
16 | OldSound\RabbitMqBundle\RabbitMq\DynamicConsumer
17 | OldSound\RabbitMqBundle\RabbitMq\BatchConsumer
18 | OldSound\RabbitMqBundle\RabbitMq\AnonConsumer
19 | OldSound\RabbitMqBundle\RabbitMq\RpcClient
20 | OldSound\RabbitMqBundle\RabbitMq\RpcServer
21 | OldSound\RabbitMqBundle\RabbitMq\AMQPLoggedChannel
22 | OldSound\RabbitMqBundle\DataCollector\MessageDataCollector
23 | OldSound\RabbitMqBundle\RabbitMq\AmqpPartsHolder
24 | OldSound\RabbitMqBundle\RabbitMq\Fallback
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/RabbitMq/AMQPConnectionFactory.php:
--------------------------------------------------------------------------------
1 | '',
18 | 'host' => 'localhost',
19 | 'port' => 5672,
20 | 'user' => 'guest',
21 | 'password' => 'guest',
22 | 'vhost' => '/',
23 | 'connection_timeout' => 3,
24 | 'read_write_timeout' => 3,
25 | 'ssl_context' => null,
26 | 'keepalive' => false,
27 | 'heartbeat' => 0,
28 | 'hosts' => [],
29 | 'channel_rpc_timeout' => 0.0,
30 | ];
31 |
32 | /**
33 | * Constructor
34 | *
35 | * @param string $class FQCN of AMQPConnection class to instantiate.
36 | * @param array $parameters Map containing parameters resolved by
37 | * Extension.
38 | * @param ConnectionParametersProviderInterface $parametersProvider Optional service providing/overriding
39 | * connection parameters.
40 | */
41 | public function __construct(
42 | $class,
43 | array $parameters,
44 | ?ConnectionParametersProviderInterface $parametersProvider = null
45 | ) {
46 | $this->class = $class;
47 | $this->parameters = array_merge($this->parameters, $parameters);
48 | $this->parameters = $this->parseUrl($this->parameters);
49 |
50 | foreach ($this->parameters['hosts'] as $key => $hostParameters) {
51 | if (!isset($hostParameters['url'])) {
52 | continue;
53 | }
54 |
55 | $this->parameters['hosts'][$key] = $this->parseUrl($hostParameters);
56 | }
57 |
58 | if ($parametersProvider) {
59 | $this->parameters = array_merge($this->parameters, $parametersProvider->getConnectionParameters());
60 | }
61 |
62 | if (is_array($this->parameters['ssl_context'])) {
63 | $this->parameters['context'] = !empty($this->parameters['ssl_context'])
64 | ? stream_context_create(['ssl' => $this->parameters['ssl_context']])
65 | : null;
66 | }
67 |
68 | }
69 |
70 | /**
71 | * Creates the appropriate connection using current parameters.
72 | *
73 | * @return AbstractConnection
74 | * @throws \Exception
75 | */
76 | public function createConnection()
77 | {
78 | if (isset($this->parameters['constructor_args']) && is_array($this->parameters['constructor_args'])) {
79 | $constructorArgs = array_values($this->parameters['constructor_args']);
80 | return new $this->class(...$constructorArgs);
81 | }
82 |
83 | $hosts = $this->parameters['hosts'] ?: [$this->parameters];
84 | $options = $this->parameters;
85 | unset($options['hosts']);
86 |
87 | if ($this->class == AMQPSocketConnection::class || is_subclass_of($this->class, AMQPSocketConnection::class)) {
88 | $options['read_timeout'] ??= $this->parameters['read_write_timeout'];
89 | $options['write_timeout'] ??= $this->parameters['read_write_timeout'];
90 | }
91 |
92 | // No need to unpack options, they will be handled inside connection classes
93 | return $this->class::create_connection($hosts, $options);
94 | }
95 |
96 | /**
97 | * Parses connection parameters from URL parameter.
98 | *
99 | * @param array $parameters
100 | *
101 | * @return array
102 | */
103 | private function parseUrl(array $parameters)
104 | {
105 | if (!$parameters['url']) {
106 | return $parameters;
107 | }
108 |
109 | $url = parse_url($parameters['url']);
110 |
111 | if ($url === false || !isset($url['scheme']) || !in_array($url['scheme'], ['amqp', 'amqps'], true)) {
112 | throw new InvalidConfigurationException('Malformed parameter "url".');
113 | }
114 |
115 | // See https://www.rabbitmq.com/uri-spec.html
116 | if (isset($url['host'])) {
117 | $parameters['host'] = urldecode($url['host']);
118 | }
119 | if (isset($url['port'])) {
120 | $parameters['port'] = (int)$url['port'];
121 | }
122 | if (isset($url['user'])) {
123 | $parameters['user'] = urldecode($url['user']);
124 | }
125 | if (isset($url['pass'])) {
126 | $parameters['password'] = urldecode($url['pass']);
127 | }
128 | if (isset($url['path'])) {
129 | $parameters['vhost'] = urldecode(ltrim($url['path'], '/'));
130 | }
131 |
132 | if (isset($url['query'])) {
133 | $query = [];
134 | parse_str($url['query'], $query);
135 | $parameters = array_merge($parameters, $query);
136 | }
137 |
138 | unset($parameters['url']);
139 |
140 | return $parameters;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/RabbitMq/BaseAmqp.php:
--------------------------------------------------------------------------------
1 | 'text/plain', 'delivery_mode' => 2];
23 |
24 | /**
25 | * @var LoggerInterface
26 | */
27 | protected $logger;
28 |
29 | protected $exchangeOptions = [
30 | 'passive' => false,
31 | 'durable' => true,
32 | 'auto_delete' => false,
33 | 'internal' => false,
34 | 'nowait' => false,
35 | 'arguments' => null,
36 | 'ticket' => null,
37 | 'declare' => true,
38 | ];
39 |
40 | protected $queueOptions = [
41 | 'name' => '',
42 | 'passive' => false,
43 | 'durable' => true,
44 | 'exclusive' => false,
45 | 'auto_delete' => false,
46 | 'nowait' => false,
47 | 'arguments' => null,
48 | 'ticket' => null,
49 | 'declare' => true,
50 | ];
51 |
52 | protected $consumerOptions = [
53 | 'no_ack' => false,
54 | ];
55 |
56 | /**
57 | * @var EventDispatcherInterface|null
58 | */
59 | protected $eventDispatcher = null;
60 |
61 | /**
62 | * @param AbstractConnection $conn
63 | * @param AMQPChannel|null $ch
64 | * @param string|null $consumerTag
65 | */
66 | public function __construct(AbstractConnection $conn, ?AMQPChannel $ch = null, $consumerTag = null)
67 | {
68 | $this->conn = $conn;
69 | $this->ch = $ch;
70 |
71 | if ($conn->connectOnConstruct()) {
72 | $this->getChannel();
73 | }
74 |
75 | $this->consumerTag = $consumerTag ?? sprintf("PHPPROCESS_%s_%s", gethostname(), getmypid());
76 |
77 | $this->logger = new NullLogger();
78 | }
79 |
80 | public function __destruct()
81 | {
82 | $this->close();
83 | }
84 |
85 | public function close()
86 | {
87 | if ($this->ch) {
88 | try {
89 | $this->ch->close();
90 | } catch (\Exception $e) {
91 | // ignore on shutdown
92 | }
93 | }
94 |
95 | if ($this->conn && $this->conn->isConnected()) {
96 | try {
97 | $this->conn->close();
98 | } catch (\Exception $e) {
99 | // ignore on shutdown
100 | }
101 | }
102 | }
103 |
104 | public function reconnect()
105 | {
106 | if (!$this->conn->isConnected()) {
107 | return;
108 | }
109 |
110 | $this->conn->reconnect();
111 | }
112 |
113 | /**
114 | * @return AMQPChannel
115 | */
116 | public function getChannel()
117 | {
118 | if (empty($this->ch) || null === $this->ch->getChannelId()) {
119 | $this->ch = $this->conn->channel();
120 | }
121 |
122 | return $this->ch;
123 | }
124 |
125 | /**
126 | * @param AMQPChannel $ch
127 | *
128 | * @return void
129 | */
130 | public function setChannel(AMQPChannel $ch)
131 | {
132 | $this->ch = $ch;
133 | }
134 |
135 | /**
136 | * @throws \InvalidArgumentException
137 | * @param array $options
138 | * @return void
139 | */
140 | public function setExchangeOptions(array $options = [])
141 | {
142 | if (!isset($options['name'])) {
143 | throw new \InvalidArgumentException('You must provide an exchange name');
144 | }
145 |
146 | if (empty($options['type'])) {
147 | throw new \InvalidArgumentException('You must provide an exchange type');
148 | }
149 |
150 | $this->exchangeOptions = array_merge($this->exchangeOptions, $options);
151 | }
152 |
153 | /**
154 | * @param array $options
155 | * @return void
156 | */
157 | public function setQueueOptions(array $options = [])
158 | {
159 | $this->queueOptions = array_merge($this->queueOptions, $options);
160 | }
161 |
162 | /**
163 | * @param array $options
164 | * @return void
165 | */
166 | public function setConsumerOptions(array $options = [])
167 | {
168 | $this->consumerOptions = array_merge($this->consumerOptions, $options);
169 | }
170 |
171 | /**
172 | * @param string $routingKey
173 | * @return void
174 | */
175 | public function setRoutingKey($routingKey)
176 | {
177 | $this->routingKey = $routingKey;
178 | }
179 |
180 | public function setupFabric()
181 | {
182 | if (!$this->exchangeDeclared) {
183 | $this->exchangeDeclare();
184 | }
185 |
186 | if (!$this->queueDeclared) {
187 | $this->queueDeclare();
188 | }
189 | }
190 |
191 | /**
192 | * disables the automatic SetupFabric when using a consumer or producer
193 | */
194 | public function disableAutoSetupFabric()
195 | {
196 | $this->autoSetupFabric = false;
197 | }
198 |
199 | /**
200 | * @param LoggerInterface $logger
201 | */
202 | public function setLogger($logger)
203 | {
204 | $this->logger = $logger;
205 | }
206 |
207 | /**
208 | * Declares exchange
209 | */
210 | protected function exchangeDeclare()
211 | {
212 | if ($this->exchangeOptions['declare']) {
213 | $this->getChannel()->exchange_declare(
214 | $this->exchangeOptions['name'],
215 | $this->exchangeOptions['type'],
216 | $this->exchangeOptions['passive'],
217 | $this->exchangeOptions['durable'],
218 | $this->exchangeOptions['auto_delete'],
219 | $this->exchangeOptions['internal'],
220 | $this->exchangeOptions['nowait'],
221 | $this->exchangeOptions['arguments'],
222 | $this->exchangeOptions['ticket']
223 | );
224 |
225 | $this->exchangeDeclared = true;
226 | }
227 | }
228 |
229 | /**
230 | * Declares queue, creates if needed
231 | */
232 | protected function queueDeclare()
233 | {
234 | if ($this->queueOptions['declare']) {
235 | [$queueName, , ] = $this->getChannel()->queue_declare(
236 | $this->queueOptions['name'],
237 | $this->queueOptions['passive'],
238 | $this->queueOptions['durable'],
239 | $this->queueOptions['exclusive'],
240 | $this->queueOptions['auto_delete'],
241 | $this->queueOptions['nowait'],
242 | $this->queueOptions['arguments'],
243 | $this->queueOptions['ticket']
244 | );
245 |
246 | if (isset($this->queueOptions['routing_keys']) && count($this->queueOptions['routing_keys']) > 0) {
247 | foreach ($this->queueOptions['routing_keys'] as $routingKey) {
248 | $this->queueBind($queueName, $this->exchangeOptions['name'], $routingKey, $this->queueOptions['arguments'] ?? []);
249 | }
250 | } else {
251 | $this->queueBind($queueName, $this->exchangeOptions['name'], $this->routingKey, $this->queueOptions['arguments'] ?? []);
252 | }
253 |
254 | $this->queueDeclared = true;
255 | }
256 | }
257 |
258 | /**
259 | * Binds queue to an exchange
260 | *
261 | * @param string $queue
262 | * @param string $exchange
263 | * @param string $routing_key
264 | */
265 | protected function queueBind($queue, $exchange, $routing_key, array $arguments = [])
266 | {
267 | // queue binding is not permitted on the default exchange
268 | if ('' !== $exchange) {
269 | $this->getChannel()->queue_bind($queue, $exchange, $routing_key, false, $arguments);
270 | }
271 | }
272 |
273 | /**
274 | * @param EventDispatcherInterface $eventDispatcher
275 | *
276 | * @return BaseAmqp
277 | */
278 | public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
279 | {
280 | $this->eventDispatcher = $eventDispatcher;
281 |
282 | return $this;
283 | }
284 |
285 | /**
286 | * @param string $eventName
287 | * @param AMQPEvent $event
288 | */
289 | protected function dispatchEvent($eventName, AMQPEvent $event)
290 | {
291 | if ($this->getEventDispatcher() instanceof ContractsEventDispatcherInterface) {
292 | $this->getEventDispatcher()->dispatch(
293 | $event,
294 | $eventName
295 | );
296 | }
297 | }
298 |
299 | /**
300 | * @return EventDispatcherInterface|null
301 | */
302 | public function getEventDispatcher()
303 | {
304 | return $this->eventDispatcher;
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/RabbitMq/Consumer.php:
--------------------------------------------------------------------------------
1 | memoryLimit = $memoryLimit;
50 | }
51 |
52 | /**
53 | * Get the memory limit
54 | *
55 | * @return int|null
56 | */
57 | public function getMemoryLimit()
58 | {
59 | return $this->memoryLimit;
60 | }
61 |
62 | /**
63 | * Consume the message
64 | *
65 | * @param int $msgAmount
66 | *
67 | * @return int
68 | *
69 | * @throws AMQPTimeoutException
70 | */
71 | public function consume($msgAmount)
72 | {
73 | $this->target = $msgAmount;
74 |
75 | $this->setupConsumer();
76 |
77 | $this->setLastActivityDateTime(new \DateTime());
78 | while ($this->getChannel()->is_consuming()) {
79 | $this->dispatchEvent(OnConsumeEvent::NAME, new OnConsumeEvent($this));
80 | $this->maybeStopConsumer();
81 |
82 | /*
83 | * Be careful not to trigger ::wait() with 0 or less seconds, when
84 | * graceful max execution timeout is being used.
85 | */
86 |
87 | $waitTimeout = $this->chooseWaitTimeout();
88 | if ($this->gracefulMaxExecutionDateTime
89 | && $waitTimeout < 1
90 | ) {
91 | return $this->gracefulMaxExecutionTimeoutExitCode;
92 | }
93 |
94 | if (!$this->forceStop) {
95 | try {
96 | $this->getChannel()->wait(null, false, $waitTimeout);
97 | $this->setLastActivityDateTime(new \DateTime());
98 | } catch (AMQPTimeoutException $e) {
99 | $now = time();
100 |
101 | if ($this->gracefulMaxExecutionDateTime
102 | && $this->gracefulMaxExecutionDateTime <= new \DateTime("@$now")
103 | ) {
104 | return $this->gracefulMaxExecutionTimeoutExitCode;
105 | } elseif ($this->getIdleTimeout()
106 | && ($this->getLastActivityDateTime()->getTimestamp() + $this->getIdleTimeout() <= $now)
107 | ) {
108 | $idleEvent = new OnIdleEvent($this);
109 | $this->dispatchEvent(OnIdleEvent::NAME, $idleEvent);
110 |
111 | if ($idleEvent->isForceStop()) {
112 | if (null !== $this->getIdleTimeoutExitCode()) {
113 | return $this->getIdleTimeoutExitCode();
114 | } else {
115 | throw $e;
116 | }
117 | }
118 | }
119 | }
120 | }
121 | }
122 |
123 | return 0;
124 | }
125 |
126 | /**
127 | * Purge the queue
128 | */
129 | public function purge()
130 | {
131 | $this->getChannel()->queue_purge($this->queueOptions['name'], true);
132 | }
133 |
134 | /**
135 | * Delete the queue
136 | */
137 | public function delete()
138 | {
139 | $this->getChannel()->queue_delete($this->queueOptions['name'], true);
140 | }
141 |
142 | protected function processMessageQueueCallback(AMQPMessage $msg, $queueName, $callback)
143 | {
144 | $this->dispatchEvent(
145 | BeforeProcessingMessageEvent::NAME,
146 | new BeforeProcessingMessageEvent($this, $msg)
147 | );
148 | try {
149 | $processFlag = call_user_func($callback, $msg);
150 | $this->handleProcessMessage($msg, $processFlag);
151 | $this->dispatchEvent(
152 | AfterProcessingMessageEvent::NAME,
153 | new AfterProcessingMessageEvent($this, $msg)
154 | );
155 | $this->logger->debug('Queue message processed', [
156 | 'amqp' => [
157 | 'queue' => $queueName,
158 | 'message' => $msg,
159 | 'return_code' => $processFlag,
160 | ],
161 | ]);
162 | } catch (Exception\StopConsumerException $e) {
163 | $this->logger->info('Consumer requested stop', [
164 | 'amqp' => [
165 | 'queue' => $queueName,
166 | 'message' => $msg,
167 | 'stacktrace' => $e->getTraceAsString(),
168 | ],
169 | ]);
170 | $this->handleProcessMessage($msg, $e->getHandleCode());
171 | $this->stopConsuming();
172 | } catch (\Exception $e) {
173 | $this->logger->error($e->getMessage(), [
174 | 'amqp' => [
175 | 'queue' => $queueName,
176 | 'message' => $msg,
177 | 'stacktrace' => $e->getTraceAsString(),
178 | ],
179 | ]);
180 | throw $e;
181 | } catch (\Error $e) {
182 | $this->logger->error($e->getMessage(), [
183 | 'amqp' => [
184 | 'queue' => $queueName,
185 | 'message' => $msg,
186 | 'stacktrace' => $e->getTraceAsString(),
187 | ],
188 | ]);
189 | throw $e;
190 | }
191 | }
192 |
193 | public function processMessage(AMQPMessage $msg)
194 | {
195 | $this->processMessageQueueCallback($msg, $this->queueOptions['name'], $this->callback);
196 | }
197 |
198 | protected function handleProcessMessage(AMQPMessage $msg, $processFlag)
199 | {
200 | if ($processFlag === ConsumerInterface::MSG_REJECT_REQUEUE || false === $processFlag) {
201 | // Reject and requeue message to RabbitMQ
202 | $msg->reject();
203 | } elseif ($processFlag === ConsumerInterface::MSG_SINGLE_NACK_REQUEUE) {
204 | // NACK and requeue message to RabbitMQ
205 | $msg->nack(true);
206 | } elseif ($processFlag === ConsumerInterface::MSG_REJECT) {
207 | // Reject and drop
208 | $msg->reject(false);
209 | } elseif ($processFlag !== ConsumerInterface::MSG_ACK_SENT) {
210 | // Remove message from queue only if callback return not false
211 | $msg->ack();
212 | }
213 |
214 | $this->consumed++;
215 | $this->maybeStopConsumer();
216 |
217 | if (!is_null($this->getMemoryLimit()) && $this->isRamAlmostOverloaded()) {
218 | $this->stopConsuming();
219 | }
220 | }
221 |
222 | /**
223 | * Checks if memory in use is greater or equal than memory allowed for this process
224 | *
225 | * @return boolean
226 | */
227 | protected function isRamAlmostOverloaded()
228 | {
229 | $memoryManager = new MemoryConsumptionChecker(new NativeMemoryUsageProvider());
230 |
231 | return $memoryManager->isRamAlmostOverloaded($this->getMemoryLimit().'M', '5M');
232 | }
233 |
234 | /**
235 | * @param \DateTime|null $dateTime
236 | */
237 | public function setGracefulMaxExecutionDateTime(?\DateTime $dateTime = null)
238 | {
239 | $this->gracefulMaxExecutionDateTime = $dateTime;
240 | }
241 |
242 | /**
243 | * @param int $secondsInTheFuture
244 | */
245 | public function setGracefulMaxExecutionDateTimeFromSecondsInTheFuture($secondsInTheFuture)
246 | {
247 | $this->setGracefulMaxExecutionDateTime(new \DateTime("+{$secondsInTheFuture} seconds"));
248 | }
249 |
250 | /**
251 | * @param int $exitCode
252 | */
253 | public function setGracefulMaxExecutionTimeoutExitCode($exitCode)
254 | {
255 | $this->gracefulMaxExecutionTimeoutExitCode = $exitCode;
256 | }
257 |
258 | public function setTimeoutWait(int $timeoutWait): void
259 | {
260 | $this->timeoutWait = $timeoutWait;
261 | }
262 |
263 | /**
264 | * @return \DateTime|null
265 | */
266 | public function getGracefulMaxExecutionDateTime()
267 | {
268 | return $this->gracefulMaxExecutionDateTime;
269 | }
270 |
271 | /**
272 | * @return int
273 | */
274 | public function getGracefulMaxExecutionTimeoutExitCode()
275 | {
276 | return $this->gracefulMaxExecutionTimeoutExitCode;
277 | }
278 |
279 | public function getTimeoutWait(): ?int
280 | {
281 | return $this->timeoutWait;
282 | }
283 |
284 | /**
285 | * Choose the timeout wait (in seconds) to use for the $this->getChannel()->wait() method.
286 | */
287 | private function chooseWaitTimeout(): int
288 | {
289 | if ($this->gracefulMaxExecutionDateTime) {
290 | $allowedExecutionDateInterval = $this->gracefulMaxExecutionDateTime->diff(new \DateTime());
291 | $allowedExecutionSeconds = $allowedExecutionDateInterval->days * 86400
292 | + $allowedExecutionDateInterval->h * 3600
293 | + $allowedExecutionDateInterval->i * 60
294 | + $allowedExecutionDateInterval->s;
295 |
296 | if (!$allowedExecutionDateInterval->invert) {
297 | $allowedExecutionSeconds *= -1;
298 | }
299 |
300 | /*
301 | * Respect the idle timeout if it's set and if it's less than
302 | * the remaining allowed execution.
303 | */
304 | if ($this->getIdleTimeout()
305 | && $this->getIdleTimeout() < $allowedExecutionSeconds
306 | ) {
307 | $waitTimeout = $this->getIdleTimeout();
308 | } else {
309 | $waitTimeout = $allowedExecutionSeconds;
310 | }
311 | } else {
312 | $waitTimeout = $this->getIdleTimeout();
313 | }
314 |
315 | if (!is_null($this->getTimeoutWait()) && $this->getTimeoutWait() > 0) {
316 | $waitTimeout = min($waitTimeout, $this->getTimeoutWait());
317 | }
318 | return $waitTimeout;
319 | }
320 |
321 | public function setLastActivityDateTime(\DateTime $dateTime)
322 | {
323 | $this->lastActivityDateTime = $dateTime;
324 | }
325 |
326 | protected function getLastActivityDateTime(): ?\DateTime
327 | {
328 | return $this->lastActivityDateTime;
329 | }
330 | }
331 |
--------------------------------------------------------------------------------
/RabbitMq/BatchConsumer.php:
--------------------------------------------------------------------------------
1 | gracefulMaxExecutionDateTime = $dateTime;
87 | }
88 |
89 | /**
90 | * @param int $secondsInTheFuture
91 | */
92 | public function setGracefulMaxExecutionDateTimeFromSecondsInTheFuture($secondsInTheFuture)
93 | {
94 | $this->setGracefulMaxExecutionDateTime(new \DateTime("+{$secondsInTheFuture} seconds"));
95 | }
96 |
97 | /**
98 | * @param \Closure|callable $callback
99 | *
100 | * @return $this
101 | */
102 | public function setCallback($callback)
103 | {
104 | $this->callback = $callback;
105 |
106 | return $this;
107 | }
108 |
109 | public function consume(int $batchAmountTarget = 0)
110 | {
111 | $this->batchAmountTarget = $batchAmountTarget;
112 |
113 | $this->setupConsumer();
114 |
115 | while ($this->getChannel()->is_consuming()) {
116 | if ($this->isCompleteBatch()) {
117 | $this->batchConsume();
118 | }
119 |
120 | $this->checkGracefulMaxExecutionDateTime();
121 | $this->maybeStopConsumer();
122 |
123 | $timeout = $this->isEmptyBatch() ? $this->getIdleTimeout() : $this->getTimeoutWait();
124 |
125 | try {
126 | $this->getChannel()->wait(null, false, $timeout);
127 | } catch (AMQPTimeoutException $e) {
128 | if (!$this->isEmptyBatch()) {
129 | $this->batchConsume();
130 | $this->maybeStopConsumer();
131 | } elseif ($this->keepAlive === true) {
132 | continue;
133 | } elseif (null !== $this->getIdleTimeoutExitCode()) {
134 | return $this->getIdleTimeoutExitCode();
135 | } else {
136 | throw $e;
137 | }
138 | }
139 | }
140 |
141 | return 0;
142 | }
143 |
144 | private function batchConsume()
145 | {
146 | try {
147 | $processFlags = call_user_func($this->callback, $this->messages);
148 | $this->handleProcessMessages($processFlags);
149 | $this->logger->debug('Queue message processed', [
150 | 'amqp' => [
151 | 'queue' => $this->queueOptions['name'],
152 | 'messages' => $this->messages,
153 | 'return_codes' => $processFlags,
154 | ],
155 | ]);
156 | } catch (Exception\StopConsumerException $e) {
157 | $this->logger->info('Consumer requested stop', [
158 | 'amqp' => [
159 | 'queue' => $this->queueOptions['name'],
160 | 'message' => $this->messages,
161 | 'stacktrace' => $e->getTraceAsString(),
162 | ],
163 | ]);
164 | $this->handleProcessMessages($e->getHandleCode());
165 | $this->resetBatch();
166 | $this->stopConsuming();
167 | } catch (\Exception $e) {
168 | $this->logger->error($e->getMessage(), [
169 | 'amqp' => [
170 | 'queue' => $this->queueOptions['name'],
171 | 'message' => $this->messages,
172 | 'stacktrace' => $e->getTraceAsString(),
173 | ],
174 | ]);
175 | $this->resetBatch();
176 | throw $e;
177 | } catch (\Error $e) {
178 | $this->logger->error($e->getMessage(), [
179 | 'amqp' => [
180 | 'queue' => $this->queueOptions['name'],
181 | 'message' => $this->messages,
182 | 'stacktrace' => $e->getTraceAsString(),
183 | ],
184 | ]);
185 | $this->resetBatch();
186 | throw $e;
187 | }
188 |
189 | $this->batchAmount++;
190 | $this->resetBatch();
191 | }
192 |
193 | /**
194 | * @param mixed $processFlags
195 | *
196 | * @return void
197 | */
198 | protected function handleProcessMessages($processFlags = null)
199 | {
200 | $processFlags = $this->analyzeProcessFlags($processFlags);
201 | foreach ($processFlags as $deliveryTag => $processFlag) {
202 | $this->handleProcessFlag($deliveryTag, $processFlag);
203 | }
204 | }
205 |
206 | /**
207 | * @param string|int $deliveryTag
208 | * @param mixed $processFlag
209 | *
210 | * @return void
211 | */
212 | private function handleProcessFlag($deliveryTag, $processFlag)
213 | {
214 | if ($processFlag === ConsumerInterface::MSG_REJECT_REQUEUE || false === $processFlag) {
215 | // Reject and requeue message to RabbitMQ
216 | $this->getMessageChannel($deliveryTag)->basic_reject($deliveryTag, true);
217 | } elseif ($processFlag === ConsumerInterface::MSG_SINGLE_NACK_REQUEUE) {
218 | // NACK and requeue message to RabbitMQ
219 | $this->getMessageChannel($deliveryTag)->basic_nack($deliveryTag, false, true);
220 | } elseif ($processFlag === ConsumerInterface::MSG_REJECT) {
221 | // Reject and drop
222 | $this->getMessageChannel($deliveryTag)->basic_reject($deliveryTag, false);
223 | } elseif ($processFlag === ConsumerInterface::MSG_ACK_SENT) {
224 | // do nothing, ACK should be already sent
225 | } else {
226 | // Remove message from queue only if callback return not false
227 | $this->getMessageChannel($deliveryTag)->basic_ack($deliveryTag);
228 | }
229 | }
230 |
231 | /**
232 | * @return bool
233 | */
234 | protected function isCompleteBatch()
235 | {
236 | return $this->batchCounter === $this->prefetchCount;
237 | }
238 |
239 | /**
240 | * @return bool
241 | */
242 | protected function isEmptyBatch()
243 | {
244 | return $this->batchCounter === 0;
245 | }
246 |
247 | /**
248 | * @param AMQPMessage $msg
249 | *
250 | * @return void
251 | *
252 | * @throws \Error
253 | * @throws \Exception
254 | */
255 | public function processMessage(AMQPMessage $msg)
256 | {
257 | $this->addMessage($msg);
258 |
259 | $this->maybeStopConsumer();
260 | }
261 |
262 | /**
263 | * @param mixed $processFlags
264 | *
265 | * @return array
266 | */
267 | private function analyzeProcessFlags($processFlags = null)
268 | {
269 | if (is_array($processFlags)) {
270 | if (count($processFlags) !== $this->batchCounter) {
271 | throw new AMQPRuntimeException(
272 | 'Method batchExecute() should return an array with elements equal with the number of messages processed'
273 | );
274 | }
275 |
276 | return $processFlags;
277 | }
278 |
279 | $response = [];
280 | foreach ($this->messages as $deliveryTag => $message) {
281 | $response[$deliveryTag] = $processFlags;
282 | }
283 |
284 | return $response;
285 | }
286 |
287 |
288 | /**
289 | * @return void
290 | */
291 | private function resetBatch()
292 | {
293 | $this->messages = [];
294 | $this->batchCounter = 0;
295 | }
296 |
297 | /**
298 | * @param AMQPMessage $message
299 | *
300 | * @return void
301 | */
302 | private function addMessage(AMQPMessage $message)
303 | {
304 | $this->batchCounter++;
305 | $this->messages[$message->getDeliveryTag()] = $message;
306 | }
307 |
308 | /**
309 | * @param int $deliveryTag
310 | *
311 | * @return AMQPMessage|null
312 | */
313 | private function getMessage($deliveryTag)
314 | {
315 | return $this->messages[$deliveryTag]
316 | ?? null
317 | ;
318 | }
319 |
320 | /**
321 | * @param int $deliveryTag
322 | *
323 | * @return AMQPChannel
324 | *
325 | * @throws AMQPRuntimeException
326 | */
327 | private function getMessageChannel($deliveryTag)
328 | {
329 | $message = $this->getMessage($deliveryTag);
330 | if ($message === null) {
331 | throw new AMQPRuntimeException(sprintf('Unknown delivery_tag %d!', $deliveryTag));
332 | }
333 |
334 | return $message->getChannel();
335 | }
336 |
337 | /**
338 | * @return void
339 | */
340 | public function stopConsuming()
341 | {
342 | if (!$this->isEmptyBatch()) {
343 | $this->batchConsume();
344 | }
345 |
346 | $this->getChannel()->basic_cancel($this->getConsumerTag(), false, true);
347 | }
348 |
349 | /**
350 | * @return void
351 | */
352 | protected function setupConsumer()
353 | {
354 | if ($this->autoSetupFabric) {
355 | $this->setupFabric();
356 | }
357 |
358 | $this->getChannel()->basic_consume($this->queueOptions['name'], $this->getConsumerTag(), false, $this->consumerOptions['no_ack'], false, false, [$this, 'processMessage']);
359 | }
360 |
361 | /**
362 | * @return void
363 | *
364 | * @throws \BadFunctionCallException
365 | */
366 | protected function maybeStopConsumer()
367 | {
368 | if (extension_loaded('pcntl') && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true)) {
369 | if (!function_exists('pcntl_signal_dispatch')) {
370 | throw new \BadFunctionCallException("Function 'pcntl_signal_dispatch' is referenced in the php.ini 'disable_functions' and can't be called.");
371 | }
372 |
373 | pcntl_signal_dispatch();
374 | }
375 |
376 | if ($this->forceStop || ($this->batchAmount == $this->batchAmountTarget && $this->batchAmountTarget > 0)) {
377 | $this->stopConsuming();
378 | }
379 |
380 | if (null !== $this->getMemoryLimit() && $this->isRamAlmostOverloaded()) {
381 | $this->stopConsuming();
382 | }
383 | }
384 |
385 | /**
386 | * @param string $tag
387 | *
388 | * @return $this
389 | */
390 | public function setConsumerTag($tag)
391 | {
392 | $this->consumerTag = $tag;
393 |
394 | return $this;
395 | }
396 |
397 | /**
398 | * @return string
399 | */
400 | public function getConsumerTag()
401 | {
402 | return $this->consumerTag;
403 | }
404 |
405 | /**
406 | * @return void
407 | */
408 | public function forceStopConsumer()
409 | {
410 | $this->forceStop = true;
411 | }
412 |
413 | /**
414 | * Sets the qos settings for the current channel
415 | * Consider that prefetchSize and global do not work with rabbitMQ version <= 8.0
416 | *
417 | * @param int $prefetchSize
418 | * @param int $prefetchCount
419 | * @param bool $global
420 | */
421 | public function setQosOptions($prefetchSize = 0, $prefetchCount = 0, $global = false)
422 | {
423 | $this->prefetchCount = $prefetchCount;
424 | $this->getChannel()->basic_qos($prefetchSize, $prefetchCount, $global);
425 | }
426 |
427 | /**
428 | * @param int $idleTimeout
429 | *
430 | * @return $this
431 | */
432 | public function setIdleTimeout($idleTimeout)
433 | {
434 | $this->idleTimeout = $idleTimeout;
435 |
436 | return $this;
437 | }
438 |
439 | /**
440 | * Set exit code to be returned when there is a timeout exception
441 | *
442 | * @param int $idleTimeoutExitCode
443 | *
444 | * @return $this
445 | */
446 | public function setIdleTimeoutExitCode($idleTimeoutExitCode)
447 | {
448 | $this->idleTimeoutExitCode = $idleTimeoutExitCode;
449 |
450 | return $this;
451 | }
452 |
453 | /**
454 | * keepAlive
455 | *
456 | * @return $this
457 | */
458 | public function keepAlive()
459 | {
460 | $this->keepAlive = true;
461 |
462 | return $this;
463 | }
464 |
465 | /**
466 | * Purge the queue
467 | */
468 | public function purge()
469 | {
470 | $this->getChannel()->queue_purge($this->queueOptions['name'], true);
471 | }
472 |
473 | /**
474 | * Delete the queue
475 | */
476 | public function delete()
477 | {
478 | $this->getChannel()->queue_delete($this->queueOptions['name'], true);
479 | }
480 |
481 | /**
482 | * Checks if memory in use is greater or equal than memory allowed for this process
483 | *
484 | * @return boolean
485 | */
486 | protected function isRamAlmostOverloaded()
487 | {
488 | return (memory_get_usage(true) >= ($this->getMemoryLimit() * 1048576));
489 | }
490 |
491 | /**
492 | * @return int
493 | */
494 | public function getIdleTimeout()
495 | {
496 | return $this->idleTimeout;
497 | }
498 |
499 | /**
500 | * Get exit code to be returned when there is a timeout exception
501 | *
502 | * @return int|null
503 | */
504 | public function getIdleTimeoutExitCode()
505 | {
506 | return $this->idleTimeoutExitCode;
507 | }
508 |
509 | /**
510 | * Resets the consumed property.
511 | * Use when you want to call start() or consume() multiple times.
512 | */
513 | public function resetConsumed()
514 | {
515 | $this->consumed = 0;
516 | }
517 |
518 | /**
519 | * @param int $timeout
520 | *
521 | * @return $this
522 | */
523 | public function setTimeoutWait($timeout)
524 | {
525 | $this->timeoutWait = $timeout;
526 |
527 | return $this;
528 | }
529 |
530 | /**
531 | * @param int $amount
532 | *
533 | * @return $this
534 | */
535 | public function setPrefetchCount($amount)
536 | {
537 | $this->prefetchCount = $amount;
538 |
539 | return $this;
540 | }
541 |
542 | /**
543 | * @return int
544 | */
545 | public function getTimeoutWait()
546 | {
547 | return $this->timeoutWait;
548 | }
549 |
550 | /**
551 | * @return int
552 | */
553 | public function getPrefetchCount()
554 | {
555 | return $this->prefetchCount;
556 | }
557 |
558 | /**
559 | * Set the memory limit
560 | *
561 | * @param int $memoryLimit
562 | */
563 | public function setMemoryLimit($memoryLimit)
564 | {
565 | $this->memoryLimit = $memoryLimit;
566 | }
567 |
568 | /**
569 | * Get the memory limit
570 | *
571 | * @return int
572 | */
573 | public function getMemoryLimit()
574 | {
575 | return $this->memoryLimit;
576 | }
577 |
578 | /**
579 | * Check graceful max execution date time and stop if limit is reached
580 | *
581 | * @return void
582 | */
583 | private function checkGracefulMaxExecutionDateTime()
584 | {
585 | if (!$this->gracefulMaxExecutionDateTime) {
586 | return;
587 | }
588 |
589 | $now = new \DateTime();
590 |
591 | if ($this->gracefulMaxExecutionDateTime > $now) {
592 | return;
593 | }
594 |
595 | $this->forceStopConsumer();
596 | }
597 | }
598 |
--------------------------------------------------------------------------------
/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Configuration implements ConfigurationInterface
16 | {
17 | /**
18 | * @var string
19 | */
20 | protected $name;
21 |
22 | /**
23 | * Configuration constructor.
24 | *
25 | * @param string $name
26 | */
27 | public function __construct($name)
28 | {
29 | $this->name = $name;
30 | }
31 |
32 | public function getConfigTreeBuilder(): TreeBuilder
33 | {
34 | $tree = new TreeBuilder($this->name);
35 | /** @var ArrayNodeDefinition $rootNode */
36 | $rootNode = $tree->getRootNode();
37 |
38 | $rootNode
39 | ->children()
40 | ->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
41 | ->booleanNode('enable_collector')->defaultValue(false)->end()
42 | ->booleanNode('sandbox')->defaultValue(false)->end()
43 | ->end()
44 | ;
45 |
46 | $this->addConnections($rootNode);
47 | $this->addBindings($rootNode);
48 | $this->addProducers($rootNode);
49 | $this->addConsumers($rootNode);
50 | $this->addMultipleConsumers($rootNode);
51 | $this->addDynamicConsumers($rootNode);
52 | $this->addBatchConsumers($rootNode);
53 | $this->addAnonConsumers($rootNode);
54 | $this->addRpcClients($rootNode);
55 | $this->addRpcServers($rootNode);
56 |
57 | return $tree;
58 | }
59 |
60 | protected function addConnections(ArrayNodeDefinition $node)
61 | {
62 | $node
63 | ->fixXmlConfig('connection')
64 | ->children()
65 | ->arrayNode('connections')
66 | ->useAttributeAsKey('key')
67 | ->canBeUnset()
68 | ->prototype('array')
69 | ->children()
70 | ->scalarNode('url')->defaultValue('')->end()
71 | ->scalarNode('host')->defaultValue('localhost')->end()
72 | ->scalarNode('port')->defaultValue(5672)->end()
73 | ->scalarNode('user')->defaultValue('guest')->end()
74 | ->scalarNode('password')->defaultValue('guest')->end()
75 | ->scalarNode('vhost')->defaultValue('/')->end()
76 | ->enumNode('login_method')
77 | ->values([
78 | 'AMQPLAIN', // Can be replaced by AMQPConnectionConfig constants when PhpAmqpLib 2 support is dropped
79 | 'PLAIN',
80 | 'EXTERNAL',
81 | ])
82 | ->defaultValue('AMQPLAIN')
83 | ->end()
84 | ->arrayNode('hosts')
85 | ->info('connection_timeout, read_write_timeout, use_socket, ssl_context, keepalive,
86 | heartbeat and connection_parameters_provider should be specified globally when
87 | you are using multiple hosts')
88 | ->canBeUnset()
89 | ->prototype('array')
90 | ->children()
91 | ->scalarNode('url')->defaultValue('')->end()
92 | ->scalarNode('host')->defaultValue('localhost')->end()
93 | ->scalarNode('port')->defaultValue(5672)->end()
94 | ->scalarNode('user')->defaultValue('guest')->end()
95 | ->scalarNode('password')->defaultValue('guest')->end()
96 | ->scalarNode('vhost')->defaultValue('/')->end()
97 | ->end()
98 | ->end()
99 | ->end()
100 | ->booleanNode('lazy')->defaultFalse()->end()
101 | ->scalarNode('connection_timeout')->defaultValue(3)->end()
102 | ->scalarNode('read_write_timeout')->defaultValue(3)->end()
103 | ->scalarNode('channel_rpc_timeout')->defaultValue(0.0)->end()
104 | ->booleanNode('use_socket')->defaultValue(false)->end()
105 | ->arrayNode('ssl_context')
106 | ->useAttributeAsKey('key')
107 | ->canBeUnset()
108 | ->prototype('variable')->end()
109 | ->end()
110 | ->booleanNode('keepalive')->defaultFalse()->info('requires php-amqplib v2.4.1+ and PHP5.4+')->end()
111 | ->scalarNode('heartbeat')->defaultValue(0)->info('requires php-amqplib v2.4.1+')->end()
112 | ->scalarNode('connection_parameters_provider')->end()
113 | ->end()
114 | ->end()
115 | ->end()
116 | ->end()
117 | ;
118 | }
119 |
120 | protected function addProducers(ArrayNodeDefinition $node)
121 | {
122 | $node
123 | ->fixXmlConfig('producer')
124 | ->children()
125 | ->arrayNode('producers')
126 | ->canBeUnset()
127 | ->useAttributeAsKey('key')
128 | ->prototype('array')
129 | ->append($this->getExchangeConfiguration())
130 | ->append($this->getQueueConfiguration())
131 | ->children()
132 | ->scalarNode('connection')->defaultValue('default')->end()
133 | ->scalarNode('auto_setup_fabric')->defaultTrue()->end()
134 | ->scalarNode('class')->defaultValue('%old_sound_rabbit_mq.producer.class%')->end()
135 | ->scalarNode('enable_logger')->defaultFalse()->end()
136 | ->scalarNode('service_alias')->defaultValue(null)->end()
137 | ->scalarNode('default_routing_key')->defaultValue('')->end()
138 | ->scalarNode('default_content_type')->defaultValue(Producer::DEFAULT_CONTENT_TYPE)->end()
139 | ->integerNode('default_delivery_mode')->min(1)->max(2)->defaultValue(2)->end()
140 | ->end()
141 | ->end()
142 | ->end()
143 | ->end()
144 | ;
145 | }
146 |
147 | protected function addBindings(ArrayNodeDefinition $node)
148 | {
149 | $node
150 | ->fixXmlConfig('binding')
151 | ->children()
152 | ->arrayNode('bindings')
153 | ->canBeUnset()
154 | ->prototype('array')
155 | ->children()
156 | ->scalarNode('connection')->defaultValue('default')->end()
157 | ->scalarNode('exchange')->defaultNull()->end()
158 | ->scalarNode('destination')->defaultNull()->end()
159 | ->scalarNode('routing_key')->defaultNull()->end()
160 | ->booleanNode('nowait')->defaultFalse()->end()
161 | ->booleanNode('destination_is_exchange')->defaultFalse()->end()
162 | ->variableNode('arguments')->defaultNull()->end()
163 | ->scalarNode('class')->defaultValue('%old_sound_rabbit_mq.binding.class%')->end()
164 | ->end()
165 | ->end()
166 | ->end()
167 | ->end()
168 | ;
169 | }
170 |
171 | protected function addConsumers(ArrayNodeDefinition $node)
172 | {
173 | $node
174 | ->fixXmlConfig('consumer')
175 | ->children()
176 | ->arrayNode('consumers')
177 | ->canBeUnset()
178 | ->useAttributeAsKey('key')
179 | ->prototype('array')
180 | ->append($this->getExchangeConfiguration())
181 | ->append($this->getQueueConfiguration())
182 | ->children()
183 | ->scalarNode('connection')->defaultValue('default')->end()
184 | ->scalarNode('callback')->isRequired()->end()
185 | ->scalarNode('idle_timeout')->end()
186 | ->scalarNode('idle_timeout_exit_code')->end()
187 | ->scalarNode('timeout_wait')->end()
188 | ->arrayNode('graceful_max_execution')
189 | ->canBeUnset()
190 | ->children()
191 | ->integerNode('timeout')->end()
192 | ->integerNode('exit_code')->defaultValue(0)->end()
193 | ->end()
194 | ->end()
195 | ->scalarNode('auto_setup_fabric')->defaultTrue()->end()
196 | ->arrayNode('options')
197 | ->canBeUnset()
198 | ->children()
199 | ->booleanNode('no_ack')->defaultFalse()->end()
200 | ->end()
201 | ->end()
202 | ->arrayNode('qos_options')
203 | ->canBeUnset()
204 | ->children()
205 | ->scalarNode('prefetch_size')->defaultValue(0)->end()
206 | ->scalarNode('prefetch_count')->defaultValue(0)->end()
207 | ->booleanNode('global')->defaultFalse()->end()
208 | ->end()
209 | ->end()
210 | ->scalarNode('enable_logger')->defaultFalse()->end()
211 | ->end()
212 | ->end()
213 | ->end()
214 | ->end()
215 | ;
216 | }
217 |
218 | protected function addMultipleConsumers(ArrayNodeDefinition $node)
219 | {
220 | $node
221 | ->fixXmlConfig('multiple_consumer')
222 | ->children()
223 | ->arrayNode('multiple_consumers')
224 | ->canBeUnset()
225 | ->useAttributeAsKey('key')
226 | ->prototype('array')
227 | ->append($this->getExchangeConfiguration())
228 | ->children()
229 | ->scalarNode('connection')->defaultValue('default')->end()
230 | ->scalarNode('idle_timeout')->end()
231 | ->scalarNode('idle_timeout_exit_code')->end()
232 | ->scalarNode('timeout_wait')->end()
233 | ->scalarNode('auto_setup_fabric')->defaultTrue()->end()
234 | ->arrayNode('options')
235 | ->canBeUnset()
236 | ->children()
237 | ->booleanNode('no_ack')->defaultFalse()->end()
238 | ->end()
239 | ->end()
240 | ->arrayNode('graceful_max_execution')
241 | ->canBeUnset()
242 | ->children()
243 | ->integerNode('timeout')->end()
244 | ->integerNode('exit_code')->defaultValue(0)->end()
245 | ->end()
246 | ->end()
247 | ->append($this->getMultipleQueuesConfiguration())
248 | ->arrayNode('qos_options')
249 | ->canBeUnset()
250 | ->children()
251 | ->scalarNode('prefetch_size')->defaultValue(0)->end()
252 | ->scalarNode('prefetch_count')->defaultValue(0)->end()
253 | ->booleanNode('global')->defaultFalse()->end()
254 | ->end()
255 | ->end()
256 | ->scalarNode('queues_provider')->defaultNull()->end()
257 | ->scalarNode('enable_logger')->defaultFalse()->end()
258 | ->end()
259 | ->end()
260 | ->end()
261 | ;
262 | }
263 |
264 | protected function addDynamicConsumers(ArrayNodeDefinition $node)
265 | {
266 | $node
267 | ->fixXmlConfig('dynamic_consumer')
268 | ->children()
269 | ->arrayNode('dynamic_consumers')
270 | ->canBeUnset()
271 | ->useAttributeAsKey('key')
272 | ->prototype('array')
273 | ->append($this->getExchangeConfiguration())
274 | ->children()
275 | ->scalarNode('connection')->defaultValue('default')->end()
276 | ->scalarNode('callback')->isRequired()->end()
277 | ->scalarNode('idle_timeout')->end()
278 | ->scalarNode('idle_timeout_exit_code')->end()
279 | ->scalarNode('timeout_wait')->end()
280 | ->arrayNode('graceful_max_execution')
281 | ->canBeUnset()
282 | ->children()
283 | ->integerNode('timeout')->end()
284 | ->integerNode('exit_code')->defaultValue(0)->end()
285 | ->end()
286 | ->end()
287 | ->scalarNode('auto_setup_fabric')->defaultTrue()->end()
288 | ->arrayNode('options')
289 | ->canBeUnset()
290 | ->children()
291 | ->booleanNode('no_ack')->defaultFalse()->end()
292 | ->end()
293 | ->end()
294 | ->arrayNode('qos_options')
295 | ->canBeUnset()
296 | ->children()
297 | ->scalarNode('prefetch_size')->defaultValue(0)->end()
298 | ->scalarNode('prefetch_count')->defaultValue(0)->end()
299 | ->booleanNode('global')->defaultFalse()->end()
300 | ->end()
301 | ->end()
302 | ->scalarNode('queue_options_provider')->isRequired()->end()
303 | ->scalarNode('enable_logger')->defaultFalse()->end()
304 | ->end()
305 | ->end()
306 | ->end()
307 | ->end()
308 | ;
309 | }
310 |
311 | /**
312 | * @param ArrayNodeDefinition $node
313 | *
314 | * @return void
315 | */
316 | protected function addBatchConsumers(ArrayNodeDefinition $node)
317 | {
318 | $node
319 | ->children()
320 | ->arrayNode('batch_consumers')
321 | ->canBeUnset()
322 | ->useAttributeAsKey('key')
323 | ->prototype('array')
324 | ->append($this->getExchangeConfiguration())
325 | ->append($this->getQueueConfiguration())
326 | ->children()
327 | ->scalarNode('connection')->defaultValue('default')->end()
328 | ->scalarNode('callback')->isRequired()->end()
329 | ->scalarNode('idle_timeout')->end()
330 | ->scalarNode('timeout_wait')->defaultValue(3)->end()
331 | ->scalarNode('idle_timeout_exit_code')->end()
332 | ->scalarNode('keep_alive')->defaultFalse()->end()
333 | ->arrayNode('graceful_max_execution')
334 | ->canBeUnset()
335 | ->children()
336 | ->integerNode('timeout')->end()
337 | ->end()
338 | ->end()
339 | ->scalarNode('auto_setup_fabric')->defaultTrue()->end()
340 | ->arrayNode('options')
341 | ->canBeUnset()
342 | ->children()
343 | ->booleanNode('no_ack')->defaultFalse()->end()
344 | ->end()
345 | ->end()
346 | ->arrayNode('qos_options')
347 | ->children()
348 | ->scalarNode('prefetch_size')->defaultValue(0)->end()
349 | ->scalarNode('prefetch_count')->defaultValue(2)->end()
350 | ->booleanNode('global')->defaultFalse()->end()
351 | ->end()
352 | ->end()
353 | ->scalarNode('enable_logger')->defaultFalse()->end()
354 | ->end()
355 | ->end()
356 | ->end()
357 | ->end()
358 | ;
359 | }
360 |
361 | protected function addAnonConsumers(ArrayNodeDefinition $node)
362 | {
363 | $node
364 | ->fixXmlConfig('anon_consumer')
365 | ->children()
366 | ->arrayNode('anon_consumers')
367 | ->canBeUnset()
368 | ->useAttributeAsKey('key')
369 | ->prototype('array')
370 | ->append($this->getExchangeConfiguration())
371 | ->children()
372 | ->scalarNode('connection')->defaultValue('default')->end()
373 | ->scalarNode('callback')->isRequired()->end()
374 | ->arrayNode('options')
375 | ->canBeUnset()
376 | ->children()
377 | ->booleanNode('no_ack')->defaultFalse()->end()
378 | ->end()
379 | ->end()
380 | ->end()
381 | ->end()
382 | ->end()
383 | ->end()
384 | ;
385 | }
386 |
387 | protected function addRpcClients(ArrayNodeDefinition $node)
388 | {
389 | $node
390 | ->fixXmlConfig('rpc_client')
391 | ->children()
392 | ->arrayNode('rpc_clients')
393 | ->canBeUnset()
394 | ->useAttributeAsKey('key')
395 | ->prototype('array')
396 | ->children()
397 | ->scalarNode('connection')->defaultValue('default')->end()
398 | ->booleanNode('expect_serialized_response')->defaultTrue()->end()
399 | ->scalarNode('unserializer')->defaultValue('unserialize')->end()
400 | ->booleanNode('lazy')->defaultFalse()->end()
401 | ->booleanNode('direct_reply_to')->defaultFalse()->end()
402 | ->end()
403 | ->end()
404 | ->end()
405 | ->end()
406 | ;
407 | }
408 |
409 | protected function addRpcServers(ArrayNodeDefinition $node)
410 | {
411 | $node
412 | ->fixXmlConfig('rpc_server')
413 | ->children()
414 | ->arrayNode('rpc_servers')
415 | ->canBeUnset()
416 | ->useAttributeAsKey('key')
417 | ->prototype('array')
418 | ->append($this->getExchangeConfiguration())
419 | ->append($this->getQueueConfiguration())
420 | ->children()
421 | ->scalarNode('connection')->defaultValue('default')->end()
422 | ->scalarNode('callback')->isRequired()->end()
423 | ->arrayNode('qos_options')
424 | ->canBeUnset()
425 | ->children()
426 | ->scalarNode('prefetch_size')->defaultValue(0)->end()
427 | ->scalarNode('prefetch_count')->defaultValue(0)->end()
428 | ->booleanNode('global')->defaultFalse()->end()
429 | ->end()
430 | ->end()
431 | ->scalarNode('serializer')->defaultValue('serialize')->end()
432 | ->scalarNode('enable_logger')->defaultFalse()->end()
433 | ->end()
434 | ->end()
435 | ->end()
436 | ->end()
437 | ;
438 | }
439 |
440 | protected function getExchangeConfiguration()
441 | {
442 | $node = new ArrayNodeDefinition('exchange_options');
443 |
444 | return $node
445 | ->children()
446 | ->scalarNode('name')->isRequired()->end()
447 | ->scalarNode('type')->isRequired()->end()
448 | ->booleanNode('passive')->defaultValue(false)->end()
449 | ->booleanNode('durable')->defaultValue(true)->end()
450 | ->booleanNode('auto_delete')->defaultValue(false)->end()
451 | ->booleanNode('internal')->defaultValue(false)->end()
452 | ->booleanNode('nowait')->defaultValue(false)->end()
453 | ->booleanNode('declare')->defaultValue(true)->end()
454 | ->variableNode('arguments')->defaultNull()->end()
455 | ->scalarNode('ticket')->defaultNull()->end()
456 | ->end()
457 | ;
458 | }
459 |
460 | protected function getQueueConfiguration()
461 | {
462 | $node = new ArrayNodeDefinition('queue_options');
463 |
464 | $this->addQueueNodeConfiguration($node);
465 |
466 | return $node;
467 | }
468 |
469 | protected function getMultipleQueuesConfiguration()
470 | {
471 | $node = new ArrayNodeDefinition('queues');
472 | $prototypeNode = $node->prototype('array');
473 |
474 | $this->addQueueNodeConfiguration($prototypeNode);
475 |
476 | $prototypeNode->children()
477 | ->scalarNode('callback')->isRequired()->end()
478 | ->end();
479 |
480 | $prototypeNode->end();
481 |
482 | return $node;
483 | }
484 |
485 | protected function addQueueNodeConfiguration(ArrayNodeDefinition $node)
486 | {
487 | $node
488 | ->fixXmlConfig('routing_key')
489 | ->children()
490 | ->scalarNode('name')->end()
491 | ->booleanNode('passive')->defaultFalse()->end()
492 | ->booleanNode('durable')->defaultTrue()->end()
493 | ->booleanNode('exclusive')->defaultFalse()->end()
494 | ->booleanNode('auto_delete')->defaultFalse()->end()
495 | ->booleanNode('nowait')->defaultFalse()->end()
496 | ->booleanNode('declare')->defaultTrue()->end()
497 | ->variableNode('arguments')->defaultNull()->end()
498 | ->scalarNode('ticket')->defaultNull()->end()
499 | ->arrayNode('routing_keys')
500 | ->prototype('scalar')->end()
501 | ->defaultValue([])
502 | ->end()
503 | ->end()
504 | ;
505 | }
506 | }
507 |
--------------------------------------------------------------------------------
/DependencyInjection/OldSoundRabbitMqExtension.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | class OldSoundRabbitMqExtension extends Extension
24 | {
25 | /**
26 | * @var ContainerBuilder
27 | */
28 | private $container;
29 |
30 | /**
31 | * @var Boolean Whether the data collector is enabled
32 | */
33 | private $collectorEnabled;
34 |
35 | private $channelIds = [];
36 |
37 | private $config = [];
38 |
39 | public function load(array $configs, ContainerBuilder $container): void
40 | {
41 | $this->container = $container;
42 |
43 | $loader = new XmlFileLoader($this->container, new FileLocator([__DIR__ . '/../Resources/config']));
44 | $loader->load('rabbitmq.xml');
45 |
46 | $configuration = $this->getConfiguration($configs, $container);
47 | $this->config = $this->processConfiguration($configuration, $configs);
48 |
49 | $this->collectorEnabled = $this->config['enable_collector'];
50 |
51 | $this->loadConnections();
52 | $this->loadBindings();
53 | $this->loadProducers();
54 | $this->loadConsumers();
55 | $this->loadMultipleConsumers();
56 | $this->loadDynamicConsumers();
57 | $this->loadBatchConsumers();
58 | $this->loadAnonConsumers();
59 | $this->loadRpcClients();
60 | $this->loadRpcServers();
61 |
62 | if ($this->collectorEnabled && $this->channelIds) {
63 | $channels = [];
64 | foreach (array_unique($this->channelIds) as $id) {
65 | $channels[] = new Reference($id);
66 | }
67 |
68 | $definition = $container->getDefinition('old_sound_rabbit_mq.data_collector');
69 | $definition->replaceArgument(0, $channels);
70 | } else {
71 | $this->container->removeDefinition('old_sound_rabbit_mq.data_collector');
72 | }
73 | }
74 |
75 | public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface
76 | {
77 | return new Configuration($this->getAlias());
78 | }
79 |
80 | protected function loadConnections()
81 | {
82 | foreach ($this->config['connections'] as $key => $connection) {
83 | $connectionSuffix = $connection['use_socket'] ? 'socket_connection.class' : 'connection.class';
84 | $classParam =
85 | $connection['lazy']
86 | ? '%old_sound_rabbit_mq.lazy.'.$connectionSuffix.'%'
87 | : '%old_sound_rabbit_mq.'.$connectionSuffix.'%';
88 |
89 | $definition = new Definition('%old_sound_rabbit_mq.connection_factory.class%', [
90 | $classParam, $connection,
91 | ]);
92 | if (isset($connection['connection_parameters_provider'])) {
93 | $definition->addArgument(new Reference($connection['connection_parameters_provider']));
94 | unset($connection['connection_parameters_provider']);
95 | }
96 | $definition->setPublic(false);
97 | $factoryName = sprintf('old_sound_rabbit_mq.connection_factory.%s', $key);
98 | $this->container->setDefinition($factoryName, $definition);
99 |
100 | $definition = new Definition($classParam);
101 | if (method_exists($definition, 'setFactory')) {
102 | // to be inlined in services.xml when dependency on Symfony DependencyInjection is bumped to 2.6
103 | $definition->setFactory([new Reference($factoryName), 'createConnection']);
104 | } else {
105 | // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6
106 | $definition->setFactoryService($factoryName);
107 | $definition->setFactoryMethod('createConnection');
108 | }
109 | $definition->addTag('old_sound_rabbit_mq.connection');
110 | $definition->setPublic(true);
111 |
112 | $this->container->setDefinition(sprintf('old_sound_rabbit_mq.connection.%s', $key), $definition);
113 | }
114 | }
115 |
116 | protected function loadBindings()
117 | {
118 | if ($this->config['sandbox']) {
119 | return;
120 | }
121 | foreach ($this->config['bindings'] as $binding) {
122 | ksort($binding);
123 | $definition = new Definition($binding['class']);
124 | $definition->addTag('old_sound_rabbit_mq.binding');
125 | $definition->addMethodCall('setArguments', [$binding['arguments']]);
126 | $definition->addMethodCall('setDestination', [$binding['destination']]);
127 | $definition->addMethodCall('setDestinationIsExchange', [$binding['destination_is_exchange']]);
128 | $definition->addMethodCall('setExchange', [$binding['exchange']]);
129 | $definition->addMethodCall('isNowait', [$binding['nowait']]);
130 | $definition->addMethodCall('setRoutingKey', [$binding['routing_key']]);
131 | $this->injectConnection($definition, $binding['connection']);
132 | $key = md5(json_encode($binding));
133 | if ($this->collectorEnabled) {
134 | // in the context of a binding, I don't thing logged channels are needed?
135 | $this->injectLoggedChannel($definition, $key, $binding['connection']);
136 | }
137 | $this->container->setDefinition(sprintf('old_sound_rabbit_mq.binding.%s', $key), $definition);
138 | }
139 | }
140 |
141 | protected function loadProducers()
142 | {
143 | if ($this->config['sandbox'] == false) {
144 | foreach ($this->config['producers'] as $key => $producer) {
145 | $definition = new Definition($producer['class']);
146 | $definition->setPublic(true);
147 | $definition->addTag('old_sound_rabbit_mq.base_amqp');
148 | $definition->addTag('old_sound_rabbit_mq.producer');
149 | //this producer doesn't define an exchange -> using AMQP Default
150 | if (!isset($producer['exchange_options'])) {
151 | $producer['exchange_options'] = $this->getDefaultExchangeOptions();
152 | }
153 | $definition->addMethodCall('setExchangeOptions', [$this->normalizeArgumentKeys($producer['exchange_options'])]);
154 | //this producer doesn't define a queue -> using AMQP Default
155 | if (!isset($producer['queue_options'])) {
156 | $producer['queue_options'] = $this->getDefaultQueueOptions();
157 | }
158 | $definition->addMethodCall('setQueueOptions', [$producer['queue_options']]);
159 | $this->injectConnection($definition, $producer['connection']);
160 | if ($this->collectorEnabled) {
161 | $this->injectLoggedChannel($definition, $key, $producer['connection']);
162 | }
163 | if (!$producer['auto_setup_fabric']) {
164 | $definition->addMethodCall('disableAutoSetupFabric');
165 | }
166 |
167 | if ($producer['enable_logger']) {
168 | $this->injectLogger($definition);
169 | }
170 |
171 | $producerServiceName = sprintf('old_sound_rabbit_mq.%s_producer', $key);
172 |
173 | $this->container->setDefinition($producerServiceName, $definition);
174 | if (null !== $producer['service_alias']) {
175 | $this->container->setAlias($producer['service_alias'], $producerServiceName);
176 | }
177 |
178 | // register alias for argument auto wiring
179 | if (method_exists($this->container, 'registerAliasForArgument')) {
180 | $argName = !str_ends_with(strtolower($key), 'producer') ? sprintf('%sProducer', $key) : $key;
181 | $this->container
182 | ->registerAliasForArgument($producerServiceName, ProducerInterface::class, $argName)
183 | ->setPublic(false);
184 |
185 | $this->container
186 | ->registerAliasForArgument($producerServiceName, $producer['class'], $argName)
187 | ->setPublic(false);
188 | }
189 |
190 | $definition->addMethodCall('setDefaultRoutingKey', [$producer['default_routing_key']]);
191 | $definition->addMethodCall('setContentType', [$producer['default_content_type']]);
192 | $definition->addMethodCall('setDeliveryMode', [$producer['default_delivery_mode']]);
193 | }
194 | } else {
195 | foreach ($this->config['producers'] as $key => $producer) {
196 | $definition = new Definition('%old_sound_rabbit_mq.fallback.class%');
197 | $producerServiceName = sprintf('old_sound_rabbit_mq.%s_producer', $key);
198 | $this->container->setDefinition($producerServiceName, $definition);
199 |
200 | // register alias for argumen auto wiring
201 | if (method_exists($this->container, 'registerAliasForArgument')) {
202 | $argName = !str_ends_with(strtolower($key), 'producer') ? sprintf('%sProducer', $key) : $key;
203 | $this->container
204 | ->registerAliasForArgument($producerServiceName, ProducerInterface::class, $argName)
205 | ->setPublic(false);
206 | }
207 | }
208 | }
209 | }
210 |
211 | protected function loadConsumers()
212 | {
213 | foreach ($this->config['consumers'] as $key => $consumer) {
214 | $definition = new Definition('%old_sound_rabbit_mq.consumer.class%');
215 | $definition->setPublic(true);
216 | $definition->addTag('old_sound_rabbit_mq.base_amqp');
217 | $definition->addTag('old_sound_rabbit_mq.consumer');
218 | //this consumer doesn't define an exchange -> using AMQP Default
219 | if (!isset($consumer['exchange_options'])) {
220 | $consumer['exchange_options'] = $this->getDefaultExchangeOptions();
221 | }
222 | $definition->addMethodCall('setExchangeOptions', [$this->normalizeArgumentKeys($consumer['exchange_options'])]);
223 | //this consumer doesn't define a queue -> using AMQP Default
224 | if (!isset($consumer['queue_options'])) {
225 | $consumer['queue_options'] = $this->getDefaultQueueOptions();
226 | }
227 | $definition->addMethodCall('setQueueOptions', [$this->normalizeArgumentKeys($consumer['queue_options'])]);
228 | $definition->addMethodCall('setCallback', [[new Reference($consumer['callback']), 'execute']]);
229 |
230 | if (array_key_exists('qos_options', $consumer)) {
231 | $definition->addMethodCall('setQosOptions', [
232 | $consumer['qos_options']['prefetch_size'],
233 | $consumer['qos_options']['prefetch_count'],
234 | $consumer['qos_options']['global'],
235 | ]);
236 | }
237 |
238 | if (isset($consumer['idle_timeout'])) {
239 | $definition->addMethodCall('setIdleTimeout', [$consumer['idle_timeout']]);
240 | }
241 | if (isset($consumer['idle_timeout_exit_code'])) {
242 | $definition->addMethodCall('setIdleTimeoutExitCode', [$consumer['idle_timeout_exit_code']]);
243 | }
244 | if (isset($consumer['timeout_wait'])) {
245 | $definition->addMethodCall('setTimeoutWait', [$consumer['timeout_wait']]);
246 | }
247 | if (isset($consumer['graceful_max_execution'])) {
248 | $definition->addMethodCall(
249 | 'setGracefulMaxExecutionDateTimeFromSecondsInTheFuture',
250 | [$consumer['graceful_max_execution']['timeout']]
251 | );
252 | $definition->addMethodCall(
253 | 'setGracefulMaxExecutionTimeoutExitCode',
254 | [$consumer['graceful_max_execution']['exit_code']]
255 | );
256 | }
257 | if (!$consumer['auto_setup_fabric']) {
258 | $definition->addMethodCall('disableAutoSetupFabric');
259 | }
260 | if (isset($consumer['options'])) {
261 | $definition->addMethodCall(
262 | 'setConsumerOptions',
263 | [$this->normalizeArgumentKeys($consumer['options'])]
264 | );
265 | }
266 |
267 | $this->injectConnection($definition, $consumer['connection']);
268 | if ($this->collectorEnabled) {
269 | $this->injectLoggedChannel($definition, $key, $consumer['connection']);
270 | }
271 |
272 | if ($consumer['enable_logger']) {
273 | $this->injectLogger($definition);
274 | }
275 |
276 | $name = sprintf('old_sound_rabbit_mq.%s_consumer', $key);
277 | $this->container->setDefinition($name, $definition);
278 | $this->addDequeuerAwareCall($consumer['callback'], $name);
279 |
280 | // register alias for argument auto wiring
281 | if (method_exists($this->container, 'registerAliasForArgument')) {
282 | $argName = !str_ends_with(strtolower($key), 'consumer') ? sprintf('%sConsumer', $key) : $key;
283 | $this->container
284 | ->registerAliasForArgument($name, ConsumerInterface::class, $argName)
285 | ->setPublic(false);
286 |
287 | $this->container
288 | ->registerAliasForArgument($name, '%old_sound_rabbit_mq.consumer.class%', $argName)
289 | ->setPublic(false);
290 | }
291 | }
292 | }
293 |
294 | protected function loadMultipleConsumers()
295 | {
296 | foreach ($this->config['multiple_consumers'] as $key => $consumer) {
297 | $queues = [];
298 | $callbacks = [];
299 |
300 | if (empty($consumer['queues']) && empty($consumer['queues_provider'])) {
301 | throw new InvalidConfigurationException(
302 | "Error on loading $key multiple consumer. " .
303 | "Either 'queues' or 'queues_provider' parameters should be defined."
304 | );
305 | }
306 |
307 | foreach ($consumer['queues'] as $queueName => $queueOptions) {
308 | $queues[$queueOptions['name']] = $queueOptions;
309 | $queues[$queueOptions['name']]['callback'] = [new Reference($queueOptions['callback']), 'execute'];
310 | $callbacks[] = $queueOptions['callback'];
311 | }
312 |
313 | $definition = new Definition('%old_sound_rabbit_mq.multi_consumer.class%');
314 | $definition
315 | ->setPublic(true)
316 | ->addTag('old_sound_rabbit_mq.base_amqp')
317 | ->addTag('old_sound_rabbit_mq.multi_consumer')
318 | ->addMethodCall('setExchangeOptions', [$this->normalizeArgumentKeys($consumer['exchange_options'])])
319 | ->addMethodCall('setQueues', [$this->normalizeArgumentKeys($queues)]);
320 |
321 | if ($consumer['queues_provider']) {
322 | $definition->addMethodCall(
323 | 'setQueuesProvider',
324 | [new Reference($consumer['queues_provider'])]
325 | );
326 | }
327 |
328 | if (array_key_exists('qos_options', $consumer)) {
329 | $definition->addMethodCall('setQosOptions', [
330 | $consumer['qos_options']['prefetch_size'],
331 | $consumer['qos_options']['prefetch_count'],
332 | $consumer['qos_options']['global'],
333 | ]);
334 | }
335 |
336 | if (isset($consumer['idle_timeout'])) {
337 | $definition->addMethodCall('setIdleTimeout', [$consumer['idle_timeout']]);
338 | }
339 | if (isset($consumer['idle_timeout_exit_code'])) {
340 | $definition->addMethodCall('setIdleTimeoutExitCode', [$consumer['idle_timeout_exit_code']]);
341 | }
342 | if (isset($consumer['timeout_wait'])) {
343 | $definition->addMethodCall('setTimeoutWait', [$consumer['timeout_wait']]);
344 | }
345 | if (isset($consumer['graceful_max_execution'])) {
346 | $definition->addMethodCall(
347 | 'setGracefulMaxExecutionDateTimeFromSecondsInTheFuture',
348 | [$consumer['graceful_max_execution']['timeout']]
349 | );
350 | $definition->addMethodCall(
351 | 'setGracefulMaxExecutionTimeoutExitCode',
352 | [$consumer['graceful_max_execution']['exit_code']]
353 | );
354 | }
355 | if (!$consumer['auto_setup_fabric']) {
356 | $definition->addMethodCall('disableAutoSetupFabric');
357 | }
358 | if (isset($consumer['options'])) {
359 | $definition->addMethodCall(
360 | 'setConsumerOptions',
361 | [$this->normalizeArgumentKeys($consumer['options'])]
362 | );
363 | }
364 |
365 | $this->injectConnection($definition, $consumer['connection']);
366 | if ($this->collectorEnabled) {
367 | $this->injectLoggedChannel($definition, $key, $consumer['connection']);
368 | }
369 |
370 | if ($consumer['enable_logger']) {
371 | $this->injectLogger($definition);
372 | }
373 |
374 | $name = sprintf('old_sound_rabbit_mq.%s_multiple', $key);
375 | $this->container->setDefinition($name, $definition);
376 | if ($consumer['queues_provider']) {
377 | $this->addDequeuerAwareCall($consumer['queues_provider'], $name);
378 | }
379 | foreach ($callbacks as $callback) {
380 | $this->addDequeuerAwareCall($callback, $name);
381 | }
382 | }
383 | }
384 |
385 | protected function loadDynamicConsumers()
386 | {
387 | foreach ($this->config['dynamic_consumers'] as $key => $consumer) {
388 | if (empty($consumer['queue_options_provider'])) {
389 | throw new InvalidConfigurationException(
390 | "Error on loading $key dynamic consumer. " .
391 | "'queue_provider' parameter should be defined."
392 | );
393 | }
394 |
395 | $definition = new Definition('%old_sound_rabbit_mq.dynamic_consumer.class%');
396 | $definition
397 | ->setPublic(true)
398 | ->addTag('old_sound_rabbit_mq.base_amqp')
399 | ->addTag('old_sound_rabbit_mq.consumer')
400 | ->addTag('old_sound_rabbit_mq.dynamic_consumer')
401 | ->addMethodCall('setExchangeOptions', [$this->normalizeArgumentKeys($consumer['exchange_options'])])
402 | ->addMethodCall('setCallback', [[new Reference($consumer['callback']), 'execute']]);
403 |
404 | if (array_key_exists('qos_options', $consumer)) {
405 | $definition->addMethodCall('setQosOptions', [
406 | $consumer['qos_options']['prefetch_size'],
407 | $consumer['qos_options']['prefetch_count'],
408 | $consumer['qos_options']['global'],
409 | ]);
410 | }
411 |
412 | $definition->addMethodCall(
413 | 'setQueueOptionsProvider',
414 | [new Reference($consumer['queue_options_provider'])]
415 | );
416 |
417 | if (isset($consumer['idle_timeout'])) {
418 | $definition->addMethodCall('setIdleTimeout', [$consumer['idle_timeout']]);
419 | }
420 | if (isset($consumer['idle_timeout_exit_code'])) {
421 | $definition->addMethodCall('setIdleTimeoutExitCode', [$consumer['idle_timeout_exit_code']]);
422 | }
423 | if (isset($consumer['timeout_wait'])) {
424 | $definition->addMethodCall('setTimeoutWait', [$consumer['timeout_wait']]);
425 | }
426 | if (isset($consumer['graceful_max_execution'])) {
427 | $definition->addMethodCall(
428 | 'setGracefulMaxExecutionDateTimeFromSecondsInTheFuture',
429 | [$consumer['graceful_max_execution']['timeout']]
430 | );
431 | $definition->addMethodCall(
432 | 'setGracefulMaxExecutionTimeoutExitCode',
433 | [$consumer['graceful_max_execution']['exit_code']]
434 | );
435 | }
436 | if (!$consumer['auto_setup_fabric']) {
437 | $definition->addMethodCall('disableAutoSetupFabric');
438 | }
439 | if (isset($consumer['options'])) {
440 | $definition->addMethodCall(
441 | 'setConsumerOptions',
442 | [$this->normalizeArgumentKeys($consumer['options'])]
443 | );
444 | }
445 |
446 | $this->injectConnection($definition, $consumer['connection']);
447 | if ($this->collectorEnabled) {
448 | $this->injectLoggedChannel($definition, $key, $consumer['connection']);
449 | }
450 |
451 | if ($consumer['enable_logger']) {
452 | $this->injectLogger($definition);
453 | }
454 |
455 | $name = sprintf('old_sound_rabbit_mq.%s_dynamic', $key);
456 | $this->container->setDefinition($name, $definition);
457 | $this->addDequeuerAwareCall($consumer['callback'], $name);
458 | $this->addDequeuerAwareCall($consumer['queue_options_provider'], $name);
459 | }
460 | }
461 |
462 | protected function loadBatchConsumers()
463 | {
464 | foreach ($this->config['batch_consumers'] as $key => $consumer) {
465 | $definition = new Definition('%old_sound_rabbit_mq.batch_consumer.class%');
466 |
467 | if (!isset($consumer['exchange_options'])) {
468 | $consumer['exchange_options'] = $this->getDefaultExchangeOptions();
469 | }
470 |
471 | $definition
472 | ->setPublic(true)
473 | ->addTag('old_sound_rabbit_mq.base_amqp')
474 | ->addTag('old_sound_rabbit_mq.batch_consumer')
475 | ->addMethodCall('setTimeoutWait', [$consumer['timeout_wait']])
476 | ->addMethodCall('setPrefetchCount', [$consumer['qos_options']['prefetch_count']])
477 | ->addMethodCall('setCallback', [[new Reference($consumer['callback']), 'batchExecute']])
478 | ->addMethodCall('setExchangeOptions', [$this->normalizeArgumentKeys($consumer['exchange_options'])])
479 | ->addMethodCall('setQueueOptions', [$this->normalizeArgumentKeys($consumer['queue_options'])])
480 | ->addMethodCall('setQosOptions', [
481 | $consumer['qos_options']['prefetch_size'],
482 | $consumer['qos_options']['prefetch_count'],
483 | $consumer['qos_options']['global'],
484 | ])
485 | ;
486 |
487 | if (isset($consumer['idle_timeout_exit_code'])) {
488 | $definition->addMethodCall('setIdleTimeoutExitCode', [$consumer['idle_timeout_exit_code']]);
489 | }
490 |
491 | if (isset($consumer['idle_timeout'])) {
492 | $definition->addMethodCall('setIdleTimeout', [$consumer['idle_timeout']]);
493 | }
494 |
495 | if (isset($consumer['graceful_max_execution'])) {
496 | $definition->addMethodCall(
497 | 'setGracefulMaxExecutionDateTimeFromSecondsInTheFuture',
498 | [$consumer['graceful_max_execution']['timeout']]
499 | );
500 | }
501 |
502 | if (!$consumer['auto_setup_fabric']) {
503 | $definition->addMethodCall('disableAutoSetupFabric');
504 | }
505 |
506 | if (isset($consumer['options'])) {
507 | $definition->addMethodCall(
508 | 'setConsumerOptions',
509 | [$this->normalizeArgumentKeys($consumer['options'])]
510 | );
511 | }
512 |
513 | if ($consumer['keep_alive']) {
514 | $definition->addMethodCall('keepAlive');
515 | }
516 |
517 | $this->injectConnection($definition, $consumer['connection']);
518 | if ($this->collectorEnabled) {
519 | $this->injectLoggedChannel($definition, $key, $consumer['connection']);
520 | }
521 |
522 | if ($consumer['enable_logger']) {
523 | $this->injectLogger($definition);
524 | }
525 |
526 | $this->container->setDefinition(sprintf('old_sound_rabbit_mq.%s_batch', $key), $definition);
527 | }
528 | }
529 |
530 | protected function loadAnonConsumers()
531 | {
532 | foreach ($this->config['anon_consumers'] as $key => $anon) {
533 | $definition = new Definition('%old_sound_rabbit_mq.anon_consumer.class%');
534 | $definition
535 | ->setPublic(true)
536 | ->addTag('old_sound_rabbit_mq.base_amqp')
537 | ->addTag('old_sound_rabbit_mq.anon_consumer')
538 | ->addMethodCall('setExchangeOptions', [$this->normalizeArgumentKeys($anon['exchange_options'])])
539 | ->addMethodCall('setCallback', [[new Reference($anon['callback']), 'execute']]);
540 |
541 | if (isset($anon['options'])) {
542 | $definition->addMethodCall(
543 | 'setConsumerOptions',
544 | [$this->normalizeArgumentKeys($anon['options'])]
545 | );
546 | }
547 |
548 | $this->injectConnection($definition, $anon['connection']);
549 | if ($this->collectorEnabled) {
550 | $this->injectLoggedChannel($definition, $key, $anon['connection']);
551 | }
552 |
553 | $name = sprintf('old_sound_rabbit_mq.%s_anon', $key);
554 | $this->container->setDefinition($name, $definition);
555 | $this->addDequeuerAwareCall($anon['callback'], $name);
556 | }
557 | }
558 |
559 | /**
560 | * Symfony 2 converts '-' to '_' when defined in the configuration. This leads to problems when using x-ha-policy
561 | * parameter. So we revert the change for right configurations.
562 | *
563 | * @param array $config
564 | *
565 | * @return array
566 | */
567 | private function normalizeArgumentKeys(array $config): array
568 | {
569 | if (isset($config['arguments'])) {
570 | $arguments = $config['arguments'];
571 | // support for old configuration
572 | if (is_string($arguments)) {
573 | $arguments = $this->argumentsStringAsArray($arguments);
574 | }
575 |
576 | $newArguments = [];
577 | foreach ($arguments as $key => $value) {
578 | if (strstr($key, '_')) {
579 | $key = str_replace('_', '-', $key);
580 | }
581 | $newArguments[$key] = $value;
582 | }
583 | $config['arguments'] = $newArguments;
584 | }
585 | return $config;
586 | }
587 |
588 | /**
589 | * Support for arguments provided as string. Support for old configuration files.
590 | *
591 | * @deprecated
592 | * @param string $arguments
593 | * @return array
594 | */
595 | private function argumentsStringAsArray($arguments): array
596 | {
597 | $argumentsArray = [];
598 |
599 | $argumentPairs = explode(',', $arguments);
600 | foreach ($argumentPairs as $argument) {
601 | $argumentPair = explode(':', $argument);
602 | $type = 'S';
603 | if (isset($argumentPair[2])) {
604 | $type = $argumentPair[2];
605 | }
606 | $argumentsArray[$argumentPair[0]] = [$type, $argumentPair[1]];
607 | }
608 |
609 | return $argumentsArray;
610 | }
611 |
612 | protected function loadRpcClients()
613 | {
614 | foreach ($this->config['rpc_clients'] as $key => $client) {
615 | $definition = new Definition('%old_sound_rabbit_mq.rpc_client.class%');
616 | $definition->setLazy($client['lazy']);
617 | $definition
618 | ->addTag('old_sound_rabbit_mq.rpc_client')
619 | ->addMethodCall('initClient', [$client['expect_serialized_response']]);
620 | $this->injectConnection($definition, $client['connection']);
621 | if ($this->collectorEnabled) {
622 | $this->injectLoggedChannel($definition, $key, $client['connection']);
623 | }
624 | if (array_key_exists('unserializer', $client)) {
625 | $definition->addMethodCall('setUnserializer', [$client['unserializer']]);
626 | }
627 | if (array_key_exists('direct_reply_to', $client)) {
628 | $definition->addMethodCall('setDirectReplyTo', [$client['direct_reply_to']]);
629 | }
630 | $definition->setPublic(true);
631 |
632 | $this->container->setDefinition(sprintf('old_sound_rabbit_mq.%s_rpc', $key), $definition);
633 | }
634 | }
635 |
636 | protected function loadRpcServers()
637 | {
638 | foreach ($this->config['rpc_servers'] as $key => $server) {
639 | $definition = new Definition('%old_sound_rabbit_mq.rpc_server.class%');
640 | $definition
641 | ->setPublic(true)
642 | ->addTag('old_sound_rabbit_mq.base_amqp')
643 | ->addTag('old_sound_rabbit_mq.rpc_server')
644 | ->addMethodCall('initServer', [$key])
645 | ->addMethodCall('setCallback', [[new Reference($server['callback']), 'execute']]);
646 | $this->injectConnection($definition, $server['connection']);
647 | if ($this->collectorEnabled) {
648 | $this->injectLoggedChannel($definition, $key, $server['connection']);
649 | }
650 | if (array_key_exists('qos_options', $server)) {
651 | $definition->addMethodCall('setQosOptions', [
652 | $server['qos_options']['prefetch_size'],
653 | $server['qos_options']['prefetch_count'],
654 | $server['qos_options']['global'],
655 | ]);
656 | }
657 | if (array_key_exists('exchange_options', $server)) {
658 | $definition->addMethodCall('setExchangeOptions', [$server['exchange_options']]);
659 | }
660 | if (array_key_exists('queue_options', $server)) {
661 | $definition->addMethodCall('setQueueOptions', [$server['queue_options']]);
662 | }
663 | if (array_key_exists('serializer', $server)) {
664 | $definition->addMethodCall('setSerializer', [$server['serializer']]);
665 | }
666 | $this->container->setDefinition(sprintf('old_sound_rabbit_mq.%s_server', $key), $definition);
667 | }
668 | }
669 |
670 | protected function injectLoggedChannel(Definition $definition, $name, $connectionName)
671 | {
672 | $id = sprintf('old_sound_rabbit_mq.channel.%s', $name);
673 | $channel = new Definition('%old_sound_rabbit_mq.logged.channel.class%');
674 | $channel
675 | ->setPublic(false)
676 | ->addTag('old_sound_rabbit_mq.logged_channel');
677 | $this->injectConnection($channel, $connectionName);
678 |
679 | $this->container->setDefinition($id, $channel);
680 |
681 | $this->channelIds[] = $id;
682 | $definition->addArgument(new Reference($id));
683 | }
684 |
685 | protected function injectConnection(Definition $definition, $connectionName)
686 | {
687 | $definition->addArgument(new Reference(sprintf('old_sound_rabbit_mq.connection.%s', $connectionName)));
688 | }
689 |
690 | public function getAlias(): string
691 | {
692 | return 'old_sound_rabbit_mq';
693 | }
694 |
695 | /**
696 | * Add proper dequeuer aware call
697 | *
698 | * @param string $callback
699 | * @param string $name
700 | * @throws \ReflectionException
701 | */
702 | protected function addDequeuerAwareCall($callback, $name)
703 | {
704 | if (!$this->container->has($callback)) {
705 | return;
706 | }
707 |
708 | $callbackDefinition = $this->container->findDefinition($callback);
709 | $refClass = new \ReflectionClass($callbackDefinition->getClass());
710 | if ($refClass->implementsInterface('OldSound\RabbitMqBundle\RabbitMq\DequeuerAwareInterface')) {
711 | $callbackDefinition->addMethodCall('setDequeuer', [new Reference($name)]);
712 | }
713 | }
714 |
715 | private function injectLogger(Definition $definition)
716 | {
717 | $definition->addTag('monolog.logger', [
718 | 'channel' => 'phpamqplib',
719 | ]);
720 | $definition->addMethodCall('setLogger', [new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]);
721 | }
722 |
723 | /**
724 | * Get default AMQP exchange options
725 | *
726 | * @return array
727 | */
728 | protected function getDefaultExchangeOptions(): array
729 | {
730 | return [
731 | 'name' => '',
732 | 'type' => 'direct',
733 | 'passive' => true,
734 | 'declare' => false,
735 | ];
736 | }
737 |
738 | /**
739 | * Get default AMQP queue options
740 | *
741 | * @return array
742 | */
743 | protected function getDefaultQueueOptions(): array
744 | {
745 | return [
746 | 'name' => '',
747 | 'declare' => false,
748 | ];
749 | }
750 | }
751 |
--------------------------------------------------------------------------------