Simple PHP Template Parsing

I want to create a simple PHP Class for parsing basic HTML email templates in PHP. Very basic... Pass a PHP array into a function which has a variable containing the Email template HTML with placeholders {{var_name}}. The PHP Array's Key will be the variable name in the template and it;s Value will be the desired output when the email HTML is sent as an email.

I thought it might be useful for me to create a simple PHP Class that could do this same thing and speed things up by being flexible.

So here is some basic example HTML for the Email body.... Variables that will need to be replaced in the template with values from PHP variables are wrapped with {{var_name}}

<html>
<body>
<h1>Account Details</h1>
<p>Thank you for registering on our site, your account details are as follows:<br>
Username: {{username}}<br>
Password: {{password}} </p>
</body>
</html> 

In the above example there are 2 variables that need to be populated. {{username}} and {{password}}

I would like to be able to simply pass my Class function a PHP Array where the Array key is the variable name and the value is the value that would be populated in my email template.

So something like this would be passed into my method/function that parses the email template....

$emailValues = array(
    'username' => 'My username value here',
    'password' => 'My password value here'
);

$emailHtml = new EmailParser($emailValues);


echo $emailHtml;  

Would look like this...

<html>
<body>
<h1>Account Details</h1>
<p>Thank you for registering on our site, your account details are as follows:<br>
Username: My username value here<br>
Password: My password value here </p>
</body>
</html> 

I am curious how I could best achieve this? My main question would be how to pass in the PHP Array and have it map out to a variable name to do the replacements. The PHP Array Key would be the Variable name in the template.

It should just be a case of looping through the values and using str_replace on them.

Here's an example:

<?php
$emailValues = array(
    'username' => 'My username value here',
    'password' => 'My password value here'
);

$emailHtml = new EmailParser($emailValues);
echo $emailHtml->output();

class EmailParser {

    protected $_openingTag = '{{';
    protected $_closingTag = '}}';
    protected $_emailValues;
    protected $_emailHtml = <<<HTML
<html>
<body>
<h1>Account Details</h1>
<p>Thank you for registering on our site, your account details are as follows:<br>
Username: {{username}}<br>
Password: {{password}} </p>
</body>
</html> 
HTML;

    public function __construct($emailValues) {
        $this->_emailValues = $emailValues; 
    }

    public function output() {
        $html = $this->_emailHtml;
        foreach ($this->_emailValues as $key => $value) {
            $html = str_replace($this->_openingTag . $key . $this->_closingTag, $value, $html);
        }
        return $html;
    }
}

This article is for people looking to build a very simple template parsing system in PHP. Once you understand how a template system works feel free to take the ideas presented here and mold the system to your specifics. Note this article assumes a fair amount of ability with PHP and objects in PHP About …

these two functions should help :

function template($string,$hash) {
    foreach ( $hash as $ind=>$val ) {
        $string = str_replace('{{'.$ind.'}}',$val,$string);
    }   
    $string = preg_replace('/\{\{(.*?)\}\}/is','',$string);
    return $string;
}

function template_file($file,$hash) {
    $string = file_get_contents($file);
    if ($string) {
        $string = template($string,$hash);
    }
    return $string;
}

To keep this nice and simple, simple open up class.template.inc.php and create a public property called $entries. This will store the entries for parsing later.

Non-recursive solution:

<?php
    Class ParseTemplate
    {
        public function Email( $sTemplateName, $aPlaceholders, $aData )
        {
            $sReplacedHtml = '';
            try
            {
                if( !empty( $sTemplateName ) && !empty( $aPlaceholders ) && !empty( $aData ) )
                {
                    $iCountPlaceholders = count( $aPlaceholders );
                    $iCountData         = count( $aData );
                    if( $iCountData !== $iCountPlaceholders )
                    {
                        throw new Exception( 'Placeholders and data don\'t match' );
                    }
                    if( file_exists( $sTemplateName ) )
                    {
                        $sHtml = file_get_contents( $sTemplateName );
                        for( $i = 0; $i < $iCountData; ++$i )
                        {
                            $sHtml = str_ireplace( $aPlaceholders[ $i ], $aData[ $i ], $sHtml );
                        }
                        $sReplacedHtml = $sHtml;

                    }
                }           
            }
            catch( Exception $oException )
            {
                // Log if desired.
            }
            return $sReplacedHtml;
        }
    }

    $aPlaceholders = array( '{{username}}', '{{password}}' );
    $aData         = array( 'Why so pro', 'dontchangeme' );

    $oParser = new ParseTemplate;
    $sReplacedHtml = $oParser->Email( 'intemplate.html', $aPlaceholders, $aData );
    file_put_contents( 'outtemplate.html', $sReplacedHtml );
?>

The nesting of blocks is permitted, but be careful while parsing. You have to set and parse the deepest inner block first and then set and parse from inner to outer. In IT the whole template file itself is nested in a meta block called "__global__". Most block-related functions use this block name as default.

I already use a function for this kind of purpose.

// file class.bo3.php, more about this class here: https://github.com/One-Shift/BO3-BOzon/blob/master/backoffice/class/class.bo3.php
class bo3 {
  public static function c2r ($args = [], $target) {
    foreach ($args as $index => $value) { $target = str_replace("{c2r-$index}", $value, $target); }
    return $target;
  }
}

and to run it

$emailValues = ['
  username' => 'My username value here', // in the template the code need to be "{c2r-username}"
  'password' => 'My password value here' // in the template the code need to be "{c2r-password}"
];

$template = file_get_contents("path/to/html/file")

$finishedTemplate = bo3::c2r($emailValues, $template);

If you want to change the code format from {c2r-$index} to {{ $index }}, just change it in the class function c2r().

A simple Lexer. Below are some of the few recipes from the initial chapter of the book, on designing a LL(1) lexer and parser. The language we want to recognize is an extremely simple one – a list of names like [a,b,c,d,e]. A LL(1) lexer for the same is shown below. test_lexer.php

This PHP script notifies you when a webpage changes. The script can send an email, or tweet, or text any address. PHP Simple HTML DOM Parser is a dream utility for developers that work with both PHP and the DOM because developers can easily find DOM elements using PHP.

Filter Description [attribute] Matches elements that have the specified attribute. [!attribute] Matches elements that don't have the specified attribute. [attribute=value] Matches elements that have the specified attribute with a certain value.

The SimpleXML Parser. SimpleXML is a tree-based parser. SimpleXML provides an easy way of getting an element's name, attributes and textual content if you know the XML document's structure or layout. SimpleXML turns an XML document into a data structure you can iterate through like a collection of arrays and objects.

Comments
  • Any template system like Twig combined with any mailing class like SwiftMailer will do the trick.
  • @ceejayoz I am hoping to avoid all that and do something super lightweight and simple. I dont need a template library or mailler, just need to know how to make an array convert to the variable names for the replacements. The whole Class should be 1 file and not even that large for what I am doing! Thanks though
  • Excellent! I think this is exactly what I was looking to do but wasn't exactly sure how to approach it from a flexible point of view! Thank you
  • This gives me the core functionality to build on like passing in a path to the email templates, etc... great work!
  • There could be issues here, for example you'll need to check that each of the expected values is being passed, but you get the general idea!