Skip to content

Commit 121f3e7

Browse files
committed
REST API: Ensure empty meta is returned as an object in view context
1 parent 4d3b0b9 commit 121f3e7

3 files changed

Lines changed: 107 additions & 3 deletions

File tree

src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,15 @@ public function filter_response_by_context( $data, $context ) {
5858
unset( $data['content']['rendered'] );
5959

6060
// Add the core wp_pattern_sync_status meta as top level property to the response.
61-
$data['wp_pattern_sync_status'] = $data['meta']['wp_pattern_sync_status'] ?? '';
62-
unset( $data['meta']['wp_pattern_sync_status'] );
61+
$meta = (array) $data['meta'];
62+
$data['wp_pattern_sync_status'] = $meta['wp_pattern_sync_status'] ?? '';
63+
64+
if ( is_object( $data['meta'] ) ) {
65+
unset( $data['meta']->wp_pattern_sync_status );
66+
} else {
67+
unset( $data['meta']['wp_pattern_sync_status'] );
68+
}
69+
6370
return $data;
6471
}
6572

src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ public function register_field() {
7474
*
7575
* @param int $object_id Object ID to fetch meta for.
7676
* @param WP_REST_Request $request Full details about the request.
77-
* @return array Array containing the meta values keyed by name.
77+
* @return array|object Array containing the meta values keyed by name,
78+
* or an empty object if to ensure JSON object encoding.
7879
*/
7980
public function get_value( $object_id, $request ) {
8081
$fields = $this->get_registered_fields();
@@ -105,6 +106,11 @@ public function get_value( $object_id, $request ) {
105106
$response[ $name ] = $value;
106107
}
107108

109+
// Use stdClass so that JSON result is {} and not [].
110+
if ( empty( $response ) ) {
111+
return (object) array();
112+
}
113+
108114
return $response;
109115
}
110116

@@ -582,6 +588,10 @@ public static function prepare_value( $value, $request, $args ) {
582588
* @return array|false The meta array, if valid, false otherwise.
583589
*/
584590
public function check_meta_is_array( $value, $request, $param ) {
591+
if ( is_object( $value ) ) {
592+
$value = (array) $value;
593+
}
594+
585595
if ( ! is_array( $value ) ) {
586596
return false;
587597
}

tests/phpunit/tests/rest-api/rest-blocks-controller.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,91 @@ public function test_wp_patterns_sync_status_post_meta() {
255255
$this->assertArrayHasKey( 'wp_pattern_sync_status', $data );
256256
$this->assertArrayNotHasKey( 'wp_pattern_sync_status', $data['meta'] );
257257
}
258+
259+
/**
260+
* Tests that the Blocks Controller handles an empty object (stdClass) for meta correctly.
261+
*
262+
* This verifies the compatibility of filter_response_by_context with object types
263+
* introduced in the patch.
264+
*
265+
* @ticket 54484
266+
*/
267+
public function test_filter_response_by_context_with_empty_object_meta() {
268+
wp_set_current_user( self::$user_ids['editor'] );
269+
270+
// Register meta for testing.
271+
register_post_meta(
272+
'wp_block',
273+
'wp_pattern_sync_status',
274+
array(
275+
'single' => true,
276+
'type' => 'string',
277+
'show_in_rest' => true,
278+
)
279+
);
280+
281+
// Force simulate meta being converted to an object (simulating Patch B's get_value behavior).
282+
add_filter(
283+
'rest_prepare_wp_block',
284+
function ( $response ) {
285+
$data = $response->get_data();
286+
// Simulate when meta is empty or cast to an object.
287+
if ( empty( $data['meta'] ) ) {
288+
$data['meta'] = new stdClass();
289+
}
290+
$response->set_data( $data );
291+
return $response;
292+
},
293+
10
294+
);
295+
296+
$request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );
297+
$response = rest_get_server()->dispatch( $request );
298+
$data = $response->get_data();
299+
300+
// Verify: Top-level property should exist even if meta is an object.
301+
$this->assertArrayHasKey( 'wp_pattern_sync_status', $data );
302+
303+
// Verify: Internal meta property should be correctly unset (regardless of being an object or array).
304+
if ( is_object( $data['meta'] ) ) {
305+
$this->assertObjectNotHasAttribute( 'wp_pattern_sync_status', $data['meta'] );
306+
} else {
307+
$this->assertArrayNotHasKey( 'wp_pattern_sync_status', $data['meta'] );
308+
}
309+
}
310+
311+
/**
312+
* Tests that check_meta_is_array correctly handles object types.
313+
*
314+
* @ticket 54484
315+
*/
316+
public function test_check_meta_is_array_accepts_object() {
317+
wp_set_current_user( self::$user_ids['editor'] );
318+
319+
// Implement all abstract methods required by WP_REST_Meta_Fields.
320+
$meta_fields = new class( 'post' ) extends WP_REST_Meta_Fields {
321+
protected function get_meta_type() {
322+
return 'post';
323+
}
324+
325+
protected function get_rest_field_type() {
326+
return 'post';
327+
}
328+
};
329+
330+
// Simulate an object parsed from JSON (stdClass).
331+
$object_value = new stdClass();
332+
$object_value->test_key = 'test_value';
333+
334+
// Execute the method from the patch.
335+
$result = $meta_fields->check_meta_is_array( $object_value, new WP_REST_Request(), 'meta' );
336+
337+
// Verify: The object should be converted to an array to pass validation.
338+
$this->assertIsArray( $result, 'The object should be converted to an array to pass validation.' );
339+
$this->assertArrayHasKey( 'test_key', $result );
340+
$this->assertSame( 'test_value', $result['test_key'] );
341+
342+
// Verify: Values that are neither object nor array (e.g., string) should still return false.
343+
$this->assertFalse( $meta_fields->check_meta_is_array( 'not-an-array', new WP_REST_Request(), 'meta' ) );
344+
}
258345
}

0 commit comments

Comments
 (0)