Allow theme preprocess hooks to abort rendering

Created on 9 August 2023, over 1 year ago

Problem/Motivation

It would be very useful if preprocess hooks could prevent or abort the rendering completely.

To give some context, I was using this "hack" on 2 projects to prevent rendering empty regions, fields, layouts and paragraphs.
As an example, here is the preprocess I was using to prevent empty regions to render (sidebars for example):

function mymodule_preprocess_region(&$variables, $hook, &$info) {
  if (empty($variables['content'])) {
    $info['function'] = '';
  }
}

The method is convenient because in many cases, it is not easy at all to inject a #pre_render callback when the render array is built (which does have a way to prevent rendering) but that hack is no longer possible due to the changes from:
- #2760659: Allow the use of callbacks instead of global functions in preprocess function callbacks : call_user_func_array() now prevents passing the $info var as reference and modifying it.
- #3097889: Remove deprecated theme functions : is even more problematic since it removed theme functions which the hack was relying on.

Another advantage of this hack is that it skips the twig template to be rendered.
When any twig template is rendered, and if you have twig debug enabled, the debug part (HTML comments) will always be output which in many cases, can be annoying and tricky to detect emptiness.
This can apply for regions, fields, layouts, for example or any theme hook rendered with a twig template which may end up outputting nothing.
Example:

$region = [
  '#theme' => 'region',
  '#region' => 'region_name',
];
print (\Drupal::service('renderer')->renderPlain([
  '#theme' => 'region',
  '#region' => 'region_name',
]));

=> we get the twig debug part (HTML comments).

Proposed resolution

I'd like to hear some ideas on how to solve these kind of issues.
But I see 2 potential changes:
1. Alow preprocess hooks to abort rendering, by passing $variables['#abort_render'] = TRUE; for example.
2. Change twig_render_template() so that it won't output anything (twig debug HTML comments) when the render output is empty.

Remaining tasks

TODO

User interface changes

TODO

API changes

TODO

Data model changes

TODO

Release notes snippet

TODO

Feature request
Status

Active

Version

11.0 🔥

Component
Theme 

Last updated 5 days ago

Created by

🇧🇪Belgium herved

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @herved
  • 🇧🇪Belgium herved

    I found out about 🌱 [meta] Themes improperly check renderable arrays when determining visibility Needs work and I'm trying to wrap my head around it.
    FWIW here are 2 patches as mentioned in the description.
    With this approach preprocess hooks are responsible to bubble up cache metadata.
    Are there any downsides to doing this?

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    29,958 pass
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    29,933 pass, 7 fail
  • 🇧🇪Belgium herved
    +++ b/core/themes/engines/twig/twig.engine
    @@ -48,14 +42,27 @@ function twig_render_template($template_file, array $variables) {
    +  if (empty(trim($rendered_markup))) {
    

    Duh, empty() is the wrong condition... let's try trim($rendered_markup) === ''

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    29,958 pass
  • 🇩🇪Germany donquixote

    So the origin for this hack was, if I remember correctly, the observation that we cannot reliably determine if something is "empty" before all preprocess functions have run.

    I think we would want to implement this hack in the last of all preprocess functions.

    Therefore, would it make sense to introduce another callback that runs _after_ preprocess, that is specifically made for emptiness checks?

    Also, perhaps we need a convincing test case to get a better grip on what we are trying to accomplish.

  • 🇩🇪Germany donquixote

    Another interesting solution could be if a template could abort early and force empty output.
    This would probably need to be done in twig directly.

  • 🇧🇪Belgium herved

    Hi donquixote, thanks for jumping in :)

    Indeed, we are currently adding our preprocess last using hook_theme_registry_alter() and I remember we also had to use hook_module_implements_alter() to place our theme_registry_alter in last position because of the field_group module also implementing it. What a mess ^^
    So yes, that would make total sense. Perhaps a postprocess hook ?

    Also, perhaps we need a convincing test case to get a better grip on what we are trying to accomplish.

    True, I will try to write some scenarios tomorrow.

  • 🇧🇪Belgium herved

    The process layer would have been a good candidate to achieve this but it was removed in D8 #1843650: Remove the process layer (hook_process and hook_process_HOOK)
    So I have no idea what we could do here. Pretty sure we don't want to reinstate it...

    In terms of scenarios: region (content var), field (items var), layout (content var) and paragraph (content var) are some examples of theme hooks where those variables can contain a complex render array which turns out to be completely empty after rendering.
    By complex render array I mean not just #cache and #access but nested render arrays.

  • 🇺🇸United States euk

    Here is a simpler path with a config option available - https://www.drupal.org/project/drupal/issues/3400912 Make Twig debug comments optional for empty markup. Active

  • 🇧🇪Belgium herved

    Thanks @euk

    I thought about creating a specific issue for it but I see you did it already, I'll have a look at it, thanks.
    It looks like a better approach than #6 here (BTW don't use that one it's messing the order of debug markup).
    The approach in #3400912 makes it non disruptive by using a switch, which is interesting but could also make it harder to solve the other issue I'm trying to solve here.
    I'll leave this open for now.

  • 🇺🇸United States euk

    @herved, thanks in advance! Would be lovely to get the config option incorporated.

  • 🇧🇪Belgium herved

    Hiding patch: prevent_twig_debug_only-3380299-6.patch (covered by Make Twig debug comments optional for empty markup. Active )
    The other patch (allow_preprocess_abort_rendering-3380299-4.patch) is still relevant though.

  • 🇨🇦Canada Liam Morland Ontario, CA 🇨🇦

    In a similar vein, it would be useful if a preprocess function could change which template is actually used.

    Instead of #abort_render, there could be a way to set a different template name or set it to FALSE to abort rendering.

Production build 0.71.5 2024