Ten Things Every WordPress Plugin Developer Should Know
Plugins are a major part of why WordPress powers millions of blogs and websites around the world. The ability to extend WordPress to meet just about any need is a powerful motivator for choosing WordPress over other alternatives. Having written several plugins myself, I’ve come to learn many (but certainly not all) of the ins-and-outs of WordPress plugin development, and this article is a culmination of the things I think every WordPress plugin developer should know. Oh, and keep in mind everything you see here is compatible with WordPress 3.0+.
Don’t Develop Without Debugging
The first thing you should do when developing a WordPress plugin is to enable debugging, and I suggest leaving it on the entire time you’re writing plugin code. When things go wrong, WordPress raises warnings and error messages, but if you can’t see them then they might as well have not been raised at all.
Enabling debugging also turns on WordPress notices, which is important because that’s how you’ll know if you’re using any deprecated functions. Deprecated functions may be removed from future versions of WordPress, and just about every WordPress release contains functions slated to die at a later date. If you see that you are using a deprecated function, it’s best to find its replacement and use that instead.
How to Enable Debugging
By default, WordPress debugging is turned off, so to enable it, open wp-config.php (tip: make a backup copy of this file that you can revert to later if needed) in the root of your WordPress installation and look for this line:
define('WP_DEBUG', false);
Replace that line with the following:
// Turns WordPress debugging on
define('WP_DEBUG', true);
// Tells WordPress to log everything to the /wp-content/debug.log file
define('WP_DEBUG_LOG', true);
// Doesn't force the PHP 'display_errors' variable to be on
define('WP_DEBUG_DISPLAY', false);
// Hides errors from being displayed on-screen
@ini_set('display_errors', 0);
With those lines added to your wp-config.php file, debugging is fully enabled. Here’s an example of a notice that got logged to /wp-content/debug.log for using a deprecated function:
[15-Feb-2011 20:09:14] PHP Notice: get_usermeta is deprecated since version 3.0! Use get_user_meta() instead. in C:\Code\Plugins\wordpress\wp-includes\functions.php on line 3237
With debugging enabled, keep a close eye on /wp-content/debug.log as you develop your plugin. Doing so will save you, your users, and other plugin developers a lot of headaches.
How to Log Your Own Debug Statements
So what about logging your own debug statements? Well, the simplest way is to use echo and see the message on the page. It’s the quick-and-dirty-hack way to debug, but everyone has done it one time or another. A better way would be to create a function that does this for you, and then you can see all of your own debug statements in the debug.log file with everything else.
Here’s a function you can use; notice that it only logs the message if WP_DEBUG is enabled:
function log_me($message) {
if (WP_DEBUG === true) {
if (is_array($message) || is_object($message)) {
error_log(print_r($message, true));
} else {
error_log($message);
}
}
}
And then you can call the log_me function like this:
log_me(array('This is a message' => 'for debugging purposes'));
log_me('This is a message for debugging purposes');
Use the BlackBox Debug Bar Plugin
I only recently discovered this plugin, but it’s already been a huge help as I work on my own plugins. The BlackBox plugin adds a thin black bar to the top of any WordPress post or page, and provides quick access to errors, global variables, profile data, and SQL queries.
![]()
Clicking on the Globals tab in the bar shows all of the global variables and their values that were part of the request, essentially everything in the $_GET, $_POST, $_COOKIE, $_SESSION, and $_SERVER variables:

The next tab is the Profiler, which displays the time that passed since the profiler was started and the total memory WordPress was using when the checkpoint was reached:

You can add your own checkpoints to the Profiler by putting this line of code anywhere in your plugin where you want to capture a measurement:
apply_filters('debug', 'This is a checkpoint');
Perhaps the most valuable tab in the BlackBox plugin is the SQL tab, which shows you all of the database queries that executed as part of the request. Very useful for determining long-running database calls:

And finally we have the Errors tab, which lists all of the notices, warnings, and errors that occurred during the request:

By providing quick access to essential debug information, the BlackBox plugin is a big-timer when it comes to debugging your WordPress plugin.
Prefix Your Functions
One of the first things that bit me when I started developing WordPress plugins was finding out that other plugin developers sometimes use the same names for functions that I use. For example, function names like copy_file(), save_data(), and database_table_exists() have a decent chance of being used by other plugins in addition to yours.
The reason for this is because when WordPress activates a plugin, PHP loads the functions from the plugin into the WordPress execution space, where all functions from all plugins live together. There is no separation or isolation of functions for each plugin, which means that each function must be uniquely named.
Fortunately, there is an easy way around this, and it’s to name all of your plugin functions with a prefix. For example, the common functions I mentioned previously might now look like this:
function myplugin_copy_file() {
}
function myplugin_save_data() {
}
function myplugin_database_table_exists() {
}
Another common naming convention is to use a prefix that is an abbreviation of your plugin’s name, such as “My Awesome WordPress Plugin”, in which case the function names would be:
function mawp_copy_file() {
}
function mawp_save_data() {
}
function mawp_database_table_exists() {
}
There is one caveat to this, however. If you use PHP classes that contain your functions (which in many cases is a good idea), you don’t really have to worry about clashing with functions defined elsewhere. For example, let’s say you have a class in your plugin named “CommonFunctions” with a copy_file() function, and another plugin has the same copy_file() function defined, but not in a class. Invoking the two functions would look similar to this:
// Calls the copy_file() function from your class
$common = new CommonFunctions();
$common->copy_file();
// Calls the copy_file() function from the other plugin
copy_file();
By using classes, the need to explicitly prefix your functions goes away. Just keep in mind that WordPress will raise an error if you use a function name that’s already taken, so keep an eye on the debug.log file to know if you’re in the clear or not.
Global Paths Are Handy
Writing the PHP code to make your plugin work is one thing, but if you want to make it look and feel good at the same time, you’ll need to include some images, CSS, and perhaps a little JavaScript as well (maybe in the form of a jQuery plugin). And in typical fashion, you’ll most likely organize these files into their own folders, such as “images”, “css”, and “js”.
That’s all well and good, but how should you code your plugin so that it can always find those files, no matter what domain the plugin is running under? The best way that I’ve found is to create your own global paths that can be used anywhere in your plugin code.
For example, I always create four global variables for my plugins, one each for the following:
- The path to the theme directory
- The name of the plugin
- The path to the plugin directory
- The url of the plugin
For which the code looks like this:
if (!defined('MYPLUGIN_THEME_DIR'))
define('MYPLUGIN_THEME_DIR', ABSPATH . 'wp-content/themes/' . get_template());
if (!defined('MYPLUGIN_PLUGIN_NAME'))
define('MYPLUGIN_PLUGIN_NAME', trim(dirname(plugin_basename(__FILE__)), '/'));
if (!defined('MYPLUGIN_PLUGIN_DIR'))
define('MYPLUGIN_PLUGIN_DIR', WP_PLUGIN_DIR . '/' . MYPLUGIN_PLUGIN_NAME);
if (!defined('MYPLUGIN_PLUGIN_URL'))
define('MYPLUGIN_PLUGIN_URL', WP_PLUGIN_URL . '/' . MYPLUGIN_PLUGIN_NAME);
Having these global paths defined lets me write the code below in my plugin anywhere I need to, and I know it will resolve correctly for any website that uses the plugin:
$image = MYPLUGIN_PLUGIN_URL . '/images/my-image.jpg';
$style = MYPLUGIN_PLUGIN_URL . '/css/my-style.css';
$script = MYPLUGIN_PLUGIN_URL . '/js/my-script.js';
Store the Plugin Version for Upgrades
When it comes to WordPress plugins, one of the things you’ll have to deal with sooner or later is upgrades. For instance, let’s say the first version of your plugin required one database table, but the next version requires another table. How do you know if you should run the code that creates the second database table?
I suggest storing the plugin version in the WordPress database so that you can read it later to decide certain upgrade actions your plugin should take. To do this, you’ll need to create a couple more global variables and invoke the add_option() function:
if (!defined('MYPLUGIN_VERSION_KEY'))
define('MYPLUGIN_VERSION_KEY', 'myplugin_version');
if (!defined('MYPLUGIN_VERSION_NUM'))
define('MYPLUGIN_VERSION_NUM', '1.0.0');
add_option(MYPLUGIN_VERSION_KEY, MYPLUGIN_VERSION_NUM);
I certainly could have simply called add_option('myplugin_version', '1.0.0'); without the need for the global variables, but like the global path variables, I’ve found these just as handy for using in other parts of a plugin, such as a Dashboard or About page.
Also note that update_option() could have been used instead of add_option(). The difference is that add_option() does nothing if the option already exists, whereas update_option() checks to see if the option already exists, and if it doesn’t, it will add the option to the database using add_option(); otherwise, it updates the option with the value provided.
Then, when it comes time to check whether or not to perform upgrade actions, your plugin will end up with code that looks similar to this:
$new_version = '2.0.0';
if (get_option(MYPLUGIN_VERSION_KEY) != $new_version) {
// Execute your upgrade logic here
// Then update the version value
update_option(MYPLUGIN_VERSION_KEY, $new_version);
}
Use dbDelta() to Create/Update Database Tables
If your plugin requires its own database tables, you will inevitably need to modify those tables in future versions of your plugin. This can get a bit tricky to manage if you’re not careful, but WordPress helps alleviate this problem by providing the dbDelta() function.
A useful feature of the dbDelta() function is that it can be used for both creating and updating tables, but according to the WordPress codex page “Creating Tables with Plugins”, it’s a little picky:
- You have to put each field on its own line in your SQL statement.
- You have to have two spaces between the words PRIMARY KEY and the definition of your primary key.
- You must use the keyword KEY rather than its synonym INDEX and you must include at least one KEY.
Knowing these rules, we can use the function below to create a table that contains an ID, a name, and an email:
function myplugin_create_database_table() {
global $wpdb;
$table = $wpdb->prefix . 'myplugin_table_name';
$sql = "CREATE TABLE " . $table . " (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(100) NOT NULL DEFAULT '',
email VARCHAR(100) NOT NULL DEFAULT '',
UNIQUE KEY id (id)
);";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
Important: The dbDelta() function is found in wp-admin/includes/upgrade.php, but it has to be included manually because it’s not loaded by default.
So now we have a table, but in the next version we need to expand the size of the name column from 100 to 250. Fortunately dbDelta() makes this straightforward, and using our upgrade logic previously, the next version of the plugin will have code similar to this:
$new_version = '2.0.0';
if (get_option(MYPLUGIN_VERSION_KEY) != $new_version) {
myplugin_update_database_table();
update_option(MYPLUGIN_VERSION_KEY, $new_version);
}
function myplugin_update_database_table() {
global $wpdb;
$table = $wpdb->prefix . 'myplugin_table_name';
$sql = "CREATE TABLE " . $table . " (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(250) NOT NULL DEFAULT '', // Bigger name column
email VARCHAR(100) NOT NULL DEFAULT '',
UNIQUE KEY id (id)
);";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
While there are other ways to create and update database tables for your WordPress plugin, it’s hard to ignore the flexibility of the dbDelta() function.
Know the Difference Between include, include_once, require, and require_once
There will come a time during the development of your plugin where you will want to put code into other files so that maintaining your plugin is a bit easier. For instance, a common practice is to create a functions.php file that contains all of the shared functions that all of the files in your plugin can use.
Let’s say your main plugin file is named myplugin.php and you want to include the functions.php file. You can use any of these lines of code to do it:
include 'functions.php';
include_once 'functions.php';
require 'functions.php';
require_once 'functions.php';
But which should you use? It mostly depends on your expected outcome of the file not being there.
- include: Includes and evaluates the specified file, throwing a warning if the file can’t be found.
- include_once: Same as include, but if the file has already been included it will not be included again.
- require: Includes and evaluates the specified file (same as include), but instead of a warning, throws a fatal error if the file can’t be found.
- require_once: Same as require, but if the file has already been included it will not be included again.
My experience has been to always use include_once because a) how I structure and use my files usually requires them to be included once and only once, and b) if a required file can’t be found I don’t expect parts of the plugin to work, but it doesn’t need to break anything else either.
Your expectations may vary from mine, but it’s important to know the subtle differences between the four ways of including files.
Use bloginfo(‘wpurl’) Instead of bloginfo(‘url’)
By and large, WordPress is installed in the root folder of a website; it’s standard operating procedure. However, every now and then you’ll come across websites that install WordPress into a separate subdirectory under the root. Seems innocent enough, but the location of WordPress is critically important.
To demonstrate, in the “General Settings” section of the WordPress admin panel, you’ll find the “WordPress address (URL)” and “Site address (URL)” settings, and for sites where WordPress is installed into the root directory, they will have the exact same values:

But for sites where WordPress is installed into a subdirectory under the root (in this case a “wordpress” subdirectory), their values will be different:

At this stage it’s important to know the following:
- bloginfo(‘wpurl’) equals the “WordPress address (URL)” setting
- bloginfo(‘url’) equals the “Site address (URL)” setting
Where this matters is when you need to build URLs to certain resources or pages. For example, if you want to provide a link to the WordPress login screen, you could do this:
// URL will be http://mydomain.com/wp-login.php
<a href="<?php bloginfo('url') ?>/wp-login.php">Login</a>
But that won’t resolve to the correct URL in the scenario such as the one above where WordPress is installed to the “wordpress” subdirectory. To do this correctly, you must use bloginfo('wpurl') instead:
// URL will be http://mydomain.com/wordpress/wp-login.php
<a href="<?php bloginfo('wpurl') ?>/wp-login.php">Login</a>
Using bloginfo('wpurl') instead of bloginfo('url') is the safest way to go when building links and URLs inside your plugin because it works in both scenarios: when WordPress is installed in the root of a website and also when it’s installed in a subdirectory. Using bloginfo('url') only gets you the first one.
How and When to Use Actions and Filters
WordPress allows developers to add their own code during the execution of a request by providing various hooks. These hooks come in the form of actions and filters:
- Actions: WordPress invokes actions at certain points during the execution request and when certain events occur.
- Filters: WordPress uses filters to modify text before adding it to the database and before displaying it on-screen.
The number of actions and filters is quite large, so we can’t get into them all here, but let’s at least take a look at how they are used.
Here’s an example of how to use the admin_print_styles action, which allows you to add your own stylesheets to the WordPress admin pages:
add_action('admin_print_styles', 'myplugin_admin_print_styles');
function myplugin_admin_print_styles() {
$handle = 'myplugin-css';
$src = MYPLUGIN_PLUGIN_URL . '/styles.css';
wp_register_style($handle, $src);
wp_enqueue_style($handle);
}
And here’s how you would use the the_content filter to add a “Follow me on Twitter!” link to the bottom of every post:
add_filter('the_content', 'myplugin_the_content');
function myplugin_the_content($content) {
$output = $content;
$output .= '<p>';
$output .= '<a href="http://twitter.com/username">Follow me on Twitter!</a>';
$output .= '</p>';
return $output;
}
It’s impossible to write a WordPress plugin without actions and filters, and knowing what’s available to use and when to use them can make a big difference. See the WordPress codex page “Plugin API/Action Reference” for the complete list of actions and the page “Plugin API/Filter Reference” for the complete list of filters.
Tip: Pay close attention to the order in which the actions are listed on its codex page. While not an exact specification, my experimentation and trial-and-error has shown it to be pretty close to the order in which actions are invoked during the WordPress request pipeline.
Add Your Own Settings Page or Admin Menu
Many WordPress plugins require users to enter settings or options for the plugin to operate properly, and the way plugin authors accomplish this is by either adding their own settings page to an existing menu or by adding their own new top-level admin menu to WordPress.
How to Add a Settings Page
A common practice for adding your own admin settings page is to use the add_menu() hook to call the add_options_page() function:
add_action('admin_menu', 'myplugin_admin_menu');
function myplugin_admin_menu() {
$page_title = 'My Plugin Settings';
$menu_title = 'My Plugin';
$capability = 'manage_options';
$menu_slug = 'myplugin-settings';
$function = 'myplugin_settings';
add_options_page($page_title, $menu_title, $capability, $menu_slug, $function);
}
function myplugin_settings() {
if (!current_user_can('manage_options')) {
wp_die('You do not have sufficient permissions to access this page.');
}
// Here is where you could start displaying the HTML needed for the settings
// page, or you could include a file that handles the HTML output for you.
}
By invoking the add_options_page() function, we see that the “My Plugin” option has been added to the built-in Settings menu in the WordPress admin panel:

The add_options_page() function is really just a wrapper function on top of the add_submenu_page() function, and there are other wrapper functions that do similar work for the other sections of the WordPress admin panel:
- add_dashboard_page()
- add_posts_page()
- add_media_page()
- add_links_page()
- add_pages_page()
- add_comments_page()
- add_theme_page()
- add_plugins_page()
- add_users_page()
- add_management_page()
How to Add a Custom Admin Menu
Those wrapper functions work great, but what if you wanted to create your own admin menu section for your plugin? For example, what if you wanted to create a “My Plugin” admin section with more than just the Settings page, such as a Help page? This is how you would do that:
add_action('admin_menu', 'myplugin_menu_pages');
function myplugin_menu_pages() {
// Add the top-level admin menu
$page_title = 'My Plugin Settings';
$menu_title = 'My Plugin';
$capability = 'manage_options';
$menu_slug = 'myplugin-settings';
$function = 'myplugin_settings';
add_menu_page($page_title, $menu_title, $capability, $menu_slug, $function);
// Add submenu page with same slug as parent to ensure no duplicates
$sub_menu_title = 'Settings';
add_submenu_page($menu_slug, $page_title, $sub_menu_title, $capability, $menu_slug, $function);
// Now add the submenu page for Help
$submenu_page_title = 'My Plugin Help';
$submenu_title = 'Help';
$submenu_slug = 'myplugin-help';
$submenu_function = 'myplugin_help';
add_submenu_page($menu_slug, $submenu_page_title, $submenu_title, $capability, $submenu_slug, $submenu_function);
}
function myplugin_settings() {
if (!current_user_can('manage_options')) {
wp_die('You do not have sufficient permissions to access this page.');
}
// Render the HTML for the Settings page or include a file that does
}
function myplugin_help() {
if (!current_user_can('manage_options')) {
wp_die('You do not have sufficient permissions to access this page.');
}
// Render the HTML for the Help page or include a file that does
}
Notice that this code doesn’t use any of the wrapper functions. Instead, it calls add_menu_page() (for the parent menu page) and add_submenu_page() (for the child pages) to create a separate “My Plugin” admin menu that contains the Settings and Help pages:

One advantage of adding your own custom menu is that it’s easier for users to find the settings for your plugin because they aren’t buried within one of the built-in WordPress admin menus. Keeping that in mind, if your plugin is simple enough to only require a single admin page, then using one of the wrapper functions might make the most sense. But if you need more than that, creating a custom admin menu is the way to go.
Provide a Shortcut to Your Settings Page with Plugin Action Links
In much the same way that adding your own custom admin menu helps give the sense of a well-rounded plugin, plugin action links work in the same fashion. So what are plugin action links? It’s best to start with a picture:

See the “Deactivate” and “Edit” links underneath the name of the plugin? Those are plugin action links, and WordPress provides a filter named plugin_action_links for you to add more. Basically, plugin action links are a great way to add a quick shortcut to your most commonly used admin menu page.
Keeping with our Settings admin page, here’s how we would add a plugin action link for it:
add_filter('plugin_action_links', 'myplugin_plugin_action_links', 10, 2);
function myplugin_plugin_action_links($links, $file) {
static $this_plugin;
if (!$this_plugin) {
$this_plugin = plugin_basename(__FILE__);
}
if ($file == $this_plugin) {
// The "page" query string value must be equal to the slug
// of the Settings admin page we defined earlier, which in
// this case equals "myplugin-settings".
$settings_link = '<a href="' . get_bloginfo('wpurl') . '/wp-admin/admin.php?page=myplugin-settings">Settings</a>';
array_unshift($links, $settings_link);
}
return $links;
}
With this code in place, now when you view your plugins list you’ll see this:

Here we provided a plugin action link to the Settings admin page, which is the same thing as clicking on Settings from our custom admin menu. The benefit of the plugin action link is that users see it immediately after they activate the plugin, thus adding to the overall experience.
Additional Resources
I’ve covered a lot in this article, but there’s plenty more out there to keep you busy awhile. The most comprehensive documentation for WordPress plugin development can be found on the WordPress Codex, a huge collection of pages documenting everything that is WordPress. Below are some of the more pertinent links from the codex you’ll need:
- Writing a Plugin – If you’re brand new to WordPress plugin development, start here.
- Plugin API – Action Reference
- Plugin API – Filter Reference
- Pluggable Functions
- Data Validation
I also suggest reading Joost de Valk’s article “Lessons Learned From Maintaining a WordPress Plug-In“, which provides more good tips on WordPress plugin development.
(vf) (ik)



Eddie Gear
March 8th, 2011 5:48 amGreat insights Dave. I loved the way you have pointed out the things that any developer that needs to keep track of. Thanks for sharing.
Frederick Luna
March 8th, 2011 6:07 amIntersting stuff about adding a custom menu and a settings page :D great post Dave.
Matt Rittman
March 8th, 2011 6:26 amGreat article Dave! This will really help out my future endeavors with WordPress plugin development. Thanks for sharing!
David
March 8th, 2011 6:27 amThis is brilliant, thanks for the write up. Bookmarked! I see that you recommend the Blackbox debug plugin. Having just started creating my own plugins, why this and not the DebugBar plugin (http://wordpress.org/extend/plugins/debug-bar/)? What’s the primary difference?
Dave Donaldson
March 8th, 2011 3:31 pm@David – I didn’t include the DebugBar plugin because it requires WordPress 3.1, which was only in Release Candidate mode at the time of writing. Both plugins provide similar debug info, so I suggest trying both and going with the one you like best.
Chris
March 8th, 2011 6:35 amGreat advices! I wish it would have been released much earlier – could have saved me a lot of time and trouble :)
Regarding bloginfo(wpurl): The codex says to use site_url() instead, for it also supports https, eg. if is_ssl() is true.
Dave Donaldson
March 8th, 2011 7:09 am@Chris – I can’t find the codex article that says to use site_url() instead of bloginfo(‘wpurl’), but yes, I suppose you could do that. There seems to be some confusion about whether or not site_url() includes the install directory of WordPress (if installed in a subdirectory), but looking at the core WordPress code it looks like it does.
Christofer
March 15th, 2011 12:03 am@Dave — Maybe, this is the page Chris referred to:
http://codex.wordpress.org/Function_Reference/get_bloginfo
“‘wpurl’ / ‘siteurl’ – Returns the ‘WordPress address (URI)’ [..] Consider using site_url() instead.”
Pete
March 8th, 2011 6:38 amNice article, thanks for sharing. I especially like the notes on debugging & function prefixes. Hopefully this will change the coding habits of some Developers when writing a WP plugin.
Donna Vitan
March 8th, 2011 6:48 amAnother awesome and well described tutorial Dave! I especially love how concise the part about creating admin settings panel and menu.
Cheers,
Thomas Bachmann
March 8th, 2011 6:55 amthanks!
… if you are a trained professional developer and familiar with PHP only half of it should be new (wordpress specifics)
Hung Bui
March 8th, 2011 7:04 amGreat stuff! Very useful thought.
kwasseem
March 8th, 2011 7:30 amPrecise and short; good, clear checkpoints!
Samundra Shrestha
March 8th, 2011 7:33 amReally loved the way you have mentioned the tricks for wordpress developer. I liked the way you described the usuage of require, require_once, include and include_once. All most of us use them but never seem to care since we all use include_once most of the time :).
I have already been using some of the tricks you have mentioned.
Great Article.
best regards,
Samundra Shrestha
Realtime Solutions, Nepal
Pieter Carette
March 8th, 2011 7:35 amGreat post. It makes me want to write a plugin! :)
Joseph Carrington
March 8th, 2011 7:37 amGood stuff. I’d point out that using a class as a fill-in for a namespace is not exactly the intention of OOP however.
Dave Donaldson
March 8th, 2011 7:53 am@Joseph Carrington – You’re right, using classes as a namespace fill-in is not the intention of OOP, but it is a common practice for avoiding function name collisions in PHP and WordPress.
Josh Hartman
March 8th, 2011 7:54 amGreat info! I wasn’t aware of the bloginfo(‘wpurl’) vs. bloginfo(‘url’) difference.
David
March 8th, 2011 7:54 amReally enjoyed this. It was encouraging that I found I was already doing most of these things. Some good tips, though, and I’ve updated my plugin with some of these suggestions.
I tried blackbar, but got tons of deprecated errors, which kind of made me laugh…
Dave Donaldson
March 8th, 2011 7:57 am@David – Hehe, yea, the irony of that was not lost on me :-)
Vivek Parmar
March 8th, 2011 8:12 amThanks for so much, getitng started with plugin development and wordpress theme development, and this article help me a lot in knowing many things that i do not know
scasmflop
March 8th, 2011 8:53 amGreat article, very informative without straying too far down rabbit trails. Thanks.
Raherian
March 8th, 2011 9:25 amHi,
Using blackbox-debug-bar with wp_debug on true display a bunch of notices, not very good for developping :/
Dave Donaldson
March 8th, 2011 9:28 am@Raherian – But that’s kind of the point of enabling debugging in the first place, to see all of the notices you wouldn’t have otherwise known were there.
Raherian
March 8th, 2011 12:14 pmYes but this plugin is usefull for developping, and i develop with wp_debug true, so having a bunch of notices while developping isn’t good.
It were a good idea to make a plugin available on repository only if he doesn’t have any notices, like themes ^^
aurel
March 8th, 2011 9:32 amSo far I developed only two themes, both of them using different methods (or no method apart from “hope it will work”)
it seems that these 10 plugins will make the process much easier to manage and hopefully to be able to understand by things work – that way things could stick in the head when developing the next theme
thanks
Alan Stevens
March 8th, 2011 9:32 amNice article, Dave. I don’t know if there is a codex article on using site_url() but this article has some good tips on guarding for ssl: http://blogs.sitepoint.com/2011/02/22/crimes-against-wordpress/
Thai Bui
March 8th, 2011 9:35 amGreat article. One of my biggest hurdles is debugging my plugins properly and these tips are outstanding. I usually use firephp to see what’s going on with the variables being passed back and fourth. Such an invaluable tool.
Conrad Muan
March 8th, 2011 10:16 amGreat post. I’ll have to try blackbar in my future projects.
I was also _just_ looking for a tutorial on adding menu items when I found this gem.
Thanks again,
Conrad
Zé
March 8th, 2011 10:55 amGreat article, but I am a little sad that i18n wasn’t contemplated from the beginning for all those strings, i.e.
$page_title = __('My Plugin Settings', 'myplugin_textdomain');instead of
$page_title = 'My Plugin Settings';It’s great for all other languages to have plugins that are translatable from the get go.
http://codex.wordpress.org/I18n_for_WordPress_Developers
Dave Donaldson
March 8th, 2011 2:55 pm@Ze – I mentioned on another comment that I thought about including internationalization but decided against it for this article. The topic is probably big enough for another article on its own, so maybe I’ll write that one next.
Drew
March 8th, 2011 11:48 amThanks for the lead on Blackbox!. I installed it mid-read on your post and played with it for 30 minutes on three of my sites. Then I remember I was reading your post so I came back to finish. Great roundup!
Tomáš Kapler
March 8th, 2011 12:12 pmone very common problem from US writers missing – forgotting internationalization so __(), _x(), _e() functions etc.
Dave Donaldson
March 8th, 2011 12:29 pm@Tomas – I thought about including a section on internationalization, but decided against it for this article. Maybe I’ll write another article just for that.
Brett Lewis
March 8th, 2011 3:23 pmThis is great :)
Thank you so much.
Phuoc Huynh
March 8th, 2011 6:24 pmThanks for this great. This is awesome !
Patent Litigation
March 8th, 2011 10:03 pmHere is a similar story
Until recently, migrating all your blogs to a single multisite blog was a massive task because of the need to resolve conflicting IDs across different tables. Fortunately, newer WordPress versions have built-in functions to make life a bit easier for all of us.
Steve mg
March 8th, 2011 11:02 pmAs a customer of WordPress plugins, my biggest gripe is the lack of screenshots displaying what the plugin does. Is it really that hard to grab screens? Great you’ve made this fantastic plugin, and also great that you’ve put it up for free, just telling me what it does, isn’t going to completely sell me on it.
Screenshots, do it.
Mick
March 8th, 2011 11:42 pmI wish every single plugin developer followed these simple rules!
It would make life a little bit easier
Justin
March 9th, 2011 12:43 amThis is a great article. Thanks for consolidating all of this information instead of making me search everywhere for it!
Jerry
March 9th, 2011 12:45 amDave, Awesome post. Pretty helpful for green-hands like me for wordpress.
Usually we use netbeans and X-debug for debugging. This time we could try blackbox for help :)
One question: How do you do unit test for new built WP plugin? Use PHPUnit or something else? I am quite new to the test procedure of PHP, any help will be much appreciated.
jeFFF
March 9th, 2011 12:59 amThanks a lot for this great article Dave !
Ruslan
March 9th, 2011 1:29 amOne thing that you have understand about include vs include_once, require vs require_once is performance penalty.
If you develop your code, and you are 100% for sure, that after you included your file in following code it wont be included again, never use *_once functions. They are more resource eating that simple include or require.
Read this article to understand difference in C level:
http://www.techyouruniverse.com/software/php-performance-tip-require-versus-require_once
Fernando Agüero
March 9th, 2011 1:42 amOne of the best post written in Smashing Magazine. Now I have no doubts.
Thank you!
Baltech
March 9th, 2011 2:13 amGreat you’ve made this fantastic plugin, and also great that you’ve put it up for free.It seems that these 10 plugins will make the process much easier to manage and hopefully to be able to understand by things. Thanks for give us this information.
Baltech
March 9th, 2011 2:14 amGreat you’ve made this fantastic plugin, and also great that you’ve put it up for free.It seems that these 10 plugins will make the process much easier to manage and hopefully to be able to understand by things. Thanks for this useful knowledge..
Thorsten
March 9th, 2011 2:36 amA very important topic that is missing here is Data Validation.
http://codex.wordpress.org/Data_Validation
Max
March 9th, 2011 3:05 amOne bad thing was described above.
When webmaster changes wp-content directory name, following code will break the site:
define('MYPLUGIN_THEME_DIR', ABSPATH . 'wp-content/themes/' . get_template());You should use following code instead wrong one:
define('MYPLUGIN_THEME_DIR', WP_CONTENT_DIR . 'themes/' . get_template());Dave Donaldson
March 9th, 2011 6:10 am@Max – Good point, thanks for catching that. Using WP_CONTENT_DIR instead of a hardcoded ‘wp-content’ string would be better.
YellowShark
March 15th, 2011 9:53 amThanks for pointing this out, I 100% concur – every WP install I do, I move the wp-content to site root, so i wind up with a nice structure like:
/themes
/plugins
/wordpress
/index.php
Developers should definitely utilize WP_PLUGIN_DIR and WP_CONTENT_DIR. Google either of those terms for more info on how to leverage them.
STPo
March 9th, 2011 4:17 amAnd please: do NOT fill the front-end HTML with plugin-specific CSS/jQuery/meta crap without a way to properly erase them if it is not required. I code my own front-end markup and i’m tired to see jQuery/Prototype/Mootools included everytime I use a plugin…
PLEASE!
catalin cimpanu
March 9th, 2011 4:49 amHell of a job on this article.
wparena
March 9th, 2011 5:36 amAlthough my current WordPress blogs have some of the SeO plugins installed I have found a good tips on your list that I must to follow
djavupixel
March 9th, 2011 10:56 amGood WordPress summary to develop a WordPress plugin.
Great work and good explanations!
Douglas Bonneville
March 9th, 2011 11:47 amDave: Great article – I’m going to give making a plugin a shot pretty soon and I’ll start here. I’ve edited a few, but this outline really lowers the threshold to new development. This is one of the best WordPress articles I’ve seen in a while, and certainly on my list of top articles at Smashing!
darell
March 9th, 2011 4:36 pmThanks for your post, it’s very helpful for my daily work and thoughts.
Jurdefur
March 10th, 2011 1:38 amSeriously why is everybody still using wordpress its the biggest crap of code i ever seen, its like IE6 under the browsers…
Christian K. Nordtømme
March 10th, 2011 2:29 amThanks for this. I’m just now learning how to develop my own plugins, and have bookmarked this post.
I also wish more plugin developers made the plugin’s documentation and help easier accessible, though.
I don’t think digging up readme files from the plugin folder, rummaging through every page on the author’s website or trying to decipher the code in search of basic answers should be neccessary. Instead, a “Quick Start” action link (either to 100% self-explanatory settings or to a readme file) should be standard.
jakub
March 10th, 2011 2:42 amThis is superuseful article! In-depth yet still accessible for occassional developers. Much better than “25 nice inspirational designs in pink” or “100 beautiful photos of furry pets” type stuff…
Marty
March 10th, 2011 3:08 pmexcellent post… stumbled.. :)
seedy
March 10th, 2011 11:38 pmthese articles are getting lamer and lamer
web3mantra
March 11th, 2011 3:18 amThanks for sharing this post.I like this post.It contains good examples of wordpress plugin developer should know.
ig-ffm
March 12th, 2011 11:16 amI really appreciate your posting. Thanks.
There is one issue I’ld like to add: localization.
I have to translate all kinds of plugins and that can be a really annoying waste of time, if a plugin is not meant to be localized.
Rahul
March 13th, 2011 10:23 amGreat post. Really helps a lot. But need to ask something. I saw this effect on a recently launched blog with wp posts coming in like a js gallery slider.
http://sputznik.com/
How have they achieved this transition and post thing? Is there a plugin that I could use? Thanks for the help.
Dave Donaldson
March 14th, 2011 4:07 am@Rahul – It looks like that transition is done with jQuery and the easing plugin for jQuery. Just give it a quick Google and you’ll find what you need.
Rahul
March 24th, 2011 11:23 amThanks a ton. Is there any plugin for this?
Manny Fleurmond
March 13th, 2011 9:26 pmI also recommend using the plugins_url() function to find your plugins base directory:
http://codex.wordpress.org/Function_Reference/plugins_url
Dax Davis
March 13th, 2011 9:52 pmThis is a great post. Thanks for bringing it all together in one place.
Valentinas
March 14th, 2011 1:28 amGreat article! Particularly the part about using wp_debug is a must-read for every plugin developer.
By the way, just wanted to point out that there is a function wp_login_url() for login url. People should use this, because some plugins may modify it with filter that is inside it and bloginfo(‘wpurl’) . “/wp-login.php” is not necessary the right login URL.
Dave Donaldson
March 14th, 2011 4:08 am@Valentinas – Good catch about the wp_login_url() function, thanks for pointing that out.
Gavin Potts
March 14th, 2011 7:34 amAwesome post. 9 of 10 i’ve found extremely interesting and enlightening…
I’d just like to query the tip: “Use bloginfo(‘wpurl’) Instead of bloginfo(‘url’)”. I believe this is bad advice.
When developing a WordPress website, developers may be forced to work in a live environment with a holding page in place. If you use ‘wpurl’ in your themes/plugins, this will fail, as you will need to rename index.php and point ‘siteurl’ to the index page to have the website up for development.
If all of your internal links are output with the ‘wpurl’ parameter, these will link to your holding page and you will have to type permalinks into your browser manually instead of clicking on links within the website.
Jim Krill
March 14th, 2011 6:54 pmThis is a great article for anyone wanting to develop a WordPress plugin. Hopefully it will lead to some brilliant plug-ins which will enrich the WordPress community even more.
Thanks Dave!
Philip Norton
March 15th, 2011 1:58 amThis is a great list, but one thing that every WordPress developer should know about (more important than knowing about require_once() etc) is nonces. Nonces are a very important part of WordPress that help to combat security problems and should definitely be on this list.
http://codex.wordpress.org/WordPress_Nonces
Jason LaRose
March 15th, 2011 9:15 amOne thing every WordPress plugin developer should do is NOT include default styles for their generated HTML. Doing so means reverse engineering for other WordPress developers. If you do add default styles, add in the option to deactivate the CSS.
Dan Coulter
March 15th, 2011 12:12 pmNothing about wp_enqueue_script()?
Permana Jayanta
March 15th, 2011 6:40 pmReally helpful post as I’m writing WordPress plugin now. Maybe should added list of functions that can help plugin development, instead of writing a functions again.
Kyathi
March 16th, 2011 10:24 pmThank you.. superb.. !!! its really helpful…
Sterling Hamilton
April 9th, 2011 11:15 amHey there!
A few things…
In regards to dbDelta:
“require_once(ABSPATH . ‘wp-admin/includes/upgrade.php’);”
That means you are now including a core WP file into your plugin…not really recommended. Further more, by doing that – it will start throwing aggressive cache busting headers. This will get in the way of caching plugins and proxies.
Also – rather than prefixing every function, it’s better to just do an Object in PHP and give that a unique name. That way prefixing is not needed – just a unique object name.
And the BlackBox plugin has been known to drastically slow down servers, so it should not be used in any sort of production environment.
Also – you want to load your plugins meta data rather than creating your own. So version number/plugin path/etc are able to be loaded like this: https://gist.github.com/911684
Another really good tip…do NOT rely on environment variables like HTTP_HOST, REQUEST_URI and such.
These tend to be unreliable – its best to use the wordpress variables for determining locations and what not.
cogmios`
May 6th, 2011 10:30 amBecause of the topic the “Objective Best Practices for Plugin Development?” on http://wordpress.stackexchange.com/questions/715/objective-best-practices-for-plugin-development is also a good addition.
Maybe to add to the debugging part: I also found using Zend Server community edition together with PHP PDT increase my debugging abilities (I dumped xampp for it).
For making names unique well… I use php 5.3 with namespaces -end of problem- the language solves it for me. So I just state “this plugin is only for php 5.3 onwards” (although some discussion exist on “only 5.3″ http://groups.google.com/group/wp-hackers/browse_thread/thread/19dc7c7197f66754/9a60ad0b37ccaf1b?show_docid=9a60ad0b37ccaf1b&pli=1)
anyon
May 7th, 2011 2:25 pmrule #1 wordpress is coded badly you will not learn anything about design patterns or best practices from looking at the wordpress source code, use your initiative ;)
Brett Widmann
May 10th, 2011 7:22 pmThanks for these really awesome tips! They will be really helpful.
mikaweb
September 11th, 2011 2:28 amNice tips. I’m currently writing my first plugin and it helped me a lot especially for the upgrade system.
liskel
August 25th, 2012 4:22 amReally nice things to keep in mind and great for beginners exploring the deep depths of wordpress plugin development!
Thanks!
Ryan S
September 14th, 2012 12:09 amHi thanks for the tip, I just notice in Settings link, I think there is the better way to declare setting link.
something like this
$settings_link = ‘Settings‘;
Ryan S
Ryan S
September 14th, 2012 12:10 amOops sorry for this second comment, it does not look good on my previous comment.
$settings_link = 'Settings';Arch!tect
January 7th, 2013 11:27 pmthanks dude!
David Hazelden
February 17th, 2013 8:04 amGood stuff!
Byron Dunn
February 28th, 2013 7:50 amGreat article. Being new to WP development, this is really helpful. Even for those of us struggling to tweak plugins we use.
Manuel Marquez
March 21st, 2013 1:43 amThanks Dave,
These are fantastic tips to start quality plugin development. Please keep up the good work!
Much appreciated.
Cheers,
Manuel