2024 年 PHP 日本大会

输入/输出流

CLI CLI SAPI 定义了一些 I/O 流常量,使命令行编程更容易。

CLI 特定常量
常量 描述
STDIN

已打开的 stdin 流。这避免了使用以下代码打开它:

<?php
$stdin
= fopen('php://stdin', 'r');
?>
如果要从 stdin 读取单行,可以使用:
<?php
$line
= trim(fgets(STDIN)); // 从 STDIN 读取一行
fscanf(STDIN, "%d\n", $number); // 从 STDIN 读取数字
?>

STDOUT

已打开的 stdout 流。这避免了使用以下代码打开它:

<?php
$stdout
= fopen('php://stdout', 'w');
?>

STDERR

已打开的 stderr 流。这避免了使用以下代码打开它:

<?php
$stderr
= fopen('php://stderr', 'w');
?>

综上所述,不需要自己打开例如 stderr 的流,只需使用常量代替流资源即可。

php -r 'fwrite(STDERR, "stderr\n");'
不需要显式关闭这些流,因为脚本结束时 PHP 会自动关闭它们。

注意:

如果从 stdin 读取 PHP 脚本,则这些常量不可用。

添加注释

用户贡献的注释 4 个注释

30
Aurelien Marchand
13 年前
请记住,在多进程应用程序(最适合 CLI)中,I/O 操作通常会阻止处理信号。

例如,如果父进程正在等待 fread(STDIN),它将不会处理 SIGCHLD,即使您为此定义了信号处理程序,也要等到 fread 调用返回之后。

在这种情况下,您的解决方案是等待 stream_select() 来确定读取是否会阻塞。关键的是,等待 stream_select() 不会阻止处理信号。

Aurelien
17
phil_php at zieaon dot com
6 年前
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");
?>
11
ecrist at secure-computing dot net
13 年前
以下代码展示了如何测试 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";
}
?>
0
phil_php at zieaon dot com
4 年前
在 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");

?>
To Top