Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

shell

php

stdin

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)

like image 616
cwd Avatar asked Aug 08 '13 23:08

cwd


3 Answers

You can use the select function such as:

$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);
like image 146
Pavel Kurinnoy Avatar answered Nov 19 '22 23:11

Pavel Kurinnoy


stream_get_meta_data helped me :)

And as mentioned in the previous answer by Seth Battin stream_set_blocking($fh, false); works very well 👍

The next code reads data from the command line if provided and skips when it's not.

For example: echo "x" | php render.php and php render.php

In the first case, I provide some data from another stream (I really need to see the changed files from git, something like git status | php render.php.

Here is an example of my solution which works:

$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);
}
like image 37
Yaro Avatar answered Nov 20 '22 01:11

Yaro


Regarding your specific problem of hanging when there is no input: php stream reads are blocking operations by default. You can change that behavior with stream_set_blocking(). Like so:

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

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

like image 3
Seth Battin Avatar answered Nov 19 '22 23:11

Seth Battin