Ticket #31985: 31985.diff
File 31985.diff, 40.6 KB (added by , 10 years ago) |
---|
-
src/wp-includes/class-wp-network.php
1 <?php 2 3 /** 4 * Main network class 5 * 6 * This class contains the values and functional methods for interacting with 7 * a network of WordPress sites. Traditionally used for multisite, this class 8 * is used by the $current_site global to setup the current network 9 * 10 * This class is most useful in WordPress multi-network installations where the 11 * ability to interact with any network of sites is required. 12 * 13 * @since WordPress (4.3.0) 14 */ 15 class WP_Network { 16 17 /** 18 * Unique numerical ID 19 * 20 * @since WordPress (4.3.0) 21 * 22 * @var int 23 */ 24 public $id = 0; 25 26 /** 27 * Domain of this network 28 * 29 * @since WordPress (4.3.0) 30 * 31 * @var string 32 */ 33 public $domain = ''; 34 35 /** 36 * Path of this network 37 * 38 * @since WordPress (4.3.0) 39 * 40 * @var string 41 */ 42 public $path = ''; 43 44 /** 45 * The ID of the site mapped to this network 46 * 47 * Named "blog" vs. "site" for legacy reasons 48 * 49 * @since WordPress (4.3.0) 50 * 51 * @var int 52 */ 53 public $blog_id = 0; 54 55 /** 56 * Domain used for cookies for this network 57 * 58 * @since WordPress (4.3.0) 59 * 60 * @var int 61 */ 62 public $cookie_domain = ''; 63 64 /** 65 * Array of core options for this network 66 * 67 * @since WordPress (4.3.0) 68 * 69 * @var array 70 */ 71 public $core_options = array(); 72 73 /** 74 * Name of this network 75 * 76 * Named "nite" vs. "network" for legacy reasons 77 * 78 * @since WordPress (4.3.0) 79 * 80 * @var string 81 */ 82 public $site_name = ''; 83 84 /** 85 * Create a new WP_Network object 86 * 87 * Will populate all relevant data if a network ID or object is passed 88 * 89 * @since WordPress (4.3.0) 90 * 91 * @param mixed $network stdClass object or network ID 92 * @return object 93 */ 94 public function __construct( $network = false ) { 95 96 // Bail if not populating from an existing network 97 if ( empty( $network ) ) { 98 return; 99 } 100 101 // Get the network ID 102 if ( is_object( $network ) && isset( $network->id ) ) { 103 $network_id = $network->id; 104 } elseif ( is_numeric( $network ) ) { 105 $network_id = (int) $network; 106 } else { 107 return; 108 } 109 110 // Populate network object and return it 111 if ( ! empty( $network_id ) ) { 112 $this->populate( $network_id ); 113 return $this; 114 } 115 } 116 117 public function populate( $network_id = 0 ) { 118 119 // Query for network 120 $network = self::get_by_id( $network_id ); 121 122 // Bail if no network to update 123 if ( false === $network ) { 124 return false; 125 } 126 127 // Set the main data 128 $this->set_main_data( $network ); 129 130 // Set supplemental data based on the needs of multisite 131 $this->set_cookie_domain(); 132 $this->set_core_options(); 133 $this->set_site_name(); 134 } 135 136 /** 137 * Set the network ID, domain, and path 138 * 139 * @since WordPress (4.3.0) 140 */ 141 private function set_main_data( $network ) { 142 $this->id = $network->id; 143 $this->domain = $network->domain; 144 $this->path = $network->path; 145 } 146 147 /** 148 * Set the cookie domain, based on the network domain 149 * 150 * @todo What if the domain of the network doesn't match the current site? 151 * 152 * @since WordPress (4.3.0) 153 */ 154 private function set_cookie_domain() { 155 $this->cookie_domain = $this->domain; 156 if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) { 157 $this->cookie_domain = substr( $this->cookie_domain, 4 ); 158 } 159 } 160 161 /** 162 * Set the core options for this network object 163 * 164 * @since WordPress (4.3.0) 165 */ 166 private function set_core_options() { 167 $this->core_options = self::get_core_options( $this->id ); 168 } 169 170 /** 171 * @todo What if the domain of the network doesn't match the current site? 172 */ 173 private function set_site_name() { 174 175 // Attempt to use core option 176 if ( ! empty( $this->core_options['site_name'] ) ) { 177 $this->site_name = $this->core_options['site_name']; 178 179 // Fallback to domain 180 } else { 181 $this->site_name = ucfirst( $this->domain ); 182 } 183 } 184 185 public static function get_by_id( $network_id = 0 ) { 186 global $wpdb; 187 188 // Check cache 189 $network = wp_cache_get( $network_id, 'networks' ); 190 191 // Cache miss 192 if ( false === $network ) { 193 194 // Query for network data 195 $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d", $network_id ) ); 196 197 // Bail if network cannot be found 198 if ( empty( $network ) || is_wp_error( $network ) ) { 199 $network = false; 200 } 201 202 // Add network to cache 203 wp_cache_add( $network_id, $network, 'networks' ); 204 } 205 206 // Return the network 207 return $network; 208 } 209 210 /** 211 * Retrieve a network by its domain and path. 212 * 213 * @since 4.3.0 214 * 215 * @param string $domain Domain to check. 216 * @param string $path Path to check. 217 * @param int|null $segments Path segments to use. Defaults to null, or the full path. 218 * 219 * @return object|bool Network object if successful. False when no network is found. 220 */ 221 public static function get_by_path( $domain = '', $path = '', $segments = null ) { 222 global $wpdb; 223 224 $domains = array( $domain ); 225 $pieces = explode( '.', $domain ); 226 227 /* 228 * It's possible one domain to search is 'com', but it might as well 229 * be 'localhost' or some other locally mapped domain. 230 */ 231 while ( array_shift( $pieces ) ) { 232 if ( ! empty( $pieces ) ) { 233 $domains[] = implode( '.', $pieces ); 234 } 235 } 236 237 /* 238 * If we've gotten to this function during normal execution, there is 239 * more than one network installed. At this point, who knows how many 240 * we have. Attempt to optimize for the situation where networks are 241 * only domains, thus meaning paths never need to be considered. 242 * 243 * This is a very basic optimization; anything further could have 244 * drawbacks depending on the setup, so this is best done per-install. 245 */ 246 $using_paths = true; 247 if ( wp_using_ext_object_cache() ) { 248 $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' ); 249 if ( false === $using_paths ) { 250 $using_paths = (bool) $wpdb->get_var( "SELECT id FROM {$wpdb->site} WHERE path <> '/' LIMIT 1" ); 251 wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options' ); 252 } 253 } 254 255 $paths = array(); 256 if ( true === $using_paths ) { 257 $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) ); 258 259 /** 260 * Filter the number of path segments to consider when searching for a site. 261 * 262 * @since 3.9.0 263 * 264 * @param int|null $segments The number of path segments to consider. WordPress by default looks at 265 * one path segment. The function default of null only makes sense when you 266 * know the requested path should match a network. 267 * @param string $domain The requested domain. 268 * @param string $path The requested path, in full. 269 */ 270 $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path ); 271 272 if ( ( null !== $segments ) && count( $path_segments ) > $segments ) { 273 $path_segments = array_slice( $path_segments, 0, $segments ); 274 } 275 276 while ( count( $path_segments ) ) { 277 $paths[] = '/' . implode( '/', $path_segments ) . '/'; 278 array_pop( $path_segments ); 279 } 280 281 $paths[] = '/'; 282 } 283 284 /** 285 * Determine a network by its domain and path. 286 * 287 * This allows one to short-circuit the default logic, perhaps by 288 * replacing it with a routine that is more optimal for your setup. 289 * 290 * Return null to avoid the short-circuit. Return false if no network 291 * can be found at the requested domain and path. Otherwise, return 292 * an object from wp_get_network(). 293 * 294 * @since 3.9.0 295 * 296 * @param null|bool|object $network Network value to return by path. 297 * @param string $domain The requested domain. 298 * @param string $path The requested path, in full. 299 * @param int|null $segments The suggested number of paths to consult. 300 * Default null, meaning the entire path was to be consulted. 301 * @param array $paths The paths to search for, based on $path and $segments. 302 */ 303 $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths ); 304 if ( null !== $pre ) { 305 return $pre; 306 } 307 308 // @todo Consider additional optimization routes, perhaps as an opt-in for plugins. 309 // We already have paths covered. What about how far domains should be drilled down (including www)? 310 311 $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'"; 312 313 if ( false === $using_paths ) { 314 $network = $wpdb->get_row( "SELECT * FROM {$wpdb->site} 315 WHERE domain IN ({$search_domains}) 316 ORDER BY CHAR_LENGTH(domain) 317 DESC LIMIT 1" ); 318 319 if ( ! empty( $network ) && ! is_wp_error( $network ) ) { 320 return new WP_Network( $network->id ); 321 } 322 323 return false; 324 325 } else { 326 $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'"; 327 $networks = $wpdb->get_results( "SELECT * FROM {$wpdb->site} 328 WHERE domain IN ({$search_domains}) 329 AND path IN ({$search_paths}) 330 ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" ); 331 } 332 333 /* 334 * Domains are sorted by length of domain, then by length of path. 335 * The domain must match for the path to be considered. Otherwise, 336 * a network with the path of / will suffice. 337 */ 338 $found = false; 339 foreach ( $networks as $network ) { 340 if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) { 341 if ( in_array( $network->path, $paths, true ) ) { 342 $found = true; 343 break; 344 } 345 } 346 if ( $network->path === '/' ) { 347 $found = true; 348 break; 349 } 350 } 351 352 if ( true === $found ) { 353 return new WP_Network( $network->id ); 354 } 355 356 return false; 357 } 358 359 public static function get_core_options( $network_id = 0 ) { 360 global $wpdb; 361 362 // Setup core options query 363 $meta_keys = self::get_core_option_keys(); 364 $core_options_in = "'" . implode( "', '", $meta_keys ) . "'"; 365 $sql = $wpdb->prepare( "SELECT meta_key, meta_value 366 FROM {$wpdb->sitemeta} 367 WHERE meta_key IN ({$core_options_in}) 368 AND site_id = %d", $network_id ); 369 370 // Query for core network options 371 $options = $wpdb->get_results( $sql ); 372 373 // Bail if no options found 374 if ( empty( $options ) || is_wp_error( $options ) ) { 375 return array(); 376 } 377 378 // Setup return value array 379 $retval = array(); 380 381 // Loop through options and add them to the object cache 382 foreach ( $options as $option ) { 383 384 // Setup option values to cache 385 $key = $option->meta_key; 386 $cache_key = "{$network_id}:$key"; 387 $option->meta_value = maybe_unserialize( $option->meta_value ); 388 389 // Cache the option value 390 wp_cache_set( $cache_key, $option->meta_value, 'site-options' ); 391 392 // Add option to array 393 $retval[ $option->meta_key ] = $option->meta_value; 394 } 395 396 // Return the options 397 return $retval; 398 } 399 400 public static function get_core_option_keys() { 401 return array( 402 'site_name', 403 'siteurl', 404 'active_sitewide_plugins', 405 '_site_transient_timeout_theme_roots', 406 '_site_transient_theme_roots', 407 'site_admins', 408 'can_compress_scripts', 409 'global_terms_enabled', 410 'ms_files_rewriting' 411 ); 412 } 413 414 /** 415 * Get all active network-wide plugins for this network 416 * 417 * @since WordPress (4.3.0) 418 * 419 * @param int $network_id 420 * @return array 421 */ 422 public static function get_active_network_plugins( $network_id = 0 ) { 423 $network_options = self::get_core_options( $network_id ); 424 425 if ( empty( $network_options['active_sitewide_plugins'] ) ) { 426 return array(); 427 } 428 $active_plugins = $network_options['active_sitewide_plugins']; 429 430 // Setup the plugins array 431 $plugins = array(); 432 $active_plugins = array_keys( $active_plugins ); 433 434 // Sort plugins array 435 sort( $active_plugins ); 436 437 foreach ( $active_plugins as $plugin ) { 438 if ( self::validate_network_plugin( $plugin ) ) { 439 $plugins[] = WP_PLUGIN_DIR . '/' . $plugin; 440 } 441 } 442 443 return $plugins; 444 } 445 446 /** 447 * Ensure a single network-wide plugin is valid 448 * 449 * @since WordPress (4.3.0) 450 * 451 * @param type $plugin 452 * 453 * @return boolean 454 */ 455 private static function validate_network_plugin( $plugin = '' ) { 456 457 // $plugin must validate as file 458 if ( ! validate_file( $plugin ) ) { 459 return false; 460 } 461 462 // $plugin must end with '.php' 463 if ( '.php' !== substr( $plugin, -4 ) ) { 464 return false; 465 } 466 467 // $plugin must exist 468 if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) { 469 return false; 470 } 471 472 return true; 473 } 474 475 public static function add( $domain = '', $path = '' ) { 476 477 } 478 479 /** 480 * Update a networks domain & path based on its ID 481 * 482 * @since WordPress (4.3.0) 483 * 484 * @global type $wpdb 485 * 486 * @param int $network_id Existing network ID 487 * @param string $domain New network domain 488 * @param string $path New network path 489 * 490 * @return mixed False if network not found. Results of query if updated. 491 */ 492 public static function update( $network_id = 0, $domain = '', $path = '' ) { 493 global $wpdb; 494 495 // Does network exist 496 $network = self::get_by_id( $network_id ); 497 498 // Bail if no network to update 499 if ( false === $network ) { 500 return false; 501 } 502 503 // Set the domain 504 if ( ! empty( $domain ) ) { 505 $network->domain = $domain; 506 } 507 508 // Set the path 509 if ( ! empty( $path ) ) { 510 $network->path = $path; 511 } 512 513 // Query for network data 514 $sql = $wpdb->prepare( "UPDATE {$wpdb->site} SET domain = %s, path = %s WHERE id = %d", $network->domain, $network->path, $network->id ); 515 $network = $wpdb->query( $sql ); 516 517 // Bail if network cannot be found 518 if ( empty( $network ) || is_wp_error( $network ) ) { 519 return false; 520 } 521 522 // Return results of WPDB::query() 523 return $network; 524 } 525 526 public static function delete( $network_id = 0 ) { 527 global $wpdb; 528 529 // Does network exist 530 $network = self::get_by_id( $network_id ); 531 532 // Bail if no network to delete 533 if ( false === $network ) { 534 return false; 535 } 536 537 // Query for network data 538 $sql = $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id = %d", $network->id ); 539 $network = $wpdb->query( $sql ); 540 } 541 } -
src/wp-includes/ms-load.php
16 16 * @return bool True if subdomain configuration is enabled, false otherwise. 17 17 */ 18 18 function is_subdomain_install() { 19 if ( defined('SUBDOMAIN_INSTALL') ) 19 20 if ( defined( 'SUBDOMAIN_INSTALL' ) ) { 20 21 return SUBDOMAIN_INSTALL; 22 } 21 23 22 if ( defined( 'VHOST') && VHOST == 'yes' )24 if ( defined( 'VHOST' ) && ( VHOST === 'yes' ) ) { 23 25 return true; 26 } 24 27 25 28 return false; 26 29 } … … 47 50 48 51 foreach ( $active_plugins as $plugin ) { 49 52 if ( ! validate_file( $plugin ) // $plugin must validate as file 50 51 52 53 $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;53 && '.php' == substr( $plugin, -4 ) // $plugin must end with '.php' 54 && file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist 55 ) 56 $plugins[] = WP_PLUGIN_DIR . '/' . $plugin; 54 57 } 55 58 return $plugins; 56 59 } … … 113 116 } 114 117 115 118 /** 116 * Retrieve a network object by its domain and path.119 * Set relevant multisite globals 117 120 * 118 * @since 3.9.0121 * @since WordPress (4.3.0) 119 122 * 120 * @param string $domain Domain to check. 121 * @param string $path Path to check. 122 * @param int|null $segments Path segments to use. Defaults to null, or the full path. 123 * @return object|bool Network object if successful. False when no network is found. 123 * @global object $current_site 124 * @global object $current_blog 125 * @global object $wpdb 126 * 127 * @return if already set by sunrise.php 124 128 */ 125 function get_network_by_path( $domain, $path, $segments = null) {126 global $ wpdb;129 function ms_set_current_globals() { 130 global $current_site, $current_blog, $wpdb; 127 131 128 $domains = array( $domain ); 129 $pieces = explode( '.', $domain ); 132 // Bail if network and site are already set 133 if ( isset( $current_site ) && isset( $current_blog ) ) { 134 return; 135 } 130 136 131 /* 132 * It's possible one domain to search is 'com', but it might as well 133 * be 'localhost' or some other locally mapped domain. 134 */ 135 while ( array_shift( $pieces ) ) { 136 if ( $pieces ) { 137 $domains[] = implode( '.', $pieces ); 138 } 137 // Given the domain and path, let's try to identify the network and site. 138 // Usually, it's easier to query the site first, which declares its network. 139 // In limited situations, though, we either can or must find the network first. 140 141 $domain = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) ); 142 if ( substr( $domain, -3 ) == ':80' ) { 143 $domain = substr( $domain, 0, -3 ); 144 $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 ); 145 } elseif ( substr( $domain, -4 ) == ':443' ) { 146 $domain = substr( $domain, 0, -4 ); 147 $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 ); 139 148 } 140 149 150 $path = stripslashes( $_SERVER['REQUEST_URI'] ); 151 if ( is_admin() ) { 152 $path = preg_replace( '#(.*)/wp-admin/.*#', '$1/', $path ); 153 } 154 list( $path ) = explode( '?', $path ); 155 156 // If the network is defined in wp-config.php, we can simply use that. 157 if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) { 158 $current_site = new WP_Network(); 159 $current_site->id = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1; 160 $current_site->domain = DOMAIN_CURRENT_SITE; 161 $current_site->path = PATH_CURRENT_SITE; 162 163 if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) { 164 $current_site->blog_id = BLOG_ID_CURRENT_SITE; 165 } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated. 166 $current_site->blog_id = BLOGID_CURRENT_SITE; 167 } 168 169 if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) { 170 $current_blog = get_site_by_path( $domain, $path ); 171 172 // If the current network has a path and also matches the domain and 173 // path of the request, we need to look for a site using the first path 174 // segment following the network's path. 175 } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) { 176 $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) ); 177 178 // Otherwise, use the first path segment (as usual). 179 } else { 180 $current_blog = get_site_by_path( $domain, $path, 1 ); 181 } 182 141 183 /* 142 * If we've gotten to this function during normal execution, there is 143 * more than one network installed. At this point, who knows how many 144 * we have. Attempt to optimize for the situation where networks are 145 * only domains, thus meaning paths never need to be considered. 146 * 147 * This is a very basic optimization; anything further could have drawbacks 148 * depending on the setup, so this is best done per-install. 184 * A "subdomain" install can be re-interpreted to mean "can support anydomain". 185 * If we're not dealing with one of these installs, then the important part 186 * is determining the network first, because we need the network's path to 187 * identify any sites. 149 188 */ 150 $using_paths = true; 151 if ( wp_using_ext_object_cache() ) { 152 $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' ); 153 if ( false === $using_paths ) { 154 $using_paths = (bool) $wpdb->get_var( "SELECT id FROM $wpdb->site WHERE path <> '/' LIMIT 1" ); 155 wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options' ); 189 } elseif ( ! is_subdomain_install() ) { 190 191 // Attempt to get the current network from cache 192 $current_site = wp_cache_get( 'current_network', 'site-options' ); 193 if ( false === $current_site ) { 194 195 // Are there even two networks installed? 196 $one_network = $wpdb->get_row( "SELECT * FROM {$wpdb->site} LIMIT 2" ); 197 if ( 1 === $wpdb->num_rows ) { 198 $current_site = new WP_Network( $one_network ); 199 wp_cache_add( 'current_network', $current_site, 'site-options' ); 200 } elseif ( 0 === $wpdb->num_rows ) { 201 ms_not_installed(); 202 } 156 203 } 157 }158 204 159 $paths = array(); 160 if ( $using_paths ) { 161 $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) ); 205 // No network found, so attempt to query it by path 206 if ( empty( $current_site ) ) { 207 $current_site = get_network_by_path( $domain, $path, 1 ); 208 } 162 209 163 /** 164 * Filter the number of path segments to consider when searching for a site. 165 * 166 * @since 3.9.0 167 * 168 * @param int|null $segments The number of path segments to consider. WordPress by default looks at 169 * one path segment. The function default of null only makes sense when you 170 * know the requested path should match a network. 171 * @param string $domain The requested domain. 172 * @param string $path The requested path, in full. 173 */ 174 $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path ); 210 // Bail if no network found 211 if ( empty( $current_site ) ) { 212 ms_not_installed(); 213 214 // Set current site if path matches 215 } elseif ( $path === $current_site->path ) { 216 $current_blog = get_site_by_path( $domain, $path ); 175 217 176 if ( null !== $segments && count($path_segments ) > $segments ) { 177 $path_segments = array_slice( $path_segments, 0, $segments ); 218 // Search the network path + one more path segment (on top of the network path). 219 } else { 220 $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) ); 178 221 } 222 } else { 179 223 180 while ( count( $path_segments ) ) { 181 $paths[] = '/' . implode( '/', $path_segments ) . '/'; 182 array_pop( $path_segments ); 224 // Find the site by the domain and at most the first path segment. 225 $current_blog = get_site_by_path( $domain, $path, 1 ); 226 if ( ! empty( $current_blog ) ) { 227 $network_id = $current_blog->site_id ? $current_blog->site_id : 1; 228 $current_site = new WP_Network( $network_id ); 229 230 // If you don't have a site with the same domain/path as a network, 231 // you're pretty screwed, but... 232 } else { 233 $current_site = get_network_by_path( $domain, $path, 1 ); 183 234 } 235 } 184 236 185 $paths[] = '/'; 237 // The network declared by the site trumps any constants. 238 if ( ! empty( $current_blog ) && ( $current_blog->site_id !== $current_site->id ) ) { 239 $current_site = new WP_Network( $current_blog->site_id ); 186 240 } 187 241 188 /** 189 * Determine a network by its domain and path. 190 * 191 * This allows one to short-circuit the default logic, perhaps by 192 * replacing it with a routine that is more optimal for your setup. 193 * 194 * Return null to avoid the short-circuit. Return false if no network 195 * can be found at the requested domain and path. Otherwise, return 196 * an object from wp_get_network(). 197 * 198 * @since 3.9.0 199 * 200 * @param null|bool|object $network Network value to return by path. 201 * @param string $domain The requested domain. 202 * @param string $path The requested path, in full. 203 * @param int|null $segments The suggested number of paths to consult. 204 * Default null, meaning the entire path was to be consulted. 205 * @param array $paths The paths to search for, based on $path and $segments. 206 */ 207 $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths ); 208 if ( null !== $pre ) { 209 return $pre; 242 // Bail if no network has been found by now 243 if ( empty( $current_site ) ) { 244 ms_not_installed(); 210 245 } 211 246 212 // @todo Consider additional optimization routes, perhaps as an opt-in for plugins. 213 // We already have paths covered. What about how far domains should be drilled down (including www)? 247 // @todo Investigate when exactly this can occur. 248 if ( empty( $current_blog ) && defined( 'WP_INSTALLING' ) ) { 249 $current_blog = new stdClass; 250 $current_blog->blog_id = $blog_id = 1; 251 } 214 252 215 $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'"; 253 // No site has been found, bail. 254 if ( empty( $current_blog ) ) { 216 255 217 if ( ! $using_paths ) { 218 $network = $wpdb->get_row( "SELECT id, domain, path FROM $wpdb->site 219 WHERE domain IN ($search_domains) ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" ); 220 if ( $network ) { 221 return wp_get_network( $network ); 222 } 223 return false; 256 // We're going to redirect to the network URL, with some possible modifications. 257 $scheme = is_ssl() ? 'https' : 'http'; 258 $destination = "$scheme://{$current_site->domain}{$current_site->path}"; 224 259 225 } else { 226 $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'"; 227 $networks = $wpdb->get_results( "SELECT id, domain, path FROM $wpdb->site 228 WHERE domain IN ($search_domains) AND path IN ($search_paths) 229 ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" ); 230 } 260 /** 261 * Fires when a network can be determined but a site cannot. 262 * 263 * At the time of this action, the only recourse is to redirect somewhere 264 * and exit. If you want to declare a particular site, do so earlier. 265 * 266 * @since 3.9.0 267 * 268 * @param object $current_site The network that had been determined. 269 * @param string $domain The domain used to search for a site. 270 * @param string $path The path used to search for a site. 271 */ 272 do_action( 'ms_site_not_found', $current_site, $domain, $path ); 231 273 232 /* 233 * Domains are sorted by length of domain, then by length of path. 234 * The domain must match for the path to be considered. Otherwise, 235 * a network with the path of / will suffice. 236 */ 237 $found = false; 238 foreach ( $networks as $network ) { 239 if ( $network->domain === $domain || "www.$network->domain" === $domain ) { 240 if ( in_array( $network->path, $paths, true ) ) { 241 $found = true; 242 break; 274 // For a "subdomain" install, redirect to the signup form specifically. 275 if ( is_subdomain_install() && ! defined( 'NOBLOGREDIRECT' ) ) { 276 $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain ); 277 278 // For a "subdomain" install, the NOBLOGREDIRECT constant 279 // can be used to avoid a redirect to the signup form. 280 // Using the ms_site_not_found action is preferred to the constant. 281 } elseif ( is_subdomain_install() ) { 282 if ( '%siteurl%' !== NOBLOGREDIRECT ) { 283 $destination = NOBLOGREDIRECT; 243 284 } 285 286 // If the domain we were searching for matches the network's domain, 287 // it's no use redirecting back to ourselves -- it'll cause a loop. 288 // As we couldn't find a site, we're simply not installed. 289 } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) { 290 ms_not_installed(); 244 291 } 245 if ( $network->path === '/' ) { 246 $found = true; 247 break; 248 } 292 293 header( 'Location: ' . $destination ); 294 exit; 249 295 } 250 296 251 if ( $found ) { 252 return wp_get_network( $network ); 297 // Figure out the current network's main site. 298 if ( ! isset( $current_site->blog_id ) || 0 === $current_site->blog_id ) { 299 if ( ( $current_blog->domain === $current_site->domain ) && ( $current_blog->path === $current_site->path ) ) { 300 $current_site->blog_id = $current_blog->blog_id; 301 } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) { 302 $current_site->blog_id = $wpdb->get_var( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE domain = %s AND path = %s", $current_site->domain, $current_site->path ) ); 303 wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' ); 304 } 253 305 } 254 306 255 return false; 307 // Set some legacy globals 308 $GLOBALS['blog_id'] = $current_blog->blog_id; 309 $GLOBALS['public'] = $current_blog->public; 310 $GLOBALS['site_id'] = $current_blog->site_id; 311 312 // Load the site options 313 wp_load_core_site_options( $current_site->id ); 256 314 } 257 315 258 316 /** 317 * Set the table prefix according to current site 318 * 319 * @since WordPress (4.3.0) 320 * 321 * @global object $wpdb 322 * @global string $table_prefix 323 * @global object $current_blog 324 */ 325 function ms_set_table_prefix() { 326 global $wpdb, $table_prefix, $current_blog; 327 328 $wpdb->set_prefix( $table_prefix, false ); 329 $wpdb->set_blog_id( $current_blog->blog_id, $current_blog->site_id ); 330 $table_prefix = $wpdb->get_blog_prefix(); 331 } 332 333 /** 334 * Set globals used when switching between sites 335 * 336 * @since WordPress (4.3.0) 337 * 338 * @see switch_to_blog() 339 * @see restore_current_blog() 340 */ 341 function ms_set_switched_stacks() { 342 $GLOBALS['_wp_switched_stack'] = array(); 343 $GLOBALS['switched'] = false; 344 } 345 346 /** 347 * Retrieve a network object by its domain and path. 348 * 349 * @since 3.9.0 350 * 351 * @param string $domain Domain to check. 352 * @param string $path Path to check. 353 * @param int|null $segments Path segments to use. Defaults to null, or the full path. 354 * @return object|bool Network object if successful. False when no network is found. 355 */ 356 function get_network_by_path( $domain, $path, $segments = null ) { 357 return WP_Network::get_by_path( $domain, $path, $segments ); 358 } 359 360 /** 259 361 * Retrieve an object containing information about the requested network. 260 362 * 261 363 * @since 3.9.0 … … 264 366 * @return object|bool Object containing network information if found, false if not. 265 367 */ 266 368 function wp_get_network( $network ) { 267 global $wpdb; 268 269 if ( ! is_object( $network ) ) { 270 $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->site WHERE id = %d", $network ) ); 271 if ( ! $network ) { 272 return false; 273 } 274 } 275 276 return $network; 369 return new WP_Network( $network ); 277 370 } 278 371 279 372 /** -
src/wp-includes/ms-settings.php
10 10 * @since 3.0.0 11 11 */ 12 12 13 /** Include Multisite initialization functions */ 13 // Require multisite initialization functions 14 require_once( ABSPATH . WPINC . '/class-wp-network.php' ); 14 15 require_once( ABSPATH . WPINC . '/ms-load.php' ); 15 16 require_once( ABSPATH . WPINC . '/ms-default-constants.php' ); 16 17 18 // Allow custom multisite/network detection & configuration 17 19 if ( defined( 'SUNRISE' ) ) { 18 20 include_once( WP_CONTENT_DIR . '/sunrise.php' ); 19 21 } 20 22 21 / ** Check for and define SUBDOMAIN_INSTALL and the deprecated VHOST constant. */23 // Check for and define SUBDOMAIN_INSTALL (and the deprecated VHOST constant) 22 24 ms_subdomain_constants(); 23 25 24 if ( !isset( $current_site ) || !isset( $current_blog ) ) { 26 // Attempt to set the relevant globals 27 ms_set_current_globals(); 25 28 26 // Given the domain and path, let's try to identify the network and site. 27 // Usually, it's easier to query the site first, which declares its network. 28 // In limited situations, though, we either can or must find the network first. 29 // $table_prefix can be set in sunrise.php 30 ms_set_table_prefix(); 29 31 30 $domain = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) ); 31 if ( substr( $domain, -3 ) == ':80' ) { 32 $domain = substr( $domain, 0, -3 ); 33 $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 ); 34 } elseif ( substr( $domain, -4 ) == ':443' ) { 35 $domain = substr( $domain, 0, -4 ); 36 $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 ); 37 } 32 // Setup site-switching globals 33 ms_set_switched_stacks(); 38 34 39 $path = stripslashes( $_SERVER['REQUEST_URI'] );40 if ( is_admin() ) {41 $path = preg_replace( '#(.*)/wp-admin/.*#', '$1/', $path );42 }43 list( $path ) = explode( '?', $path );44 45 // If the network is defined in wp-config.php, we can simply use that.46 if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) {47 $current_site = new stdClass;48 $current_site->id = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1;49 $current_site->domain = DOMAIN_CURRENT_SITE;50 $current_site->path = PATH_CURRENT_SITE;51 if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) {52 $current_site->blog_id = BLOG_ID_CURRENT_SITE;53 } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated.54 $current_site->blog_id = BLOGID_CURRENT_SITE;55 }56 57 if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) {58 $current_blog = get_site_by_path( $domain, $path );59 } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) {60 // If the current network has a path and also matches the domain and path of the request,61 // we need to look for a site using the first path segment following the network's path.62 $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) );63 } else {64 // Otherwise, use the first path segment (as usual).65 $current_blog = get_site_by_path( $domain, $path, 1 );66 }67 68 } elseif ( ! is_subdomain_install() ) {69 /*70 * A "subdomain" install can be re-interpreted to mean "can support any domain".71 * If we're not dealing with one of these installs, then the important part is determining72 * the network first, because we need the network's path to identify any sites.73 */74 if ( ! $current_site = wp_cache_get( 'current_network', 'site-options' ) ) {75 // Are there even two networks installed?76 $one_network = $wpdb->get_row( "SELECT * FROM $wpdb->site LIMIT 2" ); // [sic]77 if ( 1 === $wpdb->num_rows ) {78 $current_site = wp_get_network( $one_network );79 wp_cache_add( 'current_network', $current_site, 'site-options' );80 } elseif ( 0 === $wpdb->num_rows ) {81 ms_not_installed();82 }83 }84 if ( empty( $current_site ) ) {85 $current_site = get_network_by_path( $domain, $path, 1 );86 }87 88 if ( empty( $current_site ) ) {89 ms_not_installed();90 } elseif ( $path === $current_site->path ) {91 $current_blog = get_site_by_path( $domain, $path );92 } else {93 // Search the network path + one more path segment (on top of the network path).94 $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) );95 }96 } else {97 // Find the site by the domain and at most the first path segment.98 $current_blog = get_site_by_path( $domain, $path, 1 );99 if ( $current_blog ) {100 $current_site = wp_get_network( $current_blog->site_id ? $current_blog->site_id : 1 );101 } else {102 // If you don't have a site with the same domain/path as a network, you're pretty screwed, but:103 $current_site = get_network_by_path( $domain, $path, 1 );104 }105 }106 107 // The network declared by the site trumps any constants.108 if ( $current_blog && $current_blog->site_id != $current_site->id ) {109 $current_site = wp_get_network( $current_blog->site_id );110 }111 112 // No network has been found, bail.113 if ( empty( $current_site ) ) {114 ms_not_installed();115 }116 117 // @todo Investigate when exactly this can occur.118 if ( empty( $current_blog ) && defined( 'WP_INSTALLING' ) ) {119 $current_blog = new stdClass;120 $current_blog->blog_id = $blog_id = 1;121 }122 123 // No site has been found, bail.124 if ( empty( $current_blog ) ) {125 // We're going to redirect to the network URL, with some possible modifications.126 $scheme = is_ssl() ? 'https' : 'http';127 $destination = "$scheme://{$current_site->domain}{$current_site->path}";128 129 /**130 * Fires when a network can be determined but a site cannot.131 *132 * At the time of this action, the only recourse is to redirect somewhere133 * and exit. If you want to declare a particular site, do so earlier.134 *135 * @since 3.9.0136 *137 * @param object $current_site The network that had been determined.138 * @param string $domain The domain used to search for a site.139 * @param string $path The path used to search for a site.140 */141 do_action( 'ms_site_not_found', $current_site, $domain, $path );142 143 if ( is_subdomain_install() && ! defined( 'NOBLOGREDIRECT' ) ) {144 // For a "subdomain" install, redirect to the signup form specifically.145 $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain );146 } elseif ( is_subdomain_install() ) {147 // For a "subdomain" install, the NOBLOGREDIRECT constant148 // can be used to avoid a redirect to the signup form.149 // Using the ms_site_not_found action is preferred to the constant.150 if ( '%siteurl%' !== NOBLOGREDIRECT ) {151 $destination = NOBLOGREDIRECT;152 }153 } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) {154 /*155 * If the domain we were searching for matches the network's domain,156 * it's no use redirecting back to ourselves -- it'll cause a loop.157 * As we couldn't find a site, we're simply not installed.158 */159 ms_not_installed();160 }161 162 header( 'Location: ' . $destination );163 exit;164 }165 166 // @todo What if the domain of the network doesn't match the current site?167 $current_site->cookie_domain = $current_site->domain;168 if ( 'www.' === substr( $current_site->cookie_domain, 0, 4 ) ) {169 $current_site->cookie_domain = substr( $current_site->cookie_domain, 4 );170 }171 172 // Figure out the current network's main site.173 if ( ! isset( $current_site->blog_id ) ) {174 if ( $current_blog->domain === $current_site->domain && $current_blog->path === $current_site->path ) {175 $current_site->blog_id = $current_blog->blog_id;176 } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) {177 $current_site->blog_id = $wpdb->get_var( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE domain = %s AND path = %s",178 $current_site->domain, $current_site->path ) );179 wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' );180 }181 }182 183 $blog_id = $current_blog->blog_id;184 $public = $current_blog->public;185 186 if ( empty( $current_blog->site_id ) ) {187 // This dates to [MU134] and shouldn't be relevant anymore,188 // but it could be possible for arguments passed to insert_blog() etc.189 $current_blog->site_id = 1;190 }191 192 $site_id = $current_blog->site_id;193 wp_load_core_site_options( $site_id );194 }195 196 $wpdb->set_prefix( $table_prefix, false ); // $table_prefix can be set in sunrise.php197 $wpdb->set_blog_id( $current_blog->blog_id, $current_blog->site_id );198 $table_prefix = $wpdb->get_blog_prefix();199 $_wp_switched_stack = array();200 $switched = false;201 202 35 // need to init cache again after blog_id is set 203 36 wp_start_object_cache(); 204 37 205 if ( ! isset( $current_site->site_name ) ) {206 $current_site->site_name = get_site_option( 'site_name' );207 if ( ! $current_site->site_name ) {208 $current_site->site_name = ucfirst( $current_site->domain );209 }210 }211 212 38 // Define upload directory constants 213 39 ms_upload_constants(); -
src/wp-includes/option.php
180 180 } 181 181 182 182 /** 183 * Loads and caches certain often requested site options if is_multisite() and a persistent cache is not being used. 183 * Loads and caches certain often requested site options if is_multisite() and 184 * a persistent cache is not being used. 184 185 * 185 186 * @since 3.0.0 186 187 * 187 * @param int $site_id Optional site ID for which to query the options. Defaults to the current site. 188 * @param int $network_id Optional site ID for which to query the options. 189 * Defaults to the current site. 188 190 */ 189 function wp_load_core_site_options( $ site_id = null ) {191 function wp_load_core_site_options( $network_id = null ) { 190 192 global $wpdb; 191 193 192 if ( !is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) ) 194 // Bail if not multisite 195 if ( ! is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) ) { 193 196 return; 197 } 194 198 195 if ( empty($site_id) ) 196 $site_id = $wpdb->siteid; 199 // Use the current 200 if ( empty( $network_id ) ) { 201 $network_id = $wpdb->siteid; 202 } 197 203 198 $core_options = array('site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting' ); 199 200 $core_options_in = "'" . implode("', '", $core_options) . "'"; 201 $options = $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN ($core_options_in) AND site_id = %d", $site_id) ); 202 203 foreach ( $options as $option ) { 204 $key = $option->meta_key; 205 $cache_key = "{$site_id}:$key"; 206 $option->meta_value = maybe_unserialize( $option->meta_value ); 207 208 wp_cache_set( $cache_key, $option->meta_value, 'site-options' ); 209 } 204 // Load core options for this network 205 WP_Network::get_core_options( $network_id ); 210 206 } 211 207 212 208 /**