windwalker/console

Windwalker 控制器包

安装数: 14,185

依赖项: 3

建议者: 0

安全性: 0

星标: 3

关注者: 4

分支: 0

开放问题: 0

类型:windwalker-package


README

Windwalker Console 包为您的 CLI 应用程序提供了一个优雅且嵌套的命令结构。

通过 Composer 安装

将以下内容添加到您的 composer.json 文件中的 require 块中。

{
    "require": {
        "windwalker/console": "~3.0"
    }
}

嵌套命令结构

          Console Application (RootCommand)
                         |
              ----------------------
              |                    |
          CommandA              CommandB
              |                    |
        ------------          ------------
        |          |          |          |
    CommandC   CommandD    CommandE   CommandF

如果我们输入

$ php cli/console.php commandA commandC foo bar -a -bc -d=e --flower=sakura

那么我们将直接跳转到 CommandC 类,接下来的 foo bar 将作为参数。

class CommandC extend AbstractCommand
{
    public function execute()
    {
        $arg1 = $this->getArgument(0); // foo
        $arg2 = $this->getArgument(0); // bar
        
        $opt = $this->io->get('d') // e
        $opt = $this->io->get('flower') // sakura
    }
}

初始化 Console

Console 是主要应用程序,帮助我们创建命令行程序。

cli/console.php 文件中的示例 console 应用程序骨架

<?php

// Load the Composer autoloader
include __DIR__ . '/../vendor/autoload.php';

use Windwalker\Console\Console;

$console = new Console;

$console->execute();

execute() 将找到与 CLI 输入参数匹配的命令。如果没有注册任何命令,console 将执行 Default Command

默认根命令

RootCommand 是一个继承自基本 Command 的命令对象。它提供了一些有用的辅助函数,我们可以通过输入来列出所有命令

$ php cli/app.php

默认输出为

Windwalker Console - version: 1.0
------------------------------------------------------------

[console.php Help]

The default application command

Usage:
  console.php <command> [option]


Options:

  -h | --help       Display this help message.
  -q | --quiet      Do not output any message.
  -v | --verbose    Increase the verbosity of messages.
  --ansi            Set 'off' to suppress ANSI colors on unsupported terminals.


Welcome to Windwalker Console.

为 RootCommand 设置处理器

我们可以向每个命令添加闭包,该命令将首先执行此函数。在 $console 上使用 setHandler(),Console 将自动将代码传递给 RootCommand

<?php
// cli/console.php

// ...

$console->setHandler(
	function($command)
	{
		$command->out('This is default command.');

		return 0; // Return exit code.
	}
);

$console->execute();

此代码将执行相同的操作

<?php
// cli/console.php

// ...

$console->getRootCommand()
    ->setHandler(
        function($command)
        {
            $command->out('This is default command.');

            return 0; // Return exit code.
        }
    );

$console->execute();

重新输入 $ php cli/console.php 并输出

This is default command.

如果我们想再次获取帮助,只需输入

$ cli/console.php help

# OR

$ cli/console.php --help

注意:命令只返回介于 0 和 255 之间的整数,0 表示成功,而其他则表示失败或其他状态。Unix/Linux 的退出代码含义请参阅:退出代码含义

向 Console 添加帮助信息

Console 包含一些帮助信息,如:nameversiondescriptionusagehelp

如果我们将这些信息添加到 Console

// cli/console.php

// ...

$console = new \Windwalker\Console\Console;

$console->setName('Example Console')
	->setVersion('1.2.3')
	->setUsage('console.php <commands> <arguments> [-h|--help] [-q|--quiet]')
	->setDescription('Hello World')
	->setHelp(
<<<HELP
Hello, this is an example console, if you want to do something, see above:

$ foo bar -h => foo bar --help

---------

$ foo bar yoo -q => foo bar yoo --quiet
HELP
	);

// ...

帮助信息将显示

console example

向 Console 添加一级命令

现在,我们只使用默认的根命令。除了 HelpCommand 之外,没有其他一级命令可供调用。

我们可以通过以下代码添加新命令

<?php
// cli/console.php

$console->register('foo')
	->setDescription('This is first level foo command.')
	->setUsage('foo command [--option]')
	->setHelp('foo help')
	->setHandler(
		function($command)
		{
			$command->out('This is Foo Command executing code.');
		}
	);

然后我们输入

$ cli/console.php foo

我们将得到

This is Foo Command executing code.

如果我们输入 help

$ cli/console.php -h

foo 命令的描述将自动添加到默认命令参数列表中。

foo-help

声明命令类

我们可以创建自己的命令对象,而不是在运行时设置。

这是一个 FooCommand 声明示例

<?php
// src/Myapp/Command/FooCommand.php

namespace Myapp\Command;

use Windwalker\Console\Command\Command;

class FooCommand extends Command
{
    protected $name  = 'foo';
    protected $usage = 'foo command [--option]';
    protected $help  = 'foo help';
    protected $description = 'This is first level foo command.';

    public function initialise()
    {
        // We can also set help message in initialise method 
        $this->setDescription('This is first level foo command.')
            ->setUsage('foo command [--option]')
            ->setHelp('foo help');
    }

    public function doExecute()
    {
        $this->out('This is Foo Command executing.');
    }
}

然后我们将其注册到 Console 中

<?php
// cli/console.php

$console->addCommand(new FooCommand);

获取参数和选项

我们可以使用此代码来获取参数和选项,并在 FooCommand 中设置它们。

// src/Myapp/Command/FooCommand.php

public function initialise()
{
    // Define options first that we can set option aliases.
    $this->addOption(array('y', 'yell')) // First element `y` will be option name, others will be alias
        ->alias('Y') // Add a new alias
        ->defaultValue(0)
        ->description('Yell will make output upper case.');
        
    // Global options will pass to every child.
    $this->addGlobalOption('s')
        ->defaultValue(0)
        ->description('Yell will make output upper case.');
}

public function doExecute()
{
    $name = #this->getArgument(0);

    if (!$name)
    {
        $this->io->in('Please enter a name: ');
    }

    $reply = 'Hello ' . $name;

    if ($this->getOption('y'))
    {
        $reply = strtoupper($reply);
    }

    if ($this->getOption('q'))
    {
        $reply = strtolower($reply);
    }

    $this->out($reply);
}

如果我们输入

$ php cli/console.php foo Asika --yell

# OR

$ php cli/console.php foo Asika -y

getOption() 方法将自动检测选项别名,然后我们可以获取

HELLO: ASIKA

注意:我们必须先使用 addOption() 来定义选项,然后才能通过 $this->getOption('x') 获取我们想要的输入选项。如果我们不这样做,我们必须使用 $this->io->get('x') 来获取选项值,但这种方式不支持选项别名。

添加二级命令以及更多...

现在,FooCommand 是我们命令树中的一级命令,如果我们想在 FooCommand 下添加几个命令,我们可以使用 addCommand() 方法。现在我们向 FooCommand 添加两个 baryoo 命令。

在运行时添加命令。

我们可以使用 addCommand() 将命令添加为其他命令的子命令。

如果一个命令有一个或多个子命令,参数表示调用与该参数名称相同的子命令。

如果没有子命令,Command对象将运行已设置的处理器闭包,如果没有设置处理器,则运行doExecute()。然后可以通过$this->getArgument({offset})获取剩余的参数。

<?php
// src/Myapp/Command/FooCommand.php

use Windwalker\Console\Option\Option;

//...

    public function initialise()
    {
        $this->addCommand('bar')
            ->description('Bar description.');
            
        $this->addCommand('yoo')
            ->description('Yoo description.')
            ->addOption(new Option(array('y', 'yell'), 0))
            ->addGlobalOption(new Option('s', 0, 'desc'));
    }

通过类添加命令

首先声明BarCommandYooCommand类。

<?php
// src/Myapp/Command/Foo/BarCommand.php

namespace Myapp\Command\Foo;

use Windwalker\Console\Command\Command;

class BarCommand extends Command
{
    protected $name = 'bar';
    protected $usage = 'bar command [--option]';
    protected $help  = 'bar help';
    protected $description = 'This is second level bar command.';

    public function initialise()
    {
        $this->addOption(new Option(array('y', 'yell'), 0))
            ->addGlobalOption(new Option('s', 0, 'desc'));
    }

    public function doExecute()
    {
        $this->out('This is Bar Command executing.');
        
        $arg1 = $this->getArgument(0);
        
        if ($arg1)
        {
            $this->out('Argument1: ' . $arg1);
        }
    }
}

然后注册它们到FooCommand

<?php
// src/Myapp/Command/FooCommand.php

use Myapp\Command\Foo\BarCommand;
use Myapp\Command\Foo\YooCommand;

//...

    public function initialise()
    {
        $this->addCommand(new BarCommand)
            ->addCommand(new YooCommand);
    }

好的,让我们开始输入

$ cli/console.php foo bar

我们得到

This is Bar Command executing code.

然后输入

$ cli/console.php foo bar sakura

获取

This is Bar Command executing code.
Argument1: sakura

通过路径获取子命令

$command = $console->getCommand('foo/bar'); // BarCommand

// OR

$command = $command->getChild('foo/bar/baz');

提示器

提示器是一组对话框工具,帮助我们向用户提问。

$prompter = new \Windwalker\Console\Prompter\TextPrompter;

$name = $prompter->ask('Tell me your name:', 'default');

或者在构造函数中设置问题。

$prompter = new TextPrompter('Tell me your name: ', $this->io);

// If argument not exists, auto ask user.
$name = $this->getArgument(0, $prompter);

验证输入值

$prompter = new \Windwalker\Console\Prompter\ValidatePrompter;

$prompter->setAttempt(3);

$prompter->ask('Please enter username: ');

如果我们没有输入任何内容,ValidatePrompter将尝试问我们三次(我们通过setAttempt()设置这个数字)。

Please enter username:
  Not a valid value.

Please enter username:
  Not a valid value.

Please enter username:
  Not a valid value.

我们可以设置闭包来验证我们的规则

$prompter->setAttempt(3)
    ->setNoValidMessage('No valid number.')
    ->setHandler(
    function($value)
    {
        return $value == 9;
    }
);

$prompter->ask('Please enter right number: ');

结果

Please enter right number: 1
No valid number.

Please enter right number: 2
No valid number.

Please enter right number: 3
No valid number.

如果验证失败,我们可以选择关闭我们的进程

// ...

$prompter->failToClose(true, 'Number validate fail and close');

$prompter->ask('Please enter right number: ');

结果

Please enter right number:
No valid number.

Please enter right number:
No valid number.

Please enter right number:
No valid number.

Number validate fail and close

选择列表

$options = array(
    's' => 'sakura',
    'r' => 'Rose',
    'o' => 'Olive'
);

$prompter = new \Windwalker\Console\Prompter\SelectPrompter('Which do you want: ', $options);

$result = $prompter->ask();

$command->out('You choose: ' . $result);

输出

  [s] - sakura
  [r] - Rose
  [o] - Olive

Which do you want: r
You choose: r

布尔提示器

BooleanPrompter将输入字符串转换为布尔类型,(y, yes, 1) 将是 true,(n, no, 0, null) 将是 false

$prompter = new \Windwalker\Console\Prompter\BooleanPrompter;

$result = $prompter->ask('Do you wan to do this [Y/n]: ');

var_dump($result);

结果

Do you wan to do this [Y/n]: y
bool(true)

可用的提示器

  • TextPrompter
  • SelectPrompter
  • CallbackPrompter
  • ValidatePrompter
  • NotNullPrompter
  • PasswordPrompter

可用的提示器

HelpCommand

HelpCommand将自动为我们生成帮助列表。

当我们使用addCommand()addOption()并设置一些描述或其他信息到这些对象中时,它们将保存所有信息。然后当我们输入$ cli/console.php help somethine$ cli/console.php somethine --help,HelpCommand将返回帮助信息给我们。

每个命令都有这些信息,您可以使用setter和getter来访问它们

  • Name(命令名称。RootCommand的名称是文件名。)
  • Description(命令描述,将在帮助输出中的标题后显示。)
  • Usage(将在当前命令的帮助输出中显示。)
  • Help(将在帮助输出底部显示,作为当前命令的指南)

控制台信息

  • Name(应用程序名称,将在帮助输出中作为标题显示。)
  • Description(RootCommand描述。)
  • Usage(RootCommand使用方法。)
  • Help(RootCommand帮助信息。)

使用自定义描述符

如果您想覆盖应用程序的Descriptor,可以这样做

<?php
use Myapp\Command\Descriptor\XmlDescriptorHelper;
use Myapp\Command\Descriptor\XmlCommandDescriptor;
use Myapp\Command\Descriptor\XmlOptionDescriptor;

// ...

$descriptor = new new XmlDescriptorHelper(
    new XmlCommandDescriptor,
    new XmlOptionDescriptor
);

$console->getRootCommand()
    ->getChild('help')
    ->setDescriptor($descriptor);

// ...

在不使用控制台的情况下使用命令

我们可以使用Command而不使用控制台,请参阅Command README

致谢

Windwalker Console整合了许多来自其他CLI包的想法。以下是Windwalker获得灵感的项目简短列表。