16

Update- Thanks for all the responses. This Q is getting kind of messy, so I started a sequel if anyone's interested.


I was throwing together a quick script for a friend and stumbled across a really simple way of doing templating in PHP.

Basically, the idea is to parse the html document as a heredoc string, so variables inside of it will be expanded by PHP.

A passthrough function allows for expression evaluation and function and static method calls within the string:

function passthrough($s){return $s;}
$_="passthrough";

The code to parse the document inside a heredoc string is ridiculously simple:

$t=file_get_contents('my_template.html');
eval("\$r=<<<_END_OF_FILE_\n$t\_END_OF_FILE_;\n");
echo $r;

The only problem is, it uses eval.

Questions

  • Can anyone think of a way to do this sort of templating without using eval, but without adding a parser or a ton of regex madness?

  • Any suggestions for escaping stray dollar signs that don't belong to PHP variables without writing a full-on parser? Does the stray dollar sign problem render this approach not viable for 'serious' use?


Here's some sample templated HTML code.

<script>var _lang = {$_(json_encode($lang))};</script>
<script src='/blah.js'></script>
<link href='/blah.css' type='text/css' rel='stylesheet'>

<form class="inquiry" method="post" action="process.php" onsubmit="return validate(this)">

  <div class="filter">
    <h2> 
      {$lang['T_FILTER_TITLE']}
    </h2>
    <a href='#{$lang['T_FILTER_ALL']}' onclick='applyFilter();'>
      {$lang['T_FILTER_ALL']}
    </a>
    {$filter_html}
  </div>

  <table class="inventory" id="inventory_table">
    {$table_rows}
    <tr class="static"><th colspan="{$_($cols+1)}">
      {$lang['T_FORM_HELP']}
    </th></tr>
    {$form_fields}
    <tr class="static">
      <td id="validation" class="send" colspan="{$cols}">&nbsp;</td>
      <td colspan="1" class="send"><input type="submit" value="{$lang['T_SEND']}" /></td>
    </tr>
  </table>

</form>

Why use templating?


There's been some discussion of whether creating a templating layer is necessary in PHP, which, admittedly, is already pretty good at templating.

Some quick reasons templating is useful:

  • You can control it

    If you preprocess the file before it goes to the interpreter, you have more control over it. You can inject stuff, lock down permissions, scrape for malicious php / javascript, cache it, run it through an xsl template, whatever.

  • Good MVC design

    Templating promotes separation of view from model and controller.

    When jumping in and out of <?php ?> tags in your view, it's easy to get lazy and do some database queries or perform some other server action. Using a method like the above, only one statement may be used per 'block' (no semicolons), so it's much more difficult to get caught in that trap. <?= ... ?> have pretty much the same benefit, but...

  • Short tags aren't always enabled

    ...and we want our app to run on various configurations.

When I initially hack a concept together it starts out as one php file. But before it grows I'm not happy unless all php files have only one <?php at the beginning, and one ?> at the end, and preferably all are classes except stuff like the controller, settings, image server, etc.

I don't want much PHP in my views at all, because designers become confused when dreamweaver or whatever poops the bed when it sees something like this:

<a href="<?php $img="$img_server/$row['pic'].png"; echo $img; ?>">
  <img src="<?php echo $img; ?>" /></a>

This is hard enough for a programmer to look at. The average graphic designer won't go anywhere near it. Something like this is a much easier to cope with:

<a href="{$img}"><img src="{$img}" /></a>

The programmer kept his nasty code out of the html, and now the designer can work his design magic. Yay!

Quick update

Taking everyone's advice into consideration, I think preprocessing the files is the way to go, and the intermediate files should be as close as normal "php templating" as possible, with the templates being syntactic sugar. Eval still in place for now while I play with it. The heredoc thing has sort of changed its role. I'll write more later and try to respond to some of the answers, but for now...

<?php



class HereTemplate {

  static $loops;

  public function __construct () {
    $loops=array();
  }

  public function passthrough ($v) { return $v; }

  public function parse_markup ($markup, $no_escape=null, $vars=array()) {
    extract($vars);
    $eot='_EOT_'.rand(1,999999).'_EOT_';
    $do='passthrough';
    if (!$no_escape) $markup=preg_replace(
      array(
        '#{?{each.*(\$\w*).*(\$\w*).*(\$\w*).*}}?#', 
        '#{?{each.*(\$\w*).*(\$\w*).*}}?#', 
        '#{?{each}}?#',
        '#{{#', '#}}#',
        '#{_#', '#_}#',
        ),
      array(
        "<?php foreach (\\1 as \\2=>\\3) { ?>", 
        "<?php foreach (\\1 as \\2) { ?>", 
        "<?php } ?>",
        "<?php echo <<<$eot\n{\$this->passthrough(", ")}\n$eot\n ?>",
        "<?php ", " ?>",
        ), 
      $markup);
    ob_start(); 
    eval(" ?>$markup<?php ");
    echo $markup;
    return ob_get_clean();
  }

  public function parse_file ($file) {
    // include $file;
    return $this->parse_markup(file_get_contents($file));
  }

}


// test stuff


$ht = new HereTemplate();
echo $ht->parse_file($argv[1]);


?>

...

<html>

{{each $_SERVER $key $value}

<div id="{{$key}}">

{{!print_r($value)}}

</div>

{each}}



</html>
Community
  • 1
  • 1
Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • 3
    [phptemplatinglanguage.com](http://phptemplatinglanguage.com) – alex Oct 14 '10 at 04:29
  • 1
    @alex - having PHP tags opening and closing left and right is exactly what I want to avoid... – Dagg Nabbit Oct 14 '10 at 04:39
  • 1
    @no: What are you reasons for avoiding it, if you don't mind? To look better? Easier for designers? Cheers. – alex Oct 14 '10 at 05:08
  • @alex: It's less to write and easier to understand ;) `` simply isn't the same as `{{var|e}}` (TWIG). – NikiC Oct 17 '10 at 18:30
  • +1 and a star for you — I support using PHP itself as a templating language myself but I'm not like others who go all out to beat up anyone who chooses to implement a custom template syntax (I've seen tons of such people out there on the Web). It's an opportunity to get creative, after all, and I think this question demonstrates exactly that! – BoltClock Oct 18 '10 at 15:39
  • 2
    this question demonstrates that bad design leads to problems. PHP is perfect template system, no need to use anything else. – Your Common Sense Oct 18 '10 at 17:44
  • @nikic: It that becomes an issue, it takes about 1 minute to write the wrapper and now you can do `` or even `` if you're inclined. I'm not really against an extra layer of template language if it helps designers, but is it just me who finds that most template languages eventually breaks down in complicated cases and have to resort to a the real programming language behind (PHP)? – kizzx2 Oct 18 '10 at 17:45
  • @kizzx2 just to let you know. A designer is a guy who knows only Illustrator and Photoshop out of computer programs. They hardly have an idea of HTML and CSS. – Your Common Sense Oct 18 '10 at 17:55
  • @kizzx2: Well, I can only say how my experience with Twig was. Maybe there really *are* cases where you can't do something with Twig, but I have yet to encounter one. Twig (and similar template engines) simply are very powerful. They support (Dynamic) Template Inheritance, Makros and loads of other stuff. It's not just variables and loops anymore. Furthermore, if you really do encounter something that isn't possible with the current feature set, Twig's open source, you can fork and implement yourself ;) – NikiC Oct 18 '10 at 18:29
  • @Col: Aren't you the "think practically"-guy? I really don't see a reason why you shouldn't compile `{ $var }` to ``. It's a small overhead for the developer, but much nicer to look at for the one implementing the design (even if this someone is the developer, it's still nicer!) – NikiC Oct 18 '10 at 18:31
  • @kizzx2 again: Sure, you can write an `e` function. But **still** you have more code to write. A PHP tag inside an attribute of a HTML tag simply is *strange*. – NikiC Oct 18 '10 at 18:34
  • @nikic I am using =$var ?> which looks no less nice. – Your Common Sense Oct 18 '10 at 18:35
  • @Col: Still `= ?>` inside HTML attributes is strange (for me, maybe not for you). Furthermore (as people already have said) short tags may be and are turned off. And even though `= ?>` may be still okey `` definitely is not. – NikiC Oct 18 '10 at 18:38
  • @Col: First of all, let us make this clear: **THIS THREAD IS NOT ABOUT "HOW TO USE PHP AS A TEMPLATE ENGINE WITHOUT ANY FURTHER PROCESSING"!!!** If that was the question, then you were absolutely right that I shouldn't proclaim that you mustn't use PHP as a template engine. But here **YOU** are the one trying to say something that is **AGAINST** the topic of this question. So, to be more precise: If you have a question about PDO, do you comment that it may be turned off? If there is a question about mod_rewrite, do you suggest not using it, because it may be unavailable? NO! But here you do... – NikiC Oct 18 '10 at 19:05
  • @Col: I am starting to get really pissed by your arrogance. I would never say that you *mustn't* use PHP as a template engine. I did that myself until I realized that I were better of with Twig. I only advise not to do so. You on the other hand say that using a template engine deserves death penalty or something like that. – NikiC Oct 18 '10 at 19:08
  • @nikic well it was i bit rude, i have to admit - I'm just got tired of that nonsense. But the point is still the same. possibility of turn off is not a reason to mention – Your Common Sense Oct 18 '10 at 19:26
  • @Col: Some goes for me. My last two comments were quite aggressive ^^ You are obviously right, short tags are enabled on most hosters. I simply use a template engine because I think the syntax's nicer ;) But if you are okay with PHP, then use it ;) – NikiC Oct 18 '10 at 20:19
  • Hey `@no`. You can't limit yourself to just output variables. There will be logical blocks anyway. So, your care of "designer" in vain anyway. Your illusions are too far away from real world needs. Try to use a template with some real life code. With loops, conditions etc. And, **of course, with no HTML code in the business logic part** – Your Common Sense Oct 19 '10 at 06:06
  • Col. Shrapnel: I already have. It's a custom tag framework for PHP. We've used it on plenty of projects... but I'm trying to go back to basics with this thing as much as possible. It's progressing in an interesting direction; I'll post more later – Dagg Nabbit Oct 19 '10 at 06:45
  • If you have HTML in your business logic, anything you do will be in vain. It has nothing to do with templates. To find a proper solution, first of all you have answer yourself a question: why do you need to use templates? – Your Common Sense Oct 19 '10 at 07:21
  • Hm, is there any nice way you could allow logic in a heredoc-template? You found a way for executing functions. But I see no way how you could use control structures, even if you use lambda magic and stuff. – NikiC Oct 19 '10 at 10:32
  • @Col. Shrapnel: yes, that's the whole point, keeping the HTML out of the business logic and the business logic out of the view. Is there something you want to say, or are you just going to keep repeating yourself? – Dagg Nabbit Oct 19 '10 at 18:55
  • @nikic: not really; the entire string is parsed at once so any flow control, etc would have to be done after the fact. I was able to get passable loop and conditional structures into the heredoc, but only with string replacement tricks and some output buffering crazyness. In the end, I thought it would be best if the intermediate code was as close to "php as a templating language" as possible. – Dagg Nabbit Oct 19 '10 at 18:58
  • @no: I have thought about stuff like `$__{function() use($posts) { foreach ($posts as $post) { /* ... */ }}}`. But that's crazy complicated and unintelligible. – NikiC Oct 19 '10 at 19:03
  • Nope, no more trying to make it sensible. I would prefer to watch your weak attempts to invent a caricature of Smarty. Good luck :) – Your Common Sense Oct 19 '10 at 19:24
  • 1
    @Col. Shrapnel: Yeah, you're not doing too hot at making sense... I'd prefer if you just watch, unless you're able to communicate your point any better than you have been. – Dagg Nabbit Oct 19 '10 at 20:12
  • @nikic: Yeah, that would only work with PHP 5.3, and like you said it's hacky... I think lambda functions will still have their place if I pursue this idea. Will update later. – Dagg Nabbit Oct 19 '10 at 20:31
  • Sorry pal, it's not my fault. You are too deep in your dreams to listen. No need to be a genius to understand that you are just re-inventing Smarty. An ugly homegrown parody of it. So I told. But you don't read. That's the problem. – Your Common Sense Oct 19 '10 at 20:47
  • @no: Why do you use heredoc + passthrough for a simple echo? – NikiC Oct 19 '10 at 20:52
  • @nikic: If I used double quotes for the echo, double quotes inside the expression would break the template... you wouldn't be able to do something like `{{"$path/$file.png"}}`. Passthrough is used so any expression can be evaluated, not just things that start with `$`. – Dagg Nabbit Oct 20 '10 at 00:12
  • @nikic: Ugh, just realized how silly that was. Guess I was still in heredoc mode. – Dagg Nabbit Oct 20 '10 at 00:19

8 Answers8

28

PHP was itself originally intended as a templating language (ie a simple method of allowing you to embed code inside HTML).

As you see from your own examples, it got too complicated to justify being used in this way most of the time, so good practice moved away from that to using it more as a traditional language, and only breaking out of the <?php ?> tags as little as possible.

The trouble was that people still wanted a templating language, so platforms like Smarty were invented. But if you look at them now, Smarty supports stuff like its own variables and foreach loops... and before long, Smarty templates start to have the same issues as PHP templates used to have; you may as well just have used native PHP in the first place.

What I'm trying to say here is that the ideals of a simple templating language aren't actually that easy to get right. It's virtually impossible to make it both simple enough not to scare off the designers and at the same time give it enough flexibility to actually do what you need it to do.

mensi
  • 9,580
  • 2
  • 34
  • 43
Spudley
  • 166,037
  • 39
  • 233
  • 307
  • 7
    +1 for dissuading people from writing a template language for a template language! – GWW Oct 21 '10 at 18:54
  • 1
    I think that `foreach` loops in a template are nothing bad. Looping and conditionals are required to build templates. – NikiC Oct 23 '10 at 11:17
  • 1
    @nikic - I have no problem with foreach or any other programming aspect in a templating language; you're right, they are necessary (albeit often overused). My point was to answer point in the question about PHP tags being too complex for the designer who is scared off by programming constructs, by showing that all template languages suffer from this, if they are going to be powerful enough to actually be useful. The designer is just going to have to get used to having programming code in his templates, and in that case he may just as well stick with plain PHP. – Spudley Oct 23 '10 at 12:54
12

If you don't wont to use a big template engines like Twig (which I sincerely recommend) you can still get good results with little code.

The basic idea that all the template engines share is to compile a template with friendly, easy-to-understand syntax to fast and cacheable PHP code. Normally they would accomplish this by parsing your source code and then compiling it. But even if you don't want to use something that complicated you can achieve good results using regular expressions.

So, basic idea:

function renderTemplate($templateName, $templateVars) {
    $templateLocation = 'tpl/'      . $templateName . '.php';
    $cacheLocation    = 'tplCache/' . $templateName . '.php';
    if (!file_exists($cacheLocation) || filemtime($cacheLocation) < filemtime($templateLocation)) {
        // compile template and save to cache location
    }

    // extract template variables ($templateVars['a'] => $a)
    extract($templateVars);

    // run template
    include 'tplCache/' . $templateName . '.php';
}

So basically we first compile the template and then execute it. Compilation is only done if either the cached template doesn't yet exist or there is a newer version of the template than the one in the cache.

So, let's talk about compiling. We will define two syntaxes: For output and for control structures. Output is always escaped by default. If you don't want to escape it you must mark it as "safe". This gives additional security. So, here an example of our syntax:

{% foreach ($posts as $post): }
    <h1>{ $post->name }</h1>
    <p>{ $post->body }</p>
    {!! $post->link }
{% endforeach; }

So, you use { something } to escape and echo something. You use {!! something} to directly echo something, without escaping it. And you use {% command } to execute some bit of PHP code without echoing it (for example for control structures).

So, here's the compilation code for that:

$code = file_get_contents($templateLocation);

$code = preg_replace('~\{\s*(.+?)\s*\}~', '<?php echo htmlspecialchars($1, ENT_QUOTES) ?>', $code);
$code = preg_replace('~\{!!\s*(.+?)\s*\}~', '<?php echo $1 ?>', $code);
$code = preg_replace('~\{%\s*(.+?)\s*\}~', '<?php $1 ?>', $code);

file_put_contents($cacheLocation, $code);

And that's it. You though have to note, that this is more error prone than a real template engine. But it will work for most cases. Furthermore note that this allows the writer of the template to execute arbitrary code. That's both a pro and a con.

So, here's the whole code:

function renderTemplate($templateName, $templateVars) {
    $templateLocation = 'tpl/'      . $templateName . '.php';
    $cacheLocation    = 'tplCache/' . $templateName . '.php';
    if (!file_exists($cacheLocation) || filemtime($cacheLocation) < filemtime($templateLocation)) {
        $code = file_get_contents($templateLocation);

        $code = preg_replace('~\{\s*(.+?)\s*\}~', '<?php echo htmlspecialchars($1, ENT_QUOTES) ?>', $code);
        $code = preg_replace('~\{!!\s*(.+?)\s*\}~', '<?php echo $1 ?>', $code);
        $code = preg_replace('~\{%\s*(.+?)\s*\}~', '<?php $1 ?>', $code);

        file_put_contents($cacheLocation, $code);
    }

    // extract template variables ($templateVars['a'] => $a)
    extract($templateVars, EXTR_SKIP);

    // run template
    include 'tplCache/' . $templateName . '.php';
}

I haven't tested the above code ;) It's only the basic idea.

NikiC
  • 100,734
  • 37
  • 191
  • 225
  • This would be my approach too. It has a couple benefits: 1) It avoids eval(), 2) It avoids the overhead of parsing the template on each page load, and 3) Op code cachers shouldn't have any problem caching the final php code. – mellowsoon Oct 18 '10 at 17:12
  • a template language cannot be limited to just output variables. There always will be a logical blocks. Why to answer this question if you never used to use a template yourself? – Your Common Sense Oct 19 '10 at 06:16
  • 1
    @Col: Well, this template language *does* support logical blocks. As you can see in my syntax example you could for example use a `foreach` loop. Same goes for `if` or any other language construct PHP supports. And why do you say that I never used a template myself? Imho Twig is considered a template engine and thus I consider myself a user of templates. PS: Have you downvoted this answer Col? – NikiC Oct 19 '10 at 10:29
  • didn't notice it at first. So, you have just change `` to `{%` and back. well it's even more ugly than I thought at first. I'd downvote this frankenstein twice if I could. – Your Common Sense Oct 19 '10 at 10:37
  • 1
    @Col: That's exactly what you would do. Only difference is, that `{%` would be short_tags-independent (okay, I already know your opinion on that). Furthermore nobody prevents you from using `` or `` to ``. I'll think about it, thanks for feedback. – NikiC Oct 19 '10 at 10:45
11

I'm gonna do something silly and suggest something that requires no templating engine at all and requires only at most 5 characters more per variable/call than what you have there - replace {$foo} with <?=$foo?> and then you can use include for all your templating needs

If all you need is variable replacement though this is a templating function i actually use:

function fillTemplate($tplName,$tplVars){
  $tpl=file_get_contents("tplDir/".$tplName);
  foreach($tplVars as $k=>$v){
    $tpl = preg_replace('/{'.preg_quote($k).'}/',$v,$tpl);
  }
  return $tpl;
}

if you want to be able to call functions or have loops, there is basicly no way around calling eval short of pre-processing.

tobyodavies
  • 27,347
  • 5
  • 42
  • 57
  • I considered something like replacing `{{` with ``, but it could get dicey. Maybe a different delimiter character would be better... – Dagg Nabbit Oct 14 '10 at 04:41
  • Ah, I just realized what you're getting at. Short tags are disabled on most of the servers I deploy to. Also it looks terrible inside of a tag attribute, and breaks syntax highlighting on many editors. – Dagg Nabbit Oct 14 '10 at 05:01
  • 1
    If you want to avoid eval you could preprocess the templates to replace `{...}`` with `` but that makes it somewhat more of a pain and somewhat less clear in that it requires 'compilation' of sorts – tobyodavies Oct 14 '10 at 05:10
  • Preprocessing is the best I think. eval will kill the performances. – Savageman Oct 16 '10 at 23:33
  • @savageman: PHP is fully interpreted anyway, it doesn't (by default) store its byte code between invocations, it has to parse, byte-compile and evaluate each page on every hit... PHP itself is basicly calling `eval_which_handles_open_and_close_tags(file_get_contents($url))` on every page load, so it shouldn't significantly change performance – tobyodavies Oct 17 '10 at 00:29
  • Except it's really dumb to not use an op-code cache. And if you use one (which is recommended), the eval'd won't be cached. – Savageman Oct 17 '10 at 13:48
  • 1
    Do you want `preg_quote()` instead of `preg_escape()` ? – alex Oct 18 '10 at 01:04
  • Yes, i wrote that from memory... changed now – tobyodavies Oct 18 '10 at 02:02
  • 1
    @tobyodavies: I'm not sure, but I think we users have just as much access to `eval_which_handles_open_and_close_tags`, because I vaguely remember that `eval('?>' . $phpfile);` works. – Bart van Heukelom Oct 18 '10 at 15:30
  • The worst part: a template language cannot be limited to just output variables. There always will be a logical blocks. Why to answer this question if you never used to use a template yourself? – Your Common Sense Oct 19 '10 at 06:12
  • 1
    @Col. My point was that if you want to avoid the performance problems associated with eval, you can either use php short tags (or preprocess to php tags, see earlier comment), limit yourself to just variable replacement or use eval, or worse, your own (or someone else's) interpreter for a mini language please read the answer before commenting. – tobyodavies Oct 19 '10 at 23:06
3

There is no ultimate solution. Each has pros and cons. But you already concluded what you want. And it seems a very sensible direction. So I suggest you just find the most efficient way to achieve it.

You basically only need to enclose your documents in some heredoc syntactic sugar. At the start of each file:

<?=<<<EOF

And at the end of each template file:

EOF;
?>

Achievement award. But obviously this confuses most syntax highlighting engines. I could fix my text editor, it's open source. But Dreamweaver is a different thing. So the only useful option is to use a small pre-compiler script that can convert between templates with raw $varnames-HTML and Heredoc-enclosed Templates. It's a very basic regex and file rewriting approach:

#!/usr/bin/php -Cq
<?php
foreach (glob("*.tpl") as $fn) {
    $file = file_get_contents($fn);
    if (preg_match("/<\?.+<<</m")) {  // remove
        $file = preg_replace("/<\?(=|php\s+print)\s*<<<\s*EOF\s*|\s+EOF;\s*\?>\s*/m", "", $file);
    }
    else {   // add heredoc wrapper
        $file = "<?php print <<<EOF\n" . trim($file) . "\nEOF;\n?>";
    }
    file_put_contents($fn, $file);
}
?>

This is a given - somewhere you will need templates with a slight amount of if-else logic. For coherent handling you should therefore have all templates behave as proper PHP without special eval/regex handling wrapper. This allows you to easily switch between heredoc templates, but also have a few with normal <?php print output. Mix and match as appropriate, and the designers can work on the majority of files but avoid the few complex cases. For exampe for my templates I'm often using just:

include(template("index"));   // works for heredoc & normal php templ

No extra handler, and works for both common template types (raw php and smartyish html files). The only downside is the occasional use of said converter script.

I'd also add a extract(array_map("htmlspecialchars",get_defined_vars())); on top of each template for security.

Anyway, your passthrough method is exceptionally clever I have to say. I'd call the heredoc alias $php however, so $_ is still available for gettext.

<a href="calc.html">{$php(1+5+7*3)}</a> is more readable than Smarty

I think I'm going to adopt this trick myself.

<div>{$php(include(template($ifelse ? "if.tpl" : "else.tpl")))}</div>

Is stretching it a bit, but it seems after all possible to have simple logic in heredoc templates. Might lead to template-fileritis, yet helps enforcing a most simple template logic.

Offtopic: If the three <<<heredoc&EOF; syntax lines still appear too dirty, then the best no-eval option is using a regular expression based parser. I do not agree with the common myth that that's slower than native PHP. In fact I believe the PHP tokenizer and parser lag behind PCRE. Especially if it's solely about interpolating variables. It's just that the latter isn't APC/Zend-cached, you'd be on your own there.

mario
  • 144,265
  • 20
  • 237
  • 291
2

Personally, I wouldn't touch with a stick any templating system where forgetting to escape a variable creates a remote code execution vulnerability.

Tgr
  • 27,442
  • 12
  • 81
  • 118
0

Dead-simple templating using a function:

<?php

function template($color) {
        $template = <<< ENDTEMPLATE
The colors I like are {$color} and purple.
ENDTEMPLATE;

        return $template . "\n";
}

$color = 'blue';
echo template($color);

$color = 'turquoise';
echo template($color);

This outputs:

The colors I like are blue and purple.
The colors I like are turquoise and purple.

Nothing fancy, but it does work using standard PHP without extensions. Additionally, the use of functions to encapsulate the templates should help with proper MVC separation. Also (and this is what I needed for my coding today) I can save the filled-out template away for output to a file (later on in my program).

Mark Leighton Fisher
  • 5,609
  • 2
  • 18
  • 29
0

This is a minimal implementation of mustache to just substitute variables.

// Example:
//   miniMustache(
//      "{{documentName }} - pag {{ page.current }} / {{ page.total }}",
//      array(
//         'documentName' => 'YourCompany Homepage', 
//         'page' => array('current' => 1, 'total' => 10)
//      )
//    )
//    
// Render: "YourCompany Homepage - pag 1 / 10"

    function miniMustache($tmpl, $vars){
        return preg_replace_callback( '/\{\{([A-z0-9_\.\s]+)\}\}/',
            function ($matches) use ($vars) {
                //Remove white spaces and split by "."
                $var = explode('.',preg_replace('/[\s]/', '', $matches[1]));
                $value = $vars;
                foreach($var as $el){
                    $value = $value[$el];
                }
                return $value;
            }, 
            $tmpl);
    }

In some cases, it is more than enough. In case you need full power: https://github.com/bobthecow/mustache.php

Chris Cinelli
  • 4,679
  • 4
  • 28
  • 40
0

Personally i'm using this template engine: http://articles.sitepoint.com/article/beyond-template-engine/5

I really like it a lot, especially because of it's simplicity. It's kinda similar to your latest incarnation, but IMHO a better approach than using heredoc and putting yet another layer of parsing above the PHP one. No eval() either, but output buffering, and scoped template variables, too. Use like this:

<?php   
require_once('template.php');   

// Create a template object for the outer template and set its variables.     
$tpl = new Template('./templates/');   
$tpl->set('title', 'User List');   

// Create a template object for the inner template and set its variables.
// The fetch_user_list() function simply returns an array of users.
$body = new Template('./templates/');   
$body->set('user_list', fetch_user_list());   

// Set the fetched template of the inner template to the 'body' variable
// in the outer template.
$tpl->set('body', $body->fetch('user_list.tpl.php'));   

// Echo the results.
echo $tpl->fetch('index.tpl.php');   
?>

The outter template would look like this:

<html>
  <head>
    <title><?=$title;?></title>
  </head>
  <body>
    <h2><?=$title;?></h2>
        <?=$body;?>
  </body>
</html>

and the inner one (goes inside the outter template's $body variable) like this:

<table>
   <tr>
       <th>Id</th>
       <th>Name</th>
       <th>Email</th>
       <th>Banned</th>
   </tr>
<? foreach($user_list as $user): ?>
   <tr>
       <td align="center"><?=$user['id'];?></td>
       <td><?=$user['name'];?></td>
       <td><a href="mailto:<?=$user['email'];?>"><?=$user['email'];?></a></td>
       <td align="center"><?=($user['banned'] ? 'X' : '&nbsp;');?></td>
   </tr>
<? endforeach; ?>
</table>

If you don't like / can't use short-tags then replace them with echos. That's as close to dirt-simple as you can get, while still having all the features you'll need IMHO.

DanMan
  • 11,323
  • 4
  • 40
  • 61
  • 1
    Well, an unescaped output of a field that allows emails, for example, is probably ripe for script insertion XSS, since there's so much that can be allowed in an email, and such a variety of ways to insert scripts. So just from there there's a problem, which is why templates like this are mostly useless comparison with template engines. So h($someVar);?> is probably the least verbose you can get and still be effective in pure php (with h() being an escape function wrapping). I'm all in favor of pure php in a templating approach, but people continually overstate the best minimum verbosity. – Kzqai Feb 20 '12 at 02:58
  • Well, this is simplified, of course. Security and input handling is a different topic. The `$email` here would be coming from the model, so it would have already passed sanity checks. – DanMan Feb 20 '12 at 11:38