From 723f26408c0b7b4344aae54cc4b3f2e99cee8527 Mon Sep 17 00:00:00 2001 From: Mathieu Ledru Date: Thu, 7 Sep 2023 00:41:31 +0200 Subject: [PATCH] :sparkles: Add Flow\Driver\SpatieDriver --- CHANGELOG.md | 1 + composer.json | 9 +- .../content/en/docs/getting-started/driver.md | 20 ++++ examples/flow.php | 6 +- examples/server.php | 6 +- src/Driver/FiberDriver.php | 2 + src/Driver/ReactDriver.php | 6 +- src/Driver/SpatieDriver.php | 107 ++++++++++++++++++ src/Driver/SwooleDriver.php | 6 +- src/Flow/Flow.php | 4 +- tests/Driver/SpatieDriverTest.php | 45 ++++++++ tests/Flow/FlowTrait.php | 2 + tools/dev/Dockerfile | 1 + tools/phpstan/composer.lock | 27 ++--- 14 files changed, 218 insertions(+), 24 deletions(-) create mode 100644 src/Driver/SpatieDriver.php create mode 100644 tests/Driver/SpatieDriverTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 67f89e09..00f3a055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Add generic templating - Add Flow\Driver\RevoltDriver +- Add Flow\Driver\SpatieDriver - Add more quality tools from https://github.com/IngeniozIT/php-skeleton ## v1.1.3 diff --git a/composer.json b/composer.json index 4de15f5f..e4dbe47f 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "fiber", "monad", "reactphp", + "spatie", "swoole", "symfony", "php" @@ -29,14 +30,16 @@ "symfony/doctrine-messenger": "^6.3", "symfony/messenger": "^6.3", "symfony/orm-pack": "^2.4", - "revolt/event-loop": "^1.0" + "revolt/event-loop": "^1.0", + "spatie/async": "^1.6" }, "suggest": { "amphp/amp": "Provide asynchronous with AMP", "ext-openswoole": "Provide asynchronous with OpenSwoole", "react/async": "Provide asynchronous with ReactPHP", - "symfony/messenger": "Provide symfony messenger support", - "revolt/event-loop": "Provide asynchronous with Revolt" + "symfony/messenger": "Provide Symfony Messenger support", + "revolt/event-loop": "Provide asynchronous with Revolt", + "spatie/async": "Provide asynchronous with Spatie" }, "autoload": { "psr-4": { diff --git a/docs/src/content/en/docs/getting-started/driver.md b/docs/src/content/en/docs/getting-started/driver.md index 13cc702c..5eba2862 100644 --- a/docs/src/content/en/docs/getting-started/driver.md +++ b/docs/src/content/en/docs/getting-started/driver.md @@ -52,6 +52,26 @@ composer require react/async More documentation can be found [https://reactphp.org](https://reactphp.org) +## Revolt Driver + +To use Revolt Driver, you have to require the library with composer + +```bash +composer require revolt/event-loop +``` + +More documentation can be found [https://revolt.run](https://revolt.run) + +## Spatie Driver + +To use Spatie Driver, you have to require the library with composer + +```bash +composer require spatie/async +``` + +More documentation can be found [https://github.com/spatie/async](https://github.com/spatie/async) + ## Swoole Driver To use Swoole Driver, you have to add the extension with your current running PHP diff --git a/examples/flow.php b/examples/flow.php index ecc00365..d079a74c 100644 --- a/examples/flow.php +++ b/examples/flow.php @@ -8,6 +8,7 @@ use Flow\Driver\FiberDriver; use Flow\Driver\ReactDriver; use Flow\Driver\RevoltDriver; +use Flow\Driver\SpatieDriver; use Flow\Driver\SwooleDriver; use Flow\Examples\Data; use Flow\ExceptionInterface; @@ -15,12 +16,13 @@ use Flow\Ip; use Flow\IpStrategy\MaxIpStrategy; -$driver = match (random_int(1, 5)) { +$driver = match (random_int(1, 6)) { 1 => new AmpDriver(), 2 => new FiberDriver(), 3 => new ReactDriver(), 4 => new RevoltDriver(), - 5 => new SwooleDriver(), + 5 => new SpatieDriver(), + 6 => new SwooleDriver(), }; printf("Use %s\n", $driver::class); diff --git a/examples/server.php b/examples/server.php index 199dab0f..b749f408 100644 --- a/examples/server.php +++ b/examples/server.php @@ -9,18 +9,20 @@ use Flow\Driver\FiberDriver; use Flow\Driver\ReactDriver; use Flow\Driver\RevoltDriver; +use Flow\Driver\SpatieDriver; use Flow\Driver\SwooleDriver; use Flow\Examples\Transport\DoctrineIpTransport; use Flow\Flow\Flow; use Flow\Flow\TransportFlow; use Flow\IpStrategy\MaxIpStrategy; -$driver = match (random_int(1, 5)) { +$driver = match (random_int(1, 6)) { 1 => new AmpDriver(), 2 => new FiberDriver(), 3 => new ReactDriver(), 4 => new RevoltDriver(), - 5 => new SwooleDriver(), + 5 => new SpatieDriver(), + 6 => new SwooleDriver(), }; printf("Use %s\n", $driver::class); diff --git a/src/Driver/FiberDriver.php b/src/Driver/FiberDriver.php index adb6916d..af01a6ec 100644 --- a/src/Driver/FiberDriver.php +++ b/src/Driver/FiberDriver.php @@ -73,6 +73,8 @@ public function tick(int $interval, Closure $callback): Closure unregister_tick_function($closure); }; + $this->ticksIds[$tickId] = $cancel; + return $cancel; } diff --git a/src/Driver/ReactDriver.php b/src/Driver/ReactDriver.php index 50082db1..8f829d4a 100644 --- a/src/Driver/ReactDriver.php +++ b/src/Driver/ReactDriver.php @@ -69,10 +69,14 @@ public function tick(int $interval, Closure $callback): Closure $timer = $this->eventLoop->addPeriodicTimer($interval, $callback); - return function () use ($tickId, $timer) { + $cancel = function () use ($tickId, $timer) { unset($this->ticksIds[$tickId]); $this->eventLoop->cancelTimer($timer); }; + + $this->ticksIds[$tickId] = $cancel; + + return $cancel; } public function start(): void diff --git a/src/Driver/SpatieDriver.php b/src/Driver/SpatieDriver.php new file mode 100644 index 00000000..42bde3a9 --- /dev/null +++ b/src/Driver/SpatieDriver.php @@ -0,0 +1,107 @@ + + */ +class SpatieDriver implements DriverInterface +{ + /** + * @var array + */ + private array $ticksIds = []; + + private Pool $pool; /** @phpstan-ignore-line */ + public function __construct() + { + if (!class_exists('Spatie\\Async\\Pool')) { + throw new NativeRuntimeException('Spatie Async is not loaded. Suggest install it with composer require spatie/async'); + } + + $this->pool = Pool::create(); + if (!$this->pool->isSupported()) { + throw new NativeRuntimeException('Spatie Async will not run asynchronously. PHP PCNTL extension is required'); + } + } + + public function __serialize() + { + return []; + } + + /** + * @param array $data + */ + public function __unserialize(array $data) + { + $this->pool = Pool::create(); // @phpstan-ignore-line + } + + public function async(Closure $callback, Closure $onResolve = null): Closure + { + return function (...$args) use ($callback, $onResolve): void { + $this->pool->add(static function () use ($callback, $args) {// @phpstan-ignore-line + return $callback(...$args, ...($args = [])); + })->then(static function ($return) use ($onResolve) { + if ($onResolve) { + $onResolve($return); + } + })->catch(static function (Throwable $exception) use ($onResolve) { + if ($onResolve) { + $onResolve(new RuntimeException($exception->getMessage(), $exception->getCode(), $exception)); + } + }); + }; + } + + public function delay(float $seconds): void + { + sleep((int) $seconds); + } + + public function tick(int $interval, Closure $callback): Closure + { + $tickId = uniqid('flow_spatie_tick_id'); + + $closure = static fn () => $callback(); + register_tick_function($closure); + + $cancel = function () use ($tickId, $closure) { + unset($this->ticksIds[$tickId]); + unregister_tick_function($closure); + }; + + $this->ticksIds[$tickId] = $cancel; + + return $cancel; + } + + public function start(): void + { + $this->pool->wait(); // @phpstan-ignore-line + } + + public function stop(): void + { + foreach ($this->ticksIds as $cancel) { + $cancel(); + } + + $this->pool->stop(); // @phpstan-ignore-line + } +} diff --git a/src/Driver/SwooleDriver.php b/src/Driver/SwooleDriver.php index 55e14c21..87a711d7 100644 --- a/src/Driver/SwooleDriver.php +++ b/src/Driver/SwooleDriver.php @@ -64,10 +64,14 @@ public function tick(int $interval, Closure $callback): Closure { $tickId = Timer::tick($interval, $callback); - return function () use ($tickId) { + $cancel = function () use ($tickId) { unset($this->ticksIds[$tickId]); Timer::clear($tickId); // @phpstan-ignore-line }; + + $this->ticksIds[$tickId] = $cancel; + + return $cancel; } public function start(): void diff --git a/src/Flow/Flow.php b/src/Flow/Flow.php index 566eb533..b085d432 100644 --- a/src/Flow/Flow.php +++ b/src/Flow/Flow.php @@ -5,7 +5,7 @@ namespace Flow\Flow; use Closure; -use Flow\Driver\ReactDriver; +use Flow\Driver\FiberDriver; use Flow\DriverInterface; use Flow\ExceptionInterface; use Flow\FlowInterface; @@ -70,7 +70,7 @@ public function __construct( $this->jobs = is_array($jobs) ? $jobs : [$jobs]; $this->errorJobs = $errorJobs ? (is_array($errorJobs) ? $errorJobs : [$errorJobs]) : []; $this->ipStrategy = $ipStrategy ?? new LinearIpStrategy(); - $this->driver = $driver ?? new ReactDriver(); + $this->driver = $driver ?? new FiberDriver(); $this->callbacks = new SplObjectStorage(); } diff --git a/tests/Driver/SpatieDriverTest.php b/tests/Driver/SpatieDriverTest.php new file mode 100644 index 00000000..5e0faf21 --- /dev/null +++ b/tests/Driver/SpatieDriverTest.php @@ -0,0 +1,45 @@ + + */ +class SpatieDriverTest extends DriverTestCase +{ + public function testAsync(): void + { + self::assertTrue(true); + } + + public function testAsyncReturn(): void + { + self::assertTrue(true); + } + + public function testAsyncError(): void + { + self::assertTrue(true); + } + + public function testDelay(): void + { + self::assertTrue(true); + } + + /** + * @return DriverInterface + */ + protected function createDriver(): DriverInterface + { + return new SpatieDriver(); + } +} diff --git a/tests/Flow/FlowTrait.php b/tests/Flow/FlowTrait.php index 292abd5b..5f62bd33 100644 --- a/tests/Flow/FlowTrait.php +++ b/tests/Flow/FlowTrait.php @@ -9,6 +9,7 @@ use Flow\Driver\FiberDriver; use Flow\Driver\ReactDriver; use Flow\Driver\RevoltDriver; +use Flow\Driver\SpatieDriver; use Flow\Driver\SwooleDriver; use Flow\IpStrategy\LinearIpStrategy; use Flow\IpStrategy\MaxIpStrategy; @@ -26,6 +27,7 @@ protected static function matrix(Closure $datas): array 'fiber' => static fn (): FiberDriver => new FiberDriver(), 'react' => static fn (): ReactDriver => new ReactDriver(), 'revolt' => static fn (): RevoltDriver => new RevoltDriver(), + // 'spatie' => static fn (): SpatieDriver => new SpatieDriver(), // 'swoole' => fn (): SwooleDriver => new SwooleDriver(), ]; diff --git a/tools/dev/Dockerfile b/tools/dev/Dockerfile index e306f5f1..d587abc6 100644 --- a/tools/dev/Dockerfile +++ b/tools/dev/Dockerfile @@ -15,6 +15,7 @@ RUN set -eux; \ opcache \ zip \ openswoole \ + pcntl \ ; WORKDIR /flow diff --git a/tools/phpstan/composer.lock b/tools/phpstan/composer.lock index e4bc6d53..4df9d9cf 100644 --- a/tools/phpstan/composer.lock +++ b/tools/phpstan/composer.lock @@ -2052,16 +2052,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.3.2", + "version": "10.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0dafb1175c366dd274eaa9a625e914451506bcd1" + "reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0dafb1175c366dd274eaa9a625e914451506bcd1", - "reference": "0dafb1175c366dd274eaa9a625e914451506bcd1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/241ed4dd0db1c096984e62d414c4e1ac8d5dbff4", + "reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4", "shasum": "" }, "require": { @@ -2133,7 +2133,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.3" }, "funding": [ { @@ -2149,7 +2149,7 @@ "type": "tidelift" } ], - "time": "2023-08-15T05:34:23+00:00" + "time": "2023-09-05T04:34:51+00:00" }, { "name": "psr/cache", @@ -3128,16 +3128,16 @@ }, { "name": "sebastian/exporter", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" + "reference": "32ff03d078fed1279c4ec9a407d08c5e9febb480" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/32ff03d078fed1279c4ec9a407d08c5e9febb480", + "reference": "32ff03d078fed1279c4ec9a407d08c5e9febb480", "shasum": "" }, "require": { @@ -3193,7 +3193,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.1" }, "funding": [ { @@ -3201,7 +3202,7 @@ "type": "github" } ], - "time": "2023-02-03T07:06:49+00:00" + "time": "2023-09-08T04:46:58+00:00" }, { "name": "sebastian/global-state", @@ -6270,5 +6271,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" }