Hi internals,
I have been experimenting with a small PHP extension for native terminal
helpers:
https://github.com/prateekbhujel/php-terminal
The goal is to expose a small cross-platform CLI terminal layer for the
pieces that are currently awkward to normalize in userland, especially on
Windows:
- checking whether stdin/stdout/stderr are TTYs
- reading terminal size
- enabling/restoring raw mode safely
- reading a single key with normalized names
- writing directly to stdout/stderr
This started from a practical issue with prompt-style PHP CLIs. Laravel
Prompts, for example, currently cannot use its normal interactive prompt
flow on Windows because the backend depends on Unix-style terminal
behavior. I opened a proof-of-concept integration here:
https://github.com/laravel/prompts/pull/242
The extension is still alpha, and I am not proposing it for core right
now. I mainly want feedback on whether this API shape makes sense from a
PHP CLI/runtime point of view, and whether this is better kept as
PECL/ecosystem work first.
Initial release with Windows builds:
https://github.com/prateekbhujel/php-terminal/releases/tag/v0.1.0
If this would be better discussed on internals-win first, I am happy to
move it there. I am mainly looking for feedback on API shape, naming,
Windows behavior, and missing primitives before taking it further.
Thanks,
Pratik
The goal is to expose a small cross-platform CLI terminal layer for
the pieces that are currently awkward to normalize in userland,
especially on Windows:
- checking whether stdin/stdout/stderr are TTYs
- reading terminal size
- enabling/restoring raw mode safely
- reading a single key with normalized names
- writing directly to stdout/stderr
I think that this is something useful to have.
The extension is still alpha, and I am not proposing it for core right
now. I mainly want feedback on whether this API shape makes sense from
a PHP CLI/runtime point of view, and whether this is better kept as
PECL/ecosystem work first.
I have had a look at the API, and from my point of view, I think I would
like to see, with in mind a possible introduction into PHP core:
-
All functions to be part of a Terminal\Terminal class — we have
guidelines for this in place: https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#bundled-extensions -
The getBackend() method should return an enum, as there are
(currently) only two possible values: windows, and posix. -
Reading a key returns either a string containing a character, or a
sequence of characters describing an action (up, down, etc). I think,
because both of these are strings, this is awkward. Perhaps it would
be better for actual characters to return strings, and again, the
special key-presses to be case of an Enum.In addition, would it return Unicode graphemes (think emojis), or just
code points? -
The stream names (STDOUT, STDIN, and STDERR) again should probably an
Enum.
cheers,
Derick
--
https://derickrethans.nl | https://xdebug.org | https://xdebug.cloud
Author of Xdebug. Like it? Consider supporting me: https://xdebug.org/support
mastodon: @derickr@phpc.social @xdebug@phpc.social
Hi Derick,
Thanks, that makes sense.
I moved the package in that direction now. The current v0.3.0 API uses
Terminal\Terminal with camelCase methods, plus Terminal\Backend,
Terminal\Stream, and Terminal\Key enums:
https://github.com/prateekbhujel/php-terminal/releases/tag/v0.3.0
I also removed the old procedural API instead of keeping compatibility
around it. Since this is still pre-1.0 and not something people should be
depending on yet, it felt better to make the shape cleaner now.
For readKey(), special keys now return Terminal\Key cases, while
printable input still returns a string.
For Unicode, it currently returns the next encoded code point from the
terminal, not a full grapheme cluster. That seemed like the clearer
low-level primitive to me, but I am happy to adjust that if core would
prefer a different contract.
Thanks again for looking at it.
Pratik Bhujel
The goal is to expose a small cross-platform CLI terminal layer for
the pieces that are currently awkward to normalize in userland,
especially on Windows:
- checking whether stdin/stdout/stderr are TTYs
- reading terminal size
- enabling/restoring raw mode safely
- reading a single key with normalized names
- writing directly to stdout/stderr
I think that this is something useful to have.
The extension is still alpha, and I am not proposing it for core right
now. I mainly want feedback on whether this API shape makes sense from
a PHP CLI/runtime point of view, and whether this is better kept as
PECL/ecosystem work first.I have had a look at the API, and from my point of view, I think I would
like to see, with in mind a possible introduction into PHP core:
All functions to be part of a Terminal\Terminal class — we have
guidelines for this in place:
https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#bundled-extensionsThe getBackend() method should return an enum, as there are
(currently) only two possible values: windows, and posix.Reading a key returns either a string containing a character, or a
sequence of characters describing an action (up, down, etc). I think,
because both of these are strings, this is awkward. Perhaps it would
be better for actual characters to return strings, and again, the
special key-presses to be case of an Enum.In addition, would it return Unicode graphemes (think emojis), or just
code points?The stream names (STDOUT, STDIN, and STDERR) again should probably an
Enum.cheers,
Derick--
https://derickrethans.nl | https://xdebug.org | https://xdebug.cloud
Author of Xdebug. Like it? Consider supporting me:
https://xdebug.org/support
mastodon: @derickr@phpc.social @xdebug@phpc.social
Hi Derick, hi internals,
Small update from my side: I pushed this further based on the feedback here.
The current release is v0.4.1 now:
https://github.com/prateekbhujel/php-terminal/releases/tag/v0.4.1
The API is now fully on the Terminal\Terminal class + enums shape. Raw mode
restore uses an opaque Terminal\ModeToken object, special keys return
Terminal\Key cases, printable input returns strings, and resize is surfaced
as Terminal\Key::Resize on both POSIX and Windows.
I also documented the current key scope more clearly: code points rather
than grapheme clusters for now, no F1-F12/modifier normalization yet, and
the POSIX sequence timeout defaults to 25ms.
At this point I am mainly wondering what the right next step is: keep
hardening it as a PECL/ecosystem package first, or is there anything in the
current API shape that would already be a blocker if this ever moved closer
to core?
Thanks,
Pratik
On Tue, Jun 2, 2026 at 3:42 PM Pratik Bhujel prateekbhujelpb@gmail.com
wrote:
Hi Derick,
Thanks, that makes sense.
I moved the package in that direction now. The current v0.3.0 API uses
Terminal\Terminalwith camelCase methods, plusTerminal\Backend,
Terminal\Stream, andTerminal\Keyenums:https://github.com/prateekbhujel/php-terminal/releases/tag/v0.3.0
I also removed the old procedural API instead of keeping compatibility
around it. Since this is still pre-1.0 and not something people should be
depending on yet, it felt better to make the shape cleaner now.For
readKey(), special keys now returnTerminal\Keycases, while
printable input still returns a string.For Unicode, it currently returns the next encoded code point from the
terminal, not a full grapheme cluster. That seemed like the clearer
low-level primitive to me, but I am happy to adjust that if core would
prefer a different contract.Thanks again for looking at it.
Pratik Bhujel
The goal is to expose a small cross-platform CLI terminal layer for
the pieces that are currently awkward to normalize in userland,
especially on Windows:
- checking whether stdin/stdout/stderr are TTYs
- reading terminal size
- enabling/restoring raw mode safely
- reading a single key with normalized names
- writing directly to stdout/stderr
I think that this is something useful to have.
The extension is still alpha, and I am not proposing it for core right
now. I mainly want feedback on whether this API shape makes sense from
a PHP CLI/runtime point of view, and whether this is better kept as
PECL/ecosystem work first.I have had a look at the API, and from my point of view, I think I would
like to see, with in mind a possible introduction into PHP core:
All functions to be part of a Terminal\Terminal class — we have
guidelines for this in place:
https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#bundled-extensionsThe getBackend() method should return an enum, as there are
(currently) only two possible values: windows, and posix.Reading a key returns either a string containing a character, or a
sequence of characters describing an action (up, down, etc). I think,
because both of these are strings, this is awkward. Perhaps it would
be better for actual characters to return strings, and again, the
special key-presses to be case of an Enum.In addition, would it return Unicode graphemes (think emojis), or just
code points?The stream names (STDOUT, STDIN, and STDERR) again should probably an
Enum.cheers,
Derick--
https://derickrethans.nl | https://xdebug.org | https://xdebug.cloud
Author of Xdebug. Like it? Consider supporting me:
https://xdebug.org/support
mastodon: @derickr@phpc.social @xdebug@phpc.social