Skip to content

Commit

Permalink
Use fallback implementation when stty command fails (#63)
Browse files Browse the repository at this point in the history
* Fall back when `stty` execution fails

* Improve error message when a fallback doesn't exist
  • Loading branch information
jessarcher authored Sep 11, 2023
1 parent d6a1b43 commit 59df383
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 5 deletions.
7 changes: 6 additions & 1 deletion src/Concerns/Fallback.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Laravel\Prompts\Concerns;

use Closure;
use RuntimeException;

trait Fallback
{
Expand Down Expand Up @@ -49,7 +50,11 @@ public static function fallbackUsing(Closure $fallback): void
*/
public function fallback(): mixed
{
$fallback = static::$fallbacks[static::class];
$fallback = static::$fallbacks[static::class] ?? null;

if ($fallback === null) {
throw new RuntimeException('No fallback implementation registered for ['.static::class.']');
}

return $fallback($this);
}
Expand Down
11 changes: 10 additions & 1 deletion src/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Laravel\Prompts\Output\ConsoleOutput;
use RuntimeException;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

abstract class Prompt
{
Expand Down Expand Up @@ -80,12 +81,20 @@ public function prompt(): mixed

$this->checkEnvironment();

try {
static::terminal()->setTty('-icanon -isig -echo');
} catch (Throwable $e) {
static::output()->writeln("<comment>{$e->getMessage()}</comment>");
static::fallbackWhen(true);

return $this->fallback();
}

register_shutdown_function(function () {
$this->restoreCursor();
static::terminal()->restoreTty();
});

static::terminal()->setTty('-icanon -isig -echo');
$this->hideCursor();
$this->render();

Expand Down
32 changes: 29 additions & 3 deletions src/Terminal.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laravel\Prompts;

use RuntimeException;
use Symfony\Component\Console\Terminal as SymfonyTerminal;

class Terminal
Expand Down Expand Up @@ -36,9 +37,9 @@ public function read(): string
*/
public function setTty(string $mode): void
{
$this->initialTtyMode ??= (shell_exec('stty -g') ?: null);
$this->initialTtyMode ??= $this->exec('stty -g');

shell_exec("stty $mode");
$this->exec("stty $mode");
}

/**
Expand All @@ -47,7 +48,7 @@ public function setTty(string $mode): void
public function restoreTty(): void
{
if ($this->initialTtyMode) {
shell_exec("stty {$this->initialTtyMode}");
$this->exec("stty {$this->initialTtyMode}");

$this->initialTtyMode = null;
}
Expand Down Expand Up @@ -76,4 +77,29 @@ public function exit(): void
{
exit(1);
}

/**
* Execute the given command and return the output.
*/
protected function exec(string $command): string
{
$process = proc_open($command, [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
], $pipes);

if (! $process) {
throw new RuntimeException('Failed to create process.');
}

$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
$code = proc_close($process);

if ($code !== 0 || $stdout === false) {
throw new RuntimeException(trim($stderr ?: "Unknown error (code: $code)"), $code);
}

return $stdout;
}
}

0 comments on commit 59df383

Please sign in to comment.