/** * @params include: * - objectCache : Name of an object cache registered in $wgObjectCaches. * This defaults to the one specified by $wgMainCacheType. * - cacheTTL : Seconds to cache the aggregate data before regenerating. * @param array $params */ protected function __construct(array $params) { parent::__construct($params); $this->cache = isset($params['objectCache']) ? wfGetCache($params['objectCache']) : wfGetMainCache(); $this->cacheTTL = isset($params['cacheTTL']) ? $params['cacheTTL'] : 180; // 3 min }
/** * Prepares the environment before running a test. */ protected function setUp() { global $wgMemc; parent::setUp(); $this->user = User::newFromName("someReviewer"); $wgMemc = wfGetCache(CACHE_DB); }
/** * Get contents of a javascript file for inline use. * * Roughly based MediaWiki core methods: * - ResourceLoader::filter() * - ResourceLoaderFileModule::readScriptFiles() * * @param string $name Path to file relative to /modules/inline/ * @return string Minified script * @throws Exception If file doesn't exist */ protected static function getInlineScript($name) { // Get file $filePath = __DIR__ . '/../../modules/inline/' . $name; if (!file_exists($filePath)) { throw new Exception(__METHOD__ . ": file not found: \"{$filePath}\""); } $contents = file_get_contents($filePath); // Try minified from cache $key = wfMemcKey('centralauth', 'minify-js', md5($contents)); $cache = wfGetCache(CACHE_ANYTHING); $cacheEntry = $cache->get($key); if (is_string($cacheEntry)) { return $cacheEntry; } // Compute new value $result = ''; try { $result = JavaScriptMinifier::minify($contents) . "\n/* cache key: {$key} */"; $cache->set($key, $result); } catch (Exception $e) { MWExceptionHandler::logException($e); wfDebugLog('CentralAuth', __METHOD__ . ": minification failed for {$name}: {$e}"); $result = ResourceLoader::formatException($e) . "\n" . $contents; } return $result; }
/** * @since 1.21 * @deprecated 1.25 Construct a SiteStore instance directly instead. * * @param ORMTable|null $sitesTable * @param BagOStuff|null $cache * * @return SiteStore */ public static function newInstance(ORMTable $sitesTable = null, BagOStuff $cache = null) { if ($cache === null) { $cache = wfGetCache(wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING); } $siteStore = new DBSiteStore(); return new static($siteStore, $cache); }
/** * Constructor. * @param string $title * @param integer $revision * @param ObjectCache $cache: (optional) cache client. * @param Http $http: (optional) HTTP client. */ function __construct($title, $revision, $cache = NULL, $http = NULL) { global $wgEventLoggingDBname; $this->title = $title; $this->revision = $revision; $this->cache = $cache ?: wfGetCache(CACHE_ANYTHING); $this->http = $http ?: new Http(); $this->key = "schema:{$wgEventLoggingDBname}:{$title}:{$revision}"; }
function __construct($memCached, $useDB, $expiry) { if (!$memCached) { $memCached = wfGetCache(CACHE_NONE); } $this->mMemc = $memCached; $this->mDisable = !$useDB; $this->mExpiry = $expiry; }
protected function setUp() { parent::setUp(); global $wgHooks; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgTranslateTranslationServices' => array())); $wgHooks['TranslatePostInitGroups'] = array(array($this, 'getTestGroups')); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); }
function run() { global $wgUser; // Initialization $title = $this->title; // Other stuff $user = $this->getUser(); $summary = $this->getSummary(); $base = $this->getBase(); PageTranslationHooks::$allowTargetEdit = true; $oldUser = $wgUser; $wgUser = $user; $error = ''; $article = new Article( $title, 0 ); $ok = $article->doDeleteArticle( $summary, false, 0, true, $error ); if ( !$ok ) { $logger = new LogPage( 'pagetranslation' ); $params = array( 'user' => $this->getPerformer(), 'target' => $base, 'error' => base64_encode( serialize( $ok ) ), // This is getting ridiculous ); $doer = User::newFromName( $this->getPerformer() ); $msg = $this->getFull() ? 'deletefnok' : 'deletelnok'; $logger->addEntry( $msg, $title, null, array( serialize( $params ) ), $doer ); } PageTranslationHooks::$allowTargetEdit = false; $cache = wfGetCache( CACHE_DB ); $pages = (array) $cache->get( wfMemcKey( 'pt-base', $base ) ); $lastitem = array_pop( $pages ); if ( $title->getPrefixedText() === $lastitem ) { $cache->delete( wfMemcKey( 'pt-base', $base ) ); $logger = new LogPage( 'pagetranslation' ); $params = array( 'user' => $this->getPerformer() ); $doer = User::newFromName( $this->getPerformer() ); $msg = $this->getFull() ? 'deletefok' : 'deletelok'; $logger->addEntry( $msg, Title::newFromText( $base ), null, array( serialize( $params ) ), $doer ); $tpage = TranslatablePage::newFromTitle( $title ); $tpage->getTranslationPercentages( true ); foreach ( $tpage->getTranslationPages() as $page ) { $page->invalidateCache(); } $title->invalidateCache(); } $wgUser = $oldUser; return true; }
protected function setUp() { parent::setUp(); global $wgHooks; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgGroupPermissions' => array(), 'wgTranslateMessageNamespaces' => array(NS_MEDIAWIKI))); $wgHooks['TranslatePostInitGroups'] = array(array($this, 'getTestGroups')); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); MessageIndex::setInstance(new HashMessageIndex()); MessageIndex::singleton()->rebuild(); }
public function setUp() { parent::setUp(); global $wgHooks; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgTranslateTranslationServices' => array(), 'wgTranslateDelayedMessageIndexRebuild' => false)); $wgHooks['TranslatePostInitGroups'] = array(); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); MessageIndex::setInstance(new HashMessageIndex()); MessageIndex::singleton()->rebuild(); }
protected function setUp() { parent::setUp(); global $wgHooks; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgTranslateTranslationServices' => array(), 'wgTranslateCacheDirectory' => $this->getNewTempDirectory())); $wgHooks['TranslatePostInitGroups'] = array(); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); MessageIndex::setInstance(new HashMessageIndex()); MessageIndex::singleton()->rebuild(); }
/** * Implement a rudimentary rate limiting system, * we can't use User::pingLImiter() because stewards * have the "noratelimit" userright * * Hardcoded to allow 1 merge per 60 seconds * * @return bool true if we should let the user proceed */ private function checkRateLimit() { $cache = wfGetCache(CACHE_ANYTHING); $key = 'centralauth:usermerge:' . md5($this->getUser()->getName()); $found = $cache->get($key); if ($found === false) { $cache->set($key, true, 60); return true; } else { return false; } }
protected function setUp() { parent::setUp(); $conf = array(__DIR__ . '/data/ParentGroups.yaml'); global $wgHooks; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgTranslateGroupFiles' => $conf, 'wgTranslateTranslationServices' => array())); $wgHooks['TranslatePostInitGroups'] = array('MessageGroups::getConfiguredGroups'); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); MessageIndex::setInstance(new HashMessageIndex()); MessageIndex::singleton()->rebuild(); }
protected function setUp() { parent::setUp(); global $wgHooks, $wgTranslateTranslationServices, $wgTranslateTestTTMServer; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgTranslateTranslationServices' => array())); $wgTranslateTranslationServices['TTMServer'] = $wgTranslateTestTTMServer; $wgHooks['TranslatePostInitGroups'] = array(array($this, 'addGroups')); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); MessageIndex::setInstance(new HashMessageIndex()); MessageIndex::singleton()->rebuild(); }
function __construct() { $this->cache = wfGetCache(CACHE_ANYTHING); // if (HuijiPrefix::hasPrefix($prefix)){ // $this->mPrefix = $prefix; // } else { // $this->mPrefix = ''; // } // $this->cache = wfGetCache( CACHE_ANYTHING ); // $this->loadFromRow(); // $siteCache = self::getSiteCache(); // $siteCache->set($this->mPrefix, $this); }
protected function setUp() { parent::setUp(); global $wgHooks; $this->setMwGlobals(array('wgHooks' => $wgHooks, 'wgEnablePageTranslation' => true, 'wgTranslateTranslationServices' => array())); TranslateHooks::setupTranslate(); $wgHooks['TranslatePostInitGroups'] = array('MessageGroups::getTranslatablePages'); $mg = MessageGroups::singleton(); $mg->setCache(wfGetCache('hash')); $mg->recache(); MessageIndex::setInstance(new HashMessageIndex()); MessageIndex::singleton()->rebuild(); }
public static function updateCache() { global $wgFlaggedRevsStatsAge; $rNamespaces = FlaggedRevs::getReviewNamespaces(); if (empty($rNamespaces)) { return; // no SQL errors please :) } // Set key to limit duplicate updates... $dbCache = wfGetCache(CACHE_DB); $keySQL = wfMemcKey('flaggedrevs', 'statsUpdating'); $dbCache->set($keySQL, '1', $wgFlaggedRevsStatsAge); // Get total, reviewed, and synced page count for each namespace list($ns_total, $ns_reviewed, $ns_synced) = self::getPerNamespaceTotals(); // Getting mean pending edit time // @TODO: percentiles? $avePET = self::getMeanPendingEditTime(); # Get wait (till review) time samples for anon edits... $reviewData = self::getEditReviewTimes($dbCache, 'anons'); $dbw = wfGetDB(DB_MASTER); // The timestamp to identify this whole batch of data $encDataTimestamp = $dbw->timestamp(); $dataSet = array(); $dataSet[] = array('frs_stat_key' => 'reviewLag-sampleStartTimestamp', 'frs_stat_val' => $reviewData['sampleStartTS'], 'frs_timestamp' => $encDataTimestamp); $dataSet[] = array('frs_stat_key' => 'reviewLag-sampleEndTimestamp', 'frs_stat_val' => $reviewData['sampleEndTS'], 'frs_timestamp' => $encDataTimestamp); // All-namespace percentiles... foreach ($reviewData['percTable'] as $percentile => $seconds) { $dataSet[] = array('frs_stat_key' => 'reviewLag-percentile:' . (int) $percentile, 'frs_stat_val' => $seconds, 'frs_timestamp' => $encDataTimestamp); } // Sample size... $dataSet[] = array('frs_stat_key' => 'reviewLag-sampleSize', 'frs_stat_val' => $reviewData['sampleSize'], 'frs_timestamp' => $encDataTimestamp); // All-namespace ave/med review lag & ave pending lag stats... $dataSet[] = array('frs_stat_key' => 'reviewLag-average', 'frs_stat_val' => $reviewData['average'], 'frs_timestamp' => $encDataTimestamp); $dataSet[] = array('frs_stat_key' => 'reviewLag-median', 'frs_stat_val' => $reviewData['median'], 'frs_timestamp' => $encDataTimestamp); $dataSet[] = array('frs_stat_key' => 'pendingLag-average', 'frs_stat_val' => $avePET, 'frs_timestamp' => $encDataTimestamp); // Per-namespace total/reviewed/synced stats... foreach ($rNamespaces as $namespace) { $dataSet[] = array('frs_stat_key' => 'totalPages-NS:' . (int) $namespace, 'frs_stat_val' => isset($ns_total[$namespace]) ? $ns_total[$namespace] : 0, 'frs_timestamp' => $encDataTimestamp); $dataSet[] = array('frs_stat_key' => 'reviewedPages-NS:' . (int) $namespace, 'frs_stat_val' => isset($ns_reviewed[$namespace]) ? $ns_reviewed[$namespace] : 0, 'frs_timestamp' => $encDataTimestamp); $dataSet[] = array('frs_stat_key' => 'syncedPages-NS:' . (int) $namespace, 'frs_stat_val' => isset($ns_synced[$namespace]) ? $ns_synced[$namespace] : 0, 'frs_timestamp' => $encDataTimestamp); } // Save the data... $dbw->begin(); $dbw->insert('flaggedrevs_statistics', $dataSet, __FUNCTION__, array('IGNORE')); $dbw->commit(); // Stats are now up to date! $key = wfMemcKey('flaggedrevs', 'statsUpdated'); $dbCache->set($key, '1', $wgFlaggedRevsStatsAge); $dbCache->delete($keySQL); }
/** * @param array $params * @throws MWException */ protected function __construct(array $params) { $this->wiki = $params['wiki']; $this->type = $params['type']; $this->claimTTL = isset($params['claimTTL']) ? $params['claimTTL'] : 0; $this->maxTries = isset($params['maxTries']) ? $params['maxTries'] : 3; if (isset($params['order']) && $params['order'] !== 'any') { $this->order = $params['order']; } else { $this->order = $this->optimalOrder(); } if (!in_array($this->order, $this->supportedOrders())) { throw new MWException(__CLASS__ . " does not support '{$this->order}' order."); } $this->dupCache = wfGetCache(CACHE_ANYTHING); $this->aggr = isset($params['aggregator']) ? $params['aggregator'] : new JobQueueAggregatorNull(array()); }
function run() { // Initialization $title = $this->title; // Other stuff $user = $this->getUser(); $summary = $this->getSummary(); $base = $this->getBase(); $doer = User::newFromName($this->getPerformer()); PageTranslationHooks::$allowTargetEdit = true; $error = ''; $wikipage = new WikiPage($title); $ok = $wikipage->doDeleteArticle($summary, false, 0, true, $error, $user); if (!$ok) { $params = array('target' => $base, 'error' => $ok); $type = $this->getFull() ? 'deletefnok' : 'deletelnok'; $entry = new ManualLogEntry('pagetranslation', $type); $entry->setPerformer($doer); $entry->setTarget($title); $entry->setParameters($params); $logid = $entry->insert(); $entry->publish($logid); } PageTranslationHooks::$allowTargetEdit = false; $cache = wfGetCache(CACHE_DB); $pages = (array) $cache->get(wfMemcKey('pt-base', $base)); $lastitem = array_pop($pages); if ($title->getPrefixedText() === $lastitem) { $cache->delete(wfMemcKey('pt-base', $base)); $type = $this->getFull() ? 'deletefok' : 'deletelok'; $entry = new ManualLogEntry('pagetranslation', $type); $entry->setPerformer($doer); $entry->setTarget(Title::newFromText($base)); $logid = $entry->insert(); $entry->publish($logid); $tpage = TranslatablePage::newFromTitle($title); $tpage->getTranslationPercentages(true); foreach ($tpage->getTranslationPages() as $page) { $page->invalidateCache(); } $title->invalidateCache(); } return true; }
/** * @param $params array */ protected function __construct( array $params ) { $this->wiki = $params['wiki']; $this->type = $params['type']; $this->claimTTL = isset( $params['claimTTL'] ) ? $params['claimTTL'] : 0; $this->maxTries = isset( $params['maxTries'] ) ? $params['maxTries'] : 3; if ( isset( $params['order'] ) && $params['order'] !== 'any' ) { $this->order = $params['order']; } else { $this->order = $this->optimalOrder(); } if ( !in_array( $this->order, $this->supportedOrders() ) ) { throw new MWException( __CLASS__ . " does not support '{$this->order}' order." ); } $this->checkDelay = !empty( $params['checkDelay'] ); if ( $this->checkDelay && !$this->supportsDelayedJobs() ) { throw new MWException( __CLASS__ . " does not support delayed jobs." ); } $this->dupCache = wfGetCache( CACHE_ANYTHING ); }
public function __construct() { $this->cache = wfGetCache(CACHE_ACCEL); }
/** * @param BagOStuff $memCached A cache instance. If none, fall back to CACHE_NONE. * @param bool $useDB * @param int $expiry Lifetime for cache. @see $mExpiry. */ function __construct($memCached, $useDB, $expiry) { global $wgUseLocalMessageCache; if (!$memCached) { $memCached = wfGetCache(CACHE_NONE); } $this->mMemc = $memCached; $this->mDisable = !$useDB; $this->mExpiry = $expiry; if ($wgUseLocalMessageCache) { $this->localCache = ObjectCache::newAccelerator(CACHE_NONE); } else { $this->localCache = wfGetCache(CACHE_NONE); } $this->wanCache = ObjectCache::getMainWANInstance(); }
/** Get the cache object used by the parser cache */ function &wfGetParserCacheStorage() { global $wgParserCacheType; $ret =& wfGetCache($wgParserCacheType); return $ret; }
/** * Validate a given script file; if valid returns the original source. * If invalid, returns replacement JS source that throws an exception. * * @param string $fileName * @param string $contents * @return string JS with the original, or a replacement error */ protected function validateScriptFile($fileName, $contents) { global $wgResourceLoaderValidateJS; if ($wgResourceLoaderValidateJS) { // Try for cache hit // Use CACHE_ANYTHING since filtering is very slow compared to DB queries $key = wfMemcKey('resourceloader', 'jsparse', self::$parseCacheVersion, md5($contents)); $cache = wfGetCache(CACHE_ANYTHING); $cacheEntry = $cache->get($key); if (is_string($cacheEntry)) { return $cacheEntry; } $parser = self::javaScriptParser(); try { $parser->parse($contents, $fileName, 1); $result = $contents; } catch (Exception $e) { // We'll save this to cache to avoid having to validate broken JS over and over... $err = $e->getMessage(); $result = "throw new Error(" . Xml::encodeJsVar("JavaScript parse error: {$err}") . ");"; } $cache->set($key, $result); return $result; } else { return $contents; } }
/** * Run JavaScript or CSS data through a filter, caching the filtered result for future calls. * * Available filters are: * * - minify-js \see JavaScriptMinifier::minify * - minify-css \see CSSMin::minify * * If $data is empty, only contains whitespace or the filter was unknown, * $data is returned unmodified. * * @param string $filter Name of filter to run * @param string $data Text to filter, such as JavaScript or CSS text * @param array $options For back-compat, can also be the boolean value for "cacheReport". Keys: * - (bool) cache: Whether to allow caching this data. Default: true. * - (bool) cacheReport: Whether to include the "cache key" report comment. Default: false. * @return string Filtered data, or a comment containing an error message */ public function filter($filter, $data, $options = array()) { // Back-compat if (is_bool($options)) { $options = array('cacheReport' => $options); } // Defaults $options += array('cache' => true, 'cacheReport' => false); $stats = RequestContext::getMain()->getStats(); // Don't filter empty content if (trim($data) === '') { return $data; } if (!in_array($filter, array('minify-js', 'minify-css'))) { $this->logger->warning('Invalid filter {filter}', array('filter' => $filter)); return $data; } if (!$options['cache']) { $result = self::applyFilter($filter, $data, $this->config); } else { $key = wfGlobalCacheKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data)); $cache = wfGetCache(wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING); $cacheEntry = $cache->get($key); if (is_string($cacheEntry)) { $stats->increment("resourceloader_cache.{$filter}.hit"); return $cacheEntry; } $result = ''; try { $statStart = microtime(true); $result = self::applyFilter($filter, $data, $this->config); $statTiming = microtime(true) - $statStart; $stats->increment("resourceloader_cache.{$filter}.miss"); $stats->timing("resourceloader_cache.{$filter}.timing", 1000 * $statTiming); if ($options['cacheReport']) { $result .= "\n/* cache key: {$key} */"; } // Set a TTL since HHVM's APC doesn't have any limitation or eviction logic. $cache->set($key, $result, 24 * 3600); } catch (Exception $e) { MWExceptionHandler::logException($e); $this->logger->warning('Minification failed: {exception}', array('exception' => $e)); $this->errors[] = self::formatExceptionNoComment($e); } } return $result; }
protected function initChild() { global $wgMemc, $wgMainCacheType; $wgMemc = wfGetCache($wgMainCacheType); $this->children = null; pcntl_signal(SIGTERM, SIG_DFL); }
/** * Run JavaScript or CSS data through a filter, caching the filtered result for future calls. * * Available filters are: * * - minify-js \see JavaScriptMinifier::minify * - minify-css \see CSSMin::minify * * If $data is empty, only contains whitespace or the filter was unknown, * $data is returned unmodified. * * @param string $filter Name of filter to run * @param string $data Text to filter, such as JavaScript or CSS text * @param string $cacheReport Whether to include the cache key report * @return string Filtered data, or a comment containing an error message */ public function filter($filter, $data, $cacheReport = true) { // For empty/whitespace-only data or for unknown filters, don't perform // any caching or processing if (trim($data) === '' || !in_array($filter, array('minify-js', 'minify-css'))) { return $data; } // Try for cache hit // Use CACHE_ANYTHING since filtering is very slow compared to DB queries $key = wfMemcKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data)); $cache = wfGetCache(CACHE_ANYTHING); $cacheEntry = $cache->get($key); if (is_string($cacheEntry)) { wfIncrStats("rl-{$filter}-cache-hits"); return $cacheEntry; } $result = ''; // Run the filter - we've already verified one of these will work try { wfIncrStats("rl-{$filter}-cache-misses"); switch ($filter) { case 'minify-js': $result = JavaScriptMinifier::minify($data, $this->config->get('ResourceLoaderMinifierStatementsOnOwnLine'), $this->config->get('ResourceLoaderMinifierMaxLineLength')); if ($cacheReport) { $result .= "\n/* cache key: {$key} */"; } break; case 'minify-css': $result = CSSMin::minify($data); if ($cacheReport) { $result .= "\n/* cache key: {$key} */"; } break; } // Save filtered text to Memcached $cache->set($key, $result); } catch (Exception $e) { MWExceptionHandler::logException($e); wfDebugLog('resourceloader', __METHOD__ . ": minification failed: {$e}"); $this->errors[] = self::formatExceptionNoComment($e); } return $result; }
/** * Runs JavaScript or CSS data through a filter, caching the filtered result for future calls. * * Available filters are: * - minify-js \see JavaScriptMinifier::minify * - minify-css \see CSSMin::minify * * If $data is empty, only contains whitespace or the filter was unknown, * $data is returned unmodified. * * @param $filter String: Name of filter to run * @param $data String: Text to filter, such as JavaScript or CSS text * @return String: Filtered data, or a comment containing an error message */ protected function filter($filter, $data) { global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength; wfProfileIn(__METHOD__); // For empty/whitespace-only data or for unknown filters, don't perform // any caching or processing if (trim($data) === '' || !in_array($filter, array('minify-js', 'minify-css'))) { wfProfileOut(__METHOD__); return $data; } // Try for cache hit // Use CACHE_ANYTHING since filtering is very slow compared to DB queries $key = wfMemcKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data)); $cache = wfGetCache(CACHE_ANYTHING); $cacheEntry = $cache->get($key); if (is_string($cacheEntry)) { wfProfileOut(__METHOD__); return $cacheEntry; } $result = ''; // Run the filter - we've already verified one of these will work try { switch ($filter) { case 'minify-js': $result = JavaScriptMinifier::minify($data, $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength); $result .= "\n/* cache key: {$key} */"; break; case 'minify-css': $result = CSSMin::minify($data); $result .= "\n/* cache key: {$key} */"; break; } // Save filtered text to Memcached $cache->set($key, $result); } catch (Exception $exception) { // Return exception as a comment $result = $this->makeComment($exception->__toString()); } wfProfileOut(__METHOD__); return $result; }
function getFirstLetterData() { if ($this->firstLetterData !== null) { return $this->firstLetterData; } $cache = wfGetCache(CACHE_ANYTHING); $cacheKey = wfMemcKey('first-letters', $this->locale, $this->digitTransformLanguage->getCode(), self::getICUVersion()); $cacheEntry = $cache->get($cacheKey); if ($cacheEntry && isset($cacheEntry['version']) && $cacheEntry['version'] == self::FIRST_LETTER_VERSION) { $this->firstLetterData = $cacheEntry; return $this->firstLetterData; } // Generate data from serialized data file if (isset(self::$tailoringFirstLetters[$this->locale])) { $letters = wfGetPrecompiledData("first-letters-root.ser"); // Append additional characters $letters = array_merge($letters, self::$tailoringFirstLetters[$this->locale]); // Remove unnecessary ones, if any if (isset(self::$tailoringFirstLetters['-' . $this->locale])) { $letters = array_diff($letters, self::$tailoringFirstLetters['-' . $this->locale]); } // Apply digit transforms $digits = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); $letters = array_diff($letters, $digits); foreach ($digits as $digit) { $letters[] = $this->digitTransformLanguage->formatNum($digit, true); } } else { $letters = wfGetPrecompiledData("first-letters-{$this->locale}.ser"); if ($letters === false) { throw new MWException("MediaWiki does not support ICU locale " . "\"{$this->locale}\""); } } /* Sort the letters. * * It's impossible to have the precompiled data file properly sorted, * because the sort order changes depending on ICU version. If the * array is not properly sorted, the binary search will return random * results. * * We also take this opportunity to remove primary collisions. */ $letterMap = array(); foreach ($letters as $letter) { $key = $this->getPrimarySortKey($letter); if (isset($letterMap[$key])) { // Primary collision // Keep whichever one sorts first in the main collator if ($this->mainCollator->compare($letter, $letterMap[$key]) < 0) { $letterMap[$key] = $letter; } } else { $letterMap[$key] = $letter; } } ksort($letterMap, SORT_STRING); /* Remove duplicate prefixes. Basically if something has a sortkey * which is a prefix of some other sortkey, then it is an * expansion and probably should not be considered a section * header. * * For example 'þ' is sometimes sorted as if it is the letters * 'th'. Other times it is its own primary element. Another * example is '₨'. Sometimes its a currency symbol. Sometimes it * is an 'R' followed by an 's'. * * Additionally an expanded element should always sort directly * after its first element due to they way sortkeys work. * * UCA sortkey elements are of variable length but no collation * element should be a prefix of some other element, so I think * this is safe. See: * - https://ssl.icu-project.org/repos/icu/icuhtml/trunk/design/collation/ICU_collation_design.htm * - http://site.icu-project.org/design/collation/uca-weight-allocation * * Additionally, there is something called primary compression to * worry about. Basically, if you have two primary elements that * are more than one byte and both start with the same byte then * the first byte is dropped on the second primary. Additionally * either \x03 or \xFF may be added to mean that the next primary * does not start with the first byte of the first primary. * * This shouldn't matter much, as the first primary is not * changed, and that is what we are comparing against. * * tl;dr: This makes some assumptions about how icu implements * collations. It seems incredibly unlikely these assumptions * will change, but nonetheless they are assumptions. */ $prev = false; $duplicatePrefixes = array(); foreach ($letterMap as $key => $value) { // Remove terminator byte. Otherwise the prefix // comparison will get hung up on that. $trimmedKey = rtrim($key, ""); if ($prev === false || $prev === '') { $prev = $trimmedKey; // We don't yet have a collation element // to compare against, so continue. continue; } // Due to the fact the array is sorted, we only have // to compare with the element directly previous // to the current element (skipping expansions). // An element "X" will always sort directly // before "XZ" (Unless we have "XY", but we // do not update $prev in that case). if (substr($trimmedKey, 0, strlen($prev)) === $prev) { $duplicatePrefixes[] = $key; // If this is an expansion, we don't want to // compare the next element to this element, // but to what is currently $prev continue; } $prev = $trimmedKey; } foreach ($duplicatePrefixes as $badKey) { wfDebug("Removing '{$letterMap[$badKey]}' from first letters.\n"); unset($letterMap[$badKey]); // This code assumes that unsetting does not change sort order. } $data = array('chars' => array_values($letterMap), 'keys' => array_keys($letterMap), 'version' => self::FIRST_LETTER_VERSION); // Reduce memory usage before caching unset($letterMap); // Save to cache $this->firstLetterData = $data; $cache->set($cacheKey, $data, $cache::TTL_WEEK); return $data; }
/** * Runs JavaScript or CSS data through a filter, caching the filtered result for future calls. * * Available filters are: * - minify-js \see JavaScriptMinifier::minify * - minify-css \see CSSMin::minify * * If $data is empty, only contains whitespace or the filter was unknown, * $data is returned unmodified. * * @param $filter String: Name of filter to run * @param $data String: Text to filter, such as JavaScript or CSS text * @return String: Filtered data, or a comment containing an error message */ protected function filter($filter, $data) { global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength; wfProfileIn(__METHOD__); // For empty/whitespace-only data or for unknown filters, don't perform // any caching or processing if (trim($data) === '' || !in_array($filter, array('minify-js', 'minify-css'))) { wfProfileOut(__METHOD__); return $data; } // Try for cache hit // Use CACHE_ANYTHING since filtering is very slow compared to DB queries $key = wfMemcKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data)); $cache = wfGetCache(CACHE_ANYTHING); $cacheEntry = $cache->get($key); if (is_string($cacheEntry)) { wfProfileOut(__METHOD__); return $cacheEntry; } $result = ''; // Run the filter - we've already verified one of these will work try { switch ($filter) { case 'minify-js': // Wikia - change begin - @author: wladek global $wgResourceLoaderJavascriptMinifier; $minifierCallback = 'JavaScriptMinifier::minify'; if (!empty($wgResourceLoaderJavascriptMinifier)) { $minifierCallback = $wgResourceLoaderJavascriptMinifier; } if (!is_callable($minifierCallback)) { throw new MWException('ResourceLoader invalid javascript minifier: ' . $minifierCallback); } $result = call_user_func($minifierCallback, $data, $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength); // Wikia - change end $result .= "\n\n/* cache key: {$key} */\n"; break; case 'minify-css': // Wikia - change begin - @author: wladek global $wgResourceLoaderCssMinifier; $minifierCallback = 'CSSMin::minify'; if (!empty($wgResourceLoaderCssMinifier)) { $minifierCallback = $wgResourceLoaderCssMinifier; } if (!is_callable($minifierCallback)) { throw new MWException('ResourceLoader invalid css minifier: ' . $minifierCallback); } $result = call_user_func($minifierCallback, $data); // Wikia - change end $result .= "\n\n/* cache key: {$key} */\n"; break; } // Save filtered text to Memcached $cache->set($key, $result); } catch (Exception $exception) { // Return exception as a comment $result = $this->formatException($exception); } wfProfileOut(__METHOD__); return $result; }