Skip to content

Commit 029ee16

Browse files
mnapolitillkruss
andauthored
Simplify using Octane (#43)
* Provide a default `serverless.yml` with FPM by default * Move Bref initialization code in the service provider so that it applies to all runtime types * Enable direct deployments by default because they are faster * Remove the population of env vars from SSM parameters This is a native Bref feature since Bref 2.0. * Remove `deploymentMethod` as it is set by default in Bref 2.0 * Run `config:cache` on cold starts using the new Bref hooks * Simplify the Octane handler * Simplify the Octane runtime usage * Make sure file handlers are still supported * flatten * inline * formatting * fix --------- Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
1 parent 93b49c0 commit 029ee16

File tree

13 files changed

+247
-172
lines changed

13 files changed

+247
-172
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [v2.0.0]
88
### Breaking Changes
99
- Logs are now written in plain text by default instead of JSON. To enable JSON logs, set `channels.stderr.formatter` to `Monolog\Formatter\JsonFormatter::class` in `config/logging.php`.
10+
- The automatic population of environment variables via `APP_SSM_PREFIX` and `APP_SSM_PARAMETERS` has been removed. The native Bref 2.0 feature to load SSM parameters into environment variables can be used instead ([#36](https://github.com/cachewerk/bref-laravel-bridge/pull/36))
11+
- If you use Octane, remove the `bref/runtime.php` file, remove the `APP_RUNTIME` environment variable (in `serverless.yml`) and set your Octane function handler to: `handler: CacheWerk\BrefLaravelBridge\Http\OctaneHandler`.
1012

1113
## [Unreleased]
1214
## [v0.3.0] - 2022-11-15

README.md

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ composer require cachewerk/bref-laravel-bridge
2828
php artisan vendor:publish --tag=bref-runtime
2929
```
3030

31-
By default the runtime is published to `php/` where Bref's PHP configuration resides, but it can be move anywhere.
31+
This will create the `serverless.yml` config file.
3232

3333
Next, we need to set up in the `AWS_ACCOUNT_ID` environment variable in your `serverless.yml`:
3434

@@ -38,67 +38,62 @@ provider:
3838
AWS_ACCOUNT_ID: ${aws:accountId}
3939
```
4040
41-
Then set up your functions:
41+
Finally, deploy your app:
4242
43-
```yml
44-
functions:
45-
web:
46-
handler: php/runtime.php
47-
environment:
48-
APP_RUNTIME: octane
49-
BREF_LOOP_MAX: 250
50-
layers:
51-
- ${bref:layer.php-81}
52-
events:
53-
- httpApi: '*'
43+
```bash
44+
serverless deploy
45+
```
5446

55-
queue:
56-
handler: php/runtime.php
57-
timeout: 59
58-
environment:
59-
APP_RUNTIME: queue
60-
layers:
61-
- ${bref:layer.php-81}
62-
events:
63-
- sqs:
64-
arn: !GetAtt Queue.Arn
65-
batchSize: 1
66-
maximumBatchingWindow: 60
67-
68-
cli:
69-
handler: php/runtime.php
70-
timeout: 720
71-
environment:
72-
APP_RUNTIME: cli
73-
layers:
74-
- ${bref:layer.php-81}
75-
- ${bref:layer.console}
76-
events:
77-
- schedule:
78-
rate: rate(1 minute)
79-
input: '"schedule:run"'
47+
When running in AWS Lambda, the Laravel application will automatically cache its configuration when booting. You don't need to run `php artisan config:cache` before deploying.
48+
49+
You can deploy to different environments (aka "stages") by using the `--stage` option:
50+
51+
```bash
52+
serverless deploy --stage=staging
8053
```
8154

82-
If you don't want to use Octane, simply remove `APP_RUNTIME` and `BREF_LOOP_MAX` from the `web` function.
55+
Check out some more [comprehensive examples](examples/).
56+
57+
## Octane
8358

84-
To avoid setting secrets as environment variables on your Lambda functions, you can inject them directly into the Lambda runtime:
59+
If you want to run the HTTP application with Laravel Octane, you will to change the following options in the `web` function:
8560

8661
```yml
87-
provider:
88-
environment:
89-
APP_SSM_PREFIX: /${self:service}-${sls:stage}/
90-
APP_SSM_PARAMETERS: "APP_KEY, DATABASE_URL"
62+
functions:
63+
web:
64+
handler: CacheWerk\BrefLaravelBridge\Http\OctaneHandler
65+
environment:
66+
BREF_LOOP_MAX: 250
67+
layers:
68+
- ${bref:layer.php-81}
69+
# ...
9170
```
9271

93-
This will inject `APP_KEY` and `DATABASE_URL` using your service name and stage, for example from `/myapp-staging/APP_KEY`.
72+
## Laravel Queues
9473

95-
Finally, deploy your app:
74+
If you want to run Laravel Queues, you will need to publish the PHP runtime (just like in the "Octane" section above):
9675

9776
```
98-
sls deploy --stage=staging
77+
php artisan vendor:publish --tag=bref-runtime
9978
```
10079

101-
Check out some more [comprehensive examples](examples/).
80+
Then, you can add a `queue` function to `serverless.yml`:
81+
82+
```yml
83+
functions:
84+
queue:
85+
handler: php/runtime.php
86+
timeout: 59 # in seconds
87+
environment:
88+
APP_RUNTIME: queue
89+
layers:
90+
- ${bref:layer.php-81}
91+
events:
92+
- sqs:
93+
arn: !GetAtt Queue.Arn
94+
batchSize: 1
95+
maximumBatchingWindow: 60
96+
```
10297
10398
## Configuration
10499

composer.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"require": {
1111
"php": "^8.0",
1212
"aws/aws-sdk-php": "^3.222",
13-
"bref/bref": "^1.5",
13+
"bref/bref": "^2.0.0-beta14",
1414
"illuminate/container": "^8.0 || ^9.0",
1515
"illuminate/contracts": "^8.0 || ^9.0",
1616
"illuminate/http": "^8.0 || ^9.0",
@@ -30,7 +30,10 @@
3030
"autoload": {
3131
"psr-4": {
3232
"CacheWerk\\BrefLaravelBridge\\": "src"
33-
}
33+
},
34+
"files": [
35+
"src/bref-init.php"
36+
]
3437
},
3538
"config": {
3639
"sort-packages": true

examples/getting-started/serverless.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ provider:
2323
APP_URL: ${param:appUrl}
2424
APP_ENV: ${sls:stage}
2525
APP_DEBUG: ${param:debug}
26-
APP_SSM_PREFIX: /${self:service}-${sls:stage}/
27-
APP_SSM_PARAMETERS: "APP_KEY"
2826
AWS_ACCOUNT_ID: ${aws:accountId}
2927
ASSET_URL: https://${param:assetsBucket}/${sls:instanceId}
3028
LOG_LEVEL: ${param:logLevel}
@@ -44,9 +42,6 @@ provider:
4442
- Effect: Allow
4543
Resource: !GetAtt Queue.Arn
4644
Action: [sqs:GetQueueUrl, sqs:GetQueueAttributes, sqs:SendMessage, sqs:receiveMessage, sqs:DeleteMessage, sqs:PurgeQueue]
47-
- Effect: Allow
48-
Resource: arn:aws:ssm:${aws:region}:${aws:accountId}:parameter/${self:service}-${sls:stage}/*
49-
Action: [ssm:GetParameters]
5045
# Allow Lambda to read and write files in the S3 storage bucket
5146
- Effect: Allow
5247
Action: s3:*
@@ -60,10 +55,9 @@ plugins:
6055

6156
functions:
6257
web:
63-
handler: php/runtime.php
58+
handler: CacheWerk\BrefLaravelBridge\Http\OctaneHandler
6459
timeout: 20
6560
environment:
66-
APP_RUNTIME: octane
6761
BREF_LOOP_MAX: 250
6862
BREF_BINARY_RESPONSES: 1
6963
layers:

phpcs.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
<rule ref="PSR1.Files.SideEffects.FoundWithSymbols">
1010
<exclude-pattern>*/stubs/runtime.php</exclude-pattern>
11+
<exclude-pattern>src/bref-init.php</exclude-pattern>
1112
</rule>
1213

1314
<rule ref="Generic.Files.LineLength">

src/BrefServiceProvider.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ public function register()
6767
*/
6868
public function boot(Dispatcher $dispatcher, LogManager $logManager, FailedJobProviderInterface $queueFailer)
6969
{
70+
$this->app->useStoragePath(StorageDirectories::Path);
71+
7072
if ($this->app->runningInConsole()) {
73+
$this->publishes([
74+
__DIR__ . '/../stubs/serverless.yml' => config_path('serverless.yml'),
75+
], 'serverless-config');
76+
7177
$this->publishes([
7278
__DIR__ . '/../config/bref.php' => config_path('bref.php'),
7379
], 'bref-config');

src/HandlerResolver.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace CacheWerk\BrefLaravelBridge;
4+
5+
use RuntimeException;
6+
use Bref\Runtime\FileHandlerLocator;
7+
use Psr\Container\ContainerInterface;
8+
use Illuminate\Foundation\Application;
9+
10+
/**
11+
* This class resolves Lambda handlers.
12+
*
13+
* It extends the default Bref behavior (that resolves handlers from files)
14+
* to also resolve class handlers from the Laravel container.
15+
*/
16+
class HandlerResolver implements ContainerInterface
17+
{
18+
private ?Application $laravelApp;
19+
private FileHandlerLocator $fileLocator;
20+
21+
public function __construct()
22+
{
23+
// Bref's default handler resolver
24+
$this->fileLocator = new FileHandlerLocator;
25+
$this->laravelApp = null;
26+
}
27+
28+
public function get(string $id)
29+
{
30+
// By default, we check if the handler is a file name (classic Bref behavior)
31+
if ($this->fileLocator->has($id)) {
32+
return $this->fileLocator->get($id);
33+
}
34+
35+
// If not, we try to get the handler from the Laravel container
36+
return $this->laravelApp()->get($id);
37+
}
38+
39+
public function has(string $id): bool
40+
{
41+
// By default, we check if the handler is a file name (classic Bref behavior)
42+
if ($this->fileLocator->has($id)) {
43+
return true;
44+
}
45+
46+
// If not, we try to get the handler from the Laravel container
47+
return $this->laravelApp()->has($id);
48+
}
49+
50+
/**
51+
* Create and return the Laravel application.
52+
*/
53+
private function laravelApp(): Application
54+
{
55+
// Only create it once
56+
if ($this->laravelApp) {
57+
return $this->laravelApp;
58+
}
59+
60+
$bootstrapFile = getcwd() . '/bootstrap/app.php';
61+
62+
if (! file_exists($bootstrapFile)) {
63+
throw new RuntimeException(
64+
"Unable to locate `{$bootstrapFile}`: Bref tried to load that file to retrieve the Laravel app"
65+
);
66+
}
67+
68+
$this->laravelApp = require $bootstrapFile;
69+
70+
if (! $this->laravelApp instanceof Application) {
71+
throw new RuntimeException(sprintf(
72+
"Expected the `%s` file to return a %s object, instead it returned `%s`",
73+
$bootstrapFile,
74+
Application::class,
75+
is_object($this->laravelApp) ? get_class($this->laravelApp) : gettype($this->laravelApp),
76+
));
77+
}
78+
79+
return $this->laravelApp;
80+
}
81+
}

src/Http/OctaneHandler.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@
1616

1717
class OctaneHandler extends HttpHandler
1818
{
19+
private OctaneClient $octaneClient;
20+
21+
public function __construct()
22+
{
23+
$this->octaneClient = new OctaneClient(
24+
getcwd(),
25+
(bool) ($_ENV['OCTANE_PERSIST_DATABASE_SESSIONS'] ?? false)
26+
);
27+
}
28+
1929
/**
2030
* {@inheritDoc}
2131
*/
22-
public function handleRequest(?HttpRequestEvent $event, ?Context $context): HttpResponse
32+
public function handleRequest(HttpRequestEvent $event, Context $context): HttpResponse
2333
{
2434
$request = Request::createFromBase(
2535
SymfonyRequestBridge::convertRequest($event, $context)
@@ -28,7 +38,7 @@ public function handleRequest(?HttpRequestEvent $event, ?Context $context): Http
2838
if (MaintenanceMode::active()) {
2939
$response = MaintenanceMode::response($request)->prepare($request);
3040
} else {
31-
$response = OctaneClient::handle($request);
41+
$response = $this->octaneClient->handle($request);
3242
}
3343

3444
if (! $response->headers->has('Content-Type')) {

src/Octane/OctaneClient.php

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,18 @@ class OctaneClient implements Client
2020
{
2121
/**
2222
* The Octane worker.
23-
*
24-
* @var \Laravel\Octane\Worker
2523
*/
26-
protected static $worker;
24+
private Worker $worker;
2725

2826
/**
29-
* The Octane response.
30-
*
31-
* @var \Laravel\Octane\OctaneResponse|null
27+
* The response of the last request that was processed.
3228
*/
33-
protected static $response;
29+
private OctaneResponse|null $response;
3430

35-
/**
36-
* Boots an Octane worker instance.
37-
*
38-
* @param string $basePath
39-
* @param bool $persistDatabaseSession
40-
* @return void
41-
*/
42-
public static function boot(string $basePath, bool $persistDatabaseSession)
31+
public function __construct(string $basePath, bool $persistDatabaseSession)
4332
{
44-
static::$worker = tap(
45-
new Worker(new ApplicationFactory($basePath), new self)
33+
$this->worker = tap(
34+
new Worker(new ApplicationFactory($basePath), $this)
4635
)->boot()->onRequestHandled(
4736
static::manageDatabaseSessions($persistDatabaseSession)
4837
);
@@ -54,14 +43,14 @@ public static function boot(string $basePath, bool $persistDatabaseSession)
5443
* @param \Illuminate\Http\Request $request
5544
* @return \Symfony\Component\HttpFoundation\Response
5645
*/
57-
public static function handle(Request $request): Response
46+
public function handle(Request $request): Response
5847
{
59-
static::$worker->application()->useStoragePath('/tmp/storage');
48+
$this->worker->application()->useStoragePath('/tmp/storage');
6049

61-
static::$worker->handle($request, new RequestContext);
50+
$this->worker->handle($request, new RequestContext);
6251

63-
$response = clone static::$response->response;
64-
static::$response = null;
52+
$response = clone $this->response->response;
53+
$this->response = null;
6554

6655
return $response;
6756
}
@@ -72,14 +61,14 @@ public static function handle(Request $request): Response
7261
public function error(Throwable $exception, Application $app, Request $request, RequestContext $context): void
7362
{
7463
try {
75-
static::$response = new OctaneResponse(
64+
$this->response = new OctaneResponse(
7665
$app[ExceptionHandler::class]->render($request, $exception)
7766
);
7867
} catch (Throwable $throwable) {
7968
fwrite(STDERR, $throwable->getMessage());
8069
fwrite(STDERR, $exception->getMessage());
8170

82-
static::$response = new OctaneResponse(
71+
$this->response = new OctaneResponse(
8372
new Response('Internal Server Error', 500)
8473
);
8574
}
@@ -90,7 +79,7 @@ public function error(Throwable $exception, Application $app, Request $request,
9079
*/
9180
public function respond(RequestContext $context, OctaneResponse $response): void
9281
{
93-
static::$response = $response;
82+
$this->response = $response;
9483
}
9584

9685
/**

0 commit comments

Comments
 (0)