PHP CLI: How to read a single character of input from the TTY (without waiting for the enter key)?

前端 未结 4 1471
醉梦人生
醉梦人生 2020-11-29 02:53

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.

相关标签:
4条回答
  • 2020-11-29 03:31

    The solution for me was to set -icanon mode on the TTY (using stty). Eg.:

    stty -icanon
    

    So, the the code that now works is:

    #!/usr/bin/php
    <?php
    system("stty -icanon");
    echo "input# ";
    while ($c = fread(STDIN, 1)) {
        echo "Read from STDIN: " . $c . "\ninput# ";
    }
    ?>
    

    Output:

    input# fRead from STDIN: f
    input# oRead from STDIN: o
    input# oRead from STDIN: o
    input# 
    Read from STDIN: 
    
    input# 
    

    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:

    <?php
    
    // Save existing tty configuration
    $term = `stty -g`;
    
    // Make lots of drastic changes to the tty
    system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef");
    
    // Reset the tty back to the original configuration
    system("stty '" . $term . "'");
    
    ?>
    

    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:

    <?php
    
    // Make lots of drastic changes to the tty
    system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef");
    
    // Reset the tty back to sane defaults
    system("stty sane");
    
    ?>
    
    0 讨论(0)
  • 2020-11-29 03:35

    Here is a way that works for me with readline and stream functions, without needing to mess with tty stuff.

    readline_callback_handler_install('', function() { });
    while (true) {
      $r = array(STDIN);
      $w = NULL;
      $e = NULL;
      $n = stream_select($r, $w, $e, null);
      if ($n && in_array(STDIN, $r)) {
        $c = stream_get_contents(STDIN, 1);
        echo "Char read: $c\n";
        break;
      }
    }
    

    Tested with PHP 5.5.8 on OSX.

    0 讨论(0)
  • 2020-11-29 03:35

    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 uses readline_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).

    function readchar($prompt)
    {
        readline_callback_handler_install($prompt, function() {});
        $char = stream_get_contents(STDIN, 1);
        readline_callback_handler_remove();
        return $char;
    }
    
    // example:
    if (!in_array(
        readchar('Continue? [Y/n] '), ["\n", 'y', 'Y']
        // enter/return key ("\n") for default 'Y'
    )) die("Good Bye\n");
    $name = readline("Name: ");
    echo "Hello {$name}.\n";
    
    0 讨论(0)
  • 2020-11-29 03:47
    <?php
    `stty -icanon`;
    // this will do it
    stream_set_blocking(STDIN, 0);
    echo "Press 'Q' to quit\n";
    while(1){
       if (ord(fgetc(STDIN)) == 113) {
           echo "QUIT detected...";
           break;
       }
       echo "we are waiting for something...";
    }
    
    0 讨论(0)
提交回复
热议问题