Passed
Pull Request — 5.1 (#1748)
by guanguans
09:27
created

Input   F

Complexity

Total Complexity 89

Size/Duplication

Total Lines 445
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 141
dl 0
loc 445
rs 2
c 0
b 0
f 0
wmc 89

26 Methods

Rating   Name   Duplication   Size   Complexity  
A hasParameterOption() 0 13 5
A getOptions() 0 3 1
A parseShortOptionSet() 0 15 5
B parse() 0 15 11
A addShortOption() 0 7 2
A getArguments() 0 3 1
A bind() 0 7 1
A getFirstArgument() 0 10 4
A setOption() 0 7 2
A __toString() 0 15 4
A setArgument() 0 7 2
A __construct() 0 11 2
A escapeToken() 0 3 2
A hasOption() 0 3 2
A getParameterOption() 0 20 6
A parseArgument() 0 15 5
A validate() 0 4 2
A setTokens() 0 3 1
A getArgument() 0 8 3
A hasArgument() 0 3 1
A setInteractive() 0 3 1
A parseShortOption() 0 14 4
A getOption() 0 7 3
A parseLongOption() 0 8 2
A isInteractive() 0 3 1
C addLongOption() 0 41 16

How to fix   Complexity   

Complex Class

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

1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
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
12
namespace think\console;
13
14
use think\console\input\Argument;
15
use think\console\input\Definition;
16
use think\console\input\Option;
17
18
class Input
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
19
{
20
21
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
22
     * @var Definition
23
     */
24
    protected $definition;
25
26
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
27
     * @var Option[]
28
     */
29
    protected $options = [];
30
31
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
32
     * @var Argument[]
33
     */
34
    protected $arguments = [];
35
36
    protected $interactive = true;
37
38
    private $tokens;
0 ignored issues
show
Coding Style introduced by
Private member variable "tokens" must be prefixed with an underscore
Loading history...
39
    private $parsed;
0 ignored issues
show
Coding Style introduced by
Private member variable "parsed" must be prefixed with an underscore
Loading history...
40
41
    public function __construct($argv = null)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
42
    {
43
        if (null === $argv) {
44
            $argv = $_SERVER['argv'];
45
            // 去除命令名
46
            array_shift($argv);
47
        }
48
49
        $this->tokens = $argv;
50
51
        $this->definition = new Definition();
52
    }
53
54
    protected function setTokens(array $tokens)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
55
    {
56
        $this->tokens = $tokens;
57
    }
58
59
    /**
60
     * 绑定实例
61
     * @param Definition $definition A InputDefinition instance
62
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
63
    public function bind(Definition $definition)
64
    {
65
        $this->arguments  = [];
66
        $this->options    = [];
67
        $this->definition = $definition;
68
69
        $this->parse();
70
    }
71
72
    /**
73
     * 解析参数
74
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
75
    protected function parse()
76
    {
77
        $parseOptions = true;
78
        $this->parsed = $this->tokens;
79
        while (null !== $token = array_shift($this->parsed)) {
80
            if ($parseOptions && '' == $token) {
81
                $this->parseArgument($token);
82
            } elseif ($parseOptions && '--' == $token) {
83
                $parseOptions = false;
84
            } elseif ($parseOptions && 0 === strpos($token, '--')) {
85
                $this->parseLongOption($token);
86
            } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
87
                $this->parseShortOption($token);
88
            } else {
89
                $this->parseArgument($token);
90
            }
91
        }
92
    }
93
94
    /**
95
     * 解析短选项
96
     * @param string $token 当前的指令.
97
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
98
    private function parseShortOption($token)
0 ignored issues
show
Coding Style introduced by
Private method name "Input::parseShortOption" must be prefixed with an underscore
Loading history...
99
    {
100
        $name = substr($token, 1);
101
102
        if (strlen($name) > 1) {
103
            if ($this->definition->hasShortcut($name[0])
104
                && $this->definition->getOptionForShortcut($name[0])->acceptValue()
105
            ) {
106
                $this->addShortOption($name[0], substr($name, 1));
107
            } else {
108
                $this->parseShortOptionSet($name);
109
            }
110
        } else {
111
            $this->addShortOption($name, null);
112
        }
113
    }
114
115
    /**
116
     * 解析短选项
117
     * @param string $name 当前指令
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
118
     * @throws \RuntimeException
119
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
120
    private function parseShortOptionSet($name)
0 ignored issues
show
Coding Style introduced by
Private method name "Input::parseShortOptionSet" must be prefixed with an underscore
Loading history...
121
    {
122
        $len = strlen($name);
123
        for ($i = 0; $i < $len; ++$i) {
124
            if (!$this->definition->hasShortcut($name[$i])) {
125
                throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
126
            }
127
128
            $option = $this->definition->getOptionForShortcut($name[$i]);
129
            if ($option->acceptValue()) {
130
                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
131
132
                break;
133
            } else {
134
                $this->addLongOption($option->getName(), null);
135
            }
136
        }
137
    }
138
139
    /**
140
     * 解析完整选项
141
     * @param string $token 当前指令
142
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
143
    private function parseLongOption($token)
0 ignored issues
show
Coding Style introduced by
Private method name "Input::parseLongOption" must be prefixed with an underscore
Loading history...
144
    {
145
        $name = substr($token, 2);
146
147
        if (false !== $pos = strpos($name, '=')) {
148
            $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
149
        } else {
150
            $this->addLongOption($name, null);
151
        }
152
    }
153
154
    /**
155
     * 解析参数
156
     * @param string $token 当前指令
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
157
     * @throws \RuntimeException
158
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
159
    private function parseArgument($token)
0 ignored issues
show
Coding Style introduced by
Private method name "Input::parseArgument" must be prefixed with an underscore
Loading history...
160
    {
161
        $c = count($this->arguments);
162
163
        if ($this->definition->hasArgument($c)) {
164
            $arg = $this->definition->getArgument($c);
165
166
            $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
167
168
        } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
169
            $arg = $this->definition->getArgument($c - 1);
170
171
            $this->arguments[$arg->getName()][] = $token;
172
        } else {
173
            throw new \RuntimeException('Too many arguments.');
174
        }
175
    }
176
177
    /**
178
     * 添加一个短选项的值
179
     * @param string $shortcut 短名称
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
180
     * @param mixed  $value    值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
181
     * @throws \RuntimeException
182
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
183
    private function addShortOption($shortcut, $value)
0 ignored issues
show
Coding Style introduced by
Private method name "Input::addShortOption" must be prefixed with an underscore
Loading history...
184
    {
185
        if (!$this->definition->hasShortcut($shortcut)) {
186
            throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
187
        }
188
189
        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
190
    }
191
192
    /**
193
     * 添加一个完整选项的值
194
     * @param string $name  选项名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
195
     * @param mixed  $value 值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
196
     * @throws \RuntimeException
197
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
198
    private function addLongOption($name, $value)
0 ignored issues
show
Coding Style introduced by
Private method name "Input::addLongOption" must be prefixed with an underscore
Loading history...
199
    {
200
        if (!$this->definition->hasOption($name)) {
201
            throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
202
        }
203
204
        $option = $this->definition->getOption($name);
205
206
        if (false === $value) {
207
            $value = null;
208
        }
209
210
        if (null !== $value && !$option->acceptValue()) {
211
            throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
212
        }
213
214
        if (null === $value && $option->acceptValue() && count($this->parsed)) {
215
            $next = array_shift($this->parsed);
216
            if (isset($next[0]) && '-' !== $next[0]) {
217
                $value = $next;
218
            } elseif (empty($next)) {
219
                $value = '';
220
            } else {
221
                array_unshift($this->parsed, $next);
222
            }
223
        }
224
225
        if (null === $value) {
226
            if ($option->isValueRequired()) {
227
                throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
228
            }
229
230
            if (!$option->isArray()) {
231
                $value = $option->isValueOptional() ? $option->getDefault() : true;
232
            }
233
        }
234
235
        if ($option->isArray()) {
236
            $this->options[$name][] = $value;
237
        } else {
238
            $this->options[$name] = $value;
239
        }
240
    }
241
242
    /**
243
     * 获取第一个参数
244
     * @return string|null
245
     */
246
    public function getFirstArgument()
247
    {
248
        foreach ($this->tokens as $token) {
249
            if ($token && '-' === $token[0]) {
250
                continue;
251
            }
252
253
            return $token;
254
        }
255
        return;
256
    }
257
258
    /**
259
     * 检查原始参数是否包含某个值
260
     * @param string|array $values 需要检查的值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
261
     * @return bool
262
     */
263
    public function hasParameterOption($values)
264
    {
265
        $values = (array) $values;
266
267
        foreach ($this->tokens as $token) {
268
            foreach ($values as $value) {
269
                if ($token === $value || 0 === strpos($token, $value . '=')) {
270
                    return true;
271
                }
272
            }
273
        }
274
275
        return false;
276
    }
277
278
    /**
279
     * 获取原始选项的值
280
     * @param string|array $values  需要检查的值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
281
     * @param mixed        $default 默认值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
282
     * @return mixed The option value
283
     */
284
    public function getParameterOption($values, $default = false)
285
    {
286
        $values = (array) $values;
287
        $tokens = $this->tokens;
288
289
        while (0 < count($tokens)) {
290
            $token = array_shift($tokens);
291
292
            foreach ($values as $value) {
293
                if ($token === $value || 0 === strpos($token, $value . '=')) {
294
                    if (false !== $pos = strpos($token, '=')) {
295
                        return substr($token, $pos + 1);
296
                    }
297
298
                    return array_shift($tokens);
299
                }
300
            }
301
        }
302
303
        return $default;
304
    }
305
306
    /**
307
     * 验证输入
308
     * @throws \RuntimeException
309
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
310
    public function validate()
311
    {
312
        if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
313
            throw new \RuntimeException('Not enough arguments.');
314
        }
315
    }
316
317
    /**
318
     * 检查输入是否是交互的
319
     * @return bool
320
     */
321
    public function isInteractive()
322
    {
323
        return $this->interactive;
324
    }
325
326
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $interactive should have a doc-comment as per coding-style.
Loading history...
327
     * 设置输入的交互
328
     * @param bool
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
329
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
330
    public function setInteractive($interactive)
331
    {
332
        $this->interactive = (bool) $interactive;
333
    }
334
335
    /**
336
     * 获取所有的参数
337
     * @return Argument[]
338
     */
339
    public function getArguments()
340
    {
341
        return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
342
    }
343
344
    /**
345
     * 根据名称获取参数
346
     * @param string $name 参数名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
347
     * @return mixed
348
     * @throws \InvalidArgumentException
349
     */
350
    public function getArgument($name)
351
    {
352
        if (!$this->definition->hasArgument($name)) {
353
            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
354
        }
355
356
        return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)
357
            ->getDefault();
358
    }
359
360
    /**
361
     * 设置参数的值
362
     * @param string $name  参数名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
363
     * @param string $value 值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
364
     * @throws \InvalidArgumentException
365
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
366
    public function setArgument($name, $value)
367
    {
368
        if (!$this->definition->hasArgument($name)) {
369
            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
370
        }
371
372
        $this->arguments[$name] = $value;
373
    }
374
375
    /**
376
     * 检查是否存在某个参数
377
     * @param string|int $name 参数名或位置
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
378
     * @return bool
379
     */
380
    public function hasArgument($name)
381
    {
382
        return $this->definition->hasArgument($name);
383
    }
384
385
    /**
386
     * 获取所有的选项
387
     * @return Option[]
388
     */
389
    public function getOptions()
390
    {
391
        return array_merge($this->definition->getOptionDefaults(), $this->options);
392
    }
393
394
    /**
395
     * 获取选项值
396
     * @param string $name 选项名称
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
397
     * @return mixed
398
     * @throws \InvalidArgumentException
399
     */
400
    public function getOption($name)
401
    {
402
        if (!$this->definition->hasOption($name)) {
403
            throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
404
        }
405
406
        return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
407
    }
408
409
    /**
410
     * 设置选项值
411
     * @param string      $name  选项名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
412
     * @param string|bool $value 值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
413
     * @throws \InvalidArgumentException
414
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
415
    public function setOption($name, $value)
416
    {
417
        if (!$this->definition->hasOption($name)) {
418
            throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
419
        }
420
421
        $this->options[$name] = $value;
422
    }
423
424
    /**
425
     * 是否有某个选项
426
     * @param string $name 选项名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
427
     * @return bool
428
     */
429
    public function hasOption($name)
430
    {
431
        return $this->definition->hasOption($name) && isset($this->options[$name]);
432
    }
433
434
    /**
435
     * 转义指令
436
     * @param string $token
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
437
     * @return string
438
     */
439
    public function escapeToken($token)
440
    {
441
        return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
442
    }
443
444
    /**
445
     * 返回传递给命令的参数的字符串
446
     * @return string
447
     */
448
    public function __toString()
449
    {
450
        $tokens = array_map(function ($token) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
451
            if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
452
                return $match[1] . $this->escapeToken($match[2]);
453
            }
454
455
            if ($token && '-' !== $token[0]) {
456
                return $this->escapeToken($token);
457
            }
458
459
            return $token;
460
        }, $this->tokens);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
461
462
        return implode(' ', $tokens);
463
    }
464
}
465