'Non-blocking on STDIN in PHP CLI

Is there anyway to read from STDIN with PHP that is non blocking:

I tried this:

stream_set_blocking(STDIN, false);
echo fread(STDIN, 1);

and this:

$stdin = fopen('php://stdin', 'r');
stream_set_blocking($stdin, false);
echo 'Press enter to force run command...' . PHP_EOL;
echo fread($stdin, 1);

but it still blocks until fread gets some data.

I noticed a few open bug reports about this (7 years old), so if it can't be done, does any one know any crude hacks that could accomplish this (on Windows and Linux)?



Solution 1:[1]

Just a notice, that non blocking STDIN working, now.

Solution 2:[2]

system('stty cbreak');
while(true){
    if($char = fread(STDIN, 1)) {
        echo chr(8) . mb_strtoupper($char);
    }
}

Solution 3:[3]

Petah, I can't help with the PHP side of this directly, but I can refer you to an article I ran across a while ago in which someone emulated transistors by testing within a shell script for the existence of pending data for a named pipe. It's a fascinating read, and takes shell scripting to a whole new level of geekiness. :-)

The article is here: http://www.linusakesson.net/programming/pipelogic/

So ... in answer to your "crude hacks" request, I suppose you could shunt your stdio through named pipes, then exec() the tool whose source is included at the URL above to test whether anything is waiting to be sent through the pipe. You'd probably want to develop some wrapper functions to help with stuff.

I suspect the pipelogic solution is Linux-only, or at least would require a unix-like operating system. No idea how this could be accomplished on Windows.

Solution 4:[4]

Using Martins Example above, and adding user3684669 code with a little modification of my own. Here is a small program displaying a clock and updating every second. When ever a key is pressed I echo it to the screen. I also hide the default echoing of the character to the screen. Works in Linux.

<?php
//-- Save the keyboard state
//-- then use cbreak and turn off echo
$stty_orig = '';
function init_keyboard() {
  global $stty_orig;

  $stty_orig = shell_exec("stty -g");
  system('stty cbreak -echo');
}

//-- Return keyboard state
function close_keyboard() {
  global $stty_orig;

  system('stty '.$stty_orig);
}

//-- clear the screen
function clear() {
  echo "\x1B[2J\x1B[0m";
}

//-- move cursor to x,y on the screen
function gotoxy($x, $y) {
  echo "\x1B[".$y.";".$x."H";
}

//-- print a string at x,y on the screen
function printxy($x, $y, $str) {
  gotoxy($x,$y);
  echo $str;
}

//-- Check if we have a character and return it if we do.
function non_block_read($fd) {
    $read = array($fd);
    $write = array();
    $except = array();
    $result = stream_select($read, $write, $except, 0);
    if($result === false) throw new Exception('stream_select failed');
    if($result === 0) return false;
    $data = stream_get_line($fd, 1);
    return $data;
}

//-- main program starts here.
init_keyboard();
clear();
printxy(20,15,'Press Q to quit!');
while(1) {
    $x = "";
    if($x = non_block_read(STDIN)) {
        //echo "Input: " . $x . "\n";
        // handle your input here
        printxy(20,13,'Last Key Pressed: ['.$x."]  ");
        if ($x == 'Q') {
            printxy(1,20,"bye!\n");
            break;
        }
    } else {
        // perform your processing here
        $date_time = date('F j,Y h:i:sa');
        printxy(20,12,$date_time."   ");
    }
    sleep(1);
}
close_keyboard();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 user3684669
Solution 3 ghoti
Solution 4 Dharman