Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1067 diff -u -p -r1.1067 common.inc --- includes/common.inc 21 Dec 2009 07:58:59 -0000 1.1067 +++ includes/common.inc 22 Dec 2009 07:22:25 -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 22 Dec 2009 07:22:25 -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->staticCache = 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()) { @@ -89,7 +111,7 @@ class DrupalDefaultEntityController impl $this->conditions = $conditions; $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])) { @@ -108,17 +130,35 @@ 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. + $cached_entities = array(); + 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. + $queried_entities = array(); if ($this->ids === FALSE || $this->ids || $this->revisionId || ($this->conditions && !$passed_ids)) { // Build the query. $this->buildQuery(); @@ -132,15 +172,31 @@ class DrupalDefaultEntityController impl // entity type specific load callback, for example hook_node_load(). if (!empty($queried_entities)) { $this->attachLoad($queried_entities); - $entities += $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']); + } + } } - if ($this->cache) { - // Add entities to the cache if we are not loading a revision. - if (!empty($queried_entities) && !$this->revisionId) { - $this->cacheSet($queried_entities); + // Now that attachLoad() has been run, merge in $cached_entities. + $new_entities = $queried_entities + $cached_entities; + + // Allow modules to act on entities without the result being persistently + // cached. + if (!empty($new_entities)) { + $this->attachAfterLoad($new_entities); + if ($this->staticCache) { + // Add entities to the cache if we are not loading a revision. + if (!empty($new_entities) && !$this->revisionId) { + $this->staticCacheSet($new_entities); + } } } + // Now merge statically cached entities and those fetched from the db + // or the persistent cache. + $entities += $new_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. @@ -257,6 +313,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 +341,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 +371,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 22 Dec 2009 07:22:26 -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.819 diff -u -p -r1.819 comment.module --- modules/comment/comment.module 21 Dec 2009 13:47:32 -0000 1.819 +++ modules/comment/comment.module 22 Dec 2009 07:22:26 -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.66 diff -u -p -r1.66 field.attach.inc --- modules/field/field.attach.inc 21 Dec 2009 13:47:32 -0000 1.66 +++ modules/field/field.attach.inc 22 Dec 2009 07:22:26 -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 22 Dec 2009 07:22:27 -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.1186 diff -u -p -r1.1186 node.module --- modules/node/node.module 21 Dec 2009 13:47:32 -0000 1.1186 +++ modules/node/node.module 22 Dec 2009 07:22:27 -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 22 Dec 2009 07:22:28 -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 22 Dec 2009 07:22:29 -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 22 Dec 2009 07:22:29 -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.552 diff -u -p -r1.552 taxonomy.module --- modules/taxonomy/taxonomy.module 21 Dec 2009 13:47:32 -0000 1.552 +++ modules/taxonomy/taxonomy.module 22 Dec 2009 07:22:29 -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', ), @@ -962,8 +967,8 @@ function taxonomy_implode_tags($tags, $v */ function taxonomy_field_info() { return array( - 'taxonomy_term' => array( - 'label' => t('Taxonomy term'), + 'term_reference' => array( + 'label' => t('Term reference'), 'description' => t('This field stores a reference to a taxonomy term.'), 'default_widget' => 'options_select', 'default_formatter' => 'taxonomy_term_link', @@ -986,7 +991,7 @@ function taxonomy_field_widget_info() { return array( 'taxonomy_autocomplete' => array( 'label' => t('Autocomplete term widget (tagging)'), - 'field types' => array('taxonomy_term'), + 'field types' => array('term_reference'), 'settings' => array( 'size' => 60, 'autocomplete_path' => 'taxonomy/autocomplete', @@ -1002,8 +1007,8 @@ function taxonomy_field_widget_info() { * Implements hook_field_widget_info_alter(). */ function taxonomy_field_widget_info_alter(&$info) { - $info['options_select']['field types'][] = 'taxonomy_term'; - $info['options_buttons']['field types'][] = 'taxonomy_term'; + $info['options_select']['field types'][] = 'term_reference'; + $info['options_buttons']['field types'][] = 'term_reference'; } /** @@ -1045,7 +1050,7 @@ function taxonomy_field_validate($obj_ty if (!empty($item['tid'])) { if (!isset($allowed_values[$item['tid']])) { $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'taxonomy_term_illegal_value', + 'error' => 'term_reference_illegal_value', 'message' => t('%name: illegal value.', array('%name' => t($instance['label']))), ); } @@ -1068,13 +1073,13 @@ function taxonomy_field_is_empty($item, */ function taxonomy_field_formatter_info() { return array( - 'taxonomy_term_link' => array( + 'term_reference_link' => array( 'label' => t('Link'), - 'field types' => array('taxonomy_term'), + 'field types' => array('term_reference'), ), - 'taxonomy_term_plain' => array( + 'term_reference_plain' => array( 'label' => t('Plain text'), - 'field types' => array('taxonomy_term'), + 'field types' => array('term_reference'), ), ); } @@ -1086,7 +1091,7 @@ function taxonomy_field_formatter_view($ $element = array(); switch ($display['type']) { - case 'taxonomy_term_link': + case 'term_reference_link': foreach ($items as $delta => $item) { // @todo Remove this when "node_view() does not call // field_attach_prepare_view()" bug is fixed. @@ -1094,7 +1099,7 @@ function taxonomy_field_formatter_view($ if (!isset($item['taxonomy_term'])) { $item['taxonomy_term'] = taxonomy_term_load($item['tid']); } - $term = $item['taxonomy_term']; + $term = $item['taxonomy_term_']; $element[$delta] = array( '#type' => 'link', '#title' => $term->name, @@ -1103,7 +1108,7 @@ function taxonomy_field_formatter_view($ } break; - case 'taxonomy_term_plain': + case 'term_reference_plain': foreach ($items as $delta => $item) { $term = $item['taxonomy_term']; $element[$delta] = array( @@ -1188,7 +1193,7 @@ function _taxonomy_clean_field_cache($te } // Load info for all taxonomy term fields. - $fields = field_read_fields(array('type' => 'taxonomy_term')); + $fields = field_read_fields(array('type' => 'term_reference')); foreach ($fields as $field_name => $field) { // Assemble an array of vocabulary IDs that are used in this field.