diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/option.php --- a/wp/wp-includes/option.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-includes/option.php Fri Sep 05 18:40:08 2025 +0200 @@ -29,7 +29,7 @@ * * Exceptions: * - * 1. When the option has not been saved in the database, the `$default` value + * 1. When the option has not been saved in the database, the `$default_value` value * is returned if provided. If not, boolean `false` is returned. * 2. When one of the Options API filters is used: {@see 'pre_option_$option'}, * {@see 'default_option_$option'}, or {@see 'option_$option'}, the returned @@ -67,15 +67,15 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @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. + * @param string $option Name of the option to retrieve. Expected to not be SQL-escaped. + * @param mixed $default_value Optional. Default value to return if the option does not exist. * @return mixed Value of the option. A value of any type may be returned, including * scalar (string, boolean, float, integer), null, array, object. * Scalar and null values will be returned as strings as long as they originate * from a database stored option value. If there is no option in the database, * boolean `false` is returned. */ -function get_option( $option, $default = false ) { +function get_option( $option, $default_value = false ) { global $wpdb; if ( is_scalar( $option ) ) { @@ -95,7 +95,7 @@ 'comment_whitelist' => 'comment_previously_approved', ); - if ( ! wp_installing() && isset( $deprecated_keys[ $option ] ) ) { + if ( isset( $deprecated_keys[ $option ] ) && ! wp_installing() ) { _deprecated_argument( __FUNCTION__, '5.5.0', @@ -106,7 +106,7 @@ $deprecated_keys[ $option ] ) ); - return get_option( $deprecated_keys[ $option ], $default ); + return get_option( $deprecated_keys[ $option ], $default_value ); } /** @@ -114,22 +114,40 @@ * * The dynamic portion of the hook name, `$option`, refers to the option name. * - * Returning a truthy value from the filter will effectively short-circuit retrieval - * and return the passed value instead. + * Returning a value other than false from the filter will short-circuit retrieval + * and return that value instead. * * @since 1.5.0 * @since 4.4.0 The `$option` parameter was added. - * @since 4.9.0 The `$default` parameter was added. + * @since 4.9.0 The `$default_value` parameter was added. + * + * @param mixed $pre_option The value to return instead of the option value. This differs from + * `$default_value`, 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_value The fallback value to return if the option does not exist. + * Default false. + */ + $pre = apply_filters( "pre_option_{$option}", false, $option, $default_value ); + + /** + * Filters the value of all existing options before it is retrieved. * - * @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. + * Returning a truthy value from the filter will effectively short-circuit retrieval + * and return the passed value instead. + * + * @since 6.1.0 + * + * @param mixed $pre_option The value to return instead of the option value. This differs from + * `$default_value`, 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 Name of the option. + * @param mixed $default_value The fallback value to return if the option does not exist. + * Default false. */ - $pre = apply_filters( "pre_option_{$option}", false, $option, $default ); + $pre = apply_filters( 'pre_option', $pre, $option, $default_value ); if ( false !== $pre ) { return $pre; @@ -143,27 +161,6 @@ $passed_default = func_num_args() > 1; if ( ! wp_installing() ) { - // 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. - * - * The dynamic portion of the hook name, `$option`, refers to the option name. - * - * @since 3.4.0 - * @since 4.4.0 The `$option` parameter was added. - * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value. - * - * @param mixed $default The default value to return if the option does not exist - * in the database. - * @param string $option Option name. - * @param bool $passed_default Was `get_option()` passed a default value? - */ - return apply_filters( "default_option_{$option}", $default, $option, $passed_default ); - } - $alloptions = wp_load_alloptions(); if ( isset( $alloptions[ $option ] ) ) { @@ -172,6 +169,31 @@ $value = wp_cache_get( $option, 'options' ); if ( false === $value ) { + // Prevent non-existent options from triggering multiple queries. + $notoptions = wp_cache_get( 'notoptions', 'options' ); + + // Prevent non-existent `notoptions` key from triggering multiple key lookups. + if ( ! is_array( $notoptions ) ) { + $notoptions = array(); + wp_cache_set( 'notoptions', $notoptions, 'options' ); + } elseif ( isset( $notoptions[ $option ] ) ) { + /** + * Filters the default value for an option. + * + * The dynamic portion of the hook name, `$option`, refers to the option name. + * + * @since 3.4.0 + * @since 4.4.0 The `$option` parameter was added. + * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value. + * + * @param mixed $default_value The default value to return if the option does not exist + * in the database. + * @param string $option Option name. + * @param bool $passed_default Was `get_option()` passed a default value? + */ + return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default ); + } + $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. @@ -179,15 +201,11 @@ $value = $row->option_value; wp_cache_add( $option, $value, 'options' ); } 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' ); /** This filter is documented in wp-includes/option.php */ - return apply_filters( "default_option_{$option}", $default, $option, $passed_default ); + return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default ); } } } @@ -200,7 +218,7 @@ $value = $row->option_value; } else { /** This filter is documented in wp-includes/option.php */ - return apply_filters( "default_option_{$option}", $default, $option, $passed_default ); + return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default ); } } @@ -230,6 +248,293 @@ } /** + * Primes specific options into the cache with a single database query. + * + * Only options that do not already exist in cache will be loaded. + * + * @since 6.4.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string[] $options An array of option names to be loaded. + */ +function wp_prime_option_caches( $options ) { + global $wpdb; + + $alloptions = wp_load_alloptions(); + $cached_options = wp_cache_get_multiple( $options, 'options' ); + $notoptions = wp_cache_get( 'notoptions', 'options' ); + if ( ! is_array( $notoptions ) ) { + $notoptions = array(); + } + + // Filter options that are not in the cache. + $options_to_prime = array(); + foreach ( $options as $option ) { + if ( + ( ! isset( $cached_options[ $option ] ) || false === $cached_options[ $option ] ) + && ! isset( $alloptions[ $option ] ) + && ! isset( $notoptions[ $option ] ) + ) { + $options_to_prime[] = $option; + } + } + + // Bail early if there are no options to be loaded. + if ( empty( $options_to_prime ) ) { + return; + } + + $results = $wpdb->get_results( + $wpdb->prepare( + sprintf( + "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN (%s)", + implode( ',', array_fill( 0, count( $options_to_prime ), '%s' ) ) + ), + $options_to_prime + ) + ); + + $options_found = array(); + foreach ( $results as $result ) { + /* + * The cache is primed with the raw value (i.e. not maybe_unserialized). + * + * `get_option()` will handle unserializing the value as needed. + */ + $options_found[ $result->option_name ] = $result->option_value; + } + wp_cache_set_multiple( $options_found, 'options' ); + + // If all options were found, no need to update `notoptions` cache. + if ( count( $options_found ) === count( $options_to_prime ) ) { + return; + } + + $options_not_found = array_diff( $options_to_prime, array_keys( $options_found ) ); + + // Add the options that were not found to the cache. + $update_notoptions = false; + foreach ( $options_not_found as $option_name ) { + if ( ! isset( $notoptions[ $option_name ] ) ) { + $notoptions[ $option_name ] = true; + $update_notoptions = true; + } + } + + // Only update the cache if it was modified. + if ( $update_notoptions ) { + wp_cache_set( 'notoptions', $notoptions, 'options' ); + } +} + +/** + * Primes the cache of all options registered with a specific option group. + * + * @since 6.4.0 + * + * @global array $new_allowed_options + * + * @param string $option_group The option group to load options for. + */ +function wp_prime_option_caches_by_group( $option_group ) { + global $new_allowed_options; + + if ( isset( $new_allowed_options[ $option_group ] ) ) { + wp_prime_option_caches( $new_allowed_options[ $option_group ] ); + } +} + +/** + * Retrieves multiple options. + * + * Options are loaded as necessary first in order to use a single database query at most. + * + * @since 6.4.0 + * + * @param string[] $options An array of option names to retrieve. + * @return array An array of key-value pairs for the requested options. + */ +function get_options( $options ) { + wp_prime_option_caches( $options ); + + $result = array(); + foreach ( $options as $option ) { + $result[ $option ] = get_option( $option ); + } + + return $result; +} + +/** + * Sets the autoload values for multiple options in the database. + * + * Autoloading too many options can lead to performance problems, especially if the options are not frequently used. + * This function allows modifying the autoload value for multiple options without changing the actual option value. + * This is for example recommended for plugin activation and deactivation hooks, to ensure any options exclusively used + * by the plugin which are generally autoloaded can be set to not autoload when the plugin is inactive. + * + * @since 6.4.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param array $options Associative array of option names and their autoload values to set. The option names are + * expected to not be SQL-escaped. The autoload values accept 'yes'|true to enable or 'no'|false + * to disable. + * @return array Associative array of all provided $options as keys and boolean values for whether their autoload value + * was updated. + */ +function wp_set_option_autoload_values( array $options ) { + global $wpdb; + + if ( ! $options ) { + return array(); + } + + $grouped_options = array( + 'on' => array(), + 'off' => array(), + ); + $results = array(); + foreach ( $options as $option => $autoload ) { + wp_protect_special_option( $option ); // Ensure only valid options can be passed. + if ( 'off' === $autoload || 'no' === $autoload || false === $autoload ) { // Sanitize autoload value and categorize accordingly. + $grouped_options['off'][] = $option; + } else { + $grouped_options['on'][] = $option; + } + $results[ $option ] = false; // Initialize result value. + } + + $where = array(); + $where_args = array(); + foreach ( $grouped_options as $autoload => $options ) { + if ( ! $options ) { + continue; + } + $placeholders = implode( ',', array_fill( 0, count( $options ), '%s' ) ); + $where[] = "autoload != '%s' AND option_name IN ($placeholders)"; + $where_args[] = $autoload; + foreach ( $options as $option ) { + $where_args[] = $option; + } + } + $where = 'WHERE ' . implode( ' OR ', $where ); + + /* + * Determine the relevant options that do not already use the given autoload value. + * If no options are returned, no need to update. + */ + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare + $options_to_update = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options $where", $where_args ) ); + if ( ! $options_to_update ) { + return $results; + } + + // Run UPDATE queries as needed (maximum 2) to update the relevant options' autoload values to 'yes' or 'no'. + foreach ( $grouped_options as $autoload => $options ) { + if ( ! $options ) { + continue; + } + $options = array_intersect( $options, $options_to_update ); + $grouped_options[ $autoload ] = $options; + if ( ! $grouped_options[ $autoload ] ) { + continue; + } + + // Run query to update autoload value for all the options where it is needed. + $success = $wpdb->query( + $wpdb->prepare( + "UPDATE $wpdb->options SET autoload = %s WHERE option_name IN (" . implode( ',', array_fill( 0, count( $grouped_options[ $autoload ] ), '%s' ) ) . ')', + array_merge( + array( $autoload ), + $grouped_options[ $autoload ] + ) + ) + ); + if ( ! $success ) { + // Set option list to an empty array to indicate no options were updated. + $grouped_options[ $autoload ] = array(); + continue; + } + + // Assume that on success all options were updated, which should be the case given only new values are sent. + foreach ( $grouped_options[ $autoload ] as $option ) { + $results[ $option ] = true; + } + } + + /* + * If any options were changed to 'on', delete their individual caches, and delete 'alloptions' cache so that it + * is refreshed as needed. + * If no options were changed to 'on' but any options were changed to 'no', delete them from the 'alloptions' + * cache. This is not necessary when options were changed to 'on', since in that situation the entire cache is + * deleted anyway. + */ + if ( $grouped_options['on'] ) { + wp_cache_delete_multiple( $grouped_options['on'], 'options' ); + wp_cache_delete( 'alloptions', 'options' ); + } elseif ( $grouped_options['off'] ) { + $alloptions = wp_load_alloptions( true ); + + foreach ( $grouped_options['off'] as $option ) { + if ( isset( $alloptions[ $option ] ) ) { + unset( $alloptions[ $option ] ); + } + } + + wp_cache_set( 'alloptions', $alloptions, 'options' ); + } + + return $results; +} + +/** + * Sets the autoload value for multiple options in the database. + * + * This is a wrapper for {@see wp_set_option_autoload_values()}, which can be used to set different autoload values for + * each option at once. + * + * @since 6.4.0 + * + * @see wp_set_option_autoload_values() + * + * @param string[] $options List of option names. Expected to not be SQL-escaped. + * @param string|bool $autoload Autoload value to control whether to load the options when WordPress starts up. + * Accepts 'yes'|true to enable or 'no'|false to disable. + * @return array Associative array of all provided $options as keys and boolean values for whether their autoload value + * was updated. + */ +function wp_set_options_autoload( array $options, $autoload ) { + return wp_set_option_autoload_values( + array_fill_keys( $options, $autoload ) + ); +} + +/** + * Sets the autoload value for an option in the database. + * + * This is a wrapper for {@see wp_set_option_autoload_values()}, which can be used to set the autoload value for + * multiple options at once. + * + * @since 6.4.0 + * + * @see wp_set_option_autoload_values() + * + * @param string $option Name of the option. Expected to not be SQL-escaped. + * @param string|bool $autoload Autoload value to control whether to load the option when WordPress starts up. + * Accepts 'yes'|true to enable or 'no'|false to disable. + * @return bool True if the autoload value was modified, false otherwise. + */ +function wp_set_option_autoload( $option, $autoload ) { + $result = wp_set_option_autoload_values( array( $option => $autoload ) ); + if ( isset( $result[ $option ] ) ) { + return $result[ $option ]; + } + return false; +} + +/** * Protects WordPress special option from being modified. * * Will die if $option is in protected list. Protected options are 'alloptions' @@ -277,6 +582,22 @@ function wp_load_alloptions( $force_cache = false ) { global $wpdb; + /** + * Filters the array of alloptions before it is populated. + * + * Returning an array from the filter will effectively short circuit + * wp_load_alloptions(), returning that value instead. + * + * @since 6.2.0 + * + * @param array|null $alloptions An array of alloptions. Default null. + * @param bool $force_cache Whether to force an update of the local cache from the persistent cache. Default false. + */ + $alloptions = apply_filters( 'pre_wp_load_alloptions', null, $force_cache ); + if ( is_array( $alloptions ) ) { + return $alloptions; + } + if ( ! wp_installing() || ! is_multisite() ) { $alloptions = wp_cache_get( 'alloptions', 'options', $force_cache ); } else { @@ -285,7 +606,8 @@ if ( ! $alloptions ) { $suppress = $wpdb->suppress_errors(); - $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" ); + $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload IN ( '" . implode( "', '", esc_sql( wp_autoload_values_to_autoload() ) ) . "' )" ); + if ( ! $alloptions_db ) { $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options" ); } @@ -321,39 +643,150 @@ } /** - * Loads and caches certain often requested site options if is_multisite() and a persistent cache is not being used. + * Primes specific network options for the current network into the cache with a single database query. + * + * Only network options that do not already exist in cache will be loaded. + * + * If site is not multisite, then call wp_prime_option_caches(). + * + * @since 6.6.0 + * + * @see wp_prime_network_option_caches() * - * @since 3.0.0 + * @param string[] $options An array of option names to be loaded. + */ +function wp_prime_site_option_caches( array $options ) { + wp_prime_network_option_caches( null, $options ); +} + +/** + * Primes specific network options into the cache with a single database query. + * + * Only network options that do not already exist in cache will be loaded. + * + * If site is not multisite, then call wp_prime_option_caches(). + * + * @since 6.6.0 * * @global wpdb $wpdb WordPress database abstraction object. * - * @param int $network_id Optional site ID for which to query the options. Defaults to the current site. + * @param int $network_id ID of the network. Can be null to default to the current network ID. + * @param string[] $options An array of option names to be loaded. */ -function wp_load_core_site_options( $network_id = null ) { +function wp_prime_network_option_caches( $network_id, array $options ) { global $wpdb; - if ( ! is_multisite() || wp_using_ext_object_cache() || wp_installing() ) { + if ( wp_installing() ) { return; } - if ( empty( $network_id ) ) { + if ( ! is_multisite() ) { + wp_prime_option_caches( $options ); + return; + } + + if ( $network_id && ! is_numeric( $network_id ) ) { + return; + } + + $network_id = (int) $network_id; + + // Fallback to the current network if a network ID is not specified. + if ( ! $network_id ) { $network_id = get_current_network_id(); } - $core_options = array( 'site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting' ); - - $core_options_in = "'" . implode( "', '", $core_options ) . "'"; - $options = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN ($core_options_in) AND site_id = %d", $network_id ) ); - - $data = array(); + $cache_keys = array(); foreach ( $options as $option ) { - $key = $option->meta_key; - $cache_key = "{$network_id}:$key"; - $option->meta_value = maybe_unserialize( $option->meta_value ); - - $data[ $cache_key ] = $option->meta_value; + $cache_keys[ $option ] = "{$network_id}:{$option}"; + } + + $cache_group = 'site-options'; + $cached_options = wp_cache_get_multiple( array_values( $cache_keys ), $cache_group ); + + $notoptions_key = "$network_id:notoptions"; + $notoptions = wp_cache_get( $notoptions_key, $cache_group ); + + if ( ! is_array( $notoptions ) ) { + $notoptions = array(); + } + + // Filter options that are not in the cache. + $options_to_prime = array(); + foreach ( $cache_keys as $option => $cache_key ) { + if ( + ( ! isset( $cached_options[ $cache_key ] ) || false === $cached_options[ $cache_key ] ) + && ! isset( $notoptions[ $option ] ) + ) { + $options_to_prime[] = $option; + } + } + + // Bail early if there are no options to be loaded. + if ( empty( $options_to_prime ) ) { + return; } - wp_cache_set_multiple( $data, 'site-options' ); + + $query_args = $options_to_prime; + $query_args[] = $network_id; + $results = $wpdb->get_results( + $wpdb->prepare( + sprintf( + "SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN (%s) AND site_id = %s", + implode( ',', array_fill( 0, count( $options_to_prime ), '%s' ) ), + '%d' + ), + $query_args + ) + ); + + $data = array(); + $options_found = array(); + foreach ( $results as $result ) { + $key = $result->meta_key; + $cache_key = $cache_keys[ $key ]; + $data[ $cache_key ] = maybe_unserialize( $result->meta_value ); + $options_found[] = $key; + } + wp_cache_set_multiple( $data, $cache_group ); + // If all options were found, no need to update `notoptions` cache. + if ( count( $options_found ) === count( $options_to_prime ) ) { + return; + } + + $options_not_found = array_diff( $options_to_prime, $options_found ); + + // Add the options that were not found to the cache. + $update_notoptions = false; + foreach ( $options_not_found as $option_name ) { + if ( ! isset( $notoptions[ $option_name ] ) ) { + $notoptions[ $option_name ] = true; + $update_notoptions = true; + } + } + + // Only update the cache if it was modified. + if ( $update_notoptions ) { + wp_cache_set( $notoptions_key, $notoptions, $cache_group ); + } +} + +/** + * Loads and primes caches of certain often requested network options if is_multisite(). + * + * @since 3.0.0 + * @since 6.3.0 Also prime caches for network options when persistent object cache is enabled. + * @since 6.6.0 Uses wp_prime_network_option_caches(). + * + * @param int $network_id Optional. Network ID of network for which to prime network options cache. Defaults to current network. + */ +function wp_load_core_site_options( $network_id = null ) { + if ( ! is_multisite() || wp_installing() ) { + return; + } + $core_options = array( 'site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting', 'WPLANG' ); + + wp_prime_network_option_caches( $network_id, $core_options ); } /** @@ -373,12 +806,21 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @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. + * @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 bool|null $autoload Optional. Whether to load the option when WordPress starts up. + * Accepts a boolean, or `null` to stick with the initial value or, if no initial value is set, + * to leave the decision up to default heuristics in WordPress. + * For existing options, + * `$autoload` can only be updated using `update_option()` if `$value` is also changed. + * For backward compatibility 'yes' and 'no' are also accepted. + * Autoloading too many options can lead to performance problems, especially if the + * options are not frequently used. For options which are accessed across several places + * in the frontend, it is recommended to autoload them, by using true. + * For options which are accessed only on few specific URLs, it is recommended + * to not autoload them, by using false. + * For non-existent options, the default is null, which means WordPress will determine + * the autoload value. * @return bool True if the value was updated, false otherwise. */ function update_option( $option, $value, $autoload = null ) { @@ -401,7 +843,7 @@ 'comment_whitelist' => 'comment_previously_approved', ); - if ( ! wp_installing() && isset( $deprecated_keys[ $option ] ) ) { + if ( isset( $deprecated_keys[ $option ] ) && ! wp_installing() ) { _deprecated_argument( __FUNCTION__, '5.5.0', @@ -464,11 +906,6 @@ /** This filter is documented in wp-includes/option.php */ if ( apply_filters( "default_option_{$option}", false, $option, false ) === $old_value ) { - // Default setting for new options is 'yes'. - if ( null === $autoload ) { - $autoload = 'yes'; - } - return add_option( $option, $value, '', $autoload ); } @@ -490,7 +927,17 @@ ); if ( null !== $autoload ) { - $update_args['autoload'] = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes'; + $update_args['autoload'] = wp_determine_option_autoload_value( $option, $value, $serialized_value, $autoload ); + } else { + // Retrieve the current autoload value to reevaluate it in case it was set automatically. + $raw_autoload = $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) ); + $allow_values = array( 'auto-on', 'auto-off', 'auto' ); + if ( in_array( $raw_autoload, $allow_values, true ) ) { + $autoload = wp_determine_option_autoload_value( $option, $value, $serialized_value, $autoload ); + if ( $autoload !== $raw_autoload ) { + $update_args['autoload'] = $autoload; + } + } } $result = $wpdb->update( $wpdb->options, $update_args, array( 'option_name' => $option ) ); @@ -506,11 +953,33 @@ } if ( ! wp_installing() ) { - $alloptions = wp_load_alloptions( true ); - if ( isset( $alloptions[ $option ] ) ) { + if ( ! isset( $update_args['autoload'] ) ) { + // Update the cached value based on where it is currently cached. + $alloptions = wp_load_alloptions( true ); + + if ( isset( $alloptions[ $option ] ) ) { + $alloptions[ $option ] = $serialized_value; + wp_cache_set( 'alloptions', $alloptions, 'options' ); + } else { + wp_cache_set( $option, $serialized_value, 'options' ); + } + } elseif ( in_array( $update_args['autoload'], wp_autoload_values_to_autoload(), true ) ) { + // Delete the individual cache, then set in alloptions cache. + wp_cache_delete( $option, 'options' ); + + $alloptions = wp_load_alloptions( true ); + $alloptions[ $option ] = $serialized_value; wp_cache_set( 'alloptions', $alloptions, 'options' ); } else { + // Delete the alloptions cache, then set the individual cache. + $alloptions = wp_load_alloptions( true ); + + if ( isset( $alloptions[ $option ] ) ) { + unset( $alloptions[ $option ] ); + wp_cache_set( 'alloptions', $alloptions, 'options' ); + } + wp_cache_set( $option, $serialized_value, 'options' ); } } @@ -556,18 +1025,26 @@ * options the same as the ones which are protected. * * @since 1.0.0 + * @since 6.6.0 The $autoload parameter's default value was changed to null. * * @global wpdb $wpdb WordPress database abstraction object. * - * @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. + * @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 bool|null $autoload Optional. Whether to load the option when WordPress starts up. + * Accepts a boolean, or `null` to leave the decision up to default heuristics in WordPress. + * For backward compatibility 'yes' and 'no' are also accepted. + * Autoloading too many options can lead to performance problems, especially if the + * options are not frequently used. For options which are accessed across several places + * in the frontend, it is recommended to autoload them, by using 'yes'|true. + * For options which are accessed only on few specific URLs, it is recommended + * to not autoload them, by using false. + * Default is null, which means WordPress will determine the autoload value. * @return bool True if the option was added, false otherwise. */ -function add_option( $option, $value = '', $deprecated = '', $autoload = 'yes' ) { +function add_option( $option, $value = '', $deprecated = '', $autoload = null ) { global $wpdb; if ( ! empty( $deprecated ) ) { @@ -591,7 +1068,7 @@ 'comment_whitelist' => 'comment_previously_approved', ); - if ( ! wp_installing() && isset( $deprecated_keys[ $option ] ) ) { + if ( isset( $deprecated_keys[ $option ] ) && ! wp_installing() ) { _deprecated_argument( __FUNCTION__, '5.5.0', @@ -613,8 +1090,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 ] ) ) { @@ -625,7 +1104,8 @@ } $serialized_value = maybe_serialize( $value ); - $autoload = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes'; + + $autoload = wp_determine_option_autoload_value( $option, $value, $serialized_value, $autoload ); /** * Fires before an option is added. @@ -643,7 +1123,7 @@ } if ( ! wp_installing() ) { - if ( 'yes' === $autoload ) { + if ( in_array( $autoload, wp_autoload_values_to_autoload(), true ) ) { $alloptions = wp_load_alloptions( true ); $alloptions[ $option ] = $serialized_value; wp_cache_set( 'alloptions', $alloptions, 'options' ); @@ -687,7 +1167,7 @@ } /** - * Removes option by name. Prevents removal of protected WordPress options. + * Removes an option by name. Prevents removal of protected WordPress options. * * @since 1.2.0 * @@ -727,8 +1207,9 @@ $result = $wpdb->delete( $wpdb->options, array( 'option_name' => $option ) ); if ( ! wp_installing() ) { - if ( 'yes' === $row->autoload ) { + if ( in_array( $row->autoload, wp_autoload_values_to_autoload(), true ) ) { $alloptions = wp_load_alloptions( true ); + if ( is_array( $alloptions ) && isset( $alloptions[ $option ] ) ) { unset( $alloptions[ $option ] ); wp_cache_set( 'alloptions', $alloptions, 'options' ); @@ -767,6 +1248,96 @@ } /** + * Determines the appropriate autoload value for an option based on input. + * + * This function checks the provided autoload value and returns a standardized value + * ('on', 'off', 'auto-on', 'auto-off', or 'auto') based on specific conditions. + * + * If no explicit autoload value is provided, the function will check for certain heuristics around the given option. + * It will return `auto-on` to indicate autoloading, `auto-off` to indicate not autoloading, or `auto` if no clear + * decision could be made. + * + * @since 6.6.0 + * @access private + * + * @param string $option The name of the option. + * @param mixed $value The value of the option to check its autoload value. + * @param mixed $serialized_value The serialized value of the option to check its autoload value. + * @param bool|null $autoload The autoload value to check. + * Accepts 'on'|true to enable or 'off'|false to disable, or + * 'auto-on', 'auto-off', or 'auto' for internal purposes. + * Any other autoload value will be forced to either 'auto-on', + * 'auto-off', or 'auto'. + * 'yes' and 'no' are supported for backward compatibility. + * @return string Returns the original $autoload value if explicit, or 'auto-on', 'auto-off', + * or 'auto' depending on default heuristics. + */ +function wp_determine_option_autoload_value( $option, $value, $serialized_value, $autoload ) { + + // Check if autoload is a boolean. + if ( is_bool( $autoload ) ) { + return $autoload ? 'on' : 'off'; + } + + switch ( $autoload ) { + case 'on': + case 'yes': + return 'on'; + case 'off': + case 'no': + return 'off'; + } + + /** + * Allows to determine the default autoload value for an option where no explicit value is passed. + * + * @since 6.6.0 + * + * @param bool|null $autoload The default autoload value to set. Returning true will be set as 'auto-on' in the + * database, false will be set as 'auto-off', and null will be set as 'auto'. + * @param string $option The passed option name. + * @param mixed $value The passed option value to be saved. + */ + $autoload = apply_filters( 'wp_default_autoload_value', null, $option, $value, $serialized_value ); + if ( is_bool( $autoload ) ) { + return $autoload ? 'auto-on' : 'auto-off'; + } + + return 'auto'; +} + +/** + * Filters the default autoload value to disable autoloading if the option value is too large. + * + * @since 6.6.0 + * @access private + * + * @param bool|null $autoload The default autoload value to set. + * @param string $option The passed option name. + * @param mixed $value The passed option value to be saved. + * @param mixed $serialized_value The passed option value to be saved, in serialized form. + * @return bool|null Potentially modified $default. + */ +function wp_filter_default_autoload_value_via_option_size( $autoload, $option, $value, $serialized_value ) { + /** + * Filters the maximum size of option value in bytes. + * + * @since 6.6.0 + * + * @param int $max_option_size The option-size threshold, in bytes. Default 150000. + * @param string $option The name of the option. + */ + $max_option_size = (int) apply_filters( 'wp_max_autoloaded_option_size', 150000, $option ); + $size = ! empty( $serialized_value ) ? strlen( $serialized_value ) : 0; + + if ( $size > $max_option_size ) { + return false; + } + + return $autoload; +} + +/** * Deletes a transient. * * @since 2.8.0 @@ -832,8 +1403,8 @@ * * The dynamic portion of the hook name, `$transient`, refers to the transient name. * - * Returning a truthy value from the filter will effectively short-circuit retrieval - * and return the passed value instead. + * Returning a value other than false from the filter will short-circuit retrieval + * and return that value instead. * * @since 2.8.0 * @since 4.4.0 The `$transient` parameter was added @@ -856,9 +1427,11 @@ if ( ! wp_installing() ) { // 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; - $timeout = get_option( $transient_timeout ); + wp_prime_option_caches( array( $transient_option, $transient_timeout ) ); + $timeout = get_option( $transient_timeout ); if ( false !== $timeout && $timeout < time() ) { delete_option( $transient_option ); delete_option( $transient_timeout ); @@ -938,24 +1511,27 @@ } else { $transient_timeout = '_transient_timeout_' . $transient; $transient_option = '_transient_' . $transient; + wp_prime_option_caches( array( $transient_option, $transient_timeout ) ); if ( false === get_option( $transient_option ) ) { - $autoload = 'yes'; + $autoload = true; if ( $expiration ) { - $autoload = 'no'; - add_option( $transient_timeout, time() + $expiration, '', 'no' ); + $autoload = false; + add_option( $transient_timeout, time() + $expiration, '', false ); } $result = add_option( $transient_option, $value, '', $autoload ); } else { - // If expiration is requested, but the transient has no timeout option, - // delete, then re-create transient rather than update. + /* + * 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 ); - add_option( $transient_timeout, time() + $expiration, '', 'no' ); - $result = add_option( $transient_option, $value, '', 'no' ); + add_option( $transient_timeout, time() + $expiration, '', false ); + $result = add_option( $transient_option, $value, '', false ); $update = false; } else { update_option( $transient_timeout, time() + $expiration ); @@ -1004,9 +1580,13 @@ /** * Deletes all expired transients. * + * Note that this function won't do anything if an external object cache is in use. + * * The multi-table delete syntax is used to delete the transient record * from table a, and the corresponding transient_timeout record from table b. * + * @global wpdb $wpdb WordPress database abstraction object. + * * @since 4.9.0 * * @param bool $force_db Optional. Force cleanup to run against the database even when an external object cache is used. @@ -1092,7 +1672,7 @@ $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE[ 'wp-settings-' . $user_id ] ); // No change or both empty. - if ( $cookie == $settings ) { + if ( $cookie === $settings ) { return; } @@ -1109,8 +1689,8 @@ // The cookie is not set in the current browser or the saved value is newer. $secure = ( 'https' === parse_url( admin_url(), PHP_URL_SCHEME ) ); - setcookie( 'wp-settings-' . $user_id, $settings, time() + YEAR_IN_SECONDS, SITECOOKIEPATH, null, $secure ); - setcookie( 'wp-settings-time-' . $user_id, time(), time() + YEAR_IN_SECONDS, SITECOOKIEPATH, null, $secure ); + setcookie( 'wp-settings-' . $user_id, $settings, time() + YEAR_IN_SECONDS, SITECOOKIEPATH, '', $secure ); + setcookie( 'wp-settings-time-' . $user_id, time(), time() + YEAR_IN_SECONDS, SITECOOKIEPATH, '', $secure ); $_COOKIE[ 'wp-settings-' . $user_id ] = $settings; } @@ -1119,22 +1699,22 @@ * * @since 2.7.0 * - * @param string $name The name of the setting. - * @param string|false $default Optional. Default value to return when $name is not set. Default false. + * @param string $name The name of the setting. + * @param string|false $default_value Optional. Default value to return when $name is not set. Default false. * @return mixed The last saved user setting or the default value/false if it doesn't exist. */ -function get_user_setting( $name, $default = false ) { +function get_user_setting( $name, $default_value = false ) { $all_user_settings = get_all_user_settings(); - return isset( $all_user_settings[ $name ] ) ? $all_user_settings[ $name ] : $default; + return isset( $all_user_settings[ $name ] ) ? $all_user_settings[ $name ] : $default_value; } /** * Adds or updates user interface setting. * - * Both $name and $value can contain only ASCII letters, numbers, hyphens, 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(). + * This function has to be used before any output has started as it calls `setcookie()`. * * @since 2.8.0 * @@ -1159,7 +1739,7 @@ * * Deleting settings would reset them to the defaults. * - * This function has to be used before any output has started as it calls setcookie(). + * This function has to be used before any output has started as it calls `setcookie()`. * * @since 2.7.0 * @@ -1298,13 +1878,13 @@ * * @see get_network_option() * - * @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. + * @param string $option Name of the option to retrieve. Expected to not be SQL-escaped. + * @param mixed $default_value 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. */ -function get_site_option( $option, $default = false, $deprecated = true ) { - return get_network_option( null, $option, $default ); +function get_site_option( $option, $default_value = false, $deprecated = true ) { + return get_network_option( null, $option, $default_value ); } /** @@ -1326,7 +1906,7 @@ } /** - * Removes a option by name for the current network. + * Removes an option by name for the current network. * * @since 2.8.0 * @since 4.4.0 Modified into wrapper for delete_network_option() @@ -1365,12 +1945,12 @@ * * @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 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 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_value 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 ) { +function get_network_option( $network_id, $option, $default_value = false ) { global $wpdb; if ( $network_id && ! is_numeric( $network_id ) ) { @@ -1389,25 +1969,25 @@ * * The dynamic portion of the hook name, `$option`, refers to the option name. * - * Returning a truthy value from the filter will effectively short-circuit retrieval - * and return the passed value instead. + * Returning a value other than false from the filter will short-circuit retrieval + * and return that value instead. * * @since 2.9.0 As 'pre_site_option_' . $key * @since 3.0.0 * @since 4.4.0 The `$option` parameter was added. * @since 4.7.0 The `$network_id` parameter was added. - * @since 4.9.0 The `$default` parameter was added. + * @since 4.9.0 The `$default_value` 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 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 false. + * @param mixed $pre_option The value to return instead of the option value. This differs from + * `$default_value`, 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_value The fallback value to return if the option does not exist. + * Default false. */ - $pre = apply_filters( "pre_site_option_{$option}", false, $option, $network_id, $default ); + $pre = apply_filters( "pre_site_option_{$option}", false, $option, $network_id, $default_value ); if ( false !== $pre ) { return $pre; @@ -1420,7 +2000,7 @@ if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) { /** - * Filters a specific default network option. + * Filters the value of a specific default network option. * * The dynamic portion of the hook name, `$option`, refers to the option name. * @@ -1428,18 +2008,18 @@ * @since 4.4.0 The `$option` parameter was added. * @since 4.7.0 The `$network_id` parameter was added. * - * @param mixed $default The value to return if the site option does not exist - * in the database. - * @param string $option Option name. - * @param int $network_id ID of the network. + * @param mixed $default_value The value to return if the site option does not exist + * in the database. + * @param string $option Option name. + * @param int $network_id ID of the network. */ - return apply_filters( "default_site_option_{$option}", $default, $option, $network_id ); + return apply_filters( "default_site_option_{$option}", $default_value, $option, $network_id ); } if ( ! is_multisite() ) { /** This filter is documented in wp-includes/option.php */ - $default = apply_filters( 'default_site_option_' . $option, $default, $option, $network_id ); - $value = get_option( $option, $default ); + $default_value = apply_filters( 'default_site_option_' . $option, $default_value, $option, $network_id ); + $value = get_option( $option, $default_value ); } else { $cache_key = "$network_id:$option"; $value = wp_cache_get( $cache_key, 'site-options' ); @@ -1461,7 +2041,7 @@ wp_cache_set( $notoptions_key, $notoptions, 'site-options' ); /** This filter is documented in wp-includes/option.php */ - $value = apply_filters( 'default_site_option_' . $option, $default, $option, $network_id ); + $value = apply_filters( 'default_site_option_' . $option, $default_value, $option, $network_id ); } } } @@ -1539,12 +2119,14 @@ $notoptions_key = "$network_id:notoptions"; if ( ! is_multisite() ) { - $result = add_option( $option, $value, '', 'no' ); + $result = add_option( $option, $value, '', false ); } 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 ] ) ) { @@ -1738,7 +2320,7 @@ wp_protect_special_option( $option ); - $old_value = get_network_option( $network_id, $option, false ); + $old_value = get_network_option( $network_id, $option ); /** * Filters a specific network option before its value is updated. @@ -1783,7 +2365,7 @@ } if ( ! is_multisite() ) { - $result = update_option( $option, $value, 'no' ); + $result = update_option( $option, $value, false ); } else { $value = sanitize_option( $option, $value ); @@ -1908,8 +2490,8 @@ * * The dynamic portion of the hook name, `$transient`, refers to the transient name. * - * Returning a truthy value from the filter will effectively short-circuit retrieval - * and return the passed value instead. + * Returning a value other than boolean false will short-circuit retrieval and + * return that value instead. * * @since 2.9.0 * @since 4.4.0 The `$transient` parameter was added. @@ -1933,7 +2515,9 @@ $transient_option = '_site_transient_' . $transient; if ( ! in_array( $transient, $no_timeout, true ) ) { $transient_timeout = '_site_transient_timeout_' . $transient; - $timeout = get_site_option( $transient_timeout ); + wp_prime_site_option_caches( array( $transient_option, $transient_timeout ) ); + + $timeout = get_site_option( $transient_timeout ); if ( false !== $timeout && $timeout < time() ) { delete_site_option( $transient_option ); delete_site_option( $transient_timeout ); @@ -2011,6 +2595,7 @@ } else { $transient_timeout = '_site_transient_timeout_' . $transient; $option = '_site_transient_' . $transient; + wp_prime_site_option_caches( array( $option, $transient_timeout ) ); if ( false === get_site_option( $option ) ) { if ( $expiration ) { @@ -2074,6 +2659,7 @@ 'name' => 'title', ), 'type' => 'string', + 'label' => __( 'Title' ), 'description' => __( 'Site title.' ), ) ); @@ -2086,6 +2672,7 @@ 'name' => 'description', ), 'type' => 'string', + 'label' => __( 'Tagline' ), 'description' => __( 'Site tagline.' ), ) ); @@ -2216,6 +2803,7 @@ array( 'show_in_rest' => true, 'type' => 'integer', + 'label' => __( 'Maximum posts per page' ), 'description' => __( 'Blog pages show at most.' ), 'default' => 10, ) @@ -2227,6 +2815,7 @@ array( 'show_in_rest' => true, 'type' => 'string', + 'label' => __( 'Show on front' ), 'description' => __( 'What to show on the front page' ), ) ); @@ -2237,6 +2826,7 @@ array( 'show_in_rest' => true, 'type' => 'integer', + 'label' => __( 'Page on front' ), 'description' => __( 'The ID of the page that should be displayed on the front page' ), ) ); @@ -2275,6 +2865,7 @@ ), ), 'type' => 'string', + 'label' => __( 'Allow comments on new posts' ), 'description' => __( 'Allow people to submit comments on new posts.' ), ) ); @@ -2289,6 +2880,7 @@ * @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. + * @since 6.6.0 Added the `label` argument. * * @global array $new_allowed_options * @global array $wp_registered_settings @@ -2302,6 +2894,7 @@ * * @type string $type The type of data associated with this setting. * Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'. + * @type string $label A label of the data attached to this setting. * @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. @@ -2322,6 +2915,7 @@ $defaults = array( 'type' => 'string', 'group' => $option_group, + 'label' => '', 'description' => '', 'sanitize_callback' => null, 'show_in_rest' => false, @@ -2456,7 +3050,10 @@ $option_group = 'reading'; } - $pos = array_search( $option_name, (array) $new_allowed_options[ $option_group ], true ); + $pos = false; + if ( isset( $new_allowed_options[ $option_group ] ) ) { + $pos = array_search( $option_name, (array) $new_allowed_options[ $option_group ], true ); + } if ( false !== $pos ) { unset( $new_allowed_options[ $option_group ][ $pos ] ); @@ -2528,20 +3125,45 @@ * * @since 4.7.0 * - * @param mixed $default Existing default value to return. + * @param mixed $default_value 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 ) { +function filter_default_option( $default_value, $option, $passed_default ) { if ( $passed_default ) { - return $default; + return $default_value; } $registered = get_registered_settings(); if ( empty( $registered[ $option ] ) ) { - return $default; + return $default_value; } return $registered[ $option ]['default']; } + +/** + * Returns the values that trigger autoloading from the options table. + * + * @since 6.6.0 + * + * @return string[] The values that trigger autoloading. + */ +function wp_autoload_values_to_autoload() { + $autoload_values = array( 'yes', 'on', 'auto-on', 'auto' ); + + /** + * Filters the autoload values that should be considered for autoloading from the options table. + * + * The filter can only be used to remove autoload values from the default list. + * + * @since 6.6.0 + * + * @param string[] $autoload_values Autoload values used to autoload option. + * Default list contains 'yes', 'on', 'auto-on', and 'auto'. + */ + $filtered_values = apply_filters( 'wp_autoload_values_to_autoload', $autoload_values ); + + return array_intersect( $filtered_values, $autoload_values ); +}