请记住,在多进程应用程序(最适合 CLI)中,I/O 操作通常会阻止处理信号。
例如,如果父进程正在等待 fread(STDIN),它将不会处理 SIGCHLD,即使您为此定义了信号处理程序,也要等到 fread 调用返回之后。
在这种情况下,您的解决方案是等待 stream_select() 来确定读取是否会阻塞。关键的是,等待 stream_select() 不会阻止处理信号。
Aurelien
CLI CLI SAPI 定义了一些 I/O 流常量,使命令行编程更容易。
常量 | 描述 |
---|---|
STDIN |
已打开的 <?php stdin 读取单行,可以使用:<?php |
STDOUT |
已打开的 <?php |
STDERR |
已打开的 <?php |
综上所述,不需要自己打开例如 stderr
的流,只需使用常量代替流资源即可。
php -r 'fwrite(STDERR, "stderr\n");'
注意:
如果从
stdin
读取 PHP 脚本,则这些常量不可用。
请记住,在多进程应用程序(最适合 CLI)中,I/O 操作通常会阻止处理信号。
例如,如果父进程正在等待 fread(STDIN),它将不会处理 SIGCHLD,即使您为此定义了信号处理程序,也要等到 fread 调用返回之后。
在这种情况下,您的解决方案是等待 stream_select() 来确定读取是否会阻塞。关键的是,等待 stream_select() 不会阻止处理信号。
Aurelien
STDIN 中的命令行界面数据只有在按下回车键后才可用。
在第一次读取 STDIN 之前添加 "readline_callback_handler_install('', function(){});" 可以捕获单个按键。
注意:这似乎只在 Linux CLI 下有效,在 Apache 或 Windows CLI 中无效。
这可以用来隐藏密码或与 'stream_select' 一起使用以创建非阻塞键盘监视器。
<?php
// 演示:不使用 readline_callback_handler_install('', function(){});
$resSTDIN=fopen("php://stdin","r");
echo("输入 'x'。然后按回车键。");
$strChar = stream_get_contents($resSTDIN, 1);
echo("\n您输入了:".$strChar."\n\n");
fclose($resSTDIN);
// 演示:使用 readline_callback_handler_install('', function(){});
// 此行删除了对 STDIN 上 <CR> 的等待
readline_callback_handler_install('', function(){});
$resSTDIN=fopen("php://stdin","r");
echo("我们现在运行了:readline_callback_handler_install('', function(){});\n");
echo("按 'y' 键");
$strChar = stream_get_contents($resSTDIN, 1);
echo("\n您按下了:".$strChar."\n但不必按 <cr>\n");
fclose($resSTDIN);
readline_callback_handler_remove ();
echo("\n再见\n")
?>
它还隐藏了来自 CLI 的文本,因此可以用于密码隐藏等。
例如
<?php
readline_callback_handler_install('', function(){});
echo("输入密码并按回车键。(不要使用真实的密码!)\n");
echo("密码:");
$strObscured='';
while(true)
{
$strChar = stream_get_contents(STDIN, 1);
if($strChar===chr(10))
{
break;
}
$strObscured.=$strChar;
echo("*");
}
echo("\n");
echo("您输入了:".$strObscured."\n");
?>
以下代码展示了如何测试 STDIN 上的输入。在本例中,我们正在寻找 CSV 数据,因此我们使用 fgetcsv 读取 STDIN,如果它创建了一个数组,我们假设 STDIN 上有 CVS 输入,如果没有创建数组,我们假设 STDIN 没有输入,稍后查找带有 CSV 文件名的参数。
注意,如果没有 stream_set_blocking() 调用,fgetcsv() 会在 STDIN 上挂起,等待用户的输入,这没有用,因为我们正在寻找管道文件。如果它还没有在这里,它就不会来了。
<?php
stream_set_blocking(STDIN, 0);
$csv_ar = fgetcsv(STDIN);
if (is_array($csv_ar)){
print "STDIN 上的 CVS\n";
} else {
print "查找 ARGV 中的 CSV 文件名。\n";
}
?>
在 Linux CLI 下 - STDIN、STDOUT 和 STDERR 可以关闭并重新连接到不同的 php 流,例如文件、管道甚至 UDP socket_stream。(我使用此技术将长时间运行的后台脚本的输出/错误发送到文件,以便在出现问题时进行调试。)
例如:(下面创建/追加文件“/tmp/php_stdout.txt”)
<?php
// 此代码仅在 Linux 的 CLI 环境下有效
// 注意:在关闭 STDOUT 之前,输出不会以 $ 为前缀
// 获取当前控制台的 STDOUT 路径,以便稍后重新连接!
$strOldSTDOUT=(posix_ttyname(STDOUT));
echo("这行文字将输出到当前控制台\r\n");
// 关闭 STDOUT 资源
fclose(STDOUT);
// 将 $STDOUT 重新打开为文件,注意:之后所有 $STDOUT 的使用都会以 $ 为前缀
$STDOUT=fopen("/tmp/php_stdout.txt","a");
echo("这行文字应该追加到 /tmp/php_stdout.txt 文件\r\n");
// 再次关闭 stdout 以便重新连接控制台。注意:我们仍在使用
fclose($STDOUT);
// 使用之前获取的控制台路径
$STDOUT=fopen($strOldSTDOUT,"r+");
echo("我们已经回到控制台了\r\n");
?>