/wp-includes/ms-load.php

  1. <?php 
  2. /** 
  3. * These functions are needed to load Multisite. 
  4. * 
  5. * @since 3.0.0 
  6. * 
  7. * @package WordPress 
  8. * @subpackage Multisite 
  9. */ 
  10.  
  11. /** 
  12. * Whether a subdomain configuration is enabled. 
  13. * 
  14. * @since 3.0.0 
  15. * 
  16. * @return bool True if subdomain configuration is enabled, false otherwise. 
  17. */ 
  18. function is_subdomain_install() { 
  19. if ( defined('SUBDOMAIN_INSTALL') ) 
  20. return SUBDOMAIN_INSTALL; 
  21.  
  22. return ( defined( 'VHOST' ) && VHOST == 'yes' ); 
  23.  
  24. /** 
  25. * Returns array of network plugin files to be included in global scope. 
  26. * 
  27. * The default directory is wp-content/plugins. To change the default directory 
  28. * manually, define `WP_PLUGIN_DIR` and `WP_PLUGIN_URL` in `wp-config.php`. 
  29. * 
  30. * @access private 
  31. * @since 3.1.0 
  32. * 
  33. * @return array Files to include. 
  34. */ 
  35. function wp_get_active_network_plugins() { 
  36. $active_plugins = (array) get_site_option( 'active_sitewide_plugins', array() ); 
  37. if ( empty( $active_plugins ) ) 
  38. return array(); 
  39.  
  40. $plugins = array(); 
  41. $active_plugins = array_keys( $active_plugins ); 
  42. sort( $active_plugins ); 
  43.  
  44. foreach ( $active_plugins as $plugin ) { 
  45. if ( ! validate_file( $plugin ) // $plugin must validate as file 
  46. && '.php' == substr( $plugin, -4 ) // $plugin must end with '.php' 
  47. && file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist 
  48. $plugins[] = WP_PLUGIN_DIR . '/' . $plugin; 
  49. return $plugins; 
  50.  
  51. /** 
  52. * Checks status of current blog. 
  53. * 
  54. * Checks if the blog is deleted, inactive, archived, or spammed. 
  55. * 
  56. * Dies with a default message if the blog does not pass the check. 
  57. * 
  58. * To change the default message when a blog does not pass the check,  
  59. * use the wp-content/blog-deleted.php, blog-inactive.php and 
  60. * blog-suspended.php drop-ins. 
  61. * 
  62. * @since 3.0.0 
  63. * 
  64. * @return true|string Returns true on success, or drop-in file to include. 
  65. */ 
  66. function ms_site_check() { 
  67.  
  68. /** 
  69. * Filters checking the status of the current blog. 
  70. * 
  71. * @since 3.0.0 
  72. * 
  73. * @param bool null Whether to skip the blog status check. Default null. 
  74. */ 
  75. $check = apply_filters( 'ms_site_check', null ); 
  76. if ( null !== $check ) 
  77. return true; 
  78.  
  79. // Allow super admins to see blocked sites 
  80. if ( is_super_admin() ) 
  81. return true; 
  82.  
  83. $blog = get_site(); 
  84.  
  85. if ( '1' == $blog->deleted ) { 
  86. if ( file_exists( WP_CONTENT_DIR . '/blog-deleted.php' ) ) 
  87. return WP_CONTENT_DIR . '/blog-deleted.php'; 
  88. else 
  89. wp_die( __( 'This site is no longer available.' ), '', array( 'response' => 410 ) ); 
  90.  
  91. if ( '2' == $blog->deleted ) { 
  92. if ( file_exists( WP_CONTENT_DIR . '/blog-inactive.php' ) ) { 
  93. return WP_CONTENT_DIR . '/blog-inactive.php'; 
  94. } else { 
  95. $admin_email = str_replace( '@', ' AT ', get_site_option( 'admin_email', 'support@' . get_network()->domain ) ); 
  96. wp_die( 
  97. /** translators: %s: admin email link */ 
  98. sprintf( __( 'This site has not been activated yet. If you are having problems activating your site, please contact %s.' ),  
  99. sprintf( '<a href="mailto:%s">%s</a>', $admin_email ) 
  100. ); 
  101.  
  102. if ( $blog->archived == '1' || $blog->spam == '1' ) { 
  103. if ( file_exists( WP_CONTENT_DIR . '/blog-suspended.php' ) ) 
  104. return WP_CONTENT_DIR . '/blog-suspended.php'; 
  105. else 
  106. wp_die( __( 'This site has been archived or suspended.' ), '', array( 'response' => 410 ) ); 
  107.  
  108. return true; 
  109.  
  110. /** 
  111. * Retrieve the closest matching network for a domain and path. 
  112. * 
  113. * @since 3.9.0 
  114. * 
  115. * @internal In 4.4.0, converted to a wrapper for WP_Network::get_by_path() 
  116. * 
  117. * @param string $domain Domain to check. 
  118. * @param string $path Path to check. 
  119. * @param int|null $segments Path segments to use. Defaults to null, or the full path. 
  120. * @return WP_Network|false Network object if successful. False when no network is found. 
  121. */ 
  122. function get_network_by_path( $domain, $path, $segments = null ) { 
  123. return WP_Network::get_by_path( $domain, $path, $segments ); 
  124.  
  125. /** 
  126. * Retrieves the closest matching site object by its domain and path. 
  127. *  
  128. * This will not necessarily return an exact match for a domain and path. Instead, it 
  129. * breaks the domain and path into pieces that are then used to match the closest 
  130. * possibility from a query. 
  131. * 
  132. * The intent of this method is to match a site object during bootstrap for a 
  133. * requested site address 
  134. * 
  135. * @since 3.9.0 
  136. * @since 4.7.0 Updated to always return a `WP_Site` object. 
  137. * 
  138. * @global wpdb $wpdb WordPress database abstraction object. 
  139. * 
  140. * @param string $domain Domain to check. 
  141. * @param string $path Path to check. 
  142. * @param int|null $segments Path segments to use. Defaults to null, or the full path. 
  143. * @return WP_Site|false Site object if successful. False when no site is found. 
  144. */ 
  145. function get_site_by_path( $domain, $path, $segments = null ) { 
  146. $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) ); 
  147.  
  148. /** 
  149. * Filters the number of path segments to consider when searching for a site. 
  150. * 
  151. * @since 3.9.0 
  152. * 
  153. * @param int|null $segments The number of path segments to consider. WordPress by default looks at 
  154. * one path segment following the network path. The function default of 
  155. * null only makes sense when you know the requested path should match a site. 
  156. * @param string $domain The requested domain. 
  157. * @param string $path The requested path, in full. 
  158. */ 
  159. $segments = apply_filters( 'site_by_path_segments_count', $segments, $domain, $path ); 
  160.  
  161. if ( null !== $segments && count( $path_segments ) > $segments ) { 
  162. $path_segments = array_slice( $path_segments, 0, $segments ); 
  163.  
  164. $paths = array(); 
  165.  
  166. while ( count( $path_segments ) ) { 
  167. $paths[] = '/' . implode( '/', $path_segments ) . '/'; 
  168. array_pop( $path_segments ); 
  169.  
  170. $paths[] = '/'; 
  171.  
  172. /** 
  173. * Determine a site by its domain and path. 
  174. * 
  175. * This allows one to short-circuit the default logic, perhaps by 
  176. * replacing it with a routine that is more optimal for your setup. 
  177. * 
  178. * Return null to avoid the short-circuit. Return false if no site 
  179. * can be found at the requested domain and path. Otherwise, return 
  180. * a site object. 
  181. * 
  182. * @since 3.9.0 
  183. * 
  184. * @param null|bool|WP_Site $site Site value to return by path. 
  185. * @param string $domain The requested domain. 
  186. * @param string $path The requested path, in full. 
  187. * @param int|null $segments The suggested number of paths to consult. 
  188. * Default null, meaning the entire path was to be consulted. 
  189. * @param array $paths The paths to search for, based on $path and $segments. 
  190. */ 
  191. $pre = apply_filters( 'pre_get_site_by_path', null, $domain, $path, $segments, $paths ); 
  192. if ( null !== $pre ) { 
  193. if ( false !== $pre && ! $pre instanceof WP_Site ) { 
  194. $pre = new WP_Site( $pre ); 
  195. return $pre; 
  196.  
  197. /** 
  198. * @todo 
  199. * caching, etc. Consider alternative optimization routes,  
  200. * perhaps as an opt-in for plugins, rather than using the pre_* filter. 
  201. * For example: The segments filter can expand or ignore paths. 
  202. * If persistent caching is enabled, we could query the DB for a path <> '/' 
  203. * then cache whether we can just always ignore paths. 
  204. */ 
  205.  
  206. // Either www or non-www is supported, not both. If a www domain is requested,  
  207. // query for both to provide the proper redirect. 
  208. $domains = array( $domain ); 
  209. if ( 'www.' === substr( $domain, 0, 4 ) ) { 
  210. $domains[] = substr( $domain, 4 ); 
  211.  
  212. $args = array( 
  213. 'domain__in' => $domains,  
  214. 'path__in' => $paths,  
  215. 'number' => 1,  
  216. ); 
  217.  
  218. if ( count( $domains ) > 1 ) { 
  219. $args['orderby']['domain_length'] = 'DESC'; 
  220.  
  221. if ( count( $paths ) > 1 ) { 
  222. $args['orderby']['path_length'] = 'DESC'; 
  223.  
  224. $result = get_sites( $args ); 
  225. $site = array_shift( $result ); 
  226.  
  227. if ( $site ) { 
  228. return $site; 
  229.  
  230. return false; 
  231.  
  232. /** 
  233. * Identifies the network and site of a requested domain and path and populates the 
  234. * corresponding network and site global objects as part of the multisite bootstrap process. 
  235. * 
  236. * Prior to 4.6.0, this was a procedural block in `ms-settings.php`. It was wrapped into 
  237. * a function to facilitate unit tests. It should not be used outside of core. 
  238. * 
  239. * Usually, it's easier to query the site first, which then declares its network. 
  240. * In limited situations, we either can or must find the network first. 
  241. * 
  242. * If a network and site are found, a `true` response will be returned so that the 
  243. * request can continue. 
  244. * 
  245. * If neither a network or site is found, `false` or a URL string will be returned 
  246. * so that either an error can be shown or a redirect can occur. 
  247. * 
  248. * @since 4.6.0 
  249. * @access private 
  250. * 
  251. * @global wpdb $wpdb WordPress database abstraction object. 
  252. * @global WP_Network $current_site The current network. 
  253. * @global WP_Site $current_blog The current site. 
  254. * 
  255. * @param string $domain The requested domain. 
  256. * @param string $path The requested path. 
  257. * @param bool $subdomain Optional. Whether a subdomain (true) or subdirectory (false) configuration. 
  258. * Default false. 
  259. * @return bool|string True if bootstrap successfully populated `$current_blog` and `$current_site`. 
  260. * False if bootstrap could not be properly completed. 
  261. * Redirect URL if parts exist, but the request as a whole can not be fulfilled. 
  262. */ 
  263. function ms_load_current_site_and_network( $domain, $path, $subdomain = false ) { 
  264. global $wpdb, $current_site, $current_blog; 
  265.  
  266. // If the network is defined in wp-config.php, we can simply use that. 
  267. if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) { 
  268. $current_site = new stdClass; 
  269. $current_site->id = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1; 
  270. $current_site->domain = DOMAIN_CURRENT_SITE; 
  271. $current_site->path = PATH_CURRENT_SITE; 
  272. if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) { 
  273. $current_site->blog_id = BLOG_ID_CURRENT_SITE; 
  274. } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated. 
  275. $current_site->blog_id = BLOGID_CURRENT_SITE; 
  276.  
  277. if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) { 
  278. $current_blog = get_site_by_path( $domain, $path ); 
  279. } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) { 
  280. // If the current network has a path and also matches the domain and path of the request,  
  281. // we need to look for a site using the first path segment following the network's path. 
  282. $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) ); 
  283. } else { 
  284. // Otherwise, use the first path segment (as usual). 
  285. $current_blog = get_site_by_path( $domain, $path, 1 ); 
  286.  
  287. } elseif ( ! $subdomain ) { 
  288. /** 
  289. * A "subdomain" install can be re-interpreted to mean "can support any domain". 
  290. * If we're not dealing with one of these installs, then the important part is determining 
  291. * the network first, because we need the network's path to identify any sites. 
  292. */ 
  293. if ( ! $current_site = wp_cache_get( 'current_network', 'site-options' ) ) { 
  294. // Are there even two networks installed? 
  295. $one_network = $wpdb->get_row( "SELECT * FROM $wpdb->site LIMIT 2" ); // [sic] 
  296. if ( 1 === $wpdb->num_rows ) { 
  297. $current_site = new WP_Network( $one_network ); 
  298. wp_cache_add( 'current_network', $current_site, 'site-options' ); 
  299. } elseif ( 0 === $wpdb->num_rows ) { 
  300. // A network not found hook should fire here. 
  301. return false; 
  302.  
  303. if ( empty( $current_site ) ) { 
  304. $current_site = WP_Network::get_by_path( $domain, $path, 1 ); 
  305.  
  306. if ( empty( $current_site ) ) { 
  307. /** 
  308. * Fires when a network cannot be found based on the requested domain and path. 
  309. * 
  310. * At the time of this action, the only recourse is to redirect somewhere 
  311. * and exit. If you want to declare a particular network, do so earlier. 
  312. * 
  313. * @since 4.4.0 
  314. * 
  315. * @param string $domain The domain used to search for a network. 
  316. * @param string $path The path used to search for a path. 
  317. */ 
  318. do_action( 'ms_network_not_found', $domain, $path ); 
  319.  
  320. return false; 
  321. } elseif ( $path === $current_site->path ) { 
  322. $current_blog = get_site_by_path( $domain, $path ); 
  323. } else { 
  324. // Search the network path + one more path segment (on top of the network path). 
  325. $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) ); 
  326. } else { 
  327. // Find the site by the domain and at most the first path segment. 
  328. $current_blog = get_site_by_path( $domain, $path, 1 ); 
  329. if ( $current_blog ) { 
  330. $current_site = WP_Network::get_instance( $current_blog->site_id ? $current_blog->site_id : 1 ); 
  331. } else { 
  332. // If you don't have a site with the same domain/path as a network, you're pretty screwed, but: 
  333. $current_site = WP_Network::get_by_path( $domain, $path, 1 ); 
  334.  
  335. // The network declared by the site trumps any constants. 
  336. if ( $current_blog && $current_blog->site_id != $current_site->id ) { 
  337. $current_site = WP_Network::get_instance( $current_blog->site_id ); 
  338.  
  339. // No network has been found, bail. 
  340. if ( empty( $current_site ) ) { 
  341. /** This action is documented in wp-includes/ms-settings.php */ 
  342. do_action( 'ms_network_not_found', $domain, $path ); 
  343.  
  344. return false; 
  345.  
  346. // During activation of a new subdomain, the requested site does not yet exist. 
  347. if ( empty( $current_blog ) && wp_installing() ) { 
  348. $current_blog = new stdClass; 
  349. $current_blog->blog_id = $blog_id = 1; 
  350. $current_blog->public = 1; 
  351.  
  352. // No site has been found, bail. 
  353. if ( empty( $current_blog ) ) { 
  354. // We're going to redirect to the network URL, with some possible modifications. 
  355. $scheme = is_ssl() ? 'https' : 'http'; 
  356. $destination = "$scheme://{$current_site->domain}{$current_site->path}"; 
  357.  
  358. /** 
  359. * Fires when a network can be determined but a site cannot. 
  360. * 
  361. * At the time of this action, the only recourse is to redirect somewhere 
  362. * and exit. If you want to declare a particular site, do so earlier. 
  363. * 
  364. * @since 3.9.0 
  365. * 
  366. * @param object $current_site The network that had been determined. 
  367. * @param string $domain The domain used to search for a site. 
  368. * @param string $path The path used to search for a site. 
  369. */ 
  370. do_action( 'ms_site_not_found', $current_site, $domain, $path ); 
  371.  
  372. if ( $subdomain && ! defined( 'NOBLOGREDIRECT' ) ) { 
  373. // For a "subdomain" install, redirect to the signup form specifically. 
  374. $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain ); 
  375. } elseif ( $subdomain ) { 
  376. // For a "subdomain" install, the NOBLOGREDIRECT constant 
  377. // can be used to avoid a redirect to the signup form. 
  378. // Using the ms_site_not_found action is preferred to the constant. 
  379. if ( '%siteurl%' !== NOBLOGREDIRECT ) { 
  380. $destination = NOBLOGREDIRECT; 
  381. } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) { 
  382. /** 
  383. * If the domain we were searching for matches the network's domain,  
  384. * it's no use redirecting back to ourselves -- it'll cause a loop. 
  385. * As we couldn't find a site, we're simply not installed. 
  386. */ 
  387. return false; 
  388.  
  389. return $destination; 
  390.  
  391. // Figure out the current network's main site. 
  392. if ( empty( $current_site->blog_id ) ) { 
  393. if ( $current_blog->domain === $current_site->domain && $current_blog->path === $current_site->path ) { 
  394. $current_site->blog_id = $current_blog->blog_id; 
  395. } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) { 
  396. $current_site->blog_id = $wpdb->get_var( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE domain = %s AND path = %s",  
  397. $current_site->domain, $current_site->path ) ); 
  398. wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' ); 
  399.  
  400. return true; 
  401.  
  402. /** 
  403. * Displays a failure message. 
  404. * 
  405. * Used when a blog's tables do not exist. Checks for a missing $wpdb->site table as well. 
  406. * 
  407. * @access private 
  408. * @since 3.0.0 
  409. * @since 4.4.0 The `$domain` and `$path` parameters were added. 
  410. * 
  411. * @global wpdb $wpdb WordPress database abstraction object. 
  412. * 
  413. * @param string $domain The requested domain for the error to reference. 
  414. * @param string $path The requested path for the error to reference. 
  415. */ 
  416. function ms_not_installed( $domain, $path ) { 
  417. global $wpdb; 
  418.  
  419. if ( ! is_admin() ) { 
  420. dead_db(); 
  421.  
  422. wp_load_translations_early(); 
  423.  
  424. $title = __( 'Error establishing a database connection' ); 
  425.  
  426. $msg = '<h1>' . $title . '</h1>'; 
  427. $msg .= '<p>' . __( 'If your site does not display, please contact the owner of this network.' ) . ''; 
  428. $msg .= ' ' . __( 'If you are the owner of this network please check that MySQL is running properly and all tables are error free.' ) . '</p>'; 
  429. $query = $wpdb->prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $wpdb->site ) ); 
  430. if ( ! $wpdb->get_var( $query ) ) { 
  431. $msg .= '<p>' . sprintf( 
  432. /** translators: %s: table name */ 
  433. __( '<strong>Database tables are missing.</strong> This means that MySQL is not running, WordPress was not installed properly, or someone deleted %s. You really should look at your database now.' ),  
  434. '<code>' . $wpdb->site . '</code>' 
  435. ) . '</p>'; 
  436. } else { 
  437. $msg .= '<p>' . sprintf( 
  438. /** translators: 1: site url, 2: table name, 3: database name */ 
  439. __( '<strong>Could not find site %1$s.</strong> Searched for table %2$s in database %3$s. Is that right?' ),  
  440. '<code>' . rtrim( $domain . $path, '/' ) . '</code>',  
  441. '<code>' . $wpdb->blogs . '</code>',  
  442. '<code>' . DB_NAME . '</code>' 
  443. ) . '</p>'; 
  444. $msg .= '<p><strong>' . __( 'What do I do now?' ) . '</strong> '; 
  445. /** translators: %s: Codex URL */ 
  446. $msg .= sprintf( __( 'Read the <a href="%s" target="_blank">bug report</a> page. Some of the guidelines there may help you figure out what went wrong.' ),  
  447. __( 'https://codex.wordpress.org/Debugging_a_WordPress_Network' ) 
  448. ); 
  449. $msg .= ' ' . __( 'If you’re still stuck with this message, then check that your database contains the following tables:' ) . '</p><ul>'; 
  450. foreach ( $wpdb->tables('global') as $t => $table ) { 
  451. if ( 'sitecategories' == $t ) 
  452. continue; 
  453. $msg .= '<li>' . $table . '</li>'; 
  454. $msg .= '</ul>'; 
  455.  
  456. wp_die( $msg, $title, array( 'response' => 500 ) ); 
  457.  
  458. /** 
  459. * This deprecated function formerly set the site_name property of the $current_site object. 
  460. * 
  461. * This function simply returns the object, as before. 
  462. * The bootstrap takes care of setting site_name. 
  463. * 
  464. * @access private 
  465. * @since 3.0.0 
  466. * @deprecated 3.9.0 Use get_current_site() instead. 
  467. * 
  468. * @param object $current_site 
  469. * @return object 
  470. */ 
  471. function get_current_site_name( $current_site ) { 
  472. _deprecated_function( __FUNCTION__, '3.9.0', 'get_current_site()' ); 
  473. return $current_site; 
  474.  
  475. /** 
  476. * This deprecated function managed much of the site and network loading in multisite. 
  477. * 
  478. * The current bootstrap code is now responsible for parsing the site and network load as 
  479. * well as setting the global $current_site object. 
  480. * 
  481. * @access private 
  482. * @since 3.0.0 
  483. * @deprecated 3.9.0 
  484. * 
  485. * @global object $current_site 
  486. * 
  487. * @return object 
  488. */ 
  489. function wpmu_current_site() { 
  490. global $current_site; 
  491. _deprecated_function( __FUNCTION__, '3.9.0' ); 
  492. return $current_site; 
  493.  
  494. /** 
  495. * Retrieve an object containing information about the requested network. 
  496. * 
  497. * @since 3.9.0 
  498. * @deprecated 4.7.0 Use `get_network()` 
  499. * @see get_network() 
  500. * 
  501. * @internal In 4.6.0, converted to use get_network() 
  502. * 
  503. * @param object|int $network The network's database row or ID. 
  504. * @return WP_Network|false Object containing network information if found, false if not. 
  505. */ 
  506. function wp_get_network( $network ) { 
  507. _deprecated_function( __FUNCTION__, '4.7.0', 'get_network()' ); 
  508.  
  509. $network = get_network( $network ); 
  510. if ( null === $network ) { 
  511. return false; 
  512.  
  513. return $network; 
.