Make WordPress Core


Ignore:
Timestamp:
10/10/2023 09:35:09 AM (20 months ago)
Author:
spacedmonkey
Message:

Options, Meta APIs: Prevent unnecessary database updates caused by strict comparisons in update_network_option.

Previously, in the update_network_option function, strict comparisons between old and new values could erroneously trigger updates in cases where there was no actual change in values. Building upon the groundwork laid in #22192, this commit introduces the use of the _is_equal_database_value function to accurately compare values before initiating any database updates. This change ensures consistency between the behaviors of update_option and update_network_option.

Furthermore, we have incorporated similar workarounds that were previously implemented in [56717]. These changes ensure that the raw version of network option values is considered in the comparisons, enhancing the overall reliability of the update process.

Props mukesh27, flixos90, joemcgill, costdev, spacedmonkey.
Fixes #59360.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/option.php

    r56796 r56814  
    21262126    wp_protect_special_option( $option );
    21272127
    2128     $old_value = get_network_option( $network_id, $option, false );
     2128    $old_value = get_network_option( $network_id, $option );
    21292129
    21302130    /**
     
    21462146
    21472147    /*
     2148     * To get the actual raw old value from the database, any existing pre filters need to be temporarily disabled.
     2149     * Immediately after getting the raw value, they are reinstated.
     2150     * The raw value is only used to determine whether a value is present in the database. It is not used anywhere
     2151     * else, and is not passed to any of the hooks either.
     2152     */
     2153    global $wp_filter;
     2154
     2155    /** This filter is documented in wp-includes/option.php */
     2156    $default_value = apply_filters( "default_site_option_{$option}", false, $option, $network_id );
     2157
     2158    $has_site_filter   = has_filter( "pre_site_option_{$option}" );
     2159    $has_option_filter = has_filter( "pre_option_{$option}" );
     2160    if ( $has_site_filter || $has_option_filter ) {
     2161        if ( $has_site_filter ) {
     2162            $old_ms_filters = $wp_filter[ "pre_site_option_{$option}" ];
     2163            unset( $wp_filter[ "pre_site_option_{$option}" ] );
     2164        }
     2165
     2166        if ( $has_option_filter ) {
     2167            $old_single_site_filters = $wp_filter[ "pre_option_{$option}" ];
     2168            unset( $wp_filter[ "pre_option_{$option}" ] );
     2169        }
     2170
     2171        if ( is_multisite() ) {
     2172            $raw_old_value = get_network_option( $network_id, $option );
     2173        } else {
     2174            $raw_old_value = get_option( $option, $default_value );
     2175        }
     2176
     2177        if ( $has_site_filter ) {
     2178            $wp_filter[ "pre_site_option_{$option}" ] = $old_ms_filters;
     2179        }
     2180        if ( $has_option_filter ) {
     2181            $wp_filter[ "pre_option_{$option}" ] = $old_single_site_filters;
     2182        }
     2183    } else {
     2184        $raw_old_value = $old_value;
     2185    }
     2186
     2187    if ( ! is_multisite() ) {
     2188        /** This filter is documented in wp-includes/option.php */
     2189        $default_value = apply_filters( "default_option_{$option}", $default_value, $option, true );
     2190    }
     2191
     2192    /*
    21482193     * If the new and old values are the same, no need to update.
    21492194     *
    2150      * Unserialized values will be adequate in most cases. If the unserialized
    2151      * data differs, the (maybe) serialized data is checked to avoid
    2152      * unnecessary database calls for otherwise identical object instances.
    2153      *
    2154      * See https://core.trac.wordpress.org/ticket/44956
    2155      */
    2156     if ( $value === $old_value || maybe_serialize( $value ) === maybe_serialize( $old_value ) ) {
     2195     * An exception applies when no value is set in the database, i.e. the old value is the default.
     2196     * In that case, the new value should always be added as it may be intentional to store it rather than relying on the default.
     2197     *
     2198     * See https://core.trac.wordpress.org/ticket/44956 and https://core.trac.wordpress.org/ticket/22192 and https://core.trac.wordpress.org/ticket/59360
     2199     */
     2200    if (
     2201        $value === $raw_old_value ||
     2202        (
     2203            false !== $raw_old_value &&
     2204            /*
     2205             * Single site stores values in the `option_value` field, which cannot be set to NULL.
     2206             * This means a PHP `null` value will be cast to an empty string, which can be considered
     2207             * equal to values such as an empty string, or false when cast to string.
     2208             *
     2209             * However, Multisite stores values in the `meta_value` field, which can be set to NULL.
     2210             * As NULL is unique in the database, skip checking an old or new value of NULL
     2211             * against any other value.
     2212             */
     2213            ( ! is_multisite() || ! ( null === $raw_old_value || null === $value ) ) &&
     2214            _is_equal_database_value( $raw_old_value, $value )
     2215        )
     2216    ) {
    21572217        return false;
    21582218    }
    21592219
    2160     if ( false === $old_value ) {
     2220    if ( $default_value === $raw_old_value ) {
    21612221        return add_network_option( $network_id, $option, $value );
    21622222    }
Note: See TracChangeset for help on using the changeset viewer.