Completed
Push — master ( a7cd2a...eabd6c )
by Stephen
38:42
created

WP_Network::get_instance()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 22
Ratio 100 %
Metric Value
dl 22
loc 22
rs 8.6737
cc 5
eloc 12
nc 4
nop 1
1
<?php
2
/**
3
 * Network API: WP_Network class
4
 *
5
 * @package WordPress
6
 * @subpackage Multisite
7
 * @since 4.4.0
8
 */
9
10
/**
11
 * Core class used for interacting with a multisite network.
12
 *
13
 * This class is used during load to populate the `$current_site` global and
14
 * setup the current network.
15
 *
16
 * This class is most useful in WordPress multi-network installations where the
17
 * ability to interact with any network of sites is required.
18
 *
19
 * @since 4.4.0
20
 */
21
class WP_Network {
22
23
	/**
24
	 * Network ID.
25
	 *
26
	 * A numeric string, for compatibility reasons.
27
	 *
28
	 * @since 4.4.0
29
	 * @access public
30
	 * @var string
31
	 */
32
	public $id;
33
34
	/**
35
	 * Domain of the network.
36
	 *
37
	 * @since 4.4.0
38
	 * @access public
39
	 * @var string
40
	 */
41
	public $domain = '';
42
43
	/**
44
	 * Path of the network.
45
	 *
46
	 * @since 4.4.0
47
	 * @access public
48
	 * @var string
49
	 */
50
	public $path = '';
51
52
	/**
53
	 * The ID of the network's main site.
54
	 *
55
	 * Named "blog" vs. "site" for legacy reasons. A main site is mapped to
56
	 * the network when the network is created.
57
	 *
58
	 * A numeric string, for compatibility reasons.
59
	 *
60
	 * @since 4.4.0
61
	 * @access public
62
	 * @var string
63
	 */
64
	public $blog_id = 0;
65
66
	/**
67
	 * Domain used to set cookies for this network.
68
	 *
69
	 * @since 4.4.0
70
	 * @access public
71
	 * @var string
72
	 */
73
	public $cookie_domain = '';
74
75
	/**
76
	 * Name of this network.
77
	 *
78
	 * Named "site" vs. "network" for legacy reasons.
79
	 *
80
	 * @since 4.4.0
81
	 * @access public
82
	 * @var string
83
	 */
84
	public $site_name = '';
85
86
	/**
87
	 * Retrieve a network from the database by its ID.
88
	 *
89
	 * @since 4.4.0
90
	 * @access public
91
	 *
92
	 * @global wpdb $wpdb WordPress database abstraction object.
93
	 *
94
	 * @param int $network_id The ID of the network to retrieve.
95
	 * @return WP_Network|bool The network's object if found. False if not.
96
	 */
97 View Code Duplication
	public static function get_instance( $network_id ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
		global $wpdb;
99
100
		$network_id = (int) $network_id;
101
		if ( ! $network_id ) {
102
			return false;
103
		}
104
105
		$_network = wp_cache_get( $network_id, 'networks' );
106
107
		if ( ! $_network ) {
108
			$_network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d LIMIT 1", $network_id ) );
109
110
			if ( empty( $_network ) || is_wp_error( $_network ) ) {
111
				return false;
112
			}
113
114
			wp_cache_add( $network_id, $_network, 'networks' );
115
		}
116
117
		return new WP_Network( $_network );
118
	}
119
120
	/**
121
	 * Create a new WP_Network object.
122
	 *
123
	 * Will populate object properties from the object provided and assign other
124
	 * default properties based on that information.
125
	 *
126
	 * @since 4.4.0
127
	 * @access public
128
	 *
129
	 * @param WP_Network|object $network A network object.
130
	 */
131
	public function __construct( $network ) {
132
		foreach( get_object_vars( $network ) as $key => $value ) {
133
			$this->$key = $value;
134
		}
135
136
		$this->_set_site_name();
137
		$this->_set_cookie_domain();
138
	}
139
140
	/**
141
	 * Set the site name assigned to the network if one has not been populated.
142
	 *
143
	 * @since 4.4.0
144
	 * @access private
145
	 */
146
	private function _set_site_name() {
147
		if ( ! empty( $this->site_name ) ) {
148
			return;
149
		}
150
151
		$default = ucfirst( $this->domain );
152
		$this->site_name = get_network_option( $this->id, 'site_name', $default );
153
	}
154
155
	/**
156
	 * Set the cookie domain based on the network domain if one has
157
	 * not been populated.
158
	 *
159
	 * @todo What if the domain of the network doesn't match the current site?
160
	 *
161
	 * @since 4.4.0
162
	 * @access private
163
	 */
164
	private function _set_cookie_domain() {
165
		if ( ! empty( $this->cookie_domain ) ) {
166
			return;
167
		}
168
169
		$this->cookie_domain = $this->domain;
170
		if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) {
171
			$this->cookie_domain = substr( $this->cookie_domain, 4 );
172
		}
173
	}
174
175
	/**
176
	 * Retrieve the closest matching network for a domain and path.
177
	 *
178
	 * This will not necessarily return an exact match for a domain and path. Instead, it
179
	 * breaks the domain and path into pieces that are then used to match the closest
180
	 * possibility from a query.
181
	 *
182
	 * The intent of this method is to match a network during bootstrap for a
183
	 * requested site address.
184
	 *
185
	 * @since 4.4.0
186
	 * @access public
187
	 * @static
188
	 *
189
	 * @param string   $domain   Domain to check.
190
	 * @param string   $path     Path to check.
191
	 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
192
	 * @return WP_Network|bool Network object if successful. False when no network is found.
193
	 */
194
	public static function get_by_path( $domain = '', $path = '', $segments = null ) {
195
		global $wpdb;
196
197
		$domains = array( $domain );
198
		$pieces  = explode( '.', $domain );
199
200
		/*
201
		 * It's possible one domain to search is 'com', but it might as well
202
		 * be 'localhost' or some other locally mapped domain.
203
		 */
204
		while ( array_shift( $pieces ) ) {
205
			if ( ! empty( $pieces ) ) {
206
				$domains[] = implode( '.', $pieces );
207
			}
208
		}
209
210
		/*
211
		 * If we've gotten to this function during normal execution, there is
212
		 * more than one network installed. At this point, who knows how many
213
		 * we have. Attempt to optimize for the situation where networks are
214
		 * only domains, thus meaning paths never need to be considered.
215
		 *
216
		 * This is a very basic optimization; anything further could have
217
		 * drawbacks depending on the setup, so this is best done per-install.
218
		 */
219
		$using_paths = true;
220 View Code Duplication
		if ( wp_using_ext_object_cache() ) {
221
			$using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
222
			if ( false === $using_paths ) {
223
				$using_paths = (int) $wpdb->get_var( "SELECT id FROM {$wpdb->site} WHERE path <> '/' LIMIT 1" );
224
				wp_cache_add( 'networks_have_paths', $using_paths, 'site-options'  );
225
			}
226
		}
227
228
		$paths = array();
229
		if ( $using_paths ) {
230
			$path_segments = array_filter( explode( '/', trim( $path, '/' ) ) );
231
232
			/**
233
			 * Filter the number of path segments to consider when searching for a site.
234
			 *
235
			 * @since 3.9.0
236
			 *
237
			 * @param int|null $segments The number of path segments to consider. WordPress by default looks at
238
			 *                           one path segment. The function default of null only makes sense when you
239
			 *                           know the requested path should match a network.
240
			 * @param string   $domain   The requested domain.
241
			 * @param string   $path     The requested path, in full.
242
			 */
243
			$segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
244
245 View Code Duplication
			if ( ( null !== $segments ) && count( $path_segments ) > $segments ) {
246
				$path_segments = array_slice( $path_segments, 0, $segments );
247
			}
248
249 View Code Duplication
			while ( count( $path_segments ) ) {
250
				$paths[] = '/' . implode( '/', $path_segments ) . '/';
251
				array_pop( $path_segments );
252
			}
253
254
			$paths[] = '/';
255
		}
256
257
		/**
258
		 * Determine a network by its domain and path.
259
		 *
260
		 * This allows one to short-circuit the default logic, perhaps by
261
		 * replacing it with a routine that is more optimal for your setup.
262
		 *
263
		 * Return null to avoid the short-circuit. Return false if no network
264
		 * can be found at the requested domain and path. Otherwise, return
265
		 * an object from wp_get_network().
266
		 *
267
		 * @since 3.9.0
268
		 *
269
		 * @param null|bool|object $network  Network value to return by path.
270
		 * @param string           $domain   The requested domain.
271
		 * @param string           $path     The requested path, in full.
272
		 * @param int|null         $segments The suggested number of paths to consult.
273
		 *                                   Default null, meaning the entire path was to be consulted.
274
		 * @param array            $paths    The paths to search for, based on $path and $segments.
275
		 */
276
		$pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
277
		if ( null !== $pre ) {
278
			return $pre;
279
		}
280
281
		// @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
282
		// We already have paths covered. What about how far domains should be drilled down (including www)?
283
284
		$search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
285
286
		if ( ! $using_paths ) {
287
			$network = $wpdb->get_row( "
288
				SELECT * FROM {$wpdb->site}
289
				WHERE domain IN ({$search_domains})
290
				ORDER BY CHAR_LENGTH(domain)
291
				DESC LIMIT 1
292
			" );
293
294
			if ( ! empty( $network ) && ! is_wp_error( $network ) ) {
295
				return new WP_Network( $network );
296
			}
297
298
			return false;
299
300
		} else {
301
			$search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
302
			$networks = $wpdb->get_results( "
303
				SELECT * FROM {$wpdb->site}
304
				WHERE domain IN ({$search_domains})
305
				AND path IN ({$search_paths})
306
				ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC
307
			" );
308
		}
309
310
		/*
311
		 * Domains are sorted by length of domain, then by length of path.
312
		 * The domain must match for the path to be considered. Otherwise,
313
		 * a network with the path of / will suffice.
314
		 */
315
		$found = false;
316
		foreach ( $networks as $network ) {
317
			if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) {
318
				if ( in_array( $network->path, $paths, true ) ) {
319
					$found = true;
320
					break;
321
				}
322
			}
323
			if ( $network->path === '/' ) {
324
				$found = true;
325
				break;
326
			}
327
		}
328
329
		if ( true === $found ) {
330
			return new WP_Network( $network );
0 ignored issues
show
Bug introduced by
The variable $network seems to be defined by a foreach iteration on line 316. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
331
		}
332
333
		return false;
334
	}
335
}
336