[pp-1] Explore adding an ImplementationList object for hook implmentations

Created on 16 April 2025, 5 months ago

Problem/Motivation

Postponed on πŸ“Œ Hook ordering across OOP, procedural and with extra types Active
This could simplify module handler since there are some sorting and lookup operations we repeat.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component

extension system

Created by

πŸ‡ΊπŸ‡ΈUnited States nicxvan

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @nicxvan
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • First commit to issue fork.
  • Pipeline finished with Failed
    4 months ago
    Total: 174s
    #484873
  • Pipeline finished with Failed
    4 months ago
    Total: 239s
    #484882
  • Pipeline finished with Success
    4 months ago
    Total: 2793s
    #484909
  • πŸ‡©πŸ‡ͺGermany donquixote

    Let's have one round of review, to see if people agree with the direction or have different ideas where this should go.

  • Pipeline finished with Failed
    4 months ago
    Total: 5175s
    #484975
  • Pipeline finished with Success
    4 months ago
    #486878
  • Pipeline finished with Failed
    4 months ago
    Total: 824s
    #486879
  • Pipeline finished with Failed
    4 months ago
    Total: 245s
    #486964
  • Pipeline finished with Failed
    4 months ago
    Total: 584s
    #506889
  • Pipeline finished with Failed
    3 months ago
    Total: 228s
    #514694
  • Pipeline finished with Failed
    3 months ago
    Total: 331s
    #514693
  • Pipeline finished with Success
    3 months ago
    Total: 942s
    #514702
  • Pipeline finished with Failed
    3 months ago
    Total: 986s
    #514703
  • πŸ‡©πŸ‡ͺGermany donquixote

    donquixote β†’ changed the visibility of the branch 3519561-DeprecationCanaryTest to hidden.

  • Merge request !12316Draft: Add DeprecationCanaryTest. β†’ (Open) created by donquixote
  • Pipeline finished with Failed
    3 months ago
    Total: 117s
    #515408
  • πŸ‡©πŸ‡ͺGermany donquixote

    donquixote β†’ changed the visibility of the branch 3519561-hook-ImplementationList-remove-getHookListeners to hidden.

  • πŸ‡©πŸ‡ͺGermany donquixote

    donquixote β†’ changed the visibility of the branch 3519561-DeprecationCanaryTest to hidden.

  • Pipeline finished with Success
    3 months ago
    Total: 697s
    #515551
  • πŸ‡©πŸ‡ͺGermany donquixote

    I added the unit test for ImplementationList in this issue, to prepare for πŸ› HookCollectorPass registers event listeners but they do not follow the required calling convention Active .

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Took a pass at it I think this is a pretty nice consolation!

    Qrite a few comments and I definitely need to take a deeper look, but I like the direction.

  • Pipeline finished with Failed
    3 months ago
    Total: 523s
    #515844
  • Pipeline finished with Success
    3 months ago
    Total: 550s
    #515856
  • Pipeline finished with Failed
    3 months ago
    Total: 131s
    #516307
  • Pipeline finished with Success
    3 months ago
    Total: 659s
    #516753
  • Pipeline finished with Success
    2 months ago
    Total: 754s
    #532621
  • Status changed to RTBC 2 months ago
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Ok I think this is ready!

    I've read through this several times, both changes in isolation and the files as they will be merged.

    This is a nice consolidation separating the concerns so module handler doesn't need to parse so much.
    I checked through all of the places that ModuleHandler gets the hooks and confirmed they are replaced with ImplementationLists.

    Early on I was concerned how this would work with Alter, but they are already ordered and they are retrieved before runtime ordering happens.

    Tests look good, I didn't see anything missing a test.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Edit, actually this might need a CR since we're adding a new object and we're changing some methods.

  • πŸ‡©πŸ‡ͺGermany donquixote

    Actually on second thought this might need a CR since we're adding a new object and we're changing some methods.

    Do we write change records for protected methods and protected properties?
    And do we write change records for new classes marked as internal?

  • Pipeline finished with Success
    2 months ago
    Total: 863s
    #533457
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Ok I asked in slack:

    @godotislate shared this link https://www.drupal.org/about/core/policies/core-change-policies/bc-polic... β†’

    And @quietone shared this link https://www.drupal.org/about/core/policies/core-change-policies/change-r... β†’

    They basically said it depends on contrib usage and the docs say changes to internal code so not need a cr.

    The thing is this isn't just a change to internal code it's renaming and changing the return type of a method.

    I think it's low risk for a few reasons:
    1 I maintain the only module I'm aware of that decorate module handler.
    2 this is required to simplify the removal of event dispatcher from hooks and that will necessarily be a large bc break.
    3 I don't see any way to cleanly deprecate getflathooklisteners
    4 the method itself is brand new

    I'm going to mark this as ready, I'm happy to write a cr if someone else thinks it's necessary.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Took a pass at credit, added @godotislate and @quietone for their assistance in slack.

  • πŸ‡©πŸ‡ͺGermany donquixote

    Going for a simpler and more confident title.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Let's actually change the names from module / Modules to extensions so we can use this for themes too if we make them available.

    I'm going to create a separate branch with that change.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    nicxvan β†’ changed the visibility of the branch 11.x to hidden.

  • Merge request !13020Resolve #3519561 "Extensionimplementationlist" β†’ (Open) created by nicxvan
  • Pipeline finished with Failed
    22 days ago
    Total: 251s
    #575165
  • Pipeline finished with Success
    22 days ago
    Total: 984s
    #575184
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Can we get a review of the new MR? All it is is the same MR @donquixote wrote but all references to module replaced with extension to make this more generic so we can use this with other extensions in the future.

  • πŸ‡©πŸ‡ͺGermany donquixote

    Let's actually change the names from module / Modules to extensions so we can use this for themes too if we make them available.

    I am not sure about this change.
    The problems that we solve with this ImplementationList are (currently) very specific to module hooks.

    For module hooks, every implementation needs a module associated with it, for these reasons:

    • In ModuleHandler->invokeAllWith(), the $module is passed to the callback.
    • In ModuleHandler->getCombinedListeners() (for ->alter()), the implementations are grouped by module before they are passed to hook_module_implements_alter().
    • In ModuleHandler->getCombinedListeners() (for ->alter()), when we apply the ordering rules, these also get access to the modules.

    Also, until now, we don't have services registered for themes.

    For theme hooks, we might be able work with a simpler ImplementationList which does not track the theme names.

    On the other hand: If, in the future, we do align theme hooks more with module hooks, we may want to refactor parts of ModuleHandler and HookCollectorPass, moving shared functionality into a shared class.

    Personally I would suggest to keep ImplementationList focused on modules for now, and also use the term "module" in variable names.
    The class is already marked as internal, and the properties are protected, so it will be easy to change in the future.

    In the future when we reuse this for themes we can either rename the properties, or we can create a new class, depending on the needs.

    Until that happens, the code will be easier to understand if it says "$module" rather than "$extension".
    We name things for what they are, not for what they could be.

  • I touched on something similar in a MR comment, but I agree with #31. As proposed, the `extensions` property is a flat array AFAICT, and it would not be possible to differentiate between themes and modules in the future without refactoring. In which case, I think that suggests we should just use modules nomenclature for now and refactor whatever we need to accommodate themes later.

  • πŸ‡©πŸ‡ͺGermany donquixote

    I touched on something similar in a MR comment, but I agree with #31. As proposed, the `extensions` property is a flat array AFAICT, and it would not be possible to differentiate between themes and modules in the future without refactoring.

    I imagine that even if we reuse this for theme hooks, we would have separate instances for modules and themes.
    So we would never have a mix of modules and themes.
    You would know from the context whether $list->extensions gives you only modules or only themes.

    But, even if we want to reuse large parts of this ImplementationList class, we might still want to maintain two separate classes, even if they are similar, and even if the only difference is the name of these properties. DRY can be overrated.
    I suspect there would be more differences, some methods might not be needed for themes.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Thank you for reviewing this, I have a couple of quick comments though:

    Also, until now, we don't have services registered for themes.

    OOP hooks in themes will necessarily include this.

    For theme hooks, we might be able work with a simpler ImplementationList which does not track the theme names.

    I don't think that is true, we have base themes and admin themes, so the same hook can before multiple themes and they all have to execute in the right order, but only for the active theme.

    On the other hand: If, in the future, we do align theme hooks more with module hooks, we may want to refactor parts of ModuleHandler and HookCollectorPass, moving shared functionality into a shared class.

    Yes, but that is not part of this MR at all.
    Also ModuleHandler won't have much overlap if any at all and HookCollectorPass has less overlap than you might think.

    The class is already marked as internal, and the properties are protected, so it will be easy to change in the future.

    Just because it's internal doesn't mean we shouldn't try to plan ahead a bit.

    There would be separate instances for themes and modules as @donquixote said in 33.

    I think the theme version would only need: filterByExtensionNames

    Maybe this isn't worth pursuing and we just have a much simplified ImplementationList for themes when the time comes.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    nicxvan β†’ changed the visibility of the branch 3519561-ExtensionImplementationList to hidden.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    I think you guys convinced me, there were a couple of suggestions from @godotislate that I think are relevant here, if we can apply those and confirm it's still green I think we can move this back to RTBC.

  • Pipeline finished with Running
    21 days ago
    #576608
  • Pipeline finished with Failed
    21 days ago
    Total: 788s
    #576634
  • Pipeline finished with Success
    18 days ago
    Total: 852s
    #578783
  • Rebased since HEAD's been fixed.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Thanks! Back to rtbc the updates were fairly minor.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    Left 3 suggestions. Was going to leave the status as they were quite subjective, but changing it because I believe list<string, string> is wrong.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Addressed all feedback, back to needs review!

  • Pipeline finished with Failed
    14 days ago
    Total: 159s
    #581617
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    One of the array_all updates broke the asserts so I undid them.
    I don't think that @mstrelan considers that suggestion is enough to block this, but I'll leave that to @mstrelan to confirm, I made all other changes.

  • Pipeline finished with Success
    14 days ago
    Total: 759s
    #581630
  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    Yes it's not a blocker. I hadn't realised the callback for array_all requires both the value and the key. I think it would have to be written like this:

    assert(array_all($listeners, fn ($value, $key) => is_callable($value)));
    assert(array_all($modules, fn ($value, $key) => is_string($value)));
    
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Yeah, which i don't think it's much better let's leave it as is.

    I'm going to go ahead and mark this ready since the only change was a phpstan type and test variable name.

  • Status changed to Needs work 5 days ago
  • The Needs Review Queue Bot β†’ tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Rebased, it was just the test conversion to attributes, nothing crucial changed, I'll keep an eye on tests.

  • Pipeline finished with Success
    5 days ago
    Total: 1371s
    #590327
Production build 0.71.5 2024