[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Cache for outputs of the PHP parser 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup Cache Parser 22 */ 23 24 /** 25 * @ingroup Cache Parser 26 * @todo document 27 */ 28 class ParserCache { 29 /** @var MWMemcached */ 30 private $mMemc; 31 /** 32 * Get an instance of this object 33 * 34 * @return ParserCache 35 */ 36 public static function singleton() { 37 static $instance; 38 if ( !isset( $instance ) ) { 39 global $parserMemc; 40 $instance = new ParserCache( $parserMemc ); 41 } 42 return $instance; 43 } 44 45 /** 46 * Setup a cache pathway with a given back-end storage mechanism. 47 * May be a memcached client or a BagOStuff derivative. 48 * 49 * @param MWMemcached $memCached 50 * @throws MWException 51 */ 52 protected function __construct( $memCached ) { 53 if ( !$memCached ) { 54 throw new MWException( "Tried to create a ParserCache with an invalid memcached" ); 55 } 56 $this->mMemc = $memCached; 57 } 58 59 /** 60 * @param Article $article 61 * @param string $hash 62 * @return mixed|string 63 */ 64 protected function getParserOutputKey( $article, $hash ) { 65 global $wgRequest; 66 67 // idhash seem to mean 'page id' + 'rendering hash' (r3710) 68 $pageid = $article->getID(); 69 $renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' ); 70 71 $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" ); 72 return $key; 73 } 74 75 /** 76 * @param Article $article 77 * @return mixed|string 78 */ 79 protected function getOptionsKey( $article ) { 80 $pageid = $article->getID(); 81 return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" ); 82 } 83 84 /** 85 * Provides an E-Tag suitable for the whole page. Note that $article 86 * is just the main wikitext. The E-Tag has to be unique to the whole 87 * page, even if the article itself is the same, so it uses the 88 * complete set of user options. We don't want to use the preference 89 * of a different user on a message just because it wasn't used in 90 * $article. For example give a Chinese interface to a user with 91 * English preferences. That's why we take into account *all* user 92 * options. (r70809 CR) 93 * 94 * @param Article $article 95 * @param ParserOptions $popts 96 * @return string 97 */ 98 public function getETag( $article, $popts ) { 99 return 'W/"' . $this->getParserOutputKey( $article, 100 $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ) . 101 "--" . $article->getTouched() . '"'; 102 } 103 104 /** 105 * Retrieve the ParserOutput from ParserCache, even if it's outdated. 106 * @param Article $article 107 * @param ParserOptions $popts 108 * @return ParserOutput|bool False on failure 109 */ 110 public function getDirty( $article, $popts ) { 111 $value = $this->get( $article, $popts, true ); 112 return is_object( $value ) ? $value : false; 113 } 114 115 /** 116 * Generates a key for caching the given article considering 117 * the given parser options. 118 * 119 * @note Which parser options influence the cache key 120 * is controlled via ParserOutput::recordOption() or 121 * ParserOptions::addExtraKey(). 122 * 123 * @note Used by Article to provide a unique id for the PoolCounter. 124 * It would be preferable to have this code in get() 125 * instead of having Article looking in our internals. 126 * 127 * @todo Document parameter $useOutdated 128 * 129 * @param Article $article 130 * @param ParserOptions $popts 131 * @param bool $useOutdated (default true) 132 * @return bool|mixed|string 133 */ 134 public function getKey( $article, $popts, $useOutdated = true ) { 135 global $wgCacheEpoch; 136 137 if ( $popts instanceof User ) { 138 wfWarn( "Use of outdated prototype ParserCache::getKey( &\$article, &\$user )\n" ); 139 $popts = ParserOptions::newFromUser( $popts ); 140 } 141 142 // Determine the options which affect this article 143 $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) ); 144 if ( $optionsKey != false ) { 145 if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) { 146 wfIncrStats( "pcache_miss_expired" ); 147 $cacheTime = $optionsKey->getCacheTime(); 148 wfDebug( "Parser options key expired, touched " . $article->getTouched() 149 . ", epoch $wgCacheEpoch, cached $cacheTime\n" ); 150 return false; 151 } elseif ( $optionsKey->isDifferentRevision( $article->getLatest() ) ) { 152 wfIncrStats( "pcache_miss_revid" ); 153 $revId = $article->getLatest(); 154 $cachedRevId = $optionsKey->getCacheRevisionId(); 155 wfDebug( "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n" ); 156 return false; 157 } 158 159 // $optionsKey->mUsedOptions is set by save() by calling ParserOutput::getUsedOptions() 160 $usedOptions = $optionsKey->mUsedOptions; 161 wfDebug( "Parser cache options found.\n" ); 162 } else { 163 if ( !$useOutdated ) { 164 return false; 165 } 166 $usedOptions = ParserOptions::legacyOptions(); 167 } 168 169 return $this->getParserOutputKey( 170 $article, 171 $popts->optionsHash( $usedOptions, $article->getTitle() ) 172 ); 173 } 174 175 /** 176 * Retrieve the ParserOutput from ParserCache. 177 * false if not found or outdated. 178 * 179 * @param Article $article 180 * @param ParserOptions $popts 181 * @param bool $useOutdated (default false) 182 * 183 * @return ParserOutput|bool False on failure 184 */ 185 public function get( $article, $popts, $useOutdated = false ) { 186 global $wgCacheEpoch; 187 wfProfileIn( __METHOD__ ); 188 189 $canCache = $article->checkTouched(); 190 if ( !$canCache ) { 191 // It's a redirect now 192 wfProfileOut( __METHOD__ ); 193 return false; 194 } 195 196 $touched = $article->getTouched(); 197 198 $parserOutputKey = $this->getKey( $article, $popts, $useOutdated ); 199 if ( $parserOutputKey === false ) { 200 wfIncrStats( 'pcache_miss_absent' ); 201 wfProfileOut( __METHOD__ ); 202 return false; 203 } 204 205 $value = $this->mMemc->get( $parserOutputKey ); 206 if ( !$value ) { 207 wfDebug( "ParserOutput cache miss.\n" ); 208 wfIncrStats( "pcache_miss_absent" ); 209 wfProfileOut( __METHOD__ ); 210 return false; 211 } 212 213 wfDebug( "ParserOutput cache found.\n" ); 214 215 // The edit section preference may not be the appropiate one in 216 // the ParserOutput, as we are not storing it in the parsercache 217 // key. Force it here. See bug 31445. 218 $value->setEditSectionTokens( $popts->getEditSection() ); 219 220 if ( !$useOutdated && $value->expired( $touched ) ) { 221 wfIncrStats( "pcache_miss_expired" ); 222 $cacheTime = $value->getCacheTime(); 223 wfDebug( "ParserOutput key expired, touched $touched, " 224 . "epoch $wgCacheEpoch, cached $cacheTime\n" ); 225 $value = false; 226 } elseif ( $value->isDifferentRevision( $article->getLatest() ) ) { 227 wfIncrStats( "pcache_miss_revid" ); 228 $revId = $article->getLatest(); 229 $cachedRevId = $value->getCacheRevisionId(); 230 wfDebug( "ParserOutput key is for an old revision, latest $revId, cached $cachedRevId\n" ); 231 $value = false; 232 } else { 233 wfIncrStats( "pcache_hit" ); 234 } 235 236 wfProfileOut( __METHOD__ ); 237 return $value; 238 } 239 240 /** 241 * @param ParserOutput $parserOutput 242 * @param WikiPage $page 243 * @param ParserOptions $popts 244 * @param string $cacheTime Time when the cache was generated 245 * @param int $revId Revision ID that was parsed 246 */ 247 public function save( $parserOutput, $page, $popts, $cacheTime = null, $revId = null ) { 248 $expire = $parserOutput->getCacheExpiry(); 249 if ( $expire > 0 ) { 250 $cacheTime = $cacheTime ?: wfTimestampNow(); 251 if ( !$revId ) { 252 $revision = $page->getRevision(); 253 $revId = $revision ? $revision->getId() : null; 254 } 255 256 $optionsKey = new CacheTime; 257 $optionsKey->mUsedOptions = $parserOutput->getUsedOptions(); 258 $optionsKey->updateCacheExpiry( $expire ); 259 260 $optionsKey->setCacheTime( $cacheTime ); 261 $parserOutput->setCacheTime( $cacheTime ); 262 $optionsKey->setCacheRevisionId( $revId ); 263 $parserOutput->setCacheRevisionId( $revId ); 264 265 $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() ); 266 267 $parserOutputKey = $this->getParserOutputKey( $page, 268 $popts->optionsHash( $optionsKey->mUsedOptions, $page->getTitle() ) ); 269 270 // Save the timestamp so that we don't have to load the revision row on view 271 $parserOutput->setTimestamp( $page->getTimestamp() ); 272 273 $msg = "Saved in parser cache with key $parserOutputKey" . 274 " and timestamp $cacheTime" . 275 " and revision id $revId" . 276 "\n"; 277 278 $parserOutput->mText .= "\n<!-- $msg -->\n"; 279 wfDebug( $msg ); 280 281 // Save the parser output 282 $this->mMemc->set( $parserOutputKey, $parserOutput, $expire ); 283 284 // ...and its pointer 285 $this->mMemc->set( $this->getOptionsKey( $page ), $optionsKey, $expire ); 286 } else { 287 wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" ); 288 } 289 } 290 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |