Using Premailer to convert styles to inline styles for HTML emails automatically with PHP

David Carr

8 min read - 12th Jul, 2015

Table of Contents

Sending HTML emails can be tricky, not all mail clients support the same css. Most ignore external stylesheets, inline styles are the only way to format layouts for maximum compatability.

Applying inline styles to elements can be hard to maintain, this is where Premailer comes in, Premailer can take a file or HTML markup and take any styles inside style tags and place them inside the tags as inline style attributes.

Premailer provides an api to allow usage within your applications. http://premailer.dialect.ca/api

The Premailer API is accessed via an HTTP POST to http://premailer.dialect.ca/api/0.1/documents.

To use the API curl will be used, here is a reusable function to run curl requests

function curlContents($url, $data = false, $method = 'GET', $headers = false, $returnInfo = false) {    
    $ch = curl_init();

    if($method == 'POST') {
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        if($data !== false) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
    } else {
        if($data !== false) {
            if(is_array($data)) {
                $dataTokens = array();
                foreach($data as $key => $value) {
                    array_push($dataTokens, urlencode($key).'='.urlencode($value));
                }
                $data = implode('&', $dataTokens);
            }
            curl_setopt($ch, CURLOPT_URL, $url.'?'.$data);
        } else {
            curl_setopt($ch, CURLOPT_URL, $url);
        }
    }
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    if($headers !== false) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }

    $contents = curl_exec($ch);

    if($returnInfo) {
        $info = curl_getinfo($ch);
    }

    curl_close($ch);

    if($returnInfo) {
        return array('contents' => $contents, 'info' => $info);
    } else {
        return $contents;
    }
}

Next create a function called preMail, this function expects the HTML to be passed. the curlContents function is used to initiate the api request, the second argument is an array of options for the API in this case setting the type to HTML. 

This request returns a json object using the documents – html parts to call another curl request and return the html contents from the api’s html element.

function preMail($html)
{
    //iniate an api request pass in $body returns a json object
    $request = curlContents('http://premailer.dialect.ca/api/0.1/documents', array('html' => $html), 'POST');

    //collect the result and convert into array
    $result = json_decode($request, true);

    //return html contents
    return curlContents($result['documents']['html']);
}

To provide a demo markup, I’m using https://github.com/leemunroe/responsive-html-email-template it’s a responive HTML template.

ob_start();
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Really Simple HTML Email Template</title>
<style>
/* -------------------------------------
        GLOBAL
------------------------------------- */
* {
    margin: 0;
    padding: 0;
    font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
    font-size: 100%;
    line-height: 1.6;
}

img {
    max-width: 100%;
}

body {
    -webkit-font-smoothing: antialiased;
    -webkit-text-size-adjust: none;
    width: 100%!important;
    height: 100%;
}


/* -------------------------------------
        ELEMENTS
------------------------------------- */
a {
    color: #348eda;
}

.btn-primary {
    text-decoration: none;
    color: #FFF;
    background-color: #348eda;
    border: solid #348eda;
    border-width: 10px 20px;
    line-height: 2;
    font-weight: bold;
    margin-right: 10px;
    text-align: center;
    cursor: pointer;
    display: inline-block;
    border-radius: 25px;
}

.btn-secondary {
    text-decoration: none;
    color: #FFF;
    background-color: #aaa;
    border: solid #aaa;
    border-width: 10px 20px;
    line-height: 2;
    font-weight: bold;
    margin-right: 10px;
    text-align: center;
    cursor: pointer;
    display: inline-block;
    border-radius: 25px;
}

.last {
    margin-bottom: 0;
}

.first {
    margin-top: 0;
}

.padding {
    padding: 10px 0;
}


/* -------------------------------------
        BODY
------------------------------------- */
table.body-wrap {
    width: 100%;
    padding: 20px;
}

table.body-wrap .container {
    border: 1px solid #f0f0f0;
}


/* -------------------------------------
        FOOTER
------------------------------------- */
table.footer-wrap {
    width: 100%;    
    clear: both!important;
}

.footer-wrap .container p {
    font-size: 12px;
    color: #666;

}

table.footer-wrap a {
    color: #999;
}


/* -------------------------------------
        TYPOGRAPHY
------------------------------------- */
h1, h2, h3 {
    font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
    color: #000;
    margin: 40px 0 10px;
    line-height: 1.2;
    font-weight: 200;
}

h1 {
    font-size: 36px;
}
h2 {
    font-size: 28px;
}
h3 {
    font-size: 22px;
}

p, ul, ol {
    margin-bottom: 10px;
    font-weight: normal;
    font-size: 14px;
}

ul li, ol li {
    margin-left: 5px;
    list-style-position: inside;
}

/* ---------------------------------------------------
        RESPONSIVENESS
        Nuke it from orbit. It's the only way to be sure.
------------------------------------------------------ */

/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
    display: block!important;
    max-width: 600px!important;
    margin: 0 auto!important; /* makes it centered */
    clear: both!important;
}

/* Set the padding on the td rather than the div for Outlook compatibility */
.body-wrap .container {
    padding: 20px;
}

/* This should also be a block element, so that it will fill 100% of the .container */
.content {
    max-width: 600px;
    margin: 0 auto;
    display: block;
}

/* Let's make sure tables in the content area are 100% wide */
.content table {
    width: 100%;
}

</style>
</head>

<body bgcolor="#f6f6f6">

<!-- body -->
<table class="body-wrap" bgcolor="#f6f6f6">
    <tr>
        <td></td>
        <td class="container" bgcolor="#FFFFFF">

            <!-- content -->
            <div class="content">
            <table>
                <tr>
                    <td>
                        <p>Hi there,</p>
                        <p>Sometimes all you want is to send a simple HTML email with a basic design.</p>
                        <h1>Really simple HTML email template</h1>
                        <p>This is a really simple email template. Its sole purpose is to get you to click the button below.</p>
                        <h2>How do I use it?</h2>
                        <p>All the information you need is on GitHub.</p>
                        <table>
                            <tr>
                                <td class="padding">
                                    <p><a href="https://github.com/leemunroe/html-email-template" class="btn-primary">View the source and instructions on GitHub</a></p>
                                </td>
                            </tr>
                        </table>
                        <p>Feel free to use, copy, modify this email template as you wish.</p>
                        <p>Thanks, have a lovely day.</p>
                        <p><a href="http://twitter.com/leemunroe">Follow @leemunroe on Twitter</a></p>
                    </td>
                </tr>
            </table>
            </div>
            <!-- /content -->

        </td>
        <td></td>
    </tr>
</table>
<!-- /body -->

<!-- footer -->
<table class="footer-wrap">
    <tr>
        <td></td>
        <td class="container">

            <!-- content -->
            <div class="content">
                <table>
                    <tr>
                        <td align="center">
                            <p>Don't like these annoying emails? <a href="#"><unsubscribe>Unsubscribe</unsubscribe></a>.
                            </p>
                        </td>
                    </tr>
                </table>
            </div>
            <!-- /content -->

        </td>
        <td></td>
    </tr>
</table>
<!-- /footer -->

</body>
</html>

<?php
$body = ob_get_clean();

To convert the markup using preMailer call preMail function which internally calls curls and premailer api.

echo preMail($body);

Putting it all together:

<?php
function curlContents($url, $data = false, $method = 'GET', $headers = false, $returnInfo = false) {    
    $ch = curl_init();

    if($method == 'POST') {
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        if($data !== false) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
    } else {
        if($data !== false) {
            if(is_array($data)) {
                $dataTokens = array();
                foreach($data as $key => $value) {
                    array_push($dataTokens, urlencode($key).'='.urlencode($value));
                }
                $data = implode('&amp;', $dataTokens);
            }
            curl_setopt($ch, CURLOPT_URL, $url.'?'.$data);
        } else {
            curl_setopt($ch, CURLOPT_URL, $url);
        }
    }
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    if($headers !== false) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }

    $contents = curl_exec($ch);

    if($returnInfo) {
        $info = curl_getinfo($ch);
    }

    curl_close($ch);

    if($returnInfo) {
        return array('contents' => $contents, 'info' => $info);
    } else {
        return $contents;
    }
}

function preMail($html)
{
    //iniate an api request pass in $body returns a json object
    $request = curlContents('http://premailer.dialect.ca/api/0.1/documents', array('html' => $html), 'POST');

    //collect the result and convert into array
    $result = json_decode($request, true);

    //return html contents
    return curlContents($result['documents']['html']);
}

ob_start();
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Really Simple HTML Email Template</title>
<style>
/* -------------------------------------
        GLOBAL
------------------------------------- */
* {
    margin: 0;
    padding: 0;
    font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
    font-size: 100%;
    line-height: 1.6;
}

img {
    max-width: 100%;
}

body {
    -webkit-font-smoothing: antialiased;
    -webkit-text-size-adjust: none;
    width: 100%!important;
    height: 100%;
}


/* -------------------------------------
        ELEMENTS
------------------------------------- */
a {
    color: #348eda;
}

.btn-primary {
    text-decoration: none;
    color: #FFF;
    background-color: #348eda;
    border: solid #348eda;
    border-width: 10px 20px;
    line-height: 2;
    font-weight: bold;
    margin-right: 10px;
    text-align: center;
    cursor: pointer;
    display: inline-block;
    border-radius: 25px;
}

.btn-secondary {
    text-decoration: none;
    color: #FFF;
    background-color: #aaa;
    border: solid #aaa;
    border-width: 10px 20px;
    line-height: 2;
    font-weight: bold;
    margin-right: 10px;
    text-align: center;
    cursor: pointer;
    display: inline-block;
    border-radius: 25px;
}

.last {
    margin-bottom: 0;
}

.first {
    margin-top: 0;
}

.padding {
    padding: 10px 0;
}


/* -------------------------------------
        BODY
------------------------------------- */
table.body-wrap {
    width: 100%;
    padding: 20px;
}

table.body-wrap .container {
    border: 1px solid #f0f0f0;
}


/* -------------------------------------
        FOOTER
------------------------------------- */
table.footer-wrap {
    width: 100%;    
    clear: both!important;
}

.footer-wrap .container p {
    font-size: 12px;
    color: #666;

}

table.footer-wrap a {
    color: #999;
}


/* -------------------------------------
        TYPOGRAPHY
------------------------------------- */
h1, h2, h3 {
    font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
    color: #000;
    margin: 40px 0 10px;
    line-height: 1.2;
    font-weight: 200;
}

h1 {
    font-size: 36px;
}
h2 {
    font-size: 28px;
}
h3 {
    font-size: 22px;
}

p, ul, ol {
    margin-bottom: 10px;
    font-weight: normal;
    font-size: 14px;
}

ul li, ol li {
    margin-left: 5px;
    list-style-position: inside;
}

/* ---------------------------------------------------
        RESPONSIVENESS
        Nuke it from orbit. It's the only way to be sure.
------------------------------------------------------ */

/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
    display: block!important;
    max-width: 600px!important;
    margin: 0 auto!important; /* makes it centered */
    clear: both!important;
}

/* Set the padding on the td rather than the div for Outlook compatibility */
.body-wrap .container {
    padding: 20px;
}

/* This should also be a block element, so that it will fill 100% of the .container */
.content {
    max-width: 600px;
    margin: 0 auto;
    display: block;
}

/* Let's make sure tables in the content area are 100% wide */
.content table {
    width: 100%;
}

</style>
</head>

<body bgcolor="#f6f6f6">

<!-- body -->
<table class="body-wrap" bgcolor="#f6f6f6">
    <tr>
        <td></td>
        <td class="container" bgcolor="#FFFFFF">

            <!-- content -->
            <div class="content">
            <table>
                <tr>
                    <td>
                        <p>Hi there,</p>
                        <p>Sometimes all you want is to send a simple HTML email with a basic design.</p>
                        <h1>Really simple HTML email template</h1>
                        <p>This is a really simple email template. Its sole purpose is to get you to click the button below.</p>
                        <h2>How do I use it?</h2>
                        <p>All the information you need is on GitHub.</p>
                        <table>
                            <tr>
                                <td class="padding">
                                    <p><a href="https://github.com/leemunroe/html-email-template" class="btn-primary">View the source and instructions on GitHub</a></p>
                                </td>
                            </tr>
                        </table>
                        <p>Feel free to use, copy, modify this email template as you wish.</p>
                        <p>Thanks, have a lovely day.</p>
                        <p><a href="http://twitter.com/leemunroe">Follow @leemunroe on Twitter</a></p>
                    </td>
                </tr>
            </table>
            </div>
            <!-- /content -->

        </td>
        <td></td>
    </tr>
</table>
<!-- /body -->

<!-- footer -->
<table class="footer-wrap">
    <tr>
        <td></td>
        <td class="container">

            <!-- content -->
            <div class="content">
                <table>
                    <tr>
                        <td align="center">
                            <p>Don't like these annoying emails? <a href="#"><unsubscribe>Unsubscribe</unsubscribe></a>.
                            </p>
                        </td>
                    </tr>
                </table>
            </div>
            <!-- /content -->

        </td>
        <td></td>
    </tr>
</table>
<!-- /footer -->

</body>
</html>

<?php
$body = ob_get_clean();

echo preMail($body);

 

0 comments
Add a comment

Copyright © 2024 DC Blog - All rights reserved.