public function getVersion() { if ($this->version === self::INHERIT_VALUE) { return $this->context->getVersion(); } return $this->version; }
public function getVersion() { if (!is_null($this->version)) { return $this->version; } else { return $this->context->getVersion(); } }
/** * Helper for createLoaderURL() * * @since 1.24 * @see makeLoaderQuery * @param ResourceLoaderContext $context * @param array $extraQuery * @return array */ public static function createLoaderQuery(ResourceLoaderContext $context, $extraQuery = array()) { return self::makeLoaderQuery($context->getModules(), $context->getLanguage(), $context->getSkin(), $context->getUser(), $context->getVersion(), $context->getDebug(), $context->getOnly(), $context->getRequest()->getBool('printable'), $context->getRequest()->getBool('handheld'), $extraQuery); }
/** * Get the URL or URLs to load for this module's CSS in debug mode. * The default behavior is to return a load.php?only=styles URL for * the module, but file-based modules will want to override this to * load the files directly. See also getScriptURLsForDebug() * * @param $context ResourceLoaderContext: Context object * @return Array: array( mediaType => array( URL1, URL2, ... ), ... ) */ public function getStyleURLsForDebug(ResourceLoaderContext $context) { $url = ResourceLoader::makeLoaderURL(array($this->getName()), $context->getLanguage(), $context->getSkin(), $context->getUser(), $context->getVersion(), true, 'styles', $context->getRequest()->getBool('printable'), $context->getRequest()->getBool('handheld')); return array('all' => array($url)); }
/** * Get the load.php URL that will produce this image. * * @param ResourceLoaderContext $context Any context * @param string $script URL to load.php * @param string|null $variant Variant to get the URL for * @param string $format Format to get the URL for, 'original' or 'rasterized' * @return string */ public function getUrl(ResourceLoaderContext $context, $script, $variant, $format) { $query = array('modules' => $this->getModule(), 'image' => $this->getName(), 'variant' => $variant, 'format' => $format, 'lang' => $context->getLanguage(), 'version' => $context->getVersion()); return wfExpandUrl(wfAppendQuery($script, $query), PROTO_RELATIVE); }
/** * Send out code for a response from file cache if possible * * @param $fileCache ResourceFileCache: Cache object for this request URL * @param $context ResourceLoaderContext: Context in which to generate a response * @return bool If this found a cache file and handled the response */ protected function tryRespondFromFileCache(ResourceFileCache $fileCache, ResourceLoaderContext $context) { global $wgResourceLoaderMaxage; // Buffer output to catch warnings. ob_start(); // Get the maximum age the cache can be $maxage = is_null($context->getVersion()) ? $wgResourceLoaderMaxage['unversioned']['server'] : $wgResourceLoaderMaxage['versioned']['server']; // Minimum timestamp the cache file must have $good = $fileCache->isCacheGood(wfTimestamp(TS_MW, time() - $maxage)); if (!$good) { try { // RL always hits the DB on file cache miss... wfGetDB(DB_SLAVE); } catch (DBConnectionError $e) { // ...check if we need to fallback to cache $good = $fileCache->isCacheGood(); // cache existence check } } if ($good) { $ts = $fileCache->cacheTimestamp(); // Send content type and cache headers $this->sendResponseHeaders($context, $ts, false); // If there's an If-Modified-Since header, respond with a 304 appropriately if ($this->tryRespondLastModified($context, $ts)) { return false; // output handled (buffers cleared) } $response = $fileCache->fetchText(); // Capture any PHP warnings from the output buffer and append them to the // response in a comment if we're in debug mode. if ($context->getDebug() && strlen($warnings = ob_get_contents())) { $response = "/*\n{$warnings}\n*/\n" . $response; } // Remove the output buffer and output the response ob_end_clean(); echo $response . "\n/* Cached {$ts} */"; return true; // cache hit } // Clear buffer ob_end_clean(); return false; // cache miss }
/** * Customize caching policy for RL modules * * * cache "static" modules for 30 days when cb param in the URL matches $wgStyleVersion * * cache "dynamic" modules for 30 days when version param is present in the URL and matches $mtime timestamp * * otherwise fallback to caching for 5 minutes * * @see BAC-1241 * * @param ResourceLoader $rl * @param ResourceLoaderContext $context * @param $mtime int UNIX timestamp from module(s) calculated from filesystem * @param $maxage int UNIX timestamp for maxage * @param $smaxage int UNIX timestamp for smaxage * @return bool it's a hook */ public static function onResourceLoaderModifyMaxAge(ResourceLoader $rl, ResourceLoaderContext $context, $mtime, &$maxage, &$smaxage) { global $wgStyleVersion, $wgResourceLoaderMaxage; // parse cb and version provided as URL parameters // version%3D123456-20140220T090000Z // cb%3D123456%26 $version = explode('-', (string) $context->getVersion(), 2); if (count($version) === 2) { list($cb, $ts) = $version; $ts = strtotime($ts); // convert MW to UNIX timestamp } else { $cb = $context->getRequest()->getVal('cb', false); $ts = false; } // check if at least one of required modules serves dynamic content $hasDynamicModule = false; $modules = $context->getModules(); foreach ($modules as $moduleName) { if (!$rl->getModule($moduleName) instanceof ResourceLoaderFileModule) { $hasDynamicModule = true; break; } } if ($hasDynamicModule) { // use long TTL when version value matches $mtime passed to the hook $useLongTTL = !empty($ts) && $ts <= $mtime; } else { // use long TTL when cache buster value from URL matches $wgStyleVersion $useLongTTL = !empty($cb) && $cb <= $wgStyleVersion; } // modify caching times if (!$useLongTTL) { WikiaLogger::instance()->info('rl.shortTTL', ['modules' => join(',', $modules), 'cb' => $cb, 'ts' => $ts]); } $cachingTimes = $wgResourceLoaderMaxage[$useLongTTL ? 'versioned' : 'unversioned']; $maxage = $cachingTimes['client']; $smaxage = $cachingTimes['server']; return true; }
/** * Outputs a response to a resource load-request, including a content-type header. * * @param $context ResourceLoaderContext: Context in which a response should be formed */ public function respond(ResourceLoaderContext $context) { global $wgResourceLoaderMaxage, $wgCacheEpoch; // Buffer output to catch warnings. Normally we'd use ob_clean() on the // top-level output buffer to clear warnings, but that breaks when ob_gzhandler // is used: ob_clean() will clear the GZIP header in that case and it won't come // back for subsequent output, resulting in invalid GZIP. So we have to wrap // the whole thing in our own output buffer to be sure the active buffer // doesn't use ob_gzhandler. // See http://bugs.php.net/bug.php?id=36514 ob_start(); wfProfileIn(__METHOD__); $errors = ''; // Split requested modules into two groups, modules and missing $modules = array(); $missing = array(); foreach ($context->getModules() as $name) { if (isset($this->moduleInfos[$name])) { $module = $this->getModule($name); // Do not allow private modules to be loaded from the web. // This is a security issue, see bug 34907. if ($module->getGroup() === 'private') { $errors .= $this->makeComment("Cannot show private module \"{$name}\""); continue; } $modules[$name] = $this->getModule($name); } else { $missing[] = $name; } } // If a version wasn't specified we need a shorter expiry time for updates // to propagate to clients quickly if (is_null($context->getVersion())) { $maxage = $wgResourceLoaderMaxage['unversioned']['client']; $smaxage = $wgResourceLoaderMaxage['unversioned']['server']; } else { $maxage = $wgResourceLoaderMaxage['versioned']['client']; $smaxage = $wgResourceLoaderMaxage['versioned']['server']; } // Preload information needed to the mtime calculation below try { $this->preloadModuleInfo(array_keys($modules), $context); } catch (Exception $e) { // Add exception to the output as a comment $errors .= $this->makeComment($e->__toString()); } wfProfileIn(__METHOD__ . '-getModifiedTime'); // To send Last-Modified and support If-Modified-Since, we need to detect // the last modified time $mtime = wfTimestamp(TS_UNIX, $wgCacheEpoch); foreach ($modules as $module) { try { // Calculate maximum modified time $mtime = max($mtime, $module->getModifiedTime($context)); } catch (Exception $e) { // Add exception to the output as a comment $errors .= $this->makeComment($e->__toString()); } } wfProfileOut(__METHOD__ . '-getModifiedTime'); if ($context->getOnly() === 'styles') { header('Content-Type: text/css; charset=utf-8'); } else { header('Content-Type: text/javascript; charset=utf-8'); } header('Last-Modified: ' . wfTimestamp(TS_RFC2822, $mtime)); if ($context->getDebug()) { // Do not cache debug responses header('Cache-Control: private, no-cache, must-revalidate'); header('Pragma: no-cache'); } else { header("Cache-Control: public, max-age={$maxage}, s-maxage={$smaxage}"); $exp = min($maxage, $smaxage); header('Expires: ' . wfTimestamp(TS_RFC2822, $exp + time())); } // If there's an If-Modified-Since header, respond with a 304 appropriately // Some clients send "timestamp;length=123". Strip the part after the first ';' // so we get a valid timestamp. $ims = $context->getRequest()->getHeader('If-Modified-Since'); // Never send 304s in debug mode if ($ims !== false && !$context->getDebug()) { $imsTS = strtok($ims, ';'); if ($mtime <= wfTimestamp(TS_UNIX, $imsTS)) { // There's another bug in ob_gzhandler (see also the comment at // the top of this function) that causes it to gzip even empty // responses, meaning it's impossible to produce a truly empty // response (because the gzip header is always there). This is // a problem because 304 responses have to be completely empty // per the HTTP spec, and Firefox behaves buggily when they're not. // See also http://bugs.php.net/bug.php?id=51579 // To work around this, we tear down all output buffering before // sending the 304. // On some setups, ob_get_level() doesn't seem to go down to zero // no matter how often we call ob_get_clean(), so instead of doing // the more intuitive while ( ob_get_level() > 0 ) ob_get_clean(); // we have to be safe here and avoid an infinite loop. for ($i = 0; $i < ob_get_level(); $i++) { ob_end_clean(); } header('HTTP/1.0 304 Not Modified'); header('Status: 304 Not Modified'); wfProfileOut(__METHOD__); return; } } // Generate a response $response = $this->makeModuleResponse($context, $modules, $missing); // Prepend comments indicating exceptions $response = $errors . $response; // Capture any PHP warnings from the output buffer and append them to the // response in a comment if we're in debug mode. if ($context->getDebug() && strlen($warnings = ob_get_contents())) { $response = $this->makeComment($warnings) . $response; } // Remove the output buffer and output the response ob_end_clean(); echo $response; wfProfileOut(__METHOD__); }
/** * Get the load.php URL that will produce this image. * * @param ResourceLoaderContext $context Any context * @param string $script URL to load.php * @param string|null $variant Variant to get the URL for * @param string $format Format to get the URL for, 'original' or 'rasterized' * @return string */ public function getUrl(ResourceLoaderContext $context, $script, $variant, $format) { $query = ['modules' => $this->getModule(), 'image' => $this->getName(), 'variant' => $variant, 'format' => $format, 'lang' => $context->getLanguage(), 'version' => $context->getVersion()]; return wfAppendQuery($script, $query); }
/** * Get the URL or URLs to load for this module's CSS in debug mode. * The default behavior is to return a load.php?only=styles URL for * the module, but file-based modules will want to override this to * load the files directly. See also getScriptURLsForDebug() * * @param $context ResourceLoaderContext: Context object * @return Array: array( mediaType => array( URL1, URL2, ... ), ... ) */ public function getStyleURLsForDebug(ResourceLoaderContext $context) { global $wgLoadScript; // TODO factor out to ResourceLoader static method and deduplicate from makeResourceLoaderLink() $query = array('modules' => $this->getName(), 'only' => 'styles', 'skin' => $context->getSkin(), 'user' => $context->getUser(), 'debug' => 'true', 'version' => $context->getVersion()); ksort($query); return array('all' => array(wfAppendQuery($wgLoadScript, $query) . '&*')); }