Bref is a composer package that makes it easy to run serverless PHP applications on AWS Lambda. It achieves this by providing the required layers needed to run PHP applications since they are not supported natively on Lambda.

In this tutorial, we will be building and deploying a serverless PHP application that processes incoming emails programmatically using SendGrid Inbound Parse.

Prerequisites

To follow along, you will need:

  • PHP >= 7.2 (to use the latest version of Bref)
  • Composer, npm, and the serverless CLI installed
  • SendGrid Inbound Parse configured for your domain/subdomain (so that SendGrid can handle all incoming emails for such domain)
  • Ngrok installed (to expose your Bref application while developing locally)

Installing Bref and application dependencies

To get started, create a folder for your application (I am naming mine bref-email-watch) and enter into the directory with the command below:

$ mkdir bref-email-watch && cd bref-email-watch

Install the application dependencies which comprise the bref CLI, phpdotenv to enable us to load environment variables from a .env file, and nexylan/slack to interact with Slack’s API:

$ composer require bref/bref vlucas/phpdotenv nexylan/slack php-http/discovery

With our dependencies installed, initialize Bref by running ./vendor/bin/bref in the project directory and select the HTTP option from the interactive menu.

Output of “bref init” commandOutput of “bref init” command

The command will create a serverless.yml file which acts as the manifest for how the serverless framework will deploy your application as well as an index.php file to serve as an entry point into the application.

Next, create a .env file in the project folder and add the Slack hook URL:

SLACK_HOOK_URL="HERE_LIVES_YOUR_SLACK_HOOK_URL"

Parsing incoming emails from SendGrid

The application works by receiving JSON payloads (in the form of HTTP post requests) from SendGrid each time there is a new mail on the configured domain. We will modify the generated index.php file to parse these payloads, extract the sender and the recipient (using regex and PHP’s preg_match()), and send a Slack message to the relevant channel containing the extracted data.

Open the index.php file and replace its content with the code block below:

try {
    if (strtoupper($_SERVER['REQUEST_METHOD'] != 'POST')) {
        throw new Exception("Received non-post request on webhook handler");
    }

    if (json_last_error() != JSON_ERROR_NONE) {
        $em = "Error while parsing payload: ".json_last_error_msg();
        throw new Exception($em);
    }

    $from = $_POST['from'];
    $to = $_POST['to'];

    preg_match("#<(.*?)>#", $from, $sender);
    preg_match("#<(.*?)>#", $to, $recipient);
    $senderAddr = $sender[1];
    $recipientAddr = $recipient[1];

    $message = "*You've got mail!*\n";
    $message .= "*To:* ".$recipientAddr."\n";
    $message .= "*From:* ".$senderAddr;

    notifyOnSlack($message, true);

    // send OK back to SendGrid so they stop bothering our webhook
    header("Content-type: application/json; charset=utf-8");
    echo json_encode(["message" => "OK"]);
    exit(0);
} catch (Exception $e) {
    notifyOnSlack($e->getMessage());
    header("Content-type: application/json; charset=utf-8");
    http_response_code(400);
    echo json_encode(["message" => $e->getMessage()]);
    exit(0);
}

Sending slack notifications for new emails

In the previous code block, we referenced a notifyOnSlack function that doesn’t exist yet. This function is responsible for sending the $message parameter it receives to Slack. To implement it, load the variables declared in the .env file into your application by adding the following code to the top of the index.php file (just before the try block):

require_once './vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

Next, wire up the function implementation, like this:

function notifyOnSlack($message, $markdown = false)
{
    $slackHookUrl = $_ENV["SLACK_HOOK_URL"];
    $options = [
        "channel" => "#general",
        "allow_markdown" => $markdown,
        "username" => "bref-email-watch",
    ];
    $client = new Nexy\Slack\Client(
        \Http\Discovery\Psr18ClientDiscovery::find(),
        \Http\Discovery\Psr17FactoryDiscovery::findRequestFactory(),
        \Http\Discovery\Psr17FactoryDiscovery::findStreamFactory(),
        $slackHookUrl,
        $options
    );
    $client->send($message);
}

The function loads the SLACK_HOOK_URL environment variable from the .env file and then sets up the options, which includes the channel the message is to be sent to, to then be passed to the Slack client. We also instantiate the client by passing in the HTTPlug discovery services which allow it to find and use any HTTP client that conforms to the PSR standard.

Testing the serverless functions locally

Now that our application is set up, start the built-in PHP server on port 3000 and open an ngrok tunnel on the same port:

$ php -S localhost:3000
$ ngrok http 3000

The ngrok command generates Forwarding URL, like this_:_

ngrok command output

Copy the URL and visit your SendGrid’s Inbound Parse settings page. Now, click on the Add Host & URL button and paste in the copied URL in the Destination URL field.

Inbound Parse webhook settings

You may want to set up a proper subdomain since SendGrid will notify your webhook of EVERY email that comes to the domain name (irrespective of the username).

Next, send an email to an email address on the domain you specified and the notification should show up on Slack like this:

sample slack notificationsample slack notification

#php #sendgrid #serverless #developer

Receiving Emails with Bref PHP and SendGrid
2.50 GEEK