Posted on

WordPress Boolean Options and JavaScript : Handle With Care

JavaScript WordPress Banner

After chasing my tail for the past 5 days I finally uncovered the source of a bug in my code that was causing Store Locator Plus to not initialize properly on fresh installs. The culprit? How WordPress handles booleans. In particular, how WordPress handles the persistent storage and the JavaScript localization of booleans.

The important lesson:

WordPress option storage is done using STRINGS*.
* there is a caveat here: SERIALIZED options are stored with the data type,
single options (non-arrays) are stored as strings

WordPress JavaScript localization uses PHP and JavaScript DATA TYPES.

 

What does this mean for plugin coding?

If you are passing WordPress options that have been fetched from the wp_options table with get_option to JavaScript via the wp_localize_script() method your booleans that are set to true/false will be passed to JavaScript as ‘1’/’0′. On the other hand, if you are setting a PHP variable to true/false and passing that to JavaScript they are set as a JavaScript boolean value of true/false.

The difference is important.

In JavaScript you write this for booleans:

if ( myoption.booleanvar ) { alert('true'); }

However you write this for the string version:

if ( myoption.booleanvar === '1' ) { alert('true'); }

How did this break my code?

When my plugin environment loads for the first time it uses an array to store option defaults. Since the user has not yet set or saved the plugin options the get_option call returns an empty array and those defaults are employed. However my option array looked something like this:

$options = array( 'booleanvar' => true );

To further compound the problem, I introduced yet ANOTHER issue by converting what was previously a single-element option to a serialized array in the latest patch release.   Serialized data is MUCH faster.  When you are fetching/setting more than a single option for your plugin.   To be a good WordPress citizen I’ve been slowly migrating all my legacy options to a single serial array.   HOWEVER the update did something like this:

$old_option = get_option('booleanvar');  // singular boolean options are stored as strings '1'/'0'
$options = array ( 'booleanvar' => $old_option ); // $old_option is a string
update_option( 'my_option_setting', $options);
delete_option('booleanvar');

My code is a lot “deeper” than shown here, but the basic idea was to fetch the pre-existing value of the singular option and store it in my options array. I save it back out to persistent storage and blast the old non-serialized option out of the database. THIS conversion for existing sites works well as the singular option variables will store and retrieve boolean data as a STRING. Thus my option comes back as ‘1’/’0′ depending on how the user left it and all is good in JavaScript-land.

HOWEVER, if you note above the NEW DEFAULT is to set booleanvar as a boolean with a default value of true. When storing a compound option (named array) with update_option it stores and retrieves values using the DATA TYPE.

$options = array( 'booleanvar' => true );
update_option( 'my_option_settings', $options );  // serial options are stored as proper data types
$options = get_option( 'my_option_settings');

Here the $options[‘booleanvar’] is set to a proper boolean.

As you can probably guess, this creates inconsistency in the Javascript.

Why? Because my JavaScript has been written for the most common use case which is one where users have set and saved the plugin options at least once. The JavaScript code uses the string comparison code shown above, ( myoption.booleanvar === ‘1’ ). It works as expected every time after the user has saved the options at least once.

Why does it work after options are saved? Because WordPress returns the boolean variable as the string value ‘1’. You can see this for yourself by playing with the update_option() and get_option() functions in WordPress to store/get a boolean true value. Since my code uses the stored values if they are set my booleanvar is not taking on the default true setting after the first save, it is coming back as ‘1’ which is what JavaScript expects to see.

The Lesson?

Use string values of ‘1’ and ‘0’ for your WordPress option values any time you are not using 100% serialized option values. It ensures consistency throughout your application by following the precedence set by the WordPress get_option function.

Yes, you can get away with using boolean true/false values. It even makes the code more readable, IMO. However it can cause issues if you ever decide you need to pass those values along to your JavaScript functions with wp_localize_script() if you are not 100% certain you are using pure non-translated boolean variables throughout.

If you are ever uncertain of your true data types add a gettype($varname) debugging output to your plugin code. Remember that simple print_r statements will convert booleans to strings as well, use gettype to be certain of what flavor variable you have inside PHP.