X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/includes/cache/HTMLFileCache.php diff --git a/includes/cache/HTMLFileCache.php b/includes/cache/HTMLFileCache.php new file mode 100644 index 00000000..7ae2ee0e --- /dev/null +++ b/includes/cache/HTMLFileCache.php @@ -0,0 +1,246 @@ +mKey = ( $title instanceof Title ) + ? $title->getPrefixedDBkey() + : (string)$title; + $this->mType = (string)$action; + $this->mExt = 'html'; + } + + /** + * Cacheable actions + * @return array + */ + protected static function cacheablePageActions() { + return [ 'view', 'history' ]; + } + + /** + * Get the base file cache directory + * @return string + */ + protected function cacheDirectory() { + return $this->baseCacheDirectory(); // no subdir for b/c with old cache files + } + + /** + * Get the cache type subdirectory (with the trailing slash) or the empty string + * Alter the type -> directory mapping to put action=view cache at the root. + * + * @return string + */ + protected function typeSubdirectory() { + if ( $this->mType === 'view' ) { + return ''; // b/c to not skip existing cache + } else { + return $this->mType . '/'; + } + } + + /** + * Check if pages can be cached for this request/user + * @param IContextSource $context + * @param int $mode One of the HTMLFileCache::MODE_* constants (since 1.28) + * @return bool + */ + public static function useFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) { + $config = MediaWikiServices::getInstance()->getMainConfig(); + + if ( !$config->get( 'UseFileCache' ) && $mode !== self::MODE_REBUILD ) { + return false; + } elseif ( $config->get( 'DebugToolbar' ) ) { + wfDebug( "HTML file cache skipped. \$wgDebugToolbar on\n" ); + + return false; + } + + // Get all query values + $queryVals = $context->getRequest()->getValues(); + foreach ( $queryVals as $query => $val ) { + if ( $query === 'title' || $query === 'curid' ) { + continue; // note: curid sets title + // Normal page view in query form can have action=view. + } elseif ( $query === 'action' && in_array( $val, self::cacheablePageActions() ) ) { + continue; + // Below are header setting params + } elseif ( $query === 'maxage' || $query === 'smaxage' ) { + continue; + } + + return false; + } + + $user = $context->getUser(); + // Check for non-standard user language; this covers uselang, + // and extensions for auto-detecting user language. + $ulang = $context->getLanguage(); + + // Check that there are no other sources of variation + if ( $user->getId() || $ulang->getCode() !== $config->get( 'LanguageCode' ) ) { + return false; + } + + if ( $mode === self::MODE_NORMAL ) { + if ( $user->getNewtalk() ) { + return false; + } + } + + // Allow extensions to disable caching + return Hooks::run( 'HTMLFileCache::useFileCache', [ $context ] ); + } + + /** + * Read from cache to context output + * @param IContextSource $context + * @param int $mode One of the HTMLFileCache::MODE_* constants + * @return void + */ + public function loadFromFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) { + global $wgContLang; + $config = MediaWikiServices::getInstance()->getMainConfig(); + + wfDebug( __METHOD__ . "()\n" ); + $filename = $this->cachePath(); + + if ( $mode === self::MODE_OUTAGE ) { + // Avoid DB errors for queries in sendCacheControl() + $context->getTitle()->resetArticleID( 0 ); + } + + $context->getOutput()->sendCacheControl(); + header( "Content-Type: {$config->get( 'MimeType' )}; charset=UTF-8" ); + header( "Content-Language: {$wgContLang->getHtmlCode()}" ); + if ( $this->useGzip() ) { + if ( wfClientAcceptsGzip() ) { + header( 'Content-Encoding: gzip' ); + readfile( $filename ); + } else { + /* Send uncompressed */ + wfDebug( __METHOD__ . " uncompressing cache file and sending it\n" ); + readgzfile( $filename ); + } + } else { + readfile( $filename ); + } + + $context->getOutput()->disable(); // tell $wgOut that output is taken care of + } + + /** + * Save this cache object with the given text. + * Use this as an ob_start() handler. + * + * Normally this is only registed as a handler if $wgUseFileCache is on. + * If can be explicitly called by rebuildFileCache.php when it takes over + * handling file caching itself, disabling any automatic handling the the + * process. + * + * @param string $text + * @return string|bool The annotated $text or false on error + */ + public function saveToFileCache( $text ) { + if ( strlen( $text ) < 512 ) { + // Disabled or empty/broken output (OOM and PHP errors) + return $text; + } + + wfDebug( __METHOD__ . "()\n", 'private' ); + + $now = wfTimestampNow(); + if ( $this->useGzip() ) { + $text = str_replace( + '', '\n", $text ); + } else { + $text = str_replace( + '', '\n", $text ); + } + + // Store text to FS... + $compressed = $this->saveText( $text ); + if ( $compressed === false ) { + return $text; // error + } + + // gzip output to buffer as needed and set headers... + if ( $this->useGzip() ) { + // @todo Ugly wfClientAcceptsGzip() function - use context! + if ( wfClientAcceptsGzip() ) { + header( 'Content-Encoding: gzip' ); + + return $compressed; + } else { + return $text; + } + } else { + return $text; + } + } + + /** + * Clear the file caches for a page for all actions + * @param Title $title + * @return bool Whether $wgUseFileCache is enabled + */ + public static function clearFileCache( Title $title ) { + $config = MediaWikiServices::getInstance()->getMainConfig(); + + if ( !$config->get( 'UseFileCache' ) ) { + return false; + } + + foreach ( self::cacheablePageActions() as $type ) { + $fc = new self( $title, $type ); + $fc->clearCache(); + } + + return true; + } +}