On Fri, Oct 2, 2026, at 10:59 AM, Tim Düsterhus wrote:
> Hi
>
> On 2026-10-02 16:19, Larry Garfield wrote:
>> My main concern is being able to mock the terminal in order to 
>> effectively test code that uses the terminal, without putting a 
>> proprietary thin wrapper around it (which largely defeats the purpose 
>> of having a good API in core).
>
> It is not clear to me what you mean by “proprietary” here. As I had 
> mentioned in my email, the RFC’s own PR is tested against processes 
> spawned using `proc_open()` with `pty` descriptors. The tests are 
> naturally BSD-licensed, just like PHP itself is (since PHP 8.6).

I didn't mean proprietary as in license.  I mean, for example, a 
SymfonyTerminal that wraps a Terminal so that SymfonyTerminal can be mocked.  
Which of course is different than LaravelTerminal.  That would be a bad place 
to end up, and we should avoid that.  ("Wrap 3rd party code so you can mock it" 
is a common recommendation, though as in this case it can lead to other 
problems.)

> In your test you would create a process that emits scripted output 
> (possibly in response to some input), just like you would in the “mocked 
> interface implementation” and then pass the PTY input/output streams to 
> whatever you want to test, which will then call 
> `(System)Terminal::fromStreams()` using them as parameters. The logic 
> under test reads inputs from the Terminal instance and writes output 
> using `fwrite()`. The subprocess will also enable you to properly model 
> concurrency, delay and timing in IO processing. Terminal interactions in 
> the real world are not synchronous either: The terminal logic relies on 
> timing to distinguish escape sequences from individual characters 
> (that's what the `$sequenceTimeout` is for) and the user might already 
> provide additional input while your application is still busy rendering 
> output and not yet expecting additional data.
>
>> Interfaces are the standard way of doing that.  If you have a 
>> suggestion for a better way, I'm happy to see it.
>
> My email included 5 arguments as to why an interface is the wrong design 
> here. Do you plan to engage with those?

No, because I am not advocating for interfaces.  I am advocating for a clean 
and obvious mocking/testing mechanism, for which interfaces are a common 
solution.  I am not wedded to interfaces as the solution, just that there is a 
reliable one that is self-evident (and/or documented).  That is, my invitation 
to suggest a better way was in no way factious or snarky.

If I understand what you and Bob (thanks Bob) are suggesting, one would do 
something like:

$in = fopen('php://memory');
$out = fopen('php://memory');

fputs($in, "first line\n");
fputs($in, "second line\n");
rewind($in);

$t = Terminal::fromStreams($in, $out);
$line = $t->readLine();
assert($line === 'first line');
$line = $t->readLine();
assert($line === 'second line');

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?

If so, then I agree the interfaces become unnecessary, and the above should be 
included in the RFC as the recommended testing approach.

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?

--Larry Garfield

Reply via email to