Total Complexity | 58 |
Total Lines | 475 |
Duplicated Lines | 0 % |
Changes | 0 |
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 |
||
21 | class Command |
||
1 ignored issue
–
show
|
|||
22 | { |
||
23 | |||
24 | /** @var Console */ |
||
25 | private $console; |
||
26 | private $name; |
||
27 | private $aliases = []; |
||
28 | private $definition; |
||
29 | private $help; |
||
30 | private $description; |
||
31 | private $ignoreValidationErrors = false; |
||
32 | private $consoleDefinitionMerged = false; |
||
33 | private $consoleDefinitionMergedWithArgs = false; |
||
34 | private $code; |
||
35 | private $synopsis = []; |
||
36 | private $usages = []; |
||
37 | |||
38 | /** @var Input */ |
||
39 | protected $input; |
||
40 | |||
41 | /** @var Output */ |
||
42 | protected $output; |
||
43 | |||
44 | /** @var App */ |
||
45 | protected $app; |
||
46 | |||
47 | /** |
||
48 | * 构造方法 |
||
49 | * @param string|null $name 命令名称,如果没有设置则比如在 configure() 里设置 |
||
1 ignored issue
–
show
|
|||
50 | * @throws \LogicException |
||
51 | * @api |
||
52 | */ |
||
53 | public function __construct($name = null) |
||
54 | { |
||
55 | $this->definition = new Definition(); |
||
56 | |||
57 | if (null !== $name) { |
||
58 | $this->setName($name); |
||
59 | } |
||
60 | |||
61 | $this->configure(); |
||
62 | |||
63 | if (!$this->name) { |
||
64 | throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); |
||
65 | } |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * 忽略验证错误 |
||
70 | */ |
||
71 | public function ignoreValidationErrors(): void |
||
72 | { |
||
73 | $this->ignoreValidationErrors = true; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * 设置控制台 |
||
78 | * @param Console $console |
||
79 | */ |
||
80 | public function setConsole(Console $console = null): void |
||
81 | { |
||
82 | $this->console = $console; |
||
83 | } |
||
84 | |||
85 | /** |
||
86 | * 获取控制台 |
||
87 | * @return Console |
||
88 | * @api |
||
89 | */ |
||
90 | public function getConsole(): Console |
||
91 | { |
||
92 | return $this->console; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * 设置app |
||
97 | * @param App $app |
||
98 | */ |
||
99 | public function setApp(App $app) |
||
100 | { |
||
101 | $this->app = $app; |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * 获取app |
||
106 | * @return App |
||
107 | */ |
||
108 | public function getApp() |
||
109 | { |
||
110 | return $this->app; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * 是否有效 |
||
115 | * @return bool |
||
116 | */ |
||
117 | public function isEnabled(): bool |
||
118 | { |
||
119 | return true; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * 配置指令 |
||
124 | */ |
||
125 | protected function configure() |
||
126 | { |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * 执行指令 |
||
131 | * @param Input $input |
||
1 ignored issue
–
show
|
|||
132 | * @param Output $output |
||
1 ignored issue
–
show
|
|||
133 | * @return null|int |
||
134 | * @throws \LogicException |
||
135 | * @see setCode() |
||
1 ignored issue
–
show
|
|||
136 | */ |
||
137 | protected function execute(Input $input, Output $output) |
||
138 | { |
||
139 | return $this->app->invoke([$this, 'handle']); |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * 用户验证 |
||
144 | * @param Input $input |
||
145 | * @param Output $output |
||
146 | */ |
||
147 | protected function interact(Input $input, Output $output) |
||
148 | { |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * 初始化 |
||
153 | * @param Input $input An InputInterface instance |
||
154 | * @param Output $output An OutputInterface instance |
||
155 | */ |
||
156 | protected function initialize(Input $input, Output $output) |
||
157 | { |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * 执行 |
||
162 | * @param Input $input |
||
1 ignored issue
–
show
|
|||
163 | * @param Output $output |
||
1 ignored issue
–
show
|
|||
164 | * @return int |
||
165 | * @throws \Exception |
||
166 | * @see setCode() |
||
1 ignored issue
–
show
|
|||
167 | * @see execute() |
||
1 ignored issue
–
show
|
|||
168 | */ |
||
169 | public function run(Input $input, Output $output): int |
||
170 | { |
||
171 | $this->input = $input; |
||
172 | $this->output = $output; |
||
173 | |||
174 | $this->getSynopsis(true); |
||
175 | $this->getSynopsis(false); |
||
176 | |||
177 | $this->mergeConsoleDefinition(); |
||
178 | |||
179 | try { |
||
180 | $input->bind($this->definition); |
||
181 | } catch (\Exception $e) { |
||
182 | if (!$this->ignoreValidationErrors) { |
||
183 | throw $e; |
||
184 | } |
||
185 | } |
||
186 | |||
187 | $this->initialize($input, $output); |
||
188 | |||
189 | if ($input->isInteractive()) { |
||
190 | $this->interact($input, $output); |
||
191 | } |
||
192 | |||
193 | $input->validate(); |
||
194 | |||
195 | if ($this->code) { |
||
196 | $statusCode = call_user_func($this->code, $input, $output); |
||
197 | } else { |
||
198 | $statusCode = $this->execute($input, $output); |
||
199 | } |
||
200 | |||
201 | return is_numeric($statusCode) ? (int) $statusCode : 0; |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * 设置执行代码 |
||
206 | * @param callable $code callable(InputInterface $input, OutputInterface $output) |
||
1 ignored issue
–
show
|
|||
207 | * @return Command |
||
208 | * @throws \InvalidArgumentException |
||
209 | * @see execute() |
||
1 ignored issue
–
show
|
|||
210 | */ |
||
211 | public function setCode(callable $code) |
||
212 | { |
||
213 | if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) { |
||
214 | $r = new \ReflectionFunction($code); |
||
215 | if (null === $r->getClosureThis()) { |
||
216 | $code = \Closure::bind($code, $this); |
||
217 | } |
||
218 | } |
||
219 | |||
220 | $this->code = $code; |
||
221 | |||
222 | return $this; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * 合并参数定义 |
||
227 | * @param bool $mergeArgs |
||
228 | */ |
||
229 | public function mergeConsoleDefinition(bool $mergeArgs = true) |
||
230 | { |
||
231 | if (null === $this->console |
||
232 | || (true === $this->consoleDefinitionMerged |
||
233 | && ($this->consoleDefinitionMergedWithArgs || !$mergeArgs)) |
||
234 | ) { |
||
235 | return; |
||
236 | } |
||
237 | |||
238 | if ($mergeArgs) { |
||
239 | $currentArguments = $this->definition->getArguments(); |
||
240 | $this->definition->setArguments($this->console->getDefinition()->getArguments()); |
||
241 | $this->definition->addArguments($currentArguments); |
||
242 | } |
||
243 | |||
244 | $this->definition->addOptions($this->console->getDefinition()->getOptions()); |
||
245 | |||
246 | $this->consoleDefinitionMerged = true; |
||
247 | if ($mergeArgs) { |
||
248 | $this->consoleDefinitionMergedWithArgs = true; |
||
249 | } |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * 设置参数定义 |
||
254 | * @param array|Definition $definition |
||
1 ignored issue
–
show
|
|||
255 | * @return Command |
||
256 | * @api |
||
257 | */ |
||
258 | public function setDefinition($definition) |
||
259 | { |
||
260 | if ($definition instanceof Definition) { |
||
261 | $this->definition = $definition; |
||
262 | } else { |
||
263 | $this->definition->setDefinition($definition); |
||
264 | } |
||
265 | |||
266 | $this->consoleDefinitionMerged = false; |
||
267 | |||
268 | return $this; |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * 获取参数定义 |
||
273 | * @return Definition |
||
274 | * @api |
||
275 | */ |
||
276 | public function getDefinition(): Definition |
||
277 | { |
||
278 | return $this->definition; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * 获取当前指令的参数定义 |
||
283 | * @return Definition |
||
284 | */ |
||
285 | public function getNativeDefinition(): Definition |
||
286 | { |
||
287 | return $this->getDefinition(); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * 添加参数 |
||
292 | * @param string $name 名称 |
||
1 ignored issue
–
show
|
|||
293 | * @param int $mode 类型 |
||
1 ignored issue
–
show
|
|||
294 | * @param string $description 描述 |
||
1 ignored issue
–
show
|
|||
295 | * @param mixed $default 默认值 |
||
1 ignored issue
–
show
|
|||
296 | * @return Command |
||
297 | */ |
||
298 | public function addArgument(string $name, int $mode = null, string $description = '', $default = null) |
||
299 | { |
||
300 | $this->definition->addArgument(new Argument($name, $mode, $description, $default)); |
||
301 | |||
302 | return $this; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * 添加选项 |
||
307 | * @param string $name 选项名称 |
||
1 ignored issue
–
show
|
|||
308 | * @param string $shortcut 别名 |
||
1 ignored issue
–
show
|
|||
309 | * @param int $mode 类型 |
||
1 ignored issue
–
show
|
|||
310 | * @param string $description 描述 |
||
1 ignored issue
–
show
|
|||
311 | * @param mixed $default 默认值 |
||
1 ignored issue
–
show
|
|||
312 | * @return Command |
||
313 | */ |
||
314 | public function addOption(string $name, string $shortcut = null, int $mode = null, string $description = '', $default = null) |
||
315 | { |
||
316 | $this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default)); |
||
317 | |||
318 | return $this; |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * 设置指令名称 |
||
323 | * @param string $name |
||
1 ignored issue
–
show
|
|||
324 | * @return Command |
||
325 | * @throws \InvalidArgumentException |
||
326 | */ |
||
327 | public function setName(string $name) |
||
328 | { |
||
329 | $this->validateName($name); |
||
330 | |||
331 | $this->name = $name; |
||
332 | |||
333 | return $this; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * 获取指令名称 |
||
338 | * @return string |
||
339 | */ |
||
340 | public function getName(): string |
||
341 | { |
||
342 | return $this->name ?: ''; |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * 设置描述 |
||
347 | * @param string $description |
||
1 ignored issue
–
show
|
|||
348 | * @return Command |
||
349 | */ |
||
350 | public function setDescription(string $description) |
||
351 | { |
||
352 | $this->description = $description; |
||
353 | |||
354 | return $this; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * 获取描述 |
||
359 | * @return string |
||
360 | */ |
||
361 | public function getDescription(): string |
||
362 | { |
||
363 | return $this->description ?: ''; |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * 设置帮助信息 |
||
368 | * @param string $help |
||
1 ignored issue
–
show
|
|||
369 | * @return Command |
||
370 | */ |
||
371 | public function setHelp(string $help) |
||
372 | { |
||
373 | $this->help = $help; |
||
374 | |||
375 | return $this; |
||
376 | } |
||
377 | |||
378 | /** |
||
379 | * 获取帮助信息 |
||
380 | * @return string |
||
381 | */ |
||
382 | public function getHelp(): string |
||
383 | { |
||
384 | return $this->help ?: ''; |
||
385 | } |
||
386 | |||
387 | /** |
||
388 | * 描述信息 |
||
389 | * @return string |
||
390 | */ |
||
391 | public function getProcessedHelp(): string |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * 设置别名 |
||
409 | * @param string[] $aliases |
||
1 ignored issue
–
show
|
|||
410 | * @return Command |
||
411 | * @throws \InvalidArgumentException |
||
412 | */ |
||
413 | public function setAliases(iterable $aliases) |
||
414 | { |
||
415 | foreach ($aliases as $alias) { |
||
416 | $this->validateName($alias); |
||
417 | } |
||
418 | |||
419 | $this->aliases = $aliases; |
||
420 | |||
421 | return $this; |
||
422 | } |
||
423 | |||
424 | /** |
||
425 | * 获取别名 |
||
426 | * @return array |
||
427 | */ |
||
428 | public function getAliases(): array |
||
429 | { |
||
430 | return $this->aliases; |
||
431 | } |
||
432 | |||
433 | /** |
||
434 | * 获取简介 |
||
435 | * @param bool $short 是否简单的 |
||
1 ignored issue
–
show
|
|||
436 | * @return string |
||
437 | */ |
||
438 | public function getSynopsis(bool $short = false): string |
||
439 | { |
||
440 | $key = $short ? 'short' : 'long'; |
||
441 | |||
442 | if (!isset($this->synopsis[$key])) { |
||
443 | $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); |
||
444 | } |
||
445 | |||
446 | return $this->synopsis[$key]; |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * 添加用法介绍 |
||
451 | * @param string $usage |
||
1 ignored issue
–
show
|
|||
452 | * @return $this |
||
453 | */ |
||
454 | public function addUsage(string $usage) |
||
463 | } |
||
464 | |||
465 | /** |
||
466 | * 获取用法介绍 |
||
467 | * @return array |
||
468 | */ |
||
469 | public function getUsages(): array |
||
470 | { |
||
471 | return $this->usages; |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * 验证指令名称 |
||
476 | * @param string $name |
||
1 ignored issue
–
show
|
|||
477 | * @throws \InvalidArgumentException |
||
478 | */ |
||
479 | private function validateName(string $name) |
||
480 | { |
||
481 | if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { |
||
482 | throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); |
||
483 | } |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * 输出表格 |
||
488 | * @param Table $table |
||
1 ignored issue
–
show
|
|||
489 | * @return string |
||
490 | */ |
||
491 | protected function table(Table $table): string |
||
496 | } |
||
497 | } |
||
498 |