From af0bdc5da81ee90ca0433d658006ab5e89f2a228 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Thu, 25 Dec 2025 19:56:55 +0900 Subject: [PATCH 1/5] get_block_wrapper_attributes(): Preserve zero value --- src/wp-includes/blocks.php | 62 +++++++++ src/wp-includes/class-wp-block-supports.php | 58 +-------- .../blocks/getBlockWrapperAttributes.php | 121 ++++++++++++++++++ 3 files changed, 184 insertions(+), 57 deletions(-) create mode 100644 tests/phpunit/tests/blocks/getBlockWrapperAttributes.php diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index a8ed76d3bd71f..b5ae40373af8f 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -127,6 +127,68 @@ function get_block_asset_url( $path ) { return plugins_url( basename( $path ), $path ); } +/** + * Generates a string of attributes by applying to the current block being + * rendered all of the features that the block supports. + * + * @since 5.6.0 + * + * @param string[] $extra_attributes Optional. Array of extra attributes to render on the block wrapper. + * @return string String of HTML attributes. + */ +function get_block_wrapper_attributes( $extra_attributes = array() ) { + $new_attributes = WP_Block_Supports::get_instance()->apply_block_supports(); + + if ( empty( $new_attributes ) && empty( $extra_attributes ) ) { + return ''; + } + + // This is hardcoded on purpose. + // We only support a fixed list of attributes. + $attributes_to_merge = array( 'style', 'class', 'id', 'aria-label' ); + $attributes = array(); + foreach ( $attributes_to_merge as $attribute_name ) { + $new_value = array_key_exists( $attribute_name, $new_attributes ) ? (string) $new_attributes[ $attribute_name ] : ''; + $extra_value = array_key_exists( $attribute_name, $extra_attributes ) ? (string) $extra_attributes[ $attribute_name ] : ''; + + if ( '' === $new_value && '' === $extra_value ) { + continue; + } + + if ( '' === $new_value ) { + $attributes[ $attribute_name ] = $extra_value; + continue; + } + + if ( '' === $extra_value ) { + $attributes[ $attribute_name ] = $new_value; + continue; + } + + $attributes[ $attribute_name ] = $extra_value . ' ' . $new_value; + } + + foreach ( $extra_attributes as $attribute_name => $value ) { + if ( ! in_array( $attribute_name, $attributes_to_merge, true ) ) { + $string_value = (string) $value; + if ( '' !== $string_value ) { + $attributes[ $attribute_name ] = $string_value; + } + } + } + + if ( empty( $attributes ) ) { + return ''; + } + + $normalized_attributes = array(); + foreach ( $attributes as $key => $value ) { + $normalized_attributes[] = $key . '="' . esc_attr( $value ) . '"'; + } + + return implode( ' ', $normalized_attributes ); +} + /** * Finds a script module ID for the selected block metadata field. It detects * when a path to file was provided and optionally finds a corresponding asset diff --git a/src/wp-includes/class-wp-block-supports.php b/src/wp-includes/class-wp-block-supports.php index ec5bc9c8d6846..02743b8274fa9 100644 --- a/src/wp-includes/class-wp-block-supports.php +++ b/src/wp-includes/class-wp-block-supports.php @@ -121,7 +121,7 @@ public function apply_block_supports() { if ( ! empty( $new_attributes ) ) { foreach ( $new_attributes as $attribute_name => $attribute_value ) { - if ( empty( $output[ $attribute_name ] ) ) { + if ( ! array_key_exists( $attribute_name, $output ) || '' === (string) $output[ $attribute_name ] ) { $output[ $attribute_name ] = $attribute_value; } else { $output[ $attribute_name ] .= " $attribute_value"; @@ -162,59 +162,3 @@ private function register_attributes() { } } } - -/** - * Generates a string of attributes by applying to the current block being - * rendered all of the features that the block supports. - * - * @since 5.6.0 - * - * @param string[] $extra_attributes Optional. Array of extra attributes to render on the block wrapper. - * @return string String of HTML attributes. - */ -function get_block_wrapper_attributes( $extra_attributes = array() ) { - $new_attributes = WP_Block_Supports::get_instance()->apply_block_supports(); - - if ( empty( $new_attributes ) && empty( $extra_attributes ) ) { - return ''; - } - - // This is hardcoded on purpose. - // We only support a fixed list of attributes. - $attributes_to_merge = array( 'style', 'class', 'id', 'aria-label' ); - $attributes = array(); - foreach ( $attributes_to_merge as $attribute_name ) { - if ( empty( $new_attributes[ $attribute_name ] ) && empty( $extra_attributes[ $attribute_name ] ) ) { - continue; - } - - if ( empty( $new_attributes[ $attribute_name ] ) ) { - $attributes[ $attribute_name ] = $extra_attributes[ $attribute_name ]; - continue; - } - - if ( empty( $extra_attributes[ $attribute_name ] ) ) { - $attributes[ $attribute_name ] = $new_attributes[ $attribute_name ]; - continue; - } - - $attributes[ $attribute_name ] = $extra_attributes[ $attribute_name ] . ' ' . $new_attributes[ $attribute_name ]; - } - - foreach ( $extra_attributes as $attribute_name => $value ) { - if ( ! in_array( $attribute_name, $attributes_to_merge, true ) ) { - $attributes[ $attribute_name ] = $value; - } - } - - if ( empty( $attributes ) ) { - return ''; - } - - $normalized_attributes = array(); - foreach ( $attributes as $key => $value ) { - $normalized_attributes[] = $key . '="' . esc_attr( $value ) . '"'; - } - - return implode( ' ', $normalized_attributes ); -} diff --git a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php new file mode 100644 index 0000000000000..8a667cb8307a4 --- /dev/null +++ b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php @@ -0,0 +1,121 @@ +is_registered( 'core/example' ) ) { + $registry->unregister( 'core/example' ); + } + if ( $registry->is_registered( 'core/example' ) ) { + $registry->unregister( 'core/example' ); + } + + parent::tear_down(); + } + + /** + * @ticket 64452 + */ + public function test_preserves_zero_values() { + WP_Block_Supports::init(); + register_block_type( + 'core/example', + array( + 'supports' => array( + 'customClassName' => true, + 'ariaLabel' => true, + ), + ) + ); + WP_Block_Supports::$block_to_render = array( + 'blockName' => 'core/example', + 'attrs' => array( + 'className' => '0', + 'ariaLabel' => 0, + ), + ); + $result = get_block_wrapper_attributes(); + $this->assertSame( 'class="0 wp-block-example" aria-label="0"', $result ); + } + + /** + * @ticket 64452 + */ + public function test_preserves_zero_values_from_extra_attributes() { + WP_Block_Supports::init(); + register_block_type( 'core/example' ); + WP_Block_Supports::$block_to_render = array( 'blockName' => 'core/example' ); + + $result = get_block_wrapper_attributes( + array( + 'class' => '0', + 'aria-label' => 0, + 'data-foo' => 0, + 'data-var' => '0', + ) + ); + $this->assertSame( 'class="0 wp-block-example" aria-label="0" data-foo="0" data-var="0"', $result ); + } + + /** + * @ticket 64452 + */ + public function test_excludes_falsy_values_except_zero() { + WP_Block_Supports::init(); + register_block_type( + 'core/example', + array( + 'supports' => array( + 'customClassName' => true, + 'ariaLabel' => true, + ), + ) + ); + WP_Block_Supports::$block_to_render = array( + 'blockName' => 'core/example', + 'attrs' => array( + 'className' => false, + 'ariaLabel' => null, + ), + ); + + $result = get_block_wrapper_attributes(); + $this->assertSame( 'class="wp-block-example"', $result ); + } + + /** + * @ticket 64452 + */ + public function test_excludes_falsy_values_except_zero_from_extra_attributes() { + WP_Block_Supports::init(); + register_block_type( 'core/example' ); + WP_Block_Supports::$block_to_render = array( 'blockName' => 'core/example' ); + + $result = get_block_wrapper_attributes( + array( + 'class' => false, + 'aria-label' => null, + 'data-var' => false, + 'data-baz' => null, + ) + ); + $this->assertSame( 'class="wp-block-example"', $result ); + } +} + From e08f442859308abc8d657873352115f744caef33 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Thu, 25 Dec 2025 20:11:10 +0900 Subject: [PATCH 2/5] Fix phpcs error --- tests/phpunit/tests/blocks/getBlockWrapperAttributes.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php index 8a667cb8307a4..7cc709a85041c 100644 --- a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php +++ b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php @@ -118,4 +118,3 @@ public function test_excludes_falsy_values_except_zero_from_extra_attributes() { $this->assertSame( 'class="wp-block-example"', $result ); } } - From 1b4f2ed3de4755740b20147dad70658ddcb82f4f Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:08:25 +0900 Subject: [PATCH 3/5] Update tests/phpunit/tests/blocks/getBlockWrapperAttributes.php Co-authored-by: Weston Ruter --- tests/phpunit/tests/blocks/getBlockWrapperAttributes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php index 7cc709a85041c..2b8768e0513b1 100644 --- a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php +++ b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php @@ -50,6 +50,7 @@ public function test_preserves_zero_values() { 'ariaLabel' => 0, ), ); + $result = get_block_wrapper_attributes(); $this->assertSame( 'class="0 wp-block-example" aria-label="0"', $result ); } From d6ac965b8f3fdd36c2f32bf91884069fc5a3bf0b Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Tue, 30 Dec 2025 15:13:36 +0900 Subject: [PATCH 4/5] Fix PHP lint error --- tests/phpunit/tests/blocks/getBlockWrapperAttributes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php index 2b8768e0513b1..c3c31cd92e877 100644 --- a/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php +++ b/tests/phpunit/tests/blocks/getBlockWrapperAttributes.php @@ -65,10 +65,10 @@ public function test_preserves_zero_values_from_extra_attributes() { $result = get_block_wrapper_attributes( array( - 'class' => '0', + 'class' => '0', 'aria-label' => 0, - 'data-foo' => 0, - 'data-var' => '0', + 'data-foo' => 0, + 'data-var' => '0', ) ); $this->assertSame( 'class="0 wp-block-example" aria-label="0" data-foo="0" data-var="0"', $result ); From 88e61a59237e84ba258fe18068b137eb010f3ec6 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Wed, 31 Dec 2025 22:31:10 +0900 Subject: [PATCH 5/5] Restore the function location --- src/wp-includes/blocks.php | 62 --------------------- src/wp-includes/class-wp-block-supports.php | 62 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index b5ae40373af8f..a8ed76d3bd71f 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -127,68 +127,6 @@ function get_block_asset_url( $path ) { return plugins_url( basename( $path ), $path ); } -/** - * Generates a string of attributes by applying to the current block being - * rendered all of the features that the block supports. - * - * @since 5.6.0 - * - * @param string[] $extra_attributes Optional. Array of extra attributes to render on the block wrapper. - * @return string String of HTML attributes. - */ -function get_block_wrapper_attributes( $extra_attributes = array() ) { - $new_attributes = WP_Block_Supports::get_instance()->apply_block_supports(); - - if ( empty( $new_attributes ) && empty( $extra_attributes ) ) { - return ''; - } - - // This is hardcoded on purpose. - // We only support a fixed list of attributes. - $attributes_to_merge = array( 'style', 'class', 'id', 'aria-label' ); - $attributes = array(); - foreach ( $attributes_to_merge as $attribute_name ) { - $new_value = array_key_exists( $attribute_name, $new_attributes ) ? (string) $new_attributes[ $attribute_name ] : ''; - $extra_value = array_key_exists( $attribute_name, $extra_attributes ) ? (string) $extra_attributes[ $attribute_name ] : ''; - - if ( '' === $new_value && '' === $extra_value ) { - continue; - } - - if ( '' === $new_value ) { - $attributes[ $attribute_name ] = $extra_value; - continue; - } - - if ( '' === $extra_value ) { - $attributes[ $attribute_name ] = $new_value; - continue; - } - - $attributes[ $attribute_name ] = $extra_value . ' ' . $new_value; - } - - foreach ( $extra_attributes as $attribute_name => $value ) { - if ( ! in_array( $attribute_name, $attributes_to_merge, true ) ) { - $string_value = (string) $value; - if ( '' !== $string_value ) { - $attributes[ $attribute_name ] = $string_value; - } - } - } - - if ( empty( $attributes ) ) { - return ''; - } - - $normalized_attributes = array(); - foreach ( $attributes as $key => $value ) { - $normalized_attributes[] = $key . '="' . esc_attr( $value ) . '"'; - } - - return implode( ' ', $normalized_attributes ); -} - /** * Finds a script module ID for the selected block metadata field. It detects * when a path to file was provided and optionally finds a corresponding asset diff --git a/src/wp-includes/class-wp-block-supports.php b/src/wp-includes/class-wp-block-supports.php index 02743b8274fa9..a752d413a810a 100644 --- a/src/wp-includes/class-wp-block-supports.php +++ b/src/wp-includes/class-wp-block-supports.php @@ -162,3 +162,65 @@ private function register_attributes() { } } } + +/** + * Generates a string of attributes by applying to the current block being + * rendered all of the features that the block supports. + * + * @since 5.6.0 + * + * @param string[] $extra_attributes Optional. Array of extra attributes to render on the block wrapper. + * @return string String of HTML attributes. + */ +function get_block_wrapper_attributes( $extra_attributes = array() ) { + $new_attributes = WP_Block_Supports::get_instance()->apply_block_supports(); + + if ( empty( $new_attributes ) && empty( $extra_attributes ) ) { + return ''; + } + + // This is hardcoded on purpose. + // We only support a fixed list of attributes. + $attributes_to_merge = array( 'style', 'class', 'id', 'aria-label' ); + $attributes = array(); + foreach ( $attributes_to_merge as $attribute_name ) { + $new_value = array_key_exists( $attribute_name, $new_attributes ) ? (string) $new_attributes[ $attribute_name ] : ''; + $extra_value = array_key_exists( $attribute_name, $extra_attributes ) ? (string) $extra_attributes[ $attribute_name ] : ''; + + if ( '' === $new_value && '' === $extra_value ) { + continue; + } + + if ( '' === $new_value ) { + $attributes[ $attribute_name ] = $extra_value; + continue; + } + + if ( '' === $extra_value ) { + $attributes[ $attribute_name ] = $new_value; + continue; + } + + $attributes[ $attribute_name ] = $extra_value . ' ' . $new_value; + } + + foreach ( $extra_attributes as $attribute_name => $value ) { + if ( ! in_array( $attribute_name, $attributes_to_merge, true ) ) { + $string_value = (string) $value; + if ( '' !== $string_value ) { + $attributes[ $attribute_name ] = $string_value; + } + } + } + + if ( empty( $attributes ) ) { + return ''; + } + + $normalized_attributes = array(); + foreach ( $attributes as $key => $value ) { + $normalized_attributes[] = $key . '="' . esc_attr( $value ) . '"'; + } + + return implode( ' ', $normalized_attributes ); +}