This morning, I found myself converting a WordPress Page Template into a plugin when I found myself limited by the WordPress Shortcode API. “Limited?” you ask. “The WordPress Shortcode API is awesome and easy to use… how could you possibly feel limited?” Well, I ran into a problem, but I found a solution… go easy, man… go easy…
The Problem
I had a rather large chunk of css that I needed to print during the wp_head action, but I only wanted it to be printed on pages and posts that use the shortcode. This was a bit of an issue because you cannot include any logic inside your shortcode handler function that would help you out with the wp_head action. The shortcode is parsed during the the_content filter which fires afters the wp_head action… errrrr… it still hurts to think about… but it doesn’t have to!
Lets Define a Class
class red_text{
var $shortcode_name = 'red';
var $pattern = '<!-- red-text plugin -->';
var $posts_content = '';
}
Here we have defined a class named “red_text” which contains 3 properties:
- $shortcode_name: This is the actual name of the shortocode that a user would type in between square brackets. In this scenario a user would use [red] if they wanted to use this shortcode in a post or a page.
- $pattern: This is a string that contains an html comment which serves two purposes… it will enable users to easily identify code that our plugin prints and it will give us something to test against… basically, if this string is in the post_content, our script will know that it should include the css.
- The third and final property is $posts_content. We are declaring it as an empty string.
Adding a Constructor
class red_text{
var $shortcode_name = 'red';
var $pattern = '<!-- red-text plugin -->';
var $posts_content = '';
function red_text() {
}
}
Since we are using an object, we will need to define a constructor. The constructor is a method that is called whenever a new instance of the object is called. For this example, we will store all of our hooks in this method. It is important to note that I am using php4 object syntax here, meaning that the name of the constructor is the same as the name of the class.
The Shortcode
function red_text() {
add_shortcode( $this->shortcode_name, array( &$this, 'shortcode' ) );
}
function shortcode( $atts, $content = null ) {
$o = '';
$o.= "\n\t" . $this->pattern;
$o.= "\n\t" . '<span class="' . $this->shortcode_name . '">' . $content . '</span>';
return $o;
}
Here we have registered a shortcode in the constructor and defined a handler method for it. Notice that the handler will return a string that includes the $pattern property. This is crucial!
Should I include the css?
function include_css() {
global $posts;
$o = false;
if( !empty( $posts ) && is_array( $posts ) )
foreach( $posts as $post )
$this->posts_content.= apply_filters( 'the_content', $post->post_content );
if( !empty( $this->posts_content ) )
if( strstr( $this->posts_content, $this->pattern ) )
$o = true;
return $o;
}
The next method that we will need to add is include_css(). When I read the name of the method, it is a question: “Should I include the css?”. Let’s take a closer look on what is going on here:
- The scope of the $posts variable is set to global. This is a variable that WordPress generates for us by default, if there is a post, page or attachment in the loop, $posts will be an array of objects.
- Next, we define variable $o as false. This is the default answer to the question: “Should I include the css?”.
- Now we will need to loop over the $posts array. Each iteration, we will append the value of $post->post_content to the property $posts_content. This will give us a string containing the contents of all posts in the current WordPress Loop. Please note the the value of $post->post_content is filtered using the “the_content” filter, This filter will parse our shortcodes, thus rendering the html comment stored in the $pattern property.
- The next set of code is a nested conditional. Basically, it searches through our $posts_content property and tries to find a match for $pattern. If a match is found, it will change the value of $o to true.
- Finally, the value of $o is returned.
Time to Print The CSS
function css() {
$o = <<<EOF
{$this->pattern}
<style type="text/css">
.{$this->shortcode_name}{
color: #f00;
}
</style>
EOF;
print ( $this->include_css() ) ? $o : '';
}
This method is simply named “css” and will print a style tag only if the include_css() method returns true. Please bear in mind that we will need to add a hook for this new method. See below for finished class.
The Complete Object
$my_red_text_shortcode = new red_text();
class red_text{
var $shortcode_name = 'red';
var $pattern = '<!-- red-text plugin -->';
var $posts_content = '';
function red_text() {
add_shortcode( $this->shortcode_name, array( &$this, 'shortcode' ) );
add_action( 'wp_head', array( &$this, 'css' ) );
}
function shortcode( $atts, $content = null ) {
$o = '';
$o.= "\n\t" . $this->pattern;
$o.= "\n\t" . '<span class="' . $this->shortcode_name . '">' . $content . '</span>';
return $o;
}
function include_css() {
global $posts;
$o = false;
if( !empty( $posts ) )
foreach( $posts as $post )
$this->posts_content.= apply_filters( 'the_content', $post->post_content );
if( !empty( $this->posts_content ) )
if( strstr( $this->posts_content, $this->pattern ) )
$o = true;
return $o;
}
/* Style the Shortcode's Output */
function css() {
$o = <<<EOF
{$this->pattern}
<style type="text/css">
.{$this->shortcode_name}{
color: #f00;
}
</style>
EOF;
print ( $this->include_css() ) ? $o : '';
}
}
You can use this code in your themes’ functions.php file or turn it into a plugin by adding the appropriate code to the top.


I neat way round, but I can’t help thinking there’s an easier way.
http://codex.wordpress.org/Plugin_API/Action_Reference
According to this, wp_head actually comes quite a way down in the processing for a page, and $post is available globally a few actions before that, so I would try logic something like the following code (untested – mainly to get the logic across):
function conditionally_add_css () { global $post; if ( strstr( $post->post_content, '[red]' ) ) { wp_enqueue_style('red-stylesheet', 'path/to/stylesheet.css'); } } add_action('get_header', 'conditionally_add_css');That may be too simplified, but basically, you hook a function to fire before wp_head (either get_header, template_redirect, wp, posts_selection etc), and that adds a link to a stylesheet. If the wp_enqueue doesn’t work directly, then it may need to be extracted into another function that is hooked into wp_head. Obviously a final code block would also extract the shortcode into a variable so the code block could be re-used, and this would add another level of processing in.
Hi Gary, Thanks for writing. I agree that the function could be called a bit earlier but (in the plugin I was making) I wanted to print the styles into the head.
As for the function, there is one thing that I find issue with which led to a bit of the complexity of my solution:
You are only scanning the content of the global object $post. While this will definitely work where is_singular() returns true, it will not include the styles for archive, category, search, etc. This is why I chose to scan the “post_content” of every entry in the $posts array. This array contains all the posts on the current page view.
As for this part:
You may be right here. I actually forget why I thought that scanning for the shortcode was a bad idea… but i did and that’s why I used an html comment instead. Hmmm… maybe I’m just overly paranoid :) This will probably work fine and cut back on the wealth of function calls provided by apply_filters().
Thanks for the suggestions!
-Mike