Advertisement

A Crash-Course in WordPress Plugin Development

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

Despite an extensive codex, many WordPress users remain unfamiliar with how to create their own custom plugins. In today's screencast, we'll start from scratch and build our first usable plugin. For this example, we'll write a simple "tuts formatting" function that allows a blog editor to more easily format articles.

Screencast

Step 1: Your First Plugin

When creating a WordPress plugin, the first step is obviously to make sure that you have access to a WordPress installation. If you don't currently have a copy:

  1. Visit WordPress.org and download the most recent version to your harddrive.
  2. Install WAMP or MAMP if you won't be working on a server.
  3. Drag your downloaded WordPress folder into your websites folder.
  4. Create a new database with MySQL or the command line.
  5. In Firefox, browse to your new folder and begin the installation.

* Note - this isn't a "getting started with WordPress" tutorial. As such, I won't go over the installation instructions in detail. Instead, I highly recommend that you review Drew Douglass' "WordPress for Designers" series on the ThemeForest blog.

The Plugins Folder

Folder Structure

Once WordPress is running, browse to wp-content -> plugins. This is where all of your plugins will be stored. Creating a new plugin is a simple matter of adding a new folder, index file, and inserting a few comments. Let's do that now.

  1. Within the plugins folder, right-click and create a new folder - call it "Tuts_Formatting".
  2. In your favorite code editor, save a blank document as 'index.php' and place it inside this folder.
  3. Paste in the following comments at the top of your blank page:
<?php 

/*
Plugin Name: Tuts Formatting
Plugin URI: http://net.tutsplus.com
Description: Saves me time.
Version: 1.0
Author: Jeffrey Way
Author URI: http://net.tutsplus.com
*/

WordPress recognizes these comments and then creates an option within your admin panel.

Plugin Info

Note that if these comments are not included, WordPress will NOT recognize your plugin. I recommend that you create a snippet and assign a keystroke if you'll be creating many plugins in the future. I used TextExpander and assigned a key of "plugincomments".

text expander

Step 2: Outlining Our Plugin

As I'm sure many of you can imagine, Nettuts+ requires our authors to follow a set of guidelines when submitting tutorials. For example:

  • Place all headings within H3 tags.
  • Wrap your images within divs with a class of tutorial image. (This adds the background and borders around every image on Nettuts+.)

Though most of our writers do it correctly, we often receive tutorials that need formatting revisions. In these instances, I have two options: return the tutorial to the sender, or make the changes myself. More than not, I typically make the changes myself.

Now wouldn't it be convenient if I could create a WordPress plugin that will handle some of the workload for me?

That's exactly what I did. However, that particular plugin might be a bit too advanced for your first one. Instead, I've created a "slimmed down" version that we'll be working with today. All that it will do is:

  1. Append the "Subscribe" info to the bottom of every tutorial.
  2. Replace all H2 tags with H3 tags. (Per our rules)
  3. Check to see if the author wrapped his images within divs. If he hasn't, our plugin will take care of it.

Step 3: Filters

From the WordPress codex...

"Filters are functions that WordPress passes data through, at certain points in execution, just before taking some action with the data (such as adding it to the database or sending it to the browser screen). Filters sit between the database and the browser (when WordPress is generating pages), and between the browser and the database (when WordPress is adding new posts and comments to the database); most input and output in WordPress passes through at least one filter. WordPress does some filtering by default, and your plugin can add its own filtering."

To clarify, filters allow us to "latch" on to certain sections before the page is rendered. In our case, we want to latch onto the main content before it is displayed. That way, we can modify the formatting, as outlined above. To accomplish this, we can create a filter for "the_content". Doing so is quite simple. Paste the following snippet into your index page.

add_filter('the_content', 'Tut_Formatting');

This block of code essentially means: "before display the content (or the body of the blog post), run the function "Tut_Formatting". Let's create that function now. Add the following just before your 'add_filter' code-block.

function Tut_Formatting($content) {
}

When we use "add_filter", we can pass the contents of the filtered item to our function - notice the $content parameter. In this case, $content will store the body of a specific blog posting.

$content

Step 4: The Function

Let's begin by appending the "subscribe" info to the bottom of each posting. Within your function, create a new variable called $end_of_tut.

$end_of_tut = <<<EOT
<ul class="webroundup">
    <li>Follow us on <a href="http://www.twitter.com/nettuts">Twitter</a>, or subscribe to the <a href="http://feeds.feedburner.com/nettuts" title="NETTUTS RSS Feed">NETTUTS RSS Feed</a> for more daily web development tuts and articles.</li></ul>

<p>
  <script type="text/javascript"><!--digg_url = "post permalink (not digg url)"; // --></script>
  <script src="http://digg.com/tools/diggthis.js" type="text/javascript"></script>
</p>	
EOT;

What you see above is the exact code that we use for every Nettuts+ posting. It merely contains links to our Twitter and RSS feeds, and then appends a Digg link. Digg provides that snippet of code at the bottom; just copy and paste it.

HereDocs

When mixing PHP and HTML, heredocs are an easy way to clean up your code. By using the syntax <<<KEY, we can then insert regular html. We designate the closing of our html block by restating our key - in this case, EOT (for end of text). For more information, review the "Diving into PHP: Day 8" video on the ThemeForest blog.

If this was all that we wanted to accomplish, we would only need to return the content plus this new block of html. Doing so is easy:

return $content . $end_of_tut;

Our function returns the body of the blog posting along with the "subscribe" info appended to the end. However, we're going to do much more than this, so let's continue on.

Regular Expressions

I chose to use regular expressions to handle the wrapping of our images. In case you forgot, we need to search the posting and check to see if the author wrapped all of their images within divs with a class of "tutorial_image". If they have, great - we don't need to do anything. However, if they haven't done so, we'll use regular expressions as our work-horse.

$match = preg_match_all('/<div class=[\'"]?tutorial_image[\'"]?>\s*<img src=[\'"]?.+\.(jpg|gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]\s*<\/div>/i', $content, $matches);

Preg_match_all is a PHP function that accepts three parameters:

  1. The expression to search for.
  2. What are we searching through?
  3. Where will the matches be stored?

We begin by searching through the content of the blog posting - represented by the $content variable - and checking if there are images wrapped within divs. If you're unfamiliar with regular expressions, don't worry - it's not that complicated.

/<div class=[\'"]?tutorial_image[\'"]?>\s*<img src=[\'"]?.+\.(jpg|gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]\s*<\/div>/i
  1. Regular expressions are wrapped within delimiters. These can be anything you like. I tend to follow the Perl syntax and use the / /. Notice how these wrap our expression.
  2. We begin by searching for "<div class=". Now with regular expressions, we must compensate for what the author MIGHT do. Some may prefer to wrap their attributes in single quotes, while others may omit them entirely (though they shouldn't). Knowing this, let's allow either a double quote, single quote, or nothing. We do this by: [\'"]? When characters are wrapped within brackets, only one character will be used. The back-slash is simply to escape the single quote. The question mark afterward means, "allow zero or one of the preceding character." This is how we allow for the author not using any quotes.
  3. Continuing on - tutorial_image[\'"]? -- We're searching for the string "tutorial_image" once again followed by a single or double quote.
  4. \s* means "look for zero or more spaces." As you might have guessed, we represent a space with "\s".
  5. Now, we need to match the image tag. You should now understand what the first section refers to: <img src=[\'"]?
  6. .+\. tells the engine to look for one or more of any character until it comes to a period. ("." refers to any character; "+" refers to one or more of the proceeding character; "\." literally refers to a period.
  7. Now we need to find the correct extension. We search for either jpg, gif, jpeg, or png following by a single, double, or no quote.
  8. The final block simply closes out our image and div tags.
  9. The "i" at the very end informs the engine that the letters should not case insensitive.

As you can imagine, it's very difficult to explain regular expressions with words. If that last section completely confused you, watch the screencast for a much more in-depth explanation.

Now that we've create our expression, we need to tell the engine exactly what we're searching through. As I'm sure you've guessed, we need to search through $content. Add this as your second parameter. The third parameters designates an array where the matches will be stored.

$match = preg_match_all('/<div class=[\'"]?tutorial_image[\'"]?>\s*<img src=[\'"]?.+\.(jpg|gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]\s*<\/div>/i', $content, $matches);

Checking for Matches

When we run this regular expressions, we can expect one of two outcomes:

  1. $match will be equal to 0, because the author forgot to wrap his images within divs.
  2. $match will be equal to 1 or more - proving that the author remembered to wrap his images, in which case we don't need to do anything.

We need to check the value of $match and proceed differently depending on its value.

if(!$match) 
{
	$theContent = preg_replace('/<img src=[\'"]?.+\.(jpg|gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]/', '<div class="tutorial_image">$0</div>', $content);
}
	
else 
{
	$theContent = $content;
}

We use !$match to check if the variable is equal to zero. If it is, we'll need to wrap our images accordingly. On the other hand, if it's equal to one or more, that expression will return false, in which case the "else" statement will run instead. Let's examine that first block:

We're creating a new variable called $theContent. Next, we use another PHP function called "preg_replace". This function accepts three parameters:

  1. The regular expression
  2. What to replace any matches with
  3. What to search.
preg_replace('/<img src=[\'"]?.+\.(jpg|gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]/', '<div class="tutorial_image">$0</div>', $content);

We're pasting in that same regular expression from before, minus the div tags. So, we're essentially finding all images within the content. The second parameter creates our div tag and pastes the matched image within - designated by $0, which returns the entire matched string. The third parameter informs the engine of what string we're searching through.

Else

Now, if $match is equal to one or more, the else statement will run instead. In this scenario, the $theContent variable will be equal to $content, or the original body of text from the_content. It's as simple as that!

Replacing H2s

The next step is to search for any uses of <h2> tags, and replace them with <h3>.

$theContent = preg_replace('/<h2>/', '<h3>', $theContent);
$theContent = preg_replace('/<\/h2>/', '</h3>', $theContent);

Hopefully, this syntax is becoming more clear. It should be rather obvious what the code is doing.

Step 5: Returning

The final step is to return the text from our function. However, we should consider something. We don't want the blog postings on the home page to contain the "subscribe" info as well. This will clutter the page with a bunch of Digg buttons. Let's reserve that for single postings. We can use a WordPress function called "is_single()" to determine if the user is on a single page or not.

return (is_single()) ? $theContent . $end_of_tut : $theContent;

Here, we're using the ternary operator. We can break this snippet down into three easy sections.

  1. Is the user on a single page?
  2. If they are, return $theContent and append the "Subscribe" information - referenced by the variable $end_of_tut.
  3. If they are not, just return $theContent only.

That's Our Plugin!

Return to your WordPress admin panel, and click on the "Plugins" tab. .

Plugins

Next, activate the "Tuts_Formatting" plugin.

Activation

Create a New Posting

If our plugin is working correctly, its magic will occur behind the scenes. Create a new blog posting and try it out! Create a couple of h2 headings, add some images, and then click "Publish". If everything worked correctly, the page will be modified accordingly. Using Firebug or "View Source", we can verify this.

Images Wrapped Within Divs

Firebug

I've applied a bit of basic CSS to style our "tutorial_image" div.

tutorial image added

H2 Tags

The H2 tags have now been successfully changed to H3s.

H2 to H3

Subscribe Info

If viewing a single posting, the "Subscribe" information should be appended to the bottom. Note that, if you're working on a local server, the Digg button won't display correctly. Don't worry, it'll work once you've transferred the files to your server.

Subscribe Info Appended

Final Plugin Code

<?php 

/*
Plugin Name: Tuts Formatting
Plugin URI: http://net.tutsplus.com
Description: Saves me time.
Version: 1.0
Author: Jeffrey Way
Author URI: http://net.tutsplus.com
*/	

function Tut_Formatting($content) {

	$end_of_tut = <<<EOT
<ul class="webroundup">
    <li>Follow us on <a href="http://www.twitter.com/nettuts">Twitter</a>, or subscribe to the <a href="http://feeds.feedburner.com/nettuts" title="NETTUTS RSS Feed">NETTUTS RSS Feed</a> for more daily web development tuts and articles.</li></ul>
<p>
<script type="text/javascript"><!--digg_url = "post permalink (not digg url)"; // -->
</script>
<script src="http://digg.com/tools/diggthis.js" type="text/javascript"></script></p>	
EOT;

	
	$match = preg_match_all('/<div class=[\'"]?tutorial_image[\'"]?>\s*<img src=[\'"]?.+\.(jpg|gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]\s*<\/div>/i', $content, $matches);

	if(!$match) 
	{
		$theContent = preg_replace('/<img src=[\'"]?.+\.(jpg||gif|jpeg|png)[\'"]?(\s.+)?[(\s\/>)|(>)]/', '<div class="tutorial_image">$0</div>', $content);
	}
	
	else 
	{
		$theContent = $content;
	}
	
	$theContent = preg_replace('/<h2>/', '<h3>', $theContent);
	$theContent = preg_replace('/<\/h2>/', '</h3>', $theContent);
    
    # Or you can combine those two lines above into one if you would rather.
    # $theContent = preg_replace(’/<(\/?)h2>/’, ‘<$1h3>’, $theContent);
	
	return (is_single()) ? $theContent . $end_of_tut : $theContent;

} // end of Tut_Formatting

add_filter('the_content', 'Tut_Formatting');

Hopefully, this relatively simple example will get you started with WordPress plugin development. I highly recommend that you review the codex to gain a greater understanding. The sky's the limit when creating WordPress plugins; there's very little that you can't do. What are your favorite plugins?


Advertisement