@@ -34,6 +34,17 @@ class WP_Settings_Abilities {
3434 */
3535 const CATEGORY = 'site ' ;
3636
37+ /**
38+ * Settings exposed through the Abilities API, computed once at registration.
39+ *
40+ * Cached so the input/output schema and the executed result derive from the exact same
41+ * structure, and {@see get_registered_settings()} is only walked once per request.
42+ *
43+ * @since 7.1.0
44+ * @var array<string, array{option: string, group: string, default: mixed, schema: array<string, mixed>}>|null
45+ */
46+ private static $ exposed_settings = null ;
47+
3748 /**
3849 * Registers all settings abilities.
3950 *
@@ -58,21 +69,28 @@ public static function register(): void {
5869 * @since 7.1.0
5970 */
6071 public static function register_get_settings (): void {
61- $ settings = self ::get_exposed_settings ();
62- $ groups = array_values ( array_unique ( array_filter ( wp_list_pluck ( $ settings , 'group ' ) ) ) );
63- $ setting_names = array_keys ( $ settings );
64- $ properties = array ();
72+ // Compute once; execute_get_settings() reuses this exact structure.
73+ self ::$ exposed_settings = self ::get_exposed_settings ();
74+
75+ $ settings = self ::$ exposed_settings ;
76+ $ field_names = array_keys ( $ settings );
77+ $ groups = array ();
78+ $ properties = array ();
6579 foreach ( $ settings as $ exposed_name => $ setting ) {
6680 $ properties [ $ exposed_name ] = $ setting ['schema ' ];
81+ if ( '' === $ setting ['group ' ] || in_array ( $ setting ['group ' ], $ groups , true ) ) {
82+ continue ;
83+ }
84+ $ groups [] = $ setting ['group ' ];
6785 }
6886
6987 wp_register_ability (
7088 'core/settings ' ,
7189 array (
7290 'label ' => __ ( 'Get Settings ' ),
73- 'description ' => __ ( 'Returns WordPress settings as a flat map of setting name to value. By default returns all settings exposed to abilities, or optionally a subset filtered by settings group or by setting name. ' ),
91+ 'description ' => __ ( 'Returns WordPress settings as a flat map of setting name to value. By default returns all settings exposed to abilities, or optionally a subset filtered by settings group, by setting name, or both . ' ),
7492 'category ' => self ::CATEGORY ,
75- 'input_schema ' => self ::get_settings_input_schema ( $ groups , $ setting_names ),
93+ 'input_schema ' => self ::get_settings_input_schema ( $ groups , $ field_names ),
7694 'output_schema ' => array (
7795 'type ' => 'object ' ,
7896 'description ' => __ ( 'A map of setting name to its current value. ' ),
@@ -104,20 +122,20 @@ public static function register_get_settings(): void {
104122 public static function execute_get_settings ( $ input = array () ): array {
105123 $ input = is_array ( $ input ) ? $ input : array ();
106124
107- $ settings = self ::get_exposed_settings ();
108- $ group = isset ( $ input ['group ' ] ) ? ( string ) $ input ['group ' ] : '' ;
109- $ names = isset ( $ input ['settings ' ] ) && is_array ( $ input ['settings ' ] ) ? $ input ['settings ' ] : array ();
125+ $ settings = self ::$ exposed_settings ?? self :: get_exposed_settings ();
126+ $ group = isset ( $ input ['group ' ] ) && is_string ( $ input [ ' group ' ] ) ? $ input ['group ' ] : '' ;
127+ $ fields = isset ( $ input ['fields ' ] ) && is_array ( $ input ['fields ' ] ) ? $ input ['fields ' ] : array ();
110128
111129 $ result = array ();
112130 foreach ( $ settings as $ exposed_name => $ setting ) {
113131 if ( '' !== $ group && $ setting ['group ' ] !== $ group ) {
114132 continue ;
115133 }
116- if ( ! empty ( $ names ) && ! in_array ( $ exposed_name , $ names , true ) ) {
134+ if ( ! empty ( $ fields ) && ! in_array ( $ exposed_name , $ fields , true ) ) {
117135 continue ;
118136 }
119137
120- $ type = isset ( $ setting ['schema ' ]['type ' ] ) ? ( string ) $ setting ['schema ' ]['type ' ] : 'string ' ;
138+ $ type = isset ( $ setting ['schema ' ]['type ' ] ) && is_string ( $ setting [ ' schema ' ][ ' type ' ] ) ? $ setting ['schema ' ]['type ' ] : 'string ' ;
121139 $ value = get_option ( $ setting ['option ' ], $ setting ['default ' ] );
122140
123141 $ result [ $ exposed_name ] = self ::cast_value ( $ value , $ type );
@@ -138,55 +156,38 @@ public static function has_permission(): bool {
138156 }
139157
140158 /**
141- * Builds the input schema for the get ability: filter by group XOR by name.
159+ * Builds the input schema for the get ability: optional filters by group and/or name.
160+ *
161+ * Both `group` and `fields` are optional; supplying both narrows the response to their
162+ * intersection, and supplying neither returns every exposed setting.
142163 *
143164 * @since 7.1.0
144165 *
145- * @param string[] $groups Available settings groups.
146- * @param string[] $setting_names Available exposed setting names.
166+ * @param string[] $groups Available settings groups.
167+ * @param string[] $field_names Available exposed setting names.
147168 * @return array<string, mixed> The input JSON Schema.
148169 */
149- protected static function get_settings_input_schema ( array $ groups , array $ setting_names ): array {
170+ protected static function get_settings_input_schema ( array $ groups , array $ field_names ): array {
150171 return array (
151- 'type ' => 'object ' ,
152- 'default ' => array (),
153- // Filter by group OR by name, but not both at once.
154- 'oneOf ' => array (
155- array (
156- 'title ' => __ ( 'All settings ' ),
157- 'type ' => 'object ' ,
158- 'additionalProperties ' => false ,
159- ),
160- array (
161- 'title ' => __ ( 'Filter by group ' ),
162- 'type ' => 'object ' ,
163- 'required ' => array ( 'group ' ),
164- 'properties ' => array (
165- 'group ' => array (
166- 'type ' => 'string ' ,
167- 'enum ' => $ groups ,
168- 'description ' => __ ( 'Return only settings that belong to this settings group. ' ),
169- ),
170- ),
171- 'additionalProperties ' => false ,
172+ 'type ' => 'object ' ,
173+ // Object (not array()) so the serialized schema default is {}, consistent with type:object.
174+ 'default ' => (object ) array (),
175+ 'properties ' => array (
176+ 'group ' => array (
177+ 'type ' => 'string ' ,
178+ 'enum ' => $ groups ,
179+ 'description ' => __ ( 'Return only settings that belong to this settings group. ' ),
172180 ),
173- array (
174- 'title ' => __ ( 'Filter by name ' ),
175- 'type ' => 'object ' ,
176- 'required ' => array ( 'settings ' ),
177- 'properties ' => array (
178- 'settings ' => array (
179- 'type ' => 'array ' ,
180- 'items ' => array (
181- 'type ' => 'string ' ,
182- 'enum ' => $ setting_names ,
183- ),
184- 'description ' => __ ( 'Return only the settings with these names. ' ),
185- ),
181+ 'fields ' => array (
182+ 'type ' => 'array ' ,
183+ 'items ' => array (
184+ 'type ' => 'string ' ,
185+ 'enum ' => $ field_names ,
186186 ),
187- 'additionalProperties ' => false ,
187+ 'description ' => __ ( ' Return only the settings with these names. ' ) ,
188188 ),
189189 ),
190+ 'additionalProperties ' => false ,
190191 );
191192 }
192193
@@ -212,11 +213,11 @@ protected static function get_exposed_settings(): array {
212213 }
213214
214215 $ option_name = (string ) $ option_name ;
215- $ exposed_name = is_array ( $ show ) && ! empty ( $ show ['name ' ] ) ? ( string ) $ show ['name ' ] : $ option_name ;
216+ $ exposed_name = is_array ( $ show ) && isset ( $ show ['name ' ] ) && is_string ( $ show [ ' name ' ] ) && '' !== $ show [ ' name ' ] ? $ show ['name ' ] : $ option_name ;
216217
217218 $ settings [ $ exposed_name ] = array (
218219 'option ' => $ option_name ,
219- 'group ' => isset ( $ args ['group ' ] ) ? ( string ) $ args ['group ' ] : '' ,
220+ 'group ' => isset ( $ args ['group ' ] ) && is_string ( $ args [ ' group ' ] ) ? $ args ['group ' ] : '' ,
220221 'default ' => array_key_exists ( 'default ' , $ args ) ? $ args ['default ' ] : false ,
221222 'schema ' => self ::value_schema ( $ args , $ show ),
222223 );
@@ -236,7 +237,7 @@ protected static function get_exposed_settings(): array {
236237 */
237238 protected static function value_schema ( array $ args , $ show ): array {
238239 $ schema = array (
239- 'type ' => isset ( $ args ['type ' ] ) ? ( string ) $ args ['type ' ] : 'string ' ,
240+ 'type ' => isset ( $ args ['type ' ] ) && is_string ( $ args [ ' type ' ] ) ? $ args ['type ' ] : 'string ' ,
240241 );
241242 if ( ! empty ( $ args ['label ' ] ) ) {
242243 $ schema ['title ' ] = $ args ['label ' ];
@@ -245,7 +246,9 @@ protected static function value_schema( array $args, $show ): array {
245246 $ schema ['description ' ] = $ args ['description ' ];
246247 }
247248 if ( is_array ( $ show ) && isset ( $ show ['schema ' ] ) && is_array ( $ show ['schema ' ] ) ) {
248- $ schema = array_merge ( $ schema , $ show ['schema ' ] );
249+ /** @var array<string, mixed> $show_schema */
250+ $ show_schema = $ show ['schema ' ];
251+ $ schema = array_merge ( $ schema , $ show_schema );
249252 }
250253
251254 return $ schema ;
@@ -265,12 +268,15 @@ protected static function cast_value( $value, string $type ) {
265268 case 'boolean ' :
266269 return (bool ) $ value ;
267270 case 'integer ' :
268- return ( int ) $ value ;
271+ return is_scalar ( $ value ) ? ( int ) $ value : 0 ;
269272 case 'number ' :
270- return ( float ) $ value ;
273+ return is_scalar ( $ value ) ? ( float ) $ value : 0.0 ;
271274 case 'array ' :
272- case 'object ' :
273275 return is_array ( $ value ) ? $ value : array ();
276+ case 'object ' :
277+ // Cast to object so an empty/non-array value serializes as {} (not []) and
278+ // satisfies the `object` output schema validated by execute().
279+ return (object ) ( is_array ( $ value ) ? $ value : array () );
274280 default :
275281 return is_scalar ( $ value ) ? (string ) $ value : $ value ;
276282 }
0 commit comments