public function __construct() { // We use a try/catch instead of the $fallback parameter because // we don't want to fail here if $wgObjectCaches is not configured // properly for APC setup try { $this->cache = ObjectCache::getLocalServerInstance(); } catch (MWException $e) { $this->cache = new EmptyBagOStuff(); } }
/** * Returns a given template function if found, otherwise throws an exception. * @param string $templateName The name of the template (without file suffix) * @return callable * @throws RuntimeException */ protected function getTemplate($templateName) { // If a renderer has already been defined for this template, reuse it if (isset($this->renderers[$templateName]) && is_callable($this->renderers[$templateName])) { return $this->renderers[$templateName]; } $filename = $this->getTemplateFilename($templateName); if (!file_exists($filename)) { throw new RuntimeException("Could not locate template: {$filename}"); } // Read the template file $fileContents = file_get_contents($filename); // Generate a quick hash for cache invalidation $fastHash = md5($fileContents); // Fetch a secret key for building a keyed hash of the PHP code $config = ConfigFactory::getDefaultInstance()->makeConfig('main'); $secretKey = $config->get('SecretKey'); if ($secretKey) { // See if the compiled PHP code is stored in cache. $cache = ObjectCache::getLocalServerInstance(CACHE_ANYTHING); $key = $cache->makeKey('template', $templateName, $fastHash); $code = $this->forceRecompile ? null : $cache->get($key); if (!$code) { $code = $this->compileForEval($fileContents, $filename); // Prefix the cached code with a keyed hash (64 hex chars) as an integrity check $cache->set($key, hash_hmac('sha256', $code, $secretKey) . $code); } else { // Verify the integrity of the cached PHP code $keyedHash = substr($code, 0, 64); $code = substr($code, 64); if ($keyedHash !== hash_hmac('sha256', $code, $secretKey)) { // Generate a notice if integrity check fails trigger_error("Template failed integrity check: {$filename}"); } } // If there is no secret key available, don't use cache } else { $code = $this->compileForEval($fileContents, $filename); } $renderer = eval($code); if (!is_callable($renderer)) { throw new RuntimeException("Requested template, {$templateName}, is not callable"); } $this->renderers[$templateName] = $renderer; return $renderer; }
/** * Return IDs that are sequential *only* for this node and bucket * * @see UIDGenerator::newSequentialPerNodeID() * @param string $bucket Arbitrary bucket name (should be ASCII) * @param int $bits Bit size (16 to 48) of resulting numbers before wrap-around * @param int $count Number of IDs to return (1 to 10000) * @param int $flags (supports UIDGenerator::QUICK_VOLATILE) * @return array Ordered list of float integer values * @throws MWException */ protected function getSequentialPerNodeIDs($bucket, $bits, $count, $flags) { if ($count <= 0) { return array(); // nothing to do } elseif ($count > 10000) { throw new MWException("Number of requested IDs ({$count}) is too high."); } elseif ($bits < 16 || $bits > 48) { throw new MWException("Requested bit size ({$bits}) is out of range."); } $counter = null; // post-increment persistent counter value // Use APC/eAccelerator/xcache if requested, available, and not in CLI mode; // Counter values would not survive accross script instances in CLI mode. $cache = null; if ($flags & self::QUICK_VOLATILE && PHP_SAPI !== 'cli') { $cache = ObjectCache::getLocalServerInstance(); } if ($cache) { $counter = $cache->incr($bucket, $count); if ($counter === false) { if (!$cache->add($bucket, (int) $count)) { throw new MWException('Unable to set value to ' . get_class($cache)); } $counter = $count; } } // Note: use of fmod() avoids "division by zero" on 32 bit machines if ($counter === null) { $path = wfTempDir() . '/mw-' . __CLASS__ . '-' . rawurlencode($bucket) . '-48'; // Get the UID lock file handle if (isset($this->fileHandles[$path])) { $handle = $this->fileHandles[$path]; } else { $handle = fopen($path, 'cb+'); $this->fileHandles[$path] = $handle ?: null; // cache } // Acquire the UID lock file if ($handle === false) { throw new MWException("Could not open '{$path}'."); } elseif (!flock($handle, LOCK_EX)) { fclose($handle); throw new MWException("Could not acquire '{$path}'."); } // Fetch the counter value and increment it... rewind($handle); $counter = floor(trim(fgets($handle))) + $count; // fetch as float // Write back the new counter value ftruncate($handle, 0); rewind($handle); fwrite($handle, fmod($counter, pow(2, 48))); // warp-around as needed fflush($handle); // Release the UID lock file flock($handle, LOCK_UN); } $ids = array(); $divisor = pow(2, $bits); $currentId = floor($counter - $count); // pre-increment counter value for ($i = 0; $i < $count; ++$i) { $ids[] = fmod(++$currentId, $divisor); } return $ids; }
/** * @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::getLocalServerInstance(CACHE_NONE); } else { $this->localCache = wfGetCache(CACHE_NONE); } $this->wanCache = ObjectCache::getMainWANInstance(); }
/** * @param array $params Array with keys: * - servers : Required. Array of server info structures. * - loadMonitor : Name of a class used to fetch server lag and load. * - readOnlyReason : Reason the master DB is read-only if so [optional] * @throws MWException */ public function __construct(array $params) { if (!isset($params['servers'])) { throw new MWException(__CLASS__ . ': missing servers parameter'); } $this->mServers = $params['servers']; $this->mWaitTimeout = self::POS_WAIT_TIMEOUT; $this->mReadIndex = -1; $this->mWriteIndex = -1; $this->mConns = array('local' => array(), 'foreignUsed' => array(), 'foreignFree' => array()); $this->mLoads = array(); $this->mWaitForPos = false; $this->mErrorConnection = false; $this->mAllowLagged = false; if (isset($params['readOnlyReason']) && is_string($params['readOnlyReason'])) { $this->readOnlyReason = $params['readOnlyReason']; } if (isset($params['loadMonitor'])) { $this->mLoadMonitorClass = $params['loadMonitor']; } else { $master = reset($params['servers']); if (isset($master['type']) && $master['type'] === 'mysql') { $this->mLoadMonitorClass = 'LoadMonitorMySQL'; } else { $this->mLoadMonitorClass = 'LoadMonitorNull'; } } foreach ($params['servers'] as $i => $server) { $this->mLoads[$i] = $server['load']; if (isset($server['groupLoads'])) { foreach ($server['groupLoads'] as $group => $ratio) { if (!isset($this->mGroupLoads[$group])) { $this->mGroupLoads[$group] = array(); } $this->mGroupLoads[$group][$i] = $ratio; } } } $this->srvCache = ObjectCache::getLocalServerInstance(); if (isset($params['trxProfiler'])) { $this->trxProfiler = $params['trxProfiler']; } else { $this->trxProfiler = new TransactionProfiler(); } }
/** * Compile a LESS file into CSS. * * Keeps track of all used files and adds them to localFileRefs. * * @since 1.22 * @since 1.27 Added $context paramter. * @throws Exception If less.php encounters a parse error * @param string $fileName File path of LESS source * @param ResourceLoaderContext $context Context in which to generate script * @return string CSS source */ protected function compileLessFile($fileName, ResourceLoaderContext $context) { static $cache; if (!$cache) { $cache = ObjectCache::getLocalServerInstance(CACHE_ANYTHING); } // Construct a cache key from the LESS file name and a hash digest // of the LESS variables used for compilation. $vars = $this->getLessVars($context); ksort($vars); $varsHash = hash('md4', serialize($vars)); $cacheKey = $cache->makeGlobalKey('LESS', $fileName, $varsHash); $cachedCompile = $cache->get($cacheKey); // If we got a cached value, we have to validate it by getting a // checksum of all the files that were loaded by the parser and // ensuring it matches the cached entry's. if (isset($cachedCompile['hash'])) { $contentHash = FileContentsHasher::getFileContentsHash($cachedCompile['files']); if ($contentHash === $cachedCompile['hash']) { $this->localFileRefs = array_merge($this->localFileRefs, $cachedCompile['files']); return $cachedCompile['css']; } } $compiler = $context->getResourceLoader()->getLessCompiler($vars); $css = $compiler->parseFile($fileName)->getCss(); $files = $compiler->AllParsedFiles(); $this->localFileRefs = array_merge($this->localFileRefs, $files); $cache->set($cacheKey, ['css' => $css, 'files' => $files, 'hash' => FileContentsHasher::getFileContentsHash($files)], 60 * 60 * 24); // 86400 seconds, or 24 hours. return $css; }
/** * Determine if an image exists on the 'bad image list'. * * The format of MediaWiki:Bad_image_list is as follows: * * Only list items (lines starting with "*") are considered * * The first link on a line must be a link to a bad image * * Any subsequent links on the same line are considered to be exceptions, * i.e. articles where the image may occur inline. * * @param string $name The image name to check * @param Title|bool $contextTitle The page on which the image occurs, if known * @param string $blacklist Wikitext of a file blacklist * @return bool */ function wfIsBadImage($name, $contextTitle = false, $blacklist = null) { # Handle redirects; callers almost always hit wfFindFile() anyway, # so just use that method because it has a fast process cache. $file = wfFindFile($name); // get the final name $name = $file ? $file->getTitle()->getDBkey() : $name; # Run the extension hook $bad = false; if (!Hooks::run('BadImage', array($name, &$bad))) { return $bad; } $cache = ObjectCache::getLocalServerInstance('hash'); $key = wfMemcKey('bad-image-list', $blacklist === null ? 'default' : md5($blacklist)); $badImages = $cache->get($key); if ($badImages === false) { // cache miss if ($blacklist === null) { $blacklist = wfMessage('bad_image_list')->inContentLanguage()->plain(); // site list } # Build the list now $badImages = array(); $lines = explode("\n", $blacklist); foreach ($lines as $line) { # List items only if (substr($line, 0, 1) !== '*') { continue; } # Find all links $m = array(); if (!preg_match_all('/\\[\\[:?(.*?)\\]\\]/', $line, $m)) { continue; } $exceptions = array(); $imageDBkey = false; foreach ($m[1] as $i => $titleText) { $title = Title::newFromText($titleText); if (!is_null($title)) { if ($i == 0) { $imageDBkey = $title->getDBkey(); } else { $exceptions[$title->getPrefixedDBkey()] = true; } } } if ($imageDBkey !== false) { $badImages[$imageDBkey] = $exceptions; } } $cache->set($key, $badImages, 60); } $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false; $bad = isset($badImages[$name]) && !isset($badImages[$name][$contextKey]); return $bad; }
/** * 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 Keys: * - (bool) cache: Whether to allow caching this data. Default: true. * @return string Filtered data, or a comment containing an error message */ public static function filter($filter, $data, array $options = array()) { if (strpos($data, ResourceLoader::FILTER_NOMIN) !== false) { return $data; } if (isset($options['cache']) && $options['cache'] === false) { return self::applyFilter($filter, $data); } $stats = RequestContext::getMain()->getStats(); $cache = ObjectCache::getLocalServerInstance(CACHE_ANYTHING); $key = $cache->makeGlobalKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data)); $result = $cache->get($key); if ($result === false) { $stats->increment("resourceloader_cache.{$filter}.miss"); $result = self::applyFilter($filter, $data); $cache->set($key, $result, 24 * 3600); } else { $stats->increment("resourceloader_cache.{$filter}.hit"); } if ($result === null) { // Cached failure $result = $data; } return $result; }
/** * Get the config array for a backend object with a given name * * @param string $name * @return array Parameters to FileBackend::__construct() * @throws InvalidArgumentException */ public function config($name) { if (!isset($this->backends[$name])) { throw new InvalidArgumentException("No backend defined with the name `{$name}`."); } $class = $this->backends[$name]['class']; $config = $this->backends[$name]['config']; $config['class'] = $class; $config += ['wikiId' => wfWikiID(), 'mimeCallback' => [$this, 'guessMimeInternal'], 'obResetFunc' => 'wfResetOutputBuffers', 'streamMimeFunc' => ['StreamFile', 'contentTypeFromPath'], 'tmpDirectory' => wfTempDir(), 'statusWrapper' => ['Status', 'wrap'], 'wanCache' => MediaWikiServices::getInstance()->getMainWANObjectCache(), 'srvCache' => ObjectCache::getLocalServerInstance('hash'), 'logger' => LoggerFactory::getInstance('FileOperation'), 'profiler' => Profiler::instance()]; $config['lockManager'] = LockManagerGroup::singleton($config['wikiId'])->get($config['lockManager']); $config['fileJournal'] = isset($config['fileJournal']) ? FileJournal::factory($config['fileJournal'], $name) : FileJournal::factory(['class' => 'NullFileJournal'], $name); return $config; }
public function __construct($parent) { $this->parent = $parent; $this->srvCache = ObjectCache::getLocalServerInstance('hash'); $this->mainCache = ObjectCache::getLocalClusterInstance(); }
/** * Retrieve the version of the installed ImageMagick * You can use PHPs version_compare() to use this value * Value is cached for one hour. * @return string|bool Representing the IM version; false on error */ protected function getMagickVersion() { $cache = ObjectCache::getLocalServerInstance(CACHE_NONE); return $cache->getWithSetCallback('imagemagick-version', $cache::TTL_HOUR, function () { global $wgImageMagickConvertCommand; $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) . ' -version'; wfDebug(__METHOD__ . ": Running convert -version\n"); $retval = ''; $return = wfShellExec($cmd, $retval); $x = preg_match('/Version: ImageMagick ([0-9]*\\.[0-9]*\\.[0-9]*)/', $return, $matches); if ($x != 1) { wfDebug(__METHOD__ . ": ImageMagick version check failed\n"); return false; } return $matches[1]; }); }
/** * Get the lock manager object with a given name * * @param string $name * @return LockManager * @throws Exception */ public function get($name) { if (!isset($this->managers[$name])) { throw new Exception("No lock manager defined with the name `{$name}`."); } // Lazy-load the actual lock manager instance if (!isset($this->managers[$name]['instance'])) { $class = $this->managers[$name]['class']; $config = $this->managers[$name]['config']; if ($class === 'DBLockManager') { $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); $lb = $lbFactory->newMainLB($config['domain']); $dbw = $lb->getLazyConnectionRef(DB_MASTER, [], $config['domain']); $config['dbServers']['localDBMaster'] = $dbw; $config['srvCache'] = ObjectCache::getLocalServerInstance('hash'); } $config['logger'] = LoggerFactory::getInstance('LockManager'); $this->managers[$name]['instance'] = new $class($config); } return $this->managers[$name]['instance']; }
/** * Construct a new instance from configuration. * * @param array $config Parameters include: * - dbServers : Associative array of DB names to server configuration. * Configuration is an associative array that includes: * - host : DB server name * - dbname : DB name * - type : DB type (mysql,postgres,...) * - user : DB user * - password : DB user password * - tablePrefix : DB table prefix * - flags : DB flags (see DatabaseBase) * - dbsByBucket : Array of 1-16 consecutive integer keys, starting from 0, * each having an odd-numbered list of DB names (peers) as values. * Any DB named 'localDBMaster' will automatically use the DB master * settings for this wiki (without the need for a dbServers entry). * Only use 'localDBMaster' if the domain is a valid wiki ID. * - lockExpiry : Lock timeout (seconds) for dropped connections. [optional] * This tells the DB server how long to wait before assuming * connection failure and releasing all the locks for a session. */ public function __construct(array $config) { parent::__construct($config); $this->dbServers = isset($config['dbServers']) ? $config['dbServers'] : array(); // likely just using 'localDBMaster' // Sanitize srvsByBucket config to prevent PHP errors $this->srvsByBucket = array_filter($config['dbsByBucket'], 'is_array'); $this->srvsByBucket = array_values($this->srvsByBucket); // consecutive if (isset($config['lockExpiry'])) { $this->lockExpiry = $config['lockExpiry']; } else { $met = ini_get('max_execution_time'); $this->lockExpiry = $met ? $met : 60; // use some sane amount if 0 } $this->safeDelay = $this->lockExpiry <= 0 ? 60 : $this->lockExpiry; // cover worst case foreach ($this->srvsByBucket as $bucket) { if (count($bucket) > 1) { // multiple peers // Tracks peers that couldn't be queried recently to avoid lengthy // connection timeouts. This is useless if each bucket has one peer. $this->statusCache = ObjectCache::getLocalServerInstance(); break; } } $this->session = wfRandomString(31); }
/** * Constructor. */ public function __construct() { $this->cache = ObjectCache::getLocalServerInstance('hash'); }
/** * Get a DateFormatter object * * @param Language|string|null $lang In which language to format the date * Defaults to the site content language * @return DateFormatter */ public static function getInstance($lang = null) { global $wgContLang, $wgMainCacheType; $lang = $lang ? wfGetLangObj($lang) : $wgContLang; $cache = ObjectCache::getLocalServerInstance($wgMainCacheType); static $dateFormatter = false; if (!$dateFormatter) { $dateFormatter = $cache->getWithSetCallback($cache->makeKey('dateformatter', $lang->getCode()), $cache::TTL_HOUR, function () use($lang) { return new DateFormatter($lang); }); } return $dateFormatter; }
/** * @see FileBackendStore::__construct() * Additional $config params include: * - swiftAuthUrl : Swift authentication server URL * - swiftUser : Swift user used by MediaWiki (account:username) * - swiftKey : Swift authentication key for the above user * - swiftAuthTTL : Swift authentication TTL (seconds) * - swiftTempUrlKey : Swift "X-Account-Meta-Temp-URL-Key" value on the account. * Do not set this until it has been set in the backend. * - shardViaHashLevels : Map of container names to sharding config with: * - base : base of hash characters, 16 or 36 * - levels : the number of hash levels (and digits) * - repeat : hash subdirectories are prefixed with all the * parent hash directory names (e.g. "a/ab/abc") * - cacheAuthInfo : Whether to cache authentication tokens in APC, XCache, ect. * If those are not available, then the main cache will be used. * This is probably insecure in shared hosting environments. * - rgwS3AccessKey : Rados Gateway S3 "access key" value on the account. * Do not set this until it has been set in the backend. * This is used for generating expiring pre-authenticated URLs. * Only use this when using rgw and to work around * http://tracker.newdream.net/issues/3454. * - rgwS3SecretKey : Rados Gateway S3 "secret key" value on the account. * Do not set this until it has been set in the backend. * This is used for generating expiring pre-authenticated URLs. * Only use this when using rgw and to work around * http://tracker.newdream.net/issues/3454. */ public function __construct(array $config) { parent::__construct($config); // Required settings $this->swiftAuthUrl = $config['swiftAuthUrl']; $this->swiftUser = $config['swiftUser']; $this->swiftKey = $config['swiftKey']; // Optional settings $this->authTTL = isset($config['swiftAuthTTL']) ? $config['swiftAuthTTL'] : 15 * 60; // some sane number $this->swiftTempUrlKey = isset($config['swiftTempUrlKey']) ? $config['swiftTempUrlKey'] : ''; $this->shardViaHashLevels = isset($config['shardViaHashLevels']) ? $config['shardViaHashLevels'] : ''; $this->rgwS3AccessKey = isset($config['rgwS3AccessKey']) ? $config['rgwS3AccessKey'] : ''; $this->rgwS3SecretKey = isset($config['rgwS3SecretKey']) ? $config['rgwS3SecretKey'] : ''; // HTTP helper client $this->http = new MultiHttpClient(array()); // Cache container information to mask latency if (isset($config['wanCache']) && $config['wanCache'] instanceof WANObjectCache) { $this->memCache = $config['wanCache']; } // Process cache for container info $this->containerStatCache = new ProcessCacheLRU(300); // Cache auth token information to avoid RTTs if (!empty($config['cacheAuthInfo'])) { if (PHP_SAPI === 'cli') { // Preferrably memcached $this->srvCache = ObjectCache::getLocalClusterInstance(); } else { // Look for APC, XCache, WinCache, ect... $this->srvCache = ObjectCache::getLocalServerInstance(CACHE_NONE); } } else { $this->srvCache = new EmptyBagOStuff(); } }
/** * Constructor. * * FIXME: It is possible to construct a Database object with no associated * connection object, by specifying no parameters to __construct(). This * feature is deprecated and should be removed. * * DatabaseBase subclasses should not be constructed directly in external * code. DatabaseBase::factory() should be used instead. * * @param array $params Parameters passed from DatabaseBase::factory() */ function __construct(array $params) { global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode; $this->srvCache = ObjectCache::getLocalServerInstance('hash'); $server = $params['host']; $user = $params['user']; $password = $params['password']; $dbName = $params['dbname']; $flags = $params['flags']; $tablePrefix = $params['tablePrefix']; $schema = $params['schema']; $foreign = $params['foreign']; $this->mFlags = $flags; if ($this->mFlags & DBO_DEFAULT) { if ($wgCommandLineMode) { $this->mFlags &= ~DBO_TRX; } else { $this->mFlags |= DBO_TRX; } } $this->mSessionVars = $params['variables']; /** Get the default table prefix*/ if ($tablePrefix === 'get from global') { $this->mTablePrefix = $wgDBprefix; } else { $this->mTablePrefix = $tablePrefix; } /** Get the database schema*/ if ($schema === 'get from global') { $this->mSchema = $wgDBmwschema; } else { $this->mSchema = $schema; } $this->mForeign = $foreign; if (isset($params['trxProfiler'])) { $this->trxProfiler = $params['trxProfiler']; // override } if ($user) { $this->open($server, $user, $password, $dbName); } }
/** * @since 1.16.3 * @return array */ public function getFirstLetterData() { if ($this->firstLetterData === null) { $cache = ObjectCache::getLocalServerInstance(CACHE_ANYTHING); $cacheKey = $cache->makeKey('first-letters', $this->locale, $this->digitTransformLanguage->getCode(), self::getICUVersion(), self::FIRST_LETTER_VERSION); $this->firstLetterData = $cache->getWithSetCallback($cacheKey, $cache::TTL_WEEK, function () { return $this->fetchFirstLetterData(); }); } return $this->firstLetterData; }
/** * Return a singleton instance, based on the global configs. * @return HKDF * @throws MWException */ protected static function singleton() { global $wgHKDFAlgorithm, $wgHKDFSecret, $wgSecretKey, $wgMainCacheType; $secret = $wgHKDFSecret ?: $wgSecretKey; if (!$secret) { throw new MWException("Cannot use MWCryptHKDF without a secret."); } // In HKDF, the context can be known to the attacker, but this will // keep simultaneous runs from producing the same output. $context = array(); $context[] = microtime(); $context[] = getmypid(); $context[] = gethostname(); // Setup salt cache. Use APC, or fallback to the main cache if it isn't setup $cache = ObjectCache::getLocalServerInstance($wgMainCacheType); if (is_null(self::$singleton)) { self::$singleton = new self($secret, $wgHKDFAlgorithm, $cache, $context); } return self::$singleton; }