r39949 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r39948‎ | r39949 | r39950 >
Date:16:08, 25 August 2008
Author:tstarling
Status:old
Tags:
Comment:
* Revert revert r39662 of my parser changes.
* Fixed image link whitespace handling (Brion's complaint, r39662)
* Added fuzz test capability to parserTests.php
* Added __destruct() functions to Parser and Language, and called them explicitly from parserTests.inc, to avoid unconstrained memory usage during fuzz testing.
* Added unified diff to output of Parser_DiffTest
* Fixed whitespace change in Parser::doTableStuff() (found by fuzzing)
* Added feature to RELEASE-NOTES which I'd committed last time but forgotten to note: <gallery> will accept image names with no "Image:" prefix (rediscovered by fuzzing)
* Limit memory usage in Title::getInterwikiLink()
* Fixed chronic fail of all interwiki link parser tests (hid Siebrand's complaint, r39464)
* Fixed chronic fail of one of the LanguageConverter parser tests. Was actually an ignored bug.
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/Title.php (modified) (history)
  • /trunk/phase3/includes/parser/LinkHolderArray.php (modified) (history)
  • /trunk/phase3/includes/parser/Parser.php (modified) (history)
  • /trunk/phase3/includes/parser/Parser_DiffTest.php (modified) (history)
  • /trunk/phase3/languages/Language.php (modified) (history)
  • /trunk/phase3/languages/LanguageConverter.php (modified) (history)
  • /trunk/phase3/maintenance/parserTests.inc (modified) (history)
  • /trunk/phase3/maintenance/parserTests.php (modified) (history)
  • /trunk/phase3/maintenance/parserTestsStaticParserHook.php (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/parserTests.inc
@@ -26,7 +26,7 @@
2727
2828 /** */
2929 $options = array( 'quick', 'color', 'quiet', 'help', 'show-output', 'record' );
30 -$optionsWithArgs = array( 'regex' );
 30+$optionsWithArgs = array( 'regex', 'seed' );
3131
3232 require_once( 'commandLine.inc' );
3333 require_once( "$IP/maintenance/parserTestsParserHook.php" );
@@ -62,6 +62,11 @@
6363 */
6464 private $oldTablePrefix;
6565
 66+ private $maxFuzzHairLength = 20;
 67+ private $maxFuzzTestLength = 1000;
 68+ private $fuzzSeed = 0;
 69+ private $memoryLimit = 50;
 70+
6671 /**
6772 * Sets terminal colorization and diff/quick modes depending on OS and
6873 * command-line options (--color and --quick).
@@ -117,6 +122,10 @@
118123 }
119124 $this->keepUploads = isset( $options['keep-uploads'] );
120125
 126+ if ( isset( $options['seed'] ) ) {
 127+ $this->fuzzSeed = intval( $options['seed'] ) - 1;
 128+ }
 129+
121130 $this->hooks = array();
122131 $this->functionHooks = array();
123132 }
@@ -134,6 +143,116 @@
135144 }
136145
137146 /**
 147+ * Run a fuzz test series
 148+ * Draw input from a set of test files
 149+ */
 150+ function fuzzTest( $filenames ) {
 151+ $dict = $this->getFuzzInput( $filenames );
 152+ $this->setupDatabase();
 153+ ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
 154+
 155+ $numTotal = 0;
 156+ $numSuccess = 0;
 157+ $user = new User;
 158+ $opts = ParserOptions::newFromUser( $user );
 159+ $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
 160+
 161+ while ( true ) {
 162+ // Generate test input
 163+ mt_srand( ++$this->fuzzSeed );
 164+ $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
 165+ $input = '';
 166+ while ( strlen( $input ) < $totalLength ) {
 167+ $hairLength = mt_rand( 1, $this->maxFuzzHairLength );
 168+ $offset = mt_rand( 0, strlen( $dict ) - $hairLength );
 169+ $input .= substr( $dict, $offset, $hairLength );
 170+ }
 171+
 172+ $this->setupGlobals();
 173+ $parser = $this->getParser();
 174+ // Run the test
 175+ try {
 176+ $parser->parse( $input, $title, $opts );
 177+ $fail = false;
 178+ } catch ( Exception $exception ) {
 179+ $fail = true;
 180+ }
 181+
 182+ if ( $fail ) {
 183+ echo "Test failed with seed {$this->fuzzSeed}\n";
 184+ echo "Input:\n";
 185+ var_dump( $input );
 186+ echo "\n\n";
 187+ echo "$exception\n";
 188+ } else {
 189+ $numSuccess++;
 190+ }
 191+ $numTotal++;
 192+ $this->teardownGlobals();
 193+ $parser->__destruct();
 194+
 195+ if ( $numTotal % 100 == 0 ) {
 196+ $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
 197+ echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
 198+ if ( $usage > 90 ) {
 199+ echo "Out of memory:\n";
 200+ $memStats = $this->getMemoryBreakdown();
 201+ foreach ( $memStats as $name => $usage ) {
 202+ echo "$name: $usage\n";
 203+ }
 204+ $this->abort();
 205+ }
 206+ }
 207+ }
 208+ }
 209+
 210+ /**
 211+ * Get an input dictionary from a set of parser test files
 212+ */
 213+ function getFuzzInput( $filenames ) {
 214+ $dict = '';
 215+ foreach( $filenames as $filename ) {
 216+ $contents = file_get_contents( $filename );
 217+ preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches );
 218+ foreach ( $matches[1] as $match ) {
 219+ $dict .= $match . "\n";
 220+ }
 221+ }
 222+ return $dict;
 223+ }
 224+
 225+ /**
 226+ * Get a memory usage breakdown
 227+ */
 228+ function getMemoryBreakdown() {
 229+ $memStats = array();
 230+ foreach ( $GLOBALS as $name => $value ) {
 231+ $memStats['$'.$name] = strlen( serialize( $value ) );
 232+ }
 233+ $classes = get_declared_classes();
 234+ foreach ( $classes as $class ) {
 235+ $rc = new ReflectionClass( $class );
 236+ $props = $rc->getStaticProperties();
 237+ $memStats[$class] = strlen( serialize( $props ) );
 238+ $methods = $rc->getMethods();
 239+ foreach ( $methods as $method ) {
 240+ $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
 241+ }
 242+ }
 243+ $functions = get_defined_functions();
 244+ foreach ( $functions['user'] as $function ) {
 245+ $rf = new ReflectionFunction( $function );
 246+ $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
 247+ }
 248+ asort( $memStats );
 249+ return $memStats;
 250+ }
 251+
 252+ function abort() {
 253+ $this->abort();
 254+ }
 255+
 256+ /**
138257 * Run a series of tests listed in the given text files.
139258 * Each test consists of a brief description, wikitext input,
140259 * and the expected HTML output.
@@ -267,6 +386,24 @@
268387 }
269388
270389 /**
 390+ * Get a Parser object
 391+ */
 392+ function getParser() {
 393+ global $wgParserConf;
 394+ $class = $wgParserConf['class'];
 395+ $parser = new $class( $wgParserConf );
 396+ foreach( $this->hooks as $tag => $callback ) {
 397+ $parser->setHook( $tag, $callback );
 398+ }
 399+ foreach( $this->functionHooks as $tag => $bits ) {
 400+ list( $callback, $flags ) = $bits;
 401+ $parser->setFunctionHook( $tag, $callback, $flags );
 402+ }
 403+ wfRunHooks( 'ParserTestParser', array( &$parser ) );
 404+ return $parser;
 405+ }
 406+
 407+ /**
271408 * Run a given wikitext input through a freshly-constructed wiki parser,
272409 * and compare the output against the expected results.
273410 * Prints status and explanatory messages to stdout.
@@ -276,7 +413,6 @@
277414 * @return bool
278415 */
279416 private function runTest( $desc, $input, $result, $opts ) {
280 - global $wgParserConf;
281417 if( $this->showProgress ) {
282418 $this->showTesting( $desc );
283419 }
@@ -300,18 +436,7 @@
301437 }
302438
303439 $noxml = (bool)preg_match( '~\\b noxml \\b~x', $opts );
304 -
305 - $class = $wgParserConf['class'];
306 - $parser = new $class( $wgParserConf );
307 - foreach( $this->hooks as $tag => $callback ) {
308 - $parser->setHook( $tag, $callback );
309 - }
310 - foreach( $this->functionHooks as $tag => $bits ) {
311 - list( $callback, $flags ) = $bits;
312 - $parser->setFunctionHook( $tag, $callback, $flags );
313 - }
314 - wfRunHooks( 'ParserTestParser', array( &$parser ) );
315 -
 440+ $parser = $this->getParser();
316441 $title =& Title::makeTitle( NS_MAIN, $titleText );
317442
318443 $matches = array();
@@ -441,6 +566,7 @@
442567 $langObj = Language::factory( $lang );
443568 $GLOBALS['wgLang'] = $langObj;
444569 $GLOBALS['wgContLang'] = $langObj;
 570+ $GLOBALS['wgMemc'] = new FakeMemCachedClient;
445571
446572 //$GLOBALS['wgMessageCache'] = new MessageCache( new BagOStuff(), false, 0, $GLOBALS['wgDBname'] );
447573
@@ -551,10 +677,10 @@
552678 # Hack: insert a few Wikipedia in-project interwiki prefixes,
553679 # for testing inter-language links
554680 $db->insert( 'interwiki', array(
555 - array( 'iw_prefix' => 'Wikipedia',
 681+ array( 'iw_prefix' => 'wikipedia',
556682 'iw_url' => 'http://en.wikipedia.org/wiki/$1',
557683 'iw_local' => 0 ),
558 - array( 'iw_prefix' => 'MeatBall',
 684+ array( 'iw_prefix' => 'meatball',
559685 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
560686 'iw_local' => 0 ),
561687 array( 'iw_prefix' => 'zh',
@@ -621,11 +747,12 @@
622748 return;
623749 }
624750
 751+ /*
625752 $tables = $this->listTables();
626753 $db = wfGetDB( DB_MASTER );
627754 foreach ( $tables as $table ) {
628755 $db->query( "DROP TABLE `parsertest_$table`" );
629 - }
 756+ }*/
630757 }
631758
632759 /**
@@ -645,6 +772,10 @@
646773 }
647774
648775 wfDebug( "Creating upload directory $dir\n" );
 776+ if ( file_exists( $dir ) ) {
 777+ wfDebug( "Already exists!\n" );
 778+ return $dir;
 779+ }
649780 mkdir( $dir );
650781 mkdir( $dir . '/3' );
651782 mkdir( $dir . '/3/3a' );
@@ -658,6 +789,8 @@
659790 */
660791 private function teardownGlobals() {
661792 RepoGroup::destroySingleton();
 793+ LinkCache::singleton()->clear();
 794+ $GLOBALS['wgLang']->__destruct();
662795 foreach( $this->savedGlobals as $var => $val ) {
663796 $GLOBALS[$var] = $val;
664797 }
Index: trunk/phase3/maintenance/parserTests.php
@@ -28,22 +28,21 @@
2929 if( isset( $options['help'] ) ) {
3030 echo <<<ENDS
3131 MediaWiki $wgVersion parser test suite
32 -Usage: php parserTests.php [--quick] [--quiet] [--show-output]
33 - [--color[=(yes|no)]]
34 - [--regex=<expression>] [--file=<testfile>]
35 - [--record] [--compare]
36 - [--help]
 32+Usage: php parserTests.php [options...]
 33+
3734 Options:
3835 --quick Suppress diff output of failed tests
3936 --quiet Suppress notification of passed tests (shows only failed tests)
4037 --show-output Show expected and actual output
41 - --color Override terminal detection and force color output on or off
 38+ --color[=yes|no] Override terminal detection and force color output on or off
4239 use wgCommandLineDarkBg = true; if your term is dark
4340 --regex Only run tests whose descriptions which match given regex
44 - --file Run test cases from a custom file instead of parserTests.txt
 41+ --file=<testfile> Run test cases from a custom file instead of parserTests.txt
4542 --record Record tests in database
4643 --compare Compare with recorded results, without updating the database.
4744 --keep-uploads Re-use the same upload directory for each test, don't delete it
 45+ --fuzz Do a fuzz test instead of a normal test
 46+ --seed <n> Start the fuzz test from the specified seed
4847 --help Show this help message
4948
5049
@@ -67,7 +66,10 @@
6867 # Print out software version to assist with locating regressions
6968 $version = SpecialVersion::getVersion();
7069 echo( "This is MediaWiki version {$version}.\n\n" );
71 -$ok = $tester->runTestsFromFiles( $files );
7270
73 -exit ($ok ? 0 : -1);
74 -
 71+if ( isset( $options['fuzz'] ) ) {
 72+ $tester->fuzzTest( $files );
 73+} else {
 74+ $ok = $tester->runTestsFromFiles( $files );
 75+ exit ($ok ? 0 : -1);
 76+}
Index: trunk/phase3/maintenance/parserTestsStaticParserHook.php
@@ -27,18 +27,19 @@
2828 if ( ! count( $argv ) ) {
2929 $buf = $in;
3030 return '';
31 - } else if ( count( $argv ) === 1 && $argv['action'] === 'flush' && $in === null ) {
 31+ } else if ( count( $argv ) === 1 && isset( $argv['action'] )
 32+ && $argv['action'] === 'flush' && $in === null )
 33+ {
3234 // Clear the buffer, we probably don't need to
3335 $tmp = $buf;
3436 $buf = null;
3537 return $tmp;
3638 } else
3739 // wtf?
38 - die(
 40+ return
3941 "\nCall this extension as <statictag>string</statictag> or as" .
4042 " <statictag action=flush/>, not in any other way.\n" .
4143 "text: " . var_export( $in, true ) . "\n" .
42 - "argv: " . var_export( $argv, true ) . "\n"
43 - );
 44+ "argv: " . var_export( $argv, true ) . "\n";
4445 }
4546
Index: trunk/phase3/includes/parser/LinkHolderArray.php
@@ -12,6 +12,15 @@
1313 }
1414
1515 /**
 16+ * Reduce memory usage to reduce the impact of circular references
 17+ */
 18+ function __destruct() {
 19+ foreach ( $this as $name => $value ) {
 20+ unset( $this->$name );
 21+ }
 22+ }
 23+
 24+ /**
1625 * Merge another LinkHolderArray into this one
1726 */
1827 function merge( $other ) {
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;
 102+ var $mLinkHolders, $mLinkID;
103103 var $mIncludeSizes, $mPPNodeCount, $mDefaultSort;
104104 var $mTplExpandCache; // empty-frame expansion cache
105105 var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
@@ -143,6 +143,18 @@
144144 }
145145
146146 /**
 147+ * Reduce memory usage to reduce the impact of circular references
 148+ */
 149+ function __destruct() {
 150+ if ( isset( $this->mLinkHolders ) ) {
 151+ $this->mLinkHolders->__destruct();
 152+ }
 153+ foreach ( $this as $name => $value ) {
 154+ unset( $this->$name );
 155+ }
 156+ }
 157+
 158+ /**
147159 * Do various kinds of initialisation on the first call of the parser
148160 */
149161 function firstCallInit() {
@@ -179,17 +191,8 @@
180192 $this->mStripState = new StripState;
181193 $this->mArgStack = false;
182194 $this->mInPre = false;
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 - );
 195+ $this->mLinkHolders = new LinkHolderArray( $this );
 196+ $this->mLinkID = 0;
194197 $this->mRevisionTimestamp = $this->mRevisionId = null;
195198
196199 /**
@@ -204,7 +207,7 @@
205208 */
206209 #$this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString();
207210 # Changed to \x7f to allow XML double-parsing -- TS
208 - $this->mUniqPrefix = "\x7fUNIQ" . Parser::getRandomString();
 211+ $this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString();
209212
210213
211214 # Clear these on every parse, bug 4549
@@ -294,7 +297,7 @@
295298 */
296299
297300 global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang;
298 - $fname = 'Parser::parse-' . wfGetCaller();
 301+ $fname = __METHOD__.'-' . wfGetCaller();
299302 wfProfileIn( __METHOD__ );
300303 wfProfileIn( $fname );
301304
@@ -328,7 +331,6 @@
329332 );
330333 $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text );
331334
332 - # only once and last
333335 $text = $this->doBlockLevels( $text, $linestart );
334336
335337 $this->replaceLinkHolders( $text );
@@ -348,7 +350,7 @@
349351 $uniq_prefix = $this->mUniqPrefix;
350352 $matches = array();
351353 $elements = array_keys( $this->mTransparentTagHooks );
352 - $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
 354+ $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
353355
354356 foreach( $matches as $marker => $data ) {
355357 list( $element, $content, $params, $tag ) = $data;
@@ -366,7 +368,7 @@
367369 $text = Sanitizer::normalizeCharReferences( $text );
368370
369371 if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) {
370 - $text = Parser::tidy($text);
 372+ $text = self::tidy($text);
371373 } else {
372374 # attempt to sanitize at least some nesting problems
373375 # (bug #2702 and quite a few others)
@@ -471,6 +473,8 @@
472474 function &getTitle() { return $this->mTitle; }
473475 function getOptions() { return $this->mOptions; }
474476 function getRevisionId() { return $this->mRevisionId; }
 477+ function getOutput() { return $this->mOutput; }
 478+ function nextLinkID() { return $this->mLinkID++; }
475479
476480 function getFunctionLang() {
477481 global $wgLang, $wgContLang;
@@ -549,7 +553,7 @@
550554 $text = $inside;
551555 $tail = null;
552556 } else {
553 - if( $element == '!--' ) {
 557+ if( $element === '!--' ) {
554558 $end = '/(-->)/';
555559 } else {
556560 $end = "/(<\\/$element\\s*>)/i";
@@ -658,9 +662,9 @@
659663 ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>'.
660664 '<head><title>test</title></head><body>'.$text.'</body></html>';
661665 if( $wgTidyInternal ) {
662 - $correctedtext = Parser::internalTidy( $wrappedtext );
 666+ $correctedtext = self::internalTidy( $wrappedtext );
663667 } else {
664 - $correctedtext = Parser::externalTidy( $wrappedtext );
 668+ $correctedtext = self::externalTidy( $wrappedtext );
665669 }
666670 if( is_null( $correctedtext ) ) {
667671 wfDebug( "Tidy error detected!\n" );
@@ -677,8 +681,7 @@
678682 */
679683 function externalTidy( $text ) {
680684 global $wgTidyConf, $wgTidyBin, $wgTidyOpts;
681 - $fname = 'Parser::externalTidy';
682 - wfProfileIn( $fname );
 685+ wfProfileIn( __METHOD__ );
683686
684687 $cleansource = '';
685688 $opts = ' -utf8';
@@ -707,7 +710,7 @@
708711 }
709712 }
710713
711 - wfProfileOut( $fname );
 714+ wfProfileOut( __METHOD__ );
712715
713716 if( $cleansource == '' && $text != '') {
714717 // Some kind of error happened, so we couldn't get the corrected text.
@@ -729,8 +732,7 @@
730733 */
731734 function internalTidy( $text ) {
732735 global $wgTidyConf, $IP, $wgDebugTidy;
733 - $fname = 'Parser::internalTidy';
734 - wfProfileIn( $fname );
 736+ wfProfileIn( __METHOD__ );
735737
736738 $tidy = new tidy;
737739 $tidy->parseString( $text, $wgTidyConf, 'utf8' );
@@ -748,7 +750,7 @@
749751 "\n-->";
750752 }
751753
752 - wfProfileOut( $fname );
 754+ wfProfileOut( __METHOD__ );
753755 return $cleansource;
754756 }
755757
@@ -758,34 +760,35 @@
759761 * @private
760762 */
761763 function doTableStuff ( $text ) {
762 - $fname = 'Parser::doTableStuff';
763 - wfProfileIn( $fname );
 764+ wfProfileIn( __METHOD__ );
764765
765 - $lines = explode ( "\n" , $text );
 766+ $lines = StringUtils::explode( "\n", $text );
 767+ $out = '';
766768 $td_history = array (); // Is currently a td tag open?
767769 $last_tag_history = array (); // Save history of last lag activated (td, th or caption)
768770 $tr_history = array (); // Is currently a tr tag open?
769771 $tr_attributes = array (); // history of tr attributes
770772 $has_opened_tr = array(); // Did this table open a <tr> element?
771773 $indent_level = 0; // indent level of the table
772 - foreach ( $lines as $key => $line )
773 - {
774 - $line = trim ( $line );
775774
 775+ foreach ( $lines as $outLine ) {
 776+ $line = trim( $outLine );
 777+
776778 if( $line == '' ) { // empty line, go to next line
 779+ $out .= $outLine."\n";
777780 continue;
778781 }
779 - $first_character = $line{0};
 782+ $first_character = $line[0];
780783 $matches = array();
781784
782 - if ( preg_match( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) {
 785+ if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) {
783786 // First check if we are starting a new table
784787 $indent_level = strlen( $matches[1] );
785788
786789 $attributes = $this->mStripState->unstripBoth( $matches[2] );
787790 $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' );
788791
789 - $lines[$key] = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
 792+ $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
790793 array_push ( $td_history , false );
791794 array_push ( $last_tag_history , '' );
792795 array_push ( $tr_history , false );
@@ -793,8 +796,9 @@
794797 array_push ( $has_opened_tr , false );
795798 } else if ( count ( $td_history ) == 0 ) {
796799 // Don't do any of the following
 800+ $out .= $outLine."\n";
797801 continue;
798 - } else if ( substr ( $line , 0 , 2 ) == '|}' ) {
 802+ } else if ( substr ( $line , 0 , 2 ) === '|}' ) {
799803 // We are ending a table
800804 $line = '</table>' . substr ( $line , 2 );
801805 $last_tag = array_pop ( $last_tag_history );
@@ -811,8 +815,8 @@
812816 $line = "</{$last_tag}>{$line}";
813817 }
814818 array_pop ( $tr_attributes );
815 - $lines[$key] = $line . str_repeat( '</dd></dl>' , $indent_level );
816 - } else if ( substr ( $line , 0 , 2 ) == '|-' ) {
 819+ $outLine = $line . str_repeat( '</dd></dl>' , $indent_level );
 820+ } else if ( substr ( $line , 0 , 2 ) === '|-' ) {
817821 // Now we have a table row
818822 $line = preg_replace( '#^\|-+#', '', $line );
819823
@@ -835,21 +839,21 @@
836840 $line = "</{$last_tag}>{$line}";
837841 }
838842
839 - $lines[$key] = $line;
 843+ $outLine = $line;
840844 array_push ( $tr_history , false );
841845 array_push ( $td_history , false );
842846 array_push ( $last_tag_history , '' );
843847 }
844 - else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 ) == '|+' ) {
 848+ else if ( $first_character === '|' || $first_character === '!' || substr ( $line , 0 , 2 ) === '|+' ) {
845849 // This might be cell elements, td, th or captions
846 - if ( substr ( $line , 0 , 2 ) == '|+' ) {
 850+ if ( substr ( $line , 0 , 2 ) === '|+' ) {
847851 $first_character = '+';
848852 $line = substr ( $line , 1 );
849853 }
850854
851855 $line = substr ( $line , 1 );
852856
853 - if ( $first_character == '!' ) {
 857+ if ( $first_character === '!' ) {
854858 $line = str_replace ( '!!' , '||' , $line );
855859 }
856860
@@ -859,13 +863,13 @@
860864 // attribute values containing literal "||".
861865 $cells = StringUtils::explodeMarkup( '||' , $line );
862866
863 - $lines[$key] = '';
 867+ $outLine = '';
864868
865869 // Loop through each table cell
866870 foreach ( $cells as $cell )
867871 {
868872 $previous = '';
869 - if ( $first_character != '+' )
 873+ if ( $first_character !== '+' )
870874 {
871875 $tr_after = array_pop ( $tr_attributes );
872876 if ( !array_pop ( $tr_history ) ) {
@@ -883,11 +887,11 @@
884888 $previous = "</{$last_tag}>{$previous}";
885889 }
886890
887 - if ( $first_character == '|' ) {
 891+ if ( $first_character === '|' ) {
888892 $last_tag = 'td';
889 - } else if ( $first_character == '!' ) {
 893+ } else if ( $first_character === '!' ) {
890894 $last_tag = 'th';
891 - } else if ( $first_character == '+' ) {
 895+ } else if ( $first_character === '+' ) {
892896 $last_tag = 'caption';
893897 } else {
894898 $last_tag = '';
@@ -910,38 +914,42 @@
911915 $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
912916 }
913917
914 - $lines[$key] .= $cell;
 918+ $outLine .= $cell;
915919 array_push ( $td_history , true );
916920 }
917921 }
 922+ $out .= $outLine . "\n";
918923 }
919924
920925 // Closing open td, tr && table
921926 while ( count ( $td_history ) > 0 )
922927 {
923928 if ( array_pop ( $td_history ) ) {
924 - $lines[] = '</td>' ;
 929+ $out .= "</td>\n";
925930 }
926931 if ( array_pop ( $tr_history ) ) {
927 - $lines[] = '</tr>' ;
 932+ $out .= "</tr>\n";
928933 }
929934 if ( !array_pop ( $has_opened_tr ) ) {
930 - $lines[] = "<tr><td></td></tr>" ;
 935+ $out .= "<tr><td></td></tr>\n" ;
931936 }
932937
933 - $lines[] = '</table>' ;
 938+ $out .= "</table>\n";
934939 }
935940
936 - $output = implode ( "\n" , $lines ) ;
 941+ // Remove trailing line-ending (b/c)
 942+ if ( substr( $out, -1 ) === "\n" ) {
 943+ $out = substr( $out, 0, -1 );
 944+ }
937945
938946 // special case: don't return empty table
939 - if( $output == "<table>\n<tr><td></td></tr>\n</table>" ) {
940 - $output = '';
 947+ if( $out === "<table>\n<tr><td></td></tr>\n</table>" ) {
 948+ $out = '';
941949 }
942950
943 - wfProfileOut( $fname );
 951+ wfProfileOut( __METHOD__ );
944952
945 - return $output;
 953+ return $out;
946954 }
947955
948956 /**
@@ -952,12 +960,11 @@
953961 */
954962 function internalParse( $text ) {
955963 $isMain = true;
956 - $fname = 'Parser::internalParse';
957 - wfProfileIn( $fname );
 964+ wfProfileIn( __METHOD__ );
958965
959966 # Hook to suspend the parser in this state
960967 if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$this->mStripState ) ) ) {
961 - wfProfileOut( $fname );
 968+ wfProfileOut( __METHOD__ );
962969 return $text ;
963970 }
964971
@@ -990,14 +997,15 @@
991998 $text = $this->doMagicLinks( $text );
992999 $text = $this->formatHeadings( $text, $isMain );
9931000
994 - wfProfileOut( $fname );
 1001+ wfProfileOut( __METHOD__ );
9951002 return $text;
9961003 }
9971004
9981005 /**
9991006 * Replace special strings like "ISBN xxx" and "RFC xxx" with
10001007 * magic external links.
1001 - *
 1008+ *
 1009+ * DML
10021010 * @private
10031011 */
10041012 function doMagicLinks( $text ) {
@@ -1018,10 +1026,10 @@
10191027 }
10201028
10211029 function magicLinkCallback( $m ) {
1022 - if ( substr( $m[0], 0, 1 ) == '<' ) {
 1030+ if ( substr( $m[0], 0, 1 ) === '<' ) {
10231031 # Skip HTML element
10241032 return $m[0];
1025 - } elseif ( substr( $m[0], 0, 4 ) == 'ISBN' ) {
 1033+ } elseif ( substr( $m[0], 0, 4 ) === 'ISBN' ) {
10261034 $isbn = $m[2];
10271035 $num = strtr( $isbn, array(
10281036 '-' => '',
@@ -1033,11 +1041,11 @@
10341042 $titleObj->escapeLocalUrl() .
10351043 "\" class=\"internal\">ISBN $isbn</a>";
10361044 } else {
1037 - if ( substr( $m[0], 0, 3 ) == 'RFC' ) {
 1045+ if ( substr( $m[0], 0, 3 ) === 'RFC' ) {
10381046 $keyword = 'RFC';
10391047 $urlmsg = 'rfcurl';
10401048 $id = $m[1];
1041 - } elseif ( substr( $m[0], 0, 4 ) == 'PMID' ) {
 1049+ } elseif ( substr( $m[0], 0, 4 ) === 'PMID' ) {
10421050 $keyword = 'PMID';
10431051 $urlmsg = 'pubmedurl';
10441052 $id = $m[1];
@@ -1060,14 +1068,13 @@
10611069 * @private
10621070 */
10631071 function doHeadings( $text ) {
1064 - $fname = 'Parser::doHeadings';
1065 - wfProfileIn( $fname );
 1072+ wfProfileIn( __METHOD__ );
10661073 for ( $i = 6; $i >= 1; --$i ) {
10671074 $h = str_repeat( '=', $i );
10681075 $text = preg_replace( "/^$h(.+)$h\\s*$/m",
10691076 "<h$i>\\1</h$i>", $text );
10701077 }
1071 - wfProfileOut( $fname );
 1078+ wfProfileOut( __METHOD__ );
10721079 return $text;
10731080 }
10741081
@@ -1077,15 +1084,14 @@
10781085 * @return string the altered text
10791086 */
10801087 function doAllQuotes( $text ) {
1081 - $fname = 'Parser::doAllQuotes';
1082 - wfProfileIn( $fname );
 1088+ wfProfileIn( __METHOD__ );
10831089 $outtext = '';
1084 - $lines = explode( "\n", $text );
 1090+ $lines = StringUtils::explode( "\n", $text );
10851091 foreach ( $lines as $line ) {
1086 - $outtext .= $this->doQuotes ( $line ) . "\n";
 1092+ $outtext .= $this->doQuotes( $line ) . "\n";
10871093 }
10881094 $outtext = substr($outtext, 0,-1);
1089 - wfProfileOut( $fname );
 1095+ wfProfileOut( __METHOD__ );
10901096 return $outtext;
10911097 }
10921098
@@ -1147,9 +1153,9 @@
11481154 {
11491155 $x1 = substr ($arr[$i-1], -1);
11501156 $x2 = substr ($arr[$i-1], -2, 1);
1151 - if ($x1 == ' ') {
 1157+ if ($x1 === ' ') {
11521158 if ($firstspace == -1) $firstspace = $i;
1153 - } else if ($x2 == ' ') {
 1159+ } else if ($x2 === ' ') {
11541160 if ($firstsingleletterword == -1) $firstsingleletterword = $i;
11551161 } else {
11561162 if ($firstmultiletterword == -1) $firstmultiletterword = $i;
@@ -1189,7 +1195,7 @@
11901196 {
11911197 if (($i % 2) == 0)
11921198 {
1193 - if ($state == 'both')
 1199+ if ($state === 'both')
11941200 $buffer .= $r;
11951201 else
11961202 $output .= $r;
@@ -1198,41 +1204,41 @@
11991205 {
12001206 if (strlen ($r) == 2)
12011207 {
1202 - if ($state == 'i')
 1208+ if ($state === 'i')
12031209 { $output .= '</i>'; $state = ''; }
1204 - else if ($state == 'bi')
 1210+ else if ($state === 'bi')
12051211 { $output .= '</i>'; $state = 'b'; }
1206 - else if ($state == 'ib')
 1212+ else if ($state === 'ib')
12071213 { $output .= '</b></i><b>'; $state = 'b'; }
1208 - else if ($state == 'both')
 1214+ else if ($state === 'both')
12091215 { $output .= '<b><i>'.$buffer.'</i>'; $state = 'b'; }
12101216 else # $state can be 'b' or ''
12111217 { $output .= '<i>'; $state .= 'i'; }
12121218 }
12131219 else if (strlen ($r) == 3)
12141220 {
1215 - if ($state == 'b')
 1221+ if ($state === 'b')
12161222 { $output .= '</b>'; $state = ''; }
1217 - else if ($state == 'bi')
 1223+ else if ($state === 'bi')
12181224 { $output .= '</i></b><i>'; $state = 'i'; }
1219 - else if ($state == 'ib')
 1225+ else if ($state === 'ib')
12201226 { $output .= '</b>'; $state = 'i'; }
1221 - else if ($state == 'both')
 1227+ else if ($state === 'both')
12221228 { $output .= '<i><b>'.$buffer.'</b>'; $state = 'i'; }
12231229 else # $state can be 'i' or ''
12241230 { $output .= '<b>'; $state .= 'b'; }
12251231 }
12261232 else if (strlen ($r) == 5)
12271233 {
1228 - if ($state == 'b')
 1234+ if ($state === 'b')
12291235 { $output .= '</b><i>'; $state = 'i'; }
1230 - else if ($state == 'i')
 1236+ else if ($state === 'i')
12311237 { $output .= '</i><b>'; $state = 'b'; }
1232 - else if ($state == 'bi')
 1238+ else if ($state === 'bi')
12331239 { $output .= '</i></b>'; $state = ''; }
1234 - else if ($state == 'ib')
 1240+ else if ($state === 'ib')
12351241 { $output .= '</b></i>'; $state = ''; }
1236 - else if ($state == 'both')
 1242+ else if ($state === 'both')
12371243 { $output .= '<i><b>'.$buffer.'</b></i>'; $state = ''; }
12381244 else # ($state == '')
12391245 { $buffer = ''; $state = 'both'; }
@@ -1241,21 +1247,21 @@
12421248 $i++;
12431249 }
12441250 # Now close all remaining tags. Notice that the order is important.
1245 - if ($state == 'b' || $state == 'ib')
 1251+ if ($state === 'b' || $state === 'ib')
12461252 $output .= '</b>';
1247 - if ($state == 'i' || $state == 'bi' || $state == 'ib')
 1253+ if ($state === 'i' || $state === 'bi' || $state === 'ib')
12481254 $output .= '</i>';
1249 - if ($state == 'bi')
 1255+ if ($state === 'bi')
12501256 $output .= '</b>';
12511257 # There might be lonely ''''', so make sure we have a buffer
1252 - if ($state == 'both' && $buffer)
 1258+ if ($state === 'both' && $buffer)
12531259 $output .= '<b><i>'.$buffer.'</i></b>';
12541260 return $output;
12551261 }
12561262 }
12571263
12581264 /**
1259 - * Replace external links
 1265+ * Replace external links (REL)
12601266 *
12611267 * Note: this is all very hackish and the order of execution matters a lot.
12621268 * Make sure to run maintenance/parserTests.php if you change this code.
@@ -1264,8 +1270,7 @@
12651271 */
12661272 function replaceExternalLinks( $text ) {
12671273 global $wgContLang;
1268 - $fname = 'Parser::replaceExternalLinks';
1269 - wfProfileIn( $fname );
 1274+ wfProfileIn( __METHOD__ );
12701275
12711276 $sk = $this->mOptions->getSkin();
12721277
@@ -1299,7 +1304,7 @@
13001305 $dtrail = '';
13011306
13021307 # Set linktype for CSS - if URL==text, link is essentially free
1303 - $linktype = ($text == $url) ? 'free' : 'text';
 1308+ $linktype = ($text === $url) ? 'free' : 'text';
13041309
13051310 # No link text, e.g. [http://domain.tld/some.link]
13061311 if ( $text == '' ) {
@@ -1335,11 +1340,11 @@
13361341 # Register link in the output object.
13371342 # Replace unnecessary URL escape codes with the referenced character
13381343 # This prevents spammers from hiding links from the filters
1339 - $pasteurized = Parser::replaceUnusualEscapes( $url );
 1344+ $pasteurized = self::replaceUnusualEscapes( $url );
13401345 $this->mOutput->addExternalLink( $pasteurized );
13411346 }
13421347
1343 - wfProfileOut( $fname );
 1348+ wfProfileOut( __METHOD__ );
13441349 return $s;
13451350 }
13461351
@@ -1349,8 +1354,7 @@
13501355 */
13511356 function replaceFreeExternalLinks( $text ) {
13521357 global $wgContLang;
1353 - $fname = 'Parser::replaceFreeExternalLinks';
1354 - wfProfileIn( $fname );
 1358+ wfProfileIn( __METHOD__ );
13551359
13561360 $bits = preg_split( '/(\b(?:' . wfUrlProtocols() . '))/S', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
13571361 $s = array_shift( $bits );
@@ -1412,7 +1416,7 @@
14131417 $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() );
14141418 # Register it in the output object...
14151419 # Replace unnecessary URL escape codes with their equivalent characters
1416 - $pasteurized = Parser::replaceUnusualEscapes( $url );
 1420+ $pasteurized = self::replaceUnusualEscapes( $url );
14171421 $this->mOutput->addExternalLink( $pasteurized );
14181422 }
14191423 $s .= $text . $trail;
@@ -1420,7 +1424,7 @@
14211425 $s .= $protocol . $remainder;
14221426 }
14231427 }
1424 - wfProfileOut( $fname );
 1428+ wfProfileOut( __METHOD__ );
14251429 return $s;
14261430 }
14271431
@@ -1436,7 +1440,7 @@
14371441 */
14381442 static function replaceUnusualEscapes( $url ) {
14391443 return preg_replace_callback( '/%[0-9A-Fa-f]{2}/',
1440 - array( 'Parser', 'replaceUnusualEscapesCallback' ), $url );
 1444+ array( __CLASS__, 'replaceUnusualEscapesCallback' ), $url );
14411445 }
14421446
14431447 /**
@@ -1480,35 +1484,48 @@
14811485
14821486 /**
14831487 * Process [[ ]] wikilinks
 1488+ * @return processed text
14841489 *
14851490 * @private
14861491 */
14871492 function replaceInternalLinks( $s ) {
 1493+ $this->mLinkHolders->merge( $this->replaceInternalLinks2( $s ) );
 1494+ return $s;
 1495+ }
 1496+
 1497+ /**
 1498+ * Process [[ ]] wikilinks (RIL)
 1499+ * @return LinkHolderArray
 1500+ *
 1501+ * @private
 1502+ */
 1503+ function replaceInternalLinks2( &$s ) {
14881504 global $wgContLang;
1489 - static $fname = 'Parser::replaceInternalLinks' ;
14901505
1491 - wfProfileIn( $fname );
 1506+ wfProfileIn( __METHOD__ );
14921507
1493 - wfProfileIn( $fname.'-setup' );
1494 - static $tc = FALSE;
 1508+ wfProfileIn( __METHOD__.'-setup' );
 1509+ static $tc = FALSE, $e1, $e1_img;
14951510 # the % is needed to support urlencoded titles as well
1496 - if ( !$tc ) { $tc = Title::legalChars() . '#%'; }
 1511+ if ( !$tc ) {
 1512+ $tc = Title::legalChars() . '#%';
 1513+ # Match a link having the form [[namespace:link|alternate]]trail
 1514+ $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD";
 1515+ # Match cases where there is no "]]", which might still be images
 1516+ $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD";
 1517+ }
14971518
14981519 $sk = $this->mOptions->getSkin();
 1520+ $holders = new LinkHolderArray( $this );
14991521
15001522 #split the entire text string on occurences of [[
1501 - $a = explode( '[[', ' ' . $s );
 1523+ $a = StringUtils::explode( '[[', ' ' . $s );
15021524 #get the first element (all text up to first [[), and remove the space we added
1503 - $s = array_shift( $a );
 1525+ $s = $a->current();
 1526+ $a->next();
 1527+ $line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void"
15041528 $s = substr( $s, 1 );
15051529
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 -
15131530 $useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
15141531 $e2 = null;
15151532 if ( $useLinkPrefixExtension ) {
@@ -1518,8 +1535,8 @@
15191536 }
15201537
15211538 if( is_null( $this->mTitle ) ) {
1522 - wfProfileOut( $fname );
1523 - wfProfileOut( $fname.'-setup' );
 1539+ wfProfileOut( __METHOD__.'-setup' );
 1540+ wfProfileOut( __METHOD__ );
15241541 throw new MWException( __METHOD__.": \$this->mTitle is null\n" );
15251542 }
15261543 $nottalk = !$this->mTitle->isTalkPage();
@@ -1541,13 +1558,20 @@
15421559 $selflink = array($this->mTitle->getPrefixedText());
15431560 }
15441561 $useSubpages = $this->areSubpagesAllowed();
1545 - wfProfileOut( $fname.'-setup' );
 1562+ wfProfileOut( __METHOD__.'-setup' );
15461563
15471564 # Loop for each link
1548 - for ($k = 0; isset( $a[$k] ); $k++) {
1549 - $line = $a[$k];
 1565+ for ( ; $line !== false && $line !== null ; $a->next(), $line = $a->current() ) {
 1566+ # Check for excessive memory usage
 1567+ if ( $holders->isBig() ) {
 1568+ # Too big
 1569+ # Do the existence check, replace the link holders and clear the array
 1570+ $holders->replace( $s );
 1571+ $holders->clear();
 1572+ }
 1573+
15501574 if ( $useLinkPrefixExtension ) {
1551 - wfProfileIn( $fname.'-prefixhandling' );
 1575+ wfProfileIn( __METHOD__.'-prefixhandling' );
15521576 if ( preg_match( $e2, $s, $m ) ) {
15531577 $prefix = $m[2];
15541578 $s = $m[1];
@@ -1559,12 +1583,12 @@
15601584 $prefix = $first_prefix;
15611585 $first_prefix = false;
15621586 }
1563 - wfProfileOut( $fname.'-prefixhandling' );
 1587+ wfProfileOut( __METHOD__.'-prefixhandling' );
15641588 }
15651589
15661590 $might_be_img = false;
15671591
1568 - wfProfileIn( "$fname-e1" );
 1592+ wfProfileIn( __METHOD__."-e1" );
15691593 if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt
15701594 $text = $m[2];
15711595 # If we get a ] at the beginning of $m[3] that means we have a link that's something like:
@@ -1598,18 +1622,18 @@
15991623 $trail = "";
16001624 } else { # Invalid form; output directly
16011625 $s .= $prefix . '[[' . $line ;
1602 - wfProfileOut( "$fname-e1" );
 1626+ wfProfileOut( __METHOD__."-e1" );
16031627 continue;
16041628 }
1605 - wfProfileOut( "$fname-e1" );
1606 - wfProfileIn( "$fname-misc" );
 1629+ wfProfileOut( __METHOD__."-e1" );
 1630+ wfProfileIn( __METHOD__."-misc" );
16071631
16081632 # Don't allow internal links to pages containing
16091633 # PROTO: where PROTO is a valid URL protocol; these
16101634 # should be external links.
16111635 if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) {
16121636 $s .= $prefix . '[[' . $line ;
1613 - wfProfileOut( "$fname-misc" );
 1637+ wfProfileOut( __METHOD__."-misc" );
16141638 continue;
16151639 }
16161640
@@ -1620,33 +1644,36 @@
16211645 $link = $m[1];
16221646 }
16231647
1624 - $noforce = (substr($m[1], 0, 1) != ':');
 1648+ $noforce = (substr($m[1], 0, 1) !== ':');
16251649 if (!$noforce) {
16261650 # Strip off leading ':'
16271651 $link = substr($link, 1);
16281652 }
16291653
1630 - wfProfileOut( "$fname-misc" );
1631 - wfProfileIn( "$fname-title" );
 1654+ wfProfileOut( __METHOD__."-misc" );
 1655+ wfProfileIn( __METHOD__."-title" );
16321656 $nt = Title::newFromText( $this->mStripState->unstripNoWiki($link) );
16331657 if( !$nt ) {
16341658 $s .= $prefix . '[[' . $line;
1635 - wfProfileOut( "$fname-title" );
 1659+ wfProfileOut( __METHOD__."-title" );
16361660 continue;
16371661 }
16381662
16391663 $ns = $nt->getNamespace();
16401664 $iw = $nt->getInterWiki();
1641 - wfProfileOut( "$fname-title" );
 1665+ wfProfileOut( __METHOD__."-title" );
16421666
16431667 if ($might_be_img) { # if this is actually an invalid link
1644 - wfProfileIn( "$fname-might_be_img" );
 1668+ wfProfileIn( __METHOD__."-might_be_img" );
16451669 if ($ns == NS_IMAGE && $noforce) { #but might be an image
16461670 $found = false;
1647 - while (isset ($a[$k+1]) ) {
 1671+ while ( true ) {
16481672 #look at the next 'line' to see if we can close it there
1649 - $spliced = array_splice( $a, $k + 1, 1 );
1650 - $next_line = array_shift( $spliced );
 1673+ $a->next();
 1674+ $next_line = $a->current();
 1675+ if ( $next_line === false || $next_line === null ) {
 1676+ break;
 1677+ }
16511678 $m = explode( ']]', $next_line, 3 );
16521679 if ( count( $m ) == 3 ) {
16531680 # the first ]] closes the inner link, the second the image
@@ -1666,19 +1693,19 @@
16671694 if ( !$found ) {
16681695 # we couldn't find the end of this imageLink, so output it raw
16691696 #but don't ignore what might be perfectly normal links in the text we've examined
1670 - $text = $this->replaceInternalLinks($text);
 1697+ $holders->merge( $this->replaceInternalLinks2( $text ) );
16711698 $s .= "{$prefix}[[$link|$text";
16721699 # note: no $trail, because without an end, there *is* no trail
1673 - wfProfileOut( "$fname-might_be_img" );
 1700+ wfProfileOut( __METHOD__."-might_be_img" );
16741701 continue;
16751702 }
16761703 } else { #it's not an image, so output it raw
16771704 $s .= "{$prefix}[[$link|$text";
16781705 # note: no $trail, because without an end, there *is* no trail
1679 - wfProfileOut( "$fname-might_be_img" );
 1706+ wfProfileOut( __METHOD__."-might_be_img" );
16801707 continue;
16811708 }
1682 - wfProfileOut( "$fname-might_be_img" );
 1709+ wfProfileOut( __METHOD__."-might_be_img" );
16831710 }
16841711
16851712 $wasblank = ( '' == $text );
@@ -1688,41 +1715,36 @@
16891716 if( $noforce ) {
16901717
16911718 # Interwikis
1692 - wfProfileIn( "$fname-interwiki" );
 1719+ wfProfileIn( __METHOD__."-interwiki" );
16931720 if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
16941721 $this->mOutput->addLanguageLink( $nt->getFullText() );
16951722 $s = rtrim($s . $prefix);
16961723 $s .= trim($trail, "\n") == '' ? '': $prefix . $trail;
1697 - wfProfileOut( "$fname-interwiki" );
 1724+ wfProfileOut( __METHOD__."-interwiki" );
16981725 continue;
16991726 }
1700 - wfProfileOut( "$fname-interwiki" );
 1727+ wfProfileOut( __METHOD__."-interwiki" );
17011728
17021729 if ( $ns == NS_IMAGE ) {
1703 - wfProfileIn( "$fname-image" );
 1730+ wfProfileIn( __METHOD__."-image" );
17041731 if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) {
17051732 # recursively parse links inside the image caption
17061733 # actually, this will parse them in any other parameters, too,
17071734 # but it might be hard to fix that, and it doesn't matter ATM
17081735 $text = $this->replaceExternalLinks($text);
1709 - $text = $this->replaceInternalLinks($text);
 1736+ $holders->merge( $this->replaceInternalLinks2( $text ) );
17101737
17111738 # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
1712 - $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text ) ) . $trail;
1713 - $this->mOutput->addImage( $nt->getDBkey() );
1714 -
1715 - wfProfileOut( "$fname-image" );
1716 - continue;
1717 - } else {
1718 - # We still need to record the image's presence on the page
1719 - $this->mOutput->addImage( $nt->getDBkey() );
 1739+ $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text, $holders ) ) . $trail;
17201740 }
1721 - wfProfileOut( "$fname-image" );
 1741+ $this->mOutput->addImage( $nt->getDBkey() );
 1742+ wfProfileOut( __METHOD__."-image" );
 1743+ continue;
17221744
17231745 }
17241746
17251747 if ( $ns == NS_CATEGORY ) {
1726 - wfProfileIn( "$fname-category" );
 1748+ wfProfileIn( __METHOD__."-category" );
17271749 $s = rtrim($s . "\n"); # bug 87
17281750
17291751 if ( $wasblank ) {
@@ -1741,7 +1763,7 @@
17421764 */
17431765 $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail;
17441766
1745 - wfProfileOut( "$fname-category" );
 1767+ wfProfileOut( __METHOD__."-category" );
17461768 continue;
17471769 }
17481770 }
@@ -1772,7 +1794,7 @@
17731795 if( SpecialPage::exists( $nt->getDBkey() ) ) {
17741796 $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
17751797 } else {
1776 - $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
 1798+ $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix );
17771799 }
17781800 continue;
17791801 } elseif( $ns == NS_IMAGE ) {
@@ -1786,10 +1808,10 @@
17871809 continue;
17881810 }
17891811 }
1790 - $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
 1812+ $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix );
17911813 }
1792 - wfProfileOut( $fname );
1793 - return $s;
 1814+ wfProfileOut( __METHOD__ );
 1815+ return $holders;
17941816 }
17951817
17961818 /**
@@ -1798,32 +1820,10 @@
17991821 * parsing of interwiki links, and secondly to allow all existence checks and
18001822 * article length checks (for stub links) to be bundled into a single query.
18011823 *
 1824+ * @deprecated
18021825 */
18031826 function makeLinkHolder( &$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;
 1827+ return $this->mLinkHolders->makeHolder( $nt, $text, $query, $trail, $prefix );
18281828 }
18291829
18301830 /**
@@ -1889,8 +1889,7 @@
18901890 # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
18911891 # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage
18921892
1893 - $fname = 'Parser::maybeDoSubpageLink';
1894 - wfProfileIn( $fname );
 1893+ wfProfileIn( __METHOD__ );
18951894 $ret = $target; # default return value is no change
18961895
18971896 # Some namespaces don't allow subpages,
@@ -1906,7 +1905,7 @@
19071906 # bug 7425
19081907 $target = trim( $target );
19091908 # Look at the first character
1910 - if( $target != '' && $target{0} == '/' ) {
 1909+ if( $target != '' && $target{0} === '/' ) {
19111910 # / at end means we don't want the slash to be shown
19121911 $m = array();
19131912 $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
@@ -1933,7 +1932,7 @@
19341933 if( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
19351934 $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
19361935 # / at the end means don't show full path
1937 - if( substr( $nodotdot, -1, 1 ) == '/' ) {
 1936+ if( substr( $nodotdot, -1, 1 ) === '/' ) {
19381937 $nodotdot = substr( $nodotdot, 0, -1 );
19391938 if( '' === $text ) {
19401939 $text = $nodotdot . $suffix;
@@ -1949,7 +1948,7 @@
19501949 }
19511950 }
19521951
1953 - wfProfileOut( $fname );
 1952+ wfProfileOut( __METHOD__ );
19541953 return $ret;
19551954 }
19561955
@@ -1985,10 +1984,10 @@
19861985 /* private */ function openList( $char ) {
19871986 $result = $this->closeParagraph();
19881987
1989 - if ( '*' == $char ) { $result .= '<ul><li>'; }
1990 - else if ( '#' == $char ) { $result .= '<ol><li>'; }
1991 - else if ( ':' == $char ) { $result .= '<dl><dd>'; }
1992 - else if ( ';' == $char ) {
 1988+ if ( '*' === $char ) { $result .= '<ul><li>'; }
 1989+ else if ( '#' === $char ) { $result .= '<ol><li>'; }
 1990+ else if ( ':' === $char ) { $result .= '<dl><dd>'; }
 1991+ else if ( ';' === $char ) {
19931992 $result .= '<dl><dt>';
19941993 $this->mDTopen = true;
19951994 }
@@ -1998,11 +1997,11 @@
19991998 }
20001999
20012000 /* private */ function nextItem( $char ) {
2002 - if ( '*' == $char || '#' == $char ) { return '</li><li>'; }
2003 - else if ( ':' == $char || ';' == $char ) {
 2001+ if ( '*' === $char || '#' === $char ) { return '</li><li>'; }
 2002+ else if ( ':' === $char || ';' === $char ) {
20042003 $close = '</dd>';
20052004 if ( $this->mDTopen ) { $close = '</dt>'; }
2006 - if ( ';' == $char ) {
 2005+ if ( ';' === $char ) {
20072006 $this->mDTopen = true;
20082007 return $close . '<dt>';
20092008 } else {
@@ -2014,9 +2013,9 @@
20152014 }
20162015
20172016 /* private */ function closeList( $char ) {
2018 - if ( '*' == $char ) { $text = '</li></ul>'; }
2019 - else if ( '#' == $char ) { $text = '</li></ol>'; }
2020 - else if ( ':' == $char ) {
 2017+ if ( '*' === $char ) { $text = '</li></ul>'; }
 2018+ else if ( '#' === $char ) { $text = '</li></ol>'; }
 2019+ else if ( ':' === $char ) {
20212020 if ( $this->mDTopen ) {
20222021 $this->mDTopen = false;
20232022 $text = '</dt></dl>';
@@ -2030,56 +2029,59 @@
20312030 /**#@-*/
20322031
20332032 /**
2034 - * Make lists from lines starting with ':', '*', '#', etc.
 2033+ * Make lists from lines starting with ':', '*', '#', etc. (DBL)
20352034 *
20362035 * @private
20372036 * @return string the lists rendered as HTML
20382037 */
20392038 function doBlockLevels( $text, $linestart ) {
2040 - $fname = 'Parser::doBlockLevels';
2041 - wfProfileIn( $fname );
 2039+ wfProfileIn( __METHOD__ );
20422040
20432041 # Parsing through the text line by line. The main thing
20442042 # happening here is handling of block-level elements p, pre,
20452043 # and making lists from lines starting with * # : etc.
20462044 #
2047 - $textLines = explode( "\n", $text );
 2045+ $textLines = StringUtils::explode( "\n", $text );
20482046
20492047 $lastPrefix = $output = '';
20502048 $this->mDTopen = $inBlockElem = false;
20512049 $prefixLength = 0;
20522050 $paragraphStack = false;
20532051
2054 - if ( !$linestart ) {
2055 - $output .= array_shift( $textLines );
2056 - }
20572052 foreach ( $textLines as $oLine ) {
 2053+ # Fix up $linestart
 2054+ if ( !$linestart ) {
 2055+ $output .= $oLine;
 2056+ $linestart = true;
 2057+ continue;
 2058+ }
 2059+
20582060 $lastPrefixLength = strlen( $lastPrefix );
20592061 $preCloseMatch = preg_match('/<\\/pre/i', $oLine );
20602062 $preOpenMatch = preg_match('/<pre/i', $oLine );
20612063 if ( !$this->mInPre ) {
20622064 # Multiple prefixes may abut each other for nested lists.
20632065 $prefixLength = strspn( $oLine, '*#:;' );
2064 - $pref = substr( $oLine, 0, $prefixLength );
 2066+ $prefix = substr( $oLine, 0, $prefixLength );
20652067
20662068 # eh?
2067 - $pref2 = str_replace( ';', ':', $pref );
 2069+ $prefix2 = str_replace( ';', ':', $prefix );
20682070 $t = substr( $oLine, $prefixLength );
2069 - $this->mInPre = !empty($preOpenMatch);
 2071+ $this->mInPre = (bool)$preOpenMatch;
20702072 } else {
20712073 # Don't interpret any other prefixes in preformatted text
20722074 $prefixLength = 0;
2073 - $pref = $pref2 = '';
 2075+ $prefix = $prefix2 = '';
20742076 $t = $oLine;
20752077 }
20762078
20772079 # List generation
2078 - if( $prefixLength && 0 == strcmp( $lastPrefix, $pref2 ) ) {
 2080+ if( $prefixLength && $lastPrefix === $prefix2 ) {
20792081 # Same as the last item, so no need to deal with nesting or opening stuff
2080 - $output .= $this->nextItem( substr( $pref, -1 ) );
 2082+ $output .= $this->nextItem( substr( $prefix, -1 ) );
20812083 $paragraphStack = false;
20822084
2083 - if ( substr( $pref, -1 ) == ';') {
 2085+ if ( substr( $prefix, -1 ) === ';') {
20842086 # The one nasty exception: definition lists work like this:
20852087 # ; title : definition text
20862088 # So we check for : in the remainder text to split up the
@@ -2092,21 +2094,21 @@
20932095 }
20942096 } elseif( $prefixLength || $lastPrefixLength ) {
20952097 # Either open or close a level...
2096 - $commonPrefixLength = $this->getCommon( $pref, $lastPrefix );
 2098+ $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix );
20972099 $paragraphStack = false;
20982100
20992101 while( $commonPrefixLength < $lastPrefixLength ) {
2100 - $output .= $this->closeList( $lastPrefix{$lastPrefixLength-1} );
 2102+ $output .= $this->closeList( $lastPrefix[$lastPrefixLength-1] );
21012103 --$lastPrefixLength;
21022104 }
21032105 if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
2104 - $output .= $this->nextItem( $pref{$commonPrefixLength-1} );
 2106+ $output .= $this->nextItem( $prefix[$commonPrefixLength-1] );
21052107 }
21062108 while ( $prefixLength > $commonPrefixLength ) {
2107 - $char = substr( $pref, $commonPrefixLength, 1 );
 2109+ $char = substr( $prefix, $commonPrefixLength, 1 );
21082110 $output .= $this->openList( $char );
21092111
2110 - if ( ';' == $char ) {
 2112+ if ( ';' === $char ) {
21112113 # FIXME: This is dupe of code above
21122114 if ($this->findColonNoLinks($t, $term, $t2) !== false) {
21132115 $t = $t2;
@@ -2115,10 +2117,10 @@
21162118 }
21172119 ++$commonPrefixLength;
21182120 }
2119 - $lastPrefix = $pref2;
 2121+ $lastPrefix = $prefix2;
21202122 }
21212123 if( 0 == $prefixLength ) {
2122 - wfProfileIn( "$fname-paragraph" );
 2124+ wfProfileIn( __METHOD__."-paragraph" );
21232125 # No prefix (not in list)--go to paragraph mode
21242126 // XXX: use a stack for nestable elements like span, table and div
21252127 $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
@@ -2138,9 +2140,9 @@
21392141 $inBlockElem = true;
21402142 }
21412143 } else if ( !$inBlockElem && !$this->mInPre ) {
2142 - if ( ' ' == $t{0} and ( $this->mLastSection == 'pre' or trim($t) != '' ) ) {
 2144+ if ( ' ' == $t{0} and ( $this->mLastSection === 'pre' or trim($t) != '' ) ) {
21432145 // pre
2144 - if ($this->mLastSection != 'pre') {
 2146+ if ($this->mLastSection !== 'pre') {
21452147 $paragraphStack = false;
21462148 $output .= $this->closeParagraph().'<pre>';
21472149 $this->mLastSection = 'pre';
@@ -2154,7 +2156,7 @@
21552157 $paragraphStack = false;
21562158 $this->mLastSection = 'p';
21572159 } else {
2158 - if ($this->mLastSection != 'p' ) {
 2160+ if ($this->mLastSection !== 'p' ) {
21592161 $output .= $this->closeParagraph();
21602162 $this->mLastSection = '';
21612163 $paragraphStack = '<p>';
@@ -2167,14 +2169,14 @@
21682170 $output .= $paragraphStack;
21692171 $paragraphStack = false;
21702172 $this->mLastSection = 'p';
2171 - } else if ($this->mLastSection != 'p') {
 2173+ } else if ($this->mLastSection !== 'p') {
21722174 $output .= $this->closeParagraph().'<p>';
21732175 $this->mLastSection = 'p';
21742176 }
21752177 }
21762178 }
21772179 }
2178 - wfProfileOut( "$fname-paragraph" );
 2180+ wfProfileOut( __METHOD__."-paragraph" );
21792181 }
21802182 // somewhere above we forget to get out of pre block (bug 785)
21812183 if($preCloseMatch && $this->mInPre) {
@@ -2185,7 +2187,7 @@
21862188 }
21872189 }
21882190 while ( $prefixLength ) {
2189 - $output .= $this->closeList( $pref2{$prefixLength-1} );
 2191+ $output .= $this->closeList( $prefix2[$prefixLength-1] );
21902192 --$prefixLength;
21912193 }
21922194 if ( '' != $this->mLastSection ) {
@@ -2193,7 +2195,7 @@
21942196 $this->mLastSection = '';
21952197 }
21962198
2197 - wfProfileOut( $fname );
 2199+ wfProfileOut( __METHOD__ );
21982200 return $output;
21992201 }
22002202
@@ -2206,13 +2208,12 @@
22072209 * return string the position of the ':', or false if none found
22082210 */
22092211 function findColonNoLinks($str, &$before, &$after) {
2210 - $fname = 'Parser::findColonNoLinks';
2211 - wfProfileIn( $fname );
 2212+ wfProfileIn( __METHOD__ );
22122213
22132214 $pos = strpos( $str, ':' );
22142215 if( $pos === false ) {
22152216 // Nothing to find!
2216 - wfProfileOut( $fname );
 2217+ wfProfileOut( __METHOD__ );
22172218 return false;
22182219 }
22192220
@@ -2221,7 +2222,7 @@
22222223 // Easy; no tag nesting to worry about
22232224 $before = substr( $str, 0, $pos );
22242225 $after = substr( $str, $pos+1 );
2225 - wfProfileOut( $fname );
 2226+ wfProfileOut( __METHOD__ );
22262227 return $pos;
22272228 }
22282229
@@ -2245,7 +2246,7 @@
22462247 // We found it!
22472248 $before = substr( $str, 0, $i );
22482249 $after = substr( $str, $i + 1 );
2249 - wfProfileOut( $fname );
 2250+ wfProfileOut( __METHOD__ );
22502251 return $i;
22512252 }
22522253 // Embedded in a tag; don't break it.
@@ -2255,7 +2256,7 @@
22562257 $colon = strpos( $str, ':', $i );
22572258 if( $colon === false ) {
22582259 // Nothing else interesting
2259 - wfProfileOut( $fname );
 2260+ wfProfileOut( __METHOD__ );
22602261 return false;
22612262 }
22622263 $lt = strpos( $str, '<', $i );
@@ -2264,7 +2265,7 @@
22652266 // We found it!
22662267 $before = substr( $str, 0, $colon );
22672268 $after = substr( $str, $colon + 1 );
2268 - wfProfileOut( $fname );
 2269+ wfProfileOut( __METHOD__ );
22692270 return $i;
22702271 }
22712272 }
@@ -2311,18 +2312,18 @@
23122313 break;
23132314 case 3: // self::COLON_STATE_CLOSETAG:
23142315 // In a </tag>
2315 - if( $c == ">" ) {
 2316+ if( $c === ">" ) {
23162317 $stack--;
23172318 if( $stack < 0 ) {
2318 - wfDebug( "Invalid input in $fname; too many close tags\n" );
2319 - wfProfileOut( $fname );
 2319+ wfDebug( __METHOD__.": Invalid input; too many close tags\n" );
 2320+ wfProfileOut( __METHOD__ );
23202321 return false;
23212322 }
23222323 $state = self::COLON_STATE_TEXT;
23232324 }
23242325 break;
23252326 case self::COLON_STATE_TAGSLASH:
2326 - if( $c == ">" ) {
 2327+ if( $c === ">" ) {
23272328 // Yes, a self-closed tag <blah/>
23282329 $state = self::COLON_STATE_TEXT;
23292330 } else {
@@ -2331,33 +2332,33 @@
23322333 }
23332334 break;
23342335 case 5: // self::COLON_STATE_COMMENT:
2335 - if( $c == "-" ) {
 2336+ if( $c === "-" ) {
23362337 $state = self::COLON_STATE_COMMENTDASH;
23372338 }
23382339 break;
23392340 case self::COLON_STATE_COMMENTDASH:
2340 - if( $c == "-" ) {
 2341+ if( $c === "-" ) {
23412342 $state = self::COLON_STATE_COMMENTDASHDASH;
23422343 } else {
23432344 $state = self::COLON_STATE_COMMENT;
23442345 }
23452346 break;
23462347 case self::COLON_STATE_COMMENTDASHDASH:
2347 - if( $c == ">" ) {
 2348+ if( $c === ">" ) {
23482349 $state = self::COLON_STATE_TEXT;
23492350 } else {
23502351 $state = self::COLON_STATE_COMMENT;
23512352 }
23522353 break;
23532354 default:
2354 - throw new MWException( "State machine error in $fname" );
 2355+ throw new MWException( "State machine error in " . __METHOD__ );
23552356 }
23562357 }
23572358 if( $stack > 0 ) {
2358 - wfDebug( "Invalid input in $fname; not enough close tags (stack $stack, state $state)\n" );
 2359+ wfDebug( __METHOD__.": Invalid input; not enough close tags (stack $stack, state $state)\n" );
23592360 return false;
23602361 }
2361 - wfProfileOut( $fname );
 2362+ wfProfileOut( __METHOD__ );
23622363 return false;
23632364 }
23642365
@@ -2587,12 +2588,11 @@
25882589 * @private
25892590 */
25902591 function initialiseVariables() {
2591 - $fname = 'Parser::initialiseVariables';
2592 - wfProfileIn( $fname );
 2592+ wfProfileIn( __METHOD__ );
25932593 $variableIDs = MagicWord::getVariableIDs();
25942594
25952595 $this->mVariables = new MagicWordArray( $variableIDs );
2596 - wfProfileOut( $fname );
 2596+ wfProfileOut( __METHOD__ );
25972597 }
25982598
25992599 /**
@@ -2661,8 +2661,7 @@
26622662 return $text;
26632663 }
26642664
2665 - $fname = __METHOD__;
2666 - wfProfileIn( $fname );
 2665+ wfProfileIn( __METHOD__ );
26672666
26682667 if ( $frame === false ) {
26692668 $frame = $this->getPreprocessor()->newFrame();
@@ -2675,7 +2674,7 @@
26762675 $flags = $argsOnly ? PPFrame::NO_TEMPLATES : 0;
26772676 $text = $frame->expand( $dom, $flags );
26782677
2679 - wfProfileOut( $fname );
 2678+ wfProfileOut( __METHOD__ );
26802679 return $text;
26812680 }
26822681
@@ -2738,8 +2737,7 @@
27392738 */
27402739 function braceSubstitution( $piece, $frame ) {
27412740 global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces;
2742 - $fname = __METHOD__;
2743 - wfProfileIn( $fname );
 2741+ wfProfileIn( __METHOD__ );
27442742 wfProfileIn( __METHOD__.'-setup' );
27452743
27462744 # Flags
@@ -2926,7 +2924,7 @@
29272925 }
29282926 } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
29292927 $found = false; //access denied
2930 - wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() );
 2928+ wfDebug( __METHOD__.": template inclusion denied for " . $title->getPrefixedDBkey() );
29312929 } else {
29322930 list( $text, $title ) = $this->getTemplateDom( $title );
29332931 if ( $text !== false ) {
@@ -2960,7 +2958,7 @@
29612959 # Recover the source wikitext and return it
29622960 if ( !$found ) {
29632961 $text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args );
2964 - wfProfileOut( $fname );
 2962+ wfProfileOut( __METHOD__ );
29652963 return array( 'object' => $text );
29662964 }
29672965
@@ -3019,7 +3017,7 @@
30203018 $ret = array( 'text' => $text );
30213019 }
30223020
3023 - wfProfileOut( $fname );
 3021+ wfProfileOut( __METHOD__ );
30243022 return $ret;
30253023 }
30263024
@@ -3306,7 +3304,7 @@
33073305 }
33083306 }
33093307
3310 - if ( $name == 'html' || $name == 'nowiki' ) {
 3308+ if ( $name === 'html' || $name === 'nowiki' ) {
33113309 $this->mStripState->nowiki->setPair( $marker, $output );
33123310 } else {
33133311 $this->mStripState->general->setPair( $marker, $output );
@@ -3562,12 +3560,7 @@
35633561 # <!--LINK number-->
35643562 # turns into
35653563 # link text with suffix
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 );
 3564+ $safeHeadline = $this->replaceLinkHoldersText( $safeHeadline );
35723565
35733566 # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
35743567 $tocline = preg_replace(
@@ -3643,7 +3636,7 @@
36443637 $i = 0;
36453638
36463639 foreach( $blocks as $block ) {
3647 - if( $showEditLink && $headlineCount > 0 && $i == 0 && $block != "\n" ) {
 3640+ if( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) {
36483641 # This is the [edit] link that appears for the top block of text when
36493642 # section editing is enabled
36503643
@@ -3795,7 +3788,7 @@
37963789 } else {
37973790 # Failed to validate; fall back to the default
37983791 $nickname = $username;
3799 - wfDebug( "Parser::getUserSig: $username has bad XML tags in signature.\n" );
 3792+ wfDebug( __METHOD__.": $username has bad XML tags in signature.\n" );
38003793 }
38013794 }
38023795
@@ -3901,19 +3894,17 @@
39023895 global $wgTitle;
39033896 static $executing = false;
39043897
3905 - $fname = "Parser::transformMsg";
3906 -
39073898 # Guard against infinite recursion
39083899 if ( $executing ) {
39093900 return $text;
39103901 }
39113902 $executing = true;
39123903
3913 - wfProfileIn($fname);
 3904+ wfProfileIn(__METHOD__);
39143905 $text = $this->preprocess( $text, $wgTitle, $options );
39153906
39163907 $executing = false;
3917 - wfProfileOut($fname);
 3908+ wfProfileOut(__METHOD__);
39183909 return $text;
39193910 }
39203911
@@ -4010,7 +4001,7 @@
40114002 # Add to function cache
40124003 $mw = MagicWord::get( $id );
40134004 if( !$mw )
4014 - throw new MWException( 'Parser::setFunctionHook() expecting a magic word identifier.' );
 4005+ throw new MWException( __METHOD__.'() expecting a magic word identifier.' );
40154006
40164007 $synonyms = $mw->getSynonyms();
40174008 $sensitive = intval( $mw->isCaseSensitive() );
@@ -4025,7 +4016,7 @@
40264017 $syn = '#' . $syn;
40274018 }
40284019 # Remove trailing colon
4029 - if ( substr( $syn, -1, 1 ) == ':' ) {
 4020+ if ( substr( $syn, -1, 1 ) === ':' ) {
40304021 $syn = substr( $syn, 0, -1 );
40314022 }
40324023 $this->mFunctionSynonyms[$sensitive][$syn] = $id;
@@ -4046,266 +4037,9 @@
40474038 * Replace <!--LINK--> link placeholders with actual links, in the buffer
40484039 * Placeholders created in Skin::makeLinkObj()
40494040 * Returns an array of link CSS classes, indexed by PDBK.
4050 - * $options is a bit field, RLH_FOR_UPDATE to select for update
40514041 */
40524042 function replaceLinkHolders( &$text, $options = 0 ) {
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;
 4043+ return $this->mLinkHolders->replace( $text );
43104044 }
43114045
43124046 /**
@@ -4315,39 +4049,10 @@
43164050 * @return string
43174051 */
43184052 function replaceLinkHoldersText( $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;
 4053+ return $this->mLinkHolders->replaceText( $text );
43294054 }
43304055
43314056 /**
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 - /**
43524057 * Tag hook handler for 'pre'.
43534058 */
43544059 function renderPreTag( $text, $attribs ) {
@@ -4398,7 +4103,7 @@
43994104
44004105 wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) );
44014106
4402 - $lines = explode( "\n", $text );
 4107+ $lines = StringUtils::explode( "\n", $text );
44034108 foreach ( $lines as $line ) {
44044109 # match lines like these:
44054110 # Image:someimage.jpg|This is some image
@@ -4411,7 +4116,7 @@
44124117
44134118 if ( strpos( $matches[0], '%' ) !== false )
44144119 $matches[1] = urldecode( $matches[1] );
4415 - $tp = Title::newFromText( $matches[1] );
 4120+ $tp = Title::newFromText( $matches[1], NS_IMAGE );
44164121 $nt =& $tp;
44174122 if( is_null( $nt ) ) {
44184123 # Bogus title. Ignore these so we don't bomb out later.
@@ -4477,8 +4182,11 @@
44784183
44794184 /**
44804185 * Parse image options text and use it to make an image
 4186+ * @param Title $title
 4187+ * @param string $options
 4188+ * @param LinkHolderArray $holders
44814189 */
4482 - function makeImage( $title, $options ) {
 4190+ function makeImage( $title, $options, $holders = false ) {
44834191 # Check if the options text is of the form "options|alt text"
44844192 # Options are:
44854193 # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang
@@ -4501,7 +4209,7 @@
45024210 # * bottom
45034211 # * text-bottom
45044212
4505 - $parts = array_map( 'trim', explode( '|', $options) );
 4213+ $parts = StringUtils::explode( "|", $options );
45064214 $sk = $this->mOptions->getSkin();
45074215
45084216 # Give extensions a chance to select the file revision for us
@@ -4523,13 +4231,14 @@
45244232 $params = array( 'frame' => array(), 'handler' => array(),
45254233 'horizAlign' => array(), 'vertAlign' => array() );
45264234 foreach( $parts as $part ) {
 4235+ $part = trim( $part );
45274236 list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
45284237 $validated = false;
45294238 if( isset( $paramMap[$magicName] ) ) {
45304239 list( $type, $paramName ) = $paramMap[$magicName];
45314240
45324241 // Special case; width and height come in one variable together
4533 - if( $type == 'handler' && $paramName == 'width' ) {
 4242+ if( $type === 'handler' && $paramName === 'width' ) {
45344243 $m = array();
45354244 # (bug 13500) In both cases (width/height and width only),
45364245 # permit trailing "px" for backward compatibility.
@@ -4552,7 +4261,7 @@
45534262 }
45544263 } // else no validation -- bug 13436
45554264 } else {
4556 - if ( $type == 'handler' ) {
 4265+ if ( $type === 'handler' ) {
45574266 # Validate handler parameter
45584267 $validated = $handler->validateParam( $paramName, $value );
45594268 } else {
@@ -4588,7 +4297,13 @@
45894298 }
45904299
45914300 # Strip bad stuff out of the alt text
4592 - $alt = $this->replaceLinkHoldersText( $caption );
 4301+ # We can't just use replaceLinkHoldersText() here, because if this function
 4302+ # is called from replaceInternalLinks2(), mLinkHolders won't be up to date.
 4303+ if ( $holders ) {
 4304+ $alt = $holders->replaceText( $caption );
 4305+ } else {
 4306+ $alt = $this->replaceLinkHoldersText( $caption );
 4307+ }
45934308
45944309 # make sure there are no placeholders in thumbnail attributes
45954310 # that are later expanded to html- so expand them now and
@@ -4691,7 +4406,7 @@
46924407 $sectionParts = explode( '-', $section );
46934408 $sectionIndex = array_pop( $sectionParts );
46944409 foreach ( $sectionParts as $part ) {
4695 - if ( $part == 'T' ) {
 4410+ if ( $part === 'T' ) {
46964411 $flags |= self::PTD_FOR_INCLUSION;
46974412 }
46984413 }
@@ -4708,14 +4423,14 @@
47094424 $targetLevel = 1000;
47104425 } else {
47114426 while ( $node ) {
4712 - if ( $node->getName() == 'h' ) {
 4427+ if ( $node->getName() === 'h' ) {
47134428 $bits = $node->splitHeading();
47144429 if ( $bits['i'] == $sectionIndex ) {
47154430 $targetLevel = $bits['level'];
47164431 break;
47174432 }
47184433 }
4719 - if ( $mode == 'replace' ) {
 4434+ if ( $mode === 'replace' ) {
47204435 $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
47214436 }
47224437 $node = $node->getNextSibling();
@@ -4724,7 +4439,7 @@
47254440
47264441 if ( !$node ) {
47274442 // Not found
4728 - if ( $mode == 'get' ) {
 4443+ if ( $mode === 'get' ) {
47294444 return $newText;
47304445 } else {
47314446 return $text;
@@ -4733,21 +4448,21 @@
47344449
47354450 // Find the end of the section, including nested sections
47364451 do {
4737 - if ( $node->getName() == 'h' ) {
 4452+ if ( $node->getName() === 'h' ) {
47384453 $bits = $node->splitHeading();
47394454 $curLevel = $bits['level'];
47404455 if ( $bits['i'] != $sectionIndex && $curLevel <= $targetLevel ) {
47414456 break;
47424457 }
47434458 }
4744 - if ( $mode == 'get' ) {
 4459+ if ( $mode === 'get' ) {
47454460 $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
47464461 }
47474462 $node = $node->getNextSibling();
47484463 } while ( $node );
47494464
47504465 // Write out the remainder (in replace mode only)
4751 - if ( $mode == 'replace' ) {
 4466+ if ( $mode === 'replace' ) {
47524467 // Output the replacement text
47534468 // Add two newlines on -- trailing whitespace in $newText is conventionally
47544469 // stripped by the editor, so we need both newlines to restore the paragraph gap
@@ -4977,7 +4692,7 @@
49784693 do {
49794694 $oldText = $text;
49804695 $text = $this->general->replace( $text );
4981 - } while ( $text != $oldText );
 4696+ } while ( $text !== $oldText );
49824697 wfProfileOut( __METHOD__ );
49834698 return $text;
49844699 }
@@ -4987,7 +4702,7 @@
49884703 do {
49894704 $oldText = $text;
49904705 $text = $this->nowiki->replace( $text );
4991 - } while ( $text != $oldText );
 4706+ } while ( $text !== $oldText );
49924707 wfProfileOut( __METHOD__ );
49934708 return $text;
49944709 }
@@ -4998,7 +4713,7 @@
49994714 $oldText = $text;
50004715 $text = $this->general->replace( $text );
50014716 $text = $this->nowiki->replace( $text );
5002 - } while ( $text != $oldText );
 4717+ } while ( $text !== $oldText );
50034718 wfProfileOut( __METHOD__ );
50044719 return $text;
50054720 }
@@ -5012,7 +4727,7 @@
50134728 var $output = '';
50144729
50154730 function replace( $matches ) {
5016 - if ( substr( $matches[1], -1 ) == "\n" ) {
 4731+ if ( substr( $matches[1], -1 ) === "\n" ) {
50174732 $this->output .= substr( $matches[1], 0, -1 );
50184733 } else {
50194734 $this->output .= $matches[1];
Index: trunk/phase3/includes/parser/Parser_DiffTest.php
@@ -69,9 +69,17 @@
7070 $lastResult = $currentResult;
7171 }
7272 if ( $mismatch ) {
 73+ if ( count( $results ) == 2 ) {
 74+ $results2 = array_values( $results );
 75+ $diff = wfDiff( var_export( $results2[0], true ), var_export( $results2[1], true ) );
 76+ } else {
 77+ $diff = '[too many parsers]';
 78+ }
7379 throw new MWException( "Parser_DiffTest: results mismatch on call to $name\n" .
7480 'Arguments: ' . $this->formatArray( $args ) . "\n" .
75 - 'Results: ' . $this->formatArray( $results ) . "\n" );
 81+ 'Results: ' . $this->formatArray( $results ) . "\n" .
 82+ "Diff: $diff\n"
 83+ );
7684 }
7785 return $lastResult;
7886 }
Index: trunk/phase3/includes/Title.php
@@ -410,6 +410,12 @@
411411 global $wgInterwikiCache, $wgContLang;
412412 $fname = 'Title::getInterwikiLink';
413413
 414+ if ( count( Title::$interwikiCache ) >= self::CACHE_MAX ) {
 415+ // Don't use infinite memory
 416+ reset( Title::$interwikiCache );
 417+ unset( Title::$interwikiCache[ key( Title::$interwikiCache ) ] );
 418+ }
 419+
414420 $key = $wgContLang->lc( $key );
415421
416422 $k = wfMemcKey( 'interwiki', $key );
Index: trunk/phase3/languages/LanguageConverter.php
@@ -435,8 +435,9 @@
436436 if ($isTitle) return $this->convertTitle($text);
437437
438438 $plang = $this->getPreferredVariant();
439 - $tarray = explode($this->mMarkup['end'], $text);
 439+ $tarray = StringUtils::explode($this->mMarkup['end'], $text);
440440 $text = '';
 441+ $lastDelim = false;
441442 foreach($tarray as $txt) {
442443 $marked = explode($this->mMarkup['begin'], $txt, 2);
443444
@@ -452,8 +453,17 @@
453454
454455 $text .= $crule->getDisplay();
455456 $this->applyManualConv($crule);
 457+ $lastDelim = false;
 458+ } else {
 459+ // Reinsert the }- which wasn't part of anything
 460+ $text .= $this->mMarkup['end'];
 461+ $lastDelim = true;
456462 }
457463 }
 464+ if ( $lastDelim ) {
 465+ // Remove the last delimiter (wasn't real)
 466+ $text = substr( $text, 0, -strlen( $this->mMarkup['end'] ) );
 467+ }
458468
459469 return $text;
460470 }
Index: trunk/phase3/languages/Language.php
@@ -177,6 +177,15 @@
178178 }
179179
180180 /**
 181+ * Reduce memory usage
 182+ */
 183+ function __destruct() {
 184+ foreach ( $this as $name => $value ) {
 185+ unset( $this->$name );
 186+ }
 187+ }
 188+
 189+ /**
181190 * Hook which will be called if this is the content language.
182191 * Descendants can use this to register hook functions or modify globals
183192 */
Index: trunk/phase3/RELEASE-NOTES
@@ -94,6 +94,7 @@
9595 * HTML entities like &nbsp; now work (are not escaped) in edit summaries.
9696 * (bug 13815) In the comment for page moves, use the colon-separator message
9797 instead of a hardcoded colon.
 98+* Allow <gallery> to accept image names without an Image: prefix
9899
99100 === Bug fixes in 1.14 ===
100101

Follow-up revisions

RevisionCommit summaryAuthorDate
r39980Revert r39949 "* Revert revert r39662 of my parser changes."...brion22:19, 25 August 2008

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r39464Revert r39414. Breaks processing links like [[:wikipedia:nl:User:Siebrand|Dut...siebrand09:33, 16 August 2008
r39662Revert Parser.php to r39295 good state....brion20:59, 19 August 2008

Status & tagging log