The intention is to build our AWS ecosystem using the serverless product. This will enable us to configure our AWS provisioning in a YML file which is then translated into AWS CloudFormation orchestration. In the same codebase our PHP Lambda function can be built out. It isn't my intention to provide heavy documentation here since the Contact Form architecture was defined in an earlier blog, and I have previously written many articles on the serverless / lambda / API Gateway combination of technologies to deliver HTTP content and handle form POSTs.
Notwithstanding this, I will of course document points of interest along the way so it all makes sense!
$ npm install serverless -g
$ serverless install --url https://github.com/araines/serverless-php -n d8-contact-form Serverless: Downloading and installing "serverless-php"... Serverless: Successfully installed "d8-contact-form"
$ cd d8-contact-form/ $ ls -las total 48 0 drwxrwxr-x 16 501 dialout 512 Jan 2 11:14 . 0 drwxr-xr-x 14 501 dialout 448 Jan 2 11:14 .. 4 -rwxr-xr-x 1 501 dialout 660 Feb 14 2018 buildphp.sh 4 -rw-r--r-- 1 501 dialout 569 Feb 14 2018 CHANGELOG.md 4 -rw-r--r-- 1 501 dialout 430 Feb 14 2018 composer.json 0 drwxrwxr-x 3 501 dialout 96 Jan 2 11:14 config 4 -rw-r--r-- 1 501 dialout 1011 Feb 14 2018 dockerfile.buildphp 4 -rw-r--r-- 1 501 dialout 40 Feb 14 2018 .gitattributes 4 -rw-r--r-- 1 501 dialout 120 Feb 14 2018 .gitignore 4 -rw-r--r-- 1 501 dialout 1330 Feb 14 2018 handler.js 4 -rw-r--r-- 1 501 dialout 829 Feb 14 2018 handler.php 4 -rw-r--r-- 1 501 dialout 1103 Feb 14 2018 LICENSE 4 -rwxr-xr-x 1 501 dialout 133 Feb 14 2018 php 4 -rw-r--r-- 1 501 dialout 3705 Feb 14 2018 README.md 4 -rw-r--r-- 1 501 dialout 3008 Jan 2 11:14 serverless.yml 0 drwxrwxr-x 5 501 dialout 160 Jan 2 11:14 src
$ ls -lash | grep " php" 27M -rwxr-xr-x 1 501 dialout 27M Jan 2 11:22 php
$ composer install -o --no-dev You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug Loading composer repositories with package information Updating dependencies Package operations: 9 installs, 0 updates, 0 removals - Installing psr/log (1.1.0): Downloading (100%) - Installing monolog/monolog (1.24.0): Downloading (100%) - Installing symfony/polyfill-ctype (v1.10.0): Loading from cache - Installing symfony/filesystem (v4.2.1): Downloading (100%) - Installing symfony/config (v4.2.1): Downloading (100%) - Installing symfony/contracts (v1.0.2): Downloading (100%) - Installing psr/container (1.0.0): Loading from cache - Installing symfony/dependency-injection (v4.2.1): Downloading (100%) - Installing symfony/yaml (v4.2.1): Downloading (100%) Writing lock file Generating optimized autoload files
$ serverless invoke local -f hello Got event [] [] { "statusCode": 200, "body": "Go Serverless v1.0! Your function executed successfully!" }


To use the AWS SES service, your email address will need to be verified. This is easy - navigate to Simple Email Service and click on email addresses, then "Verify a new email address". Add the email address and a verification email message will be sent for you to click back and confirm the verification process. Once verified, you'll see the second screenshot above.
custom: SENDER_EMAIL: xxxx@xxxxxxxx RECIPIENT_EMAIL: xxxx@xxxxxxxx
provider: name: aws runtime: nodejs6.10 stage: dev region: eu-west-1 environment: SENDER: ${self:custom.SENDER_EMAIL} RECIPIENT: ${self:custom.RECIPIENT_EMAIL} DOMAIN: "*" iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: "*"
$ sls deploy Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... Serverless: Checking Stack create progress...
$ composer require aws/aws-sdk-php You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug Using version ^3.90 for aws/aws-sdk-php ./composer.json has been updated
- Add the filter and simplexml libraries to dockerfile.buildphp
- Make a note of the php creation date and filesize.
- Make sure buildphp.sh is executable on the command line and run it.
<?php
$event['sender_email'] = getenv('SENDER');
$event['recipient_email'] = getenv('RECIPIENT');
?>
- I have added the necessary use clauses to the AWS SDK client and the exception class
- The protected $client_config holds the configuration we have - but note that further down I am getting the credentials from the CredentialProvider
- The recipients are an array and so it is possible if required to add multiples here.
D8ContactFormHandler.php
<?php
namespace Raines\Serverless;
require 'vendor/autoload.php';
use Aws\Ses\SesClient;
use Aws\Exception\AwsException;
class D8ContactFormHandler implements Handler
{
protected $client_config = [
'region' => 'eu-west-1',
'version' => '2010-12-01',
'credentials.cache' => TRUE,
'validation' => FALSE,
];
/**
* {@inheritdoc}
*/
public function handle(array $event, Context $context)
{
$logger = $context->getLogger();
// Comment out for debugging
//$logger->notice('Got event', $event);
// Set up AWS SDK
$this->client_config['credentials'] = \Aws\Credentials\CredentialProvider::env();
$SesClient = new SesClient($this->client_config);
$sender_email = $event['sender_email'];
$recipient_emails[] = $event['recipient_email'];
return [
'statusCode' => 200,
'body' => 'Go Serverless v1.0! Your function executed successfully!',
];
}
}
?>
D8ContactFormHandler.php
<?php
// Process the submitted form.
// *TODO* This could do with more validation.
$fields = [];
parse_str($event['body'], $fields);
if (!isset($fields['name'])) $fields['name'] = '{unknown name}';
if (!isset($fields['mail'])) $fields['mail'] = '{unknown email address}';
if (!isset($fields['subject'][0]['value'])) $fields['subject'][0]['value'] = '{unknown subject}';
if (!isset($fields['message'][0]['value'])) $fields['message'][0]['value'] = '{unknown message}';
$subject = '[badzilla.co.uk website feedback] '.$fields['subject'][0]['value'];
$plaintext_body = 'From: '.$fields['name'].' '.$fields['mail'].
'Subject: '.$fields['subject'][0]['value'].
' Message: '. $fields['message'][0]['value'];
$html_body = '<h1>'.$fields['subject'][0]['value'].'</h1>'.
'<h2>'.'From: '.$fields['name'].' <a href="mailto:"'.$fields['mail'].'">'.$fields['mail'].'</a>'.'</h2>'.
'<p>'.$fields['message'][0]['value'].'</p>';
$char_set = 'UTF-8';
try {
$result = $SesClient->sendEmail([
'Destination' => [
'ToAddresses' => $recipient_emails,
],
'ReplyToAddresses' => [$sender_email],
'Source' => $sender_email,
'Message' => [
'Body' => [
'Html' => [
'Charset' => $char_set,
'Data' => $html_body,
],
'Text' => [
'Charset' => $char_set,
'Data' => $plaintext_body,
],
],
'Subject' => [
'Charset' => $char_set,
'Data' => $subject,
],
],
]);
$messageId = $result['MessageId'];
} catch (AwsException $e) {
// output error message if fails
$logger->notice('Message', $e->getMessage());
$logger->notice('AWS Message', $e->getAwsErrorMessage());
}
?>
D8 ContactFormHandler.php
<?php
return [
'headers' => ['Location' => $event['headers']['Referer']],
'statusCode' => 307,
];
</codE>?>

During the development process you will have to do multiple deploys and doubtless you will get numerous error responses from AWS. The best way of working these issues is to use CloudWatch, and a sample screenshot is shown above. The codebase has an excellent logging facility to check variables and these are written out to CloudWatch. Have a look at:
<?php
$logger->notice('Got event', $event);
?>
The entire codebase discussed here is at https://github.com/sanddevil/serverless-php-contact-form - if you wish to extend its functionality, please send me a pull request.