【问题标题】:How can I check if stdin exists in PHP ( php-cgi )?如何检查 PHP ( php-cgi ) 中是否存在标准输入?
【发布时间】:2013-08-10 20:51:52
【问题描述】:

设置和背景

我正在处理需要以/usr/bin/php-cgi 而不是/usr/local/bin/php 运行的脚本,但我无法检查stdin

如果我使用 /usr/local/bin/php 作为解释器,我可以做类似的事情

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

这似乎不适用于 php-cgi - 看起来总是未定义。我检查了man page for 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)

【问题讨论】:

    标签: shell stdin php


    【解决方案1】:

    可以使用select函数如:

    $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);
    

    【讨论】:

    • 完美解决方案!满足我的所有要求
    【解决方案2】:

    关于您在没有输入时挂起的具体问题:默认情况下,php流读取是阻塞操作。您可以使用stream_set_blocking() 更改该行为。像这样:

    $fh = fopen('php://stdin', 'r');
    stream_set_blocking($fh, false);
    $stdin = fgets($fh);
    echo "stdin: '$stdin'";  // immediately returns "stdin: ''"
    

    注意this solution does not work with that magic file handle STDIN.

    【讨论】:

      【解决方案3】:

      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);
      }
      

      【讨论】:

        【解决方案4】:

        问题是您在代码中使用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 语句,但它并没有不断吐出输出 - 我真的相信它是“挂起”试图读取标准输入
        • 奇怪的是,如果我在 while 循环中回显,每次按 Enter 时都会看到不同的内容?因此,如果您写一些东西并按回车键,然后执行ctrl + d,您根本不会得到输出?
        • @cwd - 如果您在没有任何输入的情况下调用脚本,即./myscript.php,那么它将在fgets(...) 停止,直到您按下回车键,这不是您没有得到任何输入的原因吗?如果在循环中添加回声,输出?
        猜你喜欢
        • 1970-01-01
        • 2012-02-03
        • 2012-02-04
        • 2019-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多