generated from mnapoli/project-template
-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #45 from brefphp/support-class-handlers
Handle requests with the Kernel + handle events with Symfony services - Symfony Runtime integration
- Loading branch information
Showing
15 changed files
with
431 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,12 +10,9 @@ jobs: | |
max-parallel: 10 | ||
fail-fast: false | ||
matrix: | ||
php: ['7.3', '7.4', '8.0', '8.1'] | ||
sf_version: ['4.4.*', '5.0.*', '5.2.*', '5.4.*', '6.0.*'] | ||
php: ['7.4', '8.0', '8.1'] | ||
sf_version: ['5.2.*', '5.4.*', '6.0.*'] | ||
exclude: | ||
- php: 7.3 | ||
sf_version: 6.0.* | ||
|
||
- php: 7.4 | ||
sf_version: 6.0.* | ||
|
||
|
@@ -48,7 +45,7 @@ jobs: | |
- name: Set up PHP | ||
uses: shivammathur/[email protected] | ||
with: | ||
php-version: 7.3 | ||
php-version: 7.4 | ||
coverage: pcov | ||
|
||
- name: Checkout code | ||
|
@@ -59,5 +56,5 @@ jobs: | |
|
||
- name: Run tests | ||
env: | ||
PHP_VERSION: 7.3 | ||
PHP_VERSION: 7.4 | ||
run: ./vendor/bin/phpunit -v --coverage-text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
/composer.phar | ||
/composer.lock | ||
.phpunit.result.cache | ||
/var/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Bref\SymfonyBridge; | ||
|
||
use Bref\Runtime\FileHandlerLocator; | ||
use Bref\SymfonyBridge\Http\KernelAdapter; | ||
use Bref\SymfonyBridge\Runtime\BrefRuntime; | ||
use Exception; | ||
use Psr\Container\ContainerInterface; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
use Symfony\Component\HttpKernel\KernelInterface; | ||
|
||
/** | ||
* This class resolves handlers. | ||
* | ||
* For example, if we configure `handler: xyz` in serverless.yml, then Bref | ||
* will call this class to resolve `xyz` into the real Lambda handler. | ||
*/ | ||
class HandlerResolver implements ContainerInterface | ||
{ | ||
private ?ContainerInterface $symfonyContainer; | ||
private FileHandlerLocator $fileLocator; | ||
|
||
public function __construct() | ||
{ | ||
// Bref's default handler resolver | ||
$this->fileLocator = new FileHandlerLocator; | ||
$this->symfonyContainer = null; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function get($id) | ||
{ | ||
$isComposed = strpos($id, ':') !== false; | ||
|
||
// By default we check if the handler is a file name (classic Bref behavior) | ||
if (! $isComposed && $this->fileLocator->has($id)) { | ||
return $this->fileLocator->get($id); | ||
} | ||
|
||
$service = $id; | ||
|
||
$bootstrapFile = null; | ||
if ($isComposed) { | ||
[$bootstrapFile, $service] = explode(':', $id, 2); | ||
} | ||
|
||
// If not, we try to get the handler from the Symfony container | ||
$handler = $this->symfonyContainer($bootstrapFile)->get($service); | ||
|
||
// If the kernel was configured as a handler, then we wrap it to make it a valid HTTP handler for Lambda | ||
if ($handler instanceof HttpKernelInterface) { | ||
$handler = new KernelAdapter($handler); | ||
} | ||
|
||
return $handler; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function has($id): bool | ||
{ | ||
$isComposed = strpos($id, ':') !== false; | ||
|
||
// By default we check if the handler is a file name (classic Bref behavior) | ||
if (! $isComposed && $this->fileLocator->has($id)) { | ||
return true; | ||
} | ||
|
||
$service = $id; | ||
|
||
$bootstrapFile = null; | ||
if ($isComposed) { | ||
[$bootstrapFile, $service] = explode(':', $id, 2); | ||
} | ||
|
||
// If not, we try to get the handler from the Symfony container | ||
return $this->symfonyContainer($bootstrapFile)->has($service); | ||
} | ||
|
||
/** | ||
* Create and return the Symfony container. | ||
*/ | ||
private function symfonyContainer(?string $bootstrapFile = null): ContainerInterface | ||
{ | ||
// Only create it once | ||
if (! $this->symfonyContainer) { | ||
$bootstrapFile = $bootstrapFile ?: 'public/index.php'; | ||
|
||
if (! file_exists($bootstrapFile)) { | ||
throw new Exception( | ||
"Cannot find file '$bootstrapFile': the Bref-Symfony bridge tried to require that file to get the Symfony kernel. If your application does not have that file, follow the Bref-Symfony documentation to create and configure a file that returns the Symfony Kernel." | ||
); | ||
} | ||
|
||
$app = require $bootstrapFile; | ||
|
||
if (! is_object($app)) { | ||
throw new Exception(sprintf( | ||
"The '%s' file must return an anonymous function (that returns the Symfony Kernel). Instead it returned '%s'. Either edit the file to return an anonymous function, or create a separate file (follow the online documentation to do so).", | ||
$bootstrapFile, | ||
// @phpstan-ignore-next-line | ||
is_object($app) ? get_class($app) : gettype($app), | ||
)); | ||
} | ||
|
||
$projectDir = getenv('LAMBDA_TASK_ROOT') ?: null; | ||
|
||
// Use the Symfony Runtime component to resolve the closure and get the PSR-11 container | ||
$options = $_SERVER['APP_RUNTIME_OPTIONS'] ?? []; | ||
if ($projectDir) { | ||
$options['project_dir'] = $projectDir; | ||
} | ||
$runtime = new BrefRuntime($options); | ||
|
||
[$app, $args] = $runtime | ||
->getResolver($app) | ||
->resolve(); | ||
|
||
$container = $app(...$args); | ||
|
||
if ($container instanceof KernelInterface) { | ||
$container->boot(); | ||
$container = $container->getContainer(); | ||
} | ||
|
||
if (! $container instanceof ContainerInterface) { | ||
throw new Exception(sprintf( | ||
"The closure returned by '%s' must return either a Symfony Kernel or a PSR-11 container. Instead it returned '%s'", | ||
$bootstrapFile, | ||
is_object($container) ? get_class($container) : gettype($container), | ||
)); | ||
} | ||
|
||
$this->symfonyContainer = $container; | ||
} | ||
|
||
return $this->symfonyContainer; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Bref\SymfonyBridge\Http; | ||
|
||
use Nyholm\Psr7\Factory\Psr17Factory; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; | ||
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
use Symfony\Component\HttpKernel\TerminableInterface; | ||
|
||
/** | ||
* This turns a Symfony Kernel into a PSR-15 handler. | ||
* | ||
* That means the Symfony Kernel can now be used by Bref (which supports PSR-15) | ||
* to handle HTTP requests from API Gateway. | ||
*/ | ||
class KernelAdapter implements RequestHandlerInterface | ||
{ | ||
private HttpKernelInterface $kernel; | ||
// PSR-15 to Symfony converters | ||
private HttpFoundationFactory $symfonyFactory; | ||
private PsrHttpFactory $psrFactory; | ||
|
||
public function __construct(HttpKernelInterface $kernel) | ||
{ | ||
$this->kernel = $kernel; | ||
$this->symfonyFactory = new HttpFoundationFactory; | ||
$psr17Factory = new Psr17Factory; | ||
$this->psrFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory); | ||
} | ||
|
||
public function handle(ServerRequestInterface $request): ResponseInterface | ||
{ | ||
// From PSR-7 to Symfony | ||
$symfonyRequest = $this->symfonyFactory->createRequest($request); | ||
|
||
$symfonyResponse = $this->kernel->handle($symfonyRequest); | ||
|
||
if ($this->kernel instanceof TerminableInterface) { | ||
$this->kernel->terminate($symfonyRequest, $symfonyResponse); | ||
} | ||
|
||
// From Symfony to PSR-7 | ||
return $this->psrFactory->createResponse($symfonyResponse); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Bref\SymfonyBridge\Runtime; | ||
|
||
use Symfony\Component\Runtime\SymfonyRuntime; | ||
|
||
class BrefRuntime extends SymfonyRuntime | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php declare(strict_types=1); | ||
|
||
use Bref\Bref; | ||
use Bref\SymfonyBridge\HandlerResolver; | ||
|
||
/** | ||
* File executed when the application starts: it registers a Bref PSR-11 "handler resolver". | ||
* | ||
* This is what Bref will use to turn handler names (strings defined in serverless.yml/AWS Lambda) | ||
* into classes that can handle the Lambda events. | ||
*/ | ||
if (class_exists(Bref::class)) { | ||
Bref::setContainer(static fn () => new HandlerResolver); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Bref\SymfonyBridge\Test\Fixtures; | ||
|
||
class MyService | ||
{ | ||
} |
Oops, something went wrong.