How to Write and Deploy a PHP Lambda Function with SAM CLI

We have three options. We can:

This article will focus on the first option; how to build, push and deploy a PHP container image to Lambda with Docker and the AWS SAM CLI.

This article will explain:

  1. How to structure the function.
  2. How to test locally.
  3. How to deploy.

How does it work?

  1. A bootstrap file fetches the next invocation from the Lambda runtime.
  2. The boostrap passes event data to PHP.
  3. PHP processes the event and returns a success or error response to the Lambda runtime.

Step 1: Install

Ensure you have installed:

Step 2: Build your Function

Create your function with this structure:

MyApp
├── src
│   └── MyClass.php
├── 99-prod-overrides.ini
├── bootstrap
├── composer.json
├── Dockerfile
├── handler.php
├── template.yaml

composer.json

Use composer to install dependencies, define autoload standards and dump the autoload file.

View composer.json

bootstrap

The bootstrap file fetches the next lambda invocation and hands it to handler.php with the PHP CLI.

While we could write the bootstrap in PHP, using bash gives us 2 advantages:

  • a smaller boostrap gives us a faster Lambda warm up.
  • a bash bootstrap contains memory leaks to a single invocation.

The bootstrap

View bootstrap

99-prod-overrides.ini

Add ini overrides in the 99-prod-overrides.ini file.

Dockerfile

We build from the official AWS lambda provided Amazon Linux 2 image. You can extend from any base, but you will then need to implement the Lambda runtime yourself.

We use unofficial Remi repo in this example to simplify installing PHP and PHP dependencies and extensions. Use this AWS tutorial if you want to go the official route.

The dockerfile follows these steps:

  1. Install packages.
  2. Copy bootstrap to /var/runtime
  3. Copy handler and code to /var/task
  4. Install dependencies.

View Dockerfile

handler.php

The handler.php file passes event data to your function code and returns a success or error response to the Lambda runtime.

In this examplehandler.php passes event data to the MyClass::run static function. It returns an error response if any exception is thrown.

View handler.php

View MyClass

template.yaml

We use the SAM CLI to build, develop and deploy serverless cuntionss. SAM uses CloudFormation Templates to define resources.

We use the Properties.PackateType, and MetaData.Docker* properties to tell SAM and AWS to

It sets the DEST build arg to aws - you can create a parameter, or a git ignored local template file to build the local dev version.

View template.yaml

Step 3: Test the System Locally

We can test the system in 2 ways:

1. sam local invoke ...

sam build
sam local invoke PhpTestFunction

If it works you should see:

Invoking Container created from phptestfunction:latest
Building image..........
Skip pulling image and use local one: phptestfunction:rapid-1.13.2.

START RequestId: xxx-xxx-xxx-xxx Version: $LATEST
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    34  100    16  100    18  16000  18000 --:--:-- --:--:-- --:--:-- 34000
END RequestId: xxx-xxx-xxx-xxx
REPORT RequestId: xxx-xxx-xxx-xxx  Init Duration: 0.50 ms  Duration: 71.64 ms      Billed Duration: 100 ms Memory Size: 128 MB     Max Memory Used: 128 MB
Yep, it is working

2. Replicate the Lambda API Flow with cURL from Host

In one terminal build and run with docker:

docker build --build-arg DEST=aws -t php-lambda .
docker run -p 9000:8080 -it php-lambda

In another terminal POST to the runtime

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d "{}"

If it works, in the docker run terminal you should see:

START RequestId: d9e7cfe1-eddc-48a8-9211-a1a3e44ec3bb Version: $LATEST
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    34  100    16  100    18   8000   9000 --:--:-- --:--:-- --:--:-- 17000
END RequestId: d9e7cfe1-eddc-48a8-9211-a1a3e44ec3bb
REPORT RequestId: d9e7cfe1-eddc-48a8-9211-a1a3e44ec3bb  Init Duration: 0.34 ms  Duration: 50.08 ms      Billed Duration: 100 ms Memory Size: 3008 MB    Max Memory Used: 3008 MB

In the cURL terminal you should see:

Yep, it is working

Step 4: Deploy

Step 5: Test the System on AWS