34 use Psr\Log\LoggerAwareInterface;
35 use Psr\Log\LoggerInterface;
37 use Wikimedia\RequestTimeout\TimeoutException;
38 use Wikimedia\ScopedCallback;
44 define(
'MSG_CACHE_VERSION', 2 );
69 private const WAN_TTL = IExpiringStore::TTL_DAY;
142 $lckey = strtr( $key,
' ',
'_' );
143 if ( $lckey ===
'' ) {
148 if ( ord( $lckey ) < 128 ) {
149 $lckey[0] = strtolower( $lckey[0] );
151 $lckey = MediaWikiServices::getInstance()->getContentLanguage()->lcfirst( $lckey );
190 $this->srvCache = $serverCache;
199 $this->hookRunner =
new HookRunner( $hookContainer );
202 $this->cache =
new MapCacheLRU( self::MAX_REQUEST_LANGUAGES );
204 $this->mDisable = !( $options[
'useDB'] ??
true );
217 if ( !$this->mParserOptions ) {
219 $user = $context->getUser();
220 if ( !$user->isSafeToLoad() ) {
225 $po->setAllowUnsafeRawHtml(
false );
233 $this->mParserOptions->setAllowUnsafeRawHtml(
false );
246 $cacheKey = $this->srvCache->makeKey( __CLASS__, $code );
248 return $this->srvCache->get( $cacheKey );
258 $cacheKey = $this->srvCache->makeKey( __CLASS__, $code );
259 $this->srvCache->set( $cacheKey,
$cache );
283 protected function load( $code, $mode =
null ) {
284 if ( !is_string( $code ) ) {
285 throw new InvalidArgumentException(
"Missing language code" );
288 # Don't do double loading...
293 # 8 lines of code just to say (once) that message cache is disabled
294 if ( $this->mDisable ) {
295 static $shownDisabled =
false;
296 if ( !$shownDisabled ) {
297 $this->logger->debug( __METHOD__ .
': disabled' );
298 $shownDisabled =
true;
304 # Loading code starts
305 $success =
false; # Keep track of success
306 $staleCache =
false; # a cache array with expired data, or
false if none has been loaded
307 $where = []; # Debug info, delayed to avoid spamming debug log too much
309 # Hash of the contents is stored in memcache, to detect if data-center cache
310 # or local cache goes out of date (e.g. due to replace() on some other server)
312 $this->cacheVolatile[$code] = $hashVolatile;
314 # Try the local cache and check against the cluster hash key...
317 $where[] =
'local cache is empty';
318 } elseif ( !isset(
$cache[
'HASH'] ) ||
$cache[
'HASH'] !== $hash ) {
319 $where[] =
'local cache has the wrong hash';
322 $where[] =
'local cache is expired';
324 } elseif ( $hashVolatile ) {
325 $where[] =
'local cache validation key is expired/volatile';
328 $where[] =
'got from local cache';
334 $cacheKey = $this->clusterCache->makeKey(
'messages', $code );
335 # Try the global cache. If it is empty, try to acquire a lock. If
336 # the lock can't be acquired, wait for the other thread to finish
337 # and then try the global cache a second time.
338 for ( $failedAttempts = 0; $failedAttempts <= 1; $failedAttempts++ ) {
339 if ( $hashVolatile && $staleCache ) {
340 # Do not bother fetching the whole cache blob to avoid I/O.
341 # Instead, just try to get the non-blocking $statusKey lock
342 # below, and use the local stale value if it was not acquired.
343 $where[] =
'global cache is presumed expired';
345 $cache = $this->clusterCache->
get( $cacheKey );
347 $where[] =
'global cache is empty';
349 $where[] =
'global cache is expired';
351 } elseif ( $hashVolatile ) {
352 # DB results are replica DB lag prone until the holdoff TTL passes.
353 # By then, updates should be reflected in loadFromDBWithLock().
354 # One thread regenerates the cache while others use old values.
355 $where[] =
'global cache is expired/volatile';
358 $where[] =
'got from global cache';
366 # Done, no need to retry
370 # We need to call loadFromDB. Limit the concurrency to one process.
371 # This prevents the site from going down when the cache expires.
372 # Note that the DB slam protection lock here is non-blocking.
374 if ( $loadStatus ===
true ) {
377 } elseif ( $staleCache ) {
378 # Use the stale cache while some other thread constructs the new one
379 $where[] =
'using stale cache';
380 $this->cache->set( $code, $staleCache );
383 } elseif ( $failedAttempts > 0 ) {
384 # Already blocked once, so avoid another lock/unlock cycle.
385 # This case will typically be hit if memcached is down, or if
386 # loadFromDB() takes longer than LOCK_WAIT.
387 $where[] =
"could not acquire status key.";
389 } elseif ( $loadStatus ===
'cantacquire' ) {
390 # Wait for the other thread to finish, then retry. Normally,
391 # the memcached get() will then yield the other thread's result.
392 $where[] =
'waited for other thread to complete';
395 # Disable cache; $loadStatus is 'disabled'
402 $where[] =
'loading FAILED - cache is disabled';
403 $this->mDisable =
true;
404 $this->cache->set( $code, [] );
405 $this->logger->error( __METHOD__ .
": Failed to load $code" );
406 # This used to throw an exception, but that led to nasty side effects like
407 # the whole wiki being instantly down if the memcached server died
411 throw new LogicException(
"Process cache for '$code' should be set by now." );
414 $info = implode(
', ', $where );
415 $this->logger->debug( __METHOD__ .
": Loading $code... $info" );
427 # If cache updates on all levels fail, give up on message overrides.
428 # This is to avoid easy site outages; see $saveSuccess comments below.
429 $statusKey = $this->clusterCache->makeKey(
'messages', $code,
'status' );
430 $status = $this->clusterCache->get( $statusKey );
431 if ( $status ===
'error' ) {
432 $where[] =
"could not load; method is still globally disabled";
436 # Now let's regenerate
437 $where[] =
'loading from database';
439 # Lock the cache to prevent conflicting writes.
440 # This lock is non-blocking so stale cache can quickly be used.
441 # Note that load() will call a blocking getReentrantScopedLock()
442 # after this if it really need to wait for any current thread.
443 $cacheKey = $this->clusterCache->makeKey(
'messages', $code );
445 if ( !$scopedLock ) {
446 $where[] =
'could not acquire main lock';
447 return 'cantacquire';
451 $this->cache->set( $code,
$cache );
454 if ( !$saveSuccess ) {
469 $this->clusterCache->set( $statusKey,
'error', 60 * 5 );
470 $where[] =
'could not save cache, disabled globally for 5 minutes';
472 $where[] =
"could not save global cache";
489 $maxMsgCacheEntrySize = MediaWikiServices::getInstance()->getMainConfig()
490 ->get( MainConfigNames::MaxMsgCacheEntrySize );
491 $adaptiveMessageCache = MediaWikiServices::getInstance()->getMainConfig()
492 ->get( MainConfigNames::AdaptiveMessageCache );
502 if ( $adaptiveMessageCache && $code !== $this->contLangCode ) {
503 if ( !$this->cache->has( $this->contLangCode ) ) {
504 $this->
load( $this->contLangCode );
506 $mostused = array_keys( $this->cache->get( $this->contLangCode ) );
507 foreach ( $mostused as $key => $value ) {
508 $mostused[$key] =
"$value/$code";
514 'page_is_redirect' => 0,
517 if ( count( $mostused ) ) {
518 $conds[
'page_title'] = $mostused;
519 } elseif ( $code !== $this->contLangCode ) {
520 $conds[] =
'page_title' .
$dbr->buildLike(
$dbr->anyString(),
'/', $code );
522 # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses
523 # other than language code.
524 $conds[] =
'page_title NOT' .
525 $dbr->buildLike(
$dbr->anyString(),
'/',
$dbr->anyString() );
531 [
'page_title',
'page_latest' ],
532 array_merge( $conds, [
'page_len > ' . intval( $maxMsgCacheEntrySize ) ] ),
533 __METHOD__ .
"($code)-big"
535 foreach (
$res as $row ) {
537 if ( $adaptiveMessageCache || $this->
isMainCacheable( $row->page_title )
539 $cache[$row->page_title] =
'!TOO BIG';
542 $cache[
'EXCESSIVE'][$row->page_title] = $row->page_latest;
547 $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
549 $revQuery = $revisionStore->getQueryInfo( [
'page' ] );
562 array_diff(
$revQuery[
'tables'], [
'page' ] )
568 array_merge( $conds, [
569 'page_len <= ' . intval( $maxMsgCacheEntrySize ),
570 'page_latest = rev_id'
572 __METHOD__ .
"($code)-small",
576 $result = $revisionStore->newRevisionsFromBatch(
$res, [
577 'slots' => [ SlotRecord::MAIN ],
580 $revisions = $result->isOK() ? $result->getValue() : [];
581 foreach (
$res as $row ) {
583 if ( $adaptiveMessageCache || $this->
isMainCacheable( $row->page_title )
586 $rev = $revisions[$row->rev_id] ??
null;
587 $content = $rev ? $rev->getContent( SlotRecord::MAIN ) :
null;
589 }
catch ( TimeoutException $e ) {
591 }
catch ( Exception $ex ) {
595 if ( !is_string( $text ) ) {
597 $this->logger->error(
599 .
": failed to load message page text for {$row->page_title} ($code)"
602 $entry =
' ' . $text;
604 $cache[$row->page_title] = $entry;
608 $cache[
'EXCESSIVE'][$row->page_title] = $row->page_latest;
615 # Hash for validating local cache (APC). No need to take into account
616 # messages larger than $wgMaxMsgCacheEntrySize, since those are only
617 # stored and fetched from memcache.
620 unset(
$cache[
'EXCESSIVE'] );
638 return $this->cache->hasField(
$lang,
'VERSION' );
654 $name = $this->contLang->lcfirst( $name );
657 if ( strpos( $name,
'conversiontable/' ) === 0 ) {
660 $msg = preg_replace(
'/\/[a-z0-9-]{2,}$/',
'', $name );
662 if ( $code ===
null ) {
664 if ( $this->systemMessageNames ===
null ) {
665 $this->systemMessageNames = array_fill_keys(
666 $this->localisationCache->getSubitemList( $this->contLangCode,
'messages' ),
669 return isset( $this->systemMessageNames[$msg] );
672 return $this->localisationCache->getSubitem( $code,
'messages', $msg ) !==
null;
683 if ( $this->mDisable ) {
688 if ( strpos(
$title,
'/' ) !==
false && $code === $this->contLangCode ) {
694 if ( $text ===
false ) {
696 $this->cache->setField( $code,
$title,
'!NONEXISTENT' );
699 $this->cache->setField( $code,
$title,
' ' . $text );
705 DeferredUpdates::PRESEND
715 $maxMsgCacheEntrySize = MediaWikiServices::getInstance()->getMainConfig()
716 ->get( MainConfigNames::MaxMsgCacheEntrySize );
720 $this->clusterCache->makeKey(
'messages', $code )
722 if ( !$scopedLock ) {
723 foreach ( $replacements as list(
$title ) ) {
724 $this->logger->error(
725 __METHOD__ .
': could not acquire lock to update {title} ({code})',
726 [
'title' =>
$title,
'code' => $code ] );
734 if ( $this->
load( $code, self::FOR_UPDATE ) ) {
741 $newTextByTitle = [];
745 $wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
746 foreach ( $replacements as list(
$title ) ) {
748 $page->loadPageData( $page::READ_LATEST );
751 $newTextByTitle[
$title] = $text ??
'';
753 if ( !is_string( $text ) ) {
755 } elseif ( strlen( $text ) > $maxMsgCacheEntrySize ) {
757 $newBigTitles[
$title] = $page->getLatest();
768 foreach ( $newBigTitles as
$title => $id ) {
770 $this->wanCache->set(
772 ' ' . $newTextByTitle[
$title],
778 $cache[
'LATEST'] = time();
780 $this->cache->set( $code,
$cache );
787 ScopedCallback::consume( $scopedLock );
791 $this->wanCache->touchCheckKey( $this->
getCheckKey( $code ) );
794 $blobStore = MediaWikiServices::getInstance()->getResourceLoader()->getMessageBlobStore();
795 foreach ( $replacements as list(
$title, $msg ) ) {
796 $blobStore->updateMessage( $this->contLang->lcfirst( $msg ) );
797 $this->hookRunner->onMessageCacheReplace(
$title, $newTextByTitle[
$title] );
808 if ( !isset(
$cache[
'VERSION'] ) || !isset(
$cache[
'EXPIRY'] ) ) {
831 if ( $dest ===
'all' ) {
832 $cacheKey = $this->clusterCache->makeKey(
'messages', $code );
852 $value = $this->wanCache->get(
853 $this->wanCache->makeKey(
'messages', $code,
'hash',
'v1' ),
855 [ $this->getCheckKey( $code ) ]
859 $hash = $value[
'hash'];
860 if ( ( time() - $value[
'latest'] ) < WANObjectCache::TTL_MINUTE ) {
867 $expired = ( $curTTL < 0 );
875 return [ $hash, $expired ];
889 $this->wanCache->set(
890 $this->wanCache->makeKey(
'messages', $code,
'hash',
'v1' ),
893 'latest' =>
$cache[
'LATEST'] ?? 0
895 WANObjectCache::TTL_INDEFINITE
905 return $this->clusterCache->getScopedLock( $key, $timeout, self::LOCK_TTL, __METHOD__ );
941 public function get( $key, $useDB =
true, $langcode =
true ) {
942 if ( is_int( $key ) ) {
946 } elseif ( !is_string( $key ) ) {
948 } elseif ( $key ===
'' ) {
956 $this->hookRunner->onMessageCache__get( $lckey );
962 !$this->mDisable && $useDB
966 if ( $message ===
false ) {
967 $parts = explode(
'/', $lckey );
971 if ( count( $parts ) == 2 && $parts[1] !==
'' ) {
972 $message = $this->localisationCache->getSubitem( $parts[1],
'messages', $parts[0] );
973 if ( $message ===
null ) {
980 if ( $message !==
false ) {
982 $message = str_replace(
984 # Fix
for trailing whitespace, removed by textarea
986 # Fix
for NBSP, converted to space by firefox
1021 if ( $message !==
false ) {
1026 $message = $this->
getMessageForLang( $this->contLang, $lckey, $useDB, $alreadyTried );
1041 $langcode =
$lang->getCode();
1045 $uckey = $this->contLang->ucfirst( $lckey );
1047 if ( !isset( $alreadyTried[$langcode] ) ) {
1052 if ( $message !==
false ) {
1055 $alreadyTried[$langcode] =
true;
1062 $message =
$lang->getMessage( $lckey );
1063 if ( $message !==
null ) {
1069 $fallbackChain = $this->languageFallback->getAll( $langcode );
1071 foreach ( $fallbackChain as $code ) {
1072 if ( isset( $alreadyTried[$code] ) ) {
1080 if ( $message !==
false ) {
1083 $alreadyTried[$code] =
true;
1098 if ( $langcode === $this->contLangCode ) {
1102 return "$uckey/$langcode";
1122 $this->
load( $code );
1124 $entry = $this->cache->getField( $code,
$title );
1126 if ( $entry !==
null ) {
1128 if ( substr( $entry, 0, 1 ) ===
' ' ) {
1130 return (
string)substr( $entry, 1 );
1131 } elseif ( $entry ===
'!NONEXISTENT' ) {
1140 $this->cache->getField( $code,
'HASH' )
1151 $this->cache->getField( $code,
'HASH' )
1154 if ( $entry ===
null || substr( $entry, 0, 1 ) !==
' ' ) {
1158 $this->hookRunner->onMessagesPreLoad(
$title, $message, $code );
1159 if ( $message !==
false ) {
1160 $this->cache->setField( $code,
$title,
' ' . $message );
1162 $this->cache->setField( $code,
$title,
'!NONEXISTENT' );
1169 if ( $entry !==
false && substr( $entry, 0, 1 ) ===
' ' ) {
1170 if ( $this->cacheVolatile[$code] ) {
1172 $this->logger->debug(
1173 __METHOD__ .
': loading volatile key \'{titleKey}\'',
1174 [
'titleKey' =>
$title,
'code' => $code ] );
1176 $this->cache->setField( $code,
$title, $entry );
1179 return (
string)substr( $entry, 1 );
1182 $this->cache->setField( $code,
$title,
'!NONEXISTENT' );
1194 $fname = __METHOD__;
1195 return $this->srvCache->getWithSetCallback(
1196 $this->srvCache->makeKey(
'messages-big', $hash, $dbKey ),
1197 BagOStuff::TTL_HOUR,
1198 function () use ( $code, $dbKey, $hash, $fname ) {
1199 return $this->wanCache->getWithSetCallback(
1202 function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code, $fname ) {
1209 $revision = MediaWikiServices::getInstance()
1210 ->getRevisionLookup()
1211 ->getKnownCurrentRevision(
$title );
1215 return '!NONEXISTENT';
1217 $content = $revision->getContent( SlotRecord::MAIN );
1221 $this->logger->warning(
1222 $fname .
': failed to load page text for \'{titleKey}\'',
1223 [
'titleKey' => $dbKey,
'code' => $code ]
1228 if ( !is_string( $message ) ) {
1233 return '!NONEXISTENT';
1236 return ' ' . $message;
1252 if ( strpos( $message,
'{{' ) ===
false ) {
1256 if ( $this->mInParser ) {
1263 $popts->setInterfaceMessage( $interface );
1264 $popts->setTargetLanguage( $language );
1266 $userlang = $popts->setUserLang( $language );
1267 $this->mInParser =
true;
1268 $message = $parser->transformMsg( $message, $popts, $page );
1269 $this->mInParser =
false;
1270 $popts->setUserLang( $userlang );
1280 if ( !$this->mParser ) {
1281 $parser = MediaWikiServices::getInstance()->getParser();
1282 # Clone it and store it
1283 $this->mParser = clone $parser;
1298 $interface =
false, $language =
null
1302 if ( $this->mInParser ) {
1303 return htmlspecialchars( $text );
1308 $popts->setInterfaceMessage( $interface );
1310 if ( is_string( $language ) ) {
1311 $language = $this->langFactory->getLanguage( $language );
1313 $popts->setTargetLanguage( $language );
1316 $logger = LoggerFactory::getInstance(
'GlobalTitleFail' );
1318 __METHOD__ .
' called with no title set.',
1319 [
'exception' =>
new Exception ]
1325 # It's not uncommon having a null $wgTitle in scripts. See r80898
1326 # Create a ghost title in such case
1327 $page = PageReferenceValue::localReference(
1329 'Badtitle/title not set in ' . __METHOD__
1333 $this->mInParser =
true;
1334 $res = $parser->parse( $text, $page, $popts, $linestart );
1335 $this->mInParser =
false;
1341 $this->mDisable =
true;
1345 $this->mDisable =
false;
1370 $langs = $this->languageNameUtils->getLanguageNames();
1371 foreach ( array_keys( $langs ) as $code ) {
1372 $this->wanCache->touchCheckKey( $this->
getCheckKey( $code ) );
1374 $this->cache->clear();
1382 $pieces = explode(
'/', $key );
1383 if ( count( $pieces ) < 2 ) {
1387 $lang = array_pop( $pieces );
1388 if ( !$this->languageNameUtils->getLanguageName(
1390 LanguageNameUtils::AUTONYMS,
1391 LanguageNameUtils::DEFINED
1396 $message = implode(
'/', $pieces );
1398 return [ $message,
$lang ];
1410 $this->
load( $code );
1411 if ( !$this->cache->has( $code ) ) {
1417 unset(
$cache[
'VERSION'] );
1418 unset(
$cache[
'EXPIRY'] );
1419 unset(
$cache[
'EXCESSIVE'] );
1424 return array_map( [ $this->contLang,
'lcfirst' ], array_keys(
$cache ) );
1436 if ( $msgText ===
null ) {
1442 if ( $this->contLangConverter->hasVariants() ) {
1443 $this->contLangConverter->updateConversionTable( $linkTarget );
1452 return $this->wanCache->makeKey(
'messages', $code );
1467 $msgText =
$content->getWikitextForTransclusion();
1468 if ( $msgText ===
false || $msgText ===
null ) {
1471 $this->logger->warning(
1472 __METHOD__ .
": message content doesn't provide wikitext "
1473 .
"(content model: " .
$content->getModel() .
")" );
1489 return $this->wanCache->makeKey(
'messages-big', $hash,
$title );
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
const MSG_CACHE_VERSION
MediaWiki message cache structure version.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgTitle
Class representing a cache/ephemeral data store.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the pending update queue for execution at the appropriate time.
A BagOStuff object with no objects in it.
Base class for language-specific code.
getCode()
Get the internal language code for this language object.
Caching for the contents of localisation files.
Handles a simple LRU key/value map with a maximum number of entries.
set( $key, $value, $rank=self::RANK_TOP)
Set a key/value pair.
get( $key, $maxAge=INF, $default=null)
Get the value for a key.
A class containing constants representing the names of configuration variables.
Message cache purging and in-place update handler for specific message page changes.
Cache messages that are defined by MediaWiki-namespace pages or by hooks.
getValidationHash( $code)
Get the md5 used to validate the local APC cache.
isLanguageLoaded( $lang)
Whether the language was loaded and its data is still in the process cache.
loadFromDBWithLock( $code, array &$where, $mode=null)
const LOCK_TTL
How long memcached locks last.
loadFromDB( $code, $mode=null)
Loads cacheable messages from the database.
getMessageForLang( $lang, $lckey, $useDB, &$alreadyTried)
Given a language, try and fetch messages from that language and its fallbacks.
saveToLocalCache( $code, $cache)
Save the cache to APC.
LanguageFactory $langFactory
getMessagePageName( $langcode, $uckey)
Get the message page name for a given language.
const MAX_REQUEST_LANGUAGES
The size of the MapCacheLRU which stores message data.
saveToCaches(array $cache, $dest, $code=false)
Shortcut to update caches.
refreshAndReplaceInternal( $code, array $replacements)
setValidationHash( $code, array $cache)
Set the md5 used to validate the local disk cache.
isCacheExpired( $cache)
Is the given cache array expired due to time passing or a version change?
getMsgFromNamespace( $title, $code)
Get a message from the MediaWiki namespace, with caching.
getReentrantScopedLock( $key, $timeout=self::WAIT_SEC)
const WAIT_SEC
How long to wait for memcached locks.
bool $mDisable
Should mean that database cannot be used, but check.
getLocalCache( $code)
Try to load the cache from APC.
parse( $text, PageReference $page=null, $linestart=true, $interface=false, $language=null)
transform( $message, $interface=false, $language=null, PageReference $page=null)
LocalisationCache $localisationCache
LanguageNameUtils $languageNameUtils
isMainCacheable( $name, $code=null)
Can the given DB key be added to the main cache blob? To reduce the impact of abuse of the MediaWiki ...
LanguageFallback $languageFallback
load( $code, $mode=null)
Loads messages from caches or from database in this order: (1) local message cache (if $wgUseLocalMes...
__construct(WANObjectCache $wanCache, BagOStuff $clusterCache, BagOStuff $serverCache, Language $contLang, LanguageConverterFactory $langConverterFactory, LoggerInterface $logger, array $options, LanguageFactory $langFactory, LocalisationCache $localisationCache, LanguageNameUtils $languageNameUtils, LanguageFallback $languageFallback, HookContainer $hookContainer)
updateMessageOverride(LinkTarget $linkTarget, Content $content=null)
Purge message caches when a MediaWiki: page is created, updated, or deleted.
getMessageFromFallbackChain( $lang, $lckey, $useDB)
Given a language, try and fetch messages from that language.
isDisabled()
Whether DB/cache usage is disabled for determining messages.
setLogger(LoggerInterface $logger)
loadCachedMessagePageEntry( $dbKey, $code, $hash)
getMessageTextFromContent(Content $content=null)
clear()
Clear all stored messages in global and local cache.
getAllMessageKeys( $code)
Get all message keys stored in the message cache for a given language.
MapCacheLRU $cache
Process cache of loaded messages that are defined in MediaWiki namespace.
static normalizeKey( $key)
Normalize message key input.
bool[] $cacheVolatile
Map of (language code => boolean)
array $systemMessageNames
Map of (lowercase message key => unused) for all software defined messages.
ParserOptions $mParserOptions
Message cache has its own parser which it uses to transform messages.
ILanguageConverter $contLangConverter
replace( $title, $text)
Updates cache as necessary when message page is changed.
getParserOptions()
ParserOptions is lazy initialised.
bigMessageCacheKey( $hash, $title)
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
static newFromAnon()
Get a ParserOptions object for an anonymous user.
static getMain()
Get the RequestContext object associated with the main request.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Multi-datacenter aware caching interface.
Base interface for content objects.
if(!isset( $args[0])) $lang