on Sep 20th, 2008PHP Templating in 5.3+

So, if you read my last post about pQuery you’d be familiar with me working on a way to enhance the PHP templating “experience”. I wasn’t very happy with how pQuery-0.1 turned out in the end as it’s way to bulky to split it up in several files, so I started glancing at PHP5.3 and what I could (ab-)use closures for. The technique I came up with borrows the DOM-hierarchy from pQuery but allows you to embed the layout code inside the templates, without mixing it with HTML.

How is this achieved? Well through a combination of some of PHPs lesser known features, namely:

  • The ability to return things from included files with the syntax $result = include 'file.php'; if you have a return statement in your include file.
  • If you have php-tags in the top of an XML-file it’s interpreted by the xml parser as a part of the xml document in the same way that is, but is ignored.
  • Closures in PHP5.3

So, let me give you a “small” template example, and let me explain how it works.

<?php
return function($tree, $data) {

	$tree->root->body->ul[1]->li->loop($data, function($tag, $item) {
		$tag->h2->text = $item['title'];
		$tag->p->text = $item['body'];
		$tag->span->em->text = sprintf($tag->span->em->text, $item['date']);
		$tag->span->em->a['href'] = "mailto:".$item['author']['email'];
		$tag->span->em->a->text = $item['author']['name'];

		// If empty, remove ul-list + h3 tag comment header
		if(empty($item['comments'])) {
			$tag->ul->remove();
			$tag->h3->remove();

		} else {
			$tag->ul->li->loop($item['comments'], function($tag, $item) {
				$tag->h4->text = $item['title'];
				$tag->p->text = $item['body'];
				$tag->span->em->text = str_replace(array_keys($item), array_values($item), $tag->span->em->text);
			});
		}

	});
}
?>
<html>
	<head>
		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
		<title>example</title>
	</head>
	<body id="example" onload="">
		<include file="menu.html" />
		<ul id="post-list">
			<li>
				<h2></h2>
				<span class="small-text block">
					<em>
						Posted %s by
						<a href=""> </a>
					</em>
				</span>
				<p></p>
				<h3>Comments</h3>
				<ul>
					<li>
						<h4></h4>
						<span class="small-text block"><em class="test">Posted date by name</em></span>
						<p></p>
					</li>
				</ul>
			</li>
		</ul>
	</body>
</html>

The closure at the top gets fed (as the first argument) the DOM hierarchy of the HTML template below the PHP tags and is then free to modify it however it sees fit, please note that the bulky assignment syntax used with both str_replace() and sprintf() is just there until I have time to put in a better already working version of it.

The jQuery-style syntax works pretty much the same way, allowing it to be extended with custom plugins (the loop()-method call you see in the php-section is a default plugin I wrote).

This approach allows you to both keep the layout and layout-code together in one file, but still separating them so you don’t have to deal with that spaghetti-mess that PHP is so well known for.

If you want to check out the code and play around with it, here’s the .zip-file just zip it up somewhere and check out example.php + example.html in your favorite editor, and of course the rest of the source if you want to, but remember - this requires php5.3-alpha.

6 Responses to “PHP Templating in 5.3+”

  1. pytechdon 20 Sep 2008 at 9:21 pm

    At this point, why not just break down and use full XSLT - serialize an array into XML and feed the XML to any number of XSLT engines?

  2. Wheelwrighton 21 Sep 2008 at 2:05 am

    Good attempt in PHP, here is my Visual Basic equivalent (not 100% exact because I don’t know PHP but I tested it and it works):

    For Each li In xml…
    li..Value = item(”title”)
    li..Value = item(”body”)
    li….Value.Replace(”%”, item(”date”))
    li…
    ..@href = “mailto:” & item(”author_email”)
    li….
    .Value = item(”author_name”)
    ‘ If empty, remove ul-list + h3 tag comment header
    If item(”comments”) = “” Then
    li….Remove()
    li….Remove()
    Else
    For Each li2 In li…
    li2..Value = item(”title”)
    li2..Value = item(”body”)
    li2….Value = str_replace(item.Keys, item.Values, li2….Value)
    Next
    End If
    Next

  3. Wheelwrighton 21 Sep 2008 at 2:06 am

    Delete my previous entry, it came out garbled

  4. TheIdeaManon 01 Jan 2009 at 11:49 pm

    Thanks for building this. What license is it released under? Do you have plans to support a PHP 5.2 version of it?

    I’d love to see a project like this built and continue to develop.

  5. Fredrikon 02 Jan 2009 at 4:00 pm

    TheIdeaMan: Since this is not possible to do in PHP versions < 5.3 since we’re lacking closures there wont be a 5.2 version of it.

    atm. I dont do any work in PHP since we use C# and i’ve switched to using Python for my personal projects so I’m afraid there wont be any further development of this codebase.

    license for the code now? The MIT license.

  6. TheIdeaManon 02 Jan 2009 at 6:31 pm

    Thanks for the follow-up Fredrik and for the license info. PHP 5.3 will be a great step forward when it’s finally released.

    I’m going to look into switching this back to PHP 5.2 compatible code–sadly without closures–since PHP 5.3 is a ways off and the namespace implementation will likely be very different when it’s all said and done.

    It’s sad that you’re not using PHP much any more, but honestly, I understand why. If PHP 5.3 fails to live up to expectations, Python will be my next move.

    Thanks, Fredrik.

Trackback URI | Comments RSS

Leave a Reply