Creating Simple oEmbed-Based WordPress Shortcodes

Say you wanted to create a shortcode like this: [youtube id="991WcoEPwb8"]

And instead of manually creating the HTML yourself, you wanted to use YouTube’s oEmbed provider to get the HTML. While that’s easy to do using WordPress’ existing functions (wp_oembed_get() for example), you must implement your own caching of the result as WordPress’ oEmbed class does not include any caching of it’s own.

However, WordPress comes with an [embed] shortcode that’s also secretly used for the shortcode-less embeds. A cool trick is to make that shortcode’s existing code (complete with caching) work for you! This post explains how to do it.

First, the code:

add_shortcode( 'youtube', 'my_youtube_shortcode' );

function my_youtube_shortcode( $atts ) {

	// We need to use the WP_Embed class instance
	global $wp_embed;

	// The "id" parameter is required
	if ( empty($atts['id']) )
		return '';

	// Construct the YouTube URL
	$url = 'http://www.youtube.com/watch?v=' . $atts['id'];

	// Run the URL through the  handler.
	// This handler handles calling the oEmbed class
	// and more importantly will also do the caching!
	return $wp_embed->shortcode( $atts, $url );
}

We start by gaining access to the WP_Embed instance that we’ll be making use of and then making sure we have the required video ID (you can do whatever you want here). We then create the full URL to the video that oEmbed will need. Lastly the real time saver — we pass the attributes (“id” will be ignored) and the constructed URL to the [embed] shortcode handler which will fetch the result and cache it.

Simple, huh?

One thing to note though: if you’re using this along with a non-default oEmbed provider, you’ll also need to whitelist it using wp_oembed_add_provider().

WordPress Code: Earlier Shortcodes

WordPress shortcodes run at priority 11 which is after wpautop(), wptexturize(), and other various default filters. While this can be desirable so that wpautop() doesn’t affect the output of the shortcode (the function is well known for not being perfect), it can also be a drawback as wptexturize() will malform your shortcode contents.

Take this shortcode usage for example:

This is some text.

[foobar]This is how you "quote" things.[/foobar]

This is some more text.

The “foobar” shortcode callback will receive the following string:

This is how you “quote” things.

Well that’s not obviously right. Okay, so you could fix it with some str_replace()‘es to reverse the fancy quotes and other changes, but why go to all the trouble?

Instead, let’s just make the shortcode run before priority 10 when wpautop(), wptexturize(), etc. mangle our shortcodes. That way we can process the shortcode as the user typed it and when once we’re done let wpautop() and it’s friends handle the content like they were designed to do.

First, here’s the code I use. I’ll explain it afterward.

// This will do nothing but will allow the shortcode to be stripped
add_shortcode( 'foobar', '__return_false' );

// Actual processing of the shortcode happens here
function foobar_run_shortcode( $content ) {
	global $shortcode_tags;

	// Backup current registered shortcodes and clear them all out
	$orig_shortcode_tags = $shortcode_tags;
	remove_all_shortcodes();

	add_shortcode( 'foobar', 'shortcode_foobar' );

	// Do the shortcode (only the one above is registered)
	$content = do_shortcode( $content );

	// Put the original shortcodes back
	$shortcode_tags = $orig_shortcode_tags;

	return $content;
}

add_filter( 'the_content', 'foobar_run_shortcode', 7 );

The function starts by global’ing the variable $shortcode_tags. This is the variable that contains a list of all registered shortcodes. We then make a copy of that variable (so we can restore it later) and then empty it out so that no shortcodes are registered.

Now that there’s no shortcodes registered, we register our shortcode and call do_shortcode() which is the function that replaces shortcodes with their contents. Once that’s done, we restore all of the previously registered shortcodes (this also unregisters our shortcode so it doesn’t run again) and return the result of the do_shortcode() call.

Lastly we register that function as a filter but at an earlier priority than wptexturize() so that it will run first. Any number between 1 and 9 will do — I just use 7 as it’s fairly late but still leaves room for other filters to come after it but before wptexturize(). That and it’s my favorite number. 😉

Questions? Improvements? Then leave a comment. 🙂