diff --git a/src/wp-includes/class-wp-network.php b/src/wp-includes/class-wp-network.php index 311f3378a5..bce09a2f2e 100644 --- a/src/wp-includes/class-wp-network.php +++ b/src/wp-includes/class-wp-network.php @@ -148,9 +148,9 @@ class WP_Network { case 'id': return (int) $this->id; case 'blog_id': - return $this->blog_id; + return $this->get_main_site_id(); case 'site_id': - return (int) $this->blog_id; + return (int) $this->get_main_site_id(); } return null; @@ -201,6 +201,25 @@ class WP_Network { } } + /** + * Returns the main site ID for the network. + * + * Internal method used by the magic getter for the 'blog_id' and 'site_id' + * properties. + * + * @since 4.9.0 + * @see get_main_site_id() + * + * @return string Main site ID as numeric string, for compatibility reasons. + */ + private function get_main_site_id() { + if ( empty( $this->blog_id ) ) { + $this->blog_id = (string) get_main_site_id( $this->id ); + } + + return $this->blog_id; + } + /** * Set the site name assigned to the network if one has not been populated. * diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index dfbb0bcfe6..bab1f7463b 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -4390,19 +4390,92 @@ function wp_suspend_cache_invalidation( $suspend = true ) { * Determine whether a site is the main site of the current network. * * @since 3.0.0 + * @since 4.9.0 The $network_id parameter has been added. * - * @param int $site_id Optional. Site ID to test. Defaults to current site. + * @param int $site_id Optional. Site ID to test. Defaults to current site. + * @param int $network_id Optional. Network ID of the network to check for. + * Defaults to current network. * @return bool True if $site_id is the main site of the network, or if not * running Multisite. */ -function is_main_site( $site_id = null ) { - if ( ! is_multisite() ) +function is_main_site( $site_id = null, $network_id = null ) { + if ( ! is_multisite() ) { return true; + } - if ( ! $site_id ) + if ( ! $site_id ) { $site_id = get_current_blog_id(); + } - return (int) $site_id === (int) get_network()->site_id; + $site_id = (int) $site_id; + + return $site_id === get_main_site_id( $network_id ); +} + +/** + * Gets the main site ID. + * + * @since 4.9.0 + * + * @param int $network_id Optional. The ID of the network for which to get the main site. + * Defaults to the current network. + * @return int The ID of the main site. + */ +function get_main_site_id( $network_id = null ) { + if ( ! is_multisite() ) { + return 1; + } + + $network = get_network( $network_id ); + if ( ! $network ) { + return 0; + } + + /** + * Filters the main site ID. + * + * Returning anything other than null will effectively short-circuit the function, returning + * the result parsed as an integer immediately. + * + * @since 4.9.0 + * + * @param int|null $main_site_id If anything other than null is returned, it is interpreted as the main site ID. + * @param int $network_id The ID of the network for which the main site was detected. + */ + $main_site_id = apply_filters( 'pre_get_main_site_id', null, $network->id ); + if ( null !== $main_site_id ) { + return (int) $main_site_id; + } + + if ( ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) && $network->domain === DOMAIN_CURRENT_SITE && $network->path === PATH_CURRENT_SITE ) + || ( defined( 'SITE_ID_CURRENT_SITE' ) && $network->id == SITE_ID_CURRENT_SITE ) ) { + if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) { + return BLOG_ID_CURRENT_SITE; + } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated. + return BLOGID_CURRENT_SITE; + } + } + + $site = get_site(); + if ( $site->domain === $network->domain && $site->path === $network->path ) { + $main_site_id = (int) $site->id; + } else { + $main_site_id = wp_cache_get( 'network:' . $network->id . ':main_site', 'site-options' ); + if ( false === $main_site_id ) { + $_sites = get_sites( array( + 'fields' => 'ids', + 'number' => 1, + 'domain' => $network->domain, + 'path' => $network->path, + 'network_id' => $network->id, + ) ); + $main_site_id = ! empty( $_sites ) ? array_shift( $_sites ) : 0; + + wp_cache_add( 'network:' . $network->id . ':main_site', $main_site_id, 'site-options' ); + } + } + + return (int) $main_site_id; } /** diff --git a/src/wp-includes/ms-load.php b/src/wp-includes/ms-load.php index 3c51b3d158..7a00745a95 100644 --- a/src/wp-includes/ms-load.php +++ b/src/wp-includes/ms-load.php @@ -135,7 +135,7 @@ function get_network_by_path( $domain, $path, $segments = null ) { /** * Retrieves the closest matching site object by its domain and path. - * + * * This will not necessarily return an exact match for a domain and path. Instead, it * breaks the domain and path into pieces that are then used to match the closest * possibility from a query. @@ -424,13 +424,7 @@ function ms_load_current_site_and_network( $domain, $path, $subdomain = false ) // Figure out the current network's main site. if ( empty( $current_site->blog_id ) ) { - if ( $current_blog->domain === $current_site->domain && $current_blog->path === $current_site->path ) { - $current_site->blog_id = $current_blog->blog_id; - } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) { - $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 ) ); - wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' ); - } + $current_site->blog_id = get_main_site_id( $current_site->id ); } return true; diff --git a/tests/phpunit/tests/multisite/getMainSiteId.php b/tests/phpunit/tests/multisite/getMainSiteId.php new file mode 100644 index 0000000000..a5a719e92b --- /dev/null +++ b/tests/phpunit/tests/multisite/getMainSiteId.php @@ -0,0 +1,110 @@ + array( 'domain' => 'wordpress.org', 'path' => '/' ), + 'wp.org/' => array( 'domain' => 'wp.org', 'path' => '/' ), // A network with no sites. + ); + + foreach ( self::$network_ids as &$id ) { + $id = $factory->network->create( $id ); + } + unset( $id ); + + self::$site_ids = array( + 'www.w.org/' => array( 'domain' => 'www.w.org', 'path' => '/' ), + 'wordpress.org/' => array( 'domain' => 'wordpress.org', 'path' => '/', 'site_id' => self::$network_ids['wordpress.org/'] ), + 'wordpress.org/foo/' => array( 'domain' => 'wordpress.org', 'path' => '/foo/', 'site_id' => self::$network_ids['wordpress.org/'] ), + ); + + foreach ( self::$site_ids as &$id ) { + $id = $factory->blog->create( $id ); + } + unset( $id ); + } + + public static function wpTearDownAfterClass() { + foreach( self::$site_ids as $id ) { + wpmu_delete_blog( $id, true ); + } + + global $wpdb; + + foreach( self::$network_ids as $id ) { + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->sitemeta} WHERE site_id = %d", $id ) ); + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id= %d", $id ) ); + } + + wp_update_network_site_counts(); + } + + /** + * @ticket 29684 + */ + public function test_get_main_site_id_on_main_site_returns_self() { + $this->assertSame( get_current_blog_id(), get_main_site_id() ); + } + + /** + * @ticket 29684 + */ + public function test_get_main_site_id_returns_main_site_in_switched_context() { + $main_site_id = get_current_blog_id(); + $other_site_id = self::$site_ids['www.w.org/']; + + switch_to_blog( $other_site_id ); + $result = get_main_site_id(); + restore_current_blog(); + + $this->assertSame( $main_site_id, $result ); + } + + /** + * @ticket 29684 + */ + public function test_get_main_site_id_with_different_network_returns_correct_id() { + $this->assertSame( self::$site_ids['wordpress.org/'], get_main_site_id( self::$network_ids['wordpress.org/'] ) ); + } + + /** + * @ticket 29684 + */ + public function test_get_main_site_id_on_network_without_site_returns_0() { + $this->assertSame( 0, get_main_site_id( self::$network_ids['wp.org/'] ) ); + } + + /** + * @ticket 29684 + */ + public function test_get_main_site_id_on_invalid_network_returns_0() { + $this->assertSame( 0, get_main_site_id( 333 ) ); + } + + /** + * @ticket 29684 + */ + public function test_get_main_site_id_filtered() { + add_filter( 'pre_get_main_site_id', array( $this, 'filter_get_main_site_id' ) ); + $result = get_main_site_id(); + + $this->assertSame( 333, $result ); + } + + public function filter_get_main_site_id() { + return 333; + } +} + +endif; diff --git a/tests/phpunit/tests/multisite/network.php b/tests/phpunit/tests/multisite/network.php index 671cd36419..33c59802d3 100644 --- a/tests/phpunit/tests/multisite/network.php +++ b/tests/phpunit/tests/multisite/network.php @@ -583,6 +583,15 @@ class Tests_Multisite_Network extends WP_UnitTestCase { $this->assertEquals( $original_count + 1, $result ); } + + /** + * @ticket 29684 + */ + public function test_network_blog_id_set() { + $network = get_network( self::$different_network_id ); + + $this->assertSame( (string) self::$different_site_ids[0], $network->blog_id ); + } } endif; diff --git a/tests/phpunit/tests/multisite/site.php b/tests/phpunit/tests/multisite/site.php index 64dc67d48e..47981d2f41 100644 --- a/tests/phpunit/tests/multisite/site.php +++ b/tests/phpunit/tests/multisite/site.php @@ -11,6 +11,7 @@ if ( is_multisite() ) : class Tests_Multisite_Site extends WP_UnitTestCase { protected $suppress = false; protected static $network_ids; + protected static $site_ids; function setUp() { global $wpdb; @@ -33,11 +34,25 @@ class Tests_Multisite_Site extends WP_UnitTestCase { $id = $factory->network->create( $id ); } unset( $id ); + + self::$site_ids = array( + 'make.wordpress.org/' => array( 'domain' => 'make.wordpress.org', 'path' => '/', 'site_id' => self::$network_ids['make.wordpress.org/'] ), + 'make.wordpress.org/foo/' => array( 'domain' => 'make.wordpress.org', 'path' => '/foo/', 'site_id' => self::$network_ids['make.wordpress.org/'] ), + ); + + foreach ( self::$site_ids as &$id ) { + $id = $factory->blog->create( $id ); + } + unset( $id ); } public static function wpTearDownAfterClass() { global $wpdb; + foreach( self::$site_ids as $id ) { + wpmu_delete_blog( $id, true ); + } + foreach( self::$network_ids as $id ) { $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->sitemeta} WHERE site_id = %d", $id ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id= %d", $id ) ); @@ -1028,6 +1043,20 @@ class Tests_Multisite_Site extends WP_UnitTestCase { function filter_allow_unavailable_languages( $value, $option, $original_value ) { return $original_value; } + + /** + * @ticket 29684 + */ + public function test_is_main_site_different_network() { + $this->assertTrue( is_main_site( self::$site_ids['make.wordpress.org/'], self::$network_ids['make.wordpress.org/'] ) ); + } + + /** + * @ticket 29684 + */ + public function test_is_main_site_different_network_random_site() { + $this->assertFalse( is_main_site( self::$site_ids['make.wordpress.org/foo/'], self::$network_ids['make.wordpress.org/'] ) ); + } } endif;