Problem/Motivation

drupal_alter() gets invoked in themes regardless of the hook. Thus themes can implement such as hook_element_info_alter(). The popular Omega theme uses that to provide a scripts element.

The code that does that in drupal_alter() relies on the $theme global, which means that it only works once drupal_theme_initialize() has run. If it gets called before that, alter hook implementations in themes simply get ignored.

Because element_info() statically caches the information from the alter hook, however, this means that if element_info() is called before drupal_theme_initialize() in _drupal_bootstrap_full(), then those implementations will not be called throughout the entire request.

Before drupal_initialize_theme() gets called menu_set_custom_theme() gets called, which ends up calling the access callback of the current menu item. Thus, if anything triggered from an access callback ends up calling drupal_render() then the return value of element_info() will be different.

This is very confusing and incredibly tedious to debug. The concrete use-case we hit this with was a little bit involved and involved a few contributed (albeit popular) modules, but because Omega (7.x-4.0) uses hook_element_info_alter() to render JavaScript, the bug resulted in no JavaScript being rendered on the page under some very strange conditions.

Proposed resolution

?

Remaining tasks

User interface changes

API changes

Comments

tstoeckler’s picture

Status: Active » Needs review
StatusFileSize
new3.31 KB

Note that switching the calls to menu_set_custom_theme() and drupal_theme_initialize() fixes this problem. Not sure what the repercussions of that would be, though.

For reference, here's the code and comment I comitted to our project to fix this, to provide some backstory:

  // Register the 'scripts' element on behalf of Omega theme. When a call to
  // drupal_render() happens before drupal_theme_initialize() is called in
  // _drupal_bootstrap_full(), in particular from menu_set_custom_theme(), the
  // element_info() is cached without theme information available. The cache
  // then gets re-used throughout the request even after the themes have been
  // loaded. Therefore we duplicate omega_element_info_alter() here so that the
  // provided 'scripts' element is always available, even before
  // drupal_theme_initialize().
  $info['scripts'] = array(
    '#items' => array(),
    '#pre_render' => array('omega_pre_render_scripts'),
    '#group_callback' => 'omega_group_js',
    '#aggregate_callback' => 'omega_aggregate_js',
  );
  // For reference, the call stack for which this bug surfaced was the
  // following. It requires the Chaos Tools Suite, Real name, and Entity tokens
  // modules to be enabled, a request to a menu route with ctools_menu_access()
  // as the access callback (for example by using the Page Manager module), no
  // entry for the current user in the {realname} table and a realname pattern
  // which contains fields.. Some specifics about the occurence of this bug are
  // still unclear.
  //  1. _drupal_bootstrap_full()
  //  2. menu_set_custom_theme()
  //  3. menu_get_custom_theme()
  //  4. menu_get_item()
  //  5. _menu_translate()
  //  6. _menu_check_access()
  //  7. ctools_menu_access() (as an access callback for the menu route)
  //  8. ctools_access()
  //  9. ctools_access_get_loggedin_context()
  // 10. ctools_context_create()
  // 11. ctools_context_create_user()
  // 12. user_load()
  // 13. user_load_multiple()
  // 14. entity_load()
  // 15. DrupalDefaultEntityController::load()
  // 16. UserController::attachLoad()
  // 17. DrupalDefaultEntityController::attachLoad()
  // 18. realname_user_load()
  // 19. realname_load_multiple()
  // 20. realname_update()
  // 21. token_replace()
  // 22. token_generate()
  // 23. module_invoke_all()
  // 24. field_tokens()
  // 25. drupal_render()
  // 26. element_info()

Status: Needs review » Needs work

The last submitted patch, 1: 2351731-1-element-info-alter-theme.patch, failed testing.

tstoeckler’s picture

Don't know about the fail in FileScanDirectoryTest but the fail in ThemeTestCase - and the fact there is only one - and that it's the one in line 102 - proves that this is a problem.

tstoeckler’s picture

Adding issue where the relevant code was added to _drupal_bootstrap_full()

tstoeckler’s picture

markhalliwell’s picture

I haven't actually run into this issue yet, but was searching issues because of #2024217: Should themes be able to implement hooks, e.g. hook_system_info_alter()? and found this issue too.

Seems to me that an "easy fix" would be to just call drupal_static_reset('element_info') from the base-theme's template.php file (i.e. when the theme is finally initialized).

tstoeckler’s picture

Sounds like a great idea, I updated #2351739: Clear static element_info cache in template.php so that hook_element_info_alter() is always called in the Omega queue with that suggestion.

markhalliwell’s picture

StatusFileSize
new4.92 KB
new1.01 KB

This fixes the static cache being set when themes are not yet initialized.

markhalliwell’s picture

StatusFileSize
new4.92 KB
new1.01 KB

This fixes the static cache being set when themes are not yet initialized.

markhalliwell’s picture

Component: base system » theme system
Status: Needs work » Needs review

Sorry for double post...

markhalliwell’s picture

Status: Needs review » Closed (duplicate)
Related issues: +#2448843: [regression] Themes unable to implement hook_element_info_alter()

I guess we should not have duplicates...

David_Rothstein’s picture

Status: Closed (duplicate) » Needs review

Let's reopen this; it's related to the Drupal 8 issue but not the same problem at all, and the patch here is older and not a backport of that one.

Some things that could be taken from that issue:

  1. Maybe look at the tests that went in there?
  2. Current patch above fixes this by not using a static cache at all when there's no theme yet. The suggestion from the other issue was to instead have the static cache keyed by theme (which is nice in the sense that it makes the fewest assumptions, and also does the most caching). Another option might be to not change the cache but just clear it when the theme is initialized...

See also some of the discussion in #496170: module_implements() cache can be polluted by module_invoke_all() being called (in)directly prior to full bootstrap completion which could be relevant.

terribeausejour’s picture

@David_Rothstein, thank you for re-opening this on Drupal 8. I will reiterate a summary of my posts from the related issue: https://www.drupal.org/node/2448843#comment-11013565, hoping my use case helps and hoping to get some help with a fix.

In reading this, I am not actually sure this is the same issue, so if the enlightened ones would please correct me if I need to move this elsewhere I would appreciate it. I am on Drupal 8.0.5. The site was built with Bootstrap 8.x-3.0-alpha1. The crash occurs when I submit a custom form (that is inside a block) after an ajax call is made based on a change event in a render element on the form. The crash does not occur if the submit is executed without that change event/ajax callback taking effect.

A partial call stack is below:

Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'bootstrap_form_process' not found or invalid function name in Drupal\Core\Form\FormBuilder->doBuildForm() (line 984 of core/lib/Drupal/Core/Form/FormBuilder.php).
Drupal\Core\Form\FormBuilder->doBuildForm('donate_form_vehicle', Array, Object) (Line: 560)
Drupal\Core\Form\FormBuilder->processForm('donate_form_vehicle', Array, Object) (Line: 319)
Drupal\Core\Form\FormBuilder->buildForm('Drupal\donate\Form\DonateFormVehicle', Object) (Line: 217)
Drupal\Core\Form\FormBuilder->getForm('Drupal\donate\Form\DonateFormVehicle') (Line: 34)
Drupal\donate\Plugin\Block\DonateBlock->build() (Line: 116)

As far as I can tell from the notes, there was a patch or patches to Drupal 8.x core related to this issue. However, with Drupal 8 and my version of the Bootstrap theme, it still occurs. Importantly, I tried updating my theme to the latest Bootstrap 8.x-3.0-beta3 and the problem went away, which seemed like great news until I found that the update wreaked havoc with my already built site, most importantly, the nav bar completely broke, so I reverted to the prior Bootstrap theme. If the patch for this is in core, perhaps there is a corresponding fix needed in Bootstrap theme? If so, is it possible to get an isolated patch to apply to the older Bootstrap theme as a "band-aid" solution?

Again, if anyone feels this post should go elsewhere, please let me know. Thanks much..

lokapujya’s picture

@David_Rothstein, thank you for re-opening this on Drupal 8.

It's not reopened on Drupal 8; This issue is for Drupal 7.

David Hernández’s picture

Status: Needs review » Needs work

I come here from the next issues on the Bootstrap theme: https://www.drupal.org/project/bootstrap/issues/2156371 and https://www.drupal.org/node/2613270

I tried the patch from #9 with no luck. I still see the same error.

Status: Needs work » Closed (outdated)

Automatically closed because Drupal 7 security and bugfix support has ended as of 5 January 2025. If the issue verifiably applies to later versions, please reopen with details and update the version.