Readline callback read char: примеры (PHP)

Использование readline_callback_read_char для неблокирующего ввода в PHP
Раздел: Ввод данных
readline_callback_read_char: void

Функция readline_callback_read_char в PHP

Общее описание

Функция readline_callback_read_char() является частью расширения Readline в PHP. Она применяется для неблокирующего чтения ввода с терминала, что позволяет обрабатывать пользовательский ввод посимвольно, не приостанавливая выполнение скрипта до нажатия клавиши Enter. Это полезно для создания интерактивных консольных приложений, таких как CLI-инструменты с автодополнением, «живыми» подсказками или обработкой специальных клавиш (например, стрелок).

Аргументы

У функции нет параметров. Она считывает один символ из буфера, подготовленного колбэк-обработчиком, установленным с помощью readline_callback_handler_install().

Принцип работы

Механизм основан на установке обработчика ввода и запуске цикла событий. Функция readline_callback_read_char() проверяет, доступен ли новый символ для чтения. Если символ есть, он передается в колбэк-функцию, где его можно обработать. Для работы требуется предварительный вызов readline_callback_handler_install() и последующий запуск readline_callback_loop() или организация собственного цикла с использованием stream_select().

Примеры использования readline_callback_read_char

Базовый пример с колбэком

Пример показывает простую обработку ввода по одному символу.

<?php
function handleInput($input) {
    echo "Введен символ: " . $input . "\n";
    // Условие завершения
    if ($input === 'q') {
        readline_callback_handler_remove();
    }
}

readline_callback_handler_install('Введите символ (q для выхода): ', 'handleInput');

while (true) {
    $w = NULL;
    $e = NULL;
    $r = [STDIN];
    $n = stream_select($r, $w, $e, null);
    if ($n && in_array(STDIN, $r)) {
        readline_callback_read_char();
    }
    if (!function_exists('readline_callback_handler_remove') || !readline_info('callback_handler_installed')) {
        break;
    }
}
Введите символ (q для выхода): a
Введен символ: a
b
Введен символ: b
q
Введен символ: q
Обработка специальных клавиш

Пример демонстрирует возможность различать обычные символы и управляющие последовательности.

<?php
function handleInput($input) {
    if ($input === "\x1B[A") {
        echo "Нажата стрелка вверх.\n";
    } elseif ($input === "\n") {
        echo "Нажат Enter.\n";
    } else {
        echo "Символ: $input\n";
    }
}

readline_callback_handler_install('', 'handleInput');
readline_callback_handler_install('Тест (стрелки, Enter): ', 'handleInput');
$stdin = fopen('php://stdin', 'r');
stream_set_blocking($stdin, false);
while (true) {
    $r = [$stdin];
    $w = $e = [];
    if (stream_select($r, $w, $e, 0, 200000)) {
        readline_callback_read_char();
    }
    // Условие выхода для примера
    static $i=0; if ($i++ > 100000) break;
}

Альтернативы в PHP

Функция readline()

Стандартная функция readline() считывает всю строку до нажатия Enter в блокирующем режиме. Она проще в использовании, но не позволяет обрабатывать ввод посимвольно или реагировать на специальные клавиши до завершения ввода.

$name = readline("Введите имя: ");
echo "Привет, $name";
Функция fgets(STDIN)

Чтение строки из стандартного потока ввода. Также работает в блокирующем режиме. Подходит для простого посточного ввода, но не предоставляет возможностей Readline, таких как история команд или автодополнение.

echo "Введите текст: ";
$line = fgets(STDIN, 1024);
echo "Вы ввели: $line";

Выбор альтернативы: readline_callback_read_char применяется, когда требуется неблокирующий ввод, немедленная реакция на клавиши или создание сложных интерактивных интерфейсов. Для простого считывания строки удобнее readline() или fgets().

Аналоги в других языках

Python: модуль msvcrt или curses

В Python для посимвольного неблокирующего ввода в Windows используется msvcrt.getch(), а в Unix-подобных системах — модуль curses или настройка терминала через tty и termios.

# Пример для Windows
import msvcrt
print("Нажмите клавишу...")
key = msvcrt.getch()
print(f"Символ: {key}")
Нажмите клавишу...
Символ: b'a'
JavaScript (Node.js): модуль readline

Модуль readline в Node.js позволяет создавать интерактивные интерфейсы. Для посимвольного ввода используется событие 'keypress' при настройке raw-режима терминала.

const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
process.stdin.on('keypress', (str, key) => {
  console.log(`Клавиша: ${key.name}`);
  if (key.ctrl && key.name === 'c') process.exit();
});
console.log('Нажимайте клавиши...');
Отличия от PHP

В Python и JS подходы более низкоуровневые или тесно связаны с системными библиотеками терминала. PHP-функция readline_callback_read_char абстрагирует часть сложностей, предоставляя интерфейс на основе колбэков, но требует ручного управления циклом событий.

Типичные ошибки

Отсутствие установленного обработчика

Вызов readline_callback_read_char() до readline_callback_handler_install() приводит к предупреждению и неработоспособности.

<?php
readline_callback_read_char(); // Ошибка: нет обработчика
PHP Warning: readline_callback_read_char(): unable to read callback...
Забытый вызов readline_callback_read_char в цикле

Без регулярного вызова этой функции в цикле события колбэк не будет получать новые символы.

<?php
function cb($i) { echo $i; }
readline_callback_handler_install('', 'cb');
// Цикл отсутствует - ввод не обрабатывается
sleep(5);
Некорректное завершение

Невызов readline_callback_handler_remove() может оставить терминал в нежелательном состоянии.

<?php
function cb($i) { 
    if ($i === 'q') {
        // Завершение без удаления обработчика
        exit;
    }
}
readline_callback_handler_install('', 'cb');
// ... цикл

Изменения в версиях PHP

Для функции readline_callback_read_char() в версиях PHP 8.x существенных изменений не было. Однако, важно отметить, что само расширение Readline не всегда доступно по умолчанию, особенно на Windows. Поведение функции остается стабильным. В PHP 8.1 и выше рекомендуется проверять доступность расширения через extension_loaded('readline'). Критических изменений в сигнатуре или возвращаемых значениях не вносилось.

Расширенные примеры

Интерактивный таймер с вводом

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

Пример php
<?php
function handleInput($input) {
    static $count = 0;
    $count++;
    echo "\nСимвол #$count: $input";
    if ($input === 'q') {
        readline_callback_handler_remove();
        echo "\nВыход.\n";
    }
}

readline_callback_handler_install('Ввод (q - выход): ', 'handleInput');
$start = time();
$stdin = fopen('php://stdin', 'r');
stream_set_blocking($stdin, false);
echo "Таймер запущен...\n";
while (true) {
    // Таймер
    $elapsed = time() - $start;
    echo "\rПрошло секунд: $elapsed ";
    // Проверка ввода
    $r = [$stdin];
    $w = $e = [];
    if (stream_select($r, $w, $e, 0, 10000)) {
        readline_callback_read_char();
    }
    if (!readline_info('callback_handler_installed')) {
        break;
    }
    usleep(50000);
}
Эмуляция автодополнения

Пример обработки клавиши Tab для вывода подсказки.

Пример php
<?php
$completions = ['apple', 'apricot', 'banana'];
$currentInput = '';

function handleInput($input) {
    global $currentInput, $completions;
    if ($input === "\t") { // Tab
        $matches = array_filter($completions, function($item) use ($currentInput) {
            return strpos($item, $currentInput) === 0;
        });
        if (!empty($matches)) {
            echo "\nВарианты: " . implode(', ', $matches) . "\n";
            readline_on_new_line(); // Перемещаем курсор на новую строку
            readline_redisplay();
        }
    } elseif (ord($input) >= 32) { // Печатные символы
        $currentInput .= $input;
    } elseif ($input === "\177") { // Backspace (ASCII 127)
        $currentInput = substr($currentInput, 0, -1);
    }
}

readline_callback_handler_install('Введите фрукт (Tab для подсказки): ', 'handleInput');
// ... цикл с stream_select и readline_callback_read_char

PHP readline_callback_read_char function comments

En
Readline callback read char Reads a character and informs the readline callback interface when a line is received