Sending Mail Using Amazon's SES (Simple Email Service)
I couldn't find too many good examples for this online and the Amazon AWS PHP SDK had incomplete documentation for a SendEmail
function when I was researching this topic.
NOTE: My biggest flaw with this was I was using the SMTP username and password instead of my AWS credentials. Use your AWS credentials when sending emails using the SDK.
I was getting this error:
SignatureDoesNotMatch, Status Code: 403, AWS Request ID: xxxxx, AWS Error Type: client,
AWS Error Message: The request signature we calculated does not match the signature you provided.
Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
The Canonical String for this request should have been
Setup
It's easy to install the Amazon SDK using PEAR (per Amazon documentation):sudo pear -D auto_discover=1 install pear.amazonwebservices.com/sdk
Once you get the SDK, you also need to make sure you have the Amazon SES setup correctly to let you send emails. If you account is sandboxed, you will only be able to send emails to the email addresses in the verified senders list. Verify an email address that you own and use that in your sample code.
When you have the right credentials, sending Email using the SDK is very easy.
Sample Code
require 'AWSSDKforPHP/aws.phar';
use Aws\Ses\SesClient;
$client = SesClient::factory(array(
'key' => 'aws_key',
'secret' => 'aws_secret',
'region' => 'us-east-1'
));
$msg = array();
$msg['Source'] = "authorized_aws_email@somewhere.com";
$msg['Destination']['ToAddresses'][] = "someone@somwhere.com";
$msg['Message']['Subject']['Data'] = "Text only subject";
$msg['Message']['Subject']['Charset'] = "UTF-8";
$msg['Message']['Body']['Text']['Data'] ="Text data of email";
$msg['Message']['Body']['Text']['Charset'] = "UTF-8";
$msg['Message']['Body']['Html']['Data'] ="HTML Data of email<br />";
$msg['Message']['Body']['Html']['Charset'] = "UTF-8";
try{
$result = $client->sendEmail($msg);
$msg_id = $result->get('MessageId');
echo("MessageId: $msg_id");
print_r($result);
} catch (Exception $e) {
echo($e->getMessage());
}
print_r($msg);
Result
Run the above code using the correct information and your emails should be on their way. Make sure to set the SenderID, DKIM, and SPF on your domain and Amazon properly to prevent your emails getting marked as spam.
Sending Attachments
Use the example usage code below in your main program to send attachments using SES's sendRawEmail
function:
include_once("SESUtils.php");
$subject_str = "Some Subject";
$body_str = "<strong>Some email body</strong>";
$attachment_str = get_file_contents("/htdocs/test/sample.pdf");
$result = SESUtils::deliver_mail_with_attachment(
array('email1@gmail.com', 'email2@lutz-engr.com'),
$subject_str, $body_str, 'sender@verifiedbyaws',
$attachment_str);
print_r($result);
Complete Solution with Latest Updates
Update#1 - (2015-01-27) Michael Deal was kind enough to provide additional features and enhancements in this new version
Update#2 - (2015-03-03) Code has been updated to properly handle plaintext with HTML and multiple attachments. It's not handled quite the way you would think. Thank you RFC-2046!
Here is the supporting code to help the code above work. Save the code below as 'SESUtils.PHP' and include it in your main program. Happy emailing!
<?php
require_once('AWSSDKforPHP/aws.phar');
use Aws\Ses\SesClient;
class SESUtils {
const version = "1.0";
const AWS_KEY = "YOUR-KEY";
const AWS_SEC = "YOUR-SECRET";
const AWS_REGION = "us-east-1";
const MAX_ATTACHMENT_NAME_LEN = 60;
public static function sendMail($params) {
$to = self::getParam($params, 'to', true);
$subject = self::getParam($params, 'subject', true);
$body = self::getParam($params, 'message', true);
$from = self::getParam($params, 'from', true);
$replyTo = self::getParam($params, 'replyTo');
$files = self::getParam($params, 'files');
$res = new ResultHelper();
$client = SesClient::factory(array(
'key' => self::AWS_KEY,
'secret' => self::AWS_SEC,
'region' => self::AWS_REGION
));
if (is_array($to)) {
$to_str = rtrim(implode(',', $to), ',');
} else {
$to_str = $to;
}
$msg = "To: $to_str\n";
$msg .= "From: $from\n";
if ($replyTo) {
$msg .= "Reply-To: $replyTo\n";
}
$subject = mb_encode_mimeheader($subject, 'UTF-8');
$msg .= "Subject: $subject\n";
$msg .= "MIME-Version: 1.0\n";
$msg .= "Content-Type: multipart/mixed;\n";
$boundary = uniqid("_Part_".time(), true);
$boundary2 = uniqid("_Part2_".time(), true);
$msg .= " boundary=\"$boundary\"\n";
$msg .= "\n";
$msg .= "--$boundary\n";
$msg .= "Content-Type: multipart/alternative;\n";
$msg .= " boundary=\"$boundary2\"\n";
$msg .= "\n";
$msg .= "--$boundary2\n";
$msg .= "Content-Type: text/plain; charset=utf-8\n";
$msg .= "Content-Transfer-Encoding: 7bit\n";
$msg .= "\n";
$msg .= strip_tags($body);
$msg .= "\n";
$msg .= "--$boundary2\n";
$msg .= "Content-Type: text/html; charset=utf-8\n";
$msg .= "Content-Transfer-Encoding: 7bit\n";
$msg .= "\n";
$msg .= $body;
$msg .= "\n";
$msg .= "--$boundary2--\n";
if (is_array($files)) {
$count = count($files);
foreach ($files as $file) {
$msg .= "\n";
$msg .= "--$boundary\n";
$msg .= "Content-Transfer-Encoding: base64\n";
$clean_filename = self::clean_filename($file["name"], self::MAX_ATTACHMENT_NAME_LEN);
$msg .= "Content-Type: {$file['mime']}; name=$clean_filename;\n";
$msg .= "Content-Disposition: attachment; filename=$clean_filename;\n";
$msg .= "\n";
$msg .= base64_encode(file_get_contents($file['filepath']));
$msg .= "\n--$boundary";
}
$msg .= "--\n";
}
try {
$ses_result = $client->sendRawEmail(
array(
'RawMessage' => array(
'Data' => base64_encode($msg)
)
), array(
'Source' => $from,
'Destinations' => $to_str
)
);
if ($ses_result) {
$res->message_id = $ses_result->get('MessageId');
} else {
$res->success = false;
$res->result_text = "Amazon SES did not return a MessageId";
}
} catch (Exception $e) {
$res->success = false;
$res->result_text = $e->getMessage().
" - To: $to_str, Sender: $from, Subject: $subject";
}
return $res;
}
private static function getParam($params, $param, $required = false) {
$value = isset($params[$param]) ? $params[$param] : null;
if ($required && empty($value)) {
throw new Exception('"'.$param.'" parameter is required.');
} else {
return $value;
}
}
public static function clean_filename($str, $limit = 0, $replace=array(), $delimiter='-') {
if( !empty($replace) ) {
$str = str_replace((array)$replace, ' ', $str);
}
$clean = iconv('UTF-8', 'ASCII//TRANSLIT', $str);
$clean = preg_replace("/[^a-zA-Z0-9\.\/_| -]/", '', $clean);
$clean = preg_replace("/[\/| -]+/", '-', $clean);
if ($limit > 0) {
$arr = explode(".", $clean);
$size = count($arr);
$base = "";
$ext = "";
if ($size > 0) {
for ($i = 0; $i < $size; $i++) {
if ($i < $size - 1) {
$base .= $arr[$i];
if ($i < $size - 2)
$base .= ".";
} else {
if ($i > 0)
$ext = ".";
$ext .= $arr[$i];
}
}
}
$bn_size = mb_strlen($base);
$ex_size = mb_strlen($ext);
$bn_new = mb_substr($base, 0, $limit - $ex_size);
$clean = mb_substr($bn_new.$ext, 0, $limit);
}
return $clean;
}
}
class ResultHelper {
public $success = true;
public $result_text = "";
public $message_id = "";
}
?>