• pkarjala

    (@pkarjala)


    I am working on an existing plugin where we perform a modification to a site_options field whenever a user’s role changes.

    Right now we are tracking this by hooking into user_profile_update_errors and set_user_role.

    user_profile_update_errors hook will always catch edits to a user by firing at the end of the user update function, whether done on the user edit page or the profile page for a given user.

    set_user_role hook will catch user role changes done through the wp-admin/users.php listing page using the bulk role change drop down window.

    The issue is that the set_user_role will also occur shortly after the user_profile_update_errors does when editing a user via the edit page or profile page, resulting in our functionality being run twice when it only needs to run once in those cases.

    This may just be a regression on our part; I think we were using user_profile_update_errors to look for any issues with the updating the user before calling our own functionality.

    Is it safe to assume that we can just hook into set_user_role, and that this is the only place where the codebase will have updated a user role? Or is there another hook I need to look for in case there is a different place where the user’s role may have been updated?

Viewing 7 replies - 1 through 7 (of 7 total)
  • Moderator t-p

    (@t-p)

    I am working on an existing plugin

    (1) What’s exact name of this plugin?
    (2) Plugin acquired from?

    Thread Starter pkarjala

    (@pkarjala)

    The plugin is https://wordpress.org/support/plugin/authorizer/, I am one of the developers on it.

    Moderator bcworkz

    (@bcworkz)

    I don’t have an answer to your question. I’m somewhat confident no one here knows offhand. I recommend placing an error_log() line or similar mechanism into your callbacks and do your own testing to find out for yourself. If you would please report your findings here, it’d be very much appreciated.

    In many cases it’s not detrimental for callbacks to execute multiple times per request since the end result is unchanged. For example, if I update “my_option” to “foo”, it doesn’t matter if it’s done once or five times, the end result is the same. Yes, there’s a minor performance hit. Typically negligible.

    It’s not uncommon for even a single action or filter hook to fire multiple times in one request. For the situation where multiple executions is a problem, such as when incrementing a counter, the callback should use one of the remove_*() functions to remove itself from the call stack so it will not be called again should the hook fire more than once per request. You should call the removal function straight away because waiting until the end could cause a race condition where execution starts again before the removal can be completed.

    Not only can a callback remove itself, it can remove other callbacks that were added elsewhere. Your set_user_role callback could remove the user_profile_update_errors callback and vice versa. I don’t think it’ll be necessary, but it’s an option that’s available to us.

    Thread Starter pkarjala

    (@pkarjala)

    Sounds good; I’m already part way down the rabbit hole on this one and getting a good handle on the workflow. We’re trying to hunt down a weird bug related to roles, and in the process trying to reduce complexity. If we can reduce the hooks when role changes occur to just one place it will make troubleshooting issues easier down the road.

    I’ll update with findings once I have more info.

    Thread Starter pkarjala

    (@pkarjala)

    OK, following the logic flow, user editing is handled in edit_user (see https://developer.wordpress.org/reference/functions/edit_user/).

    In edit_user are two functions that appear to get called whenever a user is updated or created: wp_update_user (https://developer.wordpress.org/reference/functions/wp_update_user/) and wp_insert_user (https://developer.wordpress.org/reference/functions/wp_insert_user/). The former actually calls and passes data to the latter, so we’ll focus there.

    wp_insert_user will update roles by calling set_role on the user object (see https://github.com/WordPress/wordpress-develop/blob/master/src/wp-includes/user.php#L2144). This will presumably only get called if everything else with the user has not thrown an error, based on where it is in the insert user call.

    set_role (https://developer.wordpress.org/reference/classes/wp_user/set_role/) has a hook in it called set_user_role (see https://developer.wordpress.org/reference/hooks/set_user_role/). This appears to be only called if there are no issues with the set_role and happens only if the user’s role actually was properly set.

    Based on this, it’s looking like it’s safe for us to remove references to user_profile_update_errors, which is called early on in edit_user and before either wp_update_user and wp_insert_user, which means it would not really be indicative of whether the user’s role has actually been set at that point since all of the subsequent logic to actually set the user’s role had not yet been called. Instead we should use the set_user_role hook to verify that a role has been changed successfully, and run our code based off of that.

    I hope that helps explain our refactoring work!

    Moderator bcworkz

    (@bcworkz)

    Thanks for the detailed summary! So “set_user_role” is indeed the only hook necessary and it is the one most suitable for your purpose.

    Thread Starter pkarjala

    (@pkarjala)

    It does look that way. We’re going to continue refactoring and I’ll check back in if we discover anything that’s an exception to this.

Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘Hooking into User Role changes’ is closed to new replies.