Merge "Documentation for the new hook system"
This commit is contained in:
commit
3cd87b81cf
2 changed files with 222 additions and 5 deletions
219
docs/Hooks.md
Normal file
219
docs/Hooks.md
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
Hooks
|
||||
=====
|
||||
|
||||
## Introduction
|
||||
|
||||
Hooks allow MediaWiki Core to call extensions or allow one extension to call
|
||||
another extension.
|
||||
|
||||
Starting in MediaWiki 1.35, each hook called by MediaWiki Core has an
|
||||
associated interface with a single method. To call the hook, obtain a "hook
|
||||
runner" object, which implements the relevant interface, and call the relevant
|
||||
method. To handle a hook event in an extension, create a handler object which
|
||||
implements the interface.
|
||||
|
||||
The name of the interface is the name of the hook with "Hook" added to the end.
|
||||
Interfaces are typically placed in the namespace of their primary caller.
|
||||
|
||||
The method name for the hook is the name of the hook, prefixed with "on".
|
||||
|
||||
Several hooks had colons in their name, which are invalid in an interface or
|
||||
method name. These hooks have interfaces and method names in which the colons
|
||||
are replaced with underscores.
|
||||
|
||||
For example, if the hook is called `Mash`, we might have the interface:
|
||||
|
||||
interface MashHook {
|
||||
public function onMash( $banana );
|
||||
}
|
||||
|
||||
Hooks events can also be handled in the old way, as documented in hooks.txt.
|
||||
|
||||
Hooks can be defined and called by extensions. The extension should define a
|
||||
hook interface for each hook, as described above.
|
||||
|
||||
## HookContainer
|
||||
|
||||
HookContainer is a service which is responsible for maintaining a list of hook
|
||||
handlers and calling those handlers when requested. HookContainer is not aware
|
||||
of hook interfaces or parameter types.
|
||||
|
||||
HookContainer provides hook metadata. For example, `isRegistered()` tells us
|
||||
whether there are any handlers for a given hook event.
|
||||
|
||||
A HookContainer instance can be obtained from the global service locator with
|
||||
MediaWikiServices::getHookContainer(). When implementing a service that needs
|
||||
to call a hook, a HookContainer object should be passed to the constructor of
|
||||
the service.
|
||||
|
||||
## Hook runner classes
|
||||
|
||||
A hook runner is a class which implements hook interfaces, proxying the calls
|
||||
to `HookContainer::run()`.
|
||||
|
||||
MediaWiki has two hook runner classes: HookRunner and ApiHookRunner.
|
||||
ApiHookRunner has proxy methods for all hooks which are called by the Action
|
||||
API. HookRunner has proxy methods for all hooks which are called by other parts
|
||||
of Core. Some hooks are implemented in both classes.
|
||||
|
||||
Extensions which call hooks should create their own hook runner class, by
|
||||
analogy with the ones in Core. Hook runner classes are effectively internal
|
||||
to the module which calls the relevant hooks. Reorganisation of the hook
|
||||
calling code may lead to methods being removed from hook runner classes. Thus,
|
||||
it is safer for extensions to define their own hook runner classes even if
|
||||
they are calling Core hooks.
|
||||
|
||||
New code should typically be written in a service which takes a HookContainer
|
||||
as a constructor parameter. However, for the convenience of existing static
|
||||
functions in MediaWiki Core, `Hooks::runner()` may be used to obtain a
|
||||
HookRunner instance. This is equivalent to
|
||||
|
||||
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() )
|
||||
|
||||
For example, to call the hook `Mash`, as defined above, in static code:
|
||||
|
||||
Hooks::runner()->onMash( $banana );
|
||||
|
||||
## How to handle a hook event in an extension
|
||||
|
||||
In extension.json, there is a new attribute called `HookHandlers`. This is
|
||||
an object mapping the handler name to an ObjectFactory specification describing
|
||||
how to create the handler object. The specification will typically have a
|
||||
`class` member with the name of the handler class. For example, in an extension
|
||||
called `FoodProcessor`, we may have:
|
||||
|
||||
"HookHandlers": {
|
||||
"main": {
|
||||
"class": "MediaWiki\\Extension\\FoodProcessor\\HookHandler"
|
||||
}
|
||||
}
|
||||
|
||||
Then in the Hooks attribute, instead of a function name, the value will be the
|
||||
handler name:
|
||||
|
||||
"Hooks": {
|
||||
"Mash": "main"
|
||||
}
|
||||
|
||||
Or more explicitly, by using an object instead of a string for the handler:
|
||||
|
||||
"Hooks": {
|
||||
"Mash": {
|
||||
"handler": "main"
|
||||
}
|
||||
}
|
||||
|
||||
Then the extension will define a handler class:
|
||||
|
||||
namespace MediaWiki\Extension\FoodProcessor;
|
||||
|
||||
class HookHandler implements MashHook {
|
||||
public function onMash( $banana ) {
|
||||
// Implementation goes here
|
||||
}
|
||||
}
|
||||
|
||||
## Returning and aborting
|
||||
|
||||
If a hook handler returns false, HookContainer will stop iterating through the
|
||||
list of handlers and will immediately return false.
|
||||
|
||||
If a hook handler returns true, or if there is no return value (causing it to
|
||||
effectively return null), then HookContainer will continue to call any other
|
||||
remaining handlers. Eventually HookContainer::run() will return true.
|
||||
|
||||
If there were no registered handlers, HookContainer::run() will return true.
|
||||
|
||||
Some hooks are declared to be "not abortable". If a handler for a non-abortable
|
||||
hook returns false, an exception will be thrown. A hook is declared to be not
|
||||
abortable by passing `[ "abortable" => false ]` in the $options parameter to
|
||||
HookContainer::run().
|
||||
|
||||
Aborting is properly used to enforce a convention that only one extension
|
||||
may handle a given hook call.
|
||||
|
||||
Aborting is sometimes used as a generic return value, to indicate that the
|
||||
caller should stop performing some action.
|
||||
|
||||
Most hook callers do not check the return value from HookContainer::run() and
|
||||
there is no real concept of aborting. The only effect of returning `false` from
|
||||
a handler of these hooks is to break other extensions.
|
||||
|
||||
Theoretically, extensions which are registered first in LocalSettings.php will
|
||||
be called first, and thus will have the first opportunity to abort a hook call.
|
||||
This behaviour should not be relied upon. In the new hook system, handlers
|
||||
registered in the legacy way are called first, before handlers registered in
|
||||
the new way.
|
||||
|
||||
## Parameters passed by reference
|
||||
|
||||
The typical way for a handler to return data to the caller is by modifying a
|
||||
parameter which was passed by reference. This is sometimes called "replacement".
|
||||
|
||||
Reference parameters were somewhat overused in early versions of MediaWiki. You
|
||||
may find that some parameters passed by reference cannot reasonably be modified.
|
||||
Replacement either has no effect on the caller or would cause unexpected or
|
||||
inconsistent effects. Handlers should generally only replace a parameter when it
|
||||
is clear from the documentation that replacement is expected.
|
||||
|
||||
## How to define a new hook
|
||||
|
||||
* Create a hook interface, typically in a subnamespace called `Hook` relative
|
||||
to the caller namespace. For example, if the caller is in a namespace called
|
||||
`MediaWiki\Foo`, the hook interface might be placed in `MediaWiki\Foo\Hook`.
|
||||
* Add an implementation to the relevant HookRunner class.
|
||||
|
||||
## Hook deprecation
|
||||
|
||||
Core hooks are deprecated by adding them to an array in the DeprecatedHooks
|
||||
class. Hooks declared in extensions may be deprecated by listing them in the
|
||||
`DeprecatedHooks` attribute:
|
||||
|
||||
"DeprecatedHooks": {
|
||||
"Mash": {
|
||||
"deprecatedVersion": "2.0",
|
||||
"component": "FoodProcessor"
|
||||
}
|
||||
}
|
||||
|
||||
If the `component` is not specified, it defaults to the name of the extension.
|
||||
|
||||
Deprecating a hook in this way activates a migration system called
|
||||
**call filtering**. Extensions opt in to call filtering of deprecated hooks by
|
||||
**acknowledging** deprecation. An extension acknowledges deprecation with the
|
||||
`deprecated` parameter in the `Hooks` attribute:
|
||||
|
||||
"Hooks": {
|
||||
"Mash": {
|
||||
"handler": "main",
|
||||
"deprecated": "true"
|
||||
}
|
||||
}
|
||||
|
||||
If deprecation is acknowledged by the extension:
|
||||
|
||||
* If MediaWiki knows that the hook is deprecated, the handler will not be
|
||||
called. The call to the handler is filtered.
|
||||
* If MediaWiki does not have the hook in its list of deprecated hooks, the
|
||||
handler will be called anyway.
|
||||
|
||||
Deprecation acknowledgement is a way for the extension to say that it has made
|
||||
some other arrangement for implementing the relevant functionality and does
|
||||
not need the handler for the deprecated hook to be called.
|
||||
|
||||
### Call filtering example
|
||||
|
||||
Suppose the hook `Mash` is deprecated in MediaWiki 2.0, and is replaced by a
|
||||
new one called `Slice`. In our example extension FoodProcessor 1.0, the
|
||||
`Mash` hook is handled. In FoodProcessor 2.0, both `Mash` and `Slice` have
|
||||
handlers, but deprecation of `Mash` is acknowledged. Thus:
|
||||
|
||||
* With MediaWiki 2.0 and FoodProcessor 1.0, `onMash` is called but raises a
|
||||
deprecation warning.
|
||||
* With MediaWiki 2.0 and FoodProcessor 2.0, `onMash` is filtered, and `onSlice`
|
||||
is called.
|
||||
* With MediaWiki 1.0 and FoodProcessor 2.0, `onMash` is called, since it is not
|
||||
yet deprecated in Core. `onSlice` is not called since it does not yet exist
|
||||
in Core.
|
||||
|
||||
So the call filtering system provides both forwards and backwards compatibility.
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
hooks.txt
|
||||
|
||||
This document describes how event hooks work in MediaWiki; how to add hooks for
|
||||
an event; and how to run hooks for an event.
|
||||
This document describes the hook system used in MediaWiki 1.34 and earlier. For
|
||||
the hook system implemented in MediaWiki 1.35, see Hooks.md.
|
||||
|
||||
==Glossary==
|
||||
|
||||
|
|
@ -1809,7 +1807,7 @@ $imagePage: ImagePage object ($this)
|
|||
&$html: HTML for the hook to add
|
||||
|
||||
'ImagePageFileHistoryLine': Called when a file history line is constructed.
|
||||
$imagePage: ImagePage object ($this)
|
||||
$imagePage: Formerly an ImagePage but since 1.27 it is an ImageHistoryList.
|
||||
$file: the file
|
||||
&$line: the HTML of the history line
|
||||
&$css: the line CSS class
|
||||
|
|
|
|||
Loading…
Reference in a new issue