Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove additional call to WP_Theme_JSON::_construct #6271

Closed

Conversation

kt-12
Copy link
Member

@kt-12 kt-12 commented Mar 14, 2024

Trac ticket: Trac 61112

Corresponding Guttenberg PR - WordPress/gutenberg#61262

This was found while analysing https://core.trac.wordpress.org/ticket/59600, but it relates mostly https://core.trac.wordpress.org/ticket/57789

In this PR we are trying to avoid second call to new WP_Theme_JSON( $theme_json_data ); as the data can be obtained from WP_Theme_JSON_data class using just a line above by introducing a new public method. At the micro level call to WP_Theme_JSON constructor is expensive.

Interestingly, I can see this change also improved static calls. I wasn't expecting that as in my opinion static lines are only executed once, however, this has challenged my view about static calls and might be we need to be mindful of the performance even while using static.

The execution time of the wp_get_theme_data_custom_templates function on the home page of TT4 was evaluated. Each page had two calls to the function. The second call was static cached so it was significantly lower than the time for the first call.

function wp_get_theme_data_custom_templates() {

	// Capture the start time
	$startTime = microtime(true);
	$data = WP_Theme_JSON_Resolver::get_theme_data( array(), array( 'with_supports' => false ) )->get_custom_templates();	
	// Capture the end time
	$endTime = microtime(true);

	// Calculate the script execution time
	$executionTime = $endTime - $startTime;
	echo "<pre>Execution Time of `wp_get_theme_data_custom_templates`: " . $executionTime . " seconds.</pre>";

	return $data;
}

The performance improvement was as noted below:
Before PR

Description Execution Time (seconds)
Run 1
Execution Time of wp_get_theme_data_custom_templates 0.0033679008483887
Execution Time of wp_get_theme_data_custom_templates 0.00087404251098633
Run 2
Execution Time of wp_get_theme_data_custom_templates 0.003180980682373
Execution Time of wp_get_theme_data_custom_templates 0.00077509880065918
Run 3
Execution Time of wp_get_theme_data_custom_templates 0.0039951801300049
Execution Time of wp_get_theme_data_custom_templates 0.00078392028808594
Run 4
Execution Time of wp_get_theme_data_custom_templates 0.0032379627227783
Execution Time of wp_get_theme_data_custom_templates 0.00077700614929199
Run 5
Execution Time of wp_get_theme_data_custom_templates 0.003154993057251
Execution Time of wp_get_theme_data_custom_templates 0.00075793266296387
Run 6
Execution Time of wp_get_theme_data_custom_templates 0.0032570362091064
Execution Time of wp_get_theme_data_custom_templates 0.00075292587280273

Mean of first call: 0.003366
Mean of static call: 0.000787

After PR

Description Execution Time (seconds)
Run 1
Execution Time of wp_get_theme_data_custom_templates 0.0030369758605957
Execution Time of wp_get_theme_data_custom_templates 0.0004730224609375
Run 2
Execution Time of wp_get_theme_data_custom_templates 0.0031139850616455
Execution Time of wp_get_theme_data_custom_templates 0.00047683715820312
Run 3
Execution Time of wp_get_theme_data_custom_templates 0.002918004989624
Execution Time of wp_get_theme_data_custom_templates 0.00047898292541504
Run 4
Execution Time of wp_get_theme_data_custom_templates 0.0026509761810303
Execution Time of wp_get_theme_data_custom_templates 0.00044488906860352
Run 5
Execution Time of wp_get_theme_data_custom_templates 0.0027339458465576
Execution Time of wp_get_theme_data_custom_templates 0.00045418739318848
Run 6
Execution Time of wp_get_theme_data_custom_templates 0.0029489994049072
Execution Time of wp_get_theme_data_custom_templates 0.00049400329589844

Mean of first call: 0.002900
Mean of static call: 0.000470


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Copy link

github-actions bot commented Mar 14, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props thekt12, joemcgill, swissspidy, audrasjb, oandregal.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@kt-12
Copy link
Member Author

kt-12 commented Apr 30, 2024

@joemcgill With the latest changes we are getting a consistent 2.5% improvement without breaking anything.

Performance Metric Trunk PR Improvement (%)
Response Time (median) 387.75 379.09 2.23%
wp-before-template (median) 151.2 145.18 3.98%
wp-before-template-db-queries (median) 0 0 0

Copy link
Member

@joemcgill joemcgill left a comment

Choose a reason for hiding this comment

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

Good find, @kt-12. This looks like a smart improvement to me. I believe the original implementation for these filters were done as part of WordPress/gutenberg#44015. @oandregal do you recall any reason why the WP_Theme_JSON_Data objects needed to return the raw theme.json data just to have them built back into new WP_Theme_JSON objects?

Profiling this PR, I can see that this reduces calls of WP_Theme_JSON::_construct() by 8, saving a measurable amount of time and memory.

Before

image

After

image

@kt-12 kt-12 changed the title Remove additional call to theme_json Remove additional call to WP_Theme_JSON::__construct May 1, 2024
@kt-12 kt-12 changed the title Remove additional call to WP_Theme_JSON::__construct Remove additional call to WP_Theme_JSON::_construct May 1, 2024
fix grammar

Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com>
Copy link
Contributor

@audrasjb audrasjb left a comment

Choose a reason for hiding this comment

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

I have a small feedback concerning the Dockblock proposed for get_theme_json()

src/wp-includes/class-wp-theme-json-data.php Show resolved Hide resolved
@audrasjb audrasjb added the props-bot Adding this label triggers the Props Bot workflow for a PR. label May 1, 2024
@github-actions github-actions bot removed the props-bot Adding this label triggers the Props Bot workflow for a PR. label May 1, 2024
Copy link
Member

@oandregal oandregal left a comment

Choose a reason for hiding this comment

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

I really like this PR, it shows attention to detail and understanding the flows. Thank you, @kt-12!

I haven't checked the performance impact, as I've seen others have done that and even if there was none this would have been a nice clean up.

I've checked a few things:

  • Origins are still setup properly.
  • Nothing changes for consumers in ways that they can unintentionally mess it up and doesn't require them to do anything.
  • The processes done for incoming data (sanitization, migrating to new theme.json version, etc.) are still in place — before were happening twice, actually.

@oandregal
Copy link
Member

Oh, by the way. It'd be nice if this change would have been done first in the Gutenberg codebase. The value that provides is immense to all of us, including easier synchronization and quick feedback from Gutenberg users if we've missed something. I have nothing against merging it in wordpress-develop first, though I'd kindly ask that the authors port the same PR to Gutenberg. Until we figure out something better, this is what we collectively have and need to care for.

@kt-12
Copy link
Member Author

kt-12 commented May 3, 2024

Oh, by the way. It'd be nice if this change would have been done first in the Gutenberg codebase. The value that provides is immense to all of us, including easier synchronization and quick feedback from Gutenberg users if we've missed something. I have nothing against merging it in wordpress-develop first, though I'd kindly ask that the authors port the same PR to Gutenberg. Until we figure out something better, this is what we collectively have and need to care for.

Thank you @oandregal for your feedback. We clearly understand your feedback about Gutenberg first approach, hence we have already created a PR for Gutenberg - WordPress/gutenberg#61262 . Once it's tested and approved on Gutenberg we can then plan to merge it for the core.

@@ -543,8 +538,7 @@ public static function get_user_data() {

/** This filter is documented in wp-includes/class-wp-theme-json-resolver.php */
$theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data( $config, 'custom' ) );
$config = $theme_json->get_data();
static::$user = new WP_Theme_JSON( $config, 'custom' );
static::$user = $theme_json->get_theme_json();
Copy link
Member

Choose a reason for hiding this comment

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

One thing I've noticed after reviewing the Gutenberg PR WordPress/gutenberg#61262 is that this is different in Gutenberg: there, we do $config['isGlobalStylesUserThemeJSON'] = true; before the call to WP_Theme_JSON. We need to consolidate that logic.

Copy link

Choose a reason for hiding this comment

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

It looks like #6616 will undo this change.

Copy link
Member Author

Choose a reason for hiding this comment

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

@talldan w.r.t to keeping both the repos same, that change should be fine. But w.r.t performance we might need to find a better way to do that later on.

@oandregal
Copy link
Member

WordPress/gutenberg#61262

Thank you so much 🙏 I approved the Gutenberg PR.

By reviewing the Gutenberg PR I noticed that the core code is missing a check: see #6271 (comment) and https://github.com/WordPress/gutenberg/pull/61262/files#r1589940214. I don't know why they have differed but that check is important and we should consolidate both codebases.

@joemcgill
Copy link
Member

@@ -255,8 +254,7 @@ public static function get_theme_data( $deprecated = array(), $options = array()
* @param WP_Theme_JSON_Data $theme_json Class to access and update the underlying data.
*/
$theme_json = apply_filters( 'wp_theme_json_data_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) );
$theme_json_data = $theme_json->get_data();
static::$theme = new WP_Theme_JSON( $theme_json_data );
static::$theme = $theme_json->get_theme_json();
Copy link

Choose a reason for hiding this comment

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

There are some linting issues in the committed code, incorrect spacing before the equals signs here.

The main reason I'm looking at this though is that a new feature was merged in Gutenberg - WordPress/gutenberg#57908. It was testing fine prior to the changes on these lines, but after the change there's an exception being thrown by this change (I bisected to find it).

To repro:

  1. Make sure you have the latest core trunk and gutenberg trunk code checked out and built, you have the plugin active and are using twentytwentyfour as your theme
  2. Add a file with the following content into wp-content/themes/twentytwentyfour/styles/block-style-contrast.json:
{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"title": "Variation A",
	"blockTypes": [ "core/group", "core/columns", "core/media-text" ],
	"styles": {
		"color": {
			"background": "var(--wp--preset--color--contrast)",
			"text": "var(--wp--preset--color--base)"
		}
	}
}
  1. Load the site editor, and you should see an error:
Warning: Undefined array key "styleVariations" in /var/www/html/wp-includes/class-wp-theme-json.php on line 2465
Warning: Trying to access array offset on value of type null in /var/www/html/wp-includes/class-wp-theme-json.php on line 2465

If you then revert the change on this line(s), the error goes away.

I don't have much of an understanding why this causes an issue. It'd be great to understand why it's happening before taking any action. I'll continue looking into it, but any help appreciated, as this'll need to be fixed prior to the 6.6 beta.

Copy link

@talldan talldan May 30, 2024

Choose a reason for hiding this comment

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

@kt-12 @joemcgill @oandregal I think I have an understanding of what's happening, but worth noting I'm not an expert with these theme json classes.

The gutenberg codebase hooks into the wp_theme_json_data_theme filter, but can return a WP_Theme_JSON_Gutenberg from the filter. Before your change, it didn't really matter as core would construct a WP_Theme_JSON from that, but now I think the change can mean core holding on to a WP_Theme_JSON_Gutenberg reference. This might be leading to some incompatibilities in the data structures. At some point the newer WP_Theme_JSON_Gutenberg data is being processed by an older WP_Theme_JSON class, and that's when the error happens. I'm not sure exactly how that happens, still investigating.

The core backport for the feature (#6662) does resolve the issue, as it updates WP_Theme_JSON to be compatible.

But I think there are question marks over what happens when the gutenberg plugin tries to implement new features in the future that extend the theme json.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @talldan. We had this reported to the related Trac ticket and have created this follow-up PR to address the issue. If you could test it and confirm that it fixes the issue, I can get it committed.

But I think there are question marks over what happens when the gutenberg plugin tries to implement new features in the future that extend the theme json.

I think this is a very good question. I've had some discussion with @tellthemachines and @oandregal about making the WP_Theme_JSON_ related classes read only in the WP-dev repo and sync them from the GB repo the way we sync other packages, which is one way to solve these incompatibilities, but we've not confirmed that approach. I need to open a new issue for us to consider those changes.

Copy link
Member

Choose a reason for hiding this comment

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

I didn't have time to look at this today but will do next week. This smells to a bug we need to fix, for which we have time in the beta period. I'm available and happy to help uncover the issue and prepare a fix. Please, ping me in any related tickets/PR (subscribed to the PRs & trac tickets mentioned).

Copy link
Member

Choose a reason for hiding this comment

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

I finally got some time to look at this today. Sharing what I know.

The flow:

Steps Core Gutenberg plugin
1. WP_Theme_JSON triggers the wp_theme_json_data_theme filter.
2. gutenberg_resolve_style_variations_* runs as a filter callback
3. Gutenberg builds its own WP_Theme_JSON_Data_Gutenberg which uses to validate the data (update_with) and then returns.
4. WP_Theme_JSON receives the validated data.

At 4, core receives some data and it expects it to be validated by the consumer of the filter (this is, the consumer uses update_with). While Gutenberg validates the data properly, it uses different metadata/schema than core does.

In the particular case that Dan raised, Gutenberg was able to get more style variations than core had:

Variations detected by Gutenberg Variations detected by core
For block core/columns: variation-a
For block core/group: variation-a
For block core/media-text: variation-a
For block core/button: outline For block core/button: outline
For block core/image: rounded For block core/image: rounded
For block core/quote: plain For block core/quote: plain

At the point of processing the variations, core is unable to find the first three variations, hence the PHP Warning: Undefined array key "styleVariations" in /var/www/src/wp-includes/class-wp-theme-json.php on line 2445 raised here. Before this PR, core did an extra validation step, getting rid of the extraneous data before processing it, so that never happened.

There's a few layers to this issue, and it only happens because Gutenberg is re-triggering the same filters than core (wp_theme_json_data_theme). The consumers of the filters run twice (one for core and the second time for the plugin). It's the first time that it fails because the metadata/schemas are different.

Unlike this other issue, this is very Gutenberg-specific because it is the result of modifying the metadata/schema of the theme.json. I don't have any more time today to look into solutions, but wanted to share what I know about as soon as possible. I'll do some more testing in the next days to assess the severity and what can we do.

Copy link
Member

Choose a reason for hiding this comment

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

For a different issue, I ended up proposing a change to how "block style variations" defined by the theme are registered at #6756 By doing that, the error reported by Dan no longer happens.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for looking into this @oandregal. It still seems like this is very similar to https://core.trac.wordpress.org/ticket/61112, but seems like the problem is that because core is not converting the WP_Theme_JSON_Data_Gutenberg object back into a WP_Theme_JSON_Data object, the incompatibilities are possible.

It seems wrong that Gutenberg would return an object from these filters that are not compatible with WP_Theme_JSON_Data objects, but we may be able to simply update the fix proposed in #6626 to check for whether the returned object is a WP_Theme_JSON_Data object, and reprocess if not, rather than just if it a get_theme_json method exists on the class.

As a matter of process, I think following up on this whole issue should be done in as a part of https://core.trac.wordpress.org/ticket/61112 so this doesn't get lost, so I'm going to summarize this issue on that ticket and update the PR with the new approach I just proposed.

Copy link
Member

Choose a reason for hiding this comment

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

Updated the approach in #6626 that should address the exception @talldan describes, though better ways of extending these objects in the Gutenberg repo are still worth investigating further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
6 participants