Example #1
0
 /**
  * Save user data to the shared cache
  *
  * This method should not be called outside the User class
  */
 public function saveToCache()
 {
     $this->load();
     $this->loadGroups();
     $this->loadOptions();
     if ($this->isAnon()) {
         // Anonymous users are uncached
         return;
     }
     $data = array();
     foreach (self::$mCacheVars as $name) {
         $data[$name] = $this->{$name};
     }
     $data['mVersion'] = self::VERSION;
     $opts = Database::getCacheSetOptions(wfGetDB(DB_SLAVE));
     $cache = ObjectCache::getMainWANInstance();
     $key = $this->getCacheKey($cache);
     $cache->set($key, $data, $cache::TTL_HOUR, $opts);
 }
Example #2
0
 /**
  * Get a message from the MediaWiki namespace, with caching. The key must
  * first be converted to two-part lang/msg form if necessary.
  *
  * Unlike self::get(), this function doesn't resolve fallback chains, and
  * some callers require this behavior. LanguageConverter::parseCachedTable()
  * and self::get() are some examples in core.
  *
  * @param string $title Message cache key with initial uppercase letter.
  * @param string $code Code denoting the language to try.
  * @return string|bool The message, or false if it does not exist or on error
  */
 public function getMsgFromNamespace($title, $code)
 {
     $this->load($code);
     if (isset($this->mCache[$code][$title])) {
         $entry = $this->mCache[$code][$title];
         if (substr($entry, 0, 1) === ' ') {
             // The message exists, so make sure a string
             // is returned.
             return (string) substr($entry, 1);
         } elseif ($entry === '!NONEXISTENT') {
             return false;
         } elseif ($entry === '!TOO BIG') {
             // Fall through and try invididual message cache below
         }
     } else {
         // XXX: This is not cached in process cache, should it?
         $message = false;
         Hooks::run('MessagesPreLoad', [$title, &$message]);
         if ($message !== false) {
             return $message;
         }
         return false;
     }
     // Try the individual message cache
     $titleKey = wfMemcKey('messages', 'individual', $title);
     $curTTL = null;
     $entry = $this->wanCache->get($titleKey, $curTTL, [wfMemcKey('messages', $code)]);
     $entry = $curTTL >= 0 ? $entry : false;
     if ($entry) {
         if (substr($entry, 0, 1) === ' ') {
             $this->mCache[$code][$title] = $entry;
             // The message exists, so make sure a string is returned
             return (string) substr($entry, 1);
         } elseif ($entry === '!NONEXISTENT') {
             $this->mCache[$code][$title] = '!NONEXISTENT';
             return false;
         } else {
             // Corrupt/obsolete entry, delete it
             $this->wanCache->delete($titleKey);
         }
     }
     // Try loading it from the database
     $dbr = wfGetDB(DB_REPLICA);
     $cacheOpts = Database::getCacheSetOptions($dbr);
     // Use newKnownCurrent() to avoid querying revision/user tables
     $titleObj = Title::makeTitle(NS_MEDIAWIKI, $title);
     if ($titleObj->getLatestRevID()) {
         $revision = Revision::newKnownCurrent($dbr, $titleObj->getArticleID(), $titleObj->getLatestRevID());
     } else {
         $revision = false;
     }
     if ($revision) {
         $content = $revision->getContent();
         if (!$content) {
             // A possibly temporary loading failure.
             wfDebugLog('MessageCache', __METHOD__ . ": failed to load message page text for {$title} ({$code})");
             $message = null;
             // no negative caching
         } else {
             // XXX: Is this the right way to turn a Content object into a message?
             // NOTE: $content is typically either WikitextContent, JavaScriptContent or
             //       CssContent. MessageContent is *not* used for storing messages, it's
             //       only used for wrapping them when needed.
             $message = $content->getWikitextForTransclusion();
             if ($message === false || $message === null) {
                 wfDebugLog('MessageCache', __METHOD__ . ": message content doesn't provide wikitext " . "(content model: " . $content->getModel() . ")");
                 $message = false;
                 // negative caching
             } else {
                 $this->mCache[$code][$title] = ' ' . $message;
                 $this->wanCache->set($titleKey, ' ' . $message, $this->mExpiry, $cacheOpts);
             }
         }
     } else {
         $message = false;
         // negative caching
     }
     if ($message === false) {
         // negative caching
         $this->mCache[$code][$title] = '!NONEXISTENT';
         $this->wanCache->set($titleKey, '!NONEXISTENT', $this->mExpiry, $cacheOpts);
     }
     return $message;
 }
Example #3
0
 /**
  * Returns a map of any tags used on the wiki to number of edits
  * tagged with them, ordered descending by the hitcount.
  * This does not include tags defined somewhere that have never been applied.
  *
  * Keeps a short-term cache in memory, so calling this multiple times in the
  * same request should be fine.
  *
  * @return array Array of string => int
  */
 public static function tagUsageStatistics()
 {
     $fname = __METHOD__;
     return ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('change-tag-statistics'), 300, function ($oldValue, &$ttl, array &$setOpts) use($fname) {
         $dbr = wfGetDB(DB_SLAVE, 'vslow');
         $setOpts += Database::getCacheSetOptions($dbr);
         $res = $dbr->select('change_tag', array('ct_tag', 'hitcount' => 'count(*)'), array(), $fname, array('GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC'));
         $out = array();
         foreach ($res as $row) {
             $out[$row->ct_tag] = $row->hitcount;
         }
         return $out;
     }, array('checkKeys' => array(wfMemcKey('change-tag-statistics')), 'lockTSE' => INF, 'pcTTL' => 30));
 }
Example #4
0
 /**
  * Returns a map of any tags used on the wiki to number of edits
  * tagged with them, ordered descending by the hitcount.
  * This does not include tags defined somewhere that have never been applied.
  *
  * Keeps a short-term cache in memory, so calling this multiple times in the
  * same request should be fine.
  *
  * @return array Array of string => int
  */
 public static function tagUsageStatistics()
 {
     $fname = __METHOD__;
     return ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('change-tag-statistics'), WANObjectCache::TTL_MINUTE * 5, function ($oldValue, &$ttl, array &$setOpts) use($fname) {
         $dbr = wfGetDB(DB_SLAVE, 'vslow');
         $setOpts += Database::getCacheSetOptions($dbr);
         $res = $dbr->select('change_tag', ['ct_tag', 'hitcount' => 'count(*)'], [], $fname, ['GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC']);
         $out = [];
         foreach ($res as $row) {
             $out[$row->ct_tag] = $row->hitcount;
         }
         return $out;
     }, ['checkKeys' => [wfMemcKey('change-tag-statistics')], 'lockTSE' => WANObjectCache::TTL_MINUTE * 5, 'pcTTL' => WANObjectCache::TTL_PROC_LONG]);
 }
Example #5
0
 /**
  * Checks if there is a redirect named as $title
  *
  * @param Title $title Title of file
  * @return bool|Title
  */
 function checkRedirect(Title $title)
 {
     $title = File::normalizeTitle($title, 'exception');
     $memcKey = $this->getSharedCacheKey('image_redirect', md5($title->getDBkey()));
     if ($memcKey === false) {
         $memcKey = $this->getLocalCacheKey('image_redirect', md5($title->getDBkey()));
         $expiry = 300;
         // no invalidation, 5 minutes
     } else {
         $expiry = 86400;
         // has invalidation, 1 day
     }
     $that = $this;
     $redirDbKey = ObjectCache::getMainWANInstance()->getWithSetCallback($memcKey, $expiry, function ($oldValue, &$ttl, array &$setOpts) use($that, $title) {
         $dbr = $that->getSlaveDB();
         // possibly remote DB
         $setOpts += Database::getCacheSetOptions($dbr);
         if ($title instanceof Title) {
             $row = $dbr->selectRow(array('page', 'redirect'), array('rd_namespace', 'rd_title'), array('page_namespace' => $title->getNamespace(), 'page_title' => $title->getDBkey(), 'rd_from = page_id'), __METHOD__);
         } else {
             $row = false;
         }
         return $row && $row->rd_namespace == NS_FILE ? Title::makeTitle($row->rd_namespace, $row->rd_title)->getDBkey() : '';
         // negative cache
     });
     // @note: also checks " " for b/c
     if ($redirDbKey !== ' ' && strval($redirDbKey) !== '') {
         // Page is a redirect to another file
         return Title::newFromText($redirDbKey, NS_FILE);
     }
     return false;
     // no redirect
 }
Example #6
0
 /**
  * Load restrictions from the page_restrictions table
  *
  * @param string $oldFashionedRestrictions Comma-separated list of page
  *   restrictions from page table (pre 1.10)
  */
 public function loadRestrictions($oldFashionedRestrictions = null)
 {
     if ($this->mRestrictionsLoaded) {
         return;
     }
     $id = $this->getArticleID();
     if ($id) {
         $cache = ObjectCache::getMainWANInstance();
         $rows = $cache->getWithSetCallback($cache->makeKey('page-restrictions', $id, $this->getLatestRevID()), $cache::TTL_DAY, function ($curValue, &$ttl, array &$setOpts) {
             $dbr = wfGetDB(DB_REPLICA);
             $setOpts += Database::getCacheSetOptions($dbr);
             return iterator_to_array($dbr->select('page_restrictions', ['pr_type', 'pr_expiry', 'pr_level', 'pr_cascade'], ['pr_page' => $this->getArticleID()], __METHOD__));
         });
         $this->loadRestrictionsFromRows($rows, $oldFashionedRestrictions);
     } else {
         $title_protection = $this->getTitleProtection();
         if ($title_protection) {
             $now = wfTimestampNow();
             $expiry = wfGetDB(DB_REPLICA)->decodeExpiry($title_protection['expiry']);
             if (!$expiry || $expiry > $now) {
                 // Apply the restrictions
                 $this->mRestrictionsExpiry['create'] = $expiry;
                 $this->mRestrictions['create'] = explode(',', trim($title_protection['permission']));
             } else {
                 // Get rid of the old restrictions
                 $this->mTitleProtection = false;
             }
         } else {
             $this->mRestrictionsExpiry['create'] = 'infinity';
         }
         $this->mRestrictionsLoaded = true;
     }
 }
Example #7
0
 /**
  * Add a title to the link cache, return the page_id or zero if non-existent
  *
  * @param LinkTarget $nt LinkTarget object to add
  * @return int Page ID or zero
  */
 public function addLinkObj(LinkTarget $nt)
 {
     $key = $this->titleFormatter->getPrefixedDBkey($nt);
     if ($this->isBadLink($key) || $nt->isExternal() || $nt->inNamespace(NS_SPECIAL)) {
         return 0;
     }
     $id = $this->getGoodLinkID($key);
     if ($id != 0) {
         return $id;
     }
     if ($key === '') {
         return 0;
     }
     // Cache template/file pages as they are less often viewed but heavily used
     if ($this->mForUpdate) {
         $row = $this->fetchPageRow(wfGetDB(DB_MASTER), $nt);
     } elseif ($this->isCacheable($nt)) {
         // These pages are often transcluded heavily, so cache them
         $cache = $this->wanCache;
         $row = $cache->getWithSetCallback($cache->makeKey('page', $nt->getNamespace(), sha1($nt->getDBkey())), $cache::TTL_DAY, function ($curValue, &$ttl, array &$setOpts) use($cache, $nt) {
             $dbr = wfGetDB(DB_REPLICA);
             $setOpts += Database::getCacheSetOptions($dbr);
             $row = $this->fetchPageRow($dbr, $nt);
             $mtime = $row ? wfTimestamp(TS_UNIX, $row->page_touched) : false;
             $ttl = $cache->adaptiveTTL($mtime, $ttl);
             return $row;
         });
     } else {
         $row = $this->fetchPageRow(wfGetDB(DB_REPLICA), $nt);
     }
     if ($row) {
         $this->addGoodLinkObjFromRow($nt, $row);
         $id = intval($row->page_id);
     } else {
         $this->addBadLinkObj($nt);
         $id = 0;
     }
     return $id;
 }
Example #8
0
 /**
  * Load a revision based on a known page ID and current revision ID from the DB
  *
  * This method allows for the use of caching, though accessing anything that normally
  * requires permission checks (aside from the text) will trigger a small DB lookup.
  * The title will also be lazy loaded, though setTitle() can be used to preload it.
  *
  * @param IDatabase $db
  * @param int $pageId Page ID
  * @param int $revId Known current revision of this page
  * @return Revision|bool Returns false if missing
  * @since 1.28
  */
 public static function newKnownCurrent(IDatabase $db, $pageId, $revId)
 {
     $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
     return $cache->getWithSetCallback($cache->makeGlobalKey('revision', $db->getWikiID(), $pageId, $revId), $cache::TTL_WEEK, function ($curValue, &$ttl, array &$setOpts) use($db, $pageId, $revId) {
         $setOpts += Database::getCacheSetOptions($db);
         $rev = Revision::loadFromPageId($db, $pageId, $revId);
         // Reflect revision deletion and user renames
         if ($rev) {
             $rev->mTitle = null;
             // mutable; lazy-load
             $rev->mRefreshMutableFields = true;
         }
         return $rev ?: false;
         // don't cache negatives
     });
 }
Example #9
0
 /**
  * Checks whether a given IP is on the autoblock whitelist.
  * TODO: this probably belongs somewhere else, but not sure where...
  *
  * @param string $ip The IP to check
  * @return bool
  */
 public static function isWhitelistedFromAutoblocks($ip)
 {
     // Try to get the autoblock_whitelist from the cache, as it's faster
     // than getting the msg raw and explode()'ing it.
     $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
     $lines = $cache->getWithSetCallback(wfMemcKey('ipb', 'autoblock', 'whitelist'), $cache::TTL_DAY, function ($curValue, &$ttl, array &$setOpts) {
         $setOpts += Database::getCacheSetOptions(wfGetDB(DB_REPLICA));
         return explode("\n", wfMessage('autoblock_whitelist')->inContentLanguage()->plain());
     });
     wfDebug("Checking the autoblock whitelist..\n");
     foreach ($lines as $line) {
         # List items only
         if (substr($line, 0, 1) !== '*') {
             continue;
         }
         $wlEntry = substr($line, 1);
         $wlEntry = trim($wlEntry);
         wfDebug("Checking {$ip} against {$wlEntry}...");
         # Is the IP in this range?
         if (IP::isInRange($ip, $wlEntry)) {
             wfDebug(" IP {$ip} matches {$wlEntry}, not autoblocking\n");
             return true;
         } else {
             wfDebug(" No match\n");
         }
     }
     return false;
 }
Example #10
0
 /**
  * Returns page counts that would be too "expensive" to retrieve by normal means.
  *
  * @param WikiPage|Article|Page $page
  * @return array
  */
 protected function pageCounts(Page $page)
 {
     $fname = __METHOD__;
     $config = $this->context->getConfig();
     return ObjectCache::getMainWANInstance()->getWithSetCallback(self::getCacheKey($page->getTitle(), $page->getLatest()), 86400 * 7, function ($oldValue, &$ttl, &$setOpts) use($page, $config, $fname) {
         $title = $page->getTitle();
         $id = $title->getArticleID();
         $dbr = wfGetDB(DB_SLAVE);
         $dbrWatchlist = wfGetDB(DB_SLAVE, 'watchlist');
         $setOpts += Database::getCacheSetOptions($dbr, $dbrWatchlist);
         $result = array();
         // Number of page watchers
         $watchers = (int) $dbrWatchlist->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $title->getNamespace(), 'wl_title' => $title->getDBkey()), $fname);
         $result['watchers'] = $watchers;
         if ($config->get('ShowUpdatedMarker')) {
             // Threshold: last visited about 26 weeks before latest edit
             $updated = wfTimestamp(TS_UNIX, $page->getTimestamp());
             $age = $config->get('WatchersMaxAge');
             $threshold = $dbrWatchlist->timestamp($updated - $age);
             // Number of page watchers who also visited a "recent" edit
             $visitingWatchers = (int) $dbrWatchlist->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $title->getNamespace(), 'wl_title' => $title->getDBkey(), 'wl_notificationtimestamp >= ' . $dbrWatchlist->addQuotes($threshold) . ' OR wl_notificationtimestamp IS NULL'), $fname);
             $result['visitingWatchers'] = $visitingWatchers;
         }
         // Total number of edits
         $edits = (int) $dbr->selectField('revision', 'COUNT(*)', array('rev_page' => $id), $fname);
         $result['edits'] = $edits;
         // Total number of distinct authors
         if ($config->get('MiserMode')) {
             $result['authors'] = 0;
         } else {
             $result['authors'] = (int) $dbr->selectField('revision', 'COUNT(DISTINCT rev_user_text)', array('rev_page' => $id), $fname);
         }
         // "Recent" threshold defined by RCMaxAge setting
         $threshold = $dbr->timestamp(time() - $config->get('RCMaxAge'));
         // Recent number of edits
         $edits = (int) $dbr->selectField('revision', 'COUNT(rev_page)', array('rev_page' => $id, "rev_timestamp >= " . $dbr->addQuotes($threshold)), $fname);
         $result['recent_edits'] = $edits;
         // Recent number of distinct authors
         $result['recent_authors'] = (int) $dbr->selectField('revision', 'COUNT(DISTINCT rev_user_text)', array('rev_page' => $id, "rev_timestamp >= " . $dbr->addQuotes($threshold)), $fname);
         // Subpages (if enabled)
         if (MWNamespace::hasSubpages($title->getNamespace())) {
             $conds = array('page_namespace' => $title->getNamespace());
             $conds[] = 'page_title ' . $dbr->buildLike($title->getDBkey() . '/', $dbr->anyString());
             // Subpages of this page (redirects)
             $conds['page_is_redirect'] = 1;
             $result['subpages']['redirects'] = (int) $dbr->selectField('page', 'COUNT(page_id)', $conds, $fname);
             // Subpages of this page (non-redirects)
             $conds['page_is_redirect'] = 0;
             $result['subpages']['nonredirects'] = (int) $dbr->selectField('page', 'COUNT(page_id)', $conds, $fname);
             // Subpages of this page (total)
             $result['subpages']['total'] = $result['subpages']['redirects'] + $result['subpages']['nonredirects'];
         }
         // Counts for the number of transclusion links (to/from)
         if ($config->get('MiserMode')) {
             $result['transclusion']['to'] = 0;
         } else {
             $result['transclusion']['to'] = (int) $dbr->selectField('templatelinks', 'COUNT(tl_from)', array('tl_namespace' => $title->getNamespace(), 'tl_title' => $title->getDBkey()), $fname);
         }
         $result['transclusion']['from'] = (int) $dbr->selectField('templatelinks', 'COUNT(*)', array('tl_from' => $title->getArticleID()), $fname);
         return $result;
     });
 }
Example #11
0
 /**
  * Returns page counts that would be too "expensive" to retrieve by normal means.
  *
  * @param WikiPage|Article|Page $page
  * @return array
  */
 protected function pageCounts(Page $page)
 {
     $fname = __METHOD__;
     $config = $this->context->getConfig();
     return ObjectCache::getMainWANInstance()->getWithSetCallback(self::getCacheKey($page->getTitle(), $page->getLatest()), WANObjectCache::TTL_WEEK, function ($oldValue, &$ttl, &$setOpts) use($page, $config, $fname) {
         $title = $page->getTitle();
         $id = $title->getArticleID();
         $dbr = wfGetDB(DB_REPLICA);
         $dbrWatchlist = wfGetDB(DB_REPLICA, 'watchlist');
         $setOpts += Database::getCacheSetOptions($dbr, $dbrWatchlist);
         $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore();
         $result = [];
         $result['watchers'] = $watchedItemStore->countWatchers($title);
         if ($config->get('ShowUpdatedMarker')) {
             $updated = wfTimestamp(TS_UNIX, $page->getTimestamp());
             $result['visitingWatchers'] = $watchedItemStore->countVisitingWatchers($title, $updated - $config->get('WatchersMaxAge'));
         }
         // Total number of edits
         $edits = (int) $dbr->selectField('revision', 'COUNT(*)', ['rev_page' => $id], $fname);
         $result['edits'] = $edits;
         // Total number of distinct authors
         if ($config->get('MiserMode')) {
             $result['authors'] = 0;
         } else {
             $result['authors'] = (int) $dbr->selectField('revision', 'COUNT(DISTINCT rev_user_text)', ['rev_page' => $id], $fname);
         }
         // "Recent" threshold defined by RCMaxAge setting
         $threshold = $dbr->timestamp(time() - $config->get('RCMaxAge'));
         // Recent number of edits
         $edits = (int) $dbr->selectField('revision', 'COUNT(rev_page)', ['rev_page' => $id, "rev_timestamp >= " . $dbr->addQuotes($threshold)], $fname);
         $result['recent_edits'] = $edits;
         // Recent number of distinct authors
         $result['recent_authors'] = (int) $dbr->selectField('revision', 'COUNT(DISTINCT rev_user_text)', ['rev_page' => $id, "rev_timestamp >= " . $dbr->addQuotes($threshold)], $fname);
         // Subpages (if enabled)
         if (MWNamespace::hasSubpages($title->getNamespace())) {
             $conds = ['page_namespace' => $title->getNamespace()];
             $conds[] = 'page_title ' . $dbr->buildLike($title->getDBkey() . '/', $dbr->anyString());
             // Subpages of this page (redirects)
             $conds['page_is_redirect'] = 1;
             $result['subpages']['redirects'] = (int) $dbr->selectField('page', 'COUNT(page_id)', $conds, $fname);
             // Subpages of this page (non-redirects)
             $conds['page_is_redirect'] = 0;
             $result['subpages']['nonredirects'] = (int) $dbr->selectField('page', 'COUNT(page_id)', $conds, $fname);
             // Subpages of this page (total)
             $result['subpages']['total'] = $result['subpages']['redirects'] + $result['subpages']['nonredirects'];
         }
         // Counts for the number of transclusion links (to/from)
         if ($config->get('MiserMode')) {
             $result['transclusion']['to'] = 0;
         } else {
             $result['transclusion']['to'] = (int) $dbr->selectField('templatelinks', 'COUNT(tl_from)', ['tl_namespace' => $title->getNamespace(), 'tl_title' => $title->getDBkey()], $fname);
         }
         $result['transclusion']['from'] = (int) $dbr->selectField('templatelinks', 'COUNT(*)', ['tl_from' => $title->getArticleID()], $fname);
         return $result;
     });
 }
Example #12
0
 /**
  * Find the number of users in a given user group.
  * @param string $group Name of group
  * @return int
  */
 static function numberingroup($group)
 {
     return ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('SiteStats', 'groupcounts', $group), 3600, function ($oldValue, &$ttl, array &$setOpts) use($group) {
         $dbr = wfGetDB(DB_SLAVE);
         $setOpts += Database::getCacheSetOptions($dbr);
         return $dbr->selectField('user_groups', 'COUNT(*)', array('ug_group' => $group), __METHOD__);
     }, array('pcTTL' => 10));
 }
Example #13
0
 /**
  * Try to load file metadata from memcached, falling back to the database
  */
 private function loadFromCache()
 {
     $this->dataLoaded = false;
     $this->extraDataLoaded = false;
     $key = $this->getCacheKey();
     if (!$key) {
         $this->loadFromDB(self::READ_NORMAL);
         return;
     }
     $cache = ObjectCache::getMainWANInstance();
     $cachedValues = $cache->getWithSetCallback($key, $cache::TTL_WEEK, function ($oldValue, &$ttl, array &$setOpts) use($cache) {
         $setOpts += Database::getCacheSetOptions($this->repo->getSlaveDB());
         $this->loadFromDB(self::READ_NORMAL);
         $fields = $this->getCacheFields('');
         $cacheVal['fileExists'] = $this->fileExists;
         if ($this->fileExists) {
             foreach ($fields as $field) {
                 $cacheVal[$field] = $this->{$field};
             }
         }
         // Strip off excessive entries from the subset of fields that can become large.
         // If the cache value gets to large it will not fit in memcached and nothing will
         // get cached at all, causing master queries for any file access.
         foreach ($this->getLazyCacheFields('') as $field) {
             if (isset($cacheVal[$field]) && strlen($cacheVal[$field]) > 100 * 1024) {
                 unset($cacheVal[$field]);
                 // don't let the value get too big
             }
         }
         if ($this->fileExists) {
             $ttl = $cache->adaptiveTTL(wfTimestamp(TS_UNIX, $this->timestamp), $ttl);
         } else {
             $ttl = $cache::TTL_DAY;
         }
         return $cacheVal;
     }, ['version' => self::VERSION]);
     $this->fileExists = $cachedValues['fileExists'];
     if ($this->fileExists) {
         $this->setProps($cachedValues);
     }
     $this->dataLoaded = true;
     $this->extraDataLoaded = true;
     foreach ($this->getLazyCacheFields('') as $field) {
         $this->extraDataLoaded = $this->extraDataLoaded && isset($cachedValues[$field]);
     }
 }
 /**
  * @since 1.28
  * @param ResourceLoaderContext $context
  * @param IDatabase $db
  * @param string[] $moduleNames
  */
 public static function preloadTitleInfo(ResourceLoaderContext $context, IDatabase $db, array $moduleNames)
 {
     $rl = $context->getResourceLoader();
     // getDB() can be overridden to point to a foreign database.
     // For now, only preload local. In the future, we could preload by wikiID.
     $allPages = [];
     /** @var ResourceLoaderWikiModule[] $wikiModules */
     $wikiModules = [];
     foreach ($moduleNames as $name) {
         $module = $rl->getModule($name);
         if ($module instanceof self) {
             $mDB = $module->getDB();
             // Subclasses may disable getDB and implement getTitleInfo differently
             if ($mDB && $mDB->getWikiID() === $db->getWikiID()) {
                 $wikiModules[] = $module;
                 $allPages += $module->getPages($context);
             }
         }
     }
     $pageNames = array_keys($allPages);
     sort($pageNames);
     $hash = sha1(implode('|', $pageNames));
     // Avoid Zend bug where "static::" does not apply LSB in the closure
     $func = [static::class, 'fetchTitleInfo'];
     $fname = __METHOD__;
     $cache = ObjectCache::getMainWANInstance();
     $allInfo = $cache->getWithSetCallback($cache->makeGlobalKey('resourceloader', 'titleinfo', $db->getWikiID(), $hash), $cache::TTL_HOUR, function ($curVal, &$ttl, array &$setOpts) use($func, $pageNames, $db, $fname) {
         $setOpts += Database::getCacheSetOptions($db);
         return call_user_func($func, $db, $pageNames, $fname);
     }, ['checkKeys' => [$cache->makeGlobalKey('resourceloader', 'titleinfo', $db->getWikiID())]]);
     foreach ($wikiModules as $wikiModule) {
         $pages = $wikiModule->getPages($context);
         // Before we intersect, map the names to canonical form (T145673).
         $intersect = [];
         foreach ($pages as $page => $unused) {
             $title = Title::newFromText($page);
             if ($title) {
                 $intersect[$title->getPrefixedText()] = 1;
             } else {
                 // Page name may be invalid if user-provided (e.g. gadgets)
                 $rl->getLogger()->info('Invalid wiki page title "{title}" in ' . __METHOD__, ['title' => $page]);
             }
         }
         $info = array_intersect_key($allInfo, $intersect);
         $pageNames = array_keys($pages);
         sort($pageNames);
         $key = implode('|', $pageNames);
         $wikiModule->setTitleInfo($key, $info);
     }
 }
Example #15
0
 /**
  * Save the file metadata to memcached
  */
 function saveToCache()
 {
     $this->load();
     $key = $this->getCacheKey();
     if (!$key) {
         return;
     }
     $fields = $this->getCacheFields('');
     $cacheVal = array('version' => MW_FILE_VERSION);
     $cacheVal['fileExists'] = $this->fileExists;
     if ($this->fileExists) {
         foreach ($fields as $field) {
             $cacheVal[$field] = $this->{$field};
         }
     }
     // Strip off excessive entries from the subset of fields that can become large.
     // If the cache value gets to large it will not fit in memcached and nothing will
     // get cached at all, causing master queries for any file access.
     foreach ($this->getLazyCacheFields('') as $field) {
         if (isset($cacheVal[$field]) && strlen($cacheVal[$field]) > 100 * 1024) {
             unset($cacheVal[$field]);
             // don't let the value get too big
         }
     }
     // Cache presence for 1 week and negatives for 1 day
     $ttl = $this->fileExists ? 86400 * 7 : 86400;
     $opts = Database::getCacheSetOptions($this->repo->getSlaveDB());
     ObjectCache::getMainWANInstance()->set($key, $cacheVal, $ttl, $opts);
 }
Example #16
0
 /**
  * Find the number of users in a given user group.
  * @param string $group Name of group
  * @return int
  */
 static function numberingroup($group)
 {
     $cache = ObjectCache::getMainWANInstance();
     return $cache->getWithSetCallback(wfMemcKey('SiteStats', 'groupcounts', $group), $cache::TTL_HOUR, function ($oldValue, &$ttl, array &$setOpts) use($group) {
         $dbr = wfGetDB(DB_REPLICA);
         $setOpts += Database::getCacheSetOptions($dbr);
         return $dbr->selectField('user_groups', 'COUNT(*)', ['ug_group' => $group], __METHOD__);
     }, ['pcTTL' => $cache::TTL_PROC_LONG]);
 }
Example #17
0
 /**
  * @since 1.27
  * @param string $cacheKey
  * @param ResourceLoaderModule $module
  * @param string $lang
  * @return string JSON blob
  */
 protected function recacheMessageBlob($cacheKey, ResourceLoaderModule $module, $lang)
 {
     $blob = $this->generateMessageBlob($module, $lang);
     $cache = $this->wanCache;
     $cache->set($cacheKey, $blob, $cache::TTL_WEEK + mt_rand(0, $cache::TTL_DAY), Database::getCacheSetOptions(wfGetDB(DB_SLAVE)));
     return $blob;
 }
Example #18
0
 /**
  * Load the interwiki, trying first memcached then the DB
  *
  * @param string $prefix The interwiki prefix
  * @return Interwiki|bool Interwiki if $prefix is valid, otherwise false
  */
 protected static function load($prefix)
 {
     global $wgInterwikiExpiry;
     $iwData = array();
     if (!Hooks::run('InterwikiLoadPrefix', array($prefix, &$iwData))) {
         return Interwiki::loadFromArray($iwData);
     }
     if (is_array($iwData)) {
         $iw = Interwiki::loadFromArray($iwData);
         if ($iw) {
             return $iw;
             // handled by hook
         }
     }
     $iwData = ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('interwiki', $prefix), $wgInterwikiExpiry, function ($oldValue, &$ttl, array &$setOpts) use($prefix) {
         $dbr = wfGetDB(DB_SLAVE);
         $setOpts += Database::getCacheSetOptions($dbr);
         $row = $dbr->selectRow('interwiki', Interwiki::selectFields(), array('iw_prefix' => $prefix), __METHOD__);
         return $row ? (array) $row : '!NONEXISTENT';
     });
     if (is_array($iwData)) {
         return Interwiki::loadFromArray($iwData) ?: false;
     }
     return false;
 }
Example #19
0
 /**
  * Returns a map of any tags used on the wiki to number of edits
  * tagged with them, ordered descending by the hitcount.
  * This does not include tags defined somewhere that have never been applied.
  *
  * Keeps a short-term cache in memory, so calling this multiple times in the
  * same request should be fine.
  *
  * @return array Array of string => int
  */
 public static function tagUsageStatistics()
 {
     static $cachedStats = null;
     // Process cache to avoid I/O and repeated regens during holdoff
     if ($cachedStats !== null) {
         return $cachedStats;
     }
     $fname = __METHOD__;
     $cachedStats = ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('change-tag-statistics'), 300, function ($oldValue, &$ttl, array &$setOpts) use($fname) {
         $dbr = wfGetDB(DB_SLAVE, 'vslow');
         $setOpts += Database::getCacheSetOptions($dbr);
         $res = $dbr->select('change_tag', array('ct_tag', 'hitcount' => 'count(*)'), array(), $fname, array('GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC'));
         $out = array();
         foreach ($res as $row) {
             $out[$row->ct_tag] = $row->hitcount;
         }
         return $out;
     }, array('checkKeys' => array(wfMemcKey('change-tag-statistics')), 'lockTSE' => INF));
     return $cachedStats;
 }
Example #20
0
 /**
  * Load user data from shared cache, given mId has already been set.
  *
  * @return bool True
  * @since 1.25
  */
 protected function loadFromCache()
 {
     $cache = ObjectCache::getMainWANInstance();
     $data = $cache->getWithSetCallback($this->getCacheKey($cache), $cache::TTL_HOUR, function ($oldValue, &$ttl, array &$setOpts) use($cache) {
         $setOpts += Database::getCacheSetOptions(wfGetDB(DB_REPLICA));
         wfDebug("User: cache miss for user {$this->mId}\n");
         $this->loadFromDatabase(self::READ_NORMAL);
         $this->loadGroups();
         $this->loadOptions();
         $data = [];
         foreach (self::$mCacheVars as $name) {
             $data[$name] = $this->{$name};
         }
         $ttl = $cache->adaptiveTTL(wfTimestamp(TS_UNIX, $this->mTouched), $ttl);
         return $data;
     }, ['pcTTL' => $cache::TTL_PROC_LONG, 'version' => self::VERSION]);
     // Restore from cache
     foreach (self::$mCacheVars as $name) {
         $this->{$name} = $data[$name];
     }
     return true;
 }