Commit b3938be1557 for woocommerce
commit b3938be15575bf2fd3b48886b1f6d53f7565b7c2
Author: Tung Du <dinhtungdu@gmail.com>
Date: Mon Aug 3 16:30:23 2026 +0700
Preserve product archive rewrite rules when WP-CLI skips the active theme and another plugin flushes permalinks (#67101)
* fix: preserve shop rewrites when WP-CLI skips themes
* chore: add shop rewrite fix changelog
* refactor: clarify product archive registration decision
* fix: trust runtime theme support during cron
* test: remove product archive E2E coverage
* refactor: narrow WP-CLI config callback
diff --git a/plugins/woocommerce/changelog/fix-cli-skipped-theme-rewrite-rules b/plugins/woocommerce/changelog/fix-cli-skipped-theme-rewrite-rules
new file mode 100644
index 00000000000..a04a1ac29b0
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-cli-skipped-theme-rewrite-rules
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Preserve product archive rewrite rules when WP-CLI skips the active theme and another plugin flushes permalinks.
diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php
index 357d04dc707..d64ac91955f 100644
--- a/plugins/woocommerce/includes/class-wc-post-types.php
+++ b/plugins/woocommerce/includes/class-wc-post-types.php
@@ -344,9 +344,11 @@ class WC_Post_Types {
$supports[] = 'comments';
}
- $shop_page_id = wc_get_page_id( 'shop' );
+ $shop_page_id = wc_get_page_id( 'shop' );
+ $theme_support = wc_current_theme_supports_woocommerce_or_fse();
+ $active_theme_skipped = self::wp_cli_skips_active_theme();
- if ( wc_current_theme_supports_woocommerce_or_fse() ) {
+ if ( self::should_register_product_archive( $theme_support, $active_theme_skipped ) ) {
$has_archive = $shop_page_id && get_post( $shop_page_id ) ? urldecode( get_page_uri( $shop_page_id ) ) : 'shop';
} else {
$has_archive = false;
@@ -356,7 +358,7 @@ class WC_Post_Types {
// Skip this check in WP-CLI and cron contexts: themes may not be loaded (e.g. --skip-themes),
// which would incorrectly record theme support as "no" and corrupt rewrite rules on the frontend.
if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) && ! wp_doing_cron() ) {
- $theme_support = wc_current_theme_supports_woocommerce_or_fse() ? 'yes' : 'no';
+ $theme_support = $theme_support ? 'yes' : 'no';
if ( get_option( 'current_theme_supports_woocommerce' ) !== $theme_support && update_option( 'current_theme_supports_woocommerce', $theme_support ) ) {
update_option( 'woocommerce_queue_flush_rewrite_rules', 'yes' );
}
@@ -541,6 +543,46 @@ class WC_Post_Types {
do_action( 'woocommerce_after_register_post_type' );
}
+ /**
+ * Resolve theme support used to register the product archive.
+ *
+ * @param bool $theme_support Whether the current request reports theme support.
+ * @param bool $active_theme_skipped Whether WP-CLI skipped the active theme.
+ * @return bool
+ */
+ private static function should_register_product_archive( bool $theme_support, bool $active_theme_skipped ): bool {
+ if ( $theme_support || ! $active_theme_skipped ) {
+ return $theme_support;
+ }
+
+ return 'yes' === get_option( 'current_theme_supports_woocommerce' );
+ }
+
+ /**
+ * Determine whether WP-CLI skipped the active theme.
+ *
+ * @return bool
+ */
+ private static function wp_cli_skips_active_theme(): bool {
+ $get_wp_cli_config = array( 'WP_CLI', 'get_config' );
+
+ if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) || ! is_callable( $get_wp_cli_config ) ) {
+ return false;
+ }
+
+ $skipped_themes = $get_wp_cli_config( 'skip-themes' );
+
+ if ( true === $skipped_themes ) {
+ return true;
+ }
+
+ if ( ! is_array( $skipped_themes ) ) {
+ $skipped_themes = explode( ',', (string) $skipped_themes );
+ }
+
+ return in_array( get_stylesheet(), $skipped_themes, true );
+ }
+
/**
* Customize taxonomies update messages.
*
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-post-types-test.php b/plugins/woocommerce/tests/php/includes/class-wc-post-types-test.php
new file mode 100644
index 00000000000..7ca0094401e
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/class-wc-post-types-test.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Tests for WC_Post_Types.
+ *
+ * @package WooCommerce\Tests\PostTypes
+ */
+
+declare( strict_types = 1 );
+
+/**
+ * Tests for WC_Post_Types.
+ */
+class WC_Post_Types_Test extends WC_Unit_Test_Case {
+
+ /**
+ * Original stored theme support value.
+ *
+ * @var mixed
+ */
+ private $original_theme_support;
+
+ /**
+ * Set up test fixtures.
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ $this->original_theme_support = get_option( 'current_theme_supports_woocommerce', '__missing__' );
+ }
+
+ /**
+ * Restore test fixtures.
+ */
+ public function tearDown(): void {
+ if ( '__missing__' === $this->original_theme_support ) {
+ delete_option( 'current_theme_supports_woocommerce' );
+ } else {
+ update_option( 'current_theme_supports_woocommerce', $this->original_theme_support );
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @testdox Product archive registration uses runtime support unless WP-CLI skipped the active theme.
+ * @dataProvider provide_product_archive_theme_support_cases
+ *
+ * @param bool $runtime_support Whether the current request reports theme support.
+ * @param bool $active_theme_skipped Whether WP-CLI skipped the active theme.
+ * @param string $stored_support Stored support from the last trusted request.
+ * @param bool $expected Expected resolved support.
+ */
+ public function test_should_register_product_archive(
+ bool $runtime_support,
+ bool $active_theme_skipped,
+ string $stored_support,
+ bool $expected
+ ): void {
+ update_option( 'current_theme_supports_woocommerce', $stored_support );
+
+ $method = new ReflectionMethod( WC_Post_Types::class, 'should_register_product_archive' );
+ $method->setAccessible( true );
+
+ $this->assertSame(
+ $expected,
+ $method->invoke( null, $runtime_support, $active_theme_skipped ),
+ 'Product archive support should only fall back to trusted stored support when WP-CLI skipped the active theme.'
+ );
+ }
+
+ /**
+ * Data provider for product archive theme support resolution.
+ *
+ * @return array<string, array{bool, bool, string, bool}>
+ */
+ public function provide_product_archive_theme_support_cases(): array {
+ return array(
+ 'supported runtime' => array( true, false, 'no', true ),
+ 'ordinary cron ignores stored support' => array( false, false, 'yes', false ),
+ 'WP-CLI skipped supported theme' => array( false, true, 'yes', true ),
+ 'WP-CLI skipped unsupported theme' => array( false, true, 'no', false ),
+ 'supported WP-CLI runtime' => array( true, true, 'no', true ),
+ );
+ }
+}