diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 36e4f77a696ae955d60b32388b123da72db903ad..125852a9e567113abc21eaba8185cc9efe4b905c 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1892,8 +1892,13 @@ function install_configure_form_submit($form, &$form_state) {
 
   // We precreated user 1 with placeholder values. Let's save the real values.
   $account = user_load(1);
-  $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1, 'timezone' => $form_state['values']['date_default_timezone']);
-  user_save($account, array_merge($form_state['values']['account'], $merge_data));
+  $account->init = $account->mail = $form_state['values']['account']['mail'];
+  $account->roles = !empty($account->roles) ? $account->roles : array();
+  $account->status = 1;
+  $account->timezone = $form_state['values']['date_default_timezone'];
+  $account->pass = $form_state['values']['account']['pass'];
+  $account->name = $form_state['values']['account']['name'];
+  $account->save();
   // Load global $user and perform final login tasks.
   $user = user_load(1);
   user_login_finalize();
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 839f86e89db0cc15128e52cf672764b26e1e6105..069e8792bc450b07a60bc846715390b4f0aa64a5 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -628,9 +628,9 @@ function block_form_user_profile_form_alter(&$form, &$form_state) {
 /**
  * Implements hook_user_presave().
  */
-function block_user_presave(&$edit, $account) {
-  if (isset($edit['block'])) {
-    $edit['data']['block'] = $edit['block'];
+function block_user_presave($account) {
+  if (isset($account->block)) {
+    $account->data['block'] = $account->block;
   }
 }
 
diff --git a/core/modules/block/block.test b/core/modules/block/block.test
index 65ddcce76f9744316d46ff5afe2775bb6472b0f2..f4a95496bbcf6a0f8927e67ce0f6b90d1731b2ca 100644
--- a/core/modules/block/block.test
+++ b/core/modules/block/block.test
@@ -542,8 +542,8 @@ class BlockCacheTestCase extends DrupalWebTestCase {
     $this->normal_user_alt = $this->drupalCreateUser();
     // Sync the roles, since drupalCreateUser() creates separate roles for
     // the same permission sets.
-    user_save($this->normal_user_alt, array('roles' => $this->normal_user->roles));
     $this->normal_user_alt->roles = $this->normal_user->roles;
+    $this->normal_user_alt->save();
 
     // Enable our test block.
     $edit['blocks[block_test_test_cache][region]'] = 'sidebar_first';
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 89ce0bce8288c93b7e6ccdcdf3f1264c137beb4f..c695c7e2eaf37a14e074b36d2e6b4b7c83a121c2 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -233,8 +233,8 @@ function contact_form_user_profile_form_alter(&$form, &$form_state) {
 /**
  * Implements hook_user_presave().
  */
-function contact_user_presave(&$edit, $account) {
-  $edit['data']['contact'] = isset($edit['contact']) ? $edit['contact'] : variable_get('contact_default_status', 1);
+function contact_user_presave($account) {
+  $account->data['contact'] = isset($account->contact) ? $account->contact : variable_get('contact_default_status', 1);
 }
 
 /**
diff --git a/core/modules/contact/contact.test b/core/modules/contact/contact.test
index d7f26acf17d1ab75f73132f195cce50765d7304d..490d8f8b380341327dbe721fd1164eb5d2d6cf03 100644
--- a/core/modules/contact/contact.test
+++ b/core/modules/contact/contact.test
@@ -374,7 +374,8 @@ class ContactPersonalTestCase extends DrupalWebTestCase {
 
     // Re-create our contacted user as a blocked user.
     $this->contact_user = $this->drupalCreateUser();
-    user_save($this->contact_user, array('status' => 0));
+    $this->contact_user->status = 0;
+    $this->contact_user->save();
 
     // Test that blocked users can still be contacted by admin.
     $this->drupalGet('user/' . $this->contact_user->uid . '/contact');
diff --git a/core/modules/entity/tests/entity_crud_hook_test.test b/core/modules/entity/tests/entity_crud_hook_test.test
index be59e99a76365908c460a936eecee0364cc8ea06..f582c7406c5299640c54ea1008a6e9d1bcdddf98 100644
--- a/core/modules/entity/tests/entity_crud_hook_test.test
+++ b/core/modules/entity/tests/entity_crud_hook_test.test
@@ -356,16 +356,15 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
    * Tests hook invocations for CRUD operations on users.
    */
   public function testUserHooks() {
-    $edit = array(
+    $account = entity_create('user', array(
       'name' => 'Test user',
       'mail' => 'test@example.com',
       'created' => REQUEST_TIME,
       'status' => 1,
       'language' => 'en',
-    );
-    $account = (object) $edit;
+    ));
     $_SESSION['entity_crud_hook_test'] = array();
-    $account = user_save($account, $edit);
+    $account->save();
 
     $this->assertHookMessageOrder(array(
       'entity_crud_hook_test_user_presave called',
@@ -375,7 +374,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     ));
 
     $_SESSION['entity_crud_hook_test'] = array();
-    $account = user_load($account->uid);
+    user_load($account->uid);
 
     $this->assertHookMessageOrder(array(
       'entity_crud_hook_test_entity_load called for type user',
@@ -383,8 +382,8 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
     ));
 
     $_SESSION['entity_crud_hook_test'] = array();
-    $edit['name'] = 'New name';
-    $account = user_save($account, $edit);
+    $account->name = 'New name';
+    $account->save();
 
     $this->assertHookMessageOrder(array(
       'entity_crud_hook_test_user_presave called',
diff --git a/core/modules/file/tests/file.test b/core/modules/file/tests/file.test
index 05083fc517a1d7a640b9fe008d3588e85d8f8551..92b7ef37290c7bff403fd049f42ea2cd4a85087c 100644
--- a/core/modules/file/tests/file.test
+++ b/core/modules/file/tests/file.test
@@ -709,9 +709,8 @@ class FileFieldRevisionTestCase extends FileFieldTestCase {
 
     // Attach the second file to a user.
     $user = $this->drupalCreateUser();
-    $edit = (array) $user;
-    $edit[$field_name][LANGUAGE_NOT_SPECIFIED][0] = (array) $node_file_r3;
-    user_save($user, $edit);
+    $user->{$field_name}[LANGUAGE_NOT_SPECIFIED][0] = (array) $node_file_r3;
+    $user->save();
     $this->drupalGet('user/' . $user->uid . '/edit');
 
     // Delete the third revision and check that the file is not deleted yet.
diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module
index 8b404f5880fae45ba63336382c7beb48c8889844..38813203425b3853d086748e857f8d9d1f3480b7 100644
--- a/core/modules/openid/openid.module
+++ b/core/modules/openid/openid.module
@@ -83,15 +83,15 @@ function openid_help($path, $arg) {
 /**
  * Implements hook_user_insert().
  */
-function openid_user_insert(&$edit, $account) {
-  if (!empty($edit['openid_claimed_id'])) {
+function openid_user_insert($account) {
+  if (!empty($account->openid_claimed_id)) {
     // The user has registered after trying to log in via OpenID.
     if (variable_get('user_email_verification', TRUE)) {
       drupal_set_message(t('Once you have verified your e-mail address, you may log in via OpenID.'));
     }
-    user_set_authmaps($account, array('authname_openid' => $edit['openid_claimed_id']));
+    user_set_authmaps($account, array('authname_openid' => $account->openid_claimed_id));
     unset($_SESSION['openid']);
-    unset($edit['openid_claimed_id']);
+    unset($account->openid_claimed_id);
   }
 }
 
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 144c2aba75bcfceeadabc9e5f7e0f8002c1ed641..e2ecd19144a8a03793cbbeaac46e73204f4820ae 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -102,9 +102,9 @@ function overlay_form_user_profile_form_alter(&$form, &$form_state) {
 /**
  * Implements hook_user_presave().
  */
-function overlay_user_presave(&$edit, $account) {
-  if (isset($edit['overlay'])) {
-    $edit['data']['overlay'] = $edit['overlay'];
+function overlay_user_presave($account) {
+  if (isset($account->overlay)) {
+    $account->data['overlay'] = $account->overlay;
   }
 }
 
@@ -311,7 +311,9 @@ function overlay_user_dismiss_message() {
     return MENU_ACCESS_DENIED;
   }
   else {
-    user_save(user_load($user->uid), array('data' => array('overlay_message_dismissed' => 1)));
+    $account = user_load($user->uid);
+    $account->data['overlay_message_dismissed'] = 1;
+    $account->save();
     drupal_set_message(t('The message has been dismissed. You can change your overlay settings at any time by visiting your profile page.'));
     // Destination is normally given. Go to the user profile as a fallback.
     drupal_goto('user/' . $user->uid . '/edit');
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 591e8a1af6dda9fe14bafa29793fb4aaa269432a..b996bbebca7257e7d03413ae8126bcc2c660803a 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -1126,7 +1126,8 @@ protected function drupalCreateUser(array $permissions = array()) {
       $edit['roles'] = array($rid => $rid);
     }
 
-    $account = user_save(drupal_anonymous_user(), $edit);
+    $account = entity_create('user', $edit);
+    $account->save();
 
     $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
     if (empty($account->uid)) {
@@ -1230,7 +1231,7 @@ protected function checkPermissions(array $permissions, $reset = FALSE) {
    *
    * @see drupalCreateUser()
    */
-  protected function drupalLogin(stdClass $user) {
+  protected function drupalLogin($user) {
     if ($this->loggedInUser) {
       $this->drupalLogout();
     }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 0976d1b873fcd409dfaa9b00b99fefd9e1e5f5d5..d9130a84cfc7206f66af8cea27b8ad984311d171 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2021,15 +2021,14 @@ function system_form_user_register_form_alter(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_user_insert().
+ * Implements hook_user_presave().
  */
-function system_user_presave(&$edit, $account) {
+function system_user_presave($account) {
   if (variable_get('configurable_timezones', 1) && empty($account->timezone) && !variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT)) {
     $account->timezone = variable_get('date_default_timezone', '');
   }
 }
 
-
 /**
  * Implements hook_user_login().
  */
diff --git a/core/modules/system/tests/session.test b/core/modules/system/tests/session.test
index 017a8babffe7cadf8c748f366241231d5381a23e..5cc8fe9476ea492955d24e69a68d61eab174ba4f 100644
--- a/core/modules/system/tests/session.test
+++ b/core/modules/system/tests/session.test
@@ -41,8 +41,8 @@ class SessionTestCase extends DrupalWebTestCase {
 
     // Verify that the session is regenerated if a module calls exit
     // in hook_user_login().
-    user_save($user, array('name' => 'session_test_user'));
     $user->name = 'session_test_user';
+    $user->save();
     $this->drupalGet('session-test/id');
     $matches = array();
     preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
diff --git a/core/modules/user/lib/Drupal/user/User.php b/core/modules/user/lib/Drupal/user/User.php
new file mode 100644
index 0000000000000000000000000000000000000000..b39d88d9ce27874276cf67bd6ecdb7ccc6b4afbc
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/User.php
@@ -0,0 +1,147 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\user\User.
+ */
+
+namespace Drupal\user;
+
+/**
+ * @todo Switch to PSR-0 for the Entity classes: http://drupal.org/node/1495024
+ */
+use Entity;
+
+/**
+ * Defines the user entity class.
+ */
+class User extends Entity {
+
+  /**
+   * The user ID.
+   *
+   * @var integer
+   */
+  public $uid;
+
+  /**
+   * The unique user name.
+   *
+   * @var string
+   */
+  public $name = '';
+
+  /**
+   * The user's password (hashed).
+   *
+   * @var string
+   */
+  public $pass;
+
+  /**
+   * The user's email address.
+   *
+   * @var string
+   */
+  public $mail = '';
+
+  /**
+   * The user's default theme.
+   *
+   * @var string
+   */
+  public $theme;
+
+  /**
+   * The user's signature.
+   *
+   * @var string
+   */
+  public $signature;
+
+  /**
+   * The user's signature format.
+   *
+   * @var string
+   */
+  public $signature_format;
+
+  /**
+   * The timestamp when the user was created.
+   *
+   * @var integer
+   */
+  public $created;
+
+  /**
+   * The timestamp when the user last accessed the site. A value of 0 means the
+   * user has never accessed the site.
+   *
+   * @var integer
+   */
+  public $access = 0;
+
+  /**
+   * The timestamp when the user last logged in. A value of 0 means the user has
+   * never logged in.
+   *
+   * @var integer
+   */
+  public $login = 0;
+
+  /**
+   * Whether the user is active (1) or blocked (0).
+   *
+   * @var integer
+   */
+  public $status = 0;
+
+  /**
+   * The user's timezone.
+   *
+   * @var string
+   */
+  public $timezone;
+
+  /**
+   * The user's langcode.
+   *
+   * @var string
+   */
+  public $langcode = LANGUAGE_NOT_SPECIFIED;
+
+  /**
+   * The user's preferred langcode for receiving emails and viewing the site.
+   *
+   * @var string
+   */
+  public $preferred_langcode = LANGUAGE_NOT_SPECIFIED;
+
+  /**
+   * The file ID of the user's picture.
+   *
+   * @var integer
+   */
+  public $picture = 0;
+
+  /**
+   * The email address used for initial account creation.
+   *
+   * @var string
+   */
+  public $init = '';
+
+  /**
+   * The user's roles.
+   *
+   * @var array
+   */
+  public $roles = array();
+
+  /**
+   * Implements EntityInterface::id().
+   */
+  public function id() {
+    return $this->uid;
+  }
+}
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd0d9b96cd59366db5fdab5cbc0ef1073584ed78
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -0,0 +1,227 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\user\UserStorageController.
+ */
+
+namespace Drupal\user;
+
+/**
+ * @todo Switch to PSR-0 for the Entity classes: http://drupal.org/node/1495024
+ */
+use EntityDatabaseStorageController;
+use EntityInterface;
+
+/**
+ * Controller class for users.
+ *
+ * This extends the EntityDatabaseStorageController class, adding required
+ * special handling for user objects.
+ */
+class UserStorageController extends EntityDatabaseStorageController {
+
+  /**
+   * Overrides EntityDatabaseStorageController::attachLoad().
+   */
+  function attachLoad(&$queried_users, $revision_id = FALSE) {
+    // Build an array of user picture IDs so that these can be fetched later.
+    $picture_fids = array();
+    foreach ($queried_users as $key => $record) {
+      if ($record->picture) {
+        $picture_fids[] = $record->picture;
+      }
+      $queried_users[$key]->data = unserialize($record->data);
+      $queried_users[$key]->roles = array();
+      if ($record->uid) {
+        $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+      }
+      else {
+        $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
+      }
+    }
+
+    // Add any additional roles from the database.
+    $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
+    foreach ($result as $record) {
+      $queried_users[$record->uid]->roles[$record->rid] = $record->name;
+    }
+
+    // Add the full file objects for user pictures if enabled.
+    if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
+      $pictures = file_load_multiple($picture_fids);
+      foreach ($queried_users as $entity) {
+        if (!empty($entity->picture) && isset($pictures[$entity->picture])) {
+          $entity->picture = $pictures[$entity->picture];
+        }
+      }
+    }
+    // Call the default attachLoad() method. This will add fields and call
+    // hook_user_load().
+    parent::attachLoad($queried_users, $revision_id);
+  }
+
+  /**
+   * Overrides EntityDatabaseStorageController::create().
+   */
+  public function create(array $values) {
+    if (!isset($values['created'])) {
+      $values['created'] = REQUEST_TIME;
+    }
+    // Users always have the authenticated user role.
+    $values['roles'][DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+
+    return parent::create($values);
+  }
+
+  /**
+   * Overrides EntityDatabaseStorageController::save().
+   */
+  public function save(EntityInterface $entity) {
+    if (empty($entity->uid)) {
+      $entity->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
+      $entity->enforceIsNew();
+    }
+    parent::save($entity);
+  }
+
+  /**
+   * Overrides EntityDatabaseStorageController::preSave().
+   */
+  protected function preSave(EntityInterface $entity) {
+    // Update the user password if it has changed.
+    if ($entity->isNew() || (!empty($entity->pass) && $entity->pass != $entity->original->pass)) {
+      // Allow alternate password hashing schemes.
+      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/includes/password.inc');
+      $entity->pass = user_hash_password(trim($entity->pass));
+      // Abort if the hashing failed and returned FALSE.
+      if (!$entity->pass) {
+        throw new EntityMalformedException('The entity does not have a password.');
+      }
+    }
+
+    if (!empty($entity->picture_upload)) {
+      $entity->picture = $entity->picture_upload;
+    }
+    // Delete the picture if the submission indicates that it should be deleted
+    // and no replacement was submitted.
+    elseif (!empty($entity->picture_delete)) {
+      $entity->picture = 0;
+      file_usage_delete($entity->original->picture, 'user', 'user', $entity->uid);
+      file_delete($entity->original->picture);
+    }
+
+    if (!$entity->isNew()) {
+      // Process picture uploads.
+      if (!empty($entity->picture->fid) && (!isset($entity->original->picture->fid) || $entity->picture->fid != $entity->original->picture->fid)) {
+        $picture = $entity->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-' . $entity->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;
+            $entity->picture = file_save($picture);
+            file_usage_add($picture, 'user', 'user', $entity->uid);
+          }
+        }
+        // Delete the previous picture if it was deleted or replaced.
+        if (!empty($entity->original->picture->fid)) {
+          file_usage_delete($entity->original->picture, 'user', 'user', $entity->uid);
+          file_delete($entity->original->picture);
+        }
+      }
+      $entity->picture = empty($entity->picture->fid) ? 0 : $entity->picture->fid;
+
+      // If the password is empty, that means it was not changed, so use the
+      // original password.
+      if (empty($entity->pass)) {
+        $entity->pass = $entity->original->pass;
+      }
+    }
+
+    // Prepare user roles.
+    if (isset($entity->roles)) {
+      $entity->roles = array_filter($entity->roles);
+    }
+
+    // Move account cancellation information into $entity->data.
+    foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
+      if (isset($entity->{$key})) {
+        $entity->data[$key] = $entity->{$key};
+      }
+    }
+  }
+
+  /**
+   * Overrides EntityDatabaseStorageController::postSave().
+   */
+  protected function postSave(EntityInterface $entity, $update) {
+
+    if ($update) {
+      // If the password has been changed, delete all open sessions for the
+      // user and recreate the current one.
+      if ($entity->pass != $entity->original->pass) {
+        drupal_session_destroy_uid($entity->uid);
+        if ($entity->uid == $GLOBALS['user']->uid) {
+          drupal_session_regenerate();
+        }
+      }
+
+      // Remove roles that are no longer enabled for the user.
+      $entity->roles = array_filter($entity->roles);
+
+      // Reload user roles if provided.
+      if ($entity->roles != $entity->original->roles) {
+        db_delete('users_roles')
+          ->condition('uid', $entity->uid)
+          ->execute();
+
+        $query = db_insert('users_roles')->fields(array('uid', 'rid'));
+        foreach (array_keys($entity->roles) as $rid) {
+          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+            $query->values(array(
+              'uid' => $entity->uid,
+              'rid' => $rid,
+            ));
+          }
+        }
+        $query->execute();
+      }
+
+      // If the user was blocked, delete the user's sessions to force a logout.
+      if ($entity->original->status != $entity->status && $entity->status == 0) {
+        drupal_session_destroy_uid($entity->uid);
+      }
+
+      // Send emails after we have the new user object.
+      if ($entity->status != $entity->original->status) {
+        // The user's status is changing; conditionally send notification email.
+        $op = $entity->status == 1 ? 'status_activated' : 'status_blocked';
+        _user_mail_notify($op, $entity);
+      }
+    }
+    else {
+      // Save user roles.
+      if (count($entity->roles) > 1) {
+        $query = db_insert('users_roles')->fields(array('uid', 'rid'));
+        foreach (array_keys($entity->roles) as $rid) {
+          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+            $query->values(array(
+              'uid' => $entity->uid,
+              'rid' => $rid,
+            ));
+          }
+        }
+        $query->execute();
+      }
+    }
+  }
+}
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index dc16906470decf3e21c0b20ac941b03e045ca31b..1b8a2532631e8524121b80a51258d0a0e05b8293 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -227,67 +227,57 @@ function hook_user_operations() {
 }
 
 /**
- * A user account is about to be created or updated.
+ * Act on a user account being inserted or updated.
  *
- * This hook is primarily intended for modules that want to store properties in
- * the serialized {users}.data column, which is automatically loaded whenever a
- * user account object is loaded, modules may add to $edit['data'] in order
- * to have their data serialized on save.
+ * This hook is invoked before the user account is saved to the database.
+ *
+ * Modules that want to store properties in the serialized {users}.data column,
+ * which is automatically loaded whenever a user account object is loaded, may
+ * add their properties to $account->data in order to have their data serialized
+ * on save.
  *
- * @param $edit
- *   The array of form values submitted by the user.
  * @param $account
- *   The user object on which the operation is performed.
+ *   The user account object.
  *
  * @see hook_user_insert()
  * @see hook_user_update()
  */
-function hook_user_presave(&$edit, $account) {
+function hook_user_presave($account) {
   // Make sure that our form value 'mymodule_foo' is stored as
   // 'mymodule_bar' in the 'data' (serialized) column.
-  if (isset($edit['mymodule_foo'])) {
-    $edit['data']['mymodule_bar'] = $edit['mymodule_foo'];
+  if (isset($account->mymodule_foo)) {
+    $account->data['mymodule_bar'] = $account->mymodule_foo;
   }
 }
 
 /**
- * A user account was created.
+ * Respond to creation of a new user account.
  *
- * The module should save its custom additions to the user object into the
- * database.
- *
- * @param $edit
- *   The array of form values submitted by the user.
  * @param $account
- *   The user object on which the operation is being performed.
+ *   The user account object.
  *
  * @see hook_user_presave()
  * @see hook_user_update()
  */
-function hook_user_insert(&$edit, $account) {
-  db_insert('mytable')
+function hook_user_insert($account) {
+  db_insert('user_changes')
     ->fields(array(
-      'myfield' => $edit['myfield'],
       'uid' => $account->uid,
+      'created' => time(),
     ))
     ->execute();
 }
 
 /**
- * A user account was updated.
- *
- * Modules may use this hook to update their user data in a custom storage
- * after a user account has been updated.
+ * Respond to updates to a user account.
  *
- * @param $edit
- *   The array of form values submitted by the user.
  * @param $account
- *   The user object on which the operation is performed.
+ *   The user account object.
  *
  * @see hook_user_presave()
  * @see hook_user_insert()
  */
-function hook_user_update(&$edit, $account) {
+function hook_user_update($account) {
   db_insert('user_changes')
     ->fields(array(
       'uid' => $account->uid,
diff --git a/core/modules/user/user.entity.inc b/core/modules/user/user.entity.inc
deleted file mode 100644
index 5549c7707188430085a83bdcb2471d3f8d103fab..0000000000000000000000000000000000000000
--- a/core/modules/user/user.entity.inc
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * @file Controller class for users.
- */
-
-/**
- * Controller class for users.
- *
- * This extends the DrupalDefaultEntityController class, adding required
- * special handling for user objects.
- */
-class UserController extends DrupalDefaultEntityController {
-
-  function attachLoad(&$queried_users, $revision_id = FALSE) {
-    // Build an array of user picture IDs so that these can be fetched later.
-    $picture_fids = array();
-    foreach ($queried_users as $key => $record) {
-      $picture_fids[] = $record->picture;
-      $queried_users[$key]->data = unserialize($record->data);
-      $queried_users[$key]->roles = array();
-      if ($record->uid) {
-        $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
-      }
-      else {
-        $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
-      }
-    }
-
-    // Add any additional roles from the database.
-    $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
-    foreach ($result as $record) {
-      $queried_users[$record->uid]->roles[$record->rid] = $record->name;
-    }
-
-    // Add the full file objects for user pictures if enabled.
-    if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
-      $pictures = file_load_multiple($picture_fids);
-      foreach ($queried_users as $account) {
-        if (!empty($account->picture) && isset($pictures[$account->picture])) {
-          $account->picture = $pictures[$account->picture];
-        }
-        else {
-          $account->picture = NULL;
-        }
-      }
-    }
-    // Call the default attachLoad() method. This will add fields and call
-    // hook_user_load().
-    parent::attachLoad($queried_users, $revision_id);
-  }
-}
diff --git a/core/modules/user/user.info b/core/modules/user/user.info
index d887352760e47d9874df109c9e27a4bab50a4dfe..8dad5a361695cf71c081d28098a7e238f6640031 100644
--- a/core/modules/user/user.info
+++ b/core/modules/user/user.info
@@ -3,7 +3,6 @@ description = Manages the user registration and login system.
 package = Core
 version = VERSION
 core = 8.x
-files[] = user.entity.inc
 files[] = user.test
 required = TRUE
 configure = admin/config/people
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 639755ba4b7c528ad03115a0b7da57adb79b2a4c..b8f7f801434a7bddd14a3cda5f6c06d89ea16a53 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -83,12 +83,8 @@ function user_help($path, $arg) {
  * be passed by reference.
  *
  * @param $type
- *   A text string that controls which user hook to invoke.  Valid choices are:
- *   - cancel: Invokes hook_user_cancel().
- *   - insert: Invokes hook_user_insert().
+ *   A text string that controls which user hook to invoke. Valid choices are:
  *   - login: Invokes hook_user_login().
- *   - presave: Invokes hook_user_presave().
- *   - update: Invokes hook_user_update().
  * @param $edit
  *   An associative array variable containing form values to be passed
  *   as the first parameter of the hook function.
@@ -145,14 +141,15 @@ function user_theme() {
  * Implements hook_entity_info().
  */
 function user_entity_info() {
-  $return = array(
+  return array(
     'user' => array(
       'label' => t('User'),
-      'controller class' => 'UserController',
+      'controller class' => 'Drupal\user\UserStorageController',
       'base table' => 'users',
       'uri callback' => 'user_uri',
       'label callback' => 'user_label',
       'fieldable' => TRUE,
+      'entity class' => 'Drupal\user\User',
       'entity keys' => array(
         'id' => 'uid',
       ),
@@ -173,7 +170,6 @@ function user_entity_info() {
       ),
     ),
   );
-  return $return;
 }
 
 /**
@@ -359,224 +355,6 @@ function user_load_by_name($name) {
   return reset($users);
 }
 
-/**
- * Save changes to a user account or add a new user.
- *
- * @param $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.
- * @param $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.
- *
- * @return
- *   A fully-loaded $user object upon successful save or FALSE if the save failed.
- *
- * @todo D8: Drop $edit and fix user_save() to be consistent with others.
- */
-function user_save($account, $edit = array()) {
-  $transaction = db_transaction();
-  try {
-    if (!empty($edit['pass'])) {
-      // Allow alternate password hashing schemes.
-      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'core/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.
-      unset($edit['pass']);
-    }
-
-    // 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);
-
-    // 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;
-    }
-    // Default the user entity language to the user's preferred language.
-    if (!isset($account->langcode) && isset($account->preferred_langcode)) {
-      $account->langcode = $account->preferred_langcode;
-    }
-    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);
-          file_delete($account->original->picture);
-        }
-      }
-      elseif (isset($edit['picture_delete']) && $edit['picture_delete']) {
-        file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
-        file_delete($account->original->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');
-      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) {
-        db_delete('users_roles')
-          ->condition('uid', $account->uid)
-          ->execute();
-
-        $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))) {
-            $query->values(array(
-              'uid' => $account->uid,
-              'rid' => $rid,
-            ));
-          }
-        }
-        $query->execute();
-      }
-
-      // Delete a blocked user's sessions to kick them if they are online.
-      if ($account->original->status != $account->status && $account->status == 0) {
-        drupal_session_destroy_uid($account->uid);
-      }
-
-      // If the password changed, delete all open sessions and recreate
-      // the current one.
-      if ($account->pass != $account->original->pass) {
-        drupal_session_destroy_uid($account->uid);
-        if ($account->uid == $GLOBALS['user']->uid) {
-          drupal_session_regenerate();
-        }
-      }
-
-      // 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);
-      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);
-      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))) {
-            $query->values(array(
-              'uid' => $account->uid,
-              'rid' => $rid,
-            ));
-          }
-        }
-        $query->execute();
-      }
-    }
-    // Clear internal properties.
-    unset($account->is_new);
-    unset($account->original);
-    // Clear the static loading cache.
-    entity_get_controller('user')->resetCache(array($account->uid));
-
-    return $account;
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    watchdog_exception('user', $e);
-    throw $e;
-  }
-}
-
 /**
  * Verify the syntax of the given name.
  */
@@ -1193,20 +971,42 @@ function user_account_form_validate($form, &$form_state) {
     if ($error = user_validate_name($form_state['values']['name'])) {
       form_set_error('name', $error);
     }
-    elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('name', db_like($form_state['values']['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
-      form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
+    // Cast the user ID as an integer. It might have been set to NULL, which
+    // could lead to unexpected results.
+    else {
+      $name_taken = (bool) db_select('users')
+        ->fields('users', array('uid'))
+        ->condition('uid', (int) $account->uid, '<>')
+        ->condition('name', db_like($form_state['values']['name']), 'LIKE')
+        ->range(0, 1)
+        ->execute()
+        ->fetchField();
+
+      if ($name_taken) {
+        form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
+      }
     }
   }
 
   $mail = $form_state['values']['mail'];
 
-  if (!empty($mail) && (bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($mail), 'LIKE')->range(0, 1)->execute()->fetchField()) {
-    // Format error message dependent on whether the user is logged in or not.
-    if ($GLOBALS['user']->uid) {
-      form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $mail)));
-    }
-    else {
-      form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $mail, '@password' => url('user/password'))));
+  if (!empty($mail)) {
+    $mail_taken = (bool) db_select('users')
+      ->fields('users', array('uid'))
+      ->condition('uid', (int) $account->uid, '<>')
+      ->condition('mail', db_like($mail), 'LIKE')
+      ->range(0, 1)
+      ->execute()
+      ->fetchField();
+
+    if ($mail_taken) {
+      // Format error message dependent on whether the user is logged in or not.
+      if ($GLOBALS['user']->uid) {
+        form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $mail)));
+      }
+      else {
+        form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $mail, '@password' => url('user/password'))));
+      }
     }
   }
 
@@ -1225,30 +1025,6 @@ function user_account_form_validate($form, &$form_state) {
   }
 }
 
-/**
- * Implements hook_user_presave().
- */
-function user_user_presave(&$edit, $account) {
-  if (!empty($edit['picture_upload'])) {
-    $edit['picture'] = $edit['picture_upload'];
-  }
-  // Delete picture if requested, and if no replacement picture was given.
-  elseif (!empty($edit['picture_delete'])) {
-    $edit['picture'] = NULL;
-  }
-  // Prepare user roles.
-  if (isset($edit['roles'])) {
-    $edit['roles'] = array_filter($edit['roles']);
-  }
-
-  // Move account cancellation information into $user->data.
-  foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
-    if (isset($edit[$key])) {
-      $edit['data'][$key] = $edit[$key];
-    }
-  }
-}
-
 function user_login_block($form) {
   $form['#action'] = url($_GET['q'], array('query' => drupal_get_destination()));
   $form['#id'] = 'user-login-form';
@@ -2248,7 +2024,8 @@ function user_authenticate($name, $password) {
 
         // Update user to new password scheme if needed.
         if (user_needs_new_hash($account)) {
-          user_save($account, array('pass' => $password));
+          $account->pass = $password;
+          $account->save();
         }
       }
     }
@@ -2303,16 +2080,16 @@ function user_external_login_register($name, $module) {
   $account = user_external_load($name);
   if (!$account) {
     // Register this new user.
-    $userinfo = array(
+    $account = entity_create('user', array(
       'name' => $name,
       'pass' => user_password(),
       'init' => $name,
       'status' => 1,
       'access' => REQUEST_TIME
-    );
-    $account = user_save(drupal_anonymous_user(), $userinfo);
-    // Terminate if an error occurred during user_save().
-    if (!$account) {
+    ));
+    $status = $account->save();
+    // Terminate if an error occurred while saving the account.
+    if ($status != SAVED_NEW) {
       drupal_set_message(t("Error saving user account."), 'error');
       return;
     }
@@ -2460,7 +2237,8 @@ function _user_cancel($edit, $account, $method) {
       if (!empty($edit['user_cancel_notify'])) {
         _user_mail_notify('status_blocked', $account);
       }
-      user_save($account, array('status' => 0));
+      $account->status = 0;
+      $account->save();
       drupal_set_message(t('%name has been disabled.', array('%name' => $account->name)));
       watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
       break;
@@ -3214,7 +2992,8 @@ function user_user_operations_unblock($accounts) {
   foreach ($accounts as $account) {
     // Skip unblocking user if they are already unblocked.
     if ($account !== FALSE && $account->status == 0) {
-      user_save($account, array('status' => 1));
+      $account->status = 1;
+      $account->save();
     }
   }
 }
@@ -3230,7 +3009,8 @@ function user_user_operations_block($accounts) {
       // For efficiency manually save the original account before applying any
       // changes.
       $account->original = clone $account;
-      user_save($account, array('status' => 0));
+      $account->status = 0;
+      $account->save();
     }
   }
 }
@@ -3239,8 +3019,6 @@ function user_user_operations_block($accounts) {
  * Callback function for admin mass adding/deleting a user role.
  */
 function user_multiple_role_edit($accounts, $operation, $rid) {
-  // The role name is not necessary as user_save() will reload the user
-  // object, but some modules' hook_user() may look at this first.
   $role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField();
 
   switch ($operation) {
@@ -3253,7 +3031,8 @@ function user_multiple_role_edit($accounts, $operation, $rid) {
           // For efficiency manually save the original account before applying
           // any changes.
           $account->original = clone $account;
-          user_save($account, array('roles' => $roles));
+          $account->roles = $roles;
+          $account->save();
         }
       }
       break;
@@ -3266,7 +3045,8 @@ function user_multiple_role_edit($accounts, $operation, $rid) {
           // For efficiency manually save the original account before applying
           // any changes.
           $account->original = clone $account;
-          user_save($account, array('roles' => $roles));
+          $account->roles = $roles;
+          $account->save();
         }
       }
       break;
@@ -3356,7 +3136,9 @@ function user_multiple_cancel_confirm_submit($form, &$form_state) {
       if ($uid == $user->uid) {
         $admin_form_state = $form_state;
         unset($admin_form_state['values']['user_cancel_confirm']);
-        $admin_form_state['values']['_account'] = $user;
+        // The $user global is not a complete user entity, so load the full
+        // entity.
+        $admin_form_state['values']['_account'] = user_load($user->uid);
         user_cancel_confirm_form_submit(array(), $admin_form_state);
       }
       else {
@@ -3430,7 +3212,7 @@ function user_build_filter_query(SelectInterface $query) {
     // the authenticated role. If so, then all users would be listed, and we can
     // skip adding it to the filter query.
     if ($key == 'permission') {
-      $account = new stdClass();
+      $account = entity_create('user', array());
       $account->uid = 'user_filter';
       $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
       if (user_access($value, $account)) {
@@ -3676,7 +3458,8 @@ function user_block_user_action(&$entity, $context = array()) {
     $uid = $GLOBALS['user']->uid;
   }
   $account = user_load($uid);
-  $account = user_save($account, array('status' => 0));
+  $account->status = 0;
+  $account->save();
   watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
 }
 
@@ -3755,7 +3538,7 @@ function user_register_form($form, &$form_state) {
     drupal_goto('user/' . $user->uid);
   }
 
-  $form['#user'] = drupal_anonymous_user();
+  $form['#user'] = entity_create('user', array());
 
   $form['#attached']['library'][] = array('system', 'jquery.cookie');
   $form['#attributes']['class'][] = 'user-info-from-cookie';
@@ -3826,14 +3609,10 @@ function user_register_submit($form, &$form_state) {
   $account = $form['#user'];
 
   entity_form_submit_build_entity('user', $account, $form, $form_state);
+  $status = $account->save();
 
-  // Populate $edit with the properties of $account, which have been edited on
-  // this form by taking over all values, which appear in the form values too.
-  $edit = array_intersect_key((array) $account, $form_state['values']);
-  $account = user_save($account, $edit);
-
-  // Terminate if an error occurred during user_save().
-  if (!$account) {
+  // Terminate if an error occurred while saving the account.
+  if ($status =! SAVED_NEW) {
     drupal_set_message(t("Error saving user account."), 'error');
     $form_state['redirect'] = '';
     return;
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index f24849cc11680262d75cd59d50ad37c5035d1b85..438fedbb398ba8423dfdc0bc0ff3c8d4e25362b4 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -264,19 +264,8 @@ function user_profile_form_submit($form, &$form_state) {
   // Remove unneeded values.
   form_state_values_clean($form_state);
 
-  // Before updating the account entity, keep an unchanged copy for use with
-  // user_save() later. This is necessary for modules implementing the user
-  // hooks to be able to react on changes by comparing the values of $account
-  // and $edit.
-  $account_unchanged = clone $account;
-
   entity_form_submit_build_entity('user', $account, $form, $form_state);
-
-  // Populate $edit with the properties of $account, which have been edited on
-  // this form by taking over all values, which appear in the form values too.
-  $edit = array_intersect_key((array) $account, $form_state['values']);
-
-  user_save($account_unchanged, $edit);
+  $account->save();
   $form_state['values']['uid'] = $account->uid;
 
   if (!empty($edit['pass'])) {
@@ -400,11 +389,9 @@ function user_cancel_confirm_form_submit($form, &$form_state) {
   else {
     // Store cancelling method and whether to notify the user in $account for
     // user_cancel_confirm().
-    $edit = array(
-      'user_cancel_method' => $form_state['values']['user_cancel_method'],
-      'user_cancel_notify' => $form_state['values']['user_cancel_notify'],
-    );
-    $account = user_save($account, $edit);
+    $account->user_cancel_method = $form_state['values']['user_cancel_method'];
+    $account->user_cancel_notify = $form_state['values']['user_cancel_notify'];
+    $account->save();
     _user_mail_notify('cancel_confirm', $account);
     drupal_set_message(t('A confirmation request to cancel your account has been sent to your e-mail address.'));
     watchdog('user', 'Sent account cancellation request to %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
diff --git a/core/modules/user/user.test b/core/modules/user/user.test
index d5fa28e9341c1923b5df60e3860f650151a28dfc..c911088d8de196c5f753780a9d8c5e015be54eda 100644
--- a/core/modules/user/user.test
+++ b/core/modules/user/user.test
@@ -168,7 +168,7 @@ class UserRegistrationTestCase extends DrupalWebTestCase {
     $this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), t('Correct time zone field.'));
     $this->assertEqual($new_user->langcode, language_default()->langcode, t('Correct language field.'));
     $this->assertEqual($new_user->preferred_langcode, language_default()->langcode, t('Correct preferred language field.'));
-    $this->assertEqual($new_user->picture, '', t('Correct picture field.'));
+    $this->assertEqual($new_user->picture, 0, t('Correct picture field.'));
     $this->assertEqual($new_user->init, $mail, t('Correct init field.'));
   }
 
@@ -553,7 +553,8 @@ class UserCancelTestCase extends DrupalWebTestCase {
       'name' => 'user1',
       'pass' => user_hash_password(trim($password)),
     );
-    // We cannot use user_save() here or the password would be hashed again.
+    // We cannot use $account->save() here, because this would result in the
+    // password being hashed again.
     db_update('users')
       ->fields($account)
       ->condition('uid', 1)
@@ -845,8 +846,8 @@ class UserCancelTestCase extends DrupalWebTestCase {
     // Create a regular user.
     $account = $this->drupalCreateUser(array());
     // This user has no e-mail address.
-    $edit = array('mail' => '');
-    $account = user_save($account, $edit);
+    $account->mail = '';
+    $account->save();
 
     // Create administrative user.
     $admin_user = $this->drupalCreateUser(array('administer users'));
@@ -1174,7 +1175,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
 
     // Load actual user data from database.
     $account = user_load($this->user->uid, TRUE);
-    $pic_path = isset($account->picture) ? $account->picture->uri : NULL;
+    $pic_path = !empty($account->picture) ? $account->picture->uri : NULL;
 
     // Check if image is displayed in user's profile page.
     $this->drupalGet('user');
@@ -1188,7 +1189,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
 
     // Load actual user data from database.
     $account1 = user_load($this->user->uid, TRUE);
-    $this->assertNull($account1->picture, 'User object has no picture');
+    $this->assertFalse($account1->picture, 'User object has no picture');
 
     $file = file_load($account->picture->fid);
     $this->assertFalse($file, 'File is removed from database');
@@ -1204,7 +1205,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
 
     // Load actual user data from database.
     $account = user_load($this->user->uid, TRUE);
-    return isset($account->picture) ? $account->picture->uri : NULL;
+    return !empty($account->picture) ? $account->picture->uri : NULL;
   }
 
   /**
@@ -1677,7 +1678,7 @@ class UserSaveTestCase extends DrupalWebTestCase {
   public static function getInfo() {
     return array(
       'name' => 'User save test',
-      'description' => 'Test user_save() for arbitrary new uid.',
+      'description' => 'Test account saving for arbitrary new uid.',
       'group' => 'User',
     );
   }
@@ -1692,16 +1693,15 @@ class UserSaveTestCase extends DrupalWebTestCase {
     $test_name = $this->randomName();
 
     // Create the base user, based on drupalCreateUser().
-    $user = array(
+    $user = entity_create('user', array(
       'name' => $test_name,
       'uid' => $test_uid,
       'mail' => $test_name . '@example.com',
-      'is_new' => TRUE,
       'pass' => user_password(),
       'status' => 1,
-    );
-    $user_by_return = user_save(drupal_anonymous_user(), $user);
-    $this->assertTrue($user_by_return, t('Loading user by return of user_save().'));
+    ));
+    $user->enforceIsNew();
+    $user->save();
 
     // Test if created user exists.
     $user_by_uid = user_load($test_uid);
@@ -1843,9 +1843,9 @@ class UserEditTestCase extends DrupalWebTestCase {
     // Create a regular user.
     $user1 = $this->drupalCreateUser(array());
     // This user has no e-mail address.
-    $edit = array('mail' => '');
-    $user1 = user_save($user1, $edit);
-    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
+    $user1->mail = '';
+    $user1->save();
+    $this->drupalPost("user/$user1->uid/edit", array('mail' => ''), t('Save'));
     $this->assertRaw(t("The changes have been saved."));
   }
 }