Try to output editsection markers instead of rendered editsection links and defer the rendering to a point where the markup does not need to be stored in the cache.

Doing this allows skins to override doEditSectionLinks without poluting the parser cache or fragmenting the parser cache more.
As a side effect it eliminates the primary cause of user language based parser cache fragmentation.
Because this makes most old parser cache entries invalid $wgUseEditSectionTokens is provided so that large installations like Wikipedia can keep their old parser cache entries.
This commit is contained in:
Daniel Friesen 2011-01-03 20:17:20 +00:00
parent 49aa485313
commit 969561ae3c
5 changed files with 76 additions and 7 deletions

View file

@ -26,6 +26,11 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
* Skin names are no longer created based on a ucfirst version of the key in $wgValidSkinNames but now
the value. This means for $wgValidSkinNames["monobook"] = "MonoBook"; the skin
loader will no longer try loading SkinMonobook and will instead load SkinMonoBook.
* The parser now attempts to output markers for editsection tokens and defer the rendering
of them so skin and language specific markup does not need to be saved inside the parser cache
note that this changes the cache key making all old entries in the parser cache invalid you
can set $wgUseEditSectionTokens to false to disable this and keep your old parser cache entries.
Note that this feature should reduce parser cache fragmentation when enabled.
=== New features in 1.18 ===
* Added a special page, disabled by default, that allows users with the

View file

@ -2825,6 +2825,18 @@ $wgEnableScaryTranscluding = false;
*/
$wgTranscludeCacheExpiry = 3600;
/**
* Output edit section links as tokens in the parser output for articles instead
* of directly as markup.
* This feature changes the default parser cache key so it's presented with a
* configuration option so that large installations with a large number of already
* existing parser cache keys can retain them.
* The purpose of this feature is to allow skins to customize the editsection
* links, however it has the side effect of also removing the most common use of
* the getUserLang parser option which causes cache fragmentation by user lang.
*/
$wgUseEditSectionTokens = true;
/** @} */ # end of parser settings }
/************************************************************************//**

View file

@ -3696,6 +3696,12 @@ class Parser {
} else {
$showEditLink = $this->mOptions->getEditSection();
}
if ( $showEditLink ) {
$editLinkAsToken = $this->mOptions->getEditSectionTokens();
if ( $editLinkAsToken ) {
$this->mOutput->setEditSectionTokens( "{$this->mUniqPrefix}-editsection-", self::MARKER_SUFFIX );
}
}
# Get all headlines for numbering them and adding funky stuff like [edit]
# links - this is for later, but we need the number of headlines right now
@ -3949,12 +3955,28 @@ class Parser {
# give headline the correct <h#> tag
if ( $showEditLink && $sectionIndex !== false ) {
if ( $isTemplate ) {
# Put a T flag in the section identifier, to indicate to extractSections()
# that sections inside <includeonly> should be counted.
$editlink = $sk->doEditSectionLink( Title::newFromText( $titleText ), "T-$sectionIndex", null, $this->mOptions->getUserLang() );
if ( $editLinkAsToken ) {
// Output edit section links as markers with styles that can be customized by skins
if ( $isTemplate ) {
# Put a T flag in the section identifier, to indicate to extractSections()
# that sections inside <includeonly> should be counted.
$editlinkArgs = array( $titleText, "T-$sectionIndex", null );
} else {
$editlinkArgs = array( $this->mTitle->getPrefixedText(), $sectionIndex, $headlineHint );
}
// We use nearly the same structure as uniqPrefix and the marker stuffix (besides there being nothing random)
// However the this is output into the parser output itself not replaced early, so we hardcode this in case
// the constants change in a different version of MediaWiki, which would break this code.
$editlink = "{$this->mUniqPrefix}-editsection-" . serialize($editlinkArgs) . self::MARKER_SUFFIX;
} else {
$editlink = $sk->doEditSectionLink( $this->mTitle, $sectionIndex, $headlineHint, $this->mOptions->getUserLang() );
// Output edit section links directly as markup like we used to
if ( $isTemplate ) {
# Put a T flag in the section identifier, to indicate to extractSections()
# that sections inside <includeonly> should be counted.
$editlink = $sk->doEditSectionLink( Title::newFromText( $titleText ), "T-$sectionIndex", null, $this->mOptions->getUserLang() );
} else {
$editlink = $sk->doEditSectionLink( $this->mTitle, $sectionIndex, $headlineHint, $this->mOptions->getUserLang() );
}
}
} else {
$editlink = '';

View file

@ -21,6 +21,7 @@ class ParserOptions {
var $mSkin = null; # Reference to the preferred skin
var $mDateFormat = null; # Date format index
var $mEditSection = true; # Create "edit section" links
var $mEditSectionTokens = false; # Output replaceable tokens for editsections instead of markup
var $mAllowSpecialInclusion; # Allow inclusion of special pages
var $mTidy = false; # Ask for tidy cleanup
var $mInterfaceMessage = false; # Which lang to call for PLURAL and GRAMMAR
@ -58,6 +59,8 @@ class ParserOptions {
function getEnableImageWhitelist() { return $this->mEnableImageWhitelist; }
function getEditSection() { $this->optionUsed('editsection');
return $this->mEditSection; }
function getEditSectionTokens() { $this->optionUsed('editsectiontokens');
return $this->mEditSectionTokens; }
function getNumberHeadings() { $this->optionUsed('numberheadings');
return $this->mNumberHeadings; }
function getAllowSpecialInclusion() { return $this->mAllowSpecialInclusion; }
@ -123,6 +126,7 @@ class ParserOptions {
function setEnableImageWhitelist( $x ) { return wfSetVar( $this->mEnableImageWhitelist, $x ); }
function setDateFormat( $x ) { return wfSetVar( $this->mDateFormat, $x ); }
function setEditSection( $x ) { return wfSetVar( $this->mEditSection, $x ); }
function setEditSectionTokens( $x ) { return wfSetVar( $this->mEditSectionTokens, $x ); }
function setNumberHeadings( $x ) { return wfSetVar( $this->mNumberHeadings, $x ); }
function setAllowSpecialInclusion( $x ) { return wfSetVar( $this->mAllowSpecialInclusion, $x ); }
function setTidy( $x ) { return wfSetVar( $this->mTidy, $x); }
@ -171,7 +175,7 @@ class ParserOptions {
function initialiseFromUser( $userInput ) {
global $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
global $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion, $wgMaxArticleSize;
global $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth, $wgCleanSignatures;
global $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth, $wgCleanSignatures, $wgUseEditSectionTokens;
global $wgExternalLinkTarget, $wgLang;
wfProfileIn( __METHOD__ );
@ -201,6 +205,7 @@ class ParserOptions {
$this->mMaxTemplateDepth = $wgMaxTemplateDepth;
$this->mCleanSignatures = $wgCleanSignatures;
$this->mExternalLinkTarget = $wgExternalLinkTarget;
$this->mEditSectionTokens = $wgUseEditSectionTokens;
$this->mNumberHeadings = $user->getOption( 'numberheadings' );
$this->mMath = $user->getOption( 'math' );
@ -307,6 +312,8 @@ class ParserOptions {
if ( !$this->mEditSection && in_array( 'editsection', $forOptions ) )
$confstr .= '!edit=0';
if ( $this->mEditSectionTokens && in_array( 'editsectiontokens', $forOptions ) )
$confstr .= '!estok=1';
if ( $this->mIsPrintable && in_array( 'printable', $forOptions ) )
$confstr .= '!printable=1';

View file

@ -119,6 +119,7 @@ class ParserOutput extends CacheTime {
$mOutputHooks = array(), # Hook tags as per $wgParserOutputHooks
$mWarnings = array(), # Warning text to be returned to the user. Wikitext formatted, in the key only
$mSections = array(), # Table of contents
$mEditSectionTokens = false, # prefix/suffix markers if edit sections were output as tokens
$mProperties = array(), # Name/value pairs to be cached in the DB
$mTOCHTML = ''; # HTML of the TOC
private $mIndexPolicy = ''; # 'index' or 'noindex'? Any other value will result in no change.
@ -134,13 +135,34 @@ class ParserOutput extends CacheTime {
$this->mTitleText = $titletext;
}
function getText() { return $this->mText; }
function getText() {
if ( $this->mEditSectionTokens ) {
$editSectionTokens = $this->mEditSectionTokens;
return preg_replace_callback( "#{$editSectionTokens[0]}(.*?){$editSectionTokens[1]}#", array( &$this, 'replaceEditSectionLinksCallback' ), $this->mText );
}
return $this->mText;
}
/**
* callback used by getText to replace editsection tokens
* @private
*/
function replaceEditSectionLinksCallback( $m ) {
global $wgUser, $wgLang;
$args = unserialize($m[1]);
$args[0] = Title::newFromText( $args[0] );
$args[] = $wgLang->getCode();
$skin = $wgUser->getSkin();
return call_user_func_array( array( $skin, 'doEditSectionLink' ), $args );
}
function &getLanguageLinks() { return $this->mLanguageLinks; }
function getInterwikiLinks() { return $this->mInterwikiLinks; }
function getCategoryLinks() { return array_keys( $this->mCategories ); }
function &getCategories() { return $this->mCategories; }
function getTitleText() { return $this->mTitleText; }
function getSections() { return $this->mSections; }
function getEditSectionTokens() { return $this->mEditSectionTokens; }
function &getLinks() { return $this->mLinks; }
function &getTemplates() { return $this->mTemplates; }
function &getImages() { return $this->mImages; }
@ -159,6 +181,7 @@ class ParserOutput extends CacheTime {
function setTitleText( $t ) { return wfSetVar( $this->mTitleText, $t ); }
function setSections( $toc ) { return wfSetVar( $this->mSections, $toc ); }
function setEditSectionTokens( $p, $s ) { return wfSetVar( $this->mEditSectionTokens, array( $p, $s ) ); }
function setIndexPolicy( $policy ) { return wfSetVar( $this->mIndexPolicy, $policy ); }
function setTOCHTML( $tochtml ) { return wfSetVar( $this->mTOCHTML, $tochtml ); }