[ 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 array        $network__in          Array of network IDs to include. Default empty.
  93       *     @type array        $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 array        $domain__in           Array of domains to include affiliated networks for. Default empty.
 108       *     @type array        $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 array        $path__in             Array of paths to include affiliated networks for. Default empty.
 111       *     @type array        $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           * Filter the network data before the query takes place.
 204           *
 205           * Return a non-null value to bypass WordPress's default network queries.
 206           *
 207           * The expected return type from this filter depends on the value passed in the request query_vars.
 208           * When `$this->query_vars['count']` is set, the filter should return the network count as an int.
 209           * When `'ids' === $this->query_vars['fields']`, the filter should return an array of network ids.
 210           * Otherwise the filter should return an array of WP_Network objects.
 211           *
 212           * @since 5.2.0
 213           *
 214           * @param array|null       $network_data Return an array of network data to short-circuit WP's network query,
 215           *                                       the network count as an integer if `$this->query_vars['count']` is set,
 216           *                                       or null to allow WP to run its normal queries.
 217           * @param WP_Network_Query $this         The WP_Network_Query instance, passed by reference.
 218           */
 219          $network_data = apply_filters_ref_array( 'networks_pre_query', array( $network_data, &$this ) );
 220  
 221          if ( null !== $network_data ) {
 222              return $network_data;
 223          }
 224  
 225          // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
 226          $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
 227  
 228          // Ignore the $fields argument as the queried result will be the same regardless.
 229          unset( $_args['fields'] );
 230  
 231          $key          = md5( serialize( $_args ) );
 232          $last_changed = wp_cache_get_last_changed( 'networks' );
 233  
 234          $cache_key   = "get_network_ids:$key:$last_changed";
 235          $cache_value = wp_cache_get( $cache_key, 'networks' );
 236  
 237          if ( false === $cache_value ) {
 238              $network_ids = $this->get_network_ids();
 239              if ( $network_ids ) {
 240                  $this->set_found_networks();
 241              }
 242  
 243              $cache_value = array(
 244                  'network_ids'    => $network_ids,
 245                  'found_networks' => $this->found_networks,
 246              );
 247              wp_cache_add( $cache_key, $cache_value, 'networks' );
 248          } else {
 249              $network_ids          = $cache_value['network_ids'];
 250              $this->found_networks = $cache_value['found_networks'];
 251          }
 252  
 253          if ( $this->found_networks && $this->query_vars['number'] ) {
 254              $this->max_num_pages = ceil( $this->found_networks / $this->query_vars['number'] );
 255          }
 256  
 257          // If querying for a count only, there's nothing more to do.
 258          if ( $this->query_vars['count'] ) {
 259              // $network_ids is actually a count in this case.
 260              return intval( $network_ids );
 261          }
 262  
 263          $network_ids = array_map( 'intval', $network_ids );
 264  
 265          if ( 'ids' == $this->query_vars['fields'] ) {
 266              $this->networks = $network_ids;
 267              return $this->networks;
 268          }
 269  
 270          if ( $this->query_vars['update_network_cache'] ) {
 271              _prime_network_caches( $network_ids );
 272          }
 273  
 274          // Fetch full network objects from the primed cache.
 275          $_networks = array();
 276          foreach ( $network_ids as $network_id ) {
 277              $_network = get_network( $network_id );
 278              if ( $_network ) {
 279                  $_networks[] = $_network;
 280              }
 281          }
 282  
 283          /**
 284           * Filters the network query results.
 285           *
 286           * @since 4.6.0
 287           *
 288           * @param WP_Network[]     $_networks An array of WP_Network objects.
 289           * @param WP_Network_Query $this      Current instance of WP_Network_Query (passed by reference).
 290           */
 291          $_networks = apply_filters_ref_array( 'the_networks', array( $_networks, &$this ) );
 292  
 293          // Convert to WP_Network instances
 294          $this->networks = array_map( 'get_network', $_networks );
 295  
 296          return $this->networks;
 297      }
 298  
 299      /**
 300       * Used internally to get a list of network IDs matching the query vars.
 301       *
 302       * @since 4.6.0
 303       *
 304       * @global wpdb $wpdb WordPress database abstraction object.
 305       *
 306       * @return int|array A single count of network IDs if a count query. An array of network IDs if a full query.
 307       */
 308  	protected function get_network_ids() {
 309          global $wpdb;
 310  
 311          $order = $this->parse_order( $this->query_vars['order'] );
 312  
 313          // Disable ORDER BY with 'none', an empty array, or boolean false.
 314          if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
 315              $orderby = '';
 316          } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
 317              $ordersby = is_array( $this->query_vars['orderby'] ) ?
 318                  $this->query_vars['orderby'] :
 319                  preg_split( '/[,\s]/', $this->query_vars['orderby'] );
 320  
 321              $orderby_array = array();
 322              foreach ( $ordersby as $_key => $_value ) {
 323                  if ( ! $_value ) {
 324                      continue;
 325                  }
 326  
 327                  if ( is_int( $_key ) ) {
 328                      $_orderby = $_value;
 329                      $_order   = $order;
 330                  } else {
 331                      $_orderby = $_key;
 332                      $_order   = $_value;
 333                  }
 334  
 335                  $parsed = $this->parse_orderby( $_orderby );
 336  
 337                  if ( ! $parsed ) {
 338                      continue;
 339                  }
 340  
 341                  if ( 'network__in' === $_orderby ) {
 342                      $orderby_array[] = $parsed;
 343                      continue;
 344                  }
 345  
 346                  $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
 347              }
 348  
 349              $orderby = implode( ', ', $orderby_array );
 350          } else {
 351              $orderby = "$wpdb->site.id $order";
 352          }
 353  
 354          $number = absint( $this->query_vars['number'] );
 355          $offset = absint( $this->query_vars['offset'] );
 356          $limits = '';
 357  
 358          if ( ! empty( $number ) ) {
 359              if ( $offset ) {
 360                  $limits = 'LIMIT ' . $offset . ',' . $number;
 361              } else {
 362                  $limits = 'LIMIT ' . $number;
 363              }
 364          }
 365  
 366          if ( $this->query_vars['count'] ) {
 367              $fields = 'COUNT(*)';
 368          } else {
 369              $fields = "$wpdb->site.id";
 370          }
 371  
 372          // Parse network IDs for an IN clause.
 373          if ( ! empty( $this->query_vars['network__in'] ) ) {
 374              $this->sql_clauses['where']['network__in'] = "$wpdb->site.id IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['network__in'] ) ) . ' )';
 375          }
 376  
 377          // Parse network IDs for a NOT IN clause.
 378          if ( ! empty( $this->query_vars['network__not_in'] ) ) {
 379              $this->sql_clauses['where']['network__not_in'] = "$wpdb->site.id NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['network__not_in'] ) ) . ' )';
 380          }
 381  
 382          if ( ! empty( $this->query_vars['domain'] ) ) {
 383              $this->sql_clauses['where']['domain'] = $wpdb->prepare( "$wpdb->site.domain = %s", $this->query_vars['domain'] );
 384          }
 385  
 386          // Parse network domain for an IN clause.
 387          if ( is_array( $this->query_vars['domain__in'] ) ) {
 388              $this->sql_clauses['where']['domain__in'] = "$wpdb->site.domain IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__in'] ) ) . "' )";
 389          }
 390  
 391          // Parse network domain for a NOT IN clause.
 392          if ( is_array( $this->query_vars['domain__not_in'] ) ) {
 393              $this->sql_clauses['where']['domain__not_in'] = "$wpdb->site.domain NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__not_in'] ) ) . "' )";
 394          }
 395  
 396          if ( ! empty( $this->query_vars['path'] ) ) {
 397              $this->sql_clauses['where']['path'] = $wpdb->prepare( "$wpdb->site.path = %s", $this->query_vars['path'] );
 398          }
 399  
 400          // Parse network path for an IN clause.
 401          if ( is_array( $this->query_vars['path__in'] ) ) {
 402              $this->sql_clauses['where']['path__in'] = "$wpdb->site.path IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__in'] ) ) . "' )";
 403          }
 404  
 405          // Parse network path for a NOT IN clause.
 406          if ( is_array( $this->query_vars['path__not_in'] ) ) {
 407              $this->sql_clauses['where']['path__not_in'] = "$wpdb->site.path NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__not_in'] ) ) . "' )";
 408          }
 409  
 410          // Falsey search strings are ignored.
 411          if ( strlen( $this->query_vars['search'] ) ) {
 412              $this->sql_clauses['where']['search'] = $this->get_search_sql(
 413                  $this->query_vars['search'],
 414                  array( "$wpdb->site.domain", "$wpdb->site.path" )
 415              );
 416          }
 417  
 418          $join = '';
 419  
 420          $where = implode( ' AND ', $this->sql_clauses['where'] );
 421  
 422          $groupby = '';
 423  
 424          $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
 425  
 426          /**
 427           * Filters the network query clauses.
 428           *
 429           * @since 4.6.0
 430           *
 431           * @param string[]         $pieces An associative array of network query clauses.
 432           * @param WP_Network_Query $this   Current instance of WP_Network_Query (passed by reference).
 433           */
 434          $clauses = apply_filters_ref_array( 'networks_clauses', array( compact( $pieces ), &$this ) );
 435  
 436          $fields  = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
 437          $join    = isset( $clauses['join'] ) ? $clauses['join'] : '';
 438          $where   = isset( $clauses['where'] ) ? $clauses['where'] : '';
 439          $orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
 440          $limits  = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
 441          $groupby = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
 442  
 443          if ( $where ) {
 444              $where = 'WHERE ' . $where;
 445          }
 446  
 447          if ( $groupby ) {
 448              $groupby = 'GROUP BY ' . $groupby;
 449          }
 450  
 451          if ( $orderby ) {
 452              $orderby = "ORDER BY $orderby";
 453          }
 454  
 455          $found_rows = '';
 456          if ( ! $this->query_vars['no_found_rows'] ) {
 457              $found_rows = 'SQL_CALC_FOUND_ROWS';
 458          }
 459  
 460          $this->sql_clauses['select']  = "SELECT $found_rows $fields";
 461          $this->sql_clauses['from']    = "FROM $wpdb->site $join";
 462          $this->sql_clauses['groupby'] = $groupby;
 463          $this->sql_clauses['orderby'] = $orderby;
 464          $this->sql_clauses['limits']  = $limits;
 465  
 466          $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
 467  
 468          if ( $this->query_vars['count'] ) {
 469              return intval( $wpdb->get_var( $this->request ) );
 470          }
 471  
 472          $network_ids = $wpdb->get_col( $this->request );
 473  
 474          return array_map( 'intval', $network_ids );
 475      }
 476  
 477      /**
 478       * Populates found_networks and max_num_pages properties for the current query
 479       * if the limit clause was used.
 480       *
 481       * @since 4.6.0
 482       *
 483       * @global wpdb $wpdb WordPress database abstraction object.
 484       */
 485  	private function set_found_networks() {
 486          global $wpdb;
 487  
 488          if ( $this->query_vars['number'] && ! $this->query_vars['no_found_rows'] ) {
 489              /**
 490               * Filters the query used to retrieve found network count.
 491               *
 492               * @since 4.6.0
 493               *
 494               * @param string           $found_networks_query SQL query. Default 'SELECT FOUND_ROWS()'.
 495               * @param WP_Network_Query $network_query        The `WP_Network_Query` instance.
 496               */
 497              $found_networks_query = apply_filters( 'found_networks_query', 'SELECT FOUND_ROWS()', $this );
 498  
 499              $this->found_networks = (int) $wpdb->get_var( $found_networks_query );
 500          }
 501      }
 502  
 503      /**
 504       * Used internally to generate an SQL string for searching across multiple columns.
 505       *
 506       * @since 4.6.0
 507       *
 508       * @global wpdb $wpdb WordPress database abstraction object.
 509       *
 510       * @param string   $string  Search string.
 511       * @param string[] $columns Array of columns to search.
 512       *
 513       * @return string Search SQL.
 514       */
 515  	protected function get_search_sql( $string, $columns ) {
 516          global $wpdb;
 517  
 518          $like = '%' . $wpdb->esc_like( $string ) . '%';
 519  
 520          $searches = array();
 521          foreach ( $columns as $column ) {
 522              $searches[] = $wpdb->prepare( "$column LIKE %s", $like );
 523          }
 524  
 525          return '(' . implode( ' OR ', $searches ) . ')';
 526      }
 527  
 528      /**
 529       * Parses and sanitizes 'orderby' keys passed to the network query.
 530       *
 531       * @since 4.6.0
 532       *
 533       * @global wpdb $wpdb WordPress database abstraction object.
 534       *
 535       * @param string $orderby Alias for the field to order by.
 536       * @return string|false Value to used in the ORDER clause. False otherwise.
 537       */
 538  	protected function parse_orderby( $orderby ) {
 539          global $wpdb;
 540  
 541          $allowed_keys = array(
 542              'id',
 543              'domain',
 544              'path',
 545          );
 546  
 547          $parsed = false;
 548          if ( $orderby == 'network__in' ) {
 549              $network__in = implode( ',', array_map( 'absint', $this->query_vars['network__in'] ) );
 550              $parsed      = "FIELD( {$wpdb->site}.id, $network__in )";
 551          } elseif ( $orderby == 'domain_length' || $orderby == 'path_length' ) {
 552              $field  = substr( $orderby, 0, -7 );
 553              $parsed = "CHAR_LENGTH($wpdb->site.$field)";
 554          } elseif ( in_array( $orderby, $allowed_keys ) ) {
 555              $parsed = "$wpdb->site.$orderby";
 556          }
 557  
 558          return $parsed;
 559      }
 560  
 561      /**
 562       * Parses an 'order' query variable and cast it to 'ASC' or 'DESC' as necessary.
 563       *
 564       * @since 4.6.0
 565       *
 566       * @param string $order The 'order' query variable.
 567       * @return string The sanitized 'order' query variable.
 568       */
 569  	protected function parse_order( $order ) {
 570          if ( ! is_string( $order ) || empty( $order ) ) {
 571              return 'ASC';
 572          }
 573  
 574          if ( 'ASC' === strtoupper( $order ) ) {
 575              return 'ASC';
 576          } else {
 577              return 'DESC';
 578          }
 579      }
 580  }


Generated: Fri Nov 15 01:00:03 2019 Cross-referenced by PHPXref 0.7.1