Introducing Pgs_StringTemplate – yet another PHP template engine

There has been a lot of discussions about Template-Engines and PHP for years with opinions ranging from “PHP is a great template language on its own!” to “Using PHP for templates feels like shooting birds with a bazooka.”

As for me, I remember having used PHP as a template language years ago and I don’t want to go back to that time. My experience is that given the power of PHP you’re likely to use it. That means sooner or later you’ll find “just one very basic query” in your template and a little later you’re likely to have more business than UI-logic in your templates. But that’s just me and others might have made different experiences. It pretty much comes down to whom you’re working with and how much you can control what they’re doing.

However, looking at the alternatives in the PHP-land you can choose between Smarty, a handful of XML-based engines, XSLT and some that use an HTML-comment like syntax. All of them have some serious shortcomings and advantages over each other and it is very important to think about who is going to edit these templates before you decide what engine to use.

As for XML and XSLT in special, let’s just point out that they’re written in XML which means they’re great when used with a WYSIWYG-tool but fall down pretty hard when some sad human being has to edit them by hand. XML is cool unless you have to write/read it.

Smarty comes with its own share of problems. It’s mostly a subset of PHP, supporting limited invocation of functions, foreach-loops and the ability to write your own plug-ins. It’s very well known for having heavy problems with recursion (the solutions I’ve seen the last time I looked at it weren’t really great). But I still prefer it over PHP as it keeps me and co-workers from doing stupid things.

However, what if you had to use recursion? What if the input wasn’t XML? (Yeah, I know it’s hard to believe, but sometimes stuff on the web just isn’t written in XML. Strange, isn’t it?).

I was in this situation, well, kind of. Actually I brought myself into this situation in order to develop the solution as part of a project for university. But that’s just fair, the result does what it should and the world has one more template-engine for PHP.

The rest of this article will discuss my Pgs_StringTemplate engine. I’ll talk about the Why? How? and When? related to it and try to explain my motivation behind it. Please keep in mind that it is totally clear to me that this engine is useless. I really know that you could do all it can do with pure PHP. I just don’t want to use PHP (as explained earlier). So thanks, but no, I don’t think you can convince me to switch to pure PHP any time soon.

Now for starters, why is it called Pgs_StringTemplate (or ST, as I’ll call it from now on)? When I looked into what was available at other places, I found StringTemplate, which turns out to be a totally different kind of template engine from what I knew before. For one thing: There are no loops. The other part is that it defines templates within a language instead of embedding the template expressions/commands within the static text. That way, you can define more than one template within a single file, kind of like a function.

I found this idea to be fascinating and ever since I first saw that engine I dreamed about writing a compiler for a comparable language that’d create pure PHP files. Having the ability to use the templates like objects. That’d surely be perfect for writing views (of the MVC-fame).

Just in case you didn’t yet checkout on StringTemplate, this isn’t the tool you’d give to your designer who doesn’t have any programming experience. In my view, this is more of a tool supposed to be used by a programmer that does have to write the view-part himself. Now for me this is perfect. I’m still working for clients on my own sometimes. Creating, updating and modifying websites and so on. And even when I need to consult a designer, he’ll just give me a PSD or image of the design he created and I’ll do the rest. That way both of us can do what we’re best at. He does the creative thinking and invents the design, and I’m using my knowledge of XHTML, CSS and so on to create the real thing.

Anyway, this project gave me the chance to actually implement my idea. I had written a basic library for writing LL(k)-Parsers in PHP long before (it’s a subset of the ANTLR3-library (just the runtime-library, not the tool itself), ported to PHP) and had gained a bit of experience in this field of software engineering, so I was pretty sure I could complete this project “in time”.

What we’d be using ST for was this: Given a Treebank-formatted text, output it in different formats. The Penn-Treebank format looks a lot like S-Expressions (think: Lisp). For example a valid sentence (possibly one of the most famous samples) might look like this:
( S
( NP-SBJ
( NP
( NNP Pierre )
( NNP Vinken ) )
( , , )
( ADJP
( NP
( CD 61 )
( NNS years ) )
( JJ old ) )
( , , ) )
( VP
( MD will )
( VP
( VB join )
( NP
( DT the )
( NN board ) )
( PP-CLR
( IN as )
( NP
( DT a )
( JJ nonexecutive )
( NN director ) ) )
( NP-TMP
( NNP Nov. )
( CD 29 ) ) ) )
( . . ) )

The head of each expression is the linguistic category of the sub-expression. For example: NP means “nominal phrase” and a VP is a “verbal phrase”.

Our goal was to parse this into an array structure and look at what kind of output we could generate using ST and without having to modify the array in PHP.

Now as I mentioned before my personal goal was to bring a bit of Parrs StringTemplate to PHP but to leverage the PHP-platform at the same time. I wanted tight integration with PHP and I wanted the output to be PHP classes.

This means that you can actually extend a template written in ST from PHP and a ST-template can extend and implement PHP classes and interfaces.

I also added the ability to “import” methods from PHP-classes (this is done by creating an object of the class-type and dispatching calls to the method to this object, instead of a template function.

This mechanism allowed me to easily implement a couple of useful functions like first and rest that definitly eased the array-walking and you can even stretch it to enable stateful templates with variables (which I didn’t include in the language by itself because I don’t really want anyone to do this).

So ST is pretty flexible and very easy to extend from PHP. To underline this, let me show you the implementation of first and rest:
class Pgs_Treebank_Util {
public function first($array) {
return $array[0];
}

public function rest($array) {
return array_slice($array, 1);
}
}

As you can see there is absolutely nothing “special” about this class. Just some class that encapsulates a bit of logic. Now, to be fair to ST: This class does nothing that ST isn’t capable of itself. But writing rest(someArray) is a bit cleaner than |array_slice(someArray, 1)|, at least in my opinion. You couldn’t have written the functions using ST though, as it only supports functions that return Strings at the moment.

On the other hand ST does support one feature that PHP doesn’t (at least not without you having to write a hole lot of code): variable arguments. The last argument of an ST-function might be a vararg, meaning that all arguments starting with this one, will be reduced to a single array (pretty much the way Java does it). This feature enables us to write another simple utility function, this time in ST itself and a bit simpler than the PHP-alternative:
template Pgs_ST_Util;

concat(parts*) = << $parts$ >>
concatWith(separator, parts*) = << $parts; separator=separator >>

This function will take all of its arguments and reduce them to a single string. So whenever you write an ST-expression that involves an array in a scalar context (like in the example above) it’ll be reduced by concatenating all of its elements to a single string. You can use the separator-option to specify the separator that should be put between two elements, like I showed in the second function.

The above will be compiled into a class named Pgs_ST_Util and posses the two defined methods. You can instantiate it and use it like any other PHP-class. For example:
$util = new Pgs_ST_Util();
$helloWorld = $util->concat("Hello ", "World");

ST also supports map-operations in addition to reduce as well as partial application and currying.
format(tree) = << $tree:concat("

    ", formatSubtree(_), "

;")$
>>

This example demonstrates the map operation as well as partial application (as you might’ve guessed, the expression after the colon isn’t a complete one, instead the “_” is a placeholder for the currently processed element in the array). A naive translation into english might sound like this: For each element in tree, concatenate |<ul>|, the result of calling formatSubtree with the current element as argument and |</ul>|, and reduce the result to a single string.

One could’ve done the same a bit easier, but more verbose, by using currying and a simple utility function:
tag(t, txt) = << <$t$>$txt$ >>
format(tree) = << $tree:formatSubtree():tag("ul")$ >>

In English: For each element in tree, invoke the formatSubtree method on it, use the result of this function as the second argument for the tag function and concatenate all of the elements in the array to one single string.

To import methods of PHP-classes or standard PHP functions, you can use an import statement which can follow the template-definition. For example:
template MyTemplate extends MyOtherTemplate implements SomeInterface;

import Pgs_ST_Util [first, rest];
import functionSpace [is_array];

functionSpace is a special name that points to the global function namespace in PHP. Therefore you can use all PHP-functions in ST without any problems.

I think these examples already showed why this solution is both, superior and inferior to Smarty. For one thing it has a very light and powerful syntax with support for quite a few functional programming concepts and is amazing when it comes to recursion. On the other hand its syntax and concepts are hard to understand for designers and it is a bit out of place when you really just want to define a single template using just a few variables.

As for the Treebank-project, we managed to implement a pretty-printed version of the input (with syntax highlighting), an alternative bracket-format (using square brackets instead of parentheses and subscripted categories – with syntax-highlighting), a simple HTML-list and SVG output. The latter was obviously the hardest to implement and forced me to implement PHP-classes to manage a stack and state (to realize variables), but the output is pretty pleasing.

If you’d like to have a look at this, you might want to check out its website. There’s a paper about the project on that site, too. But it’s written in German so it’ll be of limited use for you if you don’t speak that language. You can still look at a few more samples, though.

And before you go off and tell me: I know that the website won’t work at all in IE. That’s probably because of the content-type I send (|application/xml+xhtml|) but I need that one in order to get the inlined SVG-output to work and I don’t feel like fixing it.

ST itself will be released soon under an open source license I have yet to pick. I’m fighting with myself to get me to choose LGPL in favor of BSD/MIT. But I’m still unsure. If you’d like to play with it, send me a mail and I’ll reply with a zip-archive.

Leave a comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.