diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php index 4247cccffa..6ffda95b4e 100644 --- a/src/wp-includes/class-wp-block-type.php +++ b/src/wp-includes/class-wp-block-type.php @@ -123,7 +123,7 @@ class WP_Block_Type { /** * Validates attributes against the current block schema, populating - * defaulted and missing values, and omitting unknown attributes. + * defaulted and missing values. * * @since 5.0.0 * @@ -131,30 +131,41 @@ class WP_Block_Type { * @return array Prepared block attributes. */ public function prepare_attributes_for_render( $attributes ) { + // If there are no attribute definitions for the block type, skip + // processing and return vebatim. if ( ! isset( $this->attributes ) ) { return $attributes; } - $prepared_attributes = array(); - - foreach ( $this->attributes as $attribute_name => $schema ) { - $value = null; - - if ( isset( $attributes[ $attribute_name ] ) ) { - $is_valid = rest_validate_value_from_schema( $attributes[ $attribute_name ], $schema ); - if ( ! is_wp_error( $is_valid ) ) { - $value = rest_sanitize_value_from_schema( $attributes[ $attribute_name ], $schema ); - } + foreach ( $attributes as $attribute_name => $value ) { + // If the attribute is not defined by the block type, it cannot be + // validated. + if ( ! isset( $this->attributes[ $attribute_name ] ) ) { + continue; } - if ( is_null( $value ) && isset( $schema['default'] ) ) { - $value = $schema['default']; - } + $schema = $this->attributes[ $attribute_name ]; - $prepared_attributes[ $attribute_name ] = $value; + // Validate value by JSON schema. An invalid value should revert to + // its default, if one exists. This occurs by virtue of the missing + // attributes loop immediately following. If there is not a default + // assigned, the attribute value should remain unset. + $is_valid = rest_validate_value_from_schema( $value, $schema ); + if ( is_wp_error( $is_valid ) ) { + unset( $attributes[ $attribute_name ] ); + } } - return $prepared_attributes; + // Populate values of any missing attributes for which the block type + // defines a default. + $missing_schema_attributes = array_diff_key( $this->attributes, $attributes ); + foreach ( $missing_schema_attributes as $attribute_name => $schema ) { + if ( isset( $schema['default'] ) ) { + $attributes[ $attribute_name ] = $schema['default']; + } + } + + return $attributes; } /** diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php index f0e1e7db47..681e519313 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php @@ -61,6 +61,7 @@ class WP_REST_Block_Renderer_Controller extends WP_REST_Controller { 'type' => 'object', 'additionalProperties' => false, 'properties' => $block_type->get_attributes(), + 'default' => array(), ), 'post_id' => array( 'description' => __( 'ID of the post context.' ), diff --git a/tests/phpunit/tests/blocks/block-type.php b/tests/phpunit/tests/blocks/block-type.php index ba80ab69a9..63e0809a13 100644 --- a/tests/phpunit/tests/blocks/block-type.php +++ b/tests/phpunit/tests/blocks/block-type.php @@ -168,7 +168,8 @@ class WP_Test_Block_Type extends WP_UnitTestCase { 'wrongType' => 5, 'wrongTypeDefaulted' => 5, /* missingDefaulted */ - 'undefined' => 'omit', + 'undefined' => 'include', + 'intendedNull' => null, ); $block_type = new WP_Block_Type( @@ -189,6 +190,10 @@ class WP_Test_Block_Type extends WP_UnitTestCase { 'type' => 'string', 'default' => 'define', ), + 'intendedNull' => array( + 'type' => array( 'string', 'null' ), + 'default' => 'wrong', + ), ), ) ); @@ -198,14 +203,29 @@ class WP_Test_Block_Type extends WP_UnitTestCase { $this->assertEquals( array( 'correct' => 'include', - 'wrongType' => null, + /* wrongType */ 'wrongTypeDefaulted' => 'defaulted', 'missingDefaulted' => 'define', + 'undefined' => 'include', + 'intendedNull' => null, ), $prepared_attributes ); } + /** + * @ticket 45145 + */ + function test_prepare_attributes_none_defined() { + $attributes = array( 'exists' => 'keep' ); + + $block_type = new WP_Block_Type( 'core/dummy', array() ); + + $prepared_attributes = $block_type->prepare_attributes_for_render( $attributes ); + + $this->assertEquals( $attributes, $prepared_attributes ); + } + /** * @ticket 45097 */ diff --git a/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php b/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php index 472e448b10..dbc094ce98 100644 --- a/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php +++ b/tests/phpunit/tests/rest-api/rest-block-renderer-controller.php @@ -319,7 +319,9 @@ class REST_Block_Renderer_Controller_Test extends WP_Test_REST_Controller_Testca $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name ); $defaults = array(); foreach ( $block_type->attributes as $key => $attribute ) { - $defaults[ $key ] = isset( $attribute['default'] ) ? $attribute['default'] : null; + if ( isset( $attribute['default'] ) ) { + $defaults[ $key ] = $attribute['default']; + } } $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 87dddb8186..e2c8e8f5c7 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -4317,6 +4317,7 @@ mockedApiResponse.Schema = { }, "attributes": { "required": false, + "default": [], "description": "Attributes for core/block block", "type": "object" }, @@ -4356,6 +4357,7 @@ mockedApiResponse.Schema = { }, "attributes": { "required": false, + "default": [], "description": "Attributes for core/latest-comments block", "type": "object" }, @@ -4395,6 +4397,7 @@ mockedApiResponse.Schema = { }, "attributes": { "required": false, + "default": [], "description": "Attributes for core/archives block", "type": "object" }, @@ -4434,6 +4437,7 @@ mockedApiResponse.Schema = { }, "attributes": { "required": false, + "default": [], "description": "Attributes for core/categories block", "type": "object" }, @@ -4473,6 +4477,7 @@ mockedApiResponse.Schema = { }, "attributes": { "required": false, + "default": [], "description": "Attributes for core/latest-posts block", "type": "object" }, @@ -4512,6 +4517,7 @@ mockedApiResponse.Schema = { }, "attributes": { "required": false, + "default": [], "description": "Attributes for core/shortcode block", "type": "object" },