Jump to content

Template Engine Design Pattern


genericnumber1

Recommended Posts

I'm wondering if anyone can improve on this idea of template engine I made, it's pretty straightforward but would be interesting in hearing what you all think can be improved. I figured it would just be best to paste it here since it's really not much code at all and couldn't think of a better way of presenting it.

The template, "test.tpl"...
[pre]
<html>
<head>
<title>{PAGE_TITLE}</title>
</head>
<body>
{PAGE_HEADER}
{PAGE_BODY}
{PAGE_FOOTER}
</body>
</html>
[/pre]

--
Class Template, the class that actually interacts with the template directly...
[b]EDIT:[/b] This class was revised to allow re-setting of variables, thanks for the suggestion eric

[code=php:0]
class Template {
// Holds the template contents
public $templateContents;
// Holds the variables to be changed
public $changedVariables;

// Get template contents
public function __construct($template) {
$this->templateContents = file_get_contents(TEMPLATE_DIR . $template);
}

// Set template variable
public function setVariable($variable, $contents) {
$this->changedVariables[$variable] = $contents;
}

// Commits the variable changes then displays the template
public function displayTemplate() {
foreach ($this->changedVariables as $variable => $contents) {
$this->templateContents = str_replace('{' . $variable . '}', $contents, $this->templateContents);
}
echo $this->templateContents;
}
}
[/code]

--
class Page, the abstract parent class of what all the pages will be...

[code=php:0]
abstract class Page {
public $templateMain;
// Make the main template
public function __construct() {
$this->templateMain = new Template('test.tpl');

$this->makeTitle(); // Abstract, will be set by child
$this->makeHeader();
$this->makeBody(); // Abstract, will be set by child
$this->makeFooter();

$this->templateMain->displayTemplate();
}
// Abstract functions for the dynamic stuff
public abstract function makeTitle();
public abstract function makeBody();

// Functions for the permanent stuff
public function makeHeader() {
$this->templateMain->setVariable('PAGE_HEADER', 'Test Header');
}
public function makeFooter() {
$this->templateMain->setVariable('PAGE_FOOTER', 'Test Footer');
}
}
[/code]

--
class TestPage... the child class of Page, is just the representation of what a real page would look like

[code=php:0]
class TestPage extends Page {
// Set the dynamic stuff on a per page basis
public function makeTitle() {
$this->templateMain->setVariable('PAGE_TITLE', 'Test Title');
}
public function makeBody() {
$this->templateMain->setVariable('PAGE_BODY', 'Test Body');
}
}

new TestPage(); // Set everything in motion
[/code]
Link to comment
Share on other sites

It's pretty simple as of now. Though I might suggest that you store the variables that you assign and change them right before displaying. This way you can, for whatever reason, change the value of a variable later on. The ability to display multiple template files (which could be down by setting a template owner class, or something similar) would probably be good. Also, you're assigning the variables with HTML tags, but the point of using a template engine is to separate the display from the logic.
Link to comment
Share on other sites

[quote]
Though I might suggest that you store the variables that you assign and change them right before displaying.
[/quote]
good idea :P
[b]Edit:[/b] Did it, and changed the template class above accordingly. thanks :D

[quote]
The ability to display multiple template files (which could be down by setting a template owner class, or something similar) would probably be good.
[/quote]
could just create a "new template()" for the new tpl file... no problem

[quote]
Also, you're assigning the variables with HTML tags, but the point of using a template engine is to separate the display from the logic.
[/quote]
it's just an example :P the dynamic values will be pulled from seperate places, but I couldn't very well put a mysql table or seperate .tpl files on here without making it look chaotic :D I'll edit out the brs though, those were just for me testing the class in the first place.. just came over in the paste
Link to comment
Share on other sites

I would suggest doing a quick Google for bTemplate it's a native PHP template engine which is simple and easy to follow...

The number one goal of mine when designing a template engine is 100% HTML from PHP seperation...an enigmatic ideal.

i'll keep working on my ideas until I discover a technique :)

Cheers :)
Link to comment
Share on other sites

Ok...well I'm not sure what your after...

Smarty is over kill...the only advantage it offers over bTemplate is allowing anonymous users to safely upload new templates....95% of web sites wouldn't have this requirement...

Your template engine is extremely limited as a consequence of it's simplicity...it's secure and likely very efficient but too weak for anything but trival applications...

You simply cannot render advanced GUI using a simple "push" and replace technique such as you have provided. Not without breaking some serious best practices mainly seperating PHP from HTML.

How could you improve it? I told you...look into modeling after bTemplate. You need logic(although this is what I've investigated) in order to control advanced GUI's.
Link to comment
Share on other sites

here's a good article. I always pull this article out of the drawer when it comes to template engines:
http://www.massassi.com/php/articles/template_engines/
There is nothing in that article I disagree with, which is unique for me.

Looking at it again, this dude is the author of bTemplate.
Link to comment
Share on other sites

[quote author=redbullmarky link=topic=119472.msg489454#msg489454 date=1166684131]
here's a good article. I always pull this article out of the drawer when it comes to template engines:
http://www.massassi.com/php/articles/template_engines/
There is nothing in that article I disagree with, which is unique for me.

Looking at it again, this dude is the author of bTemplate.
[/quote]

Lucky...I was just about to chomp at the bit and say...that is bTemplate...but apparently we agree...so there will be no debating who is right or wrong *high fives*  ;D
Link to comment
Share on other sites

The thing is.. I wasn't actually trying to learn how to make a template engine, or get a template engine to use for that matter. I was trying to work on my design patterns so I did something trivial and was curious if I outlined it and planned it correctly. I probably should have gotten that out there but I didn't think it would turn into a discussion of what template engine I should be using... sorry for the confusion.

I'll take a look at btemplate though, learn that way, and of course to read the article, it seems to be an easy read as well.
Link to comment
Share on other sites

i totally understand you - only I do think (in a loser definition of what patterns/problem solving is all about) that it is kinda relevent, as it does have a baring on how you structure things.

@phppunk yep - no arguments from me. The whole "yeah but it seperates php from html" argument doesnt wash with me, as it's replacing one syntax with another. or "my designer doesnt know PHP" which doesnt either, as PHP is VERY easy to learn, especially with its alternate syntax.

@generic again, it's not that I find your class bad, but i'm dead set against the whole method of templating like this (although i never used to be).

As far as going a step further with yours (whether you keep it as smarty-type tags or PHP tags: I'm well into the whole idea of using 'layouts' - that is, having a 'master' layout (including all the doctype/CSS/JS includes, logo, possibly the nav, etc, and a 'template' in my book is a snippet that lives inside of it.
my master template generally looks something like:

master.tpl.php
[code]
<DOCTYPE etc etc>
<html>
<head>
<?=$pagehead ?>
</head>

<body>
  <div id="wrapper">
      <div id="header">
        <img src="logo.gif" alt="logo" />
      </div>

      <div id="content">
        <?=$pagebody ?>
      </div>

      <div id="footer">
        Copyright 2006 blahblahblah
      </div>
  </div>
</body>
</html>
[/code]

and my template for a page might be:
[code]
<h1>My Page</h1>
<hr />
content goes here<br />
hello world!
[/code]

i have methods for setting/getting variables, i have a loadToVar method which loads HTML from a template into a variable (for partials, repeating regions, little panels, etc - similar to what you've done with your line that does setVariable('PAGEBODY', ...)). Now that I have my main page ready, a single call to my 'render' method literally just plugs the HTML into my master layout by setting the 'pagebody' var.

trust me, it's easier than that in practice than it is to explain :) but the whole template class is something like 130 lines, is fast, supports gzip, layouts, etc.
Link to comment
Share on other sites

infact, code might make it a little clearer. my Config is a singleton. the two relevent parts of it here are 'page' which stores default variables (ie, such as copyright message, etc) and 'view' which has settings such as whether to use gzip or not.

[code]
<?php

class View extends Core
{
    public $vars = array();
    public $html;
   
    private $controller = null;
   
    public $layout = null;
   
    private $config = array();
   
   
    function __construct(&$controller)
    {   
        parent::__construct();       
       
        $this->config = Config::load('view');
       
        $this->controller = $controller;
       
        if (!isset($this->config['layout']))
        {
            $this->config['layout'] = 'master.tpl.php';
        }
        $this->layout = $this->config['layout'];
       
       
        $pagevars = Config::load('page');
       
        if (sizeof($pagevars))
        {
            $this->vars = array_merge($this->vars, $pagevars);
        }
    }
   
    /*
    * loads a template file and returns the output
    *
    */
    function load($filename)
    {
        extract($this->vars);

        ob_start();
        include($filename);
        $this->html = ob_get_clean();
               
        return $this->html;
    }


    function loadtovar($varname, $filename)
    {
        $html = $this->load($filename);
       
        $this->set($varname, $html);
    }

    /*
    * sets a view var
    *
    */
    function set($varname, $value = '')
    {
        if (!is_array($varname))
        {
            $this->vars[$varname] = $value;
        }
        else
        {
            foreach($varname as $key=>$var)
            {
                $this->vars[$key] = $var;
            }
        }
    }
       
    /*
    * displays the final output
    *
    */
    function render($return = false)
    {
        global $TIME_START;
       
        $this->vars['pagebody'] = $this->html;

        if ($this->layout)
        {
            ob_start();
            echo $this->load($_SERVER['DOCUMENT_ROOT'] . '/app/views/' . $this->layout);
            $html = ob_get_clean();
        }
        else
        {
            $html = $this->html;
        }

        $html = str_replace('{PAGE_GEN}', round(getMicrotime() - $TIME_START, 4), $html);

        if (!$return)
        {
            if (isset($this->config['gzip']) && $this->config['gzip'] == true)
            {
                ob_start();
                ob_start('ob_gzhandler');
                echo $html;
                ob_end_flush();
                header('Content-Length: '.ob_get_length());
                ob_end_flush();       
            }
            else
            {
                echo $html;
            }
        }
        else
        {
            return $html;
        }
    }
}
?>
[/code]

and an example use from within my controller:

[code]
<?php
$view = new View($this);

$view->set('message', 'Hello World!');
$view->set('thoughts', 'The world is flat');
$view->loadtovar('pagebody', 'mypage.tpl.php');

echo $view->render();
?>
[/code]
which makes the vars 'message' and 'thoughts' available to mypage.tpl.php, which in the end is plugged into 'pagebody' which is a common name i use to plug content into a master layout.

it aint perfect, but hope it gives you an idea of where i'm coming from.

cheers
Mark
Link to comment
Share on other sites

Sounds simple and effective, and I appreciate the time you took to explain it to me. I can see the point you make quite well, at least I'm pretty sure I do. That would be to avoid all the bloated and slowing secondary syntax and nesting loops and all that into a template itself, only to parse it later and just feed it the info. I hope I didn't miss your point but all in all the message I seem to be getting is that as much as you can, avoid weird syntax and just nest php and let php deal with the variables, that would be what the <?php <?= ?> tags are for, right?

Once again I appreciate the time you took in showing me all this, I took a glance over your class before posting but I'll look into it more and find out if I'm wrong or right in the message I got.
Link to comment
Share on other sites

yeah you're totally right. to me, for all the overhead you save, typing <?=$pagevar ?> isnt that much harder than typing {PAGEVAR}, even for a PHP-unaware designer.
some argue against using PHP short tags, but looking over the minutes of the meeting regarding PHP6, there are no plans to do away with them and are generally turned ON by default. couple that with PHP's [url=http://usphp.com/manual/en/control-structures.alternative-syntax.php]alternative syntax[/url] and you have something quite simple indeed without the headache and bloat of Smarty.
Link to comment
Share on other sites

Just to note, to my knowledge there isn't a Design Pattern other than MVC itself which covers "Template Engines"

The template engine may have many design pattern implementations within it, but it as a whole is a system, not a pattern.

Also to note that the use of shortags (<? or <?=) are highly discouraged - they will be removed in PHP6 and a lot of hosts already do not allow them.

As for me, I've used Smarty in the past. It's not overkill because you use what you need to use.. a lot people hate Smarty, but not as many with real reasons to do so.

I stick to using php syntax for templates. Adding an extra syntax to seperate is a good idea, at first, but soon becomes restrictive and annoying.

A simple template structure like so is what I tend to use:
[code]<!DOCTYPE blah blah>
<html>
    <head>
        <title><?php echo $this->_data['title']; ?></title>
        <?php
            foreach ($this->_data['meta_tags'] as $tag)
            {
        ?>
                <meta http-equiv="<?php echo $tag['http_equiv']; ?>" content="<?php echo $tag['content']; ?>" />
        <?php
            }
        ?>
    </head>
<!-- etc -->[/code]
Link to comment
Share on other sites

Jenk, neither <? nor <?= are going to be removed in PHP6 unless things have moved on from the meeting and the minutes that were made available. The discussion was with regards to removing <% and < script language="php" > and adding <?php= .

Sure, I'm fully aware that they're not recommended by many - but with the popularity of Ruby (thanks to Rails in particular), and with people loving the short and alternative syntax, I'd take a stab that even in further versions of PHP they'll still be around and as it stands at the moment, most versions of PHP4 and 5 have the directive turned on by default. I've been with several hosts and none have had it turned off as yet, but I can appreciate that some may do. However - with current practices and SEO with tidy URL's, most good hosts support and give the ability to maintain htaccess files which does also allow you to set the short_open_tag setting if it's disabled by the main PHP.INI. Sure - not ideal - but neither is a host that lumps you with the default settings with no way of changing anything.

If something has happened since the meeting, I'll stand corrected.
Link to comment
Share on other sites

W3C standards dictate that to be compliant, you must not use ambiguous tags, i.e. the format must be an opening &lt; (<) a quesiton mark to indicate a dynamic language (?) and the three letter representation of that language (php.)

If you don't, you are not W3C compliant - PHP are moving towards compliance. They are holding onto <? for now, because of the vast number of people using them. However - because it is an [b]optional[/b] setting and there is an [b]always on and future proof[/b] alternative, there really is no reason to use them.
Link to comment
Share on other sites

[quote]Just to note, to my knowledge there isn't a Design Pattern other than MVC itself which covers "Template Engines"[/quote]

Perhaps the author intended to say: http://en.wikipedia.org/wiki/Template_method_pattern

I've investigated and worked or one a number of template engines (each with varying advantages/disadvantages) over the years...as I consider myself something of an expert in the field of template engine design I can say from experience there are indeed patterns which can observed which are specific to template engines.

[quote]but it as a whole is a system, not a pattern[/quote]

Ahhhhh....yes and no...a template engine can consist of countless existing "known" patterns and is a system. I agree with that logic. But by nature of the fact that design patterns are abstractions of real problems, which to me suggests vague definitions...nothing concrete...I see nothing wrong with assuming a template engine/system itself is one giant design pattern...it really depends on your POV.

[quote]As for me, I've used Smarty in the past. It's not overkill because you use what you need to use.. a lot people hate Smarty, but not as many with real reasons to do so.[/quote]

Find that comment I made on devnetwork.net amigo...my arguments were stedfast, accurate and undeniable. Smarty is indeed overkill and actually promotes bad design in some instances. But again, I guess it depends on your POV. Variable modifiers...???

If that doesn't scream "wrong domain!!!" I don't know what does...

Leaving the template designer incharge of handling how currency value is rendered or how a date should be displayed??? WTF...that does little for internalization... :P

And despite Smarty's trivial caching mechanism...it still sinks...I can implement time sensitive caching in about 5 minutes and I don't get the overhead of a huge complicated library. Why I say it's overkill? Simply because including the entire PEAR lib to use one function would be overkill IMHO. All Smarty does for you is offer a clean way to seperate concerns. Which bTemplate does too but with almost ZERO overhead. Smarty has the advantage (sole and only advantage from what I can tell) of having to compile Smarty templates into a PHP template anyways (ahem...bTemplate). The advantage this offers is that you can now safely allow anonymous users to upload new templates to custmoize their look and feel. Think MySpace...but how many web sites actually let you do that??? Considering MySpace is one in a billion...I'll take my chances in favour of bTemplate...

As for the syntax argument...please...already noted...even a monkey could program in PHP and the Smarty tags are equally cryptic...

An IF statement is an IF statement (intent is the same) regardless of how it is expressed syntactically...same goes for FOR loops, etc...

Smarty does have some interesting expediant helper modules (or whatever their called) so you can render an series of OPTION tags without a FOR Loop...but again...easy to replicate in plain PHP...

Yup...all in all...I see Smarty as a waste of effort and time...maybe if they added something useful...I'd use it...until then...I'm suggesting people stay *away* from Smarty like the plague :)

Cheers dude :)

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.