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. 🙂
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?
$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).Doh, me silly, I missed the clearing line of course.
Very neat hack. Sometimes I’m glad WordPress is a silly mess of non-OO global variables 🙂
Even if it was class based, you could just manipulate
$classvar->shortcode_tags
though, right?That really depends. Most of the time a variable like that would be private or protected (although that obviously can’t be done in PHP 4). Instead there should be methods for manipulating it.
Yeah, Aaron knows better than me. I’m pretty sure that in a real OO type project the trick from your post would be considered felonous, but in WordPress its just an unusual workaround. Don’t let Jacob Santos know about it though! 😛
Rewrote the post to use a better example of why you may want to run earlier shortcodes, namely
wptexturize()
wreaking havok on your shortcode contents.Pingback: Daily Digest for November 29th | More Than Scratch The Surface
Pingback: WordPress Picks [12/13] | Techtites
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.
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,
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!
I’m sorry, forgot to code the codes.
I mean this
becomes this
Thank you for this!
Pingback: Protect Shortcodes from wpautop and the likes - Better WordPress
I love workarounds, it avoids hassles of patches.
But needless to say, should not we promote WordPress patch and syntax like:
Where last attribute defines the priority?
That’s certainly one solution, yes. 🙂
Pingback: Wordpress is converting & to & inbetween [code] brackets | SeekPHP.com
Pingback: BP Code Snippets 2.0 : finally #BuddyPress 1.5+ ready ! » imath..
Just stumbled upon this. Saved me so much mental anguish. Thanks for the post.
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!The code in this post was written from scratch by me. It’s not in Core.
However I did actually write the code in
class-wp-embed.php
as well.Pingback: WordPress: ?????????????????????? | ???
Hi Alex, Thanks for the code. Should there be two add_shortcode() calls? Should the first call be the shortcode_foobar function instead?
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.This works on most of my shortcodes by inserts just the closing paragraph tag inside one shortcode that has imbricated divs. E.g. .Please help.
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.
Pingback: Eigene Shortcodes in WordPress erstellen | Torsten Landsiedel
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?
See http://codex.wordpress.org/wpautop
OK, thanks!
Pingback: Risen – Church WordPress Theme (Responsive) | Scripts CodeCanyon
Pingback: ??–??WordPress??(??) | WordPress ??? ThemeKing
Pingback: Nulled Free Risen - Church WordPress Theme (Responsive) Download
Pingback: Templatepub – Premium Wordpress Theme Risen - Church WordPress Theme (Responsive) - Templatepub - Premium Wordpress Theme
Pingback: Risen - Church WordPress Theme (Responsive) | Nonprofit
Pingback: Risen - Church WordPress Theme (Responsive)
very good amin 😉
Pingback: Risen - Church WordPress Theme (Responsive) - Download Nulled WordPress Theme & Plugin Free
Pingback: Original – Risen – Church WordPress Theme (Responsive) – Wiihot.com
Pingback: Risen – Church WordPress Theme (Responsive) – Nulled Themes | Nulled Plugins | Best Place for Free Theme Downloads
Pingback: Risen - Church WordPress Theme (Responsive) - Premium Wordpress Themes
Pingback: Risen – Church WordPress Theme (Responsive) – SEO