2

I am trying to build a basic templating engine. Like the template engines already available as open source, I am using search and replace techniques.

However, as the search and replace have to be hardcoded, it is not so much flexible. What I mean to say is, as an example, I am using something like this

$templateMarkup = '<div class="title">{_TITLE_}</div>';
$renderedMarkup = str_replace("{_TITLE_}",$title,$templateMarkup);
echo $renderedMarkup;

As you can see, it is hardcoded. So I have to purposely know all the placeholders to accomplish a successful render.

I am a little bit weak in regular expression. But I know, if I can develop a regex, which can match all the text starting with {_ and ending _} and get the value in between them, I just might be able to create a flexible templating engine.

I need help with the regular expression.

If I am completely going the wrong way to accomplish, please do warn me.

For those who think I am reinventing the wheel. Here is my explanation

Templating engines, that are already available are quite unnecessarily complex. 
My requirements are simple and so I am builidng my own simple engine. 

Starx
  • 77,474
  • 47
  • 185
  • 261
  • Sure you need to write your own engine? Nothing out there which suits your needs? I mean why reinvent the wheel? – pintxo Apr 28 '11 at 07:23
  • I was thinking, I needed to put something about that, before I post it. But the reason is, templating engines out there are quite un necessarily complex. My requirements are simple and so I am builidng my own simple engine. – Starx Apr 28 '11 at 07:26
  • Could you name a specific way in which an existing template engine would not be fit for your purposes? It can do more, but that does not mean it is "too complex" for you. You can just use it in a basic way, can't you? – Nanne Apr 28 '11 at 07:36
  • @Nanne, I definitely could, but why not do simple operations using simple code. I dont suppose it would be wise, if I upload entire zend framework, onto my server and only use it for db connections. Although, it can be done, can't it Nanne? – Starx Apr 28 '11 at 07:45
  • 2
    You could, and maybe you should. In this case i'd say that if you want to use a template engine, go with *smarty* or something. If you want something simpler for any reason, just use PHP (as it is a template engine itself) in your html. Why do a complicated replace if you could just use the `$var` system of PHP? – Nanne Apr 28 '11 at 07:48
  • @Nanne, I appreciate your suggestion. Thanks ! – Starx Apr 28 '11 at 08:08

3 Answers3

2

Unless you're trying to restrict down what people can do within a template and want to control what markup they can put in it I'd recommend PHP as a pretty kick-ass templating language!

If you want to stick with your solution you could do something like this to manage replacements.

$template = "foo={_FOO_},bar={_BAR_},title={_TITLE_}\n";

$replacements = array(
    'title' => 'This is the title',
    'foo' => 'Footastic!',
    'bar' => 'Barbaric!'
);

function map($a) { return '{_'. strtoupper($a) .'_}';}

$keys = array_map("map", array_keys($replacements));
$rendered = str_replace($keys, array_values($replacements), $template);

echo $rendered;
Starx
  • 77,474
  • 47
  • 185
  • 261
James C
  • 14,047
  • 1
  • 34
  • 43
2

The regular expression you're looking for is {_(\w+)_}, if your template tags are only ever a single word. You'd use it a bit like this:

<?php

$replacements = array(
    "firstname" => "John",
    "lastname" => "Smith"
);

$template_markup = "Hello {_firstname_} {_lastname_}";
if(preg_match_all('/{_(\w+)_}/', $template_markup, $matches)) {
    foreach($matches[1] as $m) {
        $template_markup = str_replace("{_".$m."_}", $replacements[$m], $template_markup);
    }
}
echo $template_markup;

?>

You'll see the preg_match_all has forward slashes surrounding the regular expression, these are delimiters.

Update: If you want to expand the regular expression beyond single words, then be careful when using . to match any character. It's better to use something like this to specify that you want to include other characters: {_([\w-_]+)_}. The [\w-_] means it will match either alphanumeric characters, hyphens or underscores.

(Perhaps someone can explain why using . might be a bad idea? I'm not 100% sure).

Sam Starling
  • 5,298
  • 3
  • 35
  • 52
  • @Sam, I am supposing this would be a typo. Contained two errors. I have fixed your answer. Thanks for the solutions (+1). I can create my workaround using this. However, I will still wait, before I accept this as answer :). – Starx Apr 28 '11 at 07:49
  • @sam, how to match anything inside `{_` and `_}`? – Starx Apr 28 '11 at 07:52
  • You're right, it needed echoing as well, but in PHP when using double quotes, `"{_$m_}"` and `"{_".$m."_}"` result in exactly the same thing. Give it a go! – Sam Starling Apr 28 '11 at 07:53
  • Well, using double quotes, "{_$m_}", gave Unknown variable `$m_` error. – Starx Apr 28 '11 at 07:54
  • You're right! Good spot. I've updated my answer for your other question. – Sam Starling Apr 28 '11 at 07:56
  • regular expression are slow when you can avoid it. – Micromega Apr 28 '11 at 07:59
  • @Sam, I know you said to be clearful when using `.`, I tried this `/{_(.*)_}/` and its not matching properly. Its matching whole `firstname_} {_lastname` instead of the pieces. – Starx Apr 28 '11 at 08:05
  • @epitaph, I asked for an regular expression solution, you don't need to downvote this answer. I was using `str_replace` before, but since it is not fulfilling my requirement, I have no options but to switch. – Starx Apr 28 '11 at 08:07
  • Calling `str_replace()` multiple times isn't very efficient. Would recommend using it in array/array mode instead. – James C Apr 28 '11 at 08:10
  • @Sam, May be you forgot, but I would really appreciate if you could help me match all characters. Please see my previous comment referring to you. – Starx Apr 28 '11 at 09:47
0

I have combined the solutions provided by both Sam and James to create a new solution.

  • Thanks to Sam for the regex part
  • Thanks to James for the array mode str_replace() part.

Here is the solution:

$replacements = array(
    "firstname" => "John",
    "lastname" => "Smith"
);

function getVal($val) {
    global $replacements;
    return $replacements[$val];
}

$template_markup = "Hello {_firstname_} {_lastname_}";
preg_match_all('/{_(\w+)_}/', $template_markup, $matches);
$rendered = str_replace($matches[0], array_map("getVal",array_values($matches[1])), $template_markup);
echo $rendered;
Starx
  • 77,474
  • 47
  • 185
  • 261
  • Now, if someone would tell me how to match all the characters inside the brackets. I tried using `/{_(.*)_}/` but its not matching correctly. – Starx Apr 28 '11 at 10:25
  • You need to exclude _ and {}, should be possible with [^_{}]* instead of .* – pintxo Apr 28 '11 at 20:33