Requirement
It should be possible to pass service name instead of class name in hook definition.
Why ?
MediaWiki hooks system is based on static calls which is difficult to test. To define a new hook listener have to edit extension.json file and pass a callback which is something like Namespace\ClassName::onHookDoSomething, which means we need a Namespace\ClassName class with a public static onHookDoSomething.
MediaWIki injection documentation explains how to migrate away from static hook handler functions. This approach requires couple extra steps:
- create a static method newFromGlobalState just to instantiate a HooksListener as singleton
- refactor old onSomething static function just to call self::newFromGlobalState()->doSomething()
This allows us to create a testable code, which is a good thing. This solution has couple minor problems
- if HookClass handles 4 hooks ( => 4 methods), now the hook class will have 9 methods (4 on methods, 4 do methods and newFromGlobalState)
- it's very difficult to get 100% code coverage as now we have 5 static methods to cover
Possible solution with @ annotation
Instead of writing new system use the existing hooks instrumentation. Pass an additional flag when passing a callback, Hooks::run() will detect this flag and or call the static method (old way) or use MediaWikiServices to retrieve the service (new way).
As a flag I specified @ character. When callable is passed without preceding @ threat it the old way. When @ is there use MediaWikiServices to retrieve service.
Example extension.json hook definition:
"Hooks": { "GetPreferences" : [ "@ServiceName::hookMethod" ]
Possible solution - pass a [service, method] array
As @Tgr suggested - Pass an associative array containing two properties service which is service name and method which is method to call. When method is not passed hooks system will call on$event method.
Example extension.json hook definition:
"Hooks": { "GetPreferences" : [ [ "service" : "ServiceName", "method": "methodName" ] ]
Benefits of both solutions
With that approach we can easily introduce new system without interfering with old hooks.
Solution 1: https://gerrit.wikimedia.org/r/#/c/367421/3
Solution 2: https://gerrit.wikimedia.org/r/#/c/367421/4
See also
See also T154673: Refactor Hooks to no longer rely on global state and improve type safety