Better WordPress Shortcodes

WordPress has become a powerhouse in the build-and-host-it-yourself web world and due to that increasing popularity, there is also a ton of (not optimal?) code floating around out there. When developing code for WordPress you have to be mindful of the need to maximize compatibility across many versions of PHP and server settings, as well as not conflicting with the thousands of plugins available to users. Let’s look at a few cool tricks to avoid these pitfalls.

1. Use Namespacing!

Stop polluting the global namespace! Function and class collisions are annoying to deal with and easy to prevent. The most common method I see in practice is existence testing, which works, but isn’t ideal. That looks like this:


if (!function_exists('some_shortcode')) {
	function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
		// --> the guts
	}
	add_shortcode('some', 'some_shortcode');
}

This works, but isn’t ideal because the function “some_shortcode” is now registered in the global function namespace. If the user downloads a plugin that has this same function, whichever function is loaded first in the thread will be used.

Here’s a better approach:


namespace myThemeOrPlugin;
function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
	// --> the guts
}
add_shortcode( 'some', '\myThemeOrPlugin\some_shortcode' );

Using namespaces I can now ensure my function does not pollute the global namespace, and also add some semantic naming to help your bearings when you have 2~3 ‘shortcodes.php’ files open! This also means that you don’t have to use longer function names by prepending your theme or plugin name to your function. This leads to simpler more concise naming conventions.

2. Encapsulate With Classes

Grouping your shortcode functions together affords another opportunity to encapsulate similar pieces of logic in your theme/plugin.


namespace myThemeOrPlugin;
class shortcodes {
	function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
		// --> the guts
	}
}
add_shortcode( 'some', array('\myThemeOrPlugin\shortcodes', 'some_shortcode') );

We have not added much more code, but we have added another nice piece of semantics telling us where this code is being executed. The structure of my code now reflects the environment in which it runs. We are in ‘myThemeOrPlugin’ working with a group of ‘shortcodes’ and executing ‘some_shortcode.’ It follows then that when I add new shortcodes they remain grouped into this helper class.

3. Use the Output Buffer

When you creating shortcodes that use a lot of markup, don’t follow the antipattern of using string concatenation to create your output.

The antipattern

namespace myThemeOrPlugin;
class shortcodes {
	function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
		$output = '';
		$output .= '
'; $output .= '
'; $output .= '

Column one lipsum text

'; $output .= '
'; $output .= '
'; $output .= '

Column two lipsum text

'; $output .= '
'; $output .= '
'; return $output; } } add_shortcode( 'some', array('\myThemeOrPlugin\shortcodes', 'some_shortcode') );

Once this code gets more complicated by adding if statements, switches, and whatever else your short code uses to mutate the output it becomes hard to read!

Do this instead

namespace myThemeOrPlugin;
class shortcodes {
	function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
		ob_start() ?>
		

Column one lipsum text

Column two lipsum text

<?php return ob_get_clean(); } } add_shortcode( 'some', array('\myThemeOrPlugin\shortcodes', 'some_shortcode') );

Using the output buffer I can now free myself up to use templating syntax to make my markup cleaner. You can make this even cleaner by separating your display code completely. Now that we have encapsulated the code in a class, I can add a template loader function to further separate these logical concerns.

namespace myThemeOrPlugin;
class shortcodes {
	
	function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
		$atts = shortcode_atts( array(
			'column1'   => 'Column one lipsum text',
			'column2'  => 'Column two lipsum text'
		), $atts, 'some_shortcode' );
		return self::load_view('some_shortcode', $atts);
	}
	
	private function load_view( $template, $data = array() )
	{
		ob_start();
		extract( $data );
		include TEMPLATEPATH . '/views/' . $template . '.tpl';
		return ob_get_clean();
	}
}
add_shortcode( 'some', array('\myThemeOrPlugin\shortcodes', 'some_shortcode') );

Here I have added a private function to load my shortcode string from an external file. This truly separates the concern of doing the logic to setup parameters in my display and writing the display markup. I have created a new folder in my themes directory ‘/views’ and added the file ‘some_shortcode.tpl’ to it. That file now contains my view code:

<?= $column1 ?>

<?= $column2 ?>

4. Make It Rock!

Now that we have namespaced our code away from polluting the global arena, we have encapsulated our alike functionality and separated our logical concerns, we can make this code REALLY rock by adding an init that will automatically hook our codes into WordPress!

namespace myThemeOrPlugin;
class shortcodes {
	
	function init() {
		$methods = get_class_methods( '\myThemeOrPlugin\shortcodes' );
		foreach( $methods as $method )
		{
			if( stristr($method, '_shortcode') !== false )
				add_shortcode( str_replace('_shortcode','',$method), array('\myThemeOrPlugin\shortcodes', $method) );
		}
	}
	
	function some_shortcode( $atts, $content = null, $shortcodename = '' ) {
		$atts = shortcode_atts( array(
			'column1'   => 'Column one lipsum text',
			'column2'  => 'Column two lipsum text'
		), $atts, 'some_shortcode' );
		return self::load_view('some_shortcode', $atts);
	}
	
	function another_shortcode( $atts, $content = null, $shortcodename = '' ) {
		$atts = shortcode_atts( array(
			'something'   => 'The default for something',
			'somethingelse'  => 'The default for something else'
		), $atts, 'another_shortcode' );
		return self::load_view('another_shortcode', $atts);
	}
	
	private function load_view( $template, $data = array() )
	{
		ob_start();
		extract( $data );
		include TEMPLATEPATH . '/views/' . $template . '.tpl';
		return ob_get_clean();
	}
}

shortcodes::init();

I have added helper function init() here which simply loops through the methods in my class and if the method name ends with ‘_shortcode’ I hook it into WordPress. I always say, a lazy developer is the best developer. I can now be lazy and allow my init function to add all of my hooks. If you want to get some REAL extra credit, create a single function on your global ‘functions.php’ file that statically calls ‘::init()’ on all of your theme/plugin’s helper classes! 😀