6

In PHP, I can read in input from a command line program with the following code

$stream = STDIN;
$test = fgets($stream);
echo $test;

This works well for simple input. However, if I try to use something like an back arrow key, my shell looks like the following

This is a test^[[D^[[D^[[D    

i.e., the arrow key escape sequence of ^[[D is sent to the shell. PHP itself will interpret the arrow keys -- i.e. inputing this

This is a test^[[D^[[D^[[D^[[Dsecond test     

will output this

This is a second test

However, I'd like the shell to "correctly" (i.e. do what I think they should do, not literally what I sent) interpret the arrow keys so the insertion point moves while typing.

Is this possible in PHP? With an extension? Without an extension? I've tried variations of fgets($stream, 1) but it seems like PHP just hangs until the user inputs an enter key.

Alana Storm
  • 164,128
  • 91
  • 395
  • 599

3 Answers3

1

A bit tricky but found a way using the mbstring library:

// stream_set_blocking(STDIN, false); // Do not wait
while ($c = fread(STDIN, 16)) {
      $c = preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($c, 'UTF-8', 'UTF-8'));
      /* Up arrow */
      if ($c === "[A"){
        echo "UP";     
      }
      /* Down arrow */
      if ($c === "[B"){
        echo "DOWN";
      }
      /* Right arrow */
      if ($c === "[C"){
        echo "RIGHT";
      }
      /* LEFT arrow */
      if ($c === "[D"){
       echo "LEFT";
      } 
  }

Hide the shell character printing:

system("stty -echo");

Restore at script end:

stty echo
NVRM
  • 11,480
  • 1
  • 88
  • 87
0

No way with pure PHP: http://php.net/fgetc (see the comments)

Unirgy
  • 1,495
  • 15
  • 25
-1

The following function will wait until the user enters a character and then returns it immediately. This approach supports multibyte characters so will also work for detecting arrow key presses.

function waitForInput(){

    $input = '';

    $read = [STDIN];
    $write = null;
    $except = null;

    readline_callback_handler_install('', function() {});

    // Read characters from the command line one at a time until there aren't any more to read
    do{
        $input .= fgetc(STDIN);
    } while(stream_select($read, $write, $except, 0, 1));

    readline_callback_handler_remove();

    return $input;

}

Here is an example of using the above function to identify an arrow key press:

$input = waitForInput();

switch($input){
    case chr(27).chr(91).chr(65):
        print 'Up Arrow';
        break;
    case chr(27).chr(91).chr(66):
        print 'Down Arrow';
        break;
    case chr(27).chr(91).chr(68):
        print 'Left Arrow';
        break;
    case chr(27).chr(91).chr(67):
        print 'Right Arrow';
        break;
    default:
        print 'Char: '.$input;
        break;
}
Henry Howeson
  • 677
  • 8
  • 18