My new website uses WordPress and Smarty

Spurred on by the rather dull situation of my web dev portfolio being a single long page with pictures and writing on it (which simply will not do!), I spent the last week moving my blog (and my portfolio) to my own webspace with WordPress and a custom theme. This entailed re-learning PHP (ugh), learning more about WordPress, and pulling a lot of hair. And the whole thing is still a work in progress, but it’s here, and it’s better than my old blog. Yay!

WordPress has worked really well for me, so far. It has a really good post editor that doesn’t spew broken HTML all over my posts when I look at them wrong, the SQLite plugin works (for when I was testing locally), installing it was simple, and it has reasonably detailed documentation, most of the time.

But there is one place where WordPress and I simply cannot agree, and that is writing themes. My new site has a fancy header that picks up rows as you go from page to page. My attempt to create reasonable semantics for that under a typical WordPress theme was completely fruitless. The convention in WordPress is to write a theme in straight PHP, using stuff like the_content() to print the selected post’s content directly. Coming from Django, I was not a fan.

What I wanted was a good template engine, like Django’s. I wanted it to support inheritance, so I could have a base page and override specific parts of that base page in each subpage. I wanted the ability to use the same template for different back-ends without too much turmoil. I wanted to stick variables in page content without generating incomprehensible giberish, and I would prefer to separate logic from presentation at least a little. There is just a thing for PHP: Smarty! Now, some searching for Smarty support in WordPress led me to a lot of requests and no real implementations (or attempts), so that’s why I’m here. This site passes through Smarty for just about everything, and I probably didn’t do it right but at least for a small site this method seems to work. (And I’ll keep this post updated if it doesn’t). I’m going to write (or at least brain dump) some tips for those who are looking to do something similar. I am assuming, if you’re interested, that you have some idea how Smarty works and some idea how WordPress works, but not much of an idea is necessary.

This probably isn’t terribly efficient, though it’s worth noting that Smarty is normal PHP code once each template is run for the first time, so at least to me it intuitively felt more expensive than it ended up.

First, you need a custom WordPress theme that acts as an adapter to Smarty, because Smarty can’t talk to WordPress directly. (It can’t call functions). Unfortunately my WordPress theme was made in a big hurry so it isn’t reusable. (A clever person could do better – maybe something like the Sandbox theme). Coming from Django, I decided I would think of WordPress’s template files like Django’s views; the things that go between an HTTP request and the (Smarty) template system. Look through WordPress’s Template Files List. In each of those, instead of displaying something directly we’re going to gather data and send it over to Smarty.

You’ll need to add some stuff to functions.php. First, load the Smarty library (require('includes/Smarty.class.php')). Then we have a Smarty subclass that can be used for the project:

class Smarty_Base extends Smarty {
	function __construct() {
		parent::__construct();

		$basedir = get_theme_root().'/'.get_template();

		$this->setTemplateDir($basedir.'/smarty/templates');
		$this->setCompileDir($basedir.'/smarty/templates_c');
		$this->setCacheDir($basedir.'/smarty/cache');
		$this->setConfigDir($basedir.'/smarty/configs');

		//$this->caching = Smarty::CACHING_LIFETIME_CURRENT;

		//** un-comment the following line to show the debug console
		//$this->debugging = true;
	}
}

I added another subclass that would guarantee some variables be sent to every template. The most important ones here are wp_head and wp_footer, which are popular points for plugins to attach themselves.

class Smarty_Wordpress extends Smarty_Base {
	function __construct() {
		parent::__construct();

		$wptitle = wp_title(' | ', false, 'right');
		if ($wptitle) {
			$this->assign('page_title', sprintf("%s %s", $wptitle, get_bloginfo('name')));
		} else {
			$this->assign('page_title', sprintf("%s", get_bloginfo('name')));
		}

		$this->assign('resource', get_stylesheet_directory_uri().'/resource');

		/* get wp_head */
		ob_start();
		wp_head();
		$this->assign('wp_head', ob_get_contents());
		ob_end_clean();

		/* get wp_footer */
		ob_start();
		wp_footer();
		$this->assign('wp_footer', ob_get_contents());
		ob_end_clean();
	}
}

So, WordPress (bless its heart) doesn’t have a get_wp_head function; it will only print the head directly. We can work around that using PHP’s output buffering functions, which are those blocks of code with ob_start and ob_get_contents. Again, not pretty, but this solves our problem: now the template can choose when and where to stick wp_head and wp_footer. You’ll be using this a lot, actually.

From here, getting Smarty to render my pages has been as simple as making Smarty templates and running them (with the right values) from the appropriate WordPress template files. Because Smarty templates inherit from each other, don’t be afraid to add helper functions since you might have all of your pages sending the same variables to templates because they inherit from the same base.

For example, I added a function that is called for any template inside my blog section:

function prepare_smarty_for_blog($smarty) {
	$smarty->assign('page_number', get_query_var('paged'));
	$smarty->assign('next_posts_link', get_next_posts_link("Older posts"));
	$smarty->assign('previous_posts_link', get_previous_posts_link("Newer posts"));
	$smarty->assign('blog_sidebar_html', get_sidebar_html('blog-sidebar'));
}

By the way, get_sidebar_html is to do any collection of widgets. Just like wp_head and wp_footer, we need to use output buffering because widgets output their contents directly when asked. Otherwise, it’s a pretty ordinary sidebar that we can freely customize from the admin page:

function register_sidebars() {
	register_sidebar(array(
		'id' => 'blog-sidebar',
		'name' => 'Blog sidebar',
	));
}
add_action( 'widgets_init', 'register_sidebars' );
function get_sidebar_html($index) {
	$sidebar_html;
	/* get sidebar contents */
	ob_start();
	if ( dynamic_sidebar($index) ) {
		$sidebar_html = ob_get_contents();
	}
	ob_end_clean();
	return $sidebar_html;
}

In the end, the WordPress template for a post page (single.php) looks something like this:

<?php
/**
 * The Template for displaying all single posts.
 */

$smarty = new Smarty_Wordpress();

$post_id = get_the_id();
$post_full = new PostContent($post_id);
$smarty->assign('post', $post_full);

prepare_smarty_for_blog($smarty);

$smarty->display('blog-post.tpl');

?>

And here is the Smarty template blog-post.tpl:

{extends file="base-blog.tpl"}

{block name=main}
	<div id="post-{$post->id}" {$post->class_html}>
		{if $post->blog_import_url}
		<div class="post-message">This post was imported from my old blog at {$post->blog_import_url}</div>
		{/if}

		<div class="post-main writing expanded">
			{$post->content_html}
		</div>

		<div class="comments-wrapper">
			{$post->comments_html}
		</div>
	</div>
{/block}

Isn’t that tidy?

PostContent is just a little object that grabs bits of information for the selected post and stores it all in readily accessible variables intended for Smarty. Alternatively, you can just fill up an associative array or a dictionary and send that to Smarty. The object was useful for me because I like to needlessly add objects to things, and because I could reuse code between lists of posts and actual posts. It’s pretty simple (and pretty site-specific), but one thing that may jump at you using this approach is lots of WordPress functions know what “the_post” is based on a global variable — $post. Since we are doing so much stuff outside the ordinary flow of a WordPress theme, changing the global variable can cause some problems. So, I tend to do something like this:

global $post;
$old_post = $post;
$post = get_post($post_id);

/* do stuff with the post, like get_the_excerpt() */

$post = $old_post;

Whether you find yourself in that position seems to depend on who you are. I found my code reading post IDs and wanting to immediately delegate processing said IDs to something else. Somebody else might run get_post from a higher point, in which case that global variable silliness is completely unnecessary.

Another more common thing to keep in mind is that WordPress likes to generate strings with HTML for us right away, and sometimes we can’t change them very easily. (And usually that’s a good thing. It’s pretty cool how it will generate a comment form or a post password form and magically process it for us, and it generally just saves time and makes your theme more flexible). To make this a little tidier, I like to add _html to the end of variable names that point to HTML content, instead of fighting a losing battle and making WordPress less cool :)

And that’s pretty well it. There is still lots of boilerplate, but I find it much more readable than a lot of WordPress themes out there. Smarty’s template inheritance is really the main thing I wanted and it works beautifully. In short, you can use Smarty with WordPress, barring some limitations. If you (like me) have been convinced that dedicated template languages are the solution to everything, it could even be worthwhile!

June, 2013

I have been iterating on my theme, recently. I added some responsive design bits and pieces, I changed the layout for sidebars and footers and things, I set a maximum width for the content, and I made comments look a little prettier. This involved a lot of CSS, but it also involved a lot of changes to markup in order to support that CSS. I noticed something interesting: the last modified date for all of my PHP files except comments.php and functions.php is April 2012. I had to modify comments.php because I still haven’t gathered up the courage to sort out that mess and move that template to Smarty, and the only recent change for functions.php was registering a new sidebar. So, as a person who hates PHP, I think this was pretty cool.