iEFdev

Code, Computers & Random Junk

A Nano Template Engine

Just a very small tiny (not so shiny) template engine. Just for the fun if it…

Some (5-6) years ago I found an old post about “The One Line Template Engine!”. The code is ridiculous small:

preg_replace("/\{([^\{]{1,100}?)\}/e","$$1",file_get_contents("template.tpl"));

And that’s about it. smile

Not that I’ve use it that much, nor frequently, but it’s been great to have for very tiny things, like when you just make something very small, simple, like a single page without any advanced logic behind - more or less static, but you like to make use of some PHP functionality - and of course, still like to separate your files/code (PHP vs HTML). And also… as a small enginge when you send out an email from a form for example, and like to use misc (simple) templates.

Now, that piece of code will not work since it’s using /e in the “Regexp”. It’s been deprecated for some time now. So, not for the great usefulness, but for the funsies, let’s bring it to life again.

The fix

So, to make it work again we’ll have to use the function: preg_replace_callback() instead. The code is pretty much the same, but instead of $$1 we’ll put a (callback) function in place.

preg_replace_callback('/\{([^\{]{3,100}?)\}/', $e, file_get_contents($tpl_file));

The $e variable (declared before) will be:

$e = function($e) use ($tpl_vars) { return $tpl_vars[$e[1]]; };

The use ($tpl_vars) part… That is the array with the template variables… Before you could make separate variables, or using an array + using extract.

Together it will look like (including a ternary to check…):

$e = function($e) use ($tpl_vars) { return isset($tpl_vars[$e[1]]) ? $tpl_vars[$e[1]]: ''; };
$out = preg_replace_callback('/\{([^\{]{3,100}?)\}/', $e, file_get_contents($tpl_file));

So, it’s now a “2 Line Template Enginge”. smirk

A very tiny (still not so shiny) example would look something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// Template and vars
$tpl_file = 'templates/index.html';
$tpl_vars = [
    'TITLE'     => 'Titel text',
    'HEADER'    => 'Header text',
    'TEXT'      => 'Lorem Ipsum bla bla bla &hellip;'
];

// Parse
$e = function($e) use ($tpl_vars) { return isset($tpl_vars[$e[1]]) ? $tpl_vars[$e[1]]: ''; };
$out = preg_replace_callback('/\{([^\{]{3,100}?)\}/', $e, file_get_contents($tpl_file));

// Output
echo $out;

And the template:

1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>{TITLE}</title>
<head>
<body>
<h1>{HEADER}</h1>
<p>{TEXT}</p>
</body>
</html>

Or a simple mail template:

1
2
3
4
Welcome {USER}!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A simple hello from {SITE}&hellip;

As a function

Well, the code is old… from 2007, but still usable (no, not really). So, to step up a bit - another approach would be to make a function.

When the original code stopped to work… I didn’t have time to sit with it at the time so I used a foreach()version instead…

1
2
3
4
5
6
7
8
9
10
11
function parse($tpl, array $vars)
{
    $file = file_get_contents($tpl);
    foreach($vars as $name => $value)
    {
        $file = preg_replace("/\{$name\}/", $value, $file);
    }
    return $file;
}
// parse('/path/to/template.html', ['foo' => 'bar', 'baz' => 'foobar']);
$out = parse($tpl_file, $tpl_vars);

…and will of course work using the 2 Line version:

1
2
3
4
5
function parse($tpl, array $vars)
{
    $e = function($e) use ($vars) { return isset($vars[$e[1]]) ? $vars[$e[1]]: ''; };
    return preg_replace_callback('/\{([^\{]{3,100}?)\}/', $e, file_get_contents($tpl));
}

As a Class

Since modern code today is all about Classes, more or less. Let’s make an example using a Class.

Still a very simple, tiny (perhaps a bit more shiny) example - including “namespace” and all.

“The Nano Template Engine”.


File/folder layout:

classes/
    NanoTemplateEngine.php
index.php
templates/
    index.html
    mail/
        welcome.mail

The Class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/**
 * classes/NanoTemplateEngine.php
 */

namespace Core;

class NanoTemplateEngine
{
    public function parse($template, array $variables)
    {
/*
        $file = file_get_contents($template);
        foreach($variables as $name => $value) {
            $file = preg_replace("/\{$name}/", $value, $file);
        }
        return $file;
*/

        $e = function($e) use ($variables) { return isset($variables[$e[1]]) ? $variables[$e[1]]: ''; };
        return preg_replace_callback('/\{([^\{]{3,100}?)\}/', $e, file_get_contents($template));
    }
}

The index file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
/**
 * index.php
 */

require './classes/NanoTemplateEngine.php';

use \Core\NanoTemplateEngine as Msg;

// Template and vars
$tpl_file = 'templates/index.html';
$tpl_vars = [
    'USER' => 'Foo Bar',
    'HOST' => $_SERVER['HTTP_HOST'],
    'SITE' => sprintf('<a href="//%1s">my site</a>', $_SERVER['HTTP_HOST'])
];

$msg = new Msg;

echo $msg->parse($tpl_file, $tpl_vars);

…and the template:

1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Welcome {USER} | {HOST}</title>
<head>
<body>
<h1>Welcome {USER}</h1>
<p>A simple hello from {SITE}&hellip;</p>
</body>
</html>

Download

You can download some examples files here… Nothing fancy or so, just for the example.


You would still like to add some error checking, check if the template exists etc… But being a Class, it is actually (somewhat) useful now, in another way. At least for that simple single page, or when sending mails in a script. No need to load a full Twig system (example) only for that.

Should licese it, right? Here you go: WTFPL


Happy hacking…

/Eric

Comments