I saw a post in Coding Horror on the subject and figured I could do a better job responding this way.
I should note, while I'm at it, that this is something that's in transition at the moment and there's very much a better, cleaner way. On the other hand, I'm pretty sure that I did the right thing by writing what I did the way I did and letting the better answer appear to me.
So, even though when ASP came out back in the early days of the web and then PHP brought it out of the NT platform, I really passionately hate tag-soup based scripting. It makes my code go straight to hell, and VBScript-like languages (which I'll lump PHP into) don't help matters any.
I went through a few versions of this so far and will go through more. An early version tried to have a mini-HTML markup language using XSLT, which doesn't work.
I spent a bunch of time carefully trying to avoid tag-soup and, while I was at it, avoid writing my own parser. I was inspired a little bit by the way that ASP.NET does things, although I doubt that anything more than vague details are shared. So it ends up that all of the scripting tags that everybody uses custom XML-ish tags for are real XML in my system. Instead, I use XML namespaces and scripting.
So if you want to stick a variable into a page, you just use XML:
<query:varsub source="@posting" target="span"/>
How about inserting the XML data from a variable into a page and doing simple parsing (pretty much just embedding "safe" widgets):
<query:reparse source="@posting" target="span" />
Now, that's not exactly handy. How do you add editing blocks and tables and lists and stuff?
<query:func name="head" target="head">
<data />
</query:func>
This calls the function "head" that generates the head node for a page. In other words, I just outright exclude the possibility of ever scripting things inside of the page template.
Here's a more complicated example:
<query:func name="topblock_render" target="div">
<data>
<query:query target="map" type="DIR" path="arts" mode="to-node" format="xml/opml">
<navbar />
</query:query>
</data>
See, the rendering engine is recursive, so blocks of functionality are rendered inside out, so you can string complex operations together.
This generates a navigation bar based on the results of a query in OPML format and then converts the OPML to nice HTML. This also means that you can write any arbitrary number of OPML data sources and any arbitrary number of OPML renderers and plug them freely, so the verboseness pays off with more complicated sites. I do the same thing with Atom.
You can even put queries and rendering commands in the database and pull them out at render-time:
<query:reparse source="@posting" target="span" interpolate="true"/>
so that you can have a front-page that you can rearrange from the editing interface. So I don't have any pages that are truly hardcoded.
I'm pretty sure the only thing you can do on a formatting engine that doesn't turn it into nasty tag soup is say "Hey, drop the results from this complex procedure here". But this isn't actually a problem. In the design of most good sites, there are multiple tasks, which may not be done by the same person. So the design task is done by one person, who doesn't want to deal with what the engine is capable of right now and probably doesn't care to deal with anything other than the immediate task at hand. So that person will generate HTML mockups, with all of the CSS and HTML just right, of all of the important pages. Making the interactive bits (blogs, forums, whatever) is another task which is separated from front-end coding.
So each part needs to be able to get out of the way of the others.
When I made the new Wirehead Arts site, I put this to the test. I sat down and doodled out a look for the site in Photoshop and then converted it to a set of demo HTML pages on my local machine. Then I took those pages, swapped the mock text with formatting instructions, and had a pretty good rough version of the site.
The way I see it, XSLT is neat, but front-end designers really don't get XSLT. And HAML is also neat, and I may use it on the ruby-side functions, but front-end designers can't code, either. I do see why TAL is so cool, on the other hand.
I'm a better hacker than front-end designer, but when I'm in front-end design mode, even I don't want to deal with code.
Copyright 2007, Ken Wronkiewicz