分析 WordPress `WP_CLI` 类中的 `confirm()` 方法源码:如何请求用户确认。

各位观众,掌声鼓励一下!今天咱们来聊聊 WordPress 命令行工具 WP-CLI 里的 confirm() 方法,看看它是怎么“逼着”用户说“Yes, I do!”的,哦不,是“Yes, I am sure!”。

一、 WP-CLI confirm() 方法:确认你的选择!

在执行一些具有破坏性的操作时,比如删除数据,WP-CLI 为了防止你手抖,会跳出来问你一句:“Are you sure? (y/n)”. 这就是 confirm() 方法的功劳。它负责向用户展示提示信息,并等待用户输入 yn (或者其本地化版本) 来确认或取消操作。

二、源码解剖:confirm() 的内部机制

我们来看看 WP_CLI 类中的 confirm() 方法的源码(简化版,忽略了一些细节):

<?php

namespace WP_CLI;

class WP_CLI {

    /**
     * Asks the user to confirm something.
     *
     * @param string $question Question to ask the user.
     * @param array  $options  Optional. Array of options.
     *                         'default' - Default answer (true/false).
     * @return bool True if the user confirmed, false otherwise.
     */
    public static function confirm( $question, $options = array() ) {
        $default = isset( $options['default'] ) ? $options['default'] : false;

        WP_CLI::line( $question . ' [y/n]: ' );

        $answer = WP_CLI::read_line();

        if ( ! $answer ) {
            return $default;
        }

        $accepted = array( 'y', 'yes' );
        $rejected = array( 'n', 'no' );

        // Localize accepted and rejected responses.
        $accepted = array_merge( $accepted, WP_CLI::get_localized_keys( 'yes' ) );
        $rejected = array_merge( $rejected, WP_CLI::get_localized_keys( 'no' ) );

        $answer = strtolower( trim( $answer ) );

        if ( in_array( $answer, $accepted, true ) ) {
            return true;
        }

        if ( in_array( $answer, $rejected, true ) ) {
            return false;
        }

        WP_CLI::warning( 'Please answer 'y' or 'n'.' );

        return WP_CLI::confirm( $question, $options ); // Recursive call
    }

    /**
     * Reads a line from stdin.
     *
     * @return string
     */
    public static function read_line() {
        return trim( fgets( STDIN ) );
    }

    /**
     * Outputs a string to STDOUT, without a trailing newline.
     *
     * @param string $str String to output.
     */
    public static function line( $str = '' ) {
        fwrite( STDOUT, $str . PHP_EOL );
    }

    /**
     * Outputs a string to STDERR in yellow.
     *
     * @param string $str String to output.
     */
    public static function warning( $str ) {
        fwrite( STDERR, WP_CLI::colorize( "%yWarning: {$str}%n" ) . PHP_EOL );
    }

    /**
     * Colorize a string.
     *
     * @param string $string String to colorize.
     * @return string Colorized string.
     */
    public static function colorize( $string ) {
        $string = str_replace( '%y', "33[33m", $string ); // Yellow
        $string = str_replace( '%n', "33[0m", $string );  // Reset color
        return $string;
    }

    /**
     * Get localized keys.
     *
     * @param string $key Key to localize.
     * @return array Array of localized keys.
     */
    public static function get_localized_keys( $key ) {
        // In a real WP-CLI environment, this would pull from localization files.
        // For this example, we'll just return some common translations.
        $translations = [
            'yes' => ['sí', 'ja'],
            'no' => ['no', 'nein'],
        ];

        return isset($translations[$key]) ? $translations[$key] : [];
    }
}

让我们逐行分析:

  1. public static function confirm( $question, $options = array() ): 定义了 confirm() 方法,接受两个参数:

    • $question: 要向用户提出的问题,比如 "Are you sure you want to delete this file?"。
    • $options: 一个可选的数组,用于配置 confirm() 的行为。目前只有一个键 default,用于设置默认的回答。
  2. $default = isset( $options['default'] ) ? $options['default'] : false;: 如果 $options 数组中设置了 default 键,则使用其值作为默认答案;否则,默认答案为 false。 这意味着,如果用户直接按回车,而没有输入任何内容,那么 confirm() 将返回 false (除非你设置了 defaulttrue)。

  3. WP_CLI::line( $question . ' [y/n]: ' );: 使用 WP_CLI::line() 方法将问题显示到控制台。 注意,这里在问题后面加上了 [y/n]:,提示用户输入 ynWP_CLI::line() 实际上就是简单地调用 fwrite(STDOUT, $str . PHP_EOL),将字符串输出到标准输出。

  4. $answer = WP_CLI::read_line();: 使用 WP_CLI::read_line() 方法读取用户从控制台输入的一行文本。WP_CLI::read_line() 实际上是调用 trim( fgets( STDIN ) ),从标准输入读取一行,并去除首尾的空格。

  5. if ( ! $answer ) { return $default; }: 如果用户直接按了回车,没有输入任何内容,则返回默认答案 $default

  6. $accepted = array( 'y', 'yes' ); $rejected = array( 'n', 'no' );: 定义了两个数组,分别包含表示“接受”和“拒绝”的字符串。

  7. $accepted = array_merge( $accepted, WP_CLI::get_localized_keys( 'yes' ) ); $rejected = array_merge( $rejected, WP_CLI::get_localized_keys( 'no' ) );: 这里是关键!为了支持多语言,confirm() 方法会尝试获取“yes”和“no”的本地化版本,并将它们添加到 $accepted$rejected 数组中。 WP_CLI::get_localized_keys() 函数(在真实的 WP-CLI 中会从本地化文件中读取)返回一个数组,包含“yes”和“no”的各种语言的翻译。这里为了演示,我们简单地添加了 "sí" (西班牙语) 和 "ja" (德语)。

  8. $answer = strtolower( trim( $answer ) );: 将用户输入的答案转换为小写,并去除首尾的空格,以便进行比较。

  9. if ( in_array( $answer, $accepted, true ) ) { return true; }: 如果用户输入的答案在 $accepted 数组中,则返回 true,表示用户确认了操作。 in_array() 函数的第三个参数 true 表示使用严格比较 (类型和值都要相等)。

  10. if ( in_array( $answer, $rejected, true ) ) { return false; }: 如果用户输入的答案在 $rejected 数组中,则返回 false,表示用户取消了操作。

  11. WP_CLI::warning( 'Please answer 'y' or 'n'.' );: 如果用户输入的答案既不在 $accepted 数组中,也不在 $rejected 数组中,则显示一个警告信息,提示用户输入 ynWP_CLI::warning() 方法使用 fwrite(STDERR, WP_CLI::colorize( "%yWarning: {$str}%n" ) . PHP_EOL) 将警告信息输出到标准错误输出,并使用 WP_CLI::colorize() 方法将警告信息着色为黄色。

  12. return WP_CLI::confirm( $question, $options ); // Recursive call: 如果用户输入的答案无效,则递归调用 confirm() 方法,重新提示用户输入。 这是一个递归调用,直到用户输入有效的答案为止。

三、代码示例:如何使用 confirm()

<?php

require_once 'wp-cli.php'; // 引入 WP-CLI 类 (假设你已经定义了)

if ( WP_CLI::confirm( 'Are you sure you want to delete all the kittens?' ) ) {
    WP_CLI::line( 'Deleting all the kittens... (just kidding!)' );
    // 在这里执行删除所有 kittens 的操作 (当然,我们只是开玩笑)
} else {
    WP_CLI::line( 'Operation cancelled.' );
}

// 使用 default 选项
if ( WP_CLI::confirm( 'Do you want to enable debug mode?', ['default' => true] ) ) {
    WP_CLI::line( 'Debug mode enabled.' );
} else {
    WP_CLI::line( 'Debug mode disabled.' );
}

// 使用本地化keys,假设用户设置了语言环境为德语
if ( WP_CLI::confirm( 'Möchten Sie wirklich alle Daten löschen?', [] ) ) {
    WP_CLI::line( 'Alle Daten werden gelöscht...' );
} else {
    WP_CLI::line( 'Vorgang abgebrochen.' );
}

这段代码演示了如何使用 confirm() 方法。 第一个例子询问用户是否要删除所有的 kittens (当然,我们只是开玩笑)。 第二个例子询问用户是否要启用 debug mode,并设置了 default 选项为 true。 第三个例子使用了德语的提问,用户可以使用 "ja" 来确认。

四、confirm() 的优点和缺点

优点:

  • 防止误操作: confirm() 方法可以有效地防止用户误操作,避免意外删除数据或其他具有破坏性的操作。
  • 用户体验: 通过友好的提示信息,confirm() 方法可以提高用户体验,让用户更加清楚地了解他们正在执行的操作。
  • 多语言支持: confirm() 方法支持多语言,可以根据用户的语言环境显示不同的提示信息。
  • 代码简洁: confirm() 方法的实现非常简洁,易于理解和使用。

缺点:

  • 需要用户交互: confirm() 方法需要用户交互,这可能会降低脚本的自动化程度。 在某些情况下,你可能需要使用 --yes--force 等选项来绕过 confirm() 方法。
  • 可能导致阻塞: 如果用户没有及时响应,confirm() 方法可能会导致脚本阻塞。

五、绕过 confirm() 的方法

在某些情况下,你可能需要绕过 confirm() 方法,例如在自动化脚本中。 WP-CLI 提供了一些选项来实现这一点:

  • --yes-y 选项: 使用 --yes-y 选项可以自动回答 "yes" 到所有的 confirm() 提示。 例如:wp plugin delete akismet --yes
  • --force 选项: --force 选项通常用于强制执行操作,而无需用户确认。 例如:wp plugin uninstall akismet --force

注意: 在使用这些选项时要格外小心,确保你真的了解你正在执行的操作,因为一旦执行,可能无法撤销。

六、confirm() 的高级用法

虽然 confirm() 方法很简单,但你也可以通过一些技巧来实现更高级的用法:

  • 自定义问题: 你可以根据不同的情况,自定义 confirm() 方法的问题,以便向用户提供更具体的信息。
  • 动态生成问题: 你可以根据程序的状态,动态生成 confirm() 方法的问题,例如显示要删除的文件名或数据库表的名称。
  • 结合其他 WP-CLI 命令: 你可以将 confirm() 方法与其他 WP-CLI 命令结合使用,例如在删除插件之前,先询问用户是否要备份数据库。

七、总结

WP_CLI::confirm() 方法是一个简单而强大的工具,可以帮助你防止误操作,提高用户体验,并支持多语言。 虽然它有一些缺点,但通过合理的使用和结合其他 WP-CLI 命令,你可以充分利用它的优点,使你的 WP-CLI 脚本更加健壮和用户友好。

希望今天的讲座对大家有所帮助! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注