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;
|
|
|
|
|
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`.
|
|
|
|
*
|
2020-07-22 18:18:57 +02:00
|
|
|
* @template T as int|null
|
2020-07-21 18:06:19 +02:00
|
|
|
* @template TGenerator as \Generator<mixed,Promise|array<array-key,Promise>,mixed,Promise<T>|T>
|
|
|
|
* @template TPromise as Promise<T>
|
|
|
|
*
|
|
|
|
* @template TCallable as T|TPromise|TGenerator
|
|
|
|
*
|
|
|
|
* @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.
|
|
|
|
*
|
|
|
|
* @var callable
|
|
|
|
*
|
|
|
|
* @psalm-var callable():TCallable
|
|
|
|
*/
|
|
|
|
protected $callable;
|
|
|
|
/**
|
|
|
|
* Loop name.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $name;
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
2020-07-24 19:24:04 +02:00
|
|
|
* If possible, the callable will be bound to the current instance of the loop.
|
|
|
|
*
|
2020-07-21 18:06:19 +02:00
|
|
|
* @param callable $callable Callable to run
|
|
|
|
* @param string $name Loop name
|
|
|
|
*
|
|
|
|
* @psalm-param callable():TCallable $callable Callable to run
|
|
|
|
*/
|
|
|
|
public function __construct(callable $callable, string $name)
|
|
|
|
{
|
2020-07-24 19:24:04 +02:00
|
|
|
if ($callable instanceof \Closure) {
|
|
|
|
try {
|
|
|
|
$callable = $callable->bindTo($this);
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
// Might cause an error for wrapped object methods
|
|
|
|
}
|
|
|
|
}
|
2020-07-21 18:06:19 +02:00
|
|
|
$this->callable = $callable;
|
|
|
|
$this->name = $name;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 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) {
|
2020-07-23 14:18:33 +02:00
|
|
|
/** @psalm-var ?int|TGenerator|TPromise */
|
2020-07-21 18:06:19 +02:00
|
|
|
$timeout = $callable();
|
|
|
|
if ($timeout instanceof \Generator) {
|
2020-07-23 14:18:33 +02:00
|
|
|
/** @psalm-var ?int */
|
2022-12-23 20:44:07 +01:00
|
|
|
$timeout = $timeout;
|
2020-07-21 18:06:19 +02:00
|
|
|
} elseif ($timeout instanceof Promise) {
|
2020-07-23 14:18:33 +02:00
|
|
|
/** @psalm-var ?int */
|
2022-12-23 20:44:07 +01:00
|
|
|
$timeout = $timeout;
|
2020-07-21 18:06:19 +02:00
|
|
|
}
|
|
|
|
if ($timeout === self::PAUSE) {
|
|
|
|
$this->reportPause(0);
|
|
|
|
} elseif ($timeout > 0) {
|
|
|
|
$this->reportPause($timeout);
|
|
|
|
}
|
|
|
|
/** @psalm-suppress MixedArgument */
|
2022-12-23 20:44:07 +01:00
|
|
|
if ($timeout === self::STOP || $this->waitSignal(async(fn () => $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;
|
|
|
|
}
|
|
|
|
}
|