Skip to content

Commit 4187376

Browse files
committed
feat: support start an interactive shell for run application
1 parent 9d9aeca commit 4187376

8 files changed

+160
-17
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ Provide console parameter parsing, command run, color style output, user informa
2525
- Commonly used special format information display (`section`, `panel`, `padding`, `helpPanel`, `table`, `tree`, `title`, `list`, `multiList`)
2626
- Rich dynamic information display (`pending/loading`, `pointing`, `spinner`, `counterTxt`, `dynamicText`, `progressTxt`, `progressBar`)
2727
- Common user information interaction support (`select`, `multiSelect`, `confirm`, `ask/question`, `askPassword/askHiddenInput`)
28-
- Support for predefined parameter definitions like `symfony/console` (giving parameter values ​​by position, recommended when strict parameter restrictions are required)
28+
- Support for predefined parameter definitions like `symfony/console` (giving parameter values by position, recommended when strict parameter restrictions are required)
2929
- The color output is `windows` `linux` `mac` compatible. Environments that do not support color will automatically remove the relevant CODE.
3030
- Quickly generate auto-completion scripts for the current application in the `bash/zsh` environment
31+
- NEW: Support start an interactive shell for run application
3132

3233
### Built-in tools
3334

src/AbstractApplication.php

+62-3
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
use Inhere\Console\Concern\ApplicationHelpTrait;
2121
use Inhere\Console\Concern\InputOutputAwareTrait;
2222
use Inhere\Console\Concern\SimpleEventAwareTrait;
23+
use Inhere\Console\Util\Interact;
2324
use InvalidArgumentException;
2425
use Throwable;
2526
use Toolkit\Cli\Style;
27+
use Toolkit\Cli\Util\LineParser;
2628
use Toolkit\Stdlib\Helper\PhpHelper;
29+
use Toolkit\Sys\Proc\ProcessUtil;
30+
use Toolkit\Sys\Proc\Signal;
2731
use function array_keys;
2832
use function array_merge;
2933
use function error_get_last;
@@ -59,7 +63,8 @@ abstract class AbstractApplication implements ApplicationInterface
5963

6064
/** @var array */
6165
protected static $globalOptions = [
62-
'--debug' => 'Setting the application runtime debug level(0 - 4)',
66+
'--debug' => 'Setting the runtime log debug level(quiet 0 - 5 crazy)',
67+
'--ishell' => 'Run application an interactive shell environment',
6368
'--profile' => 'Display timing and memory usage information',
6469
'--no-color' => 'Disable color/ANSI for message output',
6570
'-h, --help' => 'Display this help message',
@@ -356,16 +361,21 @@ public function handleException($e): void
356361
protected function filterSpecialCommand(string $command): bool
357362
{
358363
if (!$command) {
359-
if ($this->input->getSameOpt(['V', 'version'])) {
364+
if ($this->input->getSameBoolOpt(GlobalOption::VERSION_OPTS)) {
360365
$this->showVersionInfo();
361366
return true;
362367
}
363368

364-
if ($this->input->getSameOpt(['h', 'help'])) {
369+
if ($this->input->getSameBoolOpt(GlobalOption::HELP_OPTS)) {
365370
$this->showHelpInfo();
366371
return true;
367372
}
368373

374+
if ($this->input->getBoolOpt(GlobalOption::ISHELL)) {
375+
$this->startInteractiveShell();
376+
return true;
377+
}
378+
369379
// default run list command
370380
// $command = $this->defaultCommand ? 'list';
371381
$command = 'list';
@@ -390,6 +400,55 @@ protected function filterSpecialCommand(string $command): bool
390400
return true;
391401
}
392402

403+
/**********************************************************
404+
* start interactive shell
405+
**********************************************************/
406+
407+
/**
408+
* start an interactive shell run
409+
*/
410+
protected function startInteractiveShell(): void
411+
{
412+
$in = $this->input;
413+
$out = $this->output;
414+
415+
$out->colored("Will start interactive shell for run application");
416+
417+
if (!($hasPcntl = ProcessUtil::hasPcntl())) {
418+
$this->debugf('php is not enable "pcntl" extension, cannot listen CTRL+C signal');
419+
}
420+
421+
if ($hasPcntl) {
422+
// register signal.
423+
ProcessUtil::installSignal(Signal::INT, static function () use ($out) {
424+
$out->colored("\nQuit by CTRL+C");
425+
exit(0);
426+
});
427+
}
428+
429+
while (true) {
430+
$line = Interact::readln('<comment>CMD ></comment> ');
431+
if ($line === 'exit' || $line === 'quit') {
432+
break;
433+
}
434+
435+
if ($hasPcntl) {
436+
// listen signal.
437+
ProcessUtil::dispatchSignal();
438+
}
439+
440+
$args = LineParser::parseIt($line);
441+
442+
// reload and parse args
443+
$in->parse($args);
444+
445+
// \vdump($in);
446+
$this->run(false);
447+
}
448+
449+
$out->colored("\nQuit. ByeBye!");
450+
}
451+
393452
/**
394453
* @param string $name
395454
* @param string|array $aliases

src/Concern/ApplicationHelpTrait.php

+21-9
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,29 @@ public function showHelpInfo(string $command = ''): void
9999
$delimiter = $this->delimiter;
100100
$binName = $in->getScriptName();
101101

102+
// built in options
103+
$globalOptions = FormatUtil::alignOptions(self::$globalOptions);
104+
102105
/** @var Output $out */
103106
$out = $this->output;
104107
$out->helpPanel([
105-
'usage' => "$binName <info>{command}</info> [--opt -v -h ...] [arg0 arg1 arg2=value2 ...]",
106-
'example' => [
107-
"$binName test (run a independent command)",
108-
"$binName home{$delimiter}index (run a command of the group)",
109-
"$binName help {command} (see a command help information)",
110-
"$binName home{$delimiter}index -h (see a command help of the group)",
111-
"$binName --auto-completion --shell-env [zsh|bash] [--gen-file stdout]",
112-
]
108+
'Usage' => "$binName <info>{command}</info> [--opt -v -h ...] [arg0 arg1 arg2=value2 ...]",
109+
'Options' => $globalOptions,
110+
'Example' => [
111+
"$binName test run a independent command",
112+
"$binName home index run a sub-command of the group",
113+
sprintf("$binName home%sindex run a sub-command of the group", $delimiter),
114+
"$binName help {command} see a command help information",
115+
"$binName home index -h see a sub-command help of the group",
116+
sprintf("$binName home%sindex -h see a sub-command help of the group", $delimiter),
117+
],
118+
'Help' => [
119+
'Generate shell auto completion scripts:',
120+
" <info>$binName --auto-completion --shell-env [zsh|bash] [--gen-file stdout]</info>",
121+
' eg:',
122+
" $binName --auto-completion --shell-env bash --gen-file stdout",
123+
" $binName --auto-completion --shell-env bash --gen-file myapp.sh",
124+
],
113125
]);
114126
}
115127

@@ -261,7 +273,7 @@ protected function dumpAutoCompletion(string $shellEnv, array $data): void
261273

262274
// info
263275
$glue = ' ';
264-
$genFile = (string)$input->getLongOpt('gen-file');
276+
$genFile = $input->getStringOpt('gen-file');
265277
$filename = 'auto-completion.' . $shellEnv;
266278
$tplDir = dirname(__DIR__, 2) . '/resource/templates';
267279

src/Controller.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,11 @@ final public function execute($input, $output)
309309

310310
// if user custom handle not found logic.
311311
if ($this->onNotFound($action)) {
312-
$this->debugf('user custom handle the action:%s not found logic', $action);
312+
$this->debugf('user custom handle the action "%s" not found logic', $action);
313313
return 0;
314314
}
315315

316-
$this->debugf('action:%s not found on the group controller', $action);
316+
$this->debugf('action "%s" not found on the group controller', $action);
317317

318318
// if you defined the method '$this->notFoundCallback' , will call it
319319
// if (($notFoundCallback = $this->notFoundCallback) && method_exists($this, $notFoundCallback)) {

src/GlobalOption.php

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class GlobalOption
1313

1414
public const DEBUG = 'debug';
1515

16+
public const ISHELL = 'ishell';
17+
1618
public const VERSION = 'version';
1719

1820
public const PROFILE = 'profile';
@@ -21,8 +23,13 @@ class GlobalOption
2123

2224
public const NO_INTERACTIVE = 'no-interactive';
2325

26+
public const HELP_OPTS = ['h', 'help'];
27+
28+
public const VERSION_OPTS = ['V', 'version'];
29+
2430
public const KEY_MAP = [
2531
'debug' => 1,
32+
'ishell' => 1,
2633
'profile' => 1,
2734
'no-color' => 1,
2835
'h' => 1,

src/IO/Input.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,22 @@ protected function collectInfo(array $args): void
7171
$this->tokens = $args;
7272
$this->script = array_shift($args);
7373
$this->flags = $args; // no script
74+
7475
// bin name
7576
$this->scriptName = basename($this->script);
77+
7678
// full script
7779
$this->fullScript = implode(' ', $args);
80+
}
7881

82+
/**
83+
* re-parse args/opts from given args
84+
*
85+
* @param array $args
86+
*/
87+
public function parse(array $args): void
88+
{
89+
$this->doParse($args);
7990
}
8091

8192
/**
@@ -140,7 +151,7 @@ public function read(string $question = '', bool $nl = false): string
140151
fwrite(Cli::getOutputStream(), $question . ($nl ? "\n" : ''));
141152
}
142153

143-
return trim(fgets($this->inputStream));
154+
return trim((string)fgets($this->inputStream));
144155
}
145156

146157
/***********************************************************************************

src/IO/Input/StringInput.php

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types=1);
2+
/**
3+
* Created by PhpStorm.
4+
* User: Inhere
5+
* Date: 2018/1/30 0030
6+
* Time: 23:41
7+
*/
8+
9+
namespace Inhere\Console\IO\Input;
10+
11+
use Inhere\Console\IO\Input;
12+
use Toolkit\Cli\Flags;
13+
use Toolkit\Cli\Util\LineParser;
14+
15+
/**
16+
* Class StringInput
17+
*
18+
* @package Inhere\Console\IO\Input
19+
*/
20+
class StringInput extends Input
21+
{
22+
/**
23+
* Input constructor.
24+
*
25+
* @param string $line
26+
* @param bool $parsing
27+
*/
28+
public function __construct(string $line, bool $parsing = true)
29+
{
30+
parent::__construct([], false);
31+
32+
if ($parsing && $line) {
33+
$flags = LineParser::parseIt($line);
34+
35+
$this->doParse($flags);
36+
}
37+
}
38+
39+
/**
40+
* @param array $args
41+
*/
42+
protected function doParse(array $args): void
43+
{
44+
[
45+
$this->args,
46+
$this->sOpts,
47+
$this->lOpts
48+
] = Flags::parseArray($args);
49+
50+
// find command name
51+
$this->command = $this->findCommandName();
52+
}
53+
}

src/Util/Interact.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static function readln($message = null, $nl = false, array $opts = []): s
4949

5050
$stream = $opts['stream'] ?? Cli::getInputStream();
5151

52-
return trim(fgets($stream));
52+
return trim((string)fgets($stream));
5353
}
5454

5555
/**

0 commit comments

Comments
 (0)