Search code examples
phpamazon-sesaws-php-sdk

Why does Amazon SES fail half the time when called using the PHP SDK?


I'm using the AWS SDK for PHP (version 3.52.33, PHP version 7.2.19) and trying to send emails using the Simple Email Service (SES). I have SES configured, and can run the example code successfully. To make my life easier, I wrote a function to send emails (send_email.php):

<?php

// Path to autoloader for AWS SDK
define('REQUIRED_FILE', "/path/to/vendor/autoload.php");

// Region:
define('REGION','us-west-2');

// Charset
define('CHARSET','UTF-8');

// Specify Sender
define('SENDER', '[email protected]');

require REQUIRED_FILE;

use Aws\Ses\SesClient;
use Aws\Ses\Exception\SesException;


function send_email($htmlBody,$textBody,$subject,$recipient) {

        $access_key = 'accessKey';
        $secret_key = 'secretKey';

        $ret_array = array('success' => false,
                        'message' => 'No Email Sent'
                        );

        $client = SesClient::factory(array(
                'version' => 'latest',
                'region' => REGION,
                'credentials' => array(
                        'key' => $access_key,
                        'secret' => $secret_key
                )
        ));


        try {
                $result = $client->sendEmail([
                        'Destination' => [
                                'ToAddresses' => [
                                        $recipient,
                                ],
                        ],
                        'Message' => [
                                'Body' => [
                                        'Html' => [
                                                'Charset' => CHARSET,
                                                'Data' => $htmlBody,
                                        ],
                                        'Text' => [
                                                'Charset' => CHARSET,
                                                'Data' => $textBody,
                                        ],
                                ],
                                'Subject' => [
                                        'Charset' => CHARSET,
                                        'Data' => $subject,
                                ],
                        ],
                        'Source' => SENDER,
                ]);

                $messageId = $result->get('MessageId');
                $ret_array['success'] = true;
                $ret_array['message'] = $messageId;
                echo("Email sent! Message ID: $messageId" . "\n");
        } catch (SesException $error) {
                echo("The email was not sent. Error message: " . $error->getAwsErrorMessage() . "\n");
                $ret_array['message'] = $error->getAwsErrorMessage();
        }

        return $ret_array;
}

This works when called from a simple testing script (test.php) in a terminal:

<?php
ini_set('display_errors','On');
error_reporting(E_ALL | E_STRICT);

require_once './send_email.php';

$email = '[email protected]';
$htmlbody = 'test';
$txtbody = 'test';
$subject = 'test email';

$success = send_email($htmlbody,$txtbody,$subject,$email);

I get output like:

[~]$ php test.php
Email sent! Message ID: 0101016c8d665369-027be596-f8da-4410-8f09-ff8d7f87181b-000000

which is great. However, I'm doing this to send automated emails from a website (new user registration, password resets, ...) and when I try to use send_email from within a larger script I get a ~%50 success rate (when using a constant email address). Either it works and everything is fine, or it fails without an error message:

The email was not sent. Error message: 

I know that an exception is being thrown, as I'm ending up in the catch statement, but I don't know how to get more information about what went wrong since there isn't a message associated with the exception. I've tried expanding what I look for in the catch block:

<snip>
catch (SesException $error) {
                echo("The email was not sent. Error message: " . $error->getAwsErrorMessage() . "\n");
                $ret_array['message'] = $error->getAwsErrorMessage();
                $ret_array['errorCode'] = $error->getAwsErrorCode();
                $ret_array['type'] = $error->getAwsErrorType();
                $ret_array['response'] = $error->getResponse();
                $ret_array['statusCode'] = $error->getStatusCode();
                $ret_array['isConnectionError'] = $error->isConnectionError();
        }

but when it fails everything is NULL except isConnectionError = false. Anecdotally, it is totally random -- I haven't been able to discern a pattern at all as to when it works and when it fails.

One other potentially relevant note: if I loop the email sending so a new user gets 10 emails, either they all succeed or they all fail.

So, does anyone have any suggestions as to what might be going wrong, or other steps I could take to help diagnose why this is happening?


Solution

  • For anyone running in to a similar issue in the future, I eventually came up with two solutions:

    Initially, I gave up hope on the AWS SDK and switched to using PHPMailer which avoided the issue (though I believe at the cost of a loss in performance).

    The real issue (I'm fairly certain now) was a mismatch in the versions of PHP between my CLI and what the webserver was providing. This question got me thinking about how that might be the issue. By updating the version of cURL and PHP that the webserver was using, I resolved the issue.