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

Pulling my reply to the top-level to reduce thread nesting. My apologies for only getting back to the RFC now, the last few weeks have been busy. I have just given the RFC another full read and have some more comments, particularly around the “mode lease handling”, which is the by far most complicated part of the RFC and where I feel that some extra explanation and examples could help folks understand the behavior better than the (extremely information dense) prose that the RFC currently contains.

Specifically:

(1) My understanding is that dropping the `ModeToken` is equivalent to passing it to `restoreMode()`. I think it would make sense to make this “destruction behavior” explicit by adding a `ModeToken::__destruct()` function to the stub, even if the internal implementation does not actually implement `__destruct()`. Seeing the `__destruct()` in the stub is a good visual indicator that the class has “special behavior” on destruction that goes beyond the usual “all associated resources are freed or closed”. The restoration is not just freeing or closing, it's actual behavior.

(2) An explicit example where multiple `ModeToken` leases are obtained with explicit comments what happens at each step would go great lengths of making the behavior approachable. From my understanding this would be a correct example?

    <?php

    $terminal = Terminal::fromStdio();

$lease1 = $terminal->enableRawMode(); // Terminal is in raw mode now. $lease2 = $terminal->enableRawMode(); // No change to the terminal mode. 2 leases active. $lease3 = $terminal->enableRawMode(); // No change to the terminal mode. 3 leases active.

assert($lease1 !== $lease2); // The lease objects are separate, there is no singleton cache.

    // Leases can be released in any order.

    unset($lease2); // No change to the terminal mode. 2 leases active.

$terminal->restoreMode($lease1); // No change to the terminal mode. 1 lease active.

    $terminal->restoreMode($lease3); // Terminal goes back to normal.

(void)$terminal->enableRawMode(); // Terminal is in raw mode and immediately goes back to normal, because the returned token is immediately destroyed.

and

    <?php

    $terminal = Terminal::fromStdio();

$lease1 = $terminal->enableRawMode(); // Terminal is in raw mode now.

unset($terminal); // Terminal object is destroyed, but the actual terminal remains in raw mode.

$terminal = Terminal::fromStdio(); // This Terminal object starts off in raw mode!

    unset($lease1); // The terminal goes back to normal.

Perhaps there are more interactions that would be worth showing as an example.

(3) Given the `(void)$terminal->enableRawMode()` call in the first example, I believe that `Terminal::enableRawMode()` should be `#[\NoDiscard]`.

(4) The RFC mentions “Final restoration restores the saved snapshot, rather than merging changes made externally while leases were active.”. It should be clear what is being restored. Is it just the terminal mode (raw or not)? What happens if the terminal was already in raw mode (e.g. using a `stty` call) when `enableRawMode()` is called? Will `enableRawMode()` be a no-op and will returning the lease put the terminal back into raw mode when another `stty` call already put it in normal mode? Or will it return a no-op lease?

(5) In the “Reading keys” section, the RFC says “The sequence wait cannot extend the overall timeout.”. Will this be a ValueError, or will it silently be clamped?

(6) For reading lines: I think the removal of the line terminator is inconsistent with every existing API. I don't necessarily disagree with the proposed behavior, but the RFC should properly justify the difference. This is also important for the documentation.

(7) The “Input state and sequential reads” section could also do with examples.

(8) I'm not sure if `Key::Resize` is good design. Just to throw a suggestion in the ring: We could have two enums `Key` and `Metadata` (or similar), with `readKey()` returning `Key|Metadata`. No strong opinion on that, though.

----------

Overall I'm pretty happy with the proposed API now. My main concern is the lack of examples which make the RFC very hard to approach and probably makes it hard for voters to make an educated decisions.

Best regards
Tim Düsterhus

Reply via email to