The Trail of the Saved Option

WARNING: In this article, I ask you to alter core WordPress files while troubleshooting. Please remember to undo any changes you make so WordPress continues working correctly. I do NOT advise making these changes on a public-facing website.

Recently, I’ve been troubleshooting why my options for a new plugin aren’t saving correctly. So far, I’ve discovered, or should I say “re-discovered” some interesting items that other devs might find useful. For starters, when saving your plugin options, ideally, you should combine them into an array before saving them. This saves the database from having tons of random options entries just for your plugin. There are exceptions, like if you need options separated from each other, but that’s another post.

When saving data into an array, you’ll need to make the name of the field something like: name_of_the_registered_setting[name_of_the_form_field]. Notice those brackets? That helps the options save process place this field’s data into the correct place within the array.

While troubleshooting options, I began to trace where the data goes and how it gets processed by WordPress, then your own plugin, so I’m going to lay it out here.

First, when you create your settings page, your form tag has these attributes:

method="post" action="options.php"

This means, the form will use the Post method to send the data to the options.php file.

The action check in wp-admin > options.php.
The action check and the added wp_die() in wp-admin > options.php.

Within the options.php file (version 3.9.1 as of this writing), line 133 checks if the action was “update”. There’s lots of code before this mostly dealing with making sure WordPress will be saving to the correct set of options, which changes if you’re saving options for Multi-site vs a single site installation.

If one checks the $_POST PHP superglobal on line 134, you’ll see the raw posted information you typed into the fields. To do this, one would use this bit of code within the root > wp-admin > options.php file:

wp_die( serialize( $_POST ) );

wp_die() stops WordPress right where it is and the first argument allows you to enter an optional message. In this case, we’re serializing the $_POST array so we can read it and using that as our message. From my test data, here’s what I see:

a:6:{s:11:"option_page";s:19:"slushman_wpmi_first";s:6:"action";s:6:"update";s:8:"_wpnonce";s:10:"4d37626dc4";s:16:"_wp_http_referer";s:77:"/showcase/wp-admin/options-general.php?page=wp-maximage&settings-updated=true";s:19:"slushman_wpmi_first";a:2:{s:15:"wpmi_test_field";s:9:"save this";s:17:"wpmi_test_field_2";s:0:"";}s:6:"submit";s:12:"Save Changes";}

In my test settings page, I have a field called  ‘slushman_wpmi_first’ and I entered ‘save this’ and clicked the Save Changes button.

After checking if the action is “update”, options.php then checks the admin referrer, which in our test case is ‘/showcase/wp-admin/options-general.php?page=wp-maximage&settings-updated=true‘. You can comment out and copy the wp_die() statement then paste it  just after this admin referrer check, but it should be the same stuff.

After this, it checks the whitelisted options. Most of the code above the update check in line 133 is about compiling that list of whitelisted options. If you used the register_setting() function for your plugin options, your options should be automatically added to the whitelisted options. It then checks your capabilities for multisite installations or whitelists your settings. If you’re not having capabilities issues, copy/pasting the wp_die() statement after line 153 really shouldn’t show anything different.

The next batch of code has to do with checking the time and date settings from the WordPress general page, so skip that block of code.

Line 168 checks if the variable $options is valid and has data. If all the whitelisting code passed, this variable should have been assigned in line 151. You can move the wp_die() statement to line 169 and change $_POST to $options to see your whitelisted options. Here’s what my test settings page shows:

The option value processing block in wp-admin > options.php.
The option value processing block in wp-admin > options.php.

This is the first bit where the data starts to be processed and possibly changed. After checking the $options variable, it breaks each option apart and processed them separately (in PHP, that’s a foreach loop). If you copy/paste the wp_die() within the foreach loop and change $options to $option, you should see the first one:


First, it checks each option to see if its unregistered, which was assigned to the $unregistered variable back in lines 135 or 138. Since you’ve used the register_setting() function, this should be false because your setting is registered, otherwise you’d see a WordPress error once the processing reaches this point.

The option is trimmed to remove extra white space and the $value variable set to NULL in lines 172 and 173.

Line 175 checks the $_POST superglobal for the option. If its not in $_POST there’s no reason to attempt to update it, so it would skip any further processing and go back to the settings page.

Otherwise in line 175, it assigns the $value variable the value of the option from $_POST. You can check this value for yourself by using this on line 176:

wp_die( serialize( $_POST['name_of_your_option'] ) );

In my test page, I get:

a:2:{s:15:"wpmi_test_field";s:9:"save this";s:17:"wpmi_test_field_2";s:0:"";}

It sees that both fields were submitted for the option ‘slushman_wpmi_first’. In the next line, it check  the data type of the $value and if its not an array, it trims it to remove white space, the sends it off to wp_unslash() to remove any slashes.

After this, the $option and the $value are sent to update_option(). One might think the processing ends there, but no. update_option() is located in the wp-includes/option.php file around line 231. Pasting this at the top of the update_option() function:

wp_die( serialize( $value ) );

Yields this result for my test page:

a:2:{s:15:"wpmi_test_field";s:0:"save this";s:17:"wpmi_test_field_2";s:0:"";}

First, it trims $option again. If $option is empty, it returns FALSE. Then, it sends $option off to wp_protect_special_option(), which is around line 136 in this same file. It prevents you from updating two options used by WordPress (since WordPress 2.2): alloptions and notoptions. These are used by WordPress in conjunction with transients to autoload options. I’d welcome any additional comments about their roles.

Next, if the $value is an object, it creates a clone of the object and works that with the clone rather than the original.

It then sends $option and $value off to sanitize_option(), which is in the wp-includes/formatting.php file. Looking at the code for sanitize_option(), most of it consists of a switch statement that looks at the $option parameter and acts accordingly. It already includes processing instructions for all the standard WordPress options, but after the switch statement, it includes a filter, ‘sanitize_option_{$option}‘. The sanitize_option_ filter is created and defined in register_setting() (yet another reason to use the Settings API). When you define your sanitization callback function, it adds that function to the sanitize_option_ filter.

At this point, your option’s $value is filtered by whatever you put into your sanitization callback function. After it finishes processing in your sanitization callback, its then returned back to the update_option function. If you add this to the top of your sanitization function:

wp_die( serialize( $input ) );

You can now see exactly what’s being passed into your sanitization callback function. In my case:

a:2:{s:15:"wpmi_test_field";s:9:"save this";s:17:"wpmi_test_field_2";s:0:"";}

After the new value is sanitized in your callback function, the current “old” value is recovered from the database using get_option().

The new value is now passed through two more optional filters, ‘pre_update_option_’ . $option‘, and ‘pre_update_option‘.

Now the new and old values are compared. If they aren’t different, there’s no reason to update the value, so it exits. If the old value doesn’t exist, The new value is sent off to add_option() to be created, the result is returned to this function as $value.

The $value is now sent off to maybe_serialize(), which is in the wp-includes/functions.php file, to possibly serialize it, or in other words, prep it to be stored in the database safely.

Updating the database in option.php
Updating the database in option.php

After this, an action is added for ‘update_option‘. One might get this mixed up with the function update_option(). The action happens right before the option is actually updated, while the function does all the checks and processing to make the updated data safe.

Finally, the updated value is written to the database using the $wpdb object and its update() method. The update() method does not alter the data.

After saving the data to the database, it refreshes the options caches and added to the auto-loading process.

There are two final actions, which allow for processing the value after its been saved to the database: ‘update_option_{$option}‘, which happens after a specific value has been updated, and ‘updated_option’, which happens after the value an option has been updated.

Lastly, if all has gone to plan, update_option() return TRUE.

Back in options.php, if errors occurred at some point in this process, they are handled and displayed appropriately.

Then, finally, the user is sent back to the plugin’s settings page.

Obviously, there are lots of places for things to go badly when saving your plugin settings. Hopefully, this breakdown can help narrow down where things happen and you can use the wp_die() function to discover where issues are happening. In all cases, I’d highly recommend using the Settings API as much as possible!

BP Profile Widgets 0.5 is out!

This is a major update for BP Profile Widgets. One thing everyone asked for is multiple instances. Since this plugin creates a BuddyPress profile field (or two) for each widget, it was a bit challenging to figure this one out. But, I did and you now have up to 5 instances of each widget available!

The other big request was to fix the crazy bug I introduced in version 0.4. Wait, software with bugs? What? Yep. I tried out a new way of getting data from the URLs people put on their profiles, but apparently the new method only works on certain server configurations. Still not sure why, but I’ve opted to simply remove it. I could already use the older way just fine, so I’ll stop trying to fix what ain’t broke!

I’ve also had several requests to internationalize BP Profile Widgets, so I read up on what needed to be done and did it. If you want to interpret this plugin into your language, check out the answer on this page and send me your translation files. I’ll update the plugin with new translations as they come in.

Lastly, I did add a few video services to the video player plugin: Wistia,, and Vine!

BP Profile Widgets

I’m excited to announce I’ve finally updated my BuddyPress plugins! I combined them all into one plugin called BP Profile Widgets. This version has many awesome upgrades like being able to select which widgets are available to use through the plugin options. When you select a widget, the plugin automatically creates the appropriate Profile Field Group and Profile Fields – no more naming issues!

On top that, I also updated the widgets with better versions of the players, oEmbed support when possible, and more vendors included. I hope you enjoy the new plugin!

Building Your First Widget

So, I spoke at WordCamp Nashville today and I had an absolute blast! The other sessions were very informative, I especially enjoyed learning about some tools for debugging plugins and themes. My session was the last one in the developer track. You can download my boilerplate widget code using the nice, giant green button on my home page or the link below. Here are my slides, I hope you learn something new:

Download the Boilerplate Widget code

WordCamp Nashville 2013

I’m excited to say I’ve been invited to speak at this year’s WordCamp Nashville! I’ll be speaking on Building Your First Widget and giving away from boilerplate code here on my site to get you started with building your first widget. I’ll also post my slides here after Saturday’s talk. I hope anyone near Nashville can come to WordCamp, I’d love to meet you and we can all learn more about WordPress together!

Six Steps to Secure Your WordPress Site

There’s a great breakdancing move called 6-step. If you’ve seen breakdancing, you’ve probably seen this before, but, like me, didn’t know what to call it. Basically, the dancer supports himself on his arms while moving his feet around in a circle. It’s great for getting momentum and launching other moves.

Why do I bring this up? You may have heard about the ongoing attacks against WordPress sites. It appears someone is using up to 90,000 different IP addresses to launch brute-force attacks against sites built with WordPress and are gaining access to some sites. Stupidly, they are trying to gain access with the username “admin” and trying to figure out the password for said user.

Here’s the WP 6-step, which will help secure your WordPress site against attacks:

1) If you have a username “admin” on your site, create another administrator user with a different name, log in as that user, and delete “admin”. If you don’t have a user named “admin”, they will just waste their time attacking your site. From my first-hand experience, changing this is an extremely important to making your site secure. Attackers always start by using the “admin” username and just having it as a valid account will leave you open.

Don’t stop there though!

2) Beef-up your passwords. While standard practice for a secure password is to use eight digits with a mix of lowercase and uppercase letters, numbers, and symbols – there’s a better way. Create a password from a story. Something memorable to you. Something people can’t easily find out without knowing you personally.

As an example: My band and I played a show in Johnson City, TN at a coffee shop and while we played one song, this guy gets up and starts dancing. He was probably in his late 50’s and he’s doing this wiggly, hippie dancing literally feet from us. We were trying not to die laughing while finishing the song. After the show, we discovered the coffee shop owner also owned the laundromat next door and had to go fix a washer. Did I mention it took an hour longer than normal to get there because a bunch of big rigs crashed on the same stretch of highway?

Some passwords one could glean from this story: DanceWasherBigRigCoffee, CoffeeWasherJohnsonLaundry, HighwayCoffeeDanceHippie, etc. See how easy that is to come up with a secure password? You could then throw in the year and an exclamation point, just to have all the traditional password requirements too. Attackers wouldn’t know the story and the length of the password is over 15 digits, making it’s basically impossible to figure out using a brute-force attack.

3) Limit login attempts. Part of this current attack is trying different passwords over and over again until they get it correct. While its silly WordPress allows this, you don’t have to. There are several great security plugins that allow you to limit login attempts before locking that IP address out of your WordPress site:

I’m sure there are more, but those are three I have experience with. Each have their upsides and shortcomings, but anything you can do to thwart an attacker and secure your site is a good thing.

4) Use some kind of caching plugin. WordPress, by itself, is pretty good, but it still needs to talk to the database to load each page. Caching plugins make copies of everything so your site loads faster. While it doesn’t seem like this can help make your site secure, this can prevent certain types of attacks as well as sudden increases in popularity.

5) This is standard WordPress advice: keep WordPress, plugins, and themes up-to-date. I usually advise people to check once a week. You should be adding content each week anyway, so knock out both at the same time.

6) Delete unused plugins and themes. While this won’t help much with the current attack, these unused items could cause problems in the future. And if you’re not using them, why keep them around?

See? Six simple steps and you’re more secure and don’t need to worry about people attacking your site. Yeah ok, I’m not a comedian and the dance thing was random. But seriously, take steps to secure your site before you have to call someone like to me to fix it.

ArtistDataPress version 0.6 released!

I’ve added some really cool stuff in this release and I’m proud to announce its officially out!

First off, I’ve added feed caching. Let me ‘splain. Before, ADP would go ask ArtistData’s servers for your info every time you reloaded the page. You may have noticed how slow that was. Fortunately, WordPress has a way that I could save the feed data when I fetch it from ArtistData and it makes everything much, much faster! The one trade-off is you’ll need to wait about an hour for new events to show up on your site. If that needs to be changed, please let me know and I’ll alter the timing based on your feedback. But I figure an hour is short enough.

Second, all the layouts, including widgets, are now responsive, which means they look good on mobile and tablets, as well as your laptop. This is just the way things are going in the web development world and I’m still surprised I hadn’t already built this in before.

The rest of the changes are fairly minor. I altered the way some layouts were done in the CSS to name things more consistently, and make them easier to read. I also lightened up the code a bunch by getting rid of stuff I didn’t need.

Hope you all enjoy the update, let me know of any bugs and/or suggestions.

Adding a Custom Class to a Submit Button

While you can hand-code the HTML for a submit button in a form, WordPress has a nice function, submit_button(), that can drop in the correct code for you.

For a recent project, I wanted to restyle some buttons to make them look like links, like on the posts page, rather than buttons. While you don’t NEED an special class to pull this off, it certainly makes it easier and neater in the CSS. I thought this would be achievable in the $other_attributes, but the $type parameter accepts any string, not just the WordPress defaults.

Basically, whatever you put there will be added to the class attribute of the button and you’re good to restyle the button however you want using your custom class.

* Default WordPress submit() parameters
* @param string $text changes the text on the button
* @param string $type determines the style of the button. WordPress styling options:
* primary - the default
* secondary
* delete
* custom - add your custom class for styling here!
* @param string $name sets the name attribute for the button, its "Submit" by default.
* @param string $wrap determines if the button is wrapped in paragraphs tags or not, the default is true.
* @param array $other_attributes sets other attributes for the button.
submit_button( 'Whatever Text', 'my-custom-class' );
view raw wp-submit-button hosted with ❤ by GitHub

Introducing ArtistDataPress 0.5!

Its been a while since ADP was updated and I know lots of people haven’t cared for the previous update that, in some cases, took away their ability to view their plugin settings. That should be resolved with this 0.5 update.

Previously, ADP was using a PHP library called cURL to fetch the XML feed (the raw shows data) from ArtistData. Fortunately, there’s a nice built-in WordPress function called wp_remote_get that uses HTTP’s GET method to fetch an external URL. This means, your server settings really shouldn’t matter because every server supports HTTP. Hopefully, that’s the last we hear of issues getting the XML feed. 🙂

I also perused the support forums on and found several requests for features. I’ve added some great new features that have been highly requested:

You can now have multiple instances of ADP by using the [artistdatapress] shortcode to specify how many show you want to display and from which url. Use it like this:


[artistdatapress maxshows=20 feedurl=]


The values in the shortcode will override the plugin settings.

I also added the iCal layout for the widget and you choose that on the widget’s settings.

Overall, I streamlined the code and improved the logic and a bunch of other stuff under the hood that should make it easier to update later. Hope you enjoy!