Completed
Pull Request — 3.x (#222)
by Norio
10:04
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
/**
24
 * This is the class that loads and manages your bundle configuration.
25
 *
26
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
27
 */
28
class SonataNotificationExtension extends Extension
29
{
30
    /**
31
     * @var int
32
     */
33
    protected $amqpCounter = 0;
34
35
    /**
36
     * {@inheritdoc}
37
     */
38
    public function load(array $configs, ContainerBuilder $container)
39
    {
40
        $configuration = new Configuration();
41
        $config = $this->processConfiguration($configuration, $configs);
42
43
        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
44
45
        $loader->load('core.xml');
46
        $loader->load('doctrine_orm.xml');
47
        $loader->load('backend.xml');
48
        $loader->load('consumer.xml');
49
        $loader->load('selector.xml');
50
        $loader->load('event.xml');
51
52
        if ($config['consumers']['register_default']) {
53
            $loader->load('default_consumers.xml');
54
        }
55
56
        $bundles = $container->getParameter('kernel.bundles');
57
58
        if (isset($bundles['FOSRestBundle']) && isset($bundles['NelmioApiDocBundle'])) {
59
            $loader->load('api_controllers.xml');
60
            $loader->load('api_form.xml');
61
        }
62
63
        if ($config['admin']['enabled'] && isset($bundles['SonataDoctrineORMAdminBundle'])) { // for now, only support for ORM
64
            $loader->load('admin.xml');
65
        }
66
67
        if (isset($bundles['LiipMonitorBundle'])) {
68
            $loader->load('checkmonitor.xml');
69
        }
70
71
        $this->checkConfiguration($config);
72
73
        $container->setAlias('sonata.notification.backend', $config['backend']);
74
        $container->setParameter('sonata.notification.backend', $config['backend']);
75
76
        $this->registerDoctrineMapping($config);
77
        $this->registerParameters($container, $config);
78
        $this->configureBackends($container, $config);
79
        $this->configureClass($container, $config);
80
        $this->configureListeners($container, $config);
81
        $this->configureAdmin($container, $config);
82
    }
83
84
    /**
85
     * @param ContainerBuilder $container
86
     * @param array            $config
87
     */
88
    public function configureClass(ContainerBuilder $container, $config)
89
    {
90
        // admin configuration
91
        $container->setParameter('sonata.notification.admin.message.entity', $config['class']['message']);
92
93
        // manager configuration
94
        $container->setParameter('sonata.notification.manager.message.entity', $config['class']['message']);
95
    }
96
97
    /**
98
     * @param ContainerBuilder $container
99
     * @param array            $config
100
     */
101
    public function configureAdmin(ContainerBuilder $container, $config)
102
    {
103
        $container->setParameter('sonata.notification.admin.message.class', $config['admin']['message']['class']);
104
        $container->setParameter('sonata.notification.admin.message.controller', $config['admin']['message']['controller']);
105
        $container->setParameter('sonata.notification.admin.message.translation_domain', $config['admin']['message']['translation']);
106
    }
107
108
    /**
109
     * @param ContainerBuilder $container
110
     * @param array            $config
111
     */
112
    public function registerParameters(ContainerBuilder $container, $config)
113
    {
114
        $container->setParameter('sonata.notification.message.class', $config['class']['message']);
115
        $container->setParameter('sonata.notification.admin.message.entity', $config['class']['message']);
116
    }
117
118
    /**
119
     * @param ContainerBuilder $container
120
     * @param array            $config
121
     */
122
    public function configureBackends(ContainerBuilder $container, $config)
123
    {
124
        // set the default value, will be erase if required
125
        $container->setAlias('sonata.notification.manager.message', 'sonata.notification.manager.message.default');
126
127
        if (isset($config['backends']['rabbitmq']) && $config['backend'] === 'sonata.notification.backend.rabbitmq') {
128
            $this->configureRabbitmq($container, $config);
129
130
            $container->removeDefinition('sonata.notification.backend.doctrine');
131
        } else {
132
            $container->removeDefinition('sonata.notification.backend.rabbitmq');
133
        }
134
135
        if (isset($config['backends']['doctrine']) && $config['backend'] === 'sonata.notification.backend.doctrine') {
136
            $checkLevel = array(
137
                MessageInterface::STATE_DONE => $config['backends']['doctrine']['states']['done'],
138
                MessageInterface::STATE_ERROR => $config['backends']['doctrine']['states']['error'],
139
                MessageInterface::STATE_IN_PROGRESS => $config['backends']['doctrine']['states']['in_progress'],
140
                MessageInterface::STATE_OPEN => $config['backends']['doctrine']['states']['open'],
141
            );
142
143
            $pause = $config['backends']['doctrine']['pause'];
144
            $maxAge = $config['backends']['doctrine']['max_age'];
145
            $batchSize = $config['backends']['doctrine']['batch_size'];
146
            $container->setAlias('sonata.notification.manager.message', $config['backends']['doctrine']['message_manager']);
147
148
            $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...
149
        }
150
    }
151
152
    /**
153
     * @param array $config
154
     */
155
    public function registerDoctrineMapping(array $config)
156
    {
157
        $collector = DoctrineCollector::getInstance();
158
159
        $collector->addIndex($config['class']['message'], 'idx_state', array(
160
            'state',
161
        ));
162
163
        $collector->addIndex($config['class']['message'], 'idx_created_at', array(
164
            'created_at',
165
        ));
166
    }
167
168
    /**
169
     * @param array $config
170
     */
171
    protected function checkConfiguration(array $config)
172
    {
173
        if (isset($config['backends']) && count($config['backends']) > 1) {
174
            throw new \RuntimeException('more than one backend configured, you can have only one backend configuration');
175
        }
176
177
        if (!isset($config['backends']['rabbitmq']) && $config['backend'] === 'sonata.notification.backend.rabbitmq') {
178
            throw new \RuntimeException('Please configure the sonata_notification.backends.rabbitmq section');
179
        }
180
181
        if (!isset($config['backends']['doctrine']) && $config['backend'] === 'sonata.notification.backend.doctrine') {
182
            throw new \RuntimeException('Please configure the sonata_notification.backends.doctrine section');
183
        }
184
    }
185
186
    /**
187
     * @param ContainerBuilder $container
188
     * @param array            $config
189
     */
190
    protected function configureListeners(ContainerBuilder $container, array $config)
191
    {
192
        $ids = $config['iteration_listeners'];
193
194
        // this one clean the unit of work after every iteration
195
        // it must be set on any backend ...
196
        $ids[] = 'sonata.notification.event.doctrine_optimize';
197
198
        if (isset($config['backends']['doctrine']) && $config['backends']['doctrine']['batch_size'] > 1) {
199
            // if the backend is doctrine and the batch size > 1, then
200
            // the unit of work must be cleaned wisely to avoid any issue
201
            // while persisting entities
202
            $ids = array(
203
                'sonata.notification.event.doctrine_backend_optimize',
204
            );
205
        }
206
207
        $container->setParameter('sonata.notification.event.iteration_listeners', $ids);
208
    }
209
210
    /**
211
     * @param ContainerBuilder $container
212
     * @param array            $config
213
     * @param bool             $checkLevel
214
     * @param int              $pause
215
     * @param int              $maxAge
216
     * @param int              $batchSize
217
     *
218
     * @throws \RuntimeException
219
     */
220
    protected function configureDoctrineBackends(ContainerBuilder $container, array $config, $checkLevel, $pause, $maxAge, $batchSize)
221
    {
222
        $queues = $config['queues'];
223
        $qBackends = array();
224
225
        $definition = $container->getDefinition('sonata.notification.backend.doctrine');
226
227
        // no queue defined, set a default one
228
        if (count($queues) == 0) {
229
            $queues = array(array(
230
                'queue' => 'default',
231
                'default' => true,
232
                'types' => array(),
233
            ));
234
        }
235
236
        $defaultSet = false;
237
        $declaredQueues = array();
238
239
        foreach ($queues as $pos => &$queue) {
240
            if (in_array($queue['queue'], $declaredQueues)) {
241
                throw new \RuntimeException('The doctrine backend does not support 2 identicals queue name, please rename one queue');
242
            }
243
244
            $declaredQueues[] = $queue['queue'];
245
246
            // make the configuration compatible with old code and rabbitmq
247
            if (isset($queue['routing_key']) && strlen($queue['routing_key']) > 0) {
248
                $queue['types'] = array($queue['routing_key']);
249
            }
250
251
            if (empty($queue['types']) && $queue['default'] === false) {
252
                throw new \RuntimeException('You cannot declared a doctrine queue with no type defined with default = false');
253
            }
254
255
            if (!empty($queue['types']) && $queue['default'] === true) {
256
                throw new \RuntimeException('You cannot declared a doctrine queue with types defined with default = true');
257
            }
258
259
            $id = $this->createDoctrineQueueBackend($container, $definition->getArgument(0), $checkLevel, $pause, $maxAge, $batchSize, $queue['queue'], $queue['types']);
260
            $qBackends[$pos] = array(
261
                'types' => $queue['types'],
262
                'backend' => new Reference($id),
263
            );
264
265
            if ($queue['default'] === true) {
266
                if ($defaultSet === true) {
267
                    throw new \RuntimeException('You can only set one doctrine default queue in your sonata notification configuration.');
268
                }
269
270
                $defaultSet = true;
271
                $defaultQueue = $queue['queue'];
272
            }
273
        }
274
275
        if ($defaultSet === false) {
276
            throw new \RuntimeException('You need to specify a valid default queue for the doctrine backend!');
277
        }
278
279
        $definition
280
            ->replaceArgument(1, $queues)
281
            ->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...
282
            ->replaceArgument(3, $qBackends)
283
        ;
284
    }
285
286
    /**
287
     * @param ContainerBuilder $container
288
     * @param string           $manager
289
     * @param bool             $checkLevel
290
     * @param int              $pause
291
     * @param int              $maxAge
292
     * @param int              $batchSize
293
     * @param string           $key
294
     * @param array            $types
295
     *
296
     * @return string
297
     */
298
    protected function createDoctrineQueueBackend(ContainerBuilder $container, $manager, $checkLevel, $pause, $maxAge, $batchSize, $key, array $types = array())
299
    {
300
        if ($key == '') {
301
            $id = 'sonata.notification.backend.doctrine.default_'.$this->amqpCounter++;
302
        } else {
303
            $id = 'sonata.notification.backend.doctrine.'.$key;
304
        }
305
306
        $definition = new Definition('Sonata\NotificationBundle\Backend\MessageManagerBackend', array($manager, $checkLevel, $pause, $maxAge, $batchSize, $types));
307
        $definition->setPublic(false);
308
309
        $container->setDefinition($id, $definition);
310
311
        return $id;
312
    }
313
314
    /**
315
     * @param ContainerBuilder $container
316
     * @param array            $config
317
     */
318
    protected function configureRabbitmq(ContainerBuilder $container, array $config)
319
    {
320
        $queues = $config['queues'];
321
        $connection = $config['backends']['rabbitmq']['connection'];
322
        $baseExchange = $config['backends']['rabbitmq']['exchange'];
323
        $amqBackends = array();
324
325
        if (count($queues) == 0) {
326
            $queues = array(array(
327
                'queue' => 'default',
328
                'default' => true,
329
                'routing_key' => '',
330
                'recover' => false,
331
                'dead_letter_exchange' => null,
332
                'dead_letter_routing_key' => null,
333
            ));
334
        }
335
336
        $deadLetterRoutingKeys = $this->getQueuesParameters('dead_letter_routing_key', $queues);
337
        $routingKeys = $this->getQueuesParameters('routing_key', $queues);
338
        foreach ($deadLetterRoutingKeys as $key) {
339
            if (!in_array($key, $routingKeys)) {
340
                throw new \RuntimeException(sprintf('You must configure the queue having the routing_key "%s" same as dead_letter_routing_key', $key));
341
            }
342
        }
343
344
        $declaredQueues = array();
345
346
        $defaultSet = false;
347
        foreach ($queues as $pos => $queue) {
348
            if (in_array($queue['queue'], $declaredQueues)) {
349
                throw new \RuntimeException('The RabbitMQ backend does not support 2 identicals queue name, please rename one queue');
350
            }
351
352
            $declaredQueues[] = $queue['queue'];
353
354
            if ($queue['dead_letter_routing_key']) {
355
                if (is_null($queue['dead_letter_exchange'])) {
356
                    throw new \RuntimeException('dead_letter_exchange must be configured when dead_letter_routing_key is set');
357
                }
358
            }
359
360
            if (in_array($queue['routing_key'], $deadLetterRoutingKeys)) {
361
                $exchange = $this->getAMQPDeadLetterExchangeByRoutingKey($queue['routing_key'], $queues);
362
            } else {
363
                $exchange = $baseExchange;
364
            }
365
366
            $id = $this->createAMQPBackend($container, $exchange, $queue['queue'], $queue['recover'], $queue['routing_key'], $queue['dead_letter_exchange'], $queue['dead_letter_routing_key']);
367
368
            $amqBackends[$pos] = array(
369
                'type' => $queue['routing_key'],
370
                'backend' => new Reference($id),
371
            );
372
373
            if ($queue['default'] === true) {
374
                if ($defaultSet === true) {
375
                    throw new \RuntimeException('You can only set one rabbitmq default queue in your sonata notification configuration.');
376
                }
377
                $defaultSet = true;
378
                $defaultQueue = $queue['routing_key'];
379
            }
380
        }
381
382
        if ($defaultSet === false) {
383
            throw new \RuntimeException('You need to specify a valid default queue for the rabbitmq backend!');
384
        }
385
386
        $container->getDefinition('sonata.notification.backend.rabbitmq')
387
            ->replaceArgument(0, $connection)
388
            ->replaceArgument(1, $queues)
389
            ->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...
390
            ->replaceArgument(3, $amqBackends)
391
        ;
392
    }
393
394
    /**
395
     * @param ContainerBuilder $container
396
     * @param string           $exchange
397
     * @param string           $name
398
     * @param string           $recover
399
     * @param string           $key
400
     * @param string           $deadLetterExchange
401
     * @param string           $deadLetterRoutingKey
402
     *
403
     * @return string
404
     */
405
    protected function createAMQPBackend(ContainerBuilder $container, $exchange, $name, $recover, $key = '', $deadLetterExchange = null, $deadLetterRoutingKey = null)
406
    {
407
        $id = 'sonata.notification.backend.rabbitmq.'.$this->amqpCounter++;
408
409
        $definition = new Definition('Sonata\NotificationBundle\Backend\AMQPBackend', array($exchange, $name, $recover, $key, $deadLetterExchange, $deadLetterRoutingKey));
410
        $definition->setPublic(false);
411
        $container->setDefinition($id, $definition);
412
413
        return $id;
414
    }
415
416
    /**
417
     * @param string $name
418
     * @param array  $queues
419
     *
420
     * @return string[]
421
     */
422
    private function getQueuesParameters($name, array $queues)
423
    {
424
        $params = array_unique(array_map(function ($q) use ($name) {
425
            return $q[$name];
426
        }, $queues));
427
428
        $idx = array_search(null, $params);
429
        if ($idx !== false) {
430
            unset($params[$idx]);
431
        }
432
433
        return $params;
434
    }
435
436
    /**
437
     * @param string $key
438
     * @param array  $queues
439
     *
440
     * @return string
441
     */
442
    private function getAMQPDeadLetterExchangeByRoutingKey($key, array $queues)
443
    {
444
        foreach ($queues as $queue) {
445
            if ($queue['dead_letter_routing_key'] === $key) {
446
                return $queue['dead_letter_exchange'];
447
            }
448
        }
449
    }
450
}
451