WP_Network   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 387
Duplicated Lines 12.66 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 49
loc 387
rs 8.3157
c 1
b 0
f 0
wmc 43
lcom 1
cbo 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
B get_instance() 22 22 5
A _set_cookie_domain() 0 10 3
A __get() 0 12 4
A __isset() 0 10 4
A __set() 13 13 4
F get_by_path() 14 141 19
A __construct() 0 8 2
A _set_site_name() 0 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WP_Network often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WP_Network, and based on these observations, apply Extract Interface, too.

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
 * @property int $id
22
 * @property int $site_id
23
 */
24
class WP_Network {
25
26
	/**
27
	 * Network ID.
28
	 *
29
	 * @since 4.4.0
30
	 * @since 4.6.0 Converted from public to private to explicitly enable more intuitive
31
	 *              access via magic methods. As part of the access change, the type was
32
	 *              also changed from `string` to `int`.
33
	 * @access private
34
	 * @var int
35
	 */
36
	private $id;
37
38
	/**
39
	 * Domain of the network.
40
	 *
41
	 * @since 4.4.0
42
	 * @access public
43
	 * @var string
44
	 */
45
	public $domain = '';
46
47
	/**
48
	 * Path of the network.
49
	 *
50
	 * @since 4.4.0
51
	 * @access public
52
	 * @var string
53
	 */
54
	public $path = '';
55
56
	/**
57
	 * The ID of the network's main site.
58
	 *
59
	 * Named "blog" vs. "site" for legacy reasons. A main site is mapped to
60
	 * the network when the network is created.
61
	 *
62
	 * A numeric string, for compatibility reasons.
63
	 *
64
	 * @since 4.4.0
65
	 * @access private
66
	 * @var string
67
	 */
68
	private $blog_id = '0';
69
70
	/**
71
	 * Domain used to set cookies for this network.
72
	 *
73
	 * @since 4.4.0
74
	 * @access public
75
	 * @var string
76
	 */
77
	public $cookie_domain = '';
78
79
	/**
80
	 * Name of this network.
81
	 *
82
	 * Named "site" vs. "network" for legacy reasons.
83
	 *
84
	 * @since 4.4.0
85
	 * @access public
86
	 * @var string
87
	 */
88
	public $site_name = '';
89
90
	/**
91
	 * Retrieve a network from the database by its ID.
92
	 *
93
	 * @since 4.4.0
94
	 * @access public
95
	 *
96
	 * @global wpdb $wpdb WordPress database abstraction object.
97
	 *
98
	 * @param int $network_id The ID of the network to retrieve.
99
	 * @return WP_Network|bool The network's object if found. False if not.
100
	 */
101 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...
102
		global $wpdb;
103
104
		$network_id = (int) $network_id;
105
		if ( ! $network_id ) {
106
			return false;
107
		}
108
109
		$_network = wp_cache_get( $network_id, 'networks' );
110
111
		if ( ! $_network ) {
112
			$_network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d LIMIT 1", $network_id ) );
113
114
			if ( empty( $_network ) || is_wp_error( $_network ) ) {
115
				return false;
116
			}
117
118
			wp_cache_add( $network_id, $_network, 'networks' );
119
		}
120
121
		return new WP_Network( $_network );
122
	}
123
124
	/**
125
	 * Create a new WP_Network object.
126
	 *
127
	 * Will populate object properties from the object provided and assign other
128
	 * default properties based on that information.
129
	 *
130
	 * @since 4.4.0
131
	 * @access public
132
	 *
133
	 * @param WP_Network|object $network A network object.
134
	 */
135
	public function __construct( $network ) {
136
		foreach( get_object_vars( $network ) as $key => $value ) {
137
			$this->$key = $value;
138
		}
139
140
		$this->_set_site_name();
141
		$this->_set_cookie_domain();
142
	}
143
144
	/**
145
	 * Getter.
146
	 *
147
	 * Allows current multisite naming conventions when getting properties.
148
	 *
149
	 * @since 4.6.0
150
	 * @access public
151
	 *
152
	 * @param string $key Property to get.
153
	 * @return mixed Value of the property. Null if not available.
154
	 */
155
	public function __get( $key ) {
156
		switch ( $key ) {
157
			case 'id';
158
				return (int) $this->id;
159
			case 'blog_id':
160
				return $this->blog_id;
161
			case 'site_id':
162
				return (int) $this->blog_id;
163
		}
164
165
		return null;
166
	}
167
168
	/**
169
	 * Isset-er.
170
	 *
171
	 * Allows current multisite naming conventions when checking for properties.
172
	 *
173
	 * @since 4.6.0
174
	 * @access public
175
	 *
176
	 * @param string $key Property to check if set.
177
	 * @return bool Whether the property is set.
178
	 */
179
	public function __isset( $key ) {
180
		switch ( $key ) {
181
			case 'id':
182
			case 'blog_id':
183
			case 'site_id':
184
				return true;
185
		}
186
187
		return false;
188
	}
189
190
	/**
191
	 * Setter.
192
	 *
193
	 * Allows current multisite naming conventions while setting properties.
194
	 *
195
	 * @since 4.6.0
196
	 * @access public
197
	 *
198
	 * @param string $key   Property to set.
199
	 * @param mixed  $value Value to assign to the property.
200
	 */
201 View Code Duplication
	public function __set( $key, $value ) {
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...
202
		switch ( $key ) {
203
			case 'id':
204
				$this->id = (int) $value;
205
				break;
206
			case 'blog_id':
207
			case 'site_id':
208
				$this->blog_id = (string) $value;
209
				break;
210
			default:
211
				$this->$key = $value;
212
		}
213
	}
214
215
	/**
216
	 * Set the site name assigned to the network if one has not been populated.
217
	 *
218
	 * @since 4.4.0
219
	 * @access private
220
	 */
221
	private function _set_site_name() {
222
		if ( ! empty( $this->site_name ) ) {
223
			return;
224
		}
225
226
		$default = ucfirst( $this->domain );
227
		$this->site_name = get_network_option( $this->id, 'site_name', $default );
228
	}
229
230
	/**
231
	 * Set the cookie domain based on the network domain if one has
232
	 * not been populated.
233
	 *
234
	 * @todo What if the domain of the network doesn't match the current site?
235
	 *
236
	 * @since 4.4.0
237
	 * @access private
238
	 */
239
	private function _set_cookie_domain() {
240
		if ( ! empty( $this->cookie_domain ) ) {
241
			return;
242
		}
243
244
		$this->cookie_domain = $this->domain;
245
		if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) {
246
			$this->cookie_domain = substr( $this->cookie_domain, 4 );
247
		}
248
	}
249
250
	/**
251
	 * Retrieve the closest matching network for a domain and path.
252
	 *
253
	 * This will not necessarily return an exact match for a domain and path. Instead, it
254
	 * breaks the domain and path into pieces that are then used to match the closest
255
	 * possibility from a query.
256
	 *
257
	 * The intent of this method is to match a network during bootstrap for a
258
	 * requested site address.
259
	 *
260
	 * @since 4.4.0
261
	 * @access public
262
	 * @static
263
	 *
264
	 * @param string   $domain   Domain to check.
265
	 * @param string   $path     Path to check.
266
	 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
267
	 * @return WP_Network|bool Network object if successful. False when no network is found.
268
	 */
269
	public static function get_by_path( $domain = '', $path = '', $segments = null ) {
270
		global $wpdb;
271
272
		$domains = array( $domain );
273
		$pieces  = explode( '.', $domain );
274
275
		/*
276
		 * It's possible one domain to search is 'com', but it might as well
277
		 * be 'localhost' or some other locally mapped domain.
278
		 */
279
		while ( array_shift( $pieces ) ) {
280
			if ( ! empty( $pieces ) ) {
281
				$domains[] = implode( '.', $pieces );
282
			}
283
		}
284
285
		/*
286
		 * If we've gotten to this function during normal execution, there is
287
		 * more than one network installed. At this point, who knows how many
288
		 * we have. Attempt to optimize for the situation where networks are
289
		 * only domains, thus meaning paths never need to be considered.
290
		 *
291
		 * This is a very basic optimization; anything further could have
292
		 * drawbacks depending on the setup, so this is best done per-install.
293
		 */
294
		$using_paths = true;
295 View Code Duplication
		if ( wp_using_ext_object_cache() ) {
296
			$using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
297
			if ( false === $using_paths ) {
298
				$using_paths = (int) $wpdb->get_var( "SELECT id FROM {$wpdb->site} WHERE path <> '/' LIMIT 1" );
299
				wp_cache_add( 'networks_have_paths', $using_paths, 'site-options'  );
300
			}
301
		}
302
303
		$paths = array();
304
		if ( $using_paths ) {
305
			$path_segments = array_filter( explode( '/', trim( $path, '/' ) ) );
306
307
			/**
308
			 * Filters the number of path segments to consider when searching for a site.
309
			 *
310
			 * @since 3.9.0
311
			 *
312
			 * @param int|null $segments The number of path segments to consider. WordPress by default looks at
313
			 *                           one path segment. The function default of null only makes sense when you
314
			 *                           know the requested path should match a network.
315
			 * @param string   $domain   The requested domain.
316
			 * @param string   $path     The requested path, in full.
317
			 */
318
			$segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
319
320 View Code Duplication
			if ( ( null !== $segments ) && count( $path_segments ) > $segments ) {
321
				$path_segments = array_slice( $path_segments, 0, $segments );
322
			}
323
324 View Code Duplication
			while ( count( $path_segments ) ) {
325
				$paths[] = '/' . implode( '/', $path_segments ) . '/';
326
				array_pop( $path_segments );
327
			}
328
329
			$paths[] = '/';
330
		}
331
332
		/**
333
		 * Determine a network by its domain and path.
334
		 *
335
		 * This allows one to short-circuit the default logic, perhaps by
336
		 * replacing it with a routine that is more optimal for your setup.
337
		 *
338
		 * Return null to avoid the short-circuit. Return false if no network
339
		 * can be found at the requested domain and path. Otherwise, return
340
		 * an object from wp_get_network().
341
		 *
342
		 * @since 3.9.0
343
		 *
344
		 * @param null|bool|object $network  Network value to return by path.
345
		 * @param string           $domain   The requested domain.
346
		 * @param string           $path     The requested path, in full.
347
		 * @param int|null         $segments The suggested number of paths to consult.
348
		 *                                   Default null, meaning the entire path was to be consulted.
349
		 * @param array            $paths    The paths to search for, based on $path and $segments.
350
		 */
351
		$pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
352
		if ( null !== $pre ) {
353
			return $pre;
354
		}
355
356
		// @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
357
		// We already have paths covered. What about how far domains should be drilled down (including www)?
358
359
		$search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
360
361
		if ( ! $using_paths ) {
362
			$network = $wpdb->get_row( "
363
				SELECT * FROM {$wpdb->site}
364
				WHERE domain IN ({$search_domains})
365
				ORDER BY CHAR_LENGTH(domain)
366
				DESC LIMIT 1
367
			" );
368
369
			if ( ! empty( $network ) && ! is_wp_error( $network ) ) {
370
				return new WP_Network( $network );
371
			}
372
373
			return false;
374
375
		} else {
376
			$search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
377
			$networks = $wpdb->get_results( "
378
				SELECT * FROM {$wpdb->site}
379
				WHERE domain IN ({$search_domains})
380
				AND path IN ({$search_paths})
381
				ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC
382
			" );
383
		}
384
385
		/*
386
		 * Domains are sorted by length of domain, then by length of path.
387
		 * The domain must match for the path to be considered. Otherwise,
388
		 * a network with the path of / will suffice.
389
		 */
390
		$found = false;
391
		foreach ( $networks as $network ) {
392
			if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) {
393
				if ( in_array( $network->path, $paths, true ) ) {
394
					$found = true;
395
					break;
396
				}
397
			}
398
			if ( $network->path === '/' ) {
399
				$found = true;
400
				break;
401
			}
402
		}
403
404
		if ( true === $found ) {
405
			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 391. 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...
406
		}
407
408
		return false;
409
	}
410
}
411