Tom Butler's programming blog

Transphporm - Fixing PHP Templating

Let's face it, templating in PHP sucks. I know I'm flogging a dead horse here, people have been discussing the merits of (and lack thereof) template engines since at least the early 2000s and I think that's why the issues with them are no longer considered relevant. People consider templating a solved problem so don't give it a second thought. I disagree with that assessment and by the end of this article I hope you will too. Let's take a step back and look at the current solution to templating in PHP.

Without straying too much into the pros/cons of template engines vs PHP code (a topic which has been done to death), is there really any practical difference between:

<ul> {{foreach users}} <li>{{user.name}}</li> {{foreach}} </ul>

and

<ul> <?php foreach ($users as $user) { ?> <li><?= $user->name; ?></li> <?php } ?> </ul>

What exactly is the problem that the former solves that the latter hasn't? These kinds of template systems claim to separate markup from php code but the way they achieve that is entirely superficial, basically just adding an alternative syntax for the underlying PHP. While that does offer what some refer to "security" insofar as it's impossible to execute arbitrary PHP code in the template, what business environment lets someone write content to the page but won't let them write some PHP code? The use case for this feature of "security" is at best minimal and tiny in comparison to where these template engines are actually used.

So I ask again: What is the benefit of them? What is the problem they solve? In some cases there is an argument of clarity, subjectively {{user.name}} might be clearer than <?= $user->name; ?> but is that really that helpful? Take a look at this example from twig:

{{ user.username|e }}

Is that clear? What is |e? It happens to mean escape, but isn't

<?= htmlentities($user->username); ?>

more telling of what is actually happening? The "clarity" argument is null and void when the template language, like Twig, becomes as complicated as (or less clear than) the PHP code it compiles into. Is it easier for designers? Doubtful, and wouldn't it be more useful for designers to learn a bit of basic PHP (a transferrable skill) rather than the syntax for the fad-of-the-month template engine?

Again, I come back to my original question: What is the benefit of them? What is the problem they solve? It's not security and it's not clarity. It's also not speed, processing a meta-language in PHP is slower than executing PHP code.

I don't have any other answers, but the problem they should solve (although dutifully ignore) is separation of concerns: The markup and the processing logic should be separated. This makes for cleaner markup that doesn't contain and processing instructions (so is incredibly easy for those designers as they need only know HTML!). To use Smarty or Twig (or blade or mustache or whatever), the designer needs to know the following:

  • HTML
  • CSS
  • Twig/Smarty special syntax
  • A lot about the data structures they're working with. Field names, variable names, whether a value is an array or a single string, etc

The designer should only be concerned with the HTML and CSS, not with the other stuff. From a designer's point of view, the processing instructions actually get in the way. If you open up the template in a browser, there will be processing instructions visible on the rendered page, getting in the way of them making a pixel-perfect design, laying out the elements with just the right amount of padding and space, and all that other designery stuff that I don't think matters enough to worry about.

The W3C realised this when they created CSS. They understood that <font> tags were poor separation of concerns, the markup should describe the structure of the data and not how it's presented. They delegated the presentation to a completely separate technology, CSS. If you wanted, you could write an entirely new styling language and as long as you added browser support, you could use it on existing HTML markup without having to rewrite the markup. Similarly, you can take a single CSS stylesheet and apply it to as many different pages as you like without needing to repeat yourself.

I mention this because currently, if you have to use the same processing logic on different markup, you need to repeat the processing logic. Consider a template like this:

<?php if ($isAdmin) { ?> <h1>Admin heading</h1> <?php } ?>

Which would be expressed similarly in your favourite template engine, just with abstracted PHP code, there would still effectively be an if statement surrounding a block of HTML

Anywhere you want to hide a block based on a criteria you must repeat the processing logic, which at best is redundant. Worse, however is that if the logic changes (e.g. you now have moderators who should also see those blocks) you need to go and change the condition everywhere. Ouch.

The if statement inside the template is no different to the old fashioned font tags. It mixes different concerns and hinders resuability as a direct result.

This is what it is though, this is what we've put up with as PHP developers for the last 15+ years. It works, it does the job and we get on with it. Very few people have taken a step back, and re-assessed the problem with fresh eyes, we just go to these kinds of template systems for a couple of reasons. Lack of choice and familiarity being the main ones. They do the job and they work.

It's easy to see how these template engines evolved. We start off in PHP having database queries with while loops that iterate over the records and generate the response all in one place. Once we realise this isn't a good way of doing it we separate out the logic... sort of. We separate out the logic that fetches the data from the logic that generates the markup. This is certainly a step in the right direction and a huge advantage over mixing every concern. But, I think this familiarity is what blinds us to the problems. We feel like we've advanced when we separate the markup generation from the database interaction and it seems that's where we get stuck. Pat yourself on the back and consider it a solved problem, never to be thought about any more.

However, I'm not interested in "does it work?" a working solution is easy to come up with for most problems, I'm more concerned with "can it be improved?" and in this instance, yes, it can. The glaring problem with separation of concerns is waiting for a neat solution.

The second problem is that they are very bottom up. You generally select a layout template with the common headers/footers/navigation/etc and a content template that displays the information for that particular page. This may go off and include other templates. The problem is, there's no simple way for the processing logic in the content template to affect the content of the layout template. This generally involves setting some variables which are then passed into the layout template later. Let's have a top down approach where the selected template is the layout, which then gets told to include the sub-templates, rather than placing a rendered sub-template in a rendered layout. By rendering the entire thing at once flexibility is enhanced.

For example, in your favourite framework how do you define the content of the <title> tag? Am I right in saying it's effectively by some some sort of global variable that gets set by a controller? Wouldn't it be better to do all this in one place?

There have, of course, been attempts at fixing these problems in the past, but generally they end up needing a lot of awkward and clunky DOM manipulation or wrappers for complex string manipulation outside the template. When you write the template you also have to write a long winded php script for the processing logic.

You're probably wondering: where am I going with all this?

Enter Transphporm

Transphporm is a work in progress attempt at an eloquent solution to the problem of templating in PHP where the markup and the processing logic are entirely separated, as it should be.

How does it work? Transphporm uses CSS inspired stylesheets to describe the processing logic entirely externally from the markup. Templates contain HTML and only HTML. There no loops or if statements inside the template. At it's most basic level, you supply a Transphporm Style Sheet and a Template:

stylesheet.tss

header {conent: "My Heading";}

template.xml

<html> <body> <h1>Example h1</h1> </body> </html>

These are put together using PHP and when compiled, the output is:

<html> <body> <h1>My Heading</h1> </body> </html>

For more examples, see the github page here.

I'm not going to go into too many examples here but want to focus more on the theory. Using this, the same .tss can be applied to multiple templates! No more repeated if ($isAdmin) lines! Just add a tss that says

.isAdmin {display: none}

And any element with the class isAdmin will get removed from the markup.

This separation of concerns allows designers to worry about HTML and HTML only, and even lets them include some dummy text (Designers much prefer lorem ipsum to seeing {{description}}!). The same tss can be reused with multiple templates, and similarly the same template can easily be reused with entirely different data structures and processing instructions, something that's just not possible with traditional template engines without re-structuring the data to fit the needs of the template.

Top Down rather than Bottom Up

Rather than pulling a content template into a layout template, in Transphporm, you use the stylesheet to include the various content templates .in the layout:

layout.xml

<html> <head> <title>Default Title</title> </head> <body> <nav> </nav> <main> </main> </body> </html>

Then, you apply a stylesheet to the layout which pulls in the relevant content. For example, the "Contact Us" page might do this:

contact.tss

title {content: "Contact Us";} main {content: template("contact.xml"); }

Each page has it's own set of processing instructions. What this means is that regardless of the content, it's easy to make page specific changes to the layout: Adding/removing css/javascript from the head tag, adjusting the meta description and even amending the navigation or pulling in one of several sidebars.

Portability

One of the biggest issues with most template engines is that they're just not portable. Because transphporm uses a simple syntax it's possible to run the exact same tss code on the client and the server. This allows developers to render templates both client side and server-side without re-writing the processing logic. Write the template and the processing logic once then render it on the client or the server!

Not only that, it enables designers to create a very basic TSS file that loads templates into a layout. Let's say the designer is working on the news article page, they could just load layout.xml, news.tss and news.tss could load news.xml into the template using a very simple syntax (as above!). The designer can preview the entire page, with their dummy content, without having to run it on the server or execute any php code!

Final words

Transphporm is around my fourth attempt at solving this issue with separation of concerns in templates and it's the only one I've been happy with the implementation of and released (the others involved ugly php objects and requiring a lot of mental agility to track exactly what was going on). It solves the problem and gives a familiar API that will be easy to learn by anyone who understands CSS.

It is a work in progress but I want people to check it out and push it to its limits so we can find out where boundaries are and push them further. Join the project on github and help develop a revolutionary template engine that actually solves the problem that template engines should have been solving since their inception.