diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index e408522ab6..dc55af08f3 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -1250,6 +1250,7 @@ function rest_get_avatar_sizes() { * @since 5.5.0 Add the "uuid" and "hex-color" formats. * Support the "minLength", "maxLength" and "pattern" keywords for strings. * Validate required properties. + * Support the "minItems" and "maxItems" keywords for arrays. * * @param mixed $value The value to validate. * @param array $args Schema array to use for validation. @@ -1287,6 +1288,16 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) { return $is_valid; } } + + if ( isset( $args['minItems'] ) && count( $value ) < $args['minItems'] ) { + /* translators: 1: Parameter, 2: number. */ + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at least %2$s items.' ), $param, number_format_i18n( $args['minItems'] ) ) ); + } + + if ( isset( $args['maxItems'] ) && count( $value ) > $args['maxItems'] ) { + /* translators: 1: Parameter, 2: number. */ + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at most %2$s items.' ), $param, number_format_i18n( $args['maxItems'] ) ) ); + } } if ( 'object' === $args['type'] ) { diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php index a2d1134f11..0d1e85cbf1 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php @@ -643,6 +643,8 @@ abstract class WP_REST_Controller { 'minLength', 'maxLength', 'pattern', + 'minItems', + 'maxItems', ); foreach ( $schema_properties as $field_id => $params ) { diff --git a/tests/phpunit/tests/rest-api/rest-controller.php b/tests/phpunit/tests/rest-api/rest-controller.php index 887cb8f76e..b04158b037 100644 --- a/tests/phpunit/tests/rest-api/rest-controller.php +++ b/tests/phpunit/tests/rest-api/rest-controller.php @@ -265,6 +265,10 @@ class WP_Test_REST_Controller extends WP_Test_REST_TestCase { $this->assertArrayHasKey( 'items', $args['somearray'] ); + foreach ( array( 'minItems', 'maxItems' ) as $property ) { + $this->assertArrayHasKey( $property, $args['somearray'] ); + } + foreach ( array( 'properties', 'additionalProperties' ) as $property ) { $this->assertArrayHasKey( $property, $args['someobject'] ); } diff --git a/tests/phpunit/tests/rest-api/rest-schema-validation.php b/tests/phpunit/tests/rest-api/rest-schema-validation.php index 09dd271a23..44a627e715 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-validation.php +++ b/tests/phpunit/tests/rest-api/rest-schema-validation.php @@ -766,4 +766,40 @@ class WP_Test_REST_Schema_Validation extends WP_UnitTestCase { $this->assertTrue( rest_validate_value_from_schema( 'â', $schema ) ); $this->assertWPError( rest_validate_value_from_schema( 'ââ', $schema ) ); } + + /** + * @ticket 48821 + */ + public function test_array_min_items() { + $schema = array( + 'type' => 'array', + 'minItems' => 1, + 'items' => array( + 'type' => 'number', + ), + ); + + $this->assertTrue( rest_validate_value_from_schema( array( 1, 2 ), $schema ) ); + $this->assertTrue( rest_validate_value_from_schema( array( 1 ), $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( array(), $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( '', $schema ) ); + } + + /** + * @ticket 48821 + */ + public function test_array_max_items() { + $schema = array( + 'type' => 'array', + 'maxItems' => 2, + 'items' => array( + 'type' => 'number', + ), + ); + + $this->assertTrue( rest_validate_value_from_schema( array( 1 ), $schema ) ); + $this->assertTrue( rest_validate_value_from_schema( array( 1, 2 ), $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( array( 1, 2, 3 ), $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( 'foobar', $schema ) ); + } } diff --git a/tests/phpunit/tests/rest-api/rest-test-controller.php b/tests/phpunit/tests/rest-api/rest-test-controller.php index a201ec1c80..e3a82586bc 100644 --- a/tests/phpunit/tests/rest-api/rest-test-controller.php +++ b/tests/phpunit/tests/rest-api/rest-test-controller.php @@ -101,11 +101,13 @@ class WP_REST_Test_Controller extends WP_REST_Controller { 'default' => 'a', ), 'somearray' => array( - 'type' => 'array', - 'items' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string', ), - 'context' => array( 'view' ), + 'minItems' => 1, + 'maxItems' => 10, + 'context' => array( 'view' ), ), 'someobject' => array( 'type' => 'object',