乐趣区

关于php:PHP的命令行扩展Readline相关函数学习

PHP 作为一个 Web 开发语言,相对来说,命令行程序并不是它的主战场。所以很多年老的 PHP 开发者可能连命令行脚本都没有写过,更别提交互式的命令操作了。而明天,咱们带来的这个扩大就是针对 PHP 的交互式命令行操作的。

readline 扩大函数实现了拜访 GNU Readline 库的接口。这些函数提供了可编辑的命令行。一个例子是在 Bash 中容许你应用箭头按键来插入字符或者翻看历史命令。因为这个库的交互个性,这个性能在你写的 Web 程序中没多大用处,然而当你写的脚本被用在命令行中时十分有用。

Readline 扩大的装置

Readline 扩大曾经退出了 PHP 的官网安装包中,如果是新的 PHP 环境,那么在编译的时候加上 –with-readline 即可。另外,咱们还须要装置操作系统的 Readline 库。当然,如果曾经是失常运行的 PHP,也能够从新编译一下。

# yum install -y readline-devel
# ./congiure xxxx --with-readline

默认状况下,如果没有在编译时减少 –whit-readline,Readline 的一些函数也是能够应用的,不过它们调用的是零碎的 libedit 库。有一些函数,比方 readline_list_history() 这种函数是无奈应用的。要想残缺的应用 Readline 扩大的能力,那么还是须要装置操作系统的 libreadline 库(下面 yum 装置的那个 readline-devel)并在 PHP 中进行相应参数的编译装置。

根本函数操作

Readline 扩大提供的函数不多,也十分的简略易用。

读取一行

$line = readline("请输出命令:"); // 读取命令行交互信息
echo $line, PHP_EOL; // aaa

运行 PHP 代码后,咱们就进入了命令提示符期待状态,并且会提醒“请输出命令:”,当咱们输出了 aaa 并回车之后,输出的内容就保留到了 $line 变量中。

命令历史列表相干操作

Readline 很弱小的一个性能就是它自带一套命令历史记录的性能。不过这个须要咱们本人手动地将命令退出到命令历史中。

$line = readline("请输出命令:"); // 读取命令行交互信息
if (!empty($line)) {readline_add_history($line); // 须要手动退出到命令历史记录中
}
echo $line, PHP_EOL; // aaa

$line = readline("请输出命令:");
if (!empty($line)) {readline_add_history($line);
}

// 命令历史记录列表
print_r(readline_list_history());
// Array
// (//     [0] => aaa
//     [1] => bbb
// )

应用 readline_add_history() 函数,就能够将一条命令退出到命令历史记录中,而后应用 readline_list_history() 就可能打印出咱们之前在交互式环境中发送过的命令记录。当然,如果只是这样简略的保留再打印那就没意思了,它还能将这些历史信息保留到内部文件进行存储。

// 将命令历史记录写入到一个文件中
readline_write_history('./readline_history');
// ./readline_history 中
// _HiStOrY_V2_
// aaa
// bbb

// 清理命令历史记录
readline_clear_history();
print_r(readline_list_history());
// Array
// (//)

// 从文件中读取命令历史记录
readline_read_history('./readline_history');
print_r(readline_list_history());
// Array
// (//     [0] => bbb
//     [1] => bbb
// )

咱们应用 readline_write_history() 函数将以后的命令历史记录保留到一个文件中,而后应用 readline_clear_history() 清理掉目前命令历史记录列表中的内容,这个时候打印 readline_list_history() 的话外面曾经没有任何货色了。接着,咱们再应用 readline_read_history() 将命令的历史记录从文件中加载回来进行还原。这一套性能是不是就十分有意思了,咱们能够记录客户的所有命令操作,不论是平安审查还是事件回放,都十分有用。

查看 Readline 状态

// 以后命令行外部的变量信息
print_r(readline_info());
// Array
// (//     [line_buffer] => bbb
//     [point] => 3
//     [end] => 3
//     [mark] => 0
//     [done] => 1
//     [pending_input] => 0
//     [prompt] => 请输出命令://     [terminal_name] => xterm-256color
//     [completion_append_character] =>
//     [completion_suppress_append] =>
//     [library_version] => 7.0
//     [readline_name] => other
//     [attempted_completion_over] => 0
// )

readline_info() 函数就比较简单了,咱们能够看到最初一条交互式命令的信息,外面包含了命令输出的内容 line_buffer,内容长度 point,提示信息 prompt 等内容。

命令提醒成果

在 Linux 等操作系统上,咱们想不起一个命令的全拼没关系,只须要记住它的前几个字符而后按两个 Tab 键就能够失去相干的命令提醒了。Readline 扩大库当然也为咱们筹备了这样的性能。

// 相似于命令行中按 Tab 键的提醒成果
readline_completion_function(function ($input, $index) {$commands = ['next', 'exit', 'quit'];
    $matches = [];
    if ($input) {
        // 如果关键字蕴含在命令中,提醒命令信息
        foreach ($commands as $c) {if (strpos($c, $input) !== false) {$matches[] = $c;
            }
        }
    }else{$matches = $commands;}
    return $matches;
});

// 应用 Tab 键测试一下吧
$line = trim(readline("请输出命令:"));
if (!empty($line)) {readline_add_history($line);
}
echo $line, PHP_EOL; // 以后输出的命令信息
// 如果命令是 exit 或者 quit,就退出程序执行
if($line == 'exit' || $line == 'quit'){exit;}

readline_completion_function() 函数会接管一个回调函数,当在交互式命令行模式下,也就是 readline 函数调用时,按下 Tab 键的时候,就会进入到这个函数的回调函数中。\$input 是以后曾经输出内容的值,$index 是第几个字符。咱们在这个回调函数中定义了几个默认的命令,当你键入一个 n 时间接按 Tab 键,程序就是提醒出残缺的 next 命令进去。当然,多个雷同的字母结尾的都是能够通过这个 $matches 数组返回出现的。

此外,在这段代码中,如果咱们输出了 exit 或者 quit。将退出程序的运行。

字符回调操作相干示例

最初几个函数咱们将通过一个简单的小测试来学习。

// 输入的内容进入这个回调函数中
function rl_callback($ret)
{
    global $c, $prompting;

    echo "您输出的内容是: $ret\n";
    $c++;

    readline_add_history($ret);

    // 限度了就调用 10 次,也能够通过命令行输出的内容来判断,比方下面的 exit 那种进行退出
    if ($c > 10) {
        $prompting = false;
        // 移除上一个装置的回调函数句柄并且复原终端设置
        readline_callback_handler_remove();} else {
        // 持续进行递归回调
        readline_callback_handler_install("[$c] 输出点什么内容:", 'rl_callback');

    }
}

$c = 1;
$prompting = true;

// 初始化一个 readline 回调接口,而后终端输入提示信息并立刻返回,须要期待 readline_callback_read_char() 函数调用后才会进入到回调函数中
readline_callback_handler_install("[$c] 输出点什么内容:", 'rl_callback');

// 当 $prompting 为 ture 时,始终期待输出信息
while ($prompting) {
    $w = null;
    $e = null;
    $r = array(STDIN);
    $n = stream_select($r, $w, $e, null);
    if ($n && in_array(STDIN, $r)) {
        // 当一个行被接管时读取一个字符并且告诉 readline 调用回调函数
        readline_callback_read_char();}
}

echo "完结,实现所有输出!\n";
// [1] 输出点什么内容: A
// 您输出的内容是: A
// [2] 输出点什么内容: B
// 您输出的内容是: B
// [3] 输出点什么内容: C
// 您输出的内容是: C
// [4] 输出点什么内容: D
// 您输出的内容是: D
// [5] 输出点什么内容: E
// 您输出的内容是: E
// [6] 输出点什么内容: F
// 您输出的内容是: F
// [7] 输出点什么内容: G
// 您输出的内容是: G
// [8] 输出点什么内容: H
// 您输出的内容是: H
// [9] 输出点什么内容: I
// 您输出的内容是: I
// [10] 输出点什么内容: J
// 您输出的内容是: J
// 完结,实现所有输出!print_r(readline_list_history());
// Array
// (//     [0] => A
//     [1] => B
//     [2] => C
//     [3] => D
//     [4] => E
//     [5] => F
//     [6] => G
//     [7] => H
//     [8] => I
//     [9] => J
// )

首先,咱们先不论下面的这个自定义的函数,间接向下看到 readline_callback_read_char()。它的作用是当一个行被接管时读取一个字符并且告诉 readline 调用回调函数。也就是当一行输出实现后,键入了回车之后,这个函数将告诉 Readline 组件去调用 readline_callback_handler_install() 注册的回调函数。

readline_callback_handler_install() 函数的性能是初始化一个 readline 回调接口,而后终端输入提示信息并立刻返回,如果在回调函数中不进行什么操作的话,这个函数就只是输入一个提醒就完结了。在咱们例子中的这个回调函数 rl_callback() 中,咱们依据以后接管命令的次数,判断如果接管的命令在十次内,则持续接管命令直到十次命令为止就调用 readline_callback_handler_remove() 移除上一个 readline_callback_handler_install() 装置的回调并复原终端的默认设置。

最初执行的后果就是正文中的内容,大家也能够本人复制下代码后运行调试,只有本人进行过的调试能力了解的更加深刻。

总结

Readline 很弱小,而且也是 PHP 默认安装包中自带的扩大。个别被退出默认的扩大都是通过工夫测验而且十分有用的扩大,大家能够依据这些内容再进行更加深刻的学习并使用到实战中。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202006/source/PHP%E7%9A%84%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%A9%E5%B1%95Readline%E7%9B%B8%E5%85%B3%E5%87%BD%E6%95%B0%E5%AD%A6%E4%B9%A0.php

参考文档:

https://www.php.net/manual/zh/book.readline.php
https://www.php.cn/php-weizijiaocheng-339883.html

===========

各自媒体平台均可搜寻【硬核项目经理】

退出移动版