Skip to content

Commit c93e5de

Browse files
REST API: Serve response data that is not an array as returned.
`rest_filter_response_fields()` leaves the response unchanged when its data is not an array, and keeps list items that are not arrays. `WP_REST_Server::response_to_data()` returns scalar and object data as it is instead of adding links to it. Routes that return such data no longer need the `response_processing` option to avoid type errors. See #66267.
1 parent 5b2ee49 commit c93e5de

3 files changed

Lines changed: 53 additions & 10 deletions

File tree

‎src/wp-includes/rest-api.php‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,8 @@ function _rest_array_intersect_key_recursive( $array1, $array2 ) {
950950
* Filters the REST API response to include only an allow-listed set of response object fields.
951951
*
952952
* @since 4.8.0
953-
* @since 7.2.0 The response is left unchanged when the route turns off the `fields` response processing.
953+
* @since 7.2.0 The response is left unchanged when the route turns off the `fields` response
954+
* processing or when the response data is not an array.
954955
*
955956
* @param WP_REST_Response $response Current response being served.
956957
* @param WP_REST_Server $server ResponseHandler instance (usually WP_REST_Server).
@@ -972,6 +973,11 @@ function rest_filter_response_fields( $response, $server, $request ) {
972973

973974
$data = $response->get_data();
974975

976+
// Only arrays have fields to filter.
977+
if ( ! is_array( $data ) ) {
978+
return $response;
979+
}
980+
975981
$fields = wp_parse_list( $request['_fields'] );
976982

977983
if ( 0 === count( $fields ) ) {
@@ -1002,7 +1008,7 @@ function rest_filter_response_fields( $response, $server, $request ) {
10021008
if ( wp_is_numeric_array( $data ) ) {
10031009
$new_data = array();
10041010
foreach ( $data as $item ) {
1005-
$new_data[] = _rest_array_intersect_key_recursive( $item, $fields_as_keyed );
1011+
$new_data[] = is_array( $item ) ? _rest_array_intersect_key_recursive( $item, $fields_as_keyed ) : $item;
10061012
}
10071013
} else {
10081014
$new_data = _rest_array_intersect_key_recursive( $data, $fields_as_keyed );

‎src/wp-includes/rest-api/class-wp-rest-server.php‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -624,22 +624,25 @@ public function serve_request( $path = null ) {
624624
* @since 4.4.0
625625
* @since 5.4.0 The `$embed` parameter can now contain a list of link relations to include.
626626
* @since 7.2.0 Added the `$request` parameter. The links are not added or embedded when the
627-
* route turns off the `links` or `embed` response processing.
627+
* route turns off the `links` or `embed` response processing, and data that is
628+
* not an array is returned as it is.
628629
*
629630
* @param WP_REST_Response $response Response object.
630631
* @param bool|string[] $embed Whether to embed all links, a filtered list of link relations, or no links.
631632
* @param WP_REST_Request|null $request Optional. The request that generated the response. It resolves the
632633
* response processing settings when the response has no matched handler.
633634
* Default null.
634-
* @return array {
635-
* Data with sub-requests embedded.
636-
*
637-
* @type array $_links Links.
638-
* @type array $_embedded Embedded objects.
639-
* }
635+
* @return mixed Data with sub-requests embedded: an array with the links under `_links` and the embedded
636+
* resources under `_embedded`, or data that is not an array as it is.
640637
*/
641638
public function response_to_data( $response, $embed, $request = null ) {
642-
$data = $response->get_data();
639+
$data = $response->get_data();
640+
641+
// Scalar and object data has no place for links or embedded resources.
642+
if ( ! is_array( $data ) && null !== $data ) {
643+
return $data;
644+
}
645+
643646
$processing = $this->get_response_processing( $request, $response );
644647
$links = $processing['links'] ? self::get_compact_response_links( $response ) : array();
645648

‎tests/phpunit/tests/rest-api/rest-server.php‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3475,4 +3475,38 @@ public function test_response_processing_is_listed_in_the_index(): void {
34753475
);
34763476
$this->assertArrayNotHasKey( 'response_processing', $data['endpoints'][1], 'An endpoint with every feature on should not list the settings.' );
34773477
}
3478+
3479+
/**
3480+
* Data provider for test_non_array_data_is_served_as_returned_by_default().
3481+
*
3482+
* @return array<string, array{0: mixed, 1: bool, 2: string}>
3483+
*/
3484+
public static function data_non_array_data_is_served_as_returned_by_default(): array {
3485+
return array(
3486+
'string' => array( 'value', true, '"value"' ),
3487+
'integer' => array( 5, true, '5' ),
3488+
'object' => array( (object) array( 'b' => 2 ), true, '{"b":2}' ),
3489+
'empty object' => array( new stdClass(), true, '{}' ),
3490+
'list of scalars' => array( array( 'a', 'b' ), false, '["a","b"]' ),
3491+
'mixed list' => array( array( array( 'b' => 2 ), 'c' ), false, '[[],"c"]' ),
3492+
);
3493+
}
3494+
3495+
/**
3496+
* @ticket 66267
3497+
* @dataProvider data_non_array_data_is_served_as_returned_by_default
3498+
*
3499+
* @param mixed $data The response data.
3500+
* @param bool $with_link Whether the response has an embeddable link.
3501+
* @param string $expected The expected response body.
3502+
*/
3503+
public function test_non_array_data_is_served_as_returned_by_default( $data, bool $with_link, string $expected ): void {
3504+
$this->register_response_processing_route( $data, $with_link );
3505+
3506+
$_GET['_fields'] = 'a';
3507+
$_GET['_embed'] = '1';
3508+
rest_get_server()->serve_request( '/test-ns/response-processing' );
3509+
3510+
$this->assertSame( $expected, rest_get_server()->sent_body, 'Data without fields, or items without fields, should be served as returned.' );
3511+
}
34783512
}

0 commit comments

Comments
 (0)