Make WordPress Core

Changeset 56717


Ignore:
Timestamp:
09/26/2023 03:53:39 PM (10 months ago)
Author:
flixos90
Message:

Options, Meta APIs: Fix follow up bug when comparing values for options using the pre_option_{$option} filter.

This fix is relevant for options such as gmt_offset that use a filter to force a specific value regardless of what is stored in the database.

Props mamaduka, flixos90, mukesh27, spacedmonkey.
See #22192.

Location:
trunk
Files:
2 edited

Legend:

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

    r56681 r56717  
    777777    $value = apply_filters( 'pre_update_option', $value, $option, $old_value );
    778778
     779    /*
     780     * To get the actual raw old value from the database, any existing pre filters need to be temporarily disabled.
     781     * Immediately after getting the raw value, they are reinstated.
     782     * The raw value is only used to determine whether a value is present in the database. It is not used anywhere
     783     * else, and is not passed to any of the hooks either.
     784     */
     785    if ( has_filter( "pre_option_{$option}" ) ) {
     786        global $wp_filter;
     787
     788        $old_filters = $wp_filter[ "pre_option_{$option}" ];
     789        unset( $wp_filter[ "pre_option_{$option}" ] );
     790
     791        $raw_old_value                       = get_option( $option );
     792        $wp_filter[ "pre_option_{$option}" ] = $old_filters;
     793    } else {
     794        $raw_old_value = $old_value;
     795    }
     796
    779797    /** This filter is documented in wp-includes/option.php */
    780798    $default_value = apply_filters( "default_option_{$option}", false, $option, false );
     
    788806     * See https://core.trac.wordpress.org/ticket/38903 and https://core.trac.wordpress.org/ticket/22192.
    789807     */
    790     if ( $old_value !== $default_value && _is_equal_database_value( $old_value, $value ) ) {
     808    if ( $raw_old_value !== $default_value && _is_equal_database_value( $raw_old_value, $value ) ) {
    791809        return false;
    792810    }
    793811
    794     if ( $old_value === $default_value ) {
     812    if ( $raw_old_value === $default_value ) {
    795813
    796814        // Default setting for new options is 'yes'.
  • trunk/tests/phpunit/tests/option/option.php

    r56681 r56717  
    439439        }
    440440    }
    441 
    442441
    443442    /**
     
    582581        $this->assertSame( 1, $actual );
    583582    }
     583
     584    /**
     585     * Tests that a non-existing option is added even when its pre filter returns a value.
     586     *
     587     * @ticket 22192
     588     *
     589     * @covers ::update_option
     590     */
     591    public function test_update_option_with_pre_filter_adds_missing_option() {
     592        // Force a return value of integer 0.
     593        add_filter( 'pre_option_foo', '__return_zero' );
     594
     595        /*
     596         * This should succeed, since the 'foo' option does not exist in the database.
     597         * The default value is false, so it differs from 0.
     598         */
     599        $this->assertTrue( update_option( 'foo', 0 ) );
     600    }
     601
     602    /**
     603     * Tests that an existing option is updated even when its pre filter returns the same value.
     604     *
     605     * @ticket 22192
     606     *
     607     * @covers ::update_option
     608     */
     609    public function test_update_option_with_pre_filter_updates_option_with_different_value() {
     610        // Add the option with a value of 1 to the database.
     611        add_option( 'foo', 1 );
     612
     613        // Force a return value of integer 0.
     614        add_filter( 'pre_option_foo', '__return_zero' );
     615
     616        /*
     617         * This should succeed, since the 'foo' option has a value of 1 in the database.
     618         * Therefore it differs from 0 and should be updated.
     619         */
     620        $this->assertTrue( update_option( 'foo', 0 ) );
     621    }
     622
     623    /**
     624     * Tests that calling update_option() does not permanently remove pre filters.
     625     *
     626     * @ticket 22192
     627     *
     628     * @covers ::update_option
     629     */
     630    public function test_update_option_maintains_pre_filters() {
     631        add_filter( 'pre_option_foo', '__return_zero' );
     632        update_option( 'foo', 0 );
     633
     634        // Assert that the filter is still present.
     635        $this->assertSame( 10, has_filter( 'pre_option_foo', '__return_zero' ) );
     636    }
    584637}
Note: See TracChangeset for help on using the changeset viewer.