prompts icon indicating copy to clipboard operation
prompts copied to clipboard

text() and other prompts not working when stdin is piped

Open henrywood opened this issue 10 months ago • 4 comments

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',
);

henrywood avatar Jan 21 '25 02:01 henrywood

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.

jessarcher avatar Jan 22 '25 00:01 jessarcher

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) ?

henrywood avatar Jan 22 '25 19:01 henrywood

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));

jessarcher avatar Jan 23 '25 00:01 jessarcher

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

p-fazzini avatar Jul 08 '25 07:07 p-fazzini