MediaWiki
REL1_20
|
00001 <?php 00027 define( 'MSG_LOAD_TIMEOUT', 60 ); 00028 define( 'MSG_LOCK_TIMEOUT', 10 ); 00029 define( 'MSG_WAIT_TIMEOUT', 10 ); 00030 define( 'MSG_CACHE_VERSION', 1 ); 00031 00037 class MessageCache { 00045 protected $mCache; 00046 00047 // Should mean that database cannot be used, but check 00048 protected $mDisable; 00049 00051 protected $mExpiry; 00052 00057 protected $mParserOptions, $mParser; 00058 00060 protected $mLoadedLanguages = array(); 00061 00065 protected $mRequestedMessages = array(); 00066 00072 protected static $mAdaptiveDataAge = 604800; // Is 7*24*3600 00073 00080 protected static $mAdaptiveInclusionThreshold = 0.05; 00081 00087 private static $instance; 00088 00092 protected $mInParser = false; 00093 00100 public static function singleton() { 00101 if ( is_null( self::$instance ) ) { 00102 global $wgUseDatabaseMessages, $wgMsgCacheExpiry; 00103 self::$instance = new self( wfGetMessageCacheStorage(), $wgUseDatabaseMessages, $wgMsgCacheExpiry ); 00104 } 00105 return self::$instance; 00106 } 00107 00113 public static function destroyInstance() { 00114 self::$instance = null; 00115 } 00116 00117 function __construct( $memCached, $useDB, $expiry ) { 00118 if ( !$memCached ) { 00119 $memCached = wfGetCache( CACHE_NONE ); 00120 } 00121 00122 $this->mMemc = $memCached; 00123 $this->mDisable = !$useDB; 00124 $this->mExpiry = $expiry; 00125 } 00126 00132 function getParserOptions() { 00133 if ( !$this->mParserOptions ) { 00134 $this->mParserOptions = new ParserOptions; 00135 $this->mParserOptions->setEditSection( false ); 00136 } 00137 return $this->mParserOptions; 00138 } 00139 00149 function loadFromLocal( $hash, $code ) { 00150 global $wgCacheDirectory, $wgLocalMessageCacheSerialized; 00151 00152 $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; 00153 00154 # Check file existence 00155 wfSuppressWarnings(); 00156 $file = fopen( $filename, 'r' ); 00157 wfRestoreWarnings(); 00158 if ( !$file ) { 00159 return false; // No cache file 00160 } 00161 00162 if ( $wgLocalMessageCacheSerialized ) { 00163 // Check to see if the file has the hash specified 00164 $localHash = fread( $file, 32 ); 00165 if ( $hash === $localHash ) { 00166 // All good, get the rest of it 00167 $serialized = ''; 00168 while ( !feof( $file ) ) { 00169 $serialized .= fread( $file, 100000 ); 00170 } 00171 fclose( $file ); 00172 return $this->setCache( unserialize( $serialized ), $code ); 00173 } else { 00174 fclose( $file ); 00175 return false; // Wrong hash 00176 } 00177 } else { 00178 $localHash = substr( fread( $file, 40 ), 8 ); 00179 fclose( $file ); 00180 if ( $hash != $localHash ) { 00181 return false; // Wrong hash 00182 } 00183 00184 # Require overwrites the member variable or just shadows it? 00185 require( $filename ); 00186 return $this->setCache( $this->mCache, $code ); 00187 } 00188 } 00189 00193 function saveToLocal( $serialized, $hash, $code ) { 00194 global $wgCacheDirectory; 00195 00196 $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; 00197 wfMkdirParents( $wgCacheDirectory, null, __METHOD__ ); // might fail 00198 00199 wfSuppressWarnings(); 00200 $file = fopen( $filename, 'w' ); 00201 wfRestoreWarnings(); 00202 00203 if ( !$file ) { 00204 wfDebug( "Unable to open local cache file for writing\n" ); 00205 return; 00206 } 00207 00208 fwrite( $file, $hash . $serialized ); 00209 fclose( $file ); 00210 wfSuppressWarnings(); 00211 chmod( $filename, 0666 ); 00212 wfRestoreWarnings(); 00213 } 00214 00215 function saveToScript( $array, $hash, $code ) { 00216 global $wgCacheDirectory; 00217 00218 $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; 00219 $tempFilename = $filename . '.tmp'; 00220 wfMkdirParents( $wgCacheDirectory, null, __METHOD__ ); // might fail 00221 00222 wfSuppressWarnings(); 00223 $file = fopen( $tempFilename, 'w' ); 00224 wfRestoreWarnings(); 00225 00226 if ( !$file ) { 00227 wfDebug( "Unable to open local cache file for writing\n" ); 00228 return; 00229 } 00230 00231 fwrite( $file, "<?php\n//$hash\n\n \$this->mCache = array(" ); 00232 00233 foreach ( $array as $key => $message ) { 00234 $key = $this->escapeForScript( $key ); 00235 $message = $this->escapeForScript( $message ); 00236 fwrite( $file, "'$key' => '$message',\n" ); 00237 } 00238 00239 fwrite( $file, ");\n?>" ); 00240 fclose( $file); 00241 rename( $tempFilename, $filename ); 00242 } 00243 00244 function escapeForScript( $string ) { 00245 $string = str_replace( '\\', '\\\\', $string ); 00246 $string = str_replace( '\'', '\\\'', $string ); 00247 return $string; 00248 } 00249 00255 function setCache( $cache, $code ) { 00256 if ( isset( $cache['VERSION'] ) && $cache['VERSION'] == MSG_CACHE_VERSION ) { 00257 $this->mCache[$code] = $cache; 00258 return true; 00259 } else { 00260 return false; 00261 } 00262 } 00263 00283 function load( $code = false ) { 00284 global $wgUseLocalMessageCache; 00285 00286 if( !is_string( $code ) ) { 00287 # This isn't really nice, so at least make a note about it and try to 00288 # fall back 00289 wfDebug( __METHOD__ . " called without providing a language code\n" ); 00290 $code = 'en'; 00291 } 00292 00293 # Don't do double loading... 00294 if ( isset( $this->mLoadedLanguages[$code] ) ) { 00295 return true; 00296 } 00297 00298 # 8 lines of code just to say (once) that message cache is disabled 00299 if ( $this->mDisable ) { 00300 static $shownDisabled = false; 00301 if ( !$shownDisabled ) { 00302 wfDebug( __METHOD__ . ": disabled\n" ); 00303 $shownDisabled = true; 00304 } 00305 return true; 00306 } 00307 00308 # Loading code starts 00309 wfProfileIn( __METHOD__ ); 00310 $success = false; # Keep track of success 00311 $where = array(); # Debug info, delayed to avoid spamming debug log too much 00312 $cacheKey = wfMemcKey( 'messages', $code ); # Key in memc for messages 00313 00314 # (1) local cache 00315 # Hash of the contents is stored in memcache, to detect if local cache goes 00316 # out of date (due to update in other thread?) 00317 if ( $wgUseLocalMessageCache ) { 00318 wfProfileIn( __METHOD__ . '-fromlocal' ); 00319 00320 $hash = $this->mMemc->get( wfMemcKey( 'messages', $code, 'hash' ) ); 00321 if ( $hash ) { 00322 $success = $this->loadFromLocal( $hash, $code ); 00323 if ( $success ) $where[] = 'got from local cache'; 00324 } 00325 wfProfileOut( __METHOD__ . '-fromlocal' ); 00326 } 00327 00328 # (2) memcache 00329 # Fails if nothing in cache, or in the wrong version. 00330 if ( !$success ) { 00331 wfProfileIn( __METHOD__ . '-fromcache' ); 00332 $cache = $this->mMemc->get( $cacheKey ); 00333 $success = $this->setCache( $cache, $code ); 00334 if ( $success ) { 00335 $where[] = 'got from global cache'; 00336 $this->saveToCaches( $cache, false, $code ); 00337 } 00338 wfProfileOut( __METHOD__ . '-fromcache' ); 00339 } 00340 00341 # (3) 00342 # Nothing in caches... so we need create one and store it in caches 00343 if ( !$success ) { 00344 $where[] = 'cache is empty'; 00345 $where[] = 'loading from database'; 00346 00347 $this->lock( $cacheKey ); 00348 00349 # Limit the concurrency of loadFromDB to a single process 00350 # This prevents the site from going down when the cache expires 00351 $statusKey = wfMemcKey( 'messages', $code, 'status' ); 00352 $success = $this->mMemc->add( $statusKey, 'loading', MSG_LOAD_TIMEOUT ); 00353 if ( $success ) { 00354 $cache = $this->loadFromDB( $code ); 00355 $success = $this->setCache( $cache, $code ); 00356 } 00357 if ( $success ) { 00358 $success = $this->saveToCaches( $cache, true, $code ); 00359 if ( $success ) { 00360 $this->mMemc->delete( $statusKey ); 00361 } else { 00362 $this->mMemc->set( $statusKey, 'error', 60 * 5 ); 00363 wfDebug( "MemCached set error in MessageCache: restart memcached server!\n" ); 00364 } 00365 } 00366 $this->unlock($cacheKey); 00367 } 00368 00369 if ( !$success ) { 00370 # Bad luck... this should not happen 00371 $where[] = 'loading FAILED - cache is disabled'; 00372 $info = implode( ', ', $where ); 00373 wfDebug( __METHOD__ . ": Loading $code... $info\n" ); 00374 $this->mDisable = true; 00375 $this->mCache = false; 00376 } else { 00377 # All good, just record the success 00378 $info = implode( ', ', $where ); 00379 wfDebug( __METHOD__ . ": Loading $code... $info\n" ); 00380 $this->mLoadedLanguages[$code] = true; 00381 } 00382 wfProfileOut( __METHOD__ ); 00383 return $success; 00384 } 00385 00394 function loadFromDB( $code ) { 00395 wfProfileIn( __METHOD__ ); 00396 global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache; 00397 $dbr = wfGetDB( DB_SLAVE ); 00398 $cache = array(); 00399 00400 # Common conditions 00401 $conds = array( 00402 'page_is_redirect' => 0, 00403 'page_namespace' => NS_MEDIAWIKI, 00404 ); 00405 00406 $mostused = array(); 00407 if ( $wgAdaptiveMessageCache ) { 00408 $mostused = $this->getMostUsedMessages(); 00409 if ( $code !== $wgLanguageCode ) { 00410 foreach ( $mostused as $key => $value ) { 00411 $mostused[$key] = "$value/$code"; 00412 } 00413 } 00414 } 00415 00416 if ( count( $mostused ) ) { 00417 $conds['page_title'] = $mostused; 00418 } elseif ( $code !== $wgLanguageCode ) { 00419 $conds[] = 'page_title' . $dbr->buildLike( $dbr->anyString(), "/$code" ); 00420 } else { 00421 # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses 00422 # other than language code. 00423 $conds[] = 'page_title NOT' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() ); 00424 } 00425 00426 # Conditions to fetch oversized pages to ignore them 00427 $bigConds = $conds; 00428 $bigConds[] = 'page_len > ' . intval( $wgMaxMsgCacheEntrySize ); 00429 00430 # Load titles for all oversized pages in the MediaWiki namespace 00431 $res = $dbr->select( 'page', 'page_title', $bigConds, __METHOD__ . "($code)-big" ); 00432 foreach ( $res as $row ) { 00433 $cache[$row->page_title] = '!TOO BIG'; 00434 } 00435 00436 # Conditions to load the remaining pages with their contents 00437 $smallConds = $conds; 00438 $smallConds[] = 'page_latest=rev_id'; 00439 $smallConds[] = 'rev_text_id=old_id'; 00440 $smallConds[] = 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize ); 00441 00442 $res = $dbr->select( 00443 array( 'page', 'revision', 'text' ), 00444 array( 'page_title', 'old_text', 'old_flags' ), 00445 $smallConds, 00446 __METHOD__ . "($code)-small" 00447 ); 00448 00449 foreach ( $res as $row ) { 00450 $text = Revision::getRevisionText( $row ); 00451 if( $text === false ) { 00452 // Failed to fetch data; possible ES errors? 00453 // Store a marker to fetch on-demand as a workaround... 00454 $entry = '!TOO BIG'; 00455 wfDebugLog( 'MessageCache', __METHOD__ . ": failed to load message page text for {$row->page_title} ($code)" ); 00456 } else { 00457 $entry = ' ' . $text; 00458 } 00459 $cache[$row->page_title] = $entry; 00460 } 00461 00462 foreach ( $mostused as $key ) { 00463 if ( !isset( $cache[$key] ) ) { 00464 $cache[$key] = '!NONEXISTENT'; 00465 } 00466 } 00467 00468 $cache['VERSION'] = MSG_CACHE_VERSION; 00469 wfProfileOut( __METHOD__ ); 00470 return $cache; 00471 } 00472 00479 public function replace( $title, $text ) { 00480 global $wgMaxMsgCacheEntrySize; 00481 wfProfileIn( __METHOD__ ); 00482 00483 if ( $this->mDisable ) { 00484 wfProfileOut( __METHOD__ ); 00485 return; 00486 } 00487 00488 list( $msg, $code ) = $this->figureMessage( $title ); 00489 00490 $cacheKey = wfMemcKey( 'messages', $code ); 00491 $this->load( $code ); 00492 $this->lock( $cacheKey ); 00493 00494 $titleKey = wfMemcKey( 'messages', 'individual', $title ); 00495 00496 if ( $text === false ) { 00497 # Article was deleted 00498 $this->mCache[$code][$title] = '!NONEXISTENT'; 00499 $this->mMemc->delete( $titleKey ); 00500 } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) { 00501 # Check for size 00502 $this->mCache[$code][$title] = '!TOO BIG'; 00503 $this->mMemc->set( $titleKey, ' ' . $text, $this->mExpiry ); 00504 } else { 00505 $this->mCache[$code][$title] = ' ' . $text; 00506 $this->mMemc->delete( $titleKey ); 00507 } 00508 00509 # Update caches 00510 $this->saveToCaches( $this->mCache[$code], true, $code ); 00511 $this->unlock( $cacheKey ); 00512 00513 // Also delete cached sidebar... just in case it is affected 00514 $codes = array( $code ); 00515 if ( $code === 'en' ) { 00516 // Delete all sidebars, like for example on action=purge on the 00517 // sidebar messages 00518 $codes = array_keys( Language::fetchLanguageNames() ); 00519 } 00520 00521 global $wgMemc; 00522 foreach ( $codes as $code ) { 00523 $sidebarKey = wfMemcKey( 'sidebar', $code ); 00524 $wgMemc->delete( $sidebarKey ); 00525 } 00526 00527 // Update the message in the message blob store 00528 global $wgContLang; 00529 MessageBlobStore::updateMessage( $wgContLang->lcfirst( $msg ) ); 00530 00531 wfRunHooks( 'MessageCacheReplace', array( $title, $text ) ); 00532 00533 wfProfileOut( __METHOD__ ); 00534 } 00535 00544 protected function saveToCaches( $cache, $memc = true, $code = false ) { 00545 wfProfileIn( __METHOD__ ); 00546 global $wgUseLocalMessageCache, $wgLocalMessageCacheSerialized; 00547 00548 $cacheKey = wfMemcKey( 'messages', $code ); 00549 00550 if ( $memc ) { 00551 $success = $this->mMemc->set( $cacheKey, $cache, $this->mExpiry ); 00552 } else { 00553 $success = true; 00554 } 00555 00556 # Save to local cache 00557 if ( $wgUseLocalMessageCache ) { 00558 $serialized = serialize( $cache ); 00559 $hash = md5( $serialized ); 00560 $this->mMemc->set( wfMemcKey( 'messages', $code, 'hash' ), $hash, $this->mExpiry ); 00561 if ($wgLocalMessageCacheSerialized) { 00562 $this->saveToLocal( $serialized, $hash, $code ); 00563 } else { 00564 $this->saveToScript( $cache, $hash, $code ); 00565 } 00566 } 00567 00568 wfProfileOut( __METHOD__ ); 00569 return $success; 00570 } 00571 00579 function lock( $key ) { 00580 $lockKey = $key . ':lock'; 00581 for ( $i = 0; $i < MSG_WAIT_TIMEOUT && !$this->mMemc->add( $lockKey, 1, MSG_LOCK_TIMEOUT ); $i++ ) { 00582 sleep( 1 ); 00583 } 00584 00585 return $i >= MSG_WAIT_TIMEOUT; 00586 } 00587 00588 function unlock( $key ) { 00589 $lockKey = $key . ':lock'; 00590 $this->mMemc->delete( $lockKey ); 00591 } 00592 00612 function get( $key, $useDB = true, $langcode = true, $isFullKey = false ) { 00613 global $wgLanguageCode, $wgContLang; 00614 00615 if ( is_int( $key ) ) { 00616 // "Non-string key given" exception sometimes happens for numerical strings that become ints somewhere on their way here 00617 $key = strval( $key ); 00618 } 00619 00620 if ( !is_string( $key ) ) { 00621 throw new MWException( 'Non-string key given' ); 00622 } 00623 00624 if ( strval( $key ) === '' ) { 00625 # Shortcut: the empty key is always missing 00626 return false; 00627 } 00628 00629 $lang = wfGetLangObj( $langcode ); 00630 if ( !$lang ) { 00631 throw new MWException( "Bad lang code $langcode given" ); 00632 } 00633 00634 $langcode = $lang->getCode(); 00635 00636 $message = false; 00637 00638 # Normalise title-case input (with some inlining) 00639 $lckey = str_replace( ' ', '_', $key ); 00640 if ( ord( $key ) < 128 ) { 00641 $lckey[0] = strtolower( $lckey[0] ); 00642 $uckey = ucfirst( $lckey ); 00643 } else { 00644 $lckey = $wgContLang->lcfirst( $lckey ); 00645 $uckey = $wgContLang->ucfirst( $lckey ); 00646 } 00647 00653 $this->mRequestedMessages[$uckey] = true; 00654 00655 # Try the MediaWiki namespace 00656 if( !$this->mDisable && $useDB ) { 00657 $title = $uckey; 00658 if( !$isFullKey && ( $langcode != $wgLanguageCode ) ) { 00659 $title .= '/' . $langcode; 00660 } 00661 $message = $this->getMsgFromNamespace( $title, $langcode ); 00662 } 00663 00664 # Try the array in the language object 00665 if ( $message === false ) { 00666 $message = $lang->getMessage( $lckey ); 00667 if ( is_null( $message ) ) { 00668 $message = false; 00669 } 00670 } 00671 00672 # Try the array of another language 00673 if( $message === false ) { 00674 $parts = explode( '/', $lckey ); 00675 # We may get calls for things that are http-urls from sidebar 00676 # Let's not load nonexistent languages for those 00677 # They usually have more than one slash. 00678 if ( count( $parts ) == 2 && $parts[1] !== '' ) { 00679 $message = Language::getMessageFor( $parts[0], $parts[1] ); 00680 if ( is_null( $message ) ) { 00681 $message = false; 00682 } 00683 } 00684 } 00685 00686 # Is this a custom message? Try the default language in the db... 00687 if( ( $message === false || $message === '-' ) && 00688 !$this->mDisable && $useDB && 00689 !$isFullKey && ( $langcode != $wgLanguageCode ) ) { 00690 $message = $this->getMsgFromNamespace( $uckey, $wgLanguageCode ); 00691 } 00692 00693 # Final fallback 00694 if( $message === false ) { 00695 return false; 00696 } 00697 00698 # Fix whitespace 00699 $message = strtr( $message, 00700 array( 00701 # Fix for trailing whitespace, removed by textarea 00702 ' ' => ' ', 00703 # Fix for NBSP, converted to space by firefox 00704 ' ' => "\xc2\xa0", 00705 ' ' => "\xc2\xa0", 00706 ) ); 00707 00708 return $message; 00709 } 00710 00720 function getMsgFromNamespace( $title, $code ) { 00721 global $wgAdaptiveMessageCache; 00722 00723 $this->load( $code ); 00724 if ( isset( $this->mCache[$code][$title] ) ) { 00725 $entry = $this->mCache[$code][$title]; 00726 if ( substr( $entry, 0, 1 ) === ' ' ) { 00727 return substr( $entry, 1 ); 00728 } elseif ( $entry === '!NONEXISTENT' ) { 00729 return false; 00730 } elseif( $entry === '!TOO BIG' ) { 00731 // Fall through and try invididual message cache below 00732 } 00733 } else { 00734 // XXX: This is not cached in process cache, should it? 00735 $message = false; 00736 wfRunHooks( 'MessagesPreLoad', array( $title, &$message ) ); 00737 if ( $message !== false ) { 00738 return $message; 00739 } 00740 00747 if ( !$wgAdaptiveMessageCache ) { 00748 return false; 00749 } 00750 } 00751 00752 # Try the individual message cache 00753 $titleKey = wfMemcKey( 'messages', 'individual', $title ); 00754 $entry = $this->mMemc->get( $titleKey ); 00755 if ( $entry ) { 00756 if ( substr( $entry, 0, 1 ) === ' ' ) { 00757 $this->mCache[$code][$title] = $entry; 00758 return substr( $entry, 1 ); 00759 } elseif ( $entry === '!NONEXISTENT' ) { 00760 $this->mCache[$code][$title] = '!NONEXISTENT'; 00761 return false; 00762 } else { 00763 # Corrupt/obsolete entry, delete it 00764 $this->mMemc->delete( $titleKey ); 00765 } 00766 } 00767 00768 # Try loading it from the database 00769 $revision = Revision::newFromTitle( 00770 Title::makeTitle( NS_MEDIAWIKI, $title ), false, Revision::READ_LATEST 00771 ); 00772 if ( $revision ) { 00773 $message = $revision->getText(); 00774 if ($message === false) { 00775 // A possibly temporary loading failure. 00776 wfDebugLog( 'MessageCache', __METHOD__ . ": failed to load message page text for {$title} ($code)" ); 00777 } else { 00778 $this->mCache[$code][$title] = ' ' . $message; 00779 $this->mMemc->set( $titleKey, ' ' . $message, $this->mExpiry ); 00780 } 00781 } else { 00782 $message = false; 00783 $this->mCache[$code][$title] = '!NONEXISTENT'; 00784 $this->mMemc->set( $titleKey, '!NONEXISTENT', $this->mExpiry ); 00785 } 00786 00787 return $message; 00788 } 00789 00797 function transform( $message, $interface = false, $language = null, $title = null ) { 00798 // Avoid creating parser if nothing to transform 00799 if( strpos( $message, '{{' ) === false ) { 00800 return $message; 00801 } 00802 00803 if ( $this->mInParser ) { 00804 return $message; 00805 } 00806 00807 $parser = $this->getParser(); 00808 if ( $parser ) { 00809 $popts = $this->getParserOptions(); 00810 $popts->setInterfaceMessage( $interface ); 00811 $popts->setTargetLanguage( $language ); 00812 00813 $userlang = $popts->setUserLang( $language ); 00814 $this->mInParser = true; 00815 $message = $parser->transformMsg( $message, $popts, $title ); 00816 $this->mInParser = false; 00817 $popts->setUserLang( $userlang ); 00818 } 00819 return $message; 00820 } 00821 00825 function getParser() { 00826 global $wgParser, $wgParserConf; 00827 if ( !$this->mParser && isset( $wgParser ) ) { 00828 # Do some initialisation so that we don't have to do it twice 00829 $wgParser->firstCallInit(); 00830 # Clone it and store it 00831 $class = $wgParserConf['class']; 00832 if ( $class == 'Parser_DiffTest' ) { 00833 # Uncloneable 00834 $this->mParser = new $class( $wgParserConf ); 00835 } else { 00836 $this->mParser = clone $wgParser; 00837 } 00838 } 00839 return $this->mParser; 00840 } 00841 00850 public function parse( $text, $title = null, $linestart = true, $interface = false, $language = null ) { 00851 if ( $this->mInParser ) { 00852 return htmlspecialchars( $text ); 00853 } 00854 00855 $parser = $this->getParser(); 00856 $popts = $this->getParserOptions(); 00857 $popts->setInterfaceMessage( $interface ); 00858 $popts->setTargetLanguage( $language ); 00859 00860 wfProfileIn( __METHOD__ ); 00861 if ( !$title || !$title instanceof Title ) { 00862 global $wgTitle; 00863 $title = $wgTitle; 00864 } 00865 // Sometimes $wgTitle isn't set either... 00866 if ( !$title ) { 00867 # It's not uncommon having a null $wgTitle in scripts. See r80898 00868 # Create a ghost title in such case 00869 $title = Title::newFromText( 'Dwimmerlaik' ); 00870 } 00871 00872 $this->mInParser = true; 00873 $res = $parser->parse( $text, $title, $popts, $linestart ); 00874 $this->mInParser = false; 00875 00876 wfProfileOut( __METHOD__ ); 00877 return $res; 00878 } 00879 00880 function disable() { 00881 $this->mDisable = true; 00882 } 00883 00884 function enable() { 00885 $this->mDisable = false; 00886 } 00887 00891 function clear() { 00892 $langs = Language::fetchLanguageNames( null, 'mw' ); 00893 foreach ( array_keys($langs) as $code ) { 00894 # Global cache 00895 $this->mMemc->delete( wfMemcKey( 'messages', $code ) ); 00896 # Invalidate all local caches 00897 $this->mMemc->delete( wfMemcKey( 'messages', $code, 'hash' ) ); 00898 } 00899 $this->mLoadedLanguages = array(); 00900 } 00901 00906 public function figureMessage( $key ) { 00907 global $wgLanguageCode; 00908 $pieces = explode( '/', $key ); 00909 if( count( $pieces ) < 2 ) { 00910 return array( $key, $wgLanguageCode ); 00911 } 00912 00913 $lang = array_pop( $pieces ); 00914 if( !Language::fetchLanguageName( $lang, null, 'mw' ) ) { 00915 return array( $key, $wgLanguageCode ); 00916 } 00917 00918 $message = implode( '/', $pieces ); 00919 return array( $message, $lang ); 00920 } 00921 00922 public static function logMessages() { 00923 wfProfileIn( __METHOD__ ); 00924 global $wgAdaptiveMessageCache; 00925 if ( !$wgAdaptiveMessageCache || !self::$instance instanceof MessageCache ) { 00926 wfProfileOut( __METHOD__ ); 00927 return; 00928 } 00929 00930 $cachekey = wfMemckey( 'message-profiling' ); 00931 $cache = wfGetCache( CACHE_DB ); 00932 $data = $cache->get( $cachekey ); 00933 00934 if ( !$data ) { 00935 $data = array(); 00936 } 00937 00938 $age = self::$mAdaptiveDataAge; 00939 $filterDate = substr( wfTimestamp( TS_MW, time() - $age ), 0, 8 ); 00940 foreach ( array_keys( $data ) as $key ) { 00941 if ( $key < $filterDate ) { 00942 unset( $data[$key] ); 00943 } 00944 } 00945 00946 $index = substr( wfTimestampNow(), 0, 8 ); 00947 if ( !isset( $data[$index] ) ) { 00948 $data[$index] = array(); 00949 } 00950 00951 foreach ( self::$instance->mRequestedMessages as $message => $_ ) { 00952 if ( !isset( $data[$index][$message] ) ) { 00953 $data[$index][$message] = 0; 00954 } 00955 $data[$index][$message]++; 00956 } 00957 00958 $cache->set( $cachekey, $data ); 00959 wfProfileOut( __METHOD__ ); 00960 } 00961 00965 public function getMostUsedMessages() { 00966 wfProfileIn( __METHOD__ ); 00967 $cachekey = wfMemcKey( 'message-profiling' ); 00968 $cache = wfGetCache( CACHE_DB ); 00969 $data = $cache->get( $cachekey ); 00970 if ( !$data ) { 00971 wfProfileOut( __METHOD__ ); 00972 return array(); 00973 } 00974 00975 $list = array(); 00976 00977 foreach( $data as $messages ) { 00978 foreach( $messages as $message => $count ) { 00979 $key = $message; 00980 if ( !isset( $list[$key] ) ) { 00981 $list[$key] = 0; 00982 } 00983 $list[$key] += $count; 00984 } 00985 } 00986 00987 $max = max( $list ); 00988 foreach ( $list as $message => $count ) { 00989 if ( $count < intval( $max * self::$mAdaptiveInclusionThreshold ) ) { 00990 unset( $list[$message] ); 00991 } 00992 } 00993 00994 wfProfileOut( __METHOD__ ); 00995 return array_keys( $list ); 00996 } 00997 01006 public function getAllMessageKeys( $code ) { 01007 global $wgContLang; 01008 $this->load( $code ); 01009 if ( !isset( $this->mCache[$code] ) ) { 01010 // Apparently load() failed 01011 return null; 01012 } 01013 $cache = $this->mCache[$code]; // Copy the cache 01014 unset( $cache['VERSION'] ); // Remove the VERSION key 01015 $cache = array_diff( $cache, array( '!NONEXISTENT' ) ); // Remove any !NONEXISTENT keys 01016 // Keys may appear with a capital first letter. lcfirst them. 01017 return array_map( array( $wgContLang, 'lcfirst' ), array_keys( $cache ) ); 01018 } 01019 }