Swoft 控制台命令组件使用详解,可以作为文档或者手册使用

在使用 swoft2 进行项目开发的过程中,难免会有一些需求需要用到控制台命令的帮助。比如:我想查看当前Container 中的各种类型实例分别有哪些,我新定义的类,是否已经正常初始化了。这个时候我们可以借助命令:php bin/swoft beans --type=[sig/pro/req/sess/def] ,来分别查看不同类型的 bean 的列表。详细命令说明如下:

shell 复制
Usage:
  bin/swoft app:beans [arguments ...] [options ...]

Global Options:
      --debug      Setting the application runtime debug level(0 - 4)
      --no-color   Disable color/ANSI for message output
  -h, --help       Display help message for application or command
  -V, --version    Display application version information

Options:
  -e, --exclude STRING     Must exclude the string on bean name
  -c, --include STRING     Must contains the string on bean name
      --type STRING        Display the bean names of the type (defaults: sig)
                           sig  - singleton
                           pro  - prototype
                           req  - request
                           sess - session
                           def  - definition

这个是如何实现的呢,swoft2 已经提供相关的控制台命令组件,我们只需要声明一个类,然后加上相应的命令相关的注解,在框架启动过程中,会自动解析此类,将其识别为一个命令。这时只要们通过类似上边的命令方式,就能执行我们自己定义的命令,而且相关命令提示,用法等等,都已经集成好了,非常简单便捷。

注意:以上命令,使用的是 devtool 组件中的自定义命令,如果你的没有,可以通过 composer require swoft/devtool,添加并且安装完毕后,重新执行命令即可。

下边,让我们来详细讲解下 swoft/console(控制台命令组件)的详细使用。

命令(Command)的组成

在 Swoft2 中,命令由以下几个主要部分组成,命令、操作、参数、选项。比如一个命令:php bin/swoft http:start -d。此命令,从 http 开始为 Swoft2 的命令部分,前边 bin/swoft相当于Swoft 的启动脚本,不在命令范围之内,但是要执行任何swoft 的命令都必须用到它。

http 是命令的主体(command),start 是命令的具体操作(action),后边的 -d是可选参数的简写形式。一个命令主体对应的代码开发中的一个类,一个命令可以有多个操作(action),每个操作相当于类中的一个成员函数。每个操作又可以对应多个参数,多个选项,参数和选项相当于函数的输入参数。

注意,这里参数和选项是两个东西,参数在swoft的命令组件中,指的是写命令的时候,不写出参数名的值。例如:php bin/swoft http:start val1 val2 ...,这其中的 val1 val2 ...分别是不同的参数,参数识别是靠位置索引(第1个,第2个...)来识别的。

选项一般指的是带中划线的那些值,这些值又分为带一个中划线和两个中划线的类型。如:php bin/swoft http:start -d或者 php bin/swoft http:start --daemon,这两个命令其实是一样的。主命令 http 一致,操作 start 一致,-d 和 --daemon 其实是一个选项的两种不同形式。-d这种带一个中划线的我们可以叫短选项或者快捷选项,--daemon这种带两个中划线的,可以叫做长选项,或者完全选项。它们是一个选项的两种不同表现形式,完全是为了减少输入命令时候字数,才有了短选项。

下边继续,逐个分析命令的组成。

主命令(Command)

也可以叫命令,为了区分我们常说的命令,我这里给它取了这个名字。

@Command 类注解,可以将一个类,自动解析为命令。一个命令中应该最少含有一个操作,否则执行命令帮助时候,可能会有异常。

@Command 注解说明

  • name:可选,命令的名称。一般小写,便于书写,不可以与其他命令重复,如果重复,虽然不会报错,但是可能覆盖之前的命令。不写默认会取类名(如果有后缀:Command,会先去掉),在拼接后缀 Command。
  • coroutine:可选,默认为 true。命令执行时候是否使用协程,根据需要选择是否开启,某些情况不宜开启,会导致数据错乱。
  • desc:可选,命令的描述。当执行 命令 --help 时候会提示,当前命令的描述信息,便于用户使用时参考
  • alias:可选,命令的别名。对于比较长的命令,可以简写,减少输入,别名也要是唯一的。
  • enabled:可选,命令是否启用。目前没有发现这个配置有什么用,保持默认true即可。
  • example:可选,提供一个使用示例,在 命令 --help 时候会显示 Example:内容。

那如何自定一个主命令呢?

定义个主命令很简单,比如:

php 复制
/**
 * 自定义 test 命令,无任何操作
 * @Command(name="test", coroutine=false, desc="测试自定义一个服务")
 */
class TestCommand {
    
}

假设我们有如下的命令要完成,关于搜索服务的命令,一部分是关于索引模块的命令,比如索引的创建、重建、删除等等;另一部分是关于检索模块的命令,比如测试检索内容,分页显示等等。

这里我来这样区分主命令,如果按照之前逻辑主命令应该为 search,两个模块为了区分出来应该为 index 和 search,相关的操作有 start/rebuild/delete/query 等等,那这样这个命令就分了三级,但是 swoft 中,search 对应的命令分组是不存在,没有这个概念,它很直接的将命令分为了命令和操作两个级别。所以我们得将以上划分修改一下,我将两个模块分为两个类,SearchIndex 和 SearchSearch(这不是重复,第一个代表search服务,第二个代表模块 search)。

操作(action)

不得不说,和 mvc 很相似。其实大多数要进行类似区分的时候,基本都是类似逻辑,因为比较直观。定义被标注了命令的类的一个成员函数,为一个操作,也需要对应的注解 @CommandMapping,这个注解,可以用来将命令和操作进行关联,就像http 请求的 controller 和 action 一样,绑定到路由,通过命令,进行路由就能找到对应的实际方法。

@CommandMapping 注解说明

  • name:可选,命令操作名称。不写默认取成员函数名作为名称。
  • alias:可选,别名。类似主命令,简化操作书写长度。
  • desc:可选,描述操作的具体用处。
  • usage:可选,不提供则不显示。使用说明,提供操作的具体使用方式。

这里继续完成之前搜索命令的示例代码:

php 复制
/**
 * search-search:query 示例
 * Class SearchSearchCommand
 *
 * @Command(name="search-search", enabled=false, desc="搜索服务搜索模块相关操作")
 * @example 搜索命令的示例内容,可以不提供
 */
class SearchSearchCommand {
    
    /**
     * @CommandMapping(name="query")
     *
     * @example 这是示例的内容,可以不提供
     *
     * @return void
     */
    public function query()
    {
        output()->success('ok');
    }
}

/**
 * search-search:rebuild 示例
 * Class SearchIndexCommand
 *
 * @Command(name="search-index", coroutine=false, desc="搜索服务索引相关操作")
 */
class SearchIndexCommand {
    /**
     * @CommandMapping(name="rebuild", usage="{fullCommand} [arguments ...] [options ...]", desc="搜索服务索引相关操作")
     *
     * @example 这是示例的内容,可以不提供
     *
     * @return void
     */
    public function rebuild()
    {
        output()->success('ok');
    }
}

参数(Arguments/Arg)

参数在命令执行的时候,实际是没有参数名的,传的直接是参数值。并且参数是通过传参的位置索引,来绑定到对应的参数名的。

如下代码所示:

通过命令行传递的参数,从第一个开始,依次会绑定到注解中的 @CommandArgument 注解。两个顺序是一致的。即第一个参数,绑定到 url,第二个参数绑定到 test

php 复制
/**
 * @CommandMapping(name="push", alias="push", desc="Push url to search engine.")
 *
 * @CommandOption(name="engine", type="array", short="eg", default={"baidu"}, mode=Command::OPT_IS_ARRAY)
 *
 * @CommandArgument(name="url", type="string", required=true, mode=Command::ARG_REQUIRED)
 * @CommandArgument(name="test", type="string", required=true, mode=Command::ARG_REQUIRED)
 * @example 提供一个示例,在帮助中显示
 *
 * @param Input $input
 * @param Output $output
 * @return void
 * @throws CommandFlagException
 */

// 假设命令为: php sitemap:push [第一个参数] [第二个参数] -eg=baidu
// 上边第一个参数就相当于 url 的值,第二个参数相当于 test 的值。
// 参数不同于 opt(可选)参数,不需要参数名=参数值这种形式。

@CommandArgument 注解说明

  • name:必填,参数名。不写在命令中,但是代码中获取对应的参数后,参数数组的键名会以此命名。不过获取参数的顺序还是要和命令中参数顺序一致。
  • desc:可选,参数描述信息。--help 时候显示的信息。
  • type:可选,默认值为 value。表示获取参数信息时候,获取到的是参数的值。
  • required:可选,默认值为 false。表示书写命令时候,是否必须有此参数。
  • optional:可选,默认为 true。表示此参数是否可以选,可以配合 default,默认值来使用。
  • array:可选,默认为false。表示此值是否可以传递多个,比如:
  • mode:可选,默认为 0。值的二进制表示,001 表示参数必须,010 表示参数可选,100表示参数是数组。这个功能,目前官方代码没有实现完全,array是不支持的。关于 array 的选项尽量不要设置。

注意,参数只能给成员函数设置注解,不能给类设置注解。

根据以上可以完善下之前search 的示例:

php 复制
/**
 * Class SearchIndexCommand
 *
 * @Command(name="search-index", coroutine=false, desc="搜索服务索引相关操作")
 */
class SearchIndexCommand {
    
	/**
     * @CommandMapping(name="related", desc="获取相关搜索词")
     * @CommandArgument(name="keyword", type="string", desc="搜索关键词")
     * @example search-search related swoft2 -l 10 ; 获取 swoft2 相关搜索词
     *
     * @param Input $input
     * @param Output $output
     * @return void
     * @throws XSException|CommandFlagException
     */
    public function related(Input $input, Output $output)
    {
        
    }
    
}

/**
 * Class SearchSearchCommand
 *
 * @Command(name="search-search", coroutine=false, desc="搜索服务搜索模块相关操作")
 * @CommandOption(name="dbName", short="db", default="db", desc="索引数据库名称")
 * @example search-search query swoft2
 */
class SearchSearchCommand {
    
    /**
     * @CommandMapping(name="query")
     *
     * @CommandArgument(name="keyword", type="string", desc="搜索关键词")
     *
     * @param Input $input
     * @param Output $output
     * @return void
     * @throws CommandFlagException
     * @throws XSException
     */
    public function query(Input $input, Output $output): void {
        
    }
    
}

选项(Options/Opt)

选项注解,既可以放到成员函数上,又可以放到类上。放到类上,表示是全局选项,此命令内的所有操作,都共享这个选项,都可以通过此选项获取选项值。而放到成员方法上,则只有此方法对应的操作会有此选项。其他操作不会有此选项。

@CommandOption 注解说明

  • name:必须,选项全称。eg:--test val这其中的 test 就是参数名。
  • short:可选,选项简写。配置short="t"之后,以上可以简写成 -t val。
  • type:可选,选项类型。默认是value。可以设置为:string/int/integer 等等。
  • default:可选,默认选项值。设置为 required=true 时候,没有传此参数,会获得此值。
  • desc:可选,选项描述信息。
  • required:可选,默认为false。此值设置为true后,命令行中必须由此选项,否则会报异常。
php 复制
// 选项的书写方式支持一下方式
-e
-e <value>
-e=<value>
--long-opt
--long-opt <value>
--long-opt=<value>

根据以上,在完善下之前的搜索相关的示例:

php 复制
/**
 * Class SearchIndexCommand
 *
 * @Command(name="search-index", coroutine=true, desc="搜索服务索引相关操作")
 * @CommandOption(name="dbName", short="db", default="db", desc="索引数据库名称")
 */
class SearchIndexCommand {
    
	/**
     * @CommandMapping(name="rebuild", usage="{fullCommand} [arguments ...] [options ...]", desc="搜索服务索引相关操作")
     * @CommandOption(name="limit", short="l", type="int", default=20, desc="每次循环从数据源处理的数据条数")
     * @CommandOption(name="buf", short="b", type="bool", default=true, desc="是否开启索引构建缓冲区")
     *
     * @param Input $input
     * @param Output $output
     * @return void
     * @throws CommandFlagException
     */
    public function index(Input $input, Output $output): void {}
    
	/**
     * 清空索引数据(慎用)
     *
     * @CommandMapping(name="clean", desc="清空当前索引数据库")
     *
     * @param Input $input
     * @param Output $output
     * @return void
     */
    public function clean(Input $input, Output $output) {}
}

/**
 * Class SearchSearchCommand
 *
 * @Command(name="search-search", coroutine=false, desc="搜索服务搜索模块相关操作")
 * @CommandOption(name="dbName", short="db", default="db", desc="索引数据库名称")
 * @example search-search query swoft2
 */
class SearchSearchCommand {
    
	/**
     * @CommandMapping(name="query")
     *
     * @CommandArgument(name="keyword", type="string", desc="搜索关键词")
     * @CommandOption(name="limit", short="l", type="int", default=10, desc="每页数量")
     * @CommandOption(name="offset", short="o", type="int", default=0, desc="偏移量")
     * @CommandOption(name="addDb", short="a", type="string", default="", desc="添加要检索的索引数据库名称")
     * @CommandOption(name="fuzzy", short="f", type="bool", default=false, desc="是否开启模糊搜索")
     *
     * @param Input $input
     * @param Output $output
     * @return void
     * @throws CommandFlagException
     * @throws XSException
     */
    public function query(Input $input, Output $output): void {}
    
	/**
     * @CommandMapping(name="related", desc="获取相关搜索词")
     * @CommandArgument(name="keyword", type="string", desc="搜索关键词")
     * @CommandOption(name="limit", short="l", type="int", default=10, desc="相关搜索词数量,最大20条")
     * @example search-search related swoft2 -l 10 ; 获取 swoft2 相关搜索词
     *
     * @param Input $input
     * @param Output $output
     * @return void
     * @throws XSException|CommandFlagException
     */
    public function related(Input $input, Output $output) {}
    
}

以上示例,基本涉及到了 Swoft 控制台命令组件的各个细节,如果还有不明白的可以到网站留言,我会根据建议进一步完善。