Every new starter at Twilio has to build an application using one of our products, then demo it to receive their fabled Track Jacket. For my application, because WiFi is always a pain at conferences, I wrote a PHP script that sends you the next talks for a given event.
Writing this so it worked locally was relatively straightforward with PHP’s inbuilt web server and ngrok, but when I got up to demo this in front of my peers, I didn’t want to be relying on my laptop to be open, awake and responding to the proxied HTTP requests. This code needs to be sitting somewhere on the internet so that it can respond to messages any time of the day or night, and not just when my laptop was open and connected to the wifi.
Serverless functions are great for this; they allow you to run code on someone else’s computers without having to deploy any infrastructure. Publishing your code can be as simple as running a CLI command, and you only pay for what you use with a generous free tier. Lambda is AWS’s offering in the serverless function space, but sadly it doesn’t support PHP out of the box.
So how can we run PHP applications on AWS Lambda?
- PHP 7 or newer
- AWS CLI and SAM tools configured with your AWS credentials
- A free Twilio account that has a telephone number
The Bref PHP open source project makes it relatively painless to deploy our PHP code as a Lambda serverless function. In late 2018, Amazon opened the door to custom runtimes and layers for Lambdas, and Bref takes advantage of this to allow PHP projects to be seamlessly deployed to Lambda. Currently, it uses the AWS SAM CLI library to do this, but integrations with the popular Serverless Framework are coming soon.
Let’s assume we've configured a phone number in the Twilio console to respond to a webhook that we are proxying to our local development environment using ngrok. Our webhook will use the Twilio PHP library to generate some dynamic TwiML that lets us know what time and date an appointment is.
Create a new file called
webhook.php in the root of your project, you’ll need to already have installed the Twilio PHP Helper Library using Composer.
<?php require_once('vendor/autoload.php'); $time = random_int(time(), time() + 2629746); $appointmentDate = DateTimeImmutable::createFromFormat('U', $time); $response = new \Twilio\TwiML\MessagingResponse(); $response->message('Your appointment is on ' . $appointmentDate->format( 'l jS F \a\t g:i A' )); echo $response;
We can wire this up as a webhook to a Twilio phone number that accepts SMS by adding the URL to the configuration for incoming messages. Now, when we send a text to the number, we'll receive a message back that contains the date and time of our appointment.
Let’s use Bref to upload our script to an AWS Lambda so that it’s available to be triggered as a webhook.
We can install Bref using Composer by running the following command in your project root:
composer require bref/bref
Bref ships with a command-line tool that simplifies everyday tasks and we can use this tool to generate the initial configuration files that Bref needs to deploy to Lambda.
When prompted by the CLI, pick an HTTP application (1) so that Bref can configure an HTTP endpoint using Amazon’s API Gateway.
$ vendor/bin/bref init
Bref has created two files in our project:
template.ymlconfigures how our application works, and what functions we want to define and how we invoke them
index.phpis the default “Hello World” PHP script that gets invoked by the default configurations. We can delete this -- we won’t be using it
We’ll need to update the
template.yml file to point to the
webhook.php file we created earlier. Because an HTTP request invokes this Lambda, it only needs to return text that gets sent as the HTTP response by the API Gateway, so we don’t need to modify the file that we used for local development at all.
Let’s rename the default
my-function to something more useful, and update the default values to be more meaningful leaving the rest of the configuration as it is.
Resources: TwilioAppointment: Type: AWS::Serverless::Function Properties: FunctionName: 'twilio-appointment' Description: 'Sends confirmation of appointment with Twilio' CodeUri: . Handler: webhook.php
With the application now configured, let's go ahead and deploy to AWS Lambda.
The Bref team have already compiled and deployed a working version of PHP that fits most use-cases as a Lambda layer so we don’t need to worry about doing that. Unless we need custom extensions or configuration the out-of-the-box settings should have us covered.
Before we can deploy, we’ll need to create an Amazon S3 bucket that Bref uses to stage the files that it needs to make the deploy. The bucket name needs to be unique across all of AWS, but we only need to do this once per-project.
aws s3 mb s3://<your-unique-bucket-name>
Next, we are going to package up all the files that are needed to deploy using the SAM CLI:
sam package --output-template-file .stack.yaml --s3-bucket <your-unique-bucket-name>
Let's deploy this function to AWS Lambda and create all of the AWS configurations that allow it to be invoked via HTTP. You’ll need to define a stack name-- the Stack is the bundle of configuration files that are uploaded to AWS CloudFormation to create the infrastructure needed inside AWS, it should be unique to your AWS account.
sam deploy --template-file .stack.yaml --capabilities CAPABILITY_IAM --stack-name <your-stack-name>
Everything has deployed successfully, but how do we invoke our function? To find the URL, we need to navigate to the CloudFormation section of the AWS Console. Make sure we're viewing the right region if you changed Bref’s configuration or you won’t be able to see your Stack. The URL to invoke the function is under the
Outputs tab of the Stack.
Clicking on the URL opens it in a browser... congratulations, we’ve invoked a PHP script through AWS Lambda!
Now, all that’s left to do is to replace our Twilio number’s SMS webhook to use the new Lambda URL, and we will have a working production system that scales on demand.
Webhooks can be a very powerful yet relatively simple way to respond to events from our Twilio phone number. SMS, Voice, Whatsapp, Autopilot and more can use webhooks to figure out what they should be doing when something interesting happens. Serverless functions are a great maintenance free solution to host these applications, and Bref makes it easier for us to run PHP on Lambda.
Let me know what you think -- do you run PHP on Lambda another way, or use a different serverless function provider? I’d love to hear more, drop me a Tweet. I can’t wait to see what you build.