-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added process control signal handler Added Spinner Added functionality to hide/show/restore the cursor
- Loading branch information
Showing
9 changed files
with
330 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<?php | ||
|
||
/** | ||
* @copyright Frederic G. Østby | ||
* @license http://www.makoframework.com/license | ||
*/ | ||
|
||
namespace mako\cli\output\helpers; | ||
|
||
use mako\cli\output\Output; | ||
|
||
use function count; | ||
use function extension_loaded; | ||
use function pcntl_fork; | ||
use function pcntl_wait; | ||
use function posix_getpid; | ||
use function posix_kill; | ||
use function usleep; | ||
|
||
/** | ||
* Spinner. | ||
*/ | ||
class Spinner | ||
{ | ||
/** | ||
* Spinner frames. | ||
*/ | ||
public const array FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; | ||
|
||
/** | ||
* Time between redraw in microseconds. | ||
*/ | ||
protected const int TIME_BETWEEN_REDRAW = 100000; | ||
|
||
/** | ||
* Can we fork the process? | ||
*/ | ||
protected bool $canFork; | ||
|
||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct( | ||
protected Output $output, | ||
protected array $frames = Spinner::FRAMES, | ||
) { | ||
$this->canFork = $this->canFork(); | ||
} | ||
|
||
/** | ||
* Returns TRUE if we can fork the process and FALSE if not. | ||
*/ | ||
protected function canFork(): bool | ||
{ | ||
return extension_loaded('pcntl') && extension_loaded('posix'); | ||
} | ||
|
||
/** | ||
* Draws the spinner. | ||
*/ | ||
protected function spinner(string $message): void | ||
{ | ||
$i = 0; | ||
|
||
$frames = count($this->frames); | ||
|
||
while (true) { | ||
$this->output->write("\r" . $this->frames[$i++ % $frames] . " {$message}"); | ||
|
||
usleep(static::TIME_BETWEEN_REDRAW); | ||
|
||
if (posix_kill(posix_getpid(), 0) === false) { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Draws the spinner. | ||
*/ | ||
public function spin(string $message, callable $callback): mixed | ||
{ | ||
$result = null; | ||
|
||
$this->output->hideCursor(); | ||
|
||
$pid = $this->canFork ? pcntl_fork() : -1; | ||
|
||
if ($pid == -1) { | ||
// We were unable to fork the process so we'll just run the callback in the current process | ||
|
||
$this->output->write($message); | ||
|
||
$result = $callback(); | ||
|
||
$this->output->clearLine(); | ||
} | ||
elseif ($pid) { | ||
// We're in the parent process so we'll execute the callback here | ||
|
||
$result = $callback(); | ||
|
||
posix_kill($pid, SIGKILL); | ||
|
||
pcntl_wait($status); | ||
|
||
$this->output->clearLine(); | ||
} | ||
else { | ||
// We're in the child process so we'll display the spinner | ||
|
||
$this->spinner($message); | ||
} | ||
|
||
$this->output->showCursor(); | ||
|
||
return $result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
<?php | ||
|
||
/** | ||
* @copyright Frederic G. Østby | ||
* @license http://www.makoframework.com/license | ||
*/ | ||
|
||
namespace mako\cli\signals; | ||
|
||
use function count; | ||
use function function_exists; | ||
use function pcntl_async_signals; | ||
use function pcntl_signal; | ||
|
||
/** | ||
* Signal handler. | ||
*/ | ||
class SignalHandler | ||
{ | ||
/** | ||
* Can we handle signals? | ||
*/ | ||
protected bool $canHandle = false; | ||
|
||
/** | ||
* Signal handlers. | ||
*/ | ||
protected array $handlers = []; | ||
|
||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct() | ||
{ | ||
if (function_exists('pcntl_async_signals')) { | ||
pcntl_async_signals(true); | ||
|
||
$this->canHandle = true; | ||
} | ||
} | ||
|
||
/** | ||
* Can we handle signals? | ||
*/ | ||
public function canHandleSignals(): bool | ||
{ | ||
return $this->canHandle; | ||
} | ||
|
||
/** | ||
* Registers a singal handler for the chosen signal. | ||
*/ | ||
public function addHandler(array|int $signal, callable $handler): void | ||
{ | ||
foreach ((array) $signal as $signalToHandle) { | ||
$this->handlers[$signalToHandle][] = $handler; | ||
|
||
if (count($this->handlers[$signalToHandle]) === 1) { | ||
pcntl_signal($signalToHandle, function ($signal): void { | ||
$count = count($this->handlers[$signal]); | ||
|
||
foreach ($this->handlers[$signal] as $i => $handler) { | ||
$isLast = $i === $count - 1; | ||
|
||
$handler($signal, $isLast); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.