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