Hook Interfaces
Each hook is defined by an interface whose name is the hook name withHook appended. The method name is the hook name prefixed with on.
Hook subnamespace relative to the caller namespace. For example, a hook in MediaWiki\Foo would have its interface in MediaWiki\Foo\Hook.
HookContainer
HookContainer is the service responsible for maintaining the list of hook handlers and dispatching calls to them. It is not aware of hook interfaces or parameter types — that is the responsibility of hook runner classes.
HookContainer exposes metadata methods such as isRegistered(), which returns whether any handlers are registered for a given hook.
HookContainer object should be passed to the service constructor rather than retrieved from the global service locator inside the method.
HookContainer was introduced in MediaWiki 1.35. It is found at MediaWiki\HookContainer\HookContainer and is part of includes/HookContainer/.Hook Runner Classes
A hook runner is a class that implements hook interfaces, proxying calls toHookContainer::run(). MediaWiki core has three hook runner classes:
HookRunner
Proxy methods for all hooks called by parts of core outside the API and ResourceLoader.
ApiHookRunner
Proxy methods for all hooks called by the Action API.
ResourceLoader\HookRunner
Hooks specific to the ResourceLoader subsystem.
Extension HookRunners
Extensions that call hooks should define their own hook runner class, even for core hooks, to avoid breakage if core reorganizes calling code.
Mash hook from static code:
HookContainer as a constructor parameter and construct the runner internally:
Implementing a Hook in an Extension
Define the HookHandler in extension.json
Use the
HookHandlers attribute to map a handler name to an ObjectFactory specification:Register the hook in extension.json
Use the Or using the explicit object form:
Hooks attribute to map a hook name to the handler name:Service Dependencies in Hook Handlers
TheHookHandlers ObjectFactory specification can list services to inject into the handler’s constructor:
noServices option, service injection is disabled. Specifying services for such a hook throws an exception at call time.
Returning and Aborting
Hook handlers communicate results back to callers primarily by modifying parameters passed by reference.- Continuing execution
- Aborting execution
Return
true or return nothing (implicitly null) to allow HookContainer to continue calling any remaining handlers:Aborting is properly used to enforce a convention that only one extension may handle a given hook call, or as a generic signal that the caller should stop performing an action. Most hook callers do not check the return value from
HookContainer::run(). Returning false from a handler of such hooks only breaks other extensions.[ "abortable" => false ] in the $options parameter to HookContainer::run(). Returning false from a handler for a non-abortable hook throws an exception.
Extensions registered first in LocalSettings.php are called first and thus have the first opportunity to abort. In the new hook system, legacy-registered handlers are called before new-style handlers.
The New Hook System vs. Legacy Function-Based Hooks
- New system (1.35+)
- Legacy system
Handler classes implement typed interfaces and are registered via
HookHandlers in extension.json. Services can be injected via ObjectFactory. Handlers are type-safe and testable in isolation.Defining a New Hook
To define a new hook in core or an extension:- Create a hook interface in a
Hooksubnamespace relative to the calling code’s namespace. - Add an implementation (proxy method) to the relevant
HookRunnerclass.
Hook subnamespace convention is recommended.
Hook Deprecation
Core hooks are deprecated by registering them in theDeprecatedHooks class. Extensions deprecate hooks they define using the DeprecatedHooks attribute in extension.json:
component is omitted, it defaults to the extension name. The hook interface should also be annotated with @deprecated in its doc comment — on the interface itself, not the method, so that implementing the interface is deprecated (not just calling it).
Call Filtering
Deprecating a hook activates call filtering. Extensions opt in by acknowledging deprecation with thedeprecated flag:
- If MediaWiki knows the hook is deprecated, the handler is not called (filtered).
- If MediaWiki does not yet list the hook as deprecated, the handler is called anyway.
Call filtering example
Call filtering example
Suppose
Mash is deprecated in MediaWiki 2.0 and replaced by Slice. In FoodProcessor 1.0 only Mash is handled. In FoodProcessor 2.0 both hooks are handled, and deprecation of Mash is acknowledged.| MediaWiki | FoodProcessor | Result |
|---|---|---|
| 2.0 | 1.0 | onMash is called but raises a deprecation warning |
| 2.0 | 2.0 | onMash is filtered; onSlice is called |
| 1.0 | 2.0 | onMash is called (not yet deprecated in core); onSlice is not called (does not yet exist) |
Silent Deprecation
To deprecate a hook without raising warnings (soft deprecation), add the"silent": true flag. Call filtering is still activated, which simplifies migrating extensions to the new hook.
