Passed
Branch master (e894a3)
by Melech
15:39 queued 01:19
created

Commander   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 298
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 34
eloc 82
dl 0
loc 298
rs 9.68
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Console\Commander;
15
16
use Valkyrja\Application\Contract\Application;
17
use Valkyrja\Console\Commander\Contract\Commander as Contract;
18
use Valkyrja\Console\Constant\ExitCode;
19
use Valkyrja\Console\Enum\ArgumentMode;
20
use Valkyrja\Console\Input\Argument;
21
use Valkyrja\Console\Input\Contract\Input as InputContract;
22
use Valkyrja\Console\Input\Input;
23
use Valkyrja\Console\Input\Option;
24
use Valkyrja\Console\Output\Contract\Output as OutputContract;
25
use Valkyrja\Console\Output\Output;
26
27
use function max;
28
use function str_repeat;
29
use function strlen;
30
31
/**
32
 * Abstract Class Handler.
33
 *
34
 * @author Melech Mizrachi
35
 */
36
abstract class Commander implements Contract
37
{
38
    /** @var string */
39
    public const string COMMAND = '';
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 39 at column 24
Loading history...
40
    /** @var string */
41
    public const string PATH = '';
42
    /** @var string */
43
    public const string SHORT_DESCRIPTION = '';
44
    /** @var string */
45
    public const string DESCRIPTION = '';
46
47
    /**
48
     * Tabbing structure to use.
49
     *
50
     * @var string
51
     */
52
    protected const string TAB = '  ';
53
54
    /** @var string */
55
    protected const string DOUBLE_TAB = self::TAB . self::TAB;
56
57
    public function __construct(
58
        protected InputContract $input = new Input(),
59
        protected OutputContract $output = new Output(),
60
    ) {
61
    }
62
63
    /**
64
     * @inheritDoc
65
     */
66
    public static function getCommand(): string
67
    {
68
        return static::COMMAND;
69
    }
70
71
    /**
72
     * @inheritDoc
73
     */
74
    public static function getPath(): string
75
    {
76
        return static::PATH;
77
    }
78
79
    /**
80
     * @inheritDoc
81
     */
82
    public static function getShortDescription(): string
83
    {
84
        return static::SHORT_DESCRIPTION;
85
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90
    public static function getDescription(): string
91
    {
92
        return static::DESCRIPTION;
93
    }
94
95
    /**
96
     * @inheritDoc
97
     */
98
    public function help(): int
99
    {
100
        $this->usageMessage();
101
        $this->argumentsSection();
102
        $this->optionsSection();
103
104
        return ExitCode::SUCCESS;
105
    }
106
107
    /**
108
     * @inheritDoc
109
     */
110
    public function run(): int
111
    {
112
        return ExitCode::SUCCESS;
113
    }
114
115
    /**
116
     * @inheritDoc
117
     */
118
    public function version(): int
119
    {
120
        $this->applicationMessage();
121
122
        return ExitCode::SUCCESS;
123
    }
124
125
    /**
126
     * The usage message and description.
127
     *
128
     * @param string|null $message [optional] The usage to use instead of default
129
     *
130
     * @return void
131
     */
132
    protected function usageMessage(string|null $message = null): void
133
    {
134
        $message ??= $this->usagePath();
135
136
        $this->sectionTitleMessage('Usage');
137
        $this->output->writeMessage(static::TAB);
138
        $this->output->writeMessage($message, true);
139
    }
140
141
    /**
142
     * The arguments section.
143
     *
144
     * @param Argument ...$arguments The argument
145
     *
146
     * @return void
147
     */
148
    protected function argumentsSection(Argument ...$arguments): void
149
    {
150
        if (empty($arguments)) {
151
            $arguments = $this->getArguments();
152
        }
153
154
        if (! $arguments) {
155
            return;
156
        }
157
158
        $longestLength = 0;
159
160
        $this->sectionDivider();
161
        $this->sectionTitleMessage('Arguments');
162
163
        foreach ($arguments as $argument) {
164
            $longestLength = max(strlen($argument->getName()), $longestLength);
165
        }
166
167
        foreach ($arguments as $argument) {
168
            $this->sectionMessage(static::TAB . $argument->getName(), $argument->getDescription(), $longestLength);
169
        }
170
    }
171
172
    /**
173
     * The options section.
174
     *
175
     * @param Option ...$options The options
176
     *
177
     * @return void
178
     */
179
    protected function optionsSection(Option ...$options): void
180
    {
181
        if (empty($options)) {
182
            $options = $this->getOptions();
183
        }
184
185
        if (! $options) {
186
            return;
187
        }
188
189
        $longestLength = 0;
190
191
        $this->sectionDivider();
192
        $this->sectionTitleMessage('Options');
193
194
        foreach ($options as $option) {
195
            $longestLength = max(strlen($this->getOptionName($option)), $longestLength);
196
        }
197
198
        foreach ($options as $option) {
199
            $this->sectionMessage($this->getOptionName($option), $option->getDescription(), $longestLength);
200
        }
201
    }
202
203
    /**
204
     * Get the usage path.
205
     *
206
     * @return string
207
     */
208
    protected function usagePath(): string
209
    {
210
        $message = static::COMMAND;
211
212
        if ($this->getOptions()) {
213
            $message .= ' [options]';
214
        }
215
216
        foreach ($this->getArguments() as $argument) {
217
            $message .= ' '
218
                . ($argument->getMode() === ArgumentMode::OPTIONAL ? '[' : '')
219
                . '<'
220
                . $argument->getName()
221
                . '>'
222
                . ($argument->getMode() === ArgumentMode::OPTIONAL ? ']' : '');
223
        }
224
225
        return $message;
226
    }
227
228
    /**
229
     * The section message.
230
     *
231
     * @param string $sectionName
232
     *
233
     * @return void
234
     */
235
    protected function sectionTitleMessage(string $sectionName): void
236
    {
237
        $this->output->getFormatter()->underscore();
238
        $this->output->writeMessage($sectionName . ':', true);
239
        $this->output->getFormatter()->resetOptions();
240
    }
241
242
    /**
243
     * Get the valid arguments.
244
     *
245
     * @return Argument[]
246
     */
247
    protected function getArguments(): array
248
    {
249
        return [];
250
    }
251
252
    /**
253
     * The sections divider.
254
     *
255
     * @return void
256
     */
257
    protected function sectionDivider(): void
258
    {
259
        $this->output->writeMessage('', true);
260
    }
261
262
    /**
263
     * The section message.
264
     *
265
     * @param string   $name          The name
266
     * @param string   $description   The description
267
     * @param int|null $longestLength The longest item length
268
     *
269
     * @return void
270
     */
271
    protected function sectionMessage(string $name, string $description, int|null $longestLength = null): void
272
    {
273
        $longestLength ??= 0;
274
        $spacesToAdd   = $longestLength - strlen($name);
275
276
        $this->output->getFormatter()->green();
277
        $this->output->writeMessage(static::TAB . $name);
278
279
        $this->output->getFormatter()->resetColor();
280
281
        $this->output->writeMessage($spacesToAdd > 0 ? str_repeat('.', $spacesToAdd) : '');
282
        $this->output->writeMessage(str_repeat('.', 8));
283
        $this->output->writeMessage($description, true);
284
    }
285
286
    /**
287
     * Get the valid options.
288
     *
289
     * @return Option[]
290
     */
291
    protected function getOptions(): array
292
    {
293
        return [];
294
    }
295
296
    /**
297
     * Get an options name for the options section.
298
     *
299
     * @param Option $option The option
300
     *
301
     * @return string
302
     */
303
    protected function getOptionName(Option $option): string
304
    {
305
        $name = '';
306
307
        if (($shortcut = $option->getShortcut()) !== null && $shortcut !== '') {
308
            $name .= '-' . $shortcut . ', ';
309
        } else {
310
            $name .= static::DOUBLE_TAB;
311
        }
312
313
        $name .= '--' . $option->getName();
314
315
        return $name;
316
    }
317
318
    /**
319
     * The application message.
320
     *
321
     * @return void
322
     */
323
    protected function applicationMessage(): void
324
    {
325
        $this->output->getFormatter()->magenta();
326
        $this->output->writeMessage('Valkyrja Application');
327
        $this->output->getFormatter()->resetColor();
328
        $this->output->writeMessage(' version ');
329
        $this->output->getFormatter()->cyan();
330
        $this->output->writeMessage(Application::VERSION, true);
331
        $this->output->getFormatter()->resetColor();
332
    }
333
}
334