}
# Should the parser cache be used?
- $useParserCache = $this->mPage->isParserCacheUsed( $wgUser, $oldid );
+ $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid );
wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
if ( $wgUser->getStubThreshold() ) {
wfIncrStats( 'pcache_miss_stub' );
# Run the parse, protected by a pool counter
wfDebug( __METHOD__ . ": doing uncached parse\n" );
- $key = $parserCache->getKey( $this, $parserOptions );
- $poolArticleView = new PoolWorkArticleView( $this, $key, $useParserCache, $parserOptions );
+ $poolArticleView = new PoolWorkArticleView( $this, $parserOptions,
+ $this->getRevIdFetched(), $useParserCache, $this->getContent() );
if ( !$poolArticleView->execute() ) {
+ $error = $poolArticleView->getError();
+ if ( $error ) {
+ $wgOut->clearHTML(); // for release() errors
+ $wgOut->enableClientCache( false );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+
+ $errortext = $error->getWikiText( false, 'view-pool-error' );
+ $wgOut->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
+ }
# Connection or timeout error
wfProfileOut( __METHOD__ );
return;
- } else {
- $outputDone = true;
}
+
+ $this->mParserOutput = $poolArticleView->getParserOutput();
+ $wgOut->addParserOutput( $this->mParserOutput );
+
+ # Don't cache a dirty ParserOutput object
+ if ( $poolArticleView->getIsDirty() ) {
+ $wgOut->setSquidMaxage( 0 );
+ $wgOut->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
+ }
+
+ $outputDone = true;
break;
# Should be unreachable, but just in case...
default:
$prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>" );
}
- /**
- * Execute the uncached parse for action=view
- * @return bool
- */
- public function doViewParse() {
- global $wgOut;
-
- $oldid = $this->getOldID();
- $parserOptions = $this->getParserOptions();
-
- # Render printable version, use printable version cache
- $parserOptions->setIsPrintable( $wgOut->isPrintable() );
-
- # Don't show section-edit links on old revisions... this way lies madness.
- if ( !$this->isCurrent() || $wgOut->isPrintable() || !$this->getTitle()->quickUserCan( 'edit' ) ) {
- $parserOptions->setEditSection( false );
- }
-
- $useParserCache = $this->useParserCache( $oldid );
- $this->outputWikiText( $this->getContent(), $useParserCache, $parserOptions );
-
- return true;
- }
-
- /**
- * Try to fetch an expired entry from the parser cache. If it is present,
- * output it and return true. If it is not present, output nothing and
- * return false. This is used as a callback function for
- * PoolCounter::executeProtected().
- *
- * @return boolean
- */
- public function tryDirtyCache() {
- global $wgOut;
- $parserCache = ParserCache::singleton();
- $options = $this->getParserOptions();
-
- if ( $wgOut->isPrintable() ) {
- $options->setIsPrintable( true );
- $options->setEditSection( false );
- }
-
- $output = $parserCache->getDirty( $this, $options );
-
- if ( $output ) {
- wfDebug( __METHOD__ . ": sending dirty output\n" );
- wfDebugLog( 'dirty', "dirty output " . $parserCache->getKey( $this, $options ) . "\n" );
- $wgOut->setSquidMaxage( 0 );
- $this->mParserOutput = $output;
- $wgOut->addParserOutput( $output );
- $wgOut->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
-
- return true;
- } else {
- wfDebugLog( 'dirty', "dirty missing\n" );
- wfDebug( __METHOD__ . ": no dirty cache\n" );
-
- return false;
- }
- }
-
/**
* View redirect
*
/**#@-*/
- /**
- * Add the primary page-view wikitext to the output buffer
- * Saves the text into the parser cache if possible.
- * Updates templatelinks if it is out of date.
- *
- * @param $text String
- * @param $cache Boolean
- * @param $parserOptions mixed ParserOptions object, or boolean false
- */
- public function outputWikiText( $text, $cache = true, $parserOptions = false ) {
- global $wgOut;
-
- $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions );
-
- $this->doCascadeProtectionUpdates( $this->mParserOutput );
-
- $wgOut->addParserOutput( $this->mParserOutput );
- }
-
/**
* Lightweight method to get the parser output for a page, checking the parser cache
* and so on. Doesn't consider most of the stuff that WikiPage::view is forced to
* @return ParserOutput or false if the given revsion ID is not found
*/
public function getParserOutput( $oldid = null, User $user = null ) {
- global $wgEnableParserCache, $wgUser;
- $user = is_null( $user ) ? $wgUser : $user;
-
- wfProfileIn( __METHOD__ );
- // Should the parser cache be used?
- $useParserCache = $wgEnableParserCache &&
- $user->getStubThreshold() == 0 &&
- $this->mPage->exists() &&
- $oldid === null;
-
- wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
-
- if ( $user->getStubThreshold() ) {
- wfIncrStats( 'pcache_miss_stub' );
- }
-
- if ( $useParserCache ) {
- $options = $this->mPage->makeParserOptions( $user );
- $parserOutput = ParserCache::singleton()->get( $this, $options );
- if ( $parserOutput !== false ) {
- wfProfileOut( __METHOD__ );
- return $parserOutput;
- }
- }
-
- // Cache miss; parse and output it.
- if ( $oldid === null ) {
- $text = $this->mPage->getRawText();
- } else {
- $rev = Revision::newFromTitle( $this->getTitle(), $oldid );
- if ( $rev === null ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
- $text = $rev->getText();
- }
-
- $output = $this->getOutputFromWikitext( $text, $useParserCache );
- wfProfileOut( __METHOD__ );
- return $output;
- }
-
- /**
- * This does all the heavy lifting for outputWikitext, except it returns the parser
- * output instead of sending it straight to $wgOut. Makes things nice and simple for,
- * say, embedding thread pages within a discussion system (LiquidThreads)
- *
- * @param $text string
- * @param $cache boolean
- * @param $parserOptions parsing options, defaults to false
- * @return ParserOutput
- */
- public function getOutputFromWikitext( $text, $cache = true, $parserOptions = false ) {
- global $wgParser, $wgEnableParserCache, $wgUseFileCache;
-
- if ( !$parserOptions ) {
- $parserOptions = $this->getParserOptions();
- }
-
- $time = - wfTime();
- $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(),
- $parserOptions, true, true, $this->getRevIdFetched() );
- $time += wfTime();
-
- # Timing hack
- if ( $time > 3 ) {
- wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
- $this->getTitle()->getPrefixedDBkey() ) );
- }
-
- if ( $wgEnableParserCache && $cache && $this->mParserOutput->isCacheable() ) {
- $parserCache = ParserCache::singleton();
- $parserCache->save( $this->mParserOutput, $this, $parserOptions );
- }
-
- // Make sure file cache is not used on uncacheable content.
- // Output that has magic words in it can still use the parser cache
- // (if enabled), though it will generally expire sooner.
- if ( !$this->mParserOutput->isCacheable() || $this->mParserOutput->containsOldMagic() ) {
- $wgUseFileCache = false;
- }
+ global $wgUser;
- if ( $this->isCurrent() ) {
- $this->mPage->doCascadeProtectionUpdates( $this->mParserOutput );
- }
+ $user = is_null( $user ) ? $wgUser : $user;
+ $parserOptions = $this->mPage->makeParserOptions( $user );
- return $this->mParserOutput;
+ return $this->mPage->getParserOutput( $parserOptions, $oldid );
}
/**
}
// ******
}
-
-class PoolWorkArticleView extends PoolCounterWork {
-
- /**
- * @var Article
- */
- private $mArticle;
-
- function __construct( $article, $key, $useParserCache, $parserOptions ) {
- parent::__construct( 'ArticleView', $key );
- $this->mArticle = $article;
- $this->cacheable = $useParserCache;
- $this->parserOptions = $parserOptions;
- }
-
- /**
- * @return bool
- */
- function doWork() {
- return $this->mArticle->doViewParse();
- }
-
- /**
- * @return bool
- */
- function getCachedWork() {
- global $wgOut;
-
- $parserCache = ParserCache::singleton();
- $this->mArticle->mParserOutput = $parserCache->get( $this->mArticle, $this->parserOptions );
-
- if ( $this->mArticle->mParserOutput !== false ) {
- wfDebug( __METHOD__ . ": showing contents parsed by someone else\n" );
- $wgOut->addParserOutput( $this->mArticle->mParserOutput );
- # Ensure that UI elements requiring revision ID have
- # the correct version information.
- $wgOut->setRevisionId( $this->mArticle->getLatest() );
- return true;
- }
- return false;
- }
-
- /**
- * @return bool
- */
- function fallback() {
- return $this->mArticle->tryDirtyCache();
- }
-
- /**
- * @param $status Status
- */
- function error( $status ) {
- global $wgOut;
-
- $wgOut->clearHTML(); // for release() errors
- $wgOut->enableClientCache( false );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
-
- $errortext = $status->getWikiText( false, 'view-pool-error' );
- $wgOut->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
-
- return false;
- }
-}
/**
* Should the parser cache be used?
*
- * @param $user User The relevant user
+ * @param $parserOptions ParserOptions to check
* @param $oldid int
* @return boolean
*/
- public function isParserCacheUsed( User $user, $oldid ) {
+ public function isParserCacheUsed( ParserOptions $parserOptions, $oldid ) {
global $wgEnableParserCache;
return $wgEnableParserCache
- && $user->getStubThreshold() == 0
+ && $parserOptions->getStubThreshold() == 0
&& $this->exists()
&& ( $oldid === null || $oldid === 0 || $oldid === $this->getLatest() )
&& $this->mTitle->isWikitextPage();
}
+ /**
+ * Get a ParserOutput for the given ParserOptions and revision ID.
+ * The the parser cache will be used if possible.
+ *
+ * @since 1.19
+ * @param $parserOptions ParserOptions to use for the parse operation
+ * @param $oldid Revision ID to get the text from, passing null or 0 will
+ * get the current revision (default value)
+ * @return ParserOutput or false if the revision was not found
+ */
+ public function getParserOutput( ParserOptions $parserOptions, $oldid = null ) {
+ global $wgParser;
+
+ wfProfileIn( __METHOD__ );
+
+ $useParserCache = $this->isParserCacheUsed( $parserOptions, $oldid );
+ wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
+ if ( $parserOptions->getStubThreshold() ) {
+ wfIncrStats( 'pcache_miss_stub' );
+ }
+
+ if ( $useParserCache ) {
+ $parserOutput = ParserCache::singleton()->get( $this, $parserOptions );
+ if ( $parserOutput !== false ) {
+ wfProfileOut( __METHOD__ );
+ return $parserOutput;
+ }
+ }
+
+ if ( $oldid === null || $oldid === 0 ) {
+ $oldid = $this->getLatest();
+ }
+
+ $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache );
+ $pool->execute();
+
+ wfProfileOut( __METHOD__ );
+
+ return $pool->getParserOutput();
+ }
+
/**
* Perform the actions of a page purging
*/
*/
public function useParserCache( $oldid ) {
global $wgUser;
- return $this->isParserCacheUsed( $wgUser, $oldid );
+ return $this->isParserCacheUsed( ParserOptions::newFromUser( $wgUser ), $oldid );
+ }
+}
+
+class PoolWorkArticleView extends PoolCounterWork {
+
+ /**
+ * @var Page
+ */
+ private $page;
+
+ /**
+ * @var string
+ */
+ private $cacheKey;
+
+ /**
+ * @var integer
+ */
+ private $revid;
+
+ /**
+ * @var ParserOptions
+ */
+ private $parserOptions;
+
+ /**
+ * @var string|null
+ */
+ private $text;
+
+ /**
+ * @var ParserOutput|false
+ */
+ private $parserOutput = false;
+
+ /**
+ * @var bool
+ */
+ private $isDirty = false;
+
+ /**
+ * @var Status|false
+ */
+ private $error = false;
+
+ /**
+ * Constructor
+ *
+ * @param $page Page
+ * @param $revid Integer: ID of the revision being parsed
+ * @param $useParserCache Boolean: whether to use the parser cache
+ * @param $parserOptions parserOptions to use for the parse operation
+ * @param $text String: text to parse or null to load it
+ */
+ function __construct( Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $text = null ) {
+ $this->page = $page;
+ $this->revid = $revid;
+ $this->cacheable = $useParserCache;
+ $this->parserOptions = $parserOptions;
+ $this->text = $text;
+ $this->cacheKey = ParserCache::singleton()->getKey( $page, $parserOptions );
+ parent::__construct( 'ArticleView', $this->cacheKey . ':revid:' . $revid );
+ }
+
+ /**
+ * Get the ParserOutput from this object, or false in case of failure
+ *
+ * @return ParserOutput
+ */
+ public function getParserOutput() {
+ return $this->parserOutput;
+ }
+
+ /**
+ * Get whether the ParserOutput is a dirty one (i.e. expired)
+ *
+ * @return bool
+ */
+ public function getIsDirty() {
+ return $this->isDirty();
+ }
+
+ /**
+ * Get a Status object in case of error or false otherwise
+ *
+ * @return Status|false
+ */
+ public function getError() {
+ return $this->error;
+ }
+
+ /**
+ * @return bool
+ */
+ function doWork() {
+ global $wgParser, $wgUseFileCache;
+
+ $isCurrent = $this->revid === $this->page->getLatest();
+
+ if ( $this->text !== null ) {
+ $text = $this->text;
+ } elseif ( $isCurrent ) {
+ $text = $this->page->getRawText();
+ } else {
+ $rev = Revision::newFromTitle( $this->page->getTitle(), $this->revid );
+ if ( $rev === null ) {
+ return false;
+ }
+ $text = $rev->getText();
+ }
+
+ $time = - wfTime();
+ $this->parserOutput = $wgParser->parse( $text, $this->page->getTitle(),
+ $this->parserOptions, true, true, $this->revid );
+ $time += wfTime();
+
+ # Timing hack
+ if ( $time > 3 ) {
+ wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
+ $this->page->getTitle()->getPrefixedDBkey() ) );
+ }
+
+ if ( $this->cacheable && $this->parserOutput->isCacheable() ) {
+ ParserCache::singleton()->save( $this->parserOutput, $this->page, $this->parserOptions );
+ }
+
+ // Make sure file cache is not used on uncacheable content.
+ // Output that has magic words in it can still use the parser cache
+ // (if enabled), though it will generally expire sooner.
+ if ( !$this->parserOutput->isCacheable() || $this->parserOutput->containsOldMagic() ) {
+ $wgUseFileCache = false;
+ }
+
+ if ( $isCurrent ) {
+ $this->page->doCascadeProtectionUpdates( $this->parserOutput );
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ function getCachedWork() {
+ $this->parserOutput = ParserCache::singleton()->get( $this->page, $this->parserOptions );
+
+ if ( $this->parserOutput === false ) {
+ wfDebug( __METHOD__ . ": parser cache miss\n" );
+ return false;
+ } else {
+ wfDebug( __METHOD__ . ": parser cache hit\n" );
+ return true;
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ function fallback() {
+ $this->parserOutput = ParserCache::singleton()->getDirty( $this->page, $this->parserOptions );
+
+ if ( $this->parserOutput === false ) {
+ wfDebugLog( 'dirty', "dirty missing\n" );
+ wfDebug( __METHOD__ . ": no dirty cache\n" );
+ return false;
+ } else {
+ wfDebug( __METHOD__ . ": sending dirty output\n" );
+ wfDebugLog( 'dirty', "dirty output {$this->cacheKey}\n" );
+ $this->isDirty = true;
+ return true;
+ }
+ }
+
+ /**
+ * @param $status Status
+ */
+ function error( $status ) {
+ $this->error = $status;
+ return false;
}
}