Skip to content

Commit 3e2f1e9

Browse files
add response runner in frankenphp (#177)
1 parent 2246529 commit 3e2f1e9

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Runtime\FrankenPhpSymfony;
6+
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpFoundation\Response;
9+
use Symfony\Component\Runtime\RunnerInterface;
10+
11+
/**
12+
* A response runner for FrankenPHP.
13+
*
14+
* @author Kévin Dunglas <[email protected]>
15+
*/
16+
class ResponseRunner implements RunnerInterface
17+
{
18+
public function __construct(
19+
private Response $response,
20+
private int $loopMax,
21+
) {
22+
}
23+
24+
public function run(): int
25+
{
26+
// Prevent worker script termination when a client connection is interrupted
27+
ignore_user_abort(true);
28+
29+
$server = array_filter($_SERVER, static fn (string $key) => !str_starts_with($key, 'HTTP_'), ARRAY_FILTER_USE_KEY);
30+
$server['APP_RUNTIME_MODE'] = 'web=1&worker=1';
31+
32+
$handler = function () use ($server, &$sfRequest): void {
33+
// Merge the environment variables coming from DotEnv with the ones tied to the current request
34+
$_SERVER += $server;
35+
36+
$sfRequest = Request::createFromGlobals();
37+
$this->response->send();
38+
};
39+
40+
$loops = 0;
41+
do {
42+
$ret = \frankenphp_handle_request($handler);
43+
44+
gc_collect_cycles();
45+
} while ($ret && (-1 === $this->loopMax || ++$loops < $this->loopMax));
46+
47+
return 0;
48+
}
49+
}

src/frankenphp-symfony/src/Runtime.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Runtime\FrankenPhpSymfony;
66

7+
use Symfony\Component\HttpFoundation\Response;
78
use Symfony\Component\HttpKernel\HttpKernelInterface;
89
use Symfony\Component\Runtime\RunnerInterface;
910
use Symfony\Component\Runtime\SymfonyRuntime;
@@ -29,8 +30,14 @@ public function __construct(array $options = [])
2930

3031
public function getRunner(?object $application): RunnerInterface
3132
{
32-
if ($application instanceof HttpKernelInterface && ($_SERVER['FRANKENPHP_WORKER'] ?? false)) {
33-
return new Runner($application, $this->options['frankenphp_loop_max']);
33+
if ($_SERVER['FRANKENPHP_WORKER'] ?? false) {
34+
if ($application instanceof HttpKernelInterface) {
35+
return new Runner($application, $this->options['frankenphp_loop_max']);
36+
}
37+
38+
if ($application instanceof Response) {
39+
return new ResponseRunner($application, $this->options['frankenphp_loop_max']);
40+
}
3441
}
3542

3643
return parent::getRunner($application);

src/frankenphp-symfony/tests/RunnerTest.php

+12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require_once __DIR__.'/function-mock.php';
88

99
use PHPUnit\Framework\TestCase;
10+
use Runtime\FrankenPhpSymfony\ResponseRunner;
1011
use Runtime\FrankenPhpSymfony\Runner;
1112
use Symfony\Component\HttpFoundation\Request;
1213
use Symfony\Component\HttpFoundation\Response;
@@ -22,6 +23,17 @@ interface TestAppInterface extends HttpKernelInterface, TerminableInterface
2223
*/
2324
class RunnerTest extends TestCase
2425
{
26+
public function testResponseRun(): void
27+
{
28+
$application = $this->createMock(Response::class);
29+
$application
30+
->expects($this->once())
31+
->method('send');
32+
33+
$runner = new ResponseRunner($application, 500);
34+
$this->assertSame(0, $runner->run());
35+
}
36+
2537
public function testRun(): void
2638
{
2739
$application = $this->createMock(TestAppInterface::class);

src/frankenphp-symfony/tests/RuntimeTest.php

+16
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
namespace Runtime\FrankenPhpSymfony\Tests;
66

77
use PHPUnit\Framework\TestCase;
8+
use Runtime\FrankenPhpSymfony\ResponseRunner;
89
use Runtime\FrankenPhpSymfony\Runner;
910
use Runtime\FrankenPhpSymfony\Runtime;
11+
use Symfony\Component\HttpFoundation\Response;
1012
use Symfony\Component\HttpKernel\HttpKernelInterface;
1113

1214
/**
@@ -16,6 +18,7 @@ final class RuntimeTest extends TestCase
1618
{
1719
public function testGetRunner(): void
1820
{
21+
unset($_SERVER['FRANKENPHP_WORKER']);
1922
$application = $this->createStub(HttpKernelInterface::class);
2023

2124
$runtime = new Runtime();
@@ -25,4 +28,17 @@ public function testGetRunner(): void
2528
$_SERVER['FRANKENPHP_WORKER'] = 1;
2629
$this->assertInstanceOf(Runner::class, $runtime->getRunner($application));
2730
}
31+
32+
public function testGetResponseRunner(): void
33+
{
34+
unset($_SERVER['FRANKENPHP_WORKER']);
35+
$application = $this->createStub(Response::class);
36+
37+
$runtime = new Runtime();
38+
$this->assertNotInstanceOf(ResponseRunner::class, $runtime->getRunner(null));
39+
$this->assertNotInstanceOf(ResponseRunner::class, $runtime->getRunner($application));
40+
41+
$_SERVER['FRANKENPHP_WORKER'] = 1;
42+
$this->assertInstanceOf(ResponseRunner::class, $runtime->getRunner($application));
43+
}
2844
}

0 commit comments

Comments
 (0)