diff --git a/src/frankenphp-symfony/CHANGELOG.md b/src/frankenphp-symfony/CHANGELOG.md index 6aeb4c0..b2f110a 100644 --- a/src/frankenphp-symfony/CHANGELOG.md +++ b/src/frankenphp-symfony/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 0.3.0 + +- Add `frankenphp_kernel_reboot` option + ## 0.2.0 - Add support for Symfony 7 diff --git a/src/frankenphp-symfony/README.md b/src/frankenphp-symfony/README.md index 1dd5341..68148b8 100644 --- a/src/frankenphp-symfony/README.md +++ b/src/frankenphp-symfony/README.md @@ -16,10 +16,14 @@ Define the environment variable `APP_RUNTIME` for your application. Dotenv Component is executed after Runtime Component, so APP_RUNTIME must be available in your container. +The `FRANKENPHP_` environment variables are used to configure the runtime. If omitted, the default values are used. + ``` docker run \ -e FRANKENPHP_CONFIG="worker ./public/index.php" \ -e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \ + -e FRANKENPHP_LOOP_MAX=500 \ + -e FRANKENPHP_KERNEL_REBOOT=never \ -v $PWD:/app \ -p 80:80 -p 443:443 \ dunglas/frankenphp @@ -40,3 +44,4 @@ return function (array $context) { ## Options * `frankenphp_loop_max`: the number of requests after which the worker must restart, to prevent weird memory leaks (default to `500`, set to `-1` to never restart) +* `frankenphp_kernel_reboot`: whether the kernel should be rebooted after a request (default to `never`, set to `always` to reboot on each request) diff --git a/src/frankenphp-symfony/src/Runner.php b/src/frankenphp-symfony/src/Runner.php index de8a9ad..0530a85 100644 --- a/src/frankenphp-symfony/src/Runner.php +++ b/src/frankenphp-symfony/src/Runner.php @@ -6,6 +6,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\RebootableInterface; use Symfony\Component\HttpKernel\TerminableInterface; use Symfony\Component\Runtime\RunnerInterface; @@ -19,6 +20,7 @@ class Runner implements RunnerInterface public function __construct( private HttpKernelInterface $kernel, private int $loopMax, + private string $kernelReboot, ) { } @@ -48,6 +50,10 @@ public function run(): int $this->kernel->terminate($sfRequest, $sfResponse); } + if ($this->kernel instanceof RebootableInterface && ('always' === $this->kernelReboot)) { + $this->kernel->reboot(null); + } + gc_collect_cycles(); } while ($ret && (-1 === $this->loopMax || ++$loops <= $this->loopMax)); diff --git a/src/frankenphp-symfony/src/Runtime.php b/src/frankenphp-symfony/src/Runtime.php index efae98b..7083a93 100644 --- a/src/frankenphp-symfony/src/Runtime.php +++ b/src/frankenphp-symfony/src/Runtime.php @@ -18,11 +18,13 @@ class Runtime extends SymfonyRuntime /** * @param array{ * frankenphp_loop_max?: int, + * frankenphp_kernel_reboot?: string, * } $options */ public function __construct(array $options = []) { $options['frankenphp_loop_max'] = (int) ($options['frankenphp_loop_max'] ?? $_SERVER['FRANKENPHP_LOOP_MAX'] ?? $_ENV['FRANKENPHP_LOOP_MAX'] ?? 500); + $options['frankenphp_kernel_reboot'] = (string) ($options['frankenphp_kernel_reboot'] ?? $_SERVER['FRANKENPHP_KERNEL_REBOOT'] ?? $_ENV['FRANKENPHP_KERNEL_REBOOT'] ?? 'never'); parent::__construct($options); } @@ -30,7 +32,7 @@ public function __construct(array $options = []) public function getRunner(?object $application): RunnerInterface { if ($application instanceof HttpKernelInterface && ($_SERVER['FRANKENPHP_WORKER'] ?? false)) { - return new Runner($application, $this->options['frankenphp_loop_max']); + return new Runner($application, $this->options['frankenphp_loop_max'], $this->options['frankenphp_kernel_reboot']); } return parent::getRunner($application); diff --git a/src/frankenphp-symfony/tests/RunnerTest.php b/src/frankenphp-symfony/tests/RunnerTest.php index 8d3caa7..86b602f 100644 --- a/src/frankenphp-symfony/tests/RunnerTest.php +++ b/src/frankenphp-symfony/tests/RunnerTest.php @@ -11,9 +11,10 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\RebootableInterface; use Symfony\Component\HttpKernel\TerminableInterface; -interface TestAppInterface extends HttpKernelInterface, TerminableInterface +interface TestAppInterface extends HttpKernelInterface, TerminableInterface, RebootableInterface { } @@ -34,10 +35,27 @@ public function testRun(): void return new Response(); }); $application->expects($this->once())->method('terminate'); + $application->expects($this->never())->method('reboot'); $_SERVER['FOO'] = 'bar'; - $runner = new Runner($application, 500); + $runner = new Runner($application, 500, 'never'); + $this->assertSame(0, $runner->run()); + } + + public function testRebootAlways(): void + { + $application = $this->createMock(TestAppInterface::class); + $application + ->expects($this->once()) + ->method('handle') + ->willReturnCallback(function (Request $request, int $type = HttpKernelInterface::MAIN_REQUEST, bool $catch = true): Response { + return new Response(); + }); + $application->expects($this->once())->method('terminate'); + $application->expects($this->once())->method('reboot'); + + $runner = new Runner($application, 500, 'always'); $this->assertSame(0, $runner->run()); } }