Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
"site deactivate",
"site delete",
"site empty",
"site get",
"site list",
"site mature",
"site meta",
Expand Down
80 changes: 80 additions & 0 deletions features/site.feature
Original file line number Diff line number Diff line change
Expand Up @@ -763,3 +763,83 @@ 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
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 (contains :// or starts with www.)
if ( false !== strpos( $site_arg, '://' ) || 0 === strpos( $site_arg, 'www.' ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot can we use get_blog_id_from_url here? it's cached. signature: function get_blog_id_from_url( $domain, $path = '/' ): int (returns 0 if no blog found, otherwise the ID of the matching blog.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Refactored to use get_blog_id_from_url() instead of the direct database query. This provides better performance through caching and uses the standard WordPress API. (e91a6e5)

// Normalize scheme-less URLs starting with www. for proper parsing.
$url_to_parse = $site_arg;
if ( 0 === strpos( $site_arg, 'www.' ) && false === strpos( $site_arg, '://' ) ) {
$url_to_parse = 'http://' . $site_arg;
}

// 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
Loading