Monday, June 24, 2013

How to use Amazon SES to Send Email from PHP


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: One pitfall with this is using the SMTP username and password instead of your AWS credentials.  Use your AWS credentials when sending emails using the SDK.

Otherwise, you may get 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


  1. Install the SDK
  2. Know if your account is sandboxed or not - If you account is sandboxed, you will only be able to send emails to the email addresses in the verified senders list.
  3. Make sure your source email's sending address is listed as a verified sender - Verify an email address that you own and use that in your sample code.  Check this email and the ReturnPath address in your AWS console if you are getting the "Email address is not verified" error.
  4. If you use the ReturnPath parameter (not shown here) to receive bounced emails, then that email address or domain must be verified as well

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'
));


//Now that you have the client ready, you can build the message

$msg = array();
$msg['Source'] = "authorized_aws_email@somewhere.com";

//ToAddresses must be an array
$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);


     //save the MessageId which can be used to track the request
     $msg_id = $result->get('MessageId');
     echo("MessageId: $msg_id");

     //view sample output
     print_r($result);
} catch (Exception $e) {
     //An error happened and the email did not get sent
     echo($e->getMessage());
}

//view the original message passed to the SDK 
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.

Update:

Because of a request from Mohit Singh, I've updated the code to allow for adding a single attachment to the email.  See below for the details.

Using Attachments:

Use this example code in your program to send an email with an attachment using Amazon SES.

include_once("SESUtils.php");

$subject_str = "Some Subject";
$body_str = "<strong>Some email body</strong>";
$attachment_str = get_file_contents("/htdocs/test/sample.pdf");


//send the email
$result = SESUtils::deliver_mail_with_attachment(

    array('email1@gmail.com', 'email2@lutz-engr.com'),       
    $subject_str, $body_str, 'sender@verifiedbyaws', 
    $attachment_str);

//now handle the result if you wish
print_r($result);


Complete Source for PHP Solution for sending mail using SES

Update #2 - (2015-01-27) Michael Deal was kind enough to provide additional features and enhancements in this new version

Update #3 - (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!


<?php

require_once('AWSSDKforPHP/aws.phar');

use Aws\Ses\SesClient;

/**
 * SESUtils is a tool to make it easier to work with Amazon Simple Email Service
 * Features:
 * A client to prepare emails for use with sending attachments or not
 * 
 * There is no warranty - use this code at your own risk.  
 * @author sbossen 
 * http://righthandedmonkey.com
 *
 * Update: Error checking and new params input array provided by Michael Deal
 * Update2: Corrected for allowing to send multiple attachments and plain text/html body
 *   Ref: Http://stackoverflow.com/questions/3902455/smtp-multipart-alternative-vs-multipart-mixed/
 */
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;

    /**
     * Usage:
        $params = array(
          "to" => "email1@gmail.com",
          "subject" => "Some subject",
          "message" => "<strong>Some email body</strong>",
          "from" => "sender@verifiedbyaws",
          //OPTIONAL
          "replyTo" => "reply_to@gmail.com",
          //OPTIONAL
          "files" => array(
            1 => array(
               "name" => "filename1", 
              "filepath" => "/path/to/file1.txt", 
              "mime" => "application/octet-stream"
            ),
            2 => array(
               "name" => "filename2", 
              "filepath" => "/path/to/file2.txt", 
              "mime" => "application/octet-stream"
            ),
          )
        );
      
      $res = SESUtils::sendMail($params);
      
     * NOTE: When sending a single file, omit the key (ie. the '1 =>') 
     * or use 0 => array(...) - otherwise the file will come out garbled
     * ie. use:
     *    "files" => array(
     *        0 => array( "name" => "filename", "filepath" => "path/to/file.txt",
     *        "mime" => "application/octet-stream")
     * 
     * For the 'to' parameter, you can send multiple recipiants with an array
     *    "to" => array("email1@gmail.com", "other@msn.com")
     * use $res->success to check if it was successful
     * use $res->message_id to check later with Amazon for further processing
     * use $res->result_text to look for error text if the task was not successful
     * 
     * @param array $params - array of parameters for the email
     * @return \ResultHelper
     */
    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();

        // get the client ready
        $client = SesClient::factory(array(
                    'key' => self::AWS_KEY,
                    'secret' => self::AWS_SEC,
                    'region' => self::AWS_REGION
        ));

        // build the message
        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";
        }

        // in case you have funny characters in the subject
        $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); //random unique string
        $boundary2 = uniqid("_Part2_".time(), true); //random unique string
        $msg .= " boundary=\"$boundary\"\n";
        $msg .= "\n";

        // now the actual body
        $msg .= "--$boundary\n";

        //since we are sending text and html emails with multiple attachments
        //we must use a combination of mixed and alternative boundaries
        //hence the use of boundary and boundary2
        $msg .= "Content-Type: multipart/alternative;\n";
        $msg .= " boundary=\"$boundary2\"\n";
        $msg .= "\n";
        $msg .= "--$boundary2\n";

        // first, the plain text
        $msg .= "Content-Type: text/plain; charset=utf-8\n";
        $msg .= "Content-Transfer-Encoding: 7bit\n";
        $msg .= "\n";
        $msg .= strip_tags($body); //remove any HTML tags
        $msg .= "\n";

        // now, the html text
        $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";

        // add attachments
        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";
            }
            // close email
            $msg .= "--\n";
        }

        // now send the email out
        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;
        }
    }

    /**
    Clean filename function - to be mail friendly 
    **/
    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) {
            //don't truncate file extension
            $arr = explode(".", $clean);
            $size = count($arr);
            $base = "";
            $ext = "";
            if ($size > 0) {
                for ($i = 0; $i < $size; $i++) {
                    if ($i < $size - 1) { //if it's not the last item, add to $bn
                        $base .= $arr[$i];
                        //if next one isn't last, add a dot
                        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);
            // doing again in case extension is long
            $clean = mb_substr($bn_new.$ext, 0, $limit); 
        }
        return $clean;
    }
    
}

class ResultHelper {

    public $success = true;
    public $result_text = "";
    public $message_id = "";

}

?>

Sending multiple attachments using SES - Completed!


The above is a more complete and robust version of the sending email attachments with Amazon SES.  The one above now lets you have more than one attachment to send from the previous version.  Hope you enjoyed this and thanks to all for participating! 



126 comments:

  1. You are my hero. Thank you so much for this post!

    ReplyDelete
    Replies
    1. You're welcome! Glad I could help. If you wouldn't mind adding a +1, I'd appreciate it.

      Delete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. Despite the fact that there are a huge number of retail sites on the web, a couple of significant brands keep on overwhelming, much like customary high road retail. By some separation, Amazon is the biggest online retailer of all.Buy Niche Organic 100% Safe USA based Amazon Traffic

    ReplyDelete
    Replies
    1. Great Article
      Cloud Computing Projects


      Networking Projects

      Final Year Projects for CSE


      JavaScript Training in Chennai

      JavaScript Training in Chennai

      The Angular Training covers a wide range of topics including Components, Angular Directives, Angular Services, Pipes, security fundamentals, Routing, and Angular programmability. The new Angular TRaining will lay the foundation you need to specialise in Single Page Application developer. Angular Training

      Delete
  4. , you can procure a specialist PHP web designer so as to tweak your site according to your business prerequisites, which is very financially savvy. CakePHP Application Development

    ReplyDelete
  5. PHP's essential use is as an "implanted" scripting language, which implies that the real PHP code is inserted in HTML code.Why use Laravel

    ReplyDelete
  6. Find approved Motorola administration focuses close to you in . Discover Location. It would be ideal if you enter your city to view Service Centers that are approved to fix your gadget.
    Best mobile service centre.

    ReplyDelete
  7. You have a good point here!I totally agree with what you have said!!Thanks for sharing your views...hope more people will read this article!!!
    tree service near me in tequesta

    ReplyDelete
  8. Superbly written article, if only all bloggers offered the same content as you, the internet would be a far better place.
    bathroom remodel reno nv

    ReplyDelete
  9. Thanks for a wonderful share. Your article has proved your hard work and experience you have got in this field. Brilliant .i love it reading.
    septic tank cleaning west palm beach

    ReplyDelete
  10. I have read your article, it is very informative and helpful for me.I admire the valuable information you offer in your articles. Thanks for posting it..
    durham remodeling contractors

    ReplyDelete
  11. Hello, I have browsed most of your posts. This post is probably where I got the most useful information for my research. Thanks for posting, maybe we can see more on this. Are you aware of any other websites on this subject.
    ac installation royal palm beach

    ReplyDelete
  12. Great article and a nice way to promote online. I’m satisfied with the information that you provided
    bathroom remodelers raleigh

    ReplyDelete
  13. This post is good enough to make somebody understand this amazing thing, and I’m sure everyone will appreciate commercial ac unit west palm beach

    ReplyDelete
  14. You have a good point here!I totally agree with what you have said!!Thanks for sharing your views...hope more people will read this article!!!
    commercial screen enclosures cape coral

    ReplyDelete
  15. Very nice bro, thanks for sharing this with us. Keep up the good work and Thank you for sharing information tree trimmers port st lucie

    ReplyDelete
  16. Great blog and a great topic as well I really get amazed to read this. It’s really good.
    free airdrops

    ReplyDelete
  17. This comment has been removed by a blog administrator.

    ReplyDelete
  18. Great blog thanks for sharing Instagram and Facebook have provided an amazing place for new brands to grow and flourish. We can find the perfect niche for your brand on the best social media platforms. Marketing through social media brings forth global audience without all these physical boundaries. Analyze and take over the competition with ease with Adhuntt Media’s digital marketing tools and strategies.
    digital marketing company in chennai

    ReplyDelete
  19. Nice blog thanks for sharing You have come to the right place. Karuna Nursery Gardens is the ideal place to begin your journey into landscape gardening. Our specialists have built some of the finest landscape garden in Chennai that too at the best price and amazing service.
    plant nursery in chennai

    ReplyDelete
  20. Excellent blog thanks for sharing Pixies beauty Shop is the best place to buy cosmetics in Chennai. With thousands of premium imported brands to choose from, you’ll never run out of lipstick again. And don’t forget about the best offers and value they provide.
    beauty Shop in Chennai

    ReplyDelete
  21. כתיבה מעולה, אהבתי. אשתף עם העוקבים שלי.
    קבוצת גבאי פייסבוק

    ReplyDelete
  22. This comment has been removed by the author.

    ReplyDelete
  23. הדעות שלי קצת חלוקות בעניין הזה אבל ללא ספק כתבת מעניין מאוד.
    טבעות אירוסין זהב לבן

    ReplyDelete
  24. רציתי רק לשאול, אפשר לשתף את הפוסט בבלוג שלי
    רהיטים לסלון

    ReplyDelete
  25. לגמרי פוסט שדורש שיתוף תודה.
    פינות אוכל

    ReplyDelete
  26. מזל שנתקלתי בכתבה הזאת. בדיוק בזמן
    טיפול prp לשיער

    ReplyDelete
  27. אין ספק שזה אחד הנושאים המעניינים. תודה על השיתוף.
    אירוע בת מצווה

    ReplyDelete
  28. לגמרי פוסט שדורש שיתוף תודה.
    מארז ליולדת

    ReplyDelete
  29. תודה על השיתוף. מחכה לכתבות חדשות.
    ברוקרים

    ReplyDelete
  30. Great Information,it has lot for stuff which is informative.I will share the post with my friend.
    animal jewelry

    ReplyDelete
  31. The best way to spend your night in 31 Dec with Hong Kong Escorts Girls, call us for bookings

    ReplyDelete
  32. סופסוף מישהו שתואם לדעותיי בנושא. תודה.
    בלוק תמונה

    ReplyDelete
  33. אין ספק שזה אחד הנושאים המעניינים. תודה על השיתוף.
    התקנת אינטרקום

    ReplyDelete
  34. Thanks for sharing this nice informatione!
    financial modelling to give you confidence in your financial strategy and provide business valuations.

    ReplyDelete
  35. It’s difficult to find experienced people in this particular topic, however, you seem like you know what you’re talking about! Thanks
    Tech news

    ReplyDelete
  36. Very useful blog thanks for sharing IndPac India the German technology Packaging and sealing machines in India is the leading manufacturer and exporter of Packing Machines in India.

    ReplyDelete
  37. Awesome article, it was exceptionally helpful! I simply began in this and I'm becoming more acquainted with it better. The post is written in very a good manner and it contains many useful information for me. Thank you very much and will look for more postings from you.


    digital marketing blog
    digital marketing bloggers
    digital marketing blogs
    digital marketing blogs in india
    digital marketing blog 2020
    digital marketing blog sites
    skartec's digital marketing blog
    skartec's blog
    digital marketing course
    digital marketing course in chennai
    digital marketing training
    skartec digital marketing academy

    ReplyDelete
  38. Thanks for your sharing! The information your share is very useful to me and many people are looking for them just like me!


    We have collection of best 2020 sex doll to fulfill your desire, so if you need realistic female sex dolls then Love Doll Palace is largest online store for you , who are providing young love doll for you, these are made by silicone and TPE, which will give you full happiness at sex duration and you can enjoy with her at any position.

    ReplyDelete
  39. אין ספק שהפוסט הזה דורש שיתוף. תודה
    ניהול מוניטין בגוגל

    ReplyDelete
  40. סופסוף מישהו שתואם לדעותיי בנושא. תודה.
    השקעות מניבות

    ReplyDelete
  41. כתיבה מעולה, אהבתי. אשתף עם העוקבים שלי.
    עיצוב חווית משתמש

    ReplyDelete
  42. Awesome and interesting article. Great things you've always shared with us. Visit Kidoriman and see Kidoriman reviews show that how people are happy with our extensive services. Our high-quality products and clothes make our customer happy. As a result, they prefer us for the shopping every time.

    ReplyDelete
  43. Application programs are intended to do explicit errands to be executed through the PC and the working framework programs are utilized to deal with the inward elements of the PC to encourage utilization of use program.
    itools crack reddit

    ReplyDelete
  44. i am browsing this website dailly , and get nice facts from here all the time .

    ReplyDelete
  45. פוסט מרענן במיוחד. לגמרי משתף.
    קאנבי

    ReplyDelete
  46. A backlink is a link created when one website links to another. Backlinks are important to SEO & impact for higher ranking. In my 7+ years seo Career i see, without backlinks a website doesn't rank higher on google SERP.

    Get Your 300+ High Quality DoFollow Backlinks Here!

    Order Now with Full Confidence & 100% satisfaction.

    ReplyDelete
  47. With Brandsoo, it’s never been easier or more convenient to shop high quality domain names and professional business name ideas logos that’ll instantly give your brand a leg up and resonate with your audience! With many different domain sellers all competing for your business, you need to know where your business and brand will see.

    ReplyDelete
  48. I really appreciate this wonderful post that you have provided for us.
    Garage door repair Pickering

    ReplyDelete
  49. With massive progress in educational technology schools are becoming hot for teachers better equipped than ever before. It’s a great time to consider a new job in education.

    ReplyDelete
  50. Forex Signals, MT4 and MT5 Indicators, Strategies, Expert Advisors, Forex News, Technical Analysis and Trade Updates in the FOREX IN WORLD

    Forex Signals Forex Strategies Forex Indicators Forex News Forex World

    ReplyDelete
  51. Thats great. I got the right one information at the right time for the right situation. Thanks for sharing.


    Global Asset Management Korea

    ReplyDelete
  52. Thanks for one marvelous posting! I enjoyed reading it; you are a great author. I will make sure to bookmark your blog and may come back someday. I want to encourage that you continue your great posts.
    oracle training in chennai

    oracle training institute in chennai

    oracle training in bangalore

    oracle training in hyderabad

    oracle training

    oracle online training

    hadoop training in chennai

    hadoop training in bangalore

    ReplyDelete
  53. I recently came across your article and have been reading along. I want to express my admiration of your writing skill and ability to make readers read from the beginning to the end. I would like to read newer posts and to share my thoughts with you.Your post is just outstanding! thanks for such a post,its really going great and great work.You have provided great knowledge
    Azure Training in Chennai

    Azure Training in Bangalore

    Azure Training in Hyderabad

    Azure Training in Pune

    Azure Training | microsoft azure certification | Azure Online Training Course

    Azure Online Training

    ReplyDelete
  54. Excellent Blog! I would Thanks for sharing this wonderful content.its very useful to us.This is incredible,I feel really happy to have seen your webpage.I gained many unknown information, the way you have clearly explained is really fantastic.keep posting such useful information.
    Full Stack Training in Chennai | Certification | Online Training Course
    Full Stack Training in Bangalore | Certification | Online Training Course

    Full Stack Training in Hyderabad | Certification | Online Training Course
    Full Stack Developer Training in Chennai | Mean Stack Developer Training in Chennai
    Full Stack Training

    Full Stack Online Training

    ReplyDelete
  55. This was an extremely wonderful post. Thanks for providing this info. 13 Reasons Why Hoodie

    ReplyDelete
  56. Shield Security Solutions Provides Ontario Security Training, Security Guard License or Security License in Ontario. Get Started Today

    ReplyDelete
  57. Great information about wilderness for beginners giving the opportunity for new people.
    rocketman denim jacket

    ReplyDelete
  58. Email is performing a very important rule in the business community in their communication maintain. I appreciate your efforts to create this topic. dissertation proposal writing services

    ReplyDelete
  59. Great blog, thanks for sharing with us. Ogen Infosystem is a leading web designing service provider in Delhi, India.
    Website Designing Company in India

    ReplyDelete
  60. חייב להחמיא על הכתיבה. מאמר מצוין.

    בריכות שחיה פיברגלס

    ReplyDelete
  61. I have to say this has been probably the most helpful posts for me. Please keep it up. I cant wait to read whats next.


    Garage Door Repair Okotoks

    ReplyDelete
  62. This is really amazing, you are very skilled blogger. Visit Ogen Infosystem for professional website designing and SEO Services.
    SEO Service in Delhi

    ReplyDelete
  63. it is really a great and helpful piece of info. I am glad that you shared this helpful information with us. Please keep us informed like this. Thank you for sharing.
    malaysia visa

    ReplyDelete
  64. אם כי אני לא מסכים עם כל מה שנכתב, מאמר מעניין
    סובארו אימפרזה sti

    ReplyDelete
  65. Our the purpose is to share the reviews about the latest Jackets,Coats and Vests also share the related Movies,Gaming, Casual,Faux Leather and Leather materials available Yellowstone Rip Wheeler Jacket

    ReplyDelete
  66. I enjoyed over read your blog post. This was actually what i was looking for and i am glad to came here!
    Website: Antique jewellery designs

    ReplyDelete
  67. Amazing Article,Really useful information to all So, I hope you will share more information to be check and share here.thanks for sharing .
    Website: Vietnam travel packages

    ReplyDelete