r39464 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r39463‎ | r39464 | r39465 >
Date:09:33, 16 August 2008
Author:siebrand
Status:old
Tags:
Comment:
Revert r39414. Breaks processing links like [[:wikipedia:nl:User:Siebrand|Dutch language Wikipedia]]. It will add a comment like "<!--IWLINK 0-->" in the HTML output. Happens even if there is one such link on a page.
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/LinkCache.php (modified) (history)
  • /trunk/phase3/includes/MessageCache.php (modified) (history)
  • /trunk/phase3/includes/StringUtils.php (modified) (history)
  • /trunk/phase3/includes/Title.php (modified) (history)
  • /trunk/phase3/includes/parser/LinkHolderArray.php (deleted) (history)
  • /trunk/phase3/includes/parser/Parser.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/LinkCache.php
@@ -9,6 +9,7 @@
1010 // becomes incompatible with the new version.
1111 /* private */ var $mClassVer = 4;
1212
 13+ /* private */ var $mPageLinks;
1314 /* private */ var $mGoodLinks, $mBadLinks;
1415 /* private */ var $mForUpdate;
1516
@@ -25,6 +26,7 @@
2627
2728 function __construct() {
2829 $this->mForUpdate = false;
 30+ $this->mPageLinks = array();
2931 $this->mGoodLinks = array();
3032 $this->mGoodLinkFields = array();
3133 $this->mBadLinks = array();
@@ -76,12 +78,14 @@
7779 $dbkey = $title->getPrefixedDbKey();
7880 $this->mGoodLinks[$dbkey] = $id;
7981 $this->mGoodLinkFields[$dbkey] = array( 'length' => $len, 'redirect' => $redir );
 82+ $this->mPageLinks[$dbkey] = $title;
8083 }
8184
8285 public function addBadLinkObj( $title ) {
8386 $dbkey = $title->getPrefixedDbKey();
8487 if ( ! $this->isBadLink( $dbkey ) ) {
8588 $this->mBadLinks[$dbkey] = 1;
 89+ $this->mPageLinks[$dbkey] = $title;
8690 }
8791 }
8892
@@ -92,6 +96,7 @@
9397 /* obsolete, for old $wgLinkCacheMemcached stuff */
9498 public function clearLink( $title ) {}
9599
 100+ public function getPageLinks() { return $this->mPageLinks; }
96101 public function getGoodLinks() { return $this->mGoodLinks; }
97102 public function getBadLinks() { return array_keys( $this->mBadLinks ); }
98103
@@ -176,6 +181,7 @@
177182 * Clears cache
178183 */
179184 public function clear() {
 185+ $this->mPageLinks = array();
180186 $this->mGoodLinks = array();
181187 $this->mGoodLinkFields = array();
182188 $this->mBadLinks = array();
Index: trunk/phase3/includes/parser/LinkHolderArray.php
@@ -1,406 +0,0 @@
2 -<?php
3 -
4 -class LinkHolderArray {
5 - var $batchSize = 1000;
6 -
7 - var $internals = array(), $interwikis = array();
8 - var $size = 0;
9 - var $parent;
10 -
11 - function __construct( $parent ) {
12 - $this->parent = $parent;
13 - }
14 -
15 - /**
16 - * Merge another LinkHolderArray into this one
17 - */
18 - function merge( $other ) {
19 - foreach ( $other->internals as $ns => $entries ) {
20 - $this->size += count( $entries );
21 - if ( !isset( $this->internals[$ns] ) ) {
22 - $this->internals[$ns] = $entries;
23 - } else {
24 - $this->internals[$ns] += $entries;
25 - }
26 - }
27 - $this->interwikis += $other->interwikis;
28 - }
29 -
30 - /**
31 - * Returns true if the memory requirements of this object are getting large
32 - */
33 - function isBig() {
34 - return $this->size > $this->batchSize;
35 - }
36 -
37 - /**
38 - * Clear all stored link holders.
39 - * Make sure you don't have any text left using these link holders, before you call this
40 - */
41 - function clear() {
42 - $this->internals = array();
43 - $this->interwikis = array();
44 - $this->size = 0;
45 - }
46 -
47 - /**
48 - * Make a link placeholder. The text returned can be later resolved to a real link with
49 - * replaceLinkHolders(). This is done for two reasons: firstly to avoid further
50 - * parsing of interwiki links, and secondly to allow all existence checks and
51 - * article length checks (for stub links) to be bundled into a single query.
52 - *
53 - */
54 - function makeHolder( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
55 - wfProfileIn( __METHOD__ );
56 - if ( ! is_object($nt) ) {
57 - # Fail gracefully
58 - $retVal = "<!-- ERROR -->{$prefix}{$text}{$trail}";
59 - } else {
60 - # Separate the link trail from the rest of the link
61 - list( $inside, $trail ) = Linker::splitTrail( $trail );
62 -
63 - $entry = array(
64 - 'title' => $nt,
65 - 'text' => $prefix.$text.$inside,
66 - 'pdbk' => $nt->getPrefixedDBkey(),
67 - );
68 - if ( $query !== '' ) {
69 - $entry['query'] = $query;
70 - }
71 -
72 - if ( $nt->isExternal() ) {
73 - // Use a globally unique ID to keep the objects mergable
74 - $key = $this->parent->nextLinkID();
75 - $this->interwikis['titles'][$key] = $entry;
76 - $retVal = "<!--IWLINK $key-->{$trail}";
77 - } else {
78 - $key = $this->parent->nextLinkID();
79 - $ns = $nt->getNamespace();
80 - $this->internals[$ns][$key] = $entry;
81 - $retVal = "<!--LINK $ns:$key-->{$trail}";
82 - }
83 - $this->size++;
84 - }
85 - wfProfileOut( __METHOD__ );
86 - return $retVal;
87 - }
88 -
89 - /**
90 - * Replace <!--LINK--> link placeholders with actual links, in the buffer
91 - * Placeholders created in Skin::makeLinkObj()
92 - * Returns an array of link CSS classes, indexed by PDBK.
93 - */
94 - function replace( &$text ) {
95 - wfProfileIn( __METHOD__ );
96 -
97 - $colours = $this->replaceInternal( $text );
98 - $this->replaceInterwiki( $text );
99 -
100 - wfProfileOut( __METHOD__ );
101 - return $colours;
102 - }
103 -
104 - /**
105 - * Replace internal links
106 - */
107 - protected function replaceInternal( &$text ) {
108 - if ( !$this->internals ) {
109 - return;
110 - }
111 -
112 - wfProfileIn( __METHOD__ );
113 - global $wgUser, $wgContLang;
114 -
115 - $pdbks = array();
116 - $colours = array();
117 - $linkcolour_ids = array();
118 - $sk = $this->parent->getOptions()->getSkin();
119 - $linkCache = LinkCache::singleton();
120 - $output = $this->parent->getOutput();
121 -
122 - wfProfileIn( __METHOD__.'-check' );
123 - $dbr = wfGetDB( DB_SLAVE );
124 - $page = $dbr->tableName( 'page' );
125 - $threshold = $wgUser->getOption('stubthreshold');
126 -
127 - # Sort by namespace
128 - ksort( $this->internals );
129 -
130 - # Generate query
131 - $query = false;
132 - $current = null;
133 - foreach ( $this->internals as $ns => $entries ) {
134 - foreach ( $entries as $index => $entry ) {
135 - $key = "$ns:$index";
136 - $title = $entry['title'];
137 - $pdbk = $entry['pdbk'];
138 -
139 - # Skip invalid entries.
140 - # Result will be ugly, but prevents crash.
141 - if ( is_null( $title ) ) {
142 - continue;
143 - }
144 -
145 - # Check if it's a static known link, e.g. interwiki
146 - if ( $title->isAlwaysKnown() ) {
147 - $colours[$pdbk] = '';
148 - } elseif ( ( $id = $linkCache->getGoodLinkID( $pdbk ) ) != 0 ) {
149 - $colours[$pdbk] = '';
150 - $output->addLink( $title, $id );
151 - } elseif ( $linkCache->isBadLink( $pdbk ) ) {
152 - $colours[$pdbk] = 'new';
153 - } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) {
154 - $colours[$pdbk] = 'new';
155 - } else {
156 - # Not in the link cache, add it to the query
157 - if ( !isset( $current ) ) {
158 - $current = $ns;
159 - $query = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len";
160 - $query .= " FROM $page WHERE (page_namespace=$ns AND page_title IN(";
161 - } elseif ( $current != $ns ) {
162 - $current = $ns;
163 - $query .= ")) OR (page_namespace=$ns AND page_title IN(";
164 - } else {
165 - $query .= ', ';
166 - }
167 -
168 - $query .= $dbr->addQuotes( $title->getDBkey() );
169 - }
170 - }
171 - }
172 - if ( $query ) {
173 - $query .= '))';
174 -
175 - $res = $dbr->query( $query, __METHOD__ );
176 -
177 - # Fetch data and form into an associative array
178 - # non-existent = broken
179 - while ( $s = $dbr->fetchObject($res) ) {
180 - $title = Title::makeTitle( $s->page_namespace, $s->page_title );
181 - $pdbk = $title->getPrefixedDBkey();
182 - $linkCache->addGoodLinkObj( $s->page_id, $title, $s->page_len, $s->page_is_redirect );
183 - $output->addLink( $title, $s->page_id );
184 - $colours[$pdbk] = $sk->getLinkColour( $title, $threshold );
185 - //add id to the extension todolist
186 - $linkcolour_ids[$s->page_id] = $pdbk;
187 - }
188 - unset( $res );
189 - //pass an array of page_ids to an extension
190 - wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) );
191 - }
192 - wfProfileOut( __METHOD__.'-check' );
193 -
194 - # Do a second query for different language variants of links and categories
195 - if($wgContLang->hasVariants()){
196 - $linkBatch = new LinkBatch();
197 - $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders)
198 - $categoryMap = array(); // maps $category_variant => $category (dbkeys)
199 - $varCategories = array(); // category replacements oldDBkey => newDBkey
200 -
201 - $categories = $output->getCategoryLinks();
202 -
203 - // Add variants of links to link batch
204 - foreach ( $this->internals as $ns => $entries ) {
205 - foreach ( $entries as $index => $entry ) {
206 - $key = "$ns:$index";
207 - $pdbk = $entry['pdbk'];
208 - $title = $entry['title'];
209 - $titleText = $title->getText();
210 -
211 - // generate all variants of the link title text
212 - $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText);
213 -
214 - // if link was not found (in first query), add all variants to query
215 - if ( !isset($colours[$pdbk]) ){
216 - foreach($allTextVariants as $textVariant){
217 - if($textVariant != $titleText){
218 - $variantTitle = Title::makeTitle( $ns, $textVariant );
219 - if(is_null($variantTitle)) continue;
220 - $linkBatch->addObj( $variantTitle );
221 - $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
222 - }
223 - }
224 - }
225 - }
226 - }
227 -
228 - // process categories, check if a category exists in some variant
229 - foreach( $categories as $category ){
230 - $variants = $wgContLang->convertLinkToAllVariants($category);
231 - foreach($variants as $variant){
232 - if($variant != $category){
233 - $variantTitle = Title::newFromDBkey( Title::makeName(NS_CATEGORY,$variant) );
234 - if(is_null($variantTitle)) continue;
235 - $linkBatch->addObj( $variantTitle );
236 - $categoryMap[$variant] = $category;
237 - }
238 - }
239 - }
240 -
241 -
242 - if(!$linkBatch->isEmpty()){
243 - // construct query
244 - $titleClause = $linkBatch->constructSet('page', $dbr);
245 -
246 - $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len";
247 -
248 - $variantQuery .= " FROM $page WHERE $titleClause";
249 -
250 - $varRes = $dbr->query( $variantQuery, __METHOD__ );
251 -
252 - // for each found variants, figure out link holders and replace
253 - while ( $s = $dbr->fetchObject($varRes) ) {
254 -
255 - $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title );
256 - $varPdbk = $variantTitle->getPrefixedDBkey();
257 - $vardbk = $variantTitle->getDBkey();
258 -
259 - $holderKeys = array();
260 - if(isset($variantMap[$varPdbk])){
261 - $holderKeys = $variantMap[$varPdbk];
262 - $linkCache->addGoodLinkObj( $s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect );
263 - $output->addLink( $variantTitle, $s->page_id );
264 - }
265 -
266 - // loop over link holders
267 - foreach($holderKeys as $key){
268 - list( $ns, $index ) = explode( ':', $key, 2 );
269 - $entry =& $this->internals[$ns][$index];
270 - $pdbk = $entry['pdbk'];
271 -
272 - if(!isset($colours[$pdbk])){
273 - // found link in some of the variants, replace the link holder data
274 - $entry['title'] = $variantTitle;
275 - $entry['pdbk'] = $varPdbk;
276 -
277 - // set pdbk and colour
278 - $colours[$varPdbk] = $sk->getLinkColour( $variantTitle, $threshold );
279 - $linkcolour_ids[$s->page_id] = $pdbk;
280 - }
281 - wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) );
282 - }
283 -
284 - // check if the object is a variant of a category
285 - if(isset($categoryMap[$vardbk])){
286 - $oldkey = $categoryMap[$vardbk];
287 - if($oldkey != $vardbk)
288 - $varCategories[$oldkey]=$vardbk;
289 - }
290 - }
291 -
292 - // rebuild the categories in original order (if there are replacements)
293 - if(count($varCategories)>0){
294 - $newCats = array();
295 - $originalCats = $output->getCategories();
296 - foreach($originalCats as $cat => $sortkey){
297 - // make the replacement
298 - if( array_key_exists($cat,$varCategories) )
299 - $newCats[$varCategories[$cat]] = $sortkey;
300 - else $newCats[$cat] = $sortkey;
301 - }
302 - $this->mOutput->parent->setCategoryLinks($newCats);
303 - }
304 - }
305 - }
306 -
307 - # Construct search and replace arrays
308 - wfProfileIn( __METHOD__.'-construct' );
309 - $replacePairs = array();
310 - foreach ( $this->internals as $ns => $entries ) {
311 - foreach ( $entries as $index => $entry ) {
312 - $pdbk = $entry['pdbk'];
313 - $title = $entry['title'];
314 - $query = isset( $entry['query'] ) ? $entry['query'] : '';
315 - $key = "$ns:$index";
316 - $searchkey = "<!--LINK $key-->";
317 - if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] == 'new' ) {
318 - $linkCache->addBadLinkObj( $title );
319 - $colours[$pdbk] = 'new';
320 - $output->addLink( $title, 0 );
321 - $replacePairs[$searchkey] = $sk->makeBrokenLinkObj( $title,
322 - $entry['text'],
323 - $query );
324 - } else {
325 - $replacePairs[$searchkey] = $sk->makeColouredLinkObj( $title, $colours[$pdbk],
326 - $entry['text'],
327 - $query );
328 - }
329 - }
330 - }
331 - $replacer = new HashtableReplacer( $replacePairs, 1 );
332 - wfProfileOut( __METHOD__.'-construct' );
333 -
334 - # Do the thing
335 - wfProfileIn( __METHOD__.'-replace' );
336 - $text = preg_replace_callback(
337 - '/(<!--LINK .*?-->)/',
338 - $replacer->cb(),
339 - $text);
340 -
341 - wfProfileOut( __METHOD__.'-replace' );
342 - wfProfileOut( __METHOD__ );
343 - }
344 -
345 - /**
346 - * Replace interwiki links
347 - */
348 - protected function replaceInterwiki( &$text ) {
349 - if ( empty( $this->mInterwikiLinkHolders['texts'] ) ) {
350 - return;
351 - }
352 -
353 - wfProfileIn( __METHOD__ );
354 - # Make interwiki link HTML
355 - $replacePairs = array();
356 - foreach( $this->mInterwikiLinkHolders['texts'] as $key => $link ) {
357 - $title = $this->mInterwikiLinkHolders['titles'][$key];
358 - $replacePairs[$key] = $sk->link( $title, $link );
359 - }
360 - $replacer = new HashtableReplacer( $replacePairs, 1 );
361 -
362 - $text = preg_replace_callback(
363 - '/<!--IWLINK (.*?)-->/',
364 - $replacer->cb(),
365 - $text );
366 - wfProfileOut( __METHOD__ );
367 - }
368 -
369 - /**
370 - * Replace <!--LINK--> link placeholders with plain text of links
371 - * (not HTML-formatted).
372 - * @param string $text
373 - * @return string
374 - */
375 - function replaceText( $text ) {
376 - wfProfileIn( __METHOD__ );
377 -
378 - $text = preg_replace_callback(
379 - '/<!--(LINK|IWLINK) (.*?)-->/',
380 - array( &$this, 'replaceTextCallback' ),
381 - $text );
382 -
383 - wfProfileOut( __METHOD__ );
384 - return $text;
385 - }
386 -
387 - /**
388 - * @param array $matches
389 - * @return string
390 - * @private
391 - */
392 - function replaceTextCallback( $matches ) {
393 - $type = $matches[1];
394 - $key = $matches[2];
395 - if( $type == 'LINK' ) {
396 - list( $ns, $index ) = explode( ':', $key, 2 );
397 - if( isset( $this->internals[$ns][$index]['text'] ) ) {
398 - return $this->internals[$ns][$index]['text'];
399 - }
400 - } elseif( $type == 'IWLINK' ) {
401 - if( isset( $this->interwikis[$key]['text'] ) ) {
402 - return $this->interwikis[$key]['text'];
403 - }
404 - }
405 - return $matches[0];
406 - }
407 -}
Index: trunk/phase3/includes/parser/Parser.php
@@ -98,7 +98,7 @@
9999 # Cleared with clearState():
100100 var $mOutput, $mAutonumber, $mDTopen, $mStripState;
101101 var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
102 - var $mInterwikiLinkHolders, $mLinkHolders, $mLinkID;
 102+ var $mInterwikiLinkHolders, $mLinkHolders;
103103 var $mIncludeSizes, $mPPNodeCount, $mDefaultSort;
104104 var $mTplExpandCache; // empty-frame expansion cache
105105 var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
@@ -179,8 +179,17 @@
180180 $this->mStripState = new StripState;
181181 $this->mArgStack = false;
182182 $this->mInPre = false;
183 - $this->mLinkHolders = new LinkHolderArray( $this );
184 - $this->mLinkID = 0;
 183+ $this->mInterwikiLinkHolders = array(
 184+ 'texts' => array(),
 185+ 'titles' => array()
 186+ );
 187+ $this->mLinkHolders = array(
 188+ 'namespaces' => array(),
 189+ 'dbkeys' => array(),
 190+ 'queries' => array(),
 191+ 'texts' => array(),
 192+ 'titles' => array()
 193+ );
185194 $this->mRevisionTimestamp = $this->mRevisionId = null;
186195
187196 /**
@@ -195,7 +204,7 @@
196205 */
197206 #$this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString();
198207 # Changed to \x7f to allow XML double-parsing -- TS
199 - $this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString();
 208+ $this->mUniqPrefix = "\x7fUNIQ" . Parser::getRandomString();
200209
201210
202211 # Clear these on every parse, bug 4549
@@ -285,7 +294,7 @@
286295 */
287296
288297 global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang;
289 - $fname = __METHOD__.'-' . wfGetCaller();
 298+ $fname = 'Parser::parse-' . wfGetCaller();
290299 wfProfileIn( __METHOD__ );
291300 wfProfileIn( $fname );
292301
@@ -319,6 +328,7 @@
320329 );
321330 $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text );
322331
 332+ # only once and last
323333 $text = $this->doBlockLevels( $text, $linestart );
324334
325335 $this->replaceLinkHolders( $text );
@@ -338,7 +348,7 @@
339349 $uniq_prefix = $this->mUniqPrefix;
340350 $matches = array();
341351 $elements = array_keys( $this->mTransparentTagHooks );
342 - $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
 352+ $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
343353
344354 foreach( $matches as $marker => $data ) {
345355 list( $element, $content, $params, $tag ) = $data;
@@ -356,7 +366,7 @@
357367 $text = Sanitizer::normalizeCharReferences( $text );
358368
359369 if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) {
360 - $text = self::tidy($text);
 370+ $text = Parser::tidy($text);
361371 } else {
362372 # attempt to sanitize at least some nesting problems
363373 # (bug #2702 and quite a few others)
@@ -461,8 +471,6 @@
462472 function &getTitle() { return $this->mTitle; }
463473 function getOptions() { return $this->mOptions; }
464474 function getRevisionId() { return $this->mRevisionId; }
465 - function getOutput() { return $this->mOutput; }
466 - function nextLinkID() { return $this->mLinkID++; }
467475
468476 function getFunctionLang() {
469477 global $wgLang, $wgContLang;
@@ -650,9 +658,9 @@
651659 ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>'.
652660 '<head><title>test</title></head><body>'.$text.'</body></html>';
653661 if( $wgTidyInternal ) {
654 - $correctedtext = self::internalTidy( $wrappedtext );
 662+ $correctedtext = Parser::internalTidy( $wrappedtext );
655663 } else {
656 - $correctedtext = self::externalTidy( $wrappedtext );
 664+ $correctedtext = Parser::externalTidy( $wrappedtext );
657665 }
658666 if( is_null( $correctedtext ) ) {
659667 wfDebug( "Tidy error detected!\n" );
@@ -669,7 +677,8 @@
670678 */
671679 function externalTidy( $text ) {
672680 global $wgTidyConf, $wgTidyBin, $wgTidyOpts;
673 - wfProfileIn( __METHOD__ );
 681+ $fname = 'Parser::externalTidy';
 682+ wfProfileIn( $fname );
674683
675684 $cleansource = '';
676685 $opts = ' -utf8';
@@ -698,7 +707,7 @@
699708 }
700709 }
701710
702 - wfProfileOut( __METHOD__ );
 711+ wfProfileOut( $fname );
703712
704713 if( $cleansource == '' && $text != '') {
705714 // Some kind of error happened, so we couldn't get the corrected text.
@@ -720,7 +729,8 @@
721730 */
722731 function internalTidy( $text ) {
723732 global $wgTidyConf, $IP, $wgDebugTidy;
724 - wfProfileIn( __METHOD__ );
 733+ $fname = 'Parser::internalTidy';
 734+ wfProfileIn( $fname );
725735
726736 $tidy = new tidy;
727737 $tidy->parseString( $text, $wgTidyConf, 'utf8' );
@@ -738,7 +748,7 @@
739749 "\n-->";
740750 }
741751
742 - wfProfileOut( __METHOD__ );
 752+ wfProfileOut( $fname );
743753 return $cleansource;
744754 }
745755
@@ -748,35 +758,34 @@
749759 * @private
750760 */
751761 function doTableStuff ( $text ) {
752 - wfProfileIn( __METHOD__ );
 762+ $fname = 'Parser::doTableStuff';
 763+ wfProfileIn( $fname );
753764
754 - $lines = StringUtils::explode( "\n", $text );
755 - $out = '';
 765+ $lines = explode ( "\n" , $text );
756766 $td_history = array (); // Is currently a td tag open?
757767 $last_tag_history = array (); // Save history of last lag activated (td, th or caption)
758768 $tr_history = array (); // Is currently a tr tag open?
759769 $tr_attributes = array (); // history of tr attributes
760770 $has_opened_tr = array(); // Did this table open a <tr> element?
761771 $indent_level = 0; // indent level of the table
 772+ foreach ( $lines as $key => $line )
 773+ {
 774+ $line = trim ( $line );
762775
763 - foreach ( $lines as $outLine ) {
764 - $line = trim( $outLine );
765 -
766776 if( $line == '' ) { // empty line, go to next line
767 - $out .= "\n";
768777 continue;
769778 }
770 - $first_character = $line[0];
 779+ $first_character = $line{0};
771780 $matches = array();
772781
773 - if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) {
 782+ if ( preg_match( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) {
774783 // First check if we are starting a new table
775784 $indent_level = strlen( $matches[1] );
776785
777786 $attributes = $this->mStripState->unstripBoth( $matches[2] );
778787 $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' );
779788
780 - $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
 789+ $lines[$key] = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
781790 array_push ( $td_history , false );
782791 array_push ( $last_tag_history , '' );
783792 array_push ( $tr_history , false );
@@ -784,7 +793,6 @@
785794 array_push ( $has_opened_tr , false );
786795 } else if ( count ( $td_history ) == 0 ) {
787796 // Don't do any of the following
788 - $out .= $outLine."\n";
789797 continue;
790798 } else if ( substr ( $line , 0 , 2 ) == '|}' ) {
791799 // We are ending a table
@@ -803,7 +811,7 @@
804812 $line = "</{$last_tag}>{$line}";
805813 }
806814 array_pop ( $tr_attributes );
807 - $outLine = $line . str_repeat( '</dd></dl>' , $indent_level );
 815+ $lines[$key] = $line . str_repeat( '</dd></dl>' , $indent_level );
808816 } else if ( substr ( $line , 0 , 2 ) == '|-' ) {
809817 // Now we have a table row
810818 $line = preg_replace( '#^\|-+#', '', $line );
@@ -827,7 +835,7 @@
828836 $line = "</{$last_tag}>{$line}";
829837 }
830838
831 - $outLine = $line;
 839+ $lines[$key] = $line;
832840 array_push ( $tr_history , false );
833841 array_push ( $td_history , false );
834842 array_push ( $last_tag_history , '' );
@@ -851,7 +859,7 @@
852860 // attribute values containing literal "||".
853861 $cells = StringUtils::explodeMarkup( '||' , $line );
854862
855 - $outLine = '';
 863+ $lines[$key] = '';
856864
857865 // Loop through each table cell
858866 foreach ( $cells as $cell )
@@ -902,42 +910,38 @@
903911 $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
904912 }
905913
906 - $outLine .= $cell;
 914+ $lines[$key] .= $cell;
907915 array_push ( $td_history , true );
908916 }
909917 }
910 - $out .= $outLine . "\n";
911918 }
912919
913920 // Closing open td, tr && table
914921 while ( count ( $td_history ) > 0 )
915922 {
916923 if ( array_pop ( $td_history ) ) {
917 - $out .= "</td>\n";
 924+ $lines[] = '</td>' ;
918925 }
919926 if ( array_pop ( $tr_history ) ) {
920 - $out .= "</tr>\n";
 927+ $lines[] = '</tr>' ;
921928 }
922929 if ( !array_pop ( $has_opened_tr ) ) {
923 - $out .= "<tr><td></td></tr>\n" ;
 930+ $lines[] = "<tr><td></td></tr>" ;
924931 }
925932
926 - $out .= "</table>\n";
 933+ $lines[] = '</table>' ;
927934 }
928935
929 - // Remove trailing line-ending (b/c)
930 - if ( substr( $out, -1 ) == "\n" ) {
931 - $out = substr( $out, 0, -1 );
932 - }
 936+ $output = implode ( "\n" , $lines ) ;
933937
934938 // special case: don't return empty table
935 - if( $out == "<table>\n<tr><td></td></tr>\n</table>" ) {
936 - $out = '';
 939+ if( $output == "<table>\n<tr><td></td></tr>\n</table>" ) {
 940+ $output = '';
937941 }
938942
939 - wfProfileOut( __METHOD__ );
 943+ wfProfileOut( $fname );
940944
941 - return $out;
 945+ return $output;
942946 }
943947
944948 /**
@@ -948,11 +952,12 @@
949953 */
950954 function internalParse( $text ) {
951955 $isMain = true;
952 - wfProfileIn( __METHOD__ );
 956+ $fname = 'Parser::internalParse';
 957+ wfProfileIn( $fname );
953958
954959 # Hook to suspend the parser in this state
955960 if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$this->mStripState ) ) ) {
956 - wfProfileOut( __METHOD__ );
 961+ wfProfileOut( $fname );
957962 return $text ;
958963 }
959964
@@ -985,7 +990,7 @@
986991 $text = $this->doMagicLinks( $text );
987992 $text = $this->formatHeadings( $text, $isMain );
988993
989 - wfProfileOut( __METHOD__ );
 994+ wfProfileOut( $fname );
990995 return $text;
991996 }
992997
@@ -1055,13 +1060,14 @@
10561061 * @private
10571062 */
10581063 function doHeadings( $text ) {
1059 - wfProfileIn( __METHOD__ );
 1064+ $fname = 'Parser::doHeadings';
 1065+ wfProfileIn( $fname );
10601066 for ( $i = 6; $i >= 1; --$i ) {
10611067 $h = str_repeat( '=', $i );
10621068 $text = preg_replace( "/^$h(.+)$h\\s*$/m",
10631069 "<h$i>\\1</h$i>", $text );
10641070 }
1065 - wfProfileOut( __METHOD__ );
 1071+ wfProfileOut( $fname );
10661072 return $text;
10671073 }
10681074
@@ -1071,14 +1077,15 @@
10721078 * @return string the altered text
10731079 */
10741080 function doAllQuotes( $text ) {
1075 - wfProfileIn( __METHOD__ );
 1081+ $fname = 'Parser::doAllQuotes';
 1082+ wfProfileIn( $fname );
10761083 $outtext = '';
1077 - $lines = StringUtils::explode( "\n", $text );
 1084+ $lines = explode( "\n", $text );
10781085 foreach ( $lines as $line ) {
1079 - $outtext .= $this->doQuotes( $line ) . "\n";
 1086+ $outtext .= $this->doQuotes ( $line ) . "\n";
10801087 }
10811088 $outtext = substr($outtext, 0,-1);
1082 - wfProfileOut( __METHOD__ );
 1089+ wfProfileOut( $fname );
10831090 return $outtext;
10841091 }
10851092
@@ -1257,7 +1264,8 @@
12581265 */
12591266 function replaceExternalLinks( $text ) {
12601267 global $wgContLang;
1261 - wfProfileIn( __METHOD__ );
 1268+ $fname = 'Parser::replaceExternalLinks';
 1269+ wfProfileIn( $fname );
12621270
12631271 $sk = $this->mOptions->getSkin();
12641272
@@ -1327,11 +1335,11 @@
13281336 # Register link in the output object.
13291337 # Replace unnecessary URL escape codes with the referenced character
13301338 # This prevents spammers from hiding links from the filters
1331 - $pasteurized = self::replaceUnusualEscapes( $url );
 1339+ $pasteurized = Parser::replaceUnusualEscapes( $url );
13321340 $this->mOutput->addExternalLink( $pasteurized );
13331341 }
13341342
1335 - wfProfileOut( __METHOD__ );
 1343+ wfProfileOut( $fname );
13361344 return $s;
13371345 }
13381346
@@ -1341,7 +1349,8 @@
13421350 */
13431351 function replaceFreeExternalLinks( $text ) {
13441352 global $wgContLang;
1345 - wfProfileIn( __METHOD__ );
 1353+ $fname = 'Parser::replaceFreeExternalLinks';
 1354+ wfProfileIn( $fname );
13461355
13471356 $bits = preg_split( '/(\b(?:' . wfUrlProtocols() . '))/S', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
13481357 $s = array_shift( $bits );
@@ -1403,7 +1412,7 @@
14041413 $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() );
14051414 # Register it in the output object...
14061415 # Replace unnecessary URL escape codes with their equivalent characters
1407 - $pasteurized = self::replaceUnusualEscapes( $url );
 1416+ $pasteurized = Parser::replaceUnusualEscapes( $url );
14081417 $this->mOutput->addExternalLink( $pasteurized );
14091418 }
14101419 $s .= $text . $trail;
@@ -1411,7 +1420,7 @@
14121421 $s .= $protocol . $remainder;
14131422 }
14141423 }
1415 - wfProfileOut( __METHOD__ );
 1424+ wfProfileOut( $fname );
14161425 return $s;
14171426 }
14181427
@@ -1427,7 +1436,7 @@
14281437 */
14291438 static function replaceUnusualEscapes( $url ) {
14301439 return preg_replace_callback( '/%[0-9A-Fa-f]{2}/',
1431 - array( __CLASS__, 'replaceUnusualEscapesCallback' ), $url );
 1440+ array( 'Parser', 'replaceUnusualEscapesCallback' ), $url );
14321441 }
14331442
14341443 /**
@@ -1471,48 +1480,35 @@
14721481
14731482 /**
14741483 * Process [[ ]] wikilinks
1475 - * @return processed text
14761484 *
14771485 * @private
14781486 */
14791487 function replaceInternalLinks( $s ) {
1480 - $this->mLinkHolders->merge( $this->replaceInternalLinks2( $s ) );
1481 - return $s;
1482 - }
1483 -
1484 - /**
1485 - * Process [[ ]] wikilinks
1486 - * @return LinkHolderArray
1487 - *
1488 - * @private
1489 - */
1490 - function replaceInternalLinks2( &$s ) {
14911488 global $wgContLang;
 1489+ static $fname = 'Parser::replaceInternalLinks' ;
14921490
1493 - wfProfileIn( __METHOD__ );
 1491+ wfProfileIn( $fname );
14941492
1495 - wfProfileIn( __METHOD__.'-setup' );
1496 - static $tc = FALSE, $e1, $e1_img;
 1493+ wfProfileIn( $fname.'-setup' );
 1494+ static $tc = FALSE;
14971495 # the % is needed to support urlencoded titles as well
1498 - if ( !$tc ) {
1499 - $tc = Title::legalChars() . '#%';
1500 - # Match a link having the form [[namespace:link|alternate]]trail
1501 - $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD";
1502 - # Match cases where there is no "]]", which might still be images
1503 - $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD";
1504 - }
 1496+ if ( !$tc ) { $tc = Title::legalChars() . '#%'; }
15051497
15061498 $sk = $this->mOptions->getSkin();
1507 - $holders = new LinkHolderArray( $this );
15081499
15091500 #split the entire text string on occurences of [[
1510 - $a = StringUtils::explode( '[[', ' ' . $s );
 1501+ $a = explode( '[[', ' ' . $s );
15111502 #get the first element (all text up to first [[), and remove the space we added
1512 - $s = $a->current();
1513 - $a->next();
1514 - $line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void"
 1503+ $s = array_shift( $a );
15151504 $s = substr( $s, 1 );
15161505
 1506+ # Match a link having the form [[namespace:link|alternate]]trail
 1507+ static $e1 = FALSE;
 1508+ if ( !$e1 ) { $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD"; }
 1509+ # Match cases where there is no "]]", which might still be images
 1510+ static $e1_img = FALSE;
 1511+ if ( !$e1_img ) { $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; }
 1512+
15171513 $useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
15181514 $e2 = null;
15191515 if ( $useLinkPrefixExtension ) {
@@ -1522,8 +1518,8 @@
15231519 }
15241520
15251521 if( is_null( $this->mTitle ) ) {
1526 - wfProfileOut( __METHOD__ );
1527 - wfProfileOut( __METHOD__.'-setup' );
 1522+ wfProfileOut( $fname );
 1523+ wfProfileOut( $fname.'-setup' );
15281524 throw new MWException( __METHOD__.": \$this->mTitle is null\n" );
15291525 }
15301526 $nottalk = !$this->mTitle->isTalkPage();
@@ -1545,20 +1541,13 @@
15461542 $selflink = array($this->mTitle->getPrefixedText());
15471543 }
15481544 $useSubpages = $this->areSubpagesAllowed();
1549 - wfProfileOut( __METHOD__.'-setup' );
 1545+ wfProfileOut( $fname.'-setup' );
15501546
15511547 # Loop for each link
1552 - for ( ; $line !== false && $line !== null ; $a->next(), $line = $a->current() ) {
1553 - # Check for excessive memory usage
1554 - if ( $holders->isBig() ) {
1555 - # Too big
1556 - # Do the existence check, replace the link holders and clear the array
1557 - $holders->replace( $s );
1558 - $holders->clear();
1559 - }
1560 -
 1548+ for ($k = 0; isset( $a[$k] ); $k++) {
 1549+ $line = $a[$k];
15611550 if ( $useLinkPrefixExtension ) {
1562 - wfProfileIn( __METHOD__.'-prefixhandling' );
 1551+ wfProfileIn( $fname.'-prefixhandling' );
15631552 if ( preg_match( $e2, $s, $m ) ) {
15641553 $prefix = $m[2];
15651554 $s = $m[1];
@@ -1570,12 +1559,12 @@
15711560 $prefix = $first_prefix;
15721561 $first_prefix = false;
15731562 }
1574 - wfProfileOut( __METHOD__.'-prefixhandling' );
 1563+ wfProfileOut( $fname.'-prefixhandling' );
15751564 }
15761565
15771566 $might_be_img = false;
15781567
1579 - wfProfileIn( __METHOD__."-e1" );
 1568+ wfProfileIn( "$fname-e1" );
15801569 if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt
15811570 $text = $m[2];
15821571 # If we get a ] at the beginning of $m[3] that means we have a link that's something like:
@@ -1609,18 +1598,18 @@
16101599 $trail = "";
16111600 } else { # Invalid form; output directly
16121601 $s .= $prefix . '[[' . $line ;
1613 - wfProfileOut( __METHOD__."-e1" );
 1602+ wfProfileOut( "$fname-e1" );
16141603 continue;
16151604 }
1616 - wfProfileOut( __METHOD__."-e1" );
1617 - wfProfileIn( __METHOD__."-misc" );
 1605+ wfProfileOut( "$fname-e1" );
 1606+ wfProfileIn( "$fname-misc" );
16181607
16191608 # Don't allow internal links to pages containing
16201609 # PROTO: where PROTO is a valid URL protocol; these
16211610 # should be external links.
16221611 if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) {
16231612 $s .= $prefix . '[[' . $line ;
1624 - wfProfileOut( __METHOD__."-misc" );
 1613+ wfProfileOut( "$fname-misc" );
16251614 continue;
16261615 }
16271616
@@ -1637,30 +1626,27 @@
16381627 $link = substr($link, 1);
16391628 }
16401629
1641 - wfProfileOut( __METHOD__."-misc" );
1642 - wfProfileIn( __METHOD__."-title" );
 1630+ wfProfileOut( "$fname-misc" );
 1631+ wfProfileIn( "$fname-title" );
16431632 $nt = Title::newFromText( $this->mStripState->unstripNoWiki($link) );
16441633 if( !$nt ) {
16451634 $s .= $prefix . '[[' . $line;
1646 - wfProfileOut( __METHOD__."-title" );
 1635+ wfProfileOut( "$fname-title" );
16471636 continue;
16481637 }
16491638
16501639 $ns = $nt->getNamespace();
16511640 $iw = $nt->getInterWiki();
1652 - wfProfileOut( __METHOD__."-title" );
 1641+ wfProfileOut( "$fname-title" );
16531642
16541643 if ($might_be_img) { # if this is actually an invalid link
1655 - wfProfileIn( __METHOD__."-might_be_img" );
 1644+ wfProfileIn( "$fname-might_be_img" );
16561645 if ($ns == NS_IMAGE && $noforce) { #but might be an image
16571646 $found = false;
1658 - while ( true ) {
 1647+ while (isset ($a[$k+1]) ) {
16591648 #look at the next 'line' to see if we can close it there
1660 - $a->next();
1661 - $next_line = $a->current();
1662 - if ( $next_line === false || $next_line === null ) {
1663 - break;
1664 - }
 1649+ $spliced = array_splice( $a, $k + 1, 1 );
 1650+ $next_line = array_shift( $spliced );
16651651 $m = explode( ']]', $next_line, 3 );
16661652 if ( count( $m ) == 3 ) {
16671653 # the first ]] closes the inner link, the second the image
@@ -1680,19 +1666,19 @@
16811667 if ( !$found ) {
16821668 # we couldn't find the end of this imageLink, so output it raw
16831669 #but don't ignore what might be perfectly normal links in the text we've examined
1684 - $holders->merge( $this->replaceInternalLinks2( $text ) );
 1670+ $text = $this->replaceInternalLinks($text);
16851671 $s .= "{$prefix}[[$link|$text";
16861672 # note: no $trail, because without an end, there *is* no trail
1687 - wfProfileOut( __METHOD__."-might_be_img" );
 1673+ wfProfileOut( "$fname-might_be_img" );
16881674 continue;
16891675 }
16901676 } else { #it's not an image, so output it raw
16911677 $s .= "{$prefix}[[$link|$text";
16921678 # note: no $trail, because without an end, there *is* no trail
1693 - wfProfileOut( __METHOD__."-might_be_img" );
 1679+ wfProfileOut( "$fname-might_be_img" );
16941680 continue;
16951681 }
1696 - wfProfileOut( __METHOD__."-might_be_img" );
 1682+ wfProfileOut( "$fname-might_be_img" );
16971683 }
16981684
16991685 $wasblank = ( '' == $text );
@@ -1702,38 +1688,41 @@
17031689 if( $noforce ) {
17041690
17051691 # Interwikis
1706 - wfProfileIn( __METHOD__."-interwiki" );
 1692+ wfProfileIn( "$fname-interwiki" );
17071693 if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
17081694 $this->mOutput->addLanguageLink( $nt->getFullText() );
17091695 $s = rtrim($s . $prefix);
17101696 $s .= trim($trail, "\n") == '' ? '': $prefix . $trail;
1711 - wfProfileOut( __METHOD__."-interwiki" );
 1697+ wfProfileOut( "$fname-interwiki" );
17121698 continue;
17131699 }
1714 - wfProfileOut( __METHOD__."-interwiki" );
 1700+ wfProfileOut( "$fname-interwiki" );
17151701
17161702 if ( $ns == NS_IMAGE ) {
1717 - wfProfileIn( __METHOD__."-image" );
 1703+ wfProfileIn( "$fname-image" );
17181704 if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) {
17191705 # recursively parse links inside the image caption
17201706 # actually, this will parse them in any other parameters, too,
17211707 # but it might be hard to fix that, and it doesn't matter ATM
17221708 $text = $this->replaceExternalLinks($text);
1723 - $holders->merge( $this->replaceInternalLinks2( $text ) );
 1709+ $text = $this->replaceInternalLinks($text);
17241710
17251711 # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
1726 - $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text, $holders ) ) . $trail;
 1712+ $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text ) ) . $trail;
 1713+ $this->mOutput->addImage( $nt->getDBkey() );
17271714
1728 - wfProfileOut( __METHOD__."-image" );
 1715+ wfProfileOut( "$fname-image" );
17291716 continue;
 1717+ } else {
 1718+ # We still need to record the image's presence on the page
 1719+ $this->mOutput->addImage( $nt->getDBkey() );
17301720 }
1731 - $this->mOutput->addImage( $nt->getDBkey() );
1732 - wfProfileOut( __METHOD__."-image" );
 1721+ wfProfileOut( "$fname-image" );
17331722
17341723 }
17351724
17361725 if ( $ns == NS_CATEGORY ) {
1737 - wfProfileIn( __METHOD__."-category" );
 1726+ wfProfileIn( "$fname-category" );
17381727 $s = rtrim($s . "\n"); # bug 87
17391728
17401729 if ( $wasblank ) {
@@ -1752,7 +1741,7 @@
17531742 */
17541743 $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail;
17551744
1756 - wfProfileOut( __METHOD__."-category" );
 1745+ wfProfileOut( "$fname-category" );
17571746 continue;
17581747 }
17591748 }
@@ -1783,7 +1772,7 @@
17841773 if( SpecialPage::exists( $nt->getDBkey() ) ) {
17851774 $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
17861775 } else {
1787 - $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix );
 1776+ $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
17881777 }
17891778 continue;
17901779 } elseif( $ns == NS_IMAGE ) {
@@ -1797,10 +1786,10 @@
17981787 continue;
17991788 }
18001789 }
1801 - $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix );
 1790+ $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
18021791 }
1803 - wfProfileOut( __METHOD__ );
1804 - return $holders;
 1792+ wfProfileOut( $fname );
 1793+ return $s;
18051794 }
18061795
18071796 /**
@@ -1809,10 +1798,32 @@
18101799 * parsing of interwiki links, and secondly to allow all existence checks and
18111800 * article length checks (for stub links) to be bundled into a single query.
18121801 *
1813 - * @deprecated
18141802 */
18151803 function makeLinkHolder( &$nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
1816 - return $this->mLinkHolders->makeHolder( $nt, $text, $query, $trail, $prefix );
 1804+ wfProfileIn( __METHOD__ );
 1805+ if ( ! is_object($nt) ) {
 1806+ # Fail gracefully
 1807+ $retVal = "<!-- ERROR -->{$prefix}{$text}{$trail}";
 1808+ } else {
 1809+ # Separate the link trail from the rest of the link
 1810+ list( $inside, $trail ) = Linker::splitTrail( $trail );
 1811+
 1812+ if ( $nt->isExternal() ) {
 1813+ $nr = array_push( $this->mInterwikiLinkHolders['texts'], $prefix.$text.$inside );
 1814+ $this->mInterwikiLinkHolders['titles'][] = $nt;
 1815+ $retVal = '<!--IWLINK '. ($nr-1) ."-->{$trail}";
 1816+ } else {
 1817+ $nr = array_push( $this->mLinkHolders['namespaces'], $nt->getNamespace() );
 1818+ $this->mLinkHolders['dbkeys'][] = $nt->getDBkey();
 1819+ $this->mLinkHolders['queries'][] = $query;
 1820+ $this->mLinkHolders['texts'][] = $prefix.$text.$inside;
 1821+ $this->mLinkHolders['titles'][] = $nt;
 1822+
 1823+ $retVal = '<!--LINK '. ($nr-1) ."-->{$trail}";
 1824+ }
 1825+ }
 1826+ wfProfileOut( __METHOD__ );
 1827+ return $retVal;
18171828 }
18181829
18191830 /**
@@ -1878,7 +1889,8 @@
18791890 # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
18801891 # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage
18811892
1882 - wfProfileIn( __METHOD__ );
 1893+ $fname = 'Parser::maybeDoSubpageLink';
 1894+ wfProfileIn( $fname );
18831895 $ret = $target; # default return value is no change
18841896
18851897 # Some namespaces don't allow subpages,
@@ -1937,7 +1949,7 @@
19381950 }
19391951 }
19401952
1941 - wfProfileOut( __METHOD__ );
 1953+ wfProfileOut( $fname );
19421954 return $ret;
19431955 }
19441956
@@ -2024,53 +2036,50 @@
20252037 * @return string the lists rendered as HTML
20262038 */
20272039 function doBlockLevels( $text, $linestart ) {
2028 - wfProfileIn( __METHOD__ );
 2040+ $fname = 'Parser::doBlockLevels';
 2041+ wfProfileIn( $fname );
20292042
20302043 # Parsing through the text line by line. The main thing
20312044 # happening here is handling of block-level elements p, pre,
20322045 # and making lists from lines starting with * # : etc.
20332046 #
2034 - $textLines = StringUtils::explode( "\n", $text );
 2047+ $textLines = explode( "\n", $text );
20352048
20362049 $lastPrefix = $output = '';
20372050 $this->mDTopen = $inBlockElem = false;
20382051 $prefixLength = 0;
20392052 $paragraphStack = false;
20402053
 2054+ if ( !$linestart ) {
 2055+ $output .= array_shift( $textLines );
 2056+ }
20412057 foreach ( $textLines as $oLine ) {
2042 - # Fix up $linestart
2043 - if ( !$linestart ) {
2044 - $output .= $oLine;
2045 - $linestart = true;
2046 - continue;
2047 - }
2048 -
20492058 $lastPrefixLength = strlen( $lastPrefix );
20502059 $preCloseMatch = preg_match('/<\\/pre/i', $oLine );
20512060 $preOpenMatch = preg_match('/<pre/i', $oLine );
20522061 if ( !$this->mInPre ) {
20532062 # Multiple prefixes may abut each other for nested lists.
20542063 $prefixLength = strspn( $oLine, '*#:;' );
2055 - $prefix = substr( $oLine, 0, $prefixLength );
 2064+ $pref = substr( $oLine, 0, $prefixLength );
20562065
20572066 # eh?
2058 - $prefix2 = str_replace( ';', ':', $prefix );
 2067+ $pref2 = str_replace( ';', ':', $pref );
20592068 $t = substr( $oLine, $prefixLength );
2060 - $this->mInPre = (bool)$preOpenMatch;
 2069+ $this->mInPre = !empty($preOpenMatch);
20612070 } else {
20622071 # Don't interpret any other prefixes in preformatted text
20632072 $prefixLength = 0;
2064 - $prefix = $prefix2 = '';
 2073+ $pref = $pref2 = '';
20652074 $t = $oLine;
20662075 }
20672076
20682077 # List generation
2069 - if( $prefixLength && $lastPrefix === $prefix2 ) {
 2078+ if( $prefixLength && 0 == strcmp( $lastPrefix, $pref2 ) ) {
20702079 # Same as the last item, so no need to deal with nesting or opening stuff
2071 - $output .= $this->nextItem( substr( $prefix, -1 ) );
 2080+ $output .= $this->nextItem( substr( $pref, -1 ) );
20722081 $paragraphStack = false;
20732082
2074 - if ( substr( $prefix, -1 ) == ';') {
 2083+ if ( substr( $pref, -1 ) == ';') {
20752084 # The one nasty exception: definition lists work like this:
20762085 # ; title : definition text
20772086 # So we check for : in the remainder text to split up the
@@ -2083,18 +2092,18 @@
20842093 }
20852094 } elseif( $prefixLength || $lastPrefixLength ) {
20862095 # Either open or close a level...
2087 - $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix );
 2096+ $commonPrefixLength = $this->getCommon( $pref, $lastPrefix );
20882097 $paragraphStack = false;
20892098
20902099 while( $commonPrefixLength < $lastPrefixLength ) {
2091 - $output .= $this->closeList( $lastPrefix[$lastPrefixLength-1] );
 2100+ $output .= $this->closeList( $lastPrefix{$lastPrefixLength-1} );
20922101 --$lastPrefixLength;
20932102 }
20942103 if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
2095 - $output .= $this->nextItem( $prefix[$commonPrefixLength-1] );
 2104+ $output .= $this->nextItem( $pref{$commonPrefixLength-1} );
20962105 }
20972106 while ( $prefixLength > $commonPrefixLength ) {
2098 - $char = substr( $prefix, $commonPrefixLength, 1 );
 2107+ $char = substr( $pref, $commonPrefixLength, 1 );
20992108 $output .= $this->openList( $char );
21002109
21012110 if ( ';' == $char ) {
@@ -2106,10 +2115,10 @@
21072116 }
21082117 ++$commonPrefixLength;
21092118 }
2110 - $lastPrefix = $prefix2;
 2119+ $lastPrefix = $pref2;
21112120 }
21122121 if( 0 == $prefixLength ) {
2113 - wfProfileIn( __METHOD__."-paragraph" );
 2122+ wfProfileIn( "$fname-paragraph" );
21142123 # No prefix (not in list)--go to paragraph mode
21152124 // XXX: use a stack for nestable elements like span, table and div
21162125 $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
@@ -2165,7 +2174,7 @@
21662175 }
21672176 }
21682177 }
2169 - wfProfileOut( __METHOD__."-paragraph" );
 2178+ wfProfileOut( "$fname-paragraph" );
21702179 }
21712180 // somewhere above we forget to get out of pre block (bug 785)
21722181 if($preCloseMatch && $this->mInPre) {
@@ -2176,7 +2185,7 @@
21772186 }
21782187 }
21792188 while ( $prefixLength ) {
2180 - $output .= $this->closeList( $prefix2[$prefixLength-1] );
 2189+ $output .= $this->closeList( $pref2{$prefixLength-1} );
21812190 --$prefixLength;
21822191 }
21832192 if ( '' != $this->mLastSection ) {
@@ -2184,7 +2193,7 @@
21852194 $this->mLastSection = '';
21862195 }
21872196
2188 - wfProfileOut( __METHOD__ );
 2197+ wfProfileOut( $fname );
21892198 return $output;
21902199 }
21912200
@@ -2197,12 +2206,13 @@
21982207 * return string the position of the ':', or false if none found
21992208 */
22002209 function findColonNoLinks($str, &$before, &$after) {
2201 - wfProfileIn( __METHOD__ );
 2210+ $fname = 'Parser::findColonNoLinks';
 2211+ wfProfileIn( $fname );
22022212
22032213 $pos = strpos( $str, ':' );
22042214 if( $pos === false ) {
22052215 // Nothing to find!
2206 - wfProfileOut( __METHOD__ );
 2216+ wfProfileOut( $fname );
22072217 return false;
22082218 }
22092219
@@ -2211,7 +2221,7 @@
22122222 // Easy; no tag nesting to worry about
22132223 $before = substr( $str, 0, $pos );
22142224 $after = substr( $str, $pos+1 );
2215 - wfProfileOut( __METHOD__ );
 2225+ wfProfileOut( $fname );
22162226 return $pos;
22172227 }
22182228
@@ -2235,7 +2245,7 @@
22362246 // We found it!
22372247 $before = substr( $str, 0, $i );
22382248 $after = substr( $str, $i + 1 );
2239 - wfProfileOut( __METHOD__ );
 2249+ wfProfileOut( $fname );
22402250 return $i;
22412251 }
22422252 // Embedded in a tag; don't break it.
@@ -2245,7 +2255,7 @@
22462256 $colon = strpos( $str, ':', $i );
22472257 if( $colon === false ) {
22482258 // Nothing else interesting
2249 - wfProfileOut( __METHOD__ );
 2259+ wfProfileOut( $fname );
22502260 return false;
22512261 }
22522262 $lt = strpos( $str, '<', $i );
@@ -2254,7 +2264,7 @@
22552265 // We found it!
22562266 $before = substr( $str, 0, $colon );
22572267 $after = substr( $str, $colon + 1 );
2258 - wfProfileOut( __METHOD__ );
 2268+ wfProfileOut( $fname );
22592269 return $i;
22602270 }
22612271 }
@@ -2304,8 +2314,8 @@
23052315 if( $c == ">" ) {
23062316 $stack--;
23072317 if( $stack < 0 ) {
2308 - wfDebug( __METHOD__.": Invalid input; too many close tags\n" );
2309 - wfProfileOut( __METHOD__ );
 2318+ wfDebug( "Invalid input in $fname; too many close tags\n" );
 2319+ wfProfileOut( $fname );
23102320 return false;
23112321 }
23122322 $state = self::COLON_STATE_TEXT;
@@ -2340,14 +2350,14 @@
23412351 }
23422352 break;
23432353 default:
2344 - throw new MWException( "State machine error in " . __METHOD__ );
 2354+ throw new MWException( "State machine error in $fname" );
23452355 }
23462356 }
23472357 if( $stack > 0 ) {
2348 - wfDebug( __METHOD__.": Invalid input; not enough close tags (stack $stack, state $state)\n" );
 2358+ wfDebug( "Invalid input in $fname; not enough close tags (stack $stack, state $state)\n" );
23492359 return false;
23502360 }
2351 - wfProfileOut( __METHOD__ );
 2361+ wfProfileOut( $fname );
23522362 return false;
23532363 }
23542364
@@ -2577,11 +2587,12 @@
25782588 * @private
25792589 */
25802590 function initialiseVariables() {
2581 - wfProfileIn( __METHOD__ );
 2591+ $fname = 'Parser::initialiseVariables';
 2592+ wfProfileIn( $fname );
25822593 $variableIDs = MagicWord::getVariableIDs();
25832594
25842595 $this->mVariables = new MagicWordArray( $variableIDs );
2585 - wfProfileOut( __METHOD__ );
 2596+ wfProfileOut( $fname );
25862597 }
25872598
25882599 /**
@@ -2650,7 +2661,8 @@
26512662 return $text;
26522663 }
26532664
2654 - wfProfileIn( __METHOD__ );
 2665+ $fname = __METHOD__;
 2666+ wfProfileIn( $fname );
26552667
26562668 if ( $frame === false ) {
26572669 $frame = $this->getPreprocessor()->newFrame();
@@ -2663,7 +2675,7 @@
26642676 $flags = $argsOnly ? PPFrame::NO_TEMPLATES : 0;
26652677 $text = $frame->expand( $dom, $flags );
26662678
2667 - wfProfileOut( __METHOD__ );
 2679+ wfProfileOut( $fname );
26682680 return $text;
26692681 }
26702682
@@ -2726,7 +2738,8 @@
27272739 */
27282740 function braceSubstitution( $piece, $frame ) {
27292741 global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces;
2730 - wfProfileIn( __METHOD__ );
 2742+ $fname = __METHOD__;
 2743+ wfProfileIn( $fname );
27312744 wfProfileIn( __METHOD__.'-setup' );
27322745
27332746 # Flags
@@ -2913,7 +2926,7 @@
29142927 }
29152928 } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
29162929 $found = false; //access denied
2917 - wfDebug( __METHOD__.": template inclusion denied for " . $title->getPrefixedDBkey() );
 2930+ wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() );
29182931 } else {
29192932 list( $text, $title ) = $this->getTemplateDom( $title );
29202933 if ( $text !== false ) {
@@ -2947,7 +2960,7 @@
29482961 # Recover the source wikitext and return it
29492962 if ( !$found ) {
29502963 $text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args );
2951 - wfProfileOut( __METHOD__ );
 2964+ wfProfileOut( $fname );
29522965 return array( 'object' => $text );
29532966 }
29542967
@@ -3006,7 +3019,7 @@
30073020 $ret = array( 'text' => $text );
30083021 }
30093022
3010 - wfProfileOut( __METHOD__ );
 3023+ wfProfileOut( $fname );
30113024 return $ret;
30123025 }
30133026
@@ -3549,7 +3562,12 @@
35503563 # <!--LINK number-->
35513564 # turns into
35523565 # link text with suffix
3553 - $safeHeadline = $this->replaceLinkHoldersText( $safeHeadline );
 3566+ $safeHeadline = preg_replace( '/<!--LINK ([0-9]*)-->/e',
 3567+ "\$this->mLinkHolders['texts'][\$1]",
 3568+ $safeHeadline );
 3569+ $safeHeadline = preg_replace( '/<!--IWLINK ([0-9]*)-->/e',
 3570+ "\$this->mInterwikiLinkHolders['texts'][\$1]",
 3571+ $safeHeadline );
35543572
35553573 # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
35563574 $tocline = preg_replace(
@@ -3777,7 +3795,7 @@
37783796 } else {
37793797 # Failed to validate; fall back to the default
37803798 $nickname = $username;
3781 - wfDebug( __METHOD__.": $username has bad XML tags in signature.\n" );
 3799+ wfDebug( "Parser::getUserSig: $username has bad XML tags in signature.\n" );
37823800 }
37833801 }
37843802
@@ -3883,17 +3901,19 @@
38843902 global $wgTitle;
38853903 static $executing = false;
38863904
 3905+ $fname = "Parser::transformMsg";
 3906+
38873907 # Guard against infinite recursion
38883908 if ( $executing ) {
38893909 return $text;
38903910 }
38913911 $executing = true;
38923912
3893 - wfProfileIn(__METHOD__);
 3913+ wfProfileIn($fname);
38943914 $text = $this->preprocess( $text, $wgTitle, $options );
38953915
38963916 $executing = false;
3897 - wfProfileOut(__METHOD__);
 3917+ wfProfileOut($fname);
38983918 return $text;
38993919 }
39003920
@@ -3990,7 +4010,7 @@
39914011 # Add to function cache
39924012 $mw = MagicWord::get( $id );
39934013 if( !$mw )
3994 - throw new MWException( __METHOD__.'() expecting a magic word identifier.' );
 4014+ throw new MWException( 'Parser::setFunctionHook() expecting a magic word identifier.' );
39954015
39964016 $synonyms = $mw->getSynonyms();
39974017 $sensitive = intval( $mw->isCaseSensitive() );
@@ -4026,9 +4046,266 @@
40274047 * Replace <!--LINK--> link placeholders with actual links, in the buffer
40284048 * Placeholders created in Skin::makeLinkObj()
40294049 * Returns an array of link CSS classes, indexed by PDBK.
 4050+ * $options is a bit field, RLH_FOR_UPDATE to select for update
40304051 */
40314052 function replaceLinkHolders( &$text, $options = 0 ) {
4032 - return $this->mLinkHolders->replace( $text );
 4053+ global $wgUser;
 4054+ global $wgContLang;
 4055+
 4056+ $fname = 'Parser::replaceLinkHolders';
 4057+ wfProfileIn( $fname );
 4058+
 4059+ $pdbks = array();
 4060+ $colours = array();
 4061+ $linkcolour_ids = array();
 4062+ $sk = $this->mOptions->getSkin();
 4063+ $linkCache = LinkCache::singleton();
 4064+
 4065+ if ( !empty( $this->mLinkHolders['namespaces'] ) ) {
 4066+ wfProfileIn( $fname.'-check' );
 4067+ $dbr = wfGetDB( DB_SLAVE );
 4068+ $page = $dbr->tableName( 'page' );
 4069+ $threshold = $wgUser->getOption('stubthreshold');
 4070+
 4071+ # Sort by namespace
 4072+ asort( $this->mLinkHolders['namespaces'] );
 4073+
 4074+ # Generate query
 4075+ $query = false;
 4076+ $current = null;
 4077+ foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) {
 4078+ # Make title object
 4079+ $title = $this->mLinkHolders['titles'][$key];
 4080+
 4081+ # Skip invalid entries.
 4082+ # Result will be ugly, but prevents crash.
 4083+ if ( is_null( $title ) ) {
 4084+ continue;
 4085+ }
 4086+ $pdbk = $pdbks[$key] = $title->getPrefixedDBkey();
 4087+
 4088+ # Check if it's a static known link, e.g. interwiki
 4089+ if ( $title->isAlwaysKnown() ) {
 4090+ $colours[$pdbk] = '';
 4091+ } elseif ( ( $id = $linkCache->getGoodLinkID( $pdbk ) ) != 0 ) {
 4092+ $colours[$pdbk] = '';
 4093+ $this->mOutput->addLink( $title, $id );
 4094+ } elseif ( $linkCache->isBadLink( $pdbk ) ) {
 4095+ $colours[$pdbk] = 'new';
 4096+ } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) {
 4097+ $colours[$pdbk] = 'new';
 4098+ } else {
 4099+ # Not in the link cache, add it to the query
 4100+ if ( !isset( $current ) ) {
 4101+ $current = $ns;
 4102+ $query = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len";
 4103+ $query .= " FROM $page WHERE (page_namespace=$ns AND page_title IN(";
 4104+ } elseif ( $current != $ns ) {
 4105+ $current = $ns;
 4106+ $query .= ")) OR (page_namespace=$ns AND page_title IN(";
 4107+ } else {
 4108+ $query .= ', ';
 4109+ }
 4110+
 4111+ $query .= $dbr->addQuotes( $this->mLinkHolders['dbkeys'][$key] );
 4112+ }
 4113+ }
 4114+ if ( $query ) {
 4115+ $query .= '))';
 4116+ if ( $options & RLH_FOR_UPDATE ) {
 4117+ $query .= ' FOR UPDATE';
 4118+ }
 4119+
 4120+ $res = $dbr->query( $query, $fname );
 4121+
 4122+ # Fetch data and form into an associative array
 4123+ # non-existent = broken
 4124+ while ( $s = $dbr->fetchObject($res) ) {
 4125+ $title = Title::makeTitle( $s->page_namespace, $s->page_title );
 4126+ $pdbk = $title->getPrefixedDBkey();
 4127+ $linkCache->addGoodLinkObj( $s->page_id, $title, $s->page_len, $s->page_is_redirect );
 4128+ $this->mOutput->addLink( $title, $s->page_id );
 4129+ $colours[$pdbk] = $sk->getLinkColour( $title, $threshold );
 4130+ //add id to the extension todolist
 4131+ $linkcolour_ids[$s->page_id] = $pdbk;
 4132+ }
 4133+ //pass an array of page_ids to an extension
 4134+ wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) );
 4135+ }
 4136+ wfProfileOut( $fname.'-check' );
 4137+
 4138+ # Do a second query for different language variants of links and categories
 4139+ if($wgContLang->hasVariants()){
 4140+ $linkBatch = new LinkBatch();
 4141+ $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders)
 4142+ $categoryMap = array(); // maps $category_variant => $category (dbkeys)
 4143+ $varCategories = array(); // category replacements oldDBkey => newDBkey
 4144+
 4145+ $categories = $this->mOutput->getCategoryLinks();
 4146+
 4147+ // Add variants of links to link batch
 4148+ foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) {
 4149+ $title = $this->mLinkHolders['titles'][$key];
 4150+ if ( is_null( $title ) )
 4151+ continue;
 4152+
 4153+ $pdbk = $title->getPrefixedDBkey();
 4154+ $titleText = $title->getText();
 4155+
 4156+ // generate all variants of the link title text
 4157+ $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText);
 4158+
 4159+ // if link was not found (in first query), add all variants to query
 4160+ if ( !isset($colours[$pdbk]) ){
 4161+ foreach($allTextVariants as $textVariant){
 4162+ if($textVariant != $titleText){
 4163+ $variantTitle = Title::makeTitle( $ns, $textVariant );
 4164+ if(is_null($variantTitle)) continue;
 4165+ $linkBatch->addObj( $variantTitle );
 4166+ $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
 4167+ }
 4168+ }
 4169+ }
 4170+ }
 4171+
 4172+ // process categories, check if a category exists in some variant
 4173+ foreach( $categories as $category ){
 4174+ $variants = $wgContLang->convertLinkToAllVariants($category);
 4175+ foreach($variants as $variant){
 4176+ if($variant != $category){
 4177+ $variantTitle = Title::newFromDBkey( Title::makeName(NS_CATEGORY,$variant) );
 4178+ if(is_null($variantTitle)) continue;
 4179+ $linkBatch->addObj( $variantTitle );
 4180+ $categoryMap[$variant] = $category;
 4181+ }
 4182+ }
 4183+ }
 4184+
 4185+
 4186+ if(!$linkBatch->isEmpty()){
 4187+ // construct query
 4188+ $titleClause = $linkBatch->constructSet('page', $dbr);
 4189+
 4190+ $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len";
 4191+
 4192+ $variantQuery .= " FROM $page WHERE $titleClause";
 4193+ if ( $options & RLH_FOR_UPDATE ) {
 4194+ $variantQuery .= ' FOR UPDATE';
 4195+ }
 4196+
 4197+ $varRes = $dbr->query( $variantQuery, $fname );
 4198+
 4199+ // for each found variants, figure out link holders and replace
 4200+ while ( $s = $dbr->fetchObject($varRes) ) {
 4201+
 4202+ $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title );
 4203+ $varPdbk = $variantTitle->getPrefixedDBkey();
 4204+ $vardbk = $variantTitle->getDBkey();
 4205+
 4206+ $holderKeys = array();
 4207+ if(isset($variantMap[$varPdbk])){
 4208+ $holderKeys = $variantMap[$varPdbk];
 4209+ $linkCache->addGoodLinkObj( $s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect );
 4210+ $this->mOutput->addLink( $variantTitle, $s->page_id );
 4211+ }
 4212+
 4213+ // loop over link holders
 4214+ foreach($holderKeys as $key){
 4215+ $title = $this->mLinkHolders['titles'][$key];
 4216+ if ( is_null( $title ) ) continue;
 4217+
 4218+ $pdbk = $title->getPrefixedDBkey();
 4219+
 4220+ if(!isset($colours[$pdbk])){
 4221+ // found link in some of the variants, replace the link holder data
 4222+ $this->mLinkHolders['titles'][$key] = $variantTitle;
 4223+ $this->mLinkHolders['dbkeys'][$key] = $variantTitle->getDBkey();
 4224+
 4225+ // set pdbk and colour
 4226+ $pdbks[$key] = $varPdbk;
 4227+ $colours[$varPdbk] = $sk->getLinkColour( $variantTitle, $threshold );
 4228+ $linkcolour_ids[$s->page_id] = $pdbk;
 4229+ }
 4230+ wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) );
 4231+ }
 4232+
 4233+ // check if the object is a variant of a category
 4234+ if(isset($categoryMap[$vardbk])){
 4235+ $oldkey = $categoryMap[$vardbk];
 4236+ if($oldkey != $vardbk)
 4237+ $varCategories[$oldkey]=$vardbk;
 4238+ }
 4239+ }
 4240+
 4241+ // rebuild the categories in original order (if there are replacements)
 4242+ if(count($varCategories)>0){
 4243+ $newCats = array();
 4244+ $originalCats = $this->mOutput->getCategories();
 4245+ foreach($originalCats as $cat => $sortkey){
 4246+ // make the replacement
 4247+ if( array_key_exists($cat,$varCategories) )
 4248+ $newCats[$varCategories[$cat]] = $sortkey;
 4249+ else $newCats[$cat] = $sortkey;
 4250+ }
 4251+ $this->mOutput->setCategoryLinks($newCats);
 4252+ }
 4253+ }
 4254+ }
 4255+
 4256+ # Construct search and replace arrays
 4257+ wfProfileIn( $fname.'-construct' );
 4258+ $replacePairs = array();
 4259+ foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) {
 4260+ $pdbk = $pdbks[$key];
 4261+ $searchkey = "<!--LINK $key-->";
 4262+ $title = $this->mLinkHolders['titles'][$key];
 4263+ if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] == 'new' ) {
 4264+ $linkCache->addBadLinkObj( $title );
 4265+ $colours[$pdbk] = 'new';
 4266+ $this->mOutput->addLink( $title, 0 );
 4267+ $replacePairs[$searchkey] = $sk->makeBrokenLinkObj( $title,
 4268+ $this->mLinkHolders['texts'][$key],
 4269+ $this->mLinkHolders['queries'][$key] );
 4270+ } else {
 4271+ $replacePairs[$searchkey] = $sk->makeColouredLinkObj( $title, $colours[$pdbk],
 4272+ $this->mLinkHolders['texts'][$key],
 4273+ $this->mLinkHolders['queries'][$key] );
 4274+ }
 4275+ }
 4276+ $replacer = new HashtableReplacer( $replacePairs, 1 );
 4277+ wfProfileOut( $fname.'-construct' );
 4278+
 4279+ # Do the thing
 4280+ wfProfileIn( $fname.'-replace' );
 4281+ $text = preg_replace_callback(
 4282+ '/(<!--LINK .*?-->)/',
 4283+ $replacer->cb(),
 4284+ $text);
 4285+
 4286+ wfProfileOut( $fname.'-replace' );
 4287+ }
 4288+
 4289+ # Now process interwiki link holders
 4290+ # This is quite a bit simpler than internal links
 4291+ if ( !empty( $this->mInterwikiLinkHolders['texts'] ) ) {
 4292+ wfProfileIn( $fname.'-interwiki' );
 4293+ # Make interwiki link HTML
 4294+ $replacePairs = array();
 4295+ foreach( $this->mInterwikiLinkHolders['texts'] as $key => $link ) {
 4296+ $title = $this->mInterwikiLinkHolders['titles'][$key];
 4297+ $replacePairs[$key] = $sk->link( $title, $link );
 4298+ }
 4299+ $replacer = new HashtableReplacer( $replacePairs, 1 );
 4300+
 4301+ $text = preg_replace_callback(
 4302+ '/<!--IWLINK (.*?)-->/',
 4303+ $replacer->cb(),
 4304+ $text );
 4305+ wfProfileOut( $fname.'-interwiki' );
 4306+ }
 4307+
 4308+ wfProfileOut( $fname );
 4309+ return $colours;
40334310 }
40344311
40354312 /**
@@ -4038,10 +4315,39 @@
40394316 * @return string
40404317 */
40414318 function replaceLinkHoldersText( $text ) {
4042 - return $this->mLinkHolders->replaceText( $text );
 4319+ $fname = 'Parser::replaceLinkHoldersText';
 4320+ wfProfileIn( $fname );
 4321+
 4322+ $text = preg_replace_callback(
 4323+ '/<!--(LINK|IWLINK) (.*?)-->/',
 4324+ array( &$this, 'replaceLinkHoldersTextCallback' ),
 4325+ $text );
 4326+
 4327+ wfProfileOut( $fname );
 4328+ return $text;
40434329 }
40444330
40454331 /**
 4332+ * @param array $matches
 4333+ * @return string
 4334+ * @private
 4335+ */
 4336+ function replaceLinkHoldersTextCallback( $matches ) {
 4337+ $type = $matches[1];
 4338+ $key = $matches[2];
 4339+ if( $type == 'LINK' ) {
 4340+ if( isset( $this->mLinkHolders['texts'][$key] ) ) {
 4341+ return $this->mLinkHolders['texts'][$key];
 4342+ }
 4343+ } elseif( $type == 'IWLINK' ) {
 4344+ if( isset( $this->mInterwikiLinkHolders['texts'][$key] ) ) {
 4345+ return $this->mInterwikiLinkHolders['texts'][$key];
 4346+ }
 4347+ }
 4348+ return $matches[0];
 4349+ }
 4350+
 4351+ /**
40464352 * Tag hook handler for 'pre'.
40474353 */
40484354 function renderPreTag( $text, $attribs ) {
@@ -4092,7 +4398,7 @@
40934399
40944400 wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) );
40954401
4096 - $lines = StringUtils::explode( "\n", $text );
 4402+ $lines = explode( "\n", $text );
40974403 foreach ( $lines as $line ) {
40984404 # match lines like these:
40994405 # Image:someimage.jpg|This is some image
@@ -4105,7 +4411,7 @@
41064412
41074413 if ( strpos( $matches[0], '%' ) !== false )
41084414 $matches[1] = urldecode( $matches[1] );
4109 - $tp = Title::newFromText( $matches[1], NS_IMAGE );
 4415+ $tp = Title::newFromText( $matches[1] );
41104416 $nt =& $tp;
41114417 if( is_null( $nt ) ) {
41124418 # Bogus title. Ignore these so we don't bomb out later.
@@ -4171,11 +4477,8 @@
41724478
41734479 /**
41744480 * Parse image options text and use it to make an image
4175 - * @param Title $title
4176 - * @param string $options
4177 - * @param LinkHolderArray $holders
41784481 */
4179 - function makeImage( $title, $options, $holders = false ) {
 4482+ function makeImage( $title, $options ) {
41804483 # Check if the options text is of the form "options|alt text"
41814484 # Options are:
41824485 # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang
@@ -4198,7 +4501,7 @@
41994502 # * bottom
42004503 # * text-bottom
42014504
4202 - $parts = StringUtils::explode( "|", $options );
 4505+ $parts = array_map( 'trim', explode( '|', $options) );
42034506 $sk = $this->mOptions->getSkin();
42044507
42054508 # Give extensions a chance to select the file revision for us
@@ -4285,13 +4588,7 @@
42864589 }
42874590
42884591 # Strip bad stuff out of the alt text
4289 - # We can't just use replaceLinkHoldersText() here, because if this function
4290 - # is called from replaceInternalLinks2(), mLinkHolders won't be up to date.
4291 - if ( $holders ) {
4292 - $alt = $holders->replaceText( $caption );
4293 - } else {
4294 - $alt = $this->replaceLinkHoldersText( $caption );
4295 - }
 4592+ $alt = $this->replaceLinkHoldersText( $caption );
42964593
42974594 # make sure there are no placeholders in thumbnail attributes
42984595 # that are later expanded to html- so expand them now and
Index: trunk/phase3/includes/Title.php
@@ -10,6 +10,12 @@
1111
1212 define ( 'GAID_FOR_UPDATE', 1 );
1313
 14+/**
 15+ * Title::newFromText maintains a cache to avoid expensive re-normalization of
 16+ * commonly used titles. On a batch operation this can become a memory leak
 17+ * if not bounded. After hitting this many titles reset the cache.
 18+ */
 19+define( 'MW_TITLECACHE_MAX', 1000 );
1420
1521 /**
1622 * Constants for pr_cascade bitfield
@@ -30,14 +36,6 @@
3137 //@}
3238
3339 /**
34 - * Title::newFromText maintains a cache to avoid expensive re-normalization of
35 - * commonly used titles. On a batch operation this can become a memory leak
36 - * if not bounded. After hitting this many titles reset the cache.
37 - */
38 - const CACHE_MAX = 1000;
39 -
40 -
41 - /**
4240 * @name Private member variables
4341 * Please use the accessor functions instead.
4442 * @private
@@ -133,7 +131,7 @@
134132 static $cachedcount = 0 ;
135133 if( $t->secureAndSplit() ) {
136134 if( $defaultNamespace == NS_MAIN ) {
137 - if( $cachedcount >= self::CACHE_MAX ) {
 135+ if( $cachedcount >= MW_TITLECACHE_MAX ) {
138136 # Avoid memory leaks on mass operations...
139137 Title::$titleCache = array();
140138 $cachedcount=0;
Index: trunk/phase3/includes/AutoLoader.php
@@ -66,7 +66,6 @@
6767 'EnotifNotifyJob' => 'includes/EnotifNotifyJob.php',
6868 'ErrorPageError' => 'includes/Exception.php',
6969 'Exif' => 'includes/Exif.php',
70 - 'ExplodeIterator' => 'includes/StringUtils.php',
7170 'ExternalEdit' => 'includes/ExternalEdit.php',
7271 'ExternalStoreDB' => 'includes/ExternalStoreDB.php',
7372 'ExternalStoreHttp' => 'includes/ExternalStoreHttp.php',
@@ -357,7 +356,6 @@
358357 # includes/parser
359358 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
360359 'DateFormatter' => 'includes/parser/DateFormatter.php',
361 - 'LinkHolderArray' => 'includes/parser/LinkHolderArray.php',
362360 'OnlyIncludeReplacer' => 'includes/parser/Parser.php',
363361 'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php',
364362 'PPDPart' => 'includes/parser/Preprocessor_DOM.php',
Index: trunk/phase3/includes/StringUtils.php
@@ -167,18 +167,6 @@
168168 $string = str_replace( '$', '\\$', $string );
169169 return $string;
170170 }
171 -
172 - /**
173 - * Workalike for explode() with limited memory usage.
174 - * Returns an Iterator
175 - */
176 - static function explode( $separator, $subject ) {
177 - if ( substr_count( $subject, $separator ) > 1000 ) {
178 - return new ExplodeIterator( $separator, $subject );
179 - } else {
180 - return new ArrayIterator( explode( $separator, $subject ) );
181 - }
182 - }
183171 }
184172
185173 /**
@@ -322,90 +310,3 @@
323311 return $result;
324312 }
325313 }
326 -
327 -/**
328 - * An iterator which works exactly like:
329 - *
330 - * foreach ( explode( $delim, $s ) as $element ) {
331 - * ...
332 - * }
333 - *
334 - * Except it doesn't use 193 byte per element
335 - */
336 -class ExplodeIterator implements Iterator {
337 - // The subject string
338 - var $subject, $subjectLength;
339 -
340 - // The delimiter
341 - var $delim, $delimLength;
342 -
343 - // The position of the start of the line
344 - var $curPos;
345 -
346 - // The position after the end of the next delimiter
347 - var $endPos;
348 -
349 - // The current token
350 - var $current;
351 -
352 - /**
353 - * Construct a DelimIterator
354 - */
355 - function __construct( $delim, $s ) {
356 - $this->subject = $s;
357 - $this->delim = $delim;
358 -
359 - // Micro-optimisation (theoretical)
360 - $this->subjectLength = strlen( $s );
361 - $this->delimLength = strlen( $delim );
362 -
363 - $this->rewind();
364 - }
365 -
366 - function rewind() {
367 - $this->curPos = 0;
368 - $this->endPos = strpos( $this->subject, $this->delim );
369 - $this->refreshCurrent();
370 - }
371 -
372 -
373 - function refreshCurrent() {
374 - if ( $this->curPos === false ) {
375 - $this->current = false;
376 - } elseif ( $this->curPos >= $this->subjectLength ) {
377 - $this->current = '';
378 - } elseif ( $this->endPos === false ) {
379 - $this->current = substr( $this->subject, $this->curPos );
380 - } else {
381 - $this->current = substr( $this->subject, $this->curPos, $this->endPos - $this->curPos );
382 - }
383 - }
384 -
385 - function current() {
386 - return $this->current;
387 - }
388 -
389 - function key() {
390 - return $this->curPos;
391 - }
392 -
393 - function next() {
394 - if ( $this->endPos === false ) {
395 - $this->curPos = false;
396 - } else {
397 - $this->curPos = $this->endPos + $this->delimLength;
398 - if ( $this->curPos >= $this->subjectLength ) {
399 - $this->endPos = false;
400 - } else {
401 - $this->endPos = strpos( $this->subject, $this->delim, $this->curPos );
402 - }
403 - }
404 - $this->refreshCurrent();
405 - return $this->current;
406 - }
407 -
408 - function valid() {
409 - return $this->curPos !== false;
410 - }
411 -}
412 -
Index: trunk/phase3/includes/MessageCache.php
@@ -44,6 +44,7 @@
4545
4646 /**
4747 * ParserOptions is lazy initialised.
 48+ * Access should probably be protected.
4849 */
4950 function getParserOptions() {
5051 if ( !$this->mParserOptions ) {
Index: trunk/phase3/RELEASE-NOTES
@@ -130,7 +130,6 @@
131131 gives results
132132 * Avoid recursive crazy expansions in section edit comments for pages which
133133 contain '/*' in the title
134 -* Fix excessive memory usage when parsing pages with lots of links
135134
136135 === API changes in 1.14 ===
137136

Follow-up revisions

RevisionCommit summaryAuthorDate
r39949* Revert revert r39662 of my parser changes....tstarling16:08, 25 August 2008

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r39414* In the parser: do link existence tests in batches of 1000. Avoids using exc...tstarling16:35, 15 August 2008

Status & tagging log