1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
<?php
/**
* Contain the HTMLFileCache class
* @file
* @ingroup Cache
*/
class HTMLFileCache extends FileCacheBase {
/**
* Construct an ObjectFileCache from a Title and an action
* @param $title Title|string Title object or prefixed DB key string
* @param $action string
* @return HTMLFileCache
*/
public static function newFromTitle( $title, $action ) {
$cache = new self();
$allowedTypes = self::cacheablePageActions();
if ( !in_array( $action, $allowedTypes ) ) {
throw new MWException( "Invalid filecache type given." );
}
$cache->mKey = ( $title instanceof Title )
? $title->getPrefixedDBkey()
: (string)$title;
$cache->mType = (string)$action;
$cache->mExt = 'html';
return $cache;
}
/**
* Cacheable actions
* @return array
*/
protected static function cacheablePageActions() {
return array( '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 $context IContextSource
* @return bool
*/
public static function useFileCache( IContextSource $context ) {
global $wgUseFileCache, $wgShowIPinHeader, $wgDebugToolbar, $wgContLang;
if ( !$wgUseFileCache ) {
return false;
}
if ( $wgShowIPinHeader || $wgDebugToolbar ) {
wfDebug( "HTML file cache skipped. Either \$wgShowIPinHeader and/or \$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()->getCode();
$clang = $wgContLang->getCode();
// Check that there are no other sources of variation
return !$user->getId() && !$user->getNewtalk() && $ulang == $clang;
}
/**
* Read from cache to context output
* @param $context IContextSource
* @return void
*/
public function loadFromFileCache( IContextSource $context ) {
global $wgMimeType, $wgLanguageCode;
wfDebug( __METHOD__ . "()\n");
$filename = $this->cachePath();
$context->getOutput()->sendCacheControl();
header( "Content-Type: $wgMimeType; charset=UTF-8" );
header( "Content-Language: $wgLanguageCode" );
if ( $this->useGzip() ) {
if ( wfClientAcceptsGzip() ) {
header( 'Content-Encoding: gzip' );
} else {
/* Send uncompressed */
readgzfile( $filename );
return;
}
}
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.
* @param $text string
* @return bool Whether $wgUseFileCache is enabled
*/
public function saveToFileCache( $text ) {
global $wgUseFileCache;
if ( !$wgUseFileCache || strlen( $text ) < 512 ) {
// Disabled or empty/broken output (OOM and PHP errors)
return $text;
}
wfDebug( __METHOD__ . "()\n", false);
$now = wfTimestampNow();
if ( $this->useGzip() ) {
$text = str_replace(
'</html>', '<!-- Cached/compressed '.$now." -->\n</html>", $text );
} else {
$text = str_replace(
'</html>', '<!-- Cached '.$now." -->\n</html>", $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 ) {
global $wgUseFileCache;
if ( !$wgUseFileCache ) {
return false;
}
foreach ( self::cacheablePageActions() as $type ) {
$fc = self::newFromTitle( $title, $type );
$fc->clearCache();
}
return true;
}
}
|