函数文档

rest_filter_response_by_context()

💡 云策文档标注

概述

rest_filter_response_by_context() 是 WordPress REST API 中的一个核心函数,用于根据给定的上下文过滤响应数据,移除不在该上下文中可用的字段。它递归处理数组或对象数据,依据端点模式(schema)中的上下文定义进行筛选。

关键要点

  • 函数作用:过滤响应数据,移除不在指定上下文中可用的字段,确保 API 响应符合模式定义。
  • 参数说明:接受三个参数:$response_data(要修改的响应数据,数组或对象)、$schema(端点的模式数组)、$context(请求的上下文字符串)。
  • 返回值:返回过滤后的响应数据,类型与输入一致(数组或对象)。
  • 递归处理:函数递归调用自身以处理嵌套的数组或对象数据,确保深层字段也根据上下文过滤。
  • 模式支持:支持模式中的 "anyOf"、"oneOf"、"patternProperties" 等关键字,用于复杂数据结构的匹配和过滤。
  • 类型处理:自动判断响应数据类型(数组或对象),并根据模式中的类型定义进行相应处理,包括回退兼容性。
  • 上下文检查:检查每个字段的模式中是否定义了 "context" 数组,如果当前上下文不在其中,则移除该字段。

代码示例

function rest_filter_response_by_context( $response_data, $schema, $context ) {
    if ( isset( $schema['anyOf'] ) ) {
        $matching_schema = rest_find_any_matching_schema( $response_data, $schema, '' );
        if ( ! is_wp_error( $matching_schema ) ) {
            if ( ! isset( $schema['type'] ) ) {
                $schema['type'] = $matching_schema['type'];
            }

            $response_data = rest_filter_response_by_context( $response_data, $matching_schema, $context );
        }
    }

    if ( isset( $schema['oneOf'] ) ) {
        $matching_schema = rest_find_one_matching_schema( $response_data, $schema, '', true );
        if ( ! is_wp_error( $matching_schema ) ) {
            if ( ! isset( $schema['type'] ) ) {
                $schema['type'] = $matching_schema['type'];
            }

            $response_data = rest_filter_response_by_context( $response_data, $matching_schema, $context );
        }
    }

    if ( ! is_array( $response_data ) && ! is_object( $response_data ) ) {
        return $response_data;
    }

    if ( isset( $schema['type'] ) ) {
        $type = $schema['type'];
    } elseif ( isset( $schema['properties'] ) ) {
        $type = 'object'; // Back compat if a developer accidentally omitted the type.
    } else {
        return $response_data;
    }

    $is_array_type  = 'array' === $type || ( is_array( $type ) && in_array( 'array', $type, true ) );
    $is_object_type = 'object' === $type || ( is_array( $type ) && in_array( 'object', $type, true ) );

    if ( $is_array_type && $is_object_type ) {
        if ( rest_is_array( $response_data ) ) {
            $is_object_type = false;
        } else {
            $is_array_type = false;
        }
    }

    $has_additional_properties = $is_object_type && isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] );

    foreach ( $response_data as $key => $value ) {
        $check = array();

        if ( $is_array_type ) {
            $check = isset( $schema['items'] ) ? $schema['items'] : array();
        } elseif ( $is_object_type ) {
            if ( isset( $schema['properties'][ $key ] ) ) {
                $check = $schema['properties'][ $key ];
            } else {
                $pattern_property_schema = rest_find_matching_pattern_property_schema( $key, $schema );
                if ( null !== $pattern_property_schema ) {
                    $check = $pattern_property_schema;
                } elseif ( $has_additional_properties ) {
                    $check = $schema['additionalProperties'];
                }
            }
        }

        if ( ! isset( $check['context'] ) ) {
            continue;
        }

        if ( ! in_array( $context, $check['context'], true ) ) {
            if ( $is_array_type ) {
                // All array items share schema, so there's no need to check each one.
                $response_data = array();
                break;
            }

            if ( is_object( $response_data ) ) {
                unset( $response_data->$key );
            } else {
                unset( $response_data[ $key ] );
            }
        } elseif ( is_array( $value ) || is_object( $value ) ) {
            $new_value = rest_filter_response_by_context( $value, $check, $context );

            if ( is_object( $response_data ) ) {
                $response_data->$key = $new_value;
            } else {
                $response_data[ $key ] = $new_value;
            }
        }
    }

    return $response_data;
}

注意事项

  • 该函数是递归的,可能对性能有影响,特别是在处理大型或深层嵌套的响应数据时,开发者需注意优化。
  • 模式中的 "context" 数组必须正确定义,否则字段可能被错误地保留或移除,影响 API 响应的准确性。
  • 从 WordPress 5.6.0 开始,支持 "patternProperties"、"anyOf" 和 "oneOf" 关键字,增强了模式匹配的灵活性。
  • 函数内部使用 rest_is_array() 等辅助函数,确保与 WordPress REST API 的其他组件兼容。

📄 原文内容

Filters the response to remove any fields not available in the given context.

Parameters

$response_dataarray|objectrequired
The response data to modify.
$schemaarrayrequired
The schema for the endpoint used to filter the response.
$contextstringrequired
The requested context.

Return

array|object The filtered response data.

Source

function rest_filter_response_by_context( $response_data, $schema, $context ) {
	if ( isset( $schema['anyOf'] ) ) {
		$matching_schema = rest_find_any_matching_schema( $response_data, $schema, '' );
		if ( ! is_wp_error( $matching_schema ) ) {
			if ( ! isset( $schema['type'] ) ) {
				$schema['type'] = $matching_schema['type'];
			}

			$response_data = rest_filter_response_by_context( $response_data, $matching_schema, $context );
		}
	}

	if ( isset( $schema['oneOf'] ) ) {
		$matching_schema = rest_find_one_matching_schema( $response_data, $schema, '', true );
		if ( ! is_wp_error( $matching_schema ) ) {
			if ( ! isset( $schema['type'] ) ) {
				$schema['type'] = $matching_schema['type'];
			}

			$response_data = rest_filter_response_by_context( $response_data, $matching_schema, $context );
		}
	}

	if ( ! is_array( $response_data ) && ! is_object( $response_data ) ) {
		return $response_data;
	}

	if ( isset( $schema['type'] ) ) {
		$type = $schema['type'];
	} elseif ( isset( $schema['properties'] ) ) {
		$type = 'object'; // Back compat if a developer accidentally omitted the type.
	} else {
		return $response_data;
	}

	$is_array_type  = 'array' === $type || ( is_array( $type ) && in_array( 'array', $type, true ) );
	$is_object_type = 'object' === $type || ( is_array( $type ) && in_array( 'object', $type, true ) );

	if ( $is_array_type && $is_object_type ) {
		if ( rest_is_array( $response_data ) ) {
			$is_object_type = false;
		} else {
			$is_array_type = false;
		}
	}

	$has_additional_properties = $is_object_type && isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] );

	foreach ( $response_data as $key => $value ) {
		$check = array();

		if ( $is_array_type ) {
			$check = isset( $schema['items'] ) ? $schema['items'] : array();
		} elseif ( $is_object_type ) {
			if ( isset( $schema['properties'][ $key ] ) ) {
				$check = $schema['properties'][ $key ];
			} else {
				$pattern_property_schema = rest_find_matching_pattern_property_schema( $key, $schema );
				if ( null !== $pattern_property_schema ) {
					$check = $pattern_property_schema;
				} elseif ( $has_additional_properties ) {
					$check = $schema['additionalProperties'];
				}
			}
		}

		if ( ! isset( $check['context'] ) ) {
			continue;
		}

		if ( ! in_array( $context, $check['context'], true ) ) {
			if ( $is_array_type ) {
				// All array items share schema, so there's no need to check each one.
				$response_data = array();
				break;
			}

			if ( is_object( $response_data ) ) {
				unset( $response_data->$key );
			} else {
				unset( $response_data[ $key ] );
			}
		} elseif ( is_array( $value ) || is_object( $value ) ) {
			$new_value = rest_filter_response_by_context( $value, $check, $context );

			if ( is_object( $response_data ) ) {
				$response_data->$key = $new_value;
			} else {
				$response_data[ $key ] = $new_value;
			}
		}
	}

	return $response_data;
}

Changelog

Version Description
5.6.0 Support the “patternProperties” keyword for objects.
Support the “anyOf” and “oneOf” keywords.
5.5.0 Introduced.