Shomeya

Two web artists. One amazing company.

Killing pesky caching during migrations and upgrades part 1: drupal_static()

from Michael Prasuhn on September 25, 2012 01:30pm

At the end of a long day of writing and testing some new features for a Drupal site I decided to be a good developer and wrap up my changes in a migration, or update function and get the code out the door. My code looked something like this:

<?php
function my_module_update_7001() {
  module_enable(array('bean'));

  $plugin_info = _bean_admin_default_plugin();
  $plugin_info['name'] = '';

  $bean_type = new BeanCustom($plugin_info);
  $bean_type->type = 'social_links';
  $bean_type->setLabel(t('Social links'));
  $bean_type->setDescription('');
  $bean_type->save(TRUE);

  user_role_grant_permissions(1, array('view any social_links bean'));
}

Seem simple enough, just create a new entity type, and setup permission inside of hook_update_N. It's a piece of cake thanks to many of the new APIs in Drupal 7, right? When I tried this code, I was presented with the following error:

PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'module' cannot be null:    [error]
INSERT INTO {role_permission} (rid, permission, module) VALUES (:db_insert_placeholder_0,
:db_insert_placeholder_1, :db_insert_placeholder_2); Array
(
    [:db_insert_placeholder_0] => 1
    [:db_insert_placeholder_1] => view any social_links bean
    [:db_insert_placeholder_2] =>
)
 in user_role_grant_permissions() (line 3034 of modules/user/user.module).

Huh? How can that module not be enabled, and why doesn't user_role_grant_permission know about this module?

Working backwards from user_role_grant_permission(), you can see that it tries to look up the module that matches the permission by invoking hook_permission() in user_permission_get_modules().

You might think that perhaps the module_implements() cache is not updated to reflect that Bean module is now enabled, but that's not the case here, since enabling the module will ensure that the cache is updated. So where's the problem?

drupal_static_reset() to the rescue?

All the way up in Bean module, inside bean_permission() it calls another function bean_get_types() to get a list of types to create permissions for. bean_get_types() uses a static cache with the drupal_static() pattern. Great, so we can just clear that cache using drupal_static_reset('bean_get_types') and be on our way, right?

There's still more caching, as it turns out that bean_get_types actually calls all the way through to bean_fetch_plugin_info which is also using drupal_static() so if your bean type uses a custom plugin it still can't find all the types.

Reset all the things

The best solution to this and other caching issues that crop up within a single request is to just call drupal_static_reset() with no parameters, which will cause all the functions using the drupal_static() pattern to have their static cache reset. You may need to do this more than once in the course of an update or migration as creating new types or changing things could cause those caches to become out of date quickly.

Drupal updates can be tricky, but knowing how to clear out stale data can save you hours of frustration and pain when trying to automate things, or use APIs in ways that their developers might not have anticipated.

blog comments powered by Disqus