Make WordPress Core


Ignore:
Timestamp:
04/03/2024 09:29:13 PM (15 months ago)
Author:
flixos90
Message:

Options, Meta APIs: Use more sensible default for autoloading options which allows WordPress core to make a decision.

An excessive amount of autoloaded options is a common cause for slow database responses, sometimes caused by very large individual autoloaded options. As it is not mandatory to provide an autoload value when adding an option to the database, it tends to be ignored, which in combination with a default value of "yes" and lack of documentation can lead to the aforementioned problem.

This changeset enhances the option autoloading behavior in several ways:

  • Update the function documentation to encourage the use of boolean true or false to explicitly provide an autoload value for an option.
  • Use new string values on and off for explicitly provided values stored in the database, to distinguish them from yes and no, since yes does not allow determining whether it was set intentionally by the developer or only as a default.
  • Effectively deprecate the values yes and no. They are still supported for backward compatibility, but now discouraged.
  • Use null as new default autoload value for add_option(). If the developer does not provide an explicit value, this will now trigger WordPress logic to determine an autoload value to use:
    • If WordPress determines that the option should not be autoloaded, it is stored in the database as auto-off. As part of this changeset, the single heuristic introduced for that is to check whether the option size is larger than a threshold of 150k bytes. This threshold is filterable via a new wp_max_autoloaded_option_size filter.
    • If WordPress determines that the option should be autoloaded, it is stored in the database as auto-on. No logic to make such a decision is introduced as part of this changeset, but a new filter wp_default_autoload_value can be used to define such heuristics, e.g. by optimization plugins.
    • If WordPress cannot determine whether or not to autoload the option, it is stored in the database as auto.
    • This effectively means that any option without an explicit autoload value provided by the developer will be stored with an autoload value of auto, unless the option's size exceeds the aforementioned threshold. Options with a value of auto are still autoloaded as of today, most importantly for backward compatibility. A new function wp_autoload_values_to_autoload() returns the list of autolaod values that dictate for an option to be autoloaded, and a new filter wp_autoload_values_to_autoload can be used to alter that list.

These behavioral changes encourage developers to be more mindful of autoloading, while providing WordPress core and optimization plugins with additional control over heuristics for autoloading options where no explicit autoload value was provided.

At the same time, the changes are fully backward compatible from a functionality perspective, with the only exception being that very large options will now no longer be autoloaded if the developer did not explicitly request for them to be autoloaded. Neither WordPress core nor plugins are able to override an explicitly provided value, which is intentional to continue giving developers full control over their own options.

Props pbearne, flixos90, joemcgill, azaozz, spacedmonkey, swissspidy, mukesh27, markjaquith.
Fixes #42441.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/option/option.php

    r56946 r57920  
    309309            'string 123'   => array( '123' ),
    310310            'integer 123'  => array( 123 ),
    311             'integer -123' => array( -123 ),
     311            'integer -123' => array( - 123 ),
    312312            'float 12.3'   => array( 12.3 ),
    313             'float -1.23'  => array( -1.23 ),
     313            'float -1.23'  => array( - 1.23 ),
    314314            'boolean true' => array( true ),
    315315        );
     
    360360    public function data_option_autoloading() {
    361361        return array(
    362             array( 'autoload_yes', 'yes', 'yes' ),
    363             array( 'autoload_true', true, 'yes' ),
    364             array( 'autoload_string', 'foo', 'yes' ),
    365             array( 'autoload_int', 123456, 'yes' ),
    366             array( 'autoload_array', array(), 'yes' ),
    367             array( 'autoload_no', 'no', 'no' ),
    368             array( 'autoload_false', false, 'no' ),
    369         );
     362            // Supported values.
     363            array( 'autoload_yes', 'yes', 'on' ),
     364            array( 'autoload_true', true, 'on' ),
     365            array( 'autoload_no', 'no', 'off' ),
     366            array( 'autoload_false', false, 'off' ),
     367            array( 'autoload_null', null, 'auto' ),
     368
     369            // Technically unsupported values.
     370            array( 'autoload_string', 'foo', 'auto' ),
     371            array( 'autoload_int', 123456, 'auto' ),
     372            array( 'autoload_array', array(), 'auto' ),
     373        );
     374    }
     375
     376    /**
     377     * @ticket 42441
     378     *
     379     * @covers ::update_option
     380     *
     381     * @dataProvider data_option_autoloading_large_option
     382     */
     383    public function test_update_option_autoloading_large_option( $autoload, $expected ) {
     384        global $wpdb;
     385        $name = 'foo';
     386        add_option( $name, 'bar' );
     387        add_filter( 'wp_max_autoloaded_option_size', array( $this, 'filter_max_option_size' ) );
     388        $value   = file( DIR_TESTDATA . '/formatting/entities.txt' );
     389        $updated = update_option( $name, $value, $autoload );
     390        $this->assertTrue( $updated );
     391
     392        $actual = $wpdb->get_row( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s LIMIT 1", $name ) );
     393        $this->assertSame( $expected, $actual->autoload );
     394    }
     395
     396    public function data_option_autoloading_large_option() {
     397        return array(
     398            'on'    => array(
     399                'autoload' => 'on',
     400                'expected' => 'on',
     401            ),
     402            'off'   => array(
     403                'autoload' => 'off',
     404                'expected' => 'off',
     405            ),
     406            'yes'   => array(
     407                'autoload' => 'yes',
     408                'expected' => 'on',
     409            ),
     410            'true'  => array(
     411                'autoload' => true,
     412                'expected' => 'on',
     413            ),
     414            'no'    => array(
     415                'autoload' => 'no',
     416                'expected' => 'off',
     417            ),
     418            'false' => array(
     419                'autoload' => false,
     420                'expected' => 'off',
     421            ),
     422            'null'  => array(
     423                'autoload' => null,
     424                'expected' => 'auto-off',
     425            ),
     426        );
     427    }
     428
     429    public function filter_max_option_size( $current ) {
     430        return 1000;
     431    }
     432
     433    /**
     434     * @ticket 42441
     435     *
     436     * @covers ::update_option
     437     */
     438    public function test_update_option_autoloading_small_option_auto() {
     439        global $wpdb;
     440
     441        $name = 'foo';
     442        add_option( $name, 'bar' );
     443        $updated = update_option( $name, 'small_option_data' );
     444        $this->assertTrue( $updated );
     445
     446        $actual = $wpdb->get_row( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s LIMIT 1", $name ) );
     447        $this->assertSame( 'auto', $actual->autoload );
    370448    }
    371449
Note: See TracChangeset for help on using the changeset viewer.