Make WordPress Core


Ignore:
Timestamp:
09/25/2023 04:23:52 PM (16 months ago)
Author:
flixos90
Message:

Options, Meta APIs: Improve logic to avoid unnecessary database writes in update_option().

Prior to this change, a strict comparison between the old and new database value could lead to a false negative, since database values are generally stored as strings. For example, passing an integer to update_option() would almost always result in an update given any existing database value for that option would be that number cast to a string.

This changeset adjusts the logic to perform an intentional "loose-y" comparison by casting the values to strings. Extensive coverage previously added in [56648] provides additional confidence that this does not introduce any backward compatibility issues.

Props mukesh27, costdev, spacedmonkey, joemcgill, flixos90, nacin, atimmer, duck_, boonebgorges.
Fixes #22192.

File:
1 edited

Legend:

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

    r56595 r56681  
    777777    $value = apply_filters( 'pre_update_option', $value, $option, $old_value );
    778778
     779    /** This filter is documented in wp-includes/option.php */
     780    $default_value = apply_filters( "default_option_{$option}", false, $option, false );
     781
    779782    /*
    780783     * If the new and old values are the same, no need to update.
    781784     *
    782      * Unserialized values will be adequate in most cases. If the unserialized
    783      * data differs, the (maybe) serialized data is checked to avoid
    784      * unnecessary database calls for otherwise identical object instances.
    785      *
    786      * See https://core.trac.wordpress.org/ticket/38903
    787      */
    788     if ( $value === $old_value || maybe_serialize( $value ) === maybe_serialize( $old_value ) ) {
     785     * An exception applies when no value is set in the database, i.e. the old value is the default.
     786     * In that case, the new value should always be added as it may be intentional to store it rather than relying on the default.
     787     *
     788     * See https://core.trac.wordpress.org/ticket/38903 and https://core.trac.wordpress.org/ticket/22192.
     789     */
     790    if ( $old_value !== $default_value && _is_equal_database_value( $old_value, $value ) ) {
    789791        return false;
    790792    }
    791793
    792     /** This filter is documented in wp-includes/option.php */
    793     if ( apply_filters( "default_option_{$option}", false, $option, false ) === $old_value ) {
     794    if ( $old_value === $default_value ) {
     795
    794796        // Default setting for new options is 'yes'.
    795797        if ( null === $autoload ) {
     
    28882890    return $registered[ $option ]['default'];
    28892891}
     2892
     2893/**
     2894 * Determines whether two values will be equal when stored in the database.
     2895 *
     2896 * @since 6.4.0
     2897 * @access private
     2898 *
     2899 * @param mixed $old_value The old value to compare.
     2900 * @param mixed $new_value The new value to compare.
     2901 * @return bool True if the values are equal, false otherwise.
     2902 */
     2903function _is_equal_database_value( $old_value, $new_value ) {
     2904    $values = array(
     2905        'old' => $old_value,
     2906        'new' => $new_value,
     2907    );
     2908
     2909    foreach ( $values as $_key => &$_value ) {
     2910        // Cast scalars or null to a string so type discrepancies don't result in cache misses.
     2911        if ( null === $_value || is_scalar( $_value ) ) {
     2912            $_value = (string) $_value;
     2913        }
     2914    }
     2915
     2916    if ( $values['old'] === $values['new'] ) {
     2917        return true;
     2918    }
     2919
     2920    /*
     2921     * Unserialized values will be adequate in most cases. If the unserialized
     2922     * data differs, the (maybe) serialized data is checked to avoid
     2923     * unnecessary database calls for otherwise identical object instances.
     2924     *
     2925     * See https://core.trac.wordpress.org/ticket/38903
     2926     */
     2927    return maybe_serialize( $old_value ) === maybe_serialize( $new_value );
     2928}
Note: See TracChangeset for help on using the changeset viewer.