From 53ce3331c46a62e70217d48bf6cac0c152df519a Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Mon, 30 Jan 2023 17:10:27 +1100 Subject: [PATCH 01/11] Add theme json class changes to edit style variations --- src/wp-includes/class-wp-theme-json.php | 92 ++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 2e39b3c3530d2..1e3df5e16a810 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -665,9 +665,17 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_styles_blocks = array(); $schema_settings_blocks = array(); foreach ( $valid_block_names as $block ) { - $schema_settings_blocks[ $block ] = static::VALID_SETTINGS; - $schema_styles_blocks[ $block ] = $styles_non_top_level; - $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; + // Build the schema for each block style variation. + $style_variation_names = isset( $input['styles']['blocks'][ $block ]['variations'] ) ? array_keys( $input['styles']['blocks'][ $block ]['variations'] ) : array(); + $schema_styles_variations = array(); + if ( ! empty( $style_variation_names ) ) { + $schema_styles_variations = array_fill_keys( $style_variation_names, $styles_non_top_level ); + } + + $schema_settings_blocks[ $block ] = static::VALID_SETTINGS; + $schema_styles_blocks[ $block ] = $styles_non_top_level; + $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; + $schema_styles_blocks[ $block ]['variations'] = $schema_styles_variations; } $schema['styles'] = static::VALID_STYLES; @@ -814,6 +822,15 @@ protected static function get_blocks_metadata() { } static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector ); } + // If the block has style variations, append their selectors to the block metadata. + if ( ! empty( $block_type->styles ) ) { + $style_selectors = array(); + foreach ( $block_type->styles as $style ) { + // The style variation classname is duplicated in the selector to ensure that it overrides core block styles. + $style_selectors[ $style['name'] ] = static::append_to_selector( '.is-style-' . $style['name'] . '.is-style-' . $style['name'], static::$blocks_metadata[ $block_name ]['selector'] ); + } + static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors; + } } return static::$blocks_metadata; @@ -2039,12 +2056,23 @@ private static function get_block_nodes( $theme_json ) { $feature_selectors = $selectors[ $name ]['features']; } + $variation_selectors = array(); + if ( isset( $node['variations'] ) ) { + foreach ( $node['variations'] as $variation => $node ) { + $variation_selectors[] = array( + 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ), + 'selector' => $selectors[ $name ]['styleVariations'][ $variation ], + ); + } + } + $nodes[] = array( - 'name' => $name, - 'path' => array( 'styles', 'blocks', $name ), - 'selector' => $selector, - 'duotone' => $duotone_selector, - 'features' => $feature_selectors, + 'name' => $name, + 'path' => array( 'styles', 'blocks', $name ), + 'selector' => $selector, + 'duotone' => $duotone_selector, + 'features' => $feature_selectors, + 'variations' => $variation_selectors, ); if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { @@ -2124,6 +2152,48 @@ public function get_styles_for_block( $block_metadata ) { } } + // If there are style variations, generate the declarations for them, including any feature selectors the block may have. + $style_variation_declarations = array(); + if ( ! empty( $block_metadata['variations'] ) ) { + foreach ( $block_metadata['variations'] as $style_variation ) { + $style_variation_node = _wp_array_get( $this->theme_json, $style_variation['path'], array() ); + $style_variation_selector = $style_variation['selector']; + + // If the block has feature selectors, generate the declarations for them within the current style variation. + if ( ! empty( $block_metadata['features'] ) ) { + foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { + if ( ! empty( $style_variation_node[ $feature_name ] ) ) { + // Prepend the variation selector to the feature selector. + $split_feature_selectors = explode( ',', $feature_selector ); + $feature_selectors = array_map( + function( $split_feature_selector ) use ( $style_variation_selector ) { + return trim( $style_variation_selector ) . trim( $split_feature_selector ); + }, + $split_feature_selectors + ); + $combined_feature_selectors = implode( ',', $feature_selectors ); + + // Compute declarations for the feature. + $new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json ); + + // Merge new declarations with any that already exist for + // the feature selector. This may occur when multiple block + // support features use the same custom selector. + if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) { + $style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations ); + } else { + $style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations; + } + // Remove the feature from the variation's node now the + // styles will be included under the feature level selector. + unset( $style_variation_node[ $feature_name ] ); + } + } + } + // Compute declarations for remaining styles not covered by feature level selectors. + $style_variation_declarations[ $style_variation_selector ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); + } + } /* * Get a reference to element name from path. * $block_metadata['path'] = array( 'styles','elements','link' ); @@ -2214,6 +2284,12 @@ function( $pseudo_selector ) use ( $selector ) { $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); } + // 6. Generate and append the style variation rulesets. + foreach ( $style_variation_declarations as $style_variation_selector => $individual_style_variation_declarations ) { + $block_rules .= static::to_ruleset( $style_variation_selector, $individual_style_variation_declarations ); + } + + return $block_rules; } From c99daceaf16f4ad7b98e3bf28aebbac7c1e53d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:01:23 +0100 Subject: [PATCH 02/11] Add test for style variations --- tests/phpunit/tests/theme/wpThemeJson.php | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 3f174b5d05cbe..ea5f67e3c9a0c 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3595,6 +3595,66 @@ public function test_get_styles_for_block_with_content_width() { $this->assertSame( $expected, $root_rules . $style_rules ); } + /** + * @ticket 57583 + */ + public function test_get_styles_for_block_with_style_variations() { + $theme_json = new WP_Theme_JSON( + array( + 'version' => 2, + 'styles' => array( + 'blocks' => array( + 'core/quote' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + 'unregisteredProperty' => 'value' + ), + ), + ), + ), + ), + ), + ); + + // Validate structure is sanitized. + $expected_theme_json = array( + 'version' => 2, + 'styles' => array( + 'blocks' => array( + 'core/quote' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + ), + ), + ), + ), + ), + ); + $sanitized_theme_json = $theme_json->get_raw_data(); + $this->assertEqualSetsWithIndex( $expected_theme_json, $sanitized_theme_json ); + + // Validate styles are generated properly. + $metadata = array( + 'path' => array( 'styles', 'blocks', 'core/quote' ), + 'selector' => '.wp-block-quote', + 'variations' => array( + array( + 'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'plain' ), + 'selector' => '.is-style-plain .is-style-plain .wp-block-quote', + ), + ), + ); + $expected_styles = '.is-style-plain .is-style-plain .wp-block-quote{background-color: hotpink;}'; + $actual_styles = $theme_json->get_styles_for_block( $metadata ); + $this->assertSame( $expected_styles, $actual_styles ); + } + /** * @ticket 56611 */ From 2c58b474a3cfcb8c5ca900f83b3bcc4c845e57ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Mon, 30 Jan 2023 16:40:21 +0100 Subject: [PATCH 03/11] fixup --- tests/phpunit/tests/theme/wpThemeJson.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index ea5f67e3c9a0c..08a47c2d9a5fc 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3646,7 +3646,7 @@ public function test_get_styles_for_block_with_style_variations() { 'variations' => array( array( 'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'plain' ), - 'selector' => '.is-style-plain .is-style-plain .wp-block-quote', + 'selector' => '.is-style-plain.is-style-plain.wp-block-quote', ), ), ); From fe1353cbad552b128c41c951826e2f43a879f258 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 31 Jan 2023 11:19:12 +1100 Subject: [PATCH 04/11] Fix test output styles. --- tests/phpunit/tests/theme/wpThemeJson.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 08a47c2d9a5fc..41fbb31f641b9 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3650,7 +3650,7 @@ public function test_get_styles_for_block_with_style_variations() { ), ), ); - $expected_styles = '.is-style-plain .is-style-plain .wp-block-quote{background-color: hotpink;}'; + $expected_styles = '.is-style-plain.is-style-plain.wp-block-quote{background-color: hotpink;}'; $actual_styles = $theme_json->get_styles_for_block( $metadata ); $this->assertSame( $expected_styles, $actual_styles ); } From 80ad395ba44dd1e090ae6c74f17129fd3e57bc19 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 31 Jan 2023 11:50:07 +1100 Subject: [PATCH 05/11] Fix lint errors. --- src/wp-includes/class-wp-theme-json.php | 1 - tests/phpunit/tests/theme/wpThemeJson.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 1e3df5e16a810..099c3dcbccb3a 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -2289,7 +2289,6 @@ function( $pseudo_selector ) use ( $selector ) { $block_rules .= static::to_ruleset( $style_variation_selector, $individual_style_variation_declarations ); } - return $block_rules; } diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 41fbb31f641b9..af0b074eb2131 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3610,7 +3610,7 @@ public function test_get_styles_for_block_with_style_variations() { 'color' => array( 'background' => 'hotpink', ), - 'unregisteredProperty' => 'value' + 'unregisteredProperty' => 'value', ), ), ), From 1f232da82eab0e58f136a4a2ce3ae9799ef43f99 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 31 Jan 2023 11:58:28 +1100 Subject: [PATCH 06/11] Fix spacing in test file. --- tests/phpunit/tests/theme/wpThemeJson.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index af0b074eb2131..98e31057a6959 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3601,13 +3601,13 @@ public function test_get_styles_for_block_with_content_width() { public function test_get_styles_for_block_with_style_variations() { $theme_json = new WP_Theme_JSON( array( - 'version' => 2, - 'styles' => array( + 'version' => 2, + 'styles' => array( 'blocks' => array( 'core/quote' => array( 'variations' => array( 'plain' => array( - 'color' => array( + 'color' => array( 'background' => 'hotpink', ), 'unregisteredProperty' => 'value', @@ -3621,8 +3621,8 @@ public function test_get_styles_for_block_with_style_variations() { // Validate structure is sanitized. $expected_theme_json = array( - 'version' => 2, - 'styles' => array( + 'version' => 2, + 'styles' => array( 'blocks' => array( 'core/quote' => array( 'variations' => array( @@ -3640,7 +3640,7 @@ public function test_get_styles_for_block_with_style_variations() { $this->assertEqualSetsWithIndex( $expected_theme_json, $sanitized_theme_json ); // Validate styles are generated properly. - $metadata = array( + $metadata = array( 'path' => array( 'styles', 'blocks', 'core/quote' ), 'selector' => '.wp-block-quote', 'variations' => array( From 159a3e32cd57b53047e374ae85c0f3f9c2f27cc1 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Tue, 31 Jan 2023 13:28:58 -0600 Subject: [PATCH 07/11] Remove trailing comma to resolve parsing error in < PHP 7.3. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André <583546+oandregal@users.noreply.github.com> --- tests/phpunit/tests/theme/wpThemeJson.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 98e31057a6959..0a44486a19793 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3616,7 +3616,7 @@ public function test_get_styles_for_block_with_style_variations() { ), ), ), - ), + ) ); // Validate structure is sanitized. From fc4a2a25a7497dd533b64e509d5a2d2cccad8ab7 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Tue, 31 Jan 2023 15:05:13 -0600 Subject: [PATCH 08/11] Tests: split and use data provider Splits the tests into 2 separate tests. Adds a data provider for each. Adds more datasets. --- tests/phpunit/tests/theme/wpThemeJson.php | 246 +++++++++++++++++++--- 1 file changed, 220 insertions(+), 26 deletions(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 0a44486a19793..53c94d4813af3 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3597,38 +3597,120 @@ public function test_get_styles_for_block_with_content_width() { /** * @ticket 57583 + * + * @dataProvider data_sanitize_for_block_with_style_variations + * + * @param array $theme_json_variations Theme.json variations to test. + * @param array $expected_sanitized Expected results after sanitizing. */ - public function test_get_styles_for_block_with_style_variations() { + public function test_sanitize_for_block_with_style_variations( $theme_json_variations, $expected_sanitized ) { $theme_json = new WP_Theme_JSON( array( 'version' => 2, 'styles' => array( + 'blocks' => array( + 'core/quote' => $theme_json_variations, + ), + ), + ) + ); + + // Validate structure is sanitized. + $sanitized_theme_json = $theme_json->get_raw_data(); + $this->assertIsArray( $sanitized_theme_json, 'Sanitized theme.json is not an array data type' ); + $this->assertArrayHasKey( 'styles', $sanitized_theme_json, 'Sanitized theme.json does not have an "styles" key' ); + $this->assertSameSetsWithIndex( $expected_sanitized, $sanitized_theme_json['styles'], 'Sanitized theme.json styles does not match' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_sanitize_for_block_with_style_variations() { + return array( + '1 variation with 1 invalid property' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + ), + ), + ), + 'expected_sanitized' => array( 'blocks' => array( 'core/quote' => array( 'variations' => array( 'plain' => array( - 'color' => array( + 'color' => array( 'background' => 'hotpink', ), - 'unregisteredProperty' => 'value', ), ), ), ), ), - ) - ); - - // Validate structure is sanitized. - $expected_theme_json = array( - 'version' => 2, - 'styles' => array( - 'blocks' => array( - 'core/quote' => array( - 'variations' => array( - 'plain' => array( - 'color' => array( - 'background' => 'hotpink', + ), + '1 variation with 2 invalid properties' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + 'invalidProperty1' => 'value1', + 'invalidProperty2' => 'value2', + ), + ), + ), + 'expected_sanitized' => array( + 'blocks' => array( + 'core/quote' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + ), + ), + ), + ), + ), + ), + '2 variations with 1 invalid property' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + 'invalidProperty1' => 'value1', + ), + 'basic' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => '#000000', + ), + 'foo' => 'bar', + ), + ), + ), + 'expected_sanitized' => array( + 'blocks' => array( + 'core/quote' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + ), + 'basic' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => '#000000', + ), ), ), ), @@ -3636,23 +3718,135 @@ public function test_get_styles_for_block_with_style_variations() { ), ), ); - $sanitized_theme_json = $theme_json->get_raw_data(); - $this->assertEqualSetsWithIndex( $expected_theme_json, $sanitized_theme_json ); + } + + /** + * @ticket 57583 + * + * @dataProvider data_get_styles_for_block_with_style_variations + * + * @param array $theme_json_variations Theme.json variations to test. + * @param string $metadata_variations Style variations to test. + * @param string $expected Expected results for styling. + */ + public function test_get_styles_for_block_with_style_variations( $theme_json_variations, $metadata_variations, $expected ) { + $theme_json = new WP_Theme_JSON( + array( + 'version' => 2, + 'styles' => array( + 'blocks' => array( + 'core/quote' => $theme_json_variations, + ), + ), + ) + ); // Validate styles are generated properly. - $metadata = array( + $metadata = array( 'path' => array( 'styles', 'blocks', 'core/quote' ), 'selector' => '.wp-block-quote', - 'variations' => array( - array( - 'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'plain' ), - 'selector' => '.is-style-plain.is-style-plain.wp-block-quote', + 'variations' => $metadata_variations, + ); + $actual_styles = $theme_json->get_styles_for_block( $metadata ); + $this->assertSame( $expected, $actual_styles ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_styles_for_block_with_style_variations() { + $plain = array( + 'metadata' => array( + 'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'plain' ), + 'selector' => '.is-style-plain.is-style-plain.wp-block-quote', + ), + 'styles' => '.is-style-plain.is-style-plain.wp-block-quote{background-color: hotpink;}', + ); + $basic = array( + 'metadata' => array( + 'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'basic' ), + 'selector' => '.is-style-basic.is-style-basic.wp-block-quote', + ), + 'styles' => '.is-style-basic.is-style-basic.wp-block-quote{background-color: #ffffff;color: #000000;}', + ); + + return array( + '1 variation with 1 invalid property' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + ), + ), + ), + 'metadata_variation' => array( $plain['metadata'] ), + 'expected' => $plain['styles'], + ), + '1 variation with 2 invalid properties' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + 'invalidProperty1' => 'value1', + 'invalidProperty2' => 'value2', + ), + ), + ), + 'metadata_variation' => array( $plain['metadata'] ), + 'expected' => $plain['styles'], + ), + '2 variations with 1 invalid property' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + 'invalidProperty1' => 'value1', + ), + 'basic' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => '#000000', + ), + 'foo' => 'bar', + ), + ), + ), + 'metadata_variation' => array( $plain['metadata'], $basic['metadata'] ), + 'expected_styles' => $plain['styles'] . $basic['styles'], + ), + '2 variations with multiple invalid properties' => array( + 'theme_json_variations' => array( + 'variations' => array( + 'plain' => array( + 'color' => array( + 'background' => 'hotpink', + ), + 'invalidProperty1' => 'value1', + 'invalidProperty2' => 'value2', + ), + 'basic' => array( + 'foo' => 'foo', + 'color' => array( + 'background' => '#ffffff', + 'text' => '#000000', + ), + 'bar' => 'bar', + 'baz' => 'baz', + ), + ), ), + 'metadata_variation' => array( $plain['metadata'], $basic['metadata'] ), + 'expected_styles' => $plain['styles'] . $basic['styles'], ), ); - $expected_styles = '.is-style-plain.is-style-plain.wp-block-quote{background-color: hotpink;}'; - $actual_styles = $theme_json->get_styles_for_block( $metadata ); - $this->assertSame( $expected_styles, $actual_styles ); } /** From 86ac232dd9f489a221770a9cdb35b0b4b07a1c7d Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Tue, 31 Jan 2023 15:34:09 -0600 Subject: [PATCH 09/11] Guard for non-array variations --- src/wp-includes/class-wp-theme-json.php | 9 ++++- tests/phpunit/tests/theme/wpThemeJson.php | 46 +++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 099c3dcbccb3a..d8a2c32fba914 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -666,7 +666,14 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_settings_blocks = array(); foreach ( $valid_block_names as $block ) { // Build the schema for each block style variation. - $style_variation_names = isset( $input['styles']['blocks'][ $block ]['variations'] ) ? array_keys( $input['styles']['blocks'][ $block ]['variations'] ) : array(); + $style_variation_names = array(); + if ( + ! empty( $input['styles']['blocks'][ $block ]['variations'] ) && + is_array( $input['styles']['blocks'][ $block ]['variations'] ) + ) { + $style_variation_names = array_keys( $input['styles']['blocks'][ $block ]['variations'] ); + } + $schema_styles_variations = array(); if ( ! empty( $style_variation_names ) ) { $schema_styles_variations = array_fill_keys( $style_variation_names, $styles_non_top_level ); diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 53c94d4813af3..a030506ec4b0d 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3720,6 +3720,52 @@ public function data_sanitize_for_block_with_style_variations() { ); } + /** + * @ticket 57583 + * + * @dataProvider data_sanitize_with_invalid_style_variation + * + * @param array $theme_json_variations The theme.json variations to test. + */ + public function test_sanitize_with_invalid_style_variation( $theme_json_variations ) { + $theme_json = new WP_Theme_JSON( + array( + 'version' => 2, + 'styles' => array( + 'blocks' => array( + 'core/quote' => $theme_json_variations, + ), + ), + ) + ); + + // Validate structure is sanitized. + $sanitized_theme_json = $theme_json->get_raw_data(); + $this->assertIsArray( $sanitized_theme_json, 'Sanitized theme.json is not an array data type' ); + $this->assertArrayNotHasKey( 'styles', $sanitized_theme_json, 'Sanitized theme.json should not have a "styles" key' ); + + } + + /** + * Data provider. + * + * @return array + */ + public function data_sanitize_with_invalid_style_variation() { + return array( + 'empty string variation' => array( + array( + 'variations' => '', + ), + ), + 'boolean variation' => array( + array( + 'variations' => false, + ), + ), + ); + } + /** * @ticket 57583 * From 994c20294df6ea20c57354b309fbcee48485eb9e Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Tue, 31 Jan 2023 15:59:48 -0600 Subject: [PATCH 10/11] Micro-optimizations in ::get_styles_for_block() --- src/wp-includes/class-wp-theme-json.php | 52 +++++++++++++------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index d8a2c32fba914..e8666022f2c59 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -2168,33 +2168,35 @@ public function get_styles_for_block( $block_metadata ) { // If the block has feature selectors, generate the declarations for them within the current style variation. if ( ! empty( $block_metadata['features'] ) ) { + $clean_style_variation_selector = trim( $style_variation_selector ); foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { - if ( ! empty( $style_variation_node[ $feature_name ] ) ) { - // Prepend the variation selector to the feature selector. - $split_feature_selectors = explode( ',', $feature_selector ); - $feature_selectors = array_map( - function( $split_feature_selector ) use ( $style_variation_selector ) { - return trim( $style_variation_selector ) . trim( $split_feature_selector ); - }, - $split_feature_selectors - ); - $combined_feature_selectors = implode( ',', $feature_selectors ); - - // Compute declarations for the feature. - $new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json ); - - // Merge new declarations with any that already exist for - // the feature selector. This may occur when multiple block - // support features use the same custom selector. - if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) { - $style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations ); - } else { - $style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations; - } - // Remove the feature from the variation's node now the - // styles will be included under the feature level selector. - unset( $style_variation_node[ $feature_name ] ); + if ( empty( $style_variation_node[ $feature_name ] ) ) { + continue; + } + // Prepend the variation selector to the feature selector. + $split_feature_selectors = explode( ',', $feature_selector ); + $feature_selectors = array_map( + static function( $split_feature_selector ) use ( $clean_style_variation_selector ) { + return $clean_style_variation_selector . trim( $split_feature_selector ); + }, + $split_feature_selectors + ); + $combined_feature_selectors = implode( ',', $feature_selectors ); + + // Compute declarations for the feature. + $new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json ); + + // Merge new declarations with any that already exist for + // the feature selector. This may occur when multiple block + // support features use the same custom selector. + if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) { + $style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations ); + } else { + $style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations; } + // Remove the feature from the variation's node now the + // styles will be included under the feature level selector. + unset( $style_variation_node[ $feature_name ] ); } } // Compute declarations for remaining styles not covered by feature level selectors. From 0768aa8d5f8ee902bdc8d0ded9f326eb56ab6376 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Tue, 31 Jan 2023 16:02:41 -0600 Subject: [PATCH 11/11] Formatting for multiline comments --- src/wp-includes/class-wp-theme-json.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index e8666022f2c59..1f03a675c51da 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -2186,16 +2186,20 @@ static function( $split_feature_selector ) use ( $clean_style_variation_selector // Compute declarations for the feature. $new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json ); - // Merge new declarations with any that already exist for - // the feature selector. This may occur when multiple block - // support features use the same custom selector. + /* + * Merge new declarations with any that already exist for + * the feature selector. This may occur when multiple block + * support features use the same custom selector. + */ if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) { $style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations ); } else { $style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations; } - // Remove the feature from the variation's node now the - // styles will be included under the feature level selector. + /* + * Remove the feature from the variation's node now the + * styles will be included under the feature level selector. + */ unset( $style_variation_node[ $feature_name ] ); } }