map_meta_cap( string $cap, int $user_id, mixed $args ): string[]
- Since
- 2.0.0, 4.9.6, 5.1.0, 5.2.0, 5.3.0, 5.7.0, 6.7.0
- Source
wp-includes/capabilities.php:45
Description
This function also accepts an ID of an object to map against if the capability is a meta capability. Meta capabilities such as edit_post and edit_user are capabilities used by this function to map to primitive capabilities that a user or role requires, such as edit_posts and edit_others_posts.
Example usage:
map_meta_cap( 'edit_posts', $user->ID );
map_meta_cap( 'edit_post', $user->ID, $post->ID );
map_meta_cap( 'edit_post_meta', $user->ID, $post->ID, $meta_key ); This function does not check whether the user has the required capabilities, it just returns what the required capabilities are.
Compatibility
- WordPress
- since 6.7.0
- PHP
- 7.4–8.6-dev
- 6.7.7
- 6.8.8
- 6.9.7
- 7.0.4
- 7.1.0
Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.
Parameters
$capstring- Capability being checked.
$user_idint- User ID.
$argsmixed- Optional further parameters, typically starting with an object ID.
Return value
string[]- Primitive capabilities required of the user.
Performance profile
How much work a call to map_meta_cap() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.
- Cost class
- Heavy
- Scaling
- Constant
- Instructions
- 15–102
- Plugin surface
- 4 hooks
- Called by
- 3
Reaches the database via get_post().
No loop in the body: the same number of instructions runs whatever you pass in.
Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 1346.
Third-party callbacks on 'auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}', 'auth_{$object_type}_meta_{$meta_key}', 'auth_{$object_type}_{$object_subtype}_meta_{$meta_key}' run inside this call, and their cost is not bounded by anything here.
3 places in core call this, so the cost is paid more often than your own code shows.
What it touches
- querycontent query
get_post()called directly - optionoption read or write
get_option()called directly - hookthird-party callbacks
apply_filters()called directly - cacheobject cache
wp_cache_get()one call below map_meta_cap() - serializeserialisation
maybe_unserialize()one call below map_meta_cap()
Further down the call graph this can also reach transient. That is the worst case, several calls deep and usually down an error path, not what a normal call pays.
What one call costs · 45 distinct outcomes
One number would be a lie: the work depends on which branch runs. These are every distinct cost map_meta_cap() can have, taken from its control-flow graph on PHP 8.5.
| When | Instructions | Calls it makes |
|---|---|---|
| always | 15–23 | apply_filters() |
!is_multisite() | 20–28 | is_multisite(), apply_filters() |
!wp_is_file_mod_allowed() | 21–23 | wp_is_file_mod_allowed(), apply_filters() |
isset($args) | 24–27 | get_post(), apply_filters() |
isset($args) | 24 | get_comment(), apply_filters() |
is_multisite() | 24–27 | is_multisite(), is_super_admin(), apply_filters() |
isset($args) && $user_id === false | 25 | is_super_admin(), apply_filters() |
wp_is_file_mod_allowed() && !is_multisite() | 25–27 | wp_is_file_mod_allowed(), is_multisite(), apply_filters() |
wp_is_file_mod_allowed() && is_multisite() | 28–31 | wp_is_file_mod_allowed(), is_multisite(), is_super_admin(), apply_filters() |
isset($args) && empty($object_subtype) | 31 | explode(), get_object_subtype(), apply_filters() |
!$user_id && is_multisite() | 31–39 | is_multisite(), is_super_admin(), user_can(), apply_filters() |
!isset($args) | 32–35 | __(), sprintf(), _doing_it_wrong(), apply_filters() |
33 further outcomes, up to 102 instructions
isset($args) | 34 | get_post(), get_option(), apply_filters() |
isset($args) | 34–44 | get_post(), get_post_type_object(), apply_filters() |
isset($args) | 34 | get_post(), get_post(), apply_filters() |
!$user_id && is_multisite() && $cap === "edit_user" | 34–40 | is_multisite(), is_super_admin(), is_super_admin(), apply_filters() |
isset($args) | 35–38 | get_comment(), get_post(), map_meta_cap(), apply_filters() |
!$user_id && is_multisite() && !is_super_admin() && $cap === "edit_user" | 39–45 | is_multisite(), is_super_admin(), is_super_admin(), user_can(), apply_filters() |
isset($args) && !empty($object_subtype) | 40–41 | explode(), get_object_subtype(), map_meta_cap(), apply_filters() |
!isset($args) | 40–44 | explode(), __(), sprintf(), _doing_it_wrong(), apply_filters() |
isset($args) | 41 | get_post(), get_option(), get_option(), apply_filters() |
isset($args) && !$post_type | 47–51 | get_post(), get_post(), get_post_type_object(), apply_filters() |
isset($args) && $post_type | 49–57 | get_post(), get_post_type_object(), get_post_status(), get_post_status_object(), apply_filters() |
isset($args) | 50–53 | get_post(), get_post_type_object(), __(), sprintf(), _doing_it_wrong(), apply_filters() |
isset($args) && $post_type | 53–61 | get_post(), get_post_type_object(), get_option(), apply_filters() |
isset($args) && !$post_type | 54–58 | get_post(), get_option(), get_option(), get_post_type_object(), apply_filters() |
isset($args) && $post_type | 56–64 | get_post(), get_post(), get_post_type_object(), get_post_status(), get_post_status_object(), apply_filters() |
isset($args) && $post_type && !$status_obj | 57–61 | get_post(), get_post_type_object(), get_post_status(), get_post_status_object(), map_meta_cap(), apply_filters() |
isset($args) && $post_type | 60–68 | get_post(), get_post(), get_post_type_object(), get_option(), apply_filters() |
isset($args) | 60 | get_post(), get_post(), get_post_type_object(), __(), sprintf(), _doing_it_wrong(), apply_filters() |
isset($args) && $post_type | 63–71 | get_post(), get_post_type_object(), get_option(), map_meta_cap(), array_merge(), apply_filters() |
isset($args) && $post_type && !$status_obj | 64–68 | get_post(), get_post(), get_post_type_object(), get_post_status(), get_post_status_object(), map_meta_cap(), apply_filters() |
isset($args) && $post_type | 65 | get_post(), get_post_type_object(), get_post_status(), get_post_status_object(), __(), get_post_status(), sprintf(), _doing_it_wrong(), apply_filters() |
isset($args) && $post_type | 67–75 | get_post(), get_option(), get_option(), get_post_type_object(), get_option(), apply_filters() |
isset($args) | 67 | get_post(), get_option(), get_option(), get_post_type_object(), __(), sprintf(), _doing_it_wrong(), apply_filters() |
isset($args) && $post_type && $post && $user_id === false | 68 | get_post(), get_post_type_object(), get_post_meta(), get_option(), apply_filters() |
isset($args) && $post_type | 70–78 | get_post(), get_post(), get_post_type_object(), get_option(), map_meta_cap(), array_merge(), apply_filters() |
isset($args) && $post_type | 72 | get_post(), get_post(), get_post_type_object(), get_post_status(), get_post_status_object(), __(), get_post_status(), sprintf(), _doing_it_wrong(), apply_filters() |
isset($args) && $post_type && $post && $user_id === false | 75 | get_post(), get_post(), get_post_type_object(), get_post_meta(), get_option(), apply_filters() |
isset($args) && $post_type | 77–85 | get_post(), get_option(), get_option(), get_post_type_object(), get_option(), map_meta_cap(), array_merge(), apply_filters() |
isset($args) && $post_type && $post && $user_id === false | 78 | get_post(), get_post_type_object(), get_post_meta(), get_option(), map_meta_cap(), array_merge(), apply_filters() |
isset($args) && $post_type && $post && $user_id === false | 82 | get_post(), get_option(), get_option(), get_post_type_object(), get_post_meta(), get_option(), apply_filters() |
isset($args) && $post_type && $post && $user_id === false | 85 | get_post(), get_post(), get_post_type_object(), get_post_meta(), get_option(), map_meta_cap(), array_merge(), apply_filters() |
isset($args) && $post_type && $post && $user_id === false | 92 | get_post(), get_option(), get_option(), get_post_type_object(), get_post_meta(), get_option(), map_meta_cap(), array_merge(), apply_filters() |
isset($args) && !empty($object_subtype) | 95–102 | explode(), get_object_subtype(), map_meta_cap(), is_protected_meta(), has_filter(), apply_filters(), apply_filters() |
This body has more branch combinations than are worth enumerating, so the table covers the outcomes found first rather than every one that exists.
Across PHP versions
| PHP | Compiled | Executed | Branches | Notes |
|---|---|---|---|---|
| 8.6-dev | 1346 | 15–102 | 211 | |
| 8.5 | 1346 | 15–102 | 211 | |
| 8.4 | 1346 | 15–102 | 211 | 3 fewer instructions than PHP 8.3 |
| 8.3 | 1349 | 15–102 | 211 | |
| 8.2 | 1349 | 15–102 | 211 | 1 more instruction than PHP 8.1 |
| 8.1 | 1348 | 15–102 | 211 | 1 more instruction than PHP 7.4 |
| 7.4 | 1347 | 15–102 | 211 |
An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.
Hooks and filters fired · 4
4 hooks fire while map_meta_cap() runs, in this order:
- apply_filters( auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype} )filterline 497 (+452 into the body)
Filters whether the user is allowed to edit a specific meta key of a specific object type and subtype.
- apply_filters( auth_{$object_type}_meta_{$meta_key} )filterline 518 (+473 into the body)
Filters whether the user is allowed to edit a specific meta key of a specific object type.
- do_action( auth_{$object_type}_{$object_subtype}_meta_{$meta_key} )filter_deprecatedline 542 (+497 into the body)
Filters whether the user is allowed to edit meta for specific object types/subtypes.
- apply_filters( map_meta_cap )filterline 879 (+834 into the body)
Filters the primitive capabilities required of the given user to satisfy the capability being checked.
Uses · 24
- is_super_admin()Determines whether user is a site admin.
- is_multisite()Determines whether Multisite is enabled.
- user_can()Returns whether a particular user has the specified capability.
- __()Retrieves the translation of $text.
- _doing_it_wrong()Marks something as being incorrectly called.
- get_post()Retrieves post data given a post ID or post object.
- get_option()Retrieves an option value based on an option name.
- get_post_type_object()Retrieves a post type object by name.
- get_post_meta()Retrieves a post meta field for the given post ID.
- map_meta_cap()Maps a capability to the primitive capabilities required of the given user to satisfy the capability being checked.
- get_post_status_object()Retrieves a post status object by name.
- get_post_status()Retrieves the post status based on the post ID.
Show all 24
- get_object_subtype()Returns the object subtype for a given object ID of a specific type.
- is_protected_meta()Determines whether a meta key is considered protected.
- has_filter()Checks if any filter has been registered for a hook.
- apply_filters()Calls the callback functions that have been added to a filter hook.
- apply_filters_deprecated()Fires functions attached to a deprecated filter hook.
- get_comment()Retrieves comment data given a comment ID or comment object.
- wp_is_file_mod_allowed()Determines whether file modifications are allowed.
- get_site_option()Retrieve an option value for the current network based on name of option.
- get_term()Gets all term data from database by term ID.
- is_wp_error()Checks whether the given variable is a WordPress Error.
- get_taxonomy()Retrieves the taxonomy object of $taxonomy.
- get_post_type_capabilities()Builds an object with all post type capabilities out of a post type object
Used by · 3
- WP_Customize_Manager::grant_edit_post_capability_for_changeset()Re-maps 'edit_post' meta cap for a customize_changeset post to be the same as 'customize' maps.
- WP_User::has_cap()Returns whether the user has the specified capability.
- map_meta_cap()Maps a capability to the primitive capabilities required of the given user to satisfy the capability being checked.
Source code
function map_meta_cap( $cap, $user_id, ...$args ) { $caps = array(); switch ( $cap ) { case 'remove_user': // In multisite the user must be a super admin to remove themselves. if ( isset( $args[0] ) && $user_id === (int) $args[0] && ! is_super_admin( $user_id ) ) { $caps[] = 'do_not_allow'; } else { $caps[] = 'remove_users'; } break; case 'promote_user': case 'add_users': $caps[] = 'promote_users'; break; case 'edit_user': case 'edit_users': // Non-existent users can't edit users, not even themselves. if ( $user_id < 1 ) { $caps[] = 'do_not_allow'; break; } // Allow user to edit themselves. if ( 'edit_user' === $cap && isset( $args[0] ) && $user_id === (int) $args[0] ) { break; } // In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin. if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) { $caps[] = 'do_not_allow'; } else { $caps[] = 'edit_users'; // edit_user maps to edit_users. } break; case 'delete_post': case 'delete_page': if ( ! isset( $args[0] ) ) { if ( 'delete_post' === $cap ) { /* translators: %s: Capability name. */ $message = __( 'When checking for the %s capability, you must always check it against a specific post.' ); } else { /* translators: %s: Capability name. */ $message = __( 'When checking for the %s capability, you must always check it against a specific page.' ); } _doing_it_wrong( __FUNCTION__, sprintf( $message, '<code>' . $cap . '</code>' ), '6.1.0' ); $caps[] = 'do_not_allow'; break; } $post = get_post( $args[0] ); if ( ! $post ) { $caps[] = 'do_not_allow'; break; } if ( 'revision' === $post->post_type ) { $caps[] = 'do_not_allow'; break; } if ( (int) get_option( 'page_for_posts' ) === $post->ID || (int) get_option( 'page_on_front' ) === $post->ID ) { $caps[] = 'manage_options'; break; } $post_type = get_post_type_object( $post->post_type ); if ( ! $post_type ) { /* translators: 1: Post type, 2: Capability name. */ $message = __( 'The post type %1$s is not registered, so it may not be reliable to check the capability %2$s against a post of that type.' );Changelog
Introduced in 2.0.0. Unchanged from 6.7.7 through 7.1.0.
Signature, return type and hooks compared across 5 parsed releases.
edit_block_binding capability.from the docblockcreate_app_password, list_app_passwords, read_app_password, edit_app_password, delete_app_passwords, delete_app_password, and update_https capabilities.from the docblock...$args parameter by adding it to the function signature.from the docblockresume_plugin and resume_theme capabilities.from the docblockupdate_php capability.from the docblockexport_others_personal_data, erase_others_personal_data, and manage_privacy_options capabilities.from the docblockAbout this page
- Parsed data
- Generated from the wordpress-develop 7.1.0 tag, from
src/wp-includes/capabilities.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it. - Corrections
- Something wrong on this page? Report it and it gets fixed in the next regeneration.