I just found the piece of information that was missing.
EDIT: And another one.
Problem with module_invoke()
module_invoke_all()
always calls module_implements()
, which checks the module include file by checking both hook_hook_info()
and hook_module_implements_alter()
.
However, module_invoke()
only checks hook_hook_info()
and not hook_module_implements_alter()
.
module_invoke()
is called e.g. in _block_render_blocks()
for hook_block_view()
.
This means that hook_module_implements_alter()
is not a good solution to split a hook_block_view()
implementation into a separate file.
This has been a problem in Crumbs, see https://www.drupal.org/node/2328535.
For other hooks it generally works ok, but you never know if a specific module wants to use module_invoke()
instead of module_invoke_all()
.
module_implements() cache polluted in bootstrap.
Another problem is caused by a core issue reported here:
module_implements_cache() can be polluted by hook_boot() implementations calling module_invoke_all() directly or indirectly
This applies only to non-boot modules, where it can happen that the implementation of hook_module_implements_alter()
is never discovered.
- An unexpected chain of events causes
module_implements($hook)
being called from hook_boot()
.
The $hook
can be any random hook, it does not need to be related to the module we are working on.
- This causes
module_implements('module_implements_alter')
to be called.
- Drupal looks for all implementations of
hook_module_implements_alter()
.
At this time, MODULENAME.module
is not included yet.
Therefore, for the rest of the request, Drupal assumes that the implementation MODULENAME_module_implements_alter()
does not exist.
- Later in the request, implementations of other hooks are being discovered, but the implementations by MODULENAME are not being found, because
MODULENAME_module_implements_alter()
is not being executed.
Alternatives
require_once
Alternatives have already been mentioned. Instead of these tricks, one can include the file directly. There may be a tiny performance impact but this is probably overshadowed by a lot of other things in Drupal.
But, instead of using module_load_include()
, I would suggest a more direct solution:
require_once __DIR__ . '/crumbs.block.inc';
Or to be PHP 5.2 compatible:
require_once dirname(__FILE__) . '/crumbs.block.inc';
Why not module_load_include()?
There is generally no need to call module_load_include()
, it could even be detrimental, if the *.module file is included from you-dont-know-where (e.g. from settings.php
), and you don't know if module_load_include()
is available.
This function is mostly there to help you determine the location of other modules. But if you are including within the same module, it is generally safer and easier and faster to work with relative file paths and explicit require_once
.
Call to helper function
As mentioned by Jimajamma, you can also implement the hook in the main *.module
file, and then include another file and call a helper function from there. This is done e.g. in Display suite.
This solution is ok, although I think it still clutters the main file, and gives you even more places to look.
And here likewise you can use require_once instead of module_load_include()
, if it is within the same module.