Problem/Motivation
hook_preprocess
, hook_preprocess_HOOK
, and template_preprocess_HOOK
functions are still required to be in a .module/.inc file.
Steps to reproduce
N/A
Proposed resolution
ModuleHandler::invoke
in pre-Drupal 11.1 didn't check whether the$module
parameter was, indeed, a module, it just called the$module_$hook
function. This was routinely used for non-module purposes, for example Views used to passcore
as a module. This functionality survives inModuleHandler::legacyInvoke
whichModuleHandler::invoke
calls as a fallback.- Therefore, it is possible to call
$this->moduleHandler->invoke($prefix, $preprocess_hook)
with whatever $prefix is used inDrupal\Core\Theme\Registry::processExtension
: theme engines, themes, the fixed stringtemplate
, and, of course modules. Usinginvoke
like this not only calls every existing preprocess function without a problem but when$prefix
is a real module then we get OOP functionality for free. - This requires caching the prefix and the hook separately. We could store them like this:
['module' => $prefix, 'hook' => 'preprocess_'. $hook]
and then because named arguments work together with the splat operator (they were designed together for PHP 5.6 but named arguments needed to wait until 8.0 -- we are well past that version), we can just pass such an array to invoke directly. - Also, we could add the fixed string
template
as a module in appropriate places -- HookCollectorPass and ModuleHandler -- and thentemplate_preprocess*
also can be implemented as OOP without many problems. - We add two new attributes extending Hook to make it easier to write preprocess and template_preprocess hooks. For now we do not extend TemplatePreprocess from Preprocess because it's not yet clear what api module can support -- the way Hook is changed internally also serves api purposes. Constants are easy to support.
- Finally we modify Registry to use invokeAllWith for gathering hook_preprocess_HOOK to avoid checking missing preprocess functions
All this requires surprisingly little code to provide both full backwards compatibility and OOP capability. What this does not provide is the ability to implement preprocess hooks in OOP code multiple times by the same module. This is not even planned as it would require a significant overhaul of the theme registry system. Do not let perfect be the enemy of good enough.
Remaining tasks
Quick note, the approach has changed a fair bit to address @berdir's feedback.
Review this MR https://git.drupalcode.org/project/drupal/-/merge_requests/10826
Need to address grouped preprocess functions.
Interdiff showing just the scanning changes for preprocess functions: https://git.drupalcode.org/issue/drupal-3495943/-/merge_requests/1/diffs
In a followup, refactor ModuleHandler and ThemeHandler so they have a common base (they used to be the same thing after all) and provide OOP capability for hooks by themes. This likely needs #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation first.
Original approach (hidden): https://git.drupalcode.org/issue/drupal-3495943/-/tree/3495943-handle-mo...
@berdir's original invokeall approach (hidden): https://git.drupalcode.org/project/drupal/-/merge_requests/10762
New approach with only functional changes (hidden): https://git.drupalcode.org/project/drupal/-/merge_requests/10825
User interface changes
N/A
Introduced terminology
API changes
Added #[Preprocess]
and #[TemplatePreprocess]
hook attributes extending #[Hook]
. It is recommended these attributes to be committed to every supported branch for the same reason Hook
and LegacyHook
was. Aspiring contrib authors can use the LegacyHook
attribute and pattern with preprocess hooks much like with any other hook to maintain a single branch which is compatible with Drupal before and after this patch.
Data model changes
N/A