如何在 PHP (php-cgi) 中检查 stdin 是否存在?

10

设置和背景

我正在编写一个脚本,需要以/usr/bin/php-cgi而不是/usr/local/bin/php运行,并且我遇到了检查stdin的问题。

如果我使用/usr/local/bin/php作为解释器,我可以进行以下操作:

if defined('STDIN'){ ... }

使用php-cgi似乎不起作用 - 似乎始终为未定义。我查看了php-cgi的手册页面,但没有发现有用的信息。此外,如果我理解正确,STDIN常量是php://stdin的文件句柄。我在某个地方读到过这个常量在php-cgi中不应该被使用。

要求

  • 必须使用shebang #!/usr/bin/php-cgi -q
  • 脚本有时会接收参数
  • 脚本有时会通过STDIN接收输入

当前脚本

#!/usr/bin/php-cgi -q
<?php

$stdin = '';
$fh = fopen('php://stdin', 'r');

if($fh)
{

    while ($line = fgets( $fh )) {
            $stdin .= $line;
    }
    fclose($fh);

}

echo $stdin;

问题行为

这个工作还不错:

$ echo hello | ./myscript.php 
hello

这个东西就是卡住了:

./myscript.php 

这些方法对我不起作用:

  • 检查defined('STDIN') // 总是返回false
  • 查看是否定义了CONTENT_LENGTH
  • 检查变量和常量

我已经将此内容添加到脚本中并以两种方式运行:

print_r(get_defined_constants());
print_r($GLOBALS);
print_r($_COOKIE);
print_r($_ENV);
print_r($_FILES);
print_r($_GET);
print_r($_POST);
print_r($_REQUEST);
print_r($_SERVER);
echo shell_exec('printenv');

我比较了输出结果,结果相同。

我不知道其他检查并通过 php-cgi 获取 stdin 的方法,而不会在不存在时锁定脚本。

/usr/bin/php-cgi -v 输出:PHP 5.4.17 (cgi-fcgi)

4个回答

7

您可以使用选择函数,例如:

$stdin = '';
$fh = fopen('php://stdin', 'r');
$read  = array($fh);
$write = NULL;
$except = NULL;
if ( stream_select( $read, $write, $except, 0 ) === 1 ) {
    while ($line = fgets( $fh )) {
            $stdin .= $line;
    }
}
fclose($fh);

1
完美的解决方案!涵盖了我所有的要求。 - YAMM

3

2

stream_get_meta_data帮了我大忙 :)

正如之前Seth Battin所提到的那样,stream_set_blocking($fh, false);非常有效。

下面的代码从命令行读取数据(如果有提供)并在没有提供时跳过。

例如:echo "x" | php render.phpphp render.php

在第一个例子中,我从另一个流中提供一些数据(我真的需要查看来自git的更改文件,类似于git status | php render.php)。

这是我的解决方案示例,它可以正常工作:

$input = [];

$fp = fopen('php://stdin', 'r+');
$info = stream_get_meta_data($fp);

if (!$info['seekable'] && $fp) {
    while (false !== ($line = fgets($fp))) {
        $input[] = trim($line);
    }
    fclose($fp);
}

1
请注意,该方法无法使用重定向。即 $ ./foo.php< data-file.txt - user3342816

0
问题在于您的代码中使用了while($line = fgets($fh))这部分,导致程序进入了无限循环。
$stdin = '';
$fh = fopen('php://stdin','r');
if($fh) {
  // read *one* line from stdin upto "\r\n"
  $stdin = fgets($fh);
  fclose($fh);
}
echo $stdin;

如果您像这样传递参数echo foo=bar | ./myscript.php,上述代码将起作用,并且在调用./myscript.php时将读取一行。

如果您想要读取更多行并保留原始代码,可以发送退出信号CTRL + D

要获取像这样传递的参数./myscript.php foo=bar,您可以检查$argv变量的内容,其中第一个参数始终是执行脚本的名称:

 ./myscript.php foo=bar

 // File: myscript.php
 $stdin = '';
 for($i = 1; $i < count($argv); i++) {
    $stdin .= $argv[$i];
 }

我不确定这是否解决了任何问题,但也许它能给你一些想法。


谢谢,但我不认为这是问题所在 - 在发布问题之前,我尝试在while循环中放置一个echo语句,它没有持续输出 - 我真的相信它正在“挂起”尝试读取stdin。 - cwd
如果我在while循环中使用echo命令,每次按回车键时看到的内容都不同,这很奇怪吗?因此,如果您输入一些内容并按回车键,然后执行ctrl + d,则根本不会输出回显。 - Cyclonecode
@cwd - 如果您在没有任何输入的情况下调用脚本,即 ./myscript.php,则它将停留在 fgets(...) 直到您按下回车键,这难道不是您在循环内添加 echo 时未能获得任何输出的原因吗? - Cyclonecode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接