Hi all,

Larry, Tim, Bob, thanks for the detailed replies. Tim, thanks also for
compiling the branch and checking the POSIX example.

> Is that correct? Pratik, are you able to confirm (via tests) that this
> would work as a testing approach, including for key and secret reads?

For readLine(), yes: exactly as you wrote it, I can confirm it works. I ran:

    $in = fopen('php://memory', 'w+');
    fwrite($in, "first line\nsecond line\n");
    rewind($in);

    $t = Io\Terminal\SystemTerminal::fromStreams($in);

    var_dump($t->readLine());
    var_dump($t->readLine());
    var_dump($t->readLine());

and got:

    string(10) "first line"
    string(11) "second line"
    NULL

I also checked empty lines, immediate EOF and a final unterminated line.
Those behave as specified.

For readKey() and readSecret(), ordinary streams like php://memory or
php://temp do not work, and are intentionally rejected because those
operations require an actual terminal device. For example:

    $t->readKey();
    // Io\Terminal\TerminalException: Failed to read key: input stream is
not a terminal

On POSIX, Tim's proc_open() + PTY example is exactly the right kind of test
for this. It gives the code a real terminal while still letting the test
script the input from pure PHP.

I also checked Windows because I did not want to assume the POSIX PTY
mechanism translated there. It does not directly, since proc_open() on
Windows does not provide the same PTY descriptor. I built the current
branch on Windows 11 ARM64 and exercised the native console path instead.
The PHP side was just:

    $t = Io\Terminal\SystemTerminal::fromStdio();
    var_dump($t->readKey());

A small harness attached to a real Windows console injected KEY_EVENT
records into CONIN$ with WriteConsoleInputW(). Injecting Up produced:

    enum(Io\Terminal\Key::Up)

I also checked Down, Left, Right, Enter, Tab, Backspace, Escape, F1,
ordinary characters, repeat counts and Unicode including a UTF-16 surrogate
pair. readSecret() was exercised through the same native console path for
normal input, empty input, backspace, Unicode, Escape/Ctrl+C cancellation
(which throws TerminalException) and zero-timeout return (which returns
null). readLine() was exercised through the native ReadConsoleW() path as
well. The ARM64 build needed an unrelated local Zend SIMD compiler
workaround, but I did not change Io\Terminal for these tests.

So the direct answer is yes for line input, but with a platform distinction
for interactive keys. Ordinary stream behavior can be scripted with normal
PHP streams. Terminal-specific behavior needs a real terminal. On POSIX
that is conveniently available through a PTY in pure PHP, as Tim showed. On
Windows the native implementation is testable too, but the equivalent test
currently needs a real console/native helper rather than the same PTY
recipe. That means Tim and Bob's point about the stream/terminal being the
underlying I/O boundary holds, while Larry's concern about a
straightforward cross-platform testing seam is still relevant for Windows
code that directly consumes readKey().

> Although, it occurs to me while typing the above, the Terminal accepts
> an output stream, but doesn't appear to have any output API. When is
> the output stream even used? Should it be removed, or a print() (or
> similar) API added?

I checked this in the implementation. The output stream is not unused.
getSize() queries the output side for terminal dimensions. readKey() also
checks the output side for size changes so it can return Key::Resize, and
on Windows a WINDOW_BUFFER_SIZE_EVENT causes the output side to be queried
again for the current dimensions. So I don't think the output stream should
be removed. I also don't think we need print() or write() just to justify
it; actual application output can continue to use fwrite()/echo and normal
stream APIs.

On the interface question, Tim's objections to the current
Terminal/ModeToken shape still make sense to me, especially the ModeToken
case where a userland token can satisfy the interface but cannot actually
be restored by the native terminal. At the same time, after checking
Windows I don't think I can honestly say that php://memory or stream
injection alone makes the interface unnecessary everywhere. It clearly
solves readLine(), and the PTY gives us a good POSIX integration seam, but
Windows userland code directly consuming readKey() does not have the same
pure-PHP PTY seam today.

I have not changed the RFC or implementation yet. I would like to settle
this point before making another API change, so that the RFC can go into
the next stage with this part resolved rather than carrying the
disagreement forward. The remaining question for me is what testing seam we
want cross-platform userland code which directly consumes readKey() to
have, without keeping the ModeToken problem Tim pointed out.

Best regards,
Pratik

Reply via email to