Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-network-query.php

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