[PHP-DEV] Re: [PHP DEV] [Discussion] Native terminal helpers for PHP CLI

[email protected] (Pratik Bhujel)
Newsgroups php.internals
Message-ID <CA+9BsNdkc8njAK_i6goN1FJE1J+LReXEKgMOoaCmtEkR-g5UuQ@mail.gmail.com>
Hi Tim, hi internals,

Thank you for the detailed feedback. Your observations on namespace
grouping, instance handles, unbacked enums, and standard library
conventions made complete sense.

Instead of just replying in theory, I took the time over the past couple of
days to step back, study how other ecosystems handle terminal interfaces,
and rebuild the extension accordingly. Version 0.8.0 is now released with
that exact architecture.

To answer your question regarding the domain problem:

The primary issue is that PHP CLI currently lacks native primitives for
non-canonical raw terminal mode and single keypress reading. Modern
interactive CLI tools in userland (such as Laravel Prompts, Symfony
Console, and interactive tools) want to deliver rich experiences like
searchable selection menus, autocomplete, multi-choice checkboxes,
spinners, and hidden password prompts.

Today, userland libraries face two severe problems:

1. On Linux and macOS, packages work around the missing primitives by
executing the external stty tool using proc_open or exec. However, inside
minimal Docker containers (such as Alpine or slim environments where stty
is absent) or environments where process execution functions are
restricted, interactive CLI tools fail completely.

2. On Windows, stty does not exist. Windows uses the Win32 Console API.
Because PHP has lacked these primitives, interactive CLI packages on
Windows either crash or fall back to crude text prompts where the user must
type choices manually and hit Enter. Reading password input without
displaying characters on Windows even required distributing separate helper
executables.

3. When a script exits unexpectedly or an unhandled exception occurs while
the terminal is in raw mode, the user shell is left corrupted with disabled
echo and hidden cursor.

Looking at how other languages tackle this:

* Rust (termion and crossterm): Rust relies on RAII guards where entering
raw mode returns a handle whose destructor automatically restores canonical
cooked mode on drop or panic.
* Go (golang.org/x/term): Provides low-level primitives in the extended
standard library (MakeRaw, Restore, GetSize, ReadPassword) on top of which
tools like Bubbletea are built.
* Node.js: Integrates raw mode toggling and size queries directly onto TTY
streams via libuv.
* Python: Provides termios on POSIX and msvcrt on Windows in the standard
library to guarantee cross-platform terminal parity.

Following your guidance, version 0.8.0 adopts the following design:

1. Namespace grouping:
All symbols are placed under the Io\Terminal namespace, avoiding top-level
name reservation and aligning with modern asynchronous and stream polling
conventions.

2. Instance-based Terminal handle:
Classes are no longer static wrappers. The Io\Terminal\Terminal class
encapsulates a stream descriptor, created via named constructors stdin(),
stdout(), stderr(), or by wrapping custom stream resources.

3. Automatic RAII cleanup:
Both Terminal instances and ModeToken objects track active raw mode and
automatically restore terminal state upon destruction. If an uncaught
exception terminates script execution, the destructor restores canonical
mode so the developer shell is never left broken.

4. Unbacked enums:
Backend, Stream, ColorDepth, and Key are now unbacked enums matching core
standards. Methods such as bits() on ColorDepth supply numerical details
without scalar backing.

5. Free-standing procedural functions:
Io\Terminal provides procedural functions like is_tty(), read_key(), and
get_size() for functional script usage.

6. Compatibility:
The legacy Terminal facade and class aliases remain in place so existing
userland code continues to work smoothly.

The complete codebase, 49 tests, and the continuous integration matrix
covering Unix and Windows across PHP versions 8.1 through 8.5 are live on
GitHub and installable via PIE (package prateekbhujel/php-terminal).

The GitHub repository is here:
https://github.com/prateekbhujel/php-terminal

Release v0.8.0 with prebuilt Windows DLL assets:
https://github.com/prateekbhujel/php-terminal/releases/tag/v0.8.0

I would love to hear any further thoughts on whether this design aligns
with what you would like to see for an RFC targeted at PHP 8.7.

Thanks,
Pratik
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.