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.'
+ );
+ }
+}