It’s for a developer easy to criticize a plugin that has just been created, to find random security flaws or over-looked elements of the design that passed unnoticed through the testing phase. As much as I love developing plugins, I’m usually very nervous about other developers criticizing my work, especially in private emails to clients ;) I can spend hours creating all of these perfect features, only for another developer to come along and criticize something that I missed while I was concentrating on a million other things. Futuremore, WordPress plugin development is usually only handled by one developer, so that means you’re in charge of design, security, bug-checking and of adding new features. That sucks, because you’re directly responsible for all of that as well.

All it takes if for one person to report your plugin, and they’ll suspend it from the WordPress repository. This can take months to amend.

I’ve been on the giving end and receiving end of this type of criticism, and I’ve also had to rebuild very poorly designed plugins. That’s why I decided to create a checklist for myself, so that I can look this over whenever I’m developing a new WordPress plugin.

Security

Add nonce fields to all admin forms. I use them  like this:

<form method="post" action="<?php echo $_SERVER['REQUEST_URI']; ?>" >

<input type="text" name="users_data" value="" />

<?php wp_nonce_field( 'plugin_name-update', 'field_to_update' ); ?>

<input type="submit" value="Save Data" />

</form>

And then in the receiving part of the data:

<?php

if ( isset($_POST['users_data']) && check_admin_referrer( 'plugin_name-update', 'field_to_update' ) ) {

// Perform secure update

}

?>

No externally accessible files. I recently worked on a site that used a .php file to make updates to the plugin’s data. He used the whole ‘include wp_load.php’ thing to get access to the database, and then made updates. In my experience, this is a very bad thing. The .php file turned out to be accessible by anyone (even people who weren’t administrators), and you could actually search Google for the website name and the name of the PHP file, and get access to a whole lot of data. This plugin was actually used by tens of thousands of people, and every one of them had this security flaw. Don’t let this developer be you – keep your files linked through the regular WordPress hooks; no wp_load funny business.

Put confirmation steps between clicking a button and performing actions. I recently developed a plugin that had a huge security flaw in it. I developed it for a company, not for popular release, so luckily I only had to make the update to fix the plugin. Basically, the plugin had a function where you could delete data from the database by clicking a button. The database was set to automatically increment a certain id number, and the delete was based on that number. The database wasn’t configured properly and without me knowing about it, the increment wasn’t working. Needless to say, as soon as someone clicked the delete button, it deleted all the data from the database, instead of just the row that they wanted to delete. I could have been in a lot of trouble for this one, but luckily the boss understood that it wasn’t entirely a coding bug. Learn from my mistakes, and put a confirmation form between the ‘delete’ button, and the actual deleting process. That way the user can check that they are really deleting the row that they want deleted.

When you can, use archiving instead of deleting. This is another thing that I learned from the mishap mentioned before. When all of that data was deleted, it really was gone. I couldn’t just magically revive the data like a lot of people expect from developers – accidentally deleting data you didn’t actually want to delete is an absolute nightmare. In this case, I created an extra field in the database where I could specify whether a row of information was ‘archived’ (which to me means ‘deleted’), or if it was still active. Once the user clicks delete, this plugin continues to act as if the data has actually been deleted; in reality, though, we can still revive this information if need be. This is a beautiful safety-net to have.

Use nonce fields especially when using AJAX. It’s really not that hard to figure out how to do it, and you simply can’t be lazy with this one. You can do it like this – this is how I do my JS enqueues:

<?php
$script_url = WP_PLUGIN_URL . '/plugin/js/script.js';
$script_file = WP_PLUGIN_DIR . '/plugin/js/script.js';
if ( file_exists($script_file) ) {
    wp_register_script( 'plugin_script', $script_url );
    wp_enqueue_script( 'plugin_script' );
}
$plugin_ajax_nonce = wp_create_nonce ('plugin-ajax-nonce');

$plugin_ajax_url = admin_url( 'admin-ajax.php' );

wp_localize_script( 'plugin_script', 'plugin_name', array( 'ajax_nonce' => $plugin_ajax_nonce, 'plugin_ajax_url' => plugin_ajax_url ));

?>

Then in that javascript file, you just make sure that it continues to send the nonce value to your receiving php file:

$.ajax({
    type: "POST",
    url: plugin_name.plugin_ajax_url,
    data: 'action=plugin_name_ajax' + '&plugin_ajax_none=' + plugin_name.ajax_nonce,
    success: function(msg){
      // Silence is golden.
    }
});

And then again in your receiving .php file:

<?php
add_action( 'wp_ajax_nopriv_plugin_name_ajax', 'plugin_name_submission_ajax' );
add_action( 'wp_ajax_plugin_name_ajax', 'plugin_name_submission_ajax' );
function plugin_name_submission_ajax () {
    $plugin_name_nonce = $_POST['plugin_ajax_nonce'];

wp_verify_nonce( ‘plugin-ajax-nonce’, $plugin_name_nonce );

}

?>

I wrote that using barely any references; I could write that stuff in my sleep, because I use it all the time for plugins.

Make sure your functions are unique, by adding a prefix. Whenever you define a function, put your prefix in it so that WordPress doesn’t confuse it with some other plugin’s function and cause the site to break. If your plugin is called ‘Brilliant Plugin’, don’t use ‘BP’ as your prefix, because that will obviously conflict with Buddy Press. Err on the side of being too careful, and make sure that your plugin doesn’t conflict.

Optimization

Keep CSS in .css files, and Javascript in .js files. This makes the content cacheable. If you need to send data to your javascript from a PHP file, do it using the localization code shown above. Your plugin should output as little inline CSS and JS as possible, really – how annoying is it when you install a plugin and find out that it’s wrecked your site’s beautiful optimization? Don’t be selfish, rather spend the extra time making this plugin’s scripts cacheable rather that just outputting it onto the person’s page.

Combine CSS and JS into as few files as possible. Instead of making the site go back and forth asking for files, try keep your files to a minimum. There is no need to have a reset.css file and main style.css for your ‘About Me’ widget – just combine your code into one file.

Use the ‘is_admin()’ function. Can you believe that some plugins load CSS and JS that is meant for the admin section in the front-end (I.E. every time a user visits the site)? By checking whether the user is in the admin or not (or better yet, by splitting your admin files and your front-end PHP files and only loading the appropriate ones), you save a lot of work for the browser at load time. This is totally non-negotiable; if you just realized that your plugin is making this mistake, please change it as soon as you can and spare the internet the inefficiency.

Use wp_enqueue_script() and wp_enqueue_style. If you need to use jQuery in your plugin, please don’t output a random script reference to the Google Code API for jQuery. Use wp_enqueue_script( ‘jquery’ ); instead, and avoid making the website load the same damn thing 8 times with different versions. Yes, it is better to load the script from Google because it’s likely to be cached, but you know what, if the user wants to do this they’ll use their own plugins to do so. Err on the side of being more careful with this, rather than causing a mess-up because you want to do something fancy.

Use arrays to store plugin options. I recently released a public plugin for a company, and it employed something like 100 small settings. There’s really no reason to create 100 new WordPress options, and update each one every time someone makes a change. That would mean 100 separate writes every time options are saved! Rather, separate your options into maybe 4 or 5 main categories of options, and then use an array in each option to store the settings. So here is an example:

$general_settings = array(

'plugin_enabled' => 'yes',

'use_custom_css' => 'no',

'show_alerts' => 'yes'

);

update_option( 'my_plugin_general', $general_settings  );

This saves us from storing and writing to three separate options whenever a general update is made.

Miscellaneous

Comment your code well, especially front-end code. You owe it to the person who installs your plugin to have it well-commented. If I install something that makes changes to my site, I feel like I deserve to know what that plugin is doing to it. Even if I don’t understand PHP, or whatever fancy function you’re employing, I still should be able to make sense of what all this code is doing. Please, be reasonable and add some commenting to your code so that users don’t feel at the mercy of developers more than they have to.

Create Software Fit for Change. Try to keep your code agile, because you’ll probably need to make updates in time. WordPress updates very often, and sometimes this can cause your plugin to break. There’s nothing worse than going back to a plugin months after its release and trying to figure out how to change one or two things. Keep things modular – even if it’s not just to save me rewriting code, I try to create new functions to handle “parts” of code, and I divide these parts up as makes sense to me. That way it gets very easy for me to see exactly what’s going on, because I can make sense of the functions and where they’re being used – it’s almost like a combination between functionality and commenting.