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.
Okay, one bug I have noticed. Some WordPress plugins, like SyntaxHighlighter Evolved, depend on wp_footer being called last, after all the other content has been created.
With the code above, when wp_footer is called, SyntaxHighlighter assumes it isn’t being used because it hasn’t been given any shortcodes yet. So, its javascript is never added to the page.
I made a quick fix in the Smarty_Wordpress class. Removed the wp_footer stuff from the constructor, and added this…
The assumption is display($template) is the last thing you call on the Smarty instance, and you’re always going to call it exactly once, so it takes that opportunity to generate the footer. It’s pretty ugly (as if doing all that code in the constructor wasn’t weird enough), but it fixes that problem.
(And then I stopped using SyntaxHighlighter Evolved, anyway, because it’s much nicer and easier to just add whatever global javascript includes I need to my template).
I’m thinking of doing an actually useful generic WordPress+Smarty template with clean semantics. Might be fun :)
Hi,
You did a great work, I really liked it, keep it up.
Have you tested it with WP 3.4.1 if it’s so then do let me know. I want to give it a try
I’m glad you like it, KB! :)
I had no trouble upgrading to 3.4.1. Most of the API changes would affect the theme features and custom background stuff in functions.php, which should be pretty detached from any template rendering.
I have been making little tweaks here and there, but mostly in my own weird stuff (which I didn’t go into here). One thing has been getting rid of output buffering where possible. WordPress has gained a few more get_ functions lately, and I’m noticing bits of documentation I hadn’t read earlier, so I have been trying to use more of those to speed things up. To get post content, for example, we can do this:
Nice post. I’m glad to see that you did it without resorting to a plugin. You shouldn’t need a plug in to integrate Smarty into the theming system.
(There is a learned helplessness, though, that I see amongst the WordPress that seem to lean towards needing a plugin to do much of anything, including adding Smarty support.)
Thank you for making this post. You have managed to spark a bit of interest for my theme framework.
Very nice information and examples,
thank you for your effort
Just had a quick scan through this, can it be used to grab template files from an existing site and display them on the wp blog? like the header and footer? is that the intended use?
If they share a local filesystem, that is definitely possible, though you can’t (reasonably) load a template file from another server. Using a template on a few different sites is a pretty good case for using a template system like Smarty, though :) If you end up with some piece of your site that’s driven by completely different server code, you can theoretically reuse the base Smarty templates. (I say “theoretically” because even in the simplest case you’ll need to be able to specify exactly which template files to load and what variables to set, and that isn’t always easy to do).
For me, the intention is just to have cleaner, more modular code for my own WordPress theme. I can make a lot of changes entirely by modifying my Smarty templates, and all the code related to page logic is somewhere else that I rarely need to look. I don’t have to navigate a maze of blocks unless I’m actually changing something related to WordPress.
Hey,
I would really would love to see the setup of you theme. Like how do you separate the smarty-template-files from the wordpress core theme template files and how you bring the data and the templates together again. I’am developing a theme based on smarty myself but find myself having problems modulizing everything nicely.
Any plans of sharing you theme or a part of it on github?
I really love you work here. Many Thank man!
Sure! It’s a bit of a hack, so you probably don’t want to do what I did, but basically I crammed all of Smarty’s stuff (includes, cache folders, templates folder) in with the rest of my theme. It’s all PHP, so it doesn’t mind at all. A clever person would probably put that stuff in a plugin (that other themes would use), at least to accomplish some reasonable separation of concerns, but it hasn’t fallen apart quite yet.
Here’s a picture.
If you’re wondering about the php-middle folder, that has templates that I’m accessing from functions.php (instead of from specific WordPress template files like attachment.php). Basically I got lazy with comments.php and it’s using the built-in wp_list_comments to generate a comments list. So, functions.php tells WordPress how to render particular types of comments, using Smarty with pingback-li.tpl and comment-li.tpl.
Oh, and I highly recommend you use mod_rewrite or put your wp-content outside your web root with this. Otherwise you’re going to have lots of Smarty’s stuff being publicly viewable, and that just adds an unnecessary security concern. You’ll notice I don’t have any resource files here: those all live in a static web server on a different subdomain. Highly recommended, if you have control over the setup.
As for sharing this on Github or some such: yeah, I guess I’ve been meaning to do that for two years now, haven’t I? I’m going to do a pretty extensive redesign, soon, so perhaps I’ll be able to generalize what I have and make it something I’d actually want to release. (Right now I think it would just worry me if other people were using it). I’ll leave a comment here when I do! :)
Nice post. And this is very helpful to improve my knowledge in smarty framework. and thanks once again.