From 969561ae3c9051eb408626e9b6debb6c0f210b71 Mon Sep 17 00:00:00 2001 From: Daniel Friesen Date: Mon, 3 Jan 2011 20:17:20 +0000 Subject: [PATCH] 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. --- RELEASE-NOTES | 5 +++++ includes/DefaultSettings.php | 12 ++++++++++++ includes/parser/Parser.php | 32 ++++++++++++++++++++++++++----- includes/parser/ParserOptions.php | 9 ++++++++- includes/parser/ParserOutput.php | 25 +++++++++++++++++++++++- 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 2e32db2f50..1ee6a97a61 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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 diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 48f8aed14c..02cc4eb342 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -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 } /************************************************************************//** diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 4a8eadfc79..b723bea433 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -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 tag if ( $showEditLink && $sectionIndex !== false ) { - if ( $isTemplate ) { - # Put a T flag in the section identifier, to indicate to extractSections() - # that sections inside 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 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 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 = ''; diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php index 62a6c7c348..52d96d4d39 100644 --- a/includes/parser/ParserOptions.php +++ b/includes/parser/ParserOptions.php @@ -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'; diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index 6a60a03be3..59552ad108 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -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 ); } -- 2.20.1