Skip to content

Commit 3771c1c

Browse files
committed
Blocks: Preserve zero values in wrapper attributes.
The string `"0"` and the integer `0` were previously dropped when merging block wrapper attributes because falsy values were filtered out. Strings and numbers are now kept and cast to a string, so zero values are preserved. Type annotations and null checks are also improved. Props westonruter, wildworks. Fixes #64452. git-svn-id: https://develop.svn.wordpress.org/trunk@62450 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 63bf1d9 commit 3771c1c

2 files changed

Lines changed: 204 additions & 8 deletions

File tree

src/wp-includes/class-wp-block-supports.php

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
* @since 5.6.0
1414
*
1515
* @access private
16+
*
17+
* @phpstan-type ApplyCallback callable( WP_Block_Type, array<string, mixed> ): array<string, mixed>
18+
* @phpstan-type RegisterCallback callable( WP_Block_Type ): void
1619
*/
1720
#[AllowDynamicProperties]
1821
class WP_Block_Supports {
@@ -22,14 +25,20 @@ class WP_Block_Supports {
2225
*
2326
* @since 5.6.0
2427
* @var array
28+
* @phpstan-var array<string, array{
29+
* name: string,
30+
* apply?: ApplyCallback,
31+
* register_attribute?: RegisterCallback,
32+
* }>
2533
*/
2634
private $block_supports = array();
2735

2836
/**
2937
* Tracks the current block to be rendered.
3038
*
3139
* @since 5.6.0
32-
* @var array
40+
* @var array|null
41+
* @phpstan-var array<string, mixed>|null
3342
*/
3443
public static $block_to_render = null;
3544

@@ -62,6 +71,8 @@ public static function get_instance() {
6271
* Initializes the block supports. It registers the block supports block attributes.
6372
*
6473
* @since 5.6.0
74+
*
75+
* @return void
6576
*/
6677
public static function init() {
6778
$instance = self::get_instance();
@@ -77,6 +88,13 @@ public static function init() {
7788
*
7889
* @param string $block_support_name Block support name.
7990
* @param array $block_support_config Array containing the properties of the block support.
91+
*
92+
* @phpstan-param array{
93+
* apply?: ApplyCallback,
94+
* register_attribute?: RegisterCallback,
95+
* } $block_support_config
96+
*
97+
* @return void
8098
*/
8199
public function register( $block_support_name, $block_support_config ) {
82100
$this->block_supports[ $block_support_name ] = array_merge(
@@ -94,12 +112,16 @@ public function register( $block_support_name, $block_support_config ) {
94112
* @return string[] Array of HTML attribute values keyed by their name.
95113
*/
96114
public function apply_block_supports() {
115+
if ( ! is_array( self::$block_to_render ) ) {
116+
return array();
117+
}
118+
97119
$block_type = WP_Block_Type_Registry::get_instance()->get_registered(
98120
self::$block_to_render['blockName']
99121
);
100122

101123
// If no render_callback, assume styles have been previously handled.
102-
if ( ! $block_type || empty( $block_type ) ) {
124+
if ( ! $block_type ) {
103125
return array();
104126
}
105127

@@ -121,7 +143,11 @@ public function apply_block_supports() {
121143

122144
if ( ! empty( $new_attributes ) ) {
123145
foreach ( $new_attributes as $attribute_name => $attribute_value ) {
124-
if ( empty( $output[ $attribute_name ] ) ) {
146+
if ( ! is_scalar( $attribute_value ) || is_bool( $attribute_value ) ) {
147+
continue;
148+
}
149+
$attribute_value = (string) $attribute_value;
150+
if ( ! array_key_exists( $attribute_name, $output ) || '' === $output[ $attribute_name ] ) {
125151
$output[ $attribute_name ] = $attribute_value;
126152
} else {
127153
$output[ $attribute_name ] .= " $attribute_value";
@@ -137,6 +163,8 @@ public function apply_block_supports() {
137163
* Registers the block attributes required by the different block supports.
138164
*
139165
* @since 5.6.0
166+
*
167+
* @return void
140168
*/
141169
private function register_attributes() {
142170
$block_registry = WP_Block_Type_Registry::get_instance();
@@ -196,7 +224,7 @@ function get_block_wrapper_attributes( $extra_attributes = array() ) {
196224
(array) preg_split( '/\s+/', $extra_attribute, -1, PREG_SPLIT_NO_EMPTY ),
197225
(array) preg_split( '/\s+/', $new_attribute, -1, PREG_SPLIT_NO_EMPTY )
198226
);
199-
$classes = array_unique( array_filter( $classes ) );
227+
$classes = array_unique( $classes );
200228
return implode( ' ', $classes );
201229
},
202230
'id' => static function ( $new_attribute, $extra_attribute ) {
@@ -207,12 +235,13 @@ function get_block_wrapper_attributes( $extra_attributes = array() ) {
207235
},
208236
);
209237

238+
// Accept strings and numbers (cast to string); reject other types (bool, null, array, object).
210239
$attributes = array();
211240
foreach ( $attribute_merge_callbacks as $attribute_name => $merge_callback ) {
212241
$new_attribute = $new_attributes[ $attribute_name ] ?? '';
213242
$extra_attribute = $extra_attributes[ $attribute_name ] ?? '';
214-
$new_attribute = is_string( $new_attribute ) ? $new_attribute : '';
215-
$extra_attribute = is_string( $extra_attribute ) ? $extra_attribute : '';
243+
$new_attribute = is_scalar( $new_attribute ) && ! is_bool( $new_attribute ) ? (string) $new_attribute : '';
244+
$extra_attribute = is_scalar( $extra_attribute ) && ! is_bool( $extra_attribute ) ? (string) $extra_attribute : '';
216245

217246
if ( '' === $new_attribute && '' === $extra_attribute ) {
218247
continue;
@@ -222,8 +251,8 @@ function get_block_wrapper_attributes( $extra_attributes = array() ) {
222251
}
223252

224253
foreach ( $extra_attributes as $attribute_name => $value ) {
225-
if ( ! isset( $attribute_merge_callbacks[ $attribute_name ] ) ) {
226-
$attributes[ $attribute_name ] = $value;
254+
if ( ! isset( $attribute_merge_callbacks[ $attribute_name ] ) && is_scalar( $value ) && ! is_bool( $value ) ) {
255+
$attributes[ $attribute_name ] = (string) $value;
227256
}
228257
}
229258

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
/**
3+
* Tests for get_block_wrapper_attributes function.
4+
*
5+
* @package WordPress
6+
* @subpackage Blocks
7+
*
8+
* @since 7.1.0
9+
*
10+
* @group blocks
11+
* @covers ::get_block_wrapper_attributes
12+
*/
13+
class Tests_Blocks_GetBlockWrapperAttributes extends WP_UnitTestCase {
14+
15+
/**
16+
* Tear down after each test.
17+
*
18+
* @since 7.1.0
19+
*/
20+
public function tear_down(): void {
21+
$registry = WP_Block_Type_Registry::get_instance();
22+
if ( $registry->is_registered( 'core/example' ) ) {
23+
$registry->unregister( 'core/example' );
24+
}
25+
26+
parent::tear_down();
27+
}
28+
29+
/**
30+
* The string '0' is preserved for block support attributes.
31+
*
32+
* @ticket 64452
33+
*/
34+
public function test_preserves_string_zero_values(): void {
35+
WP_Block_Supports::init();
36+
register_block_type(
37+
'core/example',
38+
array(
39+
'supports' => array(
40+
'customClassName' => true,
41+
'ariaLabel' => true,
42+
),
43+
)
44+
);
45+
WP_Block_Supports::$block_to_render = array(
46+
'blockName' => 'core/example',
47+
'attrs' => array(
48+
'className' => '0',
49+
'ariaLabel' => '0',
50+
),
51+
);
52+
53+
$result = get_block_wrapper_attributes();
54+
$this->assertSame( 'class="0 wp-block-example" aria-label="0"', $result );
55+
}
56+
57+
/**
58+
* @ticket 64452
59+
*/
60+
public function test_preserves_string_zero_values_from_extra_attributes(): void {
61+
WP_Block_Supports::init();
62+
register_block_type( 'core/example' );
63+
WP_Block_Supports::$block_to_render = array( 'blockName' => 'core/example' );
64+
65+
$result = get_block_wrapper_attributes(
66+
array(
67+
'class' => '0',
68+
'id' => '0',
69+
'aria-label' => '0',
70+
'data-foo' => '0',
71+
'data-var' => '0',
72+
)
73+
);
74+
$this->assertSame( 'class="0 wp-block-example" id="0" aria-label="0" data-foo="0" data-var="0"', $result );
75+
}
76+
77+
/**
78+
* @ticket 64452
79+
*/
80+
public function test_preserves_numeric_values(): void {
81+
WP_Block_Supports::init();
82+
register_block_type(
83+
'core/example',
84+
array(
85+
'supports' => array(
86+
'customClassName' => true,
87+
'ariaLabel' => true,
88+
),
89+
)
90+
);
91+
WP_Block_Supports::$block_to_render = array(
92+
'blockName' => 'core/example',
93+
'attrs' => array(
94+
'className' => 5,
95+
'ariaLabel' => 42,
96+
),
97+
);
98+
99+
$result = get_block_wrapper_attributes();
100+
$this->assertSame( 'class="5 wp-block-example" aria-label="42"', $result );
101+
}
102+
103+
/**
104+
* @ticket 64452
105+
*/
106+
public function test_preserves_numeric_values_from_extra_attributes(): void {
107+
WP_Block_Supports::init();
108+
register_block_type( 'core/example' );
109+
WP_Block_Supports::$block_to_render = array( 'blockName' => 'core/example' );
110+
111+
$result = get_block_wrapper_attributes(
112+
array(
113+
'class' => 5,
114+
'id' => 7,
115+
'aria-label' => 42,
116+
'data-foo' => 1.5,
117+
)
118+
);
119+
$this->assertSame( 'class="5 wp-block-example" id="7" aria-label="42" data-foo="1.5"', $result );
120+
}
121+
122+
/**
123+
* @ticket 64452
124+
*/
125+
public function test_excludes_non_scalar_values(): void {
126+
WP_Block_Supports::init();
127+
register_block_type(
128+
'core/example',
129+
array(
130+
'supports' => array(
131+
'customClassName' => true,
132+
'ariaLabel' => true,
133+
),
134+
)
135+
);
136+
WP_Block_Supports::$block_to_render = array(
137+
'blockName' => 'core/example',
138+
'attrs' => array(
139+
'className' => true,
140+
'ariaLabel' => array( 'x' ),
141+
),
142+
);
143+
144+
$result = get_block_wrapper_attributes();
145+
$this->assertSame( 'class="wp-block-example"', $result );
146+
}
147+
148+
/**
149+
* @ticket 64452
150+
*/
151+
public function test_excludes_non_scalar_values_from_extra_attributes(): void {
152+
WP_Block_Supports::init();
153+
register_block_type( 'core/example' );
154+
WP_Block_Supports::$block_to_render = array( 'blockName' => 'core/example' );
155+
156+
$result = get_block_wrapper_attributes(
157+
array(
158+
'class' => true,
159+
'id' => false,
160+
'aria-label' => null,
161+
'data-foo' => array( 'x' ),
162+
'data-bar' => true,
163+
)
164+
);
165+
$this->assertSame( 'class="wp-block-example"', $result );
166+
}
167+
}

0 commit comments

Comments
 (0)