Understanding MediaWiki's Hook System for Extension Development

The modern MediaWiki hook system: extension.json registration, handler classes, arguments and return values, ordering, and debugging hook execution.

Hooks are MediaWiki's event system for extension developers: a hook is a named point in the request lifecycle — PageSaveComplete, BeforePageDisplay, ParserAfterParse — where extensions register functions to run. Since MediaWiki 1.35 the mechanics are cleaner: registration in extension.json, handler classes with dependency injection, and no more global $wgHooks arrays in new code. This article is the modern pattern, plus the debugging that hooks eventually require.

1. The lifecycle in 2026

  1. An event happens in core (or another extension) — the code calls $this->getHookContainer()->run( 'MyHookName', [ $arg1, $arg2 ] );
  2. HookRunner collects all registered handlers for that hook
  3. Handlers run in registration order; unless a handler returns false (aborts the chain for that hook) or HookAbortErrors, all run

2. Registering handlers

Old way (still works, avoid in new code):

$wgHooks['BeforePageDisplay'][] = 'MyExtensionHooks::onBeforePageDisplay';

Modern way — declare in extension.json:

{
    "name": "MyExtension",
    "Hooks": {
        "BeforePageDisplay": "MyExtension\\Hook\\Handler\\Main"
    },
    "HookHandlers": {
        "MyExtension\\Hook\\Handler\\Main": {
            "class": "MyExtension\\Hook\\Handler\\Main",
            "services": [ "UserFactory", "OutputPage" ]
        }
    }
}

The handler class:

class Main implements BeforePageDisplayHook {
    public function onBeforePageDisplay( $out, $skin ): void
    {
        // act on the page output
    }
}

Interface-based handler signatures (BeforePageDisplayHook etc.) give autocompletion and type safety, and the services array injects exactly the services the class needs — the dependency injection that replaced global accessors. Since MediaWiki 1.45/wfGlobalName era: the surviving globals ($wgUser, etc.) are deprecated paths; inject services instead.

3. Arguments and return values

Handlers receive the hook's arguments (mostly by reference, so they can modify state) and can:

  • Return void/true — continue normally
  • Return false — stop other handlers for this hook (marking 'handled')
  • Throw — abort the operation, in hooks designed for it

The documented contract per hook (on Manual:Hooks) states which of these is meaningful — most hooks ignore false; some (like PageSaveComplete) are notifications that must not block.

4. Ordering inside the chain

MediaWiki offers no per-handler priority control in the modern registration; order is determined by extension load order (alphabetical) and, for legacy $wgHooks entries, insertion time. Practical consequences:

  • Rename your extension to order it — a blunt but used trick; do not rely on it
  • Hook into later points — if you must run after other extensions, BeforePageDisplay runs after most content hooks; document your expected position
  • Compose, don't fight — whenever two extensions fight over the same output, prefer one canonical handler that coordinates

5. Performance rules

  • Handlers run on every occurrence — a ParserAfterParse handler runs for every parsed page; keep work lazy and cache results (WANObjectCache)
  • Registration must be cheap: no heavy setup at load time — services instantiate lazily by design
  • Use the right hook: ParserFirstCallInit for registering parser functions is once-per-process, while ParserAfterParse is per-parse — placing logic in the wrong one is the classic slowdown

6. Debugging hooks

Three tools for 'my hook doesn't run / runs twice':

  1. Special:Version — confirm the extension is loaded (a missing entry means registration failed early)
  2. Log or error-log in the handler — ‘does it fire at all’ splits the problem in two immediately
  3. The profiler ($wgProfiler) — Hook-Runner::run(MyHook) entries show execution counts and costs per hook

The Manual:Hooks page is the index of every hook with signatures and examples — bookmark it before writing handlers, and the development manual covers the full extension lifecycle.

Subscribe to MediaWiki Tips and Tricks

Don’t miss out on the latest articles. Sign up now to get access to the library of members-only articles.
jamie@example.com
Subscribe