Skip to content

Commit

Permalink
feat: add options to listen to swoole server events
Browse files Browse the repository at this point in the history
  • Loading branch information
praswicaksono committed Feb 3, 2024
1 parent 995f141 commit 8eb882d
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 10 deletions.
14 changes: 9 additions & 5 deletions src/swoole/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,20 @@ return function (array $context) {

You can define some configurations using Symfony's Runtime `APP_RUNTIME_OPTIONS` API.

| Option | Description | Default |
| --- | --- | --- |
| `host` | The host where the server should binds to (precedes `SWOOLE_HOST` environment variable) | `127.0.0.1` |
| `port` | The port where the server should be listing (precedes `SWOOLE_PORT` environment variable) | `8000` |
| `mode` | Swoole's server mode (precedes `SWOOLE_MODE` environment variable) | `SWOOLE_PROCESS` |
| Option | Description | Default |
| --- |-----------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
| `host` | The host where the server should binds to (precedes `SWOOLE_HOST` environment variable) | `127.0.0.1` |
| `port` | The port where the server should be listing (precedes `SWOOLE_PORT` environment variable) | `8000` |
| `mode` | Swoole's server mode (precedes `SWOOLE_MODE` environment variable) | `SWOOLE_PROCESS` |
| `settings` | All Swoole's server settings ([swoole.co.uk/docs/modules/swoole-server/configuration](https://www.swoole.co.uk/docs/modules/swoole-server/configuration)) | `[]` |
| `server_event_listener_factory` | Factory function to create swoole server event listener class that implement `Runtime\Swoole\SwooleServerEventListenerInterface` | `null` |

```php
// public/index.php

use App\Kernel;
use Psr\Container\ContainerInterface;
use Runtime\Swoole\SwooleServerEventListenerInterface;

$_SERVER['APP_RUNTIME_OPTIONS'] = [
'host' => '0.0.0.0',
Expand All @@ -75,6 +78,7 @@ $_SERVER['APP_RUNTIME_OPTIONS'] = [
\Swoole\Constant::OPTION_ENABLE_STATIC_HANDLER => true,
\Swoole\Constant::OPTION_DOCUMENT_ROOT => dirname(__DIR__).'/public'
],
'server_event_listener_factory' => fn(ContainerInterface $container) => $container->get(SwooleServerEventListenerInterface::class)
];

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
Expand Down
3 changes: 2 additions & 1 deletion src/swoole/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"phpunit/phpunit": "^9.6.15",
"swoole/ide-helper": "^4.6",
"symfony/http-foundation": "^5.4.32 || ^6.3.9 || ^7.0",
"symfony/http-kernel": "^5.4.33 || ^6.3.10 || ^7.0"
"symfony/http-kernel": "^5.4.33 || ^6.3.10 || ^7.0",
"symfony/dependency-injection": "^5.4.33 || ^6.3.10 ||^7.0"
},
"conflict": {
"ext-swoole": "<4.6.0"
Expand Down
8 changes: 7 additions & 1 deletion src/swoole/src/CallableRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
*/
class CallableRunner implements RunnerInterface
{
use SwooleEventsTrait;

/** @var ServerFactory */
private $serverFactory;
/** @var callable */
Expand All @@ -24,7 +26,11 @@ public function __construct(ServerFactory $serverFactory, callable $application)

public function run(): int
{
$this->serverFactory->createServer($this->application)->start();
$server = $this->serverFactory->createServer($this->application);

$this->registerSwooleEvents($server, $this->serverFactory->getOptions());

$server->start();

return 0;
}
Expand Down
11 changes: 10 additions & 1 deletion src/swoole/src/LaravelRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request as LaravelRequest;
use Psr\Container\ContainerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Symfony\Component\Runtime\RunnerInterface;
Expand All @@ -15,6 +16,8 @@
*/
class LaravelRunner implements RunnerInterface
{
use SwooleEventsTrait;

/** @var ServerFactory */
private $serverFactory;
/** @var Kernel */
Expand All @@ -28,7 +31,13 @@ public function __construct(ServerFactory $serverFactory, Kernel $application)

public function run(): int
{
$this->serverFactory->createServer([$this, 'handle'])->start();
$server = $this->serverFactory->createServer([$this, 'handle']);

if (($container = $this->application->getApplication()) instanceof ContainerInterface) {
$this->registerSwooleEvents($server, $this->serverFactory->getOptions(), $container);
}

$server->start();

return 0;
}
Expand Down
31 changes: 31 additions & 0 deletions src/swoole/src/SwooleEventsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);

namespace Runtime\Swoole;

use Psr\Container\ContainerInterface;
use Swoole\Server;

trait SwooleEventsTrait
{
private function registerSwooleEvents(Server $server, array $options, ?ContainerInterface $container = null): void
{
if (!array_key_exists('server_event_listener_factory', $options)) {
return;
}

$eventListener = $options['server_event_listener_factory']($container);
if (!$eventListener instanceof SwooleServerEventListenerInterface) {
return;
}

$server->on('start', [$eventListener, 'onStart']);
$server->on('workerStart', [$eventListener, 'onWorkerStart']);
$server->on('workerStop', [$eventListener, 'onWorkerStop']);
$server->on('workerError', [$eventListener, 'onWorkerError']);
$server->on('workerExit', [$eventListener, 'onWorkerExit']);
$server->on('task', [$eventListener, 'onTask']);
$server->on('finish', [$eventListener, 'onFinish']);
$server->on('shutdown', [$eventListener, 'onShutdown']);
}
}
18 changes: 18 additions & 0 deletions src/swoole/src/SwooleServerEventListenerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);

namespace Runtime\Swoole;

use Swoole\Server;

interface SwooleServerEventListenerInterface
{
public function onStart(Server $server): void;
public function onShutdown(Server $server): void;
public function onWorkerStart(Server $server, int $workerId): void;
public function onWorkerStop(Server $server, int $workerId): void;
public function onWorkerError(Server $server, int $workerId, int $exitCode, int $signal): void;
public function onWorkerExit(Server $server, int $workerId): void;
public function onTask(Server $server, int $taskId, int $srcWorkerId, mixed $data): void;
public function onFinish(Server $server, int $taskId, $data): void;
}
11 changes: 10 additions & 1 deletion src/swoole/src/SymfonyRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Swoole\Http\Request;
use Swoole\Http\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\Runtime\RunnerInterface;

Expand All @@ -15,6 +16,8 @@
*/
class SymfonyRunner implements RunnerInterface
{
use SwooleEventsTrait;

/** @var ServerFactory */
private $serverFactory;
/** @var HttpKernelInterface */
Expand All @@ -28,7 +31,13 @@ public function __construct(ServerFactory $serverFactory, HttpKernelInterface $a

public function run(): int
{
$this->serverFactory->createServer([$this, 'handle'])->start();
$server = $this->serverFactory->createServer([$this, 'handle']);

if ($this->application instanceof KernelInterface) {
$this->registerSwooleEvents($server, $this->serverFactory->getOptions(), $this->application->getContainer());
}

$server->start();

return 0;
}
Expand Down
58 changes: 58 additions & 0 deletions src/swoole/tests/E2E/runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,78 @@
namespace Runtime\Swoole\Tests\E2E;

use Runtime\Swoole\Runtime;
use Runtime\Swoole\SwooleServerEventListenerInterface;
use Swoole\Constant;
use Swoole\Coroutine;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Server;

require_once __DIR__.'/../../vendor/autoload.php';

$eventListener = new class implements SwooleServerEventListenerInterface {

#[\Override] public function onStart(Server $server): void
{
echo "-- onServerStart --" . PHP_EOL;
go(function () use ($server) {
Coroutine::waitSignal(SIGINT);
$server->shutdown();
$server->stop();
});

go(function () use ($server) {
$server->task(['data' => 'Hello World']);
});
}

#[\Override] public function onShutdown(Server $server): void
{
echo "-- onServerShutdown --" . PHP_EOL;
}

#[\Override] public function onWorkerStart(Server $server, int $workerId): void
{
echo "-- onWorkerStart --" . PHP_EOL;
}

#[\Override] public function onWorkerStop(Server $server, int $workerId): void
{
echo "-- onWorkerStop --" . PHP_EOL;
}

#[\Override] public function onWorkerError(Server $server, int $workerId, int $exitCode, int $signal): void
{
echo "-- onWorkerError --" . PHP_EOL;
}

#[\Override] public function onWorkerExit(Server $server, int $workerId): void
{
echo "-- onWorkerExit --" . PHP_EOL;
}

#[\Override] public function onTask(Server $server, int $taskId, int $srcWorkerId, mixed $data): void
{
echo "-- onTask --" . PHP_EOL;
echo "task payload: " . json_encode($data) . PHP_EOL;
$server->finish($data);
}

#[\Override] public function onFinish(Server $server, int $taskId, $data): void
{
echo "-- onTaskFinish --" . PHP_EOL;
}
};
$options = [
'port' => 8001,
'mode' => SWOOLE_BASE,
'settings' => [
Constant::OPTION_WORKER_NUM => 1,
Constant::OPTION_TASK_WORKER_NUM => 1,
Constant::OPTION_ENABLE_STATIC_HANDLER => true,
Constant::OPTION_DOCUMENT_ROOT => __DIR__.'/static',
],
'server_event_lister_factory' => fn() => $eventListener
];

$runtime = new Runtime($options);
Expand Down
5 changes: 5 additions & 0 deletions src/swoole/tests/Unit/CallableRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@
use PHPUnit\Framework\TestCase;
use Runtime\Swoole\CallableRunner;
use Runtime\Swoole\ServerFactory;
use Runtime\Swoole\SwooleServerEventListenerInterface;
use Swoole\Http\Server;

class CallableRunnerTest extends TestCase
{
public function testRun(): void
{
$eventListener = $this->createMock(SwooleServerEventListenerInterface::class);

$application = static function (): void {
};

$server = $this->createMock(Server::class);
$server->expects(self::once())->method('start');
$server->expects(self::exactly(8))->method('on')->with($this->anything(), $this->anything());

$factory = $this->createMock(ServerFactory::class);
$factory->expects(self::once())->method('createServer')->with(self::equalTo($application))->willReturn($server);
$factory->expects(self::once())->method('getOptions')->willReturn(['server_event_listener_factory' => fn() => $eventListener]);

$runner = new CallableRunner($factory, $application);

Expand Down
8 changes: 8 additions & 0 deletions src/swoole/tests/Unit/LaravelRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

use Illuminate\Contracts\Http\Kernel;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Runtime\Swoole\LaravelRunner;
use Runtime\Swoole\ServerFactory;
use Runtime\Swoole\SwooleServerEventListenerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;
Expand All @@ -17,13 +19,19 @@ class LaravelRunnerTest extends TestCase
{
public function testRun(): void
{
$container = $this->createMock(ContainerInterface::class);
$eventListener = $this->createMock(SwooleServerEventListenerInterface::class);

$application = $this->createMock(Kernel::class);
$application->expects(self::once())->method('getApplication')->willReturn($container);

$server = $this->createMock(Server::class);
$server->expects(self::once())->method('start');
$server->expects(self::exactly(8))->method('on')->with($this->anything(), $this->anything());

$factory = $this->createMock(ServerFactory::class);
$factory->expects(self::once())->method('createServer')->willReturn($server);
$factory->expects(self::once())->method('getOptions')->willReturn(['server_event_listener_factory' => fn() => $eventListener]);

$runner = new LaravelRunner($factory, $application);

Expand Down
11 changes: 10 additions & 1 deletion src/swoole/tests/Unit/SymfonyRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,33 @@

use PHPUnit\Framework\TestCase;
use Runtime\Swoole\ServerFactory;
use Runtime\Swoole\SwooleServerEventListenerInterface;
use Runtime\Swoole\SymfonyRunner;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelInterface;

class SymfonyRunnerTest extends TestCase
{
public function testRun(): void
{
$application = $this->createMock(HttpKernelInterface::class);
$container = $this->createMock(SymfonyContainerInterface::class);
$eventListener = $this->createMock(SwooleServerEventListenerInterface::class);

$application = $this->createMock(KernelInterface::class);
$application->expects(self::once())->method('getContainer')->willReturn($container);

$server = $this->createMock(Server::class);
$server->expects(self::once())->method('start');
$server->expects(self::exactly(8))->method('on')->with($this->anything(), $this->anything());

$factory = $this->createMock(ServerFactory::class);
$factory->expects(self::once())->method('createServer')->willReturn($server);
$factory->expects(self::once())->method('getOptions')->willReturn(['server_event_listener_factory' => fn() => $eventListener]);

$runner = new SymfonyRunner($factory, $application);

Expand Down

0 comments on commit 8eb882d

Please sign in to comment.