LockedCommandDecorator   B
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 381
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 97.48%

Importance

Changes 11
Bugs 3 Features 1
Metric Value
wmc 42
c 11
b 3
f 1
lcom 1
cbo 5
dl 0
loc 381
ccs 116
cts 119
cp 0.9748
rs 8.295

32 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A ignoreValidationErrors() 0 4 1
A setApplication() 0 4 1
A setHelperSet() 0 4 1
A getHelperSet() 0 4 1
A getApplication() 0 4 1
A isEnabled() 0 4 1
A setLockName() 0 4 1
A run() 0 18 2
A setCode() 0 6 1
A mergeApplicationDefinition() 0 4 1
A setDefinition() 0 6 1
A getDefinition() 0 4 1
A getNativeDefinition() 0 4 1
A addArgument() 0 6 1
A addOption() 0 6 1
A setName() 0 6 1
A setProcessTitle() 0 6 1
A getName() 0 4 1
A setDescription() 0 6 1
A getDescription() 0 4 1
A setHelp() 0 6 1
A getHelp() 0 4 1
A getProcessedHelp() 0 4 1
A setAliases() 0 6 1
A getAliases() 0 4 1
A getSynopsis() 0 4 1
A getHelper() 0 4 1
A getLockHandler() 0 11 2
A getLockName() 0 16 4
A getLockPath() 0 16 4
A writeLockedMessage() 0 20 3

How to fix   Complexity   

Complex Class

Complex classes like LockedCommandDecorator 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 LockedCommandDecorator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace FrankDeJonge\LockedConsoleCommand;
4
5
use Symfony\Component\Console\Application;
6
use Symfony\Component\Console\Command\Command;
7
use Symfony\Component\Console\Helper\HelperSet;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Output\ConsoleOutputInterface;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Filesystem\LockHandler;
12
13
class LockedCommandDecorator extends Command
14
{
15
    /**
16
     * @var Command
17
     */
18
    private $decoratedCommand;
19
20
    /**
21
     * @var string|LockHandler
22
     */
23
    private $lockName;
24
25
    /**
26
     * @var string
27
     */
28
    private $lockPath;
29
30
    /**
31
     * Constructor.
32
     *
33
     * @param Command            $command
34
     * @param string|LockHandler $lockName
35
     * @param string             $lockPath
36
     */
37 30
    public function __construct(Command $command, $lockName = null, $lockPath = null)
38
    {
39 30
        $this->decoratedCommand = $command;
40 30
        $this->setLockName($lockName);
41 30
        $this->lockPath = $lockPath;
42 30
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 3
    public function ignoreValidationErrors()
48
    {
49 3
        $this->decoratedCommand->ignoreValidationErrors();
50 3
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55 3
    public function setApplication(Application $application = null)
56
    {
57 3
        $this->decoratedCommand->setApplication($application);
58 3
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 3
    public function setHelperSet(HelperSet $helperSet)
64
    {
65 3
        $this->decoratedCommand->setHelperSet($helperSet);
66 3
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 3
    public function getHelperSet()
72
    {
73 3
        return $this->decoratedCommand->getHelperSet();
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 3
    public function getApplication()
80
    {
81 3
        return $this->decoratedCommand->getApplication();
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87 3
    public function isEnabled()
88
    {
89 3
        return $this->decoratedCommand->isEnabled();
90
    }
91
92
    /**
93
     * @param string|LockHandler|null $lockName
94
     */
95 30
    public function setLockName($lockName)
96
    {
97 30
        $this->lockName = $lockName;
98 30
    }
99
100
    /**
101
     * Runs the command.
102
     *
103
     * Before the decorated command is run, a lock is requested.
104
     * When failed to acquire the lock, the command exits.
105
     *
106
     * @param InputInterface  $input  An InputInterface instance
107
     * @param OutputInterface $output An OutputInterface instance
108
     *
109
     * @return int The command exit code
110
     */
111 12
    public function run(InputInterface $input, OutputInterface $output)
112
    {
113 12
        $this->mergeApplicationDefinition();
114 12
        $input->bind($this->getDefinition());
115 12
        $lock = $this->getLockHandler($input);
116
117 12
        if ( ! $lock->lock()) {
118 6
            $this->writeLockedMessage($input, $output);
119
120 3
            return 1;
121
        }
122
123
        try {
124 6
            return $this->decoratedCommand->run($input, $output);
125
        } finally {
126 6
            $lock->release();
127
        }
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133 3
    public function setCode(callable $code)
134
    {
135 3
        $this->decoratedCommand->setCode($code);
136
137 3
        return $this;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143 15
    public function mergeApplicationDefinition($mergeArgs = true)
144
    {
145 15
        $this->decoratedCommand->mergeApplicationDefinition($mergeArgs);
146 15
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 3
    public function setDefinition($definition)
152
    {
153 3
        $this->decoratedCommand->setDefinition($definition);
154
155 3
        return $this;
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 15
    public function getDefinition()
162
    {
163 15
        return $this->decoratedCommand->getDefinition();
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169 3
    public function getNativeDefinition()
170
    {
171 3
        return $this->decoratedCommand->getNativeDefinition();
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 3
    public function addArgument($name, $mode = null, $description = '', $default = null)
178
    {
179 3
        $this->decoratedCommand->addArgument($name, $mode, $description, $default);
180
181 3
        return $this;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 3
    public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
188
    {
189 3
        $this->decoratedCommand->addOption($name, $shortcut, $mode, $description, $default);
190
191 3
        return $this;
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197 3
    public function setName($name)
198
    {
199 3
        $this->decoratedCommand->setName($name);
200
201 3
        return $this;
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207 3
    public function setProcessTitle($title)
208
    {
209 3
        $this->decoratedCommand->setProcessTitle($title);
210
211 3
        return $this;
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217 3
    public function getName()
218
    {
219 3
        return $this->decoratedCommand->getName();
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 3
    public function setDescription($description)
226
    {
227 3
        $this->decoratedCommand->setDescription($description);
228
229 3
        return $this;
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235 3
    public function getDescription()
236
    {
237 3
        return $this->decoratedCommand->getDescription();
238
    }
239
240
    /**
241
     * {@inheritdoc}
242
     */
243 3
    public function setHelp($help)
244
    {
245 3
        $this->decoratedCommand->setHelp($help);
246
247 3
        return $this;
248
    }
249
250
    /**
251
     * {@inheritdoc}
252
     */
253 3
    public function getHelp()
254
    {
255 3
        return $this->decoratedCommand->getHelp();
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261 3
    public function getProcessedHelp()
262
    {
263 3
        return $this->decoratedCommand->getProcessedHelp();
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     */
269 3
    public function setAliases($aliases)
270
    {
271 3
        $this->decoratedCommand->setAliases($aliases);
272
273 3
        return $this;
274
    }
275
276
    /**
277
     * {@inheritdoc}
278
     */
279 3
    public function getAliases()
280
    {
281 3
        return $this->decoratedCommand->getAliases();
282
    }
283
284
    /**
285
     * {@inheritdoc}
286
     */
287 3
    public function getSynopsis($short = false)
288
    {
289 3
        return $this->decoratedCommand->getSynopsis($short);
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 3
    public function getHelper($name)
296
    {
297 3
        return $this->decoratedCommand->getHelper($name);
298
    }
299
300
    /**
301
     * Get the locking helper.
302
     *
303
     * @param  InputInterface $input
304
     *
305
     * @return LockHandler
306
     */
307 12
    private function getLockHandler(InputInterface $input)
308
    {
309 12
        if ($this->lockName instanceof LockHandler) {
310 9
            return $this->lockName;
311
        }
312
313 3
        $lockName = $this->getLockName($input);
314 3
        $lockPath = $this->getLockPath($input);
315
316 3
        return new LockHandler($lockName, $lockPath);
317
    }
318
319
    /**
320
     * Get the name for the lock.
321
     *
322
     * @param  InputInterface $input
323
     *
324
     * @return string
325
     */
326 15
    public function getLockName(InputInterface $input)
327
    {
328 15
        if (is_string($this->lockName)) {
329 6
            return $this->lockName;
330
        }
331
332 9
        if ($this->lockName instanceof LockHandler) {
333 3
            return 'UNKNOWN';
334
        }
335
336 6
        if ($this->decoratedCommand instanceof SpecifiesLockName) {
337 3
            return $this->decoratedCommand->getLockName($input);
338
        }
339
340 3
        return $this->decoratedCommand->getName();
341
    }
342
343
    /**
344
     * Get the lock path.
345
     *
346
     * @param  InputInterface $input
347
     *
348
     * @return null|string
349
     */
350 12
    public function getLockPath(InputInterface $input)
351
    {
352 12
        if ($this->lockName instanceof LockHandler) {
353 3
            return 'UNKNOWN';
354
        }
355
356 9
        if ($this->lockPath !== null) {
357 3
            return $this->lockPath;
358
        }
359
360 6
        if ($this->decoratedCommand instanceof SpecifiesLockPath) {
361 3
            return $this->decoratedCommand->getLockPath($input);
362
        }
363
364 3
        return sys_get_temp_dir();
365
    }
366
367
    /**
368
     * Write the "is locked" message.
369
     *
370
     * @param InputInterface  $input
371
     * @param OutputInterface $output
372
     */
373 6
    private function writeLockedMessage(InputInterface $input, OutputInterface $output)
374
    {
375 6
        $commandName = $this->decoratedCommand->getName();
376 6
        $lockName = $this->getLockName($input);
377 6
        $lockPath = $this->getLockPath($input);
378 6
        $message = sprintf(
379 6
            '<error>Command "%s" is already running, locked with "%s" at path "%s"</error>',
380 4
            $commandName,
381 4
            $lockName,
382
            $lockPath
383 4
        );
384
385 6
        if ($output instanceof ConsoleOutputInterface) {
386
            $output = $output->getErrorOutput();
387
        }
388
389 6
        if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
390 3
            $output->writeln($message);
391 2
        }
392 3
    }
393
}
394