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. :)

33 thoughts on “WordPress Code: Earlier Shortcodes

  1. What if some plugin registers shortcode earlier than 7 ? In such a case, the global $shortcode_tags would not be empty and the early do_shortcode would actually run twice those shortcodes, right?

  2. $shortcode_tags is almost certainly not empty by the time my filter comes along. The gallery shortcode for example is registered. That’s why we back it up before we clear it out.

    Then once it’s empty and no shortcodes are registered, we register the shortcodes we want to parse early. Once those shortcodes are processed (only those will be because they’re the only ones registered), we restore the original $shortcode_tags from the backup we made earlier which puts everything back exactly as it was and in turn removes our early shortcode.

    When my filter runs doesn’t matter as it puts things back exactly as they were before it ran.

    By the way, I use this trick in my SyntaxHighlighter plugin so that I can replace my shortcodes with pre tags before wpautop() adds paragraphs to the insides of my shortcodes (once the shortcode content is wrapped in a pre, wpautop() won’t touch it).

  3. Pingback: Daily Digest for November 29th | More Than Scratch The Surface

  4. Pingback: WordPress Picks [12/13] | Techtites

  5. Hey Viper,

    Just came across this post and it totally saved my wrecked head!

    Was having issues where WP was inserting p elements in my shortcode content.

    I’m trying to make things easier for my brother to put a special offer box on his new website but want to maintain flexibility. This way he can do

    [special title=”blah”]
    // put some content here, most likely a list
    [/special]

    Interestingly the only thing that was a little strange was if, in my shortcode function, I placed HTML comments after closing divs (something I do to make it easier to find what’s going on where in my source), I still had WP trying to encapsulate the HTML comments in a p element. When I removed the HTML comments everything worked fine!

    Thanks dude.

  6. Hi, this is a very nice approach. I know this is an old post, but I still want to ask something. You said that “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)”, but could you please tell me how wpautop can affect the shortcodes? I find wpautop affects the shortcodes even if it is called before. For example,

    <div>test</div>

    will result in test. P tags are never closed. Now, if wpautop is called after shortcodes, it would just be like this test, which is what we want.

    Do you have any suggestions? Thank you!

  7. Pingback: Protect Shortcodes from wpautop and the likes - Better WordPress

  8. I love workarounds, it avoids hassles of patches.

    But needless to say, should not we promote WordPress patch and syntax like:

    add_shortcode("foo", "foo_shortcode", 7)

    Where last attribute defines the priority?

  9. Pingback: Wordpress is converting & to & inbetween [code] brackets | SeekPHP.com

  10. Pingback: BP Code Snippets 2.0 : finally #BuddyPress 1.5+ ready ! » imath..

  11. Thanks for the great article! Question: did you write this code and it made it into WP Core, or did you copy/paste it from the class-wp-embed.php? Regardless, thanks for the code!

  12. Pingback: WordPress: ?????????????????????? | ???

    • The first add_shortcode is to make sure that the shortcode is properly stripped when it’s supposed to be such as during excerpt creation and in 3.6 during the creation of titles for post formats that don’t use titles.

      The second add_shortcode is to add the code back after removing all the rest, so that it can be processed by itself.

      • Exactly. The first is so WordPress is always aware of the shortcode and that it exists, the second one is used during actual processing.

        Additionally I just edited the post and changed the first add_shortcode() call to use a dummy callback.

    • Solved it. After reading comment from above saying that even comments can influence this I looked over my problem shortcode again and noticed that I had pressed enter just before the outer closing div in my shortcode. I deleted that enter and now with two closing div tags one right after the other, without any spaces in between it works. Son of bitch this gave me a headache.

  13. Pingback: Eigene Shortcodes in WordPress erstellen | Torsten Landsiedel

  14. Hi, thanks for this post!

    In deed the shortcodes are fine now(no p tags), but I see it replaces all the other p tags with br tags in the content.
    Is this how it should normally behave or am I doing something wrong?

  15. Pingback: Risen – Church WordPress Theme (Responsive) | Scripts CodeCanyon

Leave a Reply