Make WordPress Core

Ticket #31245: 31245.2.diff

File 31245.2.diff, 42.9 KB (added by johnjamesjacoby, 7 years ago)

WP_Option, WP_Option_Query, and more

  • new file src/wp-includes/class-wp-option-query.php

    diff --git src/wp-includes/class-wp-option-query.php src/wp-includes/class-wp-option-query.php
    new file mode 100644
    index 0000000..9f7f5f5
    - +  
     1<?php
     2/**
     3 * WP_Option_Query class
     4 *
     5 * @package Plugins/Queries/Options
     6 * @since 4.8.0
     7 */
     8
     9/**
     10 * Core class used for querying options tables.
     11 *
     12 * @since 4.8.0
     13 *
     14 * @see WP_Option_Query::__construct() for accepted arguments.
     15 */
     16class WP_Option_Query {
     17
     18        /**
     19         * SQL for database query.
     20         *
     21         * @since 4.8.0
     22         * @access public
     23         * @var string
     24         */
     25        public $request;
     26
     27        /**
     28         * SQL query clauses.
     29         *
     30         * @since 4.8.0
     31         * @access protected
     32         * @var array
     33         */
     34        protected $sql_clauses = array(
     35                'select'  => '',
     36                'from'    => '',
     37                'where'   => array(),
     38                'groupby' => '',
     39                'orderby' => '',
     40                'limits'  => '',
     41        );
     42
     43        /**
     44         * Query vars set by the user.
     45         *
     46         * @since 4.8.0
     47         * @access public
     48         * @var array
     49         */
     50        public $query_vars;
     51
     52        /**
     53         * Default values for query vars.
     54         *
     55         * @since 4.8.0
     56         * @access public
     57         * @var array
     58         */
     59        public $query_var_defaults;
     60
     61        /**
     62         * List of options located by the query.
     63         *
     64         * @since 4.8.0
     65         * @access public
     66         * @var array
     67         */
     68        public $options = array();
     69
     70        /**
     71         * The amount of found options for the current query.
     72         *
     73         * @since 4.8.0
     74         * @access public
     75         * @var int
     76         */
     77        public $found_options = 0;
     78
     79        /**
     80         * The number of pages.
     81         *
     82         * @since 4.8.0
     83         * @access public
     84         * @var int
     85         */
     86        public $max_num_pages = 0;
     87
     88        /**
     89         * The database object
     90         *
     91         * @since 4.8.0
     92         *
     93         * @var WPDB
     94         */
     95        private $db;
     96
     97        /**
     98         * Sets up the option query, based on the query vars passed.
     99         *
     100         * @since 4.8.0
     101         * @access public
     102         *
     103         * @param string|array $query {
     104         *     Optional. Array or query string of option query parameters. Default empty.
     105         *
     106         *     @type int          $option_id         An option ID to only return that option. Default empty.
     107         *     @type array        $option_id__in     Array of option IDs to include. Default empty.
     108         *     @type array        $option_id__not_in Array of option IDs to exclude. Default empty.
     109         *     @type string       $name              Limit results to those affiliated with a given name.
     110         *                                           Default empty.
     111         *     @type array        $name__in          Array of types to include affiliated names for. Default empty.
     112         *     @type array        $name__not_in      Array of types to exclude affiliated names for. Default empty.
     113         *     @type string       $value             Limit results to those affiliated with a given value.
     114         *                                           Default empty.
     115         *     @type array        $value__in         Array of types to include affiliated value for. Default empty.
     116         *     @type array        $value__not_in     Array of types to exclude affiliated value for. Default empty.
     117         *     @type string       $autoload          Limit results to those affiliated with a given autoload.
     118         *                                           Default yes.
     119         *     @type array        $autoload__in      Array of types to include affiliated autoload for. Default empty.
     120         *     @type array        $autoload__not_in  Array of types to exclude affiliated autoload for. Default empty.
     121         *     @type bool         $count             Whether to return a option count (true) or array of option objects.
     122         *                                           Default false.
     123         *     @type string       $fields            Site fields to return. Accepts 'names' (returns an array of option names)
     124         *                                           or empty (returns an array of complete option objects). Default empty.
     125         *     @type int          $number            Maximum number of options to retrieve. Default null (no limit).
     126         *     @type int          $offset            Number of options to offset the query. Used to build LIMIT clause.
     127         *                                           Default 0.
     128         *     @type bool         $no_found_rows     Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
     129         *     @type string|array $orderby           Site status or array of statuses. Accepts 'option_id', 'name', 'value'
     130         *                                           Also accepts false, an empty array, or 'none' to disable `ORDER BY` clause.
     131         *                                           Default 'option_id'.
     132         *     @type string       $order             How to order retrieved options. Accepts 'ASC', 'DESC'. Default 'ASC'.
     133         *     @type string       $search            Search term(s) to retrieve matching options for. Default empty.
     134         *     @type array        $search_columns    Array of column names to be searched. Accepts 'name', 'value'.
     135         *                                           Default empty array.
     136         *
     137         *     @type bool         $update_option_cache Whether to prime the cache for found options. Default false.
     138         * }
     139         */
     140        public function __construct( $query = array() ) {
     141                $this->query_var_defaults = array(
     142                        'fields'              => '',
     143                        'option_id'           => '',
     144                        'option_id__in'       => '',
     145                        'option_id__not_in'   => '',
     146                        'name'                => '',
     147                        'name__in'            => '',
     148                        'name__not_in'        => '',
     149                        'value'               => '',
     150                        'value__in'           => '',
     151                        'value__not_in'       => '',
     152                        'autoload'            => '',
     153                        'autoload__in'        => '',
     154                        'autoload__not_in'    => '',
     155                        'number'              => null,
     156                        'offset'              => '',
     157                        'orderby'             => 'option_id',
     158                        'order'               => 'ASC',
     159                        'search'              => '',
     160                        'search_columns'      => array(),
     161                        'count'               => false,
     162                        'no_found_rows'       => true,
     163                        'meta_query'          => array(),
     164                        'update_option_cache' => true
     165                );
     166
     167                $this->query( $query );
     168        }
     169
     170        /**
     171         * Parses arguments passed to the option query with default query parameters.
     172         *
     173         * @since 4.8.0
     174         * @access public
     175         *
     176         * @see WP_Option_Query::__construct()
     177         *
     178         * @param string|array $query Array or string of WP_Option_Query arguments. See WP_Option_Query::__construct().
     179         */
     180        public function parse_query( $query = array() ) {
     181                if ( empty( $query ) ) {
     182                        $query = $this->query_vars;
     183                }
     184
     185                $this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
     186
     187                /**
     188                 * Fires after the option query vars have been parsed.
     189                 *
     190                 * @since 4.8.0
     191                 *
     192                 * @param WP_Option_Query &$this The WP_Option_Query instance (passed by reference).
     193                 */
     194                do_action_ref_array( 'parse_option_query', array( &$this ) );
     195        }
     196
     197        /**
     198         * Sets up the WordPress query for retrieving options.
     199         *
     200         * @since 4.8.0
     201         * @access public
     202         *
     203         * @param string|array $query Array or URL query string of parameters.
     204         * @return array|int List of options, or number of options when 'count' is passed as a query var.
     205         */
     206        public function query( $query ) {
     207                $this->query_vars = wp_parse_args( $query );
     208
     209                return $this->get_results();
     210        }
     211
     212        /**
     213         * Retrieves a list of options matching the query vars.
     214         *
     215         * @since 4.8.0
     216         * @access public
     217         *
     218         * @return array|int List of options, or number of options when 'count' is passed as a query var.
     219         */
     220        public function get_results() {
     221
     222                // Parse query vars first to help create an accurate cache key below
     223                $this->parse_query();
     224
     225                /**
     226                 * Fires before options are retrieved.
     227                 *
     228                 * @since 4.8.0
     229                 *
     230                 * @param WP_Option_Query &$this Current instance of WP_Option_Query, passed by reference.
     231                 */
     232                do_action_ref_array( 'pre_get_results', array( &$this ) );
     233
     234                // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
     235                $cache_key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
     236                $use_cache = ( ! wp_installing() || ! is_multisite() );
     237
     238                // Look in cache
     239                if ( true === $use_cache ) {
     240                        $last_changed = wp_cache_get_last_changed( 'options' );
     241                        $cache_key    = "get_options:{$cache_key}:{$last_changed}";
     242                        $cache_value  = wp_cache_get( $cache_key, 'options' );
     243                } else {
     244                        $cache_value  = false;
     245                }
     246
     247                // Nothing in (or not using) cache
     248                if ( false === $cache_value ) {
     249
     250                        // Query for options
     251                        $this->options = $this->get_options();
     252
     253                        // Options were found
     254                        if ( ! empty( $this->options ) ) {
     255                                $this->set_found_options( $this->options );
     256                        }
     257
     258                        // Maybe cache
     259                        if ( true === $use_cache ) {
     260
     261                                // Setup array to cache
     262                                $cache_value = array(
     263                                        'options'       => $this->options,
     264                                        'found_options' => (int) $this->found_options,
     265                                );
     266
     267                                // Add results to cache to cache
     268                                wp_cache_add( $cache_key, $cache_value, 'options' );
     269                        }
     270
     271                // Cached results found, so set query-object vars
     272                } else {
     273                        $this->options       = $cache_value['options'];
     274                        $this->found_options = (int) $cache_value['found_options'];
     275                }
     276
     277                // Maybe set max pages if limiting results
     278                if ( ! empty( $this->found_options ) && ! empty( $this->query_vars['number'] ) ) {
     279                        $this->max_num_pages = ceil( $this->found_options / $this->query_vars['number'] );
     280                }
     281
     282                // If querying for a count only, $options is actually a number
     283                if ( ! empty( $this->query_vars['count'] ) ) {
     284                        return (int) $this->options;
     285                }
     286
     287                // Prime option caches
     288                if ( ( true === $use_cache ) && ( false !== $this->query_vars['update_option_cache'] ) ) {
     289                        wp_update_option_caches( $this->options );
     290                }
     291
     292                // Fetch full option objects (likely from the primed cache)
     293                $_options = array();
     294                foreach ( $this->options as $option ) {
     295                        $_option = WP_Option::get_instance( $option, $use_cache );
     296                        if ( ! empty( $_option ) ) {
     297                                $_options[] = $_option;
     298                        }
     299                }
     300
     301                /**
     302                 * Filters the option query results.
     303                 *
     304                 * @since 4.8.0
     305                 *
     306                 * @param array                   $results An array of options.
     307                 * @param WP_Option_Query &$this Current instance of WP_Option_Query, passed by reference.
     308                 */
     309                $_options = apply_filters_ref_array( 'the_options', array( $_options, &$this ) );
     310
     311                // Convert to WP_Option instances.
     312                $this->options = $_options;
     313
     314                return $this->options;
     315        }
     316
     317        /**
     318         * Used internally to get a list of options matching the query vars.
     319         *
     320         * @since 4.8.0
     321         * @access protected
     322         *
     323         * @return int|array A single count of options if a count query. An array of options if a full query.
     324         */
     325        protected function get_options() {
     326                global $wpdb;
     327
     328                // Default ASC/DESC and ORDER BY clause
     329                $order   = $this->parse_order( $this->query_vars['order'] );
     330                $orderby = "option_id {$order}";
     331
     332                // Disable ORDER BY with 'none', an empty array, or boolean false.
     333                if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
     334                        $orderby = '';
     335
     336                // Specific ordering
     337                } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
     338
     339                        // Array or string are supported
     340                        $orders_by = is_array( $this->query_vars['orderby'] )
     341                                ? $this->query_vars['orderby']
     342                                : preg_split( '/[,\s]/', $this->query_vars['orderby'] );
     343
     344                        // Default array to
     345                        $orderby_array = array();
     346
     347                        // Loop through columns to order by
     348                        foreach ( $orders_by as $_key => $_value ) {
     349
     350                                // Skip empty values
     351                                if ( empty( $_value ) ) {
     352                                        continue;
     353                                }
     354
     355                                // Column ID
     356                                if ( is_int( $_key ) ) {
     357                                        $_orderby = $_value;
     358                                        $_order   = $order;
     359
     360                                // String
     361                                } else {
     362                                        $_orderby = $_key;
     363                                        $_order   = $_value;
     364                                }
     365
     366                                // Parse it
     367                                $parsed = $this->parse_orderby( $_orderby );
     368
     369                                // Skip if empty
     370                                if ( empty( $parsed ) ) {
     371                                        continue;
     372                                }
     373
     374                                // Add __in clauses together
     375                                if ( ( 'option_id__in' === $_orderby ) || ( 'name__in' === $_orderby ) || ( 'value__in' === $_orderby ) ) {
     376                                        $orderby_array[] = $parsed;
     377                                        continue;
     378                                }
     379
     380                                // ASC/DESC for each ORDER BY
     381                                $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
     382                        }
     383
     384                        // Concatenate them
     385                        $orderby = implode( ', ', $orderby_array );
     386                }
     387
     388                // Cast number and offset to ints to sanitize
     389                $number = intval( $this->query_vars['number'] );
     390                $offset = intval( $this->query_vars['offset'] );
     391
     392                // Limit to a specific number
     393                if ( ! empty( $number ) ) {
     394
     395                        // Offset and limit
     396                        if ( ! empty( $offset ) ) {
     397                                $limits = $offset . ',' . $number;
     398
     399                        // No offset
     400                        } else {
     401                                $limits = $number;
     402                        }
     403
     404                // No limit
     405                } else {
     406                        $limits = '';
     407                }
     408
     409                /** fields ************************************************************/
     410
     411                // Count or all table columns
     412                $fields = ! empty( $this->query_vars['count'] )
     413                         ? 'COUNT(*)'
     414                        : '*';
     415
     416                /** option_id *********************************************************/
     417
     418                // Parse option IDs for an IN clause.
     419                $option_id = absint( $this->query_vars['option_id'] );
     420                if ( ! empty( $option_id ) ) {
     421                        $this->sql_clauses['where']['option_id'] = $wpdb->prepare( "option_id = %d", $option_id );
     422                }
     423
     424                // Parse option IDs for an IN clause.
     425                if ( ! empty( $this->query_vars['option_id__in'] ) ) {
     426                        if ( 1 === count( $this->query_vars['option_id__in'] ) ) {
     427                                $this->sql_clauses['where']['option_id'] = $wpdb->prepare( "option_id = %d", reset( $this->query_vars['option_id__in'] ) );
     428                        } else {
     429                                $this->sql_clauses['where']['option_id__in'] = "option_id IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['option_id__in'] ) ) . ' )';
     430                        }
     431                }
     432
     433                // Parse option IDs for a NOT IN clause.
     434                if ( ! empty( $this->query_vars['option_id__not_in'] ) ) {
     435                        $this->sql_clauses['where']['option_id__not_in'] = "option_id NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['option_id__not_in'] ) ) . ' )';
     436                }
     437
     438                /** name **************************************************************/
     439
     440                // Parse object names for an IN clause.
     441                if ( ! empty( $this->query_vars['name'] ) ) {
     442                        $this->sql_clauses['where']['name'] = $wpdb->prepare( "option_name = %s", $this->query_vars['name'] );
     443                }
     444
     445                // Parse object names for an IN clause.
     446                if ( ! empty( $this->query_vars['name__in'] ) ) {
     447                        if ( 1 === count( $this->query_vars['name__in'] ) ) {
     448                                $this->sql_clauses['where']['name'] = $wpdb->prepare( "option_name = %s", reset( $this->query_vars['name__in'] ) );
     449                        } else {
     450                                $this->sql_clauses['where']['name__in'] = "option_name IN ( '" . implode( "','", array_map( 'esc_sql', $this->query_vars['name__in'] ) ) . "' )";
     451                        }
     452                }
     453
     454                // Parse object names for a NOT IN clause.
     455                if ( ! empty( $this->query_vars['name__not_in'] ) ) {
     456                        $this->sql_clauses['where']['name__not_in'] = "option_name NOT IN ( '" . implode( "','", array_map( 'esc_sql', $this->query_vars['name__not_in'] ) ) . "' )";
     457                }
     458
     459                /** value *************************************************************/
     460
     461                // Parse object values for an IN clause.
     462                if ( ! empty( $this->query_vars['value'] ) ) {
     463                        $this->sql_clauses['where']['value'] = $wpdb->prepare( "option_value = %s", $this->query_vars['value'] );
     464                }
     465
     466                // Parse object values for an IN clause.
     467                if ( ! empty( $this->query_vars['value__in'] ) ) {
     468                        if ( 1 === count( $this->query_vars['value__in'] ) ) {
     469                                $this->sql_clauses['where']['value'] = $wpdb->prepare( "option_value = %s", reset( $this->query_vars['value__in'] ) );
     470                        } else {
     471                                $this->sql_clauses['where']['value__in'] = "option_value IN ( '" . implode( "','", array_map( 'esc_sql', $this->query_vars['value__in'] ) ) . "' )";
     472                        }
     473                }
     474
     475                // Parse object values for a NOT IN clause.
     476                if ( ! empty( $this->query_vars['value__not_in'] ) ) {
     477                        $this->sql_clauses['where']['value__not_in'] = "option_value NOT IN ( '" . implode( "','", array_map( 'esc_sql', $this->query_vars['value__not_in'] ) ) . "' )";
     478                }
     479
     480                /** autoload **********************************************************/
     481
     482                // Parse object autoloads for an IN clause.
     483                if ( ! empty( $this->query_vars['autoload'] ) ) {
     484                        $this->sql_clauses['where']['autoload'] = $wpdb->prepare( "autoload = %s", $this->query_vars['autoload'] );
     485                }
     486
     487                // Parse object autoloads for an IN clause.
     488                if ( ! empty( $this->query_vars['autoload__in'] ) ) {
     489                        if ( 1 === count( $this->query_vars['autoload__in'] ) ) {
     490                                $this->sql_clauses['where']['autoload'] = $wpdb->prepare( "autoload = %s", reset( $this->query_vars['autoload__in'] ) );
     491                        } else {
     492                                $this->sql_clauses['where']['autoload__in'] = "autoload IN ( '" . implode( "','", $wpdb->_escape( $this->query_vars['autoload__in'] ) ) . "' )";
     493                        }
     494                }
     495
     496                // Parse object autoloads for a NOT IN clause.
     497                if ( ! empty( $this->query_vars['autoload__not_in'] ) ) {
     498                        $this->sql_clauses['where']['autoload__not_in'] = "autoload NOT IN ( '" . implode( "','", $wpdb->_escape( $this->query_vars['autoload__not_in'] ) ) . "' )";
     499                }
     500
     501                /** Search ************************************************************/
     502
     503                // Falsey search strings are ignored.
     504                if ( strlen( $this->query_vars['search'] ) ) {
     505                        $search_columns = array();
     506
     507                        // Searching specific columns
     508                        if ( ! empty( $this->query_vars['search_columns'] ) ) {
     509                                $search_columns = array_intersect( $this->query_vars['search_columns'], array( 'option_name', 'option_value' ) );
     510                        }
     511
     512                        // Default columns to search by
     513                        if ( empty( $search_columns ) ) {
     514                                $search_columns = array( 'option_name', 'option_value' );
     515                        }
     516
     517                        /**
     518                         * Filters the columns to search in a WP_Option_Query search.
     519                         *
     520                         * The default columns include 'name' and 'value'.
     521                         *
     522                         * @since 4.8.0
     523                         *
     524                         * @param array           $search_columns Array of column names to be searched.
     525                         * @param string          $search         Text being searched.
     526                         * @param WP_Option_Query $this           The current WP_Option_Query instance.
     527                         */
     528                        $search_columns = apply_filters( 'option_search_columns', $search_columns, $this->query_vars['search'], $this );
     529
     530                        // Get the search query part from columns
     531                        $this->sql_clauses['where']['search'] = $this->get_search_sql( $this->query_vars['search'], $search_columns );
     532                }
     533
     534                /** where *************************************************************/
     535
     536                $where  = implode( ' AND ', $this->sql_clauses['where'] );
     537
     538                /** Query *************************************************************/
     539
     540                $pieces = compact( array( 'fields', 'where', 'orderby', 'limits', 'groupby' ) );
     541
     542                /**
     543                 * Filters the option query clauses.
     544                 *
     545                 * @since 4.8.0
     546                 *
     547                 * @param array $pieces A compacted array of option query clauses.
     548                 * @param WP_Option_Query &$this Current instance of WP_Option_Query, passed by reference.
     549                 */
     550                $clauses = apply_filters_ref_array( 'option_clauses', array( $pieces, &$this ) );
     551
     552                // SQL_CALC_FOUND_ROWS
     553                $found_rows = ! empty( $this->query_vars['no_found_rows'] )
     554                        ? ''
     555                        : 'SQL_CALC_FOUND_ROWS';
     556
     557                // * or COUNT(*)S
     558                $fields  = ! empty( $clauses['fields'] )
     559                        ? $clauses['fields']
     560                        : '';
     561
     562                // WHERE
     563                $where   = ! empty( $clauses['where'] )
     564                        ? "WHERE {$clauses['where']}"
     565                        : '';
     566
     567                // ORDER BY
     568                $orderby = ! empty( $clauses['orderby'] )
     569                        ? "ORDER BY {$clauses['orderby']}"
     570                        : '';
     571
     572                // GROUP BY
     573                $groupby = ! empty( $clauses['groupby'] )
     574                        ? "GROUP BY {$clauses['groupby']}"
     575                        : '';
     576
     577                // LIMIT
     578                $limits  = ! empty( $clauses['limits'] )
     579                        ? "LIMIT {$clauses['limits']}"
     580                        : '';
     581
     582                // Assemble clauses
     583                $this->sql_clauses['select']  = "SELECT {$found_rows} {$fields}";
     584                $this->sql_clauses['from']    = "FROM {$wpdb->options}";
     585                $this->sql_clauses['where']   = $where;
     586                $this->sql_clauses['groupby'] = $groupby;
     587                $this->sql_clauses['orderby'] = $orderby;
     588                $this->sql_clauses['limits']  = $limits;
     589
     590                // Trim spaces off clauses and concatenate into a request
     591                $this->sql_clauses = array_map( 'trim', $this->sql_clauses );
     592                $this->request     = trim( implode( ' ', $this->sql_clauses ) );
     593
     594                // Return count or results
     595                return ( $this->query_vars['count'] )
     596                        ? intval( $wpdb->get_var( $this->request ) )
     597                        : $wpdb->get_results( $this->request );
     598        }
     599
     600        /**
     601         * Populates found_options and max_num_pages properties for the current query
     602         * if the limit clause was used.
     603         *
     604         * @since 4.8.0
     605         * @access private
     606         *
     607         * @param  array $options Optional array of options
     608         */
     609        private function set_found_options( $options = array() ) {
     610                global $wpdb;
     611
     612                if ( ! empty( $this->query_vars['number'] ) && ! empty( $this->query_vars['no_found_rows'] ) ) {
     613                        /**
     614                         * Filters the query used to retrieve found option count.
     615                         *
     616                         * @since 4.8.0
     617                         *
     618                         * @param string          $found_options_query SQL query. Default 'SELECT FOUND_ROWS()'.
     619                         * @param WP_Option_Query $option_query        The `WP_Option_Query` instance.
     620                         */
     621                        $found_options_query = apply_filters( 'found_options_query', 'SELECT FOUND_ROWS()', $this );
     622
     623                        $this->found_options = (int) $wpdb->get_var( $found_options_query );
     624                } elseif ( ! empty( $options ) ) {
     625                        $this->found_options = count( $options );
     626                }
     627        }
     628
     629        /**
     630         * Used internally to generate an SQL string for searching across multiple columns.
     631         *
     632         * @since 4.8.0
     633         * @access protected
     634         *
     635         * @param string $string  Search string.
     636         * @param array  $columns Columns to search.
     637         * @return string Search SQL.
     638         */
     639        protected function get_search_sql( $string = '', $columns = array() ) {
     640                global $wpdb;
     641
     642                // Bail if parameters are insufficient
     643                if ( empty( $string ) || empty( $columns ) || ! is_array( $columns ) ) {
     644                        return '';
     645                }
     646
     647                // Default search query
     648                $like =  ( false !== strpos( $string, '*' ) )
     649                        ? '%' . implode( '%', array_map( array( $wpdb, 'esc_like' ), explode( '*', $string ) ) ) . '%'
     650                        : '%' . $wpdb->esc_like( $string ) . '%';
     651
     652                // Default array
     653                $searches = array();
     654
     655                // LIKE query parts based on columns
     656                foreach ( $columns as $column ) {
     657                        $searches[] = $wpdb->prepare( "{$column} LIKE %s", $like );
     658                }
     659
     660                // Concatenate and return
     661                return '(' . implode( ' OR ', array_map( 'trim', $searches ) ) . ')';
     662        }
     663
     664        /**
     665         * Parses and sanitizes 'orderby' keys passed to the option query.
     666         *
     667         * @since 4.8.0
     668         * @access protected
     669         *
     670         * @param string $orderby Meta for the field to order by.
     671         * @return string|false Value to used in the ORDER clause. False otherwise.
     672         */
     673        protected function parse_orderby( $orderby = '' ) {
     674
     675                switch ( $orderby ) {
     676                        case 'id' :
     677                        case 'option_id' :
     678                                $parsed = 'option_id';
     679                                break;
     680                        case 'name' :
     681                        case 'option_name' :
     682                                $parsed = 'option_name';
     683                                break;
     684                        case 'value' :
     685                        case 'option_value' :
     686                                $parsed = 'option_value';
     687                                break;
     688                        case 'autoload' :
     689                        case 'option_autoload' :
     690                                $parsed = 'autoload';
     691                                break;
     692                        case 'option_id__in' :
     693                                $option__in = implode( ',', array_map( 'absint', $this->query_vars['option_id__in'] ) );
     694                                $parsed     = "FIELD( option_id, {$option__in} )";
     695                                break;
     696                        case 'name__in' :
     697                                $option__in = implode( ',', array_map( 'wp_unslash', $this->query_vars['name__in'] ) );
     698                                $parsed     = "FIELD( option_name, {$option__in} )";
     699                                break;
     700                        case 'value__in' :
     701                                $option__in = implode( ',', array_map( 'wp_unslash', $this->query_vars['value__in'] ) );
     702                                $parsed     = "FIELD( option_value, {$option__in} )";
     703                                break;
     704                        case 'autoload__in' :
     705                                $option__in = implode( ',', array_map( 'wp_unslash', $this->query_vars['autoload__in'] ) );
     706                                $parsed     = "FIELD( autoload, {$option__in} )";
     707                                break;
     708                        default :
     709                                $parsed = false;
     710                                break;
     711                }
     712
     713                return $parsed;
     714        }
     715
     716        /**
     717         * Parses an 'order' query variable and cast it to 'ASC' or 'DESC' as necessary.
     718         *
     719         * @since 4.8.0
     720         * @access protected
     721         *
     722         * @param string $order The 'order' query variable.
     723         * @return string The sanitized 'order' query variable.
     724         */
     725        protected function parse_order( $order = '' ) {
     726
     727                // Default ASC
     728                if ( ! is_string( $order ) || empty( $order ) ) {
     729                        return 'ASC';
     730                }
     731
     732                // ASC or DESC
     733                return ( 'ASC' === strtoupper( $order ) )
     734                        ? 'ASC'
     735                        : 'DESC';
     736        }
     737}
  • new file src/wp-includes/class-wp-option.php

    diff --git src/wp-includes/class-wp-option.php src/wp-includes/class-wp-option.php
    new file mode 100644
    index 0000000..05dde5b
    - +  
     1<?php
     2
     3/**
     4 * Meta Object
     5 *
     6 */
     7
     8// Exit if accessed directly
     9defined( 'ABSPATH' ) || exit;
     10
     11class WP_Option {
     12
     13        /**
     14         * Unique ID of the option
     15         *
     16         * @since 4.8.0
     17         *
     18         * @var int
     19         */
     20        public $id = 0;
     21
     22        /**
     23         * Unique name of the option
     24         *
     25         * @since 4.8.0
     26         *
     27         * @var string
     28         */
     29        public $name = '';
     30
     31        /**
     32         * Value of the option
     33         *
     34         * @since 4.8.0
     35         *
     36         * @var mixed
     37         */
     38        public $value = null;
     39
     40        /**
     41         * Whether to automatically load (yes|no|custom)
     42         *
     43         * @since 4.8.0
     44         *
     45         * @var string
     46         */
     47        public $autoload = '';
     48
     49        /**
     50         * Get an option instance, based on a unique option name
     51         *
     52         * @since 4.8.0
     53         *
     54         * @global WPDB $wpdb
     55         *
     56         * @param  object  $option
     57         * @param  boolean $use_cache
     58         *
     59         * @return boolean|\WP_Option
     60         */
     61        public static function get_instance( $option = '', $use_cache = true ) {
     62                global $wpdb;
     63
     64                if ( $option instanceof WP_Option ) {
     65                        return $option;
     66                } elseif ( is_object( $option ) || is_array( $option ) ) {
     67                        return new WP_Option( $option );
     68                } elseif ( is_string( $option ) ) {
     69                        $option_name = $option;
     70                } else {
     71                        return false;
     72                }
     73
     74                // Trim the option name
     75                $option_name = trim( $option_name );
     76
     77                // Check options cache
     78                if ( true === $use_cache ) {
     79                        $_option = wp_cache_get( $option_name, 'options' );
     80
     81                        // Fallback to unknown options cache
     82                        if ( false === $_option ) {
     83                                $_option = wp_cache_get( $option_name, 'notoptions' );
     84                        }
     85
     86                // No cache
     87                } else {
     88                        $_option = false;
     89                }
     90
     91                // Cache miss
     92                if ( false === $_option ) {
     93
     94                        // Query for option
     95                        $suppress = $wpdb->suppress_errors();
     96                        $_option  = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $option_name ) );
     97                        $wpdb->suppress_errors( $suppress );
     98
     99                        // No option found, so mock one for the 'notoptions' cache
     100                        if ( empty( $_option ) || is_wp_error( $_option ) ) {
     101                                $cache_group = 'notoptions';
     102                                $_option     = (object) array(
     103                                        'option_id'    => 0,
     104                                        'option_name'  => $option_name,
     105                                        'option_value' => null,
     106                                        'autoload'     => ''
     107                                );
     108
     109                        // Option found, so prepare for 'options' cache
     110                        } else {
     111                                $cache_group = 'options';
     112                        }
     113
     114                        // Add to cache
     115                        if ( true === $use_cache ) {
     116                                wp_update_option_cache( $_option, $cache_group );
     117                        }
     118
     119                        // This gets called many times, so do some garbage collection
     120                        unset( $cache_group, $suppress, $option_name );
     121                }
     122
     123                // Return WP_Option object
     124                return new WP_Option( $_option );
     125        }
     126
     127        /**
     128         * Creates a new WP_Option object.
     129         *
     130         * Will populate object properties from the object provided and assign other
     131         * default properties based on that information.
     132         *
     133         * @since 4.8.0
     134         * @access public
     135         *
     136         * @param WP_Option|object $option A option object.
     137         */
     138        public function __construct( $_option = '' ) {
     139
     140                // Bail if nothing passed
     141                if ( empty( $_option ) ) {
     142                        return;
     143                }
     144
     145                // Cast array to object
     146                if ( is_array( $_option ) ) {
     147                        $_option = (object) $_option;
     148                }
     149
     150                // Assign object variables
     151                if ( is_object( $_option ) ) {
     152                        foreach( get_object_vars( $_option ) as $key => $value ) {
     153                                $this->{$key} = $value;
     154                        }
     155                }
     156
     157                // Garbage collection to reduce memory footprint
     158                unset( $_option );
     159        }
     160
     161        /**
     162         * Getter.
     163         *
     164         * @since 4.8.0
     165         * @access public
     166         *
     167         * @param string $key Property to get.
     168         * @return mixed Value of the property. Null if not available.
     169         */
     170        public function __get( $key ) {
     171                switch ( $key ) {
     172                        case 'ID' :
     173                        case 'option_id' :
     174                                return (int) $this->id;
     175
     176                        case 'option_name' :
     177                                return trim( $this->name );
     178
     179                        case 'option_value' :
     180                                return $this->value;
     181
     182                        case 'option_autoload' :
     183                                return $this->autoload;
     184                }
     185
     186                return null;
     187        }
     188
     189        /**
     190         * Isset-er.
     191         *
     192         * @since 4.8.0
     193         * @access public
     194         *
     195         * @param string $key Property to check if set.
     196         * @return bool Whether the property is set.
     197         */
     198        public function __isset( $key ) {
     199                switch ( $key ) {
     200                        case 'ID':
     201                        case 'option_id':
     202                                return true;
     203
     204                        case 'option_name' :
     205                                return isset( $this->name );
     206
     207                        case 'option_value' :
     208                                return isset( $this->value );
     209
     210                        case 'option_autoload' :
     211                                return $this->autoload;
     212
     213                        default :
     214                                return isset( $this->{$key} );
     215                }
     216
     217                return false;
     218        }
     219
     220        /**
     221         * Setter.
     222         *
     223         * @since 4.8.0
     224         * @access public
     225         *
     226         * @param string $key   Property to set.
     227         * @param mixed  $value Value to assign to the property.
     228         */
     229        public function __set( $key, $value ) {
     230                switch ( $key ) {
     231                        case 'ID' :
     232                        case 'option_id' :
     233                                $this->id = (int) $value;
     234                                break;
     235
     236                        case 'option_name' :
     237                                $this->name = trim( $value );
     238                                break;
     239
     240                        case 'option_value' :
     241                                $this->value = $value;
     242                                break;
     243
     244                        case 'option_autoload' :
     245                                $this->autoload = $value;
     246                                break;
     247
     248                        default :
     249                                $this->{$key} = $value;
     250                                break;
     251                }
     252        }
     253}
  • src/wp-includes/functions.php

    diff --git src/wp-includes/functions.php src/wp-includes/functions.php
    index 966dcd8..e82930c 100644
     
    55 * @package WordPress
    66 */
    77
     8require( ABSPATH . WPINC . '/class-wp-option.php' );
     9require( ABSPATH . WPINC . '/class-wp-option-query.php' );
    810require( ABSPATH . WPINC . '/option.php' );
    911
    1012/**
  • src/wp-includes/option.php

    diff --git src/wp-includes/option.php src/wp-includes/option.php
    index 8bc8edc..37d0155 100644
     
    5959        // Distinguish between `false` as a default, and not passing one.
    6060        $passed_default = func_num_args() > 1;
    6161
    62         if ( ! wp_installing() ) {
    63                 // prevent non-existent options from triggering multiple queries
    64                 $notoptions = wp_cache_get( 'notoptions', 'options' );
    65                 if ( isset( $notoptions[ $option ] ) ) {
    66                         /**
    67                          * Filters the default value for an option.
    68                          *
    69                          * The dynamic portion of the hook name, `$option`, refers to the option name.
    70                          *
    71                          * @since 3.4.0
    72                          * @since 4.4.0 The `$option` parameter was added.
    73                          * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value.
    74                          *
    75                          * @param mixed  $default The default value to return if the option does not exist
    76                          *                        in the database.
    77                          * @param string $option  Option name.
    78                          * @param bool   $passed_default Was `get_option()` passed a default value?
    79                          */
    80                         return apply_filters( "default_option_{$option}", $default, $option, $passed_default );
    81                 }
     62        // Get an option instance (bypass cache if installing)
     63        $_option = WP_Option::get_instance( $option, ! wp_installing() );
    8264
    83                 $alloptions = wp_load_alloptions();
     65        // Check for option value or use null value
     66        $value = isset( $_option->option_value )
     67                ? $_option->option_value
     68                : null;
    8469
    85                 if ( isset( $alloptions[$option] ) ) {
    86                         $value = $alloptions[$option];
    87                 } else {
    88                         $value = wp_cache_get( $option, 'options' );
    89 
    90                         if ( false === $value ) {
    91                                 $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );
    92 
    93                                 // Has to be get_row instead of get_var because of funkiness with 0, false, null values
    94                                 if ( is_object( $row ) ) {
    95                                         $value = $row->option_value;
    96                                         wp_cache_add( $option, $value, 'options' );
    97                                 } else { // option does not exist, so we must cache its non-existence
    98                                         if ( ! is_array( $notoptions ) ) {
    99                                                  $notoptions = array();
    100                                         }
    101                                         $notoptions[$option] = true;
    102                                         wp_cache_set( 'notoptions', $notoptions, 'options' );
    103 
    104                                         /** This filter is documented in wp-includes/option.php */
    105                                         return apply_filters( "default_option_{$option}", $default, $option, $passed_default );
    106                                 }
    107                         }
    108                 }
    109         } else {
    110                 $suppress = $wpdb->suppress_errors();
    111                 $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );
    112                 $wpdb->suppress_errors( $suppress );
    113                 if ( is_object( $row ) ) {
    114                         $value = $row->option_value;
    115                 } else {
    116                         /** This filter is documented in wp-includes/option.php */
    117                         return apply_filters( "default_option_{$option}", $default, $option, $passed_default );
    118                 }
     70        // Option does not exist, so return the default value
     71        if ( null === $value ) {
     72                /**
     73                 * Filters the default value for an option.
     74                 *
     75                 * The dynamic portion of the hook name, `$option`, refers to the option name.
     76                 *
     77                 * @since 3.4.0
     78                 * @since 4.4.0 The `$option` parameter was added.
     79                 * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value.
     80                 *
     81                 * @param mixed  $default The default value to return if the option does not exist
     82                 *                        in the database.
     83                 * @param string $option  Option name.
     84                 * @param bool   $passed_default Was `get_option()` passed a default value?
     85                 */
     86                return apply_filters( "default_option_{$option}", $default, $option, $passed_default );
    11987        }
    12088
    12189        // If home is not set use siteurl.
    122         if ( 'home' == $option && '' == $value )
     90        if ( ( 'home' === $option ) && empty( $value ) ) {
    12391                return get_option( 'siteurl' );
     92        }
    12493
    125         if ( in_array( $option, array('siteurl', 'home', 'category_base', 'tag_base') ) )
     94        // Strip trailing slash from certain options (for back compat)
     95        if ( in_array( $option, array( 'siteurl', 'home', 'category_base', 'tag_base' ), true ) ) {
    12696                $value = untrailingslashit( $value );
     97        }
    12798
    12899        /**
    129100         * Filters the value of an existing option.
     
    171142 * Loads and caches all autoloaded options, if available or all options.
    172143 *
    173144 * @since 2.2.0
    174  *
    175  * @global wpdb $wpdb WordPress database abstraction object.
     145 * @since 4.8.0 Use WP_Option_Query
    176146 *
    177147 * @return array List of all options.
    178148 */
    179149function wp_load_alloptions() {
    180         global $wpdb;
    181150
    182         if ( ! wp_installing() || ! is_multisite() )
    183                 $alloptions = wp_cache_get( 'alloptions', 'options' );
    184         else
    185                 $alloptions = false;
     151        // Autoload options
     152        $alloptions_db = new WP_Option_Query( array( 'autoload' => 'yes' ) );
    186153
    187         if ( !$alloptions ) {
    188                 $suppress = $wpdb->suppress_errors();
    189                 if ( !$alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" ) )
    190                         $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options" );
    191                 $wpdb->suppress_errors($suppress);
    192                 $alloptions = array();
    193                 foreach ( (array) $alloptions_db as $o ) {
    194                         $alloptions[$o->option_name] = $o->option_value;
    195                 }
    196                 if ( ! wp_installing() || ! is_multisite() )
    197                         wp_cache_add( 'alloptions', $alloptions, 'options' );
     154        // No autoload options yet, so query for all options
     155        if ( empty( $alloptions_db->options ) ) {
     156                $alloptions_db = new WP_Option_Query();
    198157        }
    199158
    200         return $alloptions;
     159        // Return names & values
     160        return wp_list_pluck( $alloptions_db->options, 'value', 'name' );
    201161}
    202162
    203163/**
     
    258218function update_option( $option, $value, $autoload = null ) {
    259219        global $wpdb;
    260220
    261         $option = trim($option);
    262         if ( empty($option) )
     221        $option = trim( $option );
     222        if ( empty( $option ) ) {
    263223                return false;
     224        }
    264225
    265226        wp_protect_special_option( $option );
    266227
    267         if ( is_object( $value ) )
     228        if ( is_object( $value ) ) {
    268229                $value = clone $value;
     230        }
    269231
    270         $value = sanitize_option( $option, $value );
     232        $value     = sanitize_option( $option, $value );
    271233        $old_value = get_option( $option );
    272234
    273235        /**
     
    331293         */
    332294        do_action( 'update_option', $option, $old_value, $value );
    333295
     296        // Add the serialized value to the update arguments array
    334297        $update_args = array(
    335298                'option_value' => $serialized_value,
    336299        );
    337300
     301        // Allow autoload to be changed
    338302        if ( null !== $autoload ) {
    339303                $update_args['autoload'] = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes';
    340304        }
    341305
     306        // Update database
    342307        $result = $wpdb->update( $wpdb->options, $update_args, array( 'option_name' => $option ) );
    343         if ( ! $result )
     308        if ( ! $result ) {
    344309                return false;
    345 
    346         $notoptions = wp_cache_get( 'notoptions', 'options' );
    347         if ( is_array( $notoptions ) && isset( $notoptions[$option] ) ) {
    348                 unset( $notoptions[$option] );
    349                 wp_cache_set( 'notoptions', $notoptions, 'options' );
    350310        }
    351311
     312        // Update caches if not installing
    352313        if ( ! wp_installing() ) {
    353                 $alloptions = wp_load_alloptions();
    354                 if ( isset( $alloptions[$option] ) ) {
    355                         $alloptions[ $option ] = $serialized_value;
    356                         wp_cache_set( 'alloptions', $alloptions, 'options' );
    357                 } else {
    358                         wp_cache_set( $option, $serialized_value, 'options' );
     314
     315                // Get option (likely from cache)
     316                $_option = WP_Option::get_instance( $option );
     317
     318                // Update value & autoload
     319                $_option->option_value = $serialized_value;
     320
     321                if ( ! empty( $update_args['autoload'] ) ) {
     322                        $_option->autoload = $update_args['autoload'];
    359323                }
     324
     325                // Update the cached option object with any new values
     326                wp_update_option_cache( $_option );
     327
     328                // Invalidate all previous options queries
     329                wp_cache_set( 'last_changed', microtime(), 'options' );
    360330        }
    361331
    362332        /**
     
    424394        if ( is_object($value) )
    425395                $value = clone $value;
    426396
     397        // Check for existing values
     398        $old_value = get_option( $option );
     399
     400        /** This filter is documented in wp-includes/option.php */
     401        if ( apply_filters( "default_option_{$option}", false, $option, false ) !== $old_value ) {
     402                return false;
     403        }
     404
    427405        $value = sanitize_option( $option, $value );
    428 
    429         // Make sure the option doesn't already exist. We can check the 'notoptions' cache before we ask for a db query
    430         $notoptions = wp_cache_get( 'notoptions', 'options' );
    431         if ( !is_array( $notoptions ) || !isset( $notoptions[$option] ) )
    432                 /** This filter is documented in wp-includes/option.php */
    433                 if ( apply_filters( "default_option_{$option}", false, $option, false ) !== get_option( $option ) )
    434                         return false;
    435 
    436406        $serialized_value = maybe_serialize( $value );
    437407        $autoload = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes';
    438408
     
    450420        if ( ! $result )
    451421                return false;
    452422
     423        // Update caches if not installing
    453424        if ( ! wp_installing() ) {
    454                 if ( 'yes' == $autoload ) {
    455                         $alloptions = wp_load_alloptions();
    456                         $alloptions[ $option ] = $serialized_value;
    457                         wp_cache_set( 'alloptions', $alloptions, 'options' );
    458                 } else {
    459                         wp_cache_set( $option, $serialized_value, 'options' );
    460                 }
    461         }
    462425
    463         // This option exists now
    464         $notoptions = wp_cache_get( 'notoptions', 'options' ); // yes, again... we need it to be fresh
    465         if ( is_array( $notoptions ) && isset( $notoptions[$option] ) ) {
    466                 unset( $notoptions[$option] );
    467                 wp_cache_set( 'notoptions', $notoptions, 'options' );
     426                // Get option (likely from cache)
     427                $_option = WP_Option::get_instance( $option );
     428
     429                // Update value & autoload
     430                $_option->option_value = $serialized_value;
     431                $_option->autoload     = $autoload;
     432
     433                // Update the cached option object with any new values
     434                wp_update_option_cache( $_option );
     435
     436                // Invalidate all previous options queries
     437                wp_cache_set( 'last_changed', microtime(), 'options' );
    468438        }
    469439
    470440        /**
     
    512482        wp_protect_special_option( $option );
    513483
    514484        // Get the ID, if no ID then return
    515         $row = $wpdb->get_row( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s", $option ) );
     485        $row = $wpdb->get_row( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );
    516486        if ( is_null( $row ) )
    517487                return false;
    518488
     
    526496        do_action( 'delete_option', $option );
    527497
    528498        $result = $wpdb->delete( $wpdb->options, array( 'option_name' => $option ) );
    529         if ( ! wp_installing() ) {
    530                 if ( 'yes' == $row->autoload ) {
    531                         $alloptions = wp_load_alloptions();
    532                         if ( is_array( $alloptions ) && isset( $alloptions[$option] ) ) {
    533                                 unset( $alloptions[$option] );
    534                                 wp_cache_set( 'alloptions', $alloptions, 'options' );
    535                         }
    536                 } else {
    537                         wp_cache_delete( $option, 'options' );
     499
     500        if ( false !== $result ) {
     501
     502                // Clean the cache for the deleted option if not installing
     503                if ( ! wp_installing() ) {
     504                        wp_clean_option_cache( $option );
    538505                }
    539         }
    540         if ( $result ) {
    541506
    542507                /**
    543508                 * Fires after a specific option has been deleted.
     
    20452010
    20462011        return $registered[ $option ]['default'];
    20472012}
     2013
     2014/**
     2015 * Updates option in cache.
     2016 *
     2017 * @since 4.8.0
     2018 *
     2019 * @param WP_Option $option      Option object
     2020 * @param string    $cache_group options|notoptions
     2021 */
     2022function wp_update_option_cache( $option, $cache_group = 'options' ) {
     2023        wp_cache_set( $option->option_name, $option, $cache_group );
     2024}
     2025
     2026/**
     2027 * Updates option in cache.
     2028 *
     2029 * @since 4.8.0
     2030 *
     2031 * @param array  $options     Array of WP_Option objects.
     2032 * @param string $cache_group options|notoptions
     2033 */
     2034function wp_update_option_caches( $options = array(), $cache_group = 'options' ) {
     2035
     2036        // Bail if no options
     2037        if ( empty( $options ) ) {
     2038                return;
     2039        }
     2040
     2041        // Loop through options & add them to cache group
     2042        foreach ( $options as $option ) {
     2043                wp_update_option_cache( $option, $cache_group );
     2044        }
     2045}
     2046
     2047/**
     2048 * Clean the option cache
     2049 *
     2050 * @since 4.8.0
     2051 *
     2052 * @param int|WP_Option $option Option ID or option object to remove from the cache
     2053 */
     2054function wp_clean_option_cache( $option = '' ) {
     2055
     2056        // Get option, and bail if not found
     2057        $_option = WP_Option::get_instance( $option );
     2058        if ( empty( $_option ) || is_wp_error( $_option ) ) {
     2059                return;
     2060        }
     2061
     2062        // Delete option from cache group
     2063        wp_cache_delete( $_option->option_name, 'options'    );
     2064        wp_cache_delete( $_option->option_name, 'notoptions' );
     2065
     2066        /**
     2067         * Fires immediately after option has been removed from the object cache.
     2068         *
     2069         * @since 4.8.0
     2070         *
     2071         * @param int       $option  Option name.
     2072         * @param WP_Option $_option Option object.
     2073         */
     2074        do_action( 'clean_option_cache', $option, $_option );
     2075
     2076        // Update the last changed cache for this group
     2077        wp_cache_set( 'last_changed', microtime(), 'options' );
     2078}
  • tests/phpunit/tests/general/archives.php

    diff --git tests/phpunit/tests/general/archives.php tests/phpunit/tests/general/archives.php
    index 3e10f2a..a38e0ee 100644
     
    1616        function test_get_archives_cache() {
    1717                global $wpdb;
    1818
     19                // Prevent 'notoptions' from influencing results
     20                wp_load_alloptions();
     21
    1922                self::factory()->post->create_many( 3, array( 'post_type' => 'post' ) );
    2023                wp_cache_delete( 'last_changed', 'posts' );
    2124                $this->assertFalse( wp_cache_get( 'last_changed', 'posts' ) );
  • tests/phpunit/tests/post/query.php

    diff --git tests/phpunit/tests/post/query.php tests/phpunit/tests/post/query.php
    index 6ae8114..06ff8c7 100644
     
    395395        public function test_posts_pre_query_filter_should_bypass_database_query() {
    396396                global $wpdb;
    397397
     398                // Prevent 'notoptions' from influencing results
     399                wp_load_alloptions();
     400
    398401                add_filter( 'posts_pre_query', array( __CLASS__, 'filter_posts_pre_query' ) );
    399402
    400403                $num_queries = $wpdb->num_queries;
  • tests/phpunit/tests/term/meta.php

    diff --git tests/phpunit/tests/term/meta.php tests/phpunit/tests/term/meta.php
    index ac309dc..987f95b 100644
     
    128128                remove_action( 'pre_get_posts', array( $this, 'set_cache_results' ) );
    129129
    130130                if ( have_posts() ) {
     131
     132                        // Prevent 'notoptions' from influencing results
     133                        wp_load_alloptions();
     134
    131135                        while ( have_posts() ) {
    132136                                the_post();
    133137