Make WordPress Core


Ignore:
Timestamp:
09/19/2019 02:04:51 PM (4 years ago)
Author:
kadamwhite
Message:

REST API: Support dot.nested hierarchical properties in _fields query parameter.

Enable clients to opt-in to receipt of one or more specific sub-properties within a response, and not other sub-properties.
Skip potentially expensive filtering and processing for post resources which were explicitly not requested.

Props kadamwhite, TimothyBlynJacobs, dlh.
Fixes #42094.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api.php

    r46101 r46184  
    698698
    699699/**
     700 * Recursively computes the intersection of arrays using keys for comparison.
     701 *
     702 * @param  array $array1 The array with master keys to check.
     703 * @param  array $array2 An array to compare keys against.
     704 *
     705 * @return array An associative array containing all the entries of array1 which have keys that are present in all arguments.
     706 */
     707function _rest_array_intersect_key_recursive( $array1, $array2 ) {
     708    $array1 = array_intersect_key( $array1, $array2 );
     709    foreach ( $array1 as $key => $value ) {
     710        if ( is_array( $value ) && is_array( $array2[ $key ] ) ) {
     711            $array1[ $key ] = _rest_array_intersect_key_recursive( $value, $array2[ $key ] );
     712        }
     713    }
     714    return $array1;
     715}
     716
     717/**
    700718 * Filter the API response to include only a white-listed set of response object fields.
    701719 *
     
    724742    $fields = array_map( 'trim', $fields );
    725743
    726     $fields_as_keyed = array_combine( $fields, array_fill( 0, count( $fields ), true ) );
     744    // Create nested array of accepted field hierarchy.
     745    $fields_as_keyed = array();
     746    foreach ( $fields as $field ) {
     747        $parts = explode( '.', $field );
     748        $ref   = &$fields_as_keyed;
     749        while ( count( $parts ) > 1 ) {
     750            $next         = array_shift( $parts );
     751            $ref[ $next ] = array();
     752            $ref          = &$ref[ $next ];
     753        }
     754        $last         = array_shift( $parts );
     755        $ref[ $last ] = true;
     756    }
    727757
    728758    if ( wp_is_numeric_array( $data ) ) {
    729759        $new_data = array();
    730760        foreach ( $data as $item ) {
    731             $new_data[] = array_intersect_key( $item, $fields_as_keyed );
     761            $new_data[] = _rest_array_intersect_key_recursive( $item, $fields_as_keyed );
    732762        }
    733763    } else {
    734         $new_data = array_intersect_key( $data, $fields_as_keyed );
     764        $new_data = _rest_array_intersect_key_recursive( $data, $fields_as_keyed );
    735765    }
    736766
     
    738768
    739769    return $response;
     770}
     771
     772/**
     773 * Given an array of fields to include in a response, some of which may be
     774 * `nested.fields`, determine whether the provided field should be included
     775 * in the response body.
     776 *
     777 * If a parent field is passed in, the presence of any nested field within
     778 * that parent will cause the method to return `true`. For example "title"
     779 * will return true if any of `title`, `title.raw` or `title.rendered` is
     780 * provided.
     781 *
     782 * @since 5.3.0
     783 *
     784 * @param string $field  A field to test for inclusion in the response body.
     785 * @param array  $fields An array of string fields supported by the endpoint.
     786 * @return bool Whether to include the field or not.
     787 */
     788function rest_is_field_included( $field, $fields ) {
     789    if ( in_array( $field, $fields, true ) ) {
     790        return true;
     791    }
     792    foreach ( $fields as $accepted_field ) {
     793        // Check to see if $field is the parent of any item in $fields.
     794        // A field "parent" should be accepted if "parent.child" is accepted.
     795        if ( strpos( $accepted_field, "$field." ) === 0 ) {
     796            return true;
     797        }
     798        // Conversely, if "parent" is accepted, all "parent.child" fields should
     799        // also be accepted.
     800        if ( strpos( $field, "$accepted_field." ) === 0 ) {
     801            return true;
     802        }
     803    }
     804    return false;
    740805}
    741806
Note: See TracChangeset for help on using the changeset viewer.