I want to read a single character at-a-time from the command line in PHP, however it seems as though there is some kind of input buffering from somewhere preventing this.
Consider this code:
#!/usr/bin/php
<?php
echo "input# ";
while ($c = fread(STDIN, 1)) {
echo "Read from STDIN: " . $c . "\ninput# ";
}
?>
Typing in "foo" as the input (and pressing enter), the output I am getting is:
input# foo
Read from STDIN: f
input# Read from STDIN: o
input# Read from STDIN: o
input# Read from STDIN:
input#
The output I am expecting is:
input# f
input# Read from STDIN: f
input# o
input# Read from STDIN: o
input# o
input# Read from STDIN: o
input#
input# Read from STDIN:
input#
(That is, with characters being read and processed as they are typed).
However, currently, each character is being read only after enter is pressed. I have a suspicion the TTY is buffering the input.
Ultimately I want to be able to read keypresses such as UP arrow, DOWN arrow, etc.
Here is a way that works for me with readline and stream functions, without needing to mess with tty stuff.
Tested with PHP 5.5.8 on OSX.
The solution for me was to set
-icanon
mode on the TTY (usingstty
). Eg.:So, the the code that now works is:
Output:
Props to the answer given here:
Is there a way to wait for and get a key press from a (remote) terminal session?
For more information, see:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92
Don't forget to restore the TTY when you're done with it...
Restoring the tty configuration
Resetting the terminal back to the way it was can be done by saving the tty state before you make changes to it. You can then restore to that state when you're done.
For example:
This is the only way to preserve the tty and put it back how the user had it before you began.
Note that if you're not worried about preserving the original state, you can reset it back to a default "sane" configuration simply by doing:
The function below is a simplified version of @seb's answer that can be used to capture a single character. It does not require
stream_select
, and usesreadline_callback_handler_install
's inherent blocking rather than creating a while loop. It also removes the handler to allow further input as normal (such as readline).