text() and other prompts not working when stdin is piped
Laravel Prompts Version
0.3.3
Laravel Version
11.0
PHP Version
8.2
Operating System & Version
linux ubuntu 22.04
Terminal Application
WSL?
Description
Laravel\Prompts\text() and other functions do not work if something is piped into a process's STDIN pipe (and then read via stream_get_contents(STDIN);
ie
use function Laravel\Prompts\text;
$data = stream_get_contents(STDIN);
$desc = text( label: 'Description',
required: TRUE,
placeholder: 'Snippet description',
);
The problem is that NO input field is displayed
If I comment the $data = stream_get_contents(STDIN); line, the text() works as expected.
Note: I am using laravel/prompts composer package without Laravel !
Steps To Reproduce
use function Laravel\Prompts\text;
$data = stream_get_contents(STDIN);
$desc = text( label: 'Description',
required: TRUE,
placeholder: 'Snippet description',
);
Hi @henrywood,
I'm not sure it's possible to solve this as Prompts needs the STDIN stream to be a TTY to read the user input.
If you pipe something into the process, then STDIN won't be connected to the terminal input (stream_isatty(STDIN) is false).
Prompts attempts to handle this by switching to non-interactive mode, where default or empty values will be returned instead of rendering. However, your text prompt is marked as required so this should fail with a validation error rather than outputting nothing as you've described.
I note that stream_get_contents(STDIN) is blocking if you don't pipe anything into the process because STDIN will be endless, so no code afterwards will be executed unless you pipe something in that has an end.
But can't STDIN be reset / closed once it has been read by stream_get_contents(STDIN); ? such that it appears pristine to Prompts (when hitting the text() call) ?
I can't find a way to restore STDIN back to a TTY.
I did find that we can read the TTY directly from /dev/tty instead of STDIN, but I'm not sure how reliable/safe that is. We'd need a new way to detect when Prompts is running in a non-interactive environment (such as CI) so that it doesn't wait for user input that will never come. I'm unsure whether /dev/tty still exists and is readable in those environments.
Example code:
<?php
if (! stream_isatty(STDIN)) {
$data = stream_get_contents(STDIN, 1024);
var_dump($data);
}
$stream = fopen('/dev/tty', 'r');
var_dump(stream_isatty($stream));
var_dump(stream_get_contents($stream, 1));
This problem also happens when, for example, calling an external command before printing the field with the text function:
spin(
message: 'Create the tables...',
callback: fn () => $this->callSilent('migrate:fresh')
);
$name = text(
label: 'Insert the admin name',
default: $name ?? '',
required: true
);
The strange thing is that the field works, it just doesn't show either the field label or the ui, but if you type in the value it is received