Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace shutdown handlers with destructors #81

Merged
merged 2 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Concerns/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,12 @@ public function emit(string $event, mixed ...$data): void
$listener(...$data);
}
}

/**
* Clean the event listeners.
*/
public function clearListeners(): void
{
$this->listeners = [];
}
}
72 changes: 39 additions & 33 deletions src/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,52 +74,48 @@ abstract public function value(): mixed;
*/
public function prompt(): mixed
{
static::$interactive ??= stream_isatty(STDIN);

if (! static::$interactive) {
return $this->default();
}
try {
static::$interactive ??= stream_isatty(STDIN);

$this->capturePreviousNewLines();
if (! static::$interactive) {
return $this->default();
}

if (static::shouldFallback()) {
return $this->fallback();
}
$this->capturePreviousNewLines();

$this->checkEnvironment();
if (static::shouldFallback()) {
return $this->fallback();
}

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

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

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

$this->hideCursor();
$this->render();
$this->hideCursor();
$this->render();

while (($key = static::terminal()->read()) !== null) {
$continue = $this->handleKeyPress($key);
while (($key = static::terminal()->read()) !== null) {
$continue = $this->handleKeyPress($key);

$this->render();
$this->render();

if ($continue === false || $key === Key::CTRL_C) {
$this->restoreCursor();
static::terminal()->restoreTty();
if ($continue === false || $key === Key::CTRL_C) {
if ($key === Key::CTRL_C) {
static::terminal()->exit();
}

if ($key === Key::CTRL_C) {
static::terminal()->exit();
return $this->value();
}

return $this->value();
}
} finally {
$this->clearListeners();
}
}

Expand Down Expand Up @@ -343,4 +339,14 @@ private function checkEnvironment(): void
throw new RuntimeException('Prompts is not currently supported on Windows. Please use WSL or configure a fallback.');
}
}

/**
* Restore the cursor and terminal state.
*/
public function __destruct()
{
$this->restoreCursor();

static::terminal()->restoreTty();
}
}
29 changes: 19 additions & 10 deletions src/Spinner.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class Spinner extends Prompt
*/
public bool $static = false;

/**
* The process ID after forking.
*/
protected int $pid;

/**
* Create a new Spinner instance.
*/
Expand All @@ -42,8 +47,6 @@ public function spin(Closure $callback): mixed
{
$this->capturePreviousNewLines();

register_shutdown_function(fn () => $this->restoreCursor());

if (! function_exists('pcntl_fork')) {
return $this->renderStatically($callback);
}
Expand All @@ -56,9 +59,9 @@ public function spin(Closure $callback): mixed
$this->hideCursor();
$this->render();

$pid = pcntl_fork();
$this->pid = pcntl_fork();

if ($pid === 0) {
if ($this->pid === 0) {
while (true) { // @phpstan-ignore-line
$this->render();

Expand All @@ -67,12 +70,8 @@ public function spin(Closure $callback): mixed
usleep($this->interval * 1000);
}
} else {
register_shutdown_function(fn () => posix_kill($pid, SIGHUP));

$result = $callback();

posix_kill($pid, SIGHUP);

$this->resetTerminal($originalAsync);

return $result;
Expand All @@ -93,7 +92,6 @@ protected function resetTerminal(bool $originalAsync): void
pcntl_signal(SIGINT, SIG_DFL);

$this->eraseRenderedLines();
$this->showCursor();
}

/**
Expand All @@ -115,7 +113,6 @@ protected function renderStatically(Closure $callback): mixed
$result = $callback();
} finally {
$this->eraseRenderedLines();
$this->showCursor();
}

return $result;
Expand Down Expand Up @@ -148,4 +145,16 @@ protected function eraseRenderedLines(): void
$this->moveCursor(-999, -count($lines) + 1);
$this->eraseDown();
}

/**
* Clean up after the spinner.
*/
public function __destruct()
{
parent::__destruct();
jessarcher marked this conversation as resolved.
Show resolved Hide resolved

if (! empty($this->pid)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to me, that you want to do if ($this->pid > 0)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destructor can be called before the fork occurs, so this property would not be initialised yet.

isset($this->pid) && $this->pid > 0 would work, but at that point, it's effectively the same as ! empty($this->pid) given that $this->pid is an int.

posix_kill($this->pid, SIGHUP);
}
}
}
2 changes: 1 addition & 1 deletion src/Terminal.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function setTty(string $mode): void
*/
public function restoreTty(): void
{
if ($this->initialTtyMode) {
if (isset($this->initialTtyMode)) {
$this->exec("stty {$this->initialTtyMode}");

$this->initialTtyMode = null;
Expand Down