PHP CLI – вводите пользователя, когда делаете что-то в фоновом режиме

Я работаю над игрой, написанной на PHP, и работает в консоли. Вспомните старые MUD и другие текстовые игры, даже некоторые ASCII-арт!

Во всяком случае, то, что я пытаюсь сделать, – это то, что происходит, а также принятие ввода пользователем.

Например, предположим, что это игра с двумя игроками, и игрок 1 ждет, пока игрок 2 сделает ход. Это легко сделать, просто прослушивая сообщение.

Но что, если игрок 1 хочет изменить некоторые варианты? Что делать, если они хотят просмотреть подробности об аспектах состояния игры? Как насчет уступки игры? Есть много вещей, которые игрок может захотеть сделать, ожидая своего противника, чтобы сделать ход.

К сожалению, лучшее, что я имею сейчас, это тот факт, что Ctrl + C полностью убивает программу. Затем другой игрок остается висящим, пока соединение не будет сброшено. О, и игра полностью потеряна.

Я получаю пользовательский ввод с помощью fgets(STDIN) . Но это блокирует выполнение до тех пор, пока не будет получен вход (что обычно хорошо).

Возможно ли, чтобы консольная программа вроде этого обрабатывала вход и выход одновременно? Или я должен просто посмотреть на другой интерфейс?

Короче говоря, PHP не создан для этого, но вы можете получить некоторую помощь от одного из этих расширений . Я не уверен, насколько они тщательны, но вы, вероятно, захотите использовать текстовую библиотеку пользовательского интерфейса. (И действительно, вы, вероятно, не хотите использовать PHP для этого.)

Все, что сказано, вам нужно получить неблокирующий вход от символа STDIN по символу. К сожалению, большинство терминалов буферизуются с точки зрения PHP, поэтому вы ничего не получите до нажатия ввода.

Если вы запустите stty -icanon (или эквивалент вашей ОС) на своем терминале, чтобы отключить буферизацию, тогда следующая короткая программа в основном работает:

 <?php stream_set_blocking(STDIN, false); $line = ''; $time = microtime(true); $prompt = '> '; echo $prompt; while (true) { if (microtime(true) - $time > 5) { echo "\nTick...\n$prompt$line"; $time = microtime(true); } $c = fgetc(STDIN); if ($c !== false) { if ($c != "\n") $line .= $c; else { if ($line == 'exit' || $line == 'quit') break; else if ($line == 'help') echo "Type exit\n"; else echo "Unrecognized command.\n"; echo $prompt; $line = ''; } } } 

(Он полагается на локальное эхо, которое позволяет печатать символы по мере их ввода).

Как вы видите, мы просто зацикливаемся навсегда. Если символ существует, добавьте его в $line . Если ввод введен, обработайте $line . Между тем, мы отмечаем каждые пять секунд, чтобы показать, что мы можем делать что-то еще, пока мы ждем ввода. (Это будет потреблять максимальный процессор, вам придется выпустить sleep() чтобы обойти это.)

Это не должно быть практическим примером, по сути, но, возможно, вы задумаетесь в правильном направлении.

Можно создать игру, как вы описываете, используя ncurses (неблокирующий режим) и libevent. Таким образом, вы не приближаетесь к потреблению процессора. Обработка отдельных клавиш иногда неловкая (реализовать Backspace самостоятельно, это совсем не забавно – и знаете ли вы, что различные ОС посылают разные коды клавиш в Backspace?), И становится очень сложной, если вы хотите правильно поддерживать UTF-8. Тем не менее, вполне жизнеспособный.

В частности, полезно широко использовать libevent, читая как сетевой, так и клавиатурный (stdin) вход с ним. Эта функция позволяет вам прослушивать отдельные ключи: http://www.php.net/manual/en/function.ncurses-cbreak.php, которые позже вы можете прочитать с помощью libevent API. Ключом, который следует иметь в виду, является то, что вы иногда будете читать более одного ключа за раз, и его нужно обрабатывать (так что перебирайте все, что вы прочитали). В противном случае пользователь будет раздражен, увидев, что не все нажатия клавиш «достигают» приложения, а некоторые теряются.

Простите, Мэтью, мне придется отказаться принять ваш ответ, потому что я нашел его сам:

Используйте следующий код, чтобы получать ввод пользователя, делая еще что-то еще:

 while(/* some condition that the code running is waiting on */) { // perform one step or iteration of that code exec("choice /N /C ___ /D _ /T _",$out,$ret); // /C is a list of letters that do something // /D is the default action that will be used as a no-op // /T is the amount of time to wait, probably best set to one second switch($ret) { // handle cases - the "default" case should be "continue 2" } } 

Затем это можно использовать для прерывания цикла и входа в меню параметров или для запуска какого-либо другого события или даже для использования команды при правильном использовании.