Commit bc2fe4035c6 for woocommerce

commit bc2fe4035c6066d44aa18ca49bcbbf1ad6999a45
Author: Cvetan Cvetanov <cvetan.cvetanov@automattic.com>
Date:   Tue Jul 28 16:53:06 2026 +0300

    Fix current screen detection before WordPress screen API loads (#66701)

    * Fix current screen detection before screen API loads

    * Add changelog entry for current screen detection fix

    * refactor(admin): consolidate current screen fallback and filter docs

    The woocommerce_navigation_current_screen_id filter was applied three
    times in get_current_screen_id() with two different full docblocks,
    which hook documentation tooling cannot differentiate.

    Collapse the REST and missing-function early returns into a single
    null-screen check and keep one canonical docblock on the final filter
    instance, documenting the screen parameter as WP_Screen|null. The
    filter still receives (false, null) in every early-return case.

    Refs #37870

    * test(admin): drop subprocess bootstrap test for screen detection

    Spawning a PHP subprocess from PHPUnit was unprecedented in the suite,
    booted the whole site against the shared test database outside the
    transaction rollback, and only worked in wp-env.

    Replace it with an in-process test asserting that get_current_screen_id()
    falls back to a filterable false when no current screen is set.

    Refs #37870

    * chore(phpstan): drop stale PageController baseline entries

    The docblock consolidation replaced the bare WP_Screen annotations that
    produced two baselined parameter.phpDocType errors, so the baseline
    referenced errors that no longer exist and CI failed with unmatched
    ignored patterns.

    Remove both entries. A full PHPStan run over the plugin reports no
    errors.

    Refs #37870

diff --git a/plugins/woocommerce/changelog/37870-guard-current-screen b/plugins/woocommerce/changelog/37870-guard-current-screen
new file mode 100644
index 00000000000..436d3059f09
--- /dev/null
+++ b/plugins/woocommerce/changelog/37870-guard-current-screen
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent a fatal error when WooCommerce checks the current admin screen before the WordPress screen API is available.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 6a2ce350f6c..8ade683547a 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -47613,18 +47613,6 @@ parameters:
 			count: 1
 			path: src/Admin/Overrides/ThemeUpgraderSkin.php

-		-
-			message: '#^@param Automattic\\WooCommerce\\Admin\\WP_Screen \$current_screen does not accept actual type of parameter\: WP_Screen\.$#'
-			identifier: parameter.phpDocType
-			count: 1
-			path: src/Admin/PageController.php
-
-		-
-			message: '#^@param Automattic\\WooCommerce\\Admin\\WP_Screen \$current_screen does not accept actual type of parameter\: null\.$#'
-			identifier: parameter.phpDocType
-			count: 1
-			path: src/Admin/PageController.php
-
 		-
 			message: '#^Binary operation "\." between ''&path\='' and non\-empty\-array\<mixed\>\|non\-falsy\-string results in an error\.$#'
 			identifier: binaryOp.invalid
@@ -47664,7 +47652,7 @@ parameters:
 		-
 			message: '#^Method Automattic\\WooCommerce\\Admin\\PageController\:\:get_current_screen_id\(\) should return string but returns bool\|string\.$#'
 			identifier: return.type
-			count: 2
+			count: 1
 			path: src/Admin/PageController.php

 		-
diff --git a/plugins/woocommerce/src/Admin/PageController.php b/plugins/woocommerce/src/Admin/PageController.php
index 318439ebf3d..9b86c112920 100644
--- a/plugins/woocommerce/src/Admin/PageController.php
+++ b/plugins/woocommerce/src/Admin/PageController.php
@@ -259,20 +259,9 @@ class PageController {
 	 * @return string Current screen ID.
 	 */
 	public function get_current_screen_id() {
-		// Return early if this is a REST API request.
-		if ( wp_is_serving_rest_request() ) {
-			/**
-			 * Filter the current screen ID for REST API requests.
-			 *
-			 * @since 3.9.0
-			 *
-			 * @param string|boolean $screen_id The screen id or false if not identified.
-			 * @param WP_Screen      $current_screen The current WP_Screen.
-			 */
-			return apply_filters( 'woocommerce_navigation_current_screen_id', false, null );
-		}
+		// The screen cannot be determined during REST API requests or before the WordPress screen API loads.
+		$current_screen = ( wp_is_serving_rest_request() || ! function_exists( 'get_current_screen' ) ) ? null : get_current_screen();

-		$current_screen = get_current_screen();
 		if ( ! $current_screen ) {
 			// Filter documentation below.
 			return apply_filters( 'woocommerce_navigation_current_screen_id', false, $current_screen );
@@ -367,8 +356,10 @@ class PageController {
 		 *
 		 * Used for identifying pages to render the WooCommerce Admin header.
 		 *
-		 * @param string|boolean $screen_id The screen id or false if not identified.
-		 * @param WP_Screen      $current_screen The current WP_Screen.
+		 * @since 3.9.0
+		 *
+		 * @param string|boolean  $screen_id The screen id or false if not identified.
+		 * @param \WP_Screen|null $current_screen The current WP_Screen or null if it could not be determined.
 		 */
 		return apply_filters( 'woocommerce_navigation_current_screen_id', implode( '-', $screen_pieces ), $current_screen );
 	}
diff --git a/plugins/woocommerce/tests/php/src/Admin/PageControllerCurrentScreenTest.php b/plugins/woocommerce/tests/php/src/Admin/PageControllerCurrentScreenTest.php
new file mode 100644
index 00000000000..6ff557a4528
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Admin/PageControllerCurrentScreenTest.php
@@ -0,0 +1,61 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Admin;
+
+use Automattic\WooCommerce\Admin\PageController;
+use WC_Unit_Test_Case;
+
+/**
+ * Tests for current screen detection in PageController.
+ */
+class PageControllerCurrentScreenTest extends WC_Unit_Test_Case {
+	/**
+	 * Backup of $GLOBALS['current_screen'].
+	 *
+	 * @var object|null
+	 */
+	private $current_screen_backup;
+
+	/**
+	 * Set up test fixtures.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->current_screen_backup = $GLOBALS['current_screen'] ?? null;
+	}
+
+	/**
+	 * Tear down test fixtures.
+	 */
+	public function tearDown(): void {
+		$GLOBALS['current_screen'] = $this->current_screen_backup; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+		parent::tearDown();
+	}
+
+	/**
+	 * @testdox Should fall back to a filterable false when the current screen cannot be determined.
+	 */
+	public function test_get_current_screen_id_returns_false_when_current_screen_is_unavailable(): void {
+		$GLOBALS['current_screen'] = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+
+		$filter_args = null;
+		$filter      = function ( $screen_id, $current_screen ) use ( &$filter_args ) {
+			$filter_args = array( $screen_id, $current_screen );
+			return $screen_id;
+		};
+		add_filter( 'woocommerce_navigation_current_screen_id', $filter, 10, 2 );
+
+		$screen_id = PageController::get_instance()->get_current_screen_id();
+
+		remove_filter( 'woocommerce_navigation_current_screen_id', $filter );
+
+		$this->assertFalse( $screen_id, 'get_current_screen_id() should return false when no screen is set.' );
+		$this->assertSame(
+			array( false, null ),
+			$filter_args,
+			'The false fallback should pass through the woocommerce_navigation_current_screen_id filter with a null screen.'
+		);
+	}
+}