-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Lazy load user capabilities in WP_User object #5098
base: trunk
Are you sure you want to change the base?
Conversation
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 Core Committers: Use this line as a base for the props when committing in SVN:
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
The number of expected queries in the cacheResults test has been decreased from two to one. Specifically, the user data query is no longer expected, only the user meta data query remains. The test function has been updated accordingly to reflect this change.
Test using WordPress PlaygroundThe 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
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spacedmonkey I think there is at least one bug with the code that needs to be addressed (related to switching to another site in Multisite).
Other than that, I think it would be great if you could separate the overall metadata lazyloader integration and handle that in another PR. The role and capability logic itself is expensive enough to be lazy-loaded, which is what most of this PR is about. That in itself is already complex and may have some quirks to consider, so I think separating the two concerns makes the PRs easier to review.
As an aside, I'm not very familiar with metadata lazy-loading, but I am familiar with how WP_User
works, so I only feel comfortable with reviewing the latter.
src/wp-includes/class-wp-user.php
Outdated
$this->caps = $this->get_caps_data(); | ||
|
||
$this->get_role_caps(); | ||
wp_lazyload_user_meta( array( $this->ID ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not very familiar with how metadata lazy-loading works, so I feel like I'm not the right person to review this part. For example, one potential caveat here is that user metadata is generally global, but some meta keys only apply to specific sites.
I wonder for instance why this method is called here. Since user metadata is global, why would it need to be loaded (again) when the site is switched? Shouldn't this call be somewhere else, or only be called if it hadn't been called before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the key to not loading user meta on every request and is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that this is useful, but I don't think it's required for this PR. This line fits perfectly into the scope of https://core.trac.wordpress.org/ticket/63021, but less into lazy-loading the role/cap properties of WP_User
.
On another note, if we keep it, could you review my previous question please?
Since user metadata is global, why would it need to be loaded (again) when the site is switched? Shouldn't this call be somewhere else, or only be called if it hadn't been called before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spacedmonkey Can you please get back to me on this open question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spacedmonkey The WP_User
changes look almost good to commit, just a few points of minor feedback.
As mentioned, I would like for someone else that is more experienced with WP_Metadata_Lazyloader
to review that portion of the PR.
if ( ! $this->loaded_caps ) { | ||
$this->caps = $this->get_caps_data(); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary I think. The WP_User
class has always populated $this->caps
before, and not doing so anymore would be a breaking change. So we don't need this here, since $this->caps
will always be correct prior to calling this method.
if ( ! $this->loaded_caps ) { | |
$this->caps = $this->get_caps_data(); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lines below checks if caps is an array.
if ( is_array( $this->caps ) )
I wanted to ensure $this->caps
was set correctly before doing this check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is that that's unnecessary to check, because it'll always be set correctly based on $this->caps = $this->get_caps_data()
, which is called before this get_role_caps()
method everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method get_role_caps
is public. What you create a WP_User instance and the call get_role_caps
? It feels safer to just make sure that caps are loaded here. What does it hurt to have this here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that's a fair point. Could you add an inline comment to clarify this? For example:
if ( ! $this->loaded_caps ) { | |
$this->caps = $this->get_caps_data(); | |
} | |
// Edge-case: In case someone calls this method before lazy initialization, we need to initialize on demand. | |
if ( ! $this->loaded_caps ) { | |
$this->caps = $this->get_caps_data(); | |
} | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition, ideally this method would also set $this->loaded_caps = true
in that situation: If someone calls get_role_caps()
before initialization, the data is all set up the same way as load_capability_data()
does it. So there would be no point of lazy-loading that data again afterwards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a couple of notes and a question inline.
The change to cache_users, does make the change so user meta data is always lazy loaded and there doesn't appear to be a way for developers to trigger the active loading of user meta.
Based on manual testing it appears pretty good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spacedmonkey @peterwilsoncc I am realizing just now that the change of making the $caps
, $roles
, and $allcaps
properties protected may be considered a breaking change, albeit only relevant for edge cases.
They're still readable via magic getter and isset-er, but in theory a plugin doing some very niche capability modification stuff may be writing these properties too. With the current state of this PR, that wouldn't work anymore.
I'm not overly concerned about this, since doing so would be a bad practice, but I still wanted to flag it. I think we have two options here:
- Either accept this as a edge-case BC break we're willing to make, punt this to 6.9 to land
early
in the milestone, and cover it in a dev note. - Or add conditions for these to the
__set()
method to maintain backward compatibility (which is definitely more forgiving, but may continue encouraging this problematic pattern).
WDYT?
The wp_lazyload_user_meta function was redundant and has been removed to simplify the codebase. This change helps reduce unnecessary code maintenance and improves overall clarity.
Introduce tests for `get_role_caps()` and lazy evaluation of user roles, capabilities, and allcaps. These tests ensure proper structure and expected values for user role-related data. Validates specific capabilities for contributors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@peterwilsoncc @felixarntz @joemcgill As requested, I have removed all the lazy loading user meta stuff. This makes this patch much much smaller and easily to review.
I have added some tests to valid that get_role_caps
continues to work. Other existing tests cover add_cap, remove_cap, remove_all_caps, has_cap and set_role still work.
if ( ! $this->loaded_caps ) { | ||
$this->caps = $this->get_caps_data(); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method get_role_caps
is public. What you create a WP_User instance and the call get_role_caps
? It feels safer to just make sure that caps are loaded here. What does it hurt to have this here?
If you are change the properties manaully, I feel like that are a pretty dangerous way of changing a user role. There are other filters and ways of doing this that are less problematic. I do believe this needs a detail dev note, but I feel like we should not enable this edge. |
Trac ticket: https://core.trac.wordpress.org/ticket/58001
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.