Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dirt-simple PHP templates... can this work without `eval`?

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>
like image 958
Dagg Nabbit Avatar asked Oct 14 '10 04:10

Dagg Nabbit


1 Answers

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.

like image 159
Spudley Avatar answered Oct 20 '22 03:10

Spudley