請記住,在多程序應用程式中(最適合在 CLI 下執行),I/O 操作通常會阻礙信號的處理。
例如,如果您有一個父程序正在等待 fread(STDIN),即使您已為 SIGCHLD 定義了信號處理程式,它也不會處理 SIGCHLD,直到 fread 呼叫返回之後。
在這種情況下,您的解決方案是等待 stream_select() 以確定讀取是否會阻塞。重要的是,等待 stream_select() 不會阻礙信號的處理。
Aurelien
CLI SAPI (命令列介面/伺服器應用程式介面) 定義了一些 I/O 資料流的常數,讓命令列程式設計更容易一些。
常數 | 說明 |
---|---|
STDIN |
一個已經開啟的 stdin 資料流。這可以省去使用以下程式碼開啟它的步驟: <?php <?php |
STDOUT |
一個已經開啟的指向 `stdout` 的串流。這省去了使用以下程式碼開啟它的步驟: <?php |
STDERR |
一個已經開啟的指向 `stderr` 的串流。這省去了使用以下程式碼開啟它的步驟: <?php |
有了上述的常數,您不需要自己開啟例如 `stderr` 的串流,只需直接使用常數即可,而不需使用串流資源。
php -r 'fwrite(STDERR, "stderr\n");'
注意事項:
如果 PHP 腳本是從 `stdin` 讀取的,則這些常數將無法使用。
請記住,在多程序應用程式中(最適合在 CLI 下執行),I/O 操作通常會阻礙信號的處理。
例如,如果您有一個父程序正在等待 fread(STDIN),即使您已為 SIGCHLD 定義了信號處理程式,它也不會處理 SIGCHLD,直到 fread 呼叫返回之後。
在這種情況下,您的解決方案是等待 stream_select() 以確定讀取是否會阻塞。重要的是,等待 stream_select() 不會阻礙信號的處理。
Aurelien
STDIN 中的命令列介面資料在按下 Enter 鍵之前是無法使用的。
透過在第一次讀取 STDIN 之前新增 "readline_callback_handler_install('', function(){});",就可以捕捉到單鍵按下。
注意:這似乎只在 Linux CLI 下有效,在 Apache 或 Windows CLI 下無效。
這可以用來隱藏密碼,或與 `stream_select` 一起使用以建立非阻塞式鍵盤監控器。
<?php
// 沒有使用 readline_callback_handler_install('', function(){}) 的範例
$resSTDIN=fopen("php://stdin","r");
echo("輸入 'x'。然後按下 Enter 鍵。");
$strChar = stream_get_contents($resSTDIN, 1);
echo("\n您輸入了: ".$strChar."\n\n");
fclose($resSTDIN);
// 使用 readline_callback_handler_install('', function(){}) 的範例
// 這行移除在 STDIN 上等待 <CR> 的需求
readline_callback_handler_install('', function(){});
$resSTDIN=fopen("php://stdin","r");
echo("我們現在已執行:readline_callback_handler_install('', function(){});\n");
echo("按下 'y' 鍵");
$strChar = stream_get_contents($resSTDIN, 1);
echo("\n您按下了: ".$strChar."\n但不需要按下 <cr>\n");
fclose($resSTDIN);
readline_callback_handler_remove ();
echo("\n再見\n")
?>
它也會隱藏 CLI 中的文字,因此可以用於密碼遮蔽等用途。
例如
<?php
readline_callback_handler_install('', function(){});
echo("輸入密碼後按 Enter 鍵。(請勿使用真實密碼!)\n");
echo("密碼: ");
$strObscured='';
while(true)
{
$strChar = stream_get_contents(STDIN, 1);
if($strChar===chr(10))
{
break;
}
$strObscured.=$strChar;
echo("*");
}
echo("\n");
echo("您輸入了: ".$strObscured."\n");
?>
以下程式碼顯示如何測試 STDIN 的輸入。在這個例子中,我們要尋找 CSV 資料,所以我們使用 fgetcsv 來讀取 STDIN,如果它建立了一個陣列,我們就假設 STDIN 輸入的是 CSV 資料,如果沒有建立陣列,我們就假設 STDIN 沒有輸入,然後稍後再尋找帶有 CSV 檔案名的參數。
請注意,如果沒有呼叫 stream_set_blocking(),fgetcsv() 會在 STDIN 上掛起,等待使用者輸入,這沒有用,因為我們要找的是管道傳輸的檔案。如果它現在不在那裡,以後也不會出現。
<?php
stream_set_blocking(STDIN, 0);
$csv_ar = fgetcsv(STDIN);
if (is_array($csv_ar)){
print "CVS on STDIN\n";
} else {
print "請在 ARGV 中尋找 CSV 檔案名稱。\n";
}
?>
在 Linux CLI 下,STDIN、STDOUT 和 STDERR 可以被關閉並重新連接到不同的 PHP 串流,例如檔案、管道,甚至 UDP socket_stream。(我使用這種技術將長時間運行的背景腳本的輸出/錯誤發送到檔案,以便在出現問題時進行除錯。)
例如:(以下程式碼建立/附加檔案「/tmp/php_stdout.txt」)
<?php
// 這僅適用於 Linux 的 CLI
// 注意:在我們關閉它之前,STDOUT 前面不會加上 $
// 取得目前控制台中 STDOUT 的路徑,以便我們稍後重新連接!
$strOldSTDOUT=(posix_ttyname(STDOUT));
echo("這將輸出到目前的控制台\r\n");
// 關閉 STDOUT 資源
fclose(STDOUT);
// 將 $STDOUT 重新開啟為檔案。注意:所有後續的 $STDOUT 使用都會加上 $ 前綴
$STDOUT=fopen("/tmp/php_stdout.txt","a"); /
echo("這應該會附加到檔案 /tmp/php_stdout.txt\r\n");
// 再次關閉 stdout,以便我們可以重新連接控制台。注意:我們仍在使用
fclose($STDOUT);
// 使用我們先前取得的控制台路徑
$STDOUT=fopen($strOldSTDOUT,"r+");
echo("我們回到了控制台\r\n");
?>