4.6.x user.module user_save($account, $array = array(), $category = 'account')
4.7.x user.module user_save($account, $array = array(), $category = 'account')
5.x user.module user_save($account, $array = array(), $category = 'account')
6.x user.module user_save($account, $array = array(), $category = 'account')
7.x user.module user_save($account, $edit = array(), $category = 'account')

Save changes to a user account or add a new user.


$account: (optional) The user object to modify or add. If you want to modify an existing user account, you will need to ensure that (a) $account is an object, and (b) you have set $account->uid to the numeric user ID of the user account you wish to modify. If you want to create a new user account, you can set $account->is_new to TRUE or omit the $account->uid field.

$edit: An array of fields and values to save. For example array('name' => 'My name'). Key / value pairs added to the $edit['data'] will be serialized and saved in the {users.data} column.

$category: (optional) The category for storing profile information in.

Return value

A fully-loaded $user object upon successful save or FALSE if the save failed.

27 calls to user_save()
BlockCacheTestCase::setUp in modules/block/block.test
Sets up a Drupal site for running functional and integration tests.
ContactPersonalTestCase::testPersonalContactAccess in modules/contact/contact.test
Tests access to the personal contact form.
DrupalRenderTestCase::testDrupalRenderCache in modules/simpletest/tests/common.test
Tests caching of render items.
DrupalWebTestCase::drupalCreateUser in modules/simpletest/drupal_web_test_case.php
Create a user with a given set of permissions.
EntityCrudHookTestCase::testUserHooks in modules/simpletest/tests/entity_crud_hook_test.test
Tests hook invocations for CRUD operations on users.

... See full list


modules/user/user.module, line 422
Enables the user registration and login system.


function user_save($account, $edit = array(), $category = 'account') {
  $transaction = db_transaction();
  try {
    if (isset($edit['pass']) && strlen(trim($edit['pass'])) > 0) {
      // Allow alternate password hashing schemes.
      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
      $edit['pass'] = user_hash_password(trim($edit['pass']));
      // Abort if the hashing failed and returned FALSE.
      if (!$edit['pass']) {
        return FALSE;
    else {
      // Avoid overwriting an existing password with a blank password.
    if (isset($edit['mail'])) {
      $edit['mail'] = trim($edit['mail']);

    // Load the stored entity, if any.
    if (!empty($account->uid) && !isset($account->original)) {
      $account->original = entity_load_unchanged('user', $account->uid);

    if (empty($account)) {
      $account = new stdClass();
    if (!isset($account->is_new)) {
      $account->is_new = empty($account->uid);
    // Prepopulate $edit['data'] with the current value of $account->data.
    // Modules can add to or remove from this array in hook_user_presave().
    if (!empty($account->data)) {
      $edit['data'] = !empty($edit['data']) ? array_merge($account->data, $edit['data']) : $account->data;

    // Invoke hook_user_presave() for all modules.
    user_module_invoke('presave', $edit, $account, $category);

    // Invoke presave operations of Field Attach API and Entity API. Those APIs
    // require a fully-fledged and updated entity object. Therefore, we need to
    // copy any new property values of $edit into it.
    foreach ($edit as $key => $value) {
      $account->$key = $value;
    field_attach_presave('user', $account);
    module_invoke_all('entity_presave', $account, 'user');

    if (is_object($account) && !$account->is_new) {
      // Process picture uploads.
      if (!empty($account->picture->fid) && (!isset($account->original->picture->fid) || $account->picture->fid != $account->original->picture->fid)) {
        $picture = $account->picture;
        // If the picture is a temporary file move it to its final location and
        // make it permanent.
        if (!$picture->status) {
          $info = image_get_info($picture->uri);
          $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');

          // Prepare the pictures directory.
          file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
          $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']);

          // Move the temporary file into the final location.
          if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
            $picture->status = FILE_STATUS_PERMANENT;
            $account->picture = file_save($picture);
            file_usage_add($picture, 'user', 'user', $account->uid);
        // Delete the previous picture if it was deleted or replaced.
        if (!empty($account->original->picture->fid)) {
          file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
      elseif (isset($edit['picture_delete']) && $edit['picture_delete']) {
        file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
      // Save the picture object, if it is set. drupal_write_record() expects
      // $account->picture to be a FID.
      $picture = empty($account->picture) ? NULL : $account->picture;
      $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid;

      // Do not allow 'uid' to be changed.
      $account->uid = $account->original->uid;
      // Save changes to the user table.
      $success = drupal_write_record('users', $account, 'uid');
      // Restore the picture object.
      $account->picture = $picture;
      if ($success === FALSE) {
        // The query failed - better to abort the save than risk further
        // data loss.
        return FALSE;

      // Reload user roles if provided.
      if ($account->roles != $account->original->roles) {
          ->condition('uid', $account->uid)

        $query = db_insert('users_roles')->fields(array('uid', 'rid'));
        foreach (array_keys($account->roles) as $rid) {
          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
              'uid' => $account->uid,
              'rid' => $rid,

      // Delete a blocked user's sessions to kick them if they are online.
      if ($account->original->status != $account->status && $account->status == 0) {

      // If the password changed, delete all open sessions and recreate
      // the current one.
      if ($account->pass != $account->original->pass) {
        if ($account->uid == $GLOBALS['user']->uid) {

      // Save Field data.
      field_attach_update('user', $account);

      // Send emails after we have the new user object.
      if ($account->status != $account->original->status) {
        // The user's status is changing; conditionally send notification email.
        $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
        _user_mail_notify($op, $account);

      // Update $edit with any interim changes to $account.
      foreach ($account as $key => $value) {
        if (!property_exists($account->original, $key) || $value !== $account->original->$key) {
          $edit[$key] = $value;
      user_module_invoke('update', $edit, $account, $category);
      module_invoke_all('entity_update', $account, 'user');
    else {
      // Allow 'uid' to be set by the caller. There is no danger of writing an
      // existing user as drupal_write_record will do an INSERT.
      if (empty($account->uid)) {
        $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
      // Allow 'created' to be set by the caller.
      if (!isset($account->created)) {
        $account->created = REQUEST_TIME;
      $success = drupal_write_record('users', $account);
      if ($success === FALSE) {
        // On a failed INSERT some other existing user's uid may be returned.
        // We must abort to avoid overwriting their account.
        return FALSE;

      // Make sure $account is properly initialized.
      $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';

      field_attach_insert('user', $account);
      $edit = (array) $account;
      user_module_invoke('insert', $edit, $account, $category);
      module_invoke_all('entity_insert', $account, 'user');

      // Save user roles. Skip built-in roles, and ones that were already saved
      // to the database during hook calls.
      $rids_to_skip = array_merge(array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID), db_query('SELECT rid FROM {users_roles} WHERE uid = :uid', array(':uid' => $account->uid))->fetchCol());
      if ($rids_to_save = array_diff(array_keys($account->roles), $rids_to_skip)) {
        $query = db_insert('users_roles')->fields(array('uid', 'rid'));
        foreach ($rids_to_save as $rid) {
            'uid' => $account->uid,
            'rid' => $rid,
    // Clear internal properties.
    // Clear the static loading cache.

    return $account;
  catch (Exception $e) {
    watchdog_exception('user', $e);
    throw $e;


ookami.kb’s picture

To save fields data in user account we can act this way:

$account = user_load($uid); // Loading account
$edit = array(
  'field_some_custom_field' => array(
    'und' => array(
      0 => array(
        'value' => $new_value,
user_save($account, $edit);
stano.lacko’s picture

Depends on Field type.

Some types want other than standard "value" key name:

Link - "url"
Email - "email"

peterx’s picture

'tid' => $new_value,

drippydrop’s picture

I was looking and looking to find out how to add a new user to an organic group via a custom form.
Here is what I found out.

$new_user = array(
      'name' => $form_state['values']['name'],
      'pass' => user_password(),
      'mail' => $form_state['values']['mail'],
      'init' => $form_state['values']['mail'],
      'field_first_name' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_first_name']))),
      'field_last_name' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_last_name']))),
      'field_phone_number' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_phone_number']))),
      'status' => 1,
      'access' => REQUEST_TIME,
      'roles' => $roles,

// $account returns user object
$account = user_save(null, $new_user);

// $group is organic group node I want to add the brand new user to
$group = og_get_group('node', $node->nid);

// This adds user to the group
og_group($group->gid, array('entity' => $account));

Hope this helps some folks along with adding a new user to a group!

xcel’s picture

Wow, this was exactly what I was wanting to do.
I had to change the custom form to submitted node values but still, the whole process is there, well explained
I will not set 'access' though because it confuses admin since it says user has logged in whereas (s)he have not.
Anyway, many thanks you made me save a lot of time.

artfulrobot’s picture

This is good for a new user, but will only add membership to the $group, not edit all membership fields. If you want to absolutely set memberships to a known list then you can do this:

$new_user['og_user_node'][LANGUAGE_NONE] = array(
  array('target_id' => $group->gid),
  // ... add other groups here ...
paul2’s picture

Worth noting:

Even though the user object that is returned from user_load() (and by this function) stores an integer value in $user->picture (being the file's fid), that is not the correct data type to use if you wish to update the profile picture using this function. If you try to do so, somehow this function silently fails, and none of your other attempted edits will be saved either (still not sure why there isn't instead an error somewhere).

The correct way to update the profile picture is to pass its associated file object in the $edit array, not its integer fid.

resplin’s picture

The anonymous user in Drupal has a uid=0.

When execution reaches this line:

if (!empty($account->uid) && !isset($account->original)) {

The call to empty returns true because of the zero value, and user_save decides it is a new user. This will fail because the new user has a duplicate username.

So if you need to edit all users (I am setting a default value for a custom field), you need to strip uid=0 from the list and deal with it as a special case.

peterx’s picture

User data is going away in Drupal 8 and user data is difficult to list. Move your data into fields. You can then list users that have your data and you can list their values.

Each extra field will slow down your database a little bit. If you have several related items, you might want to group them the way the address field module groups address fields in one row.

peterx’s picture

When you save a user, other modules have their user hooks called to modify the user. If you get weird results, such as missing fields, it can be another module responding to a hook and changing stuff.

Your save might be followed by another module changing the same field. Another module might respond as if you are editing the user in the admin page. A good test is to visit the user in the admin edit page, note all the values, run your save, then visit the user in the admin edit page and look for unexpected changes.

kiamlaluno’s picture

In Drupal 8, this function has been replaced from Entity::save().

ih8grep’s picture

The example user_save($user, array("key" => "value")); is wrong! Instead do something like this

global $user;
$user_fields = user_load($user->uid);
$myfield = $user_fields->field_myfield;
$myfield['und'][0]['value'] = "This value was set programatically";
guy_schneerson’s picture

When searching for examples of updating existing users programmatically you find many variations in code and in most cases they all work.

The reason for this is that $edit (should contain changes only) is copied into $account by user_save().
As a result if you assign a changed field to account instead of to the $edit the result will be the same


  $account = user_load($uid);

  $edit = array();
  $edit['field_first_name'][LANGUAGE_NONE][0]['value'] = 'Guy';
  $edit['field_last_name'][LANGUAGE_NONE][0]['value'] = 'Schneerson';

  user_save($account, $edit);


  $account = user_load($uid);
  $account->field_first_name[LANGUAGE_NONE][0]['value'] = 'Guy';
  $account->field_last_name[LANGUAGE_NONE][0]['value'] = 'Schneerson';

  $edit = array();

  user_save($account, $edit);

Will both work, however the first one is the correct way of doing things.

guy_schneerson’s picture

$edit should contain changes you want saved, however if you have a multi value element you need to include all its values
for example adding a role

Wrong way: will remove all roles except 'new role'

  $account = user_load($uid);
  $edit = array();
  $edit['roles'][NEW_ROLE] = 'new role';
  user_save($account, $edit);

Correct way: will add 'new role' to existing roles

  $account = user_load($uid);
  $edit = array();
  $edit['roles'] = $account->roles;
  $edit['roles'][NEW_ROLE] = 'new role';
  user_save($account, $edit);
Martin P.’s picture

I wanna ask you something for this part:

$account = user_load($uid); // Loading account
$edit = array(
  'field_some_custom_field' => array(
    'und' => array(
      0 => array(
        'value' => $new_value,
user_save($account, $edit);

What is 'field_some_custom_field' ? Where do i find, what name i have to write there? I want to add custom hidden fields as targets for Feeds Module (to import users automatically one time a day).

Maybe somebody can help me how to add this function in the FeedsUserProcessor.inc.php

I have found out, that you have to add 2 parts of code for every target you want to add. I have found the code for Adding User Roles for example:

    if ($account->roles_list) {
     $roles = explode(',', $account->roles_list);
     foreach ($roles as $role_name) {
       $role_name = trim($role_name);
       if (!$role = user_role_load_by_name($role_name)) {
         // Create new role if role doesn't exist
         $role = new stdClass();
         $role->name = $role_name;
         $role = user_role_load_by_name($role->name);
       $account->roles[$role->rid] = $role->name;


	  'roles_list' => array(
		'name' => t('User roles'),
		'description' => t('User roles'),

I also know, that the first part is to use / work with the the input (but i dont know where it gets saved) and the second part is for naming and descibing the target for the backend UI. But i am not good enough to use this knowledge for adding other/additional user fields.

Maybe somebody could make an example?

tinny’s picture

$account->name = 'me';
$account->pass = 'xxx';
$account->mail = 'x@x.com';
$account->status = 1;

// 3 and 4 are role id's
$roles = array(3 => true, 4 => true);
$account->roles = $roles;

skruf’s picture

Beware. I tried this and the password was saved un-encrypted in the database. You need to add it to the second argument:

user_save($account, array('pass' => 'xxx'));

asanchez75’s picture

$newUser = array(
'name' => 'username',
'pass' => 'password', // note: do not md5 the password
'mail' => 'email address',
'status' => 1,
'init' => 'email address'
user_save(null, $newUser);

See https://drupal.org/node/1969192

tutumlum’s picture

Fill in all of your fields for an example user and run the following code:

$user = user_load(1);

Then, copy the array and modify required parts...

Fernando Vesga’s picture

Or just install Devel module and go to the user page and you will see a Devel tab with al the info.
Or, after install it, paste in your code the following

sheldonkreger’s picture

I found that if I passed the entire user object when saving an existing user, it would fail to update the user but throw no error.

For $account, you must pass an object containing ONLY the uid of the user when you want to do an update.

tamerzg’s picture

In Drupal 8 instead of user_save() you would use $user->save()

rujinxero’s picture

I need to change a users email without a confirmation email is that possible?

johnkareoke’s picture


$account = new stdClass();
$account->name = 'johnny';
$account->mail = 'johnny@google.com';
$account->init = 'johnny@google.com';
$account->pass = user_password('ferrari599');
$account->status = 1;
user_save(Null, $account);
if ($account->uid) {
  drupal_set_message('Created new user with id %uid', array('%uid' => $account->uid));


I get the following error:

Error: Call to undefined function field_attach_presave() in user_save() (line 468 of ./modules/user/user.module).

Really can't seem to figure this out. Any help appreciated.