diff --git a/wp-includes/option.php b/wp-includes/option.php index 344dfee088..83f282d5c0 100644 --- a/wp-includes/option.php +++ b/wp-includes/option.php @@ -642,51 +642,151 @@ function wp_load_alloptions( $force_cache = false ) { return apply_filters( 'alloptions', $alloptions ); } +/** + * 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() + * + * @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 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_prime_network_option_caches( $network_id, array $options ) { + global $wpdb; + + if ( wp_installing() ) { + return; + } + + 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(); + } + + $cache_keys = array(); + foreach ( $options as $option ) { + $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; + } + + $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. - * - * @global wpdb $wpdb WordPress database abstraction object. + * @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 ) { - global $wpdb; - 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' ); - if ( empty( $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' ); - - if ( wp_using_ext_object_cache() ) { - $cache_keys = array(); - foreach ( $core_options as $option ) { - $cache_keys[] = "{$network_id}:{$option}"; - } - wp_cache_get_multiple( $cache_keys, 'site-options' ); - - return; - } - - $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(); - 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; - } - wp_cache_set_multiple( $data, 'site-options' ); + wp_prime_network_option_caches( $network_id, $core_options ); } /** @@ -2415,7 +2515,9 @@ function get_site_transient( $transient ) { $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 ); @@ -2493,6 +2595,7 @@ function set_site_transient( $transient, $value, $expiration = 0 ) { } 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 ) { diff --git a/wp-includes/version.php b/wp-includes/version.php index e2c61de2aa..af7e77507c 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.6-alpha-58181'; +$wp_version = '6.6-alpha-58182'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.