Skip to content
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
"site deactivate",
"site delete",
"site empty",
"site get",
"site list",
"site mature",
"site meta",
Expand Down
128 changes: 128 additions & 0 deletions features/site.feature
Original file line number Diff line number Diff line change
Expand Up @@ -763,3 +763,131 @@ Feature: Manage sites in a multisite installation
Then STDOUT should be a table containing rows:
| blog_id | public |
| 2 | 1 |

Scenario: Get site by ID
Given a WP multisite install

When I run `wp site create --slug=testsite --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}

When I run `wp site get {SITE_ID} --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""

When I run `wp site get {SITE_ID}`
Then STDOUT should be a table containing rows:
| Field | Value |
| blog_id | {SITE_ID} |

Scenario: Get site by URL
Given a WP multisite install

When I run `wp site create --slug=testsite --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}
And I run `wp site list --blog_id={SITE_ID} --field=url`
And save STDOUT as {SITE_URL}

When I run `wp site get {SITE_URL} --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""

Scenario: Get site by URL with subdirectory
Given a WP multisite subdirectory install

When I run `wp site create --slug=mysubdir --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}

When I run `wp site get http://example.com/mysubdir/ --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""

Scenario: Use site get with site delete
Given a WP multisite install

When I run `wp site create --slug=deleteme --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}

When I run `wp site get http://example.com/deleteme/ --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""
And save STDOUT as {BLOG_ID}

When I run `wp site delete {BLOG_ID} --yes`
Then STDOUT should contain:
"""
Success: The site at
"""
And STDOUT should contain:
"""
was deleted.
"""

Scenario: Get site with invalid URL should fail
Given a WP multisite install

When I try `wp site get http://example.com/nonexistent/ --field=blog_id`
Then STDERR should contain:
"""
Error: Could not find site with URL: http://example.com/nonexistent/
"""
And the return code should be 1

Scenario: Get site by domain without scheme
Given a WP multisite subdirectory install

When I run `wp site create --slug=noscheme --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}

When I run `wp site get example.com/noscheme/ --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""

Scenario: Get site by simple domain path
Given a WP multisite subdirectory install

When I run `wp site create --slug=simplepath --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}

When I run `wp site get example.com/simplepath --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""

Scenario: Get site by subdomain without scheme
Given a WP multisite subdomain install

When I run `wp site create --slug=subdomain --porcelain`
Then STDOUT should be a number
And save STDOUT as {SITE_ID}

When I run `wp site get subdomain.example.com --field=blog_id`
Then STDOUT should be:
"""
{SITE_ID}
"""

Scenario: Get main site by domain
Given a WP multisite install

When I run `wp site get example.com --field=blog_id`
Then STDOUT should be:
"""
1
"""
133 changes: 133 additions & 0 deletions src/Site_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,139 @@ public function delete( $args, $assoc_args ) {
WP_CLI::success( "The site at '{$site_url}' was deleted." );
}

/**
* Gets details about a site in a multisite installation.
*
* ## OPTIONS
*
* <site>
* : Site ID or URL of the site to get. For subdirectory sites, use the full URL (e.g., http://example.com/subdir/).
*
* [--field=<field>]
* : Instead of returning the whole site, returns the value of a single field.
*
* [--fields=<fields>]
* : Limit the output to specific fields. Defaults to all fields.
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - yaml
* ---
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for the site:
*
* * blog_id
* * url
* * last_updated
* * registered
*
* These fields are optionally available:
*
* * site_id
* * domain
* * path
* * public
* * archived
* * mature
* * spam
* * deleted
* * lang_id
*
* ## EXAMPLES
*
* # Get site by ID
* $ wp site get 1
* +---------+-------------------------+---------------------+---------------------+
* | blog_id | url | last_updated | registered |
* +---------+-------------------------+---------------------+---------------------+
* | 1 | http://example.com/ | 2025-01-01 12:00:00 | 2025-01-01 12:00:00 |
* +---------+-------------------------+---------------------+---------------------+
*
* # Get site URL by site ID
* $ wp site get 1 --field=url
* http://example.com/
*
* # Get site ID by URL
* $ wp site get http://example.com/subdir/ --field=blog_id
* 2
*
* # Delete a site by URL
* $ wp site delete $(wp site get http://example.com/subdir/ --field=blog_id) --yes
* Success: The site at 'http://example.com/subdir/' was deleted.
*/
public function get( $args, $assoc_args ) {
if ( ! is_multisite() ) {
WP_CLI::error( 'This is not a multisite installation.' );
}

$site_arg = $args[0];
$site = null;

// Check if the argument is a URL or a domain (non-numeric)
if ( ! is_numeric( $site_arg ) ) {
// Normalize URLs without a scheme for proper parsing.
$url_to_parse = $site_arg;
if ( false === strpos( $url_to_parse, '://' ) ) {
$url_to_parse = 'http://' . $url_to_parse;
}

// Parse the URL to get domain and path
$url_parts = wp_parse_url( $url_to_parse );

if ( ! isset( $url_parts['host'] ) ) {
WP_CLI::error( "Invalid URL: {$site_arg}" );
}

$domain = $url_parts['host'];
$path = isset( $url_parts['path'] ) ? $url_parts['path'] : '/';

// Ensure path ends with /
if ( '/' !== substr( $path, -1 ) ) {
$path .= '/';
}

// Use WordPress's cached function to get the blog ID
$blog_id = get_blog_id_from_url( $domain, $path );

if ( ! $blog_id ) {
WP_CLI::error( "Could not find site with URL: {$site_arg}" );
}

$site = $this->fetcher->get_check( $blog_id );
} else {
// Treat as site ID
$site = $this->fetcher->get_check( $site_arg );
}

// Get the site details and add URL
$site_data = get_object_vars( $site );
$site_data['url'] = trailingslashit( get_home_url( $site->blog_id ) );

// Cast numeric fields to int for consistent output
$numeric_fields = [ 'blog_id', 'site_id', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ];
foreach ( $numeric_fields as $field ) {
if ( isset( $site_data[ $field ] ) && is_scalar( $site_data[ $field ] ) ) {
$site_data[ $field ] = (int) $site_data[ $field ];
}
}

// Set default fields if not specified
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = [ 'blog_id', 'url', 'last_updated', 'registered' ];
}

$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $site_data );
}

/**
* Creates a site in a multisite installation.
*
Expand Down