2022-12-23 20:44:07 +01:00
|
|
|
<?php declare(strict_types=1);
|
2020-07-21 18:06:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generic loop.
|
|
|
|
*
|
|
|
|
* @author Daniil Gentili <daniil@daniil.it>
|
|
|
|
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
|
|
|
|
* @license https://opensource.org/licenses/MIT MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace danog\Loop\Generic;
|
|
|
|
|
2022-12-24 15:03:00 +01:00
|
|
|
use Amp\Future;
|
2020-07-21 21:45:22 +02:00
|
|
|
use danog\Loop\ResumableSignalLoop;
|
2020-07-21 18:06:19 +02:00
|
|
|
|
2022-12-23 20:44:07 +01:00
|
|
|
use function Amp\async;
|
|
|
|
|
2020-07-21 18:06:19 +02:00
|
|
|
/**
|
|
|
|
* Generic loop, runs single callable.
|
|
|
|
*
|
|
|
|
* The return value of the callable can be:
|
|
|
|
* * A number - the loop will be paused for the specified number of seconds
|
|
|
|
* * GenericLoop::STOP - The loop will stop
|
|
|
|
* * GenericLoop::PAUSE - The loop will pause forever (or until loop is `resumed()`
|
|
|
|
* from outside the loop)
|
|
|
|
* * GenericLoop::CONTINUE - Return this if you want to rerun the loop immediately
|
|
|
|
*
|
|
|
|
* If the callable does not return anything,
|
|
|
|
* the loop will behave is if GenericLoop::PAUSE was returned.
|
|
|
|
*
|
|
|
|
* The loop can be stopped from the outside by signaling `true`.
|
|
|
|
*
|
2022-12-24 15:03:00 +01:00
|
|
|
* @psalm-type TCallableReturn=int|null|Future<int|null>
|
2020-07-21 18:06:19 +02:00
|
|
|
*
|
|
|
|
* @author Daniil Gentili <daniil@daniil.it>
|
|
|
|
*/
|
|
|
|
class GenericLoop extends ResumableSignalLoop
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Stop the loop.
|
|
|
|
*/
|
|
|
|
const STOP = -1;
|
|
|
|
/**
|
|
|
|
* Pause the loop.
|
|
|
|
*/
|
|
|
|
const PAUSE = null;
|
|
|
|
/**
|
|
|
|
* Rerun the loop.
|
|
|
|
*/
|
|
|
|
const CONTINUE = 0;
|
|
|
|
/**
|
|
|
|
* Callable.
|
|
|
|
*
|
2022-12-24 17:24:51 +01:00
|
|
|
* @var callable():TCallableReturn
|
2020-07-21 18:06:19 +02:00
|
|
|
*/
|
|
|
|
protected $callable;
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
2020-07-24 19:24:04 +02:00
|
|
|
* If possible, the callable will be bound to the current instance of the loop.
|
|
|
|
*
|
2022-12-24 15:03:00 +01:00
|
|
|
* @param callable():TCallableReturn $callable Callable to run
|
2020-07-21 18:06:19 +02:00
|
|
|
* @param string $name Loop name
|
|
|
|
*/
|
2022-12-24 15:03:00 +01:00
|
|
|
public function __construct(callable $callable, protected string $name)
|
2020-07-21 18:06:19 +02:00
|
|
|
{
|
2020-07-24 19:24:04 +02:00
|
|
|
if ($callable instanceof \Closure) {
|
|
|
|
try {
|
|
|
|
$callable = $callable->bindTo($this);
|
2022-12-24 15:03:00 +01:00
|
|
|
} catch (\Throwable) {
|
2020-07-24 19:24:04 +02:00
|
|
|
// Might cause an error for wrapped object methods
|
|
|
|
}
|
|
|
|
}
|
2020-07-21 18:06:19 +02:00
|
|
|
$this->callable = $callable;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Loop implementation.
|
|
|
|
*/
|
2022-12-23 20:44:07 +01:00
|
|
|
public function loop(): void
|
2020-07-21 18:06:19 +02:00
|
|
|
{
|
|
|
|
$callable = $this->callable;
|
|
|
|
while (true) {
|
|
|
|
$timeout = $callable();
|
2022-12-24 15:03:00 +01:00
|
|
|
if ($timeout instanceof Future) {
|
|
|
|
$timeout = $timeout->await();
|
2020-07-21 18:06:19 +02:00
|
|
|
}
|
|
|
|
if ($timeout === self::PAUSE) {
|
|
|
|
$this->reportPause(0);
|
|
|
|
} elseif ($timeout > 0) {
|
|
|
|
$this->reportPause($timeout);
|
|
|
|
}
|
2022-12-24 17:24:51 +01:00
|
|
|
if ($timeout === self::STOP || $this->waitSignal(async($this->pause(...), $timeout))) {
|
2020-07-23 17:03:53 +02:00
|
|
|
break;
|
2020-07-21 18:06:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Report pause, can be overriden for logging.
|
|
|
|
*
|
|
|
|
* @param integer $timeout Pause duration, 0 = forever
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected function reportPause(int $timeout): void
|
|
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
2020-07-24 19:24:04 +02:00
|
|
|
* Get loop name, provided to constructor.
|
2020-07-21 18:06:19 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function __toString(): string
|
|
|
|
{
|
|
|
|
return $this->name;
|
|
|
|
}
|
|
|
|
}
|