== 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`.
[[https://github.com/wikimedia/mediawiki/blob/master/docs/injection.txt#L212|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