From 69b9b23c49b5b05e4926645dedcc541b72fbded3 Mon Sep 17 00:00:00 2001 From: TimothyBlynJacobs Date: Thu, 1 Oct 2020 02:49:12 +0000 Subject: [PATCH] REST API: Support the patternProperties JSON Schema keyword. Props yakimun. Fixes #51024. Built from https://develop.svn.wordpress.org/trunk@49082 git-svn-id: http://core.svn.wordpress.org/trunk@48844 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/rest-api.php | 89 +++++++++++++++++++++++++++++++++++----- wp-includes/version.php | 2 +- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/wp-includes/rest-api.php b/wp-includes/rest-api.php index 99cdd3e749..6bfba39ca8 100644 --- a/wp-includes/rest-api.php +++ b/wp-includes/rest-api.php @@ -1539,6 +1539,42 @@ function rest_stabilize_value( $value ) { return $value; } +/** + * Validates if the JSON Schema pattern matches a value. + * + * @since 5.6.0 + * + * @param string $pattern The pattern to match against. + * @param string $value The value to check. + * @return bool True if the pattern matches the given value, false otherwise. + */ +function rest_validate_json_schema_pattern( $pattern, $value ) { + $escaped_pattern = str_replace( '#', '\\#', $pattern ); + + return 1 === preg_match( '#' . $escaped_pattern . '#u', $value ); +} + +/** + * Finds the schema for a property using the patternProperties keyword. + * + * @since 5.6.0 + * + * @param string $property The property name to check. + * @param array $args The schema array to use. + * @return array|null The schema of matching pattern property, or null if no patterns match. + */ +function rest_find_matching_pattern_property_schema( $property, $args ) { + if ( isset( $args['patternProperties'] ) ) { + foreach ( $args['patternProperties'] as $pattern => $child_schema ) { + if ( rest_validate_json_schema_pattern( $pattern, $property ) ) { + return $child_schema; + } + } + } + + return null; +} + /** * Validate a value based on a schema. * @@ -1553,6 +1589,7 @@ function rest_stabilize_value( $value ) { * Validate required properties. * @since 5.6.0 Support the "minProperties" and "maxProperties" keywords for objects. * Support the "multipleOf" keyword for numbers and integers. + * Support the "patternProperties" keyword for objects. * * @param mixed $value The value to validate. * @param array $args Schema array to use for validation. @@ -1650,7 +1687,19 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) { if ( is_wp_error( $is_valid ) ) { return $is_valid; } - } elseif ( isset( $args['additionalProperties'] ) ) { + continue; + } + + $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args ); + if ( null !== $pattern_property_schema ) { + $is_valid = rest_validate_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' ); + if ( is_wp_error( $is_valid ) ) { + return $is_valid; + } + continue; + } + + if ( isset( $args['additionalProperties'] ) ) { if ( false === $args['additionalProperties'] ) { /* translators: %s: Property of an object. */ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) ); @@ -1744,12 +1793,9 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) { ); } - if ( isset( $args['pattern'] ) ) { - $pattern = str_replace( '#', '\\#', $args['pattern'] ); - if ( ! preg_match( '#' . $pattern . '#u', $value ) ) { - /* translators: 1: Parameter, 2: Pattern. */ - return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) ); - } + if ( isset( $args['pattern'] ) && ! rest_validate_json_schema_pattern( $args['pattern'], $value ) ) { + /* translators: 1: Parameter, 2: Pattern. */ + return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) ); } } @@ -1897,7 +1943,16 @@ function rest_sanitize_value_from_schema( $value, $args, $param = '' ) { foreach ( $value as $property => $v ) { if ( isset( $args['properties'][ $property ] ) ) { $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); - } elseif ( isset( $args['additionalProperties'] ) ) { + continue; + } + + $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args ); + if ( null !== $pattern_property_schema ) { + $value[ $property ] = rest_sanitize_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' ); + continue; + } + + if ( isset( $args['additionalProperties'] ) ) { if ( false === $args['additionalProperties'] ) { unset( $value[ $property ] ); } elseif ( is_array( $args['additionalProperties'] ) ) { @@ -2053,6 +2108,7 @@ function rest_parse_embed_param( $embed ) { * Filters the response to remove any fields not available in the given context. * * @since 5.5.0 + * @since 5.6.0 Support the "patternProperties" keyword for objects. * * @param array|object $data The response data to modify. * @param array $schema The schema for the endpoint used to filter the response. @@ -2093,8 +2149,13 @@ function rest_filter_response_by_context( $data, $schema, $context ) { } elseif ( $is_object_type ) { if ( isset( $schema['properties'][ $key ] ) ) { $check = $schema['properties'][ $key ]; - } elseif ( $has_additional_properties ) { - $check = $schema['additionalProperties']; + } 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']; + } } } @@ -2132,6 +2193,7 @@ function rest_filter_response_by_context( $data, $schema, $context ) { * Sets the "additionalProperties" to false by default for all object definitions in the schema. * * @since 5.5.0 + * @since 5.6.0 Support the "patternProperties" keyword. * * @param array $schema The schema to modify. * @return array The modified schema. @@ -2146,6 +2208,12 @@ function rest_default_additional_properties_to_false( $schema ) { } } + if ( isset( $schema['patternProperties'] ) ) { + foreach ( $schema['patternProperties'] as $key => $child_schema ) { + $schema['patternProperties'][ $key ] = rest_default_additional_properties_to_false( $child_schema ); + } + } + if ( ! isset( $schema['additionalProperties'] ) ) { $schema['additionalProperties'] = false; } @@ -2300,6 +2368,7 @@ function rest_get_endpoint_args_for_schema( $schema, $method = WP_REST_Server::C 'items', 'properties', 'additionalProperties', + 'patternProperties', 'minProperties', 'maxProperties', 'minimum', diff --git a/wp-includes/version.php b/wp-includes/version.php index a8c5ac9ae9..26d3e23880 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.6-alpha-49081'; +$wp_version = '5.6-alpha-49082'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.