Completed
Push — master ( d39289...824463 )
by Matthew
05:09
created

WorkerCompilerPass   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 327
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 54
lcom 1
cbo 4
dl 0
loc 327
ccs 0
cts 241
cp 0
rs 7.0642
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
B process() 0 36 4
A setupTaggedServices() 0 20 3
A setupBeanstalkd() 0 12 3
A setupDoctrineManagers() 0 16 3
A setupRabbitMQ() 0 22 2
B setupRabbitMQOptions() 0 20 6
B setRabbitMqOptionsPt1() 0 23 5
B setRabbitMqOptionsPt2() 0 28 6
B getJobClass() 0 26 6
B getRunArchiveClass() 0 20 5
A testRunClass() 0 11 3
A testJobClass() 0 13 4
A getJobClassArchive() 0 17 4

How to fix   Complexity   

Complex Class

Complex classes like WorkerCompilerPass often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WorkerCompilerPass, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Dtc\QueueBundle\DependencyInjection\Compiler;
4
5
use Dtc\QueueBundle\Model\Job;
6
use Dtc\QueueBundle\Model\Run;
7
use Pheanstalk\Pheanstalk;
8
use Symfony\Component\DependencyInjection\Alias;
9
use Symfony\Component\DependencyInjection\ContainerBuilder;
10
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
11
use Symfony\Component\DependencyInjection\Definition;
12
use Symfony\Component\DependencyInjection\Reference;
13
14
class WorkerCompilerPass implements CompilerPassInterface
15
{
16
    public function process(ContainerBuilder $container)
17
    {
18
        if (false === $container->hasDefinition('dtc_queue.worker_manager')) {
19
            return;
20
        }
21
22
        $defaultManagerType = $container->getParameter('dtc_queue.default_manager');
23
        if (!$container->hasDefinition('dtc_queue.job_manager.'.$defaultManagerType)) {
24
            throw new \Exception("No job manager found for dtc_queue.job_manager.$defaultManagerType");
25
        }
26
27
        $alias = new Alias('dtc_queue.job_manager.'.$defaultManagerType);
28
        $container->setAlias('dtc_queue.job_manager', $alias);
29
30
        // Setup beanstalkd if configuration is present
31
        $this->setupBeanstalkd($container);
32
        $this->setupRabbitMQ($container);
33
34
        $definition = $container->getDefinition('dtc_queue.worker_manager');
35
        $jobManagerRef = array(new Reference('dtc_queue.job_manager'));
36
37
        $jobClass = $this->getJobClass($container);
38
        $jobArchiveClass = $this->getJobClassArchive($container);
39
        $container->setParameter('dtc_queue.class_job', $jobClass);
40
        $container->setParameter('dtc_queue.class_job_archive', $jobArchiveClass);
41
        $container->setParameter('dtc_queue.class_run', $this->getRunArchiveClass($container, 'run', 'Run'));
42
        $container->setParameter('dtc_queue.class_run_archive', $this->getRunArchiveClass($container, 'run_archive', 'RunArchive'));
43
44
        $this->setupTaggedServices($container, $definition, $jobManagerRef, $jobClass);
45
        $eventDispatcher = $container->getDefinition('dtc_queue.event_dispatcher');
46
        foreach ($container->findTaggedServiceIds('dtc_queue.event_subscriber') as $id => $attributes) {
47
            $eventSubscriber = $container->getDefinition($id);
48
            $eventDispatcher->addMethodCall('addSubscriber', [$eventSubscriber]);
49
        }
50
        $this->setupDoctrineManagers($container);
51
    }
52
53
    /**
54
     * @param ContainerBuilder $container
55
     * @param array            $jobManagerRef
56
     * @param string           $jobClass
57
     */
58
    protected function setupTaggedServices(ContainerBuilder $container, Definition $definition, array $jobManagerRef, $jobClass)
59
    {
60
        // Add each worker to workerManager, make sure each worker has instance to work
61
        foreach ($container->findTaggedServiceIds('dtc_queue.worker') as $id => $attributes) {
62
            $worker = $container->getDefinition($id);
63
            $class = $container->getDefinition($id)->getClass();
64
65
            $refClass = new \ReflectionClass($class);
66
            $workerClass = 'Dtc\QueueBundle\Model\Worker';
67
            if (!$refClass->isSubclassOf($workerClass)) {
68
                throw new \InvalidArgumentException(sprintf('Service "%s" must extend class "%s".', $id, $workerClass));
69
            }
70
71
            // Give each worker access to job manager
72
            $worker->addMethodCall('setJobManager', $jobManagerRef);
73
            $worker->addMethodCall('setJobClass', array($jobClass));
74
75
            $definition->addMethodCall('addWorker', array(new Reference($id)));
76
        }
77
    }
78
79
    /**
80
     * Sets up beanstalkd instance if appropriate.
81
     *
82
     * @param ContainerBuilder $container
83
     */
84
    protected function setupBeanstalkd(ContainerBuilder $container)
85
    {
86
        if ($container->hasParameter('dtc_queue.beanstalkd.host')) {
87
            $definition = new Definition('Pheanstalk\\Pheanstalk', [$container->getParameter('dtc_queue.beanstalkd.host')]);
88
            $container->setDefinition('dtc_queue.beanstalkd', $definition);
89
            $definition = $container->getDefinition('dtc_queue.job_manager.beanstalkd');
90
            $definition->addMethodCall('setBeanstalkd', [new Reference('dtc_queue.beanstalkd')]);
91
            if ($container->hasParameter('dtc_queue.beanstalkd.tube')) {
92
                $definition->addMethodCall('setTube', [$container->getParameter('dtc_queue.beanstalkd.tube')]);
93
            }
94
        }
95
    }
96
97
    protected function setupDoctrineManagers(ContainerBuilder $container)
98
    {
99
        $documentManager = $container->getParameter('dtc_queue.document_manager');
100
101
        $odmManager = "doctrine_mongodb.odm.{$documentManager}_document_manager";
102
        if ($container->has($odmManager)) {
103
            $container->setAlias('dtc_queue.document_manager', $odmManager);
104
        }
105
106
        $entityManager = $container->getParameter('dtc_queue.entity_manager');
107
108
        $ormManager = "doctrine.orm.{$entityManager}_entity_manager";
109
        if ($container->has($ormManager)) {
110
            $container->setAlias('dtc_queue.entity_manager', $ormManager);
111
        }
112
    }
113
114
    /**
115
     * Sets up RabbitMQ instance if appropriate.
116
     *
117
     * @param ContainerBuilder $container
118
     */
119
    protected function setupRabbitMQ(ContainerBuilder $container)
120
    {
121
        if ($container->hasParameter('dtc_queue.rabbit_mq')) {
122
            $class = 'PhpAmqpLib\\Connection\\AMQPStreamConnection';
123
            $rabbitMqConfig = $container->getParameter('dtc_queue.rabbit_mq');
124
            $arguments = [
125
                $rabbitMqConfig['host'],
126
                $rabbitMqConfig['port'],
127
                $rabbitMqConfig['user'],
128
                $rabbitMqConfig['password'],
129
                $rabbitMqConfig['vhost'],
130
            ];
131
132
            $this->setupRabbitMQOptions($container, $arguments, $class);
133
            $definition = new Definition($class, $arguments);
134
            $container->setDefinition('dtc_queue.rabbit_mq', $definition);
135
            $definition = $container->getDefinition('dtc_queue.job_manager.rabbit_mq');
136
            $definition->addMethodCall('setAMQPConnection', [new Reference('dtc_queue.rabbit_mq')]);
137
            $definition->addMethodCall('setQueueArgs', array_values($rabbitMqConfig['queue_args']));
138
            $definition->addMethodCall('setExchangeArgs', array_values($rabbitMqConfig['exchange_args']));
139
        }
140
    }
141
142
    /**
143
     * @param ContainerBuilder $container
144
     * @param array            $arguments
145
     * @param $class
146
     */
147
    protected function setupRabbitMQOptions(ContainerBuilder $container, array &$arguments, &$class)
148
    {
149
        if ($container->hasParameter('dtc_queue.rabbit_mq.ssl') && $container->getParameter('dtc_queue.rabbit_mq.ssl')) {
150
            $class = 'PhpAmqpLib\\Connection\\AMQPSSLConnection';
151
            if ($container->hasParameter('dtc_queue.rabbit_mq.ssl_options')) {
152
                $arguments[] = $container->getParameter('dtc_queue.rabbit_mq.ssl_options');
153
            } else {
154
                $arguments[] = [];
155
            }
156
            if ($container->hasParameter('dtc_queue.rabbit_mq.options')) {
157
                $arguments[] = $container->getParameter('dtc_queue.rabbit_mq.options');
158
            }
159
        } else {
160
            if ($container->hasParameter('dtc_queue.rabbit_mq.options')) {
161
                $options = $container->getParameter('dtc_queue.rabbit_mq.options');
162
                $this->setRabbitMqOptionsPt1($arguments, $options);
163
                $this->setRabbitMqOptionsPt2($arguments, $options);
164
            }
165
        }
166
    }
167
168
    protected function setRabbitMqOptionsPt1(array &$arguments, array $options)
169
    {
170
        if (isset($options['insist'])) {
171
            $arguments[] = $options['insist'];
172
        } else {
173
            $arguments[] = false;
174
        }
175
        if (isset($options['login_method'])) {
176
            $arguments[] = $options['login_method'];
177
        } else {
178
            $arguments[] = 'AMQPLAIN';
179
        }
180
        if (isset($options['login_response'])) {
181
            $arguments[] = $options['login_response'];
182
        } else {
183
            $arguments[] = null;
184
        }
185
        if (isset($options['locale'])) {
186
            $arguments[] = $options['locale'];
187
        } else {
188
            $arguments[] = 'en_US';
189
        }
190
    }
191
192
    protected function setRabbitMqOptionsPt2(array &$arguments, array $options)
193
    {
194
        if (isset($options['connection_timeout'])) {
195
            $arguments[] = $options['connection_timeout'];
196
        } else {
197
            $arguments[] = 3.0;
198
        }
199
        if (isset($options['read_write_timeout'])) {
200
            $arguments[] = $options['read_write_timeout'];
201
        } else {
202
            $arguments[] = 3.0;
203
        }
204
        if (isset($options['context'])) {
205
            $arguments[] = $options['context'];
206
        } else {
207
            $arguments[] = null;
208
        }
209
        if (isset($options['keepalive'])) {
210
            $arguments[] = $options['keepalive'];
211
        } else {
212
            $arguments[] = false;
213
        }
214
        if (isset($options['heartbeat'])) {
215
            $arguments[] = $options['heartbeat'];
216
        } else {
217
            $arguments[] = 0;
218
        }
219
    }
220
221
    /**
222
     * Determines the job class based on the queue manager type.
223
     *
224
     * @param ContainerBuilder $container
225
     *
226
     * @return mixed|string
227
     *
228
     * @throws \Exception
229
     */
230
    protected function getJobClass(ContainerBuilder $container)
231
    {
232
        $jobClass = $container->getParameter('dtc_queue.class_job');
233
        if (!$jobClass) {
234
            switch ($defaultType = $container->getParameter('dtc_queue.default_manager')) {
235
                case 'mongodb':
236
                    $jobClass = 'Dtc\\QueueBundle\\Document\\Job';
237
                    break;
238
                case 'beanstalkd':
239
                    $jobClass = 'Dtc\\QueueBundle\\Beanstalkd\\Job';
240
                    break;
241
                case 'rabbit_mq':
242
                    $jobClass = 'Dtc\\QueueBundle\\RabbitMQ\\Job';
243
                    break;
244
                case 'orm':
245
                    $jobClass = 'Dtc\\QueueBundle\\Entity\\Job';
246
                    break;
247
                default:
248
                    throw new \Exception('Unknown default_manager type '.$defaultType.' - please specify a Job class in the \'class\' configuration parameter');
249
            }
250
        }
251
252
        $this->testJobClass($jobClass);
253
254
        return $jobClass;
255
    }
256
257
    protected function getRunArchiveClass(ContainerBuilder $container, $type, $className)
258
    {
259
        $runArchiveClass = $container->hasParameter('dtc_queue.class_'.$type) ? $container->getParameter('dtc_queue.class_'.$type) : null;
260
        if (!$runArchiveClass) {
261
            switch ($container->getParameter('dtc_queue.default_manager')) {
262
                case 'mongodb':
263
                    $runArchiveClass = 'Dtc\\QueueBundle\\Document\\'.$className;
264
                    break;
265
                case 'orm':
266
                    $runArchiveClass = 'Dtc\\QueueBundle\\Entity\\'.$className;
267
                    break;
268
                default:
269
                    $runArchiveClass = 'Dtc\\QueueBundle\\Model\\Run';
270
            }
271
        }
272
273
        $this->testRunClass($runArchiveClass);
274
275
        return $runArchiveClass;
276
    }
277
278
    /**
279
     * @param string $runClass
280
     *
281
     * @throws \Exception
282
     */
283
    protected function testRunClass($runClass)
284
    {
285
        if (!class_exists($runClass)) {
286
            throw new \Exception("Can't find class $runClass");
287
        }
288
289
        $test = new $runClass();
290
        if (!$test instanceof Run) {
291
            throw new \Exception("$runClass must be instance of (or derived from) Dtc\\QueueBundle\\Model\\Run");
292
        }
293
    }
294
295
    /**
296
     * @param string|null $jobArchiveClass
0 ignored issues
show
Bug introduced by
There is no parameter named $jobArchiveClass. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
297
     *
298
     * @throws \Exception
299
     */
300
    protected function testJobClass($jobClass)
301
    {
302
        if ($jobClass) {
303
            if (!class_exists($jobClass)) {
304
                throw new \Exception("Can't find class $jobClass");
305
            }
306
307
            $test = new $jobClass();
308
            if (!$test instanceof Job) {
309
                throw new \Exception("$jobClass must be instance of (or derived from) Dtc\\QueueBundle\\Model\\Job");
310
            }
311
        }
312
    }
313
314
    /**
315
     * Determines the job class based on the queue manager type.
316
     *
317
     * @param ContainerBuilder $container
318
     *
319
     * @return mixed|string
320
     *
321
     * @throws \Exception
322
     */
323
    protected function getJobClassArchive(ContainerBuilder $container)
324
    {
325
        $jobArchiveClass = $container->getParameter('dtc_queue.class_job_archive');
326
        if (!$jobArchiveClass) {
327
            switch ($container->getParameter('dtc_queue.default_manager')) {
328
                case 'mongodb':
329
                    $jobArchiveClass = 'Dtc\\QueueBundle\\Document\\JobArchive';
330
                    break;
331
                case 'orm':
332
                    $jobArchiveClass = 'Dtc\\QueueBundle\\Entity\\JobArchive';
333
                    break;
334
            }
335
        }
336
        $this->testJobClass($jobArchiveClass);
337
338
        return $jobArchiveClass;
339
    }
340
}
341