WordPress.org

Make WordPress Core

Changeset 49082


Ignore:
Timestamp:
10/01/2020 02:47:08 AM (10 months ago)
Author:
TimothyBlynJacobs
Message:

REST API: Support the patternProperties JSON Schema keyword.

Props yakimun.
Fixes #51024.

Location:
trunk
Files:
6 edited

Legend:

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

    r49063 r49082  
    15411541
    15421542/**
     1543 * Validates if the JSON Schema pattern matches a value.
     1544 *
     1545 * @since 5.6.0
     1546 *
     1547 * @param string $pattern The pattern to match against.
     1548 * @param string $value   The value to check.
     1549 * @return bool           True if the pattern matches the given value, false otherwise.
     1550 */
     1551function rest_validate_json_schema_pattern( $pattern, $value ) {
     1552    $escaped_pattern = str_replace( '#', '\\#', $pattern );
     1553
     1554    return 1 === preg_match( '#' . $escaped_pattern . '#u', $value );
     1555}
     1556
     1557/**
     1558 * Finds the schema for a property using the patternProperties keyword.
     1559 *
     1560 * @since 5.6.0
     1561 *
     1562 * @param string $property The property name to check.
     1563 * @param array  $args     The schema array to use.
     1564 * @return array|null      The schema of matching pattern property, or null if no patterns match.
     1565 */
     1566function rest_find_matching_pattern_property_schema( $property, $args ) {
     1567    if ( isset( $args['patternProperties'] ) ) {
     1568        foreach ( $args['patternProperties'] as $pattern => $child_schema ) {
     1569            if ( rest_validate_json_schema_pattern( $pattern, $property ) ) {
     1570                return $child_schema;
     1571            }
     1572        }
     1573    }
     1574
     1575    return null;
     1576}
     1577
     1578/**
    15431579 * Validate a value based on a schema.
    15441580 *
     
    15541590 * @since 5.6.0 Support the "minProperties" and "maxProperties" keywords for objects.
    15551591 *              Support the "multipleOf" keyword for numbers and integers.
     1592 *              Support the "patternProperties" keyword for objects.
    15561593 *
    15571594 * @param mixed  $value The value to validate.
     
    16511688                    return $is_valid;
    16521689                }
    1653             } elseif ( isset( $args['additionalProperties'] ) ) {
     1690                continue;
     1691            }
     1692
     1693            $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args );
     1694            if ( null !== $pattern_property_schema ) {
     1695                $is_valid = rest_validate_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' );
     1696                if ( is_wp_error( $is_valid ) ) {
     1697                    return $is_valid;
     1698                }
     1699                continue;
     1700            }
     1701
     1702            if ( isset( $args['additionalProperties'] ) ) {
    16541703                if ( false === $args['additionalProperties'] ) {
    16551704                    /* translators: %s: Property of an object. */
     
    17451794        }
    17461795
    1747         if ( isset( $args['pattern'] ) ) {
    1748             $pattern = str_replace( '#', '\\#', $args['pattern'] );
    1749             if ( ! preg_match( '#' . $pattern . '#u', $value ) ) {
    1750                 /* translators: 1: Parameter, 2: Pattern. */
    1751                 return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) );
    1752             }
     1796        if ( isset( $args['pattern'] ) && ! rest_validate_json_schema_pattern( $args['pattern'], $value ) ) {
     1797            /* translators: 1: Parameter, 2: Pattern. */
     1798            return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) );
    17531799        }
    17541800    }
     
    18981944            if ( isset( $args['properties'][ $property ] ) ) {
    18991945                $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' );
    1900             } elseif ( isset( $args['additionalProperties'] ) ) {
     1946                continue;
     1947            }
     1948
     1949            $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args );
     1950            if ( null !== $pattern_property_schema ) {
     1951                $value[ $property ] = rest_sanitize_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' );
     1952                continue;
     1953            }
     1954
     1955            if ( isset( $args['additionalProperties'] ) ) {
    19011956                if ( false === $args['additionalProperties'] ) {
    19021957                    unset( $value[ $property ] );
     
    20542109 *
    20552110 * @since 5.5.0
     2111 * @since 5.6.0 Support the "patternProperties" keyword for objects.
    20562112 *
    20572113 * @param array|object $data    The response data to modify.
     
    20942150            if ( isset( $schema['properties'][ $key ] ) ) {
    20952151                $check = $schema['properties'][ $key ];
    2096             } elseif ( $has_additional_properties ) {
    2097                 $check = $schema['additionalProperties'];
     2152            } else {
     2153                $pattern_property_schema = rest_find_matching_pattern_property_schema( $key, $schema );
     2154                if ( null !== $pattern_property_schema ) {
     2155                    $check = $pattern_property_schema;
     2156                } elseif ( $has_additional_properties ) {
     2157                    $check = $schema['additionalProperties'];
     2158                }
    20982159            }
    20992160        }
     
    21332194 *
    21342195 * @since 5.5.0
     2196 * @since 5.6.0 Support the "patternProperties" keyword.
    21352197 *
    21362198 * @param array $schema The schema to modify.
     
    21442206            foreach ( $schema['properties'] as $key => $child_schema ) {
    21452207                $schema['properties'][ $key ] = rest_default_additional_properties_to_false( $child_schema );
     2208            }
     2209        }
     2210
     2211        if ( isset( $schema['patternProperties'] ) ) {
     2212            foreach ( $schema['patternProperties'] as $key => $child_schema ) {
     2213                $schema['patternProperties'][ $key ] = rest_default_additional_properties_to_false( $child_schema );
    21462214            }
    21472215        }
     
    23012369        'properties',
    23022370        'additionalProperties',
     2371        'patternProperties',
    23032372        'minProperties',
    23042373        'maxProperties',
  • trunk/tests/phpunit/tests/rest-api.php

    r48937 r49082  
    12851285                array( 'additional' => array( 'a' => '1' ) ),
    12861286            ),
     1287            'pattern properties'                  => array(
     1288                array(
     1289                    '$schema'              => 'http://json-schema.org/draft-04/schema#',
     1290                    'type'                 => 'object',
     1291                    'properties'           => array(
     1292                        'a' => array(
     1293                            'type'    => 'string',
     1294                            'context' => array( 'view', 'edit' ),
     1295                        ),
     1296                    ),
     1297                    'patternProperties'    => array(
     1298                        '[0-9]' => array(
     1299                            'type'    => 'string',
     1300                            'context' => array( 'view', 'edit' ),
     1301                        ),
     1302                        'c.*'   => array(
     1303                            'type'    => 'string',
     1304                            'context' => array( 'edit' ),
     1305                        ),
     1306                    ),
     1307                    'additionalProperties' => array(
     1308                        'type'    => 'string',
     1309                        'context' => array( 'edit' ),
     1310                    ),
     1311                ),
     1312                array(
     1313                    'a'  => '1',
     1314                    'b'  => '2',
     1315                    '0'  => '3',
     1316                    'ca' => '4',
     1317                ),
     1318                array(
     1319                    'a' => '1',
     1320                    '0' => '3',
     1321                ),
     1322            ),
    12871323            'multiple types object'               => array(
    12881324                array(
  • trunk/tests/phpunit/tests/rest-api/rest-controller.php

    r49063 r49082  
    292292        }
    293293
    294         foreach ( array( 'properties', 'additionalProperties', 'minProperties', 'maxProperties' ) as $property ) {
     294        $object_properties = array(
     295            'properties',
     296            'patternProperties',
     297            'additionalProperties',
     298            'minProperties',
     299            'maxProperties',
     300        );
     301        foreach ( $object_properties as $property ) {
    295302            $this->assertArrayHasKey( $property, $args['someobject'] );
    296303        }
  • trunk/tests/phpunit/tests/rest-api/rest-schema-sanitization.php

    r49008 r49082  
    235235                $schema
    236236            )
     237        );
     238    }
     239
     240    /**
     241     * @ticket 51024
     242     *
     243     * @dataProvider data_type_object_pattern_properties
     244     *
     245     * @param array $pattern_properties
     246     * @param array $value
     247     * @param array $expected
     248     */
     249    public function test_type_object_pattern_properties( $pattern_properties, $value, $expected ) {
     250        $schema = array(
     251            'type'                 => 'object',
     252            'properties'           => array(
     253                'propA' => array( 'type' => 'string' ),
     254            ),
     255            'patternProperties'    => $pattern_properties,
     256            'additionalProperties' => false,
     257        );
     258
     259        $this->assertSame( $expected, rest_sanitize_value_from_schema( $value, $schema ) );
     260    }
     261
     262    /**
     263     * @return array
     264     */
     265    public function data_type_object_pattern_properties() {
     266        return array(
     267            array( array(), array(), array() ),
     268            array( array(), array( 'propA' => 'a' ), array( 'propA' => 'a' ) ),
     269            array(
     270                array(),
     271                array(
     272                    'propA' => 'a',
     273                    'propB' => 'b',
     274                ),
     275                array( 'propA' => 'a' ),
     276            ),
     277            array(
     278                array(
     279                    'propB' => array( 'type' => 'string' ),
     280                ),
     281                array( 'propA' => 'a' ),
     282                array( 'propA' => 'a' ),
     283            ),
     284            array(
     285                array(
     286                    'propB' => array( 'type' => 'string' ),
     287                ),
     288                array(
     289                    'propA' => 'a',
     290                    'propB' => 'b',
     291                ),
     292                array(
     293                    'propA' => 'a',
     294                    'propB' => 'b',
     295                ),
     296            ),
     297            array(
     298                array(
     299                    '.*C' => array( 'type' => 'string' ),
     300                ),
     301                array(
     302                    'propA' => 'a',
     303                    'propC' => 'c',
     304                ),
     305                array(
     306                    'propA' => 'a',
     307                    'propC' => 'c',
     308                ),
     309            ),
     310            array(
     311                array(
     312                    '[0-9]' => array( 'type' => 'integer' ),
     313                ),
     314                array(
     315                    'propA' => 'a',
     316                    'prop0' => '0',
     317                ),
     318                array(
     319                    'propA' => 'a',
     320                    'prop0' => 0,
     321                ),
     322            ),
     323            array(
     324                array(
     325                    '.+' => array( 'type' => 'string' ),
     326                ),
     327                array(
     328                    ''      => '',
     329                    'propA' => 'a',
     330                ),
     331                array( 'propA' => 'a' ),
     332            ),
    237333        );
    238334    }
  • trunk/tests/phpunit/tests/rest-api/rest-schema-validation.php

    r49063 r49082  
    287287        );
    288288        $this->assertWPError( rest_validate_value_from_schema( array( 'a' => 'invalid' ), $schema ) );
     289    }
     290
     291    /**
     292     * @ticket 51024
     293     *
     294     * @dataProvider data_type_object_pattern_properties
     295     *
     296     * @param array $pattern_properties
     297     * @param array $value
     298     * @param bool $expected
     299     */
     300    public function test_type_object_pattern_properties( $pattern_properties, $value, $expected ) {
     301        $schema = array(
     302            'type'                 => 'object',
     303            'properties'           => array(
     304                'propA' => array( 'type' => 'string' ),
     305            ),
     306            'patternProperties'    => $pattern_properties,
     307            'additionalProperties' => false,
     308        );
     309
     310        if ( $expected ) {
     311            $this->assertTrue( rest_validate_value_from_schema( $value, $schema ) );
     312        } else {
     313            $this->assertWPError( rest_validate_value_from_schema( $value, $schema ) );
     314        }
     315    }
     316
     317    /**
     318     * @return array
     319     */
     320    public function data_type_object_pattern_properties() {
     321        return array(
     322            array( array(), array(), true ),
     323            array( array(), array( 'propA' => 'a' ), true ),
     324            array(
     325                array(),
     326                array(
     327                    'propA' => 'a',
     328                    'propB' => 'b',
     329                ),
     330                false,
     331            ),
     332            array(
     333                array(
     334                    'propB' => array( 'type' => 'string' ),
     335                ),
     336                array( 'propA' => 'a' ),
     337                true,
     338            ),
     339            array(
     340                array(
     341                    'propB' => array( 'type' => 'string' ),
     342                ),
     343                array(
     344                    'propA' => 'a',
     345                    'propB' => 'b',
     346                ),
     347                true,
     348            ),
     349            array(
     350                array(
     351                    '.*C' => array( 'type' => 'string' ),
     352                ),
     353                array(
     354                    'propA' => 'a',
     355                    'propC' => 'c',
     356                ),
     357                true,
     358            ),
     359            array(
     360                array(
     361                    '[0-9]' => array( 'type' => 'integer' ),
     362                ),
     363                array(
     364                    'propA' => 'a',
     365                    'prop0' => 0,
     366                ),
     367                true,
     368            ),
     369            array(
     370                array(
     371                    '[0-9]' => array( 'type' => 'integer' ),
     372                ),
     373                array(
     374                    'propA' => 'a',
     375                    'prop0' => 'notAnInteger',
     376                ),
     377                false,
     378            ),
     379            array(
     380                array(
     381                    '.+' => array( 'type' => 'string' ),
     382                ),
     383                array(
     384                    ''      => '',
     385                    'propA' => 'a',
     386                ),
     387                false,
     388            ),
     389        );
    289390    }
    290391
  • trunk/tests/phpunit/tests/rest-api/rest-test-controller.php

    r49063 r49082  
    122122                        ),
    123123                    ),
     124                    'patternProperties'    => array(
     125                        '[0-9]' => array(
     126                            'type' => 'string',
     127                        ),
     128                    ),
    124129                    'minProperties'        => 1,
    125130                    'maxProperties'        => 10,
Note: See TracChangeset for help on using the changeset viewer.