Commit 7ad71ce4112 for woocommerce

commit 7ad71ce4112fb30c1b3a02594945f137e1d0fc79
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Mon Sep 7 16:23:11 2026 +0200

    Optimize StyleAttributesUtils so unused styles aren't computed (#68345)

    * Add changelog

    * Optimize StyleAttributesUtils so unused styles aren't computed

    * Fix method return type

    * Simplify get_align_class_and_style() method

    * Harden checks

    * Simplify checks

    * Use call_user_func() to call the methods dynamically

    * Update PHPStan

    * Add tests to StyleAttributesUtils

    * Cover the selected-and-excluded overlap

diff --git a/plugins/woocommerce/changelog/fix-style-attributes-utils-optimizations b/plugins/woocommerce/changelog/fix-style-attributes-utils-optimizations
new file mode 100644
index 00000000000..48009f8ac07
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-style-attributes-utils-optimizations
@@ -0,0 +1,5 @@
+Significance: patch
+Type: performance
+Comment: Optimize StyleAttributesUtils so unused styles aren't computed
+
+
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 594918b0b8a..b0323bab2f7 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -54039,12 +54039,6 @@ parameters:
 			count: 1
 			path: src/Blocks/Utils/StyleAttributesUtils.php

-		-
-			message: '#^Method Automattic\\WooCommerce\\Blocks\\Utils\\StyleAttributesUtils\:\:join_styles\(\) should return array but returns string\.$#'
-			identifier: return.type
-			count: 1
-			path: src/Blocks/Utils/StyleAttributesUtils.php
-
 		-
 			message: '#^Parameter \#1 \$version1 of function version_compare expects string, string\|null given\.$#'
 			identifier: argument.type
diff --git a/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php b/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php
index 8314c0c964d..8ce4df2330b 100644
--- a/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php
+++ b/plugins/woocommerce/src/Blocks/Utils/StyleAttributesUtils.php
@@ -88,37 +88,9 @@ class StyleAttributesUtils {
 	public static function get_align_class_and_style( $attributes ) {
 		$align_attribute = $attributes['align'] ?? null;

-		if ( 'wide' === $align_attribute ) {
+		if ( in_array( $align_attribute, array( 'wide', 'full', 'left', 'right', 'center' ), true ) ) {
 			return array(
-				'class' => 'alignwide',
-				'style' => null,
-			);
-		}
-
-		if ( 'full' === $align_attribute ) {
-			return array(
-				'class' => 'alignfull',
-				'style' => null,
-			);
-		}
-
-		if ( 'left' === $align_attribute ) {
-			return array(
-				'class' => 'alignleft',
-				'style' => null,
-			);
-		}
-
-		if ( 'right' === $align_attribute ) {
-			return array(
-				'class' => 'alignright',
-				'style' => null,
-			);
-		}
-
-		if ( 'center' === $align_attribute ) {
-			return array(
-				'class' => 'aligncenter',
+				'class' => 'align' . $align_attribute,
 				'style' => null,
 			);
 		}
@@ -169,7 +141,7 @@ class StyleAttributesUtils {
 	 * Join classes and styles while removing duplicates and null values.
 	 *
 	 * @param array $rules Array of classes or styles.
-	 * @return array
+	 * @return string
 	 */
 	protected static function join_styles( $rules ) {
 		return implode( ' ', array_unique( array_filter( $rules ) ) );
@@ -274,13 +246,13 @@ class StyleAttributesUtils {
 	public static function get_border_width_class_and_style( $attributes ) {
 		$custom_border = $attributes['style']['border'] ?? '';

-		if ( '' === $custom_border ) {
+		if ( ! is_array( $custom_border ) ) {
 			return self::EMPTY_STYLE;
 		}

 		$style = '';

-		if ( array_key_exists( 'width', ( $custom_border ) ) && ! empty( $custom_border['width'] ) ) {
+		if ( ! empty( $custom_border['width'] ) ) {
 			// Linked sides.
 			$style = 'border-width:' . $custom_border['width'] . ';';
 		} else {
@@ -307,13 +279,13 @@ class StyleAttributesUtils {
 	public static function get_border_style_class_and_style( $attributes ) {
 		$custom_border = $attributes['style']['border'] ?? '';

-		if ( '' === $custom_border ) {
+		if ( ! is_array( $custom_border ) ) {
 			return self::EMPTY_STYLE;
 		}

 		$style = '';

-		if ( array_key_exists( 'style', ( $custom_border ) ) && ! empty( $custom_border['style'] ) ) {
+		if ( ! empty( $custom_border['style'] ) ) {
 			$style = 'border-style:' . $custom_border['style'] . ';';
 		} else {
 			foreach ( $custom_border as $side => $value ) {
@@ -374,16 +346,14 @@ class StyleAttributesUtils {

 		$custom_font_size = $attributes['style']['typography']['fontSize'] ?? '';

-		if ( ! $font_size && '' === $custom_font_size ) {
-			return self::EMPTY_STYLE;
-		}
-
 		if ( $font_size ) {
 			return array(
 				'class' => sprintf( 'has-font-size has-%s-font-size', $font_size ),
 				'style' => null,
 			);
-		} elseif ( '' !== $custom_font_size ) {
+		}
+
+		if ( '' !== $custom_font_size ) {
 			return array(
 				'class' => null,
 				'style' => sprintf( 'font-size: %s;', $custom_font_size ),
@@ -663,17 +633,15 @@ class StyleAttributesUtils {

 		$custom_text_color = $attributes['style']['color']['text'] ?? '';

-		if ( ! $text_color && ! $custom_text_color ) {
-			return self::EMPTY_STYLE;
-		}
-
 		if ( $text_color ) {
 			return array(
 				'class' => sprintf( 'has-text-color has-%s-color', $text_color ),
 				'style' => null,
 				'value' => self::get_preset_value( $text_color ),
 			);
-		} elseif ( $custom_text_color ) {
+		}
+
+		if ( $custom_text_color ) {
 			return array(
 				'class' => null,
 				'style' => sprintf( 'color: %s;', $custom_text_color ),
@@ -754,43 +722,40 @@ class StyleAttributesUtils {
 	 * @return array
 	 */
 	public static function get_classes_and_styles_by_attributes( $attributes, $properties = array(), $exclude = array() ) {
-		$classes_and_styles = array(
-			'align'            => self::get_align_class_and_style( $attributes ),
-			'background_color' => self::get_background_color_class_and_style( $attributes ),
-			'border_color'     => self::get_border_color_class_and_style( $attributes ),
-			'border_radius'    => self::get_border_radius_class_and_style( $attributes ),
-			'border_width'     => self::get_border_width_class_and_style( $attributes ),
-			'border_style'     => self::get_border_style_class_and_style( $attributes ),
-			'font_family'      => self::get_font_family_class_and_style( $attributes ),
-			'font_size'        => self::get_font_size_class_and_style( $attributes ),
-			'font_style'       => self::get_font_style_class_and_style( $attributes ),
-			'font_weight'      => self::get_font_weight_class_and_style( $attributes ),
-			'letter_spacing'   => self::get_letter_spacing_class_and_style( $attributes ),
-			'line_height'      => self::get_line_height_class_and_style( $attributes ),
-			'margin'           => self::get_margin_class_and_style( $attributes ),
-			'padding'          => self::get_padding_class_and_style( $attributes ),
-			'shadow'           => self::get_shadow_class_and_style( $attributes ),
-			'text_align'       => self::get_text_align_class_and_style( $attributes ),
-			'text_color'       => self::get_text_color_class_and_style( $attributes ),
-			'text_decoration'  => self::get_text_decoration_class_and_style( $attributes ),
-			'text_transform'   => self::get_text_transform_class_and_style( $attributes ),
-			'extra_classes'    => self::get_classes_from_attributes( $attributes ),
+		$class_style_map = array(
+			'align'            => 'get_align_class_and_style',
+			'background_color' => 'get_background_color_class_and_style',
+			'border_color'     => 'get_border_color_class_and_style',
+			'border_radius'    => 'get_border_radius_class_and_style',
+			'border_width'     => 'get_border_width_class_and_style',
+			'border_style'     => 'get_border_style_class_and_style',
+			'font_family'      => 'get_font_family_class_and_style',
+			'font_size'        => 'get_font_size_class_and_style',
+			'font_style'       => 'get_font_style_class_and_style',
+			'font_weight'      => 'get_font_weight_class_and_style',
+			'letter_spacing'   => 'get_letter_spacing_class_and_style',
+			'line_height'      => 'get_line_height_class_and_style',
+			'margin'           => 'get_margin_class_and_style',
+			'padding'          => 'get_padding_class_and_style',
+			'shadow'           => 'get_shadow_class_and_style',
+			'text_align'       => 'get_text_align_class_and_style',
+			'text_color'       => 'get_text_color_class_and_style',
+			'text_decoration'  => 'get_text_decoration_class_and_style',
+			'text_transform'   => 'get_text_transform_class_and_style',
+			'extra_classes'    => 'get_classes_from_attributes',
 		);

 		if ( ! empty( $properties ) ) {
-			foreach ( $classes_and_styles as $key => $value ) {
-				if ( ! in_array( $key, $properties, true ) ) {
-					unset( $classes_and_styles[ $key ] );
-				}
-			}
+			$class_style_map = array_intersect_key( $class_style_map, array_flip( $properties ) );
 		}

 		if ( ! empty( $exclude ) ) {
-			foreach ( $classes_and_styles as $key => $value ) {
-				if ( in_array( $key, $exclude, true ) ) {
-					unset( $classes_and_styles[ $key ] );
-				}
-			}
+			$class_style_map = array_diff_key( $class_style_map, array_flip( $exclude ) );
+		}
+
+		$classes_and_styles = array();
+		foreach ( $class_style_map as $key => $method ) {
+			$classes_and_styles[ $key ] = call_user_func( array( self::class, $method ), $attributes );
 		}

 		$classes_and_styles = array_filter( $classes_and_styles );
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Utils/StyleAttributesUtilsTest.php b/plugins/woocommerce/tests/php/src/Blocks/Utils/StyleAttributesUtilsTest.php
new file mode 100644
index 00000000000..3cd8e256647
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Blocks/Utils/StyleAttributesUtilsTest.php
@@ -0,0 +1,60 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Blocks\Utils;
+
+use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils;
+use WC_Unit_Test_Case;
+
+/**
+ * Tests for StyleAttributesUtils.
+ */
+class StyleAttributesUtilsTest extends WC_Unit_Test_Case {
+
+	/**
+	 * @testdox get_classes_and_styles_by_attributes() includes or excludes the requested handlers.
+	 *
+	 * @dataProvider provider_style_handler_dispatch
+	 *
+	 * @param array<int, string> $properties       Properties to request.
+	 * @param array<int, string> $exclude          Properties to exclude.
+	 * @param string             $expected_classes Expected class output.
+	 * @param string             $expected_styles  Expected style output.
+	 */
+	public function test_get_classes_and_styles_by_attributes_dispatches_expected_handlers( array $properties, array $exclude, string $expected_classes, string $expected_styles ): void {
+		$attributes = array(
+			'className' => 'extra',
+			'fontSize'  => 'large',
+			'textColor' => 'vivid-red',
+			'style'     => array(
+				'spacing' => array(
+					'padding' => array(
+						'top' => '8px',
+					),
+				),
+			),
+		);
+
+		$result = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes, $properties, $exclude );
+
+		$this->assertSame( $expected_classes, $result['classes'] );
+		$this->assertSame( $expected_styles, $result['styles'] );
+	}
+
+	/**
+	 * @return array<string, array{array<int, string>, array<int, string>, string, string}>
+	 */
+	public function provider_style_handler_dispatch(): array {
+		$all_classes = 'has-font-size has-large-font-size has-text-color has-vivid-red-color extra';
+		$padding     = 'padding-top:8px;';
+
+		return array(
+			'empty properties'                 => array( array(), array(), $all_classes, $padding ),
+			'requested properties'             => array( array( 'font_size', 'extra_classes' ), array(), 'has-font-size has-large-font-size extra', '' ),
+			'exclude extra_classes'            => array( array(), array( 'extra_classes' ), 'has-font-size has-large-font-size has-text-color has-vivid-red-color', $padding ),
+			'exclude font_size'                => array( array(), array( 'font_size' ), 'has-text-color has-vivid-red-color extra', $padding ),
+			'exclude padding'                  => array( array(), array( 'padding' ), $all_classes, '' ),
+			'requested and excluded font_size' => array( array( 'font_size' ), array( 'font_size' ), '', '' ),
+		);
+	}
+}