Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
25 / 25 |
|
100.00% |
10 / 10 |
CRAP | |
100.00% |
1 / 1 |
Command | |
100.00% |
25 / 25 |
|
100.00% |
10 / 10 |
16 | |
100.00% |
1 / 1 |
handle | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
safeHandle | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
handleException | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
fileLink | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
infoComment | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
gray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
inlineGray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
href | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
indentedLine | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
askForString | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Hyde\Console\Concerns; |
6 | |
7 | use Exception; |
8 | use Hyde\Hyde; |
9 | use Hyde\Facades\Config; |
10 | use LaravelZero\Framework\Commands\Command as BaseCommand; |
11 | |
12 | use function is_string; |
13 | use function array_keys; |
14 | use function array_values; |
15 | use function realpath; |
16 | use function sprintf; |
17 | use function str_repeat; |
18 | use function str_replace; |
19 | |
20 | /** |
21 | * A base class for HydeCLI command that adds some extra functionality and output |
22 | * helpers to reduce repeated code and to provide a consistent user interface. |
23 | */ |
24 | abstract class Command extends BaseCommand |
25 | { |
26 | final public const USER_EXIT = 130; |
27 | |
28 | /** |
29 | * The base handle method that can be overridden by child classes. |
30 | * |
31 | * Alternatively, implement the safeHandle method in your child class |
32 | * to utilize the automatic exception handling provided by this method. |
33 | * |
34 | * @return int The exit code. |
35 | */ |
36 | public function handle(): int |
37 | { |
38 | try { |
39 | return $this->safeHandle(); |
40 | } catch (Exception $exception) { |
41 | return $this->handleException($exception); |
42 | } |
43 | } |
44 | |
45 | /** |
46 | * This method can be overridden by child classes to provide automatic exception handling. |
47 | * |
48 | * Existing code can be converted simply by renaming the handle() method to safeHandle(). |
49 | * |
50 | * @return int The exit code. |
51 | */ |
52 | protected function safeHandle(): int |
53 | { |
54 | return Command::SUCCESS; |
55 | } |
56 | |
57 | /** |
58 | * Handle an exception that occurred during command execution. |
59 | * |
60 | * @return int The exit code |
61 | */ |
62 | public function handleException(Exception $exception): int |
63 | { |
64 | // When testing it might be more useful to see the full stack trace, so we have an option to actually throw the exception. |
65 | if (Config::getBool('app.throw_on_console_exception', false)) { |
66 | throw $exception; |
67 | } |
68 | |
69 | // If the exception was thrown from the same file as a command, then we don't need to show which file it was thrown from. |
70 | $location = str_ends_with($exception->getFile(), 'Command.php') ? '' : sprintf(' at %s:%s', |
71 | $exception->getFile(), $exception->getLine() |
72 | ); |
73 | $this->error("Error: {$exception->getMessage()}".$location); |
74 | |
75 | return Command::FAILURE; |
76 | } |
77 | |
78 | /** |
79 | * Create a filepath that can be opened in the browser from a terminal. |
80 | * |
81 | * @param string|null $label If provided, the link will be wrapped in a Symfony Console `href` tag. |
82 | * Note that not all terminals support this, and it may lead to only |
83 | * the label being shown, and the path being lost to the void. |
84 | */ |
85 | public static function fileLink(string $path, string $label = null): string |
86 | { |
87 | $link = 'file://'.str_replace('\\', '/', realpath($path) ?: Hyde::path($path)); |
88 | |
89 | return $label ? "<href=$link>$label</>" : $link; |
90 | } |
91 | |
92 | /** |
93 | * Write a nicely formatted and consistent message to the console. Using InfoComment for a lack of a better term. |
94 | * |
95 | * Text in [brackets] will automatically be wrapped in <comment> tags. |
96 | */ |
97 | public function infoComment(string $string): void |
98 | { |
99 | $replacements = [ |
100 | '[' => '</info>[<comment>', |
101 | ']' => '</comment>]<info>', |
102 | ]; |
103 | |
104 | $string = str_replace(array_keys($replacements), array_values($replacements), $string); |
105 | |
106 | $this->line("<info>$string</info>"); |
107 | } |
108 | |
109 | /** Write a grey-coloured line */ |
110 | public function gray(string $string): void |
111 | { |
112 | $this->line($this->inlineGray($string)); |
113 | } |
114 | |
115 | /** @internal This method may be confused with the ->gray method and may be removed */ |
116 | public function inlineGray(string $string): string |
117 | { |
118 | return "<fg=gray>$string</>"; |
119 | } |
120 | |
121 | /** @internal This method may be removed and should not be relied upon */ |
122 | public function href(string $link, string $label): string |
123 | { |
124 | return "<href=$link>$label</>"; |
125 | } |
126 | |
127 | /** Write a line with the specified indentation level */ |
128 | public function indentedLine(int $spaces, string $string): void |
129 | { |
130 | $this->line(str_repeat(' ', $spaces).$string); |
131 | } |
132 | |
133 | public function askForString(string $question, ?string $default = null): ?string |
134 | { |
135 | /** @var string|null $answer */ |
136 | $answer = $this->output->ask($question, $default); |
137 | |
138 | return is_string($answer) ? $answer : $default; |
139 | } |
140 | } |