Commit 48fd04b534e for woocommerce
commit 48fd04b534ec14c7efead9b01babbac4ee83e9c3
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date: Thu Aug 20 07:56:35 2026 +0200
Fix add-to-cart and password-protected product URLs on PATHINFO permalinks (#67836)
* Fix add-to-cart and password-protected product URLs on PATHINFO permalinks
* Add changelog
* Fix version number
* Format docblock
* Avoid setting index.php when there is no request path
diff --git a/plugins/woocommerce/changelog/fix-WOOAIRR-87 b/plugins/woocommerce/changelog/fix-WOOAIRR-87
new file mode 100644
index 00000000000..e29c72df44c
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-WOOAIRR-87
@@ -0,0 +1,5 @@
+Significance: patch
+Type: fix
+Comment: Fix add-to-cart and password-protected product URLs on PATHINFO permalinks
+
+
diff --git a/plugins/woocommerce/src/Blocks/Utils/Utils.php b/plugins/woocommerce/src/Blocks/Utils/Utils.php
index e4ead6c62bb..d5c2b4f7319 100644
--- a/plugins/woocommerce/src/Blocks/Utils/Utils.php
+++ b/plugins/woocommerce/src/Blocks/Utils/Utils.php
@@ -53,14 +53,30 @@ class Utils {
/**
* Get the current page URL using the request path relative to home.
*
- * @since 11.1.0
+ * @internal This function is used internally by WooCommerce blocks to get the current page URL. It is not intended for external use.
+ *
* @return string The current page URL.
+ *
+ * @since 11.1.0
*/
public static function get_current_page_url() {
- global $wp;
+ global $wp, $wp_rewrite;
$request_path = is_object( $wp ) && isset( $wp->request ) && is_string( $wp->request ) ? $wp->request : '';
- $url = home_url( user_trailingslashit( $request_path ) );
+
+ // PATHINFO permalinks keep index.php in the public URL; $wp->request does not.
+ if (
+ '' !== $request_path &&
+ $wp_rewrite instanceof \WP_Rewrite &&
+ $wp_rewrite->using_index_permalinks()
+ ) {
+ $index = is_string( $wp_rewrite->index ) && '' !== $wp_rewrite->index ? $wp_rewrite->index : 'index.php';
+ if ( $index !== $request_path && ! str_starts_with( $request_path, $index . '/' ) ) {
+ $request_path = $index . '/' . $request_path;
+ }
+ }
+
+ $url = home_url( user_trailingslashit( $request_path ) );
if ( isset( $_SERVER['QUERY_STRING'] ) && is_string( $_SERVER['QUERY_STRING'] ) && '' !== $_SERVER['QUERY_STRING'] ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Preserving the raw query string encoding and delimiters.
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Utils/UtilsTest.php b/plugins/woocommerce/tests/php/src/Blocks/Utils/UtilsTest.php
index e555753f154..a93f9d95729 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/Utils/UtilsTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/Utils/UtilsTest.php
@@ -154,22 +154,25 @@ class UtilsTest extends WC_Unit_Test_Case {
/**
* Get the current page URL after temporarily setting request state.
*
- * @param string $request_path The request path relative to home.
- * @param string|null $query_string The raw query string, or null to omit it.
- * @param bool $use_trailing_slashes Whether permalinks should use trailing slashes.
+ * @param string $request_path The request path relative to home.
+ * @param string|null $query_string The raw query string, or null to omit it.
+ * @param bool $use_trailing_slashes Whether permalinks should use trailing slashes.
+ * @param bool $using_index_permalinks Whether PATHINFO (index.php) permalinks are enabled.
* @return string
*/
- private function get_current_page_url_with_request_state( string $request_path, ?string $query_string, bool $use_trailing_slashes ): string {
+ private function get_current_page_url_with_request_state( string $request_path, ?string $query_string, bool $use_trailing_slashes, bool $using_index_permalinks = false ): string {
global $wp, $wp_rewrite;
$original_request = $wp->request;
$original_query_string_exists = array_key_exists( 'QUERY_STRING', $_SERVER ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$original_query_string = $_SERVER['QUERY_STRING'] ?? null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$original_use_trailing_slashes = $wp_rewrite->use_trailing_slashes;
+ $original_permalink_structure = $wp_rewrite->permalink_structure;
try {
$wp->request = $request_path;
$wp_rewrite->use_trailing_slashes = $use_trailing_slashes;
+ $wp_rewrite->permalink_structure = $using_index_permalinks ? '/index.php/%postname%/' : '/%postname%/';
if ( null === $query_string ) {
unset( $_SERVER['QUERY_STRING'] );
@@ -181,6 +184,7 @@ class UtilsTest extends WC_Unit_Test_Case {
} finally {
$wp->request = $original_request;
$wp_rewrite->use_trailing_slashes = $original_use_trailing_slashes;
+ $wp_rewrite->permalink_structure = $original_permalink_structure;
if ( $original_query_string_exists ) {
$_SERVER['QUERY_STRING'] = $original_query_string;
@@ -194,13 +198,14 @@ class UtilsTest extends WC_Unit_Test_Case {
* @testdox get_current_page_url() preserves request URL components exactly.
* @dataProvider provider_current_page_url_cases
*
- * @param string $request_path The request path relative to home.
- * @param string|null $query_string The raw query string, or null to omit it.
- * @param string $expected_path The expected path relative to home.
- * @param bool $use_trailing_slashes Whether permalinks should use trailing slashes.
+ * @param string $request_path The request path relative to home.
+ * @param string|null $query_string The raw query string, or null to omit it.
+ * @param string $expected_path The expected path relative to home.
+ * @param bool $use_trailing_slashes Whether permalinks should use trailing slashes.
+ * @param bool $using_index_permalinks Whether PATHINFO (index.php) permalinks are enabled.
*/
- public function test_get_current_page_url( string $request_path, ?string $query_string, string $expected_path, bool $use_trailing_slashes ): void {
- $url = $this->get_current_page_url_with_request_state( $request_path, $query_string, $use_trailing_slashes );
+ public function test_get_current_page_url( string $request_path, ?string $query_string, string $expected_path, bool $use_trailing_slashes, bool $using_index_permalinks ): void {
+ $url = $this->get_current_page_url_with_request_state( $request_path, $query_string, $use_trailing_slashes, $using_index_permalinks );
$this->assertSame(
untrailingslashit( home_url() ) . $expected_path,
@@ -212,33 +217,58 @@ class UtilsTest extends WC_Unit_Test_Case {
/**
* Current page URL inputs and their exact expected paths.
*
- * @return array<string, array{string, string|null, string, bool}>
+ * @return array<string, array{string, string|null, string, bool, bool}>
*/
public function provider_current_page_url_cases(): array {
return array(
- 'encoded label query' => array(
+ 'encoded label query' => array(
'product/hoodie',
'label=Black%20%26%20White',
'/product/hoodie/?label=Black%20%26%20White',
true,
+ false,
),
- 'question mark query value' => array(
+ 'question mark query value' => array(
'search-results',
'search=?',
'/search-results/?search=?',
true,
+ false,
),
- 'lone zero query string' => array(
+ 'lone zero query string' => array(
'product/hoodie',
'0',
'/product/hoodie/?0',
true,
+ false,
),
- 'encoded label query no trailing slash' => array(
+ 'encoded label query no trailing slash' => array(
'product/hoodie',
'label=Black%20%26%20White',
'/product/hoodie?label=Black%20%26%20White',
false,
+ false,
+ ),
+ 'index permalinks product path with query' => array(
+ 'product/hoodie',
+ 'label=Black%20%26%20White',
+ '/index.php/product/hoodie/?label=Black%20%26%20White',
+ true,
+ true,
+ ),
+ 'index permalinks already prefixed request is not doubled' => array(
+ 'index.php/product/hoodie',
+ null,
+ '/index.php/product/hoodie/',
+ true,
+ true,
+ ),
+ 'index permalinks empty request stays at home' => array(
+ '',
+ null,
+ '/',
+ true,
+ true,
),
);
}