diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-includes/option.php --- a/wp/wp-includes/option.php Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-includes/option.php Tue Dec 15 13:49:49 2020 +0100 @@ -23,7 +23,7 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @param string $option Name of option to retrieve. Expected to not be SQL-escaped. + * @param string $option Name of the option to retrieve. Expected to not be SQL-escaped. * @param mixed $default Optional. Default value to return if the option does not exist. * @return mixed Value set for the option. */ @@ -35,25 +35,48 @@ return false; } + /* + * Until a proper _deprecated_option() function can be introduced, + * redirect requests to deprecated keys to the new, correct ones. + */ + $deprecated_keys = array( + 'blacklist_keys' => 'disallowed_keys', + 'comment_whitelist' => 'comment_previously_approved', + ); + + if ( ! wp_installing() && isset( $deprecated_keys[ $option ] ) ) { + _deprecated_argument( + __FUNCTION__, + '5.5.0', + sprintf( + /* translators: 1: Deprecated option key, 2: New option key. */ + __( 'The "%1$s" option key has been renamed to "%2$s".' ), + $option, + $deprecated_keys[ $option ] + ) + ); + return get_option( $deprecated_keys[ $option ], $default ); + } + /** * Filters the value of an existing option before it is retrieved. * * The dynamic portion of the hook name, `$option`, refers to the option name. * - * Passing a truthy value to the filter will short-circuit retrieving - * the option value, returning the passed value instead. + * Returning a truthy value from the filter will effectively short-circuit retrieval + * and return the passed value instead. * * @since 1.5.0 * @since 4.4.0 The `$option` parameter was added. * @since 4.9.0 The `$default` parameter was added. * - * @param bool|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_option(). Default false (to skip past the - * short-circuit). - * @param string $option Option name. - * @param mixed $default The fallback value to return if the option does not exist. - * Default is false. + * @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_option(). + * Default false (to skip past the short-circuit). + * @param string $option Option name. + * @param mixed $default The fallback value to return if the option does not exist. + * Default false. */ $pre = apply_filters( "pre_option_{$option}", false, $option, $default ); @@ -69,8 +92,9 @@ $passed_default = func_num_args() > 1; if ( ! wp_installing() ) { - // prevent non-existent options from triggering multiple queries + // Prevent non-existent options from triggering multiple queries. $notoptions = wp_cache_get( 'notoptions', 'options' ); + if ( isset( $notoptions[ $option ] ) ) { /** * Filters the default value for an option. @@ -99,14 +123,15 @@ if ( false === $value ) { $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) ); - // Has to be get_row instead of get_var because of funkiness with 0, false, null values + // Has to be get_row() instead of get_var() because of funkiness with 0, false, null values. if ( is_object( $row ) ) { $value = $row->option_value; wp_cache_add( $option, $value, 'options' ); - } else { // option does not exist, so we must cache its non-existence + } else { // Option does not exist, so we must cache its non-existence. if ( ! is_array( $notoptions ) ) { $notoptions = array(); } + $notoptions[ $option ] = true; wp_cache_set( 'notoptions', $notoptions, 'options' ); @@ -119,6 +144,7 @@ $suppress = $wpdb->suppress_errors(); $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) ); $wpdb->suppress_errors( $suppress ); + if ( is_object( $row ) ) { $value = $row->option_value; } else { @@ -127,12 +153,12 @@ } } - // If home is not set use siteurl. - if ( 'home' == $option && '' == $value ) { + // If home is not set, use siteurl. + if ( 'home' === $option && '' === $value ) { return get_option( 'siteurl' ); } - if ( in_array( $option, array( 'siteurl', 'home', 'category_base', 'tag_base' ) ) ) { + if ( in_array( $option, array( 'siteurl', 'home', 'category_base', 'tag_base' ), true ) ) { $value = untrailingslashit( $value ); } @@ -153,7 +179,7 @@ } /** - * Protect WordPress special option from being modified. + * Protects WordPress special option from being modified. * * Will die if $option is in protected list. Protected options are 'alloptions' * and 'notoptions' options. @@ -164,12 +190,18 @@ */ function wp_protect_special_option( $option ) { if ( 'alloptions' === $option || 'notoptions' === $option ) { - wp_die( sprintf( __( '%s is a protected WP option and may not be modified' ), esc_html( $option ) ) ); + wp_die( + sprintf( + /* translators: %s: Option name. */ + __( '%s is a protected WP option and may not be modified' ), + esc_html( $option ) + ) + ); } } /** - * Print option value after sanitizing for forms. + * Prints option value after sanitizing for forms. * * @since 1.5.0 * @@ -183,23 +215,27 @@ * Loads and caches all autoloaded options, if available or all options. * * @since 2.2.0 + * @since 5.3.1 The `$force_cache` parameter was added. * * @global wpdb $wpdb WordPress database abstraction object. * + * @param bool $force_cache Optional. Whether to force an update of the local cache + * from the persistent cache. Default false. * @return array List of all options. */ -function wp_load_alloptions() { +function wp_load_alloptions( $force_cache = false ) { global $wpdb; if ( ! wp_installing() || ! is_multisite() ) { - $alloptions = wp_cache_get( 'alloptions', 'options' ); + $alloptions = wp_cache_get( 'alloptions', 'options', $force_cache ); } else { $alloptions = false; } if ( ! $alloptions ) { - $suppress = $wpdb->suppress_errors(); - if ( ! $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" ) ) { + $suppress = $wpdb->suppress_errors(); + $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" ); + if ( ! $alloptions_db ) { $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options" ); } $wpdb->suppress_errors( $suppress ); @@ -218,6 +254,7 @@ * @param array $alloptions Array with all options. */ $alloptions = apply_filters( 'pre_cache_alloptions', $alloptions ); + wp_cache_add( 'alloptions', $alloptions, 'options' ); } } @@ -267,27 +304,29 @@ } /** - * Update the value of an option that was already added. + * Updates the value of an option that was already added. * - * You do not need to serialize values. If the value needs to be serialized, then - * it will be serialized before it is inserted into the database. Remember, - * resources can not be serialized or added as an option. + * You do not need to serialize values. If the value needs to be serialized, + * then it will be serialized before it is inserted into the database. + * Remember, resources cannot be serialized or added as an option. * - * If the option does not exist, then the option will be added with the option value, - * with an `$autoload` value of 'yes'. + * If the option does not exist, it will be created. + + * This function is designed to work with or without a logged-in user. In terms of security, + * plugin developers should check the current user's capabilities before updating any options. * * @since 1.0.0 * @since 4.2.0 The `$autoload` parameter was added. * * @global wpdb $wpdb WordPress database abstraction object. * - * @param string $option Option name. Expected to not be SQL-escaped. + * @param string $option Name of the option to update. Expected to not be SQL-escaped. * @param mixed $value Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped. * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. For existing options, * `$autoload` can only be updated using `update_option()` if `$value` is also changed. * Accepts 'yes'|true to enable or 'no'|false to disable. For non-existent options, * the default value is 'yes'. Default null. - * @return bool False if value was not updated and true if value was updated. + * @return bool True if the value was updated, false otherwise. */ function update_option( $option, $value, $autoload = null ) { global $wpdb; @@ -297,6 +336,29 @@ return false; } + /* + * Until a proper _deprecated_option() function can be introduced, + * redirect requests to deprecated keys to the new, correct ones. + */ + $deprecated_keys = array( + 'blacklist_keys' => 'disallowed_keys', + 'comment_whitelist' => 'comment_previously_approved', + ); + + if ( ! wp_installing() && isset( $deprecated_keys[ $option ] ) ) { + _deprecated_argument( + __FUNCTION__, + '5.5.0', + sprintf( + /* translators: 1: Deprecated option key, 2: New option key. */ + __( 'The "%1$s" option key has been renamed to "%2$s".' ), + $option, + $deprecated_keys[ $option ] + ) + ); + return update_option( $deprecated_keys[ $option ], $value, $autoload ); + } + wp_protect_special_option( $option ); if ( is_object( $value ) ) { @@ -381,13 +443,14 @@ } $notoptions = wp_cache_get( 'notoptions', 'options' ); + if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) { unset( $notoptions[ $option ] ); wp_cache_set( 'notoptions', $notoptions, 'options' ); } if ( ! wp_installing() ) { - $alloptions = wp_load_alloptions(); + $alloptions = wp_load_alloptions( true ); if ( isset( $alloptions[ $option ] ) ) { $alloptions[ $option ] = $serialized_value; wp_cache_set( 'alloptions', $alloptions, 'options' ); @@ -420,15 +483,16 @@ * @param mixed $value The new option value. */ do_action( 'updated_option', $option, $old_value, $value ); + return true; } /** - * Add a new option. + * Adds a new option. * - * You do not need to serialize values. If the value needs to be serialized, then - * it will be serialized before it is inserted into the database. Remember, - * resources can not be serialized or added as an option. + * You do not need to serialize values. If the value needs to be serialized, + * then it will be serialized before it is inserted into the database. + * Remember, resources cannot be serialized or added as an option. * * You can create options without values and then update the values later. * Existing options will not be updated and checks are performed to ensure that you @@ -439,12 +503,13 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @param string $option Name of option to add. Expected to not be SQL-escaped. - * @param mixed $value Optional. Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped. - * @param string $deprecated Optional. Description. Not used anymore. - * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. - * Default is enabled. Accepts 'no' to disable for legacy reasons. - * @return bool False if option was not added and true if option was added. + * @param string $option Name of the option to add. Expected to not be SQL-escaped. + * @param mixed $value Optional. Option value. Must be serializable if non-scalar. + * Expected to not be SQL-escaped. + * @param string $deprecated Optional. Description. Not used anymore. + * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. + * Default is enabled. Accepts 'no' to disable for legacy reasons. + * @return bool True if the option was added, false otherwise. */ function add_option( $option, $value = '', $deprecated = '', $autoload = 'yes' ) { global $wpdb; @@ -458,6 +523,29 @@ return false; } + /* + * Until a proper _deprecated_option() function can be introduced, + * redirect requests to deprecated keys to the new, correct ones. + */ + $deprecated_keys = array( + 'blacklist_keys' => 'disallowed_keys', + 'comment_whitelist' => 'comment_previously_approved', + ); + + if ( ! wp_installing() && isset( $deprecated_keys[ $option ] ) ) { + _deprecated_argument( + __FUNCTION__, + '5.5.0', + sprintf( + /* translators: 1: Deprecated option key, 2: New option key. */ + __( 'The "%1$s" option key has been renamed to "%2$s".' ), + $option, + $deprecated_keys[ $option ] + ) + ); + return add_option( $deprecated_keys[ $option ], $value, $deprecated, $autoload ); + } + wp_protect_special_option( $option ); if ( is_object( $value ) ) { @@ -466,8 +554,10 @@ $value = sanitize_option( $option, $value ); - // Make sure the option doesn't already exist. We can check the 'notoptions' cache before we ask for a db query + // 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', 'options' ); + if ( ! is_array( $notoptions ) || ! isset( $notoptions[ $option ] ) ) { /** This filter is documented in wp-includes/option.php */ if ( apply_filters( "default_option_{$option}", false, $option, false ) !== get_option( $option ) ) { @@ -494,8 +584,8 @@ } if ( ! wp_installing() ) { - if ( 'yes' == $autoload ) { - $alloptions = wp_load_alloptions(); + if ( 'yes' === $autoload ) { + $alloptions = wp_load_alloptions( true ); $alloptions[ $option ] = $serialized_value; wp_cache_set( 'alloptions', $alloptions, 'options' ); } else { @@ -503,8 +593,9 @@ } } - // This option exists now - $notoptions = wp_cache_get( 'notoptions', 'options' ); // yes, again... we need it to be fresh + // This option exists now. + $notoptions = wp_cache_get( 'notoptions', 'options' ); // Yes, again... we need it to be fresh. + if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) { unset( $notoptions[ $option ] ); wp_cache_set( 'notoptions', $notoptions, 'options' ); @@ -532,6 +623,7 @@ * @param mixed $value Value of the option. */ do_action( 'added_option', $option, $value ); + return true; } @@ -542,8 +634,8 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @param string $option Name of option to remove. Expected to not be SQL-escaped. - * @return bool True, if option is successfully deleted. False on failure. + * @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_option( $option ) { global $wpdb; @@ -555,7 +647,7 @@ wp_protect_special_option( $option ); - // Get the ID, if no ID then return + // Get the ID, if no ID then return. $row = $wpdb->get_row( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s", $option ) ); if ( is_null( $row ) ) { return false; @@ -571,9 +663,10 @@ do_action( 'delete_option', $option ); $result = $wpdb->delete( $wpdb->options, array( 'option_name' => $option ) ); + if ( ! wp_installing() ) { - if ( 'yes' == $row->autoload ) { - $alloptions = wp_load_alloptions(); + if ( 'yes' === $row->autoload ) { + $alloptions = wp_load_alloptions( true ); if ( is_array( $alloptions ) && isset( $alloptions[ $option ] ) ) { unset( $alloptions[ $option ] ); wp_cache_set( 'alloptions', $alloptions, 'options' ); @@ -582,6 +675,7 @@ wp_cache_delete( $option, 'options' ); } } + if ( $result ) { /** @@ -603,18 +697,20 @@ * @param string $option Name of the deleted option. */ do_action( 'deleted_option', $option ); + return true; } + return false; } /** - * Delete a transient. + * Deletes a transient. * * @since 2.8.0 * * @param string $transient Transient name. Expected to not be SQL-escaped. - * @return bool true if successful, false otherwise + * @return bool True if the transient was deleted, false otherwise. */ function delete_transient( $transient ) { @@ -635,6 +731,7 @@ $option_timeout = '_transient_timeout_' . $transient; $option = '_transient_' . $transient; $result = delete_option( $option ); + if ( $result ) { delete_option( $option_timeout ); } @@ -656,7 +753,7 @@ } /** - * Get the value of a transient. + * Retrieves the value of a transient. * * If the transient does not exist, does not have a value, or has expired, * then the return value will be false. @@ -669,22 +766,23 @@ function get_transient( $transient ) { /** - * Filters the value of an existing transient. + * Filters the value of an existing transient before it is retrieved. * * The dynamic portion of the hook name, `$transient`, refers to the transient name. * - * Passing a truthy value to the filter will effectively short-circuit retrieval - * of the transient, returning the passed value instead. + * Returning a truthy value from the filter will effectively short-circuit retrieval + * and return the passed value instead. * * @since 2.8.0 * @since 4.4.0 The `$transient` parameter was added * * @param mixed $pre_transient The default value to return if the transient does not exist. * Any value other than false will short-circuit the retrieval - * of the transient, and return the returned value. + * of the transient, and return that value. * @param string $transient Transient name. */ $pre = apply_filters( "pre_transient_{$transient}", false, $transient ); + if ( false !== $pre ) { return $pre; } @@ -694,7 +792,7 @@ } else { $transient_option = '_transient_' . $transient; if ( ! wp_installing() ) { - // If option is not in alloptions, it is not autoloaded and thus has a timeout + // If option is not in alloptions, it is not autoloaded and thus has a timeout. $alloptions = wp_load_alloptions(); if ( ! isset( $alloptions[ $transient_option ] ) ) { $transient_timeout = '_transient_timeout_' . $transient; @@ -727,19 +825,19 @@ } /** - * Set/update the value of a transient. + * Sets/updates the value of a transient. * - * You do not need to serialize values. If the value needs to be serialized, then - * it will be serialized before it is set. + * You do not need to serialize values. If the value needs to be serialized, + * then it will be serialized before it is set. * * @since 2.8.0 * - * @param string $transient Transient name. Expected to not be SQL-escaped. Must be - * 172 characters or fewer in length. + * @param string $transient Transient name. Expected to not be SQL-escaped. + * Must be 172 characters or fewer in length. * @param mixed $value Transient value. Must be serializable if non-scalar. * Expected to not be SQL-escaped. * @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration). - * @return bool False if value was not set and true if value was set. + * @return bool True if the value was set, false otherwise. */ function set_transient( $transient, $value, $expiration = 0 ) { @@ -778,6 +876,7 @@ } else { $transient_timeout = '_transient_timeout_' . $transient; $transient_option = '_transient_' . $transient; + if ( false === get_option( $transient_option ) ) { $autoload = 'yes'; if ( $expiration ) { @@ -789,6 +888,7 @@ // If expiration is requested, but the transient has no timeout option, // delete, then re-create transient rather than update. $update = true; + if ( $expiration ) { if ( false === get_option( $transient_timeout ) ) { delete_option( $transient_option ); @@ -799,6 +899,7 @@ update_option( $transient_timeout, time() + $expiration ); } } + if ( $update ) { $result = update_option( $transient_option, $value ); } @@ -834,6 +935,7 @@ */ do_action( 'setted_transient', $transient, $value, $expiration ); } + return $result; } @@ -868,7 +970,7 @@ ); if ( ! is_multisite() ) { - // non-Multisite stores site transients in the options table. + // Single site stores site transients in the options table. $wpdb->query( $wpdb->prepare( "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b @@ -913,7 +1015,8 @@ return; } - if ( ! $user_id = get_current_user_id() ) { + $user_id = get_current_user_id(); + if ( ! $user_id ) { return; } @@ -926,7 +1029,7 @@ if ( isset( $_COOKIE[ 'wp-settings-' . $user_id ] ) ) { $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE[ 'wp-settings-' . $user_id ] ); - // No change or both empty + // No change or both empty. if ( $cookie == $settings ) { return; } @@ -934,7 +1037,7 @@ $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 + // 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 ); @@ -950,13 +1053,13 @@ } /** - * Retrieve user interface setting value based on setting name. + * Retrieves user interface setting value based on setting name. * * @since 2.7.0 * * @param string $name The name of the setting. * @param string $default Optional default value to return when $name is not set. - * @return mixed the last saved user setting or the default value/false if it doesn't exist. + * @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(); @@ -965,9 +1068,9 @@ } /** - * Add or update user interface setting. + * Adds or updates user interface setting. * - * Both $name and $value can contain only ASCII letters, numbers and underscores. + * 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(). * @@ -975,7 +1078,8 @@ * * @param string $name The name of the setting. * @param string $value The value for the setting. - * @return bool|null True if set successfully, false if not. Null if the current user can't be established. + * @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 ) { if ( headers_sent() ) { @@ -989,7 +1093,7 @@ } /** - * Delete user interface settings. + * Deletes user interface settings. * * Deleting settings would reset them to the defaults. * @@ -998,7 +1102,8 @@ * @since 2.7.0 * * @param string $names The name or array of names of the setting to be deleted. - * @return bool|null True if deleted successfully, false if not. Null if the current user can't be established. + * @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 ) { if ( headers_sent() ) { @@ -1024,18 +1129,19 @@ } /** - * Retrieve all user interface settings. + * Retrieves all user interface settings. * * @since 2.7.0 * * @global array $_updated_user_settings * - * @return array the last saved user settings or empty array. + * @return array The last saved user settings or empty array. */ function get_all_user_settings() { global $_updated_user_settings; - if ( ! $user_id = get_current_user_id() ) { + $user_id = get_current_user_id(); + if ( ! $user_id ) { return array(); } @@ -1048,7 +1154,7 @@ 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 + if ( strpos( $cookie, '=' ) ) { // '=' cannot be 1st char. parse_str( $cookie, $user_settings ); } } else { @@ -1064,7 +1170,7 @@ } /** - * Private. Set all user interface settings. + * Private. Sets all user interface settings. * * @since 2.8.0 * @access private @@ -1072,13 +1178,14 @@ * @global array $_updated_user_settings * * @param array $user_settings User settings. - * @return bool|null False if the current user can't be found, null if the current - * user is not a super admin or a member of the site, otherwise true. + * @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; - if ( ! $user_id = get_current_user_id() ) { + $user_id = get_current_user_id(); + if ( ! $user_id ) { return false; } @@ -1106,12 +1213,13 @@ } /** - * Delete the user settings of the current user. + * Deletes the user settings of the current user. * * @since 2.7.0 */ function delete_all_user_settings() { - if ( ! $user_id = get_current_user_id() ) { + $user_id = get_current_user_id(); + if ( ! $user_id ) { return; } @@ -1128,8 +1236,8 @@ * * @see get_network_option() * - * @param string $option Name of option to retrieve. Expected to not be SQL-escaped. - * @param mixed $default Optional value to return if option doesn't exist. Default false. + * @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. */ @@ -1138,7 +1246,7 @@ } /** - * Add a new option for the current network. + * 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. * @@ -1147,9 +1255,9 @@ * * @see add_network_option() * - * @param string $option Name of option to add. Expected to not be SQL-escaped. + * @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 False if the option was not added. True if the option was added. + * @return bool True if the option was added, false otherwise. */ function add_site_option( $option, $value ) { return add_network_option( null, $option, $value ); @@ -1163,31 +1271,31 @@ * * @see delete_network_option() * - * @param string $option Name of option to remove. Expected to not be SQL-escaped. - * @return bool True, if succeed. False, if failure. + * @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 ); } /** - * Update the value of an option that was already added for the current network. + * Updates the value of an option that was already added for the current network. * * @since 2.8.0 * @since 4.4.0 Modified into wrapper for update_network_option() * * @see update_network_option() * - * @param string $option Name of option. Expected to not be SQL-escaped. + * @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 False if value was not updated. True if value was updated. + * @return bool True if the value was updated, false otherwise. */ function update_site_option( $option, $value ) { return update_network_option( null, $option, $value ); } /** - * Retrieve a network's option value based on the option name. + * Retrieves a network's option value based on the option name. * * @since 4.4.0 * @@ -1195,9 +1303,9 @@ * * @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 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 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 ) { @@ -1215,12 +1323,12 @@ } /** - * Filters an existing network option before it is retrieved. + * 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. * - * Passing a truthy value to the filter will effectively short-circuit retrieval, - * returning the passed value instead. + * 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 3.0.0 @@ -1228,14 +1336,14 @@ * @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 - * is false (to skip past the short-circuit). + * @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. - * Default is false. + * Default false. */ $pre = apply_filters( "pre_site_option_{$option}", false, $option, $network_id, $default ); @@ -1243,7 +1351,7 @@ return $pre; } - // prevent non-existent options from triggering multiple queries + // Prevent non-existent options from triggering multiple queries. $notoptions_key = "$network_id:notoptions"; $notoptions = wp_cache_get( $notoptions_key, 'site-options' ); @@ -1277,7 +1385,7 @@ 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 + // 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 ); @@ -1286,6 +1394,7 @@ if ( ! is_array( $notoptions ) ) { $notoptions = array(); } + $notoptions[ $option ] = true; wp_cache_set( $notoptions_key, $notoptions, 'site-options' ); @@ -1318,7 +1427,7 @@ } /** - * Add a new network option. + * Adds a new network option. * * Existing options will not be updated. * @@ -1329,9 +1438,9 @@ * @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 option to add. Expected to not be SQL-escaped. + * @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 False if option was not added and true if option was added. + * @return bool True if the option was added, false otherwise. */ function add_network_option( $network_id, $option, $value ) { global $wpdb; @@ -1372,8 +1481,10 @@ } else { $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 + // 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 ) ) { return false; @@ -1398,8 +1509,9 @@ wp_cache_set( $cache_key, $value, 'site-options' ); - // This option exists now - $notoptions = wp_cache_get( $notoptions_key, 'site-options' ); // yes, again... we need it to be fresh + // This option exists now. + $notoptions = wp_cache_get( $notoptions_key, 'site-options' ); // Yes, again... we need it to be fresh. + if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) { unset( $notoptions[ $option ] ); wp_cache_set( $notoptions_key, $notoptions, 'site-options' ); @@ -1451,8 +1563,8 @@ * @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 option to remove. Expected to not be SQL-escaped. - * @return bool True, if succeed. False, if failure. + * @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_network_option( $network_id, $option ) { global $wpdb; @@ -1535,7 +1647,7 @@ } /** - * Update the value of a network option that was already added. + * Updates the value of a network option that was already added. * * @since 4.4.0 * @@ -1543,10 +1655,10 @@ * * @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 option. Expected to not be SQL-escaped. - * @param mixed $value Option value. Expected to not be SQL-escaped. - * @return bool False if value was not updated and true if value was updated. + * @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. 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_network_option( $network_id, $option, $value ) { global $wpdb; @@ -1602,6 +1714,7 @@ $notoptions_key = "$network_id:notoptions"; $notoptions = wp_cache_get( $notoptions_key, 'site-options' ); + if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) { unset( $notoptions[ $option ] ); wp_cache_set( $notoptions_key, $notoptions, 'site-options' ); @@ -1666,12 +1779,12 @@ } /** - * Delete a site transient. + * Deletes a site transient. * * @since 2.9.0 * * @param string $transient Transient name. Expected to not be SQL-escaped. - * @return bool True if successful, false otherwise + * @return bool True if the transient was deleted, false otherwise. */ function delete_site_transient( $transient ) { @@ -1692,10 +1805,12 @@ $option_timeout = '_site_transient_timeout_' . $transient; $option = '_site_transient_' . $transient; $result = delete_site_option( $option ); + if ( $result ) { delete_site_option( $option_timeout ); } } + if ( $result ) { /** @@ -1712,7 +1827,7 @@ } /** - * Get the value of a site transient. + * Retrieves the value of a site transient. * * If the transient does not exist, does not have a value, or has expired, * then the return value will be false. @@ -1727,19 +1842,19 @@ function get_site_transient( $transient ) { /** - * Filters the value of an existing site transient. + * Filters the value of an existing site transient before it is retrieved. * * The dynamic portion of the hook name, `$transient`, refers to the transient name. * - * Passing a truthy value to the filter will effectively short-circuit retrieval, - * returning the passed value instead. + * Returning a truthy value from the filter will effectively short-circuit retrieval + * and return the passed value instead. * * @since 2.9.0 * @since 4.4.0 The `$transient` parameter was added. * * @param mixed $pre_site_transient The default value to return if the site transient does not exist. * Any value other than false will short-circuit the retrieval - * of the transient, and return the returned value. + * of the transient, and return that value. * @param string $transient Transient name. */ $pre = apply_filters( "pre_site_transient_{$transient}", false, $transient ); @@ -1754,7 +1869,7 @@ // Core transients that do not have a timeout. Listed here so querying timeouts can be avoided. $no_timeout = array( 'update_core', 'update_plugins', 'update_themes' ); $transient_option = '_site_transient_' . $transient; - if ( ! in_array( $transient, $no_timeout ) ) { + if ( ! in_array( $transient, $no_timeout, true ) ) { $transient_timeout = '_site_transient_timeout_' . $transient; $timeout = get_site_option( $transient_timeout ); if ( false !== $timeout && $timeout < time() ) { @@ -1784,10 +1899,10 @@ } /** - * Set/update the value of a site transient. + * Sets/updates the value of a site transient. * - * You do not need to serialize values, if the value needs to be serialize, then - * it will be serialized before it is set. + * You do not need to serialize values. If the value needs to be serialized, + * then it will be serialized before it is set. * * @since 2.9.0 * @@ -1797,7 +1912,7 @@ * 167 characters or fewer in length. * @param mixed $value Transient value. Expected to not be SQL-escaped. * @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration). - * @return bool False if value was not set and true if value was set. + * @return bool True if the value was set, false otherwise. */ function set_site_transient( $transient, $value, $expiration = 0 ) { @@ -1834,6 +1949,7 @@ } else { $transient_timeout = '_site_transient_timeout_' . $transient; $option = '_site_transient_' . $transient; + if ( false === get_site_option( $option ) ) { if ( $expiration ) { add_site_option( $transient_timeout, time() + $expiration ); @@ -1846,6 +1962,7 @@ $result = update_site_option( $option, $value ); } } + if ( $result ) { /** @@ -1873,11 +1990,12 @@ */ do_action( 'setted_site_transient', $transient, $value, $expiration ); } + return $result; } /** - * Register default settings available in WordPress. + * Registers default settings available in WordPress. * * The settings registered here are primarily useful for the REST API, so this * does not encompass all settings available in WordPress. @@ -2064,36 +2182,47 @@ ), ), 'type' => 'string', - 'description' => __( 'Allow people to post comments on new articles.' ), + 'description' => __( 'Allow people to submit comments on new posts.' ), ) ); } /** - * Register a setting and its data. + * Registers a setting and its data. * * @since 2.7.0 * @since 4.7.0 `$args` can be passed to set flags on the setting, similar to `register_meta()`. + * @since 5.5.0 `$new_whitelist_options` was renamed to `$new_allowed_options`. + * Please consider writing more inclusive code. * - * @global array $new_whitelist_options + * @global array $new_allowed_options * @global array $wp_registered_settings * - * @param string $option_group A settings group name. Should correspond to a whitelisted option key name. - * Default whitelisted option key names include "general," "discussion," and "reading," among others. + * @param string $option_group A settings group name. Should correspond to an allowed option key name. + * Default allowed option key names include 'general', 'discussion', 'media', + * 'reading', 'writing', 'misc', 'options', and 'privacy'. * @param string $option_name The name of an option to sanitize and save. * @param array $args { * Data used to describe the setting when registered. * - * @type string $type The type of data associated with this setting. - * Valid values are 'string', 'boolean', 'integer', and 'number'. - * @type string $description A description of the data attached to this setting. - * @type callable $sanitize_callback A callback function that sanitizes the option's value. - * @type bool $show_in_rest Whether data associated with this setting should be included in the REST API. - * @type mixed $default Default value when calling `get_option()`. + * @type string $type The type of data associated with this setting. + * Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'. + * @type string $description A description of the data attached to this setting. + * @type callable $sanitize_callback A callback function that sanitizes the option's value. + * @type bool|array $show_in_rest Whether data associated with this setting should be included in the REST API. + * When registering complex settings, this argument may optionally be an + * array with a 'schema' key. + * @type mixed $default Default value when calling `get_option()`. * } */ function register_setting( $option_group, $option_name, $args = array() ) { - global $new_whitelist_options, $wp_registered_settings; + global $new_allowed_options, $wp_registered_settings; + + /* + * In 5.5.0, the `$new_whitelist_options` global variable was renamed to `$new_allowed_options`. + * Please consider writing more inclusive code. + */ + $GLOBALS['new_whitelist_options'] = &$new_allowed_options; $defaults = array( 'type' => 'string', @@ -2121,18 +2250,24 @@ * @param string $option_name Setting name. */ $args = apply_filters( 'register_setting_args', $args, $defaults, $option_group, $option_name ); + $args = wp_parse_args( $args, $defaults ); + // Require an item schema when registering settings with an array type. + if ( false !== $args['show_in_rest'] && 'array' === $args['type'] && ( ! is_array( $args['show_in_rest'] ) || ! isset( $args['show_in_rest']['schema']['items'] ) ) ) { + _doing_it_wrong( __FUNCTION__, __( 'When registering an "array" setting to show in the REST API, you must specify the schema for each array item in "show_in_rest.schema.items".' ), '5.4.0' ); + } + if ( ! is_array( $wp_registered_settings ) ) { $wp_registered_settings = array(); } - if ( 'misc' == $option_group ) { + if ( 'misc' === $option_group ) { _deprecated_argument( __FUNCTION__, '3.0.0', - /* translators: %s: misc */ sprintf( + /* translators: %s: misc */ __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) @@ -2140,12 +2275,12 @@ $option_group = 'general'; } - if ( 'privacy' == $option_group ) { + if ( 'privacy' === $option_group ) { _deprecated_argument( __FUNCTION__, '3.5.0', - /* translators: %s: privacy */ sprintf( + /* translators: %s: privacy */ __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) @@ -2153,7 +2288,8 @@ $option_group = 'reading'; } - $new_whitelist_options[ $option_group ][] = $option_name; + $new_allowed_options[ $option_group ][] = $option_name; + if ( ! empty( $args['sanitize_callback'] ) ) { add_filter( "sanitize_option_{$option_name}", $args['sanitize_callback'] ); } @@ -2161,31 +2297,50 @@ add_filter( "default_option_{$option_name}", 'filter_default_option', 10, 3 ); } + /** + * Fires immediately before the setting is registered but after its filters are in place. + * + * @since 5.5.0 + * + * @param string $option_group Setting group. + * @param string $option_name Setting name. + * @param array $args Array of setting registration arguments. + */ + do_action( 'register_setting', $option_group, $option_name, $args ); + $wp_registered_settings[ $option_name ] = $args; } /** - * Unregister a setting. + * Unregisters a setting. * * @since 2.7.0 * @since 4.7.0 `$sanitize_callback` was deprecated. The callback from `register_setting()` is now used instead. + * @since 5.5.0 `$new_whitelist_options` was renamed to `$new_allowed_options`. + * Please consider writing more inclusive code. * - * @global array $new_whitelist_options + * @global array $new_allowed_options * @global array $wp_registered_settings * - * @param string $option_group The settings group name used during registration. - * @param string $option_name The name of the option to unregister. - * @param callable $deprecated Deprecated. + * @param string $option_group The settings group name used during registration. + * @param string $option_name The name of the option to unregister. + * @param callable $deprecated Deprecated. */ function unregister_setting( $option_group, $option_name, $deprecated = '' ) { - global $new_whitelist_options, $wp_registered_settings; + global $new_allowed_options, $wp_registered_settings; - if ( 'misc' == $option_group ) { + /* + * In 5.5.0, the `$new_whitelist_options` global variable was renamed to `$new_allowed_options`. + * Please consider writing more inclusive code. + */ + $GLOBALS['new_whitelist_options'] = &$new_allowed_options; + + if ( 'misc' === $option_group ) { _deprecated_argument( __FUNCTION__, '3.0.0', - /* translators: %s: misc */ sprintf( + /* translators: %s: misc */ __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) @@ -2193,12 +2348,12 @@ $option_group = 'general'; } - if ( 'privacy' == $option_group ) { + if ( 'privacy' === $option_group ) { _deprecated_argument( __FUNCTION__, '3.5.0', - /* translators: %s: privacy */ sprintf( + /* translators: %s: privacy */ __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) @@ -2206,16 +2361,18 @@ $option_group = 'reading'; } - $pos = array_search( $option_name, (array) $new_whitelist_options[ $option_group ] ); - if ( $pos !== false ) { - unset( $new_whitelist_options[ $option_group ][ $pos ] ); + $pos = array_search( $option_name, (array) $new_allowed_options[ $option_group ], true ); + + if ( false !== $pos ) { + unset( $new_allowed_options[ $option_group ][ $pos ] ); } + if ( '' !== $deprecated ) { _deprecated_argument( __FUNCTION__, '4.7.0', - /* translators: 1: $sanitize_callback, 2: register_setting() */ sprintf( + /* translators: 1: $sanitize_callback, 2: register_setting() */ __( '%1$s is deprecated. The callback from %2$s is used instead.' ), '$sanitize_callback', 'register_setting()' @@ -2235,6 +2392,16 @@ remove_filter( "default_option_{$option_name}", 'filter_default_option', 10 ); } + /** + * Fires immediately before the setting is unregistered and after its filters have been removed. + * + * @since 5.5.0 + * + * @param string $option_group Setting group. + * @param string $option_name Setting name. + */ + do_action( 'unregister_setting', $option_group, $option_name ); + unset( $wp_registered_settings[ $option_name ] ); } } @@ -2259,16 +2426,16 @@ } /** - * Filter the default value for the option. + * Filters the default value for the option. * * For settings which register a default setting in `register_setting()`, this * function is added as a filter to `default_option_{$option}`. * * @since 4.7.0 * - * @param mixed $default Existing default value to return. - * @param string $option Option name. - * @param bool $passed_default Was `get_option()` passed a default value? + * @param mixed $default Existing default value to return. + * @param string $option Option name. + * @param bool $passed_default Was `get_option()` passed a default value? * @return mixed Filtered default value. */ function filter_default_option( $default, $option, $passed_default ) {