diff --git a/.gitignore b/.gitignore index 01314e1a67139..0f26ae8ce291a 100644 --- a/.gitignore +++ b/.gitignore @@ -110,6 +110,8 @@ wp-tests-config.php # Files for local environment config /docker-compose.override.yml +/tools/local-env/default.local.template +/tools/local-env/php-config.local.ini # Visual regression test diffs tests/visual-regression/specs/__snapshots__ diff --git a/src/wp-admin/includes/class-wp-list-table.php b/src/wp-admin/includes/class-wp-list-table.php index 6568b1563bef4..2aca7639bbf15 100644 --- a/src/wp-admin/includes/class-wp-list-table.php +++ b/src/wp-admin/includes/class-wp-list-table.php @@ -1045,7 +1045,7 @@ protected function pagination( $which ) { $current = $this->get_pagenum(); $removable_query_args = wp_removable_query_args(); - $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + $current_url = wp_get_current_request_url(); $current_url = remove_query_arg( $removable_query_args, $current_url ); @@ -1399,7 +1399,7 @@ public function get_column_count() { public function print_column_headers( $with_id = true ) { list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); - $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + $current_url = wp_get_current_request_url(); $current_url = remove_query_arg( 'paged', $current_url ); // When users click on a column header to sort by other columns. diff --git a/src/wp-admin/includes/misc.php b/src/wp-admin/includes/misc.php index 2454aad1c8d8b..b9c6aa431bfbf 100644 --- a/src/wp-admin/includes/misc.php +++ b/src/wp-admin/includes/misc.php @@ -1391,7 +1391,7 @@ function wp_admin_canonical_url() { } // Ensure we're using an absolute URL. - $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + $current_url = wp_get_current_request_url(); $filtered_url = remove_query_arg( $removable_query_args, $current_url ); /** diff --git a/src/wp-includes/admin-bar.php b/src/wp-includes/admin-bar.php index 9fc3c2b46b348..5bd0f265ba919 100644 --- a/src/wp-includes/admin-bar.php +++ b/src/wp-includes/admin-bar.php @@ -517,7 +517,7 @@ function wp_admin_bar_customize_menu( $wp_admin_bar ) { return; } - $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + $current_url = wp_get_current_request_url(); if ( is_customize_preview() && $wp_customize->changeset_uuid() ) { $current_url = remove_query_arg( 'customize_changeset_uuid', $current_url ); } diff --git a/src/wp-includes/blocks/loginout.php b/src/wp-includes/blocks/loginout.php index f83d8be424ece..5e4e80cc3634d 100644 --- a/src/wp-includes/blocks/loginout.php +++ b/src/wp-includes/blocks/loginout.php @@ -16,12 +16,8 @@ */ function render_block_core_loginout( $attributes ) { - /* - * Build the redirect URL. This current url fetching logic matches with the core. - * - * @see https://github.com/WordPress/wordpress-develop/blob/6bf62e58d21739938f3bb3f9e16ba702baf9c2cc/src/wp-includes/general-template.php#L528. - */ - $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + // Build the redirect URL. + $current_url = wp_get_current_request_url(); $user_logged_in = is_user_logged_in(); diff --git a/src/wp-includes/canonical.php b/src/wp-includes/canonical.php index 9315ba7fb7ff9..6badb2e561136 100644 --- a/src/wp-includes/canonical.php +++ b/src/wp-includes/canonical.php @@ -65,11 +65,9 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) { return; } - if ( ! $requested_url && isset( $_SERVER['HTTP_HOST'] ) ) { + if ( ! $requested_url && isset( $_SERVER['REQUEST_URI'] ) ) { // Build the URL in the address bar. - $requested_url = is_ssl() ? 'https://' : 'http://'; - $requested_url .= $_SERVER['HTTP_HOST']; - $requested_url .= $_SERVER['REQUEST_URI']; + $requested_url = wp_get_current_request_url(); } $original = parse_url( $requested_url ); diff --git a/src/wp-includes/class-wp-recovery-mode.php b/src/wp-includes/class-wp-recovery-mode.php index 7d1af1164185e..7523b0d5d1276 100644 --- a/src/wp-includes/class-wp-recovery-mode.php +++ b/src/wp-includes/class-wp-recovery-mode.php @@ -462,9 +462,7 @@ protected function redirect_protected() { require_once ABSPATH . WPINC . '/pluggable.php'; } - $scheme = is_ssl() ? 'https://' : 'http://'; - - $url = "{$scheme}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; + $url = wp_get_current_request_url(); wp_safe_redirect( $url ); exit; } diff --git a/src/wp-includes/feed.php b/src/wp-includes/feed.php index 821a3eb9be804..487bebf7ea151 100644 --- a/src/wp-includes/feed.php +++ b/src/wp-includes/feed.php @@ -668,14 +668,7 @@ function rss2_site_icon() { * @return string Correct link for the atom:self element. */ function get_self_link() { - $parsed = parse_url( home_url() ); - - $domain = $parsed['host']; - if ( isset( $parsed['port'] ) ) { - $domain .= ':' . $parsed['port']; - } - - return set_url_scheme( 'http://' . $domain . wp_unslash( $_SERVER['REQUEST_URI'] ) ); + return wp_get_current_request_url(); } /** diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index bb19b8e5f9eea..985e5d940a926 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -1923,7 +1923,7 @@ function wp_nonce_field( $action = -1, $name = '_wpnonce', $referer = true, $dis * @return string Referer field HTML markup. */ function wp_referer_field( $display = true ) { - $request_url = remove_query_arg( '_wp_http_referer' ); + $request_url = remove_query_arg( '_wp_http_referer', wp_get_current_request_url() ); $referer_field = ''; if ( $display ) { @@ -7464,7 +7464,7 @@ function wp_auth_check_load() { */ function wp_auth_check_html() { $login_url = wp_login_url(); - $current_domain = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST']; + $current_domain = network_home_url(); $same_domain = str_starts_with( $login_url, $current_domain ); /** diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 640bc54c8e754..c4c7c00c02ad7 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -525,7 +525,7 @@ function wp_login_form( $args = array() ) { $defaults = array( 'echo' => true, // Default 'redirect' value takes the user back to the request URI. - 'redirect' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], + 'redirect' => wp_get_current_request_url(), 'form_id' => 'loginform', 'label_username' => __( 'Username or Email Address' ), 'label_password' => __( 'Password' ), diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php index d6f97a845f65e..5f024e4f6384f 100644 --- a/src/wp-includes/link-template.php +++ b/src/wp-includes/link-template.php @@ -3817,6 +3817,56 @@ function network_home_url( $path = '', $scheme = null ) { return apply_filters( 'network_home_url', $url, $path, $orig_scheme ); } +/** + * Builds a full URL for the current request using the site's configured host. + * + * Replaces raw `$_SERVER['HTTP_HOST']` with the host from `home_url()`, which + * respects the DB-configured site address. This is important behind reverse + * proxies or load balancers where `HTTP_HOST` may not match the public host. + * + * If the request URI already contains the home path (standard setup), only the + * scheme and host are swapped. If the home path is missing from the request URI + * (e.g. a reverse proxy stripped a path prefix), the home path is prepended + * automatically. + * + * @since 6.9.0 + * + * @see https://core.trac.wordpress.org/ticket/53998 + * + * @param string|null $request_uri Optional. The request URI to use. Defaults to + * `$_SERVER['REQUEST_URI']`. + * @return string Full URL of the current request. + */ +function wp_get_current_request_url( $request_uri = null ) { + if ( null === $request_uri ) { + $request_uri = $_SERVER['REQUEST_URI']; + } + + $home = home_url( '/' ); + $home_path = wp_parse_url( $home, PHP_URL_PATH ); + + if ( ! $home_path ) { + $home_path = '/'; + } + + if ( str_starts_with( $request_uri, $home_path ) ) { + // Standard case: REQUEST_URI already includes the home path. + // Just replace scheme + host, keep the request URI as-is. + $parsed = wp_parse_url( $home ); + $host = isset( $parsed['host'] ) ? $parsed['host'] : $_SERVER['HTTP_HOST']; + if ( isset( $parsed['port'] ) ) { + $host .= ':' . $parsed['port']; + } + $url = set_url_scheme( 'http://' . $host . $request_uri ); + } else { + // Reverse-proxy case: REQUEST_URI is missing the home path prefix. + // Let home_url() prepend it. + $url = home_url( $request_uri ); + } + + return $url; +} + /** * Retrieves the URL to the admin area for the network. * diff --git a/src/wp-includes/nav-menu-template.php b/src/wp-includes/nav-menu-template.php index d90fdfa8061ab..0e8a8de0e5d76 100644 --- a/src/wp-includes/nav-menu-template.php +++ b/src/wp-includes/nav-menu-template.php @@ -474,7 +474,7 @@ function _wp_menu_item_classes_by_context( &$menu_items ) { $active_parent_item_ids[] = (int) $menu_item->menu_item_parent; // If the menu item corresponds to the currently requested URL. - } elseif ( 'custom' === $menu_item->object && isset( $_SERVER['HTTP_HOST'] ) ) { + } elseif ( 'custom' === $menu_item->object && isset( $_SERVER['REQUEST_URI'] ) ) { $_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] ); // If it's the customize page then it will strip the query var off the URL before entering the comparison block. @@ -482,7 +482,7 @@ function _wp_menu_item_classes_by_context( &$menu_items ) { $_root_relative_current = strtok( untrailingslashit( $_SERVER['REQUEST_URI'] ), '?' ); } - $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_root_relative_current ); + $current_url = wp_get_current_request_url( $_root_relative_current ); $raw_item_url = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url; $item_url = set_url_scheme( untrailingslashit( $raw_item_url ) ); $_indexless_current = untrailingslashit( preg_replace( '/' . preg_quote( $wp_rewrite->index, '/' ) . '$/', '', $current_url ) ); diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index 688f910dd7390..5597663f6c248 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -1287,13 +1287,8 @@ function auth_redirect() { // If https is required and request is http, redirect. if ( $secure && ! is_ssl() && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { - if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { - wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); - exit; - } else { - wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); - exit; - } + wp_safe_redirect( set_url_scheme( wp_get_current_request_url(), 'https' ) ); + exit; } /** @@ -1318,13 +1313,8 @@ function auth_redirect() { // If the user wants ssl but the session is not ssl, redirect. if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { - if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { - wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); - exit; - } else { - wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); - exit; - } + wp_safe_redirect( set_url_scheme( wp_get_current_request_url(), 'https' ) ); + exit; } return; // The cookie is good, so we're done. @@ -1332,16 +1322,15 @@ function auth_redirect() { // The cookie is no good, so force login. nocache_headers(); - if ( str_contains( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) { $redirect = wp_get_referer(); } else { - $redirect = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + $redirect = wp_get_current_request_url(); } $login_url = wp_login_url( $redirect, true ); - wp_redirect( $login_url ); + wp_safe_redirect( $login_url ); exit; } endif; diff --git a/src/wp-login.php b/src/wp-login.php index 60d9c21f3ddf1..3aa3d8f3f62f0 100644 --- a/src/wp-login.php +++ b/src/wp-login.php @@ -13,13 +13,8 @@ // Redirect to HTTPS login if forced to use SSL. if ( force_ssl_admin() && ! is_ssl() ) { - if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { - wp_safe_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); - exit; - } else { - wp_safe_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); - exit; - } + wp_safe_redirect( set_url_scheme( wp_get_current_request_url(), 'https' ) ); + exit; } /** diff --git a/tests/phpunit/tests/feed/getSelfLink.php b/tests/phpunit/tests/feed/getSelfLink.php new file mode 100644 index 0000000000000..e7f8f0eec84cc --- /dev/null +++ b/tests/phpunit/tests/feed/getSelfLink.php @@ -0,0 +1,127 @@ +original_request_uri = $_SERVER['REQUEST_URI']; + } + + public function tear_down() { + $_SERVER['REQUEST_URI'] = $this->original_request_uri; + unset( $_SERVER['HTTPS'] ); + + parent::tear_down(); + } + + /** + * Tests that get_self_link() returns a full URL using the home host. + * + * @ticket 53998 + */ + public function test_returns_full_url() { + $_SERVER['REQUEST_URI'] = '/feed/'; + + $url = get_self_link(); + + $this->assertStringStartsWith( 'http', $url ); + $this->assertStringContainsString( '/feed/', $url ); + } + + /** + * Tests that get_self_link() uses the host from home_url(). + * + * @ticket 53998 + */ + public function test_host_from_home_url() { + $original_home = get_option( 'home' ); + update_option( 'home', 'http://feeds.example.com' ); + + $_SERVER['REQUEST_URI'] = '/feed/'; + + $url = get_self_link(); + + update_option( 'home', $original_home ); + + $this->assertStringContainsString( 'feeds.example.com', $url ); + } + + /** + * Tests that get_self_link() reflects the current request scheme. + * + * @ticket 53998 + */ + public function test_scheme_follows_is_ssl() { + $_SERVER['REQUEST_URI'] = '/feed/'; + $_SERVER['HTTPS'] = 'on'; + + $url = get_self_link(); + + $this->assertStringStartsWith( 'https://', $url ); + } + + /** + * Tests that get_self_link() preserves query strings. + * + * @ticket 53998 + */ + public function test_preserves_query_string() { + $_SERVER['REQUEST_URI'] = '/feed/?cat=1&paged=2'; + + $url = get_self_link(); + + $this->assertStringContainsString( 'cat=1', $url ); + $this->assertStringContainsString( 'paged=2', $url ); + } + + /** + * Tests that get_self_link() includes the subdirectory prefix. + * + * @ticket 53998 + */ + public function test_includes_subdirectory_prefix() { + $original_home = get_option( 'home' ); + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/blog' ); + + $_SERVER['REQUEST_URI'] = '/blog/feed/'; + + $url = get_self_link(); + + update_option( 'home', $original_home ); + + $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/blog/feed/', $url ); + } + + /** + * Tests the reverse-proxy case where the path prefix is stripped. + * + * @ticket 53998 + */ + public function test_reverse_proxy_prepends_home_path() { + $original_home = get_option( 'home' ); + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/subsite' ); + + $_SERVER['REQUEST_URI'] = '/feed/'; + + $url = get_self_link(); + + update_option( 'home', $original_home ); + + $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/subsite/feed/', $url ); + } +} diff --git a/tests/phpunit/tests/functions/wpRefererField.php b/tests/phpunit/tests/functions/wpRefererField.php index 72e2fe99d5fde..f8d77e4addb66 100644 --- a/tests/phpunit/tests/functions/wpRefererField.php +++ b/tests/phpunit/tests/functions/wpRefererField.php @@ -11,23 +11,49 @@ */ class Tests_Functions_wpRefererField extends WP_UnitTestCase { + /** + * The original value of `$_SERVER['REQUEST_URI']`. + * + * @var string + */ + private $original_request_uri; + + public function set_up() { + parent::set_up(); + + $this->original_request_uri = $_SERVER['REQUEST_URI']; + } + + public function tear_down() { + $_SERVER['REQUEST_URI'] = $this->original_request_uri; + + parent::tear_down(); + } + /** * @ticket 55578 + * @ticket 53998 */ public function test_wp_referer_field() { $_SERVER['REQUEST_URI'] = '/test/'; wp_referer_field(); - $this->expectOutputString( '' ); + $this->expectOutputString( + '' + ); } /** * @ticket 55578 + * @ticket 53998 */ public function test_wp_referer_field_return() { $_SERVER['REQUEST_URI'] = '/test/'; - $this->assertSame( '', wp_referer_field( false ) ); + $this->assertSame( + '', + wp_referer_field( false ) + ); } /** @@ -42,7 +68,8 @@ public function test_wp_referer_field_return() { public function test_wp_referer_field_should_respect_display_arg( $display ) { $actual = $display ? get_echo( 'wp_referer_field' ) : wp_referer_field( false ); - $this->assertSame( '', $actual ); + $this->assertStringContainsString( '_wp_http_referer', $actual ); + $this->assertStringContainsString( 'get_referer_value( $actual ); + + $this->assertStringContainsString( 'edit.php', $value ); + $this->assertStringNotContainsString( '_wp_http_referer', $value ); + } - $_SERVER['REQUEST_URI'] = $old_request_uri; + /** + * Tests that the referer value is an absolute URL. + * + * @ticket 53998 + */ + public function test_wp_referer_field_value_is_absolute_url() { + $_SERVER['REQUEST_URI'] = '/my-account/'; - $this->assertSame( '', $actual ); + $actual = wp_referer_field( false ); + $value = $this->get_referer_value( $actual ); + + $this->assertStringStartsWith( 'http', $value, 'The _wp_http_referer value should be an absolute URL.' ); + } + + /** + * Tests that the referer value includes the home path for subdirectory installs. + * + * @ticket 53998 + */ + public function test_wp_referer_field_includes_home_path() { + $original_home = get_option( 'home' ); + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/subdir' ); + + $_SERVER['REQUEST_URI'] = '/subdir/my-account/'; + + $actual = wp_referer_field( false ); + $value = $this->get_referer_value( $actual ); + + update_option( 'home', $original_home ); + + $this->assertStringContainsString( '/subdir/my-account/', $value ); + } + + /** + * Tests that the _wp_http_referer query arg is stripped from the value. + * + * @ticket 54106 + * @ticket 53998 + */ + public function test_wp_referer_field_strips_existing_referer_arg() { + $_SERVER['REQUEST_URI'] = '/wp-admin/edit.php?_wp_http_referer=%2Fwp-admin%2Fedit.php&post_type=page'; + + $actual = wp_referer_field( false ); + $value = $this->get_referer_value( $actual ); + + $this->assertStringNotContainsString( '_wp_http_referer', $value ); + $this->assertStringContainsString( 'post_type=page', $value ); + } + + /** + * Extracts the value attribute from a wp_referer_field() output string. + * + * @param string $html The HTML output from wp_referer_field(). + * @return string The value of the value attribute. + */ + private function get_referer_value( $html ) { + preg_match( '/value="([^"]*)"/', $html, $matches ); + return html_entity_decode( $matches[1] ?? '' ); } } diff --git a/tests/phpunit/tests/link/wpGetCurrentRequestUrl.php b/tests/phpunit/tests/link/wpGetCurrentRequestUrl.php new file mode 100644 index 0000000000000..df0c8fa8a4aa3 --- /dev/null +++ b/tests/phpunit/tests/link/wpGetCurrentRequestUrl.php @@ -0,0 +1,321 @@ +original_request_uri = $_SERVER['REQUEST_URI']; + $this->original_http_host = $_SERVER['HTTP_HOST']; + $this->original_home = get_option( 'home' ); + } + + public function tear_down() { + $_SERVER['REQUEST_URI'] = $this->original_request_uri; + $_SERVER['HTTP_HOST'] = $this->original_http_host; + update_option( 'home', $this->original_home ); + unset( $_SERVER['HTTPS'] ); + + parent::tear_down(); + } + + /** + * Tests that the function returns a full URL for a root install. + * + * @ticket 53998 + */ + public function test_returns_full_url_for_root_install() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = '/sample-page/?foo=bar'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/sample-page/?foo=bar', + wp_get_current_request_url() + ); + } + + /** + * Tests that the function includes the home path for a subdirectory install + * when the REQUEST_URI already contains the subdirectory. + * + * @ticket 53998 + */ + public function test_standard_subdirectory_install() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/subdir' ); + $_SERVER['REQUEST_URI'] = '/subdir/sample-page/?foo=bar'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/subdir/sample-page/?foo=bar', + wp_get_current_request_url() + ); + } + + /** + * Tests the reverse-proxy case where the proxy strips the path prefix + * from REQUEST_URI before forwarding to WordPress. + * + * @ticket 53998 + */ + public function test_reverse_proxy_stripped_prefix() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/branch-slug' ); + $_SERVER['REQUEST_URI'] = '/sample-page/?foo=bar'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/branch-slug/sample-page/?foo=bar', + wp_get_current_request_url() + ); + } + + /** + * Tests the reverse-proxy case with a root-level request. + * + * @ticket 53998 + */ + public function test_reverse_proxy_root_request() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/branch-slug' ); + $_SERVER['REQUEST_URI'] = '/'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/branch-slug/', + wp_get_current_request_url() + ); + } + + /** + * Tests that an explicit $request_uri parameter overrides $_SERVER. + * + * @ticket 53998 + */ + public function test_explicit_request_uri_parameter() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = '/should-not-be-used/'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/explicit-path/', + wp_get_current_request_url( '/explicit-path/' ) + ); + } + + /** + * Tests that the scheme follows is_ssl() for a standard install. + * + * @ticket 53998 + */ + public function test_scheme_follows_is_ssl_https() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = '/test/'; + $_SERVER['HTTPS'] = 'on'; + + $this->assertStringStartsWith( 'https://', wp_get_current_request_url() ); + } + + /** + * Tests that the scheme is HTTP when HTTPS is off. + * + * @ticket 53998 + */ + public function test_scheme_follows_is_ssl_http() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = '/test/'; + $_SERVER['HTTPS'] = 'off'; + + $this->assertStringStartsWith( 'http://', wp_get_current_request_url() ); + } + + /** + * Tests that the host comes from home_url(), not from HTTP_HOST. + * + * @ticket 53998 + */ + public function test_host_from_home_url_not_http_host() { + update_option( 'home', 'http://configured-host.example.com' ); + $_SERVER['HTTP_HOST'] = 'container-host.internal'; + $_SERVER['REQUEST_URI'] = '/page/'; + + $url = wp_get_current_request_url(); + + $this->assertStringContainsString( 'configured-host.example.com', $url ); + $this->assertStringNotContainsString( 'container-host.internal', $url ); + } + + /** + * Tests that a port in the home URL is preserved. + * + * @ticket 53998 + */ + public function test_port_in_home_url_is_preserved() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . ':8080' ); + $_SERVER['REQUEST_URI'] = '/page/'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . ':8080/page/', + wp_get_current_request_url() + ); + } + + /** + * Tests that query strings are preserved in the returned URL. + * + * @ticket 53998 + */ + public function test_query_string_preserved() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = '/wp-admin/edit.php?post_type=page&orderby=date'; + + $url = wp_get_current_request_url(); + + $this->assertStringContainsString( 'post_type=page', $url ); + $this->assertStringContainsString( 'orderby=date', $url ); + } + + /** + * Tests the reverse-proxy case with query strings. + * + * @ticket 53998 + */ + public function test_reverse_proxy_with_query_string() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/branch' ); + $_SERVER['REQUEST_URI'] = '/wp-admin/edit.php?post_type=page'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/branch/wp-admin/edit.php?post_type=page', + wp_get_current_request_url() + ); + } + + /** + * Tests with an empty REQUEST_URI. + * + * @ticket 53998 + */ + public function test_empty_request_uri() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = ''; + + $url = wp_get_current_request_url(); + + $this->assertSame( 'http://' . WP_TESTS_DOMAIN, $url ); + } + + /** + * Tests that a trailing-slash home path is handled correctly. + * + * The home_url('/') always produces a trailing slash. When REQUEST_URI + * starts with it (which is always the case for '/'), the standard path + * should be taken. + * + * @ticket 53998 + */ + public function test_root_home_with_trailing_slash() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN ); + $_SERVER['REQUEST_URI'] = '/wp-login.php'; + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/wp-login.php', + wp_get_current_request_url() + ); + } + + /** + * Tests the reverse-proxy case with the explicit $request_uri parameter. + * + * @ticket 53998 + */ + public function test_reverse_proxy_with_explicit_request_uri() { + update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/prefix' ); + + $this->assertSame( + 'http://' . WP_TESTS_DOMAIN . '/prefix/my-account/', + wp_get_current_request_url( '/my-account/' ) + ); + } + + /** + * Tests data provider scenarios for standard and reverse-proxy installs. + * + * @ticket 53998 + * + * @dataProvider data_request_url_scenarios + * + * @param string $home The home option value. + * @param string $request_uri The value for `$_SERVER['REQUEST_URI']`. + * @param string $expected The expected URL. + */ + public function test_request_url_scenarios( $home, $request_uri, $expected ) { + update_option( 'home', $home ); + $_SERVER['REQUEST_URI'] = $request_uri; + + $this->assertSame( $expected, wp_get_current_request_url() ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_request_url_scenarios() { + $domain = WP_TESTS_DOMAIN; + + return array( + 'root install, simple path' => array( + 'home' => "http://{$domain}", + 'request_uri' => '/hello-world/', + 'expected' => "http://{$domain}/hello-world/", + ), + 'root install, wp-admin path with query' => array( + 'home' => "http://{$domain}", + 'request_uri' => '/wp-admin/options.php?updated=1', + 'expected' => "http://{$domain}/wp-admin/options.php?updated=1", + ), + 'subdirectory install, matching REQUEST_URI' => array( + 'home' => "http://{$domain}/wp", + 'request_uri' => '/wp/hello-world/', + 'expected' => "http://{$domain}/wp/hello-world/", + ), + 'reverse proxy, stripped /wp prefix' => array( + 'home' => "http://{$domain}/wp", + 'request_uri' => '/hello-world/', + 'expected' => "http://{$domain}/wp/hello-world/", + ), + 'reverse proxy, stripped /branch prefix' => array( + 'home' => "http://{$domain}/deploy-branch", + 'request_uri' => '/my-account/', + 'expected' => "http://{$domain}/deploy-branch/my-account/", + ), + 'reverse proxy, root request' => array( + 'home' => "http://{$domain}/deploy-branch", + 'request_uri' => '/', + 'expected' => "http://{$domain}/deploy-branch/", + ), + ); + } +}