Commit 6c00db89bd9 for woocommerce
commit 6c00db89bd94d8bc8bc44e96167da27aa0de2952
Author: woocommercebot <30233865+woocommercebot@users.noreply.github.com>
Date: Fri Jul 3 00:28:20 2026 +0530
[Backport to trunk] Avoid update-time fatals in 10.9 settings paths (#66214)
* Avoid update-time fatals in 10.9 settings paths (#66081)
* Avoid update-time fatals: keep a Settings controller stub and guard new settings SDK classes
* chore: add changelog for update fatal fix
* fix: tolerate settings SDK autoload failures
---------
Co-authored-by: Jorge Torres <jorge.torres@automattic.com>
Co-authored-by: Cvetan Cvetanov <cvetan.cvetanov@automattic.com>
* Remove changelog file for the backported 10.9.2 fix
---------
Co-authored-by: Daniel Mallory <daniel.mallory@automattic.com>
Co-authored-by: Jorge Torres <jorge.torres@automattic.com>
Co-authored-by: Cvetan Cvetanov <cvetan.cvetanov@automattic.com>
diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-page.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-page.php
index aeb457063aa..d89f14908b4 100644
--- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-page.php
+++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-page.php
@@ -139,9 +139,9 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
}
$section = is_string( $current_section ) ? $current_section : '';
- $context = SettingsUIRequestContext::for_settings_page( $this, $section );
+ $context = $this->get_settings_ui_request_context( $section );
- if ( ! $context->is_rendering_enabled() ) {
+ if ( ! $context || ! $context->is_rendering_enabled() ) {
return $classes;
}
@@ -253,6 +253,44 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
return apply_filters( 'woocommerce_get_settings_' . $this->id, $settings, $section_id );
}
+ /**
+ * Settings section registry instance, or null when the modern settings SDK is unavailable.
+ *
+ * The class can be missing mid-update: a 10.9 copy of this file may load before the autoloader
+ * class map can safely resolve the new registry, so a direct call would fatal.
+ *
+ * @return SettingsSectionRegistry|null
+ */
+ protected function get_settings_section_registry() {
+ try {
+ if ( ! class_exists( SettingsSectionRegistry::class ) ) {
+ return null;
+ }
+
+ return SettingsSectionRegistry::get_instance();
+ } catch ( \Throwable $e ) {
+ return null;
+ }
+ }
+
+ /**
+ * Settings UI request context, or null when the modern settings SDK is unavailable.
+ *
+ * @param string $section Section id.
+ * @return SettingsUIRequestContext|null
+ */
+ protected function get_settings_ui_request_context( $section ) {
+ try {
+ if ( ! class_exists( SettingsUIRequestContext::class ) ) {
+ return null;
+ }
+
+ return SettingsUIRequestContext::for_settings_page( $this, $section );
+ } catch ( \Throwable $e ) {
+ return null;
+ }
+ }
+
/**
* Get the settings for a given section.
* This method is invoked from 'get_settings_for_section' when no 'get_settings_for_{current_section}_section'
@@ -266,7 +304,8 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
* @return array Settings array, each item being an associative array representing a setting.
*/
protected function get_settings_for_section_core( $section_id ) {
- $registered_section = SettingsSectionRegistry::get_instance()->get_registered( $this->id, (string) $section_id );
+ $registry = $this->get_settings_section_registry();
+ $registered_section = $registry ? $registry->get_registered( $this->id, (string) $section_id ) : null;
return $registered_section ? $registered_section->get_settings( $this ) : array();
}
@@ -278,7 +317,8 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
*/
public function get_sections() {
$sections = $this->get_own_sections();
- $registered_sections = SettingsSectionRegistry::get_instance()->get_sections_for_page( $this->id );
+ $registry = $this->get_settings_section_registry();
+ $registered_sections = $registry ? $registry->get_sections_for_page( $this->id ) : array();
foreach ( $registered_sections as $section_id => $section_label ) {
// Preserve sections declared by the settings page when a registered section uses the same id.
@@ -349,9 +389,9 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
global $current_section;
$section = is_string( $current_section ) ? $current_section : '';
- $context = SettingsUIRequestContext::for_settings_page( $this, $section );
+ $context = $this->get_settings_ui_request_context( $section );
- if ( $context->is_rendering_enabled() ) {
+ if ( $context && $context->is_rendering_enabled() ) {
$settings_ui_page = $context->get_settings_ui_page();
assert( $settings_ui_page instanceof SettingsUIPageInterface );
diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways.php
index dc6df1ae1bf..1dd6937dffe 100644
--- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways.php
+++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways.php
@@ -240,7 +240,9 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
return false;
}
- return null !== SettingsSectionRegistry::get_instance()->get_registered( self::TAB_NAME, (string) $section );
+ $registry = $this->get_settings_section_registry();
+
+ return null !== $registry && null !== $registry->get_registered( self::TAB_NAME, (string) $section );
}
/**
diff --git a/plugins/woocommerce/src/Admin/API/Settings.php b/plugins/woocommerce/src/Admin/API/Settings.php
new file mode 100644
index 00000000000..36e9fcea83f
--- /dev/null
+++ b/plugins/woocommerce/src/Admin/API/Settings.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * REST API Settings Controller (compatibility stub).
+ *
+ * @package WooCommerce\Admin\API
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Admin\API;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Settings controller.
+ *
+ * The real controller was removed in 10.9 with the settings editor. This empty stub stays so an
+ * in-memory 10.8 controller list, still naming this class while the files are swapped to 10.9
+ * during an update, can instantiate it instead of fataling on the deleted file. It registers
+ * nothing.
+ *
+ * @deprecated 10.9.0
+ */
+class Settings {
+
+ /**
+ * Register routes. Intentionally a no-op.
+ */
+ public function register_routes(): void {}
+}
diff --git a/plugins/woocommerce/src/Internal/Admin/Settings.php b/plugins/woocommerce/src/Internal/Admin/Settings.php
index 0f19c7d1a59..5099942d857 100644
--- a/plugins/woocommerce/src/Internal/Admin/Settings.php
+++ b/plugins/woocommerce/src/Internal/Admin/Settings.php
@@ -416,7 +416,16 @@ class Settings {
* @return array
*/
private function add_settings_ui_schema( array $settings ): array {
- $context = SettingsUIRequestContext::get_current();
+ try {
+ if ( ! class_exists( SettingsUIRequestContext::class ) ) {
+ return $settings;
+ }
+
+ $context = SettingsUIRequestContext::get_current();
+ } catch ( \Throwable $e ) {
+ return $settings;
+ }
+
if ( ! $context ) {
return $settings;
}
diff --git a/plugins/woocommerce/src/Internal/Admin/Settings/SettingsUIRequestContext.php b/plugins/woocommerce/src/Internal/Admin/Settings/SettingsUIRequestContext.php
index dfb7c24dd6d..093fd925d8c 100644
--- a/plugins/woocommerce/src/Internal/Admin/Settings/SettingsUIRequestContext.php
+++ b/plugins/woocommerce/src/Internal/Admin/Settings/SettingsUIRequestContext.php
@@ -344,7 +344,11 @@ class SettingsUIRequestContext {
* @return SettingsUIPageInterface|null
*/
private static function resolve_settings_ui_page( \WC_Settings_Page $settings_page, string $section ): ?SettingsUIPageInterface {
- $registered_section = SettingsSectionRegistry::get_instance()->get_registered( $settings_page->get_id(), $section );
+ try {
+ $registered_section = SettingsSectionRegistry::get_instance()->get_registered( $settings_page->get_id(), $section );
+ } catch ( \Throwable $e ) {
+ $registered_section = null;
+ }
if ( $registered_section ) {
return new RegisteredSettingsSectionAdapter( $settings_page, $registered_section );
diff --git a/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php b/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php
index 65aa7ea2e31..3ce46b2a440 100644
--- a/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php
+++ b/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php
@@ -433,7 +433,16 @@ class WCAdminAssets {
* @return array
*/
private function get_settings_ui_script_dependencies(): array {
- $context = SettingsUIRequestContext::get_current();
+ try {
+ if ( ! class_exists( SettingsUIRequestContext::class ) ) {
+ return array();
+ }
+
+ $context = SettingsUIRequestContext::get_current();
+ } catch ( \Throwable $e ) {
+ return array();
+ }
+
if ( ! $context ) {
return array();
}