Search code examples
shellstdinphp

How can I check if stdin exists in PHP ( php-cgi )?


Setup and Background

I am working on script that needs to run as /usr/bin/php-cgi instead /usr/local/bin/php and I'm having trouble checking for stdin

If I use /usr/local/bin/php as the interpreter I can do something like

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

This doesn't seem to work with php-cgi - Looks to always be undefined. I checked the man page for php-cgi but didn't find it very helpful. Also, if I understand it correctly, the STDIN constant is a file handle for php://stdin. I read somewhere that constant is not supposed to be available in php-cgi

Requirements

  • The shebang needs to be #!/usr/bin/php-cgi -q
  • The script will sometimes be passed arguments
  • The script will sometimes receive input via STDIN

Current Script

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

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

if($fh)
{

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

}

echo $stdin;

Problematic Behavior

This works OK:

$ echo hello | ./myscript.php 
hello

This just hangs:

./myscript.php 

These things don't work for me:

  • Checking defined('STDIN') // always returns false
  • Looking to see if CONTENT_LENGTH is defined
  • Checking variables and constants

I have added this to the script and run it both ways:

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

I then diff'ed the output and it is the same.

I don't know any other way to check for / get stdin via php-cgi without locking up the script if it does not exist.

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


Solution

  • The problem is that you create a endless loop with the while($line = fgets($fh)) part in your code.

    $stdin = '';
    $fh = fopen('php://stdin','r');
    if($fh) {
      // read *one* line from stdin upto "\r\n"
      $stdin = fgets($fh);
      fclose($fh);
    }
    echo $stdin;
    

    The above would work if you're passing arguments like echo foo=bar | ./myscript.php and will read a single line when you call it like ./myscript.php If you like to read more lines and keep your original code you can send a quit signal CTRL + D

    To get parameters passed like ./myscript.php foo=bar you could check the contents of the $argv variable, in which the first argument always is the name of the executing script:

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

    I'm not sure that this solves anything but perhaps it give you some ideas.