2010-09-10 28 views
22

我想從PHP的命令行中一次讀取一個字符,但似乎有某種輸入緩衝從某處阻止這種情況發生。PHP CLI:如何從TTY讀取單個字符的輸入(無需等待輸入鍵)?

考慮以下代碼:

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

鍵入 「富」 作爲輸入(並按下Enter鍵),我得到的輸出是:

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# 

(也就是說,字符是rea d並按打字鍵進行處理)。

但是,目前,每個字符只有在按下輸入後才被讀取。我懷疑TTY緩衝輸入。

最後,我希望能夠讀取按鍵如向上箭頭,向下箭頭等提前

謝謝!

回答

30

我的解決方案是在TTY上設置-icanon模式(使用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# 

道具答案這裏給出:
Is there a way to wait for and get a key press from a (remote) terminal session?

有關更多信息,請參閱:
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"); 

?> 
+0

「不要忘了復位TTY,當你用它做!」 - 我們如何重置? – mpen 2011-11-10 17:12:36

+3

馬克:在最基本的時候,你可以做'stty理智'。但是,爲了保證您將tty重置爲之前的確切狀態,您應該首先保存它的狀態。我已經擴大了答案,包括如何做到這一點的例子。 – dansimau 2011-11-24 00:34:26

+0

不錯!謝謝你:) – mpen 2011-11-24 08:09:17

20

這裏是一個與我工作的方式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測試。

+4

這應該是被接受的答案。 readline需要編譯到PHP中 – h0tw1r3 2014-08-02 20:17:31

+1

。使用這個命令檢查''''--with-readline =/opt/local'''的配置:'''php -i | grep readline''' – arikin 2015-05-18 03:51:37

+1

使用(最近更新的)Arch Linux和PHP 7.0.5。 'break'導致它讀取一個字符然後停止;刪除它使一切工作很好。我也在'select'的末尾將'0'改爲'NULL',現在PHP不使用100%的CPU(!)。 *此功能似乎在我的機器上完美工作(基於初始測試,捕獲'\ 033's和所有東西)* – 2016-04-14 02:24:49

2

以下功能是@ 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"; 
-1
<?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

它似乎沒有工作。根據'stream_set_blocking'手冊說'這個函數適用於任何支持非阻塞模式的流(當前,普通文件和套接字流)。「恐怕STDIN不屬於那裏。 – DrLightman 2018-01-14 01:45:54

+0

你需要在終端中設置stty -icanon,它工作正常.. – joeldg 2018-01-15 12:10:25

+1

感謝您使用此代碼段,它可能會提供一些有限的即時幫助。一個[正確的解釋](https://meta.stackexchange.com/q/114762)將通過說明爲什麼這是一個很好的解決方案,並使其對未來的讀者更有用,從而大大提高其長期價值,類似的問題。請[編輯](https://meta.stackoverflow.com/posts/360251/edit)您的答案添加一些解釋,包括您所做的假設。 [ref](https://meta.stackoverflow.com/a/360251/8371915) – user8371915 2018-01-15 14:04:19