├── .phpstorm.meta.php
├── LICENSE
├── README.md
├── composer.json
└── src
├── CLI.php
├── Command.php
├── Commands
├── About.php
├── Help.php
└── Index.php
├── Console.php
├── Languages
├── en
│ └── cli.php
├── es
│ └── cli.php
└── pt-br
│ └── cli.php
├── Streams
├── FilterStream.php
├── Stderr.php
└── Stdout.php
└── Styles
├── BackgroundColor.php
├── ForegroundColor.php
└── Format.php
/.phpstorm.meta.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace PHPSTORM_META;
11 |
12 | registerArgumentsSet(
13 | 'cli_foreground_colors',
14 | 'black',
15 | 'blue',
16 | 'bright_black',
17 | 'bright_blue',
18 | 'bright_cyan',
19 | 'bright_green',
20 | 'bright_magenta',
21 | 'bright_red',
22 | 'bright_white',
23 | 'bright_yellow',
24 | 'cyan',
25 | 'green',
26 | 'magenta',
27 | 'red',
28 | 'white',
29 | 'yellow',
30 | );
31 | registerArgumentsSet(
32 | 'cli_background_colors',
33 | 'black',
34 | 'blue',
35 | 'bright_black',
36 | 'bright_blue',
37 | 'bright_cyan',
38 | 'bright_green',
39 | 'bright_magenta',
40 | 'bright_red',
41 | 'bright_yellow',
42 | 'cyan',
43 | 'green',
44 | 'magenta',
45 | 'red',
46 | 'white',
47 | 'yellow',
48 | );
49 | registerArgumentsSet(
50 | 'cli_formats',
51 | 'bold',
52 | 'conceal',
53 | 'crossed_out',
54 | 'doubly_underline',
55 | 'encircled',
56 | 'faint',
57 | 'fraktur',
58 | 'italic',
59 | 'primary_font',
60 | 'rapid_blink',
61 | 'reverse_video',
62 | 'slow_blink',
63 | 'underline',
64 | );
65 | expectedArguments(
66 | \Framework\CLI\CLI::box(),
67 | 1,
68 | argumentsSet('cli_background_colors')
69 | );
70 | expectedArguments(
71 | \Framework\CLI\CLI::box(),
72 | 2,
73 | argumentsSet('cli_foreground_colors')
74 | );
75 | expectedArguments(
76 | \Framework\CLI\CLI::write(),
77 | 1,
78 | argumentsSet('cli_foreground_colors')
79 | );
80 | expectedArguments(
81 | \Framework\CLI\CLI::write(),
82 | 2,
83 | argumentsSet('cli_background_colors')
84 | );
85 | expectedArguments(
86 | \Framework\CLI\CLI::style(),
87 | 1,
88 | argumentsSet('cli_foreground_colors')
89 | );
90 | expectedArguments(
91 | \Framework\CLI\CLI::style(),
92 | 2,
93 | argumentsSet('cli_background_colors')
94 | );
95 | /*expectedArguments(
96 | \Framework\CLI\CLI::style(),
97 | 3,
98 | argumentsSet('cli_formats')
99 | );*/
100 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Natan Felles
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Aplus Framework CLI Library
4 |
5 | - [Home](https://aplus-framework.com/packages/cli)
6 | - [User Guide](https://docs.aplus-framework.com/guides/libraries/cli/index.html)
7 | - [API Documentation](https://docs.aplus-framework.com/packages/cli.html)
8 |
9 | [](https://github.com/aplus-framework/cli/actions/workflows/tests.yml)
10 | [](https://coveralls.io/github/aplus-framework/cli?branch=master)
11 | [](https://packagist.org/packages/aplus/cli)
12 | [](https://aplus-framework.com/sponsor)
13 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aplus/cli",
3 | "description": "Aplus Framework CLI Library",
4 | "license": "MIT",
5 | "type": "library",
6 | "keywords": [
7 | "cli",
8 | "console",
9 | "terminal",
10 | "cron",
11 | "command",
12 | "commands"
13 | ],
14 | "authors": [
15 | {
16 | "name": "Natan Felles",
17 | "email": "natanfelles@gmail.com",
18 | "homepage": "https://natanfelles.github.io"
19 | }
20 | ],
21 | "homepage": "https://aplus-framework.com/packages/cli",
22 | "support": {
23 | "email": "support@aplus-framework.com",
24 | "issues": "https://github.com/aplus-framework/cli/issues",
25 | "forum": "https://aplus-framework.com/forum",
26 | "source": "https://github.com/aplus-framework/cli",
27 | "docs": "https://docs.aplus-framework.com/guides/libraries/cli/"
28 | },
29 | "funding": [
30 | {
31 | "type": "Aplus Sponsor",
32 | "url": "https://aplus-framework.com/sponsor"
33 | }
34 | ],
35 | "require": {
36 | "php": ">=8.3",
37 | "ext-posix": "*",
38 | "aplus/language": "^4.0"
39 | },
40 | "require-dev": {
41 | "ext-xdebug": "*",
42 | "aplus/coding-standard": "^2.8",
43 | "ergebnis/composer-normalize": "^2.25",
44 | "jetbrains/phpstorm-attributes": "^1.0",
45 | "phpmd/phpmd": "^2.13",
46 | "phpstan/phpstan": "^1.7",
47 | "phpunit/phpunit": "^10.5"
48 | },
49 | "minimum-stability": "dev",
50 | "prefer-stable": true,
51 | "autoload": {
52 | "psr-4": {
53 | "Framework\\CLI\\": "src/"
54 | }
55 | },
56 | "autoload-dev": {
57 | "psr-4": {
58 | "Tests\\CLI\\": "tests/"
59 | }
60 | },
61 | "config": {
62 | "allow-plugins": {
63 | "ergebnis/composer-normalize": true
64 | },
65 | "optimize-autoloader": true,
66 | "preferred-install": "dist",
67 | "sort-packages": true
68 | },
69 | "extra": {
70 | "branch-alias": {
71 | "dev-master": "3.x-dev"
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/CLI.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI;
11 |
12 | use Framework\CLI\Styles\BackgroundColor;
13 | use Framework\CLI\Styles\ForegroundColor;
14 | use Framework\CLI\Styles\Format;
15 | use JetBrains\PhpStorm\Pure;
16 | use Stringable;
17 | use ValueError;
18 |
19 | /**
20 | * Class CLI.
21 | *
22 | * @see https://en.wikipedia.org/wiki/ANSI_escape_code
23 | *
24 | * @package cli
25 | */
26 | class CLI
27 | {
28 | protected static string $reset = "\033[0m";
29 |
30 | /**
31 | * Tells if it is running on a Windows OS.
32 | *
33 | * @return bool
34 | */
35 | #[Pure]
36 | public static function isWindows() : bool
37 | {
38 | return \PHP_OS_FAMILY === 'Windows';
39 | }
40 |
41 | /**
42 | * Get the screen width.
43 | *
44 | * @param int $default
45 | *
46 | * @return int
47 | */
48 | public static function getWidth(int $default = 80) : int
49 | {
50 | if (static::isWindows()) {
51 | return $default;
52 | }
53 | $width = (int) \shell_exec('tput cols');
54 | if (!$width) {
55 | return $default;
56 | }
57 | return $width;
58 | }
59 |
60 | /**
61 | * Displays text wrapped to a certain width.
62 | *
63 | * @param string $text
64 | * @param int|null $width
65 | *
66 | * @return string Returns the wrapped text
67 | */
68 | public static function wrap(string $text, ?int $width = null) : string
69 | {
70 | $width ??= static::getWidth();
71 | return \wordwrap($text, $width, \PHP_EOL, true);
72 | }
73 |
74 | /**
75 | * Calculate the multibyte length of a text without style characters.
76 | *
77 | * @param string $text The text being checked for length
78 | *
79 | * @return int
80 | */
81 | public static function strlen(string $text) : int
82 | {
83 | $codes = [];
84 | foreach (ForegroundColor::cases() as $case) {
85 | $codes[] = $case->getCode();
86 | }
87 | foreach (BackgroundColor::cases() as $case) {
88 | $codes[] = $case->getCode();
89 | }
90 | foreach (Format::cases() as $case) {
91 | $codes[] = $case->getCode();
92 | }
93 | $codes[] = static::$reset;
94 | $text = \str_replace($codes, '', $text);
95 | return \mb_strlen($text);
96 | }
97 |
98 | /**
99 | * Applies styles to a text.
100 | *
101 | * @param string $text The text to be styled
102 | * @param ForegroundColor|string|null $color Foreground color
103 | * @param BackgroundColor|string|null $background Background color
104 | * @param array $formats The text formats
105 | *
106 | * @throws ValueError For invalid color, background or format
107 | *
108 | * @return string Returns the styled text
109 | */
110 | public static function style(
111 | string $text,
112 | ForegroundColor | string | null $color = null,
113 | BackgroundColor | string | null $background = null,
114 | array $formats = []
115 | ) : string {
116 | $string = '';
117 | if ($color !== null) {
118 | $string = \is_string($color)
119 | ? ForegroundColor::from($color)->getCode()
120 | : $color->getCode();
121 | }
122 | if ($background !== null) {
123 | $string .= \is_string($background)
124 | ? BackgroundColor::from($background)->getCode()
125 | : $background->getCode();
126 | }
127 | if ($formats) {
128 | foreach ($formats as $format) {
129 | $string .= \is_string($format)
130 | ? Format::from($format)->getCode()
131 | : $format->getCode();
132 | }
133 | }
134 | $string .= $text . static::$reset;
135 | return $string;
136 | }
137 |
138 | /**
139 | * Write a text in the output.
140 | *
141 | * Optionally with styles and width wrapping.
142 | *
143 | * @param string $text The text to be written
144 | * @param ForegroundColor|string|null $color Foreground color
145 | * @param BackgroundColor|string|null $background Background color
146 | * @param int|null $width Width to wrap the text. Null to do not wrap.
147 | */
148 | public static function write(
149 | string $text,
150 | ForegroundColor | string | null $color = null,
151 | BackgroundColor | string | null $background = null,
152 | ?int $width = null
153 | ) : void {
154 | if ($width !== null) {
155 | $text = static::wrap($text, $width);
156 | }
157 | if ($color !== null || $background !== null) {
158 | $text = static::style($text, $color, $background);
159 | }
160 | \fwrite(\STDOUT, $text . \PHP_EOL);
161 | }
162 |
163 | /**
164 | * Prints a new line in the output.
165 | *
166 | * @param int $lines Number of lines to be printed
167 | */
168 | public static function newLine(int $lines = 1) : void
169 | {
170 | for ($i = 0; $i < $lines; $i++) {
171 | \fwrite(\STDOUT, \PHP_EOL);
172 | }
173 | }
174 |
175 | /**
176 | * Creates a "live line".
177 | *
178 | * Erase the current line, move the cursor to the beginning of the line and
179 | * writes a text.
180 | *
181 | * @param string $text The text to be written
182 | * @param bool $finalize If true the "live line" activity ends, creating a
183 | * new line after the text
184 | */
185 | public static function liveLine(string $text, bool $finalize = false) : void
186 | {
187 | // See: https://stackoverflow.com/a/35190285
188 | $string = '';
189 | if (!static::isWindows()) {
190 | $string .= "\33[2K";
191 | }
192 | $string .= "\r";
193 | $string .= $text;
194 | if ($finalize) {
195 | $string .= \PHP_EOL;
196 | }
197 | \fwrite(\STDOUT, $string);
198 | }
199 |
200 | /**
201 | * Performs audible beep alarms.
202 | *
203 | * @param int $times How many times should the beep be played
204 | * @param int $usleep Interval in microseconds
205 | */
206 | public static function beep(int $times = 1, int $usleep = 0) : void
207 | {
208 | for ($i = 0; $i < $times; $i++) {
209 | \fwrite(\STDOUT, "\x07");
210 | \usleep($usleep);
211 | }
212 | }
213 |
214 | /**
215 | * Writes a message box.
216 | *
217 | * @param array|string $lines One line as string or multi-lines as array
218 | * @param BackgroundColor|string $background Background color
219 | * @param ForegroundColor|string $color Foreground color
220 | */
221 | public static function box(
222 | array | string $lines,
223 | BackgroundColor | string $background = BackgroundColor::black,
224 | ForegroundColor | string $color = ForegroundColor::white
225 | ) : void {
226 | $width = static::getWidth();
227 | $width -= 2;
228 | if (!\is_array($lines)) {
229 | $lines = [
230 | $lines,
231 | ];
232 | }
233 | $allLines = [];
234 | foreach ($lines as &$line) {
235 | $length = static::strlen($line);
236 | if ($length > $width) {
237 | $line = static::wrap($line, $width);
238 | }
239 | foreach (\explode(\PHP_EOL, $line) as $subLine) {
240 | $allLines[] = $subLine;
241 | }
242 | }
243 | unset($line);
244 | $blankLine = \str_repeat(' ', $width + 2);
245 | $text = static::style($blankLine, $color, $background);
246 | foreach ($allLines as $line) {
247 | $end = \str_repeat(' ', $width - static::strlen($line)) . ' ';
248 | $end = static::style($end, $color, $background);
249 | $text .= static::style(' ' . $line . $end, $color, $background);
250 | }
251 | $text .= static::style($blankLine, $color, $background);
252 | static::write($text);
253 | }
254 |
255 | /**
256 | * Writes a message to STDERR and optionally exit with a custom code.
257 | *
258 | * @param string $message The error message
259 | * @param int|null $exitCode Set null to do not exit
260 | */
261 | public static function error(string $message, ?int $exitCode = 1) : void
262 | {
263 | static::beep();
264 | \fwrite(\STDERR, static::style($message, ForegroundColor::red) . \PHP_EOL);
265 | if ($exitCode !== null) {
266 | exit($exitCode);
267 | }
268 | }
269 |
270 | /**
271 | * Clear the terminal screen.
272 | */
273 | public static function clear() : void
274 | {
275 | \fwrite(\STDOUT, "\e[H\e[2J");
276 | }
277 |
278 | /**
279 | * Get user input.
280 | *
281 | * NOTE: It is possible pass multiple lines ending each line with a backslash.
282 | *
283 | * @param string $prepend Text prepended in the input. Used internally to
284 | * allow multiple lines
285 | *
286 | * @return string Returns the user input
287 | */
288 | public static function getInput(string $prepend = '') : string
289 | {
290 | $input = \fgets(\STDIN);
291 | $input = $input === false ? '' : \trim($input);
292 | $prepend .= $input;
293 | $eolPos = false;
294 | if ($prepend) {
295 | $eolPos = \strrpos($prepend, '\\', -1);
296 | }
297 | if ($eolPos !== false) {
298 | $prepend = \substr_replace($prepend, \PHP_EOL, $eolPos);
299 | $prepend = static::getInput($prepend);
300 | }
301 | return $prepend;
302 | }
303 |
304 | /**
305 | * Prompt a question.
306 | *
307 | * @param string $question The question to prompt
308 | * @param array|string|null $options Answer options. If an array
309 | * is set, the default answer is the first value. If is a string, it will
310 | * be the default.
311 | *
312 | * @return string The answer
313 | */
314 | public static function prompt(string $question, array | string | null $options = null) : string
315 | {
316 | if ($options !== null) {
317 | $options = \is_array($options)
318 | ? \array_values($options)
319 | : [$options];
320 | }
321 | if ($options) {
322 | $opt = $options;
323 | $opt[0] = static::style($opt[0], null, null, [Format::bold]);
324 | $optionsText = isset($opt[1])
325 | ? \implode(', ', $opt)
326 | : $opt[0];
327 | $question .= ' [' . $optionsText . ']';
328 | }
329 | $question .= ': ';
330 | \fwrite(\STDOUT, $question);
331 | $answer = static::getInput();
332 | if ($answer === '' && isset($options[0])) {
333 | $answer = $options[0];
334 | }
335 | return $answer;
336 | }
337 |
338 | /**
339 | * Prompt a question with secret answer.
340 | *
341 | * @param string $question The question to prompt
342 | *
343 | * @see https://dev.to/mykeels/reading-passwords-from-stdin-in-php-1np9
344 | *
345 | * @return string The secret answer
346 | */
347 | public static function secret(string $question) : string
348 | {
349 | $question .= ': ';
350 | \fwrite(\STDOUT, $question);
351 | \exec('stty -echo');
352 | $secret = \trim((string) \fgets(\STDIN));
353 | \exec('stty echo');
354 | return $secret;
355 | }
356 |
357 | /**
358 | * Creates a well formatted table.
359 | *
360 | * @param array> $tbody Table body rows
361 | * @param array $thead Table head fields
362 | */
363 | public static function table(array $tbody, array $thead = []) : void
364 | {
365 | // All the rows in the table will be here until the end
366 | $tableRows = [];
367 | // We need only indexes and not keys
368 | if (!empty($thead)) {
369 | $tableRows[] = \array_values($thead);
370 | }
371 | foreach ($tbody as $tr) {
372 | // cast tr to array if is not - (objects...)
373 | $tableRows[] = \array_values((array) $tr);
374 | }
375 | // Yes, it really is necessary to know this count
376 | $totalRows = \count($tableRows);
377 | // Store all columns lengths
378 | // $allColsLengths[row][column] = length
379 | $allColsLengths = [];
380 | // Store maximum lengths by column
381 | // $maxColsLengths[column] = length
382 | $maxColsLengths = [];
383 | // Read row by row and define the longest columns
384 | for ($row = 0; $row < $totalRows; $row++) {
385 | $column = 0; // Current column index
386 | foreach ($tableRows[$row] as $col) {
387 | // Sets the size of this column in the current row
388 | $allColsLengths[$row][$column] = static::strlen((string) $col);
389 | // If the current column does not have a value among the larger ones
390 | // or the value of this is greater than the existing one
391 | // then, now, this assumes the maximum length
392 | if (!isset($maxColsLengths[$column])
393 | || $allColsLengths[$row][$column] > $maxColsLengths[$column]) {
394 | $maxColsLengths[$column] = $allColsLengths[$row][$column];
395 | }
396 | // We can go check the size of the next column...
397 | $column++;
398 | }
399 | }
400 | // Read row by row and add spaces at the end of the columns
401 | // to match the exact column length
402 | for ($row = 0; $row < $totalRows; $row++) {
403 | $column = 0;
404 | foreach ($tableRows[$row] as $col => $value) {
405 | $diff = $maxColsLengths[$column] - $allColsLengths[$row][$col];
406 | if ($diff) {
407 | $tableRows[$row][$column] .= \str_repeat(' ', $diff);
408 | }
409 | $column++;
410 | }
411 | }
412 | $table = $line = '';
413 | // Joins columns and append the well formatted rows to the table
414 | foreach ($tableRows as $row => $value) {
415 | // Set the table border-top
416 | if ($row === 0) {
417 | $line = '+';
418 | foreach (\array_keys($value) as $col) {
419 | $line .= \str_repeat('-', $maxColsLengths[$col] + 2) . '+';
420 | }
421 | $table .= $line . \PHP_EOL;
422 | }
423 | // Set the vertical borders
424 | $table .= '| ' . \implode(' | ', $value) . ' |' . \PHP_EOL;
425 | // Set the thead and table borders-bottom
426 | if (($row === 0 && !empty($thead)) || $row + 1 === $totalRows) {
427 | $table .= $line . \PHP_EOL;
428 | }
429 | }
430 | \fwrite(\STDOUT, $table);
431 | }
432 | }
433 |
--------------------------------------------------------------------------------
/src/Command.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI;
11 |
12 | use JetBrains\PhpStorm\Pure;
13 |
14 | /**
15 | * Class Command.
16 | *
17 | * @package cli
18 | */
19 | abstract class Command
20 | {
21 | /**
22 | * Console instance of the current command.
23 | */
24 | protected Console $console;
25 | /**
26 | * Command name.
27 | */
28 | protected string $name;
29 | /**
30 | * Command group.
31 | */
32 | protected string $group;
33 | /**
34 | * Command description.
35 | */
36 | protected string $description;
37 | /**
38 | * Command usage.
39 | */
40 | protected string $usage = 'command [options] -- [arguments]';
41 | /**
42 | * Command options.
43 | *
44 | * @var array
45 | */
46 | protected array $options = [];
47 | /**
48 | * Tells if command is active.
49 | */
50 | protected bool $active = true;
51 |
52 | /**
53 | * Command constructor.
54 | *
55 | * @param Console|null $console
56 | */
57 | public function __construct(?Console $console = null)
58 | {
59 | if ($console) {
60 | $this->console = $console;
61 | }
62 | }
63 |
64 | /**
65 | * Run the command.
66 | */
67 | abstract public function run() : void;
68 |
69 | /**
70 | * Get console instance.
71 | *
72 | * @return Console
73 | */
74 | public function getConsole() : Console
75 | {
76 | return $this->console;
77 | }
78 |
79 | /**
80 | * Set console instance.
81 | *
82 | * @param Console $console
83 | *
84 | * @return static
85 | */
86 | public function setConsole(Console $console) : static
87 | {
88 | $this->console = $console;
89 | return $this;
90 | }
91 |
92 | /**
93 | * Get command name.
94 | *
95 | * @return string
96 | */
97 | public function getName() : string
98 | {
99 | if (isset($this->name)) {
100 | return $this->name;
101 | }
102 | $name = static::class;
103 | $pos = \strrpos($name, '\\');
104 | if ($pos !== false) {
105 | $name = \substr($name, $pos + 1);
106 | }
107 | if (\str_ends_with($name, 'Command')) {
108 | $name = \substr($name, 0, -7);
109 | }
110 | $name = \strtolower($name);
111 | return $this->name = $name;
112 | }
113 |
114 | /**
115 | * Set command name.
116 | *
117 | * @param string $name
118 | *
119 | * @return static
120 | */
121 | public function setName(string $name) : static
122 | {
123 | $this->name = $name;
124 | return $this;
125 | }
126 |
127 | /**
128 | * Get command group.
129 | *
130 | * @return string|null
131 | */
132 | public function getGroup() : ?string
133 | {
134 | return $this->group ?? null;
135 | }
136 |
137 | /**
138 | * Set command group.
139 | *
140 | * @param string $group
141 | *
142 | * @return static
143 | */
144 | public function setGroup(string $group) : static
145 | {
146 | $this->group = $group;
147 | return $this;
148 | }
149 |
150 | /**
151 | * Get command description.
152 | *
153 | * @return string
154 | */
155 | public function getDescription() : string
156 | {
157 | if (isset($this->description)) {
158 | return $this->description;
159 | }
160 | $description = $this->console->getLanguage()->render('cli', 'noDescription');
161 | return $this->description = $description;
162 | }
163 |
164 | /**
165 | * Set command description.
166 | *
167 | * @param string $description
168 | *
169 | * @return static
170 | */
171 | public function setDescription(string $description) : static
172 | {
173 | $this->description = $description;
174 | return $this;
175 | }
176 |
177 | /**
178 | * Get command usage.
179 | *
180 | * @return string
181 | */
182 | #[Pure]
183 | public function getUsage() : string
184 | {
185 | return $this->usage;
186 | }
187 |
188 | /**
189 | * Set command usage.
190 | *
191 | * @param string $usage
192 | *
193 | * @return static
194 | */
195 | public function setUsage(string $usage) : static
196 | {
197 | $this->usage = $usage;
198 | return $this;
199 | }
200 |
201 | /**
202 | * Get command options.
203 | *
204 | * @return array
205 | */
206 | #[Pure]
207 | public function getOptions() : array
208 | {
209 | return $this->options;
210 | }
211 |
212 | /**
213 | * Set command options.
214 | *
215 | * @param array $options
216 | *
217 | * @return static
218 | */
219 | public function setOptions(array $options) : static
220 | {
221 | $this->options = $options;
222 | return $this;
223 | }
224 |
225 | /**
226 | * Tells if the command is active.
227 | *
228 | * @return bool
229 | */
230 | #[Pure]
231 | public function isActive() : bool
232 | {
233 | return $this->active;
234 | }
235 |
236 | /**
237 | * Activate the command.
238 | *
239 | * @return static
240 | */
241 | public function activate() : static
242 | {
243 | $this->active = true;
244 | return $this;
245 | }
246 |
247 | /**
248 | * Deactivate the command.
249 | *
250 | * @return static
251 | */
252 | public function deactivate() : static
253 | {
254 | $this->active = false;
255 | return $this;
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/src/Commands/About.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Commands;
11 |
12 | use Framework\CLI\CLI;
13 | use Framework\CLI\Command;
14 | use Framework\CLI\Styles\ForegroundColor;
15 |
16 | /**
17 | * Class About.
18 | *
19 | * @package cli
20 | */
21 | class About extends Command
22 | {
23 | public function run() : void
24 | {
25 | $lang = $this->console->getLanguage();
26 | CLI::write($lang->render('cli', 'about.line1'), ForegroundColor::brightGreen);
27 | CLI::write($lang->render('cli', 'about.line2'));
28 | CLI::write($lang->render('cli', 'about.line3'));
29 | CLI::write($lang->render('cli', 'about.line4'));
30 | CLI::write($lang->render('cli', 'about.line5'));
31 | }
32 |
33 | public function getDescription() : string
34 | {
35 | return $this->console->getLanguage()->render('cli', 'about.description');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Commands/Help.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Commands;
11 |
12 | use Framework\CLI\CLI;
13 | use Framework\CLI\Command;
14 | use Framework\CLI\Styles\ForegroundColor;
15 |
16 | /**
17 | * Class Help.
18 | *
19 | * @package cli
20 | */
21 | class Help extends Command
22 | {
23 | protected string $name = 'help';
24 | protected string $usage = 'help [command_name]';
25 |
26 | public function run() : void
27 | {
28 | $command = $this->console->getArgument(0) ?? 'help';
29 | $this->showCommand($command);
30 | }
31 |
32 | protected function showCommand(string $commandName) : void
33 | {
34 | $command = $this->console->getCommand($commandName);
35 | if ($command === null) {
36 | CLI::error(
37 | $this->console->getLanguage()->render('cli', 'commandNotFound', [$commandName]),
38 | \defined('TESTING') ? null : 1
39 | );
40 | return;
41 | }
42 | CLI::write(CLI::style(
43 | $this->console->getLanguage()->render('cli', 'command') . ': ',
44 | ForegroundColor::green
45 | ) . $command->getName());
46 | $value = $command->getGroup();
47 | if ($value !== null) {
48 | CLI::write(CLI::style(
49 | $this->console->getLanguage()->render('cli', 'group') . ': ',
50 | ForegroundColor::green
51 | ) . $value);
52 | }
53 | $value = $command->getDescription();
54 | if ($value !== '') {
55 | CLI::write(CLI::style(
56 | $this->console->getLanguage()->render('cli', 'description') . ': ',
57 | ForegroundColor::green
58 | ) . $value);
59 | }
60 | $value = $command->getUsage();
61 | if ($value !== '') {
62 | CLI::write(CLI::style(
63 | $this->console->getLanguage()->render('cli', 'usage') . ': ',
64 | ForegroundColor::green
65 | ) . $value);
66 | }
67 | $value = $command->getOptions();
68 | if ($value) {
69 | CLI::write(
70 | $this->console->getLanguage()->render('cli', 'options') . ': ',
71 | ForegroundColor::green
72 | );
73 | $newOptions = [];
74 | foreach ($value as $options => $description) {
75 | $options = $this->sortOptions($options);
76 | $newOptions[$options] = $description;
77 | }
78 | \ksort($newOptions);
79 | $lastKey = \array_key_last($newOptions);
80 | foreach ($newOptions as $option => $description) {
81 | CLI::write(' ' . $this->setColor($option));
82 | $description = \trim($description);
83 | if (!\str_ends_with($description, '.')) {
84 | $description .= '.';
85 | }
86 | CLI::write(' ' . $description);
87 | if ($option !== $lastKey) {
88 | CLI::newLine();
89 | }
90 | }
91 | }
92 | }
93 |
94 | protected function sortOptions(string $text) : string
95 | {
96 | $text = \trim(\preg_replace('/\s+/', '', $text));
97 | $text = \explode(',', $text);
98 | \sort($text);
99 | return \implode(',', $text);
100 | }
101 |
102 | protected function setColor(string $text) : string
103 | {
104 | $text = \explode(',', $text);
105 | foreach ($text as &$item) {
106 | $item = CLI::style($item, ForegroundColor::yellow);
107 | }
108 | return \implode(', ', $text);
109 | }
110 |
111 | public function getDescription() : string
112 | {
113 | return $this->console->getLanguage()->render('cli', 'help.description');
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Commands/Index.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Commands;
11 |
12 | use Framework\CLI\CLI;
13 | use Framework\CLI\Command;
14 | use Framework\CLI\Styles\ForegroundColor;
15 |
16 | /**
17 | * Class Index.
18 | *
19 | * @package cli
20 | */
21 | class Index extends Command
22 | {
23 | protected string $name = 'index';
24 | protected string $description = 'Show commands list';
25 | protected string $usage = 'index';
26 | protected array $options = [
27 | '-g' => 'Shows greeting.',
28 | ];
29 |
30 | public function run() : void
31 | {
32 | $this->showHeader();
33 | $this->showDate();
34 | if ($this->console->getOption('g')) {
35 | $this->greet();
36 | }
37 | $this->listCommands();
38 | }
39 |
40 | public function getDescription() : string
41 | {
42 | return $this->console->getLanguage()->render('cli', 'index.description');
43 | }
44 |
45 | public function getOptions() : array
46 | {
47 | return [
48 | '-g' => $this->console->getLanguage()->render('cli', 'index.option.greet'),
49 | ];
50 | }
51 |
52 | protected function listCommands() : void
53 | {
54 | $groupDefault = [];
55 | $groups = [];
56 | foreach ($this->console->getCommands() as $name => $command) {
57 | $group = $command->getGroup();
58 | if ($group === null) {
59 | $groupDefault[$name] = $command;
60 | continue;
61 | }
62 | $groups[$group][$name] = $command;
63 | }
64 | CLI::write(
65 | $this->console->getLanguage()->render('cli', 'availableCommands') . ':',
66 | ForegroundColor::yellow
67 | );
68 | [$width, $lengths] = $this->getWidthAndLengths($groupDefault);
69 | foreach ($groupDefault as $name => $command) {
70 | CLI::write(
71 | ' ' . CLI::style($name, ForegroundColor::green) . ' '
72 | // @phpstan-ignore-next-line
73 | . \str_repeat(' ', $width - $lengths[$name])
74 | . $this->editDescription($command->getDescription())
75 | );
76 | }
77 | \ksort($groups);
78 | foreach ($groups as $groupName => $commands) {
79 | CLI::newLine();
80 | CLI::write(' ' . $groupName . ':', ForegroundColor::brightYellow);
81 | [$width, $lengths] = $this->getWidthAndLengths($commands);
82 | foreach ($commands as $name => $command) {
83 | CLI::write(
84 | ' ' . CLI::style($name, ForegroundColor::green) . ' '
85 | // @phpstan-ignore-next-line
86 | . \str_repeat(' ', $width - $lengths[$name])
87 | . $this->editDescription($command->getDescription())
88 | );
89 | }
90 | }
91 | }
92 |
93 | protected function editDescription(string $description) : string
94 | {
95 | $description = \trim($description);
96 | if (!\str_ends_with($description, '.')) {
97 | $description .= '.';
98 | }
99 | return $description;
100 | }
101 |
102 | /**
103 | * @param array $commands
104 | *
105 | * @return array|int>
106 | */
107 | protected function getWidthAndLengths(array $commands) : array
108 | {
109 | $width = 0;
110 | $lengths = [];
111 | foreach (\array_keys($commands) as $name) {
112 | $lengths[$name] = \mb_strlen($name);
113 | if ($lengths[$name] > $width) {
114 | $width = $lengths[$name];
115 | }
116 | }
117 | return [$width, $lengths];
118 | }
119 |
120 | protected function showHeader() : void
121 | {
122 | $text = <<<'EOL'
123 | _ _ ____ _ ___
124 | / \ _ __ | |_ _ ___ / ___| | |_ _|
125 | / _ \ | '_ \| | | | / __| | | | | | |
126 | / ___ \| |_) | | |_| \__ \ | |___| |___ | |
127 | /_/ \_\ .__/|_|\__,_|___/ \____|_____|___|
128 | |_|
129 |
130 | EOL;
131 | CLI::write($text, ForegroundColor::green);
132 | }
133 |
134 | protected function showDate() : void
135 | {
136 | $text = $this->console->getLanguage()->date(\time(), 'full');
137 | $text = \ucfirst($text) . ' - '
138 | . \date('H:i:s') . ' - '
139 | . \date_default_timezone_get() . \PHP_EOL;
140 | CLI::write($text);
141 | }
142 |
143 | protected function greet() : void
144 | {
145 | $hour = \date('H');
146 | $timing = 'evening';
147 | if ($hour > 4 && $hour < 12) {
148 | $timing = 'morning';
149 | } elseif ($hour > 4 && $hour < 18) {
150 | $timing = 'afternoon';
151 | }
152 | $greeting = $this->console->getLanguage()
153 | ->render('cli', 'greet.' . $timing, [$this->getUser()]);
154 | CLI::write($greeting);
155 | CLI::newLine();
156 | }
157 |
158 | protected function getUser() : string
159 | {
160 | $username = \posix_getlogin();
161 | if ($username === false) {
162 | return $this->console->getLanguage()->render('cli', 'friend');
163 | }
164 | $info = \posix_getpwnam($username);
165 | if (!$info) {
166 | return $username;
167 | }
168 | $gecos = $info['gecos'] ?? '';
169 | if (!$gecos) {
170 | return $username;
171 | }
172 | $length = \strpos($gecos, ',') ?: \strlen($gecos);
173 | return \substr($gecos, 0, $length);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/Console.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI;
11 |
12 | use Framework\CLI\Commands\About;
13 | use Framework\CLI\Commands\Help;
14 | use Framework\CLI\Commands\Index;
15 | use Framework\CLI\Styles\ForegroundColor;
16 | use Framework\Language\Language;
17 | use JetBrains\PhpStorm\Pure;
18 |
19 | /**
20 | * Class Console.
21 | *
22 | * @package cli
23 | */
24 | class Console
25 | {
26 | /**
27 | * List of commands.
28 | *
29 | * @var array The command name as key and the object as value
30 | */
31 | protected array $commands = [];
32 | /**
33 | * The current command name.
34 | */
35 | protected string $command = '';
36 | /**
37 | * Input options.
38 | *
39 | * @var array The option value as string or TRUE if it
40 | * was passed without a value
41 | */
42 | protected array $options = [];
43 | /**
44 | * Input arguments.
45 | *
46 | * @var array
47 | */
48 | protected array $arguments = [];
49 | /**
50 | * The Language instance.
51 | */
52 | protected Language $language;
53 |
54 | /**
55 | * Console constructor.
56 | *
57 | * @param Language|null $language
58 | */
59 | public function __construct(?Language $language = null)
60 | {
61 | if ($language) {
62 | $this->setLanguage($language);
63 | }
64 | global $argv;
65 | $this->prepare($argv ?? []);
66 | $this->setDefaultCommands();
67 | }
68 |
69 | protected function setDefaultCommands() : static
70 | {
71 | if ($this->getCommand('index') === null) {
72 | $this->addCommand(new Index($this));
73 | }
74 | if ($this->getCommand('help') === null) {
75 | $this->addCommand(new Help($this));
76 | }
77 | if ($this->getCommand('about') === null) {
78 | $this->addCommand(new About($this));
79 | }
80 | return $this;
81 | }
82 |
83 | /**
84 | * Get all CLI options.
85 | *
86 | * @return array
87 | */
88 | #[Pure]
89 | public function getOptions() : array
90 | {
91 | return $this->options;
92 | }
93 |
94 | /**
95 | * Get a specific option or null.
96 | *
97 | * @param string $option
98 | *
99 | * @return bool|string|null The option value as string, TRUE if it
100 | * was passed without a value or NULL if the option was not set
101 | */
102 | #[Pure]
103 | public function getOption(string $option) : bool | string | null
104 | {
105 | return $this->options[$option] ?? null;
106 | }
107 |
108 | /**
109 | * Get all arguments.
110 | *
111 | * @return array
112 | */
113 | #[Pure]
114 | public function getArguments() : array
115 | {
116 | return $this->arguments;
117 | }
118 |
119 | /**
120 | * Get a specific argument or null.
121 | *
122 | * @param int $position Argument position, starting from zero
123 | *
124 | * @return string|null The argument value or null if it was not set
125 | */
126 | #[Pure]
127 | public function getArgument(int $position) : ?string
128 | {
129 | return $this->arguments[$position] ?? null;
130 | }
131 |
132 | /**
133 | * Set the Language instance.
134 | *
135 | * @param Language|null $language
136 | *
137 | * @return static
138 | */
139 | public function setLanguage(?Language $language = null) : static
140 | {
141 | $this->language = $language ?? new Language();
142 | $this->language->addDirectory(__DIR__ . '/Languages');
143 | return $this;
144 | }
145 |
146 | /**
147 | * Get the Language instance.
148 | *
149 | * @return Language
150 | */
151 | public function getLanguage() : Language
152 | {
153 | if (!isset($this->language)) {
154 | $this->setLanguage();
155 | }
156 | return $this->language;
157 | }
158 |
159 | /**
160 | * Add a command to the console.
161 | *
162 | * @param Command|class-string $command A Command instance or the class FQN
163 | *
164 | * @return static
165 | */
166 | public function addCommand(Command | string $command) : static
167 | {
168 | if (\is_string($command)) {
169 | $command = new $command();
170 | }
171 | $command->setConsole($this);
172 | $this->commands[$command->getName()] = $command;
173 | return $this;
174 | }
175 |
176 | /**
177 | * Add many commands to the console.
178 | *
179 | * @param array> $commands A list of Command
180 | * instances or the classes FQN
181 | *
182 | * @return static
183 | */
184 | public function addCommands(array $commands) : static
185 | {
186 | foreach ($commands as $command) {
187 | $this->addCommand($command);
188 | }
189 | return $this;
190 | }
191 |
192 | /**
193 | * Get an active command.
194 | *
195 | * @param string $name Command name
196 | *
197 | * @return Command|null The Command on success or null if not found
198 | */
199 | public function getCommand(string $name) : ?Command
200 | {
201 | if (isset($this->commands[$name]) && $this->commands[$name]->isActive()) {
202 | return $this->commands[$name];
203 | }
204 | return null;
205 | }
206 |
207 | /**
208 | * Get a list of active commands.
209 | *
210 | * @return array
211 | */
212 | public function getCommands() : array
213 | {
214 | $commands = $this->commands;
215 | foreach ($commands as $name => $command) {
216 | if (!$command->isActive()) {
217 | unset($commands[$name]);
218 | }
219 | }
220 | \ksort($commands);
221 | return $commands;
222 | }
223 |
224 | /**
225 | * Remove a command.
226 | *
227 | * @param string $name Command name
228 | *
229 | * @return static
230 | */
231 | public function removeCommand(string $name) : static
232 | {
233 | unset($this->commands[$name]);
234 | return $this;
235 | }
236 |
237 | /**
238 | * Remove commands.
239 | *
240 | * @param array $names Command names
241 | *
242 | * @return static
243 | */
244 | public function removeCommands(array $names) : static
245 | {
246 | foreach ($names as $name) {
247 | $this->removeCommand($name);
248 | }
249 | return $this;
250 | }
251 |
252 | /**
253 | * Tells if it has a command.
254 | *
255 | * @param string $name Command name
256 | *
257 | * @return bool
258 | */
259 | public function hasCommand(string $name) : bool
260 | {
261 | return $this->getCommand($name) !== null;
262 | }
263 |
264 | /**
265 | * Run the Console.
266 | */
267 | public function run() : void
268 | {
269 | if ($this->command === '') {
270 | $this->command = 'index';
271 | }
272 | $command = $this->getCommand($this->command);
273 | if ($command === null) {
274 | CLI::error(CLI::style(
275 | $this->getLanguage()->render('cli', 'commandNotFound', [$this->command]),
276 | ForegroundColor::brightRed
277 | ), \defined('TESTING') ? null : 1);
278 | return;
279 | }
280 | $command->run();
281 | }
282 |
283 | public function exec(string $command) : void
284 | {
285 | $argumentValues = static::commandToArgs($command);
286 | \array_unshift($argumentValues, 'removed');
287 | $this->prepare($argumentValues);
288 | $this->run();
289 | }
290 |
291 | protected function reset() : void
292 | {
293 | $this->command = '';
294 | $this->options = [];
295 | $this->arguments = [];
296 | }
297 |
298 | /**
299 | * Prepare information of the command line.
300 | *
301 | * [options] [arguments] [options]
302 | * [options] -- [arguments]
303 | * [command]
304 | * [command] [options] [arguments] [options]
305 | * [command] [options] -- [arguments]
306 | * Short option: -l, -la === l = true, a = true
307 | * Long option: --list, --all=vertical === list = true, all = vertical
308 | * Only Long Options receive values:
309 | * --foo=bar or --f=bar - "foo" and "f" are bar
310 | * -foo=bar or -f=bar - all characters are true (f, o, =, b, a, r)
311 | * After -- all values are arguments, also if is prefixed with -
312 | * Without --, arguments and options can be mixed: -ls foo -x abc --a=e.
313 | *
314 | * @param array $argumentValues
315 | */
316 | protected function prepare(array $argumentValues) : void
317 | {
318 | $this->reset();
319 | unset($argumentValues[0]);
320 | if (isset($argumentValues[1]) && $argumentValues[1][0] !== '-') {
321 | $this->command = $argumentValues[1];
322 | unset($argumentValues[1]);
323 | }
324 | $endOptions = false;
325 | foreach ($argumentValues as $value) {
326 | if ($endOptions === false && $value === '--') {
327 | $endOptions = true;
328 | continue;
329 | }
330 | if ($endOptions === false && $value[0] === '-') {
331 | if (isset($value[1]) && $value[1] === '-') {
332 | $option = \substr($value, 2);
333 | if (\str_contains($option, '=')) {
334 | [$option, $value] = \explode('=', $option, 2);
335 | $this->options[$option] = $value;
336 | continue;
337 | }
338 | $this->options[$option] = true;
339 | continue;
340 | }
341 | foreach (\str_split(\substr($value, 1)) as $item) {
342 | $this->options[$item] = true;
343 | }
344 | continue;
345 | }
346 | //$endOptions = true;
347 | $this->arguments[] = $value;
348 | }
349 | }
350 |
351 | /**
352 | * @param string $command
353 | *
354 | * @see https://someguyjeremy.com/2017/07/adventures-in-parsing-strings-to-argv-in-php.html
355 | *
356 | * @return array
357 | */
358 | #[Pure]
359 | public static function commandToArgs(string $command) : array
360 | {
361 | $charCount = \strlen($command);
362 | $argv = [];
363 | $arg = '';
364 | $inDQuote = false;
365 | $inSQuote = false;
366 | for ($i = 0; $i < $charCount; $i++) {
367 | $char = $command[$i];
368 | if ($char === ' ' && !$inDQuote && !$inSQuote) {
369 | if ($arg !== '') {
370 | $argv[] = $arg;
371 | }
372 | $arg = '';
373 | continue;
374 | }
375 | if ($inSQuote && $char === "'") {
376 | $inSQuote = false;
377 | continue;
378 | }
379 | if ($inDQuote && $char === '"') {
380 | $inDQuote = false;
381 | continue;
382 | }
383 | if ($char === '"' && !$inSQuote) {
384 | $inDQuote = true;
385 | continue;
386 | }
387 | if ($char === "'" && !$inDQuote) {
388 | $inSQuote = true;
389 | continue;
390 | }
391 | $arg .= $char;
392 | }
393 | $argv[] = $arg;
394 | return $argv;
395 | }
396 | }
397 |
--------------------------------------------------------------------------------
/src/Languages/en/cli.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | return [
11 | 'about.description' => 'Shows information about this command-line interface.',
12 | 'about.line1' => 'About',
13 | 'about.line2' => 'This interface is built on Aplus Framework CLI Library.',
14 | 'about.line3' => 'Aplus Framework CLI Library is Open Source Software (OSS).',
15 | 'about.line4' => 'Visit our website to know more: https://aplus-framework.com',
16 | 'about.line5' => 'Thanks for using Aplus Framework!',
17 | 'availableCommands' => 'Available Commands',
18 | 'command' => 'Command',
19 | 'commandNotFound' => 'Command not found: "{0}"',
20 | 'commands' => 'Commands',
21 | 'description' => 'Description',
22 | 'friend' => 'friend',
23 | 'greet.afternoon' => 'Good afternoon, {0}!',
24 | 'greet.evening' => 'Good evening, {0}!',
25 | 'greet.morning' => 'Good morning, {0}!',
26 | 'group' => 'Group',
27 | 'help.description' => 'Shows command usage help.',
28 | 'index.description' => 'Shows commands list.',
29 | 'index.option.greet' => 'Shows greeting.',
30 | 'noDescription' => 'This command does not provide a description.',
31 | 'options' => 'Options',
32 | 'usage' => 'Usage',
33 | ];
34 |
--------------------------------------------------------------------------------
/src/Languages/es/cli.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | return [
11 | 'about.description' => 'Muestra información sobre esta interfaz de línea de comandos.',
12 | 'about.line1' => 'Sobre',
13 | 'about.line2' => 'Esta interfaz se basa en Aplus Framework CLI Library.',
14 | 'about.line3' => 'Aplus Framework CLI Library es Software de Código Abierto (OSS).',
15 | 'about.line4' => 'Visite nuestro website para obtener más información: https://aplus-framework.com',
16 | 'about.line5' => '¡Gracias por usar Aplus Framework!',
17 | 'availableCommands' => 'Comandos Disponibles',
18 | 'command' => 'Comando',
19 | 'commandNotFound' => 'Comando no encontrado: "{0}"',
20 | 'commands' => 'Comandos',
21 | 'description' => 'Descripción',
22 | 'friend' => 'amigo',
23 | 'greet.afternoon' => '¡Buenas tardes, {0}!',
24 | 'greet.evening' => '¡Buenas noches, {0}!',
25 | 'greet.morning' => '¡Buenos días, {0}!',
26 | 'group' => 'Grupo',
27 | 'help.description' => 'Muestra ayuda de uso de comando.',
28 | 'index.description' => 'Muestra la lista de comandos.',
29 | 'index.option.greet' => 'Muestra saludo.',
30 | 'noDescription' => 'Este comando no proporciona una descripción.',
31 | 'options' => 'Opciones',
32 | 'usage' => 'Uso',
33 | ];
34 |
--------------------------------------------------------------------------------
/src/Languages/pt-br/cli.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | return [
11 | 'about.description' => 'Mostra informações sobre essa interface de linha de comandos.',
12 | 'about.line1' => 'Sobre',
13 | 'about.line2' => 'Essa interface é construída sobre Aplus Framework CLI Library.',
14 | 'about.line3' => 'Aplus Framework CLI Library é Software de Código Aberto (OSS).',
15 | 'about.line4' => 'Visite nosso website para saber mais: https://aplus-framework.com',
16 | 'about.line5' => 'Obrigado por usar o Aplus Framework!',
17 | 'availableCommands' => 'Comandos Disponíveis',
18 | 'command' => 'Comando',
19 | 'commandNotFound' => 'Comando não encontrado: "{0}"',
20 | 'commands' => 'Comandos',
21 | 'description' => 'Descrição',
22 | 'friend' => 'amigo',
23 | 'greet.afternoon' => 'Boa tarde, {0}!',
24 | 'greet.evening' => 'Boa noite, {0}!',
25 | 'greet.morning' => 'Bom dia, {0}!',
26 | 'group' => 'Grupo',
27 | 'help.description' => 'Mostra a ajuda de uso do comando.',
28 | 'index.description' => 'Mostra a lista de comandos.',
29 | 'index.option.greet' => 'Mostra saudação.',
30 | 'noDescription' => 'Este comando não fornece uma descrição.',
31 | 'options' => 'Opções',
32 | 'usage' => 'Uso',
33 | ];
34 |
--------------------------------------------------------------------------------
/src/Streams/FilterStream.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Streams;
11 |
12 | use JetBrains\PhpStorm\Pure;
13 |
14 | /**
15 | * Trait FilterStream.
16 | *
17 | * @package cli
18 | *
19 | * @since 2.3.1
20 | */
21 | trait FilterStream
22 | {
23 | protected static string $contents = '';
24 |
25 | /**
26 | * @param resource $in
27 | * @param resource $out
28 | * @param int $consumed
29 | * @param bool $closing
30 | *
31 | * @see https://php.net/manual/en/php-user-filter.filter.php
32 | *
33 | * @return int
34 | */
35 | public function filter($in, $out, &$consumed, $closing) : int
36 | {
37 | while ($bucket = \stream_bucket_make_writeable($in)) {
38 | static::$contents .= $bucket->data;
39 | $consumed += $bucket->datalen;
40 | \stream_bucket_append($out, $bucket);
41 | }
42 | return \PSFS_FEED_ME;
43 | }
44 |
45 | #[Pure]
46 | public static function getContents() : string
47 | {
48 | return static::$contents;
49 | }
50 |
51 | public static function reset() : void
52 | {
53 | static::$contents = '';
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Streams/Stderr.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Streams;
11 |
12 | /**
13 | * Class Stderr.
14 | *
15 | * @package cli
16 | */
17 | class Stderr extends \php_user_filter
18 | {
19 | use FilterStream;
20 |
21 | public static function init() : void
22 | {
23 | \stream_filter_register(static::class, static::class);
24 | \stream_filter_append(\STDERR, static::class);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Streams/Stdout.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Streams;
11 |
12 | /**
13 | * Class Stdout.
14 | *
15 | * @package cli
16 | */
17 | class Stdout extends \php_user_filter
18 | {
19 | use FilterStream;
20 |
21 | public static function init() : void
22 | {
23 | \stream_filter_register(static::class, static::class);
24 | \stream_filter_append(\STDOUT, static::class);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Styles/BackgroundColor.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Styles;
11 |
12 | /**
13 | * Enum BackgroundColor.
14 | *
15 | * @package cli
16 | */
17 | enum BackgroundColor : string
18 | {
19 | case black = 'black';
20 | case red = 'red';
21 | case green = 'green';
22 | case yellow = 'yellow';
23 | case blue = 'blue';
24 | case magenta = 'magenta';
25 | case cyan = 'cyan';
26 | case white = 'white';
27 | case brightBlack = 'bright_black';
28 | case brightRed = 'bright_red';
29 | case brightGreen = 'bright_green';
30 | case brightYellow = 'bright_yellow';
31 | case brightBlue = 'bright_blue';
32 | case brightMagenta = 'bright_magenta';
33 | case brightCyan = 'bright_cyan';
34 | case brightWhite = 'bright_white';
35 |
36 | public function getCode() : string
37 | {
38 | return match ($this->value) {
39 | 'black' => "\033[40m",
40 | 'red' => "\033[41m",
41 | 'green' => "\033[42m",
42 | 'yellow' => "\033[43m",
43 | 'blue' => "\033[44m",
44 | 'magenta' => "\033[45m",
45 | 'cyan' => "\033[46m",
46 | 'white' => "\033[47m",
47 | 'bright_black' => "\033[100m",
48 | 'bright_red' => "\033[101m",
49 | 'bright_green' => "\033[102m",
50 | 'bright_yellow' => "\033[103m",
51 | 'bright_blue' => "\033[104m",
52 | 'bright_magenta' => "\033[105m",
53 | 'bright_cyan' => "\033[106m",
54 | 'bright_white' => "\033[107m",
55 | };
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Styles/ForegroundColor.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Styles;
11 |
12 | /**
13 | * Enum ForegroundColor.
14 | *
15 | * @package cli
16 | */
17 | enum ForegroundColor : string
18 | {
19 | case black = 'black';
20 | case red = 'red';
21 | case green = 'green';
22 | case yellow = 'yellow';
23 | case blue = 'blue';
24 | case magenta = 'magenta';
25 | case cyan = 'cyan';
26 | case white = 'white';
27 | case brightBlack = 'bright_black';
28 | case brightRed = 'bright_red';
29 | case brightGreen = 'bright_green';
30 | case brightYellow = 'bright_yellow';
31 | case brightBlue = 'bright_blue';
32 | case brightMagenta = 'bright_magenta';
33 | case brightCyan = 'bright_cyan';
34 | case brightWhite = 'bright_white';
35 |
36 | public function getCode() : string
37 | {
38 | return match ($this->value) {
39 | 'black' => "\033[0;30m",
40 | 'red' => "\033[0;31m",
41 | 'green' => "\033[0;32m",
42 | 'yellow' => "\033[0;33m",
43 | 'blue' => "\033[0;34m",
44 | 'magenta' => "\033[0;35m",
45 | 'cyan' => "\033[0;36m",
46 | 'white' => "\033[0;37m",
47 | 'bright_black' => "\033[0;90m",
48 | 'bright_red' => "\033[0;91m",
49 | 'bright_green' => "\033[0;92m",
50 | 'bright_yellow' => "\033[0;93m",
51 | 'bright_blue' => "\033[0;94m",
52 | 'bright_magenta' => "\033[0;95m",
53 | 'bright_cyan' => "\033[0;96m",
54 | 'bright_white' => "\033[0;97m",
55 | };
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Styles/Format.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\CLI\Styles;
11 |
12 | /**
13 | * Enum Format.
14 | *
15 | * @package cli
16 | */
17 | enum Format : string
18 | {
19 | case bold = 'bold';
20 | case faint = 'faint';
21 | case italic = 'italic';
22 | case underline = 'underline';
23 | case slowBlink = 'slow_blink';
24 | case rapidBlink = 'rapid_blink';
25 | case reverseVideo = 'reverse_video';
26 | case conceal = 'conceal';
27 | case crossedOut = 'crossed_out';
28 | case primaryFont = 'primary_font';
29 | case fraktur = 'fraktur';
30 | case doublyUnderline = 'doubly_underline';
31 | case encircled = 'encircled';
32 |
33 | public function getCode() : string
34 | {
35 | return match ($this->value) {
36 | 'bold' => "\033[1m",
37 | 'faint' => "\033[2m",
38 | 'italic' => "\033[3m",
39 | 'underline' => "\033[4m",
40 | 'slow_blink' => "\033[5m",
41 | 'rapid_blink' => "\033[6m",
42 | 'reverse_video' => "\033[7m",
43 | 'conceal' => "\033[8m",
44 | 'crossed_out' => "\033[9m",
45 | 'primary_font' => "\033[10m",
46 | 'fraktur' => "\033[20m",
47 | 'doubly_underline' => "\033[21m",
48 | 'encircled' => "\033[52m",
49 | };
50 | }
51 | }
52 |
--------------------------------------------------------------------------------