Skip to content

Commit c45bf78

Browse files
authored
Improve Command (#5878)
1 parent 5518643 commit c45bf78

3 files changed

Lines changed: 509 additions & 277 deletions

File tree

src/Command.php

Lines changed: 21 additions & 277 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,13 @@
1111
*/
1212
namespace Hyperf\Command;
1313

14-
use Hyperf\Contract\Arrayable;
1514
use Hyperf\Coroutine\Coroutine;
16-
use Hyperf\Stringable\Str;
1715
use Psr\EventDispatcher\EventDispatcherInterface;
1816
use Swoole\ExitException;
1917
use Symfony\Component\Console\Command\Command as SymfonyCommand;
20-
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
21-
use Symfony\Component\Console\Helper\Table;
22-
use Symfony\Component\Console\Helper\TableStyle;
2318
use Symfony\Component\Console\Input\ArrayInput;
2419
use Symfony\Component\Console\Input\InputInterface;
2520
use Symfony\Component\Console\Output\OutputInterface;
26-
use Symfony\Component\Console\Question\ChoiceQuestion;
27-
use Symfony\Component\Console\Question\Question;
2821
use Symfony\Component\Console\Style\SymfonyStyle;
2922
use Throwable;
3023

@@ -36,52 +29,39 @@
3629
abstract class Command extends SymfonyCommand
3730
{
3831
use DisableEventDispatcher;
32+
use Concerns\InteractsWithIO;
33+
use Concerns\HasParameters;
3934

4035
/**
4136
* The name of the command.
4237
*/
4338
protected ?string $name = null;
4439

45-
protected string $description = '';
46-
47-
protected ?InputInterface $input = null;
48-
4940
/**
50-
* @var null|SymfonyStyle
41+
* The description of the command.
5142
*/
52-
protected ?OutputInterface $output = null;
53-
54-
/**
55-
* The default verbosity of output commands.
56-
*/
57-
protected int $verbosity = OutputInterface::VERBOSITY_NORMAL;
43+
protected string $description = '';
5844

5945
/**
6046
* Execution in a coroutine environment.
6147
*/
6248
protected bool $coroutine = true;
6349

50+
/**
51+
* The eventDispatcher.
52+
*/
6453
protected ?EventDispatcherInterface $eventDispatcher = null;
6554

55+
/**
56+
* The hookFlags of the command.
57+
*/
6658
protected int $hookFlags = -1;
6759

6860
/**
6961
* The name and signature of the command.
7062
*/
7163
protected ?string $signature = null;
7264

73-
/**
74-
* The mapping between human-readable verbosity levels and Symfony's OutputInterface.
75-
*/
76-
protected array $verbosityMap
77-
= [
78-
'v' => OutputInterface::VERBOSITY_VERBOSE,
79-
'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
80-
'vvv' => OutputInterface::VERBOSITY_DEBUG,
81-
'quiet' => OutputInterface::VERBOSITY_QUIET,
82-
'normal' => OutputInterface::VERBOSITY_NORMAL,
83-
];
84-
8565
/**
8666
* The exit code of the command.
8767
*/
@@ -101,208 +81,25 @@ public function __construct(string $name = null)
10181
parent::__construct($this->name);
10282
}
10383

104-
! empty($this->description) && $this->setDescription($this->description);
105-
10684
$this->addDisableDispatcherOption();
107-
}
108-
109-
/**
110-
* Run the console command.
111-
*/
112-
public function run(InputInterface $input, OutputInterface $output): int
113-
{
114-
$this->output = new SymfonyStyle($input, $output);
115-
116-
return parent::run($this->input = $input, $this->output);
117-
}
118-
119-
/**
120-
* Confirm a question with the user.
121-
*/
122-
public function confirm(string $question, bool $default = false): bool
123-
{
124-
return $this->output->confirm($question, $default);
125-
}
126-
127-
/**
128-
* Prompt the user for input.
129-
*/
130-
public function ask(string $question, string $default = null)
131-
{
132-
return $this->output->ask($question, $default);
133-
}
134-
135-
/**
136-
* Prompt the user for input with auto-completion.
137-
*
138-
* @param null|bool|float|int|string $default
139-
*/
140-
public function anticipate(string $question, array $choices, $default = null)
141-
{
142-
return $this->askWithCompletion($question, $choices, $default);
143-
}
144-
145-
/**
146-
* Prompt the user for input with auto-completion.
147-
*
148-
* @param null|bool|float|int|string $default
149-
*/
150-
public function askWithCompletion(string $question, array $choices, $default = null)
151-
{
152-
$question = new Question($question, $default);
153-
154-
$question->setAutocompleterValues($choices);
155-
156-
return $this->output->askQuestion($question);
157-
}
158-
159-
/**
160-
* Prompt the user for input but hide the answer from the console.
161-
*/
162-
public function secret(string $question, bool $fallback = true)
163-
{
164-
$question = new Question($question);
165-
166-
$question->setHidden(true)->setHiddenFallback($fallback);
167-
168-
return $this->output->askQuestion($question);
169-
}
170-
171-
/**
172-
* Give the user a multiple choice from an array of answers.
173-
* @param mixed $default
174-
*/
175-
public function choiceMultiple(
176-
string $question,
177-
array $choices,
178-
$default = null,
179-
?int $attempts = null
180-
): array {
181-
$question = new ChoiceQuestion($question, $choices, $default);
182-
183-
$question->setMaxAttempts($attempts)->setMultiselect(true);
184-
185-
return $this->output->askQuestion($question);
186-
}
187-
188-
/**
189-
* Give the user a single choice from an array of answers.
190-
*
191-
* @param mixed $default
192-
*/
193-
public function choice(
194-
string $question,
195-
array $choices,
196-
$default = null,
197-
?int $attempts = null
198-
): mixed {
199-
return $this->choiceMultiple($question, $choices, $default, $attempts)[0];
200-
}
201-
202-
/**
203-
* Format input to textual table.
204-
*/
205-
public function table(array $headers, array|Arrayable $rows, TableStyle|string $tableStyle = 'default', array $columnStyles = []): void
206-
{
207-
$table = new Table($this->output);
20885

209-
if ($rows instanceof Arrayable) {
210-
$rows = $rows->toArray();
86+
if (! empty($this->description)) {
87+
$this->setDescription($this->description);
21188
}
21289

213-
$table->setHeaders($headers)->setRows($rows)->setStyle($tableStyle);
214-
215-
foreach ($columnStyles as $columnIndex => $columnStyle) {
216-
$table->setColumnStyle($columnIndex, $columnStyle);
90+
if (! isset($this->signature)) {
91+
$this->specifyParameters();
21792
}
218-
219-
$table->render();
220-
}
221-
222-
/**
223-
* Write a string as standard output.
224-
*
225-
* @param mixed $string
226-
* @param null|mixed $style
227-
* @param null|mixed $verbosity
228-
*/
229-
public function line($string, $style = null, $verbosity = null)
230-
{
231-
$styled = $style ? "<{$style}>{$string}</{$style}>" : $string;
232-
$this->output->writeln($styled, $this->parseVerbosity($verbosity));
233-
}
234-
235-
/**
236-
* Write a string as information output.
237-
*
238-
* @param mixed $string
239-
* @param null|mixed $verbosity
240-
*/
241-
public function info($string, $verbosity = null)
242-
{
243-
$this->line($string, 'info', $verbosity);
244-
}
245-
246-
/**
247-
* Write a string as comment output.
248-
*
249-
* @param mixed $string
250-
* @param null|mixed $verbosity
251-
*/
252-
public function comment($string, $verbosity = null)
253-
{
254-
$this->line($string, 'comment', $verbosity);
255-
}
256-
257-
/**
258-
* Write a string as question output.
259-
*
260-
* @param mixed $string
261-
* @param null|mixed $verbosity
262-
*/
263-
public function question($string, $verbosity = null)
264-
{
265-
$this->line($string, 'question', $verbosity);
26693
}
26794

26895
/**
269-
* Write a string as error output.
270-
*
271-
* @param mixed $string
272-
* @param null|mixed $verbosity
273-
*/
274-
public function error($string, $verbosity = null)
275-
{
276-
$this->line($string, 'error', $verbosity);
277-
}
278-
279-
/**
280-
* Write a string as warning output.
281-
*
282-
* @param mixed $string
283-
* @param null|mixed $verbosity
96+
* Run the console command.
28497
*/
285-
public function warn($string, $verbosity = null)
98+
public function run(InputInterface $input, OutputInterface $output): int
28699
{
287-
if (! $this->output->getFormatter()->hasStyle('warning')) {
288-
$style = new OutputFormatterStyle('yellow');
289-
$this->output->getFormatter()->setStyle('warning', $style);
290-
}
291-
$this->line($string, 'warning', $verbosity);
292-
}
100+
$this->output = new SymfonyStyle($input, $output);
293101

294-
/**
295-
* Write a string in an alert box.
296-
*
297-
* @param mixed $string
298-
*/
299-
public function alert($string)
300-
{
301-
$length = Str::length(strip_tags($string)) + 12;
302-
$this->comment(str_repeat('*', $length));
303-
$this->comment('* ' . $string . ' *');
304-
$this->comment(str_repeat('*', $length));
305-
$this->output->newLine();
102+
return parent::run($this->input = $input, $this->output);
306103
}
307104

308105
/**
@@ -315,36 +112,6 @@ public function call(string $command, array $arguments = []): int
315112
return $this->getApplication()->find($command)->run($this->createInputFromArguments($arguments), $this->output);
316113
}
317114

318-
/**
319-
* Handle the current command.
320-
*/
321-
abstract public function handle();
322-
323-
/**
324-
* Set the verbosity level.
325-
*
326-
* @param mixed $level
327-
*/
328-
protected function setVerbosity($level)
329-
{
330-
$this->verbosity = $this->parseVerbosity($level);
331-
}
332-
333-
/**
334-
* Get the verbosity level in terms of Symfony's OutputInterface level.
335-
*
336-
* @param null|mixed $level
337-
*/
338-
protected function parseVerbosity($level = null): int
339-
{
340-
if (isset($this->verbosityMap[$level])) {
341-
$level = $this->verbosityMap[$level];
342-
} elseif (! is_int($level)) {
343-
$level = $this->verbosity;
344-
}
345-
return $level;
346-
}
347-
348115
/**
349116
* Create an input instance from the given arguments.
350117
*/
@@ -373,27 +140,6 @@ protected function context(): array
373140
})->all();
374141
}
375142

376-
/**
377-
* Specify the arguments and options on the command.
378-
*/
379-
protected function specifyParameters(): void
380-
{
381-
// We will loop through all the arguments and options for the command and
382-
// set them all on the base command instance. This specifies what can get
383-
// past into these commands as "parameters" to control the execution.
384-
if (method_exists($this, 'getArguments')) {
385-
foreach ($this->getArguments() ?? [] as $arguments) {
386-
call_user_func_array([$this, 'addArgument'], $arguments);
387-
}
388-
}
389-
390-
if (method_exists($this, 'getOptions')) {
391-
foreach ($this->getOptions() ?? [] as $options) {
392-
call_user_func_array([$this, 'addOption'], $options);
393-
}
394-
}
395-
}
396-
397143
/**
398144
* Configure the console command using a fluent definition.
399145
*/
@@ -413,19 +159,17 @@ protected function configureUsingFluentDefinition()
413159
protected function configure()
414160
{
415161
parent::configure();
416-
if (! isset($this->signature)) {
417-
$this->specifyParameters();
418-
}
419162
}
420163

421164
protected function execute(InputInterface $input, OutputInterface $output)
422165
{
423166
$this->disableDispatcher($input);
167+
$method = method_exists($this, 'handle') ? 'handle' : '__invoke';
424168

425-
$callback = function () {
169+
$callback = function () use ($method) {
426170
try {
427171
$this->eventDispatcher?->dispatch(new Event\BeforeHandle($this));
428-
$this->handle();
172+
$this->{$method}();
429173
$this->eventDispatcher?->dispatch(new Event\AfterHandle($this));
430174
} catch (Throwable $exception) {
431175
if (class_exists(ExitException::class) && $exception instanceof ExitException) {

0 commit comments

Comments
 (0)