From 8386a036cbc2c8e6b922f22e5f1d51ff791acc99 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Sun, 31 May 2026 18:57:23 -0700
Subject: [PATCH 1/7] Elements: Guard against non-string className in render
filter
---
src/wp-includes/block-supports/elements.php | 4 +++
.../wpRenderElementsSupport.php | 30 +++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php
index 374da09e8bc8c..1ea0f691d18d4 100644
--- a/src/wp-includes/block-supports/elements.php
+++ b/src/wp-includes/block-supports/elements.php
@@ -243,6 +243,10 @@ function wp_render_elements_support_styles( $parsed_block ) {
*/
function wp_render_elements_class_name( $block_content, $block ) {
$class_string = $block['attrs']['className'] ?? '';
+
+ if ( ! is_string( $class_string ) ) {
+ return $block_content;
+ }
preg_match( '/\bwp-elements-\S+\b/', $class_string, $matches );
if ( empty( $matches ) ) {
diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
index 9103fcba90b66..f77e608c4041a 100644
--- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
@@ -105,6 +105,36 @@ public function test_elements_block_support_class( $color_settings, $elements_st
);
}
+ /**
+ * Tests that a non-string `className` attribute does not cause a fatal
+ * error and the block content is returned unmodified.
+ *
+ * @ticket 65379
+ *
+ * Block attributes such as `className` are always expected to be strings,
+ * however invalid stored data can result in other types being present. The
+ * render filter should fail gracefully rather than passing an array to
+ * `preg_match()`.
+ *
+ * @covers ::wp_render_elements_class_name
+ */
+ public function test_elements_block_support_class_with_non_string_class_name() {
+ $block = array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(
+ 'className' => array( '0', '1' ),
+ ),
+ );
+
+ $block_content = "
Test
\n";
+
+ $this->assertSame(
+ $block_content,
+ wp_render_elements_class_name( $block_content, $block ),
+ 'Block content should be returned unchanged when className is not a string'
+ );
+ }
+
/**
* Data provider.
*
From 270928be319ff44f5c0503301378bde0fd04f794 Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 8 Jun 2026 16:42:44 -0700
Subject: [PATCH 2/7] Re-order docblock
Co-authored-by: Mukesh Panchal
---
.../phpunit/tests/block-supports/wpRenderElementsSupport.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
index f77e608c4041a..e156bfde37b23 100644
--- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
@@ -109,13 +109,13 @@ public function test_elements_block_support_class( $color_settings, $elements_st
* Tests that a non-string `className` attribute does not cause a fatal
* error and the block content is returned unmodified.
*
- * @ticket 65379
- *
* Block attributes such as `className` are always expected to be strings,
* however invalid stored data can result in other types being present. The
* render filter should fail gracefully rather than passing an array to
* `preg_match()`.
*
+ * @ticket 65379
+ *
* @covers ::wp_render_elements_class_name
*/
public function test_elements_block_support_class_with_non_string_class_name() {
From be0e3359335d5628c99d5338b3f60a4da85092a2 Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 8 Jun 2026 16:45:26 -0700
Subject: [PATCH 3/7] Add type hint
---
tests/phpunit/tests/block-supports/wpRenderElementsSupport.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
index e156bfde37b23..b5f2366ef4349 100644
--- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
@@ -118,7 +118,7 @@ public function test_elements_block_support_class( $color_settings, $elements_st
*
* @covers ::wp_render_elements_class_name
*/
- public function test_elements_block_support_class_with_non_string_class_name() {
+ public function test_elements_block_support_class_with_non_string_class_name(): void {
$block = array(
'blockName' => 'core/paragraph',
'attrs' => array(
From 7e6542d316682dde0d0ebbeade60ae276efed669 Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 8 Jun 2026 16:51:47 -0700
Subject: [PATCH 4/7] Add array shape docs for $block param
---
src/wp-includes/block-supports/elements.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php
index 1ea0f691d18d4..7c79b97df663a 100644
--- a/src/wp-includes/block-supports/elements.php
+++ b/src/wp-includes/block-supports/elements.php
@@ -240,6 +240,8 @@ function wp_render_elements_support_styles( $parsed_block ) {
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
+ *
+ * @phpstan-param array{ attrs: array{ className: string, ... }, ... } $block
*/
function wp_render_elements_class_name( $block_content, $block ) {
$class_string = $block['attrs']['className'] ?? '';
From d5aee5ca1c3d9f0e2148b9066343d078d8232570 Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 8 Jun 2026 17:06:51 -0700
Subject: [PATCH 5/7] Align the wp_render_elements_class_name() and
wp_render_custom_css_class_name() implementations
---
src/wp-includes/block-supports/custom-css.php | 21 +++++------
src/wp-includes/block-supports/elements.php | 37 +++++++++++++------
.../wpRenderElementsSupport.php | 24 ++++++++++++
3 files changed, 60 insertions(+), 22 deletions(-)
diff --git a/src/wp-includes/block-supports/custom-css.php b/src/wp-includes/block-supports/custom-css.php
index d4331ae3706ae..c947609a35698 100644
--- a/src/wp-includes/block-supports/custom-css.php
+++ b/src/wp-includes/block-supports/custom-css.php
@@ -98,32 +98,31 @@ function wp_enqueue_block_custom_css() {
* } $block
*/
function wp_render_custom_css_class_name( $block_content, $block ) {
- $class_name_attr = $block['attrs']['className'] ?? null;
-
- if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, 'wp-custom-css-' ) ) {
+ $class_name_attr = $block['attrs']['className'] ?? null;
+ $class_name_prefix = 'wp-custom-css-';
+ if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
return $block_content;
}
// Parse out the 'wp-custom-css-*' class name added by wp_render_custom_css_support_styles().
- $custom_class_name = null;
- $token_delimiter = " \t\f\r\n";
- $class_token = strtok( $class_name_attr, $token_delimiter );
+ $matched_class_name = null;
+ $token_delimiter = " \t\f\r\n";
+ $class_token = strtok( $class_name_attr, $token_delimiter );
while ( false !== $class_token ) {
- if ( str_starts_with( $class_token, 'wp-custom-css-' ) ) {
- $custom_class_name = $class_token;
+ if ( str_starts_with( $class_token, $class_name_prefix ) ) {
+ $matched_class_name = $class_token;
break;
}
$class_token = strtok( $token_delimiter );
}
- if ( null === $custom_class_name ) {
+ if ( null === $matched_class_name ) {
return $block_content;
}
$tags = new WP_HTML_Tag_Processor( $block_content );
-
if ( $tags->next_tag() ) {
$tags->add_class( 'has-custom-css' );
- $tags->add_class( $custom_class_name );
+ $tags->add_class( $matched_class_name );
}
return $tags->get_updated_html();
diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php
index 7c79b97df663a..54b96aa1dc064 100644
--- a/src/wp-includes/block-supports/elements.php
+++ b/src/wp-includes/block-supports/elements.php
@@ -237,28 +237,43 @@ function wp_render_elements_support_styles( $parsed_block ) {
* @see wp_render_elements_support_styles
* @since 6.6.0
*
- * @param string $block_content Rendered block content.
- * @param array $block Block object.
- * @return string Filtered block content.
+ * @param string $block_content Rendered block content.
+ * @param array $block Block object.
+ * @return string Filtered block content.
*
- * @phpstan-param array{ attrs: array{ className: string, ... }, ... } $block
+ * @phpstan-param array{
+ * attrs: array{
+ * className?: string,
+ * ...
+ * },
+ * ...
+ * } $block
*/
function wp_render_elements_class_name( $block_content, $block ) {
- $class_string = $block['attrs']['className'] ?? '';
-
- if ( ! is_string( $class_string ) ) {
+ $class_name_attr = $block['attrs']['className'] ?? null;
+ $class_name_prefix = 'wp-elements-';
+ if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
return $block_content;
}
- preg_match( '/\bwp-elements-\S+\b/', $class_string, $matches );
- if ( empty( $matches ) ) {
+ // Parse out the 'wp-elements-*' class name.
+ $matched_class_name = null;
+ $token_delimiter = " \t\f\r\n";
+ $class_token = strtok( $class_name_attr, $token_delimiter );
+ while ( false !== $class_token ) {
+ if ( str_starts_with( $class_token, $class_name_prefix ) ) {
+ $matched_class_name = $class_token;
+ break;
+ }
+ $class_token = strtok( $token_delimiter );
+ }
+ if ( null === $matched_class_name ) {
return $block_content;
}
$tags = new WP_HTML_Tag_Processor( $block_content );
-
if ( $tags->next_tag() ) {
- $tags->add_class( $matches[0] );
+ $tags->add_class( $matched_class_name );
}
return $tags->get_updated_html();
diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
index b5f2366ef4349..9a81ce0efc067 100644
--- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
@@ -135,6 +135,30 @@ public function test_elements_block_support_class_with_non_string_class_name():
);
}
+ /**
+ * Tests that a 'my-wp-elements-*' class name is skipped from processing.
+ *
+ * @ticket 65379
+ *
+ * @covers ::wp_render_elements_class_name
+ */
+ public function test_elements_block_support_class_with_invalid_elements_prefix(): void {
+ $block = array(
+ 'blockName' => 'core/paragraph',
+ 'attrs' => array(
+ 'className' => 'my-wp-elements-foo',
+ ),
+ );
+
+ $block_content = "Test
\n";
+
+ $this->assertSame(
+ $block_content,
+ wp_render_elements_class_name( $block_content, $block ),
+ 'Block content should be returned unchanged when className is not a string'
+ );
+ }
+
/**
* Data provider.
*
From 4265f5f1561895287e41b7774457b212a26e6b6c Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 8 Jun 2026 17:17:06 -0700
Subject: [PATCH 6/7] Fix assertion message
---
tests/phpunit/tests/block-supports/wpRenderElementsSupport.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
index 9a81ce0efc067..8e3f555de52ca 100644
--- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
@@ -155,7 +155,7 @@ public function test_elements_block_support_class_with_invalid_elements_prefix()
$this->assertSame(
$block_content,
wp_render_elements_class_name( $block_content, $block ),
- 'Block content should be returned unchanged when className is not a string'
+ 'Block content should be returned unchanged when className lacks a class with the expected prefix'
);
}
From b074209c7eff0538dd3ad128adb23376b4d906eb Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 8 Jun 2026 17:28:04 -0700
Subject: [PATCH 7/7] Add phpstan-ignore
---
tests/phpunit/tests/block-supports/wpRenderElementsSupport.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
index 8e3f555de52ca..007ba8312e495 100644
--- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
@@ -130,7 +130,7 @@ public function test_elements_block_support_class_with_non_string_class_name():
$this->assertSame(
$block_content,
- wp_render_elements_class_name( $block_content, $block ),
+ wp_render_elements_class_name( $block_content, $block ), // @phpstan-ignore argument.type (Intentionally passing bad attrs array.)
'Block content should be returned unchanged when className is not a string'
);
}