current_user_can( string $capability, mixed $args ): bool
- Since
- 2.0.0, 5.3.0, 5.8.0
- Source
wp-includes/capabilities.php:910
Test whether the current user has a capability with current_user_can(), from broad caps like manage_options to meta caps like edit_post with a post ID. Returns a boolean; super admins pass almost every check, and passing role names instead of capabilities gives unreliable results.
Description
This function also accepts an ID of an object to check against if the capability is a meta capability. Meta capabilities such as edit_post and edit_user are capabilities used by the map_meta_cap() function to map to primitive capabilities that a user or role has, such as edit_posts and edit_others_posts.
Example usage:
current_user_can( 'edit_posts' );
current_user_can( 'edit_post', $post->ID );
current_user_can( 'edit_post_meta', $post->ID, $meta_key ); While checking against particular roles in place of a capability is supported in part, this practice is discouraged as it may produce unreliable results.
Note: Will always return true if the current user is a super admin, unless specifically denied.
Compatibility
- WordPress
- since 5.8.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
$capabilitystring- Capability name.
$argsmixed- Optional further parameters, typically starting with an object ID.
Return value
bool- Whether the current user has the given capability. If
$capabilityis a meta cap and$object_idis passed, whether the current user has the given meta capability for the given object.
Code examples
Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.
Check a capability against a specific post
Meta capabilities like edit_post take the post ID and are mapped to the real capability for that post.
wp_set_current_user( 1 );
echo 'as administrator:\n';
echo ' manage_options: ', var_export( current_user_can( 'manage_options' ), true ), "\n";
echo ' edit_post 1: ', var_export( current_user_can( 'edit_post', 1 ), true ), "\n\n";
wp_set_current_user( 0 );
echo "as a logged-out visitor:\n";
echo ' manage_options: ', var_export( current_user_can( 'manage_options' ), true ), "\n";
echo ' edit_post 1: ', var_export( current_user_can( 'edit_post', 1 ), true );Never check the role instead; capabilities are what plugins and roles actually modify.
Gate a form handler by capability
Check the capability at the top of any state-changing handler, then verify the nonce; the two checks answer different questions.
add_action( 'admin_post_myplugin_save', 'myplugin_handle_save' );
function myplugin_handle_save() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You are not allowed to do that.', 'myplugin' ) );
}
check_admin_referer( 'myplugin_save' );
// ...validate and persist the submitted settings...
wp_safe_redirect( admin_url( 'options-general.php?page=myplugin&saved=1' ) );
exit;
}A capability check alone does not prevent CSRF: the user may be capable but the request forged. Always pair current_user_can() with a nonce check before writing anything.
Performance profile
How much work a call to current_user_can() 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
- Trivial
- Scaling
- Constant
- Instructions
- 11
- Plugin surface
- None
- Called by
- 50
Touches nothing outside its own arguments.
No loop in the body: the same number of instructions runs whatever you pass in.
Executed per call on PHP 8.5. The body compiles to 11.
Nothing here hands control to plugin code.
50 places in core call this, so the cost is paid more often than your own code shows.
What one call costs · 1 distinct outcome
One number would be a lie: the work depends on which branch runs. These are every distinct cost current_user_can() can have, taken from its control-flow graph on PHP 8.5.
| When | Instructions | Calls it makes |
|---|---|---|
| always | 11 | wp_get_current_user(), user_can() |
Across PHP versions
| PHP | Compiled | Executed | Branches | Notes |
|---|---|---|---|---|
| 8.6-dev | 11 | 11 | 0 | |
| 8.5 | 11 | 11 | 0 | |
| 8.4 | 11 | 11 | 0 | |
| 8.3 | 11 | 11 | 0 | |
| 8.2 | 11 | 11 | 0 | |
| 8.1 | 11 | 11 | 0 | 1 more instruction than PHP 7.4 |
| 7.4 | 10 | 10 | 0 |
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.
Uses · 2
- user_can()Returns whether a particular user has the specified capability.
- wp_get_current_user()Retrieves the current user object.
Used by · 50
- Bulk_Plugin_Upgrader_Skin::bulk_footer()Displays the footer following the bulk update process.
- Bulk_Theme_Upgrader_Skin::bulk_footer()Displays the footer following the bulk update process.
- Custom_Background::admin_page()Displays the custom background page.
- Custom_Background::ajax_background_add()Handles Ajax request for adding custom background context to an attachment.
- Custom_Background::wp_set_background_image()
- Custom_Image_Header::admin_page()Displays the page based on the current step.
- Custom_Image_Header::ajax_header_add()Given an attachment ID for a header image, updates its "last used" timestamp to now.
- Custom_Image_Header::ajax_header_crop()Gets attachment uploaded by Media Manager, crops it, then saves it as a new object. Returns JSON-encoded object details.
- Custom_Image_Header::ajax_header_remove()Given an attachment ID for a header image, unsets it as a user-uploaded header image for the active theme.
- Custom_Image_Header::step_1()Displays first step of custom header image page.
- Custom_Image_Header::take_action()Executes custom header modification.
- Plugin_Installer_Skin::after()Performs an action following a plugin install.
Show all 50
- Plugin_Upgrader_Skin::after()Performs an action following a single plugin update.
- Theme_Installer_Skin::after()Performs an action following a single theme install.
- Theme_Upgrader_Skin::after()Performs an action following a single theme update.
- WP_Comments_List_Table::ajax_user_can()
- WP_Comments_List_Table::column_response()
- WP_Comments_List_Table::extra_tablenav()
- WP_Comments_List_Table::single_row()
- WP_Customize_Control::check_capabilities()Checks if the user can use this control.
- WP_Customize_Control::render_content()Render the control's content.
- WP_Customize_Header_Image_Control::render_content()
- WP_Customize_Manager::__construct()Constructor.
- WP_Customize_Manager::check_changeset_lock_with_heartbeat()Checks locked changeset with heartbeat API.
- WP_Customize_Manager::customize_pane_settings()Prints JavaScript settings for parent window.
- WP_Customize_Manager::customize_preview_init()Prints JavaScript settings.
- WP_Customize_Manager::customize_preview_settings()Prints JavaScript settings for preview frame.
- WP_Customize_Manager::enqueue_control_scripts()Enqueues scripts for customize controls.
- WP_Customize_Manager::handle_changeset_trash_request()Handles request to trash a changeset.
- WP_Customize_Manager::handle_dismiss_autosave_or_lock_request()Deletes a given auto-draft changeset or the autosave revision for a given changeset or delete changeset lock.
- WP_Customize_Manager::handle_load_themes_request()Loads themes into the theme browsing/installation UI.
- WP_Customize_Manager::handle_override_changeset_lock_request()Removes changeset lock when take over request is sent via Ajax.
- WP_Customize_Manager::save()Handles customize_save WP Ajax request to save/update a changeset.
- WP_Customize_Manager::setup_theme()Starts preview and customize theme.
- WP_Customize_Manager::unsanitized_post_values()Gets dirty pre-sanitized setting values in the current customized state.
- WP_Customize_Manager::validate_setting_values()Validates setting values.
- WP_Customize_Media_Control::to_json()Refresh the parameters passed to the JavaScript via JSON.
- WP_Customize_Nav_Menus::__construct()Constructor.
- WP_Customize_Nav_Menus::ajax_insert_auto_draft_post()Ajax handler for adding a new auto-draft post.
- WP_Customize_Nav_Menus::ajax_load_available_items()Ajax handler for loading available menu items.
- WP_Customize_Nav_Menus::ajax_search_available_items()Ajax handler for searching available menu items.
- WP_Customize_Nav_Menus::print_post_type_container()Prints the markup for new menu items.
- WP_Customize_Nav_Menus::sanitize_nav_menus_created_posts()Sanitizes post IDs for posts created for nav menu items to be published.
- WP_Customize_Panel::check_capabilities()Checks required user capabilities and whether the theme has the feature support required by the panel.
- WP_Customize_Partial::check_capabilities()Checks if the user can refresh this partial.
- WP_Customize_Section::check_capabilities()Checks required user capabilities and whether the theme has the feature support required by the section.
- WP_Customize_Setting::check_capabilities()Validate user capabilities whether the theme supports the setting.
- WP_Customize_Theme_Control::content_template()Render a JS template for theme display.
- WP_Customize_Themes_Panel::content_template()An Underscore (JS) template for this panel's content (but not its container).
- WP_Customize_Themes_Panel::render_template()An Underscore (JS) template for rendering this panel's container.
Source code
function current_user_can( $capability, ...$args ) { return user_can( wp_get_current_user(), $capability, ...$args );}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.
...$args parameter by adding it to the function signature.from the docblockAbout this page
- Parsed data
- Generated from the wordpress-develop 6.7.7 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.