MediaWiki
REL1_19
|
00001 <?php 00010 define( 'MSG_LOAD_TIMEOUT', 60 ); 00011 define( 'MSG_LOCK_TIMEOUT', 10 ); 00012 define( 'MSG_WAIT_TIMEOUT', 10 ); 00013 define( 'MSG_CACHE_VERSION', 1 ); 00014 00020 class MessageCache { 00028 protected $mCache; 00029 00030 // Should mean that database cannot be used, but check 00031 protected $mDisable; 00032 00034 protected $mExpiry; 00035 00040 protected $mParserOptions, $mParser; 00041 00043 protected $mLoadedLanguages = array(); 00044 00048 protected $mRequestedMessages = array(); 00049 00055 protected static $mAdaptiveDataAge = 604800; // Is 7*24*3600 00056 00063 protected static $mAdaptiveInclusionThreshold = 0.05; 00064 00070 private static $instance; 00071 00075 protected $mInParser = false; 00076 00083 public static function singleton() { 00084 if ( is_null( self::$instance ) ) { 00085 global $wgUseDatabaseMessages, $wgMsgCacheExpiry; 00086 self::$instance = new self( wfGetMessageCacheStorage(), $wgUseDatabaseMessages, $wgMsgCacheExpiry ); 00087 } 00088 return self::$instance; 00089 } 00090 00096 public static function destroyInstance() { 00097 self::$instance = null; 00098 } 00099 00100 function __construct( $memCached, $useDB, $expiry ) { 00101 if ( !$memCached ) { 00102 $memCached = wfGetCache( CACHE_NONE ); 00103 } 00104 00105 $this->mMemc = $memCached; 00106 $this->mDisable = !$useDB; 00107 $this->mExpiry = $expiry; 00108 } 00109 00115 function getParserOptions() { 00116 if ( !$this->mParserOptions ) { 00117 $this->mParserOptions = new ParserOptions; 00118 } 00119 return $this->mParserOptions; 00120 } 00121 00131 function loadFromLocal( $hash, $code ) { 00132 global $wgCacheDirectory, $wgLocalMessageCacheSerialized; 00133 00134 $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; 00135 00136 # Check file existence 00137 wfSuppressWarnings(); 00138 $file = fopen( $filename, 'r' ); 00139 wfRestoreWarnings(); 00140 if ( !$file ) { 00141 return false; // No cache file 00142 } 00143 00144 if ( $wgLocalMessageCacheSerialized ) { 00145 // Check to see if the file has the hash specified 00146 $localHash = fread( $file, 32 ); 00147 if ( $hash === $localHash ) { 00148 // All good, get the rest of it 00149 $serialized = ''; 00150 while ( !feof( $file ) ) { 00151 $serialized .= fread( $file, 100000 ); 00152 } 00153 fclose( $file ); 00154 return $this->setCache( unserialize( $serialized ), $code ); 00155 } else { 00156 fclose( $file ); 00157 return false; // Wrong hash 00158 } 00159 } else { 00160 $localHash = substr( fread( $file, 40 ), 8 ); 00161 fclose( $file ); 00162 if ( $hash != $localHash ) { 00163 return false; // Wrong hash 00164 } 00165 00166 # Require overwrites the member variable or just shadows it? 00167 require( $filename ); 00168 return $this->setCache( $this->mCache, $code ); 00169 } 00170 } 00171 00175 function saveToLocal( $serialized, $hash, $code ) { 00176 global $wgCacheDirectory; 00177 00178 $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; 00179 wfMkdirParents( $wgCacheDirectory, null, __METHOD__ ); // might fail 00180 00181 wfSuppressWarnings(); 00182 $file = fopen( $filename, 'w' ); 00183 wfRestoreWarnings(); 00184 00185 if ( !$file ) { 00186 wfDebug( "Unable to open local cache file for writing\n" ); 00187 return; 00188 } 00189 00190 fwrite( $file, $hash . $serialized ); 00191 fclose( $file ); 00192 wfSuppressWarnings(); 00193 chmod( $filename, 0666 ); 00194 wfRestoreWarnings(); 00195 } 00196 00197 function saveToScript( $array, $hash, $code ) { 00198 global $wgCacheDirectory; 00199 00200 $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; 00201 $tempFilename = $filename . '.tmp'; 00202 wfMkdirParents( $wgCacheDirectory, null, __METHOD__ ); // might fail 00203 00204 wfSuppressWarnings(); 00205 $file = fopen( $tempFilename, 'w' ); 00206 wfRestoreWarnings(); 00207 00208 if ( !$file ) { 00209 wfDebug( "Unable to open local cache file for writing\n" ); 00210 return; 00211 } 00212 00213 fwrite( $file, "<?php\n//$hash\n\n \$this->mCache = array(" ); 00214 00215 foreach ( $array as $key => $message ) { 00216 $key = $this->escapeForScript( $key ); 00217 $message = $this->escapeForScript( $message ); 00218 fwrite( $file, "'$key' => '$message',\n" ); 00219 } 00220 00221 fwrite( $file, ");\n?>" ); 00222 fclose( $file); 00223 rename( $tempFilename, $filename ); 00224 } 00225 00226 function escapeForScript( $string ) { 00227 $string = str_replace( '\\', '\\\\', $string ); 00228 $string = str_replace( '\'', '\\\'', $string ); 00229 return $string; 00230 } 00231 00237 function setCache( $cache, $code ) { 00238 if ( isset( $cache['VERSION'] ) && $cache['VERSION'] == MSG_CACHE_VERSION ) { 00239 $this->mCache[$code] = $cache; 00240 return true; 00241 } else { 00242 return false; 00243 } 00244 } 00245 00264 function load( $code = false ) { 00265 global $wgUseLocalMessageCache; 00266 00267 if( !is_string( $code ) ) { 00268 # This isn't really nice, so at least make a note about it and try to 00269 # fall back 00270 wfDebug( __METHOD__ . " called without providing a language code\n" ); 00271 $code = 'en'; 00272 } 00273 00274 # Don't do double loading... 00275 if ( isset( $this->mLoadedLanguages[$code] ) ) { 00276 return true; 00277 } 00278 00279 # 8 lines of code just to say (once) that message cache is disabled 00280 if ( $this->mDisable ) { 00281 static $shownDisabled = false; 00282 if ( !$shownDisabled ) { 00283 wfDebug( __METHOD__ . ": disabled\n" ); 00284 $shownDisabled = true; 00285 } 00286 return true; 00287 } 00288 00289 # Loading code starts 00290 wfProfileIn( __METHOD__ ); 00291 $success = false; # Keep track of success 00292 $where = array(); # Debug info, delayed to avoid spamming debug log too much 00293 $cacheKey = wfMemcKey( 'messages', $code ); # Key in memc for messages 00294 00295 # (1) local cache 00296 # Hash of the contents is stored in memcache, to detect if local cache goes 00297 # out of date (due to update in other thread?) 00298 if ( $wgUseLocalMessageCache ) { 00299 wfProfileIn( __METHOD__ . '-fromlocal' ); 00300 00301 $hash = $this->mMemc->get( wfMemcKey( 'messages', $code, 'hash' ) ); 00302 if ( $hash ) { 00303 $success = $this->loadFromLocal( $hash, $code ); 00304 if ( $success ) $where[] = 'got from local cache'; 00305 } 00306 wfProfileOut( __METHOD__ . '-fromlocal' ); 00307 } 00308 00309 # (2) memcache 00310 # Fails if nothing in cache, or in the wrong version. 00311 if ( !$success ) { 00312 wfProfileIn( __METHOD__ . '-fromcache' ); 00313 $cache = $this->mMemc->get( $cacheKey ); 00314 $success = $this->setCache( $cache, $code ); 00315 if ( $success ) { 00316 $where[] = 'got from global cache'; 00317 $this->saveToCaches( $cache, false, $code ); 00318 } 00319 wfProfileOut( __METHOD__ . '-fromcache' ); 00320 } 00321 00322 # (3) 00323 # Nothing in caches... so we need create one and store it in caches 00324 if ( !$success ) { 00325 $where[] = 'cache is empty'; 00326 $where[] = 'loading from database'; 00327 00328 $this->lock( $cacheKey ); 00329 00330 # Limit the concurrency of loadFromDB to a single process 00331 # This prevents the site from going down when the cache expires 00332 $statusKey = wfMemcKey( 'messages', $code, 'status' ); 00333 $success = $this->mMemc->add( $statusKey, 'loading', MSG_LOAD_TIMEOUT ); 00334 if ( $success ) { 00335 $cache = $this->loadFromDB( $code ); 00336 $success = $this->setCache( $cache, $code ); 00337 } 00338 if ( $success ) { 00339 $success = $this->saveToCaches( $cache, true, $code ); 00340 if ( $success ) { 00341 $this->mMemc->delete( $statusKey ); 00342 } else { 00343 $this->mMemc->set( $statusKey, 'error', 60 * 5 ); 00344 wfDebug( "MemCached set error in MessageCache: restart memcached server!\n" ); 00345 } 00346 } 00347 $this->unlock($cacheKey); 00348 } 00349 00350 if ( !$success ) { 00351 # Bad luck... this should not happen 00352 $where[] = 'loading FAILED - cache is disabled'; 00353 $info = implode( ', ', $where ); 00354 wfDebug( __METHOD__ . ": Loading $code... $info\n" ); 00355 $this->mDisable = true; 00356 $this->mCache = false; 00357 } else { 00358 # All good, just record the success 00359 $info = implode( ', ', $where ); 00360 wfDebug( __METHOD__ . ": Loading $code... $info\n" ); 00361 $this->mLoadedLanguages[$code] = true; 00362 } 00363 wfProfileOut( __METHOD__ ); 00364 return $success; 00365 } 00366 00375 function loadFromDB( $code ) { 00376 wfProfileIn( __METHOD__ ); 00377 global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache; 00378 $dbr = wfGetDB( DB_SLAVE ); 00379 $cache = array(); 00380 00381 # Common conditions 00382 $conds = array( 00383 'page_is_redirect' => 0, 00384 'page_namespace' => NS_MEDIAWIKI, 00385 ); 00386 00387 $mostused = array(); 00388 if ( $wgAdaptiveMessageCache ) { 00389 $mostused = $this->getMostUsedMessages(); 00390 if ( $code !== $wgLanguageCode ) { 00391 foreach ( $mostused as $key => $value ) { 00392 $mostused[$key] = "$value/$code"; 00393 } 00394 } 00395 } 00396 00397 if ( count( $mostused ) ) { 00398 $conds['page_title'] = $mostused; 00399 } elseif ( $code !== $wgLanguageCode ) { 00400 $conds[] = 'page_title' . $dbr->buildLike( $dbr->anyString(), "/$code" ); 00401 } else { 00402 # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses 00403 # other than language code. 00404 $conds[] = 'page_title NOT' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() ); 00405 } 00406 00407 # Conditions to fetch oversized pages to ignore them 00408 $bigConds = $conds; 00409 $bigConds[] = 'page_len > ' . intval( $wgMaxMsgCacheEntrySize ); 00410 00411 # Load titles for all oversized pages in the MediaWiki namespace 00412 $res = $dbr->select( 'page', 'page_title', $bigConds, __METHOD__ . "($code)-big" ); 00413 foreach ( $res as $row ) { 00414 $cache[$row->page_title] = '!TOO BIG'; 00415 } 00416 00417 # Conditions to load the remaining pages with their contents 00418 $smallConds = $conds; 00419 $smallConds[] = 'page_latest=rev_id'; 00420 $smallConds[] = 'rev_text_id=old_id'; 00421 $smallConds[] = 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize ); 00422 00423 $res = $dbr->select( 00424 array( 'page', 'revision', 'text' ), 00425 array( 'page_title', 'old_text', 'old_flags' ), 00426 $smallConds, 00427 __METHOD__ . "($code)-small" 00428 ); 00429 00430 foreach ( $res as $row ) { 00431 $text = Revision::getRevisionText( $row ); 00432 if( $text === false ) { 00433 // Failed to fetch data; possible ES errors? 00434 // Store a marker to fetch on-demand as a workaround... 00435 $entry = '!TOO BIG'; 00436 wfDebugLog( 'MessageCache', __METHOD__ . ": failed to load message page text for {$row->page_title} ($code)" ); 00437 } else { 00438 $entry = ' ' . $text; 00439 } 00440 $cache[$row->page_title] = $entry; 00441 } 00442 00443 foreach ( $mostused as $key ) { 00444 if ( !isset( $cache[$key] ) ) { 00445 $cache[$key] = '!NONEXISTENT'; 00446 } 00447 } 00448 00449 $cache['VERSION'] = MSG_CACHE_VERSION; 00450 wfProfileOut( __METHOD__ ); 00451 return $cache; 00452 } 00453 00460 public function replace( $title, $text ) { 00461 global $wgMaxMsgCacheEntrySize; 00462 wfProfileIn( __METHOD__ ); 00463 00464 if ( $this->mDisable ) { 00465 wfProfileOut( __METHOD__ ); 00466 return; 00467 } 00468 00469 list( $msg, $code ) = $this->figureMessage( $title ); 00470 00471 $cacheKey = wfMemcKey( 'messages', $code ); 00472 $this->load( $code ); 00473 $this->lock( $cacheKey ); 00474 00475 $titleKey = wfMemcKey( 'messages', 'individual', $title ); 00476 00477 if ( $text === false ) { 00478 # Article was deleted 00479 $this->mCache[$code][$title] = '!NONEXISTENT'; 00480 $this->mMemc->delete( $titleKey ); 00481 } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) { 00482 # Check for size 00483 $this->mCache[$code][$title] = '!TOO BIG'; 00484 $this->mMemc->set( $titleKey, ' ' . $text, $this->mExpiry ); 00485 } else { 00486 $this->mCache[$code][$title] = ' ' . $text; 00487 $this->mMemc->delete( $titleKey ); 00488 } 00489 00490 # Update caches 00491 $this->saveToCaches( $this->mCache[$code], true, $code ); 00492 $this->unlock( $cacheKey ); 00493 00494 // Also delete cached sidebar... just in case it is affected 00495 $codes = array( $code ); 00496 if ( $code === 'en' ) { 00497 // Delete all sidebars, like for example on action=purge on the 00498 // sidebar messages 00499 $codes = array_keys( Language::getLanguageNames() ); 00500 } 00501 00502 global $wgMemc; 00503 foreach ( $codes as $code ) { 00504 $sidebarKey = wfMemcKey( 'sidebar', $code ); 00505 $wgMemc->delete( $sidebarKey ); 00506 } 00507 00508 // Update the message in the message blob store 00509 global $wgContLang; 00510 MessageBlobStore::updateMessage( $wgContLang->lcfirst( $msg ) ); 00511 00512 wfRunHooks( 'MessageCacheReplace', array( $title, $text ) ); 00513 00514 wfProfileOut( __METHOD__ ); 00515 } 00516 00525 protected function saveToCaches( $cache, $memc = true, $code = false ) { 00526 wfProfileIn( __METHOD__ ); 00527 global $wgUseLocalMessageCache, $wgLocalMessageCacheSerialized; 00528 00529 $cacheKey = wfMemcKey( 'messages', $code ); 00530 00531 if ( $memc ) { 00532 $success = $this->mMemc->set( $cacheKey, $cache, $this->mExpiry ); 00533 } else { 00534 $success = true; 00535 } 00536 00537 # Save to local cache 00538 if ( $wgUseLocalMessageCache ) { 00539 $serialized = serialize( $cache ); 00540 $hash = md5( $serialized ); 00541 $this->mMemc->set( wfMemcKey( 'messages', $code, 'hash' ), $hash, $this->mExpiry ); 00542 if ($wgLocalMessageCacheSerialized) { 00543 $this->saveToLocal( $serialized, $hash, $code ); 00544 } else { 00545 $this->saveToScript( $cache, $hash, $code ); 00546 } 00547 } 00548 00549 wfProfileOut( __METHOD__ ); 00550 return $success; 00551 } 00552 00560 function lock( $key ) { 00561 $lockKey = $key . ':lock'; 00562 for ( $i = 0; $i < MSG_WAIT_TIMEOUT && !$this->mMemc->add( $lockKey, 1, MSG_LOCK_TIMEOUT ); $i++ ) { 00563 sleep( 1 ); 00564 } 00565 00566 return $i >= MSG_WAIT_TIMEOUT; 00567 } 00568 00569 function unlock( $key ) { 00570 $lockKey = $key . ':lock'; 00571 $this->mMemc->delete( $lockKey ); 00572 } 00573 00593 function get( $key, $useDB = true, $langcode = true, $isFullKey = false ) { 00594 global $wgLanguageCode, $wgContLang; 00595 00596 if ( is_int( $key ) ) { 00597 // "Non-string key given" exception sometimes happens for numerical strings that become ints somewhere on their way here 00598 $key = strval( $key ); 00599 } 00600 00601 if ( !is_string( $key ) ) { 00602 throw new MWException( 'Non-string key given' ); 00603 } 00604 00605 if ( strval( $key ) === '' ) { 00606 # Shortcut: the empty key is always missing 00607 return false; 00608 } 00609 00610 $lang = wfGetLangObj( $langcode ); 00611 if ( !$lang ) { 00612 throw new MWException( "Bad lang code $langcode given" ); 00613 } 00614 00615 $langcode = $lang->getCode(); 00616 00617 $message = false; 00618 00619 # Normalise title-case input (with some inlining) 00620 $lckey = str_replace( ' ', '_', $key ); 00621 if ( ord( $key ) < 128 ) { 00622 $lckey[0] = strtolower( $lckey[0] ); 00623 $uckey = ucfirst( $lckey ); 00624 } else { 00625 $lckey = $wgContLang->lcfirst( $lckey ); 00626 $uckey = $wgContLang->ucfirst( $lckey ); 00627 } 00628 00634 $this->mRequestedMessages[$uckey] = true; 00635 00636 # Try the MediaWiki namespace 00637 if( !$this->mDisable && $useDB ) { 00638 $title = $uckey; 00639 if( !$isFullKey && ( $langcode != $wgLanguageCode ) ) { 00640 $title .= '/' . $langcode; 00641 } 00642 $message = $this->getMsgFromNamespace( $title, $langcode ); 00643 } 00644 00645 # Try the array in the language object 00646 if ( $message === false ) { 00647 $message = $lang->getMessage( $lckey ); 00648 if ( is_null( $message ) ) { 00649 $message = false; 00650 } 00651 } 00652 00653 # Try the array of another language 00654 if( $message === false ) { 00655 $parts = explode( '/', $lckey ); 00656 # We may get calls for things that are http-urls from sidebar 00657 # Let's not load nonexistent languages for those 00658 # They usually have more than one slash. 00659 if ( count( $parts ) == 2 && $parts[1] !== '' ) { 00660 $message = Language::getMessageFor( $parts[0], $parts[1] ); 00661 if ( is_null( $message ) ) { 00662 $message = false; 00663 } 00664 } 00665 } 00666 00667 # Is this a custom message? Try the default language in the db... 00668 if( ( $message === false || $message === '-' ) && 00669 !$this->mDisable && $useDB && 00670 !$isFullKey && ( $langcode != $wgLanguageCode ) ) { 00671 $message = $this->getMsgFromNamespace( $uckey, $wgLanguageCode ); 00672 } 00673 00674 # Final fallback 00675 if( $message === false ) { 00676 return false; 00677 } 00678 00679 # Fix whitespace 00680 $message = strtr( $message, 00681 array( 00682 # Fix for trailing whitespace, removed by textarea 00683 ' ' => ' ', 00684 # Fix for NBSP, converted to space by firefox 00685 ' ' => "\xc2\xa0", 00686 ' ' => "\xc2\xa0", 00687 ) ); 00688 00689 return $message; 00690 } 00691 00701 function getMsgFromNamespace( $title, $code ) { 00702 global $wgAdaptiveMessageCache; 00703 00704 $this->load( $code ); 00705 if ( isset( $this->mCache[$code][$title] ) ) { 00706 $entry = $this->mCache[$code][$title]; 00707 if ( substr( $entry, 0, 1 ) === ' ' ) { 00708 return substr( $entry, 1 ); 00709 } elseif ( $entry === '!NONEXISTENT' ) { 00710 return false; 00711 } elseif( $entry === '!TOO BIG' ) { 00712 // Fall through and try invididual message cache below 00713 } 00714 } else { 00715 // XXX: This is not cached in process cache, should it? 00716 $message = false; 00717 wfRunHooks( 'MessagesPreLoad', array( $title, &$message ) ); 00718 if ( $message !== false ) { 00719 return $message; 00720 } 00721 00728 if ( !$wgAdaptiveMessageCache ) { 00729 return false; 00730 } 00731 } 00732 00733 # Try the individual message cache 00734 $titleKey = wfMemcKey( 'messages', 'individual', $title ); 00735 $entry = $this->mMemc->get( $titleKey ); 00736 if ( $entry ) { 00737 if ( substr( $entry, 0, 1 ) === ' ' ) { 00738 $this->mCache[$code][$title] = $entry; 00739 return substr( $entry, 1 ); 00740 } elseif ( $entry === '!NONEXISTENT' ) { 00741 $this->mCache[$code][$title] = '!NONEXISTENT'; 00742 return false; 00743 } else { 00744 # Corrupt/obsolete entry, delete it 00745 $this->mMemc->delete( $titleKey ); 00746 } 00747 } 00748 00749 # Try loading it from the database 00750 $revision = Revision::newFromTitle( Title::makeTitle( NS_MEDIAWIKI, $title ) ); 00751 if ( $revision ) { 00752 $message = $revision->getText(); 00753 if ($message === false) { 00754 // A possibly temporary loading failure. 00755 wfDebugLog( 'MessageCache', __METHOD__ . ": failed to load message page text for {$title->getDbKey()} ($code)" ); 00756 } else { 00757 $this->mCache[$code][$title] = ' ' . $message; 00758 $this->mMemc->set( $titleKey, ' ' . $message, $this->mExpiry ); 00759 } 00760 } else { 00761 $message = false; 00762 $this->mCache[$code][$title] = '!NONEXISTENT'; 00763 $this->mMemc->set( $titleKey, '!NONEXISTENT', $this->mExpiry ); 00764 } 00765 00766 return $message; 00767 } 00768 00776 function transform( $message, $interface = false, $language = null, $title = null ) { 00777 // Avoid creating parser if nothing to transform 00778 if( strpos( $message, '{{' ) === false ) { 00779 return $message; 00780 } 00781 00782 if ( $this->mInParser ) { 00783 return $message; 00784 } 00785 00786 $parser = $this->getParser(); 00787 if ( $parser ) { 00788 $popts = $this->getParserOptions(); 00789 $popts->setInterfaceMessage( $interface ); 00790 $popts->setTargetLanguage( $language ); 00791 00792 $userlang = $popts->setUserLang( $language ); 00793 $this->mInParser = true; 00794 $message = $parser->transformMsg( $message, $popts, $title ); 00795 $this->mInParser = false; 00796 $popts->setUserLang( $userlang ); 00797 } 00798 return $message; 00799 } 00800 00804 function getParser() { 00805 global $wgParser, $wgParserConf; 00806 if ( !$this->mParser && isset( $wgParser ) ) { 00807 # Do some initialisation so that we don't have to do it twice 00808 $wgParser->firstCallInit(); 00809 # Clone it and store it 00810 $class = $wgParserConf['class']; 00811 if ( $class == 'Parser_DiffTest' ) { 00812 # Uncloneable 00813 $this->mParser = new $class( $wgParserConf ); 00814 } else { 00815 $this->mParser = clone $wgParser; 00816 } 00817 } 00818 return $this->mParser; 00819 } 00820 00829 public function parse( $text, $title = null, $linestart = true, $interface = false, $language = null ) { 00830 if ( $this->mInParser ) { 00831 return htmlspecialchars( $text ); 00832 } 00833 00834 $parser = $this->getParser(); 00835 $popts = $this->getParserOptions(); 00836 $popts->setInterfaceMessage( $interface ); 00837 $popts->setTargetLanguage( $language ); 00838 00839 wfProfileIn( __METHOD__ ); 00840 if ( !$title || !$title instanceof Title ) { 00841 global $wgTitle; 00842 $title = $wgTitle; 00843 } 00844 // Sometimes $wgTitle isn't set either... 00845 if ( !$title ) { 00846 # It's not uncommon having a null $wgTitle in scripts. See r80898 00847 # Create a ghost title in such case 00848 $title = Title::newFromText( 'Dwimmerlaik' ); 00849 } 00850 00851 $this->mInParser = true; 00852 $res = $parser->parse( $text, $title, $popts, $linestart ); 00853 $this->mInParser = false; 00854 00855 wfProfileOut( __METHOD__ ); 00856 return $res; 00857 } 00858 00859 function disable() { 00860 $this->mDisable = true; 00861 } 00862 00863 function enable() { 00864 $this->mDisable = false; 00865 } 00866 00870 function clear() { 00871 $langs = Language::getLanguageNames( false ); 00872 foreach ( array_keys($langs) as $code ) { 00873 # Global cache 00874 $this->mMemc->delete( wfMemcKey( 'messages', $code ) ); 00875 # Invalidate all local caches 00876 $this->mMemc->delete( wfMemcKey( 'messages', $code, 'hash' ) ); 00877 } 00878 $this->mLoadedLanguages = array(); 00879 } 00880 00885 public function figureMessage( $key ) { 00886 global $wgLanguageCode; 00887 $pieces = explode( '/', $key ); 00888 if( count( $pieces ) < 2 ) { 00889 return array( $key, $wgLanguageCode ); 00890 } 00891 00892 $lang = array_pop( $pieces ); 00893 $validCodes = Language::getLanguageNames(); 00894 if( !array_key_exists( $lang, $validCodes ) ) { 00895 return array( $key, $wgLanguageCode ); 00896 } 00897 00898 $message = implode( '/', $pieces ); 00899 return array( $message, $lang ); 00900 } 00901 00902 public static function logMessages() { 00903 wfProfileIn( __METHOD__ ); 00904 global $wgAdaptiveMessageCache; 00905 if ( !$wgAdaptiveMessageCache || !self::$instance instanceof MessageCache ) { 00906 wfProfileOut( __METHOD__ ); 00907 return; 00908 } 00909 00910 $cachekey = wfMemckey( 'message-profiling' ); 00911 $cache = wfGetCache( CACHE_DB ); 00912 $data = $cache->get( $cachekey ); 00913 00914 if ( !$data ) { 00915 $data = array(); 00916 } 00917 00918 $age = self::$mAdaptiveDataAge; 00919 $filterDate = substr( wfTimestamp( TS_MW, time() - $age ), 0, 8 ); 00920 foreach ( array_keys( $data ) as $key ) { 00921 if ( $key < $filterDate ) { 00922 unset( $data[$key] ); 00923 } 00924 } 00925 00926 $index = substr( wfTimestampNow(), 0, 8 ); 00927 if ( !isset( $data[$index] ) ) { 00928 $data[$index] = array(); 00929 } 00930 00931 foreach ( self::$instance->mRequestedMessages as $message => $_ ) { 00932 if ( !isset( $data[$index][$message] ) ) { 00933 $data[$index][$message] = 0; 00934 } 00935 $data[$index][$message]++; 00936 } 00937 00938 $cache->set( $cachekey, $data ); 00939 wfProfileOut( __METHOD__ ); 00940 } 00941 00945 public function getMostUsedMessages() { 00946 wfProfileIn( __METHOD__ ); 00947 $cachekey = wfMemcKey( 'message-profiling' ); 00948 $cache = wfGetCache( CACHE_DB ); 00949 $data = $cache->get( $cachekey ); 00950 if ( !$data ) { 00951 wfProfileOut( __METHOD__ ); 00952 return array(); 00953 } 00954 00955 $list = array(); 00956 00957 foreach( $data as $messages ) { 00958 foreach( $messages as $message => $count ) { 00959 $key = $message; 00960 if ( !isset( $list[$key] ) ) { 00961 $list[$key] = 0; 00962 } 00963 $list[$key] += $count; 00964 } 00965 } 00966 00967 $max = max( $list ); 00968 foreach ( $list as $message => $count ) { 00969 if ( $count < intval( $max * self::$mAdaptiveInclusionThreshold ) ) { 00970 unset( $list[$message] ); 00971 } 00972 } 00973 00974 wfProfileOut( __METHOD__ ); 00975 return array_keys( $list ); 00976 } 00977 00986 public function getAllMessageKeys( $code ) { 00987 global $wgContLang; 00988 $this->load( $code ); 00989 if ( !isset( $this->mCache[$code] ) ) { 00990 // Apparently load() failed 00991 return null; 00992 } 00993 $cache = $this->mCache[$code]; // Copy the cache 00994 unset( $cache['VERSION'] ); // Remove the VERSION key 00995 $cache = array_diff( $cache, array( '!NONEXISTENT' ) ); // Remove any !NONEXISTENT keys 00996 // Keys may appear with a capital first letter. lcfirst them. 00997 return array_map( array( $wgContLang, 'lcfirst' ), array_keys( $cache ) ); 00998 } 00999 }