PHP CLI:如何从TTY读取单个输入字符(无需等待回车键)?

dan*_*mau 25 php stdin command-line-interface buffering tty

我想读的,一时间从PHP命令行中的一个字符,但它好像有某种输入缓冲从某处防止这种.

考虑以下代码:

#!/usr/bin/php
<?php
echo "input# ";
while ($c = fread(STDIN, 1)) {
    echo "Read from STDIN: " . $c . "\ninput# ";
}
?>
Run Code Online (Sandbox Code Playgroud)

键入"foo"作为输入(并按Enter键),我得到的输出是:

input# foo
Read from STDIN: f
input# Read from STDIN: o
input# Read from STDIN: o
input# Read from STDIN: 

input# 
Run Code Online (Sandbox Code Playgroud)

期待的输出是:

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# 
Run Code Online (Sandbox Code Playgroud)

(即,在键入字符时读取和处理字符).

但是,目前,只有在按下输入后才会读取每个字符.我怀疑TTY正在缓冲输入.

最终我希望能够读取按下箭头,向下箭头等按键.

dan*_*mau 32

我的解决方案是-icanon在TTY上设置模式(使用stty).例如.:

stty -icanon
Run Code Online (Sandbox Code Playgroud)

所以,现在的代码是:

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

输出:

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

input# 
Run Code Online (Sandbox Code Playgroud)

这里给出答案的道具:
有没有办法等待(远程)终端会话按键?

有关更多信息,请参阅:http:
//www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92

当你完成它时不要忘记恢复TTY ......

恢复tty配置

可以通过在更改tty状态之前保存tty状态来将终端重置为原来的状态.然后,您可以在完成后恢复到该状态.

例如:

<?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 . "'");

?>
Run Code Online (Sandbox Code Playgroud)

这是保存tty的唯一方法,并在开始之前将其恢复原状.

请注意,如果您不担心保留原始状态,只需执行以下操作即可将其重置为默认的"理智"配置:

<?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");

?>
Run Code Online (Sandbox Code Playgroud)

  • 马克:在最基本的时候,你可以做到"理智".但是,为了保证您将tty重置为之前的确切状态,您应该首先保存它的状态.我已经扩展了答案,包括如何做到这一点的例子. (3认同)

seb*_*seb 22

这是一种适用于readline和stream函数的方法,无需弄乱tty东西.

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

在OSX上使用PHP 5.5.8进行测试.

  • readline 需要编译成PHP。使用以下命令检查 ```--with-readline=/opt/local``` 的配置:```php -i | grep 阅读线``` (2认同)

Syn*_*xis 5

下面的函数是@seb 答案的简化版本,可用于捕获单个字符。它不需要stream_select, 并且使用readline_callback_handler_install的固有阻塞而不是创建一个 while 循环。它还删除处理程序以允许进一步输入正常(例如 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";
Run Code Online (Sandbox Code Playgroud)