Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1066 diff -u -p -r1.1066 common.inc --- includes/common.inc 15 Dec 2009 08:45:32 -0000 1.1066 +++ includes/common.inc 21 Dec 2009 08:28:30 -0000 @@ -6297,10 +6297,11 @@ function entity_get_info($entity_type = 'fieldable' => FALSE, 'controller class' => 'DrupalDefaultEntityController', 'static cache' => TRUE, + 'entity cache' => FALSE, 'load hook' => $name . '_load', 'bundles' => array(), 'object keys' => array(), - 'cacheable' => TRUE, + 'field cache' => TRUE, 'translation' => array(), ); $entity_info[$name]['object keys'] += array( @@ -6354,8 +6355,8 @@ function entity_extract_ids($entity_type // If no bundle key provided, then we assume a single bundle, named after the // entity type. $bundle = $info['object keys']['bundle'] ? $entity->{$info['object keys']['bundle']} : $entity_type; - $cacheable = $info['cacheable']; - return array($id, $vid, $bundle, $cacheable); + $field_cache = $info['field cache']; + return array($id, $vid, $bundle, $field_cache); } /** Index: includes/entity.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/entity.inc,v retrieving revision 1.4 diff -u -p -r1.4 entity.inc --- includes/entity.inc 8 Dec 2009 06:33:11 -0000 1.4 +++ includes/entity.inc 21 Dec 2009 08:28:30 -0000 @@ -24,7 +24,12 @@ interface DrupalEntityControllerInterfac /** * Reset the internal, static entity cache. */ - public function resetCache(); + public function resetStaticCache(); + + /** + * Clear the persistent cache for an entity. + */ + public function clearEntityCache($id = NULL); /** * Load one or more entities. @@ -48,7 +53,8 @@ interface DrupalEntityControllerInterfac */ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { - protected $entityCache; + protected $entityStaticCache; + protected $entityPersistentCache; protected $entityType; protected $entityInfo; protected $hookLoadArguments; @@ -63,7 +69,7 @@ class DrupalDefaultEntityController impl public function __construct($entityType) { $this->entityType = $entityType; $this->entityInfo = entity_get_info($entityType); - $this->entityCache = array(); + $this->entityStaticCache = array(); $this->hookLoadArguments = array(); $this->idKey = $this->entityInfo['object keys']['id']; @@ -77,11 +83,27 @@ class DrupalDefaultEntityController impl } // Check if the entity type supports static caching of loaded entities. - $this->cache = !empty($this->entityInfo['static cache']); + $this->staticCache = !empty($this->entityInfo['static cache']); + // Check if the entity type supports persistent caching if loaded entities. + $this->persistentCache = !empty($this->entityInfo['entity cache']); } - public function resetCache() { - $this->entityCache = array(); + public function resetStaticCache() { + $this->entityStaticCache = array(); + } + + public function clearEntityCache($id = NULL) { + if (isset($id)) { + $cid = $this->entityType . ':' . $id; + $wildcard = FALSE; + } + else { + $id = '*'; + $wildcard = TRUE; + } + $bin = $this->entityInfo['entity cache bin']; + + cache_clear_all($cid, $bin, $wildcard); } public function load($ids = array(), $conditions = array()) { @@ -108,14 +130,30 @@ class DrupalDefaultEntityController impl $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 ($this->staticCache) { + $entities += $this->staticCacheGet($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->persistentCache && $this->ids && !$this->conditions) { + $cid = array(); + $cid_prefix = $this->entityType . ':'; + foreach ($this->ids as $id) { + $cids[] = $cid_prefix . $id; + } + if ($cached = cache_get_multiple($cids, $this->entityInfo['entity cache bin'])) { + foreach ($cached as $item) { + $id = str_replace($cid_prefix, '', $item->cid); + $cached_entities[$id] = $item->data; + } + $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. @@ -132,13 +170,28 @@ class DrupalDefaultEntityController impl // entity type specific load callback, for example hook_node_load(). if (!empty($queried_entities)) { $this->attachLoad($queried_entities); + // If these entities can be persistently cached, do so now. + if ($this->persistentCache) { + foreach ($queried_entities as $item) { + cache_set($cid_prefix . $item->{$this->idKey}, $item, $this->entityInfo['entity cache bin']); + } + } $entities += $queried_entities; } - if ($this->cache) { + // Now that attachLoad() has been run, merge in $cached_entities. + if (!empty($cached_entities)) { + $entities += $cached_entities; + } + + // Allow modules to act on entities without the result being persistently + // cached. + $this->attachAfterLoad($entities); + + if ($this->staticCache) { // Add entities to the cache if we are not loading a revision. if (!empty($queried_entities) && !$this->revisionId) { - $this->cacheSet($queried_entities); + $this->staticCacheSet($queried_entities); } } @@ -257,6 +310,27 @@ class DrupalDefaultEntityController impl } /** + * Attach data to entities after loading. + * + * This calls hook_TYPE_load_uncached() to allow modules to interact with + * entity objects without the result being persistently cached. + * + * If your hook_TYPE_load() expects special parameters apart from the + * queried entities, you can set $this->hookLoadArguments prior to + * calling the method. See NodeController::attachLoad() for an example. + */ + protected function attachAfterLoad(&$entities) { + // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are + // always the queried entities, followed by additional arguments set in + // $this->hookLoadArguments. + $args = array_merge(array($entities), $this->hookLoadArguments); + foreach (module_implements($this->entityInfo['load hook'] . '_uncached') as $module) { + call_user_func_array($module . '_' . $this->entityInfo['load hook'] . '_uncached', $args); + } + } + + + /** * Get entities from the static cache. * * @param $ids @@ -264,17 +338,17 @@ class DrupalDefaultEntityController impl * @param $conditions * If set, return entities that match all of these conditions. */ - protected function cacheGet($ids, $conditions = array()) { + protected function staticCacheGet($ids, $conditions = array()) { $entities = array(); // Load any available entities from the internal cache. - if (!empty($this->entityCache) && !$this->revisionId) { + if (!empty($this->entityStaticCache) && !$this->revisionId) { if ($ids) { - $entities += array_intersect_key($this->entityCache, array_flip($ids)); + $entities += array_intersect_key($this->entityStaticCache, array_flip($ids)); } // If loading entities only by conditions, fetch all available entities // from the cache. Entities which don't match are removed later. elseif ($conditions) { - $entities = $this->entityCache; + $entities = $this->entityStaticCache; } } @@ -294,7 +368,7 @@ class DrupalDefaultEntityController impl /** * Store entities in the static entity cache. */ - protected function cacheSet($entities) { - $this->entityCache += $entities; + protected function staticCacheSet($entities) { + $this->entityStaticCache += $entities; } } Index: modules/comment/comment.install =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.install,v retrieving revision 1.55 diff -u -p -r1.55 comment.install --- modules/comment/comment.install 8 Dec 2009 06:52:38 -0000 1.55 +++ modules/comment/comment.install 21 Dec 2009 08:28:30 -0000 @@ -215,6 +215,15 @@ function comment_update_7011() { } /** + * Add the {cache_comment} table. + */ +function comment_update_7012() { + $schema['cache_comment'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_comment']['description'] = 'Cache table used for taxonomy terms and vocabularies.'; + db_create_table('cache_comment', $schema['cache_comment']); +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ @@ -395,5 +404,8 @@ function comment_schema() { ), ); + $schema['cache_comment'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_comment']['description'] = 'Cache table used for comments.'; + return $schema; } Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.818 diff -u -p -r1.818 comment.module --- modules/comment/comment.module 17 Dec 2009 19:22:25 -0000 1.818 +++ modules/comment/comment.module 21 Dec 2009 08:28:31 -0000 @@ -108,6 +108,9 @@ function comment_entity_info() { ), 'bundles' => array(), 'static cache' => FALSE, + 'field cache' => FALSE, + 'entity cache' => TRUE, + 'entity cache bin' => 'cache_comment', ), ); Index: modules/field/field.attach.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v retrieving revision 1.65 diff -u -p -r1.65 field.attach.inc --- modules/field/field.attach.inc 20 Dec 2009 21:08:26 -0000 1.65 +++ modules/field/field.attach.inc 21 Dec 2009 08:28:31 -0000 @@ -540,7 +540,7 @@ function field_attach_load($obj_type, $o $info = entity_get_info($obj_type); // Only the most current revision of non-deleted fields for // cacheable fieldable types can be cached. - $cache_read = $load_current && $info['cacheable'] && empty($options['deleted']); + $cache_read = $load_current && $info['field cache'] && empty($options['deleted']); // In addition, do not write to the cache when loading a single field. $cache_write = $cache_read && !isset($options['field_id']); @@ -821,7 +821,7 @@ function field_attach_insert($obj_type, _field_invoke_default('insert', $obj_type, $object); _field_invoke('insert', $obj_type, $object); - list($id, $vid, $bundle, $cacheable) = entity_extract_ids($obj_type, $object); + list($id, $vid, $bundle, $field_cache) = entity_extract_ids($obj_type, $object); // Let any module insert field data before the storage engine, accumulating // saved fields along the way. @@ -854,7 +854,7 @@ function field_attach_insert($obj_type, // Let other modules act on inserting the object. module_invoke_all('field_attach_insert', $obj_type, $object); - if ($cacheable) { + if ($field_cache) { cache_clear_all("field:$obj_type:$id", 'cache_field'); } } @@ -870,7 +870,7 @@ function field_attach_insert($obj_type, function field_attach_update($obj_type, $object) { _field_invoke('update', $obj_type, $object); - list($id, $vid, $bundle, $cacheable) = entity_extract_ids($obj_type, $object); + list($id, $vid, $bundle, $field_cache) = entity_extract_ids($obj_type, $object); // Let any module update field data before the storage engine, accumulating // saved fields along the way. @@ -907,7 +907,7 @@ function field_attach_update($obj_type, // Let other modules act on updating the object. module_invoke_all('field_attach_update', $obj_type, $object); - if ($cacheable) { + if ($field_cache) { cache_clear_all("field:$obj_type:$id", 'cache_field'); } } Index: modules/node/node.install =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.install,v retrieving revision 1.37 diff -u -p -r1.37 node.install --- modules/node/node.install 4 Dec 2009 16:49:46 -0000 1.37 +++ modules/node/node.install 21 Dec 2009 08:28:31 -0000 @@ -353,6 +353,9 @@ function node_schema() { 'primary key' => array('type'), ); + $schema['cache_node'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_node']['description'] = 'Cache table used for nodes.'; + return $schema; } @@ -576,6 +579,16 @@ function node_update_7009() { } /** + * Add the {cache_node} table. + */ +function node_update_7010() { + // Add the cache_path table. + $schema['cache_node'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_node']['description'] = 'Cache table used for nodes.'; + db_create_table('cache_node', $schema['cache_node']); +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1185 diff -u -p -r1.1185 node.module --- modules/node/node.module 17 Dec 2009 19:22:25 -0000 1.1185 +++ modules/node/node.module 21 Dec 2009 08:28:32 -0000 @@ -192,6 +192,9 @@ function node_entity_info() { 'controller class' => 'NodeController', 'base table' => 'node', 'revision table' => 'node_revision', + 'entity cache bin' => 'cache_node', + 'entity cache' => TRUE, + 'field cache' => FALSE, 'fieldable' => TRUE, 'object keys' => array( 'id' => 'nid', @@ -3279,4 +3282,15 @@ class NodeController extends DrupalDefau $this->hookLoadArguments[] = array_keys($typed_nodes); parent::attachLoad($nodes); } + + protected function attachAfterLoad(&$nodes) { + // Create an array of content types and pass this to the + // object type specific callback. + $types = array(); + foreach ($nodes as $object) { + $types[$object->type] = $object->type; + } + $this->hookLoadArguments[] = $types; + parent::attachAfterLoad($nodes); + } } Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.430 diff -u -p -r1.430 system.install --- modules/system/system.install 14 Dec 2009 10:36:26 -0000 1.430 +++ modules/system/system.install 21 Dec 2009 08:28:32 -0000 @@ -604,6 +604,10 @@ function system_schema() { $schema['cache_menu']['description'] = 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.'; $schema['cache_path'] = $schema['cache']; $schema['cache_path']['description'] = 'Cache table for path alias lookup.'; + $schema['cache_file'] = $schema['cache']; + $schema['cache_file']['description'] = 'Cache table for file objects.'; + + $schema['date_format_type'] = array( 'description' => 'Stores configured date format types.', @@ -2716,6 +2720,16 @@ function system_update_7048() { } /** + * Add the {cache_file} table. + */ +function system_update_7049() { + // Add the cache_path table. + $schema['cache_file'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_file']['description'] = 'Cache table used for files.'; + db_create_table('cache_file', $schema['cache_file']); +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.862 diff -u -p -r1.862 system.module --- modules/system/system.module 15 Dec 2009 08:45:32 -0000 1.862 +++ modules/system/system.module 21 Dec 2009 08:28:33 -0000 @@ -282,6 +282,9 @@ function system_entity_info() { 'id' => 'fid', ), 'static cache' => FALSE, + 'field cache' => FALSE, + 'entity cache' => TRUE, + 'entity cache bin' => 'cache_file', ), ); } Index: modules/taxonomy/taxonomy.install =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.install,v retrieving revision 1.30 diff -u -p -r1.30 taxonomy.install --- modules/taxonomy/taxonomy.install 9 Dec 2009 15:52:25 -0000 1.30 +++ modules/taxonomy/taxonomy.install 21 Dec 2009 08:28:33 -0000 @@ -211,6 +211,9 @@ function taxonomy_schema() { ), ); + $schema['cache_taxonomy'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_taxonomy']['description'] = 'Cache table used for taxonomy terms and vocabularies.'; + return $schema; } @@ -429,3 +432,11 @@ function taxonomy_update_7006() { )); } +/** + * Add the {cache_taxonomy} table. + */ +function taxonomy_update_7007() { + $schema['cache_taxonomy'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_taxonomy']['description'] = 'Cache table used for taxonomy terms and vocabularies.'; + db_create_table('cache_taxonomy', $schema['cache_taxonomy']); +} Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.551 diff -u -p -r1.551 taxonomy.module --- modules/taxonomy/taxonomy.module 16 Dec 2009 19:57:45 -0000 1.551 +++ modules/taxonomy/taxonomy.module 21 Dec 2009 08:28:33 -0000 @@ -90,6 +90,9 @@ function taxonomy_entity_info() { 'controller class' => 'TaxonomyTermController', 'base table' => 'taxonomy_term_data', 'fieldable' => TRUE, + 'entity cache' => TRUE, + 'entity cache bin' => 'cache_taxonomy', + 'field cache' => FALSE, 'object keys' => array( 'id' => 'tid', 'bundle' => 'vocabulary_machine_name', @@ -115,6 +118,8 @@ function taxonomy_entity_info() { 'label' => t('Taxonomy vocabulary'), 'controller class' => 'TaxonomyVocabularyController', 'base table' => 'taxonomy_vocabulary', + 'entity cache' => TRUE, + 'entity cache bin' => 'cache taxonomy', 'object keys' => array( 'id' => 'vid', ),