[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/parser/ -> ParserCache.php (source)

   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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1