Command   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 25
dl 0
loc 115
rs 10
c 0
b 0
f 0
wmc 16

10 Methods

Rating   Name   Duplication   Size   Complexity  
A inlineGray() 0 3 1
A handleException() 0 14 3
A handle() 0 6 2
A gray() 0 3 1
A safeHandle() 0 3 1
A infoComment() 0 10 1
A href() 0 3 1
A fileLink() 0 5 3
A indentedLine() 0 3 1
A askForString() 0 6 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));
0 ignored issues
show
Bug introduced by
The method path() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

87
        $link = 'file://'.str_replace('\\', '/', realpath($path) ?: Hyde::/** @scrutinizer ignore-call */ path($path));
Loading history...
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
}
141