From 1400cee4e87de834b25d799d40d19920360b8f13 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 May 2025 10:29:04 +0200 Subject: [PATCH 1/5] Serialize: Respect attribute type for empty arrays --- src/wp-includes/blocks.php | 22 ++++++++++++-- tests/phpunit/tests/blocks/serialize.php | 37 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 838cd84a19921..c8a17598dee29 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1596,10 +1596,26 @@ function make_after_block_visitor( $hooked_blocks, $context, $callback = 'insert * * @since 5.3.1 * - * @param array $block_attributes Attributes object. + * @param array $block_attributes Attributes object. + * @param string|null $block_name Block name. Null if the block name is unknown. Optional. * @return string Serialized attributes. */ -function serialize_block_attributes( $block_attributes ) { +function serialize_block_attributes( $block_attributes, $block_name = null ) { + foreach ( $block_attributes as $attribute => $value ) { + if ( is_array( $value ) && empty( $value ) ) { + // An empty `array()` is encoded as `[]` in JSON. However, it's possible + // that the attribute type is really an object (associative array in PHP), + // so we need to check for that. + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name ); + if ( $block_type && isset( $block_type->attributes[ $attribute ] ) ) { + $attribute_type = $block_type->attributes[ $attribute ]['type']; + if ( 'object' === $attribute_type ) { + $block_attributes[ $attribute ] = new stdClass(); + } + } + } + } + $encoded_attributes = wp_json_encode( $block_attributes, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes ); $encoded_attributes = preg_replace( '/', $serialized_block_name, $serialized_attributes ); diff --git a/tests/phpunit/tests/blocks/serialize.php b/tests/phpunit/tests/blocks/serialize.php index b4221ef3c5a15..5ab09628d3211 100644 --- a/tests/phpunit/tests/blocks/serialize.php +++ b/tests/phpunit/tests/blocks/serialize.php @@ -10,6 +10,37 @@ * @group blocks */ class Tests_Blocks_Serialize extends WP_UnitTestCase { + /** + * Set up. + * + * @ticket 63325. + */ + public static function wpSetUpBeforeClass() { + register_block_type( + 'core/attributes', + array( + 'attributes' => array( + 'array' => array( + 'type' => 'array', + ), + 'object' => array( + 'type' => 'object', + ), + ), + ) + ); + } + + /** + * Tear down. + * + * @ticket 63325. + */ + public static function wpTearDownAfterClass() { + $registry = WP_Block_Type_Registry::get_instance(); + + $registry->unregister( 'core/attributes' ); + } /** * @dataProvider data_serialize_identity_from_parsed @@ -46,6 +77,12 @@ public function data_serialize_identity_from_parsed() { // Block with attribute values that should not be escaped. array( '' ), + + // Block with attribute values that have array keys. + array( '' ), + + // Block with attribute values that have empty keys. + array( '' ), ); } From 7e5792c48fb4872bfe1e611d10396a9b4d7614d1 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 May 2025 10:40:34 +0200 Subject: [PATCH 2/5] Tweak comments --- tests/phpunit/tests/blocks/serialize.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/blocks/serialize.php b/tests/phpunit/tests/blocks/serialize.php index 5ab09628d3211..5a529e60a04e1 100644 --- a/tests/phpunit/tests/blocks/serialize.php +++ b/tests/phpunit/tests/blocks/serialize.php @@ -78,10 +78,10 @@ public function data_serialize_identity_from_parsed() { // Block with attribute values that should not be escaped. array( '' ), - // Block with attribute values that have array keys. + // Block with empty array attribute value. array( '' ), - // Block with attribute values that have empty keys. + // Block with empty object attribute value. array( '' ), ); } From 81515324bc5a53a25bb4adba4d6df8eb5f1376ee Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 May 2025 10:51:37 +0200 Subject: [PATCH 3/5] Cache attribute definitions --- src/wp-includes/blocks.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index c8a17598dee29..b50d9c6e3f474 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1601,14 +1601,22 @@ function make_after_block_visitor( $hooked_blocks, $context, $callback = 'insert * @return string Serialized attributes. */ function serialize_block_attributes( $block_attributes, $block_name = null ) { + $attribute_definitions = null; foreach ( $block_attributes as $attribute => $value ) { if ( is_array( $value ) && empty( $value ) ) { // An empty `array()` is encoded as `[]` in JSON. However, it's possible // that the attribute type is really an object (associative array in PHP), // so we need to check for that. - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name ); - if ( $block_type && isset( $block_type->attributes[ $attribute ] ) ) { - $attribute_type = $block_type->attributes[ $attribute ]['type']; + if ( null === $attribute_definitions ) { + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name ); + if ( $block_type && isset( $block_type->attributes ) ) { + $attribute_definitions = $block_type->attributes; + } else { + $attribute_definitions = array(); + } + } + if ( ! empty( $attribute_definitions ) ) { + $attribute_type = $attribute_definitions[ $attribute ]['type']; if ( 'object' === $attribute_type ) { $block_attributes[ $attribute ] = new stdClass(); } From 27512a1145dce46e25e3575ebab4f9ccf1b52fb1 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 May 2025 10:53:34 +0200 Subject: [PATCH 4/5] Guard against 'type' not being set in attr definition --- src/wp-includes/blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index b50d9c6e3f474..05dc94c870c73 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1615,7 +1615,7 @@ function serialize_block_attributes( $block_attributes, $block_name = null ) { $attribute_definitions = array(); } } - if ( ! empty( $attribute_definitions ) ) { + if ( ! empty( $attribute_definitions ) && isset( $attribute_definitions[ $attribute ]['type'] ) ) { $attribute_type = $attribute_definitions[ $attribute ]['type']; if ( 'object' === $attribute_type ) { $block_attributes[ $attribute ] = new stdClass(); From 0570a87f17f620292758c03fe0580370dca5a0dd Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 May 2025 10:54:25 +0200 Subject: [PATCH 5/5] Add since PHPDoc --- src/wp-includes/blocks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 05dc94c870c73..64309565ec84c 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1595,6 +1595,7 @@ function make_after_block_visitor( $hooked_blocks, $context, $callback = 'insert * to ensure consistent operation between PHP and JavaScript. * * @since 5.3.1 + * @since 6.8.2 Add $block_name parameter. * * @param array $block_attributes Attributes object. * @param string|null $block_name Block name. Null if the block name is unknown. Optional.