Open klonos opened 9 months ago
OK, I have taken a stub at this: https://github.com/backdrop/backdrop/pull/4590
What this PR does:
module_invoke_all_merge_deep()
function. Initially, I have duplicated the code of module_invoke_all()
in it, but the only difference was that it was calling backdrop_array_merge_deep()
instead of array_merge_recursive()
. So I then converted it into a wrapper function for module_invoke_all()
instead.module_invoke_all()
, I needed a way to detect whether the actual module_invoke_all()
was called directly, or via module_invoke_all_merge_deep()
. Since there is no in-built way in PHP to get the name of the caller function, I initially resorted to using debug_backtrace()
, but after reading various sources on the internet, it turns out that using that function is not a good idea (performance issues - see here and here for example). Since module_invoke_all()
accepts a "static" parameter followed by an unlimited number of dynamic parameters, we can't add a "flag" parameter to it unless it is the first parameter (and then unset()
it, the same way we are currently unset()
ing the $hook
parameter). So I got that workaround in place, and it seems to work nicely.module_invoke_all()
(backwards compatibility retained), or via its alternative module_invoke_all_merge_deep()
._module_invoke_all_detect_duplicates()
function, which simply logs a notice in the event log, to notify about any occurrence of duplicate results returned by the same hook implementation in various modules. It also states that the last module/hook (currently alphabetically) "wins" (but I have plans for that once we have #6241, so I've added a @todo
there for that).hook_requirements()
implementations in 3 different modules. I am planning to move those into a test, but for now they live in the actual modules in order to provide something testable in the PR sandbox.To test in the PR sandbox:
Thoughts please? (especially @argiepiano and @bugfolder since you have both indicated that you are either affected by this problem in one of your contrib modules, or have otherwise encountered it elsewhere in the past)
@klonos, deep rabbit hole indeed! Thanks for all the work and thinking. While I haven't looked at this in depth yet, reading the description of what you are doing, the whole thing seems rather complex (and potentially prone to unexpected regressions). I'm wondering if there are easier, more localized ways to fix the problem that originated this work. For example:
hook_requirements_alter()
hook_requirements
- that is: update_check_requirements()
and system_get_requirements()
to use an alternative approach. Many modules actually avoid module_invoke_all()
and instead use a customized approach that loops through the array returned by module_implements()
, doing any needed processing with the return values there. Take a look at backdrop_get_complete_schema()
. There is no use of module_invoke_all('schema')
anywhere in Backdrop. Instead, that function loops through module_implements('schema');
. Why not use a similar pattern within update_check_requirements()
and system_get_requirements()
So, in short this feels like using a "sledgehammer to crack a nut".
create an alter hook for
hook_requirements_alter()
I favor this as the solution for dueling hook_requirements()
implementations.
For the specific case that arose in #6121, of multiple modules needing to complain if cURL wasn't present, it's less than ideal if we're asking those modules to explain why they need cURL in their 'description'
; they'll all have to "play nicely" by appending their error message to any existing 'description'
. But that can be made to work; and using an alter
hook is (relatively) simple and understandable.
However, the ability to detect duplicate hook_requirements()
entries looks like, in and of itself, a very useful thing, and could save developers some head-scratching.
This came up in https://github.com/backdrop/backdrop-issues/issues/6121#issuecomment-1836998285 and it turns out to be a known issue: https://www.drupal.org/project/drupal/issues/2865599