Add an extension for logging MediaWiki events to the system logger
[lhc/web/wiklou.git] / docs / hooks.doc
1 HOOKS.DOC
2
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.
5
6 ==Glossary==
7
8 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'.
15
16 hook
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
19 object and a method.
20
21 hook function
22 The function part of a hook.
23
24 ==Rationale==
25
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.
32
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:
37
38 function showAnArticle($article) {
39 global $wgReverseTitle, $wgCapitalizeTitle;
40
41 if ($wgReverseTitle) {
42 wfReverseTitle($article);
43 }
44
45 if ($wgCapitalizeTitle) {
46 wfCapitalizeTitle($article);
47 }
48
49 # code to actually show the article goes here
50 }
51
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:
55
56 function showAnArticle($article) {
57 global $wgReverseTitle, $wgCapitalizeTitle;
58
59 if ($wgReverseTitle) {
60 wfReverseTitle($article);
61 }
62
63 if ($wgCapitalizeTitle) {
64 wfCapitalizeTitle($article);
65 }
66
67 # code to actually show the article goes here
68
69 if ($wgNotifyArticle) {
70 wfNotifyArticleShow($article));
71 }
72 }
73
74 Using a hook-running strategy, we can avoid having all this
75 option-specific stuff in our mainline code. Using hooks, the function
76 becomes:
77
78 function showAnArticle($article) {
79 if (wfRunHooks('ArticleShow', $article)) {
80 # code to actually show the article goes here
81 wfRunHooks('ArticleShowComplete', $article);
82 }
83 }
84
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.
89
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:
95
96 function reverseArticleTitle($article) {
97 # ...
98 }
99
100 function reverseForExport($article) {
101 # ...
102 }
103
104 The setup function for the extension just has to add its hook
105 functions to the appropriate events:
106
107 setupTitleReversingExtension() {
108 global $wgHooks;
109
110 $wgHooks['ArticleShow'][] = reverseArticleTitle;
111 $wgHooks['ArticleDelete'][] = reverseArticleTitle;
112 $wgHooks['ArticleExport'][] = reverseForExport;
113 }
114
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.
118
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:
122
123 require_once('extensions/ReverseTitle.php');
124
125 ...to their LocalSettings.php file; those of us who don't want or need
126 it can just leave it out.
127
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
130 him/herself.
131
132 ==Writing hooks==
133
134 A hook is a chunk of code run at some particular event. It consists of:
135
136 * a function with some optional accompanying data, or
137 * an object with a method and some optional accompanying data.
138
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:
141
142 $wgHooks['EventName'][] = someFunction; # function, no data
143 $wgHooks['EventName'][] = array(someFunction, $someData);
144 $wgHooks['EventName'][] = array(someFunction); # weird, but OK
145
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
150
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:
155
156 # function, no data
157 someFunction($param1, $param2)
158 # function with data
159 someFunction($someData, $param1, $param2)
160
161 # object only
162 $object->onEventName($param1, $param2)
163 # object with method
164 $object->someMethod($param1, $param2)
165 # object with method and data
166 $object->someMethod($someData, $param1, $param2)
167
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.
171
172 The extra data is useful if we want to use the same function or object
173 for different purposes. For example:
174
175 $wgHooks['ArticleSaveComplete'][] = array(ircNotify, 'TimStarling');
176 $wgHooks['ArticleSaveComplete'][] = array(ircNotify, 'brion');
177
178 This code would result in ircNotify being run twice when an article is
179 saved: once for 'TimStarling', and once for 'brion'.
180
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
187
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
191 could do:
192
193 $wgHooks['UserLogin'][] = array(ldapLogin, $ldapServer);
194
195 function ldapLogin($username, $password) {
196 # log user into LDAP
197 return false;
198 }
199
200 Returning false makes less sense for events where the action is
201 complete, and will probably be ignored.
202
203 ==Using hooks==
204
205 A calling function or method uses the wfRunHooks() function to run
206 the hooks related to a particular event, like so:
207
208 class Article {
209 # ...
210 function protect() {
211 global $wgUser;
212 if (wfRunHooks('ArticleProtect', $this, $wgUser)) {
213 # protect the article
214 wfRunHooks('ArticleProtectComplete', $this, $wgUser);
215 }
216 }
217
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.
223
224 ==Events and parameters==
225
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.
228
229 'UserLoginComplete': after a user has logged in
230 $user: the user object that was created on login
231
232 'UserLogout': before a user logs out
233 $user: the user object that is about to be logged out
234
235 'UserLogoutComplete': after a user has logged out
236 $user: the user object _after_ logout (won't have name, ID, etc.)
237
238