Command   D
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 478
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 118
dl 0
loc 478
ccs 0
cts 138
cp 0
rs 4.08
c 0
b 0
f 0
wmc 59

33 Methods

Rating   Name   Duplication   Size   Complexity  
A getProcessedHelp() 0 14 1
A addUsage() 0 9 2
A getAliases() 0 3 1
A configure() 0 2 1
A initialize() 0 2 1
A isEnabled() 0 3 1
A setDefinition() 0 11 2
A setName() 0 7 1
A execute() 0 3 1
A __construct() 0 8 2
A getNativeDefinition() 0 3 1
A getName() 0 3 2
A setApp() 0 3 1
A getApp() 0 3 1
A addOption() 0 5 1
A setHelp() 0 5 1
A getHelp() 0 3 2
B mergeConsoleDefinition() 0 20 7
A table() 0 5 1
A setDescription() 0 5 1
A validateName() 0 4 2
A setConsole() 0 3 1
A getConsole() 0 3 1
A getDescription() 0 3 2
A ignoreValidationErrors() 0 3 1
A setProcessTitle() 0 5 1
A getSynopsis() 0 9 3
A getUsages() 0 3 1
A getDefinition() 0 3 1
A interact() 0 2 1
A setAliases() 0 9 2
A addArgument() 0 5 1
B run() 0 46 11

How to fix   Complexity   

Complex Class

Complex classes like Command 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.

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 Command, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: yunwuxin <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think\console;
14
15
use Exception;
16
use InvalidArgumentException;
17
use LogicException;
18
use think\App;
19
use think\Console;
20
use think\console\input\Argument;
21
use think\console\input\Definition;
22
use think\console\input\Option;
23
24
abstract class Command
25
{
26
27
    /** @var  Console */
28
    private $console;
29
    private $name;
30
    private $processTitle;
31
    private $aliases = [];
32
    private $definition;
33
    private $help;
34
    private $description;
35
    private $ignoreValidationErrors          = false;
36
    private $consoleDefinitionMerged         = false;
37
    private $consoleDefinitionMergedWithArgs = false;
38
    private $synopsis                        = [];
39
    private $usages                          = [];
40
41
    /** @var  Input */
42
    protected $input;
43
44
    /** @var  Output */
45
    protected $output;
46
47
    /** @var App */
48
    protected $app;
49
50
    /**
51
     * 构造方法
52
     * @throws LogicException
53
     * @api
54
     */
55
    public function __construct()
56
    {
57
        $this->definition = new Definition();
58
59
        $this->configure();
60
61
        if (!$this->name) {
62
            throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
63
        }
64
    }
65
66
    /**
67
     * 忽略验证错误
68
     */
69
    public function ignoreValidationErrors(): void
70
    {
71
        $this->ignoreValidationErrors = true;
72
    }
73
74
    /**
75
     * 设置控制台
76
     * @param Console $console
77
     */
78
    public function setConsole(Console $console = null): void
79
    {
80
        $this->console = $console;
81
    }
82
83
    /**
84
     * 获取控制台
85
     * @return Console
86
     * @api
87
     */
88
    public function getConsole(): Console
89
    {
90
        return $this->console;
91
    }
92
93
    /**
94
     * 设置app
95
     * @param App $app
96
     */
97
    public function setApp(App $app)
98
    {
99
        $this->app = $app;
100
    }
101
102
    /**
103
     * 获取app
104
     * @return App
105
     */
106
    public function getApp()
107
    {
108
        return $this->app;
109
    }
110
111
    /**
112
     * 是否有效
113
     * @return bool
114
     */
115
    public function isEnabled(): bool
116
    {
117
        return true;
118
    }
119
120
    /**
121
     * 配置指令
122
     */
123
    protected function configure()
124
    {
125
    }
126
127
    /**
128
     * 执行指令
129
     * @param Input  $input
130
     * @param Output $output
131
     * @return null|int
132
     * @throws LogicException
133
     * @see setCode()
134
     */
135
    protected function execute(Input $input, Output $output)
136
    {
137
        return $this->app->invoke([$this, 'handle']);
138
    }
139
140
    /**
141
     * 用户验证
142
     * @param Input  $input
143
     * @param Output $output
144
     */
145
    protected function interact(Input $input, Output $output)
0 ignored issues
show
Unused Code introduced by
The parameter $output is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

145
    protected function interact(Input $input, /** @scrutinizer ignore-unused */ Output $output)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $input is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

145
    protected function interact(/** @scrutinizer ignore-unused */ Input $input, Output $output)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
146
    {
147
    }
148
149
    /**
150
     * 初始化
151
     * @param Input  $input  An InputInterface instance
152
     * @param Output $output An OutputInterface instance
153
     */
154
    protected function initialize(Input $input, Output $output)
0 ignored issues
show
Unused Code introduced by
The parameter $output is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

154
    protected function initialize(Input $input, /** @scrutinizer ignore-unused */ Output $output)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $input is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

154
    protected function initialize(/** @scrutinizer ignore-unused */ Input $input, Output $output)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
155
    {
156
    }
157
158
    /**
159
     * 执行
160
     * @param Input  $input
161
     * @param Output $output
162
     * @return int
163
     * @throws Exception
164
     * @see setCode()
165
     * @see execute()
166
     */
167
    public function run(Input $input, Output $output): int
168
    {
169
        $this->input  = $input;
170
        $this->output = $output;
171
172
        $this->getSynopsis(true);
173
        $this->getSynopsis(false);
174
175
        $this->mergeConsoleDefinition();
176
177
        try {
178
            $input->bind($this->definition);
179
        } catch (Exception $e) {
180
            if (!$this->ignoreValidationErrors) {
181
                throw $e;
182
            }
183
        }
184
185
        $this->initialize($input, $output);
186
187
        if (null !== $this->processTitle) {
188
            if (function_exists('cli_set_process_title')) {
189
                if (false === @cli_set_process_title($this->processTitle)) {
190
                    if ('Darwin' === PHP_OS) {
191
                        $output->writeln('<comment>Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.</comment>');
192
                    } else {
193
                        $error = error_get_last();
194
                        trigger_error($error['message'], E_USER_WARNING);
195
                    }
196
                }
197
            } elseif (function_exists('setproctitle')) {
198
                setproctitle($this->processTitle);
199
            } elseif (Output::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
200
                $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
201
            }
202
        }
203
204
        if ($input->isInteractive()) {
205
            $this->interact($input, $output);
206
        }
207
208
        $input->validate();
209
210
        $statusCode = $this->execute($input, $output);
211
212
        return is_numeric($statusCode) ? (int) $statusCode : 0;
213
    }
214
215
    /**
216
     * 合并参数定义
217
     * @param bool $mergeArgs
218
     */
219
    public function mergeConsoleDefinition(bool $mergeArgs = true)
220
    {
221
        if (null === $this->console
222
            || (true === $this->consoleDefinitionMerged
223
                && ($this->consoleDefinitionMergedWithArgs || !$mergeArgs))
224
        ) {
225
            return;
226
        }
227
228
        if ($mergeArgs) {
229
            $currentArguments = $this->definition->getArguments();
230
            $this->definition->setArguments($this->console->getDefinition()->getArguments());
231
            $this->definition->addArguments($currentArguments);
232
        }
233
234
        $this->definition->addOptions($this->console->getDefinition()->getOptions());
235
236
        $this->consoleDefinitionMerged = true;
237
        if ($mergeArgs) {
238
            $this->consoleDefinitionMergedWithArgs = true;
239
        }
240
    }
241
242
    /**
243
     * 设置参数定义
244
     * @param array|Definition $definition
245
     * @return Command
246
     * @api
247
     */
248
    public function setDefinition($definition)
249
    {
250
        if ($definition instanceof Definition) {
251
            $this->definition = $definition;
252
        } else {
253
            $this->definition->setDefinition($definition);
254
        }
255
256
        $this->consoleDefinitionMerged = false;
257
258
        return $this;
259
    }
260
261
    /**
262
     * 获取参数定义
263
     * @return Definition
264
     * @api
265
     */
266
    public function getDefinition(): Definition
267
    {
268
        return $this->definition;
269
    }
270
271
    /**
272
     * 获取当前指令的参数定义
273
     * @return Definition
274
     */
275
    public function getNativeDefinition(): Definition
276
    {
277
        return $this->getDefinition();
278
    }
279
280
    /**
281
     * 添加参数
282
     * @param string $name        名称
283
     * @param int    $mode        类型
284
     * @param string $description 描述
285
     * @param mixed  $default     默认值
286
     * @return Command
287
     */
288
    public function addArgument(string $name, int $mode = null, string $description = '', $default = null)
289
    {
290
        $this->definition->addArgument(new Argument($name, $mode, $description, $default));
291
292
        return $this;
293
    }
294
295
    /**
296
     * 添加选项
297
     * @param string $name        选项名称
298
     * @param string $shortcut    别名
299
     * @param int    $mode        类型
300
     * @param string $description 描述
301
     * @param mixed  $default     默认值
302
     * @return Command
303
     */
304
    public function addOption(string $name, string $shortcut = null, int $mode = null, string $description = '', $default = null)
305
    {
306
        $this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default));
307
308
        return $this;
309
    }
310
311
    /**
312
     * 设置指令名称
313
     * @param string $name
314
     * @return Command
315
     * @throws InvalidArgumentException
316
     */
317
    public function setName(string $name)
318
    {
319
        $this->validateName($name);
320
321
        $this->name = $name;
322
323
        return $this;
324
    }
325
326
    /**
327
     * 设置进程名称
328
     *
329
     * PHP 5.5+ or the proctitle PECL library is required
330
     *
331
     * @param string $title The process title
332
     *
333
     * @return $this
334
     */
335
    public function setProcessTitle($title)
336
    {
337
        $this->processTitle = $title;
338
339
        return $this;
340
    }
341
342
    /**
343
     * 获取指令名称
344
     * @return string
345
     */
346
    public function getName(): string
347
    {
348
        return $this->name ?: '';
349
    }
350
351
    /**
352
     * 设置描述
353
     * @param string $description
354
     * @return Command
355
     */
356
    public function setDescription(string $description)
357
    {
358
        $this->description = $description;
359
360
        return $this;
361
    }
362
363
    /**
364
     *  获取描述
365
     * @return string
366
     */
367
    public function getDescription(): string
368
    {
369
        return $this->description ?: '';
370
    }
371
372
    /**
373
     * 设置帮助信息
374
     * @param string $help
375
     * @return Command
376
     */
377
    public function setHelp(string $help)
378
    {
379
        $this->help = $help;
380
381
        return $this;
382
    }
383
384
    /**
385
     * 获取帮助信息
386
     * @return string
387
     */
388
    public function getHelp(): string
389
    {
390
        return $this->help ?: '';
391
    }
392
393
    /**
394
     * 描述信息
395
     * @return string
396
     */
397
    public function getProcessedHelp(): string
398
    {
399
        $name = $this->name;
400
401
        $placeholders = [
402
            '%command.name%',
403
            '%command.full_name%',
404
        ];
405
        $replacements = [
406
            $name,
407
            $_SERVER['PHP_SELF'] . ' ' . $name,
408
        ];
409
410
        return str_replace($placeholders, $replacements, $this->getHelp());
411
    }
412
413
    /**
414
     * 设置别名
415
     * @param string[] $aliases
416
     * @return Command
417
     * @throws InvalidArgumentException
418
     */
419
    public function setAliases(iterable $aliases)
420
    {
421
        foreach ($aliases as $alias) {
422
            $this->validateName($alias);
423
        }
424
425
        $this->aliases = $aliases;
426
427
        return $this;
428
    }
429
430
    /**
431
     * 获取别名
432
     * @return array
433
     */
434
    public function getAliases(): array
435
    {
436
        return $this->aliases;
437
    }
438
439
    /**
440
     * 获取简介
441
     * @param bool $short 是否简单的
442
     * @return string
443
     */
444
    public function getSynopsis(bool $short = false): string
445
    {
446
        $key = $short ? 'short' : 'long';
447
448
        if (!isset($this->synopsis[$key])) {
449
            $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
450
        }
451
452
        return $this->synopsis[$key];
453
    }
454
455
    /**
456
     * 添加用法介绍
457
     * @param string $usage
458
     * @return $this
459
     */
460
    public function addUsage(string $usage)
461
    {
462
        if (0 !== strpos($usage, $this->name)) {
463
            $usage = sprintf('%s %s', $this->name, $usage);
464
        }
465
466
        $this->usages[] = $usage;
467
468
        return $this;
469
    }
470
471
    /**
472
     * 获取用法介绍
473
     * @return array
474
     */
475
    public function getUsages(): array
476
    {
477
        return $this->usages;
478
    }
479
480
    /**
481
     * 验证指令名称
482
     * @param string $name
483
     * @throws InvalidArgumentException
484
     */
485
    private function validateName(string $name)
486
    {
487
        if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
488
            throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
489
        }
490
    }
491
492
    /**
493
     * 输出表格
494
     * @param Table $table
495
     * @return string
496
     */
497
    protected function table(Table $table): string
498
    {
499
        $content = $table->render();
500
        $this->output->writeln($content);
501
        return $content;
502
    }
503
504
}
505