OO PHP templating

PHP Web development November 18th, 2008 by Eran Galperin

Templating is a common technique for separation of concerns in applications - separating presentational logic from domain (or business) logic. This kind of separation promotes higher maintainability and a better chance to reuse presentational code (by encapsulating it in templates), the kind of traits we would all love to have in our code base.

PHP as a language can be considered a templating system, as in its root it was meant to modify HTML pages dynamically. The need for more structured templating systems arose as PHP applications have grown more and more complex, giving birth to much more specialized and focused solutions.

Search-and-replace Vs. Include

As the title suggests, there are two prevalent forms of PHP templating, each with its own cons and pros:

  • Search-and-replace systems (to which Smarty belongs) involve running string replacement filters on a template, replacing placeholders with dynamic content. Search-and-replace systems suffer from parsing overhead which can be significant depending on the amount of content being parsed. More advanced systems in this category attempt to offset the overhead by caching parse results (Smarty for example relies heavily on caching for its performance), but this introduces the concern of maintaining the cache (determining when it becomes stale).

    Search-and-replace systems are also better suited for serving mostly static content, as regenerating content dynamically incurs the parsing overhead. Hence it is more suited for content web-sites than highly dynamic web-applications, in which every user sees a different page than the others, limiting the effectiveness of caching.

  • Include systems involve including PHP scripts which serve as templates, and passing variables directly to those. WordPress for example uses a basic implementation of such a system for its templating mechanism.Include systems suffer much less overhead for parsing templates than search-and-replace schemes, simply due to the fact that no such action takes place.

    On the other hand, include systems are prone to scoping problems. If templates are included in the normal flow of the application, then they share and pollute the scope they are in - meaning they could affect data in other parts of the application which could lead to weird behavior and decrease in reusability of templates.

Templating with Objects

Ideally, we would like to combine the pros and solve the cons of both systems. We need a way to pass an arbitrary number of variables into a template without polluting the general scope of the application, while avoiding the parsing overhead of search-and-replace routines.

Fortunately PHP presents us with such a construct - Classes. Class methods are different from regular functions in that they have a shared local scope always available - the object instance ($this). Recent PHP templating systems use this property of classes to their advantage, creating a separate scope for templates thereby allowing them to process without affecting the rest of the application.

Savant is a good example of such a system, and so is the View component of the Zend Framework. I'll be borrowing concepts from both to show how a simple implementation of such a system can be built.

In Code

In the most basic form, a templating system built with the principles I've mentioned above will look like this:

  1. class View {
  2. public function render($script) {
  3. $this->_include($script);
  4. return ob_get_clean();
  5. }
  6.  
  7. public function __get($key) {
  8. return (isset($this -> $key) ? $this -> $key : null);
  9. }
  10.  
  11. protected function _include() {
  12. include func_get_arg(0);
  13. }
  14. }

Usage is pretty simple - load some vars into a View class instance and pass a template into the render method. For a template that looks like:

  1. <h2><?php echo $this -> name; ?></h2>
  2. <p>Current position: <?php echo $this -> job; ?></p>

Passing parameters into a view instance:

  1. $view = new View();
  2.  
  3. $view -> name = 'Obama';
  4. $view -> job = 'President of the USA';
  5.  
  6. echo $view -> render('/path/to/template.phtml');

Will result in:

  1. <h2>Obama</h2>
  2. <p>Current position: President of the USA</p>

We can also invoke the render() method inside a template to chain several templates together:

  1. <h2><?php echo $this -> name; ?></h2>
  2. <p>Current position: <?php echo $this -> position; ?></p>
  3. <?php echo $this -> render('footer.phtml'); ?>
  4.  

'footer.phtml' being another separate template file.

In Review

Lets review what the class does:

  1. public function render($script) {
  2. $this->_include($script);
  3. return ob_get_clean();
  4. }

The render() method receives a $script parameter which is the path to a template script. It passes that parameter to separate method called _include() and captures the output using output buffering.

Capturing the output into a string instead of outputting to the screen at once is very useful if we want to perform this as a part of a complete process, such as preparing headers and starting sessions(), which has to happen before any output is sent to the browser. It is also useful in case we want to perform additional transformations on the output before sending it to the browser (such as parsing and filtering).

  1. protected function _include() {
  2. include func_get_arg(0);
  3. }

The _include() method is the core of this templating approach. By including the template inside a class method with no arguments (calling func_get_arg() instead), the included script has only the object instance ($this) as the available scope to get information from and pass information to. Any variables we declare inside this scope have no bearing on the outside and so collisions and scoping issues are avoided.

  1. public function __get($key) {
  2. return (isset($this -> $key) ? $this -> $key : null);
  3. }

This is really a utility method to prevent PHP from throwing a 'variable is not defined' notice in case the template attempts to access a non existing parameter.

Complete solutions like Zend_View and Savant offer many more features, such as plug-ins, filters, base directory configuration and more, but the principle is the same. While the same structure could be mimicked to some degree using procedural functions only, the simple and convenient interface of passing and retrieving parameter to/from templates is unique to this approach - making it the clear winner in my opinion.

Tags:

If you liked this article you should follow me on Twitter and/or share below:
  • http://blog.midstride.com Andre

    Nice post, I like the way you broke down the concepts easily and explained the main pro/cons. I’ve never really liked smarty while I much prefer zend’s templating system…

  • http://blog.midstride.com Andre

    Nice post, I like the way you broke down the concepts easily and explained the main pro/cons. I’ve never really liked smarty while I much prefer zend’s templating system…

  • http://www.tonybibbs.com Tony Bibbs

    Not that this is a security article but care should be given to escape your output, me thinks. PEAR::Flexy does this by default giving a bit of a edge in the XSS game.

  • http://www.toosweettobesour.com/ Daniel Cousineau

    Actually Smarty relies on compiled files, caching is an addon that is not enabled by default (much like caching of Zend_View’s requires a bit of extra effort).

    Smarty does its search and replace method and produces a file with generated PHP code. Dwoo actually works as an OOP templating system, it just compiles down the Smarty-like syntax to the files you describe above.

    For example, a Smarty template: {$var|date_format} will produce a template file that looks like:

    It stores it in a file with the file name corresponding to the last access time of the source file in question and only recompiles when there is a mismatch.

    Basically, the parsing overhead really only exists upon source file changes and all subsequent requests pull from the compiled template.

    The idea behind using Smarty-like templating systems is cleaner code and work well in environments where designers do the heavy HTML-lifting and do not need access to PHP (or should not have access to PHP).

    Please take a look at how these systems work before pulling straw man attacks :P

  • http://www.tonybibbs.com Tony Bibbs

    Not that this is a security article but care should be given to escape your output, me thinks. PEAR::Flexy does this by default giving a bit of a edge in the XSS game.

  • http://www.toosweettobesour.com/ Daniel Cousineau

    Actually Smarty relies on compiled files, caching is an addon that is not enabled by default (much like caching of Zend_View’s requires a bit of extra effort).

    Smarty does its search and replace method and produces a file with generated PHP code. Dwoo actually works as an OOP templating system, it just compiles down the Smarty-like syntax to the files you describe above.

    For example, a Smarty template: {$var|date_format} will produce a template file that looks like:

    It stores it in a file with the file name corresponding to the last access time of the source file in question and only recompiles when there is a mismatch.

    Basically, the parsing overhead really only exists upon source file changes and all subsequent requests pull from the compiled template.

    The idea behind using Smarty-like templating systems is cleaner code and work well in environments where designers do the heavy HTML-lifting and do not need access to PHP (or should not have access to PHP).

    Please take a look at how these systems work before pulling straw man attacks :P

  • http://www.toosweettobesour.com/ Daniel Cousineau

    Meh, XSS filters destroyed my PHP tag block. What I meant to say was {$var|date_format} compiles down to something similar to:

    //a few lines of setup code (namely loading assigned vars)
    echo strftime( isset($data['var']) ? $this->var : ” );

  • http://www.toosweettobesour.com/ Daniel Cousineau

    Meh, XSS filters destroyed my PHP tag block. What I meant to say was {$var|date_format} compiles down to something similar to:

    //a few lines of setup code (namely loading assigned vars)
    echo strftime( isset($data['var']) ? $this->var : ” );

  • http://www.techfounder.net Eran Galperin

    @Tony:
    I agree – a complete solution should incorporate escaping and more. I just stripped down the templating engine to it’s bare bones for the purposes of this article.
    @Daniel:
    I consider Smarty parsing into PHP files a form of caching (to avoid additional search-and-replace). Whenever the template is updated, that cache has to be invalidated. It’s true that it’s a very smart way to cache – but it’s a cache never the less.
    Also, smarty limits greatly the kind of logic that can be used in templates (to simple loops and conditionals) – which is good in some contexts, less good in others.

    Don’t get me wrong – I think Smarty is an awesome system, but I like the structure of Zend_View and Savant better :)

  • http://www.techfounder.net Eran Galperin

    @Tony:
    I agree – a complete solution should incorporate escaping and more. I just stripped down the templating engine to it’s bare bones for the purposes of this article.
    @Daniel:
    I consider Smarty parsing into PHP files a form of caching (to avoid additional search-and-replace). Whenever the template is updated, that cache has to be invalidated. It’s true that it’s a very smart way to cache – but it’s a cache never the less.
    Also, smarty limits greatly the kind of logic that can be used in templates (to simple loops and conditionals) – which is good in some contexts, less good in others.

    Don’t get me wrong – I think Smarty is an awesome system, but I like the structure of Zend_View and Savant better :)

  • http://codeutopia.net Jani Hartikainen

    These days I somewhat prefer PHP-based ones more than Smarty-like things, since they ultimately offer more flexibility when you need to do something the designers of the template engine didn’t think of… but the PHP ones tend to have one small thing that I don’t like: they make things very-too-much-irritatingly-and-pointlessly-long.

    There’s a pretty simple solution, though, which I’ve tested on couple of occasions where I haven’t had a framework: Use variable variables to redefine class properties in local scope inside a function, before requiring the actual template PHP script. While some may consider it hacky, it does take some extra length off the template code, while still preserving the flexibility of PHP.

    Now, I don’t know what kind of performance penalties or such that kind of redeclaration of everything would cause etc., so I haven’t really used that in more than some small projects, as I’d also have a proper framework with the usual view stuff in bigger projects… but it might be nice for a change to do things a bit differently =)

  • http://codeutopia.net Jani Hartikainen

    These days I somewhat prefer PHP-based ones more than Smarty-like things, since they ultimately offer more flexibility when you need to do something the designers of the template engine didn’t think of… but the PHP ones tend to have one small thing that I don’t like: they make things very-too-much-irritatingly-and-pointlessly-long.

    There’s a pretty simple solution, though, which I’ve tested on couple of occasions where I haven’t had a framework: Use variable variables to redefine class properties in local scope inside a function, before requiring the actual template PHP script. While some may consider it hacky, it does take some extra length off the template code, while still preserving the flexibility of PHP.

    Now, I don’t know what kind of performance penalties or such that kind of redeclaration of everything would cause etc., so I haven’t really used that in more than some small projects, as I’d also have a proper framework with the usual view stuff in bigger projects… but it might be nice for a change to do things a bit differently =)

  • http://www.toosweettobesour.com/ Daniel Cousineau

    @Eran which is the selling point of Smarty-style templates where I work. The complex logic beyond what Smarty provides should be transformations done in the controller in the first place, IMHO, however that’s neither here nor there. Whatever makes life easier for my Designers keeps me happy cause I don’t get pestered for questions, they can navigate and use Smarty effectively, and the designers prefer the look of Smarty-spliced code as opposed to PHP spliced code.

  • http://www.toosweettobesour.com/ Daniel Cousineau

    @Eran which is the selling point of Smarty-style templates where I work. The complex logic beyond what Smarty provides should be transformations done in the controller in the first place, IMHO, however that’s neither here nor there. Whatever makes life easier for my Designers keeps me happy cause I don’t get pestered for questions, they can navigate and use Smarty effectively, and the designers prefer the look of Smarty-spliced code as opposed to PHP spliced code.

  • Tom

    A few months ago I had a need to write me own OO template engine, and this is almost exactly what I came to except mine was backed with custom syntax much like Smarty. I needed the “sandbox” capabilities in a simple syntax, but I reached my peak potential when I realized I needed conditionals and loops. And thus, the project died. You should give it a go.

  • Tom

    A few months ago I had a need to write me own OO template engine, and this is almost exactly what I came to except mine was backed with custom syntax much like Smarty. I needed the “sandbox” capabilities in a simple syntax, but I reached my peak potential when I realized I needed conditionals and loops. And thus, the project died. You should give it a go.

  • Pingback: Eran Galperin’s Blog: OO PHP Templating : WebNetiques

  • http://antoniocs.org AntonioCS

    This is practically what I do in my framework. I just keep all the variables in a privata data array and then extract them on to the included file.
    One thing I don’t understand. Why return ob_end_clean? In the manual is says it just returns a bool, don’t you have to use something like ob_get_contents() to echo the contents of the ob_start() buffer?

  • http://antoniocs.org AntonioCS

    This is practically what I do in my framework. I just keep all the variables in a privata data array and then extract them on to the included file.
    One thing I don’t understand. Why return ob_end_clean? In the manual is says it just returns a bool, don’t you have to use something like ob_get_contents() to echo the contents of the ob_start() buffer?

  • http://www.techfounder.net Eran Galperin

    @Antonio:
    It’s ob_get_clean() not ob_end_clean(). ob_get_clean() fetches the buffer and cleans it in one go.

  • http://www.techfounder.net Eran Galperin

    @Antonio:
    It’s ob_get_clean() not ob_end_clean(). ob_get_clean() fetches the buffer and cleans it in one go.

  • http://antoniocs.org AntonioCS

    Ops!!! Misread sorry :)

  • http://antoniocs.org AntonioCS

    Ops!!! Misread sorry :)

  • http://blog.invenzzia.org/en/ Zyx

    For me, using OOP for templates looks like taking a nuclear missle to kill a fly. In fact, it’s hard to call your templates “objective” – they are normal PHP files with some loops and conditions, where the only occurence of OOP are “->” used to read the data assigned by the script. You can easily achieve the same effect with ordinary functions. Something that we may call “objective templates” can be observed in Zend Framework, where we use decorator pattern to decorate the form controls with some extra stuff.

    Smarty is not a revolution. Practically it offers nothing but changed syntax to provide the same loops and conditions, like PHP, and moreover it limits us to this very subset of PHP language. However, please notice that imperative languages in general suffer from some disadvantages: the code is not portable and reusable, heavily depends on a single script and forces us to think on the implementation of various quite simple issues every time we want to use them.

    There’s a huge potential in designing a new template language and writing a compiler for it in PHP. All we need is a good idea for the language model. The compiler can hide some mess of PHP syntax, whole algorithm implementations (declarative programming – tell, **what** you want to see, not **how** it is supposed to work, like SQL does with the databases) and do things that pure PHP will never be able to achieve.

  • http://blog.invenzzia.org/en/ Zyx

    For me, using OOP for templates looks like taking a nuclear missle to kill a fly. In fact, it’s hard to call your templates “objective” – they are normal PHP files with some loops and conditions, where the only occurence of OOP are “->” used to read the data assigned by the script. You can easily achieve the same effect with ordinary functions. Something that we may call “objective templates” can be observed in Zend Framework, where we use decorator pattern to decorate the form controls with some extra stuff.

    Smarty is not a revolution. Practically it offers nothing but changed syntax to provide the same loops and conditions, like PHP, and moreover it limits us to this very subset of PHP language. However, please notice that imperative languages in general suffer from some disadvantages: the code is not portable and reusable, heavily depends on a single script and forces us to think on the implementation of various quite simple issues every time we want to use them.

    There’s a huge potential in designing a new template language and writing a compiler for it in PHP. All we need is a good idea for the language model. The compiler can hide some mess of PHP syntax, whole algorithm implementations (declarative programming – tell, **what** you want to see, not **how** it is supposed to work, like SQL does with the databases) and do things that pure PHP will never be able to achieve.

  • http://ignite.digitalignition.net Greg Tangey

    amen to most of Zyx’s last paragraph!

    We use HTML Template IT from PEAR.. while not perfect it gets the job done well.

  • http://ignite.digitalignition.net Greg Tangey

    amen to most of Zyx’s last paragraph!

    We use HTML Template IT from PEAR.. while not perfect it gets the job done well.

  • http://www.pcspectra.com PCSpectra

    Interesting article. ALthough doesn’t Smarty also offer inclusion of files?

    Just incase you are curious, the author of ANTLR parser generator wrote an paper a while back in which he refers to templates (if memory serves me correctly) push versus pull.

    I googled quickly and found it here:

    http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf

    Perhaps worth a read?

    Cheers,
    Alex

  • http://www.pcspectra.com PCSpectra

    Interesting article. ALthough doesn’t Smarty also offer inclusion of files?

    Just incase you are curious, the author of ANTLR parser generator wrote an paper a while back in which he refers to templates (if memory serves me correctly) push versus pull.

    I googled quickly and found it here:

    http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf

    Perhaps worth a read?

    Cheers,
    Alex

  • http://www.techfounder.net Eran Galperin

    Hi Alex, nice to see you here :)
    The pattern that solves the push versus pull decision is the two-step view – http://martinfowler.com/eaaCatalog/twoStepView.html

    And you can see a PHP implementation of it in the Zend Framework – http://framework.zend.com/manual/en/zend.layout.html

    Personally, I don’t use this pattern often since I have the privilege of implementing both the domain model and the templates, so I can plan my model methods accordingly and use smart caching where needed.

  • http://www.techfounder.net Eran Galperin

    Hi Alex, nice to see you here :)
    The pattern that solves the push versus pull decision is the two-step view – http://martinfowler.com/eaaCatalog/twoStepView.html

    And you can see a PHP implementation of it in the Zend Framework – http://framework.zend.com/manual/en/zend.layout.html

    Personally, I don’t use this pattern often since I have the privilege of implementing both the domain model and the templates, so I can plan my model methods accordingly and use smart caching where needed.

  • http://hisuar.com Philips Tel

    good job!!!

  • http://hisuar.com Philips Tel

    good job!!!

  • http://www.phptalk.net/ Kai Sellgren

    I prefer for easy templating languages, because many web designers, who write (X)HTML would not be afraid of learning PHP, although Smarty has a short learning curve. But still, it is a learning curve, and I would prefer for simple tagging system like [TITLE], etc. Of course, the template would be compiled, so no string replacements will ever occur.

  • http://www.phptalk.net/ Kai Sellgren

    I prefer for easy templating languages, because many web designers, who write (X)HTML would not be afraid of learning PHP, although Smarty has a short learning curve. But still, it is a learning curve, and I would prefer for simple tagging system like [TITLE], etc. Of course, the template would be compiled, so no string replacements will ever occur.

  • Sagi

    Why you have chosen to implement _include without parameters?
    If you declare $file and use it instead of func_get_arg(0),
    would it change something?

    Thanks,
    Sagi.

  • Sagi

    Why you have chosen to implement _include without parameters?
    If you declare $file and use it instead of func_get_arg(0),
    would it change something?

    Thanks,
    Sagi.

  • http://www.techfounder.net Eran Galperin

    I explained it in the post – it is included without parameters to keep the scope absolutely clean. Only $this is available from an outside scope inside the template, and you can declare any variable there without fear of override.

  • http://www.techfounder.net Eran Galperin

    I explained it in the post – it is included without parameters to keep the scope absolutely clean. Only $this is available from an outside scope inside the template, and you can declare any variable there without fear of override.

  • Sagi

    Ok, the missing word for me was “only”.
    I don’t think it’s a big deal, because there is no danger in overriding $file.
    Also, sometimes it’s useful when you have the template’s name inside.

    Thanks.

  • Sagi

    Ok, the missing word for me was “only”.
    I don’t think it’s a big deal, because there is no danger in overriding $file.
    Also, sometimes it’s useful when you have the template’s name inside.

    Thanks.