Problem/Motivation
The bc layer for procedural hooks is slow.
Provide a way for modules to mark they have converted
Provide a way for modules that have not fully converted to mark a file as having no procedural hooks after a certain point.
Steps to reproduce
N/A
Proposed resolution
Complete skip APPROACH 2 seems to work
Add sibling file called module_name.skip.yml
Check for file existence in hook collector pass so we can skip procedural.
https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...
Attribute for partial conversion works
Add attribute called #[StopProceduralHookScan]
Check for attribute in line below and break inner foreach if it is found.
https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...
Complete skip APPROACH 1 (Does not work) 3490355-explore-ways-to
kept for reference.
Add flag in info.yml call skip_procedural_hooks_check
Check for flag in line below and pass to collectModuleHookImplementations so we can skip procedural.
https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...
Remaining tasks
Review
User interface changes
N/A
Introduced terminology
skip
StopProceduralHookScan
API changes
New attribute for stopping scan after procedural hooks.
New file for short circuiting all procedural hook scans.
Data model changes
N/A unless we figure out approach 1.
Release notes snippet
Issue fork drupal-3490355
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
nicxvan commentedComment #3
nicxvan commentedComment #4
nicxvan commentedComment #5
nicxvan commentedComment #6
catchIf we make this per-module, it will require the .info information - however, it will also mean we can skip scanning the directories and parsing the files at all.
I'm not sure if we already check .info information in a container rebuild, if not, that might not be an option.
Did also wonder about a tiny file next to the module.info.yml that indicates this - if it exists, conversion is done and stop there. Massive hack but again would be efficient.
Comment #8
nicxvan commentedOk, so the Attribute is much easier to implement, there are no tests yet, but it looks like it's working.
I'm still a bit unsure why the skip_procedural isn't being picked up. On discovery and list it is in the array, but when scanProceduralHooks is called info isn't set yet, so it defaults to scan.
It's also not set in moduleData, any advice would be appreciated.
Comment #9
nicxvan commentedOh I figured it out, this definitely needs cleanup and optimizing.
Comment #10
nicxvan commentedAdded some tests, let's see how broken things are.
Comment #11
nicxvan commentedI think marking core modules should be a follow up, but leaving a few now to see how tests react, I know for sure ordering is broken, and the new tests.
Comment #12
nicxvan commentedThe only thing failing is the skip all, the attribute works as expected.
When I debug and output the values for moduleInfo I see the test module 5 times. The first 4 times procedural is set to scan, the 5th it is set to skip, at this point though the hooks have already been scanned and cached.
Comment #13
nicxvan commentedIt looks like the compiler pass happens before the info file is parsed.
The attribute is working. I'm going to try the sibling file for now in a separate branch.
Comment #14
nicxvan commentedComment #16
nicxvan commentedComment #17
nicxvan commentedComment #18
nicxvan commentedComment #19
nicxvan commentedComment #20
godotislateThere are two open MRs. Can you clarify which one is for review and hide the other one?
Comment #21
nicxvan commentedApproach 2, !10405 the one called sibling file.
I didn't hide the other one in case someone wanted to review that approach.
Also for completeness @ghost suggested renaming covered modules to modulename.converted.module
This would require updating extension discovery and would not handle .inc files from hook hook info.
I have not provided a poc for @ghost's suggestion.
Comment #22
catchThe sibling file approach was my idea, but seeing it in the MR it doesn't look too bad. I wonder if 'hooks_converted.info.yml' would be more self documenting though?
Also - if this is set, I think we could also optimise
::collectModuleHookImplementationsto only scan inside src/Hook in the first place maybe? It could just completely ignore all other files then.Comment #23
nicxvan commentedDo you mean
moduleName.hooks_converted.yml?If so I can change that and look at optimizing that method.
Comment #24
nicxvan commentedOk I refactored that bit, now we only check once per module per collector pass. We avoid including the .module file entirely and skip all checks and cache an empty procedural count. As I type that out, do we need to set a procedural cache at all?
I think we have to leave:
Alone or risk breaking hook_hook_info
Comment #25
godotislateI don't feel strongly about this, but I thought I'd mention it: what about using container parameters instead of the sibling file approach?
So for
my_module, inmy_module.services.yml:In HookCollectorPass, the
$containerobject would need to be passed down to the relevant method, but the logic would look something like$skip_procedural = $container->hasParameter("$module.hooks_converted");Comment #26
nicxvan commentedThat is a great suggestion.
Let me do it on yet another branch, just in case something weird happens early compile pass again.
I'll close the sibling branch as long as this approach works.
Comment #29
nicxvan commentedIt's green, this has the added advantage of allowing you to mark a contrib module as converted if the maintainer has not converted it if you know there are no procedural hooks.
Comment #31
catchLooks great to me. We need a follow-up to add this to all core modules. Maybe one for all the ones that don't use hook_hook_info() / hook_module_implements_alter() and another issue for the ones that implement those two hooks.
Once we've done those follow-up issues it would be useful to get a drush site:install comparison before/after to see how much this actually helped with Umami or Standard in terms of memory usage.
Comment #32
nicxvan commentedComment #33
nicxvan commentedComment #34
godotislateOne small comment that isn't critical, but would be nice. Otherwise RTBC.
Comment #35
godotislatelgtm
Comment #38
catchService parameter is so much nicer than the extra file. We won't be able to tell the benefit from this until we convert core modules #3491275: Mark fully converted core modules as converted to OOP hooks but feels like it should be a lot.
Committed/pushed to 11.x and cherry-picked to 11.1.x, thanks!
Comment #40
ptmkenny commentedAs a module developer, I read over the change record and have a few questions:
1. If a module does not have any files where procedural hooks can be defined (for example, the only php code is classes in the /src directory), is there any benefit to specifying the parameter
module_name.hooks_converted: true?2. If a module has converted all hooks that can be converted to OOP but still has hooks that cannot be converted to OOP yet (for example, template_preprocess hooks), then
module_name.hooks_converted: truecannot be specified and the attribute#[StopProceduralHookScan]needs to be used for such hooks, correct?Comment #41
nicxvan commentedCorrect, I think you have to be careful too about includes if you manually include them.
Comment #42
nicxvan commentedThe patch here is required if you're going to use this.
#3491386: Using hooks_converted container parameter changes $dir during hook collection breaking collection of oop hooks.
Comment #43
dwwWow, this moved fast. 😅 nice work, y’all!
One complaint / concern. “Hooks_converted” is sort of vague and potentially confusing. Converted to what? Is it too late to propose something like
only_object_hooksor something? The 3 of y’all that worked on this issue are totally familiar with what hook conversion this is about, but I wonder about the other 99.99% of Drupal developers.Thanks again!
-Derek
Comment #44
nicxvan commentedWhat about no_procedural_hooks
Comment #45
dwwRight, something like that. Although I don't love configuration knobs of any sort that start with negatives. E.g.
no_procedural_hooks: trueis kinda weird. "Check this box to turn off the thing" is a UI anti-pattern. But settinghas_procedural_hooks: falseafter conversion is weird, too, since if the parameter isn't defined, defaulting to TRUE is also maybe unexpected. I dunno, naming is hard. 😅Comment #46
nicxvan commentedIt's complicated by the fact that we don't even check the value if it exists we skip procedural.