Avoiding Loops In Preprocess Hooks

I've just released a new module (fanfare) which seems to work pretty well. I have a couple of silly bug fixes to put up tonight (knew I should've called it "beta") but other than that it works great. It's called Node Reference Variables:
http://drupal.org/project/nodereference_variables

All it does is present a load of stuff (depending on other contrib modules installed and some admin options) via the hook_preprocess_node() preprocess hook for themers to use to do cool theming stuff with Node Reference CCK fields. Main feature I'm using is the jQuery UI tabs it provides.

But anyway, I digress. I had a few issues with it. You see, because with this module we're loading nodes from within the preprocess hook for nodes, the probability of creating an infinite loop by accident is very high. Especially when other functions are called within the hook that also load nodes. (We have to load referenced nodes to present them back as variables.)

To give an illustration, Node Reference Variables has (or had) these lines in the hook:

<?php
 
// check we want the mark-up
 
if (variable_get('nodereference_variables_render_enabled', TRUE)) {
   
// add the rendered node for display to the existing field array
   
$vars[$fieldname][$key]['node_rendered'] =
                    
node_view($vars[$fieldname][$key]['node']);
  } 
?>

That all seems cool, until you try and run it. In certain circumstances during testing it causes an infinite loop (when a series of nodes reference each other, in a ring or as a pair).

This is because the hook is fired when the parent node is loaded, then for each valid Node Reference field containing data we call node_view(), which fires the hook again - and if there's a reciprocal reference in the now-loaded node, it will call node_view() again, this time with the parent node, which will fire the hook for the child, which will fire the hook for the parent and so on until PHP times out.

Avoiding this is pretty straightforward, thanks to a neat feature of template_preprocess(), the master function for preprocess hooks. You may have noticed an element in every $variables array called id. It so happens that this is an integer incremented by 1 with every pass of a *specific* implementation of a hook, e.g. mymodule_preprocess_node().

This is really handy for me. I only want my function to do stuff when the *first* parent node pass happens. To achieve what I need all I need to do is something like this in my hook:

<?php
/**
* Implementation of hook_preprocess_node().
*/
function mymodule_preprocess_node(&$vars) {
  if (
$vars['id'] === 1) {
   
// now we can do stuff knowing it will only
    // happen once on the first pass
 
}
}
?>

aha

should that not be named counter or something? id is has a meaning for html elements ... anyway, thanks. was wondering what that variable meant.

Indeed

The name could be more intuitive! Perhaps I should raise an issue on d.o.

The problem with using the

The problem with using the $id var is that it would only work when the page view is focused on a node, i.e., example.com/node/7. Get into a node teaser listing or a views node listing and your module will only work for the first node.

It would be better to set a custom static tracker to keep tabs on it with the node id. Make sure the same node id isn't processed more than once.

For reference

Here is the issue I raised:
http://drupal.org/node/509226

Thanks again!

Thanks

This is an excellent point. It hadn't occurred to me as I haven't used my module in a View or teaser list context as yet, but someone will for sure and this will be a bug. Thank you for pointing it out.

Will put an issue against Node Reference Variables now so I don't forget. =)

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.