Skip to content

Commit 1563091

Browse files
committed
一些逻辑调整
- 运行逻辑优化 - 异常捕获显示优化 - 支持像symfony/console 一样,根据位置定义和获取参数
1 parent 7292b4b commit 1563091

10 files changed

+290
-135
lines changed

document.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
## 根据位置设置参数
3+
4+
```
5+
$ php examples/app demo john male 43 --opt1 value1 -y
6+
hello, this in inhere\console\examples\DemoCommand::execute
7+
this is argument and option example:
8+
the opt1's value
9+
option: opt1 |
10+
| |
11+
php examples/app demo john male 43 --opt1 value1 -y
12+
| | | | | |
13+
script command | | |______ option: yes, it use shortcat: y, and it is a Input::OPT_BOOLEAN, so no value.
14+
| |___ |
15+
argument: name | argument: age
16+
argument: sex
17+
```

examples/DemoCommand.php

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: inhere
5+
* Date: 2017-02-27
6+
* Time: 18:58
7+
*/
8+
9+
namespace inhere\console\examples;
10+
11+
use inhere\console\Command;
12+
use inhere\console\io\Input;
13+
14+
/**
15+
* Class DemoCommand
16+
* @package app\console\commands
17+
*/
18+
class DemoCommand extends Command
19+
{
20+
protected static $name = 'demo';
21+
protected static $description = 'this is a demo independent command. but config use configure(), it like symfony console: argument define by position';
22+
23+
protected function configure()
24+
{
25+
$this->createDefinition()
26+
->setDescription(self::getDescription())
27+
->setExample($this->replaceAnnotationVars('{script} {command} john male 43 --opt1 value1'))
28+
->addArgument('name', Input::ARG_REQUIRED, 'description for the argument [name], is required')
29+
->addArgument('sex', Input::ARG_OPTIONAL, 'description for the argument [sex], is optional')
30+
->addArgument('age', Input::ARG_OPTIONAL, 'description for the argument [age], is optional')
31+
->addOption('yes', 'y', Input::OPT_BOOLEAN, 'description for the option [yes], is boolean')
32+
->addOption('opt1', null, Input::OPT_REQUIRED, 'description for the option [opt1], is required')
33+
->addOption('opt2', null, Input::OPT_OPTIONAL, 'description for the option [opt2], is optional')
34+
;
35+
}
36+
37+
/**
38+
* description text by annotation. it is invalid when configure() is exists
39+
*/
40+
public function execute($input, $output)
41+
{
42+
$output->write('hello, this in ' . __METHOD__);
43+
// $name = $input->getArg('name');
44+
45+
$output->write(<<<EOF
46+
this is argument and option example:
47+
the opt1's value
48+
option: opt1 |
49+
| |
50+
php examples/app demo john male 43 --opt1 value1 -y
51+
| | | | | |
52+
script command | | |______ option: yes, it use shortcat: y, and it is a Input::OPT_BOOLEAN, so no value.
53+
| |___ |
54+
argument: name | argument: age
55+
argument: sex
56+
EOF
57+
);
58+
}
59+
}

examples/TestCommand.php

+8
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@
1616
*/
1717
class TestCommand extends Command
1818
{
19+
protected static $description = 'this is a test independent command';
20+
1921
/**
2022
* test text
2123
* @usage {name} test message
24+
* @arguments
25+
* arg1 argument description 1
26+
* arg2 argument description 2
27+
* @options
28+
* --long,-s option description 1
29+
* --opt option description 2
2230
*/
2331
public function execute($input, $output)
2432
{

examples/cli-routes.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
use inhere\console\io\Input;
1414
use inhere\console\io\Output;
1515

16-
$app->command('demo', function (Input $in, Output $out) {
16+
$app->command('exam', function (Input $in, Output $out) {
1717
$cmd = $in->getCommand();
1818

1919
$out->info('hello, this is a test command: ' . $cmd);
2020
});
2121

2222
$app->command('test', TestCommand::class);
23+
$app->command(\inhere\console\examples\DemoCommand::class);
2324
$app->controller('home', HomeController::class);

images/use-arg-position.png

491 KB
Loading

src/AbstractApp.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ protected function runtimeCheck()
109109
protected function init()
110110
{
111111
$this->commandName = $this->input->getCommand();
112+
set_exception_handler([$this, 'exceptionHandler']);
112113
}
113114

114115
/**********************************************************
@@ -142,7 +143,7 @@ public function run($exit = true)
142143
} catch (\Throwable $e) {
143144
self::fire(self::ON_RUN_ERROR, [$e, $this]);
144145
$returnCode = $e->getCode() === 0 ? __LINE__ : $e->getCode();
145-
$this->dispatchExHandler($e);
146+
$this->exceptionHandler($e);
146147
}
147148

148149
// call 'onAfterRun' service, if it is registered.
@@ -237,6 +238,10 @@ public function command(string $name, $handler = null)
237238

238239
$this->validateName($name);
239240

241+
if (isset($this->commands[$name])) {
242+
throw new \InvalidArgumentException("Command '$name' have been registered!");
243+
}
244+
240245
if (is_string($handler)) {
241246
if (!class_exists($handler)) {
242247
throw new \InvalidArgumentException("The console command class [$handler] not exists!");
@@ -281,7 +286,7 @@ public function commands(array $commands)
281286
* @param \Exception|\Throwable $e
282287
* @throws \Exception
283288
*/
284-
protected function dispatchExHandler($e)
289+
public function exceptionHandler($e)
285290
{
286291
// $this->logger->ex($e);
287292

src/AbstractCommand.php

+114-45
Original file line numberDiff line numberDiff line change
@@ -90,70 +90,68 @@ protected function configure()
9090
}
9191

9292
/**
93-
* validate input arguments and options
94-
* @return bool
93+
* @return InputDefinition
9594
*/
96-
public function validateInput()
95+
protected function createDefinition()
9796
{
98-
if (!$definition = $this->definition) {
99-
return true;
100-
}
97+
$this->definition = new InputDefinition();
10198

102-
$givenArgs = $errArgs = [];
99+
return $this->definition;
100+
}
103101

104-
foreach ($this->input->getArgs() as $key => $value) {
105-
if (is_int($key)) {
106-
$givenArgs[$key] = $value;
107-
} else {
108-
$errArgs[] = $key;
109-
}
110-
}
102+
/**
103+
* run command
104+
* @return int
105+
*/
106+
public function run()
107+
{
108+
// load input definition configure
109+
$this->configure();
111110

112-
if (count($errArgs) > 0) {
113-
throw new \RuntimeException(sprintf('Unknown arguments (error: "%s").', implode(', ', $errArgs)));
111+
if ($this->input->sameOpt(['h','help'])) {
112+
return $this->showHelp();
114113
}
115114

116-
$defArgs = $definition->getArguments();
117-
$missingArgs = array_filter(array_keys($defArgs), function ($name, $key) use ($definition, $givenArgs) {
118-
return !array_key_exists($key, $givenArgs) && $definition->argumentIsRequired($name);
119-
}, ARRAY_FILTER_USE_BOTH);
120-
121-
if (count($missingArgs) > 0) {
122-
throw new \RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArgs)));
123-
}
115+
$status = 0;
124116

125-
$index = 0;
126-
$args = [];
117+
try {
118+
App::fire(App::ON_BEFORE_EXEC, [$this]);
127119

128-
foreach ($defArgs as $name => $conf) {
129-
$args[$name] = $givenArgs[$index];
130-
$index++;
131-
}
120+
if (true !== $this->beforeRun()) {
121+
return -1;
122+
}
132123

133-
$this->input->setArgs($args);
124+
$status = $this->execute($this->input, $this->output);
125+
$this->afterRun();
134126

135-
// check options
136-
// $givenOpts = $this->input->getOpts();
137-
// $defOpts = $definition->getOptions();
127+
App::fire(App::ON_AFTER_EXEC, [$this]);
128+
} catch (\Throwable $e) {
129+
App::fire(App::ON_EXEC_ERROR, [$e, $this]);
130+
$this->handleRuntimeException($e);
131+
}
138132

139-
return true;
133+
return $status;
140134
}
141135

142136
/**
143-
* @return InputDefinition
137+
* do execute
138+
* @param Input $input
139+
* @param Output $output
140+
* @return int
144141
*/
145-
protected function createDefinition()
142+
abstract protected function execute($input, $output);
143+
144+
protected function showHelp()
146145
{
147-
$this->definition = new InputDefinition();
146+
// 创建了 InputDefinition , 则使用它的信息。
147+
// 不会再解析和使用命令的注释。
148+
if ($def = $this->getDefinition()) {
149+
$this->output->mList($def->getSynopsis());
148150

149-
return $this->definition;
151+
return true;
152+
}
150153
}
151154

152-
/**
153-
* run
154-
*/
155-
abstract public function run();
156-
157155
/**
158156
* beforeRun
159157
*/
@@ -177,7 +175,78 @@ protected function beforeRun()
177175
}
178176

179177
// do validate input arg and opt
180-
$this->validateInput();
178+
return $this->validateInput();
179+
}
180+
181+
/**
182+
* validate input arguments and options
183+
* @return bool
184+
*/
185+
public function validateInput()
186+
{
187+
if (!$def = $this->definition) {
188+
return true;
189+
}
190+
191+
$in = $this->input;
192+
$givenArgs = $errArgs = [];
193+
194+
foreach ($in->getArgs() as $key => $value) {
195+
if (is_int($key)) {
196+
$givenArgs[$key] = $value;
197+
} else {
198+
$errArgs[] = $key;
199+
}
200+
}
201+
202+
if (count($errArgs) > 0) {
203+
$this->output->liteError(sprintf('Unknown arguments (error: "%s").', implode(', ', $errArgs)));
204+
205+
return false;
206+
}
207+
208+
$defArgs = $def->getArguments();
209+
$missingArgs = array_filter(array_keys($defArgs), function ($name, $key) use ($def, $givenArgs) {
210+
return !array_key_exists($key, $givenArgs) && $def->argumentIsRequired($name);
211+
}, ARRAY_FILTER_USE_BOTH);
212+
213+
if (count($missingArgs) > 0) {
214+
$this->output->liteError(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArgs)));
215+
return false;
216+
}
217+
218+
$index = 0;
219+
$args = [];
220+
221+
foreach ($defArgs as $name => $conf) {
222+
$args[$name] = $givenArgs[$index] ?? $conf['default'];
223+
$index++;
224+
}
225+
226+
$in->setArgs($args);
227+
228+
// check options
229+
$opts = $missingOpts = [];
230+
$givenLOpts = $in->getLongOpts();
231+
$defOpts = $def->getOptions();
232+
233+
foreach ($defOpts as $name => $conf) {
234+
if (!$in->hasLOpt($name)) {
235+
if (($srt = $conf['shortcut']) && $in->hasSOpt($srt)) {
236+
$opts[$name] = $in->sOpt($srt);
237+
} elseif ($conf['required']) {
238+
$missingOpts[] = "--{$name}" . ($srt ? "|-{$srt}" : '');
239+
}
240+
}
241+
}
242+
243+
if (count($missingOpts) > 0) {
244+
$this->output->liteError(sprintf('Not enough options parameters (missing: "%s").', implode(', ', $missingOpts)));
245+
246+
return false;
247+
}
248+
249+
return true;
181250
}
182251

183252
/**

0 commit comments

Comments
 (0)