diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 06bb9f9f80..34e1c591f6 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -1051,6 +1051,28 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) { } } } + + if ( 'object' === $args['type'] ) { + if ( $value instanceof stdClass ) { + $value = (array) $value; + } + if ( ! is_array( $value ) ) { + /* translators: 1: parameter, 2: type name */ + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'object' ) ); + } + + foreach ( $value as $property => $v ) { + if ( ! isset( $args['properties'][ $property ] ) ) { + continue; + } + $is_valid = rest_validate_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); + + if ( is_wp_error( $is_valid ) ) { + return $is_valid; + } + } + } + if ( ! empty( $args['enum'] ) ) { if ( ! in_array( $value, $args['enum'], true ) ) { /* translators: 1: parameter, 2: list of valid values */ @@ -1170,6 +1192,26 @@ function rest_sanitize_value_from_schema( $value, $args ) { $value = array_values( $value ); return $value; } + + if ( 'object' === $args['type'] ) { + if ( $value instanceof stdClass ) { + $value = (array) $value; + } + if ( ! is_array( $value ) ) { + return array(); + } + + foreach ( $value as $property => $v ) { + if ( ! isset( $args['properties'][ $property ] ) ) { + unset( $value[ $property ] ); + continue; + } + $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ] ); + } + + return $value; + } + if ( 'integer' === $args['type'] ) { return (int) $value; } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 76802fea76..ed101815a1 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -391,6 +391,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() + 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database() ), 'properties' => array( 'raw' => array( @@ -413,6 +414,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() + 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database() ), 'properties' => array( 'raw' => array( diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php index 810a3fb867..415ba8e9bc 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php @@ -1200,6 +1200,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() + 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database() ), 'properties' => array( 'raw' => array( diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index 018b66ec2d..80027cfea8 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -1846,6 +1846,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() + 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database() ), 'properties' => array( 'raw' => array( @@ -1870,6 +1871,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() + 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database() ), 'properties' => array( 'raw' => array( @@ -1908,6 +1910,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database() + 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database() ), 'properties' => array( 'raw' => array( diff --git a/tests/phpunit/tests/rest-api/rest-schema-sanitization.php b/tests/phpunit/tests/rest-api/rest-schema-sanitization.php index e1ae29d3fa..cb224b349b 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-sanitization.php +++ b/tests/phpunit/tests/rest-api/rest-schema-sanitization.php @@ -146,6 +146,84 @@ class WP_Test_REST_Schema_Sanitization extends WP_UnitTestCase { $this->assertEquals( array( '1', '2' ), rest_sanitize_value_from_schema( array( 'first' => '1', 'second' => '2' ), $schema ) ); } + public function test_type_object() { + $schema = array( + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'number' + ), + ), + ); + $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => 1 ), $schema ) ); + $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => '1' ), $schema ) ); + } + + public function test_type_object_nested() { + $schema = array( + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'object', + 'properties' => array( + 'b' => array( 'type' => 'number' ), + 'c' => array( 'type' => 'number' ), + ) + ) + ), + ); + + $this->assertEquals( + array( + 'a' => array( + 'b' => 1, + 'c' => 3, + ), + ), + rest_sanitize_value_from_schema( + array( + 'a' => array( + 'b' => '1', + 'c' => '3', + ), + ), + $schema + ) + ); + $this->assertEquals( + array( + 'a' => array( + 'b' => 1, + 'c' => 3, + ), + ), + rest_sanitize_value_from_schema( + array( + 'a' => array( + 'b' => '1', + 'c' => '3', + 'd' => '1', + ), + 'b' => 1, + ), + $schema + ) + ); + $this->assertEquals( array( 'a' => array() ), rest_sanitize_value_from_schema( array( 'a' => null ), $schema ) ); + } + + public function test_type_object_stdclass() { + $schema = array( + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'number' + ), + ), + ); + $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( (object) array( 'a' => '1' ), $schema ) ); + } + public function test_type_unknown() { $schema = array( 'type' => 'lalala', diff --git a/tests/phpunit/tests/rest-api/rest-schema-validation.php b/tests/phpunit/tests/rest-api/rest-schema-validation.php index 82f58e58d8..9932538420 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-validation.php +++ b/tests/phpunit/tests/rest-api/rest-schema-validation.php @@ -181,6 +181,59 @@ class WP_Test_REST_Schema_Validation extends WP_UnitTestCase { $this->assertWPError( rest_validate_value_from_schema( array( 'first' => '1', 'second' => '2' ), $schema ) ); } + public function test_type_object() { + $schema = array( + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'number' + ), + ), + ); + $this->assertTrue( rest_validate_value_from_schema( array( 'a' => 1 ), $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( array( 'a' => 'invalid' ), $schema ) ); + } + + public function test_type_object_nested() { + $schema = array( + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'object', + 'properties' => array( + 'b' => array( 'type' => 'number' ), + 'c' => array( 'type' => 'number' ), + ) + ) + ), + ); + $this->assertTrue( + rest_validate_value_from_schema( + array( + 'a' => array( + 'b' => '1', + 'c' => 3, + ), + ), + $schema + ) + ); + $this->assertWPError( rest_validate_value_from_schema( array( 'a' => array( 'b' => 1, 'c' => 'invalid' ) ), $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( array( 'a' => 1 ), $schema ) ); + } + + public function test_type_object_stdclass() { + $schema = array( + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'number' + ), + ), + ); + $this->assertTrue( rest_validate_value_from_schema( (object) array( 'a' => 1 ), $schema ) ); + } + public function test_type_unknown() { $schema = array( 'type' => 'lalala',