From 0ff2067c52f375e0c73f38de5a9effcd3ff8db3e Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Jan 2026 17:10:43 +0000 Subject: [PATCH 01/16] Initial implementation --- src/wp-includes/abilities.php | 4 + .../abilities/class-wp-settings-abilities.php | 187 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 src/wp-includes/abilities/class-wp-settings-abilities.php diff --git a/src/wp-includes/abilities.php b/src/wp-includes/abilities.php index 0320df3b9f38a..3ea82e8867f06 100644 --- a/src/wp-includes/abilities.php +++ b/src/wp-includes/abilities.php @@ -9,6 +9,8 @@ declare( strict_types = 1 ); +require_once __DIR__ . '/abilities/class-wp-settings-abilities.php'; + /** * Registers the core ability categories. * @@ -259,4 +261,6 @@ function wp_register_core_abilities(): void { ), ) ); + + WP_Settings_Abilities::register(); } diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php new file mode 100644 index 0000000000000..bcc74e8dbf4be --- /dev/null +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -0,0 +1,187 @@ + __( 'Get Settings' ), + 'description' => __( 'Returns registered WordPress settings exposed to the REST API, grouped by their registration group. Returns key-value pairs similar to the REST API settings endpoint.' ), + 'category' => 'site', + 'input_schema' => array( + 'type' => 'object', + 'properties' => array( + 'group' => array( + 'type' => 'string', + 'description' => __( 'Optional: Filter settings by group name (e.g., general, reading, writing, discussion).' ), + ), + ), + 'additionalProperties' => false, + 'default' => array(), + ), + 'output_schema' => array( + 'type' => 'object', + 'description' => __( 'Settings grouped by registration group. Each group contains key-value pairs where the key is the setting name (or REST alias) and the value is the current setting value.' ), + 'additionalProperties' => array( + 'type' => 'object', + 'description' => __( 'A settings group containing setting name to value mappings.' ), + 'additionalProperties' => true, + ), + ), + 'execute_callback' => array( __CLASS__, 'execute_get_settings' ), + 'permission_callback' => array( __CLASS__, 'check_manage_options' ), + 'meta' => array( + 'annotations' => array( + 'readOnlyHint' => true, + 'destructiveHint' => false, + 'idempotentHint' => true, + ), + 'show_in_rest' => true, + ), + ) + ); + } + + /** + * Permission callback for settings abilities. + * + * @since 6.9.0 + * + * @return bool True if the current user can manage options, false otherwise. + */ + public static function check_manage_options(): bool { + return current_user_can( 'manage_options' ); + } + + /** + * Execute callback for core/get-settings ability. + * + * Retrieves all registered settings that are exposed to the REST API, + * grouped by their registration group. + * + * @since 6.9.0 + * + * @param array $input { + * Optional. Input parameters. + * + * @type string $group Optional. Filter settings by group name. + * } + * @return array Settings grouped by registration group. + */ + public static function execute_get_settings( $input = array() ): array { + $input = is_array( $input ) ? $input : array(); + $filter_group = ! empty( $input['group'] ) ? $input['group'] : null; + + $registered_settings = get_registered_settings(); + $settings_by_group = array(); + + foreach ( $registered_settings as $option_name => $args ) { + // Only include settings exposed to REST API. + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $group = $args['group'] ?? 'general'; + + // Skip if filtering by group and doesn't match. + if ( $filter_group && $group !== $filter_group ) { + continue; + } + + // Determine the REST name (may be aliased via show_in_rest.name). + $rest_name = $option_name; + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { + $rest_name = $args['show_in_rest']['name']; + } + + // Get default value. + $default = $args['default'] ?? null; + if ( is_array( $args['show_in_rest'] ) && isset( $args['show_in_rest']['schema']['default'] ) ) { + $default = $args['show_in_rest']['schema']['default']; + } + + // Get current value. + $value = get_option( $option_name, $default ); + + // Cast value to proper type. + $value = self::cast_value( $value, $args['type'] ?? 'string' ); + + // Initialize group if needed. + if ( ! isset( $settings_by_group[ $group ] ) ) { + $settings_by_group[ $group ] = array(); + } + + $settings_by_group[ $group ][ $rest_name ] = $value; + } + + ksort( $settings_by_group ); + + return $settings_by_group; + } + + /** + * Casts a value to the appropriate type based on the setting's registered type. + * + * @since 6.9.0 + * + * @param mixed $value The value to cast. + * @param string $type The registered type (string, boolean, integer, number, array, object). + * @return mixed The cast value. + */ + private static function cast_value( $value, string $type ) { + switch ( $type ) { + case 'boolean': + return (bool) $value; + case 'integer': + return (int) $value; + case 'number': + return (float) $value; + case 'array': + case 'object': + return is_array( $value ) ? $value : array(); + case 'string': + default: + return (string) $value; + } + } +} From 8e5d7fb7e2d3314bb291c919671fba1ee7bb9972 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Jan 2026 17:17:23 +0000 Subject: [PATCH 02/16] Improve input schema --- .../abilities/class-wp-settings-abilities.php | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index bcc74e8dbf4be..937d7457eca25 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -23,6 +23,14 @@ */ class WP_Settings_Abilities { + /** + * Available setting groups with show_in_rest enabled. + * + * @since 6.9.0 + * @var array + */ + private static $available_groups; + /** * Registers all settings abilities. * @@ -31,9 +39,47 @@ class WP_Settings_Abilities { * @return void */ public static function register(): void { + self::init(); self::register_get_settings(); } + /** + * Initializes shared data for settings abilities. + * + * @since 6.9.0 + * + * @return void + */ + private static function init(): void { + self::$available_groups = self::get_available_groups(); + } + + /** + * Gets unique setting groups that have show_in_rest enabled. + * + * @since 6.9.0 + * + * @return array List of unique group names. + */ + private static function get_available_groups(): array { + $groups = array(); + + foreach ( get_registered_settings() as $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $group = $args['group'] ?? 'general'; + if ( ! in_array( $group, $groups, true ) ) { + $groups[] = $group; + } + } + + sort( $groups ); + + return $groups; + } + /** * Registers the core/get-settings ability. * @@ -53,7 +99,8 @@ private static function register_get_settings(): void { 'properties' => array( 'group' => array( 'type' => 'string', - 'description' => __( 'Optional: Filter settings by group name (e.g., general, reading, writing, discussion).' ), + 'description' => __( 'Filter settings by group name. If omitted, returns all groups.' ), + 'enum' => self::$available_groups, ), ), 'additionalProperties' => false, From 93f1983895280a250386e8da1fe07f92b569d9c4 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Jan 2026 17:26:51 +0000 Subject: [PATCH 03/16] Improve output schema --- .../abilities/class-wp-settings-abilities.php | 98 +++++++++++++++++-- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 937d7457eca25..718ea8a93c63c 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -31,6 +31,14 @@ class WP_Settings_Abilities { */ private static $available_groups; + /** + * Dynamic output schema built from registered settings. + * + * @since 6.9.0 + * @var array + */ + private static $output_schema; + /** * Registers all settings abilities. * @@ -52,6 +60,7 @@ public static function register(): void { */ private static function init(): void { self::$available_groups = self::get_available_groups(); + self::$output_schema = self::build_output_schema(); } /** @@ -80,6 +89,85 @@ private static function get_available_groups(): array { return $groups; } + /** + * Builds a rich output schema from registered settings metadata. + * + * Creates a JSON Schema that documents each setting group and its settings + * with their types, titles, descriptions, defaults, and any additional + * schema properties from show_in_rest. + * + * @since 6.9.0 + * + * @return array JSON Schema for the output. + */ + private static function build_output_schema(): array { + $group_properties = array(); + + foreach ( get_registered_settings() as $option_name => $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $group = $args['group'] ?? 'general'; + + // Determine the REST name (may be aliased via show_in_rest.name). + $rest_name = $option_name; + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { + $rest_name = $args['show_in_rest']['name']; + } + + // Build setting schema from registered metadata. + $setting_schema = array( + 'type' => $args['type'] ?? 'string', + ); + + // Add title from label if available. + if ( ! empty( $args['label'] ) ) { + $setting_schema['title'] = $args['label']; + } + + // Use description if set, otherwise fall back to label. + if ( ! empty( $args['description'] ) ) { + $setting_schema['description'] = $args['description']; + } elseif ( ! empty( $args['label'] ) ) { + $setting_schema['description'] = $args['label']; + } + + // Include default if set. + if ( isset( $args['default'] ) ) { + $setting_schema['default'] = $args['default']; + } + + // Merge any schema from show_in_rest (enum, format, etc.). + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['schema'] ) ) { + $setting_schema = array_merge( $setting_schema, $args['show_in_rest']['schema'] ); + } + + // Initialize group if needed. + if ( ! isset( $group_properties[ $group ] ) ) { + $group_properties[ $group ] = array( + 'type' => 'object', + 'description' => sprintf( + /* translators: %s: Settings group name. */ + __( '%s settings.' ), + ucfirst( $group ) + ), + 'properties' => array(), + ); + } + + $group_properties[ $group ]['properties'][ $rest_name ] = $setting_schema; + } + + ksort( $group_properties ); + + return array( + 'type' => 'object', + 'description' => __( 'Settings grouped by registration group. Each group contains settings with their current values.' ), + 'properties' => $group_properties, + ); + } + /** * Registers the core/get-settings ability. * @@ -106,15 +194,7 @@ private static function register_get_settings(): void { 'additionalProperties' => false, 'default' => array(), ), - 'output_schema' => array( - 'type' => 'object', - 'description' => __( 'Settings grouped by registration group. Each group contains key-value pairs where the key is the setting name (or REST alias) and the value is the current setting value.' ), - 'additionalProperties' => array( - 'type' => 'object', - 'description' => __( 'A settings group containing setting name to value mappings.' ), - 'additionalProperties' => true, - ), - ), + 'output_schema' => self::$output_schema, 'execute_callback' => array( __CLASS__, 'execute_get_settings' ), 'permission_callback' => array( __CLASS__, 'check_manage_options' ), 'meta' => array( From 1eacfc3e392a2a4fb433894d03871c00842a59f8 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Jan 2026 17:41:23 +0000 Subject: [PATCH 04/16] remove irrelevant comments --- .../abilities/class-wp-settings-abilities.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 718ea8a93c63c..40bcba97cf135 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -110,40 +110,33 @@ private static function build_output_schema(): array { $group = $args['group'] ?? 'general'; - // Determine the REST name (may be aliased via show_in_rest.name). $rest_name = $option_name; if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { $rest_name = $args['show_in_rest']['name']; } - // Build setting schema from registered metadata. $setting_schema = array( 'type' => $args['type'] ?? 'string', ); - // Add title from label if available. if ( ! empty( $args['label'] ) ) { $setting_schema['title'] = $args['label']; } - // Use description if set, otherwise fall back to label. if ( ! empty( $args['description'] ) ) { $setting_schema['description'] = $args['description']; } elseif ( ! empty( $args['label'] ) ) { $setting_schema['description'] = $args['label']; } - // Include default if set. if ( isset( $args['default'] ) ) { $setting_schema['default'] = $args['default']; } - // Merge any schema from show_in_rest (enum, format, etc.). if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['schema'] ) ) { $setting_schema = array_merge( $setting_schema, $args['show_in_rest']['schema'] ); } - // Initialize group if needed. if ( ! isset( $group_properties[ $group ] ) ) { $group_properties[ $group ] = array( 'type' => 'object', @@ -243,37 +236,29 @@ public static function execute_get_settings( $input = array() ): array { $settings_by_group = array(); foreach ( $registered_settings as $option_name => $args ) { - // Only include settings exposed to REST API. if ( empty( $args['show_in_rest'] ) ) { continue; } $group = $args['group'] ?? 'general'; - // Skip if filtering by group and doesn't match. if ( $filter_group && $group !== $filter_group ) { continue; } - // Determine the REST name (may be aliased via show_in_rest.name). $rest_name = $option_name; if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { $rest_name = $args['show_in_rest']['name']; } - // Get default value. $default = $args['default'] ?? null; if ( is_array( $args['show_in_rest'] ) && isset( $args['show_in_rest']['schema']['default'] ) ) { $default = $args['show_in_rest']['schema']['default']; } - // Get current value. $value = get_option( $option_name, $default ); - - // Cast value to proper type. $value = self::cast_value( $value, $args['type'] ?? 'string' ); - // Initialize group if needed. if ( ! isset( $settings_by_group[ $group ] ) ) { $settings_by_group[ $group ] = array(); } From 5e6b094009783a09b21382bc794ab7133003a473 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Jan 2026 19:27:53 +0000 Subject: [PATCH 05/16] description update --- src/wp-includes/abilities/class-wp-settings-abilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 40bcba97cf135..59d56c00f5476 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -173,7 +173,7 @@ private static function register_get_settings(): void { 'core/get-settings', array( 'label' => __( 'Get Settings' ), - 'description' => __( 'Returns registered WordPress settings exposed to the REST API, grouped by their registration group. Returns key-value pairs similar to the REST API settings endpoint.' ), + 'description' => __( 'Returns registered WordPress settings grouped by their registration group. Returns key-value pairs per setting.' ), 'category' => 'site', 'input_schema' => array( 'type' => 'object', From ff2a4ef98f51faebc792eee79212a10b400e4f66 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 19 Jan 2026 10:25:17 +0000 Subject: [PATCH 06/16] Update src/wp-includes/abilities/class-wp-settings-abilities.php Co-authored-by: Mukesh Panchal --- src/wp-includes/abilities/class-wp-settings-abilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 59d56c00f5476..bc91bcb974485 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -7,7 +7,7 @@ * * @package WordPress * @subpackage Abilities_API - * @since 6.9.0 + * @since 7.0.0 * * @internal This class is not part of the public API. * @access private From c226a101f2415b25a1eb65fa0da4f34ca7628fff Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 14:04:26 +0000 Subject: [PATCH 07/16] Update src/wp-includes/abilities/class-wp-settings-abilities.php Co-authored-by: Dovid Levine --- src/wp-includes/abilities/class-wp-settings-abilities.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index bc91bcb974485..f6e24cbb1d262 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -192,9 +192,9 @@ private static function register_get_settings(): void { 'permission_callback' => array( __CLASS__, 'check_manage_options' ), 'meta' => array( 'annotations' => array( - 'readOnlyHint' => true, - 'destructiveHint' => false, - 'idempotentHint' => true, + 'readonly' => true, + 'destructive' => false, + 'idempotent' => true, ), 'show_in_rest' => true, ), From dbb09f8bbf9862f2ecfba3c8c02098af1d2036f3 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 14:42:30 +0000 Subject: [PATCH 08/16] Add slugs parameter to core/get-settings ability Addresses review feedback to add support for filtering settings by specific setting slugs. The input schema now supports both `group` and `slugs` parameters with a oneOf relationship making them mutually exclusive. - Add $available_slugs static property - Add get_available_slugs() method to collect setting slugs - Update input_schema with slugs parameter and oneOf constraint - Update execute_get_settings() to filter by slugs when provided --- .../abilities/class-wp-settings-abilities.php | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index f6e24cbb1d262..0f5de5283e56a 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -39,6 +39,14 @@ class WP_Settings_Abilities { */ private static $output_schema; + /** + * Available setting slugs with show_in_rest enabled. + * + * @since 7.0.0 + * @var array + */ + private static $available_slugs; + /** * Registers all settings abilities. * @@ -60,6 +68,7 @@ public static function register(): void { */ private static function init(): void { self::$available_groups = self::get_available_groups(); + self::$available_slugs = self::get_available_slugs(); self::$output_schema = self::build_output_schema(); } @@ -89,6 +98,34 @@ private static function get_available_groups(): array { return $groups; } + /** + * Gets unique setting slugs that have show_in_rest enabled. + * + * @since 7.0.0 + * + * @return array List of unique setting slugs. + */ + private static function get_available_slugs(): array { + $slugs = array(); + + foreach ( get_registered_settings() as $option_name => $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $rest_name = $option_name; + if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { + $rest_name = $args['show_in_rest']['name']; + } + + $slugs[] = $rest_name; + } + + sort( $slugs ); + + return $slugs; + } + /** * Builds a rich output schema from registered settings metadata. * @@ -180,9 +217,22 @@ private static function register_get_settings(): void { 'properties' => array( 'group' => array( 'type' => 'string', - 'description' => __( 'Filter settings by group name. If omitted, returns all groups.' ), + 'description' => __( 'Filter settings by group name. If omitted, returns all groups. Cannot be used with slugs.' ), 'enum' => self::$available_groups, ), + 'slugs' => array( + 'type' => 'array', + 'description' => __( 'Filter settings by specific setting slugs. If omitted, returns all settings. Cannot be used with group.' ), + 'items' => array( + 'type' => 'string', + 'enum' => self::$available_slugs, + ), + ), + ), + 'oneOf' => array( + array( 'required' => array( 'group' ) ), + array( 'required' => array( 'slugs' ) ), + array( 'maxProperties' => 0 ), ), 'additionalProperties' => false, 'default' => array(), @@ -224,13 +274,15 @@ public static function check_manage_options(): bool { * @param array $input { * Optional. Input parameters. * - * @type string $group Optional. Filter settings by group name. + * @type string $group Optional. Filter settings by group name. Cannot be used with slugs. + * @type string[] $slugs Optional. Filter settings by specific setting slugs. Cannot be used with group. * } * @return array Settings grouped by registration group. */ public static function execute_get_settings( $input = array() ): array { $input = is_array( $input ) ? $input : array(); $filter_group = ! empty( $input['group'] ) ? $input['group'] : null; + $filter_slugs = ! empty( $input['slugs'] ) ? $input['slugs'] : null; $registered_settings = get_registered_settings(); $settings_by_group = array(); @@ -251,6 +303,10 @@ public static function execute_get_settings( $input = array() ): array { $rest_name = $args['show_in_rest']['name']; } + if ( $filter_slugs && ! in_array( $rest_name, $filter_slugs, true ) ) { + continue; + } + $default = $args['default'] ?? null; if ( is_array( $args['show_in_rest'] ) && isset( $args['show_in_rest']['schema']['default'] ) ) { $default = $args['show_in_rest']['schema']['default']; From 74da151af721d7ab3f4b41941ca6b3f2149a604e Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 14:42:41 +0000 Subject: [PATCH 09/16] Add return type union to cast_value() method Use explicit union type (string|bool|int|float|array) instead of mixed for better type safety and documentation. --- src/wp-includes/abilities/class-wp-settings-abilities.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 0f5de5283e56a..05e54c7fe3686 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -334,9 +334,9 @@ public static function execute_get_settings( $input = array() ): array { * * @param mixed $value The value to cast. * @param string $type The registered type (string, boolean, integer, number, array, object). - * @return mixed The cast value. + * @return string|bool|int|float|array The cast value. */ - private static function cast_value( $value, string $type ) { + private static function cast_value( $value, string $type ): string|bool|int|float|array { switch ( $type ) { case 'boolean': return (bool) $value; From 8d1808992a073876ad76a48ef7c7f29720e703a5 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 15:15:19 +0000 Subject: [PATCH 10/16] remove schema 04 causing validation issues --- .../abilities/class-wp-settings-abilities.php | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 05e54c7fe3686..41b539d9e02dc 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -170,19 +170,16 @@ private static function build_output_schema(): array { $setting_schema['default'] = $args['default']; } - if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['schema'] ) ) { - $setting_schema = array_merge( $setting_schema, $args['show_in_rest']['schema'] ); - } - if ( ! isset( $group_properties[ $group ] ) ) { $group_properties[ $group ] = array( - 'type' => 'object', - 'description' => sprintf( + 'type' => 'object', + 'description' => sprintf( /* translators: %s: Settings group name. */ __( '%s settings.' ), ucfirst( $group ) ), - 'properties' => array(), + 'properties' => array(), + 'additionalProperties' => false, ); } @@ -192,9 +189,10 @@ private static function build_output_schema(): array { ksort( $group_properties ); return array( - 'type' => 'object', - 'description' => __( 'Settings grouped by registration group. Each group contains settings with their current values.' ), - 'properties' => $group_properties, + 'type' => 'object', + 'description' => __( 'Settings grouped by registration group. Each group contains settings with their current values.' ), + 'properties' => $group_properties, + 'additionalProperties' => false, ); } @@ -229,11 +227,6 @@ private static function register_get_settings(): void { ), ), ), - 'oneOf' => array( - array( 'required' => array( 'group' ) ), - array( 'required' => array( 'slugs' ) ), - array( 'maxProperties' => 0 ), - ), 'additionalProperties' => false, 'default' => array(), ), From 50a4af41e06a5b827be736cf4cb76fc03629538b Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 16:05:40 +0000 Subject: [PATCH 11/16] use raw option names --- .../abilities/class-wp-settings-abilities.php | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 41b539d9e02dc..058eb601b862e 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -113,12 +113,7 @@ private static function get_available_slugs(): array { continue; } - $rest_name = $option_name; - if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { - $rest_name = $args['show_in_rest']['name']; - } - - $slugs[] = $rest_name; + $slugs[] = $option_name; } sort( $slugs ); @@ -147,11 +142,6 @@ private static function build_output_schema(): array { $group = $args['group'] ?? 'general'; - $rest_name = $option_name; - if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { - $rest_name = $args['show_in_rest']['name']; - } - $setting_schema = array( 'type' => $args['type'] ?? 'string', ); @@ -166,10 +156,6 @@ private static function build_output_schema(): array { $setting_schema['description'] = $args['label']; } - if ( isset( $args['default'] ) ) { - $setting_schema['default'] = $args['default']; - } - if ( ! isset( $group_properties[ $group ] ) ) { $group_properties[ $group ] = array( 'type' => 'object', @@ -183,7 +169,7 @@ private static function build_output_schema(): array { ); } - $group_properties[ $group ]['properties'][ $rest_name ] = $setting_schema; + $group_properties[ $group ]['properties'][ $option_name ] = $setting_schema; } ksort( $group_properties ); @@ -291,19 +277,11 @@ public static function execute_get_settings( $input = array() ): array { continue; } - $rest_name = $option_name; - if ( is_array( $args['show_in_rest'] ) && ! empty( $args['show_in_rest']['name'] ) ) { - $rest_name = $args['show_in_rest']['name']; - } - - if ( $filter_slugs && ! in_array( $rest_name, $filter_slugs, true ) ) { + if ( $filter_slugs && ! in_array( $option_name, $filter_slugs, true ) ) { continue; } $default = $args['default'] ?? null; - if ( is_array( $args['show_in_rest'] ) && isset( $args['show_in_rest']['schema']['default'] ) ) { - $default = $args['show_in_rest']['schema']['default']; - } $value = get_option( $option_name, $default ); $value = self::cast_value( $value, $args['type'] ?? 'string' ); @@ -312,7 +290,7 @@ public static function execute_get_settings( $input = array() ): array { $settings_by_group[ $group ] = array(); } - $settings_by_group[ $group ][ $rest_name ] = $value; + $settings_by_group[ $group ][ $option_name ] = $value; } ksort( $settings_by_group ); From d1aeb9141b8548c986f2961316968d1e390ede2d Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 16:23:42 +0000 Subject: [PATCH 12/16] only apply rest filters when serving rest requests --- src/wp-includes/abilities/class-wp-settings-abilities.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 058eb601b862e..82d30191f88ef 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -83,7 +83,7 @@ private static function get_available_groups(): array { $groups = array(); foreach ( get_registered_settings() as $args ) { - if ( empty( $args['show_in_rest'] ) ) { + if ( wp_is_serving_rest_request() && empty( $args['show_in_rest'] ) ) { continue; } @@ -109,7 +109,7 @@ private static function get_available_slugs(): array { $slugs = array(); foreach ( get_registered_settings() as $option_name => $args ) { - if ( empty( $args['show_in_rest'] ) ) { + if ( wp_is_serving_rest_request() && empty( $args['show_in_rest'] ) ) { continue; } @@ -136,7 +136,7 @@ private static function build_output_schema(): array { $group_properties = array(); foreach ( get_registered_settings() as $option_name => $args ) { - if ( empty( $args['show_in_rest'] ) ) { + if ( wp_is_serving_rest_request() && empty( $args['show_in_rest'] ) ) { continue; } @@ -267,7 +267,7 @@ public static function execute_get_settings( $input = array() ): array { $settings_by_group = array(); foreach ( $registered_settings as $option_name => $args ) { - if ( empty( $args['show_in_rest'] ) ) { + if ( wp_is_serving_rest_request() && empty( $args['show_in_rest'] ) ) { continue; } From 0514b24211a9d47c907aab05171973f24a4252d8 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 16:52:32 +0000 Subject: [PATCH 13/16] add proper one off input validation --- .../abilities/class-wp-settings-abilities.php | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 82d30191f88ef..d5625d881fdb2 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -197,24 +197,44 @@ private static function register_get_settings(): void { 'description' => __( 'Returns registered WordPress settings grouped by their registration group. Returns key-value pairs per setting.' ), 'category' => 'site', 'input_schema' => array( - 'type' => 'object', - 'properties' => array( - 'group' => array( - 'type' => 'string', - 'description' => __( 'Filter settings by group name. If omitted, returns all groups. Cannot be used with slugs.' ), - 'enum' => self::$available_groups, + 'default' => (object) array(), + 'oneOf' => array( + // Branch 1: No filter (empty object). + array( + 'type' => 'object', + 'additionalProperties' => false, + 'maxProperties' => 0, + ), + // Branch 2: Filter by group only. + array( + 'type' => 'object', + 'properties' => array( + 'group' => array( + 'type' => 'string', + 'description' => __( 'Filter settings by group name.' ), + 'enum' => self::$available_groups, + ), + ), + 'required' => array( 'group' ), + 'additionalProperties' => false, ), - 'slugs' => array( - 'type' => 'array', - 'description' => __( 'Filter settings by specific setting slugs. If omitted, returns all settings. Cannot be used with group.' ), - 'items' => array( - 'type' => 'string', - 'enum' => self::$available_slugs, + // Branch 3: Filter by slugs only. + array( + 'type' => 'object', + 'properties' => array( + 'slugs' => array( + 'type' => 'array', + 'description' => __( 'Filter settings by specific setting slugs.' ), + 'items' => array( + 'type' => 'string', + 'enum' => self::$available_slugs, + ), + ), ), + 'required' => array( 'slugs' ), + 'additionalProperties' => false, ), ), - 'additionalProperties' => false, - 'default' => array(), ), 'output_schema' => self::$output_schema, 'execute_callback' => array( __CLASS__, 'execute_get_settings' ), From a3da0d907554df7ef49a3361b53a084102d11a47 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 17:00:39 +0000 Subject: [PATCH 14/16] Use 7.0 as the version --- .../abilities/class-wp-settings-abilities.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index d5625d881fdb2..76065dbe21634 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -18,7 +18,7 @@ /** * Registers core settings abilities. * - * @since 6.9.0 + * @since 7.0.0 * @access private */ class WP_Settings_Abilities { @@ -26,7 +26,7 @@ class WP_Settings_Abilities { /** * Available setting groups with show_in_rest enabled. * - * @since 6.9.0 + * @since 7.0.0 * @var array */ private static $available_groups; @@ -34,7 +34,7 @@ class WP_Settings_Abilities { /** * Dynamic output schema built from registered settings. * - * @since 6.9.0 + * @since 7.0.0 * @var array */ private static $output_schema; @@ -50,7 +50,7 @@ class WP_Settings_Abilities { /** * Registers all settings abilities. * - * @since 6.9.0 + * @since 7.0.0 * * @return void */ @@ -62,7 +62,7 @@ public static function register(): void { /** * Initializes shared data for settings abilities. * - * @since 6.9.0 + * @since 7.0.0 * * @return void */ @@ -75,7 +75,7 @@ private static function init(): void { /** * Gets unique setting groups that have show_in_rest enabled. * - * @since 6.9.0 + * @since 7.0.0 * * @return array List of unique group names. */ @@ -128,7 +128,7 @@ private static function get_available_slugs(): array { * with their types, titles, descriptions, defaults, and any additional * schema properties from show_in_rest. * - * @since 6.9.0 + * @since 7.0.0 * * @return array JSON Schema for the output. */ @@ -185,7 +185,7 @@ private static function build_output_schema(): array { /** * Registers the core/get-settings ability. * - * @since 6.9.0 + * @since 7.0.0 * * @return void */ @@ -254,7 +254,7 @@ private static function register_get_settings(): void { /** * Permission callback for settings abilities. * - * @since 6.9.0 + * @since 7.0.0 * * @return bool True if the current user can manage options, false otherwise. */ @@ -268,7 +268,7 @@ public static function check_manage_options(): bool { * Retrieves all registered settings that are exposed to the REST API, * grouped by their registration group. * - * @since 6.9.0 + * @since 7.0.0 * * @param array $input { * Optional. Input parameters. @@ -321,7 +321,7 @@ public static function execute_get_settings( $input = array() ): array { /** * Casts a value to the appropriate type based on the setting's registered type. * - * @since 6.9.0 + * @since 7.0.0 * * @param mixed $value The value to cast. * @param string $type The registered type (string, boolean, integer, number, array, object). From 00a6bbdc9e10b503c6096dc7265ba1cdceb32f10 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 17:37:53 +0000 Subject: [PATCH 15/16] fix i18n issue --- src/wp-includes/abilities/class-wp-settings-abilities.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index 76065dbe21634..de7067817afc6 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -159,11 +159,6 @@ private static function build_output_schema(): array { if ( ! isset( $group_properties[ $group ] ) ) { $group_properties[ $group ] = array( 'type' => 'object', - 'description' => sprintf( - /* translators: %s: Settings group name. */ - __( '%s settings.' ), - ucfirst( $group ) - ), 'properties' => array(), 'additionalProperties' => false, ); From 1e63bc2a869fa4241f4f8d7ee67167a008f670f3 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 21 Jan 2026 18:00:02 +0000 Subject: [PATCH 16/16] older php versions code fix --- src/wp-includes/abilities/class-wp-settings-abilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/abilities/class-wp-settings-abilities.php b/src/wp-includes/abilities/class-wp-settings-abilities.php index de7067817afc6..dfe1cb0e61111 100644 --- a/src/wp-includes/abilities/class-wp-settings-abilities.php +++ b/src/wp-includes/abilities/class-wp-settings-abilities.php @@ -322,7 +322,7 @@ public static function execute_get_settings( $input = array() ): array { * @param string $type The registered type (string, boolean, integer, number, array, object). * @return string|bool|int|float|array The cast value. */ - private static function cast_value( $value, string $type ): string|bool|int|float|array { + private static function cast_value( $value, string $type ) { switch ( $type ) { case 'boolean': return (bool) $value;