diff --git a/src/wp-includes/ms-blogs.php b/src/wp-includes/ms-blogs.php index b2fffeab16..deab15bae9 100644 --- a/src/wp-includes/ms-blogs.php +++ b/src/wp-includes/ms-blogs.php @@ -535,6 +535,118 @@ function get_site( $site = null ) { return $_site; } +/** + * Retrieves a site by a given field and value. + * + * @since 4.9.0 + * + * @param string $field Name of a field to query against. Accepts 'id', 'slug', 'url', + * 'domain' (if a subdomain install) or 'path' (if a subdirectory install). + * @param string|int $value The search value for $field. + * @param int|null $network_id Optional. ID of the network. Default is the current network. + * @return WP_Site|null The site object or null if not found. + */ +function get_site_by( $field, $value, $network_id = null ) { + $args = array(); + + // `get_sites()` will return unexpected results if empty strings are passed as arguments. + if ( 'slug' === $field || 'url' === $field || 'domain' === $field || 'path' === $field ) { + if ( ! is_string( $value ) && ! is_numeric( $value ) ) { + return null; + } + + $value = trim( $value ); + + if ( 0 === strlen( $value ) ) { + return null; + } + } + + switch ( $field ) { + case 'id': + if ( ! is_numeric( $value ) ) { + return null; + } + $args['site__in'][] = intval( $value ); + break; + case 'slug': + $network = get_network( $network_id ); + if ( ! $network ) { + return null; + } + + if ( is_subdomain_install() ) { + $args['domain'] = trim( $value, '/' ) . '.' . preg_replace( '|^www\.|', '', $network->domain ); + $args['path'] = $network->path; + } else { + $args['domain'] = $network->domain; + $args['path'] = $network->path . trim( $value, '/' ) . '/'; + } + break; + case 'url': + if ( 0 !== strpos( $value, 'http://' ) && 0 !== strpos( $value, 'https://' ) ) { + $value = 'http://' . $value; + } + + $parts = wp_parse_url( $value ); + if ( ! $parts ) { + return null; + } + + $args['domain'] = $parts['host']; + if ( ! empty( $parts['path'] ) ) { + $args['path'] = '/' . trim( $parts['path'], '/' ) . '/'; + } else { + $args['path'] = '/'; + } + break; + case 'domain': + if ( ! is_subdomain_install() ) { + return null; + } + + $args['domain'] = $value; + break; + case 'path': + if ( is_subdomain_install() ) { + return null; + } + + $args['path'] = '/' . trim( $value, '/' ) . '/'; + break; + default: + return null; + } + + if ( isset( $args['domain'] ) && substr( $args['domain'], 0, 4 ) === 'www.' ) { + $nowww = substr( $args['domain'], 4 ); + + $args['domain__in'] = array( $nowww, $args['domain'] ); + unset( $args['domain'] ); + + $args['orderby'] = 'domain_length'; + $args['order'] = 'DESC'; + } + + if ( isset( $args['path'] ) ) { + $args['path'] = str_replace( '//', '/', $args['path'] ); + } + + if ( ! empty( $network_id ) ) { + $args['network_id'] = (int) $network_id; + } + + $args['number'] = 1; + + $sites = get_sites( $args ); + + if ( empty( $sites ) ) { + return null; + } + + return array_shift( $sites ); +} + /** * Adds any sites from the given ids to the cache that do not already exist in cache. * diff --git a/tests/phpunit/tests/multisite/getSiteBy.php b/tests/phpunit/tests/multisite/getSiteBy.php new file mode 100644 index 0000000000..1ff5c455e1 --- /dev/null +++ b/tests/phpunit/tests/multisite/getSiteBy.php @@ -0,0 +1,252 @@ + array( 'domain' => 'wordpress.org', 'path' => '/' ), + 'www.wordpress.net/' => array( 'domain' => 'www.wordpress.net', 'path' => '/' ), + ); + + foreach ( self::$network_ids as &$id ) { + $id = $factory->network->create( $id ); + } + unset( $id ); + + self::$site_ids = array( + 'wordpress.org/' => array( 'domain' => 'wordpress.org', 'path' => '/', 'site_id' => self::$network_ids['wordpress.org/'] ), + 'foo.wordpress.org/' => array( 'domain' => 'foo.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/'] ), + 'www.wordpress.org/' => array( 'domain' => 'www.wordpress.org', 'path' => '/', 'site_id' => self::$network_ids['wordpress.org/'] ), + 'www.wordpress.net/' => array( 'domain' => 'www.wordpress.net', 'path' => '/', 'site_id' => self::$network_ids['www.wordpress.net/'] ), + 'foo.wordpress.net/' => array( 'domain' => 'foo.wordpress.net', 'path' => '/', 'site_id' => self::$network_ids['www.wordpress.net/'] ), + 'www.wordpress.net/foo/' => array( 'domain' => 'www.wordpress.net', 'path' => '/foo/', 'site_id' => self::$network_ids['www.wordpress.net/'] ), + ); + + 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 ) ); + } + + wp_update_network_site_counts(); + } + + public function test_get_site_by_id_with_valid_id() { + $result = get_site_by( 'id', self::$site_ids['wordpress.org/'] ); + + $this->assertEquals( self::$site_ids['wordpress.org/'], $result->id ); + } + + public function test_get_site_by_id_with_invalid_id() { + $result = get_site_by( 'id', 'wp.org' ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_slug_subdomain() { + if ( ! is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdomain configuration.' ); + } + + $result = get_site_by( 'slug', 'foo', self::$network_ids['wordpress.org/'] ); + + $this->assertEquals( self::$site_ids['foo.wordpress.org/'], $result->id ); + } + + public function test_get_site_by_slug_with_www_subdomain() { + if ( ! is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdomain configuration.' ); + } + + $result = get_site_by( 'slug', 'foo', self::$network_ids['www.wordpress.net/'] ); + + $this->assertEquals( self::$site_ids['foo.wordpress.net/'], $result->id ); + } + + public function test_get_site_by_slug_subdirectory() { + if ( is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdirectory configuration.' ); + } + + $result = get_site_by( 'slug', 'foo', self::$network_ids['wordpress.org/'] ); + + $this->assertEquals( self::$site_ids['wordpress.org/foo/'], $result->id ); + } + + public function test_get_site_by_slug_with_www_subdirectory() { + if ( is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdirectory configuration.' ); + } + + $result = get_site_by( 'slug', 'foo', self::$network_ids['www.wordpress.net/'] ); + + $this->assertEquals( self::$site_ids['www.wordpress.net/foo/'], $result->id ); + } + + public function test_get_site_by_slug_with_first_network() { + $result = get_site_by( 'slug', 'foo', self::$network_ids['wordpress.org/'] ); + + if ( is_subdomain_install() ) { + $this->assertEquals( self::$site_ids['foo.wordpress.org/'], $result->id ); + } else { + $this->assertEquals( self::$site_ids['wordpress.org/foo/'], $result->id ); + } + } + + public function test_get_site_by_slug_with_second_network() { + $result = get_site_by( 'slug', 'foo', self::$network_ids['www.wordpress.net/'] ); + + if ( is_subdomain_install() ) { + $this->assertEquals( self::$site_ids['foo.wordpress.net/'], $result->id ); + } else { + $this->assertEquals( self::$site_ids['www.wordpress.net/foo/'], $result->id ); + } + } + + public function test_get_site_by_slug_with_invalid_network() { + $result = get_site_by( 'slug', 'foo', 444 ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_slug_with_empty_string_returns_null() { + $result = get_site_by( 'slug', '' ); + + $this->assertNull( $result ); + } + + /** + * @dataProvider data_get_site_by_url + */ + public function test_get_site_by_url( $url, $expected ) { + $result = get_site_by( 'url', $url ); + + $this->assertEquals( self::$site_ids[ $expected ], $result->id ); + } + + public function data_get_site_by_url() { + return array( + array( + 'wordpress.org/foo/', + 'wordpress.org/foo/', + ), + array( + 'wordpress.org/foo', + 'wordpress.org/foo/', + ), + array( + 'foo.wordpress.org/', + 'foo.wordpress.org/', + ), + array( + 'foo.wordpress.org', + 'foo.wordpress.org/', + ), + array( + 'www.wordpress.net/', + 'www.wordpress.net/', + ), + array( + 'www.wordpress.org/', + 'www.wordpress.org/', + ), + ); + } + + public function test_get_site_by_url_with_empty_string_returns_null() { + $result = get_site_by( 'url', '' ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_url_with_invalid_url() { + $result = get_site_by( 'url', 'not a url' ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_domain_subdomain() { + if ( ! is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdomain configuration.' ); + } + + $result = get_site_by( 'domain', 'foo.wordpress.org' ); + + $this->assertEquals( self::$site_ids['foo.wordpress.org/'], $result->id ); + } + + public function test_get_site_by_domain_subdirectory() { + if ( is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdirectory configuration.' ); + } + + $result = get_site_by( 'domain', 'foo.wordpress.org' ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_domain_with_empty_string_returns_null() { + if ( ! is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdomain configuration.' ); + } + + $result = get_site_by( 'domain', '' ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_path_subdomain() { + if ( ! is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdomain configuration.' ); + } + + $result = get_site_by( 'path', '/foo/' ); + + $this->assertNull( $result ); + } + + public function test_get_site_by_path_subdirectory() { + if ( is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdirectory configuration.' ); + } + + $result = get_site_by( 'path', '/foo/' ); + + $this->assertEquals( self::$site_ids['wordpress.org/foo/'], $result->id ); + } + + public function test_get_site_by_path_with_empty_string_returns_null() { + if ( is_subdomain_install() ) { + $this->markTestSkipped( 'This test is only valid in a subdirectory configuration.' ); + } + + $result = get_site_by( 'path', '' ); + + $this->assertNull( $result ); + } +} + +endif;