Skip to content

feat(swoole): add options to listen to swoole server events #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions src/swoole/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"illuminate/http": "^9.14",
"phpunit/phpunit": "^9.6.15",
"swoole/ide-helper": "^4.6",
"symfony/dependency-injection": "^5.4.33 || ^6.3.10 || ^7.0",
"symfony/http-foundation": "^5.4.32 || ^6.3.9 || ^7.0",
"symfony/http-kernel": "^5.4.33 || ^6.3.10 || ^7.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
33 changes: 33 additions & 0 deletions src/swoole/src/SwooleEventsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Runtime\Swoole;

use Psr\Container\ContainerInterface;
use Swoole\Constant;
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(Constant::EVENT_START, [$eventListener, 'onStart']);
$server->on(Constant::EVENT_WORKER_START, [$eventListener, 'onWorkerStart']);
$server->on(Constant::EVENT_WORKER_STOP, [$eventListener, 'onWorkerStop']);
$server->on(Constant::EVENT_WORKER_ERROR, [$eventListener, 'onWorkerError']);
$server->on(Constant::EVENT_WORKER_EXIT, [$eventListener, 'onWorkerExit']);
$server->on(Constant::EVENT_TASK, [$eventListener, 'onTask']);
$server->on(Constant::EVENT_FINISH, [$eventListener, 'onFinish']);
$server->on(Constant::EVENT_SHUTDOWN, [$eventListener, 'onShutdown']);
}
}
26 changes: 26 additions & 0 deletions src/swoole/src/SwooleServerEventListenerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?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
60 changes: 60 additions & 0 deletions src/swoole/tests/E2E/runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,80 @@
namespace Runtime\Swoole\Tests\E2E;

use Runtime\Swoole\Runtime;
use Runtime\Swoole\SwooleServerEventListenerInterface;
use Swoole\Constant;
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;
echo "Manager PID: {$server->manager_pid}".PHP_EOL;

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_listener_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