* Saves and restores user interface settings stored in a cookie.
* Checks if the current user-settings cookie is updated and stores it. When no
* cookie exists (different browser used), adds the last saved cookie restoring
function wp_user_settings() {
if ( ! is_admin() || wp_doing_ajax() ) {
$user_id = get_current_user_id();
if ( ! is_user_member_of_blog() ) {
$settings = (string) get_user_option( 'user-settings', $user_id );
if ( isset( $_COOKIE[ 'wp-settings-' . $user_id ] ) ) {
$cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE[ 'wp-settings-' . $user_id ] );
// No change or both empty.
if ( $cookie == $settings ) {
$last_saved = (int) get_user_option( 'user-settings-time', $user_id );
$current = isset( $_COOKIE[ 'wp-settings-time-' . $user_id ] ) ? preg_replace( '/[^0-9]/', '', $_COOKIE[ 'wp-settings-time-' . $user_id ] ) : 0;
// The cookie is newer than the saved value. Update the user_option and leave the cookie as-is.
if ( $current > $last_saved ) {
update_user_option( $user_id, 'user-settings', $cookie, false );
update_user_option( $user_id, 'user-settings-time', time() - 5, false );
// The cookie is not set in the current browser or the saved value is newer.
$secure = ( 'https' === parse_url( admin_url(), PHP_URL_SCHEME ) );
setcookie( 'wp-settings-' . $user_id, $settings, time() + YEAR_IN_SECONDS, SITECOOKIEPATH, null, $secure );
setcookie( 'wp-settings-time-' . $user_id, time(), time() + YEAR_IN_SECONDS, SITECOOKIEPATH, null, $secure );
$_COOKIE[ 'wp-settings-' . $user_id ] = $settings;
* Retrieves user interface setting value based on setting name.
* @param string $name The name of the setting.
* @param string|false $default Optional. Default value to return when $name is not set. Default false.
* @return mixed The last saved user setting or the default value/false if it doesn't exist.
function get_user_setting( $name, $default = false ) {
$all_user_settings = get_all_user_settings();
return isset( $all_user_settings[ $name ] ) ? $all_user_settings[ $name ] : $default;
* Adds or updates user interface setting.
* Both $name and $value can contain only ASCII letters, numbers, hyphens, and underscores.
* This function has to be used before any output has started as it calls setcookie().
* @param string $name The name of the setting.
* @param string $value The value for the setting.
* @return bool|null True if set successfully, false otherwise.
* Null if the current user is not a member of the site.
function set_user_setting( $name, $value ) {
$all_user_settings = get_all_user_settings();
$all_user_settings[ $name ] = $value;
return wp_set_all_user_settings( $all_user_settings );
* Deletes user interface settings.
* Deleting settings would reset them to the defaults.
* This function has to be used before any output has started as it calls setcookie().
* @param string $names The name or array of names of the setting to be deleted.
* @return bool|null True if deleted successfully, false otherwise.
* Null if the current user is not a member of the site.
function delete_user_setting( $names ) {
$all_user_settings = get_all_user_settings();
foreach ( $names as $name ) {
if ( isset( $all_user_settings[ $name ] ) ) {
unset( $all_user_settings[ $name ] );
return wp_set_all_user_settings( $all_user_settings );
* Retrieves all user interface settings.
* @global array $_updated_user_settings
* @return array The last saved user settings or empty array.
function get_all_user_settings() {
global $_updated_user_settings;
$user_id = get_current_user_id();
if ( isset( $_updated_user_settings ) && is_array( $_updated_user_settings ) ) {
return $_updated_user_settings;
$user_settings = array();
if ( isset( $_COOKIE[ 'wp-settings-' . $user_id ] ) ) {
$cookie = preg_replace( '/[^A-Za-z0-9=&_-]/', '', $_COOKIE[ 'wp-settings-' . $user_id ] );
if ( strpos( $cookie, '=' ) ) { // '=' cannot be 1st char.
parse_str( $cookie, $user_settings );
$option = get_user_option( 'user-settings', $user_id );
if ( $option && is_string( $option ) ) {
parse_str( $option, $user_settings );
$_updated_user_settings = $user_settings;
* Private. Sets all user interface settings.
* @global array $_updated_user_settings
* @param array $user_settings User settings.
* @return bool|null True if set successfully, false if the current user could not be found.
* Null if the current user is not a member of the site.
function wp_set_all_user_settings( $user_settings ) {
global $_updated_user_settings;
$user_id = get_current_user_id();
if ( ! is_user_member_of_blog() ) {
foreach ( $user_settings as $name => $value ) {
$_name = preg_replace( '/[^A-Za-z0-9_-]+/', '', $name );
$_value = preg_replace( '/[^A-Za-z0-9_-]+/', '', $value );
if ( ! empty( $_name ) ) {
$settings .= $_name . '=' . $_value . '&';
$settings = rtrim( $settings, '&' );
parse_str( $settings, $_updated_user_settings );
update_user_option( $user_id, 'user-settings', $settings, false );
update_user_option( $user_id, 'user-settings-time', time(), false );
* Deletes the user settings of the current user.
function delete_all_user_settings() {
$user_id = get_current_user_id();
update_user_option( $user_id, 'user-settings', '', false );
setcookie( 'wp-settings-' . $user_id, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
* Retrieve an option value for the current network based on name of option.
* @since 4.4.0 The `$use_cache` parameter was deprecated.
* @since 4.4.0 Modified into wrapper for get_network_option()
* @see get_network_option()
* @param string $option Name of the option to retrieve. Expected to not be SQL-escaped.
* @param mixed $default Optional. Value to return if the option doesn't exist. Default false.
* @param bool $deprecated Whether to use cache. Multisite only. Always set to true.
* @return mixed Value set for the option.
function get_site_option( $option, $default = false, $deprecated = true ) {
return get_network_option( null, $option, $default );
* Adds a new option for the current network.
* Existing options will not be updated. Note that prior to 3.3 this wasn't the case.
* @since 4.4.0 Modified into wrapper for add_network_option()
* @see add_network_option()
* @param string $option Name of the option to add. Expected to not be SQL-escaped.
* @param mixed $value Option value, can be anything. Expected to not be SQL-escaped.
* @return bool True if the option was added, false otherwise.
function add_site_option( $option, $value ) {
return add_network_option( null, $option, $value );
* Removes a option by name for the current network.
* @since 4.4.0 Modified into wrapper for delete_network_option()
* @see delete_network_option()
* @param string $option Name of the option to delete. Expected to not be SQL-escaped.
* @return bool True if the option was deleted, false otherwise.
function delete_site_option( $option ) {
return delete_network_option( null, $option );
* Updates the value of an option that was already added for the current network.
* @since 4.4.0 Modified into wrapper for update_network_option()
* @see update_network_option()
* @param string $option Name of the option. Expected to not be SQL-escaped.
* @param mixed $value Option value. Expected to not be SQL-escaped.
* @return bool True if the value was updated, false otherwise.
function update_site_option( $option, $value ) {
return update_network_option( null, $option, $value );
* Retrieves a network's option value based on the option name.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int $network_id ID of the network. Can be null to default to the current network ID.
* @param string $option Name of the option to retrieve. Expected to not be SQL-escaped.
* @param mixed $default Optional. Value to return if the option doesn't exist. Default false.
* @return mixed Value set for the option.
function get_network_option( $network_id, $option, $default = false ) {
if ( $network_id && ! is_numeric( $network_id ) ) {
$network_id = (int) $network_id;
// Fallback to the current network if a network ID is not specified.
$network_id = get_current_network_id();
* Filters the value of an existing network option before it is retrieved.
* The dynamic portion of the hook name, `$option`, refers to the option name.
* Returning a truthy value from the filter will effectively short-circuit retrieval
* and return the passed value instead.
* @since 2.9.0 As 'pre_site_option_' . $key
* @since 4.4.0 The `$option` parameter was added.
* @since 4.7.0 The `$network_id` parameter was added.
* @since 4.9.0 The `$default` parameter was added.
* @param mixed $pre_option The value to return instead of the option value. This differs
* from `$default`, which is used as the fallback value in the event
* the option doesn't exist elsewhere in get_network_option().
* Default false (to skip past the short-circuit).
* @param string $option Option name.
* @param int $network_id ID of the network.
* @param mixed $default The fallback value to return if the option does not exist.
$pre = apply_filters( "pre_site_option_{$option}", false, $option, $network_id, $default );
// Prevent non-existent options from triggering multiple queries.
$notoptions_key = "$network_id:notoptions";
$notoptions = wp_cache_get( $notoptions_key, 'site-options' );
if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
* Filters a specific default network option.
* The dynamic portion of the hook name, `$option`, refers to the option name.
* @since 4.4.0 The `$option` parameter was added.
* @since 4.7.0 The `$network_id` parameter was added.
* @param mixed $default The value to return if the site option does not exist
* @param string $option Option name.
* @param int $network_id ID of the network.
return apply_filters( "default_site_option_{$option}", $default, $option, $network_id );
if ( ! is_multisite() ) {
/** This filter is documented in wp-includes/option.php */
$default = apply_filters( 'default_site_option_' . $option, $default, $option, $network_id );
$value = get_option( $option, $default );
$cache_key = "$network_id:$option";
$value = wp_cache_get( $cache_key, 'site-options' );
if ( ! isset( $value ) || false === $value ) {
$row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_value FROM $wpdb->sitemeta WHERE meta_key = %s AND site_id = %d", $option, $network_id ) );
// Has to be get_row() instead of get_var() because of funkiness with 0, false, null values.
if ( is_object( $row ) ) {
$value = $row->meta_value;
$value = maybe_unserialize( $value );
wp_cache_set( $cache_key, $value, 'site-options' );
if ( ! is_array( $notoptions ) ) {
$notoptions[ $option ] = true;
wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
/** This filter is documented in wp-includes/option.php */
$value = apply_filters( 'default_site_option_' . $option, $default, $option, $network_id );
if ( ! is_array( $notoptions ) ) {
wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
* Filters the value of an existing network option.
* The dynamic portion of the hook name, `$option`, refers to the option name.
* @since 2.9.0 As 'site_option_' . $key
* @since 4.4.0 The `$option` parameter was added.
* @since 4.7.0 The `$network_id` parameter was added.
* @param mixed $value Value of network option.
* @param string $option Option name.
* @param int $network_id ID of the network.
return apply_filters( "site_option_{$option}", $value, $option, $network_id );
* Adds a new network option.
* Existing options will not be updated.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int $network_id ID of the network. Can be null to default to the current network ID.
* @param string $option Name of the option to add. Expected to not be SQL-escaped.
* @param mixed $value Option value, can be anything. Expected to not be SQL-escaped.
* @return bool True if the option was added, false otherwise.
function add_network_option( $network_id, $option, $value ) {
if ( $network_id && ! is_numeric( $network_id ) ) {
$network_id = (int) $network_id;
// Fallback to the current network if a network ID is not specified.
$network_id = get_current_network_id();
wp_protect_special_option( $option );
* Filters the value of a specific network option before it is added.
* The dynamic portion of the hook name, `$option`, refers to the option name.
* @since 2.9.0 As 'pre_add_site_option_' . $key
* @since 4.4.0 The `$option` parameter was added.
* @since 4.7.0 The `$network_id` parameter was added.
* @param mixed $value Value of network option.
* @param string $option Option name.
* @param int $network_id ID of the network.
$value = apply_filters( "pre_add_site_option_{$option}", $value, $option, $network_id );
$notoptions_key = "$network_id:notoptions";
if ( ! is_multisite() ) {
$result = add_option( $option, $value, '', 'no' );
$cache_key = "$network_id:$option";
// Make sure the option doesn't already exist.
// We can check the 'notoptions' cache before we ask for a DB query.
$notoptions = wp_cache_get( $notoptions_key, 'site-options' );
if ( ! is_array( $notoptions ) || ! isset( $notoptions[ $option ] ) ) {
if ( false !== get_network_option( $network_id, $option, false ) ) {
$value = sanitize_option( $option, $value );
$serialized_value = maybe_serialize( $value );