Hi Tim, Nicolas, Thanks both for the feedback. I went through the API and the implementation again and updated the RFC and PR to reflect it.
For Tim's points: - TerminalSize now has a normal public constructor, with positive dimensions enforced. - The RFC now shows the actual stub, including strict-properties / non-serializable declarations. - Terminal::create() has been renamed to Terminal::fromStdio(). - enableRawMode() now returns ModeToken and uses TerminalException for operational/native failures. - readKey() now has a defined contract: false means timeout/no complete key before the deadline; non-TTY input, EOF and native/polling failures are exceptions. The timeout and sequenceTimeout behavior is now described explicitly in the RFC. - ModeToken lifetime and restoreMode() behavior are now described in detail. For the ownership question, I kept ModeToken opaque rather than adding a public Terminal back-reference. Nicolas, I also changed the implementation to the shared-per-terminal model you suggested. The first active token captures the original mode and owns the restoration descriptor/handle, subsequent tokens share that state, and the original mode is restored when the last active token is released. This makes release order irrelevant and also means closing the original PHP stream does not prevent restoration. I also reworked the terminal identity handling from the earlier st_rdev-only approach so different descriptors for the same logical terminal can coordinate without conflating unrelated PTYs. The RFC has been updated here: https://wiki.php.net/rfc/io_terminal Implementation: https://github.com/php/php-src/pull/23941 These are substantive API/semantic changes made during discussion, so this reply is also the announcement of those RFC changes. Thanks again for taking the time to review this. The lifecycle/ownership feedback in particular made the model much cleaner. Best regards, Pratik On Mon, 28 Sep 2026 15:00:09 +0200, Nicolas Grekas <[email protected]> wrote: > Hi Pratik, > > thanks for the RFC, PHP definitely needs native terminal support, calling > stty is a workaround we've been carrying since way too long. > > Le lun. 28 sept. 2026 à 11:49, Tim Düsterhus <[email protected]> a écrit : > > Hi > > On 2026-09-27 19:19, Pratik Bhujel wrote: > > > I would like to formally propose the Io\Terminal API for PHP 8.7: > > > > > > https://wiki.php.net/rfc/io_terminal > > Thank you for the RFC. Some questions to start of the discussion: > > 1. Should TerminalSize have a regular constructor? It seems to be safe > > to allow constructing it from userland, e.g. for testing purposes. > > 2. It would help readability if the stub would indicate the > > non-serializability (and strict properties) instead of mentioning it in > > the prose. Basically you can just take the stub file from your PR and > > include it in the RFC. > > 3. Terminal::create() should probably be ::fromStdio() or similar. > > 4. I'm not sure about false vs Exception for the various methods. > > `enableRawMode()` should probably be Exception, for `readKey()` the > > `false` return is not explained. Also the behavior of what happens when > > a timeout strikes is not explained. > > 5. Should ModeToken have a property that points back to the > > corresponding Terminal? Overall the interaction between the destructors > > and ModeToken and restoreMode should be explained more. As an example, > > what happens if the Terminal object dies before the ModeToken object? > > What if I create two Terminal objects for the same terminal? > > Related to ModeToken, what about a refcount instead? One record per terminal, > shared by all its tokens: the first enableRawMode() saves the original > termios and dup()s the fd into the record, the next ones only increment a > counter, and releasing any token decrements it. At zero, the original state > is restored through the record's own fd. That's order-independent by > construction and immune to fclose(). The RFC could then spec it in one > sentence: raw mode stays on as long as one token for that terminal is alive, > and the state from before the first token is restored when the last one goes > away. Today it says a token "restores its saved terminal mode when it is > destroyed", which isn't what happens out of order. If more modes are added > later, the record can keep the set of active requests and recompute the > target state from the original on each change. > > Cheers, > > Nicolas
