Consolidate Options with Arrays in your WordPress Plugins

If you’ve ever written a plugin for WordPress you’ve probably dealt with giving the end user options. Unless you’ve taken the low road and forced the user to directly edit the plugin file, “options” means a Settings screen in the WordPress admin, and most likely you are storing those options in the blog’s wp-options table. Over time, bloggers using various plugins can end up with hundreds of extraneous records in their options table*. If they ever stop using a plugin, or if they ever want to manually clear out options, this job can be made difficult by all the clutter, and the problem is made far worse when plugin authors add a new record for each separate setting in a plugin. Beyond the mess, plugins written this way generally make many more calls to the database than they need to, which adds unnecessary burden on the server.

I have seen and used plugins that added anywhere from ten to thirty new records to the options table, and a correspondent of mine reports that he has encountered a plugin that, upon activation, adds a whopping 140 new records to wp_options. Egad! There has to be a better way!

There is a better way. With a few simple changes to your code, plugin authors can put all the various settings into an array, and enter that array as a single record in the options table. This article will show you how.

On this site I use a plugin called ShrinkyLink. It performs the straightforward but useful function of shortening long URLs to a specified length, or replacing the link text entirely with a specified string. It was created by Andrew Rader, but as far as I can tell his web site no longer exists. For the purposes of this article, it has a number of good qualities:

  • It inserts 10 separate records into the WordPress options table
  • It is not likely to see further updates from the original author
  • It is useful and thus worthy of updating
  • The original author was kind enough to release it under the GPL2 license

The last version of ShrinkyLink was 0.2, released in 2006. Today we’re going to begin the rehabilitation of this plugin and create a version 0.3 that stores all its settings in a single array.

I’m going to split this into multiple sections:

  1. How to set and retrieve settings using arrays
  2. Upgrading your plugin’s Admin Screen form to use an array
  3. Upgrading existing settings from multiple records to a single array

Let’s get to it.

How to set and retrieve settings using arrays

WordPress allows plugin authors to easily add, manipulate, and retrieve records in the wp_options table, using the built-in add_option(), update_option(), get_option(), and delete_option() functions. When we use arrays for our settings, we will continue to use all of those functions, but we’re going to change the way we pass information through them.

If you would like to follow along with source files, download ShrinkyLink 0.2 and open up shrinkylink.php.

Let’s take a look at the original function that sets up the default settings. This is set to run when the plugin is activated:

function shrinky_init() {
	if( function_exists('add_option') ) {
		add_option( 'shrinky_comments', 'yes' );
		add_option( 'shrinky_posts', 'no' );
		add_option( 'shrinky_replace', 'yes' );
		add_option( 'shrinky_text', 'link' );
		add_option( 'shrinky_trim', 'no' );
		add_option( 'shrinky_size', '12' );
		add_option( 'shrinky_scheme', 'no' );
		add_option( 'shrinky_www', 'no' );
		add_option( 'shrinky_elipse', 'yes');
		add_option( 'shrinky_domain', 'yes' );
	}
}

Right out of the gate, that will add ten separate records to the blog’s options table.

We’re going to change it to something that looks similar, but does something quite different. First off, let’s decide what to call our new single setting. We might just go with “shrinkylink”, or “shrinkylink_settings”, but part of the point of this exercise is to keep the options table tidy; and in the interests of clarity, I’d like to go the further step of specifying in the option name that it was made by a plugin. Let’s use “plugin_shrinkylink_settings”.

Here’s the new function:

function shrinky_init() {
	$new_options = array(
		'comments' => 'yes',
		'posts' => 'no',
		'replace' => 'yes',
		'trim' => 'no',
		'text' => 'link',
		'size' => '12',
		'scheme' => 'no',
		'www' => 'no',
		'elipse' => 'yes',
		'domain' => 'yes'
	);

	add_option( 'plugin_shrinkylink_settings', $new_options );
}

You’ll note that I eliminated the if( function_exists('add_option') ) test. it was a diligent, but I think unnecessary, step. There’s no harm in leaving it in if you prefer.

Otherwise this is very straightforward. We set the various options as elements of a new array, and then use add_option() (or update_option()) to pass the entire array to the database. You should also note that I stripped the “shrinky_” off of the names of the individual settings. That was there to identify that those settings came from this plugin; but it’s no longer needed as they are contained within an array that identifies them already.

How does retrieving settings change? Previously, any time the code needed a setting, it would make a call to the database. If a function needed five different settings, that was five calls to the database:

get_option('shrinky_comments');
get_option('shrinky_posts');
get_option('shrinky_size');
get_option('shrinky_scheme');
get_option('shrinky_www');

With an array, we can make a single call to the database, and set the result to a variable. Then as we need the various settings, we don’t call the database again, we simply look at the variable:

$options = get_option('plugin_shrinkylink_settings');

$options['comments'];
$options['posts'];
$options['size'];
$options['scheme'];
$options['www'];

The same holds true for setting options. Rather than multiple calls to the database:

update_option('shrinky_comments', 'new value');
update_option('shrinky_posts', 'new value');
update_option('shrinky_size', 'new value');
update_option('shrinky_scheme', 'new value');
update_option('shrinky_www', 'new value');

…we can update the variable, and then set the option once at the end of our function:

$options = get_option('plugin_shrinkylink_settings');

$options['comments'] = 'new value';
$options['posts'] = 'new value';
$options['size'] = 'new value';
$options['scheme'] = 'new value';
$options['www'] = 'new value';

update_option('plugin_shrinkylink_settings', $options);

It is important to note that when we call update_option() at the end of the function, it must contain all of the settings, not just the ones we updated. Do not just create a new $options variable and set a few settings — instead, populate $options using get_option(), then makes changes, then update.

In the ShrinkyLink plugin, the author made an interesting choice: he completely abstracted setting and reading of options into individual functions. There is some advantage to this type of thing, but he took it much further than I would have: For each option there is a separate function for reading it or setting it, and for booleans (simple on/off) he actually has two functions — one for turning it on, and one for turning it off! This means that each of the original ten settings has two or three functions each — a grand total of twenty-eight separate functions just for reading or settings those ten options.

In this case it actually makes updating the plugin for the array quite easy, if a bit tedious.

Original “set yes” function:

function enable_comment_filter() {
	if( function_exists('update_option') ) {
		update_option( 'shrinky_comments', 'yes' );
	}
}

And the new version:

function enable_comment_filter() {
	$options = get_option('plugin_shrinkylink_settings');
	$options['comments'] = 'yes';
	update_option( 'plugin_shrinkylink_settings', $options );
}

At this level of abstraction the arrays lose their efficiency, as we end up making a call to read and a call to set every time we change an option. I personally plan to adopt this orphaned plugin and improve the code quite a bit, including eliminating the excess of get/set functions in favor of interacting with the array directly in the larger functions. This problem is specific to the setup of this particular plugin, and dealing with that goes beyond the scope of this article.

I’m not going to go over the changes to all of these little functions, as they are redundant, and by this point should be obvious. I leave the remainder as an exercise to you, the reader, should you choose to follow through….

Upgrading your plugin’s Admin Screen form to use an array

Okay, now that we know how to deal with the array directly, we need to adjust our Admin screen to handle our new settings properly. If you’re following along in source, open up shrinky-ui.php.

Within the form itself, we need to modify the form fields to put the values into an array, so that that array is past to $_POST when we submit the form. This is easier than it sounds.

Normally, each form field has a “name” attribute, and the value is passed to $_POST with that name attached to it. All we need to do is alter those names so they are formatted as an array instead of a bunch of separate strings. So rather than <input name="comments" ...>, we do something like this: <input name="shrinkylink[comments]" ...>. Every field should be given a name attribute in the form of arrayname[field]. The array name can be anything, but the field name should be the exact same name as that of the corresponding setting in the array we’ll be putting in the options table.

Heading back to the top of the form, you can see a whole bunch of code within an if clause: if( isset($_POST['info_update']) ) { ... } Delete that whole big chunk of code, we’re going to replace it with this:

if( isset($_POST['info_update']) ) {
	$new_options = $_POST['shrinkylink'];
	$bool_opts = array( 'comments', 'domain', 'elipse', 'posts', 'scheme', 'www' );
	foreach($bool_opts as $key) {
		$new_options[$key] = $new_options[$key] ? 'yes' : 'no';
	}
	update_option( 'plugin_shrinkylink_settings', $new_options);
	echo '<div id="message" class="updated fade"><p><strong>' . __('Settings saved.') . '</strong></p></div>';
}

So first we’re setting the variable $new_options to the “shrinkylink” array that the form passed to $_POST.

Next, since the boolean settings for this plugin are set up to accept explicit “yes” or “no”, I’m filling a new array, $bool_opts, with the names of all of the yes/no options, and then setting the “yes”es and “no”es with a quick foreach loop. This is particular to this plugin, and may differ in yours. You may set it to true/false, “on”/”off”, or leave it as is. Personally I prefer true/false, but I will leave it for now.

Finally, we pass the array to the options table, and echo “Settings saved” to the screen.

Upgrading existing settings from multiple records to a single array

The changes we’ve made to this plugin are great if somebody is going to use it for the first time; but what about somebody who used the previous version and has now upgraded to our new version? We still need to set the new options array according to the old settings, and then clean up the old settings. Fortunately, upgrading from separate options records to an array is surprisingly easy. Let’s take a look at the new, improved init function:

function shrinky_init() {
	$new_options = array(
		'comments' => 'yes',
		'posts' => 'no',
		'replace' => 'yes',
		'trim' => 'no',
		'text' => 'link',
		'size' => '12',
		'scheme' => 'no',
		'www' => 'no',
		'elipse' => 'yes',
		'domain' => 'yes'
	);

	// if old options exist, update to new system
	foreach( $new_options as $key => $value ) {
		if( $existing = get_option( 'shrinky_' . $key ) ) {
			$new_options[$key] = $existing;
			delete_option( 'shrinky_' . $key );
		}
	}

	add_option( 'plugin_shrinkylink_settings', $new_options );
}

First we set up the array of defaults as we did before. Before we run the add_option() our new code will run. What it does is:

  • Loop through the array of default options
  • If the wp_options table contains a record the name of which is “shrinky_” + the name of a new option:
    1. set the new option to the value of the old one, and
    2. delete the old option from the table

Then we assign the new array to the table as we did before, and we’re done!

…almost.

You will note that in the Admin screen there is a set of two radio button with a name of “shrinky[mode]”, and values of “replace” and “trim” — but in the original plugin, “replace” and “trim” are two separate settings. Here’s your final exam: What changes need to be made to smoothly incorporate those two settings into the single “mode” setting? Hint: Nothing further needs to be done to the Admin Screen code. Be sure you also transfer the old setting to the new format.

To see the final result, you can download my final ShrinkyLink 0.3. Again, I plan to further clean up and develop this plugin; this version simply reflects the methods discussed in this article.

That’s all for now. I hope you all found this useful.

Coming next: Contain your plugins’ code in classes to avoid collisions and increase reusability

Previously: Give your WordPress plugin credit without cluttering the GUI

[Update July 12: Added mention of extraneous database calls in first paragraph.]

*:Some of you have probably noticed an obvious point that I have overlooked here: Giving users the direct ability to clean up anything that your plugin does to the database. Uninstall is a topic unto itself, and I hope to discuss that in a future article.
This entry was posted in Codecraft, Webcraft and tagged , , , , , , , , . Bookmark the permalink.

38 Responses to Consolidate Options with Arrays in your WordPress Plugins

  1. Dr J says:

    nice article. well written and easy to follow. all WP plugin authors (including myself) would be smart to use arrays for our options setup

  2. Ozh says:

    Well said. I think we need an official “Clean Plugin with one DB entry” badge :)

  3. Stephen says:

    Heh. We need an official WordPress “Clean Options Validator” script. Run your plugin through it and it searches for add_option() calls… 3 strikes yer out!

  4. Rajesh says:

    Stephen, i can understand that it is good code to use an array for adding/update options. But most databases are too good nowadays, that we can still get away with these.

    the example you quoted is for adding/updating options in the mysql backend of wordpress database.

    1) wordpress options are generally supposed to be smaller tables.It is not that they have millions of records.If your find too many records there, then there is some other problem and you must clean them up. So having records even in thousands is not necessarily a problem, as mysql is capable of handling it better.Also mysql query caches, full resultsets (unlike oracle) and hence having even say 20 or 30 get_option is not really a problem.
    2) Query cache is great for applications like blogs. Generally updates are relatively rare in blogs and so per-table granularity is not a problem
    3) Full page caching plugins further help you in overcoming frequent queries.
    so I believe that one should consider a variety of factors/alternatives rather than going by thumb rules.

  5. Alex says:

    Stephen,

    Thanks for a very useful article. I’ve been searching for info like this for a while!

    Just a couple of questions though:

    1. One of your criticisms of too many add_option calls is that future deactivation of the plugin leaves a lot of clutter in the wp_options table. Personally, I use the deactivation hook with a call to a function which deletes the options added by my plugin. Surely this is an acceptable thing to do and therefore sort of negates the “clutter” argument of multiple add_options?

    2. The “Creating Options Pages” guidance on the codex says to use the wp_nonce_field(‘update-options’) function within the top of the HTML Form and input type=”hidden” name=”page_options” value=”list of options…” at the bottom of the Form. Your example doesn’t do this and uses the “if( isset($_POST[‘info_update’])etc” code instead. Is your way a better way, or am I missing something here? If using your method, is check_admin_referrer needed too?

    I’m pretty new to this stuff, hence my questions! :-)

    Thanks again for the article, and hope you have a moment to reply to this.

    Alex.

  6. Stephen says:

    I personally don’t like the practice of deleting settings on deactivation. There are a lot of cases where I may want to temporarily deactivate a plugin, but I’m going to reactivate it and want to retain settings.

    Example… some plugin is causing trouble but I don’t know which one, so I deactivate all plugins and reactivate them one by one to see when the problem starts up again. Wait… where’d all my settings go???

    There is, however, a new feature coming to WordPress 2.7 where people can _delete_ plugins within the admin interface; and plugin authors will be able to include an uninstall to run when their plugin is thus deleted.

    Regarding nonces — I left unrelated code out of the example to keep the example as simple as possible, and to focus on the technique I was describing. Use nonces, by all means. :)

  7. Alex says:

    Stephen,

    Thanks for your reply. Yes, good point about temporarily deactivating a plugin and losing settings. I think a way of deactivating without deleting settings together with an “uninstall” option for admins is a good idea. I’ll see if I can find more about this on the codex and wp-hackers list re WP 2.7.

    As for the nonce question, thanks for the clarification. But unless I’m mistaken Shrinklink doesn’t use nonces, so I’m just trying to understand whether one way is better (security, efficiency etc) than the other.

    By the way, I’ve found some great articles on your site – very useful. Nice work! Thanks.

  8. Stephen says:

    “[U]nless I?m mistaken Shrinklink doesn?t use nonces….”

    Correct. This article was modeled on a plugin by a different author. As that author appears to have abandoned it, I “adopted” it — and wrote this article starting with the adopted plugin as-is. I intended (still do) to make a full update of Shrinkylink — but other responsibilities have intervened.

    Regarding uninstall methods:
    WordPress 2.7 Plugin Uninstall Methods

  9. Pingback: Enlaces del 01/27/09 | evelio.info

  10. Pingback: Enlaces del 27/01/09 | evelio.info

  11. Ajay says:

    Great tutorial on converting the individual options to an array.

    I’m stuck with one problem. What code do you use to add more options in the future and delete old ones, when using an array structure?

    e.g. if you want to remove the option of ‘www’ in a future version

  12. Stephen says:

    Ajay —

    
    function enable_comment_filter() {
    	$options = get_option( 'plugin_shrinkylink_settings' );
    	$options['foo'] = 'bar';  // add or change option
    	unset( $options['www'] );    // delete option
    	update_option( 'plugin_shrinkylink_settings', $options );
    }
    
  13. Binh Nguyen says:

    Thanks for this article. I’m really in big trouble because the options table became 2GB large on one of the blog. I had to create a new options table replacing that one.

    I don’t know what happened then but now another options table from my main blog got problem with 4000 records. It’s only over 2MB but it crashed the whole site for few days and I wasn’t aware of that till just now.

    What I really need to know now is how to clean all the unnecessary records. Is there some php file, any code, any query, etc that can do the job? If you can help I’d really appreciate and I think other bloggers will appreciate too.

    Thanks again and looking toward for a solution.

    Binh Nguyen

    • Stephen says:

      Binh – It’s pretty odd for the options table to get that big. I would guess that you have a runaway plugin adding things that it shouldn’t. Thus the best thing you can do is look at the raw MySQL table and try to figure out what plugin is doing it, and deactivate that plugin.

      After that, cleaning the table means going through and deleting all the offending options, or, as you did before, start a new options table.

  14. redunzl says:

    one of the most helpful articles for plugin-writing i ever read.

    thanks.

  15. Pingback: Consolidate Options with Arrays in your WordPress Themes - Ptah Dunbar - Web Craftsman, WordPress hacker and Entrepreneur.

  16. Ali says:

    Firstly thanks for that amazing article, after that saving the updates as option_template and calling back is so easy for me…
    Thanks again…

    NOTE: First, i answered anti-spam question as “definitely a tree ;)” for “Which is taller, an ant or a tree?” question but your system decided that i’am nat a human :(.. ;)

  17. Sam says:

    Firstly, thanks for that hint. I used it, but my options don’t come up to screen till i refresh my settings page.. So i want to learn that, is there any trick for placing of these functions? Thanks right now..

  18. Pingback: Consolidate options with arrays in your WordPress plugins | WpMash - WordPress News

  19. Pingback: Suggestion: WordPress Option Namespacing

  20. NG says:

    In addition to your excellent approach to using arrays, a separate wp_options_plugins table should be added to WordPress on future releases. This way if the wp_options_plugins table becomes so bloated the blog starts to slow to a crawl it could be dropped, and recreated after the plugins are reactivated, without having any impact on the core WordPress settings and options stored in the wp_options table.

    It seems the WordPress architecture should have separated things when the wp-options table was first conceived. Instead blog owners are trying to manually remove entries in wp_options table after getting errors like:

    “WordPress database error Out of memory”

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>