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 | } |