[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> class-wp-network-query.php (source)

   1  <?php
   2  /**
   3   * Network API: WP_Network_Query class
   4   *
   5   * @package WordPress
   6   * @subpackage Multisite
   7   * @since 4.6.0
   8   */
   9  
  10  /**
  11   * Core class used for querying networks.
  12   *
  13   * @since 4.6.0
  14   *
  15   * @see WP_Network_Query::__construct() for accepted arguments.
  16   */
  17  class WP_Network_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       * Query vars set by the user.
  44       *
  45       * @since 4.6.0
  46       * @var array
  47       */
  48      public $query_vars;
  49  
  50      /**
  51       * Default values for query vars.
  52       *
  53       * @since 4.6.0
  54       * @var array
  55       */
  56      public $query_var_defaults;
  57  
  58      /**
  59       * List of networks located by the query.
  60       *
  61       * @since 4.6.0
  62       * @var array
  63       */
  64      public $networks;
  65  
  66      /**
  67       * The amount of found networks for the current query.
  68       *
  69       * @since 4.6.0
  70       * @var int
  71       */
  72      public $found_networks = 0;
  73  
  74      /**
  75       * The number of pages.
  76       *
  77       * @since 4.6.0
  78       * @var int
  79       */
  80      public $max_num_pages = 0;
  81  
  82      /**
  83       * Constructor.
  84       *
  85       * Sets up the network query, based on the query vars passed.
  86       *
  87       * @since 4.6.0
  88       *
  89       * @param string|array $query {
  90       *     Optional. Array or query string of network query parameters. Default empty.
  91       *
  92       *     @type int[]        $network__in          Array of network IDs to include. Default empty.
  93       *     @type int[]        $network__not_in      Array of network IDs to exclude. Default empty.
  94       *     @type bool         $count                Whether to return a network count (true) or array of network objects.
  95       *                                              Default false.
  96       *     @type string       $fields               Network fields to return. Accepts 'ids' (returns an array of network IDs)
  97       *                                              or empty (returns an array of complete network objects). Default empty.
  98       *     @type int          $number               Maximum number of networks to retrieve. Default empty (no limit).
  99       *     @type int          $offset               Number of networks to offset the query. Used to build LIMIT clause.
 100       *                                              Default 0.
 101       *     @type bool         $no_found_rows        Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
 102       *     @type string|array $orderby              Network status or array of statuses. Accepts 'id', 'domain', 'path',
 103       *                                              'domain_length', 'path_length' and 'network__in'. Also accepts false,
 104       *                                              an empty array, or 'none' to disable `ORDER BY` clause. Default 'id'.
 105       *     @type string       $order                How to order retrieved networks. Accepts 'ASC', 'DESC'. Default 'ASC'.
 106       *     @type string       $domain               Limit results to those affiliated with a given domain. Default empty.
 107       *     @type string[]     $domain__in           Array of domains to include affiliated networks for. Default empty.
 108       *     @type string[]     $domain__not_in       Array of domains to exclude affiliated networks for. Default empty.
 109       *     @type string       $path                 Limit results to those affiliated with a given path. Default empty.
 110       *     @type string[]     $path__in             Array of paths to include affiliated networks for. Default empty.
 111       *     @type string[]     $path__not_in         Array of paths to exclude affiliated networks for. Default empty.
 112       *     @type string       $search               Search term(s) to retrieve matching networks for. Default empty.
 113       *     @type bool         $update_network_cache Whether to prime the cache for found networks. Default true.
 114       * }
 115       */
 116  	public function __construct( $query = '' ) {
 117          $this->query_var_defaults = array(
 118              'network__in'          => '',
 119              'network__not_in'      => '',
 120              'count'                => false,
 121              'fields'               => '',
 122              'number'               => '',
 123              'offset'               => '',
 124              'no_found_rows'        => true,
 125              'orderby'              => 'id',
 126              'order'                => 'ASC',
 127              'domain'               => '',
 128              'domain__in'           => '',
 129              'domain__not_in'       => '',
 130              'path'                 => '',
 131              'path__in'             => '',
 132              'path__not_in'         => '',
 133              'search'               => '',
 134              'update_network_cache' => true,
 135          );
 136  
 137          if ( ! empty( $query ) ) {
 138              $this->query( $query );
 139          }
 140      }
 141  
 142      /**
 143       * Parses arguments passed to the network query with default query parameters.
 144       *
 145       * @since 4.6.0
 146       *
 147       * @param string|array $query WP_Network_Query arguments. See WP_Network_Query::__construct()
 148       */
 149  	public function parse_query( $query = '' ) {
 150          if ( empty( $query ) ) {
 151              $query = $this->query_vars;
 152          }
 153  
 154          $this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
 155  
 156          /**
 157           * Fires after the network query vars have been parsed.
 158           *
 159           * @since 4.6.0
 160           *
 161           * @param WP_Network_Query $this The WP_Network_Query instance (passed by reference).
 162           */
 163          do_action_ref_array( 'parse_network_query', array( &$this ) );
 164      }
 165  
 166      /**
 167       * Sets up the WordPress query for retrieving networks.
 168       *
 169       * @since 4.6.0
 170       *
 171       * @param string|array $query Array or URL query string of parameters.
 172       * @return array|int List of WP_Network objects, a list of network IDs when 'fields' is set to 'ids',
 173       *                   or the number of networks when 'count' is passed as a query var.
 174       */
 175  	public function query( $query ) {
 176          $this->query_vars = wp_parse_args( $query );
 177          return $this->get_networks();
 178      }
 179  
 180      /**
 181       * Gets a list of networks matching the query vars.
 182       *
 183       * @since 4.6.0
 184       *
 185       * @return array|int List of WP_Network objects, a list of network IDs when 'fields' is set to 'ids',
 186       *                   or the number of networks when 'count' is passed as a query var.
 187       */
 188  	public function get_networks() {
 189          $this->parse_query();
 190  
 191          /**
 192           * Fires before networks are retrieved.
 193           *
 194           * @since 4.6.0
 195           *
 196           * @param WP_Network_Query $this Current instance of WP_Network_Query (passed by reference).
 197           */
 198          do_action_ref_array( 'pre_get_networks', array( &$this ) );
 199  
 200          $network_data = null;
 201  
 202          /**
 203           * Filters the network data before the query takes place.
 204           *
 205           * Return a non-null value to bypass WordPress' default network queries.
 206           *
 207           * The expected return type from this filter depends on the value passed
 208           * in the request query vars:
 209           * - When `$this->query_vars['count']` is set, the filter should return
 210           *   the network count as an integer.
 211           * - When `'ids' === $this->query_vars['fields']`, the filter should return
 212           *   an array of network IDs.
 213           * - Otherwise the filter should return an array of WP_Network objects.
 214           *
 215           * Note that if the filter returns an array of network data, it will be assigned
 216           * to the `networks` property of the current WP_Network_Query instance.
 217           *
 218           * Filtering functions that require pagination information are encouraged to set
 219           * the `found_networks` and `max_num_pages` properties of the WP_Network_Query object,
 220           * passed to the filter by reference. If WP_Network_Query does not perform a database
 221           * query, it will not have enough information to generate these values itself.
 222           *
 223           * @since 5.2.0
 224           * @since 5.6.0 The returned array of network data is assigned to the `networks` property
 225           *              of the current WP_Network_Query instance.
 226           *
 227           * @param array|int|null   $network_data Return an array of network data to short-circuit WP's network query,
 228           *                                       the network count as an integer if `$this->query_vars['count']` is set,
 229           *                                       or null to allow WP to run its normal queries.
 230           * @param WP_Network_Query $this         The WP_Network_Query instance, passed by reference.
 231           */
 232          $network_data = apply_filters_ref_array( 'networks_pre_query', array( $network_data, &$this ) );
 233  
 234          if ( null !== $network_data ) {
 235              if ( is_array( $network_data ) && ! $this->query_vars['count'] ) {
 236                  $this->networks = $network_data;
 237              }
 238  
 239              return $network_data;
 240          }
 241  
 242          // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
 243          $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
 244  
 245          // Ignore the $fields argument as the queried result will be the same regardless.
 246          unset( $_args['fields'] );
 247  
 248          $key          = md5( serialize( $_args ) );
 249          $last_changed = wp_cache_get_last_changed( 'networks' );
 250  
 251          $cache_key   = "get_network_ids:$key:$last_changed";
 252          $cache_value = wp_cache_get( $cache_key, 'networks' );
 253  
 254          if ( false === $cache_value ) {
 255              $network_ids = $this->get_network_ids();
 256              if ( $network_ids ) {
 257                  $this->set_found_networks();
 258              }
 259  
 260              $cache_value = array(
 261                  'network_ids'    => $network_ids,
 262                  'found_networks' => $this->found_networks,
 263              );
 264              wp_cache_add( $cache_key, $cache_value, 'networks' );
 265          } else {
 266              $network_ids          = $cache_value['network_ids'];
 267              $this->found_networks = $cache_value['found_networks'];
 268          }
 269  
 270          if ( $this->found_networks && $this->query_vars['number'] ) {
 271              $this->max_num_pages = ceil( $this->found_networks / $this->query_vars['number'] );
 272          }
 273  
 274          // If querying for a count only, there's nothing more to do.
 275          if ( $this->query_vars['count'] ) {
 276              // $network_ids is actually a count in this case.
 277              return (int) $network_ids;
 278          }
 279  
 280          $network_ids = array_map( 'intval', $network_ids );
 281  
 282          if ( 'ids' === $this->query_vars['fields'] ) {
 283              $this->networks = $network_ids;
 284              return $this->networks;
 285          }
 286  
 287          if ( $this->query_vars['update_network_cache'] ) {
 288              _prime_network_caches( $network_ids );
 289          }
 290  
 291          // Fetch full network objects from the primed cache.
 292          $_networks = array();
 293          foreach ( $network_ids as $network_id ) {
 294              $_network = get_network( $network_id );
 295              if ( $_network ) {
 296                  $_networks[] = $_network;
 297              }
 298          }
 299  
 300          /**
 301           * Filters the network query results.
 302           *
 303           * @since 4.6.0
 304           *
 305           * @param WP_Network[]     $_networks An array of WP_Network objects.
 306           * @param WP_Network_Query $this      Current instance of WP_Network_Query (passed by reference).
 307           */
 308          $_networks = apply_filters_ref_array( 'the_networks', array( $_networks, &$this ) );
 309  
 310          // Convert to WP_Network instances.
 311          $this->networks = array_map( 'get_network', $_networks );
 312  
 313          return $this->networks;
 314      }
 315  
 316      /**
 317       * Used internally to get a list of network IDs matching the query vars.
 318       *
 319       * @since 4.6.0
 320       *
 321       * @global wpdb $wpdb WordPress database abstraction object.
 322       *
 323       * @return int|array A single count of network IDs if a count query. An array of network IDs if a full query.
 324       */
 325  	protected function get_network_ids() {
 326          global $wpdb;
 327  
 328          $order = $this->parse_order( $this->query_vars['order'] );
 329  
 330          // Disable ORDER BY with 'none', an empty array, or boolean false.
 331          if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
 332              $orderby = '';
 333          } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
 334              $ordersby = is_array( $this->query_vars['orderby'] ) ?
 335                  $this->query_vars['orderby'] :
 336                  preg_split( '/[,\s]/', $this->query_vars['orderby'] );
 337  
 338              $orderby_array = array();
 339              foreach ( $ordersby as $_key => $_value ) {
 340                  if ( ! $_value ) {
 341                      continue;
 342                  }
 343  
 344                  if ( is_int( $_key ) ) {
 345                      $_orderby = $_value;
 346                      $_order   = $order;
 347                  } else {
 348                      $_orderby = $_key;
 349                      $_order   = $_value;
 350                  }
 351  
 352                  $parsed = $this->parse_orderby( $_orderby );
 353  
 354                  if ( ! $parsed ) {
 355                      continue;
 356                  }
 357  
 358                  if ( 'network__in' === $_orderby ) {
 359                      $orderby_array[] = $parsed;
 360                      continue;
 361                  }
 362  
 363                  $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
 364              }
 365  
 366              $orderby = implode( ', ', $orderby_array );
 367          } else {
 368              $orderby = "$wpdb->site.id $order";
 369          }
 370  
 371          $number = absint( $this->query_vars['number'] );
 372          $offset = absint( $this->query_vars['offset'] );
 373          $limits = '';
 374  
 375          if ( ! empty( $number ) ) {
 376              if ( $offset ) {
 377                  $limits = 'LIMIT ' . $offset . ',' . $number;
 378              } else {
 379                  $limits = 'LIMIT ' . $number;
 380              }
 381          }
 382  
 383          if ( $this->query_vars['count'] ) {
 384              $fields = 'COUNT(*)';
 385          } else {
 386              $fields = "$wpdb->site.id";
 387          }
 388  
 389          // Parse network IDs for an IN clause.
 390          if ( ! empty( $this->query_vars['network__in'] ) ) {
 391              $this->sql_clauses['where']['network__in'] = "$wpdb->site.id IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['network__in'] ) ) . ' )';
 392          }
 393  
 394          // Parse network IDs for a NOT IN clause.
 395          if ( ! empty( $this->query_vars['network__not_in'] ) ) {
 396              $this->sql_clauses['where']['network__not_in'] = "$wpdb->site.id NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['network__not_in'] ) ) . ' )';
 397          }
 398  
 399          if ( ! empty( $this->query_vars['domain'] ) ) {
 400              $this->sql_clauses['where']['domain'] = $wpdb->prepare( "$wpdb->site.domain = %s", $this->query_vars['domain'] );
 401          }
 402  
 403          // Parse network domain for an IN clause.
 404          if ( is_array( $this->query_vars['domain__in'] ) ) {
 405              $this->sql_clauses['where']['domain__in'] = "$wpdb->site.domain IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__in'] ) ) . "' )";
 406          }
 407  
 408          // Parse network domain for a NOT IN clause.
 409          if ( is_array( $this->query_vars['domain__not_in'] ) ) {
 410              $this->sql_clauses['where']['domain__not_in'] = "$wpdb->site.domain NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__not_in'] ) ) . "' )";
 411          }
 412  
 413          if ( ! empty( $this->query_vars['path'] ) ) {
 414              $this->sql_clauses['where']['path'] = $wpdb->prepare( "$wpdb->site.path = %s", $this->query_vars['path'] );
 415          }
 416  
 417          // Parse network path for an IN clause.
 418          if ( is_array( $this->query_vars['path__in'] ) ) {
 419              $this->sql_clauses['where']['path__in'] = "$wpdb->site.path IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__in'] ) ) . "' )";
 420          }
 421  
 422          // Parse network path for a NOT IN clause.
 423          if ( is_array( $this->query_vars['path__not_in'] ) ) {
 424              $this->sql_clauses['where']['path__not_in'] = "$wpdb->site.path NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__not_in'] ) ) . "' )";
 425          }
 426  
 427          // Falsey search strings are ignored.
 428          if ( strlen( $this->query_vars['search'] ) ) {
 429              $this->sql_clauses['where']['search'] = $this->get_search_sql(
 430                  $this->query_vars['search'],
 431                  array( "$wpdb->site.domain", "$wpdb->site.path" )
 432              );
 433          }
 434  
 435          $join = '';
 436  
 437          $where = implode( ' AND ', $this->sql_clauses['where'] );
 438  
 439          $groupby = '';
 440  
 441          $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
 442  
 443          /**
 444           * Filters the network query clauses.
 445           *
 446           * @since 4.6.0
 447           *
 448           * @param string[]         $pieces An associative array of network query clauses.
 449           * @param WP_Network_Query $this   Current instance of WP_Network_Query (passed by reference).
 450           */
 451          $clauses = apply_filters_ref_array( 'networks_clauses', array( compact( $pieces ), &$this ) );
 452  
 453          $fields  = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
 454          $join    = isset( $clauses['join'] ) ? $clauses['join'] : '';
 455          $where   = isset( $clauses['where'] ) ? $clauses['where'] : '';
 456          $orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
 457          $limits  = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
 458          $groupby = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
 459  
 460          if ( $where ) {
 461              $where = 'WHERE ' . $where;
 462          }
 463  
 464          if ( $groupby ) {
 465              $groupby = 'GROUP BY ' . $groupby;
 466          }
 467  
 468          if ( $orderby ) {
 469              $orderby = "ORDER BY $orderby";
 470          }
 471  
 472          $found_rows = '';
 473          if ( ! $this->query_vars['no_found_rows'] ) {
 474              $found_rows = 'SQL_CALC_FOUND_ROWS';
 475          }
 476  
 477          $this->sql_clauses['select']  = "SELECT $found_rows $fields";
 478          $this->sql_clauses['from']    = "FROM $wpdb->site $join";
 479          $this->sql_clauses['groupby'] = $groupby;
 480          $this->sql_clauses['orderby'] = $orderby;
 481          $this->sql_clauses['limits']  = $limits;
 482  
 483          $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
 484  
 485          if ( $this->query_vars['count'] ) {
 486              return (int) $wpdb->get_var( $this->request );
 487          }
 488  
 489          $network_ids = $wpdb->get_col( $this->request );
 490  
 491          return array_map( 'intval', $network_ids );
 492      }
 493  
 494      /**
 495       * Populates found_networks and max_num_pages properties for the current query
 496       * if the limit clause was used.
 497       *
 498       * @since 4.6.0
 499       *
 500       * @global wpdb $wpdb WordPress database abstraction object.
 501       */
 502  	private function set_found_networks() {
 503          global $wpdb;
 504  
 505          if ( $this->query_vars['number'] && ! $this->query_vars['no_found_rows'] ) {
 506              /**
 507               * Filters the query used to retrieve found network count.
 508               *
 509               * @since 4.6.0
 510               *
 511               * @param string           $found_networks_query SQL query. Default 'SELECT FOUND_ROWS()'.
 512               * @param WP_Network_Query $network_query        The `WP_Network_Query` instance.
 513               */
 514              $found_networks_query = apply_filters( 'found_networks_query', 'SELECT FOUND_ROWS()', $this );
 515  
 516              $this->found_networks = (int) $wpdb->get_var( $found_networks_query );
 517          }
 518      }
 519  
 520      /**
 521       * Used internally to generate an SQL string for searching across multiple columns.
 522       *
 523       * @since 4.6.0
 524       *
 525       * @global wpdb $wpdb WordPress database abstraction object.
 526       *
 527       * @param string   $string  Search string.
 528       * @param string[] $columns Array of columns to search.
 529       * @return string Search SQL.
 530       */
 531  	protected function get_search_sql( $string, $columns ) {
 532          global $wpdb;
 533  
 534          $like = '%' . $wpdb->esc_like( $string ) . '%';
 535  
 536          $searches = array();
 537          foreach ( $columns as $column ) {
 538              $searches[] = $wpdb->prepare( "$column LIKE %s", $like );
 539          }
 540  
 541          return '(' . implode( ' OR ', $searches ) . ')';
 542      }
 543  
 544      /**
 545       * Parses and sanitizes 'orderby' keys passed to the network query.
 546       *
 547       * @since 4.6.0
 548       *
 549       * @global wpdb $wpdb WordPress database abstraction object.
 550       *
 551       * @param string $orderby Alias for the field to order by.
 552       * @return string|false Value to used in the ORDER clause. False otherwise.
 553       */
 554  	protected function parse_orderby( $orderby ) {
 555          global $wpdb;
 556  
 557          $allowed_keys = array(
 558              'id',
 559              'domain',
 560              'path',
 561          );
 562  
 563          $parsed = false;
 564          if ( 'network__in' === $orderby ) {
 565              $network__in = implode( ',', array_map( 'absint', $this->query_vars['network__in'] ) );
 566              $parsed      = "FIELD( {$wpdb->site}.id, $network__in )";
 567          } elseif ( 'domain_length' === $orderby || 'path_length' === $orderby ) {
 568              $field  = substr( $orderby, 0, -7 );
 569              $parsed = "CHAR_LENGTH($wpdb->site.$field)";
 570          } elseif ( in_array( $orderby, $allowed_keys, true ) ) {
 571              $parsed = "$wpdb->site.$orderby";
 572          }
 573  
 574          return $parsed;
 575      }
 576  
 577      /**
 578       * Parses an 'order' query variable and cast it to 'ASC' or 'DESC' as necessary.
 579       *
 580       * @since 4.6.0
 581       *
 582       * @param string $order The 'order' query variable.
 583       * @return string The sanitized 'order' query variable.
 584       */
 585  	protected function parse_order( $order ) {
 586          if ( ! is_string( $order ) || empty( $order ) ) {
 587              return 'ASC';
 588          }
 589  
 590          if ( 'ASC' === strtoupper( $order ) ) {
 591              return 'ASC';
 592          } else {
 593              return 'DESC';
 594          }
 595      }
 596  }


Generated: Wed Nov 25 01:00:04 2020 Cross-referenced by PHPXref 0.7.1