Sorry for offtopic, but I cant get any help in module development forum.

If you understand hooks, please help me here: http://drupal.org/node/54902

Comments

nathandigriz’s picture

What coincidence. I was jsut sufing for more info on this . I understand how the system works. But what I don't get is where Drupal finds the names of the hooks it will call. Where in this code does the $args list come from?

function module_invoke_all() {
  $args = func_get_args();
  $hook = array_shift($args);
  $return = array();
  foreach (module_implements($hook) as $module) {
    $function = $module .'_'. $hook;
    $result = call_user_func_array($function, $args);
    if (isset($result) && is_array($result)) {
      $return = array_merge($return, $result);
    }
    else if (isset($result)) {
      $return[] = $result;
    }
  }

  return $return;
} 

I see that e-commece module says it implements a new hook but again I cannot see where.

So what determines if something is a core hook?

Heine’s picture

func_get_args returns an array with the arguments to module_invoke_all. The first argument should be the hook to be called.

I'm not sure what you mean with 'core' hook. module_implements simply checks whether modulename_hook exists (using the function module_hook which in turn uses the php function function_exists.

--
Tips for posting to the forums.
When your problem is solved, please post a follow-up to the thread you started.

nathandigriz’s picture

How does Drupal know what the pre-determined hooks are? Like in this list how does it know that "_nodeapi" is a hook? And then look for it in each module? Some like hook_cron use module_invoke_all but there are 44 and most of them are implemented without module_invoke_all.

# hook_access
# hook_auth
# hook_block
# hook_comment
# hook_cron
# hook_db_rewrite_sql
# hook_delete
# hook_elements
# hook_execute
# hook_exit
# hook_file_download
# hook_filter
# hook_filter_tips
# hook_footer
# hook_form
# hook_form_alter
# hook_help
# hook_info
# hook_init
# hook_insert
# hook_link
# hook_load
# hook_menu
# hook_nodeapi
# hook_node_grants
# hook_node_info
# hook_onload
# hook_perm
# hook_ping
# hook_prepare
# hook_search
# hook_search_item
# hook_search_preprocess
# hook_settings
# hook_taxonomy
# hook_update
# hook_update_index
# hook_update_N
# hook_user
# hook_validate
# hook_view
# hook_xmlrpc
# module_hook
# node_hook

Heine’s picture

Drupal core executes hooks on specific points by calling module_invoke or checking (eg node.module) what modules implement for example nodeapi with the appropriate hook.

Let's take a look at cron.php:

Cron calls cron hooks by executing module_invoke_all('cron');

module_invoke_all gets the argument list via func_get_args, the first of which is the hook to call: 'cron'.

For every module that implements a specific hook ('cron') it calls the hook ('cron'). How does module_invoke_all know what modules implement the hook? It calls module_implements($hook); here module_implements('cron')

module_implements first gets a list of loaded modules, then checks whether each module implements the specified $hook (cron), by calling module_hook(module, $hook) ('modulename', 'cron') which is actually a wrapper for function_exists with modulename_$hook as parameter; in this case does function 'modulename_cron' exists; eg story_cron, taxonomy_cron, younamethemodule_cron.

(All this is done only once, the results are buffered)

This all bubbles up to module_invoke_all, which calls the function modulename_hook; modulename_cron.

Suppose you write a module, where you call

  module_invoke_all('nonsense', arg1, arg2);

now module_invoke_all will dutifully check which modules implement this hook; and do nothing, because none do.

If you now make another module, that does implement this nonsense hook eg:

function mymodule_nonsense($arg1, $arg2) {

it will get called.

I hope this made some sense.

(edit: changed _ to comma)
--
Tips for posting to the forums.
When your problem is solved, please post a follow-up to the thread you started.

nathandigriz’s picture

Sorry my connection hanged and I posted to clear my screen.

Yes I understand the module_invoke_all but as I said in my adjusted post. This does not explain the other hooks which do not use module_invoke_all. In fact I can't find any trace of most of them in any sort of grep search. hook_nodeapi, hook_delete... etc to name one or two.

Heine’s picture

That narrows it down considerably: nodeapi is invoked in node.module by the function node_invoke_node_api (this function uses module_implements to check what modules implement nodeapi).

edited to add: hook_delete is called by node_invoke($node, 'delete');

--
Tips for posting to the forums.
When your problem is solved, please post a follow-up to the thread you started.

alexis’s picture

Thanks Heine, for an 'old' Drupal developer like me your explanation has helped me to better understand how to create my own hooks in some payment processing modules I'm currently coding. And of course, to better understand all of this we developers really need to read code, it's all there for those who want to dig.

DynV’s picture

There's a constantly up-to-date hook list at this page: http://api.drupal.org/api/group/hooks

nathandigriz’s picture

There seems to be great gaps in the documentation concerning things like node hooks and module hooks. These I had no idea existed until today.

nathandigriz’s picture

Just took a glance over this stuff. None of it is described or have any instructional text. There are not any developer comments either. It is a total dead end.

So I guess I am in the same boat with Budrick. What I thought I knew is nothing now.

system hooks?
module hooks?
node hooks?

The only thing I can say is I have some knowledge of system hooks. But how all this works together seems to be a mystery. I guess this is where programmers start complaining about the documentation being inadequate?

chiggsy’s picture

the rest of computing calls these type of functions 'callbacks'. The documentation in this case is inadaquate, no question. Where do new hooks come from? What are best practices? What about the OP's original questions? He's asked some very very good questions about the drupal hooks system , and unless someone is replying to his questions privately, he's gotten very little ( including this post here. Sorry man i've been following your threads like a stray dog .. hoping for a scrap ) Ahh well.

dopry’s picture

Callback and hook have been used interchangably. Normally in callback systems though you have to 'register' you callbacks.

Understanding the hooks system isn't that difficult...

Where do hooks come from?
a developer creates on using module_invoke_all

What are best practices?
Don't create hooks until you thoroughly understand drupal's callback system. Hooks can be expensive in terms of performance. Try to keep your hooks as short as possible avoid long operations and large inner loops.

Just about everything else can be found in the Docs or in the code itself, which tends to be the best documentation in my opinion.... api.drupal.org has a lot of great info.

chx’s picture

Hooks are ordinary functions, if your module is foo then you want to write foo_access for example. Available hooks are http://drupaldocs.org/hook_ and finally, Drupal does not know what are the hooks, why should it know? As a matter of fact, "defining" a hook is as easy as module_invoke_all('something') and hook_something is "defined".
--
My developer blog. | The news is Now Public | Ask not what Drupal can do for you -- ask what you can do for Drupal.

--
Drupal development: making the world better, one patch at a time. | A bedroom without a teddy is like a face without a smile.

nathandigriz’s picture

On the surface that would seem to be okay. But when you want to know how the core works and what to expect from the code it is necessary to know more than this.

An example would be if I wanted to do what node.module does in another module or if I want to bug fix something. Drupal is not going to get better quality by people knowing less about how it works or waiting for a critical bug before trying to learn about it. Better ideas come from knowledge of what has already been done.

function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
  if (node_hook($node, $hook)) {
    $function = node_get_base($node) ."_$hook";
    return ($function($node, $a2, $a3, $a4));
  }
} 

function node_hook(&$node, $hook) {
  return module_hook(node_get_base($node), $hook);
}

function node_get_base($node) {
  return _node_names('base', $node);
} 

function _node_names($op = '', $node = NULL) {
  static $node_names = array();
  static $node_list = array();

  if (empty($node_names)) {
    $node_names = module_invoke_all('node_info');
    foreach ($node_names as $type => $value) {
      $node_list[$type] = $value['name'];
    }
  }
  if ($node) {
    if (is_array($node)) {
      $type = $node['type'];
    }
    elseif (is_object($node)) {
      $type = $node->type;
    }
    elseif (is_string($node)) {
      $type = $node;
    }
    if (!isset($node_names[$type])) {
      return FALSE;
    }
  }
  switch ($op) {
    case 'base':
      return $node_names[$type]['base'];
    case 'list':
      return $node_list;
    case 'name':
      return $node_list[$type];
  }
}

The above is very hard to follow. There are no comments or documentation to speak of.

dopry’s picture

You are making it too complex. Node module is a pretty complex starting point... It has a bunch of code for special cases such as modules implementing multiple node types, which is what you see here....

Basically you spec out what your hooks should return, document it so other people can implement the hook if they want. You call module_invoke_all or module_invoke however you want, and process it however you want. Therer aren't any hard rules for anything, except coding style :).

I would look at hook_comment or hook_user and how they are called before looking ta node.module and node_invoke.

nathandigriz’s picture

Yes probably. but I am not doing it on purpose. I am just trying to understand why some hooks get called out of the a-z order. I started this by ask in this thread which go no reponse.

http://drupal.org/node/50562

I then wen to the mailing list and got a repeated answer about a-z ordering. But today I find that this is not the case. Since nodes can do this type of self contained invoking of hooks my problem with the alphabetic order of things can be solved.

But I have to have knowledge of how to use it and how it works first.

Budrick’s picture

2 dopry

Normally in callback systems though you have to 'register' you callbacks.

Exactly what I expected from hooks - to be registered somewhere in the core as listners for specific events.

Hooks can be expensive in terms of performance.

I guess they are. As I can understand it now, on every event (which can be several on one http request):
1) Drupal looks for all the modules that implements hooks related to the event.
2) Drupal calls all the hooks.
3) Every called hook checks for $op and other params and decides - does he want to react the event.

Listners registering are more logical, IMHO.

But now lets return to more utilitarian questions.
I am not smart and curious enough as nathandigriz is to dig core code, even though core is not working as I expect.
In this case I think that my expectations are wrong, not the core :-).

My current questions are:
1) why some hooks are not called where they shoudl? (E.G. _user hook on login. Question is here http://drupal.org/node/54480)
2) If several modules implements the same hook, how calling order is determined?

2 nathandigriz What is a-z order? Does it concern module names?
Should I name my module "z_module" to be the last in call queue? :-)

Is this suppose true? http://drupal.org/node/54902#comment-104509 (order defined by weight field in system table).
____________________________________________________________
Сауны и бани Алматы

Budrick’s picture

Yes, weight in system table affects call order.
WHY AT HELL THIS IS NOT DOCUMENTED !!!
____________________________________________________________
Сауны и бани Алматы

dman’s picture

By spending less than 2 minutes browsing drupaldocs, I see that
Module hooks are called in alphabetical order in 4.6 and they are ordered by weight and filename in 4.7

The answer to your qustion was as simple as Viewing the documentation publically available for the Hooks system, identifying module_invoke_all() as the relevant bit, then clicking twice on module_impliments() and module_list() to see in black & white exactly how, where and when the hooks are called. Even in that short tour (which took me 1/8 the time it took to write this explanation for you) I learnt a little more about the system than I knew before.

If you are intending to develop your own modules, you should learn by example, and by reading the developer documentation available to you. This includes the source code, which is so clean, (in the core at least) that any more comments would be distracting.

Ripping your little factoid out into a handbook page on its own would be harder to find than my process above was, and (as you can see the behaviour DID change) more likely to be actually wrong and misleading than the real thing.

Now, if you follow the path above, you have not just an answer to todays question, but a method for answering all (OK, most) of your questions.
Some things you just have to read for yourself.

.dan.

Set a man a fire and he's warm for a night. Set a man afire and he's warm for the rest of his life.

http://www.coders.co.nz/

Budrick’s picture

module_list() is system function, right? I mean that it is not the part of API and normaly it should be no need to call it from my module.
I`d prefer to keep my mind clean of what I won`t need.
What I need to know is some basic concepts vital for module development (e.g. hook call order).

And if one needs to dig core code (even not documentation for it, but code!) to discover a basic concept... Don`t think that it is good.

I`ve printed the whole Developers documentation and was reading it fondly thinking that that is enough. But there were no source of module.inc in pdf. And my answer was hiding there, in select query.
It is so obvious... How stupid of me...

Thinking your way all the documentation could be as short as "Here is the source. It is so clean and self-explanatory that you`ll never need anything more. ...[all the source goes here]".

Something similar already implemented in forms api docs when describing changes in 4.7:
Before: ...[several pages of code]
After: ...[several pages of code]

Before: ...[several pages of code]
After: ...[several pages of code]

dman’s picture

module_list() is part of the system, yes. So is variable_get() and node_load(). You are not prohibited from using them.

PRIVATE functions, which within PHP and other coding conventions begin with an _underscore are those you should ignore and not call yourself. Everything else is open game.

If you were building a module that cared about dependancies or something, module_list() may come in useful, and you would be expected to use it rather than do your own DB calls.

Willfully ignoring the right (or at least usual) way to do it, when a reviewed, stable and maintained function already exists to do the job is not a great way to create code.

If you are building a module that cares about load order (almost all the rest DO NOT) , you should probably look at the function that loads modules.

I don't know why you insist that your requirement is a 'basic concept'. I can't think of any other modules that it actually matters for - the fact that all 4.6 got by with just the alphabetical default shows that 300+ modules do not care about whatever problem you've encountered with your code.

There may be a better way of solving your problem - did you think about that? Could be the docs for nodeapi() are where you should be looking.

The links I provided each included half a page of php code, yes. If you wish to keep your mind clear of little things like php syntax, coding conventions, SQL queries and prior solutions, you can continue to shut your eyes when reading the online docs, but it won't help you progess.
I have no idea about the PDF you mention.

There are indeed plenty of examples where the code itself stands on its own, and is clearer or shorter than a prose explanation.
A combination of a good overview and access to the details as listed below is sufficient for most of the core functions I've seen.

The function I listed could have also documented about how the list is cached, about how it retrieves different modules if it's in bootstrap mode, about the structure of the returned module definitions, about how it handles throttling, and 6 or seven other minor details, including how it chose load order. That long story would have been harder to read than the half-screen of code that is published as part of the documentation.
If you know how to ask the question, it's sometimes better to see the answer than to read a description of it.

This is developer documentation not user help. If you are scared of code, close your editor now, keep your mind clean of stuff you don't need, and give up.

... and calm down, you got your answer, even if it did unfortunately require a bit of reading.

.dan.

http://www.coders.co.nz/

nathandigriz’s picture

You see something here just does not add up.

In 4.6 it is a-z order of the module and then the a-z order of the hooks.

In 4.7 it is weighted order of the module and then the a-z order of the hooks?

Well then this should not be true since "n" is before "v"

hook_nodeapi = "view": The node is about to be presented to the user. The module may change $node->body prior to presentation. This hook will be called after hook_view(), so the module may assume the node is filtered and now contains HTML.

http://drupaldocs.org/api/4.6/function/hook_nodeapi

Or is it just worded incorrectly?

Someone that knows please explain.

Heine’s picture

In 4.6 it is a-z order of the module and then the a-z order of the hooks.

In 4.7 it is weighted order of the module and then the a-z order of the hooks?

Sorry, but what do you mean with a-z order of hooks? From the later example you give I get the impression that you believe hook_a* will always be called before hook_b*. Clearly that's not the case.

--
Tips for posting to the forums.
When your problem is solved, please post a follow-up to the thread you started.

Budrick’s picture

Man who published book about Drupal, discovered it just yesterday
What do you expect from beginner?
____________________________________________________________
Сауны и бани Алматы

dman’s picture

But this just demonstrates that maybe no-one else has needed to ask it before now. It's not a frequent issue, and it hasn't stopped anyone else from developing their modules.

The change was made to the 4.7beta code 3 months ago, about the time that book (which I believe is targetted at users, not php coders) came out.

Could be the PDF you were looking at may not reflect everything in Drupal 4.7.0-beta.HEAD ?

However, as I say, it's a fair, reasonable question. I can't actually locate anyone else posting the question before, so maybe it's just not that big an issue.
You now have your answer - and a process for further research.

.dan.

http://www.coders.co.nz/

Budrick’s picture

PDF goes from here http://www.puregin.org/Drupal-handbooks
It is build upon online documentation (Yet it does not include some valuable stuff, e.g. forms api reference, so check online too).
The copy I am using is from 28.02.2006.

If I were using 4.6 it would be even worse - there were no order in select, and call order was unpredictable.

For those whose code logic depends of call order it can became A BIG ISSUE. Especially because nobody posted it before. I am glad I did it.

One more thing for those who might need to do the same thing as me:

In 4.7 you cant just eliminate form element from $form array, or you will not be able to use it after submit. What I did is:
in form_alter: $form['taxonomy'][1]=array('#type' => "hidden", '#value' => "null");
in nodeapi:$node->taxonomy[1] = $my_value;
(before this check that $op=='validate' and $node->type is needed type)
Or, if security allows you can just:
$form['taxonomy'][1]=array('#type' => "hidden", '#value' => $my_value); in form_alter, and dont define _nodeapi at all.

dman’s picture

It's certainly a non-trivial task you are attempting, and one that would have been even more difficult in 4.6

I've not done a lot of form_alter stuff, but I understand why you'd want to. I've got two taxonomies, each with more than 1600 terms that we currently load on each user form. I didn't even think to try disengaging them from the node edit form (it wouldn't have worked with our user requirements, but it does hurt my head to have such big lists loaded each time)
Good on you for trying to abstract things like this.

Perhaps an auto-complete instead of a select would be an option?

I wonder what would happen if you'd pre-posted an empty array where taxonomy wanted to put its stuff. Would probably get over-written, but a patch to taxonomy.module itself could have looked out for that... just speculating now.

It follows that with the all-powerful forms API, manipulation like this should become possible.

Sorry if I was unduly harsh in my comments earlier, I was just reacting to the shouting ;-)

.dan.

http://www.coders.co.nz/

Budrick’s picture

Perhaps an auto-complete instead of a select would be an option?

You see, vocabulary is presented as catalog in my module. User navigates through it. There breadcrumbs that looks like:
Vocabularu >> Term1 >> subterm1 >> sub_subterm1.
When user going to add a node, he is already on the page of the term, and tid is known. There is no need for him to select term in any way. He already did the selection when decided to add node on THAT page.

I wonder what would happen if you'd pre-posted an empty array where taxonomy wanted to put its stuff. Would probably get over-written, but a patch to taxonomy.module itself could have looked out for that... just speculating now.

Yes, taxonomy just overwrites it.
I am trying hard to avoid patching core. At least at my present level of Drupal knowledge.

Sorry if I was unduly harsh in my comments earlier, I was just reacting to the shouting ;-)

I am sorry too. I was just reactin to the shouting of my customer:"Why dont you throw away that Drupal thing if it makes you problems and start the application from scratch as I told you!". I know that starting from scratch will cause much more problems in future.
So I keep fighting for Drupal, with your kind help :)
______________
P.S.
To hide Category fieldset as well, use:

$form['taxonomy']['#title']='';
$form['taxonomy']['#collapsed']=1;
$form['taxonomy']['#weight']=10;
Budrick’s picture

As from 4.7 release you should put the real value in hidden field. In the example above, the value will not change to $my_value but will remain as null.
Replacing values in nodeapi validate worked in 4.7 rc3, but not in 4.7 release.
I consider it as issue

nathandigriz’s picture

Another question I have is why does node_invoke exist if module_invoke_all does the same work? Does using node_invoke override the normal order of things? Do hooks that are created with a node_invoke go before or after the hooks created with module_invoke_all within the same module?

In other words what is the purpose of node_hook ?

Budrick’s picture

... and calm down, you got your answer, even if it did unfortunately require a bit of reading.

Yes, I`ve got it, thanks. :-)
I won`t say it was a BIT of reading. 3 days I`ve lost.

MY INITIAL TASK WAS: UI to browse taxonomy structure, edit term description, and post nodes into secrion (section is term) rather than choosing it from select.
I need it because my vocabulary is very big.
So I need to hide taxonomy select on node add form, and substitude termID with the id of section from which node is added.
The problem with the order was that form_alter in taxonomy module which adds the select, was called after my module, so I could not affect it.
After setting weight for my module it works.

coupet’s picture

Quite interesting discussion, if possible, publish solutions.

Apache is bandwidth limited, PHP is CPU limited, and MySQL is memory limited.

Jaza’s picture

The discussion in this thread reflects an opinion that I've held for quite some time now: the hooks system has (almost) no documentation, and it desperately needs more. It is one of the key concepts that any Drupal module developer needs to understand, and it is also quite a unique aspect of Drupal's architecture.

When I started out as a module developer, I too searched through the handbooks and the forums, looking for information to help me understand just what the heck these "hook things" were. Eventually, I gave up, and decided that this was something the documentation clearly did not cover. I reluctantly resorted to looking through the source code for answers.

After much time and concentration, I eventually worked out, by myself and with nothing but the source code, the magic of module_invoke_all(), module_implements(), etc. I didn't realise before this that PHP had such powerful and flexible functions as function_exists(), func_get_args(), and (in particular) call_user_func(). I was really quite blown away by how elegantly and seamlessly all this magic was tucked into the source code: it struck me just how cool it was that you could simply write a new function (without doing any kind of registering), and the hook system would automatically call it!

However, that experience also made me realise that the source code alone is not enough for everyone. While I felt that 'working out the hooks thing' by digging through the source code was beneficial to me, and was a sort of "rite of passage" as a Drupal developer, I don't think that every potential Drupal dev should be expected to do that. More documentation is needed.

I intend to write one or more pages in the handbook about 'how hooks work in Drupal', based on my own knowledge, and on some of the things that people have posted in this thread (dman in particular). If you know of any recent additions to the handbook that address this, please point them out to me, and I'll see if there's anything further that can be added to them.

Jeremy Epstein - GreenAsh

Jeremy Epstein - GreenAsh

lammmy’s picture

But what I don't understand is, lets take the comment.module for example, if you look at the userpoints.module (which you can download and I am using to build something with), there is a userpoints_comment($comment, $op) function defined which obviously hooks into comment. The code body is :

  switch($op) {
  //new comment
    case 'insert':
      _userpoints_update_dollars($dollars, $user->uid, 'comment '.$op);
      break;
    case 'delete':
      // in this case we wish to revoke the points given from the comment
      $dollars = -$dollars;
      _userpoints_update_dollars($dollars, $comment->uid, 'comment '.$op);
      break;*/
    case 'moderate':
      $dollars = variable_get(USERPOINTS_MODERATE_COMMENT, '1');
      _userpoints_update_dollars($dollars, $comment->uid, 'comment '.$op);
      break;
    
    case 'update':
      $dollars = variable_get(USERPOINTS_POST_COMMENT, '1');
      _userpoints_update_dollars($dollars, $comment->uid, 'comment '.$op);
      break;
    
  }

My confusion kicks in when you do a search for module_invoke(...) or module_invoke_all(...) it comes up with nothing for comments, especially anything to do with 'moderate' etc, yet when I insert or delete comment, the userpoints catch it, so in this case, where is the module being invoked and how??

Thanks.

Heine’s picture

Line 1732 in comment.module (4.7.2): comment_invoke_comment(&$comment, $op)

--
The Manual | Troubleshooting FAQ | Tips for posting | Make Backups! | Consider creating a Test site.

lammmy’s picture

Ok, let me re-phrase, I thought I understood the module_invoke stuff, I didn't realize that modules did it that way (not using module_invoke at all), using their own "custom" function.

I just want to thank you for being so fast too, as well as clearing up a lot of confusion on that particular issue. Now, im wondering, do most of the modules do things the same way, by building their own module_invoke_module() type funcitons?

Thanks again!

Heine’s picture

Some modules do this, the reason is probably because the arguments need to be passed by reference so the callee can modify their contents.
--
The Manual | Troubleshooting FAQ | Tips for posting | Make Backups! | Consider creating a Test site.