Merge "parser: add vary-revision-sha1 and related ParserOutput methods"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 17 Jul 2019 23:57:17 +0000 (23:57 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 17 Jul 2019 23:57:17 +0000 (23:57 +0000)
1  2 
includes/Storage/PageEditStash.php
includes/parser/Parser.php
tests/parser/ParserTestRunner.php

@@@ -109,7 -109,7 +109,7 @@@ class PageEditStash 
                // the stash request finishes parsing. For the lock acquisition below, there is not much
                // need to duplicate parsing of the same content/user/summary bundle, so try to avoid
                // blocking at all here.
 -              $dbw = $this->lb->getConnection( DB_MASTER );
 +              $dbw = $this->lb->getConnectionRef( DB_MASTER );
                if ( !$dbw->lock( $key, $fname, 0 ) ) {
                        // De-duplicate requests on the same key
                        return self::ERROR_BUSY;
  
                if ( $editInfo->output->getFlag( 'vary-revision' ) ) {
                        // This can be used for the initial parse, e.g. for filters or doEditContent(),
-                       // but a second parse will be triggered in doEditUpdates(). This is not optimal.
+                       // but a second parse will be triggered in doEditUpdates() no matter what
                        $logger->info(
-                               "Cache for key '{key}' has vary_revision; post-insertion parse inevitable.",
-                               $context
-                       );
-               } elseif ( $editInfo->output->getFlag( 'vary-revision-id' ) ) {
-                       // Similar to the above if we didn't guess the ID correctly.
-                       $logger->debug(
-                               "Cache for key '{key}' has vary_revision_id; post-insertion parse possible.",
-                               $context
-                       );
-               } elseif ( $editInfo->output->getFlag( 'vary-revision-timestamp' ) ) {
-                       // Similar to the above if we didn't guess the timestamp correctly.
-                       $logger->debug(
-                               "Cache for key '{key}' has vary_revision_timestamp; post-insertion parse possible.",
+                               "Cache for key '{key}' has 'vary-revision'; post-insertion parse inevitable.",
                                $context
                        );
+               } else {
+                       static $flagsMaybeReparse = [
+                               // Similar to the above if we didn't guess the ID correctly
+                               'vary-revision-id',
+                               // Similar to the above if we didn't guess the timestamp correctly
+                               'vary-revision-timestamp',
+                               // Similar to the above if we didn't guess the content correctly
+                               'vary-revision-sha1'
+                       ];
+                       foreach ( $flagsMaybeReparse as $flag ) {
+                               if ( $editInfo->output->getFlag( $flag ) ) {
+                                       $logger->debug(
+                                               "Cache for key '{key}' has $flag; post-insertion parse possible.",
+                                               $context
+                                       );
+                               }
+                       }
                }
  
                return $editInfo;
         * @return string|null TS_MW timestamp or null
         */
        private function lastEditTime( User $user ) {
 -              $db = $this->lb->getConnection( DB_REPLICA );
 +              $db = $this->lb->getConnectionRef( DB_REPLICA );
 +
                $actorQuery = ActorMigration::newMigration()->getWhere( $db, 'rc_user', $user, false );
                $time = $db->selectField(
                        [ 'recentchanges' ] + $actorQuery['tables'],
@@@ -205,11 -205,9 +205,11 @@@ class Parser 
        public $mLinkID;
        public $mIncludeSizes, $mPPNodeCount, $mGeneratedPPNodeCount, $mHighestExpansionDepth;
        public $mDefaultSort;
 -      public $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
 +      public $mTplRedirCache, $mHeadings, $mDoubleUnderscores;
        public $mExpensiveFunctionCount; # number of expensive parser function calls
        public $mShowToc, $mForceTocPosition;
 +      /** @var array */
 +      public $mTplDomCache;
  
        /**
         * @var User
         *  self::OT_HTML: all templates and extension tags
         *
         * @param string $text The text to transform
 -       * @param bool|PPFrame $frame Object describing the arguments passed to the
 +       * @param false|PPFrame|array $frame Object describing the arguments passed to the
         *   template. Arguments may also be provided as an associative array, as
         *   was the usual case before MW1.12. Providing arguments this way may be
         *   useful for extensions wishing to perform variable replacement
         *   $piece['lineStart']: whether the brace was at the start of a line
         * @param PPFrame $frame The current frame, contains template arguments
         * @throws Exception
 -       * @return string The text of the template
 +       * @return string|array The text of the template
         */
        public function braceSubstitution( $piece, $frame ) {
                // Flags
                return $this->currentRevisionCache->get( $cacheKey );
        }
  
+       /**
+        * @param Title $title
+        * @return bool
+        * @since 1.34
+        */
+       public function isCurrentRevisionOfTitleCached( $title ) {
+               return (
+                       $this->currentRevisionCache &&
+                       $this->currentRevisionCache->has( $title->getPrefixedText() )
+               );
+       }
        /**
         * Wrapper around Revision::newFromTitle to allow passing additional parameters
         * without passing them on to it.
                        foreach ( $stuff['deps'] as $dep ) {
                                $this->mOutput->addTemplate( $dep['title'], $dep['page_id'], $dep['rev_id'] );
                                if ( $dep['title']->equals( $this->getTitle() ) ) {
-                                       // If we transclude ourselves, the final result
-                                       // will change based on the new version of the page
+                                       // Self-transclusion; final result may change based on the new page version
                                        $this->mOutput->setFlag( 'vary-revision' );
                                        wfDebug( __METHOD__ . ": self transclusion, setting vary-revision" );
                                }
@@@ -25,6 -25,7 +25,7 @@@
   * @file
   * @ingroup Testing
   */
  use Wikimedia\Rdbms\IDatabase;
  use MediaWiki\MediaWikiServices;
  use MediaWiki\Tidy\TidyDriverBase;
@@@ -129,6 -130,9 +130,9 @@@ class ParserTestRunner 
         */
        private $keepUploads;
  
+       /** @var Title */
+       private $defaultTitle;
        /**
         * @param TestRecorder $recorder
         * @param array $options
                if ( isset( $options['upload-dir'] ) ) {
                        $this->uploadDir = $options['upload-dir'];
                }
+               $this->defaultTitle = Title::newFromText( 'Parser test' );
        }
  
        /**
                $setup['wgNoFollowDomainExceptions'] = [ 'no-nofollow.org' ];
                $setup['wgExternalLinkTarget'] = false;
                $setup['wgLocaltimezone'] = 'UTC';
 -              $setup['wgHtml5'] = true;
                $setup['wgDisableLangConversion'] = false;
                $setup['wgDisableTitleConversion'] = false;
  
                        $options->setTidy( true );
                }
  
-               if ( isset( $opts['title'] ) ) {
-                       $titleText = $opts['title'];
-               } else {
-                       $titleText = 'Parser test';
+               $revId = 1337; // see Parser::getRevisionId()
+               $title = isset( $opts['title'] )
+                       ? Title::newFromText( $opts['title'] )
+                       : $this->defaultTitle;
+               if ( isset( $opts['lastsavedrevision'] ) ) {
+                       $content = new WikitextContent( $test['input'] );
+                       $title = Title::newFromRow( (object)[
+                               'page_id' => 187,
+                               'page_len' => $content->getSize(),
+                               'page_latest' => 1337,
+                               'page_namespace' => $title->getNamespace(),
+                               'page_title' => $title->getDBkey(),
+                               'page_is_redirect' => 0
+                       ] );
+                       $rev = new Revision(
+                               [
+                                       'id' => $title->getLatestRevID(),
+                                       'page' => $title->getArticleID(),
+                                       'user' => $user,
+                                       'content' => $content,
+                                       'timestamp' => $this->getFakeTimestamp(),
+                                       'title' => $title
+                               ],
+                               Revision::READ_LATEST,
+                               $title
+                       );
+                       $oldCallback = $options->getCurrentRevisionCallback();
+                       $options->setCurrentRevisionCallback(
+                               function ( Title $t, $parser ) use ( $title, $rev, $oldCallback ) {
+                                       if ( $t->equals( $title ) ) {
+                                               return $rev;
+                                       } else {
+                                               return call_user_func( $oldCallback, $t, $parser );
+                                       }
+                               }
+                       );
                }
  
                if ( isset( $opts['maxincludesize'] ) ) {
                $local = isset( $opts['local'] );
                $preprocessor = $opts['preprocessor'] ?? null;
                $parser = $this->getParser( $preprocessor );
-               $title = Title::newFromText( $titleText );
  
                if ( isset( $opts['styletag'] ) ) {
                        // For testing the behavior of <style> (including those deduplicated
                } elseif ( isset( $opts['preload'] ) ) {
                        $out = $parser->getPreloadText( $test['input'], $title, $options );
                } else {
-                       $output = $parser->parse( $test['input'], $title, $options, true, true, 1337 );
+                       $output = $parser->parse( $test['input'], $title, $options, true, true, $revId );
                        $out = $output->getText( [
                                'allowTOC' => !isset( $opts['notoc'] ),
                                'unwrap' => !isset( $opts['wrap'] ),
  
                // Wipe WANObjectCache process cache, which is invalidated by article insertion
                // due to T144706
 -              ObjectCache::getMainWANInstance()->clearProcessCache();
 +              MediaWikiServices::getInstance()->getMainWANObjectCache()->clearProcessCache();
  
                $this->executeSetupSnippets( $teardown );
        }