[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

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


Generated : Tue Oct 20 08:20:01 2020 Cross-referenced by PHPXref