Commit 2567b1ed31 for woocommerce

commit 2567b1ed31be8e842143f84d245603c1e5ed30fe
Author: Rostislav Wolný <1082140+costasovo@users.noreply.github.com>
Date:   Fri Dec 12 08:36:12 2025 +0100

    Prevent PHP notices about actions scheduler and translations on init (#62394)

    * Prevent notice about uninitialized action scheduler

    The notice uses the translation function and also causes:
    PHP Notice:  Function _load_textdomain_just_in_time was called incorrectly

    * Add changelog

    * Fix error reported by PHP Stan

    * Fix lint error

    * Update `@since`

    ---------

    Co-authored-by:  Ján Mikláš <neosinner@gmail.com>

diff --git a/plugins/woocommerce/changelog/wooplug-5969-improve-translation-loading-to-avoid-wordpress-php-warning b/plugins/woocommerce/changelog/wooplug-5969-improve-translation-loading-to-avoid-wordpress-php-warning
new file mode 100644
index 0000000000..aa99275a93
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-5969-improve-translation-loading-to-avoid-wordpress-php-warning
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent notice as_unschedule_all_actions() was called before the Action Scheduler data store was initialized
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 0d2fa402c5..3b37fbdd47 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -77439,12 +77439,6 @@ parameters:
 			count: 1
 			path: src/Blocks/Patterns/PTKPatternsStore.php

-		-
-			message: '#^Function as_unschedule_all_actions not found\.$#'
-			identifier: function.notFound
-			count: 1
-			path: src/Blocks/Patterns/PTKPatternsStore.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Blocks\\Patterns\\PTKPatternsStore\:\:get_patterns\(\) should return array but returns mixed\.$#'
 			identifier: return.type
diff --git a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php
index b675a9aa3f..2179f39050 100644
--- a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php
+++ b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php
@@ -187,13 +187,28 @@ class PTKPatternsStore {
 	/**
 	 * Reset the cached patterns to fetch them again from the PTK.
 	 *
+	 * @since 10.4.1 Unscheduling is deferred if Action Scheduler hasn't initialized yet.
 	 * @return void
 	 */
 	public function flush_cached_patterns() {
 		delete_option( self::OPTION_NAME );

+		if ( ! function_exists( 'as_unschedule_all_actions' ) ) {
+			return;
+		}
+
 		// Unschedule any existing fetch_patterns actions.
-		as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
+		// Defer unscheduling until Action Scheduler is ready to avoid errors during early initialization.
+		if ( did_action( 'action_scheduler_init' ) ) {
+			as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
+		} else {
+			add_action(
+				'action_scheduler_init',
+				function () {
+					as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
+				}
+			);
+		}
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php b/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php
index 7d6cd2fbf6..9f3245944d 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php
@@ -471,4 +471,53 @@ class PTKPatternsStoreTest extends \WP_UnitTestCase {

 		$this->assertFalse( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'All fetch_patterns actions should be unscheduled after flush' );
 	}
+
+	/**
+	 * Test flush_cached_patterns defers unscheduling when called before action_scheduler_init.
+	 *
+	 * This test simulates the scenario where flush_cached_patterns is called during early
+	 * initialization, before Action Scheduler has been initialized.
+	 */
+	public function test_flush_cached_patterns_defers_unscheduling_before_action_scheduler_init() {
+		global $wp_actions;
+
+		// Schedule an action.
+		as_schedule_single_action( time(), 'fetch_patterns', array(), 'woocommerce' );
+		$this->assertTrue( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'Action should be scheduled initially' );
+
+		// Save the original action_scheduler_init count.
+		$original_count = $wp_actions['action_scheduler_init'] ?? 0;
+
+		// Simulate that action_scheduler_init has not yet fired by unsetting it.
+		if ( isset( $wp_actions['action_scheduler_init'] ) ) {
+			unset( $wp_actions['action_scheduler_init'] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+		}
+
+		// Verify did_action returns 0 (not fired).
+		$this->assertEquals( 0, did_action( 'action_scheduler_init' ), 'action_scheduler_init should appear not fired' );
+
+		// Call flush_cached_patterns before action_scheduler_init has fired.
+		$this->pattern_store->flush_cached_patterns();
+
+		// The option should be deleted immediately.
+		$patterns = get_option( PTKPatternsStore::OPTION_NAME );
+		$this->assertFalse( $patterns, 'Patterns option should be deleted immediately' );
+
+		// The action should still be scheduled (unscheduling is deferred).
+		$this->assertTrue( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'Action should still be scheduled before action_scheduler_init fires' );
+
+		/**
+		 * Simulate action_scheduler_init firing.
+		 *
+		 * @since 10.4.1
+		 * @return void
+		 */
+		do_action( 'action_scheduler_init' );
+
+		// After action_scheduler_init fires, the action should be unscheduled.
+		$this->assertFalse( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'Action should be unscheduled after action_scheduler_init fires' );
+
+		// Restore the original action count.
+		$wp_actions['action_scheduler_init'] = $original_count; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+	}
 }