# [Discussion] Native terminal helpers for PHP CLI

- Thread: https://externals.io/message/131004
- Original archive: https://news-web.php.net/php.internals/131004
- Started by Pratik Bhujel on 2026-05-24T17:04:43+00:00
- Messages: 4
- RFC: Io\Terminal (Under discussion) — https://externals.io/rfc/io_terminal.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #131004 — Pratik Bhujel — 2026-05-24T17:04:43+00:00

URL: https://externals.io/message/131004#131004

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

---

## #131095 — Derick Rethans — 2026-06-02T08:42:36+00:00

URL: https://externals.io/message/131004#131095 · In reply to: #131004

> 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

---

## #131097 — Pratik Bhujel — 2026-06-02T09:57:16+00:00

URL: https://externals.io/message/131004#131097 · In reply to: #131095

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



> 
>
> > […]
>
> I think that this is something useful to have.
>
> > […]
>
> 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

---

## #131156 — Pratik Bhujel — 2026-06-06T09:53:02+00:00

URL: https://externals.io/message/131004#131156 · In reply to: #131097

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\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
>
> 
>
> > […]
