Hi Tim, Frode and Sjoerd,

I've updated the RFC to 0.6:

https://wiki.php.net/rfc/io_terminal

I'm replying to all three of you in one mail so the discussion points
stay in one place.


On 08/11/2026 19:18, Tim Düsterhus wrote:
> 1. "The first lease saves the POSIX terminal attributes"
>
> Can you, for the uninformed reader, spell out what the attributes are?
> The raw-mode (or not) is obvious. Is the size also an attribute?
> Anything else?

It's the termios struct: the input, output, control and local flags
(echo, line buffering, whether Ctrl+C raises a signal, etc.) and the
control characters, including VMIN and VTIME. On Windows it's the
console input mode. Size isn't part of it, and neither are the cursor
position or the screen contents, so a resize during a lease isn't
undone on restore. The RFC lists this now.

> 2. Would it make sense to have a getter that returns the current
> raw-mode state? This might be useful to check before calling
> `readLine()`.

I left it out because PHP can only tell whether it holds a lease, not
what the terminal is really set to. stty or another process can change
it. readLine() already throws if a lease on the same terminal is
active, even from another wrapper. If you think a lease-only check is
still useful, I can add one.

> 3. For my second example:
> [...]
> I would add a comment for the second `Terminal::fromStdio()`

Done.

> 4. For the "Input state and sequential reads" section, an example with
> two terminal instances might be useful

Added.

> 5. For the "Error Model", I wonder if EOF should be returned as a key
> instead, similar to `Resize`.

I'd keep the exception. After Resize the input is still open. After EOF
every call returns right away, so a loop that ignores unknown keys
would spin forever. In raw mode Ctrl+D comes in as "\x04", so EOF only
happens when the input is really closed. I added this to the RFC.

> 6. It would be nice to avoid duplicating the namespace in class names.
> [...] May I suggest `Terminal\Dimensions`?

Agreed. It's Dimensions and getDimensions() now, in the RFC and the PR.
I've also gone through your PR comments.


On Fri, 6 Nov 2026, Frode wrote:
> Raw mode is now a ModeToken lease and the size comes from getSize(),
> instead of four stty runs [...]

Thanks again for porting tui to it. That's the most useful test the
patch has had. getSize() is now getDimensions(), so your branch needs a
small rename.

> I would like to suggest that the RFC proceeds with a reduced scope:
> the operations userland now shells out for, and nothing that userland
> already does well.

I thought about it a lot, and I'm keeping the read methods. You're
right that tui, php-tui and Prompts have better decoders and don't need
readKey(). The reads are for the many small scripts that just want
"press a key" or "enter password" without a library or stty. Symfony's
QuestionHelper runs stty only to hide input, for example, and
readSecret() covers that.

> readKey() returns Key|string|null with 28 keys and no modifiers, so
> none of these libraries could switch to it, and the enum cannot gain
> modifiers later without a new method beside it.

Agreed, and readKey() isn't meant to replace your decoder. The RFC now
says so directly: no modifiers, mouse, paste or focus, nothing gets
enabled on the terminal, and unknown sequences come back unchanged as
strings. It also describes what tui does, a lease plus getDimensions()
with its own decoder, as a supported way to use the API. A richer decoder
should be a new API alongside readKey(), and your bytes-in, events-out idea
sounds like the right shape for it. That would be its own RFC.

> readSecret() is policy [...] that prompt libraries will want to
> decide for themselves.

It is policy, and libraries can keep their own on top of raw mode.
readSecret() is the default for people who don't want to decide.

> 1. Factories: fromStreams() is enough. [...] is there a use case that
> fromStreams(STDIN, STDOUT) does not cover?

The STDIN/STDOUT constants only exist in the CLI SAPI. fromStdio()
uses the native standard handles directly, so it still works where the
constants aren't defined, and it follows the handles if they get
reopened. In a normal CLI script the two are the same.

> 2. Raw mode on Windows: libraries that read and write escape sequences
> need virtual terminal input (ENABLE_VIRTUAL_TERMINAL_INPUT) and output
> processing. [...] Could raw mode specify them, or take an option for
> them?

Raw mode leaves both flags as they are. For output there's already
sapi_windows_vt100_support(). VT input is a real gap, since PHP can't
set it today. An option on enableRawMode() can be added later without
breaking anything. I'd rather get that right with real Windows users
than guess now, but if you think it should block the RFC, let me know.

> 3. Ctrl+C: some programs want raw input but Ctrl+C still delivered as
> a signal.

Agreed. That fits as an optional argument later.

> 4. Resize: with readKey() out, Key::Resize goes too.

Since readKey() stays, Key::Resize stays with it. Without readKey(),
SIGWINCH plus getDimensions() works as you describe, and
getDimensions() doesn't swallow a pending Resize.

> I am happy to test further revisions with tui.

That would be great, thanks.


On 23/10/2026, Sjoerd Langkemper wrote:

Two things I said I'd come back to.

> Perhaps it would be desirable to distinguish between the user
> cancelling, and a genuine error, and perhaps also EOF; for example
> with subclasses of TerminalException.

I said I wanted to settle this before the vote. I've decided not to
add them in this RFC. Subclasses of TerminalException can be added
later without breaking anything, because existing catch blocks keep
working, and I'd rather add them once real code shows which
distinctions people need.

> Is this sufficiently long, even when used over SSH or such? Is this
> configurable in readSecret as well?

To fix this, I added $sequenceTimeout to readSecret() as well, matching
readKey(). Callers on high-latency links can pass a longer duration
if needed, rather than being stuck with the 25 ms default.

Regards,
Pratik

Reply via email to