Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions src/wp-includes/block-supports/custom-css.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
37 changes: 29 additions & 8 deletions src/wp-includes/block-supports/elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,22 +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
*/
function wp_render_elements_class_name( $block_content, $block ) {
$class_string = $block['attrs']['className'] ?? '';
preg_match( '/\bwp-elements-\S+\b/', $class_string, $matches );
$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;
}

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();
Expand Down
54 changes: 54 additions & 0 deletions tests/phpunit/tests/block-supports/wpRenderElementsSupport.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,60 @@ 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.
*
* 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(): void {
$block = array(
'blockName' => 'core/paragraph',
'attrs' => array(
'className' => array( '0', '1' ),
),
);

$block_content = "<p class=\"0 1\">Test</p>\n";

$this->assertSame(
$block_content,
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'
);
}

/**
* 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 = "<p>Test</p>\n";

$this->assertSame(
$block_content,
wp_render_elements_class_name( $block_content, $block ),
'Block content should be returned unchanged when className lacks a class with the expected prefix'
);
}

/**
* Data provider.
*
Expand Down
Loading