3 This document describes how event hooks work in MediaWiki; how to add
4 hooks for an event; and how to run hooks for an event.
9 Something that happens with the wiki. For example: a user logs
10 in. A wiki page is saved. A wiki page is deleted. Often there are
11 two events associated with a single action: one before the code
12 is run to make the event happen, and one after. Each event has a
13 name, preferably in CamelCase. For example, 'UserLogin',
14 'ArticleSave', 'ArticleSaveComplete', 'ArticleDelete'.
17 A clump of code and data that should be run when an event
18 happens. This can be either a function and a chunk of data, or an
22 The function part of a hook.
26 Hooks allow us to decouple optionally-run code from code that is run
27 for everyone. It allows MediaWiki hackers, third-party developers and
28 local administrators to define code that will be run at certain points
29 in the mainline code, and to modify the data run by that mainline
30 code. Hooks can keep mainline code simple, and make it easier to
31 write extensions. Hooks are a principled alternative to local patches.
33 Consider, for example, two options in MediaWiki. One reverses the
34 order of a title before displaying the article; the other converts the
35 title to all uppercase letters. Currently, in MediaWiki code, we
36 handle this as follows:
38 function showAnArticle($article) {
39 global $wgReverseTitle, $wgCapitalizeTitle;
41 if ($wgReverseTitle) {
42 wfReverseTitle($article);
45 if ($wgCapitalizeTitle) {
46 wfCapitalizeTitle($article);
49 # code to actually show the article goes here
52 An extension writer, or a local admin, will often add custom code to
53 the function -- with or without a global variable. For example,
54 someone wanting email notification when an article is shown may add:
56 function showAnArticle($article) {
57 global $wgReverseTitle, $wgCapitalizeTitle;
59 if ($wgReverseTitle) {
60 wfReverseTitle($article);
63 if ($wgCapitalizeTitle) {
64 wfCapitalizeTitle($article);
67 # code to actually show the article goes here
69 if ($wgNotifyArticle) {
70 wfNotifyArticleShow($article));
74 Using a hook-running strategy, we can avoid having all this
75 option-specific stuff in our mainline code. Using hooks, the function
78 function showAnArticle($article) {
79 if (wfRunHooks('ArticleShow', $article)) {
80 # code to actually show the article goes here
81 wfRunHooks('ArticleShowComplete', $article);
85 We've cleaned up the code here by removing clumps of weird,
86 infrequently used code and moving them off somewhere else. It's much
87 easier for someone working with this code to see what's _really_ going
88 on, and make changes or fix bugs.
90 In addition, we can take all the code that deals with the little-used
91 title-reversing options (say) and put it in one place. Instead of
92 having a little title-reversing if-block spread all over the codebase
93 in showAnArticle, deleteAnArticle, exportArticle, etc., we can
94 concentrate it all in an extension file:
96 function reverseArticleTitle($article) {
100 function reverseForExport($article) {
104 The setup function for the extension just has to add its hook
105 functions to the appropriate events:
107 setupTitleReversingExtension() {
110 $wgHooks['ArticleShow'][] = 'reverseArticleTitle';
111 $wgHooks['ArticleDelete'][] = 'reverseArticleTitle';
112 $wgHooks['ArticleExport'][] = 'reverseForExport';
115 Having all this code related to the title-reversion option in one
116 place means that it's easier to read and understand; you don't have to
117 do a grep-find to see where the $wgReverseTitle variable is used, say.
119 If the code is well enough isolated, it can even be excluded when not
120 used -- making for some slight savings in memory and time at runtime.
121 Admins who want to have all the reversed titles can add:
123 require_once('extensions/ReverseTitle.php');
125 ...to their LocalSettings.php file; those of us who don't want or need
126 it can just leave it out.
128 The extensions don't even have to be shipped with MediaWiki; they
129 could be provided by a third-party developer or written by the admin
134 A hook is a chunk of code run at some particular event. It consists of:
136 * a function with some optional accompanying data, or
137 * an object with a method and some optional accompanying data.
139 Hooks are registered by adding them to the global $wgHooks array for a
140 given event. All the following are valid ways to define hooks:
142 $wgHooks['EventName'][] = 'someFunction'; # function, no data
143 $wgHooks['EventName'][] = array('someFunction', $someData);
144 $wgHooks['EventName'][] = array('someFunction'); # weird, but OK
146 $wgHooks['EventName'][] = $object; # object only
147 $wgHooks['EventName'][] = array($object, 'someMethod');
148 $wgHooks['EventName'][] = array($object, 'someMethod', $someData);
149 $wgHooks['EventName'][] = array($object); # weird but OK
151 When an event occurs, the function (or object method) will be called
152 with the optional data provided as well as event-specific parameters.
153 The above examples would result in the following code being executed
154 when 'EventName' happened:
157 someFunction($param1, $param2)
159 someFunction($someData, $param1, $param2)
162 $object->onEventName($param1, $param2)
164 $object->someMethod($param1, $param2)
165 # object with method and data
166 $object->someMethod($someData, $param1, $param2)
168 Note that when an object is the hook, and there's no specified method,
169 the default method called is 'onEventName'. For different events this
170 would be different: 'onArticleSave', 'onUserLogin', etc.
172 The extra data is useful if we want to use the same function or object
173 for different purposes. For example:
175 $wgHooks['ArticleSaveComplete'][] = array('ircNotify', 'TimStarling');
176 $wgHooks['ArticleSaveComplete'][] = array('ircNotify', 'brion');
178 This code would result in ircNotify being run twice when an article is
179 saved: once for 'TimStarling', and once for 'brion'.
181 Hooks can return three possible values:
182 * true: the hook has operated successfully
183 * "some string": an error occurred; processing should
184 stop and the error should be shown to the user
185 * false: the hook has successfully done the work
186 necessary and the calling function should skip
188 The last result would be for cases where the hook function replaces
189 the main functionality. For example, if you wanted to authenticate
190 users to a custom system (LDAP, another PHP program, whatever), you
193 $wgHooks['UserLogin'][] = array('ldapLogin', $ldapServer);
195 function ldapLogin($username, $password) {
200 Returning false makes less sense for events where the action is
201 complete, and will probably be ignored.
205 A calling function or method uses the wfRunHooks() function to run
206 the hooks related to a particular event, like so:
212 if (wfRunHooks('ArticleProtect', $this, $wgUser)) {
213 # protect the article
214 wfRunHooks('ArticleProtectComplete', $this, $wgUser);
218 wfRunHooks() returns true if the calling function should continue
219 processing (the hooks ran OK, or there are no hooks to run), or false
220 if it shouldn't (an error occurred, or one of the hooks handled the
221 action already). Checking the return value matters more for "before"
222 hooks than for "complete" hooks.
224 ==Events and parameters==
226 This is a list of known events and parameters; please add to it if
227 you're going to add events to the MediaWiki code.
229 'ArticleDelete': before an article is deleted
230 $article: the article (object) being deleted
231 $user: the user (object) deleting the article
232 $reason: the reason (string) the article is being deleted
234 'ArticleDeleteComplete': after an article is deleted
235 $article: the article that was deleted
236 $user: the user that deleted the article
237 $reason: the reason the article was deleted
239 'ArticleProtect': before an article is protected
240 $article: the article being protected
241 $user: the user doing the protection
242 $protect: boolean whether this is a protect or an unprotect
243 $reason: Reason for protect
244 $moveonly: boolean whether this is for move only or not
246 'ArticleProtectComplete': after an article is protected
247 $article: the article that was protected
248 $user: the user who did the protection
249 $protect: boolean whether it was a protect or an unprotect
250 $reason: Reason for protect
251 $moveonly: boolean whether it was for move only or not
253 'ArticleSave': before an article is saved
254 $article: the article (object) being saved
255 $user: the user (object) saving the article
256 $text: the new article text
257 $summary: the article summary (comment)
262 'ArticleSaveComplete': after an article is saved
263 $article: the article (object) saved
264 $user: the user (object) who saved the article
265 $text: the new article text
266 $summary: the article summary (comment)
271 'BlockIp': before an IP address or user is blocked
272 $block: the Block object about to be saved
273 $user: the user _doing_ the block (not the one being blocked)
275 'BlockIpComplete': after an IP address or user is blocked
276 $block: the Block object that was saved
277 $user: the user who did the block (not the one being blocked)
279 'UnknownAction': An unknown "action" has occured (useful for defining
282 $article: article "acted on"
284 'UserLoginComplete': after a user has logged in
285 $user: the user object that was created on login
287 'UserLogout': before a user logs out
288 $user: the user object that is about to be logged out
290 'UserLogoutComplete': after a user has logged out
291 $user: the user object _after_ logout (won't have name, ID, etc.)