How to programatically change username (user_login)?

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?

Related posts

3 comments

  1. 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.

  2. 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.

  3. 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

Comments are closed.