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();

You can read more about SQS workers in Serverless Visually Explained.

Full reference of SQS in serverless.yml.

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
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 is it executed? Using PHP-FPM. Using the PHP CLI.
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).
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.
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.
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.

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
        layers:
            - ${bref:layer.php-74}
        events:
            - httpApi: 'POST /articles'
    get-article:
        handler: get-article-handler.php
        layers:
            - ${bref:layer.php-74}
        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.

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.

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.