Index: modules/entitycache/entitycache.info
===================================================================
RCS file: modules/entitycache/entitycache.info
diff -N modules/entitycache/entitycache.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/entitycache/entitycache.info	27 Oct 2009 17:50:24 -0000
@@ -0,0 +1,8 @@
+; $Id: entitycache.info,v 1.1 2009/09/24 13:10:20 catch Exp $
+name = Entity cache
+description = Provides caching for core entities including nodes and taxonomy terms.
+
+core = 7.x
+
+files[] = entitycache.module
+files[] = entitycache.install
Index: modules/entitycache/entitycache.install
===================================================================
RCS file: modules/entitycache/entitycache.install
diff -N modules/entitycache/entitycache.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/entitycache/entitycache.install	27 Oct 2009 17:50:24 -0000
@@ -0,0 +1,17 @@
+<?php
+// $Id: entitycache.install,v 1.2 2009/09/25 09:18:47 catch Exp $
+
+/**
+ * @file Schema and install hooks for the entitycache module.
+ */
+function entitycache_schema() {
+  $schema = array();
+  $entities = entitycache_supported_entities();
+  $cache_schema = drupal_get_schema_unprocessed('system', 'cache');
+
+  foreach ($entities as $type) {
+    $schema["cache_entity_$type"] = $cache_schema;
+    $schema["cache_entity_$type"]['description'] = "Cache table used to store $type entity records.";
+  }
+  return $schema;
+}
Index: modules/entitycache/entitycache.module
===================================================================
RCS file: modules/entitycache/entitycache.module
diff -N modules/entitycache/entitycache.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/entitycache/entitycache.module	27 Oct 2009 17:50:24 -0000
@@ -0,0 +1,287 @@
+<?php
+// $Id: entitycache.module,v 1.3 2009/10/18 10:04:02 catch Exp $
+
+/**
+ * @file
+ * Implements entity hooks and classes to allow for caching of core entities.
+ */
+
+/**
+ * Implement hook_entity_info_alter();
+ */
+function entitycache_entity_info_alter(&$entity_info) {
+  $entity_info['node']['controller class'] = 'EntityCacheNodeController';
+  $entity_info['node']['cacheable'] = FALSE;
+  $entity_info['taxonomy_term']['controller class'] = 'EntityCacheTaxonomyTermController';
+  $entity_info['taxonomy_term']['cacheable'] = FALSE;
+  $entity_info['comment']['controller class'] = 'EntityCacheCommentController';
+  $entity_info['comment']['cacheable'] = FALSE;
+}
+
+/**
+ * Controller class for nodes.
+ *
+ * This extends the EntityCacheBaseController class, adding required
+ * special handling for node objects.
+ */
+class EntityCacheNodeController extends EntityCacheBaseController {
+  function attachLoad(&$nodes) {
+    NodeController::attachLoad($nodes);
+  }
+}
+
+/**
+ * Controller class for taxonomy terms.
+ *
+ * Currently an exact copy of TaxonomyTermController.
+ */
+class EntityCacheTaxonomyTermController extends EntityCacheBaseController {
+  protected $type;
+  public function load($ids = array(), $conditions = array()) {
+    return TaxonomyTermController::load($ids, $conditions);
+  }
+
+  protected function buildQuery() {
+    TaxonomyTermController::buildQuery();
+  }
+
+  protected function cacheGet($ids) {
+    return TaxonomyTermController::cacheGet($ids);
+  }
+}
+
+/**
+ * Controller class for comments.
+ *
+ * Currently a direct copy of CommentController.
+ */
+class EntityCacheCommentController extends EntityCacheBaseController {
+  protected function buildQuery() {
+    CommentController::buildQuery();
+  }
+
+  protected function attachLoad(&$comments) {
+    CommentController::attachLoad($comments);
+  }
+}
+
+/**
+ *  Set up a base controller to allow for cache_get_multiple() and cache_set().
+ */
+class EntityCacheBaseController extends DrupalDefaultEntityController {
+
+  public function load($ids = array(), $conditions = array()) {
+    $this->ids = $ids;
+    $this->conditions = $conditions;
+
+    $entities = array();
+    $cached_entities = array();
+    $queried_entities = array();
+
+    // Revisions are not statically cached, and require a different query to
+    // other conditions, so separate the revision id into its own variable.
+    if ($this->revisionKey && isset($this->conditions[$this->revisionKey])) {
+      $this->revisionId = $this->conditions[$this->revisionKey];
+      unset($this->conditions[$this->revisionKey]);
+    }
+    else {
+      $this->revisionId = FALSE;
+    }
+
+    // Create a new variable which is either a prepared version of the $ids
+    // array for later comparison with the entity cache, or FALSE if no $ids
+    // were passed. The $ids array is reduced as items are loaded from cache,
+    // and we need to know if it's empty for this reason to avoid querying the
+    // database when all requested entities are loaded from cache.
+    $passed_ids = !empty($this->ids) ? array_flip($this->ids) : FALSE;
+    // Try to load entities from the static cache, if the entity type supports
+    // static caching.
+    if ($this->cache) {
+      $entities = $this->cacheGet($this->ids, $this->conditions);
+      // If any entities were loaded, remove them from the ids still to load.
+      if ($passed_ids) {
+        $this->ids = array_keys(array_diff_key($passed_ids, $entities));
+      }
+
+      // Attempt to load any remaining entities from the persistent cache.
+      if ($this->ids && !$this->conditions) {
+        $cached = cache_get_multiple($this->ids, 'cache_entity_' . $this->entityType);
+        foreach ($cached as $item) {
+          $cached_entities[$item->cid] = $item->data;
+        }
+        if ($cached) {
+          $this->ids = array_diff($this->ids, array_keys($cached_entities));
+        }
+      }
+    }
+
+    // Load any remaining entities from the database. This is the case if $ids
+    // is set to FALSE (so we load all entities), if there are any ids left to
+    // load, if loading a revision, or if $conditions was passed without $ids.
+    if ($this->ids === FALSE || $this->ids || $this->revisionId || ($this->conditions && !$passed_ids)) {
+      // Build the query.
+      $this->buildQuery();
+      $queried_entities = $this->query
+        ->execute()
+        ->fetchAllAssoc($this->idKey);
+    }
+
+    // Pass all entities loaded from the database through $this->attachLoad(),
+    // which attaches fields (if supported by the entity type) and calls the
+    // entity type specific load callback, for example hook_node_load().
+    if (!empty($queried_entities)) {
+      $this->attachLoad($queried_entities);
+      $entities += $queried_entities;
+    }
+
+    if ($this->cache && !empty($queried_entities)) {
+      // Add entities to the cache if we are not loading a revision.
+      if (!$this->revisionId) {
+        foreach ($queried_entities as $item) {
+          cache_set($item->{$this->idKey}, $item, 'cache_entity_' . $this->entityType);
+        }
+      }
+    }
+    $new_entities = $queried_entities + $cached_entities;
+
+    if (!empty($new_entities)) {
+      $this->attachAfterLoad($new_entities);
+      if (!$this->revisionId) {
+        $this->cacheSet($new_entities);
+      }
+      $entities += $cached_entities;
+    }
+    // Ensure that the returned array is ordered the same as the original
+    // $ids array if this was passed in and remove any invalid ids.
+    if ($passed_ids) {
+      // Remove any invalid ids from the array.
+      $passed_ids = array_intersect_key($passed_ids, $entities);
+      foreach ($entities as $entity) {
+        $passed_ids[$entity->{$this->idKey}] = $entity;
+      }
+      $entities = $passed_ids;
+    }
+    return $entities;
+  }
+
+  /**
+   * Allow modules to implement uncached entity hooks.
+   *
+   * Adds an additional hook_entitycache_$type_load() for modules which need
+   * to add data to objects during the request which can't be cached.
+   * @see entitycache_entitycache_node_load().
+   */
+  protected function attachAfterLoad($new_entities) {
+    foreach (module_implements('entitycache_' . $this->entityType . '_load', $new_entities) as $module) {
+      $function = $module . '_entitycache_' . $this->entityType . '_load';
+      call_user_func_array($function, $new_entities);
+    }
+  }
+}
+
+/**
+ * Helper function to list all supported core entities.
+ *
+ * @return
+ *   An array of core entities.
+ */
+function entitycache_supported_entities() {
+  return array('node', 'comment', 'taxonomy_term');
+}
+
+/**
+ * Implement hook_entitycache_node_load().
+ */
+function entitycache_entitycache_node_load($nodes) {
+  // Both book and poll modules need to add data for the duration of the request
+  // only. We can't suppress their hooks being called then cached, so simply
+  // call them again ourselves which will overwrite the cached values with the
+  // correct ones.
+  // module_invoke() and module_exists() both break simpletest, so use
+  // function_exists() to avoid failures.
+  if (function_exists('book_node_load')) {
+    book_node_load($nodes, array());
+  }
+  if (function_exists('poll_load')) {
+    poll_load($nodes);
+  }
+}
+
+/**
+ * Helper function for clearing the node cache.
+ */
+function entitycache_node_helper($node) {
+  cache_clear_all($node->nid, 'cache_entity_node');
+}
+
+/**
+ * Implement hook_node_delete().
+ */
+function entitycache_node_delete($node) {
+  entitycache_node_helper($node);
+}
+
+/**
+ * Implement hook_node_update().
+ */
+function entitycache_node_update($node) {
+  entitycache_node_helper($node);
+}
+
+/**
+ * Implement hook_taxonomy_term_delete().
+ */
+function entitycache_taxonomy_term_delete($term) {
+  cache_clear_all($term->tid, 'cache_entity_taxonomy_term');
+}
+
+/**
+ * Implement of hook_taxonomy_term_update().
+ */
+function entitycache_taxonomy_term_update($term) {
+  cache_clear_all($term->tid, 'cache_entity_taxonomy_term');
+}
+
+/**
+ * Helper function to for comment hooks.
+ */
+function entitycache_comment_helper($comment) {
+  cache_clear_all($comment->cid, 'cache_entity_comment');
+  cache_clear_all($comment->nid, 'cache_entity_node');
+}
+
+
+/**
+ * Implement hook_comment_delete().
+ */
+function entitycache_comment_delete($comment) {
+  entitycache_comment_helper($comment);
+}
+
+/**
+ * Implement hook_comment_insert().
+ */
+function hook_comment_insert($comment) {
+  entitycache_comment_helper($comment);
+}
+
+/**
+ * Implement hook_comment_update().
+ */
+function entitycache_comment_update($comment) {
+  entitycache_comment_helper($comment);
+}
+
+/**
+ * Implement hook_comment_publish().
+ */
+function entitycache_comment_publish($comment) {
+  entitycache_comment_helper($comment);
+}
+
+/**
+ * Implement hook_comment_unpublish().
+ */
+function entitycache_comment_unpublish($comment) {
+  entitycache_comment_helper($comment);
+}
Index: profiles/default/default.info
===================================================================
RCS file: /cvs/drupal/drupal/profiles/default/default.info,v
retrieving revision 1.7
diff -u -p -r1.7 default.info
--- profiles/default/default.info	19 Oct 2009 18:28:16 -0000	1.7
+++ profiles/default/default.info	27 Oct 2009 17:50:24 -0000
@@ -19,4 +19,5 @@ dependencies[] = toolbar
 dependencies[] = field_ui
 dependencies[] = file
 dependencies[] = rdf
+dependencies[] = entitycache
 files[] = default.profile
