Problem/Motivation
The conversion of core to use oop hooks was successful using rector, there are several tasks that need to be done.
Steps to reproduce
N/A
Proposed resolution
For core and bulk conversions.
- Rough conversion, i.e. convert as close to exact as possible, (lift and shift) This is done for almost all hooks in core.
- Rough organization, DI, t(), Split out by *.api.php, add DI, convert to $this->t() Keep theme hooks that have no injection separate, separate cron, mail, and page_top
- Fine tuning - individual refactors and split out critical path performance hooks
Grouping standards
Hook classes
For step 2 we should group based on the grouping in *.api.php
For step 3 each hook should be investigated for performance and functional requirements.
Class naming standards
Hook classes should be named ModuleNameGroupingHooks.php e.g. FilleCronHooks.php
Commenting standards
Recommendations welcome.
Method naming conventions
Recommendations welcome.
Remaining tasks
Create issues for each core module
User interface changes
N/A
Introduced terminology
N/A
API changes
Data model changes
N/A
Release notes snippet
N/A
| Comment | File | Size | Author |
|---|---|---|---|
| #25 | hookOrganization.txt | 12.41 KB | nicxvan |
Issue fork drupal-3493453
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
kim.pepperIn #3493951: Split File oop hook implementations into separate classes I split out hooks based on common dependencies, and kept those with no dependencies in the
FileHooksclass.Comment #6
chi commentedI would force using single hook classes wherever it is possible. There very few use cases when hooks are highly related to each other.
Comment #7
chi commentedCross posted from #3442009: OOP hooks using attributes and event dispatcher. The "implements" seems redundant now.
It gets worse in single hook classes.
Comment #8
nicxvan commentedThank you for commenting.
Re 6 I think there are a bunch, especially if you have a shared helper method.
For 7, I agree, but what would you suggest for comments then? A description of what it is doing?
Comment #9
chi commented@nicxvan we probably need to understand why "Implements hook_*" comment was initially added to Drupal coding standards. I guess it is needed to distinguish ordinary PHP functions from functions that implement Drupal hooks. Obviously with the
Hookattribute it is no longer needed. Also api.drupal.org might use this notation when listing hook implementations.For me it seems that in most cases docblock in hooks is not needed at all.
This issue is very similar to #3376518: Allow omitting @var, @param and @return tags or their descriptions if the variable name and type is enough. Maybe we should take same approach once it is resolved.
Comment #10
nicxvan commentedIt's so the api project can find them and document implementations, there is an issue to replace that.
Comment #11
berdirI do think that it is useful to split hooks into common groups and IMHO, it's pretty often that multiple hooks are related, for example modules might implement multiple entity CRUD or access hooks. Some hooks like tokens by design come in a pair.
In the original issue, I proposed to split by hook group, which translates fairly well into the first part of a given hook.
From the meta issue to convert core:
I think one of the biggest advantages of the plugin system is that the grouping/namespacing allows you to very quickly get an overview of what kind of plugins a module provides. Using such a grouping of Hooks would IMHO have a similar benefit. Doing single hook classes means we have a lot more classes, to the point that scanning them isn't quite as efficient anymore. We can still say that standalone hooks that we don't expect to have multiple like cron could be a class with a single __invoke() method.
Comment #12
nicxvan commentedI agree, it's also useful to have less code loading for hooks when invoked too.
Comment #13
catchIt would be good to open a new coding standards issue for removing the Implements doc - I agree that looks redundant with the hook attribute.
Comment #14
macsim commentedWhile it could be a good idea for simple websites, core and contrib modules, IMHO it's a bad one for complex sites with a lot of custom code.
If one class handle all the form hooks or entities actions hooks where you might have to use multiple different services, we'll reach soon or later some
CouplingBetweenObjectsand/orExcessiveClassComplexityerrors.Comment #15
quietone commentedThere are several coding standard issues about hooks
#983268: Use @implements Doxygen directive for hook implementations
#3004139: Define form alter hook docblock standard
#782016: Need doxygen standards for overriding a theme hook/template
#1663218: Allow hook_update_N() doc block to contain details that are not displayed to the user
Maybe a meta issue is needed to discuss the 'big picture' and the ones above can be children.
Comment #16
nicxvan commented@macsim / #14
Yes, but in that case I would imagine it would be broken up across multiple custom modules. And you could break them up across multiple classes.
Nothing is intended to be enforced here beyond maybe core, this is mostly intended as guidelines.
@quietone / #15
Did you mean another meta specifically for those issues? Cause we could roll those into here. Edit, to clarify i meant make them children of this meta.
Comment #17
macsim commented@Berdir / #11 I mostly agree with you except for the groups names.
Since we already are working in a
Hookdirectory we shouldn't append "Hooks" to the groups/classes nameDRY philosophy is appliable, those classes are not meant to end up in other classes use statements and won't engender collisions forcing to use aliases.
Comment #18
nicxvan commentedOnce the ordering issue gets in they will be in use statements, but they are in the hook namespace so I agree we could drop hook from the class name.
#3485896: Hook ordering across OOP, procedural and with extra types i.e replace hook_module_implements_alter
Comment #19
kristiaanvandeneyndeHere's what I did in Group: #3508876: Convert hooks to OOP hooks with attributes
I looked at which NAME.api.php file hooks were in and grouped them, unless I only used one hook from said file and it was unlikely I would use more hooks. Example being hook_help() from help.api.php. Those hooks I put into a CoreHooks class instead.
I ended up with:
UserHooks contains the entity hooks for that entity type along with some user-specific hooks. I only put the generic entity hooks in EntityHooks.
I marked all classes as final and added typehinting for everything.
Comment #20
nicxvan commentedEdited: wrong issue.
Comment #21
dwwI believe you meant to post that at #3479141: Implement FormAlter attribute, right?
Comment #22
nicxvan commentedthanks!
Comment #23
dcam commentedJust my $0.02:
I've started to implement hook classes in my own work. A decision that I made pretty quickly was to adhere to the single-responsibility principle. This has resulted in a shift in the way that I think about hooks, which I realize was dictated largely by the fact that we could only have a single implementation per module. Now we can have multiples. We're no longer bound to lumping everything a hook needs to do into a single function.
As a result, I'm more inclined to make hook classes that focus on what function it's supposed to perform, not on what part of Drupal it alters. Granted, sometimes those are the same thing. In the case of the former, then I've been naming the class according to its purpose. For the latter, then I do name it according to the hook it contains. I like this pattern. It means that it's a little easier to know just from the class name what that class is supposed to be doing. And it means that the hook is no longer what's important - its purpose is. I've been enjoying working with them! Unit tests for hook classes are so easy to write.
I've read through this issue and another and understand that there's some performance concerns surrounding having too many hooks. And I get that Core may want to have well-defined patterns in order to prevent chaos. I just wanted to share what I've been doing as someone developing contrib and custom modules.
Comment #24
acbramley commentedRe #17 I slightly disagree. If, for example, we have
NodeHooksfor general hooks in the node module, calling the classNodejust makes it harder to find for developers as there are already a handful of classes called Node and files called Node.php.Comment #25
nicxvan commented@dcam and @acbramley, I agree with both of you.
I think that organization makes sense longer term specifically for contrib and custom code. We also need something better defined so we can make progress in core. I'm going to update the issue summary, but I think we follow three steps for organizing / converting hooks.
I have attached a file with the core hooks and the recommended grouping.
For ease of searching I've also created a text file I have attached so you can search for the hook class the hook implementations should go in.
Comment #26
macsim commented#24
I'm not sure I understand what you mean by general hooks, but I'm pretty sure these hooks could go in one of the other files suggested by #11 and #19.
If there was a
NodeHooksfile in a module I would expect to find hooks implementations related to nodes rather than "general hooks".I don't think
Nodemodule would need to contain aNodeHooksfile asNode.phpalready holds the entity logic ; not sure why the module would need to call it's own hooks rather than just add the code to the entity class.By definition, hooks are a way for other modules to alter/add behaviors on something they don't own the logic for.
While in core and for most cases,
EntityHooksshould be fine, what you say is correct:Several contrib / custom modules might end up using a
NodeHooksfile for a very specific logic related to nodes (I had something like that in my mind in #14, I do agree with #16 that, for some use cases,EntityHooksmight need to be split into multiple more specific classes and we already have an example in #19 withUserHooksin Groups module).So maybe we should keep appending Hooks to the classes name.
This will make autocomplete easier in the IDE when we want to automatically add a use statement.
Comment #27
nicxvan commentedWe definitely want to keep the module name as part of it, finding the right EntityHooks file out of dozens in a project will be a massive pain.
I think general hooks were just ones that were hard to find a place for, the file I attached I think solves that issue though since we can just use where the hooks are defined as our guide.
Comment #28
nicxvan commentedComment #29
nicxvan commentedComment #30
smustgrave commentedNot sure what there is to review? But there appear to still be opened related issues so the META is still in progress right.
Comment #31
catchIf this is a meta I think we can just make it an active plan.
Comment #32
kristiaanvandeneyndeI'm not sure about that. I tend to press the shortcut to find classes in PhpStorm and then type e.g.: "group Ent" and it would show me classes with Ent in their name inside the group namespace. I feel that adding your own module name to your classes is a bit redundant and leads to classes being harder to find. Namespaces are your friend here and I'm sure most IDEs support them in their search.
Comment #33
nicxvan commentedComment #34
kristiaanvandeneyndeJust re-emphasizing #32 as Nic commented on the MR where I'm about to commit the hook conversion to Group:
I will not be prefixing my hook classes with my module name. I will simply be naming them EntityHooks, UserHooks, and so on. It feels really wrong to completely ignore namespaces when naming classes.
My reasoning now (I may have not always done this in the past) for adding the word Group to some classes and not to others is this: Does it have to do with the concept of Group and does it not live in a Group-specific subnamespace (like plugin dirs)? Then I add it. E.g.: The concept of permissions within a group is specific and not to be confused with the concept of permissions in general. So I name those classes GroupPermissionFoo. Implementing hooks has nothing to do with Group itself, it's implementing third-party API, so I choose not to add Group to the class names.
If I CMD+O in PhpStorm for "UserHooks", I see two results, but if I type "g userho" I already see what I want. Even "group Hooks" gives me everything I need if I were to search for Group's hook implementations. I see no reason to make class names extremely long if we already have all the info we need. Keep in mind some modules have really long names.
Comment #35
nicxvan commentedI still think not prefixing is not the way to go.
G userhooks might give you one result for now, but what happens when you have two or three modules that start with a g.
Now you get three results and you have to carefully check the path of three files with the same name.
Comment #36
kristiaanvandeneyndeThen I type "gr us" or "gro us" or whatever :) PhpStorm is really good at autocompleting for you.
Comment #37
nicxvan commentedI think we should make exceptions for these three hooks since they are called in specific times.
Cron
Mail
Page_top
Comment #38
lostcarpark commentedI think general dependency injection would be next to impossible to do with rector, but converting
t()to$this->t()seems like something that could be fairly easily handled by inserting theusestatements, and replacing the existing t() calls.Comment #39
nicxvan commentedHere is the issue to work on translation rector rules: https://github.com/palantirnet/drupal-rector/pull/215