PHP contributor Pratik Bhujel has opened the Io\Terminal RFC for PHP 8.7. It proposes a native API for terminal applications, covering terminal size queries, raw mode, key input, line input, and hidden input in ext/standard.

PHP has stream functions for reading lines and an optional readline extension, but libraries still need platform-specific code for terminal behavior. The RFC targets both POSIX terminals and Windows consoles, so CLI packages can build prompts, menus, history, completion, and screen rendering on one API.

What It Proposes

The RFC introduces Io\Terminal\Terminal, with a native SystemTerminal implementation created from standard I/O or supplied streams:

use Io\Terminal\Key;
use Io\Terminal\SystemTerminal;
use Time\Duration;

$terminal = SystemTerminal::fromStdio();
$size = $terminal->getSize();

if ($size !== null) {
    printf("%dx%d\n", $size->cols, $size->rows);
}

$key = $terminal->readKey(Duration::fromSeconds(5));

if ($key === Key::Escape) {
    echo "Escape pressed\n";
}

The API includes a Key enum for special keys such as arrows, function keys, Escape, and resize events. readKey() returns a recognized Key, a string for other input, or null when no input arrives before its timeout. readLine() handles line input, while readSecret() reads hidden input for passwords and other sensitive values.

TerminalSize is a readonly value object containing the terminal's columns and rows. getSize() returns null when PHP cannot obtain a usable native size. The RFC does not use the COLUMNS or LINES environment variables as fallbacks.

Raw Mode and Testability

Applications that need raw mode can call enableRawMode(), which returns a ModeToken. The token keeps raw mode active until it is released or restored. Multiple tokens can share ownership of one terminal, and the RFC defines how the original mode is restored when the last token goes away.

The public Terminal and ModeToken types are interfaces. Userland code can implement them for tests, while SystemTerminal and SystemModeToken provide the native implementation. Calls that need an actual terminal throw TerminalException when native terminal input is unavailable.

Still Under Discussion

The RFC is currently marked Under Discussion and targets PHP 8.7. The proposal has already changed a few times during review. Terminal::create() became SystemTerminal::fromStdio(), TerminalSize gained a public constructor, and raw-mode ownership moved to a shared-per-terminal model. Of course, the RFC still needs to go through the usual voting process before it can become part of PHP.

Read More