From dd8a47998a3cf066686e81981c8b9474e9f988dd Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Wed, 4 Oct 2023 15:36:43 -0700 Subject: [PATCH 1/7] Performance: Skip iteration in block supports elements when not necessary. Previously there have been three ways in which the block supports elements code has been performing computation it doesn't need to. - It checks for every possible style attribute on a block even if there are no possible style attributes available on the block. - Once it determines that it needs to add a special class to a block it continues scanning through the rest of the attributes. - It constructs dot-separated literal key paths for the attributes and immediately and repeatedly calls `explode()` on them to create an array at runtime. The combination of these three factors leads to an outsized impact of this function on the runtime and memory pressure for what it's doing. This patch removes all three of these inefficiencies: - If no elements style attributes exist on the block it immediately returns and skips all further processing. - It stops scanning block attributes once a single one is found that imposes the requirement for the extra class name. - It constructs array literals for the key path instead of strings. This removes the need to artificially join and explode the keys at runtime. There should be no functional or visual changes in this patch, but it should reduce the overall runtime of page renders and require less memory while rendering. The impact will be proportional to the number of blocks rendered on a page. --- src/wp-includes/block-supports/elements.php | 88 +++++++++++---------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 8cd2e1795c0e2..770e3e95c30e1 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,7 +31,7 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { - if ( ! $block_content || empty( $block['attrs'] ) ) { + if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { return $block_content; } @@ -41,42 +41,42 @@ function wp_render_elements_support( $block_content, $block ) { 'button' => array( 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ), 'paths' => array( - 'style.elements.button.color.text', - 'style.elements.button.color.background', - 'style.elements.button.color.gradient', + array( 'button', 'color', 'text' ), + array( 'button', 'color', 'background' ), + array( 'button', 'color', 'gradient' ), ), ), 'link' => array( 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ), 'paths' => array( - 'style.elements.link.color.text', - 'style.elements.link.:hover.color.text', + array( 'link', 'color', 'text' ), + array( 'link', ':hover', 'color', 'text' ), ), ), 'heading' => array( 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ), 'paths' => array( - 'style.elements.heading.color.text', - 'style.elements.heading.color.background', - 'style.elements.heading.color.gradient', - 'style.elements.h1.color.text', - 'style.elements.h1.color.background', - 'style.elements.h1.color.gradient', - 'style.elements.h2.color.text', - 'style.elements.h2.color.background', - 'style.elements.h2.color.gradient', - 'style.elements.h3.color.text', - 'style.elements.h3.color.background', - 'style.elements.h3.color.gradient', - 'style.elements.h4.color.text', - 'style.elements.h4.color.background', - 'style.elements.h4.color.gradient', - 'style.elements.h5.color.text', - 'style.elements.h5.color.background', - 'style.elements.h5.color.gradient', - 'style.elements.h6.color.text', - 'style.elements.h6.color.background', - 'style.elements.h6.color.gradient', + array( 'heading', 'color', 'text' ), + array( 'heading', 'color', 'background' ), + array( 'heading', 'color', 'gradient' ), + array( 'h1', 'color', 'text' ), + array( 'h1', 'color', 'background' ), + array( 'h1', 'color', 'gradient' ), + array( 'h2', 'color', 'text' ), + array( 'h2', 'color', 'background' ), + array( 'h2', 'color', 'gradient' ), + array( 'h3', 'color', 'text' ), + array( 'h3', 'color', 'background' ), + array( 'h3', 'color', 'gradient' ), + array( 'h4', 'color', 'text' ), + array( 'h4', 'color', 'background' ), + array( 'h4', 'color', 'gradient' ), + array( 'h5', 'color', 'text' ), + array( 'h5', 'color', 'background' ), + array( 'h5', 'color', 'gradient' ), + array( 'h6', 'color', 'text' ), + array( 'h6', 'color', 'background' ), + array( 'h6', 'color', 'gradient' ), ), ), ); @@ -89,7 +89,7 @@ function wp_render_elements_support( $block_content, $block ) { return $block_content; } - $element_colors_set = 0; + $elements_style_attributes = $block['attrs']['style']['elements']; foreach ( $element_color_properties as $element_config ) { if ( $element_config['skip'] ) { @@ -97,24 +97,28 @@ function wp_render_elements_support( $block_content, $block ) { } foreach ( $element_config['paths'] as $path ) { - if ( null !== _wp_array_get( $block['attrs'], explode( '.', $path ), null ) ) { - ++$element_colors_set; + if ( null !== _wp_array_get( $elements_style_attributes, $path, null ) ) { + /* + * It only takes a single custom attribute to require that the custom + * class name be added to the block, so once one is found there's no + * need to continue looking for others. + * + * As is done with the layout hook, this code assumes that the block + * contains a single wrapper and that it's the first element in the + * rendered output. That first element, if it exists, gets the class. + */ + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } + + return $tags->get_updated_html(); } } } - if ( ! $element_colors_set ) { - return $block_content; - } - - // Like the layout hook this assumes the hook only applies to blocks with a single wrapper. - // Add the class name to the first element, presuming it's the wrapper, if it exists. - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } - - return $tags->get_updated_html(); + // If no custom attributes were found then there's nothing to modify. + return $block_content; } /** From 0f7a1d527b273c6676f85937f8287db9dce5859c Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 5 Oct 2023 11:39:23 -0700 Subject: [PATCH 2/7] Create optimized predicate function to determine block eligibility. --- src/wp-includes/block-supports/elements.php | 146 ++++++++++---------- 1 file changed, 74 insertions(+), 72 deletions(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 770e3e95c30e1..254680c5d4a05 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,94 +31,96 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { - if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { + if ( + ! $block_content || + ! isset( $block['attrs']['style']['elements'] ) || + ! wp_render_elements_support_required_for_block( $block ) + ) { return $block_content; } - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } - $element_color_properties = array( - 'button' => array( - 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ), - 'paths' => array( - array( 'button', 'color', 'text' ), - array( 'button', 'color', 'background' ), - array( 'button', 'color', 'gradient' ), - ), - ), - 'link' => array( - 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ), - 'paths' => array( - array( 'link', 'color', 'text' ), - array( 'link', ':hover', 'color', 'text' ), - ), - ), - 'heading' => array( - 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ), - 'paths' => array( - array( 'heading', 'color', 'text' ), - array( 'heading', 'color', 'background' ), - array( 'heading', 'color', 'gradient' ), - array( 'h1', 'color', 'text' ), - array( 'h1', 'color', 'background' ), - array( 'h1', 'color', 'gradient' ), - array( 'h2', 'color', 'text' ), - array( 'h2', 'color', 'background' ), - array( 'h2', 'color', 'gradient' ), - array( 'h3', 'color', 'text' ), - array( 'h3', 'color', 'background' ), - array( 'h3', 'color', 'gradient' ), - array( 'h4', 'color', 'text' ), - array( 'h4', 'color', 'background' ), - array( 'h4', 'color', 'gradient' ), - array( 'h5', 'color', 'text' ), - array( 'h5', 'color', 'background' ), - array( 'h5', 'color', 'gradient' ), - array( 'h6', 'color', 'text' ), - array( 'h6', 'color', 'background' ), - array( 'h6', 'color', 'gradient' ), - ), - ), - ); + return $tags->get_updated_html(); +} + +/** + * Determines if a block needs to be flagged for elements support + * based on its specific attributes and general block type. + * + * @since 6.4.0 + * @access private + * + * @param array $block Block object. + * @return bool + */ +function wp_render_elements_support_required_for_block( $block ) { + static $heading_elements = array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); + + /* + * Every block attribute which would require elements support is inside + * this path, so if the path itself doesn't exist there's no reason to + * perform any further checking. + */ + if ( ! isset( $block['attrs']['style']['elements'] ) ) { + return false; + } - $skip_all_element_color_serialization = $element_color_properties['button']['skip'] && - $element_color_properties['link']['skip'] && - $element_color_properties['heading']['skip']; + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); + $style_attributes = $block['attrs']['style']['elements']; - if ( $skip_all_element_color_serialization ) { - return $block_content; + // Button element support. + $supports_button = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ); + $has_button_attrs = isset( $style_attributes['button']['color'] ); + + if ( $supports_button && $has_button_attrs ) { + $button_attributes = $style_attributes['button']['color']; + + if ( + isset( $button_attributes['text'] ) || + isset( $button_attributes['background'] ) || + isset( $button_attributes['gradient'] ) + ) { + return true; + } } - $elements_style_attributes = $block['attrs']['style']['elements']; + // Link element support. + $supports_link = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ); + $has_link_attrs = isset( $style_attributes['link'] ); - foreach ( $element_color_properties as $element_config ) { - if ( $element_config['skip'] ) { - continue; + if ( $supports_link && $has_link_attrs ) { + if ( + isset( $style_attributes['link']['color']['text'] ) || + isset( $style_attributes['link'][':hover']['color']['text'] ) + ) { + return true; } + } - foreach ( $element_config['paths'] as $path ) { - if ( null !== _wp_array_get( $elements_style_attributes, $path, null ) ) { - /* - * It only takes a single custom attribute to require that the custom - * class name be added to the block, so once one is found there's no - * need to continue looking for others. - * - * As is done with the layout hook, this code assumes that the block - * contains a single wrapper and that it's the first element in the - * rendered output. That first element, if it exists, gets the class. - */ - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } + // Heading element support. + $supports_heading = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ); + if ( $supports_heading ) { + foreach ( $heading_elements as $element_name ) { + if ( ! isset( $style_attributes[ $element_name ]['color'] ) ) { + continue; + } - return $tags->get_updated_html(); + $heading_attributes = $style_attributes[ $element_name ]['color']; + if ( + isset( $heading_attributes['text'] ) || + isset( $heading_attributes['background'] ) || + isset( $heading_attributes['gradient'] ) + ) { + return true; } } } - // If no custom attributes were found then there's nothing to modify. - return $block_content; + return false; } /** From b35fda39d91a3e76b05339d67b070e1c39b515ed Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 5 Oct 2023 15:39:46 -0700 Subject: [PATCH 3/7] Inline logic to avoid function calls --- src/wp-includes/block-supports/elements.php | 59 ++++++++------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 254680c5d4a05..05620b8298416 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,42 +31,10 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { - if ( - ! $block_content || - ! isset( $block['attrs']['style']['elements'] ) || - ! wp_render_elements_support_required_for_block( $block ) - ) { - return $block_content; - } - - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } - - return $tags->get_updated_html(); -} - -/** - * Determines if a block needs to be flagged for elements support - * based on its specific attributes and general block type. - * - * @since 6.4.0 - * @access private - * - * @param array $block Block object. - * @return bool - */ -function wp_render_elements_support_required_for_block( $block ) { static $heading_elements = array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); - /* - * Every block attribute which would require elements support is inside - * this path, so if the path itself doesn't exist there's no reason to - * perform any further checking. - */ - if ( ! isset( $block['attrs']['style']['elements'] ) ) { - return false; + if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { + return $block_content; } $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); @@ -84,7 +52,12 @@ function wp_render_elements_support_required_for_block( $block ) { isset( $button_attributes['background'] ) || isset( $button_attributes['gradient'] ) ) { - return true; + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } + + return $tags->get_updated_html(); } } @@ -97,7 +70,12 @@ function wp_render_elements_support_required_for_block( $block ) { isset( $style_attributes['link']['color']['text'] ) || isset( $style_attributes['link'][':hover']['color']['text'] ) ) { - return true; + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } + + return $tags->get_updated_html(); } } @@ -115,12 +93,17 @@ function wp_render_elements_support_required_for_block( $block ) { isset( $heading_attributes['background'] ) || isset( $heading_attributes['gradient'] ) ) { - return true; + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } + + return $tags->get_updated_html(); } } } - return false; + return $block_content; } /** From b5207cb99c595c44a9db78e9dad9acf497b75a32 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 6 Oct 2023 07:27:40 -0700 Subject: [PATCH 4/7] Un-staticise list of heading elements. --- src/wp-includes/block-supports/elements.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 05620b8298416..343212c68f709 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,8 +31,6 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { - static $heading_elements = array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); - if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { return $block_content; } @@ -82,7 +80,7 @@ function wp_render_elements_support( $block_content, $block ) { // Heading element support. $supports_heading = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ); if ( $supports_heading ) { - foreach ( $heading_elements as $element_name ) { + foreach ( array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) as $element_name ) { if ( ! isset( $style_attributes[ $element_name ]['color'] ) ) { continue; } From 714237bc6555e21ac0e7fe0f2a6a56fa0dd08814 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 6 Oct 2023 09:18:09 -0700 Subject: [PATCH 5/7] Revert attempts at further optimization which didn't demonstrate improvements. This reverts commit b5207cb99c595c44a9db78e9dad9acf497b75a32. --- src/wp-includes/block-supports/elements.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 343212c68f709..05620b8298416 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,6 +31,8 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { + static $heading_elements = array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); + if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { return $block_content; } @@ -80,7 +82,7 @@ function wp_render_elements_support( $block_content, $block ) { // Heading element support. $supports_heading = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ); if ( $supports_heading ) { - foreach ( array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) as $element_name ) { + foreach ( $heading_elements as $element_name ) { if ( ! isset( $style_attributes[ $element_name ]['color'] ) ) { continue; } From 3a2f432cf19cbea96c63cc626542cc02d7e3243c Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 6 Oct 2023 09:18:44 -0700 Subject: [PATCH 6/7] Revert attempts at further optimization which didn't demonstrate improvements. This reverts commit b35fda39d91a3e76b05339d67b070e1c39b515ed. --- src/wp-includes/block-supports/elements.php | 59 +++++++++++++-------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 05620b8298416..254680c5d4a05 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,10 +31,42 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { + if ( + ! $block_content || + ! isset( $block['attrs']['style']['elements'] ) || + ! wp_render_elements_support_required_for_block( $block ) + ) { + return $block_content; + } + + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } + + return $tags->get_updated_html(); +} + +/** + * Determines if a block needs to be flagged for elements support + * based on its specific attributes and general block type. + * + * @since 6.4.0 + * @access private + * + * @param array $block Block object. + * @return bool + */ +function wp_render_elements_support_required_for_block( $block ) { static $heading_elements = array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); - if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { - return $block_content; + /* + * Every block attribute which would require elements support is inside + * this path, so if the path itself doesn't exist there's no reason to + * perform any further checking. + */ + if ( ! isset( $block['attrs']['style']['elements'] ) ) { + return false; } $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); @@ -52,12 +84,7 @@ function wp_render_elements_support( $block_content, $block ) { isset( $button_attributes['background'] ) || isset( $button_attributes['gradient'] ) ) { - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } - - return $tags->get_updated_html(); + return true; } } @@ -70,12 +97,7 @@ function wp_render_elements_support( $block_content, $block ) { isset( $style_attributes['link']['color']['text'] ) || isset( $style_attributes['link'][':hover']['color']['text'] ) ) { - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } - - return $tags->get_updated_html(); + return true; } } @@ -93,17 +115,12 @@ function wp_render_elements_support( $block_content, $block ) { isset( $heading_attributes['background'] ) || isset( $heading_attributes['gradient'] ) ) { - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } - - return $tags->get_updated_html(); + return true; } } } - return $block_content; + return false; } /** From d9242efd0ba8d50b82225d8d0dd29aa4328751aa Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 6 Oct 2023 09:18:49 -0700 Subject: [PATCH 7/7] Revert attempts at further optimization which didn't demonstrate improvements. This reverts commit 0f7a1d527b273c6676f85937f8287db9dce5859c. --- src/wp-includes/block-supports/elements.php | 146 ++++++++++---------- 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 254680c5d4a05..770e3e95c30e1 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -31,96 +31,94 @@ function wp_get_elements_class_name( $block ) { * @return string Filtered block content. */ function wp_render_elements_support( $block_content, $block ) { - if ( - ! $block_content || - ! isset( $block['attrs']['style']['elements'] ) || - ! wp_render_elements_support_required_for_block( $block ) - ) { + if ( ! $block_content || ! isset( $block['attrs']['style']['elements'] ) ) { return $block_content; } - $tags = new WP_HTML_Tag_Processor( $block_content ); - if ( $tags->next_tag() ) { - $tags->add_class( wp_get_elements_class_name( $block ) ); - } - - return $tags->get_updated_html(); -} + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); -/** - * Determines if a block needs to be flagged for elements support - * based on its specific attributes and general block type. - * - * @since 6.4.0 - * @access private - * - * @param array $block Block object. - * @return bool - */ -function wp_render_elements_support_required_for_block( $block ) { - static $heading_elements = array( 'heading', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ); - - /* - * Every block attribute which would require elements support is inside - * this path, so if the path itself doesn't exist there's no reason to - * perform any further checking. - */ - if ( ! isset( $block['attrs']['style']['elements'] ) ) { - return false; - } - - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); - $style_attributes = $block['attrs']['style']['elements']; - - // Button element support. - $supports_button = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ); - $has_button_attrs = isset( $style_attributes['button']['color'] ); + $element_color_properties = array( + 'button' => array( + 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ), + 'paths' => array( + array( 'button', 'color', 'text' ), + array( 'button', 'color', 'background' ), + array( 'button', 'color', 'gradient' ), + ), + ), + 'link' => array( + 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ), + 'paths' => array( + array( 'link', 'color', 'text' ), + array( 'link', ':hover', 'color', 'text' ), + ), + ), + 'heading' => array( + 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ), + 'paths' => array( + array( 'heading', 'color', 'text' ), + array( 'heading', 'color', 'background' ), + array( 'heading', 'color', 'gradient' ), + array( 'h1', 'color', 'text' ), + array( 'h1', 'color', 'background' ), + array( 'h1', 'color', 'gradient' ), + array( 'h2', 'color', 'text' ), + array( 'h2', 'color', 'background' ), + array( 'h2', 'color', 'gradient' ), + array( 'h3', 'color', 'text' ), + array( 'h3', 'color', 'background' ), + array( 'h3', 'color', 'gradient' ), + array( 'h4', 'color', 'text' ), + array( 'h4', 'color', 'background' ), + array( 'h4', 'color', 'gradient' ), + array( 'h5', 'color', 'text' ), + array( 'h5', 'color', 'background' ), + array( 'h5', 'color', 'gradient' ), + array( 'h6', 'color', 'text' ), + array( 'h6', 'color', 'background' ), + array( 'h6', 'color', 'gradient' ), + ), + ), + ); - if ( $supports_button && $has_button_attrs ) { - $button_attributes = $style_attributes['button']['color']; + $skip_all_element_color_serialization = $element_color_properties['button']['skip'] && + $element_color_properties['link']['skip'] && + $element_color_properties['heading']['skip']; - if ( - isset( $button_attributes['text'] ) || - isset( $button_attributes['background'] ) || - isset( $button_attributes['gradient'] ) - ) { - return true; - } + if ( $skip_all_element_color_serialization ) { + return $block_content; } - // Link element support. - $supports_link = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ); - $has_link_attrs = isset( $style_attributes['link'] ); + $elements_style_attributes = $block['attrs']['style']['elements']; - if ( $supports_link && $has_link_attrs ) { - if ( - isset( $style_attributes['link']['color']['text'] ) || - isset( $style_attributes['link'][':hover']['color']['text'] ) - ) { - return true; + foreach ( $element_color_properties as $element_config ) { + if ( $element_config['skip'] ) { + continue; } - } - // Heading element support. - $supports_heading = ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ); - if ( $supports_heading ) { - foreach ( $heading_elements as $element_name ) { - if ( ! isset( $style_attributes[ $element_name ]['color'] ) ) { - continue; - } + foreach ( $element_config['paths'] as $path ) { + if ( null !== _wp_array_get( $elements_style_attributes, $path, null ) ) { + /* + * It only takes a single custom attribute to require that the custom + * class name be added to the block, so once one is found there's no + * need to continue looking for others. + * + * As is done with the layout hook, this code assumes that the block + * contains a single wrapper and that it's the first element in the + * rendered output. That first element, if it exists, gets the class. + */ + $tags = new WP_HTML_Tag_Processor( $block_content ); + if ( $tags->next_tag() ) { + $tags->add_class( wp_get_elements_class_name( $block ) ); + } - $heading_attributes = $style_attributes[ $element_name ]['color']; - if ( - isset( $heading_attributes['text'] ) || - isset( $heading_attributes['background'] ) || - isset( $heading_attributes['gradient'] ) - ) { - return true; + return $tags->get_updated_html(); } } } - return false; + // If no custom attributes were found then there's nothing to modify. + return $block_content; } /**