Commit 790dcfaa3c5 for woocommerce

commit 790dcfaa3c5607574700a5f0b9881ca79dea8377
Author: Noruzzaman <noruzzamanrubel@gmail.com>
Date:   Tue Oct 27 00:15:27 2026 +0600

    Leverage is_admin() gate when initializing analytics integrations (#70070)

    * Leverage is_admin() gate when initializing analytics integrations

    * Keep analytics debug tools and user data filters available in REST and CLI contexts

    * Avoid duplicate refund tool visibility checks

    ---------

    Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/perf-69797-analytics-is-admin-gate b/plugins/woocommerce/changelog/perf-69797-analytics-is-admin-gate
new file mode 100644
index 00000000000..726274694c4
--- /dev/null
+++ b/plugins/woocommerce/changelog/perf-69797-analytics-is-admin-gate
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Leverage is_admin() gate when initializing analytics to avoid running admin-specific logic and queries in non-admin contexts.
diff --git a/plugins/woocommerce/src/Internal/Admin/Analytics.php b/plugins/woocommerce/src/Internal/Admin/Analytics.php
index 7ef1603dd63..23b2d503949 100644
--- a/plugins/woocommerce/src/Internal/Admin/Analytics.php
+++ b/plugins/woocommerce/src/Internal/Admin/Analytics.php
@@ -5,6 +5,7 @@

 namespace Automattic\WooCommerce\Internal\Admin;

+use Automattic\Jetpack\Constants;
 use Automattic\WooCommerce\Admin\API\Reports\Cache;
 use Automattic\WooCommerce\Utilities\OrderUtil;
 use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -110,25 +111,47 @@ class Analytics {
 			return;
 		}

-		add_filter( 'woocommerce_component_settings_preload_endpoints', array( $this, 'add_preload_endpoints' ) );
-		add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) );
-		add_action( 'admin_menu', array( $this, 'register_pages' ) );
-		add_filter( 'woocommerce_debug_tools', array( $this, 'register_cache_clear_tool' ) );
-		add_filter( 'woocommerce_debug_tools', array( $this, 'register_regenerate_order_fulfillment_status_tool' ), 12 );
-
 		// Always register the batch hook so in-flight jobs survive after the legacy
 		// flag is cleared (clearing happens before the first batch is queued).
 		add_action( 'woocommerce_analytics_refund_fix_batch', array( $this, 'process_refund_fix_batch' ) );

 		// Merchant-triggered repair of refunds double-counted before #66320.
-		add_filter( 'woocommerce_debug_tools', array( $this, 'register_refund_double_count_tool' ) );
 		add_action( self::REFUND_DOUBLE_COUNT_FIX_HOOK, array( $this, 'process_refund_double_count_fix_batch' ), 10, 2 );
 		add_action( 'woocommerce_analytics_regenerate_init', array( $this, 'maybe_cancel_refund_double_count_fix_on_regenerate' ), 10, 2 );

-		if ( self::should_show_refund_fix_tool() ) {
+		$is_admin = is_admin();
+		$is_rest  = self::is_rest_request();
+		$is_cli   = self::is_cli_request();
+
+		if ( ! $is_admin && ! $is_rest && ! $is_cli ) {
+			return;
+		}
+
+		$should_show_refund_fix_tool = self::should_show_refund_fix_tool();
+
+		// Register debug tools in admin, REST, and WP-CLI contexts where they can be consumed.
+		add_filter( 'woocommerce_debug_tools', array( $this, 'register_cache_clear_tool' ) );
+		add_filter( 'woocommerce_debug_tools', array( $this, 'register_regenerate_order_fulfillment_status_tool' ), 12 );
+		add_filter( 'woocommerce_debug_tools', array( $this, 'register_refund_double_count_tool' ) );
+
+		if ( $should_show_refund_fix_tool ) {
 			add_filter( 'woocommerce_debug_tools', array( $this, 'register_full_refund_fix_data_tool' ) );
-			add_action( 'admin_footer', array( $this, 'output_refund_fix_tool_js' ) );
-			add_action( 'wp_ajax_woocommerce_check_refund_fix_needed', array( $this, 'ajax_check_refund_fix_needed' ) );
+		}
+
+		// User data preferences are read and updated in admin and REST contexts.
+		if ( $is_admin || $is_rest ) {
+			add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) );
+		}
+
+		// Admin-only UI screens, menus, and client-side assets.
+		if ( $is_admin ) {
+			add_filter( 'woocommerce_component_settings_preload_endpoints', array( $this, 'add_preload_endpoints' ) );
+			add_action( 'admin_menu', array( $this, 'register_pages' ) );
+
+			if ( $should_show_refund_fix_tool ) {
+				add_action( 'admin_footer', array( $this, 'output_refund_fix_tool_js' ) );
+				add_action( 'wp_ajax_woocommerce_check_refund_fix_needed', array( $this, 'ajax_check_refund_fix_needed' ) );
+			}
 		}
 	}

@@ -263,6 +286,25 @@ class Analytics {
 			|| 'yes' === get_option( 'woocommerce_analytics_show_old_refund_data_tool' );
 	}

+	/**
+	 * Check whether the current execution context is a REST API request.
+	 *
+	 * @return bool
+	 */
+	private static function is_rest_request(): bool {
+		return Constants::is_true( 'REST_REQUEST' )
+			|| ( function_exists( 'WC' ) && WC() && WC()->is_rest_api_request() );
+	}
+
+	/**
+	 * Check whether the current execution context is WP-CLI.
+	 *
+	 * @return bool
+	 */
+	private static function is_cli_request(): bool {
+		return Constants::is_true( 'WP_CLI' );
+	}
+
 	/**
 	 * Register the full refund fix data tool on the WooCommerce > Status > Tools page.
 	 *
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/AnalyticsTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/AnalyticsTest.php
index 35f0b07be24..7351fb8aed7 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/AnalyticsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/AnalyticsTest.php
@@ -3,6 +3,7 @@ declare( strict_types = 1 );

 namespace Automattic\WooCommerce\Tests\Internal\Admin;

+use Automattic\Jetpack\Constants;
 use Automattic\WooCommerce\Enums\OrderStatus;
 use Automattic\WooCommerce\Internal\Admin\Analytics;
 use Automattic\WooCommerce\Internal\Admin\Schedulers\OrdersScheduler;
@@ -825,4 +826,234 @@ class AnalyticsTest extends WC_Unit_Test_Case {
 		// The tool still offers to run, rather than reporting that nothing was found.
 		$this->assertStringContainsString( 'Check and fix', $this->get_tool()['button'] );
 	}
+
+	/**
+	 * Verify that admin integrations are not registered in non-admin contexts.
+	 *
+	 * @testdox Admin integrations are not registered in non-admin contexts.
+	 */
+	public function test_admin_integrations_not_registered_in_non_admin_context(): void {
+		set_current_screen( 'front' );
+
+		$analytics = new Analytics();
+
+		try {
+			$this->assertFalse(
+				has_filter( 'woocommerce_component_settings_preload_endpoints', array( $analytics, 'add_preload_endpoints' ) ),
+				'Preload endpoints filter should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_filter( 'woocommerce_admin_get_user_data_fields', array( $analytics, 'add_user_data_fields' ) ),
+				'User data fields filter should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_action( 'admin_menu', array( $analytics, 'register_pages' ) ),
+				'Admin menu action should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) ),
+				'Cache clear tool filter should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ) ),
+				'Regenerate fulfillment status tool filter should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) ),
+				'Refund double-count tool filter should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_full_refund_fix_data_tool' ) ),
+				'Full refund fix tool filter should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_action( 'admin_footer', array( $analytics, 'output_refund_fix_tool_js' ) ),
+				'Admin footer action should not be registered outside the admin area.'
+			);
+			$this->assertFalse(
+				has_action( 'wp_ajax_woocommerce_check_refund_fix_needed', array( $analytics, 'ajax_check_refund_fix_needed' ) ),
+				'AJAX check refund fix action should not be registered outside the admin area.'
+			);
+
+			// Background Action Scheduler hooks must always be registered.
+			$this->assertNotFalse(
+				has_action( 'woocommerce_analytics_refund_fix_batch', array( $analytics, 'process_refund_fix_batch' ) ),
+				'Refund fix batch hook must remain registered in non-admin contexts for background execution.'
+			);
+			$this->assertNotFalse(
+				has_action( Analytics::REFUND_DOUBLE_COUNT_FIX_HOOK, array( $analytics, 'process_refund_double_count_fix_batch' ) ),
+				'Refund double count fix batch hook must remain registered in non-admin contexts for background execution.'
+			);
+			$this->assertNotFalse(
+				has_action( 'woocommerce_analytics_regenerate_init', array( $analytics, 'maybe_cancel_refund_double_count_fix_on_regenerate' ) ),
+				'Regenerate cancel hook must remain registered in non-admin contexts.'
+			);
+		} finally {
+			remove_action( 'woocommerce_analytics_refund_fix_batch', array( $analytics, 'process_refund_fix_batch' ) );
+			remove_action( Analytics::REFUND_DOUBLE_COUNT_FIX_HOOK, array( $analytics, 'process_refund_double_count_fix_batch' ) );
+			remove_action( 'woocommerce_analytics_regenerate_init', array( $analytics, 'maybe_cancel_refund_double_count_fix_on_regenerate' ) );
+			set_current_screen( 'dashboard' );
+		}
+	}
+
+	/**
+	 * Verify that admin integrations are registered in admin contexts.
+	 *
+	 * @testdox Admin integrations are registered in admin contexts.
+	 */
+	public function test_admin_integrations_registered_in_admin_context(): void {
+		set_current_screen( 'dashboard' );
+
+		$analytics = new Analytics();
+
+		try {
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_component_settings_preload_endpoints', array( $analytics, 'add_preload_endpoints' ) ),
+				'Preload endpoints filter should be registered in the admin area.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_admin_get_user_data_fields', array( $analytics, 'add_user_data_fields' ) ),
+				'User data fields filter should be registered in the admin area.'
+			);
+			$this->assertNotFalse(
+				has_action( 'admin_menu', array( $analytics, 'register_pages' ) ),
+				'Admin menu action should be registered in the admin area.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) ),
+				'Cache clear tool filter should be registered in the admin area.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ) ),
+				'Regenerate fulfillment status tool filter should be registered in the admin area.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) ),
+				'Refund double-count tool filter should be registered in the admin area.'
+			);
+		} finally {
+			remove_filter( 'woocommerce_component_settings_preload_endpoints', array( $analytics, 'add_preload_endpoints' ) );
+			remove_filter( 'woocommerce_admin_get_user_data_fields', array( $analytics, 'add_user_data_fields' ) );
+			remove_action( 'admin_menu', array( $analytics, 'register_pages' ) );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ), 12 );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) );
+			remove_action( 'woocommerce_analytics_refund_fix_batch', array( $analytics, 'process_refund_fix_batch' ) );
+			remove_action( Analytics::REFUND_DOUBLE_COUNT_FIX_HOOK, array( $analytics, 'process_refund_double_count_fix_batch' ) );
+			remove_action( 'woocommerce_analytics_regenerate_init', array( $analytics, 'maybe_cancel_refund_double_count_fix_on_regenerate' ) );
+		}
+	}
+
+	/**
+	 * Verify that user data and debug tools are registered in REST context, while admin UI is not.
+	 *
+	 * @testdox User data fields and debug tools are registered in REST context.
+	 */
+	public function test_integrations_registered_in_rest_context(): void {
+		set_current_screen( 'front' );
+		Constants::set_constant( 'REST_REQUEST', true );
+
+		$analytics = new Analytics();
+
+		try {
+			// Debug tools must be available in REST.
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) ),
+				'Cache clear tool filter should be registered in REST context.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ) ),
+				'Regenerate fulfillment status tool filter should be registered in REST context.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) ),
+				'Refund double-count tool filter should be registered in REST context.'
+			);
+
+			// User data fields must be available in REST.
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_admin_get_user_data_fields', array( $analytics, 'add_user_data_fields' ) ),
+				'User data fields filter should be registered in REST context.'
+			);
+
+			// Admin UI must not be registered in REST.
+			$this->assertFalse(
+				has_filter( 'woocommerce_component_settings_preload_endpoints', array( $analytics, 'add_preload_endpoints' ) ),
+				'Preload endpoints filter should not be registered in REST context.'
+			);
+			$this->assertFalse(
+				has_action( 'admin_menu', array( $analytics, 'register_pages' ) ),
+				'Admin menu action should not be registered in REST context.'
+			);
+			$this->assertFalse(
+				has_action( 'admin_footer', array( $analytics, 'output_refund_fix_tool_js' ) ),
+				'Admin footer action should not be registered in REST context.'
+			);
+		} finally {
+			Constants::clear_single_constant( 'REST_REQUEST' );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ), 12 );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) );
+			remove_filter( 'woocommerce_admin_get_user_data_fields', array( $analytics, 'add_user_data_fields' ) );
+			remove_action( 'woocommerce_analytics_refund_fix_batch', array( $analytics, 'process_refund_fix_batch' ) );
+			remove_action( Analytics::REFUND_DOUBLE_COUNT_FIX_HOOK, array( $analytics, 'process_refund_double_count_fix_batch' ) );
+			remove_action( 'woocommerce_analytics_regenerate_init', array( $analytics, 'maybe_cancel_refund_double_count_fix_on_regenerate' ) );
+			set_current_screen( 'dashboard' );
+		}
+	}
+
+	/**
+	 * Verify that debug tools are registered in WP-CLI context, while user data and admin UI are not.
+	 *
+	 * @testdox Debug tools are registered in WP-CLI context.
+	 */
+	public function test_integrations_registered_in_cli_context(): void {
+		set_current_screen( 'front' );
+		Constants::set_constant( 'WP_CLI', true );
+
+		$analytics = new Analytics();
+
+		try {
+			// Debug tools must be available in WP-CLI.
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) ),
+				'Cache clear tool filter should be registered in WP-CLI context.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ) ),
+				'Regenerate fulfillment status tool filter should be registered in WP-CLI context.'
+			);
+			$this->assertNotFalse(
+				has_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) ),
+				'Refund double-count tool filter should be registered in WP-CLI context.'
+			);
+
+			// User data fields and admin UI must not be registered in WP-CLI.
+			$this->assertFalse(
+				has_filter( 'woocommerce_admin_get_user_data_fields', array( $analytics, 'add_user_data_fields' ) ),
+				'User data fields filter should not be registered in WP-CLI context.'
+			);
+			$this->assertFalse(
+				has_filter( 'woocommerce_component_settings_preload_endpoints', array( $analytics, 'add_preload_endpoints' ) ),
+				'Preload endpoints filter should not be registered in WP-CLI context.'
+			);
+			$this->assertFalse(
+				has_action( 'admin_menu', array( $analytics, 'register_pages' ) ),
+				'Admin menu action should not be registered in WP-CLI context.'
+			);
+			$this->assertFalse(
+				has_action( 'admin_footer', array( $analytics, 'output_refund_fix_tool_js' ) ),
+				'Admin footer action should not be registered in WP-CLI context.'
+			);
+		} finally {
+			Constants::clear_single_constant( 'WP_CLI' );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_cache_clear_tool' ) );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_regenerate_order_fulfillment_status_tool' ), 12 );
+			remove_filter( 'woocommerce_debug_tools', array( $analytics, 'register_refund_double_count_tool' ) );
+			remove_action( 'woocommerce_analytics_refund_fix_batch', array( $analytics, 'process_refund_fix_batch' ) );
+			remove_action( Analytics::REFUND_DOUBLE_COUNT_FIX_HOOK, array( $analytics, 'process_refund_double_count_fix_batch' ) );
+			remove_action( 'woocommerce_analytics_regenerate_init', array( $analytics, 'maybe_cancel_refund_double_count_fix_on_regenerate' ) );
+			set_current_screen( 'dashboard' );
+		}
+	}
 }