run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄class-wp-site-query.php
1<?php
2/**
3 * Site API: WP_Site_Query class
4 *
5 * @package WordPress
6 * @subpackage Sites
7 * @since 4.6.0
8 */
9
10/**
11 * Core class used for querying sites.
12 *
13 * @since 4.6.0
14 *
15 * @see WP_Site_Query::__construct() for accepted arguments.
16 */
17#[AllowDynamicProperties]
18class WP_Site_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 * Metadata query container.
45 *
46 * @since 5.1.0
47 * @var WP_Meta_Query
48 */
49 public $meta_query = false;
50
51 /**
52 * Metadata query clauses.
53 *
54 * @since 5.1.0
55 * @var array
56 */
57 protected $meta_query_clauses;
58
59 /**
60 * Date query container.
61 *
62 * @since 4.6.0
63 * @var WP_Date_Query A date query instance.
64 */
65 public $date_query = false;
66
67 /**
68 * Query vars set by the user.
69 *
70 * @since 4.6.0
71 * @var array
72 */
73 public $query_vars;
74
75 /**
76 * Default values for query vars.
77 *
78 * @since 4.6.0
79 * @var array
80 */
81 public $query_var_defaults;
82
83 /**
84 * List of sites located by the query.
85 *
86 * @since 4.6.0
87 * @var array
88 */
89 public $sites;
90
91 /**
92 * The amount of found sites for the current query.
93 *
94 * @since 4.6.0
95 * @var int
96 */
97 public $found_sites = 0;
98
99 /**
100 * The number of pages.
101 *
102 * @since 4.6.0
103 * @var int
104 */
105 public $max_num_pages = 0;
106
107 /**
108 * Sets up the site query, based on the query vars passed.
109 *
110 * @since 4.6.0
111 * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
112 * @since 5.1.0 Introduced the 'update_site_meta_cache', 'meta_query', 'meta_key',
113 * 'meta_compare_key', 'meta_value', 'meta_type', and 'meta_compare' parameters.
114 * @since 5.3.0 Introduced the 'meta_type_key' parameter.
115 *
116 * @param string|array $query {
117 * Optional. Array or query string of site query parameters. Default empty.
118 *
119 * @type int[] $site__in Array of site IDs to include. Default empty.
120 * @type int[] $site__not_in Array of site IDs to exclude. Default empty.
121 * @type bool $count Whether to return a site count (true) or array of site objects.
122 * Default false.
123 * @type array $date_query Date query clauses to limit sites by. See WP_Date_Query.
124 * Default null.
125 * @type string $fields Site fields to return. Accepts 'ids' (returns an array of site IDs)
126 * or empty (returns an array of complete site objects). Default empty.
127 * @type int $ID A site ID to only return that site. Default empty.
128 * @type int $number Maximum number of sites to retrieve. Default 100.
129 * @type int $offset Number of sites to offset the query. Used to build LIMIT clause.
130 * Default 0.
131 * @type bool $no_found_rows Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
132 * @type string|array $orderby Site status or array of statuses. Accepts:
133 * - 'id'
134 * - 'domain'
135 * - 'path'
136 * - 'network_id'
137 * - 'last_updated'
138 * - 'registered'
139 * - 'domain_length'
140 * - 'path_length'
141 * - 'site__in'
142 * - 'network__in'
143 * - 'deleted'
144 * - 'mature'
145 * - 'spam'
146 * - 'archived'
147 * - 'public'
148 * - false, an empty array, or 'none' to disable `ORDER BY` clause.
149 * Default 'id'.
150 * @type string $order How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
151 * @type int $network_id Limit results to those affiliated with a given network ID. If 0,
152 * include all networks. Default 0.
153 * @type int[] $network__in Array of network IDs to include affiliated sites for. Default empty.
154 * @type int[] $network__not_in Array of network IDs to exclude affiliated sites for. Default empty.
155 * @type string $domain Limit results to those affiliated with a given domain. Default empty.
156 * @type string[] $domain__in Array of domains to include affiliated sites for. Default empty.
157 * @type string[] $domain__not_in Array of domains to exclude affiliated sites for. Default empty.
158 * @type string $path Limit results to those affiliated with a given path. Default empty.
159 * @type string[] $path__in Array of paths to include affiliated sites for. Default empty.
160 * @type string[] $path__not_in Array of paths to exclude affiliated sites for. Default empty.
161 * @type int $public Limit results to public sites. Accepts 1 or 0. Default empty.
162 * @type int $archived Limit results to archived sites. Accepts 1 or 0. Default empty.
163 * @type int $mature Limit results to mature sites. Accepts 1 or 0. Default empty.
164 * @type int $spam Limit results to spam sites. Accepts 1 or 0. Default empty.
165 * @type int $deleted Limit results to deleted sites. Accepts 1 or 0. Default empty.
166 * @type int $lang_id Limit results to a language ID. Default empty.
167 * @type string[] $lang__in Array of language IDs to include affiliated sites for. Default empty.
168 * @type string[] $lang__not_in Array of language IDs to exclude affiliated sites for. Default empty.
169 * @type string $search Search term(s) to retrieve matching sites for. Default empty.
170 * @type string[] $search_columns Array of column names to be searched. Accepts 'domain' and 'path'.
171 * Default empty array.
172 * @type bool $update_site_cache Whether to prime the cache for found sites. Default true.
173 * @type bool $update_site_meta_cache Whether to prime the metadata cache for found sites. Default true.
174 * @type string|string[] $meta_key Meta key or keys to filter by.
175 * @type string|string[] $meta_value Meta value or values to filter by.
176 * @type string $meta_compare MySQL operator used for comparing the meta value.
177 * See WP_Meta_Query::__construct() for accepted values and default value.
178 * @type string $meta_compare_key MySQL operator used for comparing the meta key.
179 * See WP_Meta_Query::__construct() for accepted values and default value.
180 * @type string $meta_type MySQL data type that the meta_value column will be CAST to for comparisons.
181 * See WP_Meta_Query::__construct() for accepted values and default value.
182 * @type string $meta_type_key MySQL data type that the meta_key column will be CAST to for comparisons.
183 * See WP_Meta_Query::__construct() for accepted values and default value.
184 * @type array $meta_query An associative array of WP_Meta_Query arguments.
185 * See WP_Meta_Query::__construct() for accepted values.
186 * }
187 */
188 public function __construct( $query = '' ) {
189 $this->query_var_defaults = array(
190 'fields' => '',
191 'ID' => '',
192 'site__in' => '',
193 'site__not_in' => '',
194 'number' => 100,
195 'offset' => '',
196 'no_found_rows' => true,
197 'orderby' => 'id',
198 'order' => 'ASC',
199 'network_id' => 0,
200 'network__in' => '',
201 'network__not_in' => '',
202 'domain' => '',
203 'domain__in' => '',
204 'domain__not_in' => '',
205 'path' => '',
206 'path__in' => '',
207 'path__not_in' => '',
208 'public' => null,
209 'archived' => null,
210 'mature' => null,
211 'spam' => null,
212 'deleted' => null,
213 'lang_id' => null,
214 'lang__in' => '',
215 'lang__not_in' => '',
216 'search' => '',
217 'search_columns' => array(),
218 'count' => false,
219 'date_query' => null, // See WP_Date_Query.
220 'update_site_cache' => true,
221 'update_site_meta_cache' => true,
222 'meta_query' => '',
223 'meta_key' => '',
224 'meta_value' => '',
225 'meta_type' => '',
226 'meta_compare' => '',
227 );
228
229 if ( ! empty( $query ) ) {
230 $this->query( $query );
231 }
232 }
233
234 /**
235 * Parses arguments passed to the site query with default query parameters.
236 *
237 * @since 4.6.0
238 *
239 * @see WP_Site_Query::__construct()
240 *
241 * @param string|array $query Array or string of WP_Site_Query arguments. See WP_Site_Query::__construct().
242 */
243 public function parse_query( $query = '' ) {
244 if ( empty( $query ) ) {
245 $query = $this->query_vars;
246 }
247
248 $this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
249
250 /**
251 * Fires after the site query vars have been parsed.
252 *
253 * @since 4.6.0
254 *
255 * @param WP_Site_Query $query The WP_Site_Query instance (passed by reference).
256 */
257 do_action_ref_array( 'parse_site_query', array( &$this ) );
258 }
259
260 /**
261 * Sets up the WordPress query for retrieving sites.
262 *
263 * @since 4.6.0
264 *
265 * @param string|array $query Array or URL query string of parameters.
266 * @return WP_Site[]|int[]|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids',
267 * or the number of sites when 'count' is passed as a query var.
268 */
269 public function query( $query ) {
270 $this->query_vars = wp_parse_args( $query );
271
272 return $this->get_sites();
273 }
274
275 /**
276 * Retrieves a list of sites matching the query vars.
277 *
278 * @since 4.6.0
279 *
280 * @global wpdb $wpdb WordPress database abstraction object.
281 *
282 * @return WP_Site[]|int[]|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids',
283 * or the number of sites when 'count' is passed as a query var.
284 */
285 public function get_sites() {
286 global $wpdb;
287
288 $this->parse_query();
289
290 // Parse meta query.
291 $this->meta_query = new WP_Meta_Query();
292 $this->meta_query->parse_query_vars( $this->query_vars );
293
294 /**
295 * Fires before sites are retrieved.
296 *
297 * @since 4.6.0
298 *
299 * @param WP_Site_Query $query Current instance of WP_Site_Query (passed by reference).
300 */
301 do_action_ref_array( 'pre_get_sites', array( &$this ) );
302
303 // Reparse query vars, in case they were modified in a 'pre_get_sites' callback.
304 $this->meta_query->parse_query_vars( $this->query_vars );
305 if ( ! empty( $this->meta_query->queries ) ) {
306 $this->meta_query_clauses = $this->meta_query->get_sql( 'blog', $wpdb->blogs, 'blog_id', $this );
307 }
308
309 $site_data = null;
310
311 /**
312 * Filters the site data before the get_sites query takes place.
313 *
314 * Return a non-null value to bypass WordPress' default site queries.
315 *
316 * The expected return type from this filter depends on the value passed
317 * in the request query vars:
318 * - When `$this->query_vars['count']` is set, the filter should return
319 * the site count as an integer.
320 * - When `'ids' === $this->query_vars['fields']`, the filter should return
321 * an array of site IDs.
322 * - Otherwise the filter should return an array of WP_Site objects.
323 *
324 * Note that if the filter returns an array of site data, it will be assigned
325 * to the `sites` property of the current WP_Site_Query instance.
326 *
327 * Filtering functions that require pagination information are encouraged to set
328 * the `found_sites` and `max_num_pages` properties of the WP_Site_Query object,
329 * passed to the filter by reference. If WP_Site_Query does not perform a database
330 * query, it will not have enough information to generate these values itself.
331 *
332 * @since 5.2.0
333 * @since 5.6.0 The returned array of site data is assigned to the `sites` property
334 * of the current WP_Site_Query instance.
335 *
336 * @param WP_Site[]|int[]|int|null $site_data Return an array of site data to short-circuit WP's site query,
337 * the site count as an integer if `$this->query_vars['count']` is set,
338 * or null to run the normal queries.
339 * @param WP_Site_Query $query The WP_Site_Query instance, passed by reference.
340 */
341 $site_data = apply_filters_ref_array( 'sites_pre_query', array( $site_data, &$this ) );
342
343 if ( null !== $site_data ) {
344 if ( is_array( $site_data ) && ! $this->query_vars['count'] ) {
345 $this->sites = $site_data;
346 }
347
348 return $site_data;
349 }
350
351 // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
352 $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
353
354 // Ignore the $fields, $update_site_cache, $update_site_meta_cache argument as the queried result will be the same regardless.
355 unset( $_args['fields'], $_args['update_site_cache'], $_args['update_site_meta_cache'] );
356
357 $key = md5( serialize( $_args ) );
358 $last_changed = wp_cache_get_last_changed( 'sites' );
359
360 $cache_key = "get_sites:$key";
361 $cache_value = wp_cache_get_salted( $cache_key, 'site-queries', $last_changed );
362
363 if ( false === $cache_value ) {
364 $site_ids = $this->get_site_ids();
365 if ( $site_ids ) {
366 $this->set_found_sites();
367 }
368
369 $cache_value = array(
370 'site_ids' => $site_ids,
371 'found_sites' => $this->found_sites,
372 );
373 wp_cache_set_salted( $cache_key, $cache_value, 'site-queries', $last_changed );
374 } else {
375 $site_ids = $cache_value['site_ids'];
376 $this->found_sites = $cache_value['found_sites'];
377 }
378
379 if ( $this->found_sites && $this->query_vars['number'] ) {
380 $this->max_num_pages = (int) ceil( $this->found_sites / $this->query_vars['number'] );
381 }
382
383 // If querying for a count only, there's nothing more to do.
384 if ( $this->query_vars['count'] ) {
385 // $site_ids is actually a count in this case.
386 return (int) $site_ids;
387 }
388
389 $site_ids = array_map( 'intval', $site_ids );
390
391 if ( $this->query_vars['update_site_meta_cache'] ) {
392 wp_lazyload_site_meta( $site_ids );
393 }
394
395 if ( 'ids' === $this->query_vars['fields'] ) {
396 $this->sites = $site_ids;
397
398 return $this->sites;
399 }
400
401 // Prime site network caches.
402 if ( $this->query_vars['update_site_cache'] ) {
403 _prime_site_caches( $site_ids, false );
404 }
405
406 // Fetch full site objects from the primed cache.
407 $_sites = array();
408 foreach ( $site_ids as $site_id ) {
409 $_site = get_site( $site_id );
410 if ( $_site ) {
411 $_sites[] = $_site;
412 }
413 }
414
415 /**
416 * Filters the site query results.
417 *
418 * @since 4.6.0
419 *
420 * @param WP_Site[] $_sites An array of WP_Site objects.
421 * @param WP_Site_Query $query Current instance of WP_Site_Query (passed by reference).
422 */
423 $_sites = apply_filters_ref_array( 'the_sites', array( $_sites, &$this ) );
424
425 // Convert to WP_Site instances.
426 $this->sites = array_map( 'get_site', $_sites );
427
428 return $this->sites;
429 }
430
431 /**
432 * Used internally to get a list of site IDs matching the query vars.
433 *
434 * @since 4.6.0
435 *
436 * @global wpdb $wpdb WordPress database abstraction object.
437 *
438 * @return int|array A single count of site IDs if a count query. An array of site IDs if a full query.
439 */
440 protected function get_site_ids() {
441 global $wpdb;
442
443 $order = $this->parse_order( $this->query_vars['order'] );
444
445 // Disable ORDER BY with 'none', an empty array, or boolean false.
446 if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
447 $orderby = '';
448 } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
449 $ordersby = is_array( $this->query_vars['orderby'] ) ?
450 $this->query_vars['orderby'] :
451 preg_split( '/[,\s]/', $this->query_vars['orderby'] );
452
453 $orderby_array = array();
454 foreach ( $ordersby as $_key => $_value ) {
455 if ( ! $_value ) {
456 continue;
457 }
458
459 if ( is_int( $_key ) ) {
460 $_orderby = $_value;
461 $_order = $order;
462 } else {
463 $_orderby = $_key;
464 $_order = $_value;
465 }
466
467 $parsed = $this->parse_orderby( $_orderby );
468
469 if ( ! $parsed ) {
470 continue;
471 }
472
473 if ( 'site__in' === $_orderby || 'network__in' === $_orderby ) {
474 $orderby_array[] = $parsed;
475 continue;
476 }
477
478 $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
479 }
480
481 $orderby = implode( ', ', $orderby_array );
482 } else {
483 $orderby = "{$wpdb->blogs}.blog_id $order";
484 }
485
486 $number = absint( $this->query_vars['number'] );
487 $offset = absint( $this->query_vars['offset'] );
488 $limits = '';
489
490 if ( ! empty( $number ) ) {
491 if ( $offset ) {
492 $limits = 'LIMIT ' . $offset . ',' . $number;
493 } else {
494 $limits = 'LIMIT ' . $number;
495 }
496 }
497
498 if ( $this->query_vars['count'] ) {
499 $fields = 'COUNT(*)';
500 } else {
501 $fields = "{$wpdb->blogs}.blog_id";
502 }
503
504 // Parse site IDs for an IN clause.
505 $site_id = absint( $this->query_vars['ID'] );
506 if ( ! empty( $site_id ) ) {
507 $this->sql_clauses['where']['ID'] = $wpdb->prepare( "{$wpdb->blogs}.blog_id = %d", $site_id );
508 }
509
510 // Parse site IDs for an IN clause.
511 if ( ! empty( $this->query_vars['site__in'] ) ) {
512 $this->sql_clauses['where']['site__in'] = "{$wpdb->blogs}.blog_id IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['site__in'] ) ) . ' )';
513 }
514
515 // Parse site IDs for a NOT IN clause.
516 if ( ! empty( $this->query_vars['site__not_in'] ) ) {
517 $this->sql_clauses['where']['site__not_in'] = "{$wpdb->blogs}.blog_id NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['site__not_in'] ) ) . ' )';
518 }
519
520 $network_id = absint( $this->query_vars['network_id'] );
521
522 if ( ! empty( $network_id ) ) {
523 $this->sql_clauses['where']['network_id'] = $wpdb->prepare( 'site_id = %d', $network_id );
524 }
525
526 // Parse site network IDs for an IN clause.
527 if ( ! empty( $this->query_vars['network__in'] ) ) {
528 $this->sql_clauses['where']['network__in'] = 'site_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['network__in'] ) ) . ' )';
529 }
530
531 // Parse site network IDs for a NOT IN clause.
532 if ( ! empty( $this->query_vars['network__not_in'] ) ) {
533 $this->sql_clauses['where']['network__not_in'] = 'site_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['network__not_in'] ) ) . ' )';
534 }
535
536 if ( ! empty( $this->query_vars['domain'] ) ) {
537 $this->sql_clauses['where']['domain'] = $wpdb->prepare( 'domain = %s', $this->query_vars['domain'] );
538 }
539
540 // Parse site domain for an IN clause.
541 if ( is_array( $this->query_vars['domain__in'] ) ) {
542 $this->sql_clauses['where']['domain__in'] = "domain IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__in'] ) ) . "' )";
543 }
544
545 // Parse site domain for a NOT IN clause.
546 if ( is_array( $this->query_vars['domain__not_in'] ) ) {
547 $this->sql_clauses['where']['domain__not_in'] = "domain NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['domain__not_in'] ) ) . "' )";
548 }
549
550 if ( ! empty( $this->query_vars['path'] ) ) {
551 $this->sql_clauses['where']['path'] = $wpdb->prepare( 'path = %s', $this->query_vars['path'] );
552 }
553
554 // Parse site path for an IN clause.
555 if ( is_array( $this->query_vars['path__in'] ) ) {
556 $this->sql_clauses['where']['path__in'] = "path IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__in'] ) ) . "' )";
557 }
558
559 // Parse site path for a NOT IN clause.
560 if ( is_array( $this->query_vars['path__not_in'] ) ) {
561 $this->sql_clauses['where']['path__not_in'] = "path NOT IN ( '" . implode( "', '", $wpdb->_escape( $this->query_vars['path__not_in'] ) ) . "' )";
562 }
563
564 if ( is_numeric( $this->query_vars['archived'] ) ) {
565 $archived = absint( $this->query_vars['archived'] );
566 $this->sql_clauses['where']['archived'] = $wpdb->prepare( 'archived = %s ', absint( $archived ) );
567 }
568
569 if ( is_numeric( $this->query_vars['mature'] ) ) {
570 $mature = absint( $this->query_vars['mature'] );
571 $this->sql_clauses['where']['mature'] = $wpdb->prepare( 'mature = %d ', $mature );
572 }
573
574 if ( is_numeric( $this->query_vars['spam'] ) ) {
575 $spam = absint( $this->query_vars['spam'] );
576 $this->sql_clauses['where']['spam'] = $wpdb->prepare( 'spam = %d ', $spam );
577 }
578
579 if ( is_numeric( $this->query_vars['deleted'] ) ) {
580 $deleted = absint( $this->query_vars['deleted'] );
581 $this->sql_clauses['where']['deleted'] = $wpdb->prepare( 'deleted = %d ', $deleted );
582 }
583
584 if ( is_numeric( $this->query_vars['public'] ) ) {
585 $public = absint( $this->query_vars['public'] );
586 $this->sql_clauses['where']['public'] = $wpdb->prepare( 'public = %d ', $public );
587 }
588
589 if ( is_numeric( $this->query_vars['lang_id'] ) ) {
590 $lang_id = absint( $this->query_vars['lang_id'] );
591 $this->sql_clauses['where']['lang_id'] = $wpdb->prepare( 'lang_id = %d ', $lang_id );
592 }
593
594 // Parse site language IDs for an IN clause.
595 if ( ! empty( $this->query_vars['lang__in'] ) ) {
596 $this->sql_clauses['where']['lang__in'] = 'lang_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['lang__in'] ) ) . ' )';
597 }
598
599 // Parse site language IDs for a NOT IN clause.
600 if ( ! empty( $this->query_vars['lang__not_in'] ) ) {
601 $this->sql_clauses['where']['lang__not_in'] = 'lang_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['lang__not_in'] ) ) . ' )';
602 }
603
604 // Falsey search strings are ignored.
605 if ( strlen( $this->query_vars['search'] ) ) {
606 $search_columns = array();
607
608 if ( $this->query_vars['search_columns'] ) {
609 $search_columns = array_intersect( $this->query_vars['search_columns'], array( 'domain', 'path' ) );
610 }
611
612 if ( ! $search_columns ) {
613 $search_columns = array( 'domain', 'path' );
614 }
615
616 /**
617 * Filters the columns to search in a WP_Site_Query search.
618 *
619 * The default columns include 'domain' and 'path.
620 *
621 * @since 4.6.0
622 *
623 * @param string[] $search_columns Array of column names to be searched.
624 * @param string $search Text being searched.
625 * @param WP_Site_Query $query The current WP_Site_Query instance.
626 */
627 $search_columns = apply_filters( 'site_search_columns', $search_columns, $this->query_vars['search'], $this );
628
629 $this->sql_clauses['where']['search'] = $this->get_search_sql( $this->query_vars['search'], $search_columns );
630 }
631
632 $date_query = $this->query_vars['date_query'];
633 if ( ! empty( $date_query ) && is_array( $date_query ) ) {
634 $this->date_query = new WP_Date_Query( $date_query, 'registered' );
635
636 // Strip leading 'AND'.
637 $this->sql_clauses['where']['date_query'] = preg_replace( '/^\s*AND\s*/', '', $this->date_query->get_sql() );
638 }
639
640 $join = '';
641 $groupby = '';
642
643 if ( ! empty( $this->meta_query_clauses ) ) {
644 $join .= $this->meta_query_clauses['join'];
645
646 // Strip leading 'AND'.
647 $this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $this->meta_query_clauses['where'] );
648
649 if ( ! $this->query_vars['count'] ) {
650 $groupby = "{$wpdb->blogs}.blog_id";
651 }
652 }
653
654 $where = implode( ' AND ', $this->sql_clauses['where'] );
655
656 $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
657
658 /**
659 * Filters the site query clauses.
660 *
661 * @since 4.6.0
662 *
663 * @param string[] $clauses {
664 * Associative array of the clauses for the query.
665 *
666 * @type string $fields The SELECT clause of the query.
667 * @type string $join The JOIN clause of the query.
668 * @type string $where The WHERE clause of the query.
669 * @type string $orderby The ORDER BY clause of the query.
670 * @type string $limits The LIMIT clause of the query.
671 * @type string $groupby The GROUP BY clause of the query.
672 * }
673 * @param WP_Site_Query $query Current instance of WP_Site_Query (passed by reference).
674 */
675 $clauses = apply_filters_ref_array( 'sites_clauses', array( compact( $pieces ), &$this ) );
676
677 $fields = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
678 $join = isset( $clauses['join'] ) ? $clauses['join'] : '';
679 $where = isset( $clauses['where'] ) ? $clauses['where'] : '';
680 $orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
681 $limits = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
682 $groupby = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
683
684 if ( $where ) {
685 $where = 'WHERE ' . $where;
686 }
687
688 if ( $groupby ) {
689 $groupby = 'GROUP BY ' . $groupby;
690 }
691
692 if ( $orderby ) {
693 $orderby = "ORDER BY $orderby";
694 }
695
696 $found_rows = '';
697 if ( ! $this->query_vars['no_found_rows'] ) {
698 $found_rows = 'SQL_CALC_FOUND_ROWS';
699 }
700
701 $this->sql_clauses['select'] = "SELECT $found_rows $fields";
702 $this->sql_clauses['from'] = "FROM $wpdb->blogs $join";
703 $this->sql_clauses['groupby'] = $groupby;
704 $this->sql_clauses['orderby'] = $orderby;
705 $this->sql_clauses['limits'] = $limits;
706
707 // Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
708 $this->request =
709 "{$this->sql_clauses['select']}
710 {$this->sql_clauses['from']}
711 {$where}
712 {$this->sql_clauses['groupby']}
713 {$this->sql_clauses['orderby']}
714 {$this->sql_clauses['limits']}";
715
716 if ( $this->query_vars['count'] ) {
717 return (int) $wpdb->get_var( $this->request );
718 }
719
720 $site_ids = $wpdb->get_col( $this->request );
721
722 return array_map( 'intval', $site_ids );
723 }
724
725 /**
726 * Populates found_sites and max_num_pages properties for the current query
727 * if the limit clause was used.
728 *
729 * @since 4.6.0
730 *
731 * @global wpdb $wpdb WordPress database abstraction object.
732 */
733 private function set_found_sites() {
734 global $wpdb;
735
736 if ( $this->query_vars['number'] && ! $this->query_vars['no_found_rows'] ) {
737 /**
738 * Filters the query used to retrieve found site count.
739 *
740 * @since 4.6.0
741 *
742 * @param string $found_sites_query SQL query. Default 'SELECT FOUND_ROWS()'.
743 * @param WP_Site_Query $site_query The `WP_Site_Query` instance.
744 */
745 $found_sites_query = apply_filters( 'found_sites_query', 'SELECT FOUND_ROWS()', $this );
746
747 $this->found_sites = (int) $wpdb->get_var( $found_sites_query );
748 }
749 }
750
751 /**
752 * Used internally to generate an SQL string for searching across multiple columns.
753 *
754 * @since 4.6.0
755 *
756 * @global wpdb $wpdb WordPress database abstraction object.
757 *
758 * @param string $search Search string.
759 * @param string[] $columns Array of columns to search.
760 * @return string Search SQL.
761 */
762 protected function get_search_sql( $search, $columns ) {
763 global $wpdb;
764
765 if ( str_contains( $search, '*' ) ) {
766 $like = '%' . implode( '%', array_map( array( $wpdb, 'esc_like' ), explode( '*', $search ) ) ) . '%';
767 } else {
768 $like = '%' . $wpdb->esc_like( $search ) . '%';
769 }
770
771 $searches = array();
772 foreach ( $columns as $column ) {
773 $searches[] = $wpdb->prepare( "$column LIKE %s", $like );
774 }
775
776 return '(' . implode( ' OR ', $searches ) . ')';
777 }
778
779 /**
780 * Parses and sanitizes 'orderby' keys passed to the site query.
781 *
782 * @since 4.6.0
783 *
784 * @global wpdb $wpdb WordPress database abstraction object.
785 *
786 * @param string $orderby Alias for the field to order by.
787 * @return string|false Value to used in the ORDER clause. False otherwise.
788 */
789 protected function parse_orderby( $orderby ) {
790 global $wpdb;
791
792 $parsed = false;
793
794 switch ( $orderby ) {
795 case 'site__in':
796 $site__in = implode( ',', array_map( 'absint', $this->query_vars['site__in'] ) );
797 $parsed = "FIELD( {$wpdb->blogs}.blog_id, $site__in )";
798 break;
799 case 'network__in':
800 $network__in = implode( ',', array_map( 'absint', $this->query_vars['network__in'] ) );
801 $parsed = "FIELD( {$wpdb->blogs}.site_id, $network__in )";
802 break;
803 case 'domain':
804 case 'last_updated':
805 case 'path':
806 case 'registered':
807 case 'deleted':
808 case 'spam':
809 case 'mature':
810 case 'archived':
811 case 'public':
812 $parsed = $orderby;
813 break;
814 case 'network_id':
815 $parsed = 'site_id';
816 break;
817 case 'domain_length':
818 $parsed = 'CHAR_LENGTH(domain)';
819 break;
820 case 'path_length':
821 $parsed = 'CHAR_LENGTH(path)';
822 break;
823 case 'id':
824 $parsed = "{$wpdb->blogs}.blog_id";
825 break;
826 }
827
828 if ( ! empty( $parsed ) || empty( $this->meta_query_clauses ) ) {
829 return $parsed;
830 }
831
832 $meta_clauses = $this->meta_query->get_clauses();
833 if ( empty( $meta_clauses ) ) {
834 return $parsed;
835 }
836
837 $primary_meta_query = reset( $meta_clauses );
838 if ( ! empty( $primary_meta_query['key'] ) && $primary_meta_query['key'] === $orderby ) {
839 $orderby = 'meta_value';
840 }
841
842 switch ( $orderby ) {
843 case 'meta_value':
844 if ( ! empty( $primary_meta_query['type'] ) ) {
845 $parsed = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
846 } else {
847 $parsed = "{$primary_meta_query['alias']}.meta_value";
848 }
849 break;
850 case 'meta_value_num':
851 $parsed = "{$primary_meta_query['alias']}.meta_value+0";
852 break;
853 default:
854 if ( isset( $meta_clauses[ $orderby ] ) ) {
855 $meta_clause = $meta_clauses[ $orderby ];
856 $parsed = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
857 }
858 }
859
860 return $parsed;
861 }
862
863 /**
864 * Parses an 'order' query variable and cast it to 'ASC' or 'DESC' as necessary.
865 *
866 * @since 4.6.0
867 *
868 * @param string $order The 'order' query variable.
869 * @return string The sanitized 'order' query variable.
870 */
871 protected function parse_order( $order ) {
872 if ( ! is_string( $order ) || empty( $order ) ) {
873 return 'ASC';
874 }
875
876 if ( 'ASC' === strtoupper( $order ) ) {
877 return 'ASC';
878 } else {
879 return 'DESC';
880 }
881 }
882}
883