| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 string $blog_id 23 * @property int $site_id 24 * 25 * @phpstan-property numeric-string $blog_id 26 */ 27 #[AllowDynamicProperties] 28 class WP_Network { 29 30 /** 31 * Network ID. 32 * 33 * @since 4.4.0 34 * @since 4.6.0 Converted from public to private to explicitly enable more intuitive 35 * access via magic methods. As part of the access change, the type was 36 * also changed from `string` to `int`. 37 * @var int 38 */ 39 private $id; 40 41 /** 42 * Domain of the network. 43 * 44 * @since 4.4.0 45 * @var string 46 */ 47 public $domain = ''; 48 49 /** 50 * Path of the network. 51 * 52 * @since 4.4.0 53 * @var string 54 */ 55 public $path = ''; 56 57 /** 58 * The ID of the network's main site. 59 * 60 * Named "blog" vs. "site" for legacy reasons. A main site is mapped to 61 * the network when the network is created. 62 * 63 * A numeric string, for compatibility reasons. 64 * 65 * @since 4.4.0 66 * @var string 67 * @phpstan-var numeric-string 68 */ 69 private $blog_id = '0'; 70 71 /** 72 * Domain used to set cookies for this network. 73 * 74 * @since 4.4.0 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 * @var string 86 */ 87 public $site_name = ''; 88 89 /** 90 * Retrieves a network from the database by its ID. 91 * 92 * @since 4.4.0 93 * @since 7.2.0 Cache values that are neither a network object nor the -1 miss sentinel are now treated as a cache miss and replaced. 94 * 95 * @global wpdb $wpdb WordPress database abstraction object. 96 * 97 * @param int $network_id The ID of the network to retrieve. 98 * @return WP_Network|false The network's object if found. False if not. 99 */ 100 public static function get_instance( $network_id ) { 101 global $wpdb; 102 103 $network_id = (int) $network_id; 104 if ( ! $network_id ) { 105 return false; 106 } 107 108 $_network = wp_cache_get( $network_id, 'networks' ); 109 110 // A cached -1 records a previous lookup that found nothing. Any other non-numeric value that is not a network object is treated as a cache miss. 111 if ( 112 ( ! is_object( $_network ) || ! isset( $_network->id ) ) 113 && 114 ! is_numeric( $_network ) 115 ) { 116 $_network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d LIMIT 1", $network_id ) ); 117 118 if ( empty( $_network ) || is_wp_error( $_network ) ) { 119 $_network = -1; 120 } 121 122 // Not wp_cache_add(), since an unusable cached value may still be present and must be replaced. 123 wp_cache_set( $network_id, $_network, 'networks' ); 124 } 125 126 if ( is_numeric( $_network ) ) { 127 return false; 128 } 129 130 return new WP_Network( $_network ); 131 } 132 133 /** 134 * Creates a new WP_Network object. 135 * 136 * Will populate object properties from the object provided and assign other 137 * default properties based on that information. 138 * 139 * @since 4.4.0 140 * 141 * @param WP_Network|object $network A network object. 142 */ 143 public function __construct( $network ) { 144 foreach ( get_object_vars( $network ) as $key => $value ) { 145 $this->__set( $key, $value ); 146 } 147 148 $this->_set_site_name(); 149 $this->_set_cookie_domain(); 150 } 151 152 /** 153 * Getter. 154 * 155 * Allows current multisite naming conventions when getting properties. 156 * 157 * @since 4.6.0 158 * 159 * @param string $key Property to get. 160 * @return mixed Value of the property. Null if not available. 161 */ 162 public function __get( $key ) { 163 switch ( $key ) { 164 case 'id': 165 return (int) $this->id; 166 case 'blog_id': 167 return (string) $this->get_main_site_id(); 168 case 'site_id': 169 return $this->get_main_site_id(); 170 } 171 172 return null; 173 } 174 175 /** 176 * Isset-er. 177 * 178 * Allows current multisite naming conventions when checking for properties. 179 * 180 * @since 4.6.0 181 * 182 * @param string $key Property to check if set. 183 * @return bool Whether the property is set. 184 */ 185 public function __isset( $key ) { 186 switch ( $key ) { 187 case 'id': 188 case 'blog_id': 189 case 'site_id': 190 return true; 191 } 192 193 return false; 194 } 195 196 /** 197 * Setter. 198 * 199 * Allows current multisite naming conventions while setting properties. 200 * 201 * @since 4.6.0 202 * 203 * @param string $key Property to set. 204 * @param mixed $value Value to assign to the property. 205 */ 206 public function __set( $key, $value ) { 207 switch ( $key ) { 208 case 'id': 209 $this->id = (int) $value; 210 break; 211 case 'blog_id': 212 case 'site_id': 213 $this->blog_id = (string) $value; 214 break; 215 default: 216 $this->$key = $value; 217 } 218 } 219 220 /** 221 * Returns the main site ID for the network. 222 * 223 * Internal method used by the magic getter for the 'blog_id' and 'site_id' 224 * properties. 225 * 226 * @since 4.9.0 227 * 228 * @return int The ID of the main site. 229 */ 230 private function get_main_site_id() { 231 /** 232 * Filters the main site ID. 233 * 234 * Returning a positive integer will effectively short-circuit the function. 235 * 236 * @since 4.9.0 237 * 238 * @param int|null $main_site_id If a positive integer is returned, it is interpreted as the main site ID. 239 * @param WP_Network $network The network object for which the main site was detected. 240 */ 241 $main_site_id = (int) apply_filters( 'pre_get_main_site_id', null, $this ); 242 243 if ( 0 < $main_site_id ) { 244 return $main_site_id; 245 } 246 247 if ( 0 < (int) $this->blog_id ) { 248 return (int) $this->blog_id; 249 } 250 251 if ( ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) 252 && DOMAIN_CURRENT_SITE === $this->domain && PATH_CURRENT_SITE === $this->path ) 253 || ( defined( 'SITE_ID_CURRENT_SITE' ) && (int) SITE_ID_CURRENT_SITE === $this->id ) 254 ) { 255 if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) { 256 $this->blog_id = (string) BLOG_ID_CURRENT_SITE; 257 258 return (int) $this->blog_id; 259 } 260 261 if ( defined( 'BLOGID_CURRENT_SITE' ) ) { // Deprecated. 262 $this->blog_id = (string) BLOGID_CURRENT_SITE; 263 264 return (int) $this->blog_id; 265 } 266 } 267 268 $site = get_site(); 269 if ( $site->domain === $this->domain && $site->path === $this->path ) { 270 $main_site_id = (int) $site->id; 271 } else { 272 273 $main_site_id = get_network_option( $this->id, 'main_site' ); 274 if ( false === $main_site_id ) { 275 $_sites = get_sites( 276 array( 277 'fields' => 'ids', 278 'number' => 1, 279 'domain' => $this->domain, 280 'path' => $this->path, 281 'network_id' => $this->id, 282 ) 283 ); 284 $main_site_id = ! empty( $_sites ) ? array_shift( $_sites ) : 0; 285 286 update_network_option( $this->id, 'main_site', $main_site_id ); 287 } 288 } 289 290 $this->blog_id = (string) $main_site_id; 291 292 return (int) $this->blog_id; 293 } 294 295 /** 296 * Sets the site name assigned to the network if one has not been populated. 297 * 298 * @since 4.4.0 299 */ 300 private function _set_site_name() { 301 if ( ! empty( $this->site_name ) ) { 302 return; 303 } 304 305 $default = ucfirst( $this->domain ); 306 $this->site_name = get_network_option( $this->id, 'site_name', $default ); 307 } 308 309 /** 310 * Sets the cookie domain based on the network domain if one has 311 * not been populated. 312 * 313 * @todo What if the domain of the network doesn't match the current site? 314 * 315 * @since 4.4.0 316 */ 317 private function _set_cookie_domain() { 318 if ( ! empty( $this->cookie_domain ) ) { 319 return; 320 } 321 $domain = parse_url( $this->domain, PHP_URL_HOST ); 322 $this->cookie_domain = is_string( $domain ) ? $domain : $this->domain; 323 if ( str_starts_with( $this->cookie_domain, 'www.' ) ) { 324 $this->cookie_domain = substr( $this->cookie_domain, 4 ); 325 } 326 } 327 328 /** 329 * Retrieves the closest matching network for a domain and path. 330 * 331 * This will not necessarily return an exact match for a domain and path. Instead, it 332 * breaks the domain and path into pieces that are then used to match the closest 333 * possibility from a query. 334 * 335 * The intent of this method is to match a network during bootstrap for a 336 * requested site address. 337 * 338 * @since 4.4.0 339 * 340 * @param string $domain Domain to check. 341 * @param string $path Path to check. 342 * @param int|null $segments Path segments to use. Defaults to null, or the full path. 343 * @return WP_Network|false Network object if successful. False when no network is found. 344 */ 345 public static function get_by_path( $domain = '', $path = '', $segments = null ) { 346 $domains = array( $domain ); 347 $pieces = explode( '.', $domain ); 348 349 /* 350 * It's possible one domain to search is 'com', but it might as well 351 * be 'localhost' or some other locally mapped domain. 352 */ 353 while ( array_shift( $pieces ) ) { 354 if ( ! empty( $pieces ) ) { 355 $domains[] = implode( '.', $pieces ); 356 } 357 } 358 359 /* 360 * If we've gotten to this function during normal execution, there is 361 * more than one network installed. At this point, who knows how many 362 * we have. Attempt to optimize for the situation where networks are 363 * only domains, thus meaning paths never need to be considered. 364 * 365 * This is a very basic optimization; anything further could have 366 * drawbacks depending on the setup, so this is best done per-installation. 367 */ 368 $using_paths = true; 369 if ( wp_using_ext_object_cache() ) { 370 $using_paths = get_networks( 371 array( 372 'number' => 1, 373 'count' => true, 374 'path__not_in' => '/', 375 ) 376 ); 377 } 378 379 $paths = array(); 380 if ( $using_paths ) { 381 $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) ); 382 383 /** 384 * Filters the number of path segments to consider when searching for a site. 385 * 386 * @since 3.9.0 387 * 388 * @param int|null $segments The number of path segments to consider. WordPress by default looks at 389 * one path segment. The function default of null only makes sense when you 390 * know the requested path should match a network. 391 * @param string $domain The requested domain. 392 * @param string $path The requested path, in full. 393 */ 394 $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path ); 395 396 if ( ( null !== $segments ) && count( $path_segments ) > $segments ) { 397 $path_segments = array_slice( $path_segments, 0, $segments ); 398 } 399 400 while ( count( $path_segments ) ) { 401 $paths[] = '/' . implode( '/', $path_segments ) . '/'; 402 array_pop( $path_segments ); 403 } 404 405 $paths[] = '/'; 406 } 407 408 /** 409 * Determines a network by its domain and path. 410 * 411 * This allows one to short-circuit the default logic, perhaps by 412 * replacing it with a routine that is more optimal for your setup. 413 * 414 * Return null to avoid the short-circuit. Return false if no network 415 * can be found at the requested domain and path. Otherwise, return 416 * an object from wp_get_network(). 417 * 418 * @since 3.9.0 419 * 420 * @param null|false|WP_Network $network Network value to return by path. Default null 421 * to continue retrieving the network. 422 * @param string $domain The requested domain. 423 * @param string $path The requested path, in full. 424 * @param int|null $segments The suggested number of paths to consult. 425 * Default null, meaning the entire path was to be consulted. 426 * @param string[] $paths Array of paths to search for, based on `$path` and `$segments`. 427 */ 428 $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths ); 429 if ( null !== $pre ) { 430 return $pre; 431 } 432 433 if ( ! $using_paths ) { 434 $networks = get_networks( 435 array( 436 'number' => 1, 437 'orderby' => array( 438 'domain_length' => 'DESC', 439 ), 440 'domain__in' => $domains, 441 ) 442 ); 443 444 if ( ! empty( $networks ) ) { 445 return array_shift( $networks ); 446 } 447 448 return false; 449 } 450 451 $networks = get_networks( 452 array( 453 'orderby' => array( 454 'domain_length' => 'DESC', 455 'path_length' => 'DESC', 456 ), 457 'domain__in' => $domains, 458 'path__in' => $paths, 459 ) 460 ); 461 462 /* 463 * Domains are sorted by length of domain, then by length of path. 464 * The domain must match for the path to be considered. Otherwise, 465 * a network with the path of / will suffice. 466 */ 467 $found = false; 468 foreach ( $networks as $network ) { 469 if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) { 470 if ( in_array( $network->path, $paths, true ) ) { 471 $found = true; 472 break; 473 } 474 } 475 if ( '/' === $network->path ) { 476 $found = true; 477 break; 478 } 479 } 480 481 if ( true === $found ) { 482 return $network; 483 } 484 485 return false; 486 } 487 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Sep 8 08:20:28 2026 | Cross-referenced by PHPXref |