Completed
Push — 6.0 ( a1c09e...949fb1 )
by liu
08:06
created

Command::mergeConsoleDefinition()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 12
nc 5
nop 1
dl 0
loc 20
ccs 0
cts 13
cp 0
crap 56
rs 8.8333
c 0
b 0
f 0
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 */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
28
    private $console;
0 ignored issues
show
Coding Style introduced by
Private member variable "console" must be prefixed with an underscore
Loading history...
29
    private $name;
0 ignored issues
show
Coding Style introduced by
Private member variable "name" must be prefixed with an underscore
Loading history...
30
    private $processTitle;
0 ignored issues
show
Coding Style introduced by
Private member variable "processTitle" must be prefixed with an underscore
Loading history...
31
    private $aliases = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "aliases" must be prefixed with an underscore
Loading history...
32
    private $definition;
0 ignored issues
show
Coding Style introduced by
Private member variable "definition" must be prefixed with an underscore
Loading history...
33
    private $help;
0 ignored issues
show
Coding Style introduced by
Private member variable "help" must be prefixed with an underscore
Loading history...
34
    private $description;
0 ignored issues
show
Coding Style introduced by
Private member variable "description" must be prefixed with an underscore
Loading history...
35
    private $ignoreValidationErrors          = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "ignoreValidationErrors" must be prefixed with an underscore
Loading history...
36
    private $consoleDefinitionMerged         = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "consoleDefinitionMerged" must be prefixed with an underscore
Loading history...
37
    private $consoleDefinitionMergedWithArgs = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "consoleDefinitionMergedWithArgs" must be prefixed with an underscore
Loading history...
38
    private $synopsis                        = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "synopsis" must be prefixed with an underscore
Loading history...
39
    private $usages                          = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "usages" must be prefixed with an underscore
Loading history...
40
41
    /** @var  Input */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
42
    protected $input;
43
44
    /** @var  Output */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
45
    protected $output;
46
47
    /** @var App */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
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
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
69
    public function ignoreValidationErrors(): void
70
    {
71
        $this->ignoreValidationErrors = true;
72
    }
73
74
    /**
75
     * 设置控制台
76
     * @param Console $console
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
77
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
96
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
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
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
123
    protected function configure()
124
    {
125
    }
126
127
    /**
128
     * 执行指令
129
     * @param Input  $input
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
130
     * @param Output $output
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
143
     * @param Output $output
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
144
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
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
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
161
     * @param Output $output
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
218
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
219
    public function mergeConsoleDefinition(bool $mergeArgs = true)
220
    {
221
        if (null === $this->console
222
            || (true === $this->consoleDefinitionMerged
223
                && ($this->consoleDefinitionMergedWithArgs || !$mergeArgs))
0 ignored issues
show
Coding Style introduced by
Multi-line IF statement not indented correctly; expected 12 spaces but found 16
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
483
     * @throws InvalidArgumentException
484
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
485
    private function validateName(string $name)
0 ignored issues
show
Coding Style introduced by
Private method name "Command::validateName" must be prefixed with an underscore
Loading history...
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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
     * 判断当前应用是否多应用
506
     * @return bool
507
     */
508
    protected function isMultiApp(): bool
509
    {
510
        $autoMulti = $this->app->config->get('app.auto_multi_app', false);
511
        return $autoMulti || !is_dir($this->app->getBasePath() . 'controller');
512
    }
513
}
514