From 5ce09deb181091e7db6c067ad1504cf1e64346bf Thu, 12 Jan 2012 12:30:44 +0100
From: Bram Goffings <bramgoffings@gmail.com>
Date: Thu, 12 Jan 2012 12:30:37 +0100
Subject: [PATCH] node entity



diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index e37c7fc..5ec901a 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -72,7 +72,7 @@
  *
  * Path: admin/content/book/%node
  *
- * @param $node
+ * @param Node $node
  *   The node of the top-level page in the book.
  *
  * @see book_menu()
@@ -80,7 +80,7 @@
  * @see book_admin_edit_submit()
  * @ingroup forms
  */
-function book_admin_edit($form, $form_state, $node) {
+function book_admin_edit($form, $form_state, Node $node) {
   drupal_set_title($node->title);
   $form['#node'] = $node;
   _book_admin_table($node, $form);
@@ -153,14 +153,14 @@
 /**
  * Builds the table portion of the form for the book administration page.
  *
- * @param $node
+ * @param Node $node
  *   The node of the top-level page in the book.
  * @param $form
  *   The form that is being modified.
  *
  * @see book_admin_edit()
  */
-function _book_admin_table($node, &$form) {
+function _book_admin_table(Node $node, &$form) {
   $form['table'] = array(
     '#theme' => 'book_admin_table',
     '#tree' => TRUE,
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index f9ae921..da72ded 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -86,12 +86,12 @@
 /**
  * Adds relevant book links to the node's links.
  *
- * @param $node
+ * @param Node $node
  *   The book page node to add links to.
  * @param $view_mode
  *   The view mode of the node.
  */
-function book_node_view_link($node, $view_mode) {
+function book_node_view_link(Node $node, $view_mode) {
   $links = array();
 
   if (isset($node->book['depth'])) {
@@ -201,12 +201,12 @@
  * - admin/content/book/%node
  * - node/%node/outline
  *
- * @param $node
+ * @param Node $node
  *   The node whose outline tab is to be viewed.
  *
  * @see book_menu()
  */
-function _book_outline_access($node) {
+function _book_outline_access(Node $node) {
   return user_access('administer book outlines') && node_access('view', $node);
 }
 
@@ -215,12 +215,12 @@
  *
  * Path: node/%node/outline/remove
  *
- * @param $node
+ * @param Node $node
  *   The node to remove from the outline.
  *
  * @see book_menu()
  */
-function _book_outline_remove_access($node) {
+function _book_outline_remove_access(Node $node) {
   return isset($node->book) && ($node->book['bid'] != $node->nid) && _book_outline_access($node);
 }
 
@@ -512,10 +512,10 @@
 /**
  * Builds the common elements of the book form for the node and outline forms.
  *
- * @param $node
+ * @param Node $node
  *   The node whose form is being viewed.
  */
-function _book_add_form_elements(&$form, &$form_state, $node) {
+function _book_add_form_elements(&$form, &$form_state, Node $node) {
   // If the form is being processed during the Ajax callback of our book bid
   // dropdown, then $form_state will hold the value that was selected.
   if (isset($form_state['values']['book'])) {
@@ -617,13 +617,13 @@
  * outline through node addition, node editing, node deletion, or the outline
  * tab.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being saved, added, deleted, or moved.
  *
  * @return
  *   TRUE if the menu link was saved; FALSE otherwise.
  */
-function _book_update_outline($node) {
+function _book_update_outline(Node $node) {
   if (empty($node->book['bid'])) {
     return FALSE;
   }
@@ -881,7 +881,7 @@
 /**
  * Implements hook_node_view().
  */
-function book_node_view($node, $view_mode) {
+function book_node_view(Node $node, $view_mode) {
   if ($view_mode == 'full') {
     if (!empty($node->book['bid']) && empty($node->in_preview)) {
       $node->content['book_navigation'] = array(
@@ -913,7 +913,7 @@
 /**
  * Implements hook_node_presave().
  */
-function book_node_presave($node) {
+function book_node_presave(Node $node) {
   // Always save a revision for non-administrators.
   if (!empty($node->book['bid']) && !user_access('administer nodes')) {
     $node->revision = 1;
@@ -931,7 +931,7 @@
 /**
  * Implements hook_node_insert().
  */
-function book_node_insert($node) {
+function book_node_insert(Node $node) {
   if (!empty($node->book['bid'])) {
     if ($node->book['bid'] == 'new') {
       // New nodes that are their own book.
@@ -946,7 +946,7 @@
 /**
  * Implements hook_node_update().
  */
-function book_node_update($node) {
+function book_node_update(Node $node) {
   if (!empty($node->book['bid'])) {
     if ($node->book['bid'] == 'new') {
       // New nodes that are their own book.
@@ -961,7 +961,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function book_node_predelete($node) {
+function book_node_predelete(Node $node) {
   if (!empty($node->book['bid'])) {
     if ($node->nid == $node->book['bid']) {
       // Handle deletion of a top-level post.
@@ -985,7 +985,7 @@
 /**
  * Implements hook_node_prepare().
  */
-function book_node_prepare($node) {
+function book_node_prepare(Node $node) {
   // Prepare defaults for the add/edit form.
   if (empty($node->book) && (user_access('add content to books') || user_access('administer book outlines'))) {
     $node->book = array();
@@ -1269,7 +1269,7 @@
 /**
  * Generates printer-friendly HTML for a node.
  *
- * @param $node
+ * @param Node $node
  *   The node that will be output.
  * @param $children
  *   All the rendered child nodes within the current node.
@@ -1279,7 +1279,7 @@
  *
  * @see book_export_traverse()
  */
-function book_node_export($node, $children = '') {
+function book_node_export(Node $node, $children = '') {
   $build = node_view($node, 'print');
   unset($build['#theme']);
   // @todo Rendering should happen in the template using render().
diff --git a/core/modules/book/book.pages.inc b/core/modules/book/book.pages.inc
index e5f0832..9db99df 100644
--- a/core/modules/book/book.pages.inc
+++ b/core/modules/book/book.pages.inc
@@ -99,12 +99,12 @@
  *
  * Path: node/%node/outline
  *
- * @param $node
+ * @param Node $node
  *   The book node for which to show the outline.
  *
  * @see book_menu()
  */
-function book_outline($node) {
+function book_outline(Node $node) {
   drupal_set_title($node->title);
   return drupal_get_form('book_outline_form', $node);
 }
@@ -114,14 +114,14 @@
  *
  * Allows handling of all book outline operations via the outline tab.
  *
- * @param $node
+ * @param Node $node
  *   The book node for which to show the outline.
  *
  * @see book_outline_form_submit()
  * @see book_remove_button_submit()
  * @ingroup forms
  */
-function book_outline_form($form, &$form_state, $node) {
+function book_outline_form($form, &$form_state, Node $node) {
   if (!isset($node->book)) {
     // The node is not part of any book yet - set default options.
     $node->book = _book_link_defaults($node->nid);
@@ -205,14 +205,14 @@
  *
  * Path: node/%node/outline/remove
  *
- * @param $node
+ * @param Node $node
  *   The node to delete.
  *
  * @see book_remove_form_submit()
  * @see book_menu()
  * @ingroup forms
  */
-function book_remove_form($form, &$form_state, $node) {
+function book_remove_form($form, &$form_state, Node $node) {
   $form['#node'] = $node;
   $title = array('%title' => $node->title);
 
diff --git a/core/modules/book/book.test b/core/modules/book/book.test
index 83328e0..5081dbe 100644
--- a/core/modules/book/book.test
+++ b/core/modules/book/book.test
@@ -108,7 +108,7 @@
   /**
    * Check the outline of sub-pages; previous, up, and next; and printer friendly version.
    *
-   * @param $node
+   * @param Node $node
    *   Node to check.
    * @param $nodes
    *   Nodes that should be in outline.
@@ -121,7 +121,7 @@
    * @param $breadcrumb
    *   The nodes that should be displayed in the breadcrumb.
    */
-  function checkBookNode($node, $nodes, $previous = FALSE, $up = FALSE, $next = FALSE, array $breadcrumb) {
+  function checkBookNode(Node $node, $nodes, $previous = FALSE, $up = FALSE, $next = FALSE, array $breadcrumb) {
     // $number does not use drupal_static as it should not be reset
     // since it uniquely identifies each call to checkBookNode().
     static $number = 0;
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 7ad14e8..7756e8d 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -545,13 +545,13 @@
  *   Number of comments.
  * @param $new_replies
  *   Number of new replies.
- * @param $node
+ * @param Node $node
  *   The first new comment node.
  *
  * @return
  *   "page=X" if the page number is greater than zero; empty string otherwise.
  */
-function comment_new_page_count($num_comments, $new_replies, $node) {
+function comment_new_page_count($num_comments, $new_replies, Node $node) {
   $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
   $comments_per_page = variable_get('comment_default_per_page_' . $node->type, 50);
   $pagenum = NULL;
@@ -629,7 +629,7 @@
 /**
  * Implements hook_node_view().
  */
-function comment_node_view($node, $view_mode) {
+function comment_node_view(Node $node, $view_mode) {
   $links = array();
 
   if ($node->comment != COMMENT_NODE_HIDDEN) {
@@ -733,14 +733,14 @@
 /**
  * Builds the comment-related elements for node detail pages.
  *
- * @param $node
+ * @param Node $node
  *   The node object for which to build the comment-related elements.
  *
  * @return
  *   A renderable array representing the comment-related page elements for the
  *   node.
  */
-function comment_node_page_additions($node) {
+function comment_node_page_additions(Node $node) {
   $additions = array();
 
   // Only attempt to render comments if the node has visible comments.
@@ -779,7 +779,7 @@
 /**
  * Retrieves comments for a thread.
  *
- * @param $node
+ * @param Node $node
  *   The node whose comment(s) needs rendering.
  * @param $mode
  *   The comment display mode; COMMENT_MODE_FLAT or COMMENT_MODE_THREADED.
@@ -843,7 +843,7 @@
  * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
  * to consider the trailing "/" so we use a substring only.
  */
-function comment_get_thread($node, $mode, $comments_per_page) {
+function comment_get_thread(Node $node, $mode, $comments_per_page) {
   $query = db_select('comment', 'c')->extend('PagerDefault');
   $query->addField('c', 'cid');
   $query
@@ -932,7 +932,7 @@
  *
  * @param $comment
  *   The comment object.
- * @param $node
+ * @param Node $node
  *   The node the comment is attached to.
  * @param $view_mode
  *   View mode, e.g. 'full', 'teaser'...
@@ -943,7 +943,7 @@
  * @return
  *   An array as expected by drupal_render().
  */
-function comment_view($comment, $node, $view_mode = 'full', $langcode = NULL) {
+function comment_view($comment, Node $node, $view_mode = 'full', $langcode = NULL) {
   if (!isset($langcode)) {
     $langcode = $GLOBALS['language_content']->langcode;
   }
@@ -1002,7 +1002,7 @@
  *
  * @param $comment
  *   A comment object.
- * @param $node
+ * @param Node $node
  *   The node the comment is attached to.
  * @param $view_mode
  *   View mode, e.g. 'full', 'teaser'...
@@ -1010,7 +1010,7 @@
  *   (optional) A language code to use for rendering. Defaults to the global
  *   content language of the current request.
  */
-function comment_build_content($comment, $node, $view_mode = 'full', $langcode = NULL) {
+function comment_build_content($comment, Node $node, $view_mode = 'full', $langcode = NULL) {
   if (!isset($langcode)) {
     $langcode = $GLOBALS['language_content']->langcode;
   }
@@ -1046,13 +1046,13 @@
  *
  * @param $comment
  *   The comment object.
- * @param $node
+ * @param Node $node
  *   The node the comment is attached to.
  *
  * @return
  *   A structured array of links.
  */
-function comment_links($comment, $node) {
+function comment_links($comment, Node $node) {
   $links = array();
   if ($node->comment == COMMENT_NODE_OPEN) {
     if (user_access('administer comments') && user_access('post comments')) {
@@ -1107,7 +1107,7 @@
  *
  * @param $comments
  *   An array of comments as returned by comment_load_multiple().
- * @param $node
+ * @param Node $node
  *   The node the comments are attached to.
  * @param $view_mode
  *   View mode, e.g. 'full', 'teaser'...
@@ -1122,7 +1122,7 @@
  *
  * @see drupal_render()
  */
-function comment_view_multiple($comments, $node, $view_mode = 'full', $weight = 0, $langcode = NULL) {
+function comment_view_multiple($comments, Node $node, $view_mode = 'full', $weight = 0, $langcode = NULL) {
   field_attach_prepare_view('comment', $comments, $view_mode, $langcode);
   entity_prepare_view('comment', $comments, $langcode);
 
@@ -1304,7 +1304,7 @@
 /**
  * Implements hook_node_prepare().
  */
-function comment_node_prepare($node) {
+function comment_node_prepare(Node $node) {
   if (!isset($node->comment)) {
     $node->comment = variable_get("comment_$node->type", COMMENT_NODE_OPEN);
   }
@@ -1313,7 +1313,7 @@
 /**
  * Implements hook_node_insert().
  */
-function comment_node_insert($node) {
+function comment_node_insert(Node $node) {
   // Allow bulk updates and inserts to temporarily disable the
   // maintenance of the {node_comment_statistics} table.
   if (variable_get('comment_maintain_node_statistics', TRUE)) {
@@ -1333,7 +1333,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function comment_node_predelete($node) {
+function comment_node_predelete(Node $node) {
   $cids = db_query('SELECT cid FROM {comment} WHERE nid = :nid', array(':nid' => $node->nid))->fetchCol();
   comment_delete_multiple($cids);
   db_delete('node_comment_statistics')
@@ -1344,7 +1344,7 @@
 /**
  * Implements hook_node_update_index().
  */
-function comment_node_update_index($node) {
+function comment_node_update_index(Node $node) {
   $index_comments = &drupal_static(__FUNCTION__);
 
   if ($index_comments === NULL) {
@@ -1393,7 +1393,7 @@
  * Formats a comment count string and returns it, for display with search
  * results.
  */
-function comment_node_search_result($node) {
+function comment_node_search_result(Node $node) {
   // Do not make a string if comments are hidden.
   if (user_access('access comments') && $node->comment != COMMENT_NODE_HIDDEN) {
     $comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->nid))->fetchField();
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
index 5e3d9de..1aace2c 100644
--- a/core/modules/comment/comment.pages.inc
+++ b/core/modules/comment/comment.pages.inc
@@ -17,7 +17,7 @@
  * The node or comment that is being replied to must appear above the comment
  * form to provide the user context while authoring the comment.
  *
- * @param $node
+ * @param Node $node
  *   Every comment belongs to a node. This is that node.
  * @param $pid
  *   (optional) Some comments are replies to other comments. In those cases,
@@ -26,7 +26,7 @@
  * @return
  *   The rendered parent node or comment plus the new comment form.
  */
-function comment_reply($node, $pid = NULL) {
+function comment_reply(Node $node, $pid = NULL) {
   // Set the breadcrumb trail.
   drupal_set_breadcrumb(array(l(t('Home'), NULL), l($node->title, 'node/' . $node->nid)));
   $op = isset($_POST['op']) ? $_POST['op'] : '';
diff --git a/core/modules/comment/comment.test b/core/modules/comment/comment.test
index bc31566..41651e4 100644
--- a/core/modules/comment/comment.test
+++ b/core/modules/comment/comment.test
@@ -21,7 +21,7 @@
   /**
    * Posts a comment.
    *
-   * @param $node
+   * @param Node $node
    *   Node to post comment on.
    * @param $comment
    *   Comment body.
@@ -31,7 +31,7 @@
    *   Set to NULL for no contact info, TRUE to ignore success checking, and
    *   array of values to set contact info.
    */
-  function postComment($node, $comment, $subject = '', $contact = NULL) {
+  function postComment(Node $node, $comment, $subject = '', $contact = NULL) {
     $langcode = LANGUAGE_NONE;
     $edit = array();
     $edit['comment_body[' . $langcode . '][0][value]'] = $comment;
diff --git a/core/modules/entity/tests/entity_crud_hook_test.test b/core/modules/entity/tests/entity_crud_hook_test.test
index ab28d03..c35e865 100644
--- a/core/modules/entity/tests/entity_crud_hook_test.test
+++ b/core/modules/entity/tests/entity_crud_hook_test.test
@@ -56,7 +56,7 @@
    * Test hook invocations for CRUD operations on comments.
    */
   public function testCommentHooks() {
-    $node = (object) array(
+    $node = entity_create('node', array(
       'uid' => 1,
       'type' => 'article',
       'title' => 'Test node',
@@ -67,7 +67,7 @@
       'language' => LANGUAGE_NONE,
       'created' => REQUEST_TIME,
       'changed' => REQUEST_TIME,
-    );
+    ));
     node_save($node);
     $nid = $node->nid;
 
@@ -183,7 +183,7 @@
    * Test hook invocations for CRUD operations on nodes.
    */
   public function testNodeHooks() {
-    $node = (object) array(
+    $node = entity_create('node', array(
       'uid' => 1,
       'type' => 'article',
       'title' => 'Test node',
@@ -194,7 +194,7 @@
       'language' => LANGUAGE_NONE,
       'created' => REQUEST_TIME,
       'changed' => REQUEST_TIME,
-    );
+    ));
     $_SESSION['entity_crud_hook_test'] = array();
     node_save($node);
 
diff --git a/core/modules/field_ui/field_ui.test b/core/modules/field_ui/field_ui.test
index 951d23d..048a305 100644
--- a/core/modules/field_ui/field_ui.test
+++ b/core/modules/field_ui/field_ui.test
@@ -597,7 +597,7 @@
   /**
    * Asserts that a string is found in the rendered node in a view mode.
    *
-   * @param $node
+   * @param Node $node
    *   The node.
    * @param $view_mode
    *   The view mode in which the node should be displayed.
@@ -609,14 +609,14 @@
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  function assertNodeViewText($node, $view_mode, $text, $message) {
+  function assertNodeViewText(Node $node, $view_mode, $text, $message) {
     return $this->assertNodeViewTextHelper($node, $view_mode, $text, $message, FALSE);
   }
 
   /**
    * Asserts that a string is not found in the rendered node in a view mode.
    *
-   * @param $node
+   * @param Node $node
    *   The node.
    * @param $view_mode
    *   The view mode in which the node should be displayed.
@@ -627,7 +627,7 @@
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  function assertNodeViewNoText($node, $view_mode, $text, $message) {
+  function assertNodeViewNoText(Node $node, $view_mode, $text, $message) {
     return $this->assertNodeViewTextHelper($node, $view_mode, $text, $message, TRUE);
   }
 
@@ -637,7 +637,7 @@
    * This helper function is used by assertNodeViewText() and
    * assertNodeViewNoText().
    *
-   * @param $node
+   * @param Node $node
    *   The node.
    * @param $view_mode
    *   The view mode in which the node should be displayed.
@@ -651,7 +651,7 @@
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  function assertNodeViewTextHelper($node, $view_mode, $text, $message, $not_exists) {
+  function assertNodeViewTextHelper(Node $node, $view_mode, $text, $message, $not_exists) {
     // Make sure caches on the tester side are refreshed after changes
     // submitted on the tested side.
     field_info_cache_clear();
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 90b1f89..5783a68 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -244,13 +244,13 @@
 /**
  * Check whether a content type can be used in a forum.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  *
  * @return
  *   Boolean indicating if the node can be assigned to a forum.
  */
-function _forum_node_check_node_type($node) {
+function _forum_node_check_node_type(Node $node) {
   // Fetch information about the forum field.
   $field = field_info_instance('node', 'taxonomy_forums', $node->type);
 
@@ -260,7 +260,7 @@
 /**
  * Implements hook_node_view().
  */
-function forum_node_view($node, $view_mode) {
+function forum_node_view(Node $node, $view_mode) {
   $vid = variable_get('forum_nav_vocabulary', 0);
   $vocabulary = taxonomy_vocabulary_load($vid);
   if (_forum_node_check_node_type($node)) {
@@ -285,7 +285,7 @@
  *
  * Check in particular that only a "leaf" term in the associated taxonomy.
  */
-function forum_node_validate($node, $form) {
+function forum_node_validate(Node $node, $form) {
   if (_forum_node_check_node_type($node)) {
     $langcode = $form['taxonomy_forums']['#language'];
     // vocabulary is selected, not a "container" term.
@@ -321,7 +321,7 @@
  *
  * Assign forum taxonomy when adding a topic from within a forum.
  */
-function forum_node_presave($node) {
+function forum_node_presave(Node $node) {
   if (_forum_node_check_node_type($node)) {
     // Make sure all fields are set properly:
     $node->icon = !empty($node->icon) ? $node->icon : '';
@@ -341,7 +341,7 @@
 /**
  * Implements hook_node_update().
  */
-function forum_node_update($node) {
+function forum_node_update(Node $node) {
   if (_forum_node_check_node_type($node)) {
     if (empty($node->revision) && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->nid))->fetchField()) {
       if (!empty($node->forum_tid)) {
@@ -389,7 +389,7 @@
 /**
  * Implements hook_node_insert().
  */
-function forum_node_insert($node) {
+function forum_node_insert(Node $node) {
   if (_forum_node_check_node_type($node)) {
     if (!empty($node->forum_tid)) {
       $nid = db_insert('forum')
@@ -406,7 +406,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function forum_node_predelete($node) {
+function forum_node_predelete(Node $node) {
   if (_forum_node_check_node_type($node)) {
     db_delete('forum')
       ->condition('nid', $node->nid)
diff --git a/core/modules/forum/forum.test b/core/modules/forum/forum.test
index c7c3d9c..2d60277 100644
--- a/core/modules/forum/forum.test
+++ b/core/modules/forum/forum.test
@@ -417,14 +417,14 @@
    *
    * @param $node_user
    *   The user who creates the node.
-   * @param $node
+   * @param Node $node
    *   The node being checked.
    * @param $admin
    *   Boolean to indicate whether the user can 'access administration pages'.
    * @param $response
    *   The exptected HTTP response code.
    */
-  private function verifyForums($node_user, $node, $admin, $response = 200) {
+  private function verifyForums($node_user, Node $node, $admin, $response = 200) {
     $response2 = ($admin) ? 200 : 403;
 
     // View forum help node.
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index fea7241..9b769db 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -326,7 +326,7 @@
  */
 function locale_field_node_form_submit($form, &$form_state) {
   if (field_has_translation_handler('node', 'locale')) {
-    $node = (object) $form_state['values'];
+    $node = entity_create('node', $form_state['values']);
     $available_languages = field_content_languages();
     list(, , $bundle) = entity_extract_ids('node', $node);
 
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 93d767d..6c36a0a 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -509,21 +509,21 @@
 /**
  * Implements hook_node_insert().
  */
-function menu_node_insert($node) {
+function menu_node_insert(Node $node) {
   menu_node_save($node);
 }
 
 /**
  * Implements hook_node_update().
  */
-function menu_node_update($node) {
+function menu_node_update(Node $node) {
   menu_node_save($node);
 }
 
 /**
  * Helper for hook_node_insert() and hook_node_update().
  */
-function menu_node_save($node) {
+function menu_node_save(Node $node) {
   if (isset($node->menu)) {
     $link = &$node->menu;
     if (empty($link['enabled'])) {
@@ -552,7 +552,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function menu_node_predelete($node) {
+function menu_node_predelete(Node $node) {
   // Delete all menu module links that point to this node.
   $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path AND module = 'menu'", array(':path' => 'node/' . $node->nid), array('fetch' => PDO::FETCH_ASSOC));
   foreach ($result as $m) {
@@ -563,7 +563,7 @@
 /**
  * Implements hook_node_prepare().
  */
-function menu_node_prepare($node) {
+function menu_node_prepare(Node $node) {
   if (empty($node->menu)) {
     // Prepare the node for the edit form so that $node->menu always exists.
     $menu_name = strtok(variable_get('menu_parent_' . $node->type, 'main-menu:0'), ':');
@@ -716,7 +716,7 @@
  *
  * @see menu_form_node_form_alter()
  */
-function menu_node_submit($node, $form, $form_state) {
+function menu_node_submit(Node $node, $form, $form_state) {
   // Decompose the selected menu parent option into 'menu_name' and 'plid', if
   // the form used the default parent selection widget.
   if (!empty($form_state['values']['menu']['parent'])) {
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index c297818..caf1959 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -253,7 +253,7 @@
  *
  * Note: a deny all grant is not written to the database; denies are implicit.
  *
- * @param $node
+ * @param Node $node
  *   The node that has just been saved.
  *
  * @return
@@ -262,7 +262,7 @@
  * @see _node_access_write_grants()
  * @ingroup node_access
  */
-function hook_node_access_records($node) {
+function hook_node_access_records(Node $node) {
   // We only care about the node if it has been marked private. If not, it is
   // treated just like any other node and we completely ignore it.
   if ($node->private) {
@@ -313,7 +313,7 @@
  *
  * @param $grants
  *   The $grants array returned by hook_node_access_records().
- * @param $node
+ * @param Node $node
  *   The node for which the grants were acquired.
  *
  * The preferred use of this hook is in a module that bridges multiple node
@@ -325,7 +325,7 @@
  * @see hook_node_grants_alter()
  * @ingroup node_access
  */
-function hook_node_access_records_alter(&$grants, $node) {
+function hook_node_access_records_alter(&$grants, Node $node) {
   // Our module allows editors to mark specific articles with the 'is_preview'
   // field. If the node being saved has a TRUE value for that field, then only
   // our grants are retained, and other grants are removed. Doing so ensures
@@ -456,14 +456,14 @@
  * field_attach_delete() are called, and before the node is removed from the
  * node table in the database.
  *
- * @param $node
+ * @param Node $node
  *   The node that is about to be deleted.
  *
  * @see hook_node_predelete()
  * @see node_delete_multiple()
  * @ingroup node_api_hooks
  */
-function hook_node_predelete($node) {
+function hook_node_predelete(Node $node) {
   db_delete('mytable')
     ->condition('nid', $node->nid)
     ->execute();
@@ -475,14 +475,14 @@
  * This hook is invoked from node_delete_multiple() after field_attach_delete()
  * has been called and after the node has been removed from the database.
  *
- * @param $node
+ * @param Node $node
  *   The node that has been deleted.
  *
  * @see hook_node_predelete()
  * @see node_delete_multiple()
  * @ingroup node_api_hooks
  */
-function hook_node_delete($node) {
+function hook_node_delete(Node $node) {
   drupal_set_message(t('Node: @title has been deleted', array('@title' => $node->title)));
 }
 
@@ -493,12 +493,12 @@
  * removed from the node_revision table, and before
  * field_attach_delete_revision() is called.
  *
- * @param $node
+ * @param Node $node
  *   The node revision (node object) that is being deleted.
  *
  * @ingroup node_api_hooks
  */
-function hook_node_revision_delete($node) {
+function hook_node_revision_delete(Node $node) {
   db_delete('mytable')
     ->condition('vid', $node->vid)
     ->execute();
@@ -511,12 +511,12 @@
  * node table in the database, after the type-specific hook_insert() is invoked,
  * and after field_attach_insert() is called.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being created.
  *
  * @ingroup node_api_hooks
  */
-function hook_node_insert($node) {
+function hook_node_insert(Node $node) {
   db_insert('mytable')
     ->fields(array(
       'nid' => $node->nid,
@@ -634,12 +634,12 @@
  * This hook is invoked from node_object_prepare() after the type-specific
  * hook_prepare() is invoked.
  *
- * @param $node
+ * @param Node $node
  *   The node that is about to be shown on the add/edit form.
  *
  * @ingroup node_api_hooks
  */
-function hook_node_prepare($node) {
+function hook_node_prepare(Node $node) {
   if (!isset($node->comment)) {
     $node->comment = variable_get("comment_$node->type", COMMENT_NODE_OPEN);
   }
@@ -651,7 +651,7 @@
  * This hook is invoked from node_search_execute(), after node_load()
  * and node_view() have been called.
  *
- * @param $node
+ * @param Node $node
  *   The node being displayed in a search result.
  *
  * @return array
@@ -665,7 +665,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_node_search_result($node) {
+function hook_node_search_result(Node $node) {
   $comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->nid))->fetchField();
   return array('comment' => format_plural($comments, '1 comment', '@count comments'));
 }
@@ -676,12 +676,12 @@
  * This hook is invoked from node_save() before the node is saved to the
  * database.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being inserted or updated.
  *
  * @ingroup node_api_hooks
  */
-function hook_node_presave($node) {
+function hook_node_presave(Node $node) {
   if ($node->nid && $node->moderate) {
     // Reset votes when node is updated:
     $node->score = 0;
@@ -697,12 +697,12 @@
  * table in the database, after the type-specific hook_update() is invoked, and
  * after field_attach_update() is called.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being updated.
  *
  * @ingroup node_api_hooks
  */
-function hook_node_update($node) {
+function hook_node_update(Node $node) {
   db_update('mytable')
     ->fields(array('extra' => $node->extra))
     ->condition('nid', $node->nid)
@@ -715,7 +715,7 @@
  * This hook is invoked during search indexing, after node_load(), and after
  * the result of node_view() is added as $node->rendered to the node object.
  *
- * @param $node
+ * @param Node $node
  *   The node being indexed.
  *
  * @return
@@ -723,7 +723,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_node_update_index($node) {
+function hook_node_update_index(Node $node) {
   $text = '';
   $comments = db_query('SELECT subject, comment, format FROM {comment} WHERE nid = :nid AND status = :status', array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED));
   foreach ($comments as $comment) {
@@ -747,7 +747,7 @@
  * hook_node_presave() instead. If it is really necessary to change
  * the node at the validate stage, you can use form_set_value().
  *
- * @param $node
+ * @param Node $node
  *   The node being validated.
  * @param $form
  *   The form being used to edit the node.
@@ -756,7 +756,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_node_validate($node, $form, &$form_state) {
+function hook_node_validate(Node $node, $form, &$form_state) {
   if (isset($node->end) && isset($node->start)) {
     if ($node->start > $node->end) {
       form_set_error('time', t('An event may not end before it starts.'));
@@ -775,7 +775,7 @@
  * properties. See hook_field_attach_submit() for customizing field-related
  * properties.
  *
- * @param $node
+ * @param Node $node
  *   The node object being updated in response to a form submission.
  * @param $form
  *   The form being used to edit the node.
@@ -784,7 +784,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_node_submit($node, $form, &$form_state) {
+function hook_node_submit(Node $node, $form, &$form_state) {
   // Decompose the selected menu parent option into 'menu_name' and 'plid', if
   // the form used the default parent selection widget.
   if (!empty($form_state['values']['menu']['parent'])) {
@@ -804,7 +804,7 @@
  * the RSS item generated for this node.
  * For details on how this is used, see node_feed().
  *
- * @param $node
+ * @param Node $node
  *   The node that is being assembled for rendering.
  * @param $view_mode
  *   The $view_mode parameter from node_view().
@@ -817,7 +817,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_node_view($node, $view_mode, $langcode) {
+function hook_node_view(Node $node, $view_mode, $langcode) {
   $node->content['my_additional_field'] = array(
     '#markup' => $additional_field,
     '#weight' => 10,
@@ -1026,12 +1026,12 @@
  * removed from the node table in the database, before hook_node_delete() is
  * invoked, and before field_attach_delete() is called.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being deleted.
  *
  * @ingroup node_api_hooks
  */
-function hook_delete($node) {
+function hook_delete(Node $node) {
   db_delete('mytable')
     ->condition('nid', $node->nid)
     ->execute();
@@ -1046,12 +1046,12 @@
  * This hook is invoked from node_object_prepare() before the general
  * hook_node_prepare() is invoked.
  *
- * @param $node
+ * @param Node $node
  *   The node that is about to be shown on the add/edit form.
  *
  * @ingroup node_api_hooks
  */
-function hook_prepare($node) {
+function hook_prepare(Node $node) {
   if ($file = file_check_upload($field_name)) {
     $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
     if ($file) {
@@ -1082,7 +1082,7 @@
  * displayed automatically by the node module. This hook just needs to
  * return the node title and form editing fields specific to the node type.
  *
- * @param $node
+ * @param Node $node
  *   The node being added or edited.
  * @param $form_state
  *   The form state array.
@@ -1093,7 +1093,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_form($node, &$form_state) {
+function hook_form(Node $node, &$form_state) {
   $type = node_type_get_type($node);
 
   $form['title'] = array(
@@ -1134,12 +1134,12 @@
  * node table in the database, before field_attach_insert() is called, and
  * before hook_node_insert() is invoked.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being created.
  *
  * @ingroup node_api_hooks
  */
-function hook_insert($node) {
+function hook_insert(Node $node) {
   db_insert('mytable')
     ->fields(array(
       'nid' => $node->nid,
@@ -1192,12 +1192,12 @@
  * node table in the database, before field_attach_update() is called, and
  * before hook_node_update() is invoked.
  *
- * @param $node
+ * @param Node $node
  *   The node that is being updated.
  *
  * @ingroup node_api_hooks
  */
-function hook_update($node) {
+function hook_update(Node $node) {
   db_update('mytable')
     ->fields(array('extra' => $node->extra))
     ->condition('nid', $node->nid)
@@ -1221,7 +1221,7 @@
  * have no effect.  The preferred method to change a node's content is to use
  * hook_node_presave() instead.
  *
- * @param $node
+ * @param Node $node
  *   The node being validated.
  * @param $form
  *   The form being used to edit the node.
@@ -1230,7 +1230,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_validate($node, $form, &$form_state) {
+function hook_validate(Node $node, $form, &$form_state) {
   if (isset($node->end) && isset($node->start)) {
     if ($node->start > $node->end) {
       form_set_error('time', t('An event may not end before it starts.'));
@@ -1248,7 +1248,7 @@
  * so that the node type module can define a custom method for display, or
  * add to the default display.
  *
- * @param $node
+ * @param Node $node
  *   The node to be displayed, as returned by node_load().
  * @param $view_mode
  *   View mode, e.g. 'full', 'teaser', ...
@@ -1267,7 +1267,7 @@
  *
  * @ingroup node_api_hooks
  */
-function hook_view($node, $view_mode) {
+function hook_view(Node $node, $view_mode) {
   if ($view_mode == 'full' && node_is_page($node)) {
     $breadcrumb = array();
     $breadcrumb[] = l(t('Home'), NULL);
diff --git a/core/modules/node/node.entity.inc b/core/modules/node/node.entity.inc
new file mode 100644
index 0000000..6a396e1
--- /dev/null
+++ b/core/modules/node/node.entity.inc
@@ -0,0 +1,395 @@
+<?php
+
+/**
+ * @file
+ * Entity controller and class for nodes.
+ */
+
+/**
+ * Defines the node entity class.
+ */
+class Node extends Entity {
+
+  /**
+   * The node ID.
+   *
+   * @var integer
+   */
+  public $nid;
+
+  /**
+   * The node revision ID.
+   *
+   * @var integer
+   */
+  public $vid;
+
+  /**
+   * The node content_type (bundle).
+   *
+   * @var string
+   */
+  public $type;
+
+  /**
+   * The node language.
+   *
+   * @var string
+   */
+  public $language;
+
+  /**
+   * The node title.
+   *
+   * @var string
+   */
+  public $title;
+
+  /**
+   * The node owner's user ID.
+   *
+   * @var integer
+   */
+  public $uid;
+
+  /**
+   * The node published status indicator.
+   *
+   * Unpublished nodes are only visible to their authors and to administrators.
+   * The value is either NODE_PUBLISHED or NODE_NOT_PUBLISHED.
+   *
+   * @var integer
+   */
+  public $status;
+
+  /**
+   * The node creation timestamp.
+   *
+   * @var integer
+   */
+  public $created;
+
+  /**
+   * The node modification timestamp.
+   *
+   * @var integer
+   */
+  public $changed;
+
+  /**
+   * The node comment status indicator.
+   *
+   * 0 => no comments
+   * 1 => comments are read-only
+   * 2 => open (read/write)
+   *
+   * @var integer
+   */
+  public $comment;
+
+  /**
+   * The node promotion status.
+   *
+   * Promoted nodes should be displayed on the front page of the site. The
+   * value is either NODE_PROMOTED or NODE_NOT_PROMOTED.
+   *
+   * @var integer
+   */
+  public $promote;
+
+  /**
+   * The node sticky status.
+   *
+   * Sticky nodes should be displayed at the top of lists in which they appear.
+   * The value is either NODE_STICKY or NODE_NOT_STICKY.
+   *
+   * @var integer
+   */
+  public $sticky;
+
+  /**
+   * The node translation set ID.
+   *
+   * Translations sets are based on the ID of the node containing the source
+   * text for the translation set.
+   */
+  public $tnid;
+
+  /**
+   * The node 'translationn needed' indicator.
+   *
+   * @var integer
+   */
+  public $translate;
+
+  /**
+   * The node revision creation timestamp.
+   */
+  public $revision_timestamp;
+
+  /**
+   * The node revision author's user ID.
+   */
+  public $revision_uid;
+
+  public function createDuplicate() {
+    $node = parent::createDuplicate();
+    $node->{$this->entityInfo['entity keys']['revision']} = NULL;
+    return $node;
+  }
+
+}
+
+
+/**
+ * Controller class for nodes.
+ *
+ * This extends the DrupalDefaultEntityController class, adding required
+ * special handling for node objects.
+ */
+class NodeStorageController extends EntityDatabaseStorageController {
+
+  public function delete($ids) {
+    $entities = $ids ? $this->load($ids) : FALSE;
+    if (!$entities) {
+      // If no IDs or invalid IDs were passed, do nothing.
+      return;
+    }
+    $transaction = db_transaction();
+
+    try {
+      $this->preDelete($entities);
+      foreach ($entities as $id => $entity) {
+        $this->invokeHook('predelete', $entity);
+      }
+      $ids = array_keys($entities);
+
+      db_delete($this->entityInfo['base table'])
+        ->condition($this->idKey, $ids, 'IN')
+        ->execute();
+
+      if ($this->revisionKey) {
+        db_delete($this->revisionTable)
+          ->condition($this->idKey, $ids, 'IN')
+          ->execute();
+      }
+
+      // Reset the cache as soon as the changes have been applied.
+      $this->resetCache($ids);
+
+      $this->postDelete($entities);
+      foreach ($entities as $id => $entity) {
+        $this->invokeHook('delete', $entity);
+      }
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+    }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception($this->entityType, $e);
+      throw new EntityStorageException($e->getMessage, $e->getCode, $e);
+    }
+  }
+
+  public function save(EntityInterface $entity) {
+    global $user;
+    $transaction = db_transaction();
+    try {
+      // Load the stored entity, if any.
+      if (!$entity->isNew() && !isset($entity->original)) {
+        $entity->original = entity_load_unchanged($this->entityType, $entity->id());
+      }
+
+      $this->preSave($entity);
+      $this->invokeHook('presave', $entity);
+
+      $base_keys = array();
+      if ($entity->isNew()) {
+        $op = 'insert';
+        $return = drupal_write_record($this->entityInfo['base table'], $entity);
+      }
+      else {
+        $op = 'update';
+        $return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
+      }
+
+      if ($this->revisionKey) {
+        $this->saveRevision($entity);
+      }
+
+      $this->resetCache(array($entity->{$this->idKey}));
+
+      $this->postSave($entity);
+      $this->invokeHook($op, $entity);
+
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+      unset($entity->is_new);
+      unset($entity->original);
+
+      return $return;
+    }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception($this->entityType, $e);
+      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
+    }
+  }
+
+  protected function saveRevision(EntityInterface $entity) {
+    global $user;
+
+    $temp_uid = $entity->uid;
+    $entity->uid = $user->uid;
+
+    if (empty($entity->{$this->revisionKey}) || !empty($entity->revision)) {
+      $result = drupal_write_record($this->revisionTable, $entity);
+      db_update($this->entityInfo['base table'])
+        ->fields(array($this->revisionKey => $entity->{$this->revisionKey}))
+        ->condition($this->idKey, $entity->{$this->idKey})
+        ->execute();
+    }
+    else {
+      $result = drupal_write_record($this->revisionTable, $entity, $this->revisionKey);
+    }
+    $entity->uid = $temp_uid;
+
+    return $result;
+  }
+
+  protected function preSaveRevision(EntityInterface $entity) {
+
+  }
+
+  protected function postSaveRevision(EntityInterface $entity) {
+
+  }
+
+  /**
+   * Overrides DrupalDefaultEntityController::attachLoad().
+   */
+  protected function attachLoad(&$nodes, $revision_id = FALSE) {
+    // Create an array of nodes for each content type and pass this to the
+    // object type specific callback.
+    $typed_nodes = array();
+    foreach ($nodes as $id => $entity) {
+      $typed_nodes[$entity->type][$id] = $entity;
+    }
+
+    // Call object type specific callbacks on each typed array of nodes.
+    foreach ($typed_nodes as $node_type => $nodes_of_type) {
+      if (node_hook($node_type, 'load')) {
+        $function = node_type_get_base($node_type) . '_load';
+        $function($nodes_of_type);
+      }
+    }
+    // Besides the list of nodes, pass one additional argument to
+    // hook_node_load(), containing a list of node types that were loaded.
+    $argument = array_keys($typed_nodes);
+    $this->hookLoadArguments = array($argument);
+    parent::attachLoad($nodes, $revision_id);
+  }
+
+  /**
+   * Overrides DrupalDefaultEntityController::buildQuery().
+   */
+  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
+    // Ensure that uid is taken from the {node} table,
+    // alias timestamp to revision_timestamp and add revision_uid.
+    $query = parent::buildQuery($ids, $conditions, $revision_id);
+    $fields =& $query->getFields();
+    unset($fields['timestamp']);
+    $query->addField('revision', 'timestamp', 'revision_timestamp');
+    $fields['uid']['table'] = 'base';
+    $query->addField('revision', 'uid', 'revision_uid');
+    return $query;
+  }
+
+  protected function invokeHook($hook, EntityInterface $node) {
+    if ($hook == 'insert' || $hook == 'update') {
+      node_invoke($node, $hook);
+    }
+    else if ($hook == 'predelete') {
+      node_invoke($node, 'delete');
+    }
+
+    parent::invokeHook($hook, $node);
+
+    if ($hook == 'predelete') {
+      if (module_exists('search')) {
+        search_reindex($node->nid, 'node');
+      }
+    }
+    else if ($hook == 'presave') {
+
+      if ($node->isNew() || !empty($node->revision)) {
+        // When inserting either a new node or a new node revision, $node->log
+        // must be set because {node_revision}.log is a text column and therefore
+        // cannot have a default value. However, it might not be set at this
+        // point (for example, if the user submitting a node form does not have
+        // permission to create revisions), so we ensure that it is at least an
+        // empty string in that case.
+        // @todo: Make the {node_revision}.log column nullable so that we can
+        // remove this check.
+        if (!isset($node->log)) {
+          $node->log = '';
+        }
+      }
+      elseif (!isset($node->log) || $node->log === '') {
+        // If we are updating an existing node without adding a new revision, we
+        // need to make sure $node->log is unset whenever it is empty. As long as
+        // $node->log is unset, drupal_write_record() will not attempt to update
+        // the existing database column when re-saving the revision; therefore,
+        // this code allows us to avoid clobbering an existing log entry with an
+        // empty one.
+        $node->log = $node->original->log;
+      }
+
+      // When saving a new node revision, unset any existing $node->vid so as to
+      // ensure that a new revision will actually be created, then store the old
+      // revision ID in a separate property for use by node hook implementations.
+      if (!$node->isNew() && !empty($node->revision) && $node->vid) {
+        $node->old_vid = $node->vid;
+        $node->vid = NULL;
+      }
+    }
+    else if ($hook == 'insert' || $hook == 'update') {
+      node_access_acquire_grants($node, $hook === 'update');
+    }
+  }
+
+
+  /**
+   *
+   */
+  protected function preSave(EntityInterface $node) {
+
+    if (empty($node->created)) {
+      $node->created = REQUEST_TIME;
+    }
+
+    $node->changed = REQUEST_TIME;
+    $node->timestamp = REQUEST_TIME;
+
+  }
+
+  /**
+   *
+   */
+  protected function postSave(EntityInterface $node) {
+
+  }
+
+  protected function postDelete($nodes) {
+    $ids = array_keys($nodes);
+
+    db_delete('history')
+      ->condition('nid', $ids, 'IN')
+      ->execute();
+    db_delete('node_access')
+      ->condition('nid', $ids, 'IN')
+      ->execute();
+
+  }
+}
+
diff --git a/core/modules/node/node.info b/core/modules/node/node.info
index 33b5dd2..72875ab 100644
--- a/core/modules/node/node.info
+++ b/core/modules/node/node.info
@@ -4,6 +4,7 @@
 version = VERSION
 core = 8.x
 files[] = node.module
+files[] = node.entity.inc
 files[] = node.test
 dependencies[] = entity
 configure = admin/structure/types
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index c7be759..665e5ab 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -179,7 +179,8 @@
   $return = array(
     'node' => array(
       'label' => t('Node'),
-      'controller class' => 'NodeController',
+      'controller class' => 'NodeStorageController',
+      'entity class' => 'Node',
       'base table' => 'node',
       'revision table' => 'node_revision',
       'uri callback' => 'node_uri',
@@ -256,8 +257,11 @@
 
 /**
  * Entity uri callback.
+ *
+ * @param Node $node
+ *   A node entity.
  */
-function node_uri($node) {
+function node_uri(Node $node) {
   return array(
     'path' => 'node/' . $node->nid,
   );
@@ -310,10 +314,10 @@
 /**
  * Updates the 'last viewed' timestamp of the specified node for current user.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  */
-function node_tag_new($node) {
+function node_tag_new(Node $node) {
   global $user;
   if ($user->uid) {
     db_merge('history')
@@ -373,13 +377,13 @@
 /**
  * Extracts the type name.
  *
- * @param $node
+ * @param Node $node
  *   Either a string or object, containing the node type information.
  *
  * @return
  *   Node type of the passed-in data.
  */
-function _node_extract_type($node) {
+function _node_extract_type(Node $node) {
   return is_object($node) ? $node->type : $node;
 }
 
@@ -401,7 +405,7 @@
 /**
  * Returns the node type of the passed node or node type string.
  *
- * @param $node
+ * @param Node $node
  *   A node object or string that indicates the node type to return.
  *
  * @return
@@ -411,7 +415,7 @@
  *   fields used internally and defined in _node_types_build(),
  *   hook_node_info(), and node_type_set_defaults().
  */
-function node_type_get_type($node) {
+function node_type_get_type(Node $node) {
   $type = _node_extract_type($node);
   $types = _node_types_build()->types;
   return isset($types[$type]) ? $types[$type] : FALSE;
@@ -424,7 +428,7 @@
  * execute node-type-specific hooks. For types defined in the user interface
  * and managed by node.module, the base is 'node_content'.
  *
- * @param $node
+ * @param Node $node
  *   A node object or string that indicates the node type to return.
  *
  * @return
@@ -432,7 +436,7 @@
  *
  * @see node_invoke()
  */
-function node_type_get_base($node) {
+function node_type_get_base(Node $node) {
   $type = _node_extract_type($node);
   $types = _node_types_build()->types;
   return isset($types[$type]) && isset($types[$type]->base) ? $types[$type]->base : FALSE;
@@ -454,13 +458,13 @@
 /**
  * Returns the node type name of the passed node or node type string.
  *
- * @param $node
+ * @param Node $node
  *   A node object or string that indicates the node type to return.
  *
  * @return
  *   The node type name or FALSE if the node type is not found.
  */
-function node_type_get_name($node) {
+function node_type_get_name(Node $node) {
   $type = _node_extract_type($node);
   $types = _node_types_build()->names;
   return isset($types[$type]) ? $types[$type] : FALSE;
@@ -485,7 +489,7 @@
  *   The machine-readable name of a node type to load, where '_' is replaced
  *   with '-'.
  *
- * @return
+ * @return Node|bool
  *   A node type object or FALSE if $name does not exist.
  */
 function node_type_load($name) {
@@ -860,7 +864,7 @@
 /**
  * Determines whether a node hook exists.
  *
- * @param $node
+ * @param Node $node
  *   A node object or a string containing the node type.
  * @param $hook
  *   A string containing the name of the hook.
@@ -868,7 +872,7 @@
  * @return
  *   TRUE if the $hook exists in the node type of $node.
  */
-function node_hook($node, $hook) {
+function node_hook(Node $node, $hook) {
   $base = node_type_get_base($node);
   return module_hook($base, $hook);
 }
@@ -876,7 +880,7 @@
 /**
  * Invokes a node hook.
  *
- * @param $node
+ * @param Node $node
  *   A node object or a string containing the node type.
  * @param $hook
  *   A string containing the name of the hook.
@@ -886,7 +890,7 @@
  * @return
  *   The returned value of the invoked hook.
  */
-function node_invoke($node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
+function node_invoke(Node $node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
   if (node_hook($node, $hook)) {
     $base = node_type_get_base($node);
     $function = $base . '_' . $hook;
@@ -950,10 +954,10 @@
  * Fills in a few default values, and then invokes hook_prepare() on the node
  * type module, and hook_node_prepare() on all modules.
  *
- * @param $node
+ * @param Node $node
  *   The node object.
  */
-function node_object_prepare($node) {
+function node_object_prepare(Node $node) {
   // Set up default values, if required.
   $node_options = variable_get('node_options_' . $node->type, array('status', 'promote'));
   // If this is a new node, fill in the default values.
@@ -1045,166 +1049,12 @@
 /**
  * Saves changes to a node or adds a new node.
  *
- * @param $node
+ * @param Node $node
  *   The $node object to be saved. If $node->nid is
  *   omitted (or $node->is_new is TRUE), a new node will be added.
  */
-function node_save($node) {
-  $transaction = db_transaction();
-
-  try {
-    // Load the stored entity, if any.
-    if (!empty($node->nid) && !isset($node->original)) {
-      $node->original = entity_load_unchanged('node', $node->nid);
-    }
-
-    field_attach_presave('node', $node);
-    global $user;
-
-    // Determine if we will be inserting a new node.
-    if (!isset($node->is_new)) {
-      $node->is_new = empty($node->nid);
-    }
-
-    // Set the timestamp fields.
-    if (empty($node->created)) {
-      $node->created = REQUEST_TIME;
-    }
-    // The changed timestamp is always updated for bookkeeping purposes,
-    // for example: revisions, searching, etc.
-    $node->changed = REQUEST_TIME;
-
-    $node->timestamp = REQUEST_TIME;
-    $update_node = TRUE;
-
-    // Let modules modify the node before it is saved to the database.
-    module_invoke_all('node_presave', $node);
-    module_invoke_all('entity_presave', $node, 'node');
-
-    if ($node->is_new || !empty($node->revision)) {
-      // When inserting either a new node or a new node revision, $node->log
-      // must be set because {node_revision}.log is a text column and therefore
-      // cannot have a default value. However, it might not be set at this
-      // point (for example, if the user submitting a node form does not have
-      // permission to create revisions), so we ensure that it is at least an
-      // empty string in that case.
-      // @todo: Make the {node_revision}.log column nullable so that we can
-      // remove this check.
-      if (!isset($node->log)) {
-        $node->log = '';
-      }
-    }
-    elseif (!isset($node->log) || $node->log === '') {
-      // If we are updating an existing node without adding a new revision, we
-      // need to make sure $node->log is unset whenever it is empty. As long as
-      // $node->log is unset, drupal_write_record() will not attempt to update
-      // the existing database column when re-saving the revision; therefore,
-      // this code allows us to avoid clobbering an existing log entry with an
-      // empty one.
-      unset($node->log);
-    }
-
-    // When saving a new node revision, unset any existing $node->vid so as to
-    // ensure that a new revision will actually be created, then store the old
-    // revision ID in a separate property for use by node hook implementations.
-    if (!$node->is_new && !empty($node->revision) && $node->vid) {
-      $node->old_vid = $node->vid;
-      unset($node->vid);
-    }
-
-    // Save the node and node revision.
-    if ($node->is_new) {
-      // For new nodes, save new records for both the node itself and the node
-      // revision.
-      drupal_write_record('node', $node);
-      _node_save_revision($node, $user->uid);
-      $op = 'insert';
-    }
-    else {
-      // For existing nodes, update the node record which matches the value of
-      // $node->nid.
-      drupal_write_record('node', $node, 'nid');
-      // Then, if a new node revision was requested, save a new record for
-      // that; otherwise, update the node revision record which matches the
-      // value of $node->vid.
-      if (!empty($node->revision)) {
-        _node_save_revision($node, $user->uid);
-      }
-      else {
-        _node_save_revision($node, $user->uid, 'vid');
-        $update_node = FALSE;
-      }
-      $op = 'update';
-    }
-    if ($update_node) {
-      db_update('node')
-        ->fields(array('vid' => $node->vid))
-        ->condition('nid', $node->nid)
-        ->execute();
-    }
-
-    // Call the node specific callback (if any). This can be
-    // node_invoke($node, 'insert') or
-    // node_invoke($node, 'update').
-    node_invoke($node, $op);
-
-    // Save fields.
-    $function = "field_attach_$op";
-    $function('node', $node);
-
-    module_invoke_all('node_' . $op, $node);
-    module_invoke_all('entity_' . $op, $node, 'node');
-
-    // Update the node access table for this node. There's no need to delete
-    // existing records if the node is new.
-    $delete = $op == 'update';
-    node_access_acquire_grants($node, $delete);
-
-    // Clear internal properties.
-    unset($node->is_new);
-    unset($node->original);
-    // Clear the static loading cache.
-    entity_get_controller('node')->resetCache(array($node->nid));
-
-    // Ignore slave server temporarily to give time for the
-    // saved node to be propagated to the slave.
-    db_ignore_slave();
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    watchdog_exception('node', $e);
-    throw $e;
-  }
-}
-
-/**
- * Saves a revision with the ID of the current user.
- *
- * The resulting revision ID is available afterward in $node->vid.
- *
- * @param $node
- *   The node object to be processed.
- * @param $uid
- *   User ID of the current user.
- * @param $update
- *   (optional) To indicate that this is a new record to be inserted, omit this
- *   argument. If this is an update, this argument specifies the primary keys'
- *   field names. If there is only 1 field in the key, you may pass in a string;
- *   if there are multiple fields in the key, pass in an array.
- *
- * @see node_save()
- */
-function _node_save_revision($node, $uid, $update = NULL) {
-  $temp_uid = $node->uid;
-  $node->uid = $uid;
-  if (isset($update)) {
-    drupal_write_record('node_revision', $node, $update);
-  }
-  else {
-    drupal_write_record('node_revision', $node);
-  }
-  // Have node object still show node owner's uid, not revision author's.
-  $node->uid = $temp_uid;
+function node_save(Node $node) {
+  $node->save();
 }
 
 /**
@@ -1227,59 +1077,7 @@
  * @see hook_node_delete()
  */
 function node_delete_multiple($nids) {
-  $transaction = db_transaction();
-  if (!empty($nids)) {
-    $nodes = node_load_multiple($nids, array());
-
-    try {
-      foreach ($nodes as $nid => $node) {
-        // Call the node-specific callback (if any):
-        node_invoke($node, 'delete');
-
-        // Allow modules to act prior to node deletion.
-        module_invoke_all('node_predelete', $node);
-        module_invoke_all('entity_predelete', $node, 'node');
-
-        field_attach_delete('node', $node);
-
-        // Remove this node from the search index if needed.
-        // This code is implemented in node module rather than in search module,
-        // because node module is implementing search module's API, not the other
-        // way around.
-        if (module_exists('search')) {
-          search_reindex($nid, 'node');
-        }
-      }
-
-      // Delete after calling hooks so that they can query node tables as needed.
-      db_delete('node')
-        ->condition('nid', $nids, 'IN')
-        ->execute();
-      db_delete('node_revision')
-        ->condition('nid', $nids, 'IN')
-        ->execute();
-      db_delete('history')
-        ->condition('nid', $nids, 'IN')
-        ->execute();
-      db_delete('node_access')
-       ->condition('nid', $nids, 'IN')
-       ->execute();
-
-      foreach ($nodes as $nid => $node) {
-        // Allow modules to respond to node deletion.
-        module_invoke_all('node_delete', $node);
-        module_invoke_all('entity_delete', $node, 'node');
-      }
-    }
-    catch (Exception $e) {
-      $transaction->rollback();
-      watchdog_exception('node', $e);
-      throw $e;
-    }
-
-    // Clear the page and block and node_load_multiple caches.
-    entity_get_controller('node')->resetCache();
-  }
+  entity_delete_multiple('node', $nids);
 }
 
 /**
@@ -1313,7 +1111,7 @@
 /**
  * Generates an array for rendering the given node.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $view_mode
  *   (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'full.'
@@ -1324,7 +1122,7 @@
  * @return
  *   An array as expected by drupal_render().
  */
-function node_view($node, $view_mode = 'full', $langcode = NULL) {
+function node_view(Node $node, $view_mode = 'full', $langcode = NULL) {
   if (!isset($langcode)) {
     $langcode = $GLOBALS['language_content']->langcode;
   }
@@ -1378,7 +1176,7 @@
  * Contributed modules might define additional view modes, or use existing
  * view modes in additional contexts.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $view_mode
  *   (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'full.'
@@ -1386,7 +1184,7 @@
  *   (optional) A language code to use for rendering. Defaults to the global
  *   content language of the current request.
  */
-function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
+function node_build_content(Node $node, $view_mode = 'full', $langcode = NULL) {
   if (!isset($langcode)) {
     $langcode = $GLOBALS['language_content']->langcode;
   }
@@ -1441,7 +1239,7 @@
  *
  * Path: node/%node/revisions/%/view
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $message
  *   (optional) A flag which sets a page title relevant to the revision being
@@ -1452,7 +1250,7 @@
  *
  * @see node_menu()
  */
-function node_show($node, $message = FALSE) {
+function node_show(Node $node, $message = FALSE) {
   if ($message) {
     drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))), PASS_THROUGH);
   }
@@ -1469,13 +1267,13 @@
 /**
  * Checks whether the current page is the full page view of the passed-in node.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  *
  * @return
  *   The ID of the node if this is a full page view, otherwise FALSE.
  */
-function node_is_page($node) {
+function node_is_page(Node $node) {
   $page_node = menu_get_object();
   return (!empty($page_node) ? $page_node->nid == $node->nid : FALSE);
 }
@@ -1904,7 +1702,7 @@
  * - node/%node/revisions/%/revert
  * - node/%node/revisions/%/delete
  *
- * @param $node
+ * @param Node $node
  *   The node to check.
  * @param $op
  *   (optional) The specific operation being checked. Defaults to 'view.'
@@ -1914,7 +1712,7 @@
  *
  * @see node_menu()
  */
-function _node_revision_access($node, $op = 'view') {
+function _node_revision_access(Node $node, $op = 'view') {
   $access = &drupal_static(__FUNCTION__, array());
   if (!isset($access[$node->vid])) {
     // To save additional calls to the database, return early if the user
@@ -2190,12 +1988,12 @@
  *
  * Path: node/%node
  *
- * @param $node
+ * @param Node $node
  *   The node object.
  *
  * @see node_menu()
  */
-function node_page_title($node) {
+function node_page_title(Node $node) {
   return $node->title;
 }
 
@@ -2214,8 +2012,11 @@
 
 /**
  * Returns a list of all the existing revision numbers for the node passed in.
+ *
+ * @param Node $node
+ *   The node object.
  */
-function node_revision_list($node) {
+function node_revision_list(Node $node) {
   $revisions = array();
   $result = db_query('SELECT r.vid, r.title, r.log, r.uid, n.vid AS current_vid, r.timestamp, u.name FROM {node_revision} r LEFT JOIN {node} n ON n.vid = r.vid INNER JOIN {users} u ON u.uid = r.uid WHERE r.nid = :nid ORDER BY r.vid DESC', array(':nid' => $node->nid));
   foreach ($result as $revision) {
@@ -2730,12 +2531,12 @@
  *
  * Path: node/%node
  *
- * @param $node
+ * @param Node $node
  *   The node object.
  *
  * @see node_menu()
  */
-function node_page_view($node) {
+function node_page_view(Node $node) {
   // If there is a menu link to this node, the link becomes the last part
   // of the active trail, and the link name becomes the page title.
   // Thus, we must explicitly set the page title to be the node title.
@@ -2764,10 +2565,10 @@
 /**
  * Indexes a single node.
  *
- * @param $node
+ * @param Node $node
  *   The node to index.
  */
-function _node_index_node($node) {
+function _node_index_node(Node $node) {
   $node = node_load($node->nid);
 
   // Save the changed time of the most recent indexed node, for the search
@@ -2994,7 +2795,7 @@
  *   - "update"
  *   - "delete"
  *   - "create"
- * @param $node
+ * @param Node $node
  *   The node object on which the operation is to be performed, or the node type
  *   (e.g., 'forum') for the 'create' operation.
  * @param $account
@@ -3006,7 +2807,7 @@
  *
  * @see node_menu()
  */
-function node_access($op, $node, $account = NULL) {
+function node_access($op, Node $node, $account = NULL) {
   $rights = &drupal_static(__FUNCTION__, array());
 
   if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
@@ -3106,7 +2907,7 @@
 /**
  * Implements hook_node_access().
  */
-function node_node_access($node, $op, $account) {
+function node_node_access(Node $node, $op, $account) {
   $type = is_string($node) ? $node : $node->type;
 
   if (in_array($type, node_permissions_get_configured_types())) {
@@ -3494,13 +3295,13 @@
  * via hook_node_access_records_alter() implementations, and saves the collected
  * and altered grants to the database.
  *
- * @param $node
+ * @param Node $node
  *   The $node to acquire grants for.
  * @param $delete
  *   (optional) Whether to delete existing node access records before inserting
  *   new ones. Defaults to TRUE.
  */
-function node_access_acquire_grants($node, $delete = TRUE) {
+function node_access_acquire_grants(Node $node, $delete = TRUE) {
   $grants = module_invoke_all('node_access_records', $node);
   // Let modules alter the grants.
   drupal_alter('node_access_records', $grants, $node);
@@ -3522,7 +3323,7 @@
  * Note: Don't call this function directly from a contributed module. Call
  * node_access_acquire_grants() instead.
  *
- * @param $node
+ * @param Node $node
  *   The $node being written to. All that is necessary is that it contains a
  *   nid.
  * @param $grants
@@ -3538,7 +3339,7 @@
  *   purposes, and assumes the caller has already performed a mass delete of
  *   some form.
  */
-function _node_access_write_grants($node, $grants, $realm = NULL, $delete = TRUE) {
+function _node_access_write_grants(Node $node, $grants, $realm = NULL, $delete = TRUE) {
   if ($delete) {
     $query = db_delete('node_access')->condition('nid', $node->nid);
     if ($realm) {
@@ -3727,7 +3528,7 @@
 /**
  * Implements hook_form().
  */
-function node_content_form($node, $form_state) {
+function node_content_form(Node $node, $form_state) {
   // It is impossible to define a content type without implementing hook_form()
   // @todo: remove this requirement.
   $form = array();
@@ -3838,7 +3639,7 @@
 /**
  * Sets the status of a node to 1 (published).
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $context
  *   (optional) Array of additional information about what triggered the action.
@@ -3846,7 +3647,7 @@
  *
  * @ingroup actions
  */
-function node_publish_action($node, $context = array()) {
+function node_publish_action(Node $node, $context = array()) {
   $node->status = NODE_PUBLISHED;
   watchdog('action', 'Set @type %title to published.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3854,7 +3655,7 @@
 /**
  * Sets the status of a node to 0 (unpublished).
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $context
  *   (optional) Array of additional information about what triggered the action.
@@ -3862,7 +3663,7 @@
  *
  * @ingroup actions
  */
-function node_unpublish_action($node, $context = array()) {
+function node_unpublish_action(Node $node, $context = array()) {
   $node->status = NODE_NOT_PUBLISHED;
   watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3870,7 +3671,7 @@
 /**
  * Sets the sticky-at-top-of-list property of a node to 1.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $context
  *   (optional) Array of additional information about what triggered the action.
@@ -3878,7 +3679,7 @@
  *
  * @ingroup actions
  */
-function node_make_sticky_action($node, $context = array()) {
+function node_make_sticky_action(Node $node, $context = array()) {
   $node->sticky = NODE_STICKY;
   watchdog('action', 'Set @type %title to sticky.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3886,7 +3687,7 @@
 /**
  * Sets the sticky-at-top-of-list property of a node to 0.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $context
  *   (optional) Array of additional information about what triggered the action.
@@ -3894,7 +3695,7 @@
  *
  * @ingroup actions
  */
-function node_make_unsticky_action($node, $context = array()) {
+function node_make_unsticky_action(Node $node, $context = array()) {
   $node->sticky = NODE_NOT_STICKY;
   watchdog('action', 'Set @type %title to unsticky.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3902,7 +3703,7 @@
 /**
  * Sets the promote property of a node to 1.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $context
  *   (optional) Array of additional information about what triggered the action.
@@ -3910,7 +3711,7 @@
  *
  * @ingroup actions
  */
-function node_promote_action($node, $context = array()) {
+function node_promote_action(Node $node, $context = array()) {
   $node->promote = NODE_PROMOTED;
   watchdog('action', 'Promoted @type %title to front page.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3918,7 +3719,7 @@
 /**
  * Sets the promote property of a node to 0.
  *
- * @param $node
+ * @param Node $node
  *   A node object.
  * @param $context
  *   (optional) Array of additional information about what triggered the action.
@@ -3926,7 +3727,7 @@
  *
  * @ingroup actions
  */
-function node_unpromote_action($node, $context = array()) {
+function node_unpromote_action(Node $node, $context = array()) {
   $node->promote = NODE_NOT_PROMOTED;
   watchdog('action', 'Removed @type %title from front page.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3934,12 +3735,12 @@
 /**
  * Saves a node.
  *
- * @param $node
+ * @param Node $node
  *   The node to be saved.
  *
  * @ingroup actions
  */
-function node_save_action($node) {
+function node_save_action(Node $node) {
   node_save($node);
   watchdog('action', 'Saved @type %title', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
@@ -3947,7 +3748,7 @@
 /**
  * Assigns ownership of a node to a user.
  *
- * @param $node
+ * @param Node $node
  *   A node object to modify.
  * @param $context
  *   Array of additional information about what triggered the action. Includes
@@ -3959,7 +3760,7 @@
  * @see node_assign_owner_action_submit()
  * @ingroup actions
  */
-function node_assign_owner_action($node, $context) {
+function node_assign_owner_action(Node $node, $context) {
   $node->uid = $context['owner_uid'];
   $owner_name = db_query("SELECT name FROM {users} WHERE uid = :uid", array(':uid' => $context['owner_uid']))->fetchField();
   watchdog('action', 'Changed owner of @type %title to uid %name.', array('@type' =>  node_type_get_name($node), '%title' => $node->title, '%name' => $owner_name));
@@ -4060,7 +3861,7 @@
 /**
  * Unpublishes a node containing certain keywords.
  *
- * @param $node
+ * @param Node $node
  *   A node object to modify.
  * @param $context
  *   Array of additional information about what triggered the action. Includes
@@ -4073,7 +3874,7 @@
  *
  * @ingroup actions
  */
-function node_unpublish_by_keyword_action($node, $context) {
+function node_unpublish_by_keyword_action(Node $node, $context) {
   foreach ($context['keywords'] as $keyword) {
     $elements = node_view(clone $node);
     if (strpos(drupal_render($elements), $keyword) !== FALSE || strpos($node->title, $keyword) !== FALSE) {
@@ -4140,55 +3941,6 @@
   // straightforward, we can do it right now.
   if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) {
     node_access_rebuild();
-  }
-}
-
-/**
- * Controller class for nodes.
- *
- * This extends the DrupalDefaultEntityController class, adding required
- * special handling for node objects.
- */
-class NodeController extends DrupalDefaultEntityController {
-
-  /**
-   * Overrides DrupalDefaultEntityController::attachLoad().
-   */
-  protected function attachLoad(&$nodes, $revision_id = FALSE) {
-    // Create an array of nodes for each content type and pass this to the
-    // object type specific callback.
-    $typed_nodes = array();
-    foreach ($nodes as $id => $entity) {
-      $typed_nodes[$entity->type][$id] = $entity;
-    }
-
-    // Call object type specific callbacks on each typed array of nodes.
-    foreach ($typed_nodes as $node_type => $nodes_of_type) {
-      if (node_hook($node_type, 'load')) {
-        $function = node_type_get_base($node_type) . '_load';
-        $function($nodes_of_type);
-      }
-    }
-    // Besides the list of nodes, pass one additional argument to
-    // hook_node_load(), containing a list of node types that were loaded.
-    $argument = array_keys($typed_nodes);
-    $this->hookLoadArguments = array($argument);
-    parent::attachLoad($nodes, $revision_id);
-  }
-
-  /**
-   * Overrides DrupalDefaultEntityController::buildQuery().
-   */
-  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
-    // Ensure that uid is taken from the {node} table,
-    // alias timestamp to revision_timestamp and add revision_uid.
-    $query = parent::buildQuery($ids, $conditions, $revision_id);
-    $fields =& $query->getFields();
-    unset($fields['timestamp']);
-    $query->addField('revision', 'timestamp', 'revision_timestamp');
-    $fields['uid']['table'] = 'base';
-    $query->addField('revision', 'uid', 'revision_uid');
-    return $query;
   }
 }
 
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index cd1eb6d..4e10c5c 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -87,7 +87,12 @@
   global $user;
 
   $types = node_type_get_types();
-  $node = (object) array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => LANGUAGE_NONE);
+  $node = entity_create('node', array(
+    'uid' => $user->uid,
+    'name' => (isset($user->name) ? $user->name : ''),
+    'type' => $type,
+    'language' => LANGUAGE_NONE
+  ));
   drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)), PASS_THROUGH);
   $output = drupal_get_form($type . '_node_form', $node);
 
@@ -106,7 +111,7 @@
   // $form_state['node'] contains the actual entity being edited, but we must
   // not update it with form values that have not yet been validated, so we
   // create a pseudo-entity to use during validation.
-  $node = (object) $form_state['values'];
+  $node = entity_create('node', $form_state['values']);
   node_validate($node, $form, $form_state);
   entity_form_field_validate('node', $form, $form_state);
 }
@@ -374,7 +379,7 @@
 /**
  * Generates a node preview.
  *
- * @param $node
+ * @param Node $node
  *   The node to preview.
  *
  * @return
@@ -382,7 +387,7 @@
  *
  * @see node_form_build_preview()
  */
-function node_preview($node) {
+function node_preview(Node $node) {
   if (node_access('create', $node) || node_access('update', $node)) {
     _field_invoke_multiple('load', 'node', array($node->nid => $node));
     // Load the user's name when needed.
diff --git a/core/modules/node/node.test b/core/modules/node/node.test
index d018203..9bccd71 100644
--- a/core/modules/node/node.test
+++ b/core/modules/node/node.test
@@ -226,14 +226,12 @@
     // the "log message" field), and check that the original log message is
     // preserved.
     $new_title = $this->randomName(10) . 'testNodeRevisionWithoutLogMessage1';
-    $updated_node = (object) array(
-      'nid' => $node->nid,
-      'vid' => $node->vid,
-      'uid' => $node->uid,
-      'type' => $node->type,
-      'title' => $new_title,
-      'log' => '',
-    );
+
+    $updated_node = clone $node;
+    $updated_node->title = $new_title;
+    $updated_node->log = '';
+    $updated_node->revision = FALSE;
+
     node_save($updated_node);
     $this->drupalGet('node/' . $node->nid);
     $this->assertText($new_title, t('New node title appears on the page.'));
@@ -246,14 +244,12 @@
     // Save a new node revision without providing a log message, and check that
     // this revision has an empty log message.
     $new_title = $this->randomName(10) . 'testNodeRevisionWithoutLogMessage2';
-    $updated_node = (object) array(
-      'nid' => $node->nid,
-      'vid' => $node->vid,
-      'uid' => $node->uid,
-      'type' => $node->type,
-      'title' => $new_title,
-      'revision' => 1,
-    );
+
+    $updated_node = clone $node;
+    $updated_node->title = $new_title;
+    $updated_node->revision = TRUE;
+    $updated_node->log = NULL;
+
     node_save($updated_node);
     $this->drupalGet('node/' . $node->nid);
     $this->assertText($new_title, 'New node title appears on the page.');
@@ -515,7 +511,7 @@
     );
 
     try {
-      node_save((object) $edit);
+      node_save(entity_create('node', $edit));
       $this->fail(t('Expected exception has not been thrown.'));
     }
     catch (Exception $e) {
@@ -1168,7 +1164,7 @@
       'nid' => $test_nid,
       'is_new' => TRUE,
     );
-    $node = node_submit((object) $node);
+    $node = node_submit(entity_create('node', $node));
 
     // Verify that node_submit did not overwrite the user ID.
     $this->assertEqual($node->uid, $this->web_user->uid, t('Function node_submit() preserves user ID'));
@@ -1194,7 +1190,7 @@
       'title' => $this->randomName(8),
     );
 
-    node_save((object) $edit);
+    node_save(entity_create('node', $edit));
     $node = $this->drupalGetNodeByTitle($edit['title']);
     $this->assertEqual($node->created, REQUEST_TIME, t('Creating a node sets default "created" timestamp.'));
     $this->assertEqual($node->changed, REQUEST_TIME, t('Creating a node sets default "changed" timestamp.'));
@@ -1224,7 +1220,7 @@
       'changed' => 979534800, // Drupal 1.0 release.
     );
 
-    node_save((object) $edit);
+    node_save(entity_create('node', $edit));
     $node = $this->drupalGetNodeByTitle($edit['title']);
     $this->assertEqual($node->created, 280299600, t('Creating a node uses user-set "created" timestamp.'));
     $this->assertNotEqual($node->changed, 979534800, t('Creating a node doesn\'t use user-set "changed" timestamp.'));
@@ -1245,11 +1241,11 @@
    */
   function testDeterminingChanges() {
     // Initial creation.
-    $node = (object) array(
+    $node = entity_create('node', array(
       'uid' => $this->web_user->uid,
       'type' => 'article',
       'title' => 'test_changes',
-    );
+    ));
     node_save($node);
 
     // Update the node without applying changes.
diff --git a/core/modules/node/tests/node_access_test.module b/core/modules/node/tests/node_access_test.module
index f946573..eeff91d 100644
--- a/core/modules/node/tests/node_access_test.module
+++ b/core/modules/node/tests/node_access_test.module
@@ -26,7 +26,7 @@
 /**
  * Implements hook_node_access_records().
  */
-function node_access_test_node_access_records($node) {
+function node_access_test_node_access_records(Node $node) {
   $grants = array();
   // For NodeAccessBaseTableTestCase, only set records for private nodes.
   if (!variable_get('node_access_test_private') || $node->private) {
@@ -195,28 +195,28 @@
  * Implements hook_node_predelete().
  */
 
-function node_access_test_node_predelete($node) {
+function node_access_test_node_predelete(Node $node) {
   db_delete('node_access_test')->condition('nid', $node->nid)->execute();
 }
 
 /**
  * Implements hook_node_insert().
  */
-function node_access_test_node_insert($node) {
+function node_access_test_node_insert(Node $node) {
   _node_access_test_node_write($node);
 }
 
 /**
  * Implements hook_nodeapi_update().
  */
-function node_access_test_node_update($node) {
+function node_access_test_node_update(Node $node) {
   _node_access_test_node_write($node);
 }
 
 /**
  * Helper for node insert/update.
  */
-function _node_access_test_node_write($node) {
+function _node_access_test_node_write(Node $node) {
   if (isset($node->private)) {
     db_merge('node_access_test')
       ->key(array('nid' => $node->nid))
diff --git a/core/modules/node/tests/node_test.module b/core/modules/node/tests/node_test.module
index b0ebc14..924ba8b 100644
--- a/core/modules/node/tests/node_test.module
+++ b/core/modules/node/tests/node_test.module
@@ -25,7 +25,7 @@
 /**
  * Implements hook_node_view().
  */
-function node_test_node_view($node, $view_mode) {
+function node_test_node_view(Node $node, $view_mode) {
   if ($view_mode == 'rss') {
     // Add RSS elements and namespaces when building the RSS feed.
     $node->rss_elements[] = array(
@@ -66,7 +66,7 @@
 /**
  * Implements hook_node_access_records().
  */
-function node_test_node_access_records($node) {
+function node_test_node_access_records(Node $node) {
   // Return nothing when testing for empty responses.
   if (!empty($node->disable_node_access)) {
     return;
@@ -100,7 +100,7 @@
 /**
  * Implements hook_node_access_records_alter().
  */
-function node_test_node_access_records_alter(&$grants, $node) {
+function node_test_node_access_records_alter(&$grants, Node $node) {
   if (!empty($grants)) {
     foreach ($grants as $key => $grant) {
       // Alter grant from test_page_realm to test_alter_realm and modify the gid.
@@ -123,7 +123,7 @@
 /**
  * Implements hook_node_presave().
  */
-function node_test_node_presave($node) {
+function node_test_node_presave(Node $node) {
   if ($node->title == 'testing_node_presave') {
     // Sun, 19 Nov 1978 05:00:00 GMT
     $node->created = 280299600;
@@ -141,7 +141,7 @@
 /**
  * Implements hook_node_update().
  */
-function node_test_node_update($node) {
+function node_test_node_update(Node $node) {
   // Determine changes on update.
   if (!empty($node->original) && $node->original->title == 'test_changes') {
     if ($node->original->title != $node->title) {
diff --git a/core/modules/node/tests/node_test_exception.module b/core/modules/node/tests/node_test_exception.module
index 0fe9f35..13ba06e 100644
--- a/core/modules/node/tests/node_test_exception.module
+++ b/core/modules/node/tests/node_test_exception.module
@@ -9,7 +9,7 @@
 /**
  * Implements hook_node_insert().
  */
-function node_test_exception_node_insert($node) {
+function node_test_exception_node_insert(Node $node) {
   if ($node->title == 'testing_transaction_exception') {
     throw new Exception('Test exception for rollback.');
   }
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index 85833d7..3eee1fc 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -181,7 +181,7 @@
 /**
  * Implements hook_node_insert().
  */
-function path_node_insert($node) {
+function path_node_insert(Node $node) {
   if (isset($node->path)) {
     $path = $node->path;
     $path['alias'] = trim($path['alias']);
@@ -198,7 +198,7 @@
 /**
  * Implements hook_node_update().
  */
-function path_node_update($node) {
+function path_node_update(Node $node) {
   if (isset($node->path)) {
     $path = $node->path;
     $path['alias'] = trim($path['alias']);
@@ -219,7 +219,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function path_node_predelete($node) {
+function path_node_predelete(Node $node) {
   // Delete all aliases associated with this node.
   path_delete(array('source' => 'node/' . $node->nid));
 }
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index 40a481a..d233544 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -623,10 +623,10 @@
 /**
  * Return content for 'latest poll' block.
  *
- * @param $node
+ * @param Node $node
  *   The node object to load.
  */
-function poll_block_latest_poll_view($node) {
+function poll_block_latest_poll_view(Node $node) {
   global $user;
   $output = '';
 
diff --git a/core/modules/rdf/rdf.test b/core/modules/rdf/rdf.test
index 1344ded..b01d325 100644
--- a/core/modules/rdf/rdf.test
+++ b/core/modules/rdf/rdf.test
@@ -614,10 +614,10 @@
    *
    * Tests the tracker page for RDFa markup.
    *
-   * @param $node
+   * @param Node $node
    * The node just created.
    */
-  function _testBasicTrackerRdfaMarkup($node) {
+  function _testBasicTrackerRdfaMarkup(Node $node) {
     $url = url('node/' . $node->nid);
 
     $user = ($node->uid == 0) ? 'Anonymous user' : 'Registered user';
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 14ffc80..9a58fac 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -798,7 +798,7 @@
 /**
  * Implements hook_node_update_index().
  */
-function search_node_update_index($node) {
+function search_node_update_index(Node $node) {
   // Transplant links to a node into the target node.
   $result = db_query("SELECT caption FROM {search_node_links} WHERE nid = :nid", array(':nid' => $node->nid), array('target' => 'slave'));
   $output = array();
@@ -813,7 +813,7 @@
 /**
  * Implements hook_node_update().
  */
-function search_node_update($node) {
+function search_node_update(Node $node) {
   // Reindex the node when it is updated. The node is automatically indexed
   // when it is added, simply by being added to the node table.
   search_touch_node($node->nid);
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index cdab966..5e2989d 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -959,7 +959,7 @@
     );
     $settings['body'][$settings['language']][0] += $body;
 
-    $node = (object) $settings;
+    $node = entity_create('node', $settings);
     node_save($node);
 
     // Small hack to link revisions to our test user.
diff --git a/core/modules/simpletest/tests/form_test.module b/core/modules/simpletest/tests/form_test.module
index 5e3fb17..0b2aab7 100644
--- a/core/modules/simpletest/tests/form_test.module
+++ b/core/modules/simpletest/tests/form_test.module
@@ -1595,7 +1595,9 @@
   );
   // If requested, add the test field by attaching the node page form.
   if (!empty($_REQUEST['field'])) {
-    $node = (object)array('type' => 'page');
+    $node = entity_create('node', array(
+      'type' => 'page',
+    ));
     field_attach_form('node', $node, $form, $form_state);
   }
 }
@@ -1613,12 +1615,12 @@
  */
 function form_test_two_instances() {
   global $user;
-  $node1 = (object) array(
+  $node1 = entity_create('node', array(
     'uid' => $user->uid,
     'name' => (isset($user->name) ? $user->name : ''),
     'type' => 'page',
     'language' => LANGUAGE_NONE,
-  );
+  ));
   $node2 = clone($node1);
   $return['node_form_1'] = drupal_get_form('page_node_form', $node1);
   $return['node_form_2'] = drupal_get_form('page_node_form', $node2);
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index eda808f..e29b2ff 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -114,7 +114,7 @@
 /**
  * Implements hook_node_view().
  */
-function statistics_node_view($node, $view_mode) {
+function statistics_node_view(Node $node, $view_mode) {
   if ($view_mode != 'rss') {
     if (user_access('view post access counter')) {
       $statistics = statistics_get($node->nid);
@@ -393,7 +393,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function statistics_node_predelete($node) {
+function statistics_node_predelete(Node $node) {
   // clean up statistics table when node is deleted
   db_delete('node_counter')
     ->condition('nid', $node->nid)
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 8b77f2a..6ce1d7c 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -1510,7 +1510,7 @@
 /**
  * Implements hook_node_insert().
  */
-function taxonomy_node_insert($node) {
+function taxonomy_node_insert(Node $node) {
   // Add taxonomy index entries for the node.
   taxonomy_build_node_index($node);
 }
@@ -1521,7 +1521,7 @@
  * The index lists all terms that are related to a given node entity, and is
  * therefore maintained at the entity level.
  *
- * @param $node
+ * @param Node $node
  *   The node object.
  */
 function taxonomy_build_node_index($node) {
@@ -1587,7 +1587,7 @@
 /**
  * Implements hook_node_update().
  */
-function taxonomy_node_update($node) {
+function taxonomy_node_update(Node $node) {
   // Always rebuild the node's taxonomy index entries on node save.
   taxonomy_delete_node_index($node);
   taxonomy_build_node_index($node);
@@ -1596,7 +1596,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function taxonomy_node_predelete($node) {
+function taxonomy_node_predelete(Node $node) {
   // Clean up the {taxonomy_index} table when nodes are deleted.
   taxonomy_delete_node_index($node);
 }
@@ -1604,10 +1604,10 @@
 /**
  * Deletes taxonomy index entries for a given node.
  *
- * @param $node
+ * @param Node $node
  *   The node object.
  */
-function taxonomy_delete_node_index($node) {
+function taxonomy_delete_node_index(Node $node) {
   if (variable_get('taxonomy_maintain_index_table', TRUE)) {
     db_delete('taxonomy_index')->condition('nid', $node->nid)->execute();
   }
diff --git a/core/modules/taxonomy/taxonomy.test b/core/modules/taxonomy/taxonomy.test
index bfac280..5f1cc6b 100644
--- a/core/modules/taxonomy/taxonomy.test
+++ b/core/modules/taxonomy/taxonomy.test
@@ -1014,16 +1014,12 @@
     $this->assertEqual(1, $index_count, t('Term 2 is indexed once.'));
 
     // Redo the above tests without interface.
-    $update_node = array(
-      'nid' => $node->nid,
-      'vid' => $node->vid,
-      'uid' => $node->uid,
-      'type' => $node->type,
-      'title' => $this->randomName(),
-    );
+    $updated_node = clone $node;
+    $updated_node->title = $this->randomName();
+    unset($updated_node->{$this->field_name_1});
+    unset($updated_node->{$this->field_name_2});
 
     // Update the article with no term changed.
-    $updated_node = (object) $update_node;
     node_save($updated_node);
 
     // Check that the index was not changed.
@@ -1039,8 +1035,7 @@
     $this->assertEqual(1, $index_count, t('Term 2 is indexed once.'));
 
     // Update the article to change one term.
-    $update_node[$this->field_name_1][$langcode] = array(array('tid' => $term_1->tid));
-    $updated_node = (object) $update_node;
+    $updated_node->{$this->field_name_1}[$langcode] = array(array('tid' => $term_1->tid));
     node_save($updated_node);
 
     // Check that both terms are indexed.
@@ -1056,8 +1051,7 @@
     $this->assertEqual(1, $index_count, t('Term 2 is indexed.'));
 
     // Update the article to change another term.
-    $update_node[$this->field_name_2][$langcode] = array(array('tid' => $term_1->tid));
-    $updated_node = (object) $update_node;
+    $updated_node->{$this->field_name_2}[$langcode] = array(array('tid' => $term_1->tid));
     node_save($updated_node);
 
     // Check that only one term is indexed.
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index 89907b5..8ab2188 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -165,21 +165,21 @@
 /**
  * Implements hook_node_insert().
  */
-function tracker_node_insert($node, $arg = 0) {
+function tracker_node_insert(Node $node, $arg = 0) {
   _tracker_add($node->nid, $node->uid, $node->changed);
 }
 
 /**
  * Implements hook_node_update().
  */
-function tracker_node_update($node, $arg = 0) {
+function tracker_node_update(Node $node, $arg = 0) {
   _tracker_add($node->nid, $node->uid, $node->changed);
 }
 
 /**
  * Implements hook_node_predelete().
  */
-function tracker_node_predelete($node, $arg = 0) {
+function tracker_node_predelete(Node $node, $arg = 0) {
   db_delete('tracker_node')
     ->condition('nid', $node->nid)
     ->execute();
diff --git a/core/modules/translation/tests/translation_test.module b/core/modules/translation/tests/translation_test.module
index e3bb4b5..20e5dee 100644
--- a/core/modules/translation/tests/translation_test.module
+++ b/core/modules/translation/tests/translation_test.module
@@ -8,6 +8,6 @@
 /**
  * Implements hook_node_insert().
  */
-function translation_test_node_insert($node) {
+function translation_test_node_insert(Node $node) {
   drupal_write_record('node', $node, 'nid');
 }
diff --git a/core/modules/translation/translation.module b/core/modules/translation/translation.module
index 5fbdf01..dcb0410 100644
--- a/core/modules/translation/translation.module
+++ b/core/modules/translation/translation.module
@@ -204,7 +204,7 @@
  * a translation set. If no language provider is enabled "fall back" to the
  * simple links built through the result of translation_node_get_translations().
  */
-function translation_node_view($node, $view_mode) {
+function translation_node_view(Node $node, $view_mode) {
   // If the site has no translations or is not multilingual we have no content
   // translation links to display.
   if (isset($node->tnid) && language_multilingual() && $translations = translation_node_get_translations($node->tnid)) {
@@ -261,7 +261,7 @@
 /**
  * Implements hook_node_prepare().
  */
-function translation_node_prepare($node) {
+function translation_node_prepare(Node $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->type) &&
     // And it's a new node.
@@ -311,7 +311,7 @@
 /**
  * Implements hook_node_insert().
  */
-function translation_node_insert($node) {
+function translation_node_insert(Node $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->type)) {
     if (!empty($node->translation_source)) {
@@ -346,7 +346,7 @@
 /**
  * Implements hook_node_update().
  */
-function translation_node_update($node) {
+function translation_node_update(Node $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->type)) {
     if (isset($node->translation) && $node->translation && !empty($node->language) && $node->tnid) {
@@ -375,7 +375,7 @@
  *
  * Ensure that duplicate translations can not be created for the same source.
  */
-function translation_node_validate($node, $form) {
+function translation_node_validate(Node $node, $form) {
   // Only act on translatable nodes with a tnid or translation_source.
   if (translation_supported_type($node->type) && (!empty($node->tnid) || !empty($form['#node']->translation_source->nid))) {
     $tnid = !empty($node->tnid) ? $node->tnid : $form['#node']->translation_source->nid;
@@ -389,7 +389,7 @@
 /**
  * Implements hook_node_predelete().
  */
-function translation_node_predelete($node) {
+function translation_node_predelete(Node $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->type)) {
     translation_remove_from_set($node);
diff --git a/core/modules/translation/translation.pages.inc b/core/modules/translation/translation.pages.inc
index 8c09850..4b14309 100644
--- a/core/modules/translation/translation.pages.inc
+++ b/core/modules/translation/translation.pages.inc
@@ -8,10 +8,10 @@
 /**
  * Overview page for a node's translations.
  *
- * @param $node
+ * @param Node $node
  *   Node object.
  */
-function translation_node_overview($node) {
+function translation_node_overview(Node $node) {
   include_once DRUPAL_ROOT . '/core/includes/language.inc';
 
   if ($node->tnid) {
diff --git a/core/modules/translation/translation.test b/core/modules/translation/translation.test
index f1457a8..841c673 100644
--- a/core/modules/translation/translation.test
+++ b/core/modules/translation/translation.test
@@ -224,9 +224,9 @@
     // Create a language neutral node and check that the language switcher is
     // left untouched.
     $node2 = $this->createPage($this->randomName(), $this->randomName(), LANGUAGE_NONE);
-    $node2_en = (object) array('nid' => $node2->nid, 'language' => 'en');
-    $node2_es = (object) array('nid' => $node2->nid, 'language' => 'es');
-    $node2_it = (object) array('nid' => $node2->nid, 'language' => 'it');
+    $node2_en = entity_create('node', array('nid' => $node2->nid, 'language' => 'en'));
+    $node2_es = entity_create('node', array('nid' => $node2->nid, 'language' => 'es'));
+    $node2_it = entity_create('node', array('nid' => $node2->nid, 'language' =>'it'));
     $this->assertLanguageSwitchLinks($node2_en, $node2_en, TRUE, $type);
     $this->assertLanguageSwitchLinks($node2_en, $node2_es, TRUE, $type);
     $this->assertLanguageSwitchLinks($node2_en, $node2_it, TRUE, $type);
@@ -247,8 +247,8 @@
     // Check that new nodes with a language assigned do not trigger language
     // switcher alterations when translation support is disabled.
     $node = $this->createPage($this->randomName(), $this->randomName());
-    $node_es = (object) array('nid' => $node->nid, 'language' => 'es');
-    $node_it = (object) array('nid' => $node->nid, 'language' => 'it');
+    $node_es = entity_create('node', array('nid' => $node->nid, 'language' => 'es'));
+    $node_it = entity_create('node', array('nid' => $node->nid, 'language' => 'it'));
     $this->assertLanguageSwitchLinks($node, $node, TRUE, $type);
     $this->assertLanguageSwitchLinks($node, $node_es, TRUE, $type);
     $this->assertLanguageSwitchLinks($node, $node_it, TRUE, $type);
@@ -265,7 +265,7 @@
    * Return an empty node data structure.
    */
   function emptyNode($langcode) {
-    return (object) array('nid' => NULL, 'language' => $langcode);
+    return entity_create('node', array('language' => $langcode));
   }
 
   /**
@@ -338,7 +338,7 @@
    * Create a translation for the specified basic page in the specified
    * language.
    *
-   * @param $node
+   * @param Node $node
    *   The basic page to create translation for.
    * @param $title
    *   Title of basic page in specified language.
@@ -347,7 +347,7 @@
    * @param $language
    *   Language code.
    */
-  function createTranslation($node, $title, $body, $language) {
+  function createTranslation(Node $node, $title, $body, $language) {
     $this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => $language)));
 
     $langcode = LANGUAGE_NONE;
@@ -396,7 +396,7 @@
   /**
    * Check that the specified language switch links are found/not found.
    *
-   * @param $node
+   * @param Node $node
    *   The node to display.
    * @param $translation
    *   The translation whose link has to be checked.
@@ -408,7 +408,7 @@
    * @return
    *   TRUE if the language switch links are found/not found.
    */
-  function assertLanguageSwitchLinks($node, $translation, $find = TRUE, $types = NULL) {
+  function assertLanguageSwitchLinks(Node $node, $translation, $find = TRUE, $types = NULL) {
     if (empty($types)) {
       $types = array('node', 'block-locale');
     }
diff --git a/core/modules/trigger/trigger.module b/core/modules/trigger/trigger.module
index 75f0133..582270d 100644
--- a/core/modules/trigger/trigger.module
+++ b/core/modules/trigger/trigger.module
@@ -209,13 +209,13 @@
  *
  * @param $type
  *   The type of action that is about to be called.
- * @param $node
+ * @param Node $node
  *   The node that was passed via the node hook.
  *
  * @return
  *   The object expected by the action that is about to be called.
  */
-function _trigger_normalize_node_context($type, $node) {
+function _trigger_normalize_node_context($type, Node $node) {
   // Note that comment-type actions are not supported in node contexts,
   // because we wouldn't know which comment to choose.
   switch ($type) {
@@ -229,7 +229,7 @@
 /**
  * Calls action functions for node triggers.
  *
- * @param $node
+ * @param Node $node
  *   Node object.
  * @param $op
  *   Operation to trigger.
@@ -238,7 +238,7 @@
  * @param $a4
  *   Additional argument to action function.
  */
-function _trigger_node($node, $hook, $a3 = NULL, $a4 = NULL) {
+function _trigger_node(Node $node, $hook, $a3 = NULL, $a4 = NULL) {
   // Keep objects for reuse so that changes actions make to objects can persist.
   static $objects;
   // Prevent recursion by tracking which operations have already been called.
@@ -283,35 +283,35 @@
 /**
  * Implements hook_node_view().
  */
-function trigger_node_view($node, $view_mode) {
+function trigger_node_view(Node $node, $view_mode) {
   _trigger_node($node, 'node_view', $view_mode);
 }
 
 /**
  * Implements hook_node_update().
  */
-function trigger_node_update($node) {
+function trigger_node_update(Node $node) {
   _trigger_node($node, 'node_update');
 }
 
 /**
  * Implements hook_node_presave().
  */
-function trigger_node_presave($node) {
+function trigger_node_presave(Node $node) {
   _trigger_node($node, 'node_presave');
 }
 
 /**
  * Implements hook_node_insert().
  */
-function trigger_node_insert($node) {
+function trigger_node_insert(Node $node) {
   _trigger_node($node, 'node_insert');
 }
 
 /**
  * Implements hook_node_predelete().
  */
-function trigger_node_predelete($node) {
+function trigger_node_predelete(Node $node) {
   _trigger_node($node, 'node_delete');
 }
 
