diff --git a/features/core-install.feature b/features/core-install.feature index 4b3b74c5..bca17aa6 100644 --- a/features/core-install.feature +++ b/features/core-install.feature @@ -177,6 +177,78 @@ Feature: Install WordPress core """ And the return code should be 0 + Scenario: Verify correct siteurl and home when URL has no path + Given an empty directory + And WP files + And wp-config.php + And a database + + When I run `wp core install --url=example.com --title=Test --admin_user=wpcli --admin_email=wpcli@example.org --admin_password=password --skip-email` + Then STDOUT should contain: + """ + Success: WordPress installed successfully. + """ + + When I run `wp option get home` + Then STDOUT should be: + """ + http://example.com + """ + + When I run `wp option get siteurl` + Then STDOUT should be: + """ + http://example.com + """ + + Scenario: Verify correct siteurl and home when URL has a path + Given an empty directory + And WP files + And wp-config.php + And a database + + When I run `wp core install --url=example.com/subdir --title=Test --admin_user=wpcli --admin_email=wpcli@example.org --admin_password=password --skip-email` + Then STDOUT should contain: + """ + Success: WordPress installed successfully. + """ + + When I run `wp option get home` + Then STDOUT should be: + """ + http://example.com/subdir + """ + + When I run `wp option get siteurl` + Then STDOUT should be: + """ + http://example.com/subdir + """ + + Scenario: Install ensures correct siteurl and home regardless of PHP_SELF + Given an empty directory + And WP files + And wp-config.php + And a database + + When I run `wp core install --url=https://example.com --title=Test --admin_user=wpcli --admin_email=wpcli@example.org --admin_password=password --skip-email` + Then STDOUT should contain: + """ + Success: WordPress installed successfully. + """ + + When I run `wp option get home` + Then STDOUT should be: + """ + https://example.com + """ + + When I run `wp option get siteurl` + Then STDOUT should be: + """ + https://example.com + """ + @less-than-php-7 Scenario: Install WordPress with locale set to de_DE on WP < 4.0 Given an empty directory diff --git a/src/Core_Command.php b/src/Core_Command.php index ae1c7397..112d74c2 100644 --- a/src/Core_Command.php +++ b/src/Core_Command.php @@ -469,6 +469,32 @@ public function is_installed( $args, $assoc_args ) { * @param array{url: string, title: string, admin_user: string, admin_password?: string, admin_email: string, locale?: string, 'skip-email'?: bool} $assoc_args Associative arguments. */ public function install( $args, $assoc_args ) { + // Fix $_SERVER['PHP_SELF'] and $_SERVER['SCRIPT_NAME'] early to prevent incorrect + // URL detection by WordPress. When WP-CLI is executed from the root of the + // filesystem (e.g., /wp), these variables contain the WP-CLI executable path + // rather than the WordPress installation path, which causes wp_guess_url() to + // construct incorrect URLs. This must be done as early as possible. + if ( isset( $assoc_args['url'] ) ) { + $url_parts = Utils\parse_url( $assoc_args['url'] ); + $path = isset( $url_parts['path'] ) ? $url_parts['path'] : '/'; + + // Ensure path represents a PHP script for proper WordPress URL detection. + $path = rtrim( $path, '/' ); + if ( empty( $path ) ) { + $path = '/index.php'; + } elseif ( '' === pathinfo( $path, PATHINFO_EXTENSION ) ) { + $path .= '/index.php'; + } + + $_SERVER['PHP_SELF'] = $path; + $_SERVER['SCRIPT_NAME'] = $path; + + // Set SCRIPT_FILENAME to the actual WordPress index.php if available. + if ( file_exists( Utils\trailingslashit( ABSPATH ) . 'index.php' ) ) { + $_SERVER['SCRIPT_FILENAME'] = Utils\trailingslashit( ABSPATH ) . 'index.php'; + } + } + if ( $this->do_install( $assoc_args ) ) { WP_CLI::success( 'WordPress installed successfully.' ); } else { @@ -600,6 +626,32 @@ public function multisite_convert( $args, $assoc_args ) { * @param array{url?: string, base: string, subdomains?: bool, title: string, admin_user: string, admin_password?: string, admin_email: string, 'skip-email'?: bool, 'skip-config'?: bool} $assoc_args Associative arguments. */ public function multisite_install( $args, $assoc_args ) { + // Fix $_SERVER['PHP_SELF'] and $_SERVER['SCRIPT_NAME'] early to prevent incorrect + // URL detection by WordPress. When WP-CLI is executed from the root of the + // filesystem (e.g., /wp), these variables contain the WP-CLI executable path + // rather than the WordPress installation path, which causes wp_guess_url() to + // construct incorrect URLs. This must be done as early as possible. + if ( isset( $assoc_args['url'] ) ) { + $url_parts = Utils\parse_url( $assoc_args['url'] ); + $path = isset( $url_parts['path'] ) ? $url_parts['path'] : '/'; + + // Ensure path represents a PHP script for proper WordPress URL detection. + $path = rtrim( $path, '/' ); + if ( empty( $path ) ) { + $path = '/index.php'; + } elseif ( '' === pathinfo( $path, PATHINFO_EXTENSION ) ) { + $path .= '/index.php'; + } + + $_SERVER['PHP_SELF'] = $path; + $_SERVER['SCRIPT_NAME'] = $path; + + // Set SCRIPT_FILENAME to the actual WordPress index.php if available. + if ( file_exists( Utils\trailingslashit( ABSPATH ) . 'index.php' ) ) { + $_SERVER['SCRIPT_FILENAME'] = Utils\trailingslashit( ABSPATH ) . 'index.php'; + } + } + if ( $this->do_install( $assoc_args ) ) { WP_CLI::log( 'Created single site database tables.' ); } else { @@ -663,6 +715,40 @@ private function do_install( $assoc_args ) { return false; } + // Fix $_SERVER['PHP_SELF'] and $_SERVER['SCRIPT_NAME'] early to prevent incorrect + // URL detection by WordPress during installation. When WP-CLI is executed from + // the root of the filesystem (e.g., /wp), these variables contain the WP-CLI + // executable path rather than the WordPress installation path, which causes + // wp_guess_url() to construct incorrect URLs. This must be done before loading + // any WordPress files that might use these values. + if ( isset( $assoc_args['url'] ) ) { + WP_CLI::set_url( $assoc_args['url'] ); + + $url_parts = Utils\parse_url( $assoc_args['url'] ); + $path = isset( $url_parts['path'] ) ? $url_parts['path'] : '/'; + + // Ensure path represents a PHP script for proper WordPress URL detection. + // If the path doesn't already end with a file (no extension in basename), + // append '/index.php' to represent the WordPress entry point. + $path = rtrim( $path, '/' ); + if ( empty( $path ) ) { + $path = '/index.php'; + } elseif ( '' === pathinfo( $path, PATHINFO_EXTENSION ) ) { + // Path doesn't end with a file, append /index.php + $path .= '/index.php'; + } + + $_SERVER['PHP_SELF'] = $path; + $_SERVER['SCRIPT_NAME'] = $path; + + // Set SCRIPT_FILENAME to the actual WordPress index.php if available. + // This is optional and only set when ABSPATH is defined and index.php exists. + // If not set, WordPress can still function using PHP_SELF and SCRIPT_NAME. + if ( file_exists( Utils\trailingslashit( ABSPATH ) . 'index.php' ) ) { + $_SERVER['SCRIPT_FILENAME'] = Utils\trailingslashit( ABSPATH ) . 'index.php'; + } + } + if ( true === Utils\get_flag_value( $assoc_args, 'skip-email' ) ) { if ( ! function_exists( 'wp_new_blog_notification' ) ) { // @phpstan-ignore function.inner @@ -687,12 +773,6 @@ function wp_new_blog_notification() { $args = wp_parse_args( $assoc_args, $defaults ); - // Support prompting for the `--url=`, - // which is normally a runtime argument - if ( isset( $assoc_args['url'] ) ) { - WP_CLI::set_url( $assoc_args['url'] ); - } - $public = true; $password = $args['admin_password'];