r98405 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r98404‎ | r98405 | r98406 >
Date:08:18, 29 September 2011
Author:aaron
Status:ok (Comments)
Tags:
Comment:
HTMLFileCache refactoring:
* Rewrote class and split into three classes: a base class, and html cache and a more generic cache to be used later.
* The new classes now use RequestContext.
* Renamed fetchPageText() -> fetchText().
* Split out new saveText() function from saveToFileCache().
* Various other cleanups and fixes.
Also fixed backwards setting of $wgDisableCounters in rebuildFileCache.php.
Modified paths:
  • /trunk/phase3/includes/Article.php (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/HistoryPage.php (modified) (history)
  • /trunk/phase3/includes/Wiki.php (modified) (history)
  • /trunk/phase3/includes/cache/FileCacheBase.php (added) (history)
  • /trunk/phase3/includes/cache/HTMLFileCache.php (modified) (history)
  • /trunk/phase3/includes/cache/ObjectFileCache.php (added) (history)
  • /trunk/phase3/includes/db/DatabaseError.php (modified) (history)
  • /trunk/phase3/maintenance/rebuildFileCache.php (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/rebuildFileCache.php
@@ -37,7 +37,7 @@
3838 if ( !$wgUseFileCache ) {
3939 $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true );
4040 }
41 - $wgDisableCounters = false;
 41+ $wgDisableCounters = true;
4242 $start = $this->getArg( 0, "0" );
4343 if ( !ctype_digit( $start ) ) {
4444 $this->error( "Invalid value for start parameter.", true );
@@ -83,8 +83,8 @@
8484 $article = new Article( $wgTitle );
8585 // If the article is cacheable, then load it
8686 if ( $article->isFileCacheable() ) {
87 - $cache = new HTMLFileCache( $wgTitle );
88 - if ( $cache->isFileCacheGood() ) {
 87+ $cache = HTMLFileCache::newFromTitle( $wgTitle, 'view' );
 88+ if ( $cache->isCacheGood() ) {
8989 if ( $overwrite ) {
9090 $rebuilt = true;
9191 } else {
Index: trunk/phase3/includes/Article.php
@@ -1715,10 +1715,10 @@
17161716
17171717 $called = true;
17181718 if ( $this->isFileCacheable() ) {
1719 - $cache = new HTMLFileCache( $this->getTitle() );
1720 - if ( $cache->isFileCacheGood( $this->mPage->getTouched() ) ) {
 1719+ $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'view' );
 1720+ if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
17211721 wfDebug( "Article::tryFileCache(): about to load file\n" );
1722 - $cache->loadFromFileCache();
 1722+ $cache->loadFromFileCache( $this->getContext() );
17231723 return true;
17241724 } else {
17251725 wfDebug( "Article::tryFileCache(): starting buffer\n" );
@@ -1738,8 +1738,9 @@
17391739 public function isFileCacheable() {
17401740 $cacheable = false;
17411741
1742 - if ( HTMLFileCache::useFileCache() ) {
1743 - $cacheable = $this->mPage->getID() && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
 1742+ if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
 1743+ $cacheable = $this->mPage->getID()
 1744+ && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
17441745 // Extension may have reason to disable file caching on some pages.
17451746 if ( $cacheable ) {
17461747 $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
Index: trunk/phase3/includes/db/DatabaseError.php
@@ -232,9 +232,9 @@
233233 $t = Title::newFromText( $this->msg( 'mainpage', 'Main Page' ) );
234234 }
235235
236 - $cache = new HTMLFileCache( $t );
237 - if ( $cache->isFileCached() ) {
238 - return $cache->fetchPageText();
 236+ $cache = HTMLFileCache::newFromTitle( $t, 'view' );
 237+ if ( $cache->isCached() ) {
 238+ return $cache->fetchText();
239239 } else {
240240 return '';
241241 }
Index: trunk/phase3/includes/HistoryPage.php
@@ -75,10 +75,11 @@
7676
7777 wfProfileIn( __METHOD__ );
7878
 79+ $context = RequestContext::getMain();
7980 # Fill in the file cache if not set already
80 - if ( $wgUseFileCache && HTMLFileCache::useFileCache() ) {
81 - $cache = new HTMLFileCache( $this->title, 'history' );
82 - if ( !$cache->isFileCacheGood( /* Assume up to date */ ) ) {
 81+ if ( $wgUseFileCache && HTMLFileCache::useFileCache( $context ) ) {
 82+ $cache = HTMLFileCache::newFromTitle( $this->title, 'history' );
 83+ if ( !$cache->isCacheGood( /* Assume up to date */ ) ) {
8384 ob_start( array( &$cache, 'saveToFileCache' ) );
8485 }
8586 }
Index: trunk/phase3/includes/AutoLoader.php
@@ -363,6 +363,7 @@
364364 'CacheDependency' => 'includes/cache/CacheDependency.php',
365365 'ConstantDependency' => 'includes/cache/CacheDependency.php',
366366 'DependencyWrapper' => 'includes/cache/CacheDependency.php',
 367+ 'FileCacheBase' => 'includes/cache/FileCacheBase.php',
367368 'FileDependency' => 'includes/cache/CacheDependency.php',
368369 'GlobalDependency' => 'includes/cache/CacheDependency.php',
369370 'HTMLCacheUpdate' => 'includes/cache/HTMLCacheUpdate.php',
@@ -371,6 +372,7 @@
372373 'LinkBatch' => 'includes/cache/LinkBatch.php',
373374 'LinkCache' => 'includes/cache/LinkCache.php',
374375 'MessageCache' => 'includes/cache/MessageCache.php',
 376+ 'ObjectFileCache' => 'includes/cache/ObjectFileCache.php',
375377 'SquidUpdate' => 'includes/cache/SquidUpdate.php',
376378 'TitleDependency' => 'includes/cache/CacheDependency.php',
377379 'TitleListDependency' => 'includes/cache/CacheDependency.php',
Index: trunk/phase3/includes/Wiki.php
@@ -596,16 +596,14 @@
597597
598598 if ( $wgUseFileCache && $wgTitle->getNamespace() >= 0 ) {
599599 wfProfileIn( 'main-try-filecache' );
600 - // Raw pages should handle cache control on their own,
601 - // even when using file cache. This reduces hits from clients.
602 - if ( HTMLFileCache::useFileCache() ) {
 600+ if ( HTMLFileCache::useFileCache( $this->context ) ) {
603601 /* Try low-level file cache hit */
604 - $cache = new HTMLFileCache( $wgTitle, $action );
605 - if ( $cache->isFileCacheGood( /* Assume up to date */ ) ) {
 602+ $cache = HTMLFileCache::newFromTitle( $wgTitle, $action );
 603+ if ( $cache->isCacheGood( /* Assume up to date */ ) ) {
606604 /* Check incoming headers to see if client has this cached */
607 - $timestamp = $cache->fileCacheTime();
 605+ $timestamp = $cache->cacheTimestamp();
608606 if ( !$this->context->getOutput()->checkLastModified( $timestamp ) ) {
609 - $cache->loadFromFileCache();
 607+ $cache->loadFromFileCache( $this->context );
610608 }
611609 # Do any stats increment/watchlist stuff
612610 $article = WikiPage::factory( $wgTitle );
Index: trunk/phase3/includes/cache/ObjectFileCache.php
@@ -0,0 +1,41 @@
 2+<?php
 3+class ObjectFileCache extends FileCacheBase {
 4+
 5+ public static function newFromKey( $key, $type ) {
 6+ $cache = new self();
 7+
 8+ $allowedTypes = self::cacheableTypes();
 9+ if ( !isset( $allowedTypes[$type] ) ) {
 10+ throw new MWException( "Invalid filecache type given." );
 11+ }
 12+ $cache->mKey = (string)$key;
 13+ $cache->mType = (string)$type;
 14+ $cache->mExt = $allowedTypes[$cache->mType];
 15+
 16+ return $cache;
 17+ }
 18+
 19+ /*
 20+ * Get the type => extension mapping
 21+ * @return array
 22+ */
 23+ protected static function cacheableTypes() {
 24+ return array( 'resources-js' => 'js', 'resources-css' => 'css' );
 25+ }
 26+
 27+ /**
 28+ * Get the base file cache directory
 29+ * @return string
 30+ */
 31+ protected function cacheDirectory() {
 32+ global $wgCacheDirectory, $wgFileCacheDirectory, $wgFileCacheDepth;
 33+ if ( $wgFileCacheDirectory ) {
 34+ $dir = $wgFileCacheDirectory;
 35+ } elseif ( $wgCacheDirectory ) {
 36+ $dir = "$wgCacheDirectory/object";
 37+ } else {
 38+ throw new MWException( 'Please set $wgCacheDirectory in LocalSettings.php if you wish to use the HTML file cache' );
 39+ }
 40+ return $dir;
 41+ }
 42+}
Property changes on: trunk/phase3/includes/cache/ObjectFileCache.php
___________________________________________________________________
Added: svn:eol-style
143 + native
Index: trunk/phase3/includes/cache/FileCacheBase.php
@@ -0,0 +1,173 @@
 2+<?php
 3+/**
 4+ * Contain the HTMLFileCache class
 5+ * @file
 6+ * @ingroup Cache
 7+ */
 8+abstract class FileCacheBase {
 9+ protected $mKey;
 10+ protected $mType;
 11+ protected $mExt;
 12+ protected $mFilePath;
 13+ protected $mUseGzip;
 14+
 15+ protected function __construct() {
 16+ global $wgUseGzip;
 17+
 18+ $this->mUseGzip = (bool)$wgUseGzip;
 19+ $this->mExt = 'cache';
 20+ }
 21+
 22+ /**
 23+ * Get the base cache directory (not speficic to this file)
 24+ * @return string
 25+ */
 26+ abstract protected function cacheDirectory();
 27+
 28+ /**
 29+ * Get the path to the cache file
 30+ * @return string
 31+ */
 32+ protected function cachePath() {
 33+ if ( $this->mFilePath !== null ) {
 34+ return $this->mFilePath;
 35+ }
 36+
 37+ $dir = $this->cacheDirectory();
 38+ $subDirs = $this->mType . '/' . $this->hashSubdirectory(); // includes '/'
 39+ # Avoid extension confusion
 40+ $key = str_replace( '.', '%2E', urlencode( $this->mKey ) );
 41+ # Build the full file path
 42+ $this->mFilePath = "{$dir}/{$subDirs}{$key}.{$this->mExt}";
 43+ if ( $this->useGzip() ) {
 44+ $this->mFilePath .= '.gz';
 45+ }
 46+
 47+ return $this->mFilePath;
 48+ }
 49+
 50+ /**
 51+ * Check if the cache file exists
 52+ * @return bool
 53+ */
 54+ public function isCached() {
 55+ return file_exists( $this->cachePath() );
 56+ }
 57+
 58+ /**
 59+ * Get the last-modified timestamp of the cache file
 60+ * @return string|false TS_MW timestamp
 61+ */
 62+ public function cacheTimestamp() {
 63+ $timestamp = filemtime( $this->cachePath() );
 64+ return ( $timestamp !== false )
 65+ ? wfTimestamp( TS_MW, $timestamp )
 66+ : false;
 67+ }
 68+
 69+ /**
 70+ * Check if up to date cache file exists
 71+ * @param $timestamp string MW_TS timestamp
 72+ *
 73+ * @return bool
 74+ */
 75+ public function isCacheGood( $timestamp = '' ) {
 76+ global $wgCacheEpoch;
 77+
 78+ if ( !$this->isCached() ) {
 79+ return false;
 80+ }
 81+
 82+ $cachetime = $this->cacheTimestamp();
 83+ $good = ( $timestamp <= $cachetime && $wgCacheEpoch <= $cachetime );
 84+ wfDebug( __METHOD__ . ": cachetime $cachetime, touched '{$timestamp}' epoch {$wgCacheEpoch}, good $good\n");
 85+
 86+ return $good;
 87+ }
 88+
 89+ /**
 90+ * Check if the cache is gzipped
 91+ * @return bool
 92+ */
 93+ protected function useGzip() {
 94+ return $this->mUseGzip;
 95+ }
 96+
 97+ /**
 98+ * Get the uncompressed text from the cache
 99+ * @return string
 100+ */
 101+ public function fetchText() {
 102+ if ( $this->useGzip() ) {
 103+ /* Why is there no gzfile_get_contents() or gzdecode()? */
 104+ return implode( '', gzfile( $this->cachePath() ) );
 105+ } else {
 106+ return file_get_contents( $this->cachePath() );
 107+ }
 108+ }
 109+
 110+ /**
 111+ * Save and compress text to the cache
 112+ * @return string compressed text
 113+ */
 114+ public function saveText( $text ) {
 115+ global $wgUseFileCache;
 116+ if ( !$wgUseFileCache ) {
 117+ return false;
 118+ }
 119+
 120+ if ( $this->useGzip() ) {
 121+ $text = gzencode( $text );
 122+ }
 123+
 124+ $this->checkCacheDirs(); // build parent dir
 125+ if ( !file_put_contents( $this->cachePath(), $text ) ) {
 126+ return false;
 127+ }
 128+
 129+ return $text;
 130+ }
 131+
 132+ /*
 133+ * Clear the cache for this page
 134+ * @return void
 135+ */
 136+ public function clearCache() {
 137+ wfSuppressWarnings();
 138+ unlink( $this->cachePath() );
 139+ wfRestoreWarnings();
 140+ }
 141+
 142+ /*
 143+ * Create parent directors of $this->cachePath()
 144+ * @TODO: why call wfMkdirParents() twice?
 145+ * @return void
 146+ */
 147+ protected function checkCacheDirs() {
 148+ $filename = $this->cachePath();
 149+ $mydir2 = substr( $filename, 0, strrpos( $filename, '/') ); # subdirectory level 2
 150+ $mydir1 = substr( $mydir2, 0, strrpos( $mydir2, '/') ); # subdirectory level 1
 151+
 152+ wfMkdirParents( $mydir1, null, __METHOD__ );
 153+ wfMkdirParents( $mydir2, null, __METHOD__ );
 154+ }
 155+
 156+ /*
 157+ * Return relative multi-level hash subdirectory with the trailing
 158+ * slash or the empty string if $wgFileCacheDepth is off
 159+ * @return string
 160+ */
 161+ protected function hashSubdirectory() {
 162+ global $wgFileCacheDepth;
 163+
 164+ $subdir = '';
 165+ if ( $wgFileCacheDepth > 0 ) {
 166+ $hash = md5( $this->mKey );
 167+ for ( $i = 1; $i <= $wgFileCacheDepth; $i++ ) {
 168+ $subdir .= substr( $hash, 0, $i ) . '/';
 169+ }
 170+ }
 171+
 172+ return $subdir;
 173+ }
 174+}
Property changes on: trunk/phase3/includes/cache/FileCacheBase.php
___________________________________________________________________
Added: svn:eol-style
1175 + native
Index: trunk/phase3/includes/cache/HTMLFileCache.php
@@ -4,167 +4,96 @@
55 * @file
66 * @ingroup Cache
77 */
 8+class HTMLFileCache extends FileCacheBase {
89
9 -/**
10 - * Handles talking to the file cache, putting stuff in and taking it back out.
11 - * Mostly called from Article.php for the emergency abort/fallback to cache.
12 - *
13 - * Global options that affect this module:
14 - * - $wgCachePages
15 - * - $wgCacheEpoch
16 - * - $wgUseFileCache
17 - * - $wgCacheDirectory
18 - * - $wgFileCacheDirectory
19 - * - $wgUseGzip
20 - *
21 - * @ingroup Cache
22 - */
23 -class HTMLFileCache {
 10+ public static function newFromTitle( Title $title, $action ) {
 11+ $cache = new self();
2412
25 - /**
26 - * @var Title
27 - */
28 - var $mTitle;
29 - var $mFileCache, $mType;
 13+ $allowedTypes = self::cacheablePageActions();
 14+ if ( !in_array( $action, $allowedTypes ) ) {
 15+ throw new MWException( "Invalid filecache type given." );
 16+ }
 17+ $cache->mKey = $title->getPrefixedDBkey();
 18+ $cache->mType = (string)$action;
 19+ $cache->mExt = 'html';
3020
31 - public function __construct( $title, $type = 'view' ) {
32 - $this->mTitle = $title;
33 - $this->mType = in_array( $type, self::cacheableActions() ) ? $type : false;
34 - $this->fileCacheName(); // init name
 21+ return $cache;
3522 }
3623
37 - protected static function cacheableActions() {
 24+ /*
 25+ * Cacheable actions
 26+ * @return array
 27+ */
 28+ protected static function cacheablePageActions() {
3829 return array( 'view', 'history' );
3930 }
4031
41 - public function fileCacheName() {
42 - if( !$this->mFileCache ) {
43 - global $wgCacheDirectory, $wgFileCacheDirectory, $wgFileCacheDepth;
44 -
45 - if ( $wgFileCacheDirectory ) {
46 - $dir = $wgFileCacheDirectory;
47 - } elseif ( $wgCacheDirectory ) {
48 - $dir = "$wgCacheDirectory/html";
49 - } else {
50 - throw new MWException( 'Please set $wgCacheDirectory in LocalSettings.php if you wish to use the HTML file cache' );
51 - }
52 -
53 - # Store other views of aspects of pages elsewhere
54 - $subdir = ($this->mType === 'view') ? '' : "{$this->mType}/";
55 -
56 - $key = $this->mTitle->getPrefixedDbkey();
57 - if ( $wgFileCacheDepth > 0 ) {
58 - $hash = md5( $key );
59 - for ( $i = 1; $i <= $wgFileCacheDepth; $i++ ) {
60 - $subdir .= substr( $hash, 0, $i ) . '/';
61 - }
62 - }
63 - # Avoid extension confusion
64 - $key = str_replace( '.', '%2E', urlencode( $key ) );
65 - $this->mFileCache = "{$dir}/{$subdir}{$key}.html";
66 -
67 - if( $this->useGzip() ) {
68 - $this->mFileCache .= '.gz';
69 - }
70 -
71 - wfDebug( __METHOD__ . ": {$this->mFileCache}\n" );
 32+ /**
 33+ * Get the base file cache directory
 34+ * @return string
 35+ */
 36+ protected function cacheDirectory() {
 37+ global $wgCacheDirectory, $wgFileCacheDirectory, $wgFileCacheDepth;
 38+ if ( $wgFileCacheDirectory ) {
 39+ $dir = $wgFileCacheDirectory;
 40+ } elseif ( $wgCacheDirectory ) {
 41+ $dir = "$wgCacheDirectory/object";
 42+ } else {
 43+ throw new MWException( 'Please set $wgCacheDirectory in LocalSettings.php if you wish to use the HTML file cache' );
7244 }
73 - return $this->mFileCache;
 45+ return $dir;
7446 }
7547
76 - public function isFileCached() {
77 - if( $this->mType === false ) {
78 - return false;
79 - }
80 - return file_exists( $this->fileCacheName() );
81 - }
82 -
83 - public function fileCacheTime() {
84 - return wfTimestamp( TS_MW, filemtime( $this->fileCacheName() ) );
85 - }
86 -
8748 /**
8849 * Check if pages can be cached for this request/user
 50+ * @param $context RequestContext
8951 * @return bool
9052 */
91 - public static function useFileCache() {
92 - global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest, $wgLang, $wgContLang;
93 - if( !$wgUseFileCache ) {
 53+ public static function useFileCache( RequestContext $context ) {
 54+ global $wgUseFileCache, $wgShowIPinHeader, $wgContLang;
 55+ if ( !$wgUseFileCache ) {
9456 return false;
9557 }
9658 // Get all query values
97 - $queryVals = $wgRequest->getValues();
98 - foreach( $queryVals as $query => $val ) {
99 - if( $query == 'title' || $query == 'curid' ) {
 59+ $queryVals = $context->getRequest()->getValues();
 60+ foreach ( $queryVals as $query => $val ) {
 61+ if ( $query == 'title' || $query == 'curid' ) {
10062 continue; // note: curid sets title
10163 // Normal page view in query form can have action=view.
10264 // Raw hits for pages also stored, like .css pages for example.
103 - } elseif( $query == 'action' && in_array( $val, self::cacheableActions() ) ) {
 65+ } elseif ( $query == 'action' && in_array( $val, self::cacheablePageActions() ) ) {
10466 continue;
10567 // Below are header setting params
106 - } elseif( $query == 'maxage' || $query == 'smaxage' ) {
 68+ } elseif ( $query == 'maxage' || $query == 'smaxage' ) {
10769 continue;
10870 } else {
10971 return false;
11072 }
11173 }
 74+ $user = $context->getUser();
11275 // Check for non-standard user language; this covers uselang,
11376 // and extensions for auto-detecting user language.
114 - $ulang = $wgLang->getCode();
 77+ $ulang = $context->getLang()->getCode();
11578 $clang = $wgContLang->getCode();
11679 // Check that there are no other sources of variation
117 - return !$wgShowIPinHeader && !$wgUser->getId() && !$wgUser->getNewtalk() && $ulang == $clang;
 80+ return !$wgShowIPinHeader && !$user->getId() && !$user->getNewtalk() && $ulang == $clang;
11881 }
11982
12083 /**
121 - * Check if up to date cache file exists
122 - * @param $timestamp string
123 - *
124 - * @return bool
 84+ * Read from cache to context output
 85+ * @param $context RequestContext
 86+ * @return void
12587 */
126 - public function isFileCacheGood( $timestamp = '' ) {
127 - global $wgCacheEpoch;
 88+ public function loadFromFileCache( RequestContext $context ) {
 89+ global $wgMimeType, $wgLanguageCode;
12890
129 - if( !$this->isFileCached() ) {
130 - return false;
131 - }
132 -
133 - $cachetime = $this->fileCacheTime();
134 - $good = $timestamp <= $cachetime && $wgCacheEpoch <= $cachetime;
135 -
136 - wfDebug( __METHOD__ . ": cachetime $cachetime, touched '{$timestamp}' epoch {$wgCacheEpoch}, good $good\n");
137 - return $good;
138 - }
139 -
140 - public function useGzip() {
141 - global $wgUseGzip;
142 - return $wgUseGzip;
143 - }
144 -
145 - /* In handy string packages */
146 - public function fetchRawText() {
147 - return file_get_contents( $this->fileCacheName() );
148 - }
149 -
150 - public function fetchPageText() {
151 - if( $this->useGzip() ) {
152 - /* Why is there no gzfile_get_contents() or gzdecode()? */
153 - return implode( '', gzfile( $this->fileCacheName() ) );
154 - } else {
155 - return $this->fetchRawText();
156 - }
157 - }
158 -
159 - /* Working directory to/from output */
160 - public function loadFromFileCache() {
161 - global $wgOut, $wgMimeType, $wgLanguageCode;
16291 wfDebug( __METHOD__ . "()\n");
163 - $filename = $this->fileCacheName();
164 - $wgOut->sendCacheControl();
 92+ $filename = $this->cachePath();
 93+ $context->getOutput()->sendCacheControl();
16594 header( "Content-Type: $wgMimeType; charset=UTF-8" );
16695 header( "Content-Language: $wgLanguageCode" );
167 - if( $this->useGzip() ) {
168 - if( wfClientAcceptsGzip() ) {
 96+ if ( $this->useGzip() ) {
 97+ if ( wfClientAcceptsGzip() ) {
16998 header( 'Content-Encoding: gzip' );
17099 } else {
171100 /* Send uncompressed */
@@ -173,71 +102,70 @@
174103 }
175104 }
176105 readfile( $filename );
177 - $wgOut->disable(); // tell $wgOut that output is taken care of
 106+ $context->getOutput()->disable(); // tell $wgOut that output is taken care of
178107 }
179108
180 - protected function checkCacheDirs() {
181 - $filename = $this->fileCacheName();
182 - $mydir2 = substr($filename,0,strrpos($filename,'/')); # subdirectory level 2
183 - $mydir1 = substr($mydir2,0,strrpos($mydir2,'/')); # subdirectory level 1
184 -
185 - wfMkdirParents( $mydir1, null, __METHOD__ );
186 - wfMkdirParents( $mydir2, null, __METHOD__ );
187 - }
188 -
 109+ /**
 110+ * Save this cache object with the given text.
 111+ * Use this as an ob_start() handler.
 112+ * @param $text string
 113+ * @return bool Whether $wgUseFileCache is enabled
 114+ */
189115 public function saveToFileCache( $text ) {
190116 global $wgUseFileCache;
191 - if( !$wgUseFileCache || strlen( $text ) < 512 ) {
 117+
 118+ if ( !$wgUseFileCache || strlen( $text ) < 512 ) {
192119 // Disabled or empty/broken output (OOM and PHP errors)
193120 return $text;
194121 }
195122
196123 wfDebug( __METHOD__ . "()\n", false);
197124
198 - $this->checkCacheDirs();
 125+ $now = wfTimestampNow();
 126+ if ( $this->useGzip() ) {
 127+ $text = str_replace(
 128+ '</html>', '<!-- Cached/compressed '.$now." -->\n</html>", $text );
 129+ } else {
 130+ $text = str_replace(
 131+ '</html>', '<!-- Cached '.$now." -->\n</html>", $text );
 132+ }
199133
200 - $f = fopen( $this->fileCacheName(), 'w' );
201 - if($f) {
202 - $now = wfTimestampNow();
203 - if( $this->useGzip() ) {
204 - $rawtext = str_replace( '</html>',
205 - '<!-- Cached/compressed '.$now." -->\n</html>",
206 - $text );
207 - $text = gzencode( $rawtext );
 134+ // Store text to FS...
 135+ $compressed = $this->saveText( $text );
 136+ if ( $compressed === false ) {
 137+ return $text; // error
 138+ }
 139+
 140+ // gzip output to buffer as needed and set headers...
 141+ if ( $this->useGzip() ) {
 142+ // @TODO: ugly wfClientAcceptsGzip() function - use context!
 143+ if ( wfClientAcceptsGzip() ) {
 144+ header( 'Content-Encoding: gzip' );
 145+ return $compressed;
208146 } else {
209 - $text = str_replace( '</html>',
210 - '<!-- Cached '.$now." -->\n</html>",
211 - $text );
212 - }
213 - fwrite( $f, $text );
214 - fclose( $f );
215 - if( $this->useGzip() ) {
216 - if( wfClientAcceptsGzip() ) {
217 - header( 'Content-Encoding: gzip' );
218 - return $text;
219 - } else {
220 - return $rawtext;
221 - }
222 - } else {
223147 return $text;
224148 }
 149+ } else {
 150+ return $text;
225151 }
226 - return $text;
227152 }
228153
229 - public static function clearFileCache( $title ) {
 154+ /**
 155+ * Clear the file caches for a page for all actions
 156+ * @param $title Title
 157+ * @return bool Whether $wgUseFileCache is enabled
 158+ */
 159+ public static function clearFileCache( Title $title ) {
230160 global $wgUseFileCache;
231161
232162 if ( !$wgUseFileCache ) {
233163 return false;
234164 }
235165
236 - wfSuppressWarnings();
237 - foreach( self::cacheableActions() as $type ) {
238 - $fc = new self( $title, $type );
239 - unlink( $fc->fileCacheName() );
 166+ foreach ( self::cacheablePageActions() as $type ) {
 167+ $fc = self::newFromTitle( $title, $type );
 168+ $fc->clearCache();
240169 }
241 - wfRestoreWarnings();
242170
243171 return true;
244172 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r98458FU r98405: various document cleanupaaron21:17, 29 September 2011
r98661Reverted accidental change in r98405aaron07:29, 2 October 2011
r98698FileCache:...aaron17:53, 2 October 2011
r99143FU r98405: ObjectFileCache doesn't need to be abstractaaron21:13, 6 October 2011
r110871simplify FileCacheBase::fetchText...hashar20:47, 7 February 2012

Comments

#Comment by Dantman (talk | contribs)   17:56, 29 September 2011
$context = RequestContext::getMain();

I don't consider this an acceptable replacement pattern for globals.

HistoryPage has access to an Article instance which should have access to a IContextSource, please attempt to get the context from there.

Please see some of my relevant pageoutput branch changes for reference: r1=97082&r2=97172 http://svn.wikimedia.org/viewvc/mediawiki/branches/pageoutput/includes/HistoryPage.php?r1=97082&r2=97172

#Comment by Aaron Schulz (talk | contribs)   18:00, 29 September 2011

Yes, it sucks. It's not a general pattern for use. Article needs a rewrite and HistoryPage should take a WikiPage and a Context anyway, which I'm not doing right now.

You can make the suggested change if you want of course.

#Comment by Reedy (talk | contribs)   15:26, 6 October 2011
	public static function newFromKey( $key, $type ) {
		$cache = new self();

		$cache->mKey = (string)$key;
		$cache->mType = (string)$type;
		$cache->mExt = 'cache';

		return $cache;
	}

new self(); in an abstract class isn't valid:


abstract class foo {
static function create() {
return new self();
}
}

class foo2 extends foo {

}

var_dump( foo::create() );
PHP Fatal error:  Cannot instantiate abstract class foo in /home/reedy/test.php on line 5
PHP Stack trace:
PHP   1. {main}() /home/reedy/test.php:0
PHP   2. foo::create() /home/reedy/test.php:13

abstract class foo {
static function create() {
return new self();
}
}

class foo2 extends foo {

}

var_dump( foo1::create() );
reedy@ubuntu64-web-esxi:~$ php test.php
PHP Fatal error:  Cannot instantiate abstract class foo in /home/reedy/test.php on line 5
PHP Stack trace:
PHP   1. {main}() /home/reedy/test.php:0
PHP   2. foo::create() /home/reedy/test.php:13
#Comment by Tim Starling (talk | contribs)   06:55, 23 December 2011
/* Why is there no gzfile_get_contents() or gzdecode()? */
return implode( '', gzfile( $this->cachePath() ) );

Not your code of course, but since you're claiming to have rewritten this class, I guess you get responsibility for this sort of thing. There is no gzdecode() in PHP 5.2, but there is readgzfile() which can be used with output buffering to get the file contents as a string without using large amounts of memory and CPU time by splitting it into an array. Alternatively gzopen() and a loop of gzread() can be used. gzopen() appears to return a stream resource, so stream_get_contents() would be another option.

#Comment by Hashar (talk | contribs)   20:48, 7 February 2012

r110871 implements gzopen() + stream_get_contents()

#Comment by RobLa-WMF (talk | contribs)   22:14, 30 January 2012

Giving to Antoine to peer review per conversation in MWCore meeting today.

Status & tagging log