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:
- Visit WordPress.org and download the most recent version to your harddrive.
- Install WAMP or MAMP if you won't be working on a server.
- Drag your downloaded WordPress folder into your websites folder.
- Create a new database with MySQL or the command line.
- 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
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.
- Within the plugins folder, right-click and create a new folder - call it "Tuts_Formatting".
- In your favorite code editor, save a blank document as 'index.php' and place it inside this folder.
- 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.
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".
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:
- Append the "Subscribe" info to the bottom of every tutorial.
- Replace all H2 tags with H3 tags. (Per our rules)
- 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.
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:
- The expression to search for.
- What are we searching through?
- 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
- 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.
- 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.
- Continuing on - tutorial_image[\'"]? -- We're searching for the string "tutorial_image" once again followed by a single or double quote.
- \s* means "look for zero or more spaces." As you might have guessed, we represent a space with "\s".
- Now, we need to match the image tag. You should now understand what the first section refers to: <img src=[\'"]?
- .+\. 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.
- 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.
- The final block simply closes out our image and div tags.
- 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:
- $match will be equal to 0, because the author forgot to wrap his images within divs.
- $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:
- The regular expression
- What to replace any matches with
- 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.
- Is the user on a single page?
- If they are, return $theContent and append the "Subscribe" information - referenced by the variable $end_of_tut.
- If they are not, just return $theContent only.
That's Our Plugin!
Return to your WordPress admin panel, and click on the "Plugins" tab. .
Next, activate the "Tuts_Formatting" plugin.
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
I've applied a bit of basic CSS to style our "tutorial_image" div.
H2 Tags
The H2 tags have now been successfully changed to H3s.
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.
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?
- Follow us on Twitter, or subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.
Related Posts
Check out some more great tutorials and articles that you might like
Plus Members
Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.











User Comments
( ADD YOURS )Philo April 24th
Watching it right now!
( )techietim April 24th
Whoops. Looks like you missed a quote on line 43.
add_filter(’the_content’, Tut_Formatting’);
( )Jeffrey Way April 24th
Ahh good eye! I’ve fixed it. Thanks.
( )Wayne April 24th
Interesting, I found a copy of this article here http://cssscoop.com/thescoop/a-crash-course-in-wordpress-plugin-development/
I know that you wrote it, so I figured I would let you know…
( )Philo April 24th
They posted it 2 times on front page lol. Damm Rippers
( )Jeffrey Way April 24th
This stuff is so rude.
Behrang April 24th
Great tut…thanks dude
( )Sam G. Daniel April 24th
Thanks for the article. It’s helpful to get started learning the in’s and out’s of creating a plugin.
( )Shane April 24th
Jeffrey – I haven’t watched this one yet. Perhaps I’ll get flamed for commenting before I’ve watched it.
I just wanted to say that your tutorials and screencasts are consistently some of the best on this fantastic site. I hope you feel the love here – it’s much appreciated that you take the time to write this stuff.
( )Jeffrey Way April 24th
Thanks, Shane. That’s really nice of you to say.
( )Yoosuf April 24th
downloading it now, but DD did it in WP series!, :S no comments!
( )Jeffrey Way April 24th
Drew created a little subscribe plugin. This one is more extensive.
( )yoosuf April 25th
exactly after watching it i realized
Jeffrey Way April 24th
By the way, you could always combine the H2 section into one block of code to save space. I did it this way for readability. If you want to reduce it, do:
$theContent = preg_replace(’/<(\/?)h2>/’, ‘<$1h3>’, $theContent);
( )Michael Rice April 24th
Very sweet tutorial. Simple, and useful!
( )Mexx April 24th
thx for the tutorial Jeffrey! Havent developed with wp yet. But hopefully will soon.
( )Soner Gönül April 24th
Perfect
( )Aaron Payne April 24th
NICE! thanks.
( )Nate April 24th
Nice tutorial Jeffrey – as always!!!
HOWEVER – there are sooo many tutorials on the basics of WP. It’s easy – too easy – to get a plugin going that can format certain things in certain ways….
but where are the tutorials that get down and dirty with database interaction? I want to learn how to add custom database tables, add data to those tables, display that data on the front end and the backend, learn how to manipulate it, learn how to use WP’s built in security features (nonce, etc), how to create your own plugin admin panel so you can add, edit, remove content – stuff like that.
I can read the provided documentation that WP has – but their documentation simply provides an explanation of how things work – not really any usable examples.
Think of the PHP.net manual – every function has a detailed explanation – and then user contributed demos and discussions….very helpful!
Anyway, Just sayin – I look forward to more intermediate and advanced WP tutorials.
Thanks!
( )Wassim April 24th
+1
( )lawrence77 April 24th
thanks Jeffrey as usual unusual!
You switched to MAC? why?
( )Jeffrey Way April 24th
I didn’t. I use both. Actually, lately I’ve been recording on the Mac, and editing with Windows.
( )lawrence77 April 24th
oh nice!
yoosuf April 26th
jeff proofs both are required for a teacher
, also
@jeff, try to add some ajax commenting plugin, while we are watching (i used to listen) to video when we are adding comments entire page is getting refreshed! so please
redouaneabouhafs April 24th
Good Tutorial Jeff
( )Nanochrome April 24th
Cool Screeencast This Will help me alot!!
( )What Espresso theme are you using?
Nanochrome April 24th
Is It My Theme Creamy? it sure does look like it
( )Jeffrey Way April 24th
I just can’t remember. I got it off the “Coffee House”. So maybe!
michael soriano April 25th
Nice one. Just what we need!
( )Decloedt Kristof April 25th
Okay i made plugin where you can upload image and manage them in the admin section. ( Its a advertisment/banner thing ). Now when they click delete, i wanted to show a popup that asked if they are sure they wanted to delete it. but thats normally with javascript, but then i got -headers already….. – error. I did it now with a redirect to the same page with a querystring and checking on the variables and printing some divs, with 2 buttons. But does anyone have a clue how you can add javascript to your plugin for this kind of stuff ?
( )zik April 25th
Thanks for the tut Jeff
( )Benjamin Reid April 25th
Love it, great screencast. Bookmarked for when I need to create one.
( )lawrence77 April 25th
download it or else someday they delete this post! maybe!
( )yoosuf April 25th
u re so funny yarr
lawrence77 April 26th
thanks!
chris April 25th
gona try it tomorrow
( )lawrence77 April 26th
Chris! Don’t wait for tomorrows
( )dams April 25th
too long omg. im dizzy
( )saurabh shah April 25th
wow nice screencast ….
( )Ethan April 26th
This is the most comprehensive tutorial on plugin development that I’ve ever seen. Nice Jeff!
( )Richard April 26th
Great tutorial! Waiting more interesting posts and videos.
( )Tom April 26th
Nice man! Phew, those Regular Expressions are tough!
( )Meshach April 26th
Wow.. Jeffrey is so smart. I’m amazed… :O
Thanks so much for the Screencast/Article Jeffrey!
( )Martyn Web April 27th
Ive not attempted creating any plugins myself yet but this has made it seem a whole lot easier to do.
Its good to see that plugins can be used to help more simple repetitive tasks as well rather than just more complicated one off features too.
( )Shibi Kannan April 27th
Wonderful,
( )I have been looking for this. We welcome more such great screen casts.
How about a plugin for using the custom fields for pulling in images and showing them elsewhere on the front page. Many themes provide this feature but I haven’t been able to decipher the code behind it all.
Keep up the good work
Webdev April 27th
Thank you. You outlined very well the main steps of creating a simple WP plugin.
( )Aaron April 27th
Jeffrey,
( )Great tutorial as usual. Would love more on WP plugins.
Thanks!
CgBaran Tuts April 28th
Great tutorial thanks
( )redwall_hp April 28th
For the H2 lines, you could use str_replace() instead. It’s generally faster than preg_replace for simpler search/replace operations.
Also, one thing that bothers me is that the operations are run on every page load. Seems like a waste of processing power to me. I’m sure there must be a filter that runs before a post is stored in the database…
( )Jorge Mudry May 4th
This is an excellent tutorial (like always) Tks very much for your time =).
I have to ask you something: Could you do a tutorial explaining how do you record the screencast? things like resolutions, export values and things like that?
thank you very much!!
Greetings.
Jorge
( )Matt May 4th
Great Screencast, Jeff! Always look forward to seeing more from you!
Is there any chance you could do another video or two or three about getting into more in-depth and advanced plugin capabilities, since this only scratches the surface (podPress being an example)? That would be amazing!
( )David Poindexter May 8th
That’s a great tutorial, and such high quality video, too! I watched it full screen on my 24″, and it was perfect.
I can cast a vote for some more advanced WP plugin tutorials, for sure.
( )Manuel May 19th
A really great Tutorial, – greetings from germany
( )Muhammad Adnan May 22nd
i really liked this plugin and will wait for advanced plugins now .
Thanks JW.
( )Massbase June 14th
Can you create a advanced plugin for databases queries and table installations, like how a plugin installed a new table to the db and queries stuff.
thanks
( )zach June 14th
I see you created a variable with the text, and once the plugin is installed it adds the text. I want to do something very similar, but have the text show up in a sidebar (using a widget to display data). Do I need to call a filter to call the widget/sidebar location of my text?
( )Jaspal Singh June 19th
very easy to do step-by-step tutorial to start wordpress plugin development.
( )رسين July 17th
very good tutorial ..
thanks
( )techprism October 17th
Nice Tutorial
( )VEry Good for those who just taking a start.