@@ -188,27 +188,6 @@ public function get_item_permissions_check( $request ) {
188188 return current_user_can ( 'read ' );
189189 }
190190
191- /**
192- * Normalizes schema empty object defaults.
193- *
194- * Converts empty array defaults to objects when the schema type is 'object'
195- * to ensure proper JSON serialization as {} instead of [].
196- *
197- * @since 6.9.0
198- *
199- * @param array<string, mixed> $schema The schema array.
200- * @return array<string, mixed> The normalized schema.
201- */
202- private function normalize_schema_empty_object_defaults ( array $ schema ): array {
203- if ( isset ( $ schema ['type ' ] ) && 'object ' === $ schema ['type ' ] && isset ( $ schema ['default ' ] ) ) {
204- $ default = $ schema ['default ' ];
205- if ( is_array ( $ default ) && empty ( $ default ) ) {
206- $ schema ['default ' ] = (object ) $ default ;
207- }
208- }
209- return $ schema ;
210- }
211-
212191 /**
213192 * WordPress-internal schema keywords to strip from REST responses.
214193 *
@@ -222,19 +201,42 @@ private function normalize_schema_empty_object_defaults( array $schema ): array
222201 );
223202
224203 /**
225- * Recursively removes WordPress-internal keywords from a schema.
204+ * Determines whether the value is an associative array.
205+ *
206+ * @since 7.1.0
207+ *
208+ * @param mixed $value Value.
209+ * @return bool Whether it is associative array.
210+ *
211+ * @phpstan-assert-if-true array<string, mixed> $value
212+ */
213+ private function is_associative_array ( $ value ): bool {
214+ return is_array ( $ value ) && ! wp_is_numeric_array ( $ value );
215+ }
216+
217+ /**
218+ * Transforms an ability schema for REST response output.
226219 *
227220 * Ability schemas may include WordPress-internal properties like
228221 * `sanitize_callback`, `validate_callback`, and `arg_options` that are
229222 * used server-side but are not valid JSON Schema keywords. This method
230223 * removes those specific keys so they are not exposed in REST responses.
224+ * It also converts empty array defaults to objects when the schema type is
225+ * 'object' to ensure proper JSON serialization as {} instead of [].
231226 *
232- * @since 7.0 .0
227+ * @since 7.1 .0
233228 *
234229 * @param array<string, mixed> $schema The schema array.
235- * @return array<string, mixed> The schema without WordPress-internal keywords .
230+ * @return array<string, mixed> The transformed schema .
236231 */
237- private function strip_internal_schema_keywords ( array $ schema ): array {
232+ private function prepare_schema_for_response ( array $ schema ): array {
233+ if ( isset ( $ schema ['type ' ] ) && 'object ' === $ schema ['type ' ] && isset ( $ schema ['default ' ] ) ) {
234+ $ default = $ schema ['default ' ];
235+ if ( is_array ( $ default ) && empty ( $ default ) ) {
236+ $ schema ['default ' ] = (object ) $ default ;
237+ }
238+ }
239+
238240 $ schema = array_diff_key ( $ schema , self ::INTERNAL_SCHEMA_KEYWORDS );
239241
240242 // Sub-schema maps: keys are user-defined, values are sub-schemas.
@@ -243,39 +245,39 @@ private function strip_internal_schema_keywords( array $schema ): array {
243245 foreach ( array ( 'properties ' , 'patternProperties ' , 'definitions ' , 'dependencies ' ) as $ keyword ) {
244246 if ( isset ( $ schema [ $ keyword ] ) && is_array ( $ schema [ $ keyword ] ) ) {
245247 foreach ( $ schema [ $ keyword ] as $ key => $ child_schema ) {
246- if ( is_array ( $ child_schema ) && ! wp_is_numeric_array ( $ child_schema ) ) {
247- $ schema [ $ keyword ][ $ key ] = $ this ->strip_internal_schema_keywords ( $ child_schema );
248+ if ( $ this -> is_associative_array ( $ child_schema ) ) {
249+ $ schema [ $ keyword ][ $ key ] = $ this ->prepare_schema_for_response ( $ child_schema );
248250 }
249251 }
250252 }
251253 }
252254
253255 // Single sub-schema keywords.
254256 foreach ( array ( 'not ' , 'additionalProperties ' , 'additionalItems ' ) as $ keyword ) {
255- if ( isset ( $ schema [ $ keyword ] ) && is_array ( $ schema [ $ keyword ] ) ) {
256- $ schema [ $ keyword ] = $ this ->strip_internal_schema_keywords ( $ schema [ $ keyword ] );
257+ if ( isset ( $ schema [ $ keyword ] ) && $ this -> is_associative_array ( $ schema [ $ keyword ] ) ) {
258+ $ schema [ $ keyword ] = $ this ->prepare_schema_for_response ( $ schema [ $ keyword ] );
257259 }
258260 }
259261
260262 // Items: single schema or tuple array of schemas.
261- if ( isset ( $ schema ['items ' ] ) ) {
262- if ( wp_is_numeric_array ( $ schema ['items ' ] ) ) {
263+ if ( isset ( $ schema ['items ' ] ) && is_array ( $ schema ['items ' ] ) ) {
264+ if ( $ this ->is_associative_array ( $ schema ['items ' ] ) ) {
265+ $ schema ['items ' ] = $ this ->prepare_schema_for_response ( $ schema ['items ' ] );
266+ } else {
263267 foreach ( $ schema ['items ' ] as $ index => $ item_schema ) {
264- if ( is_array ( $ item_schema ) ) {
265- $ schema ['items ' ][ $ index ] = $ this ->strip_internal_schema_keywords ( $ item_schema );
268+ if ( $ this -> is_associative_array ( $ item_schema ) ) {
269+ $ schema ['items ' ][ $ index ] = $ this ->prepare_schema_for_response ( $ item_schema );
266270 }
267271 }
268- } elseif ( is_array ( $ schema ['items ' ] ) ) {
269- $ schema ['items ' ] = $ this ->strip_internal_schema_keywords ( $ schema ['items ' ] );
270272 }
271273 }
272274
273275 // Array-of-schemas keywords.
274276 foreach ( array ( 'anyOf ' , 'oneOf ' , 'allOf ' ) as $ keyword ) {
275277 if ( isset ( $ schema [ $ keyword ] ) && is_array ( $ schema [ $ keyword ] ) ) {
276278 foreach ( $ schema [ $ keyword ] as $ index => $ sub_schema ) {
277- if ( is_array ( $ sub_schema ) ) {
278- $ schema [ $ keyword ][ $ index ] = $ this ->strip_internal_schema_keywords ( $ sub_schema );
279+ if ( $ this -> is_associative_array ( $ sub_schema ) ) {
280+ $ schema [ $ keyword ][ $ index ] = $ this ->prepare_schema_for_response ( $ sub_schema );
279281 }
280282 }
281283 }
@@ -299,12 +301,8 @@ public function prepare_item_for_response( $ability, $request ) {
299301 'label ' => $ ability ->get_label (),
300302 'description ' => $ ability ->get_description (),
301303 'category ' => $ ability ->get_category (),
302- 'input_schema ' => $ this ->strip_internal_schema_keywords (
303- $ this ->normalize_schema_empty_object_defaults ( $ ability ->get_input_schema () )
304- ),
305- 'output_schema ' => $ this ->strip_internal_schema_keywords (
306- $ this ->normalize_schema_empty_object_defaults ( $ ability ->get_output_schema () )
307- ),
304+ 'input_schema ' => $ this ->prepare_schema_for_response ( $ ability ->get_input_schema () ),
305+ 'output_schema ' => $ this ->prepare_schema_for_response ( $ ability ->get_output_schema () ),
308306 'meta ' => $ ability ->get_meta (),
309307 );
310308
0 commit comments