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
47 changes: 47 additions & 0 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -1925,6 +1925,48 @@ public function get_styles_block_nodes() {
return static::get_block_nodes( $this->theme_json );
}

/**
* Returns a filtered declarations array if there is a separator block with only a background
* style defined in theme.json by adding a color attribute to reflect the changes in the front.
*
* @since 6.1.1
*
* @param array $declarations List of declarations.
* @return array $declarations List of declarations filtered.
*/
private static function update_separator_declarations( $declarations ) {
$background_matches = array_values(
array_filter(
$declarations,
static function( $declaration ) {
return 'background-color' === $declaration['name'];
}
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I committed something similar a few months back, and learned that array_filter goes over every item in the array. Refactoring these as foreachs with a break after the first found instance is a nice little optimization.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm there are multiple array_filter()s iterating over the same dataset. This means the entire dataset is iterated each and every array_filter() in this method.

Combining all of the array_filter() circuits into one iterator, such as foreach(), means the $declarations array is walked 1 time instead of N times (N = each array_filter()).

Granted, this means refactoring and re-testing the code to be more performant. Likely not something that can happen before 6.1.1-RC.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hellofromtonya I've refactored this to match your suggestions(at least as best as I understand them).

How does this look to you now? Any further improvements we can make?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dream-encode I agree with @hellofromtonya, I think we can avoid using array_filter() here entirely and rather use a single foreach. Worth adding that also array_values() has a cost which we can avoid as well.

Given that #3555 is focused on optimizing performance by reducing excessive usage of array functions, it would feel counterproductive if we at the same time introduced new ones here.

IMO we can even have only a single foreach loop for the entire method, iterating through $declarations and checking for all three things (the first background-color value, whether there are border-color matches, and whether there are color matches).


if ( isset( $background_matches[0]['value'] ) ) {
$border_color_matches = false;
$text_color_matches = false;

foreach ( $declarations as $declaration ) {
if ( 'border-color' === $declaration['name'] ) {
$border_color_matches = true;
} elseif ( 'color' === $declaration['name'] ) {
$text_color_matches = true;
}
}
Comment on lines +1951 to +1957
Copy link
Contributor

@hellofromtonya hellofromtonya Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I look at the code, I see what you were saying @dream-encode:

In the foreach, once it finds a single true, then it can break out of the foreach.

Why? The next conditional is checking if both are false. As soon as one of the variables is true, then that conditional will not execution. So no need to keep iterating in the foreach.

My suggestion:

Add a break to each:

foreach ( $declarations as $declaration ) {
	if ( 'border-color' === $declaration['name'] ) {
		$border_color_matches = true;
		break;
	} elseif ( 'color' === $declaration['name'] ) {
		$text_color_matches = true;
		break;
	}
}

What do you think @dream-encode ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a break in both would not work properly since we need to absolutely determine if both are true. Line 1959 check both. If we bail on the first one found, the other may yet still exist in the array. However, @felixarntz suggestion to check if both have already been found then break earlier is good.


if ( ! $border_color_matches && ! $text_color_matches ) {
$declarations[] = array(
'name' => 'color',
'value' => $background_matches[0]['value'],
);
}
}

return $declarations;
Comment on lines +1938 to +1967
Copy link
Member

@felixarntz felixarntz Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dream-encode @hellofromtonya This is what I had in mind in regards to my comment above:

Suggested change
$background_matches = array_values(
array_filter(
$declarations,
static function( $declaration ) {
return 'background-color' === $declaration['name'];
}
)
);
if ( isset( $background_matches[0]['value'] ) ) {
$border_color_matches = false;
$text_color_matches = false;
foreach ( $declarations as $declaration ) {
if ( 'border-color' === $declaration['name'] ) {
$border_color_matches = true;
} elseif ( 'color' === $declaration['name'] ) {
$text_color_matches = true;
}
}
if ( ! $border_color_matches && ! $text_color_matches ) {
$declarations[] = array(
'name' => 'color',
'value' => $background_matches[0]['value'],
);
}
}
return $declarations;
$background_color = '';
$border_color_matches = false;
$text_color_matches = false;
foreach ( $declarations as $declaration ) {
if ( 'background-color' === $declaration['name'] && ! $background_color && isset( $declaration['value'] ) ) {
$background_color = $declaration['value'];
} elseif ( 'border-color' === $declaration['name'] ) {
$border_color_matches = true;
} elseif ( 'color' === $declaration['name'] ) {
$text_color_matches = true;
}
if ( $background_color && $border_color_matches && $text_color_matches ) {
break;
}
}
if ( $background_color && ! $border_color_matches && ! $text_color_matches ) {
$declarations[] = array(
'name' => 'color',
'value' => $background_color,
);
}
return $declarations;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@felixarntz yeah this is much better than my refactor. It checks things only when we need to.

}

/**
* An internal method to get the block nodes from a theme.json file.
*
Expand Down Expand Up @@ -2101,6 +2143,11 @@ function( $pseudo_selector ) use ( $selector ) {
}
}

// Update declarations if there are separators with only background color defined.
if ( '.wp-block-separator' === $selector ) {
$declarations = static::update_separator_declarations( $declarations );
}

// 2. Generate and append the rules that use the general selector.
$block_rules .= static::to_ruleset( $selector, $declarations );

Expand Down
92 changes: 92 additions & 0 deletions tests/phpunit/tests/theme/wpThemeJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -3997,4 +3997,96 @@ function data_set_spacing_sizes_when_invalid() {
),
);
}

/**
* Tests the core separator block outbut based on various provided settings.
*
* @ticket 56903
*
* @dataProvider data_update_separator_declarations
*
* @param array $separator_block_settings Example separator block settings from the data provider.
* @param array $expected_output Expected output from data provider.
*/
public function test_update_separator_declarations( $separator_block_settings, $expected_output ) {
// If only background is defined, test that includes border-color to the style so it is applied on the front end.
$theme_json = new WP_Theme_JSON(
array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/separator' => $separator_block_settings,
),
),
),
'default'
);

$stylesheet = $theme_json->get_stylesheet( array( 'styles' ) );

$this->assertSame( $expected_output, $stylesheet );
}

/**
* Data provider for separator declaration tests.
*
* @return array
*/
function data_update_separator_declarations() {
return array(
// If only background is defined, test that includes border-color to the style so it is applied on the front end.
'only background' => array(
array(
'color' => array(
'background' => 'blue',
),
),
'expected_output' => 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-separator{background-color: blue;color: blue;}',
),
// If background and text are defined, do not include border-color, as text color is enough.
'background and text, no border-color' => array(
array(
'color' => array(
'background' => 'blue',
'text' => 'red',
),
),
'expected_output' => 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-separator{background-color: blue;color: red;}',
),
// If only text is defined, do not include border-color, as by itself is enough.
'only text' => array(
array(
'color' => array(
'text' => 'red',
),
),
'expected_output' => 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-separator{color: red;}',
),
// If background, text, and border-color are defined, include everything, CSS specifity will decide which to apply.
'background, text, and border-color' => array(
array(
'color' => array(
'background' => 'blue',
'text' => 'red',
),
'border' => array(
'color' => 'pink',
),
),
'expected_output' => 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-separator{background-color: blue;border-color: pink;color: red;}',
),
// If background and border color are defined, include everything, CSS specifity will decide which to apply.
'background, text, and border-color' => array(
array(
'color' => array(
'background' => 'blue',
),
'border' => array(
'color' => 'pink',
),
),
'expected_output' => 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-block-separator{background-color: blue;border-color: pink;}',
),
);
}
}