PHP CLI programs often need to read one key, hide a password, or ask how large the terminal is. Today, libraries reach for stty or the Windows Console API. Pratik Bhujel's Io\Terminal RFC adds one native API for those jobs on POSIX systems and Windows.
How it works
You create a terminal from standard input and output, or provide your own streams:
use Io\Terminal\Key; use Io\Terminal\Terminal; use Time\Duration; $terminal = Terminal::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"; }
getSize() returns a TerminalSize value or null when no usable size exists. readKey() returns a Key enum case for keys such as arrows, Enter, Escape, Insert, Shift+Tab, and F1 through F12. Other input comes back as a string, while a timeout returns null. The API also includes readLine() and readSecret(), which reads a password without echoing it.
Holding raw mode
The key and secret methods manage temporary terminal modes. A menu that redraws after every key can hold raw mode itself:
$mode = $terminal->enableRawMode(); try { // interactive loop } finally { $terminal->restoreMode($mode); }
Raw-mode leases can be shared by multiple parts of an application. The original mode returns when the last lease is released. You have to pass the token back to restoreMode(), because the terminal doesn't remember the latest one for you.
Testing without a terminal
Terminal and ModeToken are final classes, not interfaces. To test prompts, you hand Terminal::fromStreams() an ordinary stream with scripted input:
$input = fopen('php://temp', 'w+'); fwrite($input, "Widget\nsecret\n"); rewind($input); $terminal = Terminal::fromStreams($input); assert($terminal->readLine() === 'Widget'); assert($terminal->readSecret() === 'secret');
On an ordinary stream, enableRawMode() returns a lease that does nothing instead of failing. Streams can't fake a terminal size or a resize, so code that needs those still needs a native test or its own abstraction.
Where it stands
Bhujel opened the internals discussion on September 27, 2026. Version 0.4, published October 6, replaced the public interfaces with final classes and made the restoreMode() token required. The php-src implementation is under review, and the RFC targets PHP 8.7. No vote is scheduled yet. Acceptance requires a two-thirds majority. The change adds new names under Io\Terminal; existing PHP APIs stay unchanged. Code or extensions declaring the same names will conflict, and libraries still need fallbacks for older PHP versions.