Templating with PHPTAL: Don't make your designer think (about the wrong things)
In this post
Our history of templating strategies
In former times, when some of us started to generate dynamic websites using PHP, we tended to generate XHTML output for, let's say, a blog post like this:
<?php <div class="blog-post"> <h1><?php print htmlspecialchars($post->getTitel()); ?></h1> <?php $image = $post->getFirstImage(); if ($image) require_once('draw-image.php'); ?> <div class="text"> <?php print $post->getTeaser(); ?> <a href="<?php print $urlProvider->getUrl($post); ?>"> Read more </a> </div> <ul class="categories"> <?php foreach ($post->getCategories() as $category) { ?> <li> <?php print htmlspecialchars($category->getName()); ?> </li> <?php } ?> </ul> </div> ?>
This way of writing templates was kind of error prone. On the one hand, it was more than easy to forget to close some tag or to misspell an attribute. The editor at hand could not help to identify this problem, because he either thought I would edit PHP code, giving no hint what the text outside of the php tags would be, or I would edit XHTML, what would lead to a whole lot of editor errors, because there is no such thing like a <?php ?>-Tag in XHTML. So more than often we generated not even well-formed XHTML. On the other hand, you always had to remember to apply "htmlspecialchars" to any value coming from the evil world of uncontrollable user content to prevent cross-site scripting and the like. Being inherently lazy to write, as we developers are, we often skipped the escaping, assuming that a value came from a more safe place like our customer's CMS or from the good fellow developer next door. Well, never trust anybody to give you well-formed or even valid content unless he explicitly says so!
For us at webfactory, at that time being a group of PHP developers writing object-oriented code, it made perfect sense to solve this problem by a layer of abstraction. So we invented an object-oriented tool to define and nest XHTML tags and anything alike. Taking the example above, this is how we would do it at the time of writing:
<?php class BlogPostView { public function build(Post $post, wfXHTMLTagNode $container) { $postDiv = $container->insert('<div>')->addClass('blog-post'); $postDiv->insert('<h1>')->text($post->getTitle()); $imageView = new ImageView($post->getFirstImage()); $imageView->build($postDiv); $textDiv = $postDiv->insert('<div>')->addClass('text'); $textDiv->insertFragment($post->getText()); $textDiv ->insert('<a>', array( 'href' => $urlProvider->getUrl($post) ))->text('Read more'); $categoryUl = $postDiv->insert('<ul>'); foreach ($post->getCategories() as $category) { $categoryUl->insert('<li>')->text($category->getName()); } } } ?>
With this our previous problems were solved: Nesting is failure safe and every content inserted into the output is escaped unless you explicitly use the "insertFragment"-Method, which will ensure that the given string is well-formed and valid XHTML itself.
Here comes the designer
In the last several years we recognized one kind of simple but more than important fact: Making a good website is nothing but understanding your user's needs! Your user does not care which layers of abstraction you introduced in your software, which database indices you optimized or even whatever programming language or framework you used. He cares if the website looks nice, if he finds the content he searches for, sees the buttons he wants to click or understands how the website is to answer his current purpose.
This shift of thinking started when we hired our first non-php-developer Søren, being director of arts today. He had the right kind of stubbornness to get around with the old crowd of user-neglecting geeks. "Well, I don't care if putting an image next to one of a forms fields is impossible with your new perfect form-handling-system. Nor does the poor guy who is supposed to use this form! It just makes it more understandable! So get it there, please!". Now we more than ever strive to enable our designers to get whatever content or functionality they want, to get the best out of their user-interface-expertise.
This is where separating the parts of your software correctly comes into play. I'll try to take the simple idea of Model-View-Controller here. What we think is best to achieve this aim and to reduce the amount of dependency between designers and developers, is to let the first take care of all aspects of the View-part, while letting the latter take care of the Model- and the Controller-part. So taking the above example, defining what a post is and how its content is loaded from whichever persistence-layer you've got at hand (Model), as well as to find out what the correct URL for the detail-page of the post is and what that means in terms of interpreting the URL to start the correct workflow (Controller), is within the responsibility of the developers.
What does this "public" thingy mean again?
The problem with our current templating approach is, that it is php and that it is object oriented and that it is far away from anything that looks like XHTML. Writing a view today means to understand what a class is. Even worse, if you want to reuse parts of your views you have to know how to inherit from other views or how to instantiate one from a given class. You have to know what methods are and what access modifiers do. You have to be able to recognize variables, how to call methods on an object and how loops and the like work in a modern programming language.
That is a whole lot of things a designer does not need to understand and it burdens his daily work with a lot of cognitive extra load.
So again we stepped back and tried to find a better way of templating that meets our old and new demands.
PHPTAL
After some time of evaluating we fell in love with PHPTAL. It is a PHP port of Zope's templating invention TAL. The new idea of TAL was not to introduce an own syntax (like <?php … ?>) but to move representation actions into namespaced XML-Attributes. Therefore templates in TAL are still perfectly well-formed and can even been validated when using an extended XHTML-DTD. So a good editor is able to show you any nesting errors or misspellings in attributes. Using an extended XHTML-DTD, it will even be able to offer you code completion for all TAL tags and attributes.
So now we are back in the designer's world:
<?xml version="1.0"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>This is a blog post template</title> </head> <body> <div class="blog-post" metal:define-macro="blogpost"> <h1 tal:content="post/getTitle">This is an example title</h1> <tal:block tal:define="image post/getFirstImage" tal:condition="image" metal:use-macro="reusable-snippets.html/image"> <img src="test.jpg" alt="this is an example image" /> </tal:block> <div class="text"> <tal:block tal:content="structure post/getTeaser"> This is an example teaser. </tal:block> <a tal:attributes="href wf-controller:postDetail post"> Read more </a> </div> <ul class="categories"> <li tal:repeat="category post/getCategories" tal:content="category/getName"> Example-Category </li> </ul> </div> </body> </html>
So are all of our problems solved? As already said, what you can see above is absolutely well-formed XHTML. So any good editor should give no warning unless you introduce a real syntax error.
So what about escaping? Every single value of a model, that is rendered into the output will be escaped correctly by PHPTAL, unless you tell it not to do so by adding the keyword "structure". So be lazy again.
Is it hard to understand for a designer? Well, any professional designer should know what XHTML is. He should know what XML-namespaces are. What he might need to learn is the syntax off TAL. But taking the above template, this is already about 60 percent of the attributes that are introduced by TAL. So it's nothing compared to a modern object oriented programming language. Also it just introduces concepts that correspond to presentation actions (conditional output, repeated output, replace output, fill output with content...). So designers should easily understand them.
What the blog post example also shows is that PHPTAL offers a mighty tool (METAL) to reuse snippets of your output or define layouts and organize them into folders and files. Each of these snippets (like our blog post template) or layouts is a complete XHTML page. PHPTAL allows you to place example output within the markup, so that you can open the files in any browser and get a real idea of what the output will look like. And this without even having a server or a PHP interpreter running. We think that this clears the way for efficient testing of reusable view components. I'd just like to mention OOCSS here...
But PHPTAL comes along with even more features, like caching, internationalization, pre- and post-filters to manipulate your input or output on a higher level, just to mention some of them. Describing them would definitively blast my article, so go and visit PHPTAL's website and have a look at the documentation, which is by the way very cheerful and complete.
I am currently working on a PHPTAL-DTD that extends the XHTML element and attribute set reasonably. I will write an article as soon as it is finished and it will be available for download.
As always, I would appreciative if you leave a comment along the way!