Typed PHP Lambda handlers
Handling Lambda events via an anonymous function is the simplest approach:
return function ($event) {
return 'Hello ' . $event['name'];
};
However, Bref also provides classes specific to each Lambda event for a better development experience.
Here is an example using the base Handler
class, that can handle events of any type:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
class Handler implements \Bref\Event\Handler
{
public function handle($event, Context $context)
{
return 'Hello ' . $event['name'];
}
}
return new Handler();
Autoloading
As you can see in the example above, we define the class in the "handler" file, i.e. the same file where we return new Handler()
.
But we can perfectly move that class to another directory, Composer will autoload it as usual:
<?php // my-function.php
require __DIR__ . '/vendor/autoload.php';
// The class is stored in `src/` or `app/` and autoloaded by Composer
return new \MyApp\Handler();
What is important is to configure serverless.yml
to use the file that returns the handler:
# ...
functions:
hello:
handler: my-function.php # the file that returns the handler
All the examples in this page will mix the class and the return
for simplicity.
S3 events
S3Handler
instances handle Amazon S3 events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\S3\S3Event;
use Bref\Event\S3\S3Handler;
class Handler extends S3Handler
{
public function handleS3(S3Event $event, Context $context): void
{
$bucketName = $event->getRecords()[0]->getBucket()->getName();
$fileName = $event->getRecords()[0]->getObject()->getKey();
// do something with the file
}
}
return new Handler();
For example, the class can be called whenever a new file is uploaded to S3:
# ...
functions:
resizeImage:
handler: handler.php
events:
- s3: photos
Full reference of S3 in serverless.yml
.
SQS events
SqsHandler
instances handle SQS events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\Sqs\SqsEvent;
use Bref\Event\Sqs\SqsHandler;
class Handler extends SqsHandler
{
public function handleSqs(SqsEvent $event, Context $context): void
{
foreach ($event->getRecords() as $record) {
// We can retrieve the message body of each record via `->getBody()`
$body = $record->getBody();
// do something
}
}
}
return new Handler();
Partial Batch Response
While handling a batch of records, you can mark it as partially successful to reprocess only the failed records.
In your function declaration in serverless.yml
, set functionResponseType
to ReportBatchItemFailures
to let your function return a partial success result if one or more messages in the batch have failed.
functions:
worker:
handler: handler.php
events:
- sqs:
arn: arn:aws:sqs:eu-west-1:111111111111:queue-name
batchSize: 100
functionResponseType: ReportBatchItemFailures
In your PHP code, you can now use the markAsFailed
method:
public function handleSqs(SqsEvent $event, Context $context): void
{
foreach ($event->getRecords() as $record) {
// do something
// if something went wrong, mark the record as failed
$this->markAsFailed($record);
}
}
Lift Queue Construct
It is possible to deploy a preconfigured SQS queue in serverless.yml
using the Queue
feature of the Lift plugin. For example:
# serverless.yml
# ...
constructs:
my-queue:
type: queue
worker:
handler: handler.php
Read more:
- Deploying SQS queues with Lift
- Full reference of SQS in
serverless.yml
- Learn more about SQS and workers in Serverless Visually Explained
API Gateway HTTP events
Reminder: to create HTTP applications, it is possible to use the more traditional "Bref for web apps" runtime, which runs with PHP-FPM.
That being said, it is possible to handle HTTP events from API Gateway with a simple PHP class, like other handlers detailed in this page.
Here is a full comparison between both approaches:
Bref for web apps | HTTP handler class | |
---|---|---|
What are the use cases? | To build websites, APIs, etc. This should be the default approach as it's compatible with mature PHP frameworks and tools. | Build a small website, API, webhook with very little code and no framework. |
Why does that solution exist? | For out-of-the-box compatibility with frameworks like Symfony and Laravel. | To match how other languages run in AWS Lambda, as recommended by AWS. |
How is it executed? | Using PHP-FPM. | Using the PHP CLI. |
What does the routing (i.e. separate pages)? | Your PHP framework (one Lambda receives all the URLs). | API Gateway: we define one Lambda and one handler class per route. |
How to read the request? | $_GET , $_POST , etc. |
The $request parameter (PSR-7 request). |
How to write a response? | echo , header() function, etc. |
Returning a PSR-7 response from the handler class. |
How does it work? | Bref turns an API Gateway event into a FastCGI (PHP-FPM) request. | Bref turns an API Gateway event into a PSR-7 request. |
Is each request handled in a separate PHP process? | Yes (that's how PHP-FPM works). | Yes (Bref explicitly replicates that to avoid surprises, but that can be customized). |
To create an HTTP handler class, Bref natively supports the PSR-15 and PSR-7 standards:
<?php
require __DIR__ . '/vendor/autoload.php';
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class HttpHandler implements RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): ResponseInterface
{
$name = $request->getQueryParams()['name'] ?? 'world';
return new Response(200, [], "Hello $name");
}
}
return new HttpHandler();
Since a handler is a controller for a specific route, we can use API Gateway's routing to deploy multiple Lambda functions:
functions:
create-article:
handler: create-article-handler.php
runtime: php-81
events:
- httpApi: 'POST /articles'
get-article:
handler: get-article-handler.php
runtime: php-81
events:
- httpApi: 'GET /articles/{id}'
Note that path parameters (e.g. {id}
in the example above) are available as request attributes in the PSR-7 request:
$id = $request->getAttribute('id');
Full reference of HTTP events in serverless.yml
.
Lambda event and context
The API Gateway event and Lambda context are available as attributes on the request:
/** @var $event Bref\Event\Http\HttpRequestEvent */
$event = $request->getAttribute('lambda-event');
/** @var $context Bref\Context\Context */
$context = $request->getAttribute('lambda-context');
If you're looking for the request context array, for example when using a Lambda authorizer:
$requestContext = $request->getAttribute('lambda-event')->getRequestContext();
Websocket events
WebsocketHandler
instances handle Websocket events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\ApiGateway\WebsocketEvent;
use Bref\Event\ApiGateway\WebsocketHandler;
use Bref\Event\Http\HttpResponse;
class Handler extends WebsocketHandler
{
public function handleWebsocket(WebsocketEvent $event, Context $context): HttpResponse
{
$route = $event->getRouteKey();
$eventType = $event->getEventType();
$body = $event->getBody();
return new HttpResponse('ok');
}
}
return new Handler();
Full reference for Websockets in serverless.yml
.
A complete WebSocket example is available in Serverless Visually Explained.
EventBridge events
EventBridgeHandler
instances handle EventBridge events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\EventBridge\EventBridgeEvent;
use Bref\Event\EventBridge\EventBridgeHandler;
class Handler extends EventBridgeHandler
{
public function handleEventBridge(EventBridgeEvent $event, Context $context): void
{
// We can retrieve the message data via `$event->getDetail()`
$message = $event->getDetail();
// do something
}
}
return new Handler();
You can read more about messaging with EventBridge in Serverless Visually Explained.
Full reference of EventBridge in serverless.yml
.
SNS events
SnsHandler
instances handle SNS events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\Sns\SnsEvent;
use Bref\Event\Sns\SnsHandler;
class Handler extends SnsHandler
{
public function handleSns(SnsEvent $event, Context $context): void
{
foreach ($event->getRecords() as $record) {
$message = $record->getMessage();
// do something
}
}
}
return new Handler();
Full reference of SNS in serverless.yml
.
DynamoDB events
DynamoDbHandler
instances handle DynamoDB events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\DynamoDb\DynamoDbEvent;
use Bref\Event\DynamoDb\DynamoDbHandler;
class Handler extends DynamoDbHandler
{
public function handleDynamoDb(DynamoDbEvent $event, Context $context): void
{
foreach ($event->getRecords() as $record) {
$keys = $record->getKeys();
$old = $record->getOldImage();
$new = $record->getNewImage();
// do something
}
}
}
return new Handler();
Full reference of DynamoDB in serverless.yml
.
Kinesis events
KinesisHandler
instances handle Kinesis events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\Kinesis\KinesisEvent;
use Bref\Event\Kinesis\KinesisHandler;
class Handler extends KinesisHandler
{
public function handleKinesis(KinesisEvent $event, Context $context): void
{
foreach ($event->getRecords() as $record) {
$data = $record->getData();
// do something
}
}
}
return new Handler();
Full reference of Kinesis in serverless.yml
.
Kafka events
KafkaHandler
instances handle Kafka events:
<?php
require __DIR__ . '/vendor/autoload.php';
use Bref\Context\Context;
use Bref\Event\Kafka\KafkaEvent;
use Bref\Event\Kafka\KafkaEventHandler;
class Handler extends KafkaEvent
{
public function handleKafka(KafkaEvent $event, Context $context): void
{
foreach ($event->getRecords() as $record) {
$data = $record->getValue();
// do something
}
}
}
return new Handler();