Skip irrelevant capabilities filtering in filter_user_has_cap() with whitelist #543
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This fixes #489.
CAP needs to filter the Core
has_cap()
function to allow co-authors to edit posts, even if they are not their primary author. However, the desired behavior is to filter only theedit_posts
-like caps, while leaving alone all the others. Right now this happens, but in a very sub-optimal way:The problem lies in calling
get_post_type( $post_id )
with no certainty that$post_id
is indeed a post ID. Indeed, it is a post ID when the cap is a post related one, but can be something else altogether for other caps. For example, forusers
-related caps it holds the user ID.So the issue is that
get_post_type( $post_id )
is most often called with$post_id
being either0
or a random int.get_post_type()
callsget_post()
, which will end up loading the wrong post, or no post at all. If no post is loaded, that call never gets cached and will run on every request. Considering thathas_cap()
is called hundreds of times per request, this can result in a major slow down.One would be tempted to just bail if the filtered cap is not
edit_posts
oredit_others_posts
. However, CAP can be active on any post type, and thus other caps need to be taken into account. For example, one could haveedit_downloads
, oredit_pages
caps, and CAP has to filter them.My solution is thus to get all the post types on which CAP is active, retrieve the
edit
-like caps and build an array with them. That is the whitelist it is then used to check whether the filtered cap is an interesting one, or we should bail out quickly. The whitelist is built by a separate function, that also caches the result in a class variables. Thus, the whitelist is built once per request and then used to quickly bail out infilter_user_has_cap()
if needed.Building the whitelist is pretty quick - basically as quick as reading from a global WP var, since that is what
get_post_type_object()
ultimately does.Unfortunately, we still have to retain the original check that bailed out in the sub-optimal way. This is because the whitelist can be built only after the post types have been initialized and, although this happens pretty early, there are still a handful of
has_cap()
calls that run before that happens, and we must have a way to bail out if needed. However, the too-earlyhas_cap()
calls are really just a few, so the whitelist ends up being used 99% of the times.--
A fix had already been proposed in #490, but it uses a blacklist of three caps that are often called and that we should bail out quickly, but a) there are probably more caps; b) it does not bail out when
$post_id === 0
. This PR makes #490 obsolete.