27

我想从 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# 

(也就是说,字符在输入时被读取和处理)。

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

最终我希望能够阅读诸如向上箭头、向下箭头等按键。

4

4 回答 4

35

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

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 状态来将终端重置回原来的状态。然后,您可以在完成后恢复到该状态。

例如:

<?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 并将其放回用户在您开始之前的方式的唯一方法。

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

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

?>
于 2010-09-10T12:24:58.420 回答
22

这是一种适用于我的 readline 和流函数的方法,而无需弄乱 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 测试。

于 2014-02-07T13:24:29.603 回答
6

下面的函数是@seb 答案的简化版本,可用于捕获单个字符。它不需要stream_select, 并且使用readline_callback_handler_install's 固有的阻塞而不是创建一个 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";
于 2016-07-05T17:10:27.533 回答
0
<?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...";
}
于 2017-06-01T20:38:17.667 回答