Arkandel
Arkandel

Reputation: 349

PHP - detect STDIO input

What I want is being able to optionally pipe STDIO to a PHP script. If not, it'll take input from a file instead. So sometimes I'll simply run the script, other times I'll do something like

grep text logfile | php parseLog.php

I have a loop very much like this and it works fine when STDIO exists:

while (FALSE !== ($line = fgets(STDIN)))
{
        $customLogArr[]=$line;
}

When there's no STDIO though it halts waiting for some, it doesn't even enter the loop.

What I'd like to do is be able to detect if I have STDIO input or not. Is there a way to do that?

Upvotes: 8

Views: 1611

Answers (6)

KEINOS
KEINOS

Reputation: 1224

Using posix_isatty() function might be a little simpler.

<?php

/**
 * parseLog.php
 */

echo (posix_isatty(STDIN)) ? 'no stdin' . PHP_EOL : file_get_contents('php://stdin');

$ echo 'foo' > ./logfile.txt

$ cat logfile.txt | php parseLog.php
foo

$ php parseLog.php
no stdin

posix_isatty(STDIN) determines if the STDIN is open AND connected to a terminal. Thus, while receiving data from the STDIN, it will return false.

Upvotes: 3

helvete
helvete

Reputation: 2673

Consider a script like this:

#!/usr/bin/env php                                                              
<?php                                                                           
                                                                                
$fhandle = fopen("php://stdin", 'r');                                           
$stdinData = ftell($fhandle) === false // strict comparison required                             
    ? null                                                                      
    : stream_get_contents($fhandle);                                            
var_dump($stdinData);

The sript outputs this when no stdin data provided:

$ ./test.php 
NULL

The sript outputs this when stdin data provided:

$ ./test.php < ./test.php 
string(173) "#!/usr/bin/env php
<?php

$fhandle = fopen("php://stdin", 'r');
$stdinData = ftell($fhandle) === false // strict comparison required
    ? null
    : stream_get_contents($fhandle);
var_dump($stdinData);
"

The important part is to use strict comparison (===) as ftell returns pointer position that has a value of 0 in case the handle has just been initialized.

For PHP 7.3 and earlier slightly different approach has to be utilized:

#!/usr/bin/env php
<?php

stream_set_blocking(STDIN, 0);
$stdinData = ftell(STDIN) === false // strict comparison required
    ? null
    : stream_get_contents(STDIN);
var_dump($stdinData);

Upvotes: 0

MrMesees
MrMesees

Reputation: 1613

looks like something has changed or none of these answers were ever correct. You cannot simply ftell(STDIN) to see if there is data passed from STDIN

<?php

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

outputs

-:4:
bool(false)

This is from php -v with output

PHP 7.0.22-0ubuntu0.16.04.1 (cli) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.0.22-0ubuntu0.16.04.1, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.4.0, Copyright (c) 2002-2016, by Derick Rethans

when I try to use STDIN constant I get

PHP Notice:  Use of undefined constant STDIN - assumed 'STDIN' in - on line 3

Upvotes: 2

grant sun
grant sun

Reputation: 120

if(FALSE !== ftell(STDIN))
{
    while (FALSE !== ($line = fgets(STDIN)))
    {
        $customLogArr[]=$line;
    }
}

For STDIN, if nothing can be read, ftell() will return false.

Upvotes: 1

DeDee
DeDee

Reputation: 2002

Is this what you're looking for? Forgive me if I misunderstood, this should not hold you when there's no input on STDIN.

stream_set_blocking(STDIN, 1);

function CheckSTDIN() {
    $read = array(STDIN);
    $wrte = NULL;
    $expt = NULL;
    $a = stream_select($read, $wrte, $expt, 0);
    if ($a && in_array(STDIN, $read)) {
        // you can read from STDIN now, it'll only be available if there is anything in STDIN
        // you can return the value or pass it to global or class variable
        return fread(STDIN, 255); // set your desired string length
    } else return false;
}

while (FALSE !== ($line = CheckSTDIN())) {
        $customLogArr[]=$line;
}

Upvotes: 1

Kuti Imre
Kuti Imre

Reputation: 1

The above solution didn't work for me. This is one that works.

if ($args = stream_get_contents(fopen("php://stdin", "r"))) {

    var_dump($args);

} else {
    echo 'No input';
}

Note: This will read the data piped to the script as a string: echo "Hello World" | php test.php will output string(12) "Hello World" (+ the new line at the end...) So you need to further process the data to an array if that is required. explode() seems to be a simple approach.

Upvotes: -1

Related Questions