]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/MessageCache.php
MediaWiki 1.11.0
[autoinstallsdev/mediawiki.git] / includes / MessageCache.php
1 <?php
2 /**
3  *
4  * @addtogroup Cache
5  */
6
7 /**
8  *
9  */
10 define( 'MSG_LOAD_TIMEOUT', 60);
11 define( 'MSG_LOCK_TIMEOUT', 10);
12 define( 'MSG_WAIT_TIMEOUT', 10);
13 define( 'MSG_CACHE_VERSION', 1 );
14
15 /**
16  * Message cache
17  * Performs various MediaWiki namespace-related functions
18  *
19  */
20 class MessageCache {
21         var $mCache, $mUseCache, $mDisable, $mExpiry;
22         var $mMemcKey, $mKeys, $mParserOptions, $mParser;
23         var $mExtensionMessages = array();
24         var $mInitialised = false;
25         var $mDeferred = true;
26         var $mAllMessagesLoaded;
27
28         function __construct( &$memCached, $useDB, $expiry, $memcPrefix) {
29                 wfProfileIn( __METHOD__ );
30
31                 $this->mUseCache = !is_null( $memCached );
32                 $this->mMemc = &$memCached;
33                 $this->mDisable = !$useDB;
34                 $this->mExpiry = $expiry;
35                 $this->mDisableTransform = false;
36                 $this->mMemcKey = $memcPrefix.':messages';
37                 $this->mKeys = false; # initialised on demand
38                 $this->mInitialised = true;
39                 $this->mParser = null;
40
41                 # When we first get asked for a message,
42                 # then we'll fill up the cache. If we
43                 # can return a cache hit, this saves
44                 # some extra milliseconds
45                 $this->mDeferred = true;
46
47                 wfProfileOut( __METHOD__ );
48         }
49
50         function getParserOptions() {
51                 if ( !$this->mParserOptions ) {
52                         $this->mParserOptions = new ParserOptions;
53                 }
54                 return $this->mParserOptions;
55         }
56
57         /**
58          * Try to load the cache from a local file
59          */
60         function loadFromLocal( $hash ) {
61                 global $wgLocalMessageCache;
62
63                 $this->mCache = false;
64                 if ( $wgLocalMessageCache === false ) {
65                         return;
66                 }
67
68                 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
69
70                 wfSuppressWarnings();
71                 $file = fopen( $filename, 'r' );
72                 wfRestoreWarnings();
73                 if ( !$file ) {
74                         return;
75                 }
76
77                 // Check to see if the file has the hash specified
78                 $localHash = fread( $file, 32 );
79                 if ( $hash == $localHash ) {
80                         // All good, get the rest of it
81                         $serialized = fread( $file, 10000000 );
82                         $this->setCache( unserialize( $serialized ) );
83                 }
84                 fclose( $file );
85         }
86
87         /**
88          * Save the cache to a local file
89          */
90         function saveToLocal( $serialized, $hash ) {
91                 global $wgLocalMessageCache;
92
93                 if ( $wgLocalMessageCache === false ) {
94                         return;
95                 }
96
97                 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
98                 $oldUmask = umask( 0 );
99                 wfMkdirParents( $wgLocalMessageCache, 0777 );
100                 umask( $oldUmask );
101
102                 $file = fopen( $filename, 'w' );
103                 if ( !$file ) {
104                         wfDebug( "Unable to open local cache file for writing\n" );
105                         return;
106                 }
107
108                 fwrite( $file, $hash . $serialized );
109                 fclose( $file );
110                 @chmod( $filename, 0666 );
111         }
112
113         function loadFromScript( $hash ) {
114                 global $wgLocalMessageCache;
115                 if ( $wgLocalMessageCache === false ) {
116                         return;
117                 }
118                 
119                 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
120                 
121                 wfSuppressWarnings();
122                 $file = fopen( $filename, 'r' );
123                 wfRestoreWarnings();
124                 if ( !$file ) {
125                         return;
126                 }
127                 $localHash=substr(fread($file,40),8);
128                 fclose($file);
129                 if ($hash!=$localHash) {
130                         return;
131                 }
132                 require("$wgLocalMessageCache/messages-" . wfWikiID());
133                 $this->setCache( $this->mCache);
134         }
135         
136         function saveToScript($array, $hash) {
137                 global $wgLocalMessageCache;
138                 if ( $wgLocalMessageCache === false ) {
139                         return;
140                 }
141
142                 $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
143                 $oldUmask = umask( 0 );
144                 wfMkdirParents( $wgLocalMessageCache, 0777 );
145                 umask( $oldUmask );
146                 $file = fopen( $filename.'.tmp', 'w');
147                 fwrite($file,"<?php\n//$hash\n\n \$this->mCache = array(");
148                 
149                 foreach ($array as $key => $message) {
150                         fwrite($file, "'". $this->escapeForScript($key).
151                                 "' => '" . $this->escapeForScript($message). 
152                                 "',\n");
153                 }
154                 fwrite($file,");\n?>");
155                 fclose($file);
156                 rename($filename.'.tmp',$filename);
157         }
158
159         function escapeForScript($string) {
160                 $string = str_replace( '\\', '\\\\', $string );
161                 $string = str_replace( '\'', '\\\'', $string );
162                 return $string;
163         }
164
165         /**
166          * Set the cache to $cache, if it is valid. Otherwise set the cache to false.
167          */
168         function setCache( $cache ) {
169                 if ( isset( $cache['VERSION'] ) && $cache['VERSION'] == MSG_CACHE_VERSION ) {
170                         $this->mCache = $cache;
171                 } else {
172                         $this->mCache = false;
173                 }
174         }
175
176         /**
177          * Loads messages either from memcached or the database, if not disabled
178          * On error, quietly switches to a fallback mode
179          * Returns false for a reportable error, true otherwise
180          */
181         function load() {
182                 global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
183
184                 if ( $this->mDisable ) {
185                         static $shownDisabled = false;
186                         if ( !$shownDisabled ) {
187                                 wfDebug( "MessageCache::load(): disabled\n" );
188                                 $shownDisabled = true;
189                         }
190                         return true;
191                 }
192                 if ( !$this->mUseCache ) {
193                         $this->mDeferred = false;
194                         return true;
195                 }
196
197                 $fname = 'MessageCache::load';
198                 wfProfileIn( $fname );
199                 $success = true;
200
201                 $this->mCache = false;
202
203                 # Try local cache
204                 wfProfileIn( $fname.'-fromlocal' );
205                 $hash = $this->mMemc->get( "{$this->mMemcKey}-hash" );
206                 if ( $hash ) {
207                         if ($wgLocalMessageCacheSerialized) {
208                                 $this->loadFromLocal( $hash );
209                         } else {
210                                 $this->loadFromScript( $hash );
211                         }
212                         if ( $this->mCache ) {
213                                 wfDebug( "MessageCache::load(): got from local cache\n" );
214                         }
215                 }
216                 wfProfileOut( $fname.'-fromlocal' );
217
218                 # Try memcached
219                 if ( !$this->mCache ) {
220                         wfProfileIn( $fname.'-fromcache' );
221                         $this->setCache( $this->mMemc->get( $this->mMemcKey ) );
222                         if ( $this->mCache ) {
223                                 wfDebug( "MessageCache::load(): got from global cache\n" );
224                                 # Save to local cache
225                                 if ( $wgLocalMessageCache !== false ) {
226                                         $serialized = serialize( $this->mCache );
227                                         if ( !$hash ) {
228                                                 $hash = md5( $serialized );
229                                                 $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
230                                         }
231                                         if ($wgLocalMessageCacheSerialized) {
232                                                 $this->saveToLocal( $serialized,$hash );
233                                         } else {
234                                                 $this->saveToScript( $this->mCache, $hash );
235                                         }
236                                 }
237                         }
238                         wfProfileOut( $fname.'-fromcache' );
239                 }
240
241
242                 # If there's nothing in memcached, load all the messages from the database
243                 if ( !$this->mCache ) {
244                         wfDebug( "MessageCache::load(): cache is empty\n" );
245                         $this->lock();
246                         # Other threads don't need to load the messages if another thread is doing it.
247                         $success = $this->mMemc->add( $this->mMemcKey.'-status', "loading", MSG_LOAD_TIMEOUT );
248                         if ( $success ) {
249                                 wfProfileIn( $fname.'-load' );
250                                 wfDebug( "MessageCache::load(): loading all messages from DB\n" );
251                                 $this->loadFromDB();
252                                 wfProfileOut( $fname.'-load' );
253
254                                 # Save in memcached
255                                 # Keep trying if it fails, this is kind of important
256                                 wfProfileIn( $fname.'-save' );
257                                 for ($i=0; $i<20 &&
258                                                    !$this->mMemc->set( $this->mMemcKey, $this->mCache, $this->mExpiry );
259                                          $i++ ) {
260                                         usleep(mt_rand(500000,1500000));
261                                 }
262
263                                 # Save to local cache
264                                 if ( $wgLocalMessageCache !== false ) {
265                                         $serialized = serialize( $this->mCache );
266                                         $hash = md5( $serialized );
267                                         $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
268                                         if ($wgLocalMessageCacheSerialized) {
269                                                 $this->saveToLocal( $serialized,$hash );
270                                         } else {
271                                                 $this->saveToScript( $this->mCache, $hash );
272                                         }
273                                 }
274
275                                 wfProfileOut( $fname.'-save' );
276                                 if ( $i == 20 ) {
277                                         $this->mMemc->set( $this->mMemcKey.'-status', 'error', 60*5 );
278                                         wfDebug( "MemCached set error in MessageCache: restart memcached server!\n" );
279                                 } else {
280                                         $this->mMemc->delete( $this->mMemcKey.'-status' );
281                                 }
282                         }
283                         $this->unlock();
284                 }
285
286                 if ( !is_array( $this->mCache ) ) {
287                         wfDebug( "MessageCache::load(): unable to load cache, disabled\n" );
288                         $this->mDisable = true;
289                         $this->mCache = false;
290                 }
291                 wfProfileOut( $fname );
292                 $this->mDeferred = false;
293                 return $success;
294         }
295
296         /**
297          * Loads all or main part of cacheable messages from the database
298          */
299         function loadFromDB() {
300                 global $wgMaxMsgCacheEntrySize;
301
302                 wfProfileIn( __METHOD__ );
303                 $dbr = wfGetDB( DB_SLAVE );
304                 $this->mCache = array();
305
306                 # Load titles for all oversized pages in the MediaWiki namespace
307                 $res = $dbr->select( 'page', 'page_title',
308                         array( 
309                                 'page_len > ' . intval( $wgMaxMsgCacheEntrySize ),
310                                 'page_is_redirect' => 0,
311                                 'page_namespace' => NS_MEDIAWIKI,
312                         ), 
313                         __METHOD__ );
314                 while ( $row = $dbr->fetchObject( $res ) ) {
315                         $this->mCache[$row->page_title] = '!TOO BIG';
316                 }
317                 $dbr->freeResult( $res );
318
319                 # Load text for the remaining pages
320                 $res = $dbr->select( array( 'page', 'revision', 'text' ),
321                         array( 'page_title', 'old_text', 'old_flags' ),
322                         array( 
323                                 'page_is_redirect' => 0,
324                                 'page_namespace' => NS_MEDIAWIKI,
325                                 'page_latest=rev_id',
326                                 'rev_text_id=old_id',
327                                 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize ) ), 
328                         __METHOD__ );
329
330                 for ( $row = $dbr->fetchObject( $res ); $row; $row = $dbr->fetchObject( $res ) ) {
331                         $this->mCache[$row->page_title] = ' ' . Revision::getRevisionText( $row );
332                 }
333                 $this->mCache['VERSION'] = MSG_CACHE_VERSION;
334                 $dbr->freeResult( $res );
335                 wfProfileOut( __METHOD__ );
336         }
337
338         /**
339          * Not really needed anymore
340          */
341         function getKeys() {
342                 global $wgContLang;
343                 if ( !$this->mKeys ) {
344                         $this->mKeys = array();
345                         $allMessages = Language::getMessagesFor( 'en' );
346                         foreach ( $allMessages as $key => $unused ) {
347                                 $title = $wgContLang->ucfirst( $key );
348                                 array_push( $this->mKeys, $title );
349                         }
350                 }
351                 return $this->mKeys;
352         }
353
354         function replace( $title, $text ) {
355                 global $wgLocalMessageCache, $wgLocalMessageCacheSerialized, $parserMemc;
356                 global $wgMaxMsgCacheEntrySize;
357
358                 wfProfileIn( __METHOD__ );
359                 $this->lock();
360                 $this->load();
361                 $parserMemc->delete(wfMemcKey('sidebar'));
362                 if ( is_array( $this->mCache ) ) {
363                         if ( $text === false ) {
364                                 # Article was deleted
365                                 unset( $this->mCache[$title] );
366                                 $this->mMemc->delete( "$this->mMemcKey:{$title}" );
367                         } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
368                                 $this->mCache[$title] = '!TOO BIG';
369                                 $this->mMemc->set( "$this->mMemcKey:{$title}", ' '.$text, $this->mExpiry );
370                         } else {
371                                 $this->mCache[$title] = ' ' . $text;
372                                 $this->mMemc->delete( "$this->mMemcKey:{$title}" );
373                         }
374                         $this->mMemc->set( $this->mMemcKey, $this->mCache, $this->mExpiry );
375
376                         # Save to local cache
377                         if ( $wgLocalMessageCache !== false ) {
378                                 $serialized = serialize( $this->mCache );
379                                 $hash = md5( $serialized );
380                                 $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
381                                 if ($wgLocalMessageCacheSerialized) {
382                                         $this->saveToLocal( $serialized,$hash );
383                                 } else {
384                                         $this->saveToScript( $this->mCache, $hash );
385                                 }
386                         }
387                 }
388                 $this->unlock();
389                 wfProfileOut( __METHOD__ );
390         }
391
392         /**
393          * Returns success
394          * Represents a write lock on the messages key
395          */
396         function lock() {
397                 if ( !$this->mUseCache ) {
398                         return true;
399                 }
400
401                 $lockKey = $this->mMemcKey . 'lock';
402                 for ($i=0; $i < MSG_WAIT_TIMEOUT && !$this->mMemc->add( $lockKey, 1, MSG_LOCK_TIMEOUT ); $i++ ) {
403                         sleep(1);
404                 }
405
406                 return $i >= MSG_WAIT_TIMEOUT;
407         }
408
409         function unlock() {
410                 if ( !$this->mUseCache ) {
411                         return;
412                 }
413
414                 $lockKey = $this->mMemcKey . 'lock';
415                 $this->mMemc->delete( $lockKey );
416         }
417
418         /**
419          * Get a message from either the content language or the user language.
420          *
421          * @param string $key The message cache key
422          * @param bool $useDB Get the message from the DB, false to use only the localisation
423          * @param bool $forContent Get the message from the content language rather than the 
424          *                         user language
425          * @param bool $isFullKey Specifies whether $key is a two part key "lang/msg".
426          */
427         function get( $key, $useDB = true, $forContent = true, $isFullKey = false ) {
428                 global $wgContLanguageCode, $wgContLang, $wgLang;
429                 if( $forContent ) {
430                         $lang =& $wgContLang;
431                 } else {
432                         $lang =& $wgLang;
433                 }
434                 $langcode = $lang->getCode();
435                 # If uninitialised, someone is trying to call this halfway through Setup.php
436                 if( !$this->mInitialised ) {
437                         return '&lt;' . htmlspecialchars($key) . '&gt;';
438                 }
439                 # If cache initialization was deferred, start it now.
440                 if( $this->mDeferred && !$this->mDisable && $useDB ) {
441                         $this->load();
442                 }
443
444                 $message = false;
445
446                 # Normalise title-case input
447                 $lckey = $wgContLang->lcfirst( $key );
448                 $lckey = str_replace( ' ', '_', $lckey );
449
450                 # Try the MediaWiki namespace
451                 if( !$this->mDisable && $useDB ) {
452                         $title = $wgContLang->ucfirst( $lckey );
453                         if(!$isFullKey && ($langcode != $wgContLanguageCode) ) {
454                                 $title .= '/' . $langcode;
455                         }
456                         $message = $this->getMsgFromNamespace( $title );
457                 }
458                 # Try the extension array
459                 if( $message === false && isset( $this->mExtensionMessages[$langcode][$lckey] ) ) {
460                         $message = $this->mExtensionMessages[$langcode][$lckey];
461                 }
462                 if ( $message === false && isset( $this->mExtensionMessages['en'][$lckey] ) ) {
463                         $message = $this->mExtensionMessages['en'][$lckey];
464                 }
465
466                 # Try the array in the language object
467                 if( $message === false ) {
468                         #wfDebug( "Trying language object for message $key\n" );
469                         wfSuppressWarnings();
470                         $message = $lang->getMessage( $lckey );
471                         wfRestoreWarnings();
472                         if ( is_null( $message ) ) {
473                                 $message = false;
474                         }
475                 }
476
477                 # Try the array of another language
478                 if( $message === false && strpos( $lckey, '/' ) ) {
479                         $message = explode( '/', $lckey );
480                         if ( $message[1] ) {
481                                 wfSuppressWarnings();
482                                 $message = Language::getMessageFor( $message[0], $message[1] );
483                                 wfRestoreWarnings();
484                                 if ( is_null( $message ) ) {
485                                         $message = false;
486                                 }
487                         } else {
488                                 $message = false;
489                         }
490                 }
491
492                 # Is this a custom message? Try the default language in the db...
493                 if( ($message === false || $message === '-' ) &&
494                         !$this->mDisable && $useDB &&
495                         !$isFullKey && ($langcode != $wgContLanguageCode) ) {
496                         $message = $this->getMsgFromNamespace( $wgContLang->ucfirst( $lckey ) );
497                 }
498
499                 # Final fallback
500                 if( $message === false ) {
501                         return '&lt;' . htmlspecialchars($key) . '&gt;';
502                 }
503
504                 # Replace brace tags
505                 $message = $this->transform( $message );
506                 return $message;
507         }
508
509         /**
510          * Get a message from the MediaWiki namespace, with caching. The key must 
511          * first be converted to two-part lang/msg form if necessary.
512          *
513          * @param string $title Message cache key with initial uppercase letter
514          */
515         function getMsgFromNamespace( $title ) {
516                 $message = false;
517                 $type = false;
518
519                 # Try the cache
520                 if( $this->mUseCache && isset( $this->mCache[$title] ) ) {
521                         $entry = $this->mCache[$title];
522                         $type = substr( $entry, 0, 1 );
523                         if ( $type == ' ' ) {
524                                 return substr( $entry, 1 );
525                         }
526                 }
527
528                 # Call message hooks, in case they are defined
529                 wfRunHooks('MessagesPreLoad', array( $title, &$message ) );
530                 if ( $message !== false ) {
531                         return $message;
532                 }
533
534                 # If there is no cache entry and no placeholder, it doesn't exist
535                 if ( $type != '!' && $message === false ) {
536                         return false;
537                 }
538
539                 $memcKey = $this->mMemcKey . ':' . $title;
540
541                 # Try the individual message cache
542                 if ( $this->mUseCache ) {
543                         $entry = $this->mMemc->get( $memcKey );
544                         if ( $entry ) {
545                                 $type = substr( $entry, 0, 1 );
546
547                                 if ( $type == ' ' ) {
548                                         $message = substr( $entry, 1 );
549                                         $this->mCache[$title] = $entry;
550                                         return $message;
551                                 } elseif ( $entry == '!NONEXISTENT' ) {
552                                         return false;
553                                 } else {
554                                         # Corrupt/obsolete entry, delete it
555                                         $this->mMemc->delete( $memcKey );
556                                 }
557
558                         }
559                 }
560
561                 # Try loading it from the DB
562                 $revision = Revision::newFromTitle( Title::makeTitle( NS_MEDIAWIKI, $title ) );
563                 if( $revision ) {
564                         $message = $revision->getText();
565                         if ($this->mUseCache) {
566                                 $this->mCache[$title] = ' ' . $message;
567                                 $this->mMemc->set( $memcKey, $message, $this->mExpiry );
568                         }
569                 } else {
570                         # Negative caching
571                         # Use some special text instead of false, because false gets converted to '' somewhere
572                         $this->mMemc->set( $memcKey, '!NONEXISTENT', $this->mExpiry );
573                         $this->mCache[$title] = false;
574                 }
575
576                 return $message;
577         }
578
579         function transform( $message ) {
580                 global $wgParser;
581                 if ( !$this->mParser && isset( $wgParser ) ) {
582                         # Do some initialisation so that we don't have to do it twice
583                         $wgParser->firstCallInit();
584                         # Clone it and store it
585                         $this->mParser = clone $wgParser;
586                 }
587                 if ( !$this->mDisableTransform && $this->mParser ) {
588                         if( strpos( $message, '{{' ) !== false ) {
589                                 $message = $this->mParser->transformMsg( $message, $this->getParserOptions() );
590                         }
591                 }
592                 return $message;
593         }
594
595         function disable() { $this->mDisable = true; }
596         function enable() { $this->mDisable = false; }
597         function disableTransform() { $this->mDisableTransform = true; }
598         function enableTransform() { $this->mDisableTransform = false; }
599         function setTransform( $x ) { $this->mDisableTransform = $x; }
600         function getTransform() { return $this->mDisableTransform; }
601
602         /**
603          * Add a message to the cache
604          *
605          * @param mixed $key
606          * @param mixed $value
607          * @param string $lang The messages language, English by default
608          */
609         function addMessage( $key, $value, $lang = 'en' ) {
610                 $this->mExtensionMessages[$lang][$key] = $value;
611         }
612
613         /**
614          * Add an associative array of message to the cache
615          *
616          * @param array $messages An associative array of key => values to be added
617          * @param string $lang The messages language, English by default
618          */
619         function addMessages( $messages, $lang = 'en' ) {
620                 wfProfileIn( __METHOD__ );
621                 if ( isset( $this->mExtensionMessages[$lang] ) ) {
622                         $this->mExtensionMessages[$lang] = $messages + $this->mExtensionMessages[$lang];
623                 } else {
624                         $this->mExtensionMessages[$lang] = $messages;
625                 }
626                 wfProfileOut( __METHOD__ );
627         }
628
629         /**
630          * Add a 2-D array of messages by lang. Useful for extensions.
631          *
632          * @param array $messages The array to be added
633          */
634         function addMessagesByLang( $messages ) {
635                 wfProfileIn( __METHOD__ );
636                 foreach ( $messages as $key => $value ) {
637                         $this->addMessages( $value, $key );
638                 }
639                 wfProfileOut( __METHOD__ );
640         }
641
642         /**
643          * Get the extension messages for a specific language
644          *
645          * @param string $lang The messages language, English by default
646          */
647         function getExtensionMessagesFor( $lang = 'en' ) {
648                 wfProfileIn( __METHOD__ );
649                 $messages = array();
650                 if ( isset( $this->mExtensionMessages[$lang] ) ) {
651                         $messages = $this->mExtensionMessages[$lang];
652                 }
653                 if ( $lang != 'en' ) {
654                         $messages = $messages + $this->mExtensionMessages['en'];
655                 }
656                 wfProfileOut( __METHOD__ );
657                 return $messages;
658         }
659
660         /**
661          * Clear all stored messages. Mainly used after a mass rebuild.
662          */
663         function clear() {
664                 global $wgLocalMessageCache;
665                 if( $this->mUseCache ) {
666                         # Global cache
667                         $this->mMemc->delete( $this->mMemcKey );
668                         # Invalidate all local caches
669                         $this->mMemc->delete( "{$this->mMemcKey}-hash" );
670                 }
671         }
672
673         function loadAllMessages() {
674                 global $wgExtensionMessagesFiles;
675                 if ( $this->mAllMessagesLoaded ) {
676                         return;
677                 }
678                 $this->mAllMessagesLoaded = true;
679
680                 # Some extensions will load their messages when you load their class file
681                 wfLoadAllExtensions();
682                 # Others will respond to this hook
683                 wfRunHooks( 'LoadAllMessages' );
684                 # Some register their messages in $wgExtensionMessagesFiles
685                 foreach ( $wgExtensionMessagesFiles as $name => $file ) {
686                         if ( $file ) {
687                                 $this->loadMessagesFile( $file );
688                                 $wgExtensionMessagesFiles[$name] = false;
689                         }
690                 }
691                 # Still others will respond to neither, they are EVIL. We sometimes need to know!
692         }
693
694         /**
695          * Load messages from a given file
696          */
697         function loadMessagesFile( $filename ) {
698                 $magicWords = false;
699                 require( $filename );
700                 $this->addMessagesByLang( $messages );
701
702                 if ( $magicWords !== false ) {
703                         global $wgContLang;
704                         $wgContLang->addMagicWordsByLang( $magicWords );
705                 }
706         }
707 }
708