Make WordPress Core


Ignore:
Timestamp:
08/24/2023 02:40:01 PM (6 months ago)
Author:
flixos90
Message:

Options, Meta APIs: Introduce prime_options() to load multiple options with a single database request.

WordPress's get_option() function generally relies on making individual database requests for each option, however with the majority of options (in most cases) being autoloaded, i.e. fetched once with a single database request and then stored in (memory) cache.

As part of a greater effort to reduce the amount of options that are unnecessarily autoloaded, this changeset introduces an alternative way to retrieve multiple options in a performant manner, with a single database request. This provides a reasonable alternative for e.g. plugins that use several options which only need to be loaded in a few specific screens.

Specifically, this changeset introduces the following functions:

  • prime_options( $options ) is the foundation to load multiple specific options with a single database request. Only options that aren't already cached (in alloptions or an individual cache) are retrieved from the database.
  • prime_options_by_group( $option_group ) is a convenience wrapper function for the above which allows to prime all options of a specific option group (as configured via register_setting()).
  • get_options( $options ) is another wrapper function which first primes the requested options and then returns them in an associative array, calling get_option() for each of them.

Props mukesh27, joemcgill, costdev, olliejones.
Fixes #58962.

File:
1 edited

Legend:

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

    r56370 r56445  
    252252     */
    253253    return apply_filters( "option_{$option}", maybe_unserialize( $value ), $option );
     254}
     255
     256/**
     257 * Primes specific options into the cache with a single database query.
     258 *
     259 * Only options that do not already exist in cache will be primed.
     260 *
     261 * @since 6.4.0
     262 *
     263 * @global wpdb $wpdb WordPress database abstraction object.
     264 *
     265 * @param array $options An array of option names to be primed.
     266 */
     267function prime_options( $options ) {
     268    $alloptions     = wp_load_alloptions();
     269    $cached_options = wp_cache_get_multiple( $options, 'options' );
     270
     271    // Filter options that are not in the cache.
     272    $options_to_prime = array();
     273    foreach ( $options as $option ) {
     274        if ( ( ! isset( $cached_options[ $option ] ) || ! $cached_options[ $option ] ) && ! isset( $alloptions[ $option ] ) ) {
     275            $options_to_prime[] = $option;
     276        }
     277    }
     278
     279    // Bail early if there are no options to be primed.
     280    if ( empty( $options_to_prime ) ) {
     281        return;
     282    }
     283
     284    global $wpdb;
     285    $results = $wpdb->get_results(
     286        $wpdb->prepare(
     287            sprintf(
     288                "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN (%s)",
     289                implode( ',', array_fill( 0, count( $options_to_prime ), '%s' ) )
     290            ),
     291            $options_to_prime
     292        )
     293    );
     294
     295    $options_found = array();
     296    foreach ( $results as $result ) {
     297        $options_found[ $result->option_name ] = maybe_unserialize( $result->option_value );
     298    }
     299    wp_cache_set_multiple( $options_found, 'options' );
     300
     301    // If all options were found, no need to update `notoptions` cache.
     302    if ( count( $options_found ) === count( $options_to_prime ) ) {
     303        return;
     304    }
     305
     306    $options_not_found = array_diff( $options_to_prime, array_keys( $options_found ) );
     307
     308    $notoptions = wp_cache_get( 'notoptions', 'options' );
     309
     310    if ( ! is_array( $notoptions ) ) {
     311        $notoptions = array();
     312    }
     313
     314    // Add the options that were not found to the cache.
     315    $update_notoptions = false;
     316    foreach ( $options_not_found as $option_name ) {
     317        if ( ! isset( $notoptions[ $option_name ] ) ) {
     318            $notoptions[ $option_name ] = true;
     319            $update_notoptions          = true;
     320        }
     321    }
     322
     323    // Only update the cache if it was modified.
     324    if ( $update_notoptions ) {
     325        wp_cache_set( 'notoptions', $notoptions, 'options' );
     326    }
     327}
     328
     329/**
     330 * Primes all options registered with a specific option group.
     331 *
     332 * @since 6.4.0
     333 *
     334 * @global array $new_allowed_options
     335 *
     336 * @param string $option_group The option group to prime options for.
     337 */
     338function prime_options_by_group( $option_group ) {
     339    global $new_allowed_options;
     340
     341    if ( isset( $new_allowed_options[ $option_group ] ) ) {
     342        prime_options( $new_allowed_options[ $option_group ] );
     343    }
     344}
     345
     346/**
     347 * Retrieves multiple options.
     348 *
     349 * Options are primed as necessary first in order to use a single database query at most.
     350 *
     351 * @since 6.4.0
     352 *
     353 * @param array $options An array of option names to retrieve.
     354 * @return array An array of key-value pairs for the requested options.
     355 */
     356function get_options( $options ) {
     357    prime_options( $options );
     358
     359    $result = array();
     360    foreach ( $options as $option ) {
     361        $result[ $option ] = get_option( $option );
     362    }
     363
     364    return $result;
    254365}
    255366
Note: See TracChangeset for help on using the changeset viewer.