18

As in the title, how to programatically change user's login?

I wanted to use wp_insert_user function, but it appears that when updating current user, it doesn't change their username. Should I use $wpdb->update for that? If yes, how would code for changing username look like? What consequences would changing user login have, given that WordPress API doesn't allow changing usernames?

1
  • I know this is now 10 years ago you asked, I hope my answer still might help you :)
    – user75665
    Apr 14, 2023 at 5:14

3 Answers 3

28

I was sure that wp_update_user() should do this.

It even gets user_login as param, but it looks like it ignores it, when you set this param.

So this code looks OK, but it doesn't work as you wish it did :( :

wp_update_user(
    ['ID' => $user_id, 'user_login' => $new_login] 
);

You have to call custom SQL query to update user_login:

global $wpdb;
$wpdb->update(
    $wpdb->users, 
    ['user_login' => $new_user_login], 
    ['ID' => $user_id]
);

It works OK and I don't think it has any serious consequences, because WP uses users ID to assign posts/comments (and so on) to user.

The only problem I can think of is that when this user is currently logged in, he will be logged out after user_login change.

8
  • 3
    Not really, check the notes for this function (as well as the code): Please note that we cannot change the usernames through this function, in fact the usernames cannot be changed from the admin dashboard as well since WordPress does not allow the usernames to be updated. Jun 19, 2013 at 15:13
  • Thanks, that works great! No worries about logged in users, though, as I only allow username change for logged out users (account activation via e-mail). Jun 19, 2013 at 15:40
  • @pogoking One thing you might want to consider is that user or author permalinks might be broken after enabling users to change their usernames if pretty permalinks are enabled, so you might need to make measures to compensate for that.
    – Ahmad M
    Jun 19, 2013 at 16:19
  • 2
    i needed to add global $wpdb; to make it work
    – Mau
    May 13, 2015 at 2:52
  • 4
    Old post, but im looking at doing this myself, it would be worth checking there isnt another user with the same user_login registered on your site as the login form obviously uses this value to find accounts and match password hashes
    – haxxxton
    May 24, 2016 at 5:09
7

You might consider changing the user_nicename as well via the SQL query. Once this is done, all permalinks and other functions will work perfect.

1
  • Instead you might consider using an ID based profile slug instead of a nice_name one. It will ensure that the profile will have a universal url and you won't have random 404 pages popping up (I guess that's better for SEO). Mar 27, 2021 at 14:06
0

You must use the filter to alter the $data when the user is updated, you can not act with the action you would usually hook into a user update.

Also you must port several of the security and sanity measures WordPress enacts in its code into your new function.

What you can do:

  1. Hook into the wp_pre_insert_user_data filter. Make sure you do that only under correct permissions: add_filter( 'wp_pre_insert_user_data', 'prefix_change_user_login', 10, 4 );
  2. Now craft a function prefix_change_user_login where you grab the $data, the user_login from the $_POST and pass it through a number of validation and sanitisation (see https://github.com/WordPress/wordpress-develop/blob/bb27ffce6c3b10738008f9a054782945a0744960/src/wp-includes/user.php#L2076-L2498 for more details)
  3. Further of course you will have to allow actual edit of the user login field in the WP Admin Edit User screen, or, pass that value somehow else to your code.

An example of such function:

    /**
     * Update user login name
     *
     * @since x.x.x
     * @param array    $data {
     *     Values and keys for the user.
     *
     *     @type string $user_login      The user's login. Only included if $update == false
     *     @type string $user_pass       The user's password.
     *     @type string $user_email      The user's email.
     *     @type string $user_url        The user's url.
     *     @type string $user_nicename   The user's nice name. Defaults to a URL-safe version of user's login
     *     @type string $display_name    The user's display name.
     *     @type string $user_registered MySQL timestamp describing the moment when the user registered. Defaults to
     *                                   the current UTC timestamp.
     * }
     * @param bool     $update   Whether the user is being updated rather than created.
     * @param int|null $user_id  ID of the user to be updated, or NULL if the user is being created.
     * @param array    $userdata The raw array of data passed to wp_insert_user().
     */
    function prefix_change_user_login( $data, $update, $user_id, $userdata ){

        // DO NOT FORGET TO ADD PROPER NONCE AND REFERRER VALIDATION HERE!!!!

        $sanitized_user_login = sanitize_user( wp_unslash( $_POST['user_login'] ), true );// In our case we get the new user login from the POSTed form value in WP Admin. If this is different in your use case, adapt!!

        /**
         * Filters a username after it has been sanitized.
         *
         * This filter is called before the user is created or updated.
         *
         * @since 2.0.3
         *
         * @param string $sanitized_user_login Username after it has been sanitized.
         */
        $pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
        $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );

        // Remove any non-printable chars from the login string to see if we have ended up with an empty username.
        $user_login = trim( $pre_user_login );

        if ( empty( $user_login )
            || mb_strlen( $user_login ) > 60
            || username_exists( $user_login )
            || in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ), true )
        ) {
            return $data;
        }

        $data['user_login'] = $user_login;
        $user_nicename = sanitize_title( mb_substr( $user_login, 0, 50 ) );// Who knows what the WP Devs had smoked when they decided to make the nicename 10 characters less long than the login name?
        global $wpdb;
        $user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $user_nicename, $user_login ) );

        if ( $user_nicename_check ) {
            $suffix = 2;
            while ( $user_nicename_check ) {
                // user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
                $base_length         = 49 - mb_strlen( $suffix );
                $alt_user_nicename   = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
                $user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $alt_user_nicename, $user_login ) );
                $suffix++;
            }
            $user_nicename = $alt_user_nicename;
        }
        $data['user_nicename'] = $user_nicename;

        return $data;

    }

Now this will allow you to update the user login name, and it will also update the user nicename (used in archives for example) This of course still requires you to "enable" editing on the WP Admin area, but that shouldn't be too hard

Note that if any error happens in above code (username exists already, YadaYada) NO error is shown, instead, the user just updates and fallsback to what was prior.

Note that while this code is tested, it is up to you what you do with it, and there are REASONS why wp does not allow to update the user login

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.