简介
Aura CLI包提供了一个创建和执行CLI Command命令行对象的系统。它包含解析命令行参数和处理标准输入输出流的组件,而且也是基于信号的。
Aura CLI包依赖aura.di和aura.signal包。
基本用法
动作和输入/输出
命令Command的逻辑处理一般在action()方法中。在下面的示例中,我们将演示基本的输入/输出。
<?php namespace vendor\package; use aura\cli\Command; class Example extends Command { public function action() { $this->stdio->outln('Hello World!'); $this->stdio->out('Please enter some text: '); $input = $this->stdio->in(); $this->stdio->errln('Input was ' . $input); } }
当调用这个命令Command时,将会输出"Hello Wordl!",询问用户输入,最后输出用户输入或错误流。
使用$stdio对象处理标准输入/输出流。该对象具有以下方法:
outln()和out():写入到标准输出流,第一个方法输出的结尾有换行符,第二个则没有。errln()和err():写入到标准错误流,第一个方法输出的结尾有换行符,第二个则没有。inln()和in():从标准输入流读入,直到用户敲回车键。第一个方法将保留输入末尾的换行符,第二个则不会。
信号钩子
Aura的CLI 命令Command有四个信号钩子;这些钩子是通过信号管理器$signal调用的。在动作前后使用pre-和post-action方法,在命令启动和退出前后使用pre-和post-exec方法。
<?php namespace vendor\package; use aura\cli\Command; class Example extends Command { protected $input = 'foo bar baz'; public function preExec() { // perform object setup here } public function preAction() { $this->stdio->outln('The input is currently ' . $this->input); } public function action() { $this->stdio->out('Please enter some text: '); $this->input = $this->stdio->in(); } public function postAction() { $this->stdio->outln('The input was ' . $this->input); } public function postExec() { // perform object teardown here } }
变量参数
传递命令行参数也是命令调用的一部分。在Command对象中获取命令行参数,要使用$this->params属性。
<?php namespace vendor\package; use aura\cli\Command; class Example extends Command { public function action() { foreach ($this->params as $key => $val) { $this->stdio->outln("Param $key is '$val'."); } } }
例如,如果我们输入命令。。。
$ php command.php foo bar baz
。。。那么action()方法将会输出:
Param 0 is 'foo'. Param 1 is 'bar'. Param 2 is 'baz'.
高级用法
长短选项
除了变量参数,我们还希望在调用命令的时候能够传入某些短开关和长选项参数。一般在命令行中以-a和--option=value形式出现。
为了处理选项,首先在Command对象中定义$options数组。然后就可以使用$getopt方法获取各选项值。
下面来看看关于命令行选项的示例:
<?php namespace vendor\package; use aura\cli\Command; use aura\cli\Option; class Example extends Command { protected $options = array( 'foo_bar' => array( 'long' => 'foo-bar', 'short' => 'f', 'param' => Option::PARAM_REQUIRED, 'multi' => false, 'default' => null, ), ); public function action() { $this->stdio->out("The value of -f/--foo-bar is ") $this->stdio->outln($this->getopt->foo_bar); } }
当我们调用上面命令时。。。
$ php command.php --foo-bar=gir
。。。它会输出:
The value of -f/--foo-bar is gir.
$options数组的键名就是选项的名称,而每个元素又是一个选项定义数组,键名如下:
'long': 长格式选项,在命令行中以两个"-"符号开始。长格式的参数值是等号后面的值;例如:--foo-bar=some_value。如果不需要长格式选项,此值留空即可。'short': 短格式选项,在命令行中以一个"-"符号开始。短格式的参数值是空格后面的值;例如:-f some_value。如果不需要短格式选项,此值留空即可。'param': 选项值是必须的,还是可选的,又或者是不允许的?Option::PARAM_REQUIRED表示必须传递参数值,Option::PARAM_OPTIONAL表示参数值是可选的,Option::PARAM_REJECTED表示不允许传入任何参数值。'multi': 选项是否允许在同一个命令中多递传递?例如:传入"-f foo -f bar -f zim"将会传入一个数组选项值(数组元素:'foo','bar','zim')。'default': 未传递参数时的默认值。
定义好选项并在命令行中传值后,我们可以使用$getopt对象获取选项值,就好像魔述获取类属性一样。因此,对于上面的'foo_bar'选项,我们使用$this->getopt->foo_bar获取它的值。
信号和取消动作
在执行exec()期间,Command会发送一个'pre_exec'信号给信号管理器,并把命令Command对象作为唯一参数传递给信号管理器。根据需要在这里设置命令Command对象。
在action()方法执行之前,命令Command对象会发送一个'pre_action'信号给信号管理器,并把命令Command对象作为唯一参数传递给信号管理器。
要中止action()方法运行,只需在'pre_action'信号处理器中调用命令Command对象的skipAction()方法即可。这将会跳过action()方法,直接执行'post_exec'信号。
action()方法执行完后,命令Command对象会发送一个'post_action'信号给信号管理器,并把命令Command对象作为唯一参数传递给信号管理器。(如果跳过了action()方法,'post_action'信号也不会被发送。)
最后,在exec()方法结束之前,命令Command对象会发送一个'post_exec'信号给信号管理骂,并把命令Command对象作为唯一参数传递给信号管理器。在命令执行完毕后,根据需要执行清理工作。
调用脚本和命令工厂
在我们执行命令Command对象前,我们需要一个“调用脚本”。它相当于CLI环境下的引导脚本。调用脚本是我们在命令行执行的文件;它建立执行环境,创建命令Command对象并且执行它。
在调用脚本中,不要直接实例化命令Command类。而应该创建一个数组映射(短命令名称=>关联类名称),并使用CommandFactory创建该短命令名称的命令Command对象。
例如,下面的代码将会实例化vendor.package/src目录下的vendor\package\Example类并执行它。代码有些长,且为了实现必要的功能,使用了Aura的其它包。
<?php // create a map of command names to command classes $command_map = array( 'example' => 'vendor\package\Example', ); // set up an autoloader $loader = require '/path/to/aura.autoload/scripts/instance.php'; $loader->register(); $loader->addPrefix('aura\di\\', '/path/to/aura.di/src'); $loader->addPrefix('aura\signal\\', '/path/to/aura.signal/src'); $loader->addPrefix('aura\cli\\', '/path/to/aura.cli/src'); $loader->addPrefix('vendor\package\\', '/path/to/vendor.package/src'); // instantiate and configure the DI container. use aura\di\Container; use aura\di\Forge; use aura\di\Config; $di = new Container(new Forge(new Config)); require '/path/to/aura.signal/config/default.php'; require '/path/to/aura.cli/config/default.php'; // get the cli context object from the DI container, then discard the // invoking script name from the cli context argument values $context = $di->get('cli_context'); $context->shiftArgv(); // get the command factory from the DI container and add the command map $factory = $di->get('cli_command_factory'); foreach ($command_map as $name => $class) { $factory->map($name, $class); } // using first cli context argument as the short command name, // get a new command object instance and then execute it. try { $command = $factory->newInstance($context->shiftArgv()); $command->exec(); } catch (Exception $e) { echo $e->getMessage() . PHP_EOL; }
把上述代码存到command.php文件中。然后在命令行中键入php /path/to/command.php example命令,它将会执行vendor\package\Example类。
哇哦,你可以觉得代码太多了。如果你使用aura.system框架,它会帮你处理所有工作,但如果你不想使用整个框架,那么你不得不自己写这些代码。