XSLT as a templating engine for PHP
PHP is not my language of election, but I cannot afford to be picky on my projects, and as you all should know, PHP is still probably the most used language in web development. One thing that I miss is a good unobtrusive template engine for PHP. Of course there is smarty, and a bunch of pear packages as well as some other free or commercial projects, and I’m honest to say that I’d never tried none of these, simply because I still see XSLT as a perfectly valid and powerful solution This post is a small tutorial about using XSLT as a templating engine for PHP.
There are a number of reasons that lead developers away from XSLT, specially the fact that XSLT is bloated, meaning that sometimes in order to achieve simple tasks we have to write what appears to be extremely complex code. But in my opinion, most of the times that is just a a visual illusion. There’s also the problem that we must convert our data to xml before sending it to the XSLT processor, but is that so hard to accomplish?
I can also enumerate many other arguments that make XSLT a very nice choice for a templating system, here are some on top of my head:
- We can rest sure that our output will be perfectly valid XHTML
- XSLT is supported in pretty much any programming language, making our templates very portable
- It’s like full featured programming language, with data types and functions to deal with those data types
- You can easily embed non native XSLT functions in your XSLT templates
- Having your data in a XML format, allows you to use different templates for different purposes, like a sheet for a RSS feed or for a mobile version of your website.
Now let me get started with a practical example for you to see how simple it can be. First of all lets build a user case. We have a news website that obviously displays news. Most probably we are storing a relational database to store our news, we could consider a xml database that would make our life a bit easier, or even to store our news directly into xml files, but we would miss some nice features that traditional rdbms have to offer. So for the purpose of this tutorial, we’ll work with a relational database, and because I’m a PostgreSql fan, I’ll use it, but the same code should work for mysql for example, is just a matter of changing the postgres specific functions to the mysql correspondents.
So lets assume we already have a database table named news, with columns title, body and time_published. Lets also assume that time_published holds a unix timestamp. Our index page will contain the latest ten news, so lets start building it:
//assuming the connection with the database was already established $result = pg_query("SELECT * FROM news ORDER BY time_published DESC LIMIT 10 OFFSET 0;") or die('Error!!!'); $latest_news = pg_fetch_all($result);
Nothing fancy here, just plain PHP and SQL. We fetch the latest ten news from the database, and are now holding all the results in a variable called latest_news. Hard part, how do we convert it to xml? Because we know our array structure, we can easily create a new DOMDocument, and “load” the array data into it with just a couple of loops:
$doc = new DOMDocument(); $root = new DOMElement('root'); $doc->appendChild($root); $latest_node = new DOMElement('latest'); $root->appendChild($latest_node); foreach ($latest_news as $news) { $news_node = new DOMElement('news'); $latest_node->appendChild($news_node); $news_node->setAttribute('title', $news['title']); $news_node->setAttribute('body', $news['body']); $news_node->setAttribute('time_published', $news['time_published']); }
This is simple, I create a DOMDocument object, and add a root node, and in that node i added a latest node. Then for each news in the latest_news variable, i create a news node, add it to the latest node, then I set a couple of attributes in the node that corresponds to the columns of the news table in the database. I intentionally did not add the news nodes under the root node, because I will add some more stuff to it later. Now our document is ready to be passed to a XSLT template. Lets create it first:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title>Latest news on our site</title> </head> <body> <ul> <xsl:for-each select="/root/latest/news"> <li> <div class="news_title"> <xsl:value-of select="@title" /> </div> <div class="news_body"> <xsl:value-of select="@body" /> </div> </li> </xsl:for-each> </ul> </body> </html> </xsl:template> </xsl:stylesheet>
If you know nothing about XSLT, here’s a quick explanation. As you probably have noticed, a XSLT sheet is a xml document with some special rules, with the purpose of transforming another xml document. In this case we are transforming the document we have created that contains the latest news into a html page ready to be outputted to the browser. Line 1 of our sheet is a xml processing instruction, I’m not getting into many details about this, just consider it as a requirement for the sheet to work. Next, in line 3 we have our stylesheet root tag, pretty much all XSLT sheets start like this, or similar. Then in line 4, we are instructing the processor that the result of this transformation will be in html. Then in line 6 we start a template. XSLT can have one or more templates inside a sheet, in this case we’ll just use one template. The match attribute holds a XPATH expression, which in our sheet just points to the root of the document. This indicates the processor on which nodes the template will work on. Most of the times you’ll be using it exactly like in this example. Inside the template there is something that resembles a common webpage, and is in fact a webpage, or at least what will later become a webpage. We have the opening html tag, a head tag, and a body tag. This html page will be very simple, we just have an unordered list that will contain each news in a list item. So we just open the ul tag, and inside of it we use a XSL for-each that will loop through the nodes pointed by the select attribute (a XPATH expression). Then, for each of these nodes, we open a li tag and add a couple of divs containing the title and body of our news. The XSL value-of instruction just tells the processor to output the result of the XPATH expression contained in its select attribute. And because we are inside a loop, the XPATH expression is relative to each looped node, so we just need to ask for the value of each correspondent attribute in the looped node.
If you’re still confused, don’t worry, everything will become clearer as you start playing with this. So now we’re ready to use our sheet to process our document. Here’s the PHP code that will make it possible (assuming the sheet is stored in a file named news.xml in the same folder as the PHP script):
$xsl = new XSLTProcessor(); $xsl_doc = new DOMDocument(); $xsl_doc->load('news.xsl'); $xsl->importStylesheet($xsl_doc); echo $xsl->transformToXML($doc);
First thing, we create a XSLTProcessor object. Because a XSLT sheet is a xml document, we must use a DOMDocument, to load it, and then we import it to the processor. Finally, we can just echo the result of transforming our previously created document using our XSLT sheet.
I hope this can be useful to someone, I plan to continue with this as soon as possible.
Cheers
Two things stop me from using XSLT:
- You have to create an xml document. You can do it on the fly but this means more time before displaying. You can store it but that means it must be maintained.
- Xml is easy to break. A meta character in an unexpected place and everything goes haywire. Most sites allow people to use a WYSIWYG editor if it doesn’t output xhtml all editor content needs to be escaped and you can’t use XSLT to get pieces of the content.
life insurance %-DDD home insurance rjpt auto insurance quote 36849 auto insurance 186891
cheap health insurance yqyg life insurance quotes 8-PPP home insurance 8-D life insurance quotes %-[[
Yes, those are valid concerns, but probably you haven’t explored the full potential of XSLT.
Regarding point n.1, this all can be avoided using some sort of caching system that can be easilly created depending on your needs.
point n. 2, that is not true, if you add the attribute disable-output-escaping=”yes” on your xsl:value-of tag, the content will be displayed as is.
Anyway, those are subjects I plan to cover later.
In 2007 I’ve published a simmilar 7-part series of posts in my blog also covering advanced topics like I18N, using PHP function within XSL stylesheets, etc. My blog is in german though, but the code examples should be pretty self-explanatory. I think XSLT is great to build templates with, expecially in combination with Semantic Web languages like RDF or OWL.
[...] Cerqueira has posted a quick introduction to using XSLT (an XML-based markup for styling XML documents) as a templating engine for PHP. One [...]
life insurance for seniors szflp affordable health insurance =-] health insurance coverage 43566 health insurance quotes yebfq
Social comments and analytics for this post…
This post was mentioned on Twitter by rlaksana: XSLT as a templating engine for PHP – http://su.pr/1bCXHU...
[...] Cerqueira has posted a quick introduction to using XSLT (an XML-based markup for styling XML documents) as a templating engine for PHP. One [...]
Just wanna point out that..
> We can rest sure that our output will be perfectly valid XHTML
You may have noticed that XHTML can’t be really server as XHTML (IE issues) and that the upgrade path is HTML5, making the absolutely sure part kind of pointless.
> XSLT is supported in pretty much any programming language, making our templates very portable
I feel this is almost the only good point for XSLT
> It’s like full featured programming language, with data types and functions to deal with those data types
PHP is a full featured programming language – not “like”
> You can easily embed non native XSLT functions in your XSLT templates
You can easily embed non native PHP functions…
> Having your data in a XML format, allows you to use different templates for different purposes, like a sheet for a RSS feed or for a mobile version of your website.
You can use different non-XML templates for different purproses as well.
I should probably say that XSLT is an interesting technology. I’ve seen many people talk a lot about it, and about how great it is, but I still haven’t been able to understand it for myself. Rest assured, it is on my list of “things to try” – just haven’t gotten around to it myself.
@Mario
Yes, I totally agree.
Yes, you’re right, but isn’t a bit redundant to use php as templating system for … PHP?
What I mean is that for example, you can call PHP code from within a XSLT sheet.
Hope you do,
Thanks for the comments
cheap car insurance 696 homeowners insurance >:OO cheap car insurance 73399 cheap health insurance urlyjf
life insurance quotes 888 homeowners insurance >:-DDD cheap auto insurance aqognq cheap health insurance %[[[
“Yes, you’re right, but isn’t a bit redundant to use php as templating system for … PHP?”
Yes. But how is that bad? Oh, don’t get me wrong, XSLT is awesome as you’ve shown. However, you make it seem like using the same technology for multiple things is wrong. It’s not. In fact, the best argument against XSLT is the time to learn XSLT. Using PHP gives you the ability to leverage things you already know. I also imagine it’s easier to find PHP programmers than XSLT guys.
Again, don’t get me wrong. In a perfect world, you’d have someone handling the XSLT stuff for your programmers, and you’d have your designers just doing everything in CSS and not touching the XSLT output.
Web development utopia!
@Jason
I see your point and agree to most of it. And you also uncovered a issue with using XSLT, you’ll probably have to introduce a middle guy between the PHP coder(s) and the designer(s).
And I don’t want to introduce a parallel discussion here, but sometimes that “web development utopia” is taken to serious. I guess it won’t bite a designer if he has some notions about the development technologies being used on a project. I know some of them do have that concern, but there are many who just act like the big architect who just has to make things pretty…
Just be sure to cache the stylesheets in memory if using them in a production environment. MSXML has a template object for this, or else keep the parsed stylesheet in memory another way.
cheap auto insurance quotes =-O home insurance 162 cheap home insurance 5054 life insurance quotes 197
@jon
Yep, that’s something I plan to cover a bit later…
You presented some points, but I saw no points that actually said why I should use PHP with XSLT.
Anyway, here are my points against XSLT:
- You have to write a lot of PHP code
- You have to write a lot of XSLT code
– templates are mainly for designers. I bet any designer will be scared by such a large amount of odd code
- You have to generate XML on the fly – PEFORMANCE ISSUE
- You have to process XSLT – PEFORMANCE ISSUE
I would suggest either using just plaint PHP or smarty, at least the latter has caching.
@Mike Borozdin
Well, you’re right about many of those aspects, and most of them were already covered in a way or another in previews comments.
Just don’t forget that this is a introductory tutorial, and I also mentioned that I’ve never tried other templating systems (for php at least), thus I don’t have enough background to argument against them.
But most of those issues can be easily surpassed once you get to know the system. You can dramatically reduce the amount of php code you have to write in many ways (I will not cover it in the comment), and you can easilly build a cache system.
Don’t forget one thing, smarty was built as a general purpose tool, and for wonderful it might be (and I’m pretty sure it is), it will never be as performant as if you build your own templating system for you specific case, around XSLT or not, that’s up to you and your architectural decisions.
I just have one argument against other templating engines, XSLT is core PHP (at least for most implementations)…
So I’m currently moving away from xslt to a new templating language I’m writing that’s also xml based but is compilable (HTML_Template_Nest – http://www.buyplaytix.com/nest/), but I have 2 websites running with PHP and xslt. It’s been great for me, but I feel like I’m starting to hit its performance limitations. One change I made was to create the ability to easily add customized tags, that when I parsed I replaced the tags with apply-template calls. So for example I’d declare my stylesheet like this:
Then I could insert the tag:
And it would translate into:
Save
return saveForm()
save.gif
Here’s the replace tag code:
function replaceTags() {
$tags = $this->getElementsByNS($this->filter->documentElement, “http://www.buyplaytix.com/tags”);
foreach($tags as $tag) {
$parent = $tag->parentNode;
$amap = $tag->attributes;
$attributes = Array();
foreach($amap as $a) {
$attributes[$a->name] = $a->value;
}
$name = $tag->localName;
$call = $this->filter->createElementNS(“http://www.w3.org/1999/XSL/Transform”, “call-template”);
$call->setAttribute(“name”, $name);
foreach($attributes as $key => $value) {
$param = $this->filter->createElementNS(“http://www.w3.org/1999/XSL/Transform”, “with-param”);
$param->setAttribute(“name”, $key);
if(substr($value, 0, 1) == “.” || substr($value, 0, 1) == “/” || substr($value, 0, 1) == “$”) {
$param->setAttribute(“select”, $value);
}
else {
$cdata = $this->filter->createCDATASection($value);
$param->appendChild($cdata);
}
$call->appendChild($param);
}
$comment = $this->filter->createElementNS(“http://www.w3.org/1999/XSL/Transform”, “comment”);
$call->appendChild($comment);
$parent->replaceChild($call, $tag);
}
}
}
Whoops should have been:
<xsl:stylesheet version=”1.0″ xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:bpt=”http://www.buyplaytix.com/tags”>
Then I could insert the tag:
<bpt:button title=”Save” onclick=”return saveForm()” img=”save.gif” />
And it would translate into:
<xsl:call-template name=”button”>
<xsl:with-param name=”title”>Save</xls:with-param>
<xsl:with-param name=”onclick”> return saveForm()</xls:with-param>
<xsl:with-param name=”img”>save.gif</xls:with-param>
</xsl:call-template>
@Tim
Yes, this is very nice and is the type of flexibility you can get with xml based templates, and I’ll surelly take a look at your system.
Actually I’m working on a big project, that should have been released in the beggining of the year but got some delays. But basically provides a way of creating advanced collaborative networks. We wanted to add enough power to the users to change many aspects of their networks, but obviously we cannot allow them to mess with the core PHP code, so the solution was to create a safe xml based templating system, with many features, and we did one thing that resembles your technique, but we used xml processing instructions to introduce some more sophisticated functionality.
But you sure got a terrific idea.
cheap car insurance llv life insurance =-( home owners insurance %[[[ health insurance 743487
Depends on how do you design it, there are lots of repetitive tasks here so I guess you just write PHP code once.
Performance issue was mentioned – even simple caching system solves this problem.
About designers and lots of XSLT code. Its not that hard to learn and understand, and its quite powerful. You can always make it easier for designer(s) and split xslt into many smaller files in some understandable hierarchy.
You are missing out one important feature of using XSLT – you can perform the transformation on the client and reduce the load on the server. I have been doing this for years with the Radicore framework (see http://www.radicore.org). It should work in all popular browsers – IE, Firefox, Safari, etc, (but not Opera due to a bug in their code).
@pneumatyka
Thanks for helping my cause here
@Tony Marston
Yes, you’re totally right. I missed that in my introduction, but don’t forget you have to prepare your sheet as well before retrieving it, unledd of courese you have a very common style in the entire webpage. But that’s a very good point.
When I created a framework for RSS Feeds a few years ago, I went the same route. The transformation to RSS would happen from XML that corresponded to my Domain Objects.
I chose the XML format because I thought it was easier to just serialize the object structure in XML, but for each output format, be it RSS1, 2 or ATOM I had to write another XSLT file. That wasn’t much easier. It just felt cooler
Nowadays,with context switching readily available in most PHP frameworks, XSLT is just not an option to me anymore. There is simply no point in first creating some XML, when I can easily create the desired output from PHP directly. I say kill the middleman.
@John
Well you’re right. XSLT is a good option if you’re starting your project from scratch or building your own framework for example, which in many cases is a good option, because what you’re building is aimed to solve your particular problem, and not “everyones problems”.
But I use frameworks too, I never used themin PHP, I’ve worked with a couple of open source CMS’s but never a framework. I’ve only worked with Java frameworks, namely tapestry spring and struts, grails (groovy) and most recently DJango (python).. I’m curious abouth ror but I never got the chance to work with. The only one that add a xml’istic aproach for templating was tapestry, but that’s also from another league than the others.
[...] XSLT as a templating engine for PHP [...]
“echo $xsl->transformToXML($doc);”
Shouldn’t that method be named transformToXHTML()? Or did I miss the whole point?
Sorry I think it isn’t good way to use xslt like template engine. You must generate xml first. It is extra step and I don’t see any potencial of it if you can use programming language directly (with connection of some template engine).
>What I mean is that for example, you can call PHP code from within a XSLT sheet.
How can we do it?
I know that our php code must have:
$xslt = new XSLTProcessor();
$xslt->registerPHPFunctions();
But, what should I add to my xsl document?(to have a object property or method results(i.e $book->name or $book->isAvailable() ) in transformed output)
Y9Blki kiouzmkfpdri, [url=http://adjgzmlrxznt.com/]adjgzmlrxznt[/url], [link=http://scrzafhhrkkt.com/]scrzafhhrkkt[/link], http://rlsirqppaveq.com/