From 157aeaef46c27bac89e69cfea9cc83b85bf07bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 14 Oct 2022 12:51:37 +0200 Subject: [PATCH 1/5] feat: add a runtime for FrankenPHP with Symfony --- src/frankenphp-symfony/.github/FUNDING.yml | 3 ++ src/frankenphp-symfony/.gitignore | 2 + src/frankenphp-symfony/CHANGELOG.md | 5 +++ src/frankenphp-symfony/LICENSE | 21 +++++++++++ src/frankenphp-symfony/README.md | 40 ++++++++++++++++++++ src/frankenphp-symfony/composer.json | 33 ++++++++++++++++ src/frankenphp-symfony/phpunit.xml.dist | 18 +++++++++ src/frankenphp-symfony/src/Runner.php | 44 ++++++++++++++++++++++ src/frankenphp-symfony/src/Runtime.php | 27 +++++++++++++ 9 files changed, 193 insertions(+) create mode 100644 src/frankenphp-symfony/.github/FUNDING.yml create mode 100644 src/frankenphp-symfony/.gitignore create mode 100644 src/frankenphp-symfony/CHANGELOG.md create mode 100644 src/frankenphp-symfony/LICENSE create mode 100644 src/frankenphp-symfony/README.md create mode 100644 src/frankenphp-symfony/composer.json create mode 100644 src/frankenphp-symfony/phpunit.xml.dist create mode 100644 src/frankenphp-symfony/src/Runner.php create mode 100644 src/frankenphp-symfony/src/Runtime.php diff --git a/src/frankenphp-symfony/.github/FUNDING.yml b/src/frankenphp-symfony/.github/FUNDING.yml new file mode 100644 index 0000000..ed394b0 --- /dev/null +++ b/src/frankenphp-symfony/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [nyholm] diff --git a/src/frankenphp-symfony/.gitignore b/src/frankenphp-symfony/.gitignore new file mode 100644 index 0000000..3a9875b --- /dev/null +++ b/src/frankenphp-symfony/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +composer.lock diff --git a/src/frankenphp-symfony/CHANGELOG.md b/src/frankenphp-symfony/CHANGELOG.md new file mode 100644 index 0000000..6ecda4d --- /dev/null +++ b/src/frankenphp-symfony/CHANGELOG.md @@ -0,0 +1,5 @@ +# Change Log + +## 0.1.0 + +First version diff --git a/src/frankenphp-symfony/LICENSE b/src/frankenphp-symfony/LICENSE new file mode 100644 index 0000000..a3c83af --- /dev/null +++ b/src/frankenphp-symfony/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Tobias Nyholm + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/frankenphp-symfony/README.md b/src/frankenphp-symfony/README.md new file mode 100644 index 0000000..4c76809 --- /dev/null +++ b/src/frankenphp-symfony/README.md @@ -0,0 +1,40 @@ +# RoadRunner Runtime for Symfony + +A runtime for [FrankenPHP](https://frankenphp.dev/). + +If you are new to the Symfony Runtime component, read more in the [main readme](https://github.com/php-runtime/runtime). + +## Installation + +``` +composer require runtime/frankenphp-symfony +``` + +## Usage + +Define the environment variable `APP_RUNTIME` for your application. + +``` +// .env +APP_RUNTIME=Runtime\FrankenPhpSymfony\Runtime +``` + +``` +// .rr.yaml +server: + ... + env: + APP_RUNTIME: Runtime\FrankenPhpSymfony\Runtime +``` + +```php +// public/index.php + +use App\Kernel; + +require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; + +return function (array $context) { + return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); +}; +``` diff --git a/src/frankenphp-symfony/composer.json b/src/frankenphp-symfony/composer.json new file mode 100644 index 0000000..28d2b4b --- /dev/null +++ b/src/frankenphp-symfony/composer.json @@ -0,0 +1,33 @@ +{ + "name": "runtime/frankenphp-symfony", + "description": "FrankenPHP runtime for Symfony", + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Kévin Dunglas", + "email": "kevin@dunglas.dev" + } + ], + "require": { + "php": ">=8.2.0", + "symfony/dependency-injection": "^5.4 || ^6.0", + "symfony/http-kernel": "^5.4 || ^6.0", + "symfony/runtime": "^5.4 || ^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "Runtime\\FrankenPhpSymfony\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Runtime\\FrankenPhpSymfony\\Tests\\": "tests/" + } + } +} diff --git a/src/frankenphp-symfony/phpunit.xml.dist b/src/frankenphp-symfony/phpunit.xml.dist new file mode 100644 index 0000000..f1701c2 --- /dev/null +++ b/src/frankenphp-symfony/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + + + + ./tests + + + diff --git a/src/frankenphp-symfony/src/Runner.php b/src/frankenphp-symfony/src/Runner.php new file mode 100644 index 0000000..6e8a2fc --- /dev/null +++ b/src/frankenphp-symfony/src/Runner.php @@ -0,0 +1,44 @@ + + */ +class Runner implements RunnerInterface +{ + public function __construct(private readonly HttpKernelInterface $kernel) + { + } + + public function run(): int + { + $server = array_filter($_SERVER, static fn (string $key) => !str_starts_with($key, 'HTTP_'), ARRAY_FILTER_USE_KEY); + do { + $ret = frankenphp_handle_request(function () use ($server, &$sfRequest, &$sfResponse) { + // Merge the environment variables coming from DotEnv with the one tight to the current request + $_SERVER += $server; + + $sfRequest = Request::createFromGlobals(); + $sfResponse = $this->kernel->handle($sfRequest); + + $sfResponse->send(); + }); + + if ($this->kernel instanceof TerminableInterface && $sfRequest && $sfResponse) { + $this->kernel->terminate($sfRequest, $sfResponse); + } + } while ($ret); + + return 0; + } +} diff --git a/src/frankenphp-symfony/src/Runtime.php b/src/frankenphp-symfony/src/Runtime.php new file mode 100644 index 0000000..37ae99f --- /dev/null +++ b/src/frankenphp-symfony/src/Runtime.php @@ -0,0 +1,27 @@ + + */ +class Runtime extends SymfonyRuntime +{ + public function getRunner(?object $application): RunnerInterface + { + if ($application instanceof HttpKernelInterface && ($_SERVER['FRANKENPHP_WORKER'] ?? false)) { + return new Runner($application); + } + + return parent::getRunner($application); + } +} From a8c74f1888df27e31d53b99e6e0ab1c0e09757f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 14 Oct 2022 13:32:03 +0200 Subject: [PATCH 2/5] Update src/frankenphp-symfony/README.md Co-authored-by: Alexander Schranz --- src/frankenphp-symfony/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frankenphp-symfony/README.md b/src/frankenphp-symfony/README.md index 4c76809..cc47aef 100644 --- a/src/frankenphp-symfony/README.md +++ b/src/frankenphp-symfony/README.md @@ -1,4 +1,4 @@ -# RoadRunner Runtime for Symfony +# FrankenPHP Runtime for Symfony A runtime for [FrankenPHP](https://frankenphp.dev/). From 94136bc9ffdfe0d5f56729825a7e3083341c94d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sat, 15 Oct 2022 14:47:17 +0200 Subject: [PATCH 3/5] add tests --- src/frankenphp-symfony/src/Runner.php | 4 +- src/frankenphp-symfony/tests/RunnerTest.php | 43 +++++++++++++++++++ src/frankenphp-symfony/tests/RuntimeTest.php | 30 +++++++++++++ .../tests/function-mock.php | 9 ++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/frankenphp-symfony/tests/RunnerTest.php create mode 100644 src/frankenphp-symfony/tests/RuntimeTest.php create mode 100644 src/frankenphp-symfony/tests/function-mock.php diff --git a/src/frankenphp-symfony/src/Runner.php b/src/frankenphp-symfony/src/Runner.php index 6e8a2fc..fc826bf 100644 --- a/src/frankenphp-symfony/src/Runner.php +++ b/src/frankenphp-symfony/src/Runner.php @@ -24,8 +24,8 @@ public function run(): int { $server = array_filter($_SERVER, static fn (string $key) => !str_starts_with($key, 'HTTP_'), ARRAY_FILTER_USE_KEY); do { - $ret = frankenphp_handle_request(function () use ($server, &$sfRequest, &$sfResponse) { - // Merge the environment variables coming from DotEnv with the one tight to the current request + $ret = \frankenphp_handle_request(function () use ($server, &$sfRequest, &$sfResponse): void { + // Merge the environment variables coming from DotEnv with the ones tight to the current request $_SERVER += $server; $sfRequest = Request::createFromGlobals(); diff --git a/src/frankenphp-symfony/tests/RunnerTest.php b/src/frankenphp-symfony/tests/RunnerTest.php new file mode 100644 index 0000000..8993114 --- /dev/null +++ b/src/frankenphp-symfony/tests/RunnerTest.php @@ -0,0 +1,43 @@ + + */ +class RunnerTest extends TestCase +{ + public function testRun(): 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 { + $this->assertSame('bar', $request->server->get('FOO')); + + return new Response(); + }); + $application->expects($this->once())->method('terminate'); + + $_SERVER['FOO'] = 'bar'; + + $runner = new Runner($application); + $this->assertSame(0, $runner->run()); + } +} diff --git a/src/frankenphp-symfony/tests/RuntimeTest.php b/src/frankenphp-symfony/tests/RuntimeTest.php new file mode 100644 index 0000000..8bf2590 --- /dev/null +++ b/src/frankenphp-symfony/tests/RuntimeTest.php @@ -0,0 +1,30 @@ + + */ +final class RuntimeTest extends TestCase +{ + public function testGetRunner(): void + { + $application = $this->createStub(HttpKernelInterface::class); + + $runtime = new Runtime(); + $this->assertNotInstanceOf(Runner::class, $runtime->getRunner(null)); + $this->assertNotInstanceOf(Runner::class, $runtime->getRunner($application)); + + $_SERVER['FRANKENPHP_WORKER'] = 1; + $this->assertInstanceOf(Runner::class, $runtime->getRunner($application)); + } +} diff --git a/src/frankenphp-symfony/tests/function-mock.php b/src/frankenphp-symfony/tests/function-mock.php new file mode 100644 index 0000000..f8d8d87 --- /dev/null +++ b/src/frankenphp-symfony/tests/function-mock.php @@ -0,0 +1,9 @@ + Date: Sat, 15 Oct 2022 15:15:15 +0200 Subject: [PATCH 4/5] add readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a67d989..86618f6 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,12 @@ Build high-performance, scalable, concurrent HTTP services with [Swoole](https:/ * https://github.com/php-runtime/swoole * https://github.com/php-runtime/swoole-nyholm +### FrankenPHP + +Run your Symfony application with the [FrankenPHP](https://frankenphp.dev) app server. + +* https://github.com/php-runtime/frankenphp-symfony + ### PHP-FPM and traditional web servers These runtimes are for PHP-FPM and the more traditional web servers one might From 5a89025965435adbc8798e223b30261d6e5ea1c2 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Sat, 15 Oct 2022 15:25:47 +0200 Subject: [PATCH 5/5] Make sure CI is happy --- phpstan-baseline.neon | 5 +++++ psalm.baseline.xml | 5 ++++- src/frankenphp-symfony/composer.json | 5 +++++ src/frankenphp-symfony/src/Runner.php | 5 ++++- src/frankenphp-symfony/src/Runtime.php | 1 - src/frankenphp-symfony/tests/RuntimeTest.php | 2 -- src/frankenphp-symfony/tests/function-mock.php | 1 + 7 files changed, 19 insertions(+), 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fcb9961..a98bb95 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -9,3 +9,8 @@ parameters: message: "#CurlHandle#" count: 2 path: src/bref/src/Lambda/LambdaClient.php + + - + message: "#^Function frankenphp_handle_request not found\\.$#" + count: 1 + path: src/frankenphp-symfony/src/Runner.php diff --git a/psalm.baseline.xml b/psalm.baseline.xml index 29481e9..c08bd2d 100644 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -1,10 +1,13 @@ - + $response->headers->all() + + + require_once $_SERVER['SCRIPT_FILENAME'] = $defaultSource diff --git a/src/frankenphp-symfony/composer.json b/src/frankenphp-symfony/composer.json index 28d2b4b..0a9a389 100644 --- a/src/frankenphp-symfony/composer.json +++ b/src/frankenphp-symfony/composer.json @@ -29,5 +29,10 @@ "psr-4": { "Runtime\\FrankenPhpSymfony\\Tests\\": "tests/" } + }, + "config": { + "allow-plugins": { + "symfony/runtime": true + } } } diff --git a/src/frankenphp-symfony/src/Runner.php b/src/frankenphp-symfony/src/Runner.php index fc826bf..46c8378 100644 --- a/src/frankenphp-symfony/src/Runner.php +++ b/src/frankenphp-symfony/src/Runner.php @@ -16,8 +16,11 @@ */ class Runner implements RunnerInterface { - public function __construct(private readonly HttpKernelInterface $kernel) + private HttpKernelInterface $kernel; + + public function __construct(HttpKernelInterface $kernel) { + $this->kernel = $kernel; } public function run(): int diff --git a/src/frankenphp-symfony/src/Runtime.php b/src/frankenphp-symfony/src/Runtime.php index 37ae99f..e45cb3f 100644 --- a/src/frankenphp-symfony/src/Runtime.php +++ b/src/frankenphp-symfony/src/Runtime.php @@ -4,7 +4,6 @@ namespace Runtime\FrankenPhpSymfony; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Runtime\RunnerInterface; use Symfony\Component\Runtime\SymfonyRuntime; diff --git a/src/frankenphp-symfony/tests/RuntimeTest.php b/src/frankenphp-symfony/tests/RuntimeTest.php index 8bf2590..cc91527 100644 --- a/src/frankenphp-symfony/tests/RuntimeTest.php +++ b/src/frankenphp-symfony/tests/RuntimeTest.php @@ -7,8 +7,6 @@ use PHPUnit\Framework\TestCase; use Runtime\FrankenPhpSymfony\Runner; use Runtime\FrankenPhpSymfony\Runtime; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; /** diff --git a/src/frankenphp-symfony/tests/function-mock.php b/src/frankenphp-symfony/tests/function-mock.php index f8d8d87..aa17aa8 100644 --- a/src/frankenphp-symfony/tests/function-mock.php +++ b/src/frankenphp-symfony/tests/function-mock.php @@ -4,6 +4,7 @@ function frankenphp_handle_request(callable $callable): bool { $callable(); + return false; } }