Completed
Push — 2.x-dev-kit ( 8d77e1 )
by
unknown
28:22 queued 25:50
created

ConsumerHandlerCommand::execute()   D

Complexity

Conditions 14
Paths 163

Size

Total Lines 89
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 1
Metric Value
c 4
b 1
f 1
dl 0
loc 89
rs 4.6283
cc 14
eloc 56
nc 163
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Sonata 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\Command;
13
14
use Sonata\NotificationBundle\Backend\BackendInterface;
15
use Sonata\NotificationBundle\Backend\QueueDispatcherInterface;
16
use Sonata\NotificationBundle\Consumer\ConsumerInterface;
17
use Sonata\NotificationBundle\Event\IterateEvent;
18
use Sonata\NotificationBundle\Exception\HandlingException;
19
use Sonata\NotificationBundle\Model\MessageInterface;
20
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
25
class ConsumerHandlerCommand extends ContainerAwareCommand
26
{
27
    /**
28
     * {@inheritdoc}
29
     */
30
    public function configure()
31
    {
32
        $this->setName('sonata:notification:start');
33
        $this->setDescription('Listen for incoming messages');
34
        $this->addOption('iteration', 'i', InputOption::VALUE_OPTIONAL, 'Only run n iterations before exiting', false);
35
        $this->addOption('type', null, InputOption::VALUE_OPTIONAL, 'Use a specific backed based on a message type, "all" with doctrine backend will handle all notifications no matter their type', null);
36
        $this->addOption('show-details', 'd', InputOption::VALUE_OPTIONAL, 'Show consumers return details', true);
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function execute(InputInterface $input, OutputInterface $output)
43
    {
44
        $startDate = new \DateTime();
45
46
        $output->writeln(sprintf('[%s] <info>Checking listeners</info>', $startDate->format('r')));
47
        foreach ($this->getNotificationDispatcher()->getListeners() as $type => $listeners) {
48
            $output->writeln(sprintf(' - %s', $type));
49
            foreach ($listeners as $listener) {
50
                if (!$listener[0] instanceof ConsumerInterface) {
51
                    throw new \RuntimeException(sprintf('The registered service does not implement the ConsumerInterface (class=%s', get_class($listener[0])));
52
                }
53
54
                $output->writeln(sprintf('   > %s', get_class($listener[0])));
55
            }
56
        }
57
58
        $type        = $input->getOption('type');
59
        $showDetails = $input->getOption('show-details');
60
61
        $output->write(sprintf('[%s] <info>Retrieving backend</info> ...', $startDate->format('r')));
62
        $backend     = $this->getBackend($type);
63
64
        $output->writeln('');
65
        $output->write(sprintf('[%s] <info>Initialize backend</info> ...', $startDate->format('r')));
66
67
        // initialize the backend
68
        $backend->initialize();
69
70
        $output->writeln(' done!');
71
72
        if ($type === null) {
73
            $output->writeln(sprintf('[%s] <info>Starting the backend handler</info> - %s', $startDate->format('r'), get_class($backend)));
74
        } else {
75
            $output->writeln(sprintf('[%s] <info>Starting the backend handler</info> - %s (type: %s)', $startDate->format('r'), get_class($backend), $type));
76
        }
77
78
        $startMemoryUsage = memory_get_usage(true);
79
        $i = 0;
80
        $iterator = $backend->getIterator();
81
        foreach ($iterator as $message) {
82
            ++$i;
83
84
            if (!$message instanceof MessageInterface) {
85
                throw new \RuntimeException('The iterator must return a MessageInterface instance');
86
            }
87
88
            if (!$message->getType()) {
89
                $output->write('<error>Skipping : no type defined </error>');
90
                continue;
91
            }
92
93
            $date = new \DateTime();
94
            $output->write(sprintf('[%s] <info>%s</info> #%s: ', $date->format('r'), $message->getType(), $i));
95
            $memoryUsage = memory_get_usage(true);
96
97
            try {
98
                $start = microtime(true);
99
                $returnInfos = $backend->handle($message, $this->getNotificationDispatcher());
100
101
                $currentMemory = memory_get_usage(true);
102
103
                $output->writeln(sprintf('<comment>OK! </comment> - %0.04fs, %ss, %s, %s - %s = %s, %0.02f%%',
104
                    microtime(true) - $start,
105
                    $date->format('U') - $message->getCreatedAt()->format('U'),
106
                    $this->formatMemory($currentMemory - $memoryUsage),
107
                    $this->formatMemory($currentMemory),
108
                    $this->formatMemory($startMemoryUsage),
109
                    $this->formatMemory($currentMemory - $startMemoryUsage),
110
                    ($currentMemory - $startMemoryUsage) / $startMemoryUsage * 100
111
                ));
112
113
                if ($showDetails && null !== $returnInfos) {
114
                    $output->writeln($returnInfos->getReturnMessage());
115
                }
116
            } catch (HandlingException $e) {
117
                $output->writeln(sprintf('<error>KO! - %s</error>', $e->getPrevious()->getMessage()));
118
            } catch (\Exception $e) {
119
                $output->writeln(sprintf('<error>KO! - %s</error>', $e->getMessage()));
120
            }
121
122
            $this->getEventDispatcher()->dispatch(IterateEvent::EVENT_NAME, new IterateEvent($iterator, $backend, $message));
123
124
            if ($input->getOption('iteration') && $i >= (int) $input->getOption('iteration')) {
125
                $output->writeln('End of iteration cycle');
126
127
                return;
128
            }
129
        }
130
    }
131
132
    /**
133
     * @param $memory
134
     *
135
     * @return string
136
     */
137
    private function formatMemory($memory)
138
    {
139
        if ($memory < 1024) {
140
            return $memory.'b';
141
        } elseif ($memory < 1048576) {
142
            return round($memory / 1024, 2).'Kb';
143
        }
144
145
        return round($memory / 1048576, 2).'Mb';
146
    }
147
148
    /**
149
     * @param string $type
150
     *
151
     * @return BackendInterface
152
     */
153
    private function getBackend($type = null)
154
    {
155
        $backend = $this->getContainer()->get('sonata.notification.backend');
156
157
        if ($type && !array_key_exists($type, $this->getNotificationDispatcher()->getListeners())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
158
            throw new \RuntimeException(sprintf('The type `%s` does not exist, available types: %s', $type, implode(', ', array_keys($this->getNotificationDispatcher()->getListeners()))));
159
        }
160
161
        if ($type !== null && !$backend instanceof QueueDispatcherInterface) {
162
            throw new \RuntimeException(sprintf('Unable to use the provided type %s with a non QueueDispatcherInterface backend', $type));
163
        }
164
165
        if ($backend instanceof QueueDispatcherInterface) {
166
            return $backend->getBackend($type);
167
        }
168
169
        return $backend;
170
    }
171
172
    /**
173
     * @param string $type
174
     * @param string $backend
175
     *
176
     * @throws \RuntimeException
177
     */
178
    protected function throwTypeNotFoundException($type, $backend)
179
    {
180
        throw new \RuntimeException("The requested backend for the type '".$type." 'does not exist. \nMake sure the backend '".
181
                get_class($backend)."' \nsupports multiple queues and the routing_key is defined. (Currently rabbitmq only)");
182
    }
183
184
    /**
185
     * @return EventDispatcherInterface
186
     */
187
    private function getNotificationDispatcher()
188
    {
189
        return $this->getContainer()->get('sonata.notification.dispatcher');
190
    }
191
192
    /**
193
     * @return EventDispatcherInterface
194
     */
195
    private function getEventDispatcher()
196
    {
197
        return $this->getContainer()->get('event_dispatcher');
198
    }
199
}
200