Go to the documentation of this file.
28 define(
'MSG_CACHE_VERSION', 1 );
34 define(
'MSG_LOAD_TIMEOUT', 60 );
40 define(
'MSG_LOCK_TIMEOUT', 30 );
45 define(
'MSG_WAIT_TIMEOUT', 30 );
105 if ( is_null( self::$instance ) ) {
106 global $wgUseDatabaseMessages, $wgMsgCacheExpiry;
107 self::$instance =
new self(
109 $wgUseDatabaseMessages,
123 self::$instance =
null;
131 function __construct( $memCached, $useDB, $expiry ) {
136 $this->mMemc = $memCached;
137 $this->mDisable = !$useDB;
138 $this->mExpiry = $expiry;
147 if ( !$this->mParserOptions ) {
153 $this->mParserOptions->setAllowUnsafeRawHtml(
false );
169 $filename =
"$wgCacheDirectory/messages-" .
wfWikiID() .
"-$code";
171 # Check file existence
173 $file = fopen( $filename,
'r' );
180 $localHash = fread(
$file, 32 );
181 if (
$hash === $localHash ) {
184 while ( !feof(
$file ) ) {
203 $filename =
"$wgCacheDirectory/messages-" .
wfWikiID() .
"-$code";
207 $file = fopen( $filename,
'w' );
211 wfDebug(
"Unable to open local cache file for writing\n" );
219 chmod( $filename, 0666 );
243 function load( $code =
false ) {
244 global $wgUseLocalMessageCache;
246 if ( !is_string( $code ) ) {
247 # This isn't really nice, so at least make a note about it and try to
249 wfDebug( __METHOD__ .
" called without providing a language code\n" );
253 # Don't do double loading...
254 if ( isset( $this->mLoadedLanguages[$code] ) ) {
258 # 8 lines of code just to say (once) that message cache is disabled
259 if ( $this->mDisable ) {
260 static $shownDisabled =
false;
261 if ( !$shownDisabled ) {
262 wfDebug( __METHOD__ .
": disabled\n" );
263 $shownDisabled =
true;
269 # Loading code starts
272 $staleCache =
false; # a
cache array with expired
data, or
false if none has been loaded
273 $where =
array(); # Debug info, delayed to avoid spamming debug log
too much
277 # Hash of the contents is stored in memcache, to detect if local cache goes
278 # out of date (e.g. due to replace() on some other server)
279 if ( $wgUseLocalMessageCache ) {
282 $hash = $this->mMemc->get(
wfMemcKey(
'messages', $code,
'hash' ) );
286 $where[] =
'local cache is empty or has the wrong hash';
288 $where[] =
'local cache is expired';
291 $where[] =
'got from local cache';
293 $this->mCache[$code] =
$cache;
300 # Try the global cache. If it is empty, try to acquire a lock. If
301 # the lock can't be acquired, wait for the other thread to finish
302 # and then try the global cache a second time.
303 for ( $failedAttempts = 0; $failedAttempts < 2; $failedAttempts++ ) {
305 $cache = $this->mMemc->get( $cacheKey );
307 $where[] =
'global cache is empty';
309 $where[] =
'global cache is expired';
312 $where[] =
'got from global cache';
313 $this->mCache[$code] =
$cache;
321 # Done, no need to retry
325 # We need to call loadFromDB. Limit the concurrency to a single
326 # process. This prevents the site from going down when the cache
328 $statusKey =
wfMemcKey(
'messages', $code,
'status' );
331 # Unlock the status key if there is an exception
333 $statusUnlocker =
new ScopedCallback(
function () use ( $that, $statusKey ) {
334 $that->mMemc->delete( $statusKey );
337 # Now let's regenerate
338 $where[] =
'loading from database';
340 # Lock the cache to prevent conflicting writes
341 # If this lock fails, it doesn't really matter, it just means the
342 # write is potentially non-atomic, e.g. the results of a replace()
344 if ( $this->
lock( $cacheKey ) ) {
345 $mainUnlocker =
new ScopedCallback(
function () use ( $that, $cacheKey ) {
346 $that->unlock( $cacheKey );
349 $mainUnlocker =
null;
350 $where[] =
'could not acquire main lock';
354 $this->mCache[$code] =
$cache;
362 if ( !$saveSuccess ) {
363 # Cache save has failed.
364 # There are two main scenarios where this could be a problem:
366 # - The cache is more than the maximum size (typically
369 # - Memcached has no space remaining in the relevant slab
370 # class. This is unlikely with recent versions of
373 # Either way, if there is a local cache, nothing bad will
374 # happen. If there is no local cache, disabling the message
375 # cache for all requests avoids incurring a loadFromDB()
376 # overhead on every request, and thus saves the wiki from
377 # complete downtime under moderate traffic conditions.
378 if ( !$wgUseLocalMessageCache ) {
379 $this->mMemc->set( $statusKey,
'error', 60 * 5 );
380 $where[] =
'could not save cache, disabled globally for 5 minutes';
382 $where[] =
"could not save global cache";
386 # Load from DB complete, no need to retry
388 } elseif ( $staleCache ) {
389 # Use the stale cache while some other thread constructs the new one
390 $where[] =
'using stale cache';
391 $this->mCache[$code] = $staleCache;
394 } elseif ( $failedAttempts > 0 ) {
395 # Already retried once, still failed, so don't do another lock/unlock cycle
396 # This case will typically be hit if memcached is down, or if
397 # loadFromDB() takes longer than MSG_WAIT_TIMEOUT
398 $where[] =
"could not acquire status key.";
401 $status = $this->mMemc->get( $statusKey );
402 if ( $status ===
'error' ) {
406 # Wait for the other thread to finish, then retry
407 $where[] =
'waited for other thread to complete';
408 $this->
lock( $cacheKey );
409 $this->
unlock( $cacheKey );
416 $where[] =
'loading FAILED - cache is disabled';
417 $this->mDisable =
true;
418 $this->mCache =
false;
419 # This used to throw an exception, but that led to nasty side effects like
420 # the whole wiki being instantly down if the memcached server died
422 # All good, just record the success
423 $this->mLoadedLanguages[$code] =
true;
425 $info = implode(
', ', $where );
426 wfDebug( __METHOD__ .
": Loading $code... $info\n" );
442 global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
448 'page_is_redirect' => 0,
453 if ( $wgAdaptiveMessageCache && $code !== $wgLanguageCode ) {
454 if ( !isset( $this->mCache[$wgLanguageCode] ) ) {
455 $this->
load( $wgLanguageCode );
457 $mostused = array_keys( $this->mCache[$wgLanguageCode] );
458 foreach ( $mostused
as $key =>
$value ) {
459 $mostused[$key] =
"$value/$code";
463 if ( count( $mostused ) ) {
464 $conds[
'page_title'] = $mostused;
465 } elseif ( $code !== $wgLanguageCode ) {
466 $conds[] =
'page_title' .
$dbr->buildLike(
$dbr->anyString(),
'/', $code );
468 # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses
469 # other than language code.
470 $conds[] =
'page_title NOT' .
$dbr->buildLike(
$dbr->anyString(),
'/',
$dbr->anyString() );
473 # Conditions to fetch oversized pages to ignore them
475 $bigConds[] =
'page_len > ' . intval( $wgMaxMsgCacheEntrySize );
477 # Load titles for all oversized pages in the MediaWiki namespace
478 $res =
$dbr->select(
'page',
'page_title', $bigConds, __METHOD__ .
"($code)-big" );
479 foreach (
$res as $row ) {
480 $cache[$row->page_title] =
'!TOO BIG';
483 # Conditions to load the remaining pages with their contents
484 $smallConds = $conds;
485 $smallConds[] =
'page_latest=rev_id';
486 $smallConds[] =
'rev_text_id=old_id';
487 $smallConds[] =
'page_len <= ' . intval( $wgMaxMsgCacheEntrySize );
490 array(
'page',
'revision',
'text' ),
491 array(
'page_title',
'old_text',
'old_flags' ),
493 __METHOD__ .
"($code)-small"
496 foreach (
$res as $row ) {
498 if ( $text ===
false ) {
505 .
": failed to load message page text for {$row->page_title} ($code)"
508 $entry =
' ' . $text;
510 $cache[$row->page_title] = $entry;
527 global $wgMaxMsgCacheEntrySize;
530 if ( $this->mDisable ) {
538 $cacheKey =
wfMemcKey(
'messages', $code );
539 $this->
load( $code );
540 $this->
lock( $cacheKey );
544 if ( $text ===
false ) {
545 # Article was deleted
546 $this->mCache[$code][
$title] =
'!NONEXISTENT';
547 $this->mMemc->delete( $titleKey );
548 } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
550 $this->mCache[$code][
$title] =
'!TOO BIG';
551 $this->mMemc->set( $titleKey,
' ' . $text, $this->mExpiry );
553 $this->mCache[$code][
$title] =
' ' . $text;
554 $this->mMemc->delete( $titleKey );
558 $this->
saveToCaches( $this->mCache[$code],
'all', $code );
559 $this->
unlock( $cacheKey );
562 $codes =
array( $code );
563 if ( $code ===
'en' ) {
570 foreach ( $codes
as $code ) {
571 $sidebarKey =
wfMemcKey(
'sidebar', $code );
572 $wgMemc->delete( $sidebarKey );
591 if ( !isset(
$cache[
'VERSION'] ) || !isset(
$cache[
'EXPIRY'] ) ) {
615 global $wgUseLocalMessageCache;
617 $cacheKey =
wfMemcKey(
'messages', $code );
619 if ( $dest ===
'all' ) {
625 # Save to local cache
626 if ( $wgUseLocalMessageCache ) {
629 $this->mMemc->set(
wfMemcKey(
'messages', $code,
'hash' ),
$hash );
647 function lock( $key ) {
648 $lockKey = $key .
':lock';
657 # Fail fast if memcached is totally down
660 if ( !$this->mMemc->set(
wfMemcKey(
'test' ),
'test', 1 ) ) {
671 $lockKey = $key .
':lock';
672 $this->mMemc->delete( $lockKey );
710 function get( $key, $useDB =
true, $langcode =
true, $isFullKey =
false ) {
715 if ( is_int( $key ) ) {
719 } elseif ( !is_string( $key ) ) {
721 } elseif ( $key ===
'' ) {
727 $pos = strrpos( $key,
'/' );
728 if ( $isFullKey && $pos !==
false ) {
729 $langcode = substr( $key, $pos + 1 );
730 $key = substr( $key, 0, $pos );
734 $lckey = strtr( $key,
' ',
'_' );
735 if ( ord( $lckey ) < 128 ) {
736 $lckey[0] = strtolower( $lckey[0] );
743 if ( ord( $lckey ) < 128 ) {
744 $uckey = ucfirst( $lckey );
755 !$this->mDisable && $useDB
759 if ( $message ===
false ) {
760 $parts = explode(
'/', $lckey );
764 if ( count( $parts ) == 2 && $parts[1] !==
'' ) {
766 if ( $message ===
null ) {
773 if ( $message !==
false ) {
775 $message = str_replace(
777 # Fix
for trailing whitespace, removed by textarea
779 # Fix
for NBSP, converted to space by firefox
811 $langcode = $lang->getCode();
816 if ( $langcode === $wgLanguageCode ) {
824 if ( $message !==
false ) {
829 $message = $lang->getMessage( $lckey );
830 if ( $message !==
null ) {
834 list( $fallbackChain, $siteFallbackChain ) =
839 foreach ( $fallbackChain
as $code ) {
840 if ( $code === $wgLanguageCode ) {
847 if ( $message !==
false ) {
857 if ( $message !==
false ) {
863 if ( $message !==
null ) {
869 foreach ( $siteFallbackChain
as $code ) {
871 if ( $message ===
false && $code === $wgLanguageCode ) {
876 if ( $message !==
false ) {
899 $this->
load( $code );
900 if ( isset( $this->mCache[$code][
$title] ) ) {
901 $entry = $this->mCache[$code][
$title];
902 if ( substr( $entry, 0, 1 ) ===
' ' ) {
905 return (
string)substr( $entry, 1 );
906 } elseif ( $entry ===
'!NONEXISTENT' ) {
908 } elseif ( $entry ===
'!TOO BIG' ) {
915 if ( $message !==
false ) {
922 # Try the individual message cache
924 $entry = $this->mMemc->get( $titleKey );
926 if ( substr( $entry, 0, 1 ) ===
' ' ) {
927 $this->mCache[$code][
$title] = $entry;
931 return (
string)substr( $entry, 1 );
932 } elseif ( $entry ===
'!NONEXISTENT' ) {
933 $this->mCache[$code][
$title] =
'!NONEXISTENT';
937 # Corrupt/obsolete entry, delete it
938 $this->mMemc->delete( $titleKey );
942 # Try loading it from the database
947 $content = $revision->getContent();
952 __METHOD__ .
": failed to load message page text for {$title} ($code)"
960 $message = $content->getWikitextForTransclusion();
962 if ( $message ===
false || $message ===
null ) {
965 __METHOD__ .
": message content doesn't provide wikitext "
966 .
"(content model: " . $content->getContentHandler() .
")"
971 $this->mCache[$code][
$title] =
' ' . $message;
972 $this->mMemc->set( $titleKey,
' ' . $message, $this->mExpiry );
979 if ( $message ===
false ) {
980 $this->mCache[$code][
$title] =
'!NONEXISTENT';
981 $this->mMemc->set( $titleKey,
'!NONEXISTENT', $this->mExpiry );
994 function transform( $message, $interface =
false, $language =
null,
$title =
null ) {
996 if ( strpos( $message,
'{{' ) ===
false ) {
1000 if ( $this->mInParser ) {
1007 $popts->setInterfaceMessage( $interface );
1008 $popts->setTargetLanguage( $language );
1010 $userlang = $popts->setUserLang( $language );
1011 $this->mInParser =
true;
1012 $message =
$parser->transformMsg( $message, $popts,
$title );
1013 $this->mInParser =
false;
1014 $popts->setUserLang( $userlang );
1025 if ( !$this->mParser && isset(
$wgParser ) ) {
1026 # Do some initialisation so that we don't have to do it twice
1028 # Clone it and store it
1029 $class = $wgParserConf[
'class'];
1030 if ( $class ==
'Parser_DiffTest' ) {
1032 $this->mParser =
new $class( $wgParserConf );
1049 public function parse( $text,
$title =
null, $linestart =
true,
1050 $interface =
false, $language =
null
1052 if ( $this->mInParser ) {
1053 return htmlspecialchars( $text );
1058 $popts->setInterfaceMessage( $interface );
1059 $popts->setTargetLanguage( $language );
1068 # It's not uncommon having a null $wgTitle in scripts. See r80898
1069 # Create a ghost title in such case
1073 $this->mInParser =
true;
1075 $this->mInParser =
false;
1083 $this->mDisable =
true;
1087 $this->mDisable =
false;
1095 foreach ( array_keys( $langs )
as $code ) {
1097 $this->mMemc->delete(
wfMemcKey(
'messages', $code ) );
1098 # Invalidate all local caches
1099 $this->mMemc->delete(
wfMemcKey(
'messages', $code,
'hash' ) );
1101 $this->mLoadedLanguages =
array();
1110 $pieces = explode(
'/', $key );
1111 if ( count( $pieces ) < 2 ) {
1112 return array( $key, $wgLanguageCode );
1115 $lang = array_pop( $pieces );
1117 return array( $key, $wgLanguageCode );
1120 $message = implode(
'/', $pieces );
1122 return array( $message, $lang );
1135 $this->
load( $code );
1136 if ( !isset( $this->mCache[$code] ) ) {
1141 $cache = $this->mCache[$code];
1142 unset(
$cache[
'VERSION'] );
1143 unset(
$cache[
'EXPIRY'] );
const MSG_WAIT_TIMEOUT
Number of times we will try to acquire a lock from Memcached.
$mParserOptions
Message cache has it's own parser which it uses to transform messages.
transform( $message, $interface=false, $language=null, $title=null)
Set options of the Parser.
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
array $mLoadedLanguages
Variable for tracking which variables are already loaded $mLoadedLanguages.
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
getMessageFromFallbackChain( $lang, $lckey, $uckey, $useDB)
Given a language, try and fetch a message from that language, then the fallbacks of that language,...
parse( $text, $title=null, $linestart=true, $interface=false, $language=null)
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
loadFromDB( $code)
Loads cacheable messages from the database.
$mCache
Process local cache of loaded messages that are defined in MediaWiki namespace.
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
static updateMessage( $key)
Update a single message in all message blobs it occurs in.
bool $mInParser
$mInParser
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfProfileIn( $functionname)
Begin profiling of a function.
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
foreach( $res as $row) $serialized
static getRevisionText( $row, $prefix='old_', $wiki=false)
Get revision text associated with an old or archive row $row is usually an object from wfFetchRow(),...
wfGetCache( $inputType)
Get a cache object.
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
namespace and then decline to actually register it RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok in case the handler function wants to provide a converted Content object Note that $result getContentModel() must return $toModel. Handler functions that modify $result should generally return false to further attempts at conversion. 'ContribsPager you ll need to handle error messages
you have access to all of the normal MediaWiki so you can get a DB use the cache
saveToLocal( $serialized, $hash, $code)
Save the cache to a local file.
Class for handling function-scope profiling.
static $instance
Singleton instance.
wfMemcKey()
Get a cache key.
getMsgFromNamespace( $title, $code)
Get a message from the MediaWiki namespace, with caching.
saveToCaches( $cache, $dest, $code=false)
Shortcut to update caches.
wfRestoreWarnings()
Restore error level to previous value.
lock( $key)
Represents a write lock on the messages key.
static fetchLanguageNames( $inLanguage=null, $include='mw')
Get an array of language names, indexed by code.
static getMessageFor( $key, $code)
Get a message for a given language.
do that in ParserLimitReportFormat instead $parser
wfProfileOut( $functionname='missing')
Stop profiling of a function.
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
const MSG_CACHE_VERSION
MediaWiki message cache structure version.
getLocalCache( $hash, $code)
Try to load the cache from a local file.
const MSG_LOCK_TIMEOUT
Memcached timeout when locking a key for a writing operation.
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
replace( $title, $text)
Updates cache as necessary when message page is changed.
load( $code=false)
Loads messages from caches or from database in this order: (1) local message cache (if $wgUseLocalMes...
when a variable name is used in a it is silently declared as a new masking the global
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
processing should stop and the error should be shown to the user * false
static singleton()
Get the signleton instance of this class.
Class for asserting that a callback happens when an dummy object leaves scope.
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
static newFromTitle( $title, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given title.
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
presenting them properly to the user as errors is done by the caller $title
I won t presume to tell you how to I m just describing the methods I chose to use for myself If you do choose to follow these it will probably be easier for you to collaborate with others on the but if you want to contribute without by all means do which work well I also use K &R brace matching style I know that s a religious issue for so if you want to use a style that puts opening braces on the next that s OK too
clear()
Clear all stored messages.
static consume(ScopedCallback &$sc=null)
Trigger a scoped callback and destroy it.
static fetchLanguageName( $code, $inLanguage=null, $include='all')
return false to override stock group addition can be modified try getUserPermissionsErrors userCan checks are continued by internal code can override on output return false to not delete it return false to override the default password checks & $hash
if(PHP_SAPI !='cli') $file
wfGetMessageCacheStorage()
Get the cache object used by the message cache.
Represents a title within MediaWiki.
bool $mDisable
Should mean that database cannot be used, but check $mDisable.
__construct( $memCached, $useDB, $expiry)
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning in
static getFallbacksIncludingSiteLanguage( $code)
Get the ordered list of fallback languages, ending with the fallback language chain for the site lang...
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
getParserOptions()
ParserOptions is lazy initialised.
isCacheExpired( $cache)
Is the given cache array expired due to time passing or a version change?
$mExpiry
Lifetime for cache, used by object caching.
Message cache Performs various MediaWiki namespace-related functions.
const MSG_LOAD_TIMEOUT
Memcached timeout when loading a key.
if(! $wgRequest->checkUrlExtension()) if(! $wgEnableAPI) $wgTitle
getAllMessageKeys( $code)
Get all message keys stored in the message cache for a given language.
static destroyInstance()
Destroy the singleton instance.