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

我想从PHP中的命令行一次读取一个字符,但是似乎有某种来自某个地方的输入缓冲阻止了这一点。

请考虑以下代码:

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

输入“foo”作为输入(并按回车键),我得到的输出是:

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

input# 

期望的输出是:

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# 

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

但是,目前,每个字符仅在按回车键后被读取。我怀疑TTY正在缓冲输入。

最终,我希望能够读取按键,例如向上箭头,向下箭头等。


答案 1

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

stty -icanon

因此,现在有效的代码是:

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

输出:

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

input# 

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

有关详细信息,请参阅:
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 . "'");

?>

这是保留 tty 并将其放回用户在开始之前拥有它的唯一方法。

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

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

?>

答案 2

这是一种使用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;
  }
}

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


推荐