angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-site-query.php (29308B)


      1 <?php
      2 /**
      3  * Site API: WP_Site_Query class
      4  *
      5  * @package WordPress
      6  * @subpackage Sites
      7  * @since 4.6.0
      8  */
      9 
     10 /**
     11  * Core class used for querying sites.
     12  *
     13  * @since 4.6.0
     14  *
     15  * @see WP_Site_Query::__construct() for accepted arguments.
     16  */
     17 class WP_Site_Query {
     18 
     19 	/**
     20 	 * SQL for database query.
     21 	 *
     22 	 * @since 4.6.0
     23 	 * @var string
     24 	 */
     25 	public $request;
     26 
     27 	/**
     28 	 * SQL query clauses.
     29 	 *
     30 	 * @since 4.6.0
     31 	 * @var array
     32 	 */
     33 	protected $sql_clauses = array(
     34 		'select'  => '',
     35 		'from'    => '',
     36 		'where'   => array(),
     37 		'groupby' => '',
     38 		'orderby' => '',
     39 		'limits'  => '',
     40 	);
     41 
     42 	/**
     43 	 * Metadata query container.
     44 	 *
     45 	 * @since 5.1.0
     46 	 * @var WP_Meta_Query
     47 	 */
     48 	public $meta_query = false;
     49 
     50 	/**
     51 	 * Metadata query clauses.
     52 	 *
     53 	 * @since 5.1.0
     54 	 * @var array
     55 	 */
     56 	protected $meta_query_clauses;
     57 
     58 	/**
     59 	 * Date query container.
     60 	 *
     61 	 * @since 4.6.0
     62 	 * @var WP_Date_Query A date query instance.
     63 	 */
     64 	public $date_query = false;
     65 
     66 	/**
     67 	 * Query vars set by the user.
     68 	 *
     69 	 * @since 4.6.0
     70 	 * @var array
     71 	 */
     72 	public $query_vars;
     73 
     74 	/**
     75 	 * Default values for query vars.
     76 	 *
     77 	 * @since 4.6.0
     78 	 * @var array
     79 	 */
     80 	public $query_var_defaults;
     81 
     82 	/**
     83 	 * List of sites located by the query.
     84 	 *
     85 	 * @since 4.6.0
     86 	 * @var array
     87 	 */
     88 	public $sites;
     89 
     90 	/**
     91 	 * The amount of found sites for the current query.
     92 	 *
     93 	 * @since 4.6.0
     94 	 * @var int
     95 	 */
     96 	public $found_sites = 0;
     97 
     98 	/**
     99 	 * The number of pages.
    100 	 *
    101 	 * @since 4.6.0
    102 	 * @var int
    103 	 */
    104 	public $max_num_pages = 0;
    105 
    106 	/**
    107 	 * Sets up the site query, based on the query vars passed.
    108 	 *
    109 	 * @since 4.6.0
    110 	 * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
    111 	 * @since 5.1.0 Introduced the 'update_site_meta_cache', 'meta_query', 'meta_key',
    112 	 *              'meta_value', 'meta_type' and 'meta_compare' parameters.
    113 	 *
    114 	 * @param string|array $query {
    115 	 *     Optional. Array or query string of site query parameters. Default empty.
    116 	 *
    117 	 *     @type int[]        $site__in               Array of site IDs to include. Default empty.
    118 	 *     @type int[]        $site__not_in           Array of site IDs to exclude. Default empty.
    119 	 *     @type bool         $count                  Whether to return a site count (true) or array of site objects.
    120 	 *                                                Default false.
    121 	 *     @type array        $date_query             Date query clauses to limit sites by. See WP_Date_Query.
    122 	 *                                                Default null.
    123 	 *     @type string       $fields                 Site fields to return. Accepts 'ids' (returns an array of site IDs)
    124 	 *                                                or empty (returns an array of complete site objects). Default empty.
    125 	 *     @type int          $ID                     A site ID to only return that site. Default empty.
    126 	 *     @type int          $number                 Maximum number of sites to retrieve. Default 100.
    127 	 *     @type int          $offset                 Number of sites to offset the query. Used to build LIMIT clause.
    128 	 *                                                Default 0.
    129 	 *     @type bool         $no_found_rows          Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
    130 	 *     @type string|array $orderby                Site status or array of statuses. Accepts 'id', 'domain', 'path',
    131 	 *                                                'network_id', 'last_updated', 'registered', 'domain_length',
    132 	 *                                                'path_length', 'site__in' and 'network__in'. Also accepts false,
    133 	 *                                                an empty array, or 'none' to disable `ORDER BY` clause.
    134 	 *                                                Default 'id'.
    135 	 *     @type string       $order                  How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
    136 	 *     @type int          $network_id             Limit results to those affiliated with a given network ID. If 0,
    137 	 *                                                include all networks. Default 0.
    138 	 *     @type int[]        $network__in            Array of network IDs to include affiliated sites for. Default empty.
    139 	 *     @type int[]        $network__not_in        Array of network IDs to exclude affiliated sites for. Default empty.
    140 	 *     @type string       $domain                 Limit results to those affiliated with a given domain. Default empty.
    141 	 *     @type string[]     $domain__in             Array of domains to include affiliated sites for. Default empty.
    142 	 *     @type string[]     $domain__not_in         Array of domains to exclude affiliated sites for. Default empty.
    143 	 *     @type string       $path                   Limit results to those affiliated with a given path. Default empty.
    144 	 *     @type string[]     $path__in               Array of paths to include affiliated sites for. Default empty.
    145 	 *     @type string[]     $path__not_in           Array of paths to exclude affiliated sites for. Default empty.
    146 	 *     @type int          $public                 Limit results to public sites. Accepts '1' or '0'. Default empty.
    147 	 *     @type int          $archived               Limit results to archived sites. Accepts '1' or '0'. Default empty.
    148 	 *     @type int          $mature                 Limit results to mature sites. Accepts '1' or '0'. Default empty.
    149 	 *     @type int          $spam                   Limit results to spam sites. Accepts '1' or '0'. Default empty.
    150 	 *     @type int          $deleted                Limit results to deleted sites. Accepts '1' or '0'. Default empty.
    151 	 *     @type int          $lang_id                Limit results to a language ID. Default empty.
    152 	 *     @type string[]     $lang__in               Array of language IDs to include affiliated sites for. Default empty.
    153 	 *     @type string[]     $lang__not_in           Array of language IDs to exclude affiliated sites for. Default empty.
    154 	 *     @type string       $search                 Search term(s) to retrieve matching sites for. Default empty.
    155 	 *     @type string[]     $search_columns         Array of column names to be searched. Accepts 'domain' and 'path'.
    156 	 *                                                Default empty array.
    157 	 *     @type bool         $update_site_cache      Whether to prime the cache for found sites. Default true.
    158 	 *     @type bool         $update_site_meta_cache Whether to prime the metadata cache for found sites. Default true.
    159 	 *     @type array        $meta_query             Meta query clauses to limit retrieved sites by. See `WP_Meta_Query`.
    160 	 *                                                Default empty.
    161 	 *     @type string       $meta_key               Limit sites to those matching a specific metadata key.
    162 	 *                                                Can be used in conjunction with `$meta_value`. Default empty.
    163 	 *     @type string       $meta_value             Limit sites to those matching a specific metadata value.
    164 	 *                                                Usually used in conjunction with `$meta_key`. Default empty.
    165 	 *     @type string       $meta_type              Data type that the `$meta_value` column will be CAST to for
    166 	 *                                                comparisons. Default empty.
    167 	 *     @type string       $meta_compare           Comparison operator to test the `$meta_value`. Default empty.
    168 	 * }
    169 	 */
    170 	public function __construct( $query = '' ) {
    171 		$this->query_var_defaults = array(
    172 			'fields'                 => '',
    173 			'ID'                     => '',
    174 			'site__in'               => '',
    175 			'site__not_in'           => '',
    176 			'number'                 => 100,
    177 			'offset'                 => '',
    178 			'no_found_rows'          => true,
    179 			'orderby'                => 'id',
    180 			'order'                  => 'ASC',
    181 			'network_id'             => 0,
    182 			'network__in'            => '',
    183 			'network__not_in'        => '',
    184 			'domain'                 => '',
    185 			'domain__in'             => '',
    186 			'domain__not_in'         => '',
    187 			'path'                   => '',
    188 			'path__in'               => '',
    189 			'path__not_in'           => '',
    190 			'public'                 => null,
    191 			'archived'               => null,
    192 			'mature'                 => null,
    193 			'spam'                   => null,
    194 			'deleted'                => null,
    195 			'lang_id'                => null,
    196 			'lang__in'               => '',
    197 			'lang__not_in'           => '',
    198 			'search'                 => '',
    199 			'search_columns'         => array(),
    200 			'count'                  => false,
    201 			'date_query'             => null, // See WP_Date_Query.
    202 			'update_site_cache'      => true,
    203 			'update_site_meta_cache' => true,
    204 			'meta_query'             => '',
    205 			'meta_key'               => '',
    206 			'meta_value'             => '',
    207 			'meta_type'              => '',
    208 			'meta_compare'           => '',
    209 		);
    210 
    211 		if ( ! empty( $query ) ) {
    212 			$this->query( $query );
    213 		}
    214 	}
    215 
    216 	/**
    217 	 * Parses arguments passed to the site query with default query parameters.
    218 	 *
    219 	 * @since 4.6.0
    220 	 *
    221 	 * @see WP_Site_Query::__construct()
    222 	 *
    223 	 * @param string|array $query Array or string of WP_Site_Query arguments. See WP_Site_Query::__construct().
    224 	 */
    225 	public function parse_query( $query = '' ) {
    226 		if ( empty( $query ) ) {
    227 			$query = $this->query_vars;
    228 		}
    229 
    230 		$this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
    231 
    232 		/**
    233 		 * Fires after the site query vars have been parsed.
    234 		 *
    235 		 * @since 4.6.0
    236 		 *
    237 		 * @param WP_Site_Query $this The WP_Site_Query instance (passed by reference).
    238 		 */
    239 		do_action_ref_array( 'parse_site_query', array( &$this ) );
    240 	}
    241 
    242 	/**
    243 	 * Sets up the WordPress query for retrieving sites.
    244 	 *
    245 	 * @since 4.6.0
    246 	 *
    247 	 * @param string|array $query Array or URL query string of parameters.
    248 	 * @return array|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids',
    249 	 *                   or the number of sites when 'count' is passed as a query var.
    250 	 */
    251 	public function query( $query ) {
    252 		$this->query_vars = wp_parse_args( $query );
    253 
    254 		return $this->get_sites();
    255 	}
    256 
    257 	/**
    258 	 * Retrieves a list of sites matching the query vars.
    259 	 *
    260 	 * @since 4.6.0
    261 	 *
    262 	 * @global wpdb $wpdb WordPress database abstraction object.
    263 	 *
    264 	 * @return array|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids',
    265 	 *                   or the number of sites when 'count' is passed as a query var.
    266 	 */
    267 	public function get_sites() {
    268 		global $wpdb;
    269 
    270 		$this->parse_query();
    271 
    272 		// Parse meta query.
    273 		$this->meta_query = new WP_Meta_Query();
    274 		$this->meta_query->parse_query_vars( $this->query_vars );
    275 
    276 		/**
    277 		 * Fires before sites are retrieved.
    278 		 *
    279 		 * @since 4.6.0
    280 		 *
    281 		 * @param WP_Site_Query $this Current instance of WP_Site_Query (passed by reference).
    282 		 */
    283 		do_action_ref_array( 'pre_get_sites', array( &$this ) );
    284 
    285 		// Reparse query vars, in case they were modified in a 'pre_get_sites' callback.
    286 		$this->meta_query->parse_query_vars( $this->query_vars );
    287 		if ( ! empty( $this->meta_query->queries ) ) {
    288 			$this->meta_query_clauses = $this->meta_query->get_sql( 'blog', $wpdb->blogs, 'blog_id', $this );
    289 		}
    290 
    291 		$site_data = null;
    292 
    293 		/**
    294 		 * Filters the site data before the get_sites query takes place.
    295 		 *
    296 		 * Return a non-null value to bypass WordPress' default site queries.
    297 		 *
    298 		 * The expected return type from this filter depends on the value passed
    299 		 * in the request query vars:
    300 		 * - When `$this->query_vars['count']` is set, the filter should return
    301 		 *   the site count as an integer.
    302 		 * - When `'ids' === $this->query_vars['fields']`, the filter should return
    303 		 *   an array of site IDs.
    304 		 * - Otherwise the filter should return an array of WP_Site objects.
    305 		 *
    306 		 * Note that if the filter returns an array of site data, it will be assigned
    307 		 * to the `sites` property of the current WP_Site_Query instance.
    308 		 *
    309 		 * Filtering functions that require pagination information are encouraged to set
    310 		 * the `found_sites` and `max_num_pages` properties of the WP_Site_Query object,
    311 		 * passed to the filter by reference. If WP_Site_Query does not perform a database
    312 		 * query, it will not have enough information to generate these values itself.
    313 		 *
    314 		 * @since 5.2.0
    315 		 * @since 5.6.0 The returned array of site data is assigned to the `sites` property
    316 		 *              of the current WP_Site_Query instance.
    317 		 *
    318 		 * @param array|int|null $site_data Return an array of site data to short-circuit WP's site query,
    319 		 *                                  the site count as an integer if `$this->query_vars['count']` is set,
    320 		 *                                  or null to run the normal queries.
    321 		 * @param WP_Site_Query  $query     The WP_Site_Query instance, passed by reference.
    322 		 */
    323 		$site_data = apply_filters_ref_array( 'sites_pre_query', array( $site_data, &$this ) );
    324 
    325 		if ( null !== $site_data ) {
    326 			if ( is_array( $site_data ) && ! $this->query_vars['count'] ) {
    327 				$this->sites = $site_data;
    328 			}
    329 
    330 			return $site_data;
    331 		}
    332 
    333 		// $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
    334 		$_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
    335 
    336 		// Ignore the $fields argument as the queried result will be the same regardless.
    337 		unset( $_args['fields'] );
    338 
    339 		$key          = md5( serialize( $_args ) );
    340 		$last_changed = wp_cache_get_last_changed( 'sites' );
    341 
    342 		$cache_key   = "get_sites:$key:$last_changed";
    343 		$cache_value = wp_cache_get( $cache_key, 'sites' );
    344 
    345 		if ( false === $cache_value ) {
    346 			$site_ids = $this->get_site_ids();
    347 			if ( $site_ids ) {
    348 				$this->set_found_sites();
    349 			}
    350 
    351 			$cache_value = array(
    352 				'site_ids'    => $site_ids,
    353 				'found_sites' => $this->found_sites,
    354 			);
    355 			wp_cache_add( $cache_key, $cache_value, 'sites' );
    356 		} else {
    357 			$site_ids          = $cache_value['site_ids'];
    358 			$this->found_sites = $cache_value['found_sites'];
    359 		}
    360 
    361 		if ( $this->found_sites && $this->query_vars['number'] ) {
    362 			$this->max_num_pages = ceil( $this->found_sites / $this->query_vars['number'] );
    363 		}
    364 
    365 		// If querying for a count only, there's nothing more to do.
    366 		if ( $this->query_vars['count'] ) {
    367 			// $site_ids is actually a count in this case.
    368 			return (int) $site_ids;
    369 		}
    370 
    371 		$site_ids = array_map( 'intval', $site_ids );
    372 
    373 		if ( 'ids' === $this->query_vars['fields'] ) {
    374 			$this->sites = $site_ids;
    375 
    376 			return $this->sites;
    377 		}
    378 
    379 		// Prime site network caches.
    380 		if ( $this->query_vars['update_site_cache'] ) {
    381 			_prime_site_caches( $site_ids, $this->query_vars['update_site_meta_cache'] );
    382 		}
    383 
    384 		// Fetch full site objects from the primed cache.
    385 		$_sites = array();
    386 		foreach ( $site_ids as $site_id ) {
    387 			$_site = get_site( $site_id );
    388 			if ( $_site ) {
    389 				$_sites[] = $_site;
    390 			}
    391 		}
    392 
    393 		/**
    394 		 * Filters the site query results.
    395 		 *
    396 		 * @since 4.6.0
    397 		 *
    398 		 * @param WP_Site[]     $_sites An array of WP_Site objects.
    399 		 * @param WP_Site_Query $query  Current instance of WP_Site_Query (passed by reference).
    400 		 */
    401 		$_sites = apply_filters_ref_array( 'the_sites', array( $_sites, &$this ) );
    402 
    403 		// Convert to WP_Site instances.
    404 		$this->sites = array_map( 'get_site', $_sites );
    405 
    406 		return $this->sites;
    407 	}
    408 
    409 	/**
    410 	 * Used internally to get a list of site IDs matching the query vars.
    411 	 *
    412 	 * @since 4.6.0
    413 	 *
    414 	 * @global wpdb $wpdb WordPress database abstraction object.
    415 	 *
    416 	 * @return int|array A single count of site IDs if a count query. An array of site IDs if a full query.
    417 	 */
    418 	protected function get_site_ids() {
    419 		global $wpdb;
    420 
    421 		$order = $this->parse_order( $this->query_vars['order'] );
    422 
    423 		// Disable ORDER BY with 'none', an empty array, or boolean false.
    424 		if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
    425 			$orderby = '';
    426 		} elseif ( ! empty( $this->query_vars['orderby'] ) ) {
    427 			$ordersby = is_array( $this->query_vars['orderby'] ) ?
    428 				$this->query_vars['orderby'] :
    429 				preg_split( '/[,\s]/', $this->query_vars['orderby'] );
    430 
    431 			$orderby_array = array();
    432 			foreach ( $ordersby as $_key => $_value ) {
    433 				if ( ! $_value ) {
    434 					continue;
    435 				}
    436 
    437 				if ( is_int( $_key ) ) {
    438 					$_orderby = $_value;
    439 					$_order   = $order;
    440 				} else {
    441 					$_orderby = $_key;
    442 					$_order   = $_value;
    443 				}
    444 
    445 				$parsed = $this->parse_orderby( $_orderby );
    446 
    447 				if ( ! $parsed ) {
    448 					continue;
    449 				}
    450 
    451 				if ( 'site__in' === $_orderby || 'network__in' === $_orderby ) {
    452 					$orderby_array[] = $parsed;
    453 					continue;
    454 				}
    455 
    456 				$orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
    457 			}
    458 
    459 			$orderby = implode( ', ', $orderby_array );
    460 		} else {
    461 			$orderby = "{$wpdb->blogs}.blog_id $order";
    462 		}
    463 
    464 		$number = absint( $this->query_vars['number'] );
    465 		$offset = absint( $this->query_vars['offset'] );
    466 		$limits = '';
    467 
    468 		if ( ! empty( $number ) ) {
    469 			if ( $offset ) {
    470 				$limits = 'LIMIT ' . $offset . ',' . $number;
    471 			} else {
    472 				$limits = 'LIMIT ' . $number;
    473 			}
    474 		}
    475 
    476 		if ( $this->query_vars['count'] ) {
    477 			$fields = 'COUNT(*)';
    478 		} else {
    479 			$fields = "{$wpdb->blogs}.blog_id";
    480 		}
    481 
    482 		// Parse site IDs for an IN clause.
    483 		$site_id = absint( $this->query_vars['ID'] );
    484 		if ( ! empty( $site_id ) ) {
    485 			$this->sql_clauses['where']['ID'] = $wpdb->prepare( "{$wpdb->blogs}.blog_id = %d", $site_id );
    486 		}
    487 
    488 		// Parse site IDs for an IN clause.
    489 		if ( ! empty( $this->query_vars['site__in'] ) ) {
    490 			$this->sql_clauses['where']['site__in'] = "{$wpdb->blogs}.blog_id IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['site__in'] ) ) . ' )';
    491 		}
    492 
    493 		// Parse site IDs for a NOT IN clause.
    494 		if ( ! empty( $this->query_vars['site__not_in'] ) ) {
    495 			$this->sql_clauses['where']['site__not_in'] = "{$wpdb->blogs}.blog_id NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['site__not_in'] ) ) . ' )';
    496 		}
    497 
    498 		$network_id = absint( $this->query_vars['network_id'] );
    499 
    500 		if ( ! empty( $network_id ) ) {
    501 			$this->sql_clauses['where']['network_id'] = $wpdb->prepare( 'site_id = %d', $network_id );
    502 		}
    503 
    504 		// Parse site network IDs for an IN clause.
    505 		if ( ! empty( $this->query_vars['network__in'] ) ) {
    506 			$this->sql_clauses['where']['network__in'] = 'site_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['network__in'] ) ) . ' )';
    507 		}
    508 
    509 		// Parse site network IDs for a NOT IN clause.
    510 		if ( ! empty( $this->query_vars['network__not_in'] ) ) {
    511 			$this->sql_clauses['where']['network__not_in'] = 'site_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['network__not_in'] ) ) . ' )';
    512 		}
    513 
    514 		if ( ! empty( $this->query_vars['domain'] ) ) {
    515 			$this->sql_clauses['where']['domain'] = $wpdb->prepare( 'domain = %s', $this->query_vars['domain'] );
    516 		}
    517 
    518 		// Parse site domain for an IN clause.
    519 		if ( is_array( $this->query_vars['domain__in'] ) ) {
    520 			$this->sql_clauses['where']['domain__in'] = "domain IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__in'] ) ) . "' )";
    521 		}
    522 
    523 		// Parse site domain for a NOT IN clause.
    524 		if ( is_array( $this->query_vars['domain__not_in'] ) ) {
    525 			$this->sql_clauses['where']['domain__not_in'] = "domain NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__not_in'] ) ) . "' )";
    526 		}
    527 
    528 		if ( ! empty( $this->query_vars['path'] ) ) {
    529 			$this->sql_clauses['where']['path'] = $wpdb->prepare( 'path = %s', $this->query_vars['path'] );
    530 		}
    531 
    532 		// Parse site path for an IN clause.
    533 		if ( is_array( $this->query_vars['path__in'] ) ) {
    534 			$this->sql_clauses['where']['path__in'] = "path IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__in'] ) ) . "' )";
    535 		}
    536 
    537 		// Parse site path for a NOT IN clause.
    538 		if ( is_array( $this->query_vars['path__not_in'] ) ) {
    539 			$this->sql_clauses['where']['path__not_in'] = "path NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__not_in'] ) ) . "' )";
    540 		}
    541 
    542 		if ( is_numeric( $this->query_vars['archived'] ) ) {
    543 			$archived                               = absint( $this->query_vars['archived'] );
    544 			$this->sql_clauses['where']['archived'] = $wpdb->prepare( 'archived = %s ', absint( $archived ) );
    545 		}
    546 
    547 		if ( is_numeric( $this->query_vars['mature'] ) ) {
    548 			$mature                               = absint( $this->query_vars['mature'] );
    549 			$this->sql_clauses['where']['mature'] = $wpdb->prepare( 'mature = %d ', $mature );
    550 		}
    551 
    552 		if ( is_numeric( $this->query_vars['spam'] ) ) {
    553 			$spam                               = absint( $this->query_vars['spam'] );
    554 			$this->sql_clauses['where']['spam'] = $wpdb->prepare( 'spam = %d ', $spam );
    555 		}
    556 
    557 		if ( is_numeric( $this->query_vars['deleted'] ) ) {
    558 			$deleted                               = absint( $this->query_vars['deleted'] );
    559 			$this->sql_clauses['where']['deleted'] = $wpdb->prepare( 'deleted = %d ', $deleted );
    560 		}
    561 
    562 		if ( is_numeric( $this->query_vars['public'] ) ) {
    563 			$public                               = absint( $this->query_vars['public'] );
    564 			$this->sql_clauses['where']['public'] = $wpdb->prepare( 'public = %d ', $public );
    565 		}
    566 
    567 		if ( is_numeric( $this->query_vars['lang_id'] ) ) {
    568 			$lang_id                               = absint( $this->query_vars['lang_id'] );
    569 			$this->sql_clauses['where']['lang_id'] = $wpdb->prepare( 'lang_id = %d ', $lang_id );
    570 		}
    571 
    572 		// Parse site language IDs for an IN clause.
    573 		if ( ! empty( $this->query_vars['lang__in'] ) ) {
    574 			$this->sql_clauses['where']['lang__in'] = 'lang_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['lang__in'] ) ) . ' )';
    575 		}
    576 
    577 		// Parse site language IDs for a NOT IN clause.
    578 		if ( ! empty( $this->query_vars['lang__not_in'] ) ) {
    579 			$this->sql_clauses['where']['lang__not_in'] = 'lang_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['lang__not_in'] ) ) . ' )';
    580 		}
    581 
    582 		// Falsey search strings are ignored.
    583 		if ( strlen( $this->query_vars['search'] ) ) {
    584 			$search_columns = array();
    585 
    586 			if ( $this->query_vars['search_columns'] ) {
    587 				$search_columns = array_intersect( $this->query_vars['search_columns'], array( 'domain', 'path' ) );
    588 			}
    589 
    590 			if ( ! $search_columns ) {
    591 				$search_columns = array( 'domain', 'path' );
    592 			}
    593 
    594 			/**
    595 			 * Filters the columns to search in a WP_Site_Query search.
    596 			 *
    597 			 * The default columns include 'domain' and 'path.
    598 			 *
    599 			 * @since 4.6.0
    600 			 *
    601 			 * @param string[]      $search_columns Array of column names to be searched.
    602 			 * @param string        $search         Text being searched.
    603 			 * @param WP_Site_Query $query          The current WP_Site_Query instance.
    604 			 */
    605 			$search_columns = apply_filters( 'site_search_columns', $search_columns, $this->query_vars['search'], $this );
    606 
    607 			$this->sql_clauses['where']['search'] = $this->get_search_sql( $this->query_vars['search'], $search_columns );
    608 		}
    609 
    610 		$date_query = $this->query_vars['date_query'];
    611 		if ( ! empty( $date_query ) && is_array( $date_query ) ) {
    612 			$this->date_query                         = new WP_Date_Query( $date_query, 'registered' );
    613 			$this->sql_clauses['where']['date_query'] = preg_replace( '/^\s*AND\s*/', '', $this->date_query->get_sql() );
    614 		}
    615 
    616 		$join    = '';
    617 		$groupby = '';
    618 
    619 		if ( ! empty( $this->meta_query_clauses ) ) {
    620 			$join .= $this->meta_query_clauses['join'];
    621 
    622 			// Strip leading 'AND'.
    623 			$this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $this->meta_query_clauses['where'] );
    624 
    625 			if ( ! $this->query_vars['count'] ) {
    626 				$groupby = "{$wpdb->blogs}.blog_id";
    627 			}
    628 		}
    629 
    630 		$where = implode( ' AND ', $this->sql_clauses['where'] );
    631 
    632 		$pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
    633 
    634 		/**
    635 		 * Filters the site query clauses.
    636 		 *
    637 		 * @since 4.6.0
    638 		 *
    639 		 * @param string[]      $pieces An associative array of site query clauses.
    640 		 * @param WP_Site_Query $query  Current instance of WP_Site_Query (passed by reference).
    641 		 */
    642 		$clauses = apply_filters_ref_array( 'sites_clauses', array( compact( $pieces ), &$this ) );
    643 
    644 		$fields  = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
    645 		$join    = isset( $clauses['join'] ) ? $clauses['join'] : '';
    646 		$where   = isset( $clauses['where'] ) ? $clauses['where'] : '';
    647 		$orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
    648 		$limits  = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
    649 		$groupby = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
    650 
    651 		if ( $where ) {
    652 			$where = 'WHERE ' . $where;
    653 		}
    654 
    655 		if ( $groupby ) {
    656 			$groupby = 'GROUP BY ' . $groupby;
    657 		}
    658 
    659 		if ( $orderby ) {
    660 			$orderby = "ORDER BY $orderby";
    661 		}
    662 
    663 		$found_rows = '';
    664 		if ( ! $this->query_vars['no_found_rows'] ) {
    665 			$found_rows = 'SQL_CALC_FOUND_ROWS';
    666 		}
    667 
    668 		$this->sql_clauses['select']  = "SELECT $found_rows $fields";
    669 		$this->sql_clauses['from']    = "FROM $wpdb->blogs $join";
    670 		$this->sql_clauses['groupby'] = $groupby;
    671 		$this->sql_clauses['orderby'] = $orderby;
    672 		$this->sql_clauses['limits']  = $limits;
    673 
    674 		$this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
    675 
    676 		if ( $this->query_vars['count'] ) {
    677 			return (int) $wpdb->get_var( $this->request );
    678 		}
    679 
    680 		$site_ids = $wpdb->get_col( $this->request );
    681 
    682 		return array_map( 'intval', $site_ids );
    683 	}
    684 
    685 	/**
    686 	 * Populates found_sites and max_num_pages properties for the current query
    687 	 * if the limit clause was used.
    688 	 *
    689 	 * @since 4.6.0
    690 	 *
    691 	 * @global wpdb $wpdb WordPress database abstraction object.
    692 	 */
    693 	private function set_found_sites() {
    694 		global $wpdb;
    695 
    696 		if ( $this->query_vars['number'] && ! $this->query_vars['no_found_rows'] ) {
    697 			/**
    698 			 * Filters the query used to retrieve found site count.
    699 			 *
    700 			 * @since 4.6.0
    701 			 *
    702 			 * @param string        $found_sites_query SQL query. Default 'SELECT FOUND_ROWS()'.
    703 			 * @param WP_Site_Query $site_query        The `WP_Site_Query` instance.
    704 			 */
    705 			$found_sites_query = apply_filters( 'found_sites_query', 'SELECT FOUND_ROWS()', $this );
    706 
    707 			$this->found_sites = (int) $wpdb->get_var( $found_sites_query );
    708 		}
    709 	}
    710 
    711 	/**
    712 	 * Used internally to generate an SQL string for searching across multiple columns.
    713 	 *
    714 	 * @since 4.6.0
    715 	 *
    716 	 * @global wpdb $wpdb WordPress database abstraction object.
    717 	 *
    718 	 * @param string   $string  Search string.
    719 	 * @param string[] $columns Array of columns to search.
    720 	 * @return string Search SQL.
    721 	 */
    722 	protected function get_search_sql( $string, $columns ) {
    723 		global $wpdb;
    724 
    725 		if ( false !== strpos( $string, '*' ) ) {
    726 			$like = '%' . implode( '%', array_map( array( $wpdb, 'esc_like' ), explode( '*', $string ) ) ) . '%';
    727 		} else {
    728 			$like = '%' . $wpdb->esc_like( $string ) . '%';
    729 		}
    730 
    731 		$searches = array();
    732 		foreach ( $columns as $column ) {
    733 			$searches[] = $wpdb->prepare( "$column LIKE %s", $like );
    734 		}
    735 
    736 		return '(' . implode( ' OR ', $searches ) . ')';
    737 	}
    738 
    739 	/**
    740 	 * Parses and sanitizes 'orderby' keys passed to the site query.
    741 	 *
    742 	 * @since 4.6.0
    743 	 *
    744 	 * @global wpdb $wpdb WordPress database abstraction object.
    745 	 *
    746 	 * @param string $orderby Alias for the field to order by.
    747 	 * @return string|false Value to used in the ORDER clause. False otherwise.
    748 	 */
    749 	protected function parse_orderby( $orderby ) {
    750 		global $wpdb;
    751 
    752 		$parsed = false;
    753 
    754 		switch ( $orderby ) {
    755 			case 'site__in':
    756 				$site__in = implode( ',', array_map( 'absint', $this->query_vars['site__in'] ) );
    757 				$parsed   = "FIELD( {$wpdb->blogs}.blog_id, $site__in )";
    758 				break;
    759 			case 'network__in':
    760 				$network__in = implode( ',', array_map( 'absint', $this->query_vars['network__in'] ) );
    761 				$parsed      = "FIELD( {$wpdb->blogs}.site_id, $network__in )";
    762 				break;
    763 			case 'domain':
    764 			case 'last_updated':
    765 			case 'path':
    766 			case 'registered':
    767 				$parsed = $orderby;
    768 				break;
    769 			case 'network_id':
    770 				$parsed = 'site_id';
    771 				break;
    772 			case 'domain_length':
    773 				$parsed = 'CHAR_LENGTH(domain)';
    774 				break;
    775 			case 'path_length':
    776 				$parsed = 'CHAR_LENGTH(path)';
    777 				break;
    778 			case 'id':
    779 				$parsed = "{$wpdb->blogs}.blog_id";
    780 				break;
    781 		}
    782 
    783 		if ( ! empty( $parsed ) || empty( $this->meta_query_clauses ) ) {
    784 			return $parsed;
    785 		}
    786 
    787 		$meta_clauses = $this->meta_query->get_clauses();
    788 		if ( empty( $meta_clauses ) ) {
    789 			return $parsed;
    790 		}
    791 
    792 		$primary_meta_query = reset( $meta_clauses );
    793 		if ( ! empty( $primary_meta_query['key'] ) && $primary_meta_query['key'] === $orderby ) {
    794 			$orderby = 'meta_value';
    795 		}
    796 
    797 		switch ( $orderby ) {
    798 			case 'meta_value':
    799 				if ( ! empty( $primary_meta_query['type'] ) ) {
    800 					$parsed = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
    801 				} else {
    802 					$parsed = "{$primary_meta_query['alias']}.meta_value";
    803 				}
    804 				break;
    805 			case 'meta_value_num':
    806 				$parsed = "{$primary_meta_query['alias']}.meta_value+0";
    807 				break;
    808 			default:
    809 				if ( isset( $meta_clauses[ $orderby ] ) ) {
    810 					$meta_clause = $meta_clauses[ $orderby ];
    811 					$parsed      = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
    812 				}
    813 		}
    814 
    815 		return $parsed;
    816 	}
    817 
    818 	/**
    819 	 * Parses an 'order' query variable and cast it to 'ASC' or 'DESC' as necessary.
    820 	 *
    821 	 * @since 4.6.0
    822 	 *
    823 	 * @param string $order The 'order' query variable.
    824 	 * @return string The sanitized 'order' query variable.
    825 	 */
    826 	protected function parse_order( $order ) {
    827 		if ( ! is_string( $order ) || empty( $order ) ) {
    828 			return 'ASC';
    829 		}
    830 
    831 		if ( 'ASC' === strtoupper( $order ) ) {
    832 			return 'ASC';
    833 		} else {
    834 			return 'DESC';
    835 		}
    836 	}
    837 }