Index: trunk/phase3/docs/memcached.txt |
— | — | @@ -153,17 +153,21 @@ |
154 | 154 | Parser Cache: |
155 | 155 | stored in: $parserMemc |
156 | 156 | controlled by: $wgEnableParserCache |
157 | | - key: $wgDBname:pcache:idhash:$pageid-$renderkey!$hash$edit |
| 157 | + key: $wgDBname:pcache:idhash:$pageid-$renderkey!$hash |
158 | 158 | $pageid: id of the page |
159 | 159 | $renderkey: 1 if action=render, 0 otherwise |
160 | | - $hash: hash of user options, see User::getPageRenderingHash() |
161 | | - $edit: '!edit=0' if the user can't edit the page, '' otherwise |
| 160 | + $hash: hash of user options applied to the page, see ParserOptions::optionsHash() |
162 | 161 | ex: wikidb:pcache:idhash:1-0!1!0!!en!2 |
163 | 162 | stores: ParserOutput object |
164 | | - modified by: Article::editUpdates() |
165 | | - expriy: $wgParserCacheExpireTime or one hour if it contains specific magic |
166 | | - words |
| 163 | + modified by: Article::editUpdates() or Article::getOutputFromWikitext() |
| 164 | + expiry: $wgParserCacheExpireTime or less if it contains short lived functions |
167 | 165 | |
| 166 | + key: $wgDBname:pcache:idoptions:$pageid |
| 167 | + stores: CacheTime object with an additional list of used options for the hash, |
| 168 | + serves as ParserCache pointer. |
| 169 | + modified by: ParserCache::save() |
| 170 | + expiry: The same as the ParserCache entry it points to. |
| 171 | + |
168 | 172 | Ping limiter: |
169 | 173 | controlled by: $wgRateLimits |
170 | 174 | key: $wgDBname:limiter:action:$action:ip:$ip, |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -996,6 +996,8 @@ |
997 | 997 | case 4: |
998 | 998 | # Run the parse, protected by a pool counter |
999 | 999 | wfDebug( __METHOD__ . ": doing uncached parse\n" ); |
| 1000 | + |
| 1001 | + $this->checkTouched(); |
1000 | 1002 | $key = $parserCache->getKey( $this, $parserOptions ); |
1001 | 1003 | $poolCounter = PoolCounter::factory( 'Article::view', $key ); |
1002 | 1004 | $dirtyCallback = $useParserCache ? array( $this, 'tryDirtyCache' ) : false; |
— | — | @@ -1493,10 +1495,9 @@ |
1494 | 1496 | * @return boolean |
1495 | 1497 | */ |
1496 | 1498 | public function tryDirtyCache() { |
1497 | | - |
1498 | 1499 | global $wgOut; |
1499 | 1500 | $parserCache = ParserCache::singleton(); |
1500 | | - $options = $this->getParserOptions(); |
| 1501 | + $options = clone $this->getParserOptions(); |
1501 | 1502 | |
1502 | 1503 | if ( $wgOut->isPrintable() ) { |
1503 | 1504 | $options->setIsPrintable( true ); |
— | — | @@ -3609,8 +3610,8 @@ |
3610 | 3611 | $edit->revid = $revid; |
3611 | 3612 | $edit->newText = $text; |
3612 | 3613 | $edit->pst = $this->preSaveTransform( $text ); |
3613 | | - $options = $this->getParserOptions(); |
3614 | | - $edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $options, true, true, $revid ); |
| 3614 | + $edit->popts = clone $this->getParserOptions(); |
| 3615 | + $edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $edit->popts, true, true, $revid ); |
3615 | 3616 | $edit->oldText = $this->getContent(); |
3616 | 3617 | |
3617 | 3618 | $this->mPreparedEdit = $edit; |
— | — | @@ -3649,9 +3650,8 @@ |
3650 | 3651 | |
3651 | 3652 | # Save it to the parser cache |
3652 | 3653 | if ( $wgEnableParserCache ) { |
3653 | | - $popts = $this->getParserOptions(); |
3654 | 3654 | $parserCache = ParserCache::singleton(); |
3655 | | - $parserCache->save( $editInfo->output, $this, $popts ); |
| 3655 | + $parserCache->save( $editInfo->output, $this, $editInfo->popts ); |
3656 | 3656 | } |
3657 | 3657 | |
3658 | 3658 | # Update the links tables |
— | — | @@ -4418,7 +4418,7 @@ |
4419 | 4419 | global $wgParser, $wgEnableParserCache, $wgUseFileCache; |
4420 | 4420 | |
4421 | 4421 | if ( !$parserOptions ) { |
4422 | | - $parserOptions = $this->getParserOptions(); |
| 4422 | + $parserOptions = clone $this->getParserOptions(); |
4423 | 4423 | } |
4424 | 4424 | |
4425 | 4425 | $time = - wfTime(); |
Index: trunk/phase3/includes/parser/Parser.php |
— | — | @@ -266,6 +266,7 @@ |
267 | 267 | $this->clearState(); |
268 | 268 | } |
269 | 269 | |
| 270 | + $options->resetUsage(); |
270 | 271 | $this->mOptions = $options; |
271 | 272 | $this->setTitle( $title ); # Page title has to be set for the pre-processor |
272 | 273 | |
— | — | @@ -454,6 +455,7 @@ |
455 | 456 | wfProfileIn( __METHOD__ ); |
456 | 457 | $this->clearState(); |
457 | 458 | $this->setOutputType( self::OT_PREPROCESS ); |
| 459 | + $options->resetUsage(); |
458 | 460 | $this->mOptions = $options; |
459 | 461 | $this->setTitle( $title ); |
460 | 462 | if ( $revid !== null ) { |
— | — | @@ -477,6 +479,7 @@ |
478 | 480 | # Parser (re)initialisation |
479 | 481 | $this->clearState(); |
480 | 482 | $this->setOutputType( self::OT_PLAIN ); |
| 483 | + $options->resetUsage(); |
481 | 484 | $this->mOptions = $options; |
482 | 485 | $this->setTitle( $title ); |
483 | 486 | |
— | — | @@ -4041,6 +4044,7 @@ |
4042 | 4045 | * @return String: the altered wiki markup |
4043 | 4046 | */ |
4044 | 4047 | public function preSaveTransform( $text, Title $title, $user, $options, $clearState = true ) { |
| 4048 | + $options->resetUsage(); |
4045 | 4049 | $this->mOptions = $options; |
4046 | 4050 | $this->setTitle( $title ); |
4047 | 4051 | $this->setOutputType( self::OT_WIKI ); |
— | — | @@ -4265,6 +4269,7 @@ |
4266 | 4270 | */ |
4267 | 4271 | public function startExternalParse( &$title, $options, $outputType, $clearState = true ) { |
4268 | 4272 | $this->setTitle( $title ); |
| 4273 | + $options->resetUsage(); |
4269 | 4274 | $this->mOptions = $options; |
4270 | 4275 | $this->setOutputType( $outputType ); |
4271 | 4276 | if ( $clearState ) { |
— | — | @@ -5140,6 +5145,7 @@ |
5141 | 5146 | $title = Title::newFromText( $title ); |
5142 | 5147 | } |
5143 | 5148 | $this->mTitle = $title; |
| 5149 | + $options->resetUsage(); |
5144 | 5150 | $this->mOptions = $options; |
5145 | 5151 | $this->setOutputType( $outputType ); |
5146 | 5152 | $text = $this->replaceVariables( $text ); |
Index: trunk/phase3/includes/parser/ParserCache.php |
— | — | @@ -31,45 +31,93 @@ |
32 | 32 | } |
33 | 33 | $this->mMemc = $memCached; |
34 | 34 | } |
35 | | - |
36 | | - function getKey( $article, $popts ) { |
| 35 | + |
| 36 | + protected function getParserOutputKey( $article, $hash ) { |
37 | 37 | global $wgRequest; |
38 | | - |
39 | | - if( $popts instanceof User ) // It used to be getKey( &$article, &$user ) |
40 | | - $popts = ParserOptions::newFromUser( $popts ); |
41 | | - |
42 | | - $user = $popts->mUser; |
43 | | - $printable = ( $popts->getIsPrintable() ) ? '!printable=1' : ''; |
44 | | - $hash = $user->getPageRenderingHash(); |
45 | 38 | |
46 | | - if( ! $popts->getEditSection() ) { |
47 | | - // section edit links have been suppressed |
48 | | - $edit = '!edit=0'; |
49 | | - } else { |
50 | | - $edit = ''; |
51 | | - } |
| 39 | + // idhash seem to mean 'page id' + 'rendering hash' (r3710) |
52 | 40 | $pageid = $article->getID(); |
53 | 41 | $renderkey = (int)($wgRequest->getVal('action') == 'render'); |
54 | | - $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}{$edit}{$printable}" ); |
| 42 | + |
| 43 | + $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" ); |
55 | 44 | return $key; |
56 | 45 | } |
57 | 46 | |
| 47 | + protected function getOptionsKey( $article ) { |
| 48 | + $pageid = $article->getID(); |
| 49 | + return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" ); |
| 50 | + } |
| 51 | + |
58 | 52 | function getETag( $article, $popts ) { |
59 | | - return 'W/"' . $this->getKey($article, $popts) . "--" . $article->mTouched. '"'; |
| 53 | + return 'W/"' . $this->getParserOutputKey( $article, |
| 54 | + $popts->optionsHash( ParserOptions::legacyOptions() ) ) . |
| 55 | + "--" . $article->mTouched . '"'; |
60 | 56 | } |
61 | 57 | |
62 | | - function getDirty( $article, $popts ) { |
63 | | - $key = $this->getKey( $article, $popts ); |
64 | | - wfDebug( "Trying parser cache $key\n" ); |
65 | | - $value = $this->mMemc->get( $key ); |
| 58 | + /** |
| 59 | + * Retrieve the ParserOutput from ParserCache, even if it's outdated. |
| 60 | + */ |
| 61 | + public function getDirty( $article, $popts ) { |
| 62 | + $value = $this->mMemc->get( $article, $popts, true ); |
66 | 63 | return is_object( $value ) ? $value : false; |
67 | 64 | } |
68 | 65 | |
69 | | - function get( $article, $popts ) { |
| 66 | + /** |
| 67 | + * Used to provide a unique id for the PoolCounter. |
| 68 | + * It would be preferable to have this code in get() |
| 69 | + * instead of having Article looking in our internals. |
| 70 | + * |
| 71 | + * Precondition: $article->checkTouched() has been called. |
| 72 | + */ |
| 73 | + public function getKey( $article, $popts, $useOutdated = true ) { |
70 | 74 | global $wgCacheEpoch; |
| 75 | + |
| 76 | + // Determine the options which affect this article |
| 77 | + $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) ); |
| 78 | + if ( $optionsKey !== false ) { |
| 79 | + if ( !$useOutdated && $optionsKey->expired( $article->mTouched ) ) { |
| 80 | + wfIncrStats( "pcache_miss_expired" ); |
| 81 | + $cacheTime = $optionsKey->getCacheTime(); |
| 82 | + wfDebug( "Parser options key expired, touched {$article->mTouched}, epoch $wgCacheEpoch, cached $cacheTime\n" ); |
| 83 | + return false; |
| 84 | + } |
| 85 | + |
| 86 | + $usedOptions = $optionsKey->mUsedOptions; |
| 87 | + wfDebug( "Parser cache options found.\n" ); |
| 88 | + } else { |
| 89 | + # TODO: Fail here $wgParserCacheExpireTime after deployment unless $useOutdated |
| 90 | + |
| 91 | + $usedOptions = ParserOptions::legacyOptions(); |
| 92 | + } |
| 93 | + |
| 94 | + return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions ) ); |
| 95 | + } |
| 96 | + |
| 97 | + /** |
| 98 | + * Retrieve the ParserOutput from ParserCache. |
| 99 | + * false if not found or outdated. |
| 100 | + */ |
| 101 | + public function get( $article, $popts, $useOutdated = false ) { |
| 102 | + global $wgCacheEpoch; |
71 | 103 | wfProfileIn( __METHOD__ ); |
72 | 104 | |
73 | | - $value = $this->getDirty( $article, $popts ); |
| 105 | + $canCache = $article->checkTouched(); |
| 106 | + if ( !$canCache ) { |
| 107 | + // It's a redirect now |
| 108 | + wfProfileOut( __METHOD__ ); |
| 109 | + return false; |
| 110 | + } |
| 111 | + |
| 112 | + // Having called checkTouched() ensures this will be loaded |
| 113 | + $touched = $article->mTouched; |
| 114 | + |
| 115 | + $parserOutputKey = $this->getKey( $article, $popts, $useOutdated ); |
| 116 | + if ( $parserOutputKey === false ) { |
| 117 | + wfProfileOut( __METHOD__ ); |
| 118 | + return false; |
| 119 | + } |
| 120 | + |
| 121 | + $value = $this->mMemc->get( $parserOutputKey ); |
74 | 122 | if ( !$value ) { |
75 | 123 | wfDebug( "Parser cache miss.\n" ); |
76 | 124 | wfIncrStats( "pcache_miss_absent" ); |
— | — | @@ -78,18 +126,10 @@ |
79 | 127 | } |
80 | 128 | |
81 | 129 | wfDebug( "Found.\n" ); |
82 | | - # Invalid if article has changed since the cache was made |
83 | | - $canCache = $article->checkTouched(); |
84 | | - $cacheTime = $value->getCacheTime(); |
85 | | - $touched = $article->mTouched; |
86 | | - if ( !$canCache || $value->expired( $touched ) ) { |
87 | | - if ( !$canCache ) { |
88 | | - wfIncrStats( "pcache_miss_invalid" ); |
89 | | - wfDebug( "Invalid cached redirect, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" ); |
90 | | - } else { |
91 | | - wfIncrStats( "pcache_miss_expired" ); |
92 | | - wfDebug( "Key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" ); |
93 | | - } |
| 130 | + |
| 131 | + if ( !$useOutdated && $value->expired( $touched ) ) { |
| 132 | + wfIncrStats( "pcache_miss_expired" ); |
| 133 | + wfDebug( "ParserOutput key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" ); |
94 | 134 | $value = false; |
95 | 135 | } else { |
96 | 136 | if ( isset( $value->mTimestamp ) ) { |
— | — | @@ -102,25 +142,37 @@ |
103 | 143 | return $value; |
104 | 144 | } |
105 | 145 | |
106 | | - function save( $parserOutput, $article, $popts ){ |
107 | | - $key = $this->getKey( $article, $popts ); |
| 146 | + |
| 147 | + public function save( $parserOutput, $article, $popts ) { |
108 | 148 | $expire = $parserOutput->getCacheExpiry(); |
109 | 149 | |
110 | | - if( $expire > 0 ) { |
| 150 | + if( $expire > 0 ) { |
111 | 151 | $now = wfTimestampNow(); |
| 152 | + |
| 153 | + $optionsKey = new CacheTime; |
| 154 | + $optionsKey->mUsedOptions = $popts->usedOptions(); |
| 155 | + $optionsKey->updateCacheExpiry( $expire ); |
| 156 | + |
| 157 | + $optionsKey->setCacheTime( $now ); |
112 | 158 | $parserOutput->setCacheTime( $now ); |
113 | 159 | |
| 160 | + $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() ); |
| 161 | + |
| 162 | + $parserOutputKey = $this->getParserOutputKey( $article, $popts->optionsHash( $optionsKey->mUsedOptions ) ); |
| 163 | + |
114 | 164 | // Save the timestamp so that we don't have to load the revision row on view |
115 | 165 | $parserOutput->mTimestamp = $article->getTimestamp(); |
116 | 166 | |
117 | | - $parserOutput->mText .= "\n<!-- Saved in parser cache with key $key and timestamp $now -->\n"; |
118 | | - wfDebug( "Saved in parser cache with key $key and timestamp $now\n" ); |
| 167 | + $parserOutput->mText .= "\n<!-- Saved in parser cache with key $parserOutputKey and timestamp $now -->\n"; |
| 168 | + wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $now\n" ); |
119 | 169 | |
120 | | - $this->mMemc->set( $key, $parserOutput, $expire ); |
| 170 | + // Save the parser output |
| 171 | + $this->mMemc->set( $parserOutputKey, $parserOutput, $expire ); |
121 | 172 | |
| 173 | + // ...and its pointer |
| 174 | + $this->mMemc->set( $this->getOptionsKey( $article ), $optionsKey, $expire ); |
122 | 175 | } else { |
123 | 176 | wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" ); |
124 | 177 | } |
125 | 178 | } |
126 | | - |
127 | 179 | } |
Index: trunk/phase3/includes/parser/ParserOptions.php |
— | — | @@ -38,13 +38,18 @@ |
39 | 39 | var $mIsSectionPreview; # Parsing the page for a "preview" operation on a single section |
40 | 40 | var $mIsPrintable; # Parsing the printable version of the page |
41 | 41 | |
| 42 | + |
| 43 | + protected $accessedOptions; |
| 44 | + |
42 | 45 | function getUseDynamicDates() { return $this->mUseDynamicDates; } |
43 | 46 | function getInterwikiMagic() { return $this->mInterwikiMagic; } |
44 | 47 | function getAllowExternalImages() { return $this->mAllowExternalImages; } |
45 | 48 | function getAllowExternalImagesFrom() { return $this->mAllowExternalImagesFrom; } |
46 | 49 | function getEnableImageWhitelist() { return $this->mEnableImageWhitelist; } |
47 | | - function getEditSection() { return $this->mEditSection; } |
48 | | - function getNumberHeadings() { return $this->mNumberHeadings; } |
| 50 | + function getEditSection() { $this->accessedOptions['editsection'] = true; |
| 51 | + return $this->mEditSection; } |
| 52 | + function getNumberHeadings() { $this->accessedOptions['numberheadings'] = true; |
| 53 | + return $this->mNumberHeadings; } |
49 | 54 | function getAllowSpecialInclusion() { return $this->mAllowSpecialInclusion; } |
50 | 55 | function getTidy() { return $this->mTidy; } |
51 | 56 | function getInterfaceMessage() { return $this->mInterfaceMessage; } |
— | — | @@ -58,12 +63,15 @@ |
59 | 64 | function getEnableLimitReport() { return $this->mEnableLimitReport; } |
60 | 65 | function getCleanSignatures() { return $this->mCleanSignatures; } |
61 | 66 | function getExternalLinkTarget() { return $this->mExternalLinkTarget; } |
62 | | - function getMath() { return $this->mMath; } |
63 | | - function getThumbSize() { return $this->mThumbSize; } |
| 67 | + function getMath() { $this->accessedOptions['math'] = true; |
| 68 | + return $this->mMath; } |
| 69 | + function getThumbSize() { $this->accessedOptions['thumbsize'] = true; |
| 70 | + return $this->mThumbSize; } |
64 | 71 | |
65 | 72 | function getIsPreview() { return $this->mIsPreview; } |
66 | 73 | function getIsSectionPreview() { return $this->mIsSectionPreview; } |
67 | | - function getIsPrintable() { return $this->mIsPrintable; } |
| 74 | + function getIsPrintable() { $this->accessedOptions['printable'] = true; |
| 75 | + return $this->mIsPrintable; } |
68 | 76 | |
69 | 77 | function getSkin() { |
70 | 78 | if ( !isset( $this->mSkin ) ) { |
— | — | @@ -73,6 +81,7 @@ |
74 | 82 | } |
75 | 83 | |
76 | 84 | function getDateFormat() { |
| 85 | + $this->accessedOptions['dateformat'] = true; |
77 | 86 | if ( !isset( $this->mDateFormat ) ) { |
78 | 87 | $this->mDateFormat = $this->mUser->getDatePreference(); |
79 | 88 | } |
— | — | @@ -90,6 +99,7 @@ |
91 | 100 | # Using this fragments the cache and is discouraged. Yes, {{int: }} uses this, |
92 | 101 | # producing inconsistent tables (Bug 14404). |
93 | 102 | function getUserLang() { |
| 103 | + $this->accessedOptions['userlang'] = true; |
94 | 104 | return $this->mUserLang; |
95 | 105 | } |
96 | 106 | |
— | — | @@ -191,4 +201,111 @@ |
192 | 202 | |
193 | 203 | wfProfileOut( __METHOD__ ); |
194 | 204 | } |
| 205 | + |
| 206 | + /** |
| 207 | + * Returns the options from this ParserOptions which have been used. |
| 208 | + */ |
| 209 | + public function usedOptions() { |
| 210 | + return array_keys( $this->accessedOptions ); |
| 211 | + } |
| 212 | + |
| 213 | + /** |
| 214 | + * Resets the memory of options usage. |
| 215 | + */ |
| 216 | + public function resetUsage() { |
| 217 | + $this->accessedOptions = array(); |
| 218 | + } |
| 219 | + |
| 220 | + /** |
| 221 | + * Returns the full array of options that would have been used by |
| 222 | + * in 1.16. |
| 223 | + * Used to get the old parser cache entries when available. |
| 224 | + */ |
| 225 | + public static function legacyOptions() { |
| 226 | + global $wgUseDynamicDates; |
| 227 | + $legacyOpts = array( 'math', 'stubthreshold', 'numberheadings', 'userlang', 'thumbsize', 'editsection', 'printable' ); |
| 228 | + if ( $wgUseDynamicDates ) { |
| 229 | + $legacyOpts[] = 'dateformat'; |
| 230 | + } |
| 231 | + return $legacyOpts; |
| 232 | + } |
| 233 | + |
| 234 | + /** |
| 235 | + * Generate a hash string with the values set on these ParserOptions |
| 236 | + * for the keys given in the array. |
| 237 | + * This will be used as part of the hash key for the parser cache, |
| 238 | + * so users sharign the options with vary for the same page share |
| 239 | + * the same cached data safely. |
| 240 | + * |
| 241 | + * Replaces User::getPageRenderingHash() |
| 242 | + * |
| 243 | + * Extensions which require it should install 'PageRenderingHash' hook, |
| 244 | + * which will give them a chance to modify this key based on their own |
| 245 | + * settings. |
| 246 | + * |
| 247 | + * @since 1.17 |
| 248 | + * @return \string Page rendering hash |
| 249 | + */ |
| 250 | + public function optionsHash( $forOptions ) { |
| 251 | + global $wgContLang, $wgRenderHashAppend; |
| 252 | + |
| 253 | + $confstr = ''; |
| 254 | + |
| 255 | + if ( in_array( 'math', $forOptions ) ) |
| 256 | + $confstr .= $this->mMath; |
| 257 | + else |
| 258 | + $confstr .= '*'; |
| 259 | + |
| 260 | + |
| 261 | + // Space assigned for the stubthreshold but unused |
| 262 | + // since it disables the parser cache, its value will always |
| 263 | + // be 0 when this function is called by parsercache. |
| 264 | + // The conditional is here to avoid a confusing 0 |
| 265 | + if ( in_array( 'stubthreshold', $forOptions ) ) |
| 266 | + $confstr .= '!0' ; |
| 267 | + else |
| 268 | + $confstr .= '!*' ; |
| 269 | + |
| 270 | + if ( in_array( 'dateformat', $forOptions ) ) |
| 271 | + $confstr .= '!' . $this->mDateFormat; |
| 272 | + |
| 273 | + if ( in_array( 'numberheadings', $forOptions ) ) |
| 274 | + $confstr .= '!' . ( $this->mNumberHeadings ? '1' : '' ); |
| 275 | + else |
| 276 | + $confstr .= '!*'; |
| 277 | + |
| 278 | + if ( in_array( 'userlang', $forOptions ) ) |
| 279 | + $confstr .= '!' . $this->mUserLang; |
| 280 | + else |
| 281 | + $confstr .= '!*'; |
| 282 | + |
| 283 | + if ( in_array( 'thumbsize', $forOptions ) ) |
| 284 | + $confstr .= '!' . $this->mThumbSize; |
| 285 | + else |
| 286 | + $confstr .= '!*'; |
| 287 | + |
| 288 | + // add in language specific options, if any |
| 289 | + // FIXME: This is just a way of retrieving the url/user preferred variant |
| 290 | + $confstr .= $wgContLang->getExtraHashOptions(); |
| 291 | + |
| 292 | + // Since the skin could be overloading link(), it should be |
| 293 | + // included here but in practice, none of our skins do that. |
| 294 | + // $confstr .= "!" . $this->mSkin->getSkinName(); |
| 295 | + |
| 296 | + $confstr .= $wgRenderHashAppend; |
| 297 | + |
| 298 | + if ( !$this->mEditSection && in_array( 'editsection', $forOptions ) ) |
| 299 | + $confstr .= '!edit=0'; |
| 300 | + if ( $this->mIsPrintable && in_array( 'printable', $forOptions ) ) |
| 301 | + $confstr .= '!printable=1'; |
| 302 | + |
| 303 | + // Give a chance for extensions to modify the hash, if they have |
| 304 | + // extra options or other effects on the parser cache. |
| 305 | + wfRunHooks( 'PageRenderingHash', array( &$confstr ) ); |
| 306 | + |
| 307 | + // Make it a valid memcached key fragment |
| 308 | + $confstr = str_replace( ' ', '_', $confstr ); |
| 309 | + |
| 310 | + return $confstr; |
| 311 | + } |
195 | 312 | } |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -144,6 +144,8 @@ |
145 | 145 | result in a URL ending in "#Hello?" rather than "#Hello.3F". |
146 | 146 | * (bug 8140) Add dedicated CSS classes to Special:Newpages elements |
147 | 147 | * (bug 11005) Add CSS class to empty pages in Special:Newpages |
| 148 | +* The parser cache is now shared amongst users whose different settings aren't |
| 149 | + used in the page. |
148 | 150 | |
149 | 151 | === Bug fixes in 1.17 === |
150 | 152 | * (bug 17560) Half-broken deletion moved image files to deletion archive |
— | — | @@ -287,6 +289,8 @@ |
288 | 290 | * (bug 15470) First letters of filenames are always capitalized by upload JS. |
289 | 291 | * (bug 21215) NoLocalSettings.php doesn't tolerate rewrite rules |
290 | 292 | * (bug 21052) Fix link color for stubs in NewPages |
| 293 | +* (bug 24714) Usage of {{#dateformat: }} in wikis without $wgUseDynamicDates no |
| 294 | + longer pollutes the parser cache. |
291 | 295 | |
292 | 296 | === API changes in 1.17 === |
293 | 297 | * (bug 22738) Allow filtering by action type on query=logevent. |