大家好,我是老码,今天咱们来聊聊 WordPress CLI (简称 WP-CLI) 里一个挺有意思的家伙:WP_CLICommandWithSubcommands
类。这家伙专门负责构建那些带有子命令的复杂命令,就像一个命令界的俄罗斯套娃,一层套一层,功能强大得很。
咱们先来热个身,想想我们平时用 WP-CLI 都干些啥?比如 wp plugin activate
,wp plugin deactivate
,这里的 plugin
就是主命令,activate
和 deactivate
就是子命令。 今天,我们的目标就是搞明白,如何像 WP-CLI 的开发者一样,也写出这种酷炫的、带有子命令的命令。
一、WP_CLICommandWithSubcommands
是个啥?
简单来说,WP_CLICommandWithSubcommands
是一个抽象类,它继承自 WP_CLICommand
。这意味着它已经具备了 WP_CLICommand
的所有能力(比如能被 WP-CLI 识别和执行),并在其基础上,增加了处理子命令的功能。
核心思想是,主命令本身并不直接执行什么操作,而是作为一个“容器”,将具体的任务分发给不同的子命令去执行。
二、构建带子命令的命令:理论先行
在动手写代码之前,先捋一下思路。我们需要做哪些事情?
- 定义主命令类: 这个类需要继承
WP_CLICommandWithSubcommands
。 - 注册子命令: 将子命令类注册到主命令类中。
- 定义子命令类: 这些类需要继承
WP_CLICommand
,并且实现具体的业务逻辑。 - 处理子命令的调用:
WP_CLICommandWithSubcommands
会自动处理子命令的调用,并将参数传递给相应的子命令类。
三、代码实战:一步一步来
咱们来创建一个名为 wp awesome
的主命令,它有两个子命令:wp awesome hello
和 wp awesome goodbye
。
1. 定义主命令类
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* Awesome_Command constructor.
*/
public function __construct() {
parent::__construct();
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
这段代码做了什么?
- 我们创建了一个名为
Awesome_Command
的类,它继承自WP_CLICommandWithSubcommands
。 - 构造函数里调用了父类的构造函数,这是必须的。
- 使用
WP_CLI::add_command()
将awesome
命令注册到 WP-CLI 中,并指定了对应的类。
现在,如果你在命令行输入 wp awesome
,WP-CLI 会告诉你这个命令存在,但是啥也干不了,因为它还没有子命令。
2. 定义子命令类
我们先定义 wp awesome hello
子命令:
<?php
/**
* 打印 Hello, World!
*/
class Awesome_Hello_Command extends WP_CLICommand {
/**
* 打印 Hello, World!
*
* @subcommand hello
*/
public function hello( $args, $assoc_args ) {
WP_CLI::line( 'Hello, World!' );
}
}
WP_CLI::add_command( 'awesome hello', 'Awesome_Hello_Command' );
这段代码定义了一个名为 Awesome_Hello_Command
的类,它继承自 WP_CLICommand
。hello()
方法就是具体的业务逻辑,它会打印 "Hello, World!"。 @subcommand hello
这个 DocBlock 注释非常重要,它告诉 WP-CLI 这个方法应该作为 awesome
命令的 hello
子命令来调用。
我们再定义 wp awesome goodbye
子命令:
<?php
/**
* 打印 Goodbye, World!
*/
class Awesome_Goodbye_Command extends WP_CLICommand {
/**
* 打印 Goodbye, World!
*
* @subcommand goodbye
*/
public function goodbye( $args, $assoc_args ) {
WP_CLI::line( 'Goodbye, World!' );
}
}
WP_CLI::add_command( 'awesome goodbye', 'Awesome_Goodbye_Command' );
这段代码和 Awesome_Hello_Command
类似,只是打印的内容变成了 "Goodbye, World!"。
3. 注册子命令到主命令(改进版)
现在,虽然我们已经定义了子命令类,并且使用 WP_CLI::add_command()
注册了它们,但是,按照 WP_CLICommandWithSubcommands
的设计思路,我们更希望 只 注册主命令,让它自己去发现和管理子命令。
所以,我们修改 Awesome_Command
类:
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* Awesome_Command constructor.
*/
public function __construct() {
parent::__construct();
WP_CLI::add_command( 'awesome hello', 'Awesome_Hello_Command' );
WP_CLI::add_command( 'awesome goodbye', 'Awesome_Goodbye_Command' );
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
在这个改进的版本中,我们在 Awesome_Command
的构造函数中,使用 WP_CLI::add_command()
注册了子命令。 这样,主命令就“知道”了它有哪些子命令。
更优雅的子命令注册方式:使用方法名称
WP_CLICommandWithSubcommands
还有一个更优雅的方式来注册子命令:使用方法名称。 我们可以直接在 Awesome_Command
类中定义方法,并使用 @subcommand
注释来标记它们。
首先,删除之前 Awesome_Hello_Command
和 Awesome_Goodbye_Command
类的定义,以及它们对应的 WP_CLI::add_command()
调用。
然后,修改 Awesome_Command
类:
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* 打印 Hello, World!
*
* @subcommand hello
*/
public function hello( $args, $assoc_args ) {
WP_CLI::line( 'Hello, World!' );
}
/**
* 打印 Goodbye, World!
*
* @subcommand goodbye
*/
public function goodbye( $args, $assoc_args ) {
WP_CLI::line( 'Goodbye, World!' );
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
现在,Awesome_Command
类中包含了 hello()
和 goodbye()
两个方法,它们分别对应 wp awesome hello
和 wp awesome goodbye
两个子命令。 这种方式更加简洁,也更符合面向对象的编程思想。
4. 测试你的命令
将上面的代码保存到你的 WP-CLI 命令目录(通常是 ~/.wp-cli/commands
),然后分别执行:
wp awesome hello
wp awesome goodbye
如果一切顺利,你应该能看到 "Hello, World!" 和 "Goodbye, World!" 被打印出来。
四、进阶:参数传递与处理
子命令肯定不能只是简单地打印字符串,它们需要处理参数。 WP_CLICommand
类已经提供了强大的参数处理机制。
1. 位置参数
位置参数是指按照顺序传递的参数。例如:
wp awesome greet 老码
这里的 "老码" 就是一个位置参数。
修改 Awesome_Command
类中的 hello()
方法,使其接受一个位置参数:
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* 打印 Hello, [name]!
*
* @subcommand hello
* @synopsis <name>
*/
public function hello( $args, $assoc_args ) {
$name = $args[0];
WP_CLI::line( "Hello, {$name}!" );
}
/**
* 打印 Goodbye, World!
*
* @subcommand goodbye
*/
public function goodbye( $args, $assoc_args ) {
WP_CLI::line( 'Goodbye, World!' );
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
@synopsis <name>
这个 DocBlock 注释告诉 WP-CLI,hello
子命令接受一个名为<name>
的位置参数。$args
数组包含了所有位置参数,$args[0]
就是第一个位置参数,也就是我们传入的 "老码"。
现在,执行 wp awesome hello 老码
,你应该能看到 "Hello, 老码!" 被打印出来。
2. 关联参数
关联参数是指以 --key=value
的形式传递的参数。例如:
wp awesome greet --name=老码 --age=30
这里的 --name=老码
和 --age=30
就是关联参数。
修改 Awesome_Command
类中的 hello()
方法,使其接受一个关联参数:
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* 打印 Hello, [name]!
*
* @subcommand hello
* @synopsis [--name=<name>]
*/
public function hello( $args, $assoc_args ) {
$name = WP_CLIUtilsget_flag_value( $assoc_args, 'name', 'World' );
WP_CLI::line( "Hello, {$name}!" );
}
/**
* 打印 Goodbye, World!
*
* @subcommand goodbye
*/
public function goodbye( $args, $assoc_args ) {
WP_CLI::line( 'Goodbye, World!' );
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
@synopsis [--name=<name>]
这个 DocBlock 注释告诉 WP-CLI,hello
子命令接受一个名为--name
的关联参数。$assoc_args
数组包含了所有关联参数,WP_CLIUtilsget_flag_value()
函数可以方便地获取关联参数的值,如果参数不存在,则返回默认值 "World"。
现在,执行 wp awesome hello --name=老码
,你应该能看到 "Hello, 老码!" 被打印出来。 如果执行 wp awesome hello
,则会打印 "Hello, World!"。
五、高级技巧:参数验证与提示
WP-CLI 还提供了强大的参数验证和提示功能,可以帮助用户正确地使用你的命令。
1. 参数验证
可以使用 @validate
DocBlock 注释来指定一个验证函数,用于验证参数的值。
例如,我们希望 age
参数必须是一个数字:
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* 打印 Hello, [name]!
*
* @subcommand hello
* @synopsis [--name=<name>] [--age=<age>]
* @validate age is_numeric
*/
public function hello( $args, $assoc_args ) {
$name = WP_CLIUtilsget_flag_value( $assoc_args, 'name', 'World' );
$age = WP_CLIUtilsget_flag_value( $assoc_args, 'age' );
WP_CLI::line( "Hello, {$name}!" );
if ( $age ) {
WP_CLI::line( "You are {$age} years old." );
}
}
/**
* 打印 Goodbye, World!
*
* @subcommand goodbye
*/
public function goodbye( $args, $assoc_args ) {
WP_CLI::line( 'Goodbye, World!' );
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
@validate age is_numeric
这个 DocBlock 注释告诉 WP-CLI,age
参数的值必须是一个数字。如果用户传递了一个非数字的值,WP-CLI 会显示一个错误信息。
2. 参数提示
可以使用 @suggest
DocBlock 注释来为参数提供提示。
例如,我们希望为 name
参数提供一些建议的值:
<?php
/**
* Awesome 命令,演示如何使用子命令.
*/
class Awesome_Command extends WP_CLICommandWithSubcommands {
/**
* 打印 Hello, [name]!
*
* @subcommand hello
* @synopsis [--name=<name>]
* @suggest --name=老码,--name=小李,--name=张三
*/
public function hello( $args, $assoc_args ) {
$name = WP_CLIUtilsget_flag_value( $assoc_args, 'name', 'World' );
WP_CLI::line( "Hello, {$name}!" );
}
/**
* 打印 Goodbye, World!
*
* @subcommand goodbye
*/
public function goodbye( $args, $assoc_args ) {
WP_CLI::line( 'Goodbye, World!' );
}
}
WP_CLI::add_command( 'awesome', 'Awesome_Command' );
@suggest --name=老码,--name=小李,--name=张三
这个 DocBlock 注释告诉 WP-CLI,当用户输入wp awesome hello --name=
时,应该显示 "老码"、"小李" 和 "张三" 作为建议的值。
六、总结
今天我们深入探讨了 WP_CLICommandWithSubcommands
类,学习了如何构建带有子命令的复杂命令。
特性 | 描述 |
---|---|
继承 | 主命令类必须继承自 WP_CLICommandWithSubcommands 。 |
子命令注册 | 可以通过在主命令类的构造函数中使用 WP_CLI::add_command() 注册子命令,也可以直接在主命令类中定义方法,并使用 @subcommand 注释来标记它们。 |
参数传递 | 位置参数通过 $args 数组传递,关联参数通过 $assoc_args 数组传递。 可以使用 WP_CLIUtilsget_flag_value() 函数方便地获取关联参数的值。 |
参数验证 | 可以使用 @validate DocBlock 注释来指定一个验证函数,用于验证参数的值。 |
参数提示 | 可以使用 @suggest DocBlock 注释来为参数提供提示。 |
DocBlock 注释的重要性 | DocBlock 注释是 WP-CLI 理解命令结构和参数的关键。 @subcommand 、@synopsis 、@validate 和 @suggest 等注释都非常重要。 |
优雅的子命令注册方式 | 直接在主命令类中使用 @subcommand 注释定义子命令方法,更加简洁和面向对象。 |
掌握了这些知识,你就可以轻松地构建出功能强大的 WP-CLI 命令,提高你的 WordPress 开发效率。
希望今天的讲座对你有所帮助!下次再见!