Dead Simple Shortcode Detection

Shortcode detection in WordPress plugins is essential for loading scripts and styles. When I launched Responsive Mortgage Calculator last October, I searched the internet looking for a nice way to get my JavaScript and CSS files to load. Because that’s what you do.

Background

The WordPress API offers really nice methods for loading your scripts and styles. Definitely use wp_register_script() and wp_register_style. This should go without saying. You are making life easier for developers who want to use your plugin in ways you can’t anticipate, and by registering your scripts and styles they can be loaded later with calls to wp_enqueue_script( ‘your-script-handle’ ) and wp_enqueue_style( ‘your-style-handle’ ). Please do this.

Let’s assume you have a shortcode and you have a script that you want to load. You could just enqueue the script which means it will always load, but that’s just bad form. You’d be forcing the end user to always download the script, even if it’s not needed on a page. The solution is to figure out if the shortcode is included on a page, and to only loaded if the shortcode is there. There are different methods for this including the has_shortcode() function defined by WordPress. This one makes things nice and simple.

Detecting Nested Shortcodes

Of course, nice and simple doesn’t always do the trick. The has_shortcode function does a nice job in most cases, but, as I’ve found out, the method it relies on to find the shortcode won’t work if the shortcode is nested within another shortcode. Why is this important? The single biggest issue I’ve had with Responsive Mortgage Calculator is the failure to load the JavaScript file when the shortcode is used with Visual Composer.

From what I’ve seen, Visual Composer and similar themes frameworks create layouts by generating shortcodes for each section of the layout. Your plugin shortcode is then placed inside one of these section, making it a nested shortcode. The problem with the scheme that has_shortcode uses to detect your shortcode is that it relies on an internal shortcode regular expression generator, and the generated expression is structured to only detect top level shortcodes, not nested shortcodes.

Since has_shortcode is insufficient for detecting nested shortcodes, I’ve started using a very simple solution: create a regular expression for the shortcode and run it through preg_match(). Let’s pretend the shortcode is ”my-shortcode”. I’m embedding this in a function so you can copy and paste it as is.


function my_plugin_detect_shortcode() {
	global $post;
	$pattern = ‘/\[my-shortcode.*?\]/i’;
	return preg_match( $pattern, $post->post_content );
}

I’ve used a variable to define and store the pattern, but this is more for clarities sake. The regex pattern could just as easily be defined in the preg_match function to save a line.

How it Works

First, the $post object is declared as global to access the post content within the function. This is a convenience, and, given the option, it would be better form to pass the post content to the function as a variable. I’m assuming you don’t have that luxury.

Second, the regular expression is defined. For simplicity, I’ve assumed only one shortcode. The square brackets need to be escaped at the start and end of the regex. The shortcode follows directly after the first square bracket. The expression “.*?” is a non-greedy match, meaning that it will detect zero or more characters up until the closing bracket. This ensures that you are detecting a well-formed shortcode. Finally, the lowercase “i” makes sure that the match is case insensitive, just in case someone has typed in some capital letters for the shortcode.

Lastly, return the result of the preg_match function. It’s worth looking at the php manual to make sure you’re getting what you want. It will return 1 if there is a match, 0 if there is no match, and FALSE if an error occurred. That’s usually good enough for a truthy or falsey test and to decide to load your scripts and styles.

Leave a Reply

Your email address will not be published. Required fields are marked *