1. drupal
    1. 5
    2. 6
    3. 7
    4. 8
5 – 6 user_save($account, $array = array(), $category = 'account')
7 – 8 user_save($account, $edit = array(), $category = 'account')

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

@todo D8: Drop $edit and fix user_save() to be consistent with others.


$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 {} 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.

▾ 23 functions call user_save()

ActionExampleTestCase::testActionExample in examples/action_example/action_example.test
Test Action Example.
action_example_unblock_user_action in examples/action_example/action_example.module
Action function for action_example_unblock_user_action.
BlockCacheTestCase::setUp in modules/block/block.test
ContactPersonalTestCase::testPersonalContactAccess in modules/contact/contact.test
Test personal contact form access.
DrupalWebTestCase::drupalCreateUser in modules/simpletest/drupal_web_test_case.php
Create a user with a given set of permissions. The permissions correspond to the names given on the privileges page.
EntityCrudHookTestCase::testUserHooks in modules/simpletest/tests/entity_crud_hook_test.test
Test hook invocations for CRUD operations on users.
FileFieldRevisionTestCase::testRevisions in modules/file/tests/file.test
Test creating multiple revisions of a node and managing the attached files.
FilterFormatUpgradePathTestCase::testFilterFormatUpgrade in modules/simpletest/tests/upgrade/upgrade.filter.test
Test a successful upgrade.
install_configure_form_submit in includes/
Forms API submit for the site configuration form.
overlay_user_dismiss_message in modules/overlay/overlay.module
Menu callback; dismisses the overlay accessibility message for this user.
SessionTestCase::testSessionSaveRegenerate in modules/simpletest/tests/session.test
Tests for drupal_save_session() and drupal_session_regenerate().
TriggerUserActionTestCase::testUserActionAssignmentExecution in modules/trigger/trigger.test
Tests user action assignment and execution.
UserSaveTestCase::testUserImport in modules/user/user.test
Test creating a user with arbitrary uid.
user_authenticate in modules/user/user.module
Try to validate the user's login credentials locally.
user_block_user_action in modules/user/user.module
Blocks the current user.
user_cancel_confirm_form_submit in modules/user/
Submit handler for the account cancellation confirm form.
user_external_login_register in modules/user/user.module
Helper function for authentication modules. Either logs in or registers the current user, based on username. Either way, the global $user object is populated and login tasks are performed.
user_multiple_role_edit in modules/user/user.module
Callback function for admin mass adding/deleting a user role.
user_profile_form_submit in modules/user/
Submit function for the user account and profile editing form.
user_register_submit in modules/user/user.module
Submit handler for the user registration form.
user_user_operations_block in modules/user/user.module
Callback function for admin mass blocking users.
user_user_operations_unblock in modules/user/user.module
Callback function for admin mass unblocking users.
_user_cancel in modules/user/user.module
Last batch processing step for cancelling a user account.


modules/user/user.module, line 420

function user_save($account, $edit = array(), $category = 'account') {
  $transaction = db_transaction();
  try {
    if (!empty($edit['pass'])) {
      // Allow alternate password hashing schemes.
      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/');
      $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);
      $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');
      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.
      if (count($account->roles) > 1) {
        $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,
    // Clear internal properties.
    // Clear the static loading cache.

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