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 ) {
165 $filename =
"$wgCacheDirectory/messages-" .
wfWikiID() .
"-$code";
167 # Check file existence
169 $file = fopen( $filename,
'r' );
176 $localHash = fread(
$file, 32 );
177 if (
$hash === $localHash ) {
180 while ( !feof(
$file ) ) {
199 $filename =
"$wgCacheDirectory/messages-" .
wfWikiID() .
"-$code";
203 $file = fopen( $filename,
'w' );
207 wfDebug(
"Unable to open local cache file for writing\n" );
215 chmod( $filename, 0666 );
239 function load( $code =
false ) {
240 global $wgUseLocalMessageCache;
242 if ( !is_string( $code ) ) {
243 # This isn't really nice, so at least make a note about it and try to
245 wfDebug( __METHOD__ .
" called without providing a language code\n" );
249 # Don't do double loading...
250 if ( isset( $this->mLoadedLanguages[$code] ) ) {
254 # 8 lines of code just to say (once) that message cache is disabled
255 if ( $this->mDisable ) {
256 static $shownDisabled =
false;
257 if ( !$shownDisabled ) {
258 wfDebug( __METHOD__ .
": disabled\n" );
259 $shownDisabled =
true;
265 # Loading code starts
268 $staleCache =
false; # a
cache array with expired
data, or
false if none has been loaded
269 $where =
array(); # Debug info, delayed to avoid spamming debug log
too much
273 # Hash of the contents is stored in memcache, to detect if local cache goes
274 # out of date (e.g. due to replace() on some other server)
275 if ( $wgUseLocalMessageCache ) {
278 $hash = $this->mMemc->get(
wfMemcKey(
'messages', $code,
'hash' ) );
282 $where[] =
'local cache is empty or has the wrong hash';
284 $where[] =
'local cache is expired';
287 $where[] =
'got from local cache';
289 $this->mCache[$code] =
$cache;
296 # Try the global cache. If it is empty, try to acquire a lock. If
297 # the lock can't be acquired, wait for the other thread to finish
298 # and then try the global cache a second time.
299 for ( $failedAttempts = 0; $failedAttempts < 2; $failedAttempts++ ) {
301 $cache = $this->mMemc->get( $cacheKey );
303 $where[] =
'global cache is empty';
305 $where[] =
'global cache is expired';
308 $where[] =
'got from global cache';
309 $this->mCache[$code] =
$cache;
317 # Done, no need to retry
321 # We need to call loadFromDB. Limit the concurrency to a single
322 # process. This prevents the site from going down when the cache
324 $statusKey =
wfMemcKey(
'messages', $code,
'status' );
327 # Unlock the status key if there is an exception
329 $statusUnlocker =
new ScopedCallback(
function () use ( $that, $statusKey ) {
330 $that->mMemc->delete( $statusKey );
333 # Now let's regenerate
334 $where[] =
'loading from database';
336 # Lock the cache to prevent conflicting writes
337 # If this lock fails, it doesn't really matter, it just means the
338 # write is potentially non-atomic, e.g. the results of a replace()
340 if ( $this->
lock( $cacheKey ) ) {
341 $mainUnlocker =
new ScopedCallback(
function () use ( $that, $cacheKey ) {
342 $that->unlock( $cacheKey );
345 $mainUnlocker =
null;
346 $where[] =
'could not acquire main lock';
350 $this->mCache[$code] =
$cache;
358 if ( !$saveSuccess ) {
359 # Cache save has failed.
360 # There are two main scenarios where this could be a problem:
362 # - The cache is more than the maximum size (typically
365 # - Memcached has no space remaining in the relevant slab
366 # class. This is unlikely with recent versions of
369 # Either way, if there is a local cache, nothing bad will
370 # happen. If there is no local cache, disabling the message
371 # cache for all requests avoids incurring a loadFromDB()
372 # overhead on every request, and thus saves the wiki from
373 # complete downtime under moderate traffic conditions.
374 if ( !$wgUseLocalMessageCache ) {
375 $this->mMemc->set( $statusKey,
'error', 60 * 5 );
376 $where[] =
'could not save cache, disabled globally for 5 minutes';
378 $where[] =
"could not save global cache";
382 # Load from DB complete, no need to retry
384 } elseif ( $staleCache ) {
385 # Use the stale cache while some other thread constructs the new one
386 $where[] =
'using stale cache';
387 $this->mCache[$code] = $staleCache;
390 } elseif ( $failedAttempts > 0 ) {
391 # Already retried once, still failed, so don't do another lock/unlock cycle
392 # This case will typically be hit if memcached is down, or if
393 # loadFromDB() takes longer than MSG_WAIT_TIMEOUT
394 $where[] =
"could not acquire status key.";
397 $status = $this->mMemc->get( $statusKey );
398 if ( $status ===
'error' ) {
402 # Wait for the other thread to finish, then retry
403 $where[] =
'waited for other thread to complete';
404 $this->
lock( $cacheKey );
405 $this->
unlock( $cacheKey );
412 $where[] =
'loading FAILED - cache is disabled';
413 $this->mDisable =
true;
414 $this->mCache =
false;
415 # This used to throw an exception, but that led to nasty side effects like
416 # the whole wiki being instantly down if the memcached server died
418 # All good, just record the success
419 $this->mLoadedLanguages[$code] =
true;
421 $info = implode(
', ', $where );
422 wfDebug( __METHOD__ .
": Loading $code... $info\n" );
438 global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
444 'page_is_redirect' => 0,
449 if ( $wgAdaptiveMessageCache && $code !== $wgLanguageCode ) {
450 if ( !isset( $this->mCache[$wgLanguageCode] ) ) {
451 $this->
load( $wgLanguageCode );
453 $mostused = array_keys( $this->mCache[$wgLanguageCode] );
454 foreach ( $mostused
as $key =>
$value ) {
455 $mostused[$key] =
"$value/$code";
459 if ( count( $mostused ) ) {
460 $conds[
'page_title'] = $mostused;
461 } elseif ( $code !== $wgLanguageCode ) {
462 $conds[] =
'page_title' .
$dbr->buildLike(
$dbr->anyString(),
'/', $code );
464 # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses
465 # other than language code.
466 $conds[] =
'page_title NOT' .
$dbr->buildLike(
$dbr->anyString(),
'/',
$dbr->anyString() );
469 # Conditions to fetch oversized pages to ignore them
471 $bigConds[] =
'page_len > ' . intval( $wgMaxMsgCacheEntrySize );
473 # Load titles for all oversized pages in the MediaWiki namespace
474 $res =
$dbr->select(
'page',
'page_title', $bigConds, __METHOD__ .
"($code)-big" );
475 foreach (
$res as $row ) {
476 $cache[$row->page_title] =
'!TOO BIG';
479 # Conditions to load the remaining pages with their contents
480 $smallConds = $conds;
481 $smallConds[] =
'page_latest=rev_id';
482 $smallConds[] =
'rev_text_id=old_id';
483 $smallConds[] =
'page_len <= ' . intval( $wgMaxMsgCacheEntrySize );
486 array(
'page',
'revision',
'text' ),
487 array(
'page_title',
'old_text',
'old_flags' ),
489 __METHOD__ .
"($code)-small"
492 foreach (
$res as $row ) {
494 if ( $text ===
false ) {
501 .
": failed to load message page text for {$row->page_title} ($code)"
504 $entry =
' ' . $text;
506 $cache[$row->page_title] = $entry;
523 global $wgMaxMsgCacheEntrySize;
526 if ( $this->mDisable ) {
534 $cacheKey =
wfMemcKey(
'messages', $code );
535 $this->
load( $code );
536 $this->
lock( $cacheKey );
540 if ( $text ===
false ) {
541 # Article was deleted
542 $this->mCache[$code][
$title] =
'!NONEXISTENT';
543 $this->mMemc->delete( $titleKey );
544 } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
546 $this->mCache[$code][
$title] =
'!TOO BIG';
547 $this->mMemc->set( $titleKey,
' ' . $text, $this->mExpiry );
549 $this->mCache[$code][
$title] =
' ' . $text;
550 $this->mMemc->delete( $titleKey );
554 $this->
saveToCaches( $this->mCache[$code],
'all', $code );
555 $this->
unlock( $cacheKey );
558 $codes =
array( $code );
559 if ( $code ===
'en' ) {
566 foreach ( $codes
as $code ) {
567 $sidebarKey =
wfMemcKey(
'sidebar', $code );
568 $wgMemc->delete( $sidebarKey );
587 if ( !isset(
$cache[
'VERSION'] ) || !isset(
$cache[
'EXPIRY'] ) ) {
611 global $wgUseLocalMessageCache;
613 $cacheKey =
wfMemcKey(
'messages', $code );
615 if ( $dest ===
'all' ) {
621 # Save to local cache
622 if ( $wgUseLocalMessageCache ) {
625 $this->mMemc->set(
wfMemcKey(
'messages', $code,
'hash' ),
$hash );
643 function lock( $key ) {
644 $lockKey = $key .
':lock';
653 # Fail fast if memcached is totally down
656 if ( !$this->mMemc->set(
wfMemcKey(
'test' ),
'test', 1 ) ) {
667 $lockKey = $key .
':lock';
668 $this->mMemc->delete( $lockKey );
706 function get( $key, $useDB =
true, $langcode =
true, $isFullKey =
false ) {
711 if ( is_int( $key ) ) {
715 } elseif ( !is_string( $key ) ) {
717 } elseif ( $key ===
'' ) {
723 $pos = strrpos( $key,
'/' );
724 if ( $isFullKey && $pos !==
false ) {
725 $langcode = substr( $key, $pos + 1 );
726 $key = substr( $key, 0, $pos );
730 $lckey = strtr( $key,
' ',
'_' );
731 if ( ord( $lckey ) < 128 ) {
732 $lckey[0] = strtolower( $lckey[0] );
739 if ( ord( $lckey ) < 128 ) {
740 $uckey = ucfirst( $lckey );
751 !$this->mDisable && $useDB
755 if ( $message ===
false ) {
756 $parts = explode(
'/', $lckey );
760 if ( count( $parts ) == 2 && $parts[1] !==
'' ) {
762 if ( $message ===
null ) {
769 if ( $message !==
false ) {
771 $message = str_replace(
773 # Fix
for trailing whitespace, removed by textarea
775 # Fix
for NBSP, converted to space by firefox
807 $langcode = $lang->getCode();
812 if ( $langcode === $wgLanguageCode ) {
820 if ( $message !==
false ) {
825 $message = $lang->getMessage( $lckey );
826 if ( $message !==
null ) {
830 list( $fallbackChain, $siteFallbackChain ) =
835 foreach ( $fallbackChain
as $code ) {
836 if ( $code === $wgLanguageCode ) {
843 if ( $message !==
false ) {
853 if ( $message !==
false ) {
859 if ( $message !==
null ) {
865 foreach ( $siteFallbackChain
as $code ) {
867 if ( $message ===
false && $code === $wgLanguageCode ) {
872 if ( $message !==
false ) {
895 $this->
load( $code );
896 if ( isset( $this->mCache[$code][
$title] ) ) {
897 $entry = $this->mCache[$code][
$title];
898 if ( substr( $entry, 0, 1 ) ===
' ' ) {
901 return (
string)substr( $entry, 1 );
902 } elseif ( $entry ===
'!NONEXISTENT' ) {
904 } elseif ( $entry ===
'!TOO BIG' ) {
911 if ( $message !==
false ) {
918 # Try the individual message cache
920 $entry = $this->mMemc->get( $titleKey );
922 if ( substr( $entry, 0, 1 ) ===
' ' ) {
923 $this->mCache[$code][
$title] = $entry;
927 return (
string)substr( $entry, 1 );
928 } elseif ( $entry ===
'!NONEXISTENT' ) {
929 $this->mCache[$code][
$title] =
'!NONEXISTENT';
933 # Corrupt/obsolete entry, delete it
934 $this->mMemc->delete( $titleKey );
938 # Try loading it from the database
943 $content = $revision->getContent();
948 __METHOD__ .
": failed to load message page text for {$title} ($code)"
956 $message = $content->getWikitextForTransclusion();
958 if ( $message ===
false || $message ===
null ) {
961 __METHOD__ .
": message content doesn't provide wikitext "
962 .
"(content model: " . $content->getContentHandler() .
")"
967 $this->mCache[$code][
$title] =
' ' . $message;
968 $this->mMemc->set( $titleKey,
' ' . $message, $this->mExpiry );
975 if ( $message ===
false ) {
976 $this->mCache[$code][
$title] =
'!NONEXISTENT';
977 $this->mMemc->set( $titleKey,
'!NONEXISTENT', $this->mExpiry );
990 function transform( $message, $interface =
false, $language =
null,
$title =
null ) {
992 if ( strpos( $message,
'{{' ) ===
false ) {
996 if ( $this->mInParser ) {
1003 $popts->setInterfaceMessage( $interface );
1004 $popts->setTargetLanguage( $language );
1006 $userlang = $popts->setUserLang( $language );
1007 $this->mInParser =
true;
1008 $message =
$parser->transformMsg( $message, $popts,
$title );
1009 $this->mInParser =
false;
1010 $popts->setUserLang( $userlang );
1021 if ( !$this->mParser && isset(
$wgParser ) ) {
1022 # Do some initialisation so that we don't have to do it twice
1024 # Clone it and store it
1025 $class = $wgParserConf[
'class'];
1026 if ( $class ==
'Parser_DiffTest' ) {
1028 $this->mParser =
new $class( $wgParserConf );
1045 public function parse( $text,
$title =
null, $linestart =
true,
1046 $interface =
false, $language =
null
1048 if ( $this->mInParser ) {
1049 return htmlspecialchars( $text );
1054 $popts->setInterfaceMessage( $interface );
1055 $popts->setTargetLanguage( $language );
1064 # It's not uncommon having a null $wgTitle in scripts. See r80898
1065 # Create a ghost title in such case
1069 $this->mInParser =
true;
1071 $this->mInParser =
false;
1079 $this->mDisable =
true;
1083 $this->mDisable =
false;
1091 foreach ( array_keys( $langs )
as $code ) {
1093 $this->mMemc->delete(
wfMemcKey(
'messages', $code ) );
1094 # Invalidate all local caches
1095 $this->mMemc->delete(
wfMemcKey(
'messages', $code,
'hash' ) );
1097 $this->mLoadedLanguages =
array();
1106 $pieces = explode(
'/', $key );
1107 if ( count( $pieces ) < 2 ) {
1108 return array( $key, $wgLanguageCode );
1111 $lang = array_pop( $pieces );
1113 return array( $key, $wgLanguageCode );
1116 $message = implode(
'/', $pieces );
1118 return array( $message, $lang );
1131 $this->
load( $code );
1132 if ( !isset( $this->mCache[$code] ) ) {
1137 $cache = $this->mCache[$code];
1138 unset(
$cache[
'VERSION'] );
1139 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.