Add parser callback to get a page's current revision
authorJackmcbarn <jackmcbarn@gmail.com>
Tue, 16 Sep 2014 00:07:52 +0000 (20:07 -0400)
committerJackmcbarn <jackmcbarn@gmail.com>
Fri, 19 Sep 2014 15:59:58 +0000 (11:59 -0400)
Add Parser::fetchCurrentRevisionOfTitle(). By default, this just calls
Revision::newFromTitle, but a callback can be set in ParserOptions that
will override it. Anything that runs as part of a parse should use this
wherever possible.

Bug: 70495
Change-Id: I521f1f68ad819cf0f37e63240806f10c1cceef9c

includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/parser/ParserOptions.php

index eacbecd..355a3c1 100644 (file)
@@ -1000,11 +1000,6 @@ class CoreParserFunctions {
         * @since 1.23
         */
        private static function getCachedRevisionObject( $parser, $title = null ) {
-               static $cache = null;
-               if ( $cache == null ) {
-                       $cache = new MapCacheLRU( 50 );
-               }
-
                if ( is_null( $title ) ) {
                        return null;
                }
@@ -1024,22 +1019,18 @@ class CoreParserFunctions {
                // Normalize name for cache
                $page = $title->getPrefixedDBkey();
 
-               if ( $cache->has( $page ) ) { // cache contains null values
-                       return $cache->get( $page );
+               if ( !( $parser->currentRevisionCache && $parser->currentRevisionCache->has( $page ) )
+                       && !$parser->incrementExpensiveFunctionCount() ) {
+                       return null;
                }
-               if ( $parser->incrementExpensiveFunctionCount() ) {
-                       $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
-                       $pageID = $rev ? $rev->getPage() : 0;
-                       $revID = $rev ? $rev->getId() : 0;
-                       $cache->set( $page, $rev ); // maybe null
+               $rev = $parser->fetchCurrentRevisionOfTitle( $title );
+               $pageID = $rev ? $rev->getPage() : 0;
+               $revID = $rev ? $rev->getId() : 0;
 
-                       // Register dependency in templatelinks
-                       $parser->getOutput()->addTemplate( $title, $pageID, $revID );
+               // Register dependency in templatelinks
+               $parser->getOutput()->addTemplate( $title, $pageID, $revID );
 
-                       return $rev;
-               }
-               $cache->set( $page, null );
-               return null;
+               return $rev;
        }
 
        /**
index 8bf400a..bff9c0d 100644 (file)
@@ -210,6 +210,14 @@ class Parser {
         */
        public $mLangLinkLanguages;
 
+       /**
+        * @var MapCacheLRU|null
+        * @since 1.24
+        *
+        * A cache of the current revisions of titles. Keys are $title->getPrefixedDbKey()
+        */
+       public $currentRevisionCache;
+
        /**
         * @var bool Recursive call protection.
         * This variable should be treated as if it were private.
@@ -305,6 +313,7 @@ class Parser {
                $this->mVarCache = array();
                $this->mUser = null;
                $this->mLangLinkLanguages = array();
+               $this->currentRevisionCache = null;
 
                /**
                 * Prefix for temporary replacement strings for the multipass parser.
@@ -376,6 +385,7 @@ class Parser {
 
                $this->startParse( $title, $options, self::OT_HTML, $clearState );
 
+               $this->currentRevisionCache = null;
                $this->mInputSize = strlen( $text );
                if ( $this->mOptions->getEnableLimitReport() ) {
                        $this->mOutput->resetParseStartTime();
@@ -588,6 +598,7 @@ class Parser {
                $this->mRevisionUser = $oldRevisionUser;
                $this->mRevisionSize = $oldRevisionSize;
                $this->mInputSize = false;
+               $this->currentRevisionCache = null;
                wfProfileOut( $fname );
                wfProfileOut( __METHOD__ );
 
@@ -3794,6 +3805,44 @@ class Parser {
                return array( $dom, $title );
        }
 
+       /**
+        * Fetch the current revision of a given title. Note that the revision
+        * (and even the title) may not exist in the database, so everything
+        * contributing to the output of the parser should use this method
+        * where possible, rather than getting the revisions themselves. This
+        * method also caches its results, so using it benefits performance.
+        *
+        * @since 1.24
+        * @param Title $title
+        * @return Revision
+        */
+       public function fetchCurrentRevisionOfTitle( $title ) {
+               $cacheKey = $title->getPrefixedDBkey();
+               if ( !$this->currentRevisionCache ) {
+                       $this->currentRevisionCache = new MapCacheLRU( 100 );
+               }
+               if ( !$this->currentRevisionCache->has( $cacheKey ) ) {
+                       $this->currentRevisionCache->set( $cacheKey,
+                               // Defaults to Parser::statelessFetchRevision()
+                               call_user_func( $this->mOptions->getCurrentRevisionCallback(), $title, $this )
+                       );
+               }
+               return $this->currentRevisionCache->get( $cacheKey );
+       }
+
+       /**
+        * Wrapper around Revision::newFromTitle to allow passing additional parameters
+        * without passing them on to it.
+        *
+        * @since 1.24
+        * @param Title $title
+        * @param Parser|bool $parser
+        * @return Revision
+        */
+       public static function statelessFetchRevision( $title, $parser = false ) {
+               return Revision::newFromTitle( $title );
+       }
+
        /**
         * Fetch the unparsed text of a template and register a reference to it.
         * @param Title $title
@@ -3859,9 +3908,13 @@ class Parser {
                                break;
                        }
                        # Get the revision
-                       $rev = $id
-                               ? Revision::newFromId( $id )
-                               : Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
+                       if ( $id ) {
+                               $rev = Revision::newFromId( $id );
+                       } elseif ( $parser ) {
+                               $rev = $parser->fetchCurrentRevisionOfTitle( $title );
+                       } else {
+                               $rev = Revision::newFromTitle( $title );
+                       }
                        $rev_id = $rev ? $rev->getId() : 0;
                        # If there is no current revision, there is no page
                        if ( $id === false && !$rev ) {
index 2ca9d50..9966a1d 100644 (file)
@@ -116,6 +116,12 @@ class ParserOptions {
         */
        public $mRemoveComments = true;
 
+       /**
+        * Callback for current revision fetching. Used as first argument to call_user_func().
+        */
+       public $mCurrentRevisionCallback =
+               array( 'Parser', 'statelessFetchRevision' );
+
        /**
         * Callback for template fetching. Used as first argument to call_user_func().
         */
@@ -282,6 +288,11 @@ class ParserOptions {
                return $this->mRemoveComments;
        }
 
+       /* @since 1.24 */
+       public function getCurrentRevisionCallback() {
+               return $this->mCurrentRevisionCallback;
+       }
+
        public function getTemplateCallback() {
                return $this->mTemplateCallback;
        }
@@ -455,6 +466,11 @@ class ParserOptions {
                return wfSetVar( $this->mRemoveComments, $x );
        }
 
+       /* @since 1.24 */
+       public function setCurrentRevisionCallback( $x ) {
+               return wfSetVar( $this->mCurrentRevisionCallback, $x );
+       }
+
        public function setTemplateCallback( $x ) {
                return wfSetVar( $this->mTemplateCallback, $x );
        }