I was surprised to discover that add_role() modifies the database and fails if the role already exists. There are two implications here, one first more serious than the other: 1) if you're in development and update your add_role code, you must first remove_role() 2) once you have it right, you should never have to run that code again.

So typically I have been putting my add_role() inside a wp_loaded action hook. And since I'm in development, I've also added a remove_role() before my add_role so I can be sure that if I modify my list of caps, it will actually take effect.

But clearly this is now being run every time a page of the blog is being accessed. Okay, I could put it in an admin-only action, or I could create a plugin page maybe under Users or Tools where this role can be created once. I guess I'm hoping there's a simpler, more elegant solution out there.

I don't imagine there's a run_once kind of action is there?

Or is the best practice just to add the role and then use add_cap() a bunch of times? And even then I imagine add_cap is accessing the db.

Just thinking in terms of the best way to reduce unncessary db access. What are your best practices?

  • Awesome! Thanks for this question..Just adding remove_role() function before add_role() helped me. Commented May 10, 2013 at 2:06
  • 1
    You could just do if ( ! wp_roles()->is_role( 'my_role' ) ) { add_role() }
    – Gavin
    Commented Dec 8, 2023 at 9:13
  • @Gavin I like that. Obviously this question is super old and maybe things have changed in WP about how roles are handled. I'd want to dive under the hood to see whether is_role() does some kind of database check or not. IIRC all the roles are loaded into memory somewhere in the startup sequence, so maybe is_role() doesn't need to access the DB for that info, so your solution could be pretty effective in that case!
    – Tom Auger
    Commented Dec 8, 2023 at 17:13
  • 1
    @TomAuger You got me curious so I looked up is_role() and all that the function contains is return isset( $this->role_names[ $role ] ); so yeah you're right. There's basically no overhead.
    – Gavin
    Commented Dec 10, 2023 at 15:41
  • 1
    @Gavin why don't you submit a new answer and we can revise the accepted answer, because your solution (in 2023) is wayyyy more straightforward.
    – Tom Auger
    Commented Dec 11, 2023 at 17:24

2 Answers 2


This may not have worked when this question was asked, but a much simpler solution is something like this:

if ( ! wp_roles()->is_role( 'my_role' ) ) {
    add_role( 'my_role', 'My Role', $capabilities );

The user roles and capabilities are saved in the database so once you have you have used add_role() its saved and then next load WordPress will know that role just like the built in roles.

Now if you look at the function add_role() more specifically at line 141 you will see that it only saves the role and capabilities in the database if the var $use_db is set to true (which he is by default) so you can simply change it before you call your add_role() function and the role won't be saved.


//globalize $wp_roles
global $wp_roles;
//set use_db to flase
$wp_roles->use_db = false;
//then add your role
$wp_roles->add_role( $role, $display_name, $capabilities );


If its in a test/development environment then i see no downside, but if you are on a live environment then you save the time it take to create that on role every load.

As for best practice run once, if in a plugin you should use register_activation_hook and for any thing else i use a simple custom made conditional function:

function run_once($key){
    $test_case = get_option('run_once');
    if (isset($test_case[$key]) && $test_case[$key]){
        return false;
        $test_case[$key] = true;
        return true;

if (run_once('add_user_role')){
    //do you stuff and it will only run once
  • Aw crap. I even knew about that too from some earlier rooting about the WP_Roles class. Can you think of any downside to NOT using the database for roles? And is there a WP best practice for doing something only once?
    – Tom Auger
    Commented Aug 11, 2011 at 17:31
  • Thanks for the update - I like the simplicity of the update_option solution
    – Tom Auger
    Commented Aug 16, 2011 at 9:32
  • Not really satisfying but it seems to be the best solution 👍
    – Blackbam
    Commented Feb 4, 2019 at 14:22
  • This function run_once increasing too much read/write database operations on each page load. Please don't use this. Commented Mar 11, 2020 at 18:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.