Completed
Pull Request — 3.x (#246)
by
unknown
13:12
created

createDoctrineQueueBackend()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\NotificationBundle\DependencyInjection;
13
14
use Sonata\EasyExtendsBundle\Mapper\DoctrineCollector;
15
use Sonata\NotificationBundle\Model\MessageInterface;
16
use Symfony\Component\Config\FileLocator;
17
use Symfony\Component\DependencyInjection\ContainerBuilder;
18
use Symfony\Component\DependencyInjection\Definition;
19
use Symfony\Component\DependencyInjection\Loader;
20
use Symfony\Component\DependencyInjection\Reference;
21
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
22
23
class SonataNotificationExtension extends Extension
24
{
25
    /**
26
     * @var int
27
     */
28
    protected $amqpCounter = 0;
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public function load(array $configs, ContainerBuilder $container)
34
    {
35
        $configuration = new Configuration();
36
        $config = $this->processConfiguration($configuration, $configs);
37
38
        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
39
40
        $loader->load('core.xml');
41
42
        if ('orm' == $config['db_driver']) {
43
            $loader->load('doctrine_orm.xml');
44
        } else {
45
            $loader->load('doctrine_mongodb.xml');
46
        }
47
48
        $loader->load('backend.xml');
49
        $loader->load('consumer.xml');
50
        $loader->load('selector.xml');
51
        $loader->load('event.xml');
52
53
        if ('mongodb' == $config['db_driver']) {
54
            $optimize_definition = $container->getDefinition('sonata.notification.event.doctrine_optimize');
55
            $optimize_definition->replaceArgument(0, new Reference('doctrine_mongodb'));
56
            $backend_optimize_definition = $container->getDefinition('sonata.notification.event.doctrine_backend_optimize');
57
            $backend_optimize_definition->replaceArgument(0, new Reference('doctrine_mongodb'));
58
            $selector_definition = $container->getDefinition('sonata.notification.erroneous_messages_selector');
59
            $selector_definition->replaceArgument(0, new Reference('doctrine_mongodb'));
60
        }
61
62
        if ($config['consumers']['register_default']) {
63
            $loader->load('default_consumers.xml');
64
        }
65
66
        $bundles = $container->getParameter('kernel.bundles');
67
68
        if (isset($bundles['FOSRestBundle']) && isset($bundles['NelmioApiDocBundle'])) {
69
            $loader->load('api_controllers.xml');
70
            $loader->load('api_form.xml');
71
        }
72
73
        // for now, only support for ORM
74
        if ($config['admin']['enabled'] && isset($bundles['SonataDoctrineORMAdminBundle'])) {
75
            $loader->load('admin.xml');
76
        }
77
78
        if (isset($bundles['LiipMonitorBundle'])) {
79
            $loader->load('checkmonitor.xml');
80
        }
81
82
        $this->checkConfiguration($config);
83
84
        $container->setAlias('sonata.notification.backend', $config['backend']);
85
        $container->setParameter('sonata.notification.backend', $config['backend']);
86
87
        $this->registerDoctrineMapping($config);
88
        $this->registerParameters($container, $config);
89
        $this->configureBackends($container, $config);
90
        $this->configureClass($container, $config);
91
        $this->configureListeners($container, $config);
92
        $this->configureAdmin($container, $config);
93
    }
94
95
    /**
96
     * @param ContainerBuilder $container
97
     * @param array            $config
98
     */
99
    public function configureClass(ContainerBuilder $container, $config)
100
    {
101
        // admin configuration
102
        $container->setParameter('sonata.notification.admin.message.entity', $config['class']['message']);
103
104
        // manager configuration
105
        $container->setParameter('sonata.notification.manager.message.entity', $config['class']['message']);
106
    }
107
108
    /**
109
     * @param ContainerBuilder $container
110
     * @param array            $config
111
     */
112
    public function configureAdmin(ContainerBuilder $container, $config)
113
    {
114
        $container->setParameter('sonata.notification.admin.message.class', $config['admin']['message']['class']);
115
        $container->setParameter('sonata.notification.admin.message.controller', $config['admin']['message']['controller']);
116
        $container->setParameter('sonata.notification.admin.message.translation_domain', $config['admin']['message']['translation']);
117
    }
118
119
    /**
120
     * @param ContainerBuilder $container
121
     * @param array            $config
122
     */
123
    public function registerParameters(ContainerBuilder $container, $config)
124
    {
125
        $container->setParameter('sonata.notification.message.class', $config['class']['message']);
126
        $container->setParameter('sonata.notification.admin.message.entity', $config['class']['message']);
127
    }
128
129
    /**
130
     * @param ContainerBuilder $container
131
     * @param array            $config
132
     */
133
    public function configureBackends(ContainerBuilder $container, $config)
134
    {
135
        // set the default value, will be erase if required
136
        if ('orm' == $config['db_driver']) {
137
            $container->setAlias('sonata.notification.manager.message', 'sonata.notification.manager.message.default');
138
        } else {
139
            $container->setAlias('sonata.notification.manager.message', 'sonata.notification.manager.mongodb.message.default');
140
        }
141
142
        if (isset($config['backends']['rabbitmq']) && $config['backend'] === 'sonata.notification.backend.rabbitmq') {
143
            $this->configureRabbitmq($container, $config);
144
145
            $container->removeDefinition('sonata.notification.backend.doctrine');
146
        } else {
147
            $container->removeDefinition('sonata.notification.backend.rabbitmq');
148
        }
149
150
        if (isset($config['backends']['doctrine']) && $config['backend'] === 'sonata.notification.backend.doctrine') {
151
            $checkLevel = array(
152
                MessageInterface::STATE_DONE => $config['backends']['doctrine']['states']['done'],
153
                MessageInterface::STATE_ERROR => $config['backends']['doctrine']['states']['error'],
154
                MessageInterface::STATE_IN_PROGRESS => $config['backends']['doctrine']['states']['in_progress'],
155
                MessageInterface::STATE_OPEN => $config['backends']['doctrine']['states']['open'],
156
            );
157
158
            $pause = $config['backends']['doctrine']['pause'];
159
            $maxAge = $config['backends']['doctrine']['max_age'];
160
            $batchSize = $config['backends']['doctrine']['batch_size'];
161
            if (!is_null($config['backends']['doctrine']['message_manager'])) {
162
                $container->setAlias('sonata.notification.manager.message', $config['backends']['doctrine']['message_manager']);
163
            }
164
165
            $this->configureDoctrineBackends($container, $config, $checkLevel, $pause, $maxAge, $batchSize);
0 ignored issues
show
Documentation introduced by
$checkLevel is of type array, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
166
        }
167
    }
168
169
    /**
170
     * @param array $config
171
     */
172
    public function registerDoctrineMapping(array $config)
173
    {
174
        $collector = DoctrineCollector::getInstance();
175
176
        $collector->addIndex($config['class']['message'], 'idx_state', array(
177
            'state',
178
        ));
179
180
        $collector->addIndex($config['class']['message'], 'idx_created_at', array(
181
            'created_at',
182
        ));
183
    }
184
185
    /**
186
     * @param array $config
187
     */
188
    protected function checkConfiguration(array $config)
189
    {
190
        if (isset($config['backends']) && count($config['backends']) > 1) {
191
            throw new \RuntimeException('more than one backend configured, you can have only one backend configuration');
192
        }
193
194
        if (!isset($config['backends']['rabbitmq']) && $config['backend'] === 'sonata.notification.backend.rabbitmq') {
195
            throw new \RuntimeException('Please configure the sonata_notification.backends.rabbitmq section');
196
        }
197
198
        if (!isset($config['backends']['doctrine']) && $config['backend'] === 'sonata.notification.backend.doctrine') {
199
            throw new \RuntimeException('Please configure the sonata_notification.backends.doctrine section');
200
        }
201
    }
202
203
    /**
204
     * @param ContainerBuilder $container
205
     * @param array            $config
206
     */
207
    protected function configureListeners(ContainerBuilder $container, array $config)
208
    {
209
        $ids = $config['iteration_listeners'];
210
211
        // this one clean the unit of work after every iteration
212
        // it must be set on any backend ...
213
        $ids[] = 'sonata.notification.event.doctrine_optimize';
214
215
        if (isset($config['backends']['doctrine']) && $config['backends']['doctrine']['batch_size'] > 1) {
216
            // if the backend is doctrine and the batch size > 1, then
217
            // the unit of work must be cleaned wisely to avoid any issue
218
            // while persisting entities
219
            $ids = array(
220
                'sonata.notification.event.doctrine_backend_optimize',
221
            );
222
        }
223
224
        $container->setParameter('sonata.notification.event.iteration_listeners', $ids);
225
    }
226
227
    /**
228
     * @param ContainerBuilder $container
229
     * @param array            $config
230
     * @param bool             $checkLevel
231
     * @param int              $pause
232
     * @param int              $maxAge
233
     * @param int              $batchSize
234
     *
235
     * @throws \RuntimeException
236
     */
237
    protected function configureDoctrineBackends(ContainerBuilder $container, array $config, $checkLevel, $pause, $maxAge, $batchSize)
238
    {
239
        $queues = $config['queues'];
240
        $qBackends = array();
241
242
        $definition = $container->getDefinition('sonata.notification.backend.doctrine');
243
244
        // no queue defined, set a default one
245
        if (count($queues) == 0) {
246
            $queues = array(array(
247
                'queue' => 'default',
248
                'default' => true,
249
                'types' => array(),
250
            ));
251
        }
252
253
        $defaultSet = false;
254
        $declaredQueues = array();
255
256
        foreach ($queues as $pos => &$queue) {
257
            if (in_array($queue['queue'], $declaredQueues)) {
258
                throw new \RuntimeException('The doctrine backend does not support 2 identicals queue name, please rename one queue');
259
            }
260
261
            $declaredQueues[] = $queue['queue'];
262
263
            // make the configuration compatible with old code and rabbitmq
264
            if (isset($queue['routing_key']) && strlen($queue['routing_key']) > 0) {
265
                $queue['types'] = array($queue['routing_key']);
266
            }
267
268
            if (empty($queue['types']) && $queue['default'] === false) {
269
                throw new \RuntimeException('You cannot declared a doctrine queue with no type defined with default = false');
270
            }
271
272
            if (!empty($queue['types']) && $queue['default'] === true) {
273
                throw new \RuntimeException('You cannot declared a doctrine queue with types defined with default = true');
274
            }
275
276
            $id = $this->createDoctrineQueueBackend($container, $definition->getArgument(0), $checkLevel, $pause, $maxAge, $batchSize, $queue['queue'], $queue['types']);
277
            $qBackends[$pos] = array(
278
                'types' => $queue['types'],
279
                'backend' => new Reference($id),
280
            );
281
282
            if ($queue['default'] === true) {
283
                if ($defaultSet === true) {
284
                    throw new \RuntimeException('You can only set one doctrine default queue in your sonata notification configuration.');
285
                }
286
287
                $defaultSet = true;
288
                $defaultQueue = $queue['queue'];
289
            }
290
        }
291
292
        if ($defaultSet === false) {
293
            throw new \RuntimeException('You need to specify a valid default queue for the doctrine backend!');
294
        }
295
296
        $definition
297
            ->replaceArgument(1, $queues)
298
            ->replaceArgument(2, $defaultQueue)
0 ignored issues
show
Bug introduced by
The variable $defaultQueue does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
299
            ->replaceArgument(3, $qBackends)
300
        ;
301
    }
302
303
    /**
304
     * @param ContainerBuilder $container
305
     * @param string           $manager
306
     * @param bool             $checkLevel
307
     * @param int              $pause
308
     * @param int              $maxAge
309
     * @param int              $batchSize
310
     * @param string           $key
311
     * @param array            $types
312
     *
313
     * @return string
314
     */
315
    protected function createDoctrineQueueBackend(ContainerBuilder $container, $manager, $checkLevel, $pause, $maxAge, $batchSize, $key, array $types = array())
316
    {
317
        if ($key == '') {
318
            $id = 'sonata.notification.backend.doctrine.default_'.$this->amqpCounter++;
319
        } else {
320
            $id = 'sonata.notification.backend.doctrine.'.$key;
321
        }
322
323
        $definition = new Definition('Sonata\NotificationBundle\Backend\MessageManagerBackend', array($manager, $checkLevel, $pause, $maxAge, $batchSize, $types));
324
        $definition->setPublic(false);
325
326
        $container->setDefinition($id, $definition);
327
328
        return $id;
329
    }
330
331
    /**
332
     * @param ContainerBuilder $container
333
     * @param array            $config
334
     */
335
    protected function configureRabbitmq(ContainerBuilder $container, array $config)
336
    {
337
        $queues = $config['queues'];
338
        $connection = $config['backends']['rabbitmq']['connection'];
339
        $baseExchange = $config['backends']['rabbitmq']['exchange'];
340
        $amqBackends = array();
341
342
        if (count($queues) == 0) {
343
            $queues = array(array(
344
                'queue' => 'default',
345
                'default' => true,
346
                'routing_key' => '',
347
                'recover' => false,
348
                'dead_letter_exchange' => null,
349
                'dead_letter_routing_key' => null,
350
                'ttl' => null,
351
            ));
352
        }
353
354
        $deadLetterRoutingKeys = $this->getQueuesParameters('dead_letter_routing_key', $queues);
355
        $routingKeys = $this->getQueuesParameters('routing_key', $queues);
356
357
        foreach ($deadLetterRoutingKeys as $key) {
358
            if (!in_array($key, $routingKeys)) {
359
                throw new \RuntimeException(sprintf(
360
                    'You must configure the queue having the routing_key "%s" same as dead_letter_routing_key', $key
361
                ));
362
            }
363
        }
364
365
        $declaredQueues = array();
366
367
        $defaultSet = false;
368
        foreach ($queues as $pos => $queue) {
369
            if (in_array($queue['queue'], $declaredQueues)) {
370
                throw new \RuntimeException('The RabbitMQ backend does not support 2 identicals queue name, please rename one queue');
371
            }
372
373
            $declaredQueues[] = $queue['queue'];
374
375
            if ($queue['dead_letter_routing_key']) {
376
                if (is_null($queue['dead_letter_exchange'])) {
377
                    throw new \RuntimeException(
378
                        'dead_letter_exchange must be configured when dead_letter_routing_key is set'
379
                    );
380
                }
381
            }
382
383
            if (in_array($queue['routing_key'], $deadLetterRoutingKeys)) {
384
                $exchange = $this->getAMQPDeadLetterExchangeByRoutingKey($queue['routing_key'], $queues);
385
            } else {
386
                $exchange = $baseExchange;
387
            }
388
389
            $id = $this->createAMQPBackend(
390
                $container,
391
                $exchange,
392
                $queue['queue'],
393
                $queue['recover'],
394
                $queue['routing_key'],
395
                $queue['dead_letter_exchange'],
396
                $queue['dead_letter_routing_key'],
397
                $queue['ttl']
398
            );
399
400
            $amqBackends[$pos] = array(
401
                'type' => $queue['routing_key'],
402
                'backend' => new Reference($id),
403
            );
404
405
            if ($queue['default'] === true) {
406
                if ($defaultSet === true) {
407
                    throw new \RuntimeException('You can only set one rabbitmq default queue in your sonata notification configuration.');
408
                }
409
                $defaultSet = true;
410
                $defaultQueue = $queue['routing_key'];
411
            }
412
        }
413
414
        if ($defaultSet === false) {
415
            throw new \RuntimeException('You need to specify a valid default queue for the rabbitmq backend!');
416
        }
417
418
        $container->getDefinition('sonata.notification.backend.rabbitmq')
419
            ->replaceArgument(0, $connection)
420
            ->replaceArgument(1, $queues)
421
            ->replaceArgument(2, $defaultQueue)
0 ignored issues
show
Bug introduced by
The variable $defaultQueue does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
422
            ->replaceArgument(3, $amqBackends)
423
        ;
424
    }
425
426
    /**
427
     * @param ContainerBuilder $container
428
     * @param string           $exchange
429
     * @param string           $name
430
     * @param string           $recover
431
     * @param string           $key
432
     * @param string           $deadLetterExchange
433
     * @param string           $deadLetterRoutingKey
434
     * @param int|null         $ttl
435
     *
436
     * @return string
437
     */
438
    protected function createAMQPBackend(ContainerBuilder $container, $exchange, $name, $recover, $key = '', $deadLetterExchange = null, $deadLetterRoutingKey = null, $ttl = null)
439
    {
440
        $id = 'sonata.notification.backend.rabbitmq.'.$this->amqpCounter++;
441
442
        $definition = new Definition(
443
            'Sonata\NotificationBundle\Backend\AMQPBackend',
444
            array(
445
                $exchange,
446
                $name,
447
                $recover,
448
                $key,
449
                $deadLetterExchange,
450
                $deadLetterRoutingKey,
451
                $ttl,
452
            )
453
        );
454
        $definition->setPublic(false);
455
        $container->setDefinition($id, $definition);
456
457
        return $id;
458
    }
459
460
    /**
461
     * @param string $name
462
     * @param array  $queues
463
     *
464
     * @return string[]
465
     */
466
    private function getQueuesParameters($name, array $queues)
467
    {
468
        $params = array_unique(array_map(function ($q) use ($name) {
469
            return $q[$name];
470
        }, $queues));
471
472
        $idx = array_search(null, $params);
473
        if ($idx !== false) {
474
            unset($params[$idx]);
475
        }
476
477
        return $params;
478
    }
479
480
    /**
481
     * @param string $key
482
     * @param array  $queues
483
     *
484
     * @return string
485
     */
486
    private function getAMQPDeadLetterExchangeByRoutingKey($key, array $queues)
487
    {
488
        foreach ($queues as $queue) {
489
            if ($queue['dead_letter_routing_key'] === $key) {
490
                return $queue['dead_letter_exchange'];
491
            }
492
        }
493
    }
494
}
495