Using Sane Defaults in Themes

With the release of WordPress 3.9, one of the changes to the Theme Review Guidelines is that Themes must use sane defaults. That means that Themes must not write default setting values to the database. For many Themes, this may seem like a major change; but it doesn’t have to be. This post will step through a few ways to implement sane defaults.

Portability/DRY

To make this method easier, put all of your defaults inside a function:

function themeslug_get_option_defaults() {
	$defaults = array(
		'option_1' => 'value_1',
		'option_2' => 'value_2',
		'option_3' => 'value_3'
	);
	return apply_filters( 'themeslug_option_defaults', $defaults );
}

(Note: by making the return valuable filterable, the Theme defaults can be easily overridden by a Child ThemeChild theme A Child Theme is a customized theme based upon a Parent Theme. It’s considered best practice to create a child theme if you want to modify the CSS of your theme. https://developer.wordpress.org/themes/advanced-topics/child-themes/. or PluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party.)

We’ll make use of this function later.

Options APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways.

Most Themes use the Options API, and will use get_option() to put Theme settings into a global:

$themeslug_options = get_option( 'theme_themeslug_options' );

Knowing that this get_option() caall will return FALSE if the option has not yet been saved to the database, Theme developers have taken to saving default values to the database as part of Theme initialization, like so:

if ( false == get_option( 'theme_themeslug_options' ) ) {
	update_option( 'theme_themeslug_options', themeslug_get_option_defaults() );
}
$themeslug_options = get_option( 'theme_themeslug_options' );

But this is entirely unnecessary. And everything needed to implement a better solution already exists.

As a first step, consider that get_option() includes a second parameter, which specifies the default value to return, if nothing is returned from the database:

get_option( $name, $default );

So, the simplest solution is merely to tell get_option() to return the defaults, using the function we previously defined:

$themeslug_options = get_option( 'theme_themeslug_options', themeslug_get_option_defaults() );

This works, but isn’t perfect. It will return the Theme-defined defaults if the user hasn’t saved settings to the databse. But if later versions of the Theme add, remove, or change options, this might break, since the return value is either/or: either the database-saved setting, or else the defaults. So, if the user saves settings, and then a new setting is added in a later Theme version, the new setting value won’t be included in $themeslug_options unless/until the user saves settings again.

The solution is to merge the arrays, rather than to return one or the other. WordPress has a coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. function specifically for this purpose: wp_parse_args(), which will use the settings array, and “fill in the blanks” with the defaults array:

wp_parse_args( $settings, $defaults );

Caveat: bearing in mind that wp_parse_args() expects both parameters to be arrays, and knowing that get_option() returns FALSE by default, be sure to specify get_option() returns an empty array by default: get_option( ‘theme_themeslug_options’, array() ); otherwise, wp_parse_args() will (might – see note below) choke if the user hasn’t saved settings to the database.

The construct will look something like this:

$themeslug_options = wp_parse_args( 
    get_option( 'theme_themeslug_options', array() ), 
    themeslug_get_option_defaults() 
);

This is perhaps the simplest, most elegant way to implement sane defaults.

(Note: according to Otto, passing an empty arrayy() as the second parameter to get_option() isn’t necessary. In his words: “The wp_parse_args() function checks for the first parameter to be an object or an array. If it’s neither, then it calls wp_parse_str on it, because it can take a GET URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org-like array of parameters too. The wp_parse_str() function calls PHPPHP PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used open source general-purpose scripting language that is especially suited for web development and can be embedded into HTML. https://www.php.net/manual/en/preface.php.’s parse_str on it, and does a deep strip_slashes if magic quotes is on, then filters the result. So, because false maps to the empty string, parse_str will return an empty array for it, so passing false to wp_parse_args should be A-OK and probably has been like that for a very long time. Doesn’t hurt to add the empty array(), but doesn’t really change anything.” YMMV.)

Theme Modification API

Using the Theme Modification API (get_theme_mod()/get_theme_mods()) is fairly similar.

An individual setting can be called via:

get_theme_mod( $name, $default );

But perhaps more useful, all settings can be called via:

$themeslug_options = get_theme_mods();

Since get_theme_mods() returns an array, you can use the same technique as with Options API settings:

$themeslug_options = wp_parse_args( 
    get_theme_mods(), 
    themeslug_get_option_defaults() 
);

Portability/DRY (Part 2)

To be able to use this method throughout the Theme, wrap the wp_parse_args() call inside a function:

function themeslug_get_options() {
    // Options API
    return wp_parse_args( 
        get_option( 'theme_themeslug_options', array() ), 
        themeslug_get_option_defaults() 
    );
    // Theme Mods API:
    /*
    return wp_parse_args( 
        get_theme_mods(), 
        themeslug_get_option_defaults() 
    );
    */
}

Then, wherever you need access to Theme options:

$themeslug_options = themeslug_get_options();

From there, you can globalize $themeslug_options, or cache/transient it, etc. When you need to add a new option, simply add the new option default to the defaults array, and the Theme will handle it automatically.

#guidelines, #sane-defaults, #settings-api, #theme-mods-api