Merge "Deprecate $wgEnableParserCache"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 4 Jun 2015 18:33:29 +0000 (18:33 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 4 Jun 2015 18:33:29 +0000 (18:33 +0000)
1  2 
RELEASE-NOTES-1.26
includes/page/WikiPage.php

diff --combined RELEASE-NOTES-1.26
@@@ -9,13 -9,14 +9,15 @@@ MediaWiki 1.26 is an alpha-quality bran
  production.
  
  === Configuration changes in 1.26 ===
+ * $wgEnableParserCache was deprecated, set $wgParserCacheType to CACHE_NONE
+   instead if you want to disable the parser cache.
  
  === New features in 1.26 ===
  * Change tags can now be hidden in the interface by disabling the associated
    "tag-<id>" interface message.
  * ':' (colon) is now invalid in usernames for new accounts. Existing accounts
    are not affected.
 +* Added a new hook, 'LogException', to log exceptions in nonstandard ways.
  
  ==== External libraries ====
  
@@@ -43,31 -44,17 +45,31 @@@ changes to languages because of Bugzill
  * ChangeTags::tagDescription() will return false if the interface message
    for the tag is disabled.
  * Added PageHistoryPager::doBatchLookups hook.
 +* Added ParserCacheSaveComplete to ParserCache
  * supportsDirectEditing and supportsDirectApiEditing methods added to
 -ContentHandler, to provide a way for ApiEditPage and EditPage to check
 -if direct editing of content is allowed. These methods return false,
 -by default for the ContentHandler base class and true for TextContentHandler
 -and it's derivative classes (everything in core). For Content types that
 -do not support direct editing, an alternative mechanism should be provided
 -for editing, such as action overrides or specific api modules.
 +  ContentHandler, to provide a way for ApiEditPage and EditPage to check
 +  if direct editing of content is allowed. These methods return false,
 +  by default for the ContentHandler base class and true for TextContentHandler
 +  and it's derivative classes (everything in core). For Content types that
 +  do not support direct editing, an alternative mechanism should be provided
 +  for editing, such as action overrides or specific api modules.
  * mediaWiki.confirmCloseWindow now returns an object of functions, instead of
 -one function. The callback can't be called directly any more. The callback function
 -is replaced with confirmCloseWindow.release().
 +  one function. The callback can't be called directly any more. The callback
 +  function is replaced with confirmCloseWindow.release().
  * Removed maintenance script deleteImageMemcached.php.
 +* MWFunction::newObj() was removed (deprecated in 1.25).
 +  ObjectFactory::getObjectFromSpec() should be used instead.
 +* The parser will no longer randomize the string it uses to mark the place of
 +  items that were stripped during parsing. It will use a fixed string instead.
 +  This causes the parser to re-use the regular expressions it uses to search
 +  and replace markers rather than generate novel expressions on each parse.
 +  Re-using regular expressions will improve performance on HHVM and the
 +  forthcoming PHP 7. The interfaces changes accompanying this change are:
 +  - Parser::getRandomString() and Parser::uniqPrefix() have been deprecated.
 +  - The $uniq_prefix argument for Parser::extractTagsAndParams() and the
 +    $prefix argument for StripState::_construct() are deprecated and their
 +    value is ignored.
 +
  
  == Compatibility ==
  
@@@ -1086,10 -1086,7 +1086,7 @@@ class WikiPage implements Page, IDBAcce
         * @return bool
         */
        public function isParserCacheUsed( ParserOptions $parserOptions, $oldid ) {
-               global $wgEnableParserCache;
-               return $wgEnableParserCache
-                       && $parserOptions->getStubThreshold() == 0
+               return $parserOptions->getStubThreshold() == 0
                        && $this->exists()
                        && ( $oldid === null || $oldid === 0 || $oldid === $this->getLatest() )
                        && $this->getContentHandler()->isParserCacheSupported();
                $useParserCache = $this->isParserCacheUsed( $parserOptions, $oldid );
                wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
                if ( $parserOptions->getStubThreshold() ) {
 -                      wfIncrStats( 'pcache_miss_stub' );
 +                      wfIncrStats( 'pcache.miss.stub' );
                }
  
                if ( $useParserCache ) {
         *     revision: The revision object for the inserted revision, or null.
         *
         * @since 1.21
 +       * @throws MWException
         */
        public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
                User $user = null, $serialFormat = null
  
                        if ( $changed ) {
                                $dbw->begin( __METHOD__ );
 -                              try {
  
 -                                      $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
 -                                      $status->merge( $prepStatus );
 +                              $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
 +                              $status->merge( $prepStatus );
  
 -                                      if ( !$status->isOK() ) {
 -                                              $dbw->rollback( __METHOD__ );
 +                              if ( !$status->isOK() ) {
 +                                      $dbw->rollback( __METHOD__ );
  
 -                                              return $status;
 -                                      }
 -                                      $revisionId = $revision->insertOn( $dbw );
 +                                      return $status;
 +                              }
 +                              $revisionId = $revision->insertOn( $dbw );
  
 -                                      // Update page
 -                                      //
 -                                      // We check for conflicts by comparing $oldid with the current latest revision ID.
 -                                      $ok = $this->updateRevisionOn( $dbw, $revision, $oldid, $oldIsRedirect );
 +                              // Update page
 +                              //
 +                              // We check for conflicts by comparing $oldid with the current latest revision ID.
 +                              $ok = $this->updateRevisionOn( $dbw, $revision, $oldid, $oldIsRedirect );
  
 -                                      if ( !$ok ) {
 -                                              // Belated edit conflict! Run away!!
 -                                              $status->fatal( 'edit-conflict' );
 +                              if ( !$ok ) {
 +                                      // Belated edit conflict! Run away!!
 +                                      $status->fatal( 'edit-conflict' );
  
 -                                              $dbw->rollback( __METHOD__ );
 +                                      $dbw->rollback( __METHOD__ );
  
 -                                              return $status;
 -                                      }
 +                                      return $status;
 +                              }
  
 -                                      Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
 -                                      // Update recentchanges
 -                                      if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
 -                                              // Mark as patrolled if the user can do so
 -                                              $patrolled = $wgUseRCPatrol && !count(
 +                              Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
 +
 +                              // Update recentchanges
 +                              if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
 +                                      // Mark as patrolled if the user can do so
 +                                      $patrolled = $wgUseRCPatrol && !count(
                                                $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
 -                                              // Add RC row to the DB
 -                                              $rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
 -                                                      $oldid, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
 -                                                      $revisionId, $patrolled
 -                                              );
 -
 -                                              // Log auto-patrolled edits
 -                                              if ( $patrolled ) {
 -                                                      PatrolLog::record( $rc, true, $user );
 -                                              }
 -                                      }
 -                                      $user->incEditCount();
 -                              } catch ( Exception $e ) {
 -                                      $dbw->rollback( __METHOD__ );
 -                                      // Question: Would it perhaps be better if this method turned all
 -                                      // exceptions into $status's?
 -                                      throw $e;
 +                                      // Add RC row to the DB
 +                                      RecentChange::notifyEdit(
 +                                              $now, $this->mTitle, $isminor, $user, $summary,
 +                                              $oldid, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
 +                                              $revisionId, $patrolled
 +                                      );
                                }
 +
 +                              $user->incEditCount();
 +
                                $dbw->commit( __METHOD__ );
                        } else {
                                // Bug 32948: revision ID must be set to page {{REVISIONID}} and
                        $status->value['new'] = true;
  
                        $dbw->begin( __METHOD__ );
 -                      try {
  
 -                              $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
 -                              $status->merge( $prepStatus );
 +                      $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
 +                      $status->merge( $prepStatus );
  
 -                              if ( !$status->isOK() ) {
 -                                      $dbw->rollback( __METHOD__ );
 +                      if ( !$status->isOK() ) {
 +                              $dbw->rollback( __METHOD__ );
  
 -                                      return $status;
 -                              }
 +                              return $status;
 +                      }
  
 -                              $status->merge( $prepStatus );
 +                      $status->merge( $prepStatus );
  
 -                              // Add the page record; stake our claim on this title!
 -                              // This will return false if the article already exists
 -                              $newid = $this->insertOn( $dbw );
 +                      // Add the page record; stake our claim on this title!
 +                      // This will return false if the article already exists
 +                      $newid = $this->insertOn( $dbw );
  
 -                              if ( $newid === false ) {
 -                                      $dbw->rollback( __METHOD__ );
 -                                      $status->fatal( 'edit-already-exists' );
 +                      if ( $newid === false ) {
 +                              $dbw->rollback( __METHOD__ );
 +                              $status->fatal( 'edit-already-exists' );
  
 -                                      return $status;
 -                              }
 +                              return $status;
 +                      }
  
 -                              // Save the revision text...
 -                              $revision = new Revision( array(
 -                                      'page'       => $newid,
 -                                      'title'      => $this->getTitle(), // for determining the default content model
 -                                      'comment'    => $summary,
 -                                      'minor_edit' => $isminor,
 -                                      'text'       => $serialized,
 -                                      'len'        => $newsize,
 -                                      'user'       => $user->getId(),
 -                                      'user_text'  => $user->getName(),
 -                                      'timestamp'  => $now,
 -                                      'content_model' => $content->getModel(),
 -                                      'content_format' => $serialFormat,
 -                              ) );
 -                              $revisionId = $revision->insertOn( $dbw );
 +                      // Save the revision text...
 +                      $revision = new Revision( array(
 +                              'page'       => $newid,
 +                              'title'      => $this->getTitle(), // for determining the default content model
 +                              'comment'    => $summary,
 +                              'minor_edit' => $isminor,
 +                              'text'       => $serialized,
 +                              'len'        => $newsize,
 +                              'user'       => $user->getId(),
 +                              'user_text'  => $user->getName(),
 +                              'timestamp'  => $now,
 +                              'content_model' => $content->getModel(),
 +                              'content_format' => $serialFormat,
 +                      ) );
 +                      $revisionId = $revision->insertOn( $dbw );
  
 -                              // Bug 37225: use accessor to get the text as Revision may trim it
 -                              $content = $revision->getContent(); // sanity; get normalized version
 +                      // Bug 37225: use accessor to get the text as Revision may trim it
 +                      $content = $revision->getContent(); // sanity; get normalized version
  
 -                              if ( $content ) {
 -                                      $newsize = $content->getSize();
 -                              }
 +                      if ( $content ) {
 +                              $newsize = $content->getSize();
 +                      }
  
 -                              // Update the page record with revision data
 -                              $this->updateRevisionOn( $dbw, $revision, 0 );
 +                      // Update the page record with revision data
 +                      $this->updateRevisionOn( $dbw, $revision, 0 );
  
 -                              Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
 +                      Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
  
 -                              // Update recentchanges
 -                              if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
 -                                      // Mark as patrolled if the user can do so
 -                                      $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && !count(
 -                                              $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
 -                                      // Add RC row to the DB
 -                                      $rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
 -                                              '', $newsize, $revisionId, $patrolled );
 +                      // Update recentchanges
 +                      if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
 +                              // Mark as patrolled if the user can do so
 +                              $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && !count(
 +                                      $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
 +                              // Add RC row to the DB
 +                              RecentChange::notifyNew(
 +                                      $now, $this->mTitle, $isminor, $user, $summary, $bot,
 +                                      '', $newsize, $revisionId, $patrolled
 +                              );
 +                      }
  
 -                                      // Log auto-patrolled edits
 -                                      if ( $patrolled ) {
 -                                              PatrolLog::record( $rc, true, $user );
 -                                      }
 -                              }
 -                              $user->incEditCount();
 +                      $user->incEditCount();
  
 -                      } catch ( Exception $e ) {
 -                              $dbw->rollback( __METHOD__ );
 -                              throw $e;
 -                      }
                        $dbw->commit( __METHOD__ );
  
                        // Update links, etc.
                $status->value['revision'] = $revision;
  
                $hook_args = array( &$this, &$user, $content, $summary,
 -                                                      $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
 +                      $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
  
                ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
                Hooks::run( 'PageContentSaveComplete', $hook_args );
  
                // Promote user to any groups they meet the criteria for
 -              $dbw->onTransactionIdle( function () use ( $user ) {
 +              DeferredUpdates::addCallableUpdate( function () use ( $user ) {
                        $user->addAutopromoteOnceGroups( 'onEdit' );
                        $user->addAutopromoteOnceGroups( 'onView' ); // b/c
                } );
         *   - 'no-change': don't update the article count, ever
         */
        public function doEditUpdates( Revision $revision, User $user, array $options = array() ) {
-               global $wgEnableParserCache;
                $options += array(
                        'changed' => true,
                        'created' => false,
                }
  
                // Save it to the parser cache
-               if ( $wgEnableParserCache ) {
-                       $parserCache = ParserCache::singleton();
-                       $parserCache->save(
-                               $editInfo->output, $this, $editInfo->popts, $editInfo->timestamp, $editInfo->revid
-                       );
-               }
+               ParserCache::singleton()->save(
+                       $editInfo->output, $this, $editInfo->popts, $editInfo->timestamp, $editInfo->revid
+               );
  
                // Update the links tables and other secondary data
                if ( $content ) {
                        $recursive = $options['changed']; // bug 50785
                        $updates = $content->getSecondaryDataUpdates(
                                $this->getTitle(), null, $recursive, $editInfo->output );
 -                      DataUpdate::runUpdates( $updates );
 +                      foreach ( $updates as $update ) {
 +                              DeferredUpdates::addUpdate( $update );
 +                      }
                }
  
                Hooks::run( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
  
                if ( Hooks::run( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
 -                      // Update the cached list of active users
 -                      $jobs = array( RecentChangesUpdateJob::newCacheUpdateJob() );
                        // Flush old entries from the `recentchanges` table
                        if ( mt_rand( 0, 9 ) == 0 ) {
 -                              $jobs[] = RecentChangesUpdateJob::newPurgeJob();
 +                              JobQueueGroup::singleton()->lazyPush( RecentChangesUpdateJob::newPurgeJob() );
                        }
 -
 -                      JobQueueGroup::singleton()->lazyPush( $jobs );
                }
  
                if ( !$this->exists() ) {
                } elseif ( $options['changed'] ) { // bug 50785
                        self::onArticleEdit( $this->mTitle );
                }
 -
        }
  
        /**
                $dbw->begin( __METHOD__ );
  
                if ( $id == 0 ) {
 -                      $this->loadPageData( 'forupdate' );
 +                      // T98706: lock the page from various other updates but avoid using
 +                      // WikiPage::READ_LOCKING as that will carry over the FOR UPDATE to
 +                      // the revisions queries (which also JOIN on user). Only lock the page
 +                      // row and CAS check on page_latest to see if the trx snapshot matches.
 +                      $latest = $this->lock();
 +
 +                      $this->loadPageData( WikiPage::READ_LATEST );
                        $id = $this->getID();
 -                      if ( $id == 0 ) {
 +                      if ( $id == 0 || $this->getLatest() != $latest ) {
 +                              // Page not there or trx snapshot is stale
                                $dbw->rollback( __METHOD__ );
                                $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
                                return $status;
                return $status;
        }
  
 +      /**
 +       * Lock the page row for this title and return page_latest (or 0)
 +       *
 +       * @return integer
 +       */
 +      protected function lock() {
 +              return (int)wfGetDB( DB_MASTER )->selectField(
 +                      'page',
 +                      'page_latest',
 +                      array(
 +                              'page_namespace' => $this->getTitle()->getNamespace(),
 +                              'page_title' => $this->getTitle()->getDBkey()
 +                      ),
 +                      __METHOD__,
 +                      array( 'FOR UPDATE' )
 +              );
 +      }
 +
        /**
         * Do some database updates after deletion
         *