Index: trunk/phase3/maintenance/parserTests.inc |
— | — | @@ -26,7 +26,7 @@ |
27 | 27 | |
28 | 28 | /** */ |
29 | 29 | $options = array( 'quick', 'color', 'quiet', 'help', 'show-output', 'record' ); |
30 | | -$optionsWithArgs = array( 'regex' ); |
| 30 | +$optionsWithArgs = array( 'regex', 'seed' ); |
31 | 31 | |
32 | 32 | require_once( 'commandLine.inc' ); |
33 | 33 | require_once( "$IP/maintenance/parserTestsParserHook.php" ); |
— | — | @@ -62,6 +62,11 @@ |
63 | 63 | */ |
64 | 64 | private $oldTablePrefix; |
65 | 65 | |
| 66 | + private $maxFuzzHairLength = 20; |
| 67 | + private $maxFuzzTestLength = 1000; |
| 68 | + private $fuzzSeed = 0; |
| 69 | + private $memoryLimit = 50; |
| 70 | + |
66 | 71 | /** |
67 | 72 | * Sets terminal colorization and diff/quick modes depending on OS and |
68 | 73 | * command-line options (--color and --quick). |
— | — | @@ -117,6 +122,10 @@ |
118 | 123 | } |
119 | 124 | $this->keepUploads = isset( $options['keep-uploads'] ); |
120 | 125 | |
| 126 | + if ( isset( $options['seed'] ) ) { |
| 127 | + $this->fuzzSeed = intval( $options['seed'] ) - 1; |
| 128 | + } |
| 129 | + |
121 | 130 | $this->hooks = array(); |
122 | 131 | $this->functionHooks = array(); |
123 | 132 | } |
— | — | @@ -134,6 +143,116 @@ |
135 | 144 | } |
136 | 145 | |
137 | 146 | /** |
| 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 | + /** |
138 | 257 | * Run a series of tests listed in the given text files. |
139 | 258 | * Each test consists of a brief description, wikitext input, |
140 | 259 | * and the expected HTML output. |
— | — | @@ -267,6 +386,24 @@ |
268 | 387 | } |
269 | 388 | |
270 | 389 | /** |
| 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 | + /** |
271 | 408 | * Run a given wikitext input through a freshly-constructed wiki parser, |
272 | 409 | * and compare the output against the expected results. |
273 | 410 | * Prints status and explanatory messages to stdout. |
— | — | @@ -276,7 +413,6 @@ |
277 | 414 | * @return bool |
278 | 415 | */ |
279 | 416 | private function runTest( $desc, $input, $result, $opts ) { |
280 | | - global $wgParserConf; |
281 | 417 | if( $this->showProgress ) { |
282 | 418 | $this->showTesting( $desc ); |
283 | 419 | } |
— | — | @@ -300,18 +436,7 @@ |
301 | 437 | } |
302 | 438 | |
303 | 439 | $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(); |
316 | 441 | $title =& Title::makeTitle( NS_MAIN, $titleText ); |
317 | 442 | |
318 | 443 | $matches = array(); |
— | — | @@ -441,6 +566,7 @@ |
442 | 567 | $langObj = Language::factory( $lang ); |
443 | 568 | $GLOBALS['wgLang'] = $langObj; |
444 | 569 | $GLOBALS['wgContLang'] = $langObj; |
| 570 | + $GLOBALS['wgMemc'] = new FakeMemCachedClient; |
445 | 571 | |
446 | 572 | //$GLOBALS['wgMessageCache'] = new MessageCache( new BagOStuff(), false, 0, $GLOBALS['wgDBname'] ); |
447 | 573 | |
— | — | @@ -551,10 +677,10 @@ |
552 | 678 | # Hack: insert a few Wikipedia in-project interwiki prefixes, |
553 | 679 | # for testing inter-language links |
554 | 680 | $db->insert( 'interwiki', array( |
555 | | - array( 'iw_prefix' => 'Wikipedia', |
| 681 | + array( 'iw_prefix' => 'wikipedia', |
556 | 682 | 'iw_url' => 'http://en.wikipedia.org/wiki/$1', |
557 | 683 | 'iw_local' => 0 ), |
558 | | - array( 'iw_prefix' => 'MeatBall', |
| 684 | + array( 'iw_prefix' => 'meatball', |
559 | 685 | 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1', |
560 | 686 | 'iw_local' => 0 ), |
561 | 687 | array( 'iw_prefix' => 'zh', |
— | — | @@ -621,11 +747,12 @@ |
622 | 748 | return; |
623 | 749 | } |
624 | 750 | |
| 751 | + /* |
625 | 752 | $tables = $this->listTables(); |
626 | 753 | $db = wfGetDB( DB_MASTER ); |
627 | 754 | foreach ( $tables as $table ) { |
628 | 755 | $db->query( "DROP TABLE `parsertest_$table`" ); |
629 | | - } |
| 756 | + }*/ |
630 | 757 | } |
631 | 758 | |
632 | 759 | /** |
— | — | @@ -645,6 +772,10 @@ |
646 | 773 | } |
647 | 774 | |
648 | 775 | wfDebug( "Creating upload directory $dir\n" ); |
| 776 | + if ( file_exists( $dir ) ) { |
| 777 | + wfDebug( "Already exists!\n" ); |
| 778 | + return $dir; |
| 779 | + } |
649 | 780 | mkdir( $dir ); |
650 | 781 | mkdir( $dir . '/3' ); |
651 | 782 | mkdir( $dir . '/3/3a' ); |
— | — | @@ -658,6 +789,8 @@ |
659 | 790 | */ |
660 | 791 | private function teardownGlobals() { |
661 | 792 | RepoGroup::destroySingleton(); |
| 793 | + LinkCache::singleton()->clear(); |
| 794 | + $GLOBALS['wgLang']->__destruct(); |
662 | 795 | foreach( $this->savedGlobals as $var => $val ) { |
663 | 796 | $GLOBALS[$var] = $val; |
664 | 797 | } |
Index: trunk/phase3/maintenance/parserTests.php |
— | — | @@ -28,22 +28,21 @@ |
29 | 29 | if( isset( $options['help'] ) ) { |
30 | 30 | echo <<<ENDS |
31 | 31 | 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 | + |
37 | 34 | Options: |
38 | 35 | --quick Suppress diff output of failed tests |
39 | 36 | --quiet Suppress notification of passed tests (shows only failed tests) |
40 | 37 | --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 |
42 | 39 | use wgCommandLineDarkBg = true; if your term is dark |
43 | 40 | --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 |
45 | 42 | --record Record tests in database |
46 | 43 | --compare Compare with recorded results, without updating the database. |
47 | 44 | --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 |
48 | 47 | --help Show this help message |
49 | 48 | |
50 | 49 | |
— | — | @@ -67,7 +66,10 @@ |
68 | 67 | # Print out software version to assist with locating regressions |
69 | 68 | $version = SpecialVersion::getVersion(); |
70 | 69 | echo( "This is MediaWiki version {$version}.\n\n" ); |
71 | | -$ok = $tester->runTestsFromFiles( $files ); |
72 | 70 | |
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 @@ |
28 | 28 | if ( ! count( $argv ) ) { |
29 | 29 | $buf = $in; |
30 | 30 | 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 | + { |
32 | 34 | // Clear the buffer, we probably don't need to |
33 | 35 | $tmp = $buf; |
34 | 36 | $buf = null; |
35 | 37 | return $tmp; |
36 | 38 | } else |
37 | 39 | // wtf? |
38 | | - die( |
| 40 | + return |
39 | 41 | "\nCall this extension as <statictag>string</statictag> or as" . |
40 | 42 | " <statictag action=flush/>, not in any other way.\n" . |
41 | 43 | "text: " . var_export( $in, true ) . "\n" . |
42 | | - "argv: " . var_export( $argv, true ) . "\n" |
43 | | - ); |
| 44 | + "argv: " . var_export( $argv, true ) . "\n"; |
44 | 45 | } |
45 | 46 | |
Index: trunk/phase3/includes/parser/LinkHolderArray.php |
— | — | @@ -12,6 +12,15 @@ |
13 | 13 | } |
14 | 14 | |
15 | 15 | /** |
| 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 | + /** |
16 | 25 | * Merge another LinkHolderArray into this one |
17 | 26 | */ |
18 | 27 | function merge( $other ) { |
Index: trunk/phase3/includes/parser/Parser.php |
— | — | @@ -98,7 +98,7 @@ |
99 | 99 | # Cleared with clearState(): |
100 | 100 | var $mOutput, $mAutonumber, $mDTopen, $mStripState; |
101 | 101 | var $mIncludeCount, $mArgStack, $mLastSection, $mInPre; |
102 | | - var $mInterwikiLinkHolders, $mLinkHolders; |
| 102 | + var $mLinkHolders, $mLinkID; |
103 | 103 | var $mIncludeSizes, $mPPNodeCount, $mDefaultSort; |
104 | 104 | var $mTplExpandCache; // empty-frame expansion cache |
105 | 105 | var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores; |
— | — | @@ -143,6 +143,18 @@ |
144 | 144 | } |
145 | 145 | |
146 | 146 | /** |
| 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 | + /** |
147 | 159 | * Do various kinds of initialisation on the first call of the parser |
148 | 160 | */ |
149 | 161 | function firstCallInit() { |
— | — | @@ -179,17 +191,8 @@ |
180 | 192 | $this->mStripState = new StripState; |
181 | 193 | $this->mArgStack = false; |
182 | 194 | $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; |
194 | 197 | $this->mRevisionTimestamp = $this->mRevisionId = null; |
195 | 198 | |
196 | 199 | /** |
— | — | @@ -204,7 +207,7 @@ |
205 | 208 | */ |
206 | 209 | #$this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString(); |
207 | 210 | # Changed to \x7f to allow XML double-parsing -- TS |
208 | | - $this->mUniqPrefix = "\x7fUNIQ" . Parser::getRandomString(); |
| 211 | + $this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString(); |
209 | 212 | |
210 | 213 | |
211 | 214 | # Clear these on every parse, bug 4549 |
— | — | @@ -294,7 +297,7 @@ |
295 | 298 | */ |
296 | 299 | |
297 | 300 | global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang; |
298 | | - $fname = 'Parser::parse-' . wfGetCaller(); |
| 301 | + $fname = __METHOD__.'-' . wfGetCaller(); |
299 | 302 | wfProfileIn( __METHOD__ ); |
300 | 303 | wfProfileIn( $fname ); |
301 | 304 | |
— | — | @@ -328,7 +331,6 @@ |
329 | 332 | ); |
330 | 333 | $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text ); |
331 | 334 | |
332 | | - # only once and last |
333 | 335 | $text = $this->doBlockLevels( $text, $linestart ); |
334 | 336 | |
335 | 337 | $this->replaceLinkHolders( $text ); |
— | — | @@ -348,7 +350,7 @@ |
349 | 351 | $uniq_prefix = $this->mUniqPrefix; |
350 | 352 | $matches = array(); |
351 | 353 | $elements = array_keys( $this->mTransparentTagHooks ); |
352 | | - $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); |
| 354 | + $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); |
353 | 355 | |
354 | 356 | foreach( $matches as $marker => $data ) { |
355 | 357 | list( $element, $content, $params, $tag ) = $data; |
— | — | @@ -366,7 +368,7 @@ |
367 | 369 | $text = Sanitizer::normalizeCharReferences( $text ); |
368 | 370 | |
369 | 371 | if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) { |
370 | | - $text = Parser::tidy($text); |
| 372 | + $text = self::tidy($text); |
371 | 373 | } else { |
372 | 374 | # attempt to sanitize at least some nesting problems |
373 | 375 | # (bug #2702 and quite a few others) |
— | — | @@ -471,6 +473,8 @@ |
472 | 474 | function &getTitle() { return $this->mTitle; } |
473 | 475 | function getOptions() { return $this->mOptions; } |
474 | 476 | function getRevisionId() { return $this->mRevisionId; } |
| 477 | + function getOutput() { return $this->mOutput; } |
| 478 | + function nextLinkID() { return $this->mLinkID++; } |
475 | 479 | |
476 | 480 | function getFunctionLang() { |
477 | 481 | global $wgLang, $wgContLang; |
— | — | @@ -549,7 +553,7 @@ |
550 | 554 | $text = $inside; |
551 | 555 | $tail = null; |
552 | 556 | } else { |
553 | | - if( $element == '!--' ) { |
| 557 | + if( $element === '!--' ) { |
554 | 558 | $end = '/(-->)/'; |
555 | 559 | } else { |
556 | 560 | $end = "/(<\\/$element\\s*>)/i"; |
— | — | @@ -658,9 +662,9 @@ |
659 | 663 | ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>'. |
660 | 664 | '<head><title>test</title></head><body>'.$text.'</body></html>'; |
661 | 665 | if( $wgTidyInternal ) { |
662 | | - $correctedtext = Parser::internalTidy( $wrappedtext ); |
| 666 | + $correctedtext = self::internalTidy( $wrappedtext ); |
663 | 667 | } else { |
664 | | - $correctedtext = Parser::externalTidy( $wrappedtext ); |
| 668 | + $correctedtext = self::externalTidy( $wrappedtext ); |
665 | 669 | } |
666 | 670 | if( is_null( $correctedtext ) ) { |
667 | 671 | wfDebug( "Tidy error detected!\n" ); |
— | — | @@ -677,8 +681,7 @@ |
678 | 682 | */ |
679 | 683 | function externalTidy( $text ) { |
680 | 684 | global $wgTidyConf, $wgTidyBin, $wgTidyOpts; |
681 | | - $fname = 'Parser::externalTidy'; |
682 | | - wfProfileIn( $fname ); |
| 685 | + wfProfileIn( __METHOD__ ); |
683 | 686 | |
684 | 687 | $cleansource = ''; |
685 | 688 | $opts = ' -utf8'; |
— | — | @@ -707,7 +710,7 @@ |
708 | 711 | } |
709 | 712 | } |
710 | 713 | |
711 | | - wfProfileOut( $fname ); |
| 714 | + wfProfileOut( __METHOD__ ); |
712 | 715 | |
713 | 716 | if( $cleansource == '' && $text != '') { |
714 | 717 | // Some kind of error happened, so we couldn't get the corrected text. |
— | — | @@ -729,8 +732,7 @@ |
730 | 733 | */ |
731 | 734 | function internalTidy( $text ) { |
732 | 735 | global $wgTidyConf, $IP, $wgDebugTidy; |
733 | | - $fname = 'Parser::internalTidy'; |
734 | | - wfProfileIn( $fname ); |
| 736 | + wfProfileIn( __METHOD__ ); |
735 | 737 | |
736 | 738 | $tidy = new tidy; |
737 | 739 | $tidy->parseString( $text, $wgTidyConf, 'utf8' ); |
— | — | @@ -748,7 +750,7 @@ |
749 | 751 | "\n-->"; |
750 | 752 | } |
751 | 753 | |
752 | | - wfProfileOut( $fname ); |
| 754 | + wfProfileOut( __METHOD__ ); |
753 | 755 | return $cleansource; |
754 | 756 | } |
755 | 757 | |
— | — | @@ -758,34 +760,35 @@ |
759 | 761 | * @private |
760 | 762 | */ |
761 | 763 | function doTableStuff ( $text ) { |
762 | | - $fname = 'Parser::doTableStuff'; |
763 | | - wfProfileIn( $fname ); |
| 764 | + wfProfileIn( __METHOD__ ); |
764 | 765 | |
765 | | - $lines = explode ( "\n" , $text ); |
| 766 | + $lines = StringUtils::explode( "\n", $text ); |
| 767 | + $out = ''; |
766 | 768 | $td_history = array (); // Is currently a td tag open? |
767 | 769 | $last_tag_history = array (); // Save history of last lag activated (td, th or caption) |
768 | 770 | $tr_history = array (); // Is currently a tr tag open? |
769 | 771 | $tr_attributes = array (); // history of tr attributes |
770 | 772 | $has_opened_tr = array(); // Did this table open a <tr> element? |
771 | 773 | $indent_level = 0; // indent level of the table |
772 | | - foreach ( $lines as $key => $line ) |
773 | | - { |
774 | | - $line = trim ( $line ); |
775 | 774 | |
| 775 | + foreach ( $lines as $outLine ) { |
| 776 | + $line = trim( $outLine ); |
| 777 | + |
776 | 778 | if( $line == '' ) { // empty line, go to next line |
| 779 | + $out .= $outLine."\n"; |
777 | 780 | continue; |
778 | 781 | } |
779 | | - $first_character = $line{0}; |
| 782 | + $first_character = $line[0]; |
780 | 783 | $matches = array(); |
781 | 784 | |
782 | | - if ( preg_match( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) { |
| 785 | + if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) { |
783 | 786 | // First check if we are starting a new table |
784 | 787 | $indent_level = strlen( $matches[1] ); |
785 | 788 | |
786 | 789 | $attributes = $this->mStripState->unstripBoth( $matches[2] ); |
787 | 790 | $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' ); |
788 | 791 | |
789 | | - $lines[$key] = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; |
| 792 | + $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; |
790 | 793 | array_push ( $td_history , false ); |
791 | 794 | array_push ( $last_tag_history , '' ); |
792 | 795 | array_push ( $tr_history , false ); |
— | — | @@ -793,8 +796,9 @@ |
794 | 797 | array_push ( $has_opened_tr , false ); |
795 | 798 | } else if ( count ( $td_history ) == 0 ) { |
796 | 799 | // Don't do any of the following |
| 800 | + $out .= $outLine."\n"; |
797 | 801 | continue; |
798 | | - } else if ( substr ( $line , 0 , 2 ) == '|}' ) { |
| 802 | + } else if ( substr ( $line , 0 , 2 ) === '|}' ) { |
799 | 803 | // We are ending a table |
800 | 804 | $line = '</table>' . substr ( $line , 2 ); |
801 | 805 | $last_tag = array_pop ( $last_tag_history ); |
— | — | @@ -811,8 +815,8 @@ |
812 | 816 | $line = "</{$last_tag}>{$line}"; |
813 | 817 | } |
814 | 818 | 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 ) === '|-' ) { |
817 | 821 | // Now we have a table row |
818 | 822 | $line = preg_replace( '#^\|-+#', '', $line ); |
819 | 823 | |
— | — | @@ -835,21 +839,21 @@ |
836 | 840 | $line = "</{$last_tag}>{$line}"; |
837 | 841 | } |
838 | 842 | |
839 | | - $lines[$key] = $line; |
| 843 | + $outLine = $line; |
840 | 844 | array_push ( $tr_history , false ); |
841 | 845 | array_push ( $td_history , false ); |
842 | 846 | array_push ( $last_tag_history , '' ); |
843 | 847 | } |
844 | | - else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 ) == '|+' ) { |
| 848 | + else if ( $first_character === '|' || $first_character === '!' || substr ( $line , 0 , 2 ) === '|+' ) { |
845 | 849 | // This might be cell elements, td, th or captions |
846 | | - if ( substr ( $line , 0 , 2 ) == '|+' ) { |
| 850 | + if ( substr ( $line , 0 , 2 ) === '|+' ) { |
847 | 851 | $first_character = '+'; |
848 | 852 | $line = substr ( $line , 1 ); |
849 | 853 | } |
850 | 854 | |
851 | 855 | $line = substr ( $line , 1 ); |
852 | 856 | |
853 | | - if ( $first_character == '!' ) { |
| 857 | + if ( $first_character === '!' ) { |
854 | 858 | $line = str_replace ( '!!' , '||' , $line ); |
855 | 859 | } |
856 | 860 | |
— | — | @@ -859,13 +863,13 @@ |
860 | 864 | // attribute values containing literal "||". |
861 | 865 | $cells = StringUtils::explodeMarkup( '||' , $line ); |
862 | 866 | |
863 | | - $lines[$key] = ''; |
| 867 | + $outLine = ''; |
864 | 868 | |
865 | 869 | // Loop through each table cell |
866 | 870 | foreach ( $cells as $cell ) |
867 | 871 | { |
868 | 872 | $previous = ''; |
869 | | - if ( $first_character != '+' ) |
| 873 | + if ( $first_character !== '+' ) |
870 | 874 | { |
871 | 875 | $tr_after = array_pop ( $tr_attributes ); |
872 | 876 | if ( !array_pop ( $tr_history ) ) { |
— | — | @@ -883,11 +887,11 @@ |
884 | 888 | $previous = "</{$last_tag}>{$previous}"; |
885 | 889 | } |
886 | 890 | |
887 | | - if ( $first_character == '|' ) { |
| 891 | + if ( $first_character === '|' ) { |
888 | 892 | $last_tag = 'td'; |
889 | | - } else if ( $first_character == '!' ) { |
| 893 | + } else if ( $first_character === '!' ) { |
890 | 894 | $last_tag = 'th'; |
891 | | - } else if ( $first_character == '+' ) { |
| 895 | + } else if ( $first_character === '+' ) { |
892 | 896 | $last_tag = 'caption'; |
893 | 897 | } else { |
894 | 898 | $last_tag = ''; |
— | — | @@ -910,38 +914,42 @@ |
911 | 915 | $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}"; |
912 | 916 | } |
913 | 917 | |
914 | | - $lines[$key] .= $cell; |
| 918 | + $outLine .= $cell; |
915 | 919 | array_push ( $td_history , true ); |
916 | 920 | } |
917 | 921 | } |
| 922 | + $out .= $outLine . "\n"; |
918 | 923 | } |
919 | 924 | |
920 | 925 | // Closing open td, tr && table |
921 | 926 | while ( count ( $td_history ) > 0 ) |
922 | 927 | { |
923 | 928 | if ( array_pop ( $td_history ) ) { |
924 | | - $lines[] = '</td>' ; |
| 929 | + $out .= "</td>\n"; |
925 | 930 | } |
926 | 931 | if ( array_pop ( $tr_history ) ) { |
927 | | - $lines[] = '</tr>' ; |
| 932 | + $out .= "</tr>\n"; |
928 | 933 | } |
929 | 934 | if ( !array_pop ( $has_opened_tr ) ) { |
930 | | - $lines[] = "<tr><td></td></tr>" ; |
| 935 | + $out .= "<tr><td></td></tr>\n" ; |
931 | 936 | } |
932 | 937 | |
933 | | - $lines[] = '</table>' ; |
| 938 | + $out .= "</table>\n"; |
934 | 939 | } |
935 | 940 | |
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 | + } |
937 | 945 | |
938 | 946 | // 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 = ''; |
941 | 949 | } |
942 | 950 | |
943 | | - wfProfileOut( $fname ); |
| 951 | + wfProfileOut( __METHOD__ ); |
944 | 952 | |
945 | | - return $output; |
| 953 | + return $out; |
946 | 954 | } |
947 | 955 | |
948 | 956 | /** |
— | — | @@ -952,12 +960,11 @@ |
953 | 961 | */ |
954 | 962 | function internalParse( $text ) { |
955 | 963 | $isMain = true; |
956 | | - $fname = 'Parser::internalParse'; |
957 | | - wfProfileIn( $fname ); |
| 964 | + wfProfileIn( __METHOD__ ); |
958 | 965 | |
959 | 966 | # Hook to suspend the parser in this state |
960 | 967 | if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$this->mStripState ) ) ) { |
961 | | - wfProfileOut( $fname ); |
| 968 | + wfProfileOut( __METHOD__ ); |
962 | 969 | return $text ; |
963 | 970 | } |
964 | 971 | |
— | — | @@ -990,14 +997,15 @@ |
991 | 998 | $text = $this->doMagicLinks( $text ); |
992 | 999 | $text = $this->formatHeadings( $text, $isMain ); |
993 | 1000 | |
994 | | - wfProfileOut( $fname ); |
| 1001 | + wfProfileOut( __METHOD__ ); |
995 | 1002 | return $text; |
996 | 1003 | } |
997 | 1004 | |
998 | 1005 | /** |
999 | 1006 | * Replace special strings like "ISBN xxx" and "RFC xxx" with |
1000 | 1007 | * magic external links. |
1001 | | - * |
| 1008 | + * |
| 1009 | + * DML |
1002 | 1010 | * @private |
1003 | 1011 | */ |
1004 | 1012 | function doMagicLinks( $text ) { |
— | — | @@ -1018,10 +1026,10 @@ |
1019 | 1027 | } |
1020 | 1028 | |
1021 | 1029 | function magicLinkCallback( $m ) { |
1022 | | - if ( substr( $m[0], 0, 1 ) == '<' ) { |
| 1030 | + if ( substr( $m[0], 0, 1 ) === '<' ) { |
1023 | 1031 | # Skip HTML element |
1024 | 1032 | return $m[0]; |
1025 | | - } elseif ( substr( $m[0], 0, 4 ) == 'ISBN' ) { |
| 1033 | + } elseif ( substr( $m[0], 0, 4 ) === 'ISBN' ) { |
1026 | 1034 | $isbn = $m[2]; |
1027 | 1035 | $num = strtr( $isbn, array( |
1028 | 1036 | '-' => '', |
— | — | @@ -1033,11 +1041,11 @@ |
1034 | 1042 | $titleObj->escapeLocalUrl() . |
1035 | 1043 | "\" class=\"internal\">ISBN $isbn</a>"; |
1036 | 1044 | } else { |
1037 | | - if ( substr( $m[0], 0, 3 ) == 'RFC' ) { |
| 1045 | + if ( substr( $m[0], 0, 3 ) === 'RFC' ) { |
1038 | 1046 | $keyword = 'RFC'; |
1039 | 1047 | $urlmsg = 'rfcurl'; |
1040 | 1048 | $id = $m[1]; |
1041 | | - } elseif ( substr( $m[0], 0, 4 ) == 'PMID' ) { |
| 1049 | + } elseif ( substr( $m[0], 0, 4 ) === 'PMID' ) { |
1042 | 1050 | $keyword = 'PMID'; |
1043 | 1051 | $urlmsg = 'pubmedurl'; |
1044 | 1052 | $id = $m[1]; |
— | — | @@ -1060,14 +1068,13 @@ |
1061 | 1069 | * @private |
1062 | 1070 | */ |
1063 | 1071 | function doHeadings( $text ) { |
1064 | | - $fname = 'Parser::doHeadings'; |
1065 | | - wfProfileIn( $fname ); |
| 1072 | + wfProfileIn( __METHOD__ ); |
1066 | 1073 | for ( $i = 6; $i >= 1; --$i ) { |
1067 | 1074 | $h = str_repeat( '=', $i ); |
1068 | 1075 | $text = preg_replace( "/^$h(.+)$h\\s*$/m", |
1069 | 1076 | "<h$i>\\1</h$i>", $text ); |
1070 | 1077 | } |
1071 | | - wfProfileOut( $fname ); |
| 1078 | + wfProfileOut( __METHOD__ ); |
1072 | 1079 | return $text; |
1073 | 1080 | } |
1074 | 1081 | |
— | — | @@ -1077,15 +1084,14 @@ |
1078 | 1085 | * @return string the altered text |
1079 | 1086 | */ |
1080 | 1087 | function doAllQuotes( $text ) { |
1081 | | - $fname = 'Parser::doAllQuotes'; |
1082 | | - wfProfileIn( $fname ); |
| 1088 | + wfProfileIn( __METHOD__ ); |
1083 | 1089 | $outtext = ''; |
1084 | | - $lines = explode( "\n", $text ); |
| 1090 | + $lines = StringUtils::explode( "\n", $text ); |
1085 | 1091 | foreach ( $lines as $line ) { |
1086 | | - $outtext .= $this->doQuotes ( $line ) . "\n"; |
| 1092 | + $outtext .= $this->doQuotes( $line ) . "\n"; |
1087 | 1093 | } |
1088 | 1094 | $outtext = substr($outtext, 0,-1); |
1089 | | - wfProfileOut( $fname ); |
| 1095 | + wfProfileOut( __METHOD__ ); |
1090 | 1096 | return $outtext; |
1091 | 1097 | } |
1092 | 1098 | |
— | — | @@ -1147,9 +1153,9 @@ |
1148 | 1154 | { |
1149 | 1155 | $x1 = substr ($arr[$i-1], -1); |
1150 | 1156 | $x2 = substr ($arr[$i-1], -2, 1); |
1151 | | - if ($x1 == ' ') { |
| 1157 | + if ($x1 === ' ') { |
1152 | 1158 | if ($firstspace == -1) $firstspace = $i; |
1153 | | - } else if ($x2 == ' ') { |
| 1159 | + } else if ($x2 === ' ') { |
1154 | 1160 | if ($firstsingleletterword == -1) $firstsingleletterword = $i; |
1155 | 1161 | } else { |
1156 | 1162 | if ($firstmultiletterword == -1) $firstmultiletterword = $i; |
— | — | @@ -1189,7 +1195,7 @@ |
1190 | 1196 | { |
1191 | 1197 | if (($i % 2) == 0) |
1192 | 1198 | { |
1193 | | - if ($state == 'both') |
| 1199 | + if ($state === 'both') |
1194 | 1200 | $buffer .= $r; |
1195 | 1201 | else |
1196 | 1202 | $output .= $r; |
— | — | @@ -1198,41 +1204,41 @@ |
1199 | 1205 | { |
1200 | 1206 | if (strlen ($r) == 2) |
1201 | 1207 | { |
1202 | | - if ($state == 'i') |
| 1208 | + if ($state === 'i') |
1203 | 1209 | { $output .= '</i>'; $state = ''; } |
1204 | | - else if ($state == 'bi') |
| 1210 | + else if ($state === 'bi') |
1205 | 1211 | { $output .= '</i>'; $state = 'b'; } |
1206 | | - else if ($state == 'ib') |
| 1212 | + else if ($state === 'ib') |
1207 | 1213 | { $output .= '</b></i><b>'; $state = 'b'; } |
1208 | | - else if ($state == 'both') |
| 1214 | + else if ($state === 'both') |
1209 | 1215 | { $output .= '<b><i>'.$buffer.'</i>'; $state = 'b'; } |
1210 | 1216 | else # $state can be 'b' or '' |
1211 | 1217 | { $output .= '<i>'; $state .= 'i'; } |
1212 | 1218 | } |
1213 | 1219 | else if (strlen ($r) == 3) |
1214 | 1220 | { |
1215 | | - if ($state == 'b') |
| 1221 | + if ($state === 'b') |
1216 | 1222 | { $output .= '</b>'; $state = ''; } |
1217 | | - else if ($state == 'bi') |
| 1223 | + else if ($state === 'bi') |
1218 | 1224 | { $output .= '</i></b><i>'; $state = 'i'; } |
1219 | | - else if ($state == 'ib') |
| 1225 | + else if ($state === 'ib') |
1220 | 1226 | { $output .= '</b>'; $state = 'i'; } |
1221 | | - else if ($state == 'both') |
| 1227 | + else if ($state === 'both') |
1222 | 1228 | { $output .= '<i><b>'.$buffer.'</b>'; $state = 'i'; } |
1223 | 1229 | else # $state can be 'i' or '' |
1224 | 1230 | { $output .= '<b>'; $state .= 'b'; } |
1225 | 1231 | } |
1226 | 1232 | else if (strlen ($r) == 5) |
1227 | 1233 | { |
1228 | | - if ($state == 'b') |
| 1234 | + if ($state === 'b') |
1229 | 1235 | { $output .= '</b><i>'; $state = 'i'; } |
1230 | | - else if ($state == 'i') |
| 1236 | + else if ($state === 'i') |
1231 | 1237 | { $output .= '</i><b>'; $state = 'b'; } |
1232 | | - else if ($state == 'bi') |
| 1238 | + else if ($state === 'bi') |
1233 | 1239 | { $output .= '</i></b>'; $state = ''; } |
1234 | | - else if ($state == 'ib') |
| 1240 | + else if ($state === 'ib') |
1235 | 1241 | { $output .= '</b></i>'; $state = ''; } |
1236 | | - else if ($state == 'both') |
| 1242 | + else if ($state === 'both') |
1237 | 1243 | { $output .= '<i><b>'.$buffer.'</b></i>'; $state = ''; } |
1238 | 1244 | else # ($state == '') |
1239 | 1245 | { $buffer = ''; $state = 'both'; } |
— | — | @@ -1241,21 +1247,21 @@ |
1242 | 1248 | $i++; |
1243 | 1249 | } |
1244 | 1250 | # Now close all remaining tags. Notice that the order is important. |
1245 | | - if ($state == 'b' || $state == 'ib') |
| 1251 | + if ($state === 'b' || $state === 'ib') |
1246 | 1252 | $output .= '</b>'; |
1247 | | - if ($state == 'i' || $state == 'bi' || $state == 'ib') |
| 1253 | + if ($state === 'i' || $state === 'bi' || $state === 'ib') |
1248 | 1254 | $output .= '</i>'; |
1249 | | - if ($state == 'bi') |
| 1255 | + if ($state === 'bi') |
1250 | 1256 | $output .= '</b>'; |
1251 | 1257 | # There might be lonely ''''', so make sure we have a buffer |
1252 | | - if ($state == 'both' && $buffer) |
| 1258 | + if ($state === 'both' && $buffer) |
1253 | 1259 | $output .= '<b><i>'.$buffer.'</i></b>'; |
1254 | 1260 | return $output; |
1255 | 1261 | } |
1256 | 1262 | } |
1257 | 1263 | |
1258 | 1264 | /** |
1259 | | - * Replace external links |
| 1265 | + * Replace external links (REL) |
1260 | 1266 | * |
1261 | 1267 | * Note: this is all very hackish and the order of execution matters a lot. |
1262 | 1268 | * Make sure to run maintenance/parserTests.php if you change this code. |
— | — | @@ -1264,8 +1270,7 @@ |
1265 | 1271 | */ |
1266 | 1272 | function replaceExternalLinks( $text ) { |
1267 | 1273 | global $wgContLang; |
1268 | | - $fname = 'Parser::replaceExternalLinks'; |
1269 | | - wfProfileIn( $fname ); |
| 1274 | + wfProfileIn( __METHOD__ ); |
1270 | 1275 | |
1271 | 1276 | $sk = $this->mOptions->getSkin(); |
1272 | 1277 | |
— | — | @@ -1299,7 +1304,7 @@ |
1300 | 1305 | $dtrail = ''; |
1301 | 1306 | |
1302 | 1307 | # Set linktype for CSS - if URL==text, link is essentially free |
1303 | | - $linktype = ($text == $url) ? 'free' : 'text'; |
| 1308 | + $linktype = ($text === $url) ? 'free' : 'text'; |
1304 | 1309 | |
1305 | 1310 | # No link text, e.g. [http://domain.tld/some.link] |
1306 | 1311 | if ( $text == '' ) { |
— | — | @@ -1335,11 +1340,11 @@ |
1336 | 1341 | # Register link in the output object. |
1337 | 1342 | # Replace unnecessary URL escape codes with the referenced character |
1338 | 1343 | # This prevents spammers from hiding links from the filters |
1339 | | - $pasteurized = Parser::replaceUnusualEscapes( $url ); |
| 1344 | + $pasteurized = self::replaceUnusualEscapes( $url ); |
1340 | 1345 | $this->mOutput->addExternalLink( $pasteurized ); |
1341 | 1346 | } |
1342 | 1347 | |
1343 | | - wfProfileOut( $fname ); |
| 1348 | + wfProfileOut( __METHOD__ ); |
1344 | 1349 | return $s; |
1345 | 1350 | } |
1346 | 1351 | |
— | — | @@ -1349,8 +1354,7 @@ |
1350 | 1355 | */ |
1351 | 1356 | function replaceFreeExternalLinks( $text ) { |
1352 | 1357 | global $wgContLang; |
1353 | | - $fname = 'Parser::replaceFreeExternalLinks'; |
1354 | | - wfProfileIn( $fname ); |
| 1358 | + wfProfileIn( __METHOD__ ); |
1355 | 1359 | |
1356 | 1360 | $bits = preg_split( '/(\b(?:' . wfUrlProtocols() . '))/S', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); |
1357 | 1361 | $s = array_shift( $bits ); |
— | — | @@ -1412,7 +1416,7 @@ |
1413 | 1417 | $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() ); |
1414 | 1418 | # Register it in the output object... |
1415 | 1419 | # Replace unnecessary URL escape codes with their equivalent characters |
1416 | | - $pasteurized = Parser::replaceUnusualEscapes( $url ); |
| 1420 | + $pasteurized = self::replaceUnusualEscapes( $url ); |
1417 | 1421 | $this->mOutput->addExternalLink( $pasteurized ); |
1418 | 1422 | } |
1419 | 1423 | $s .= $text . $trail; |
— | — | @@ -1420,7 +1424,7 @@ |
1421 | 1425 | $s .= $protocol . $remainder; |
1422 | 1426 | } |
1423 | 1427 | } |
1424 | | - wfProfileOut( $fname ); |
| 1428 | + wfProfileOut( __METHOD__ ); |
1425 | 1429 | return $s; |
1426 | 1430 | } |
1427 | 1431 | |
— | — | @@ -1436,7 +1440,7 @@ |
1437 | 1441 | */ |
1438 | 1442 | static function replaceUnusualEscapes( $url ) { |
1439 | 1443 | return preg_replace_callback( '/%[0-9A-Fa-f]{2}/', |
1440 | | - array( 'Parser', 'replaceUnusualEscapesCallback' ), $url ); |
| 1444 | + array( __CLASS__, 'replaceUnusualEscapesCallback' ), $url ); |
1441 | 1445 | } |
1442 | 1446 | |
1443 | 1447 | /** |
— | — | @@ -1480,35 +1484,48 @@ |
1481 | 1485 | |
1482 | 1486 | /** |
1483 | 1487 | * Process [[ ]] wikilinks |
| 1488 | + * @return processed text |
1484 | 1489 | * |
1485 | 1490 | * @private |
1486 | 1491 | */ |
1487 | 1492 | 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 ) { |
1488 | 1504 | global $wgContLang; |
1489 | | - static $fname = 'Parser::replaceInternalLinks' ; |
1490 | 1505 | |
1491 | | - wfProfileIn( $fname ); |
| 1506 | + wfProfileIn( __METHOD__ ); |
1492 | 1507 | |
1493 | | - wfProfileIn( $fname.'-setup' ); |
1494 | | - static $tc = FALSE; |
| 1508 | + wfProfileIn( __METHOD__.'-setup' ); |
| 1509 | + static $tc = FALSE, $e1, $e1_img; |
1495 | 1510 | # 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 | + } |
1497 | 1518 | |
1498 | 1519 | $sk = $this->mOptions->getSkin(); |
| 1520 | + $holders = new LinkHolderArray( $this ); |
1499 | 1521 | |
1500 | 1522 | #split the entire text string on occurences of [[ |
1501 | | - $a = explode( '[[', ' ' . $s ); |
| 1523 | + $a = StringUtils::explode( '[[', ' ' . $s ); |
1502 | 1524 | #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" |
1504 | 1528 | $s = substr( $s, 1 ); |
1505 | 1529 | |
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 | | - |
1513 | 1530 | $useLinkPrefixExtension = $wgContLang->linkPrefixExtension(); |
1514 | 1531 | $e2 = null; |
1515 | 1532 | if ( $useLinkPrefixExtension ) { |
— | — | @@ -1518,8 +1535,8 @@ |
1519 | 1536 | } |
1520 | 1537 | |
1521 | 1538 | if( is_null( $this->mTitle ) ) { |
1522 | | - wfProfileOut( $fname ); |
1523 | | - wfProfileOut( $fname.'-setup' ); |
| 1539 | + wfProfileOut( __METHOD__.'-setup' ); |
| 1540 | + wfProfileOut( __METHOD__ ); |
1524 | 1541 | throw new MWException( __METHOD__.": \$this->mTitle is null\n" ); |
1525 | 1542 | } |
1526 | 1543 | $nottalk = !$this->mTitle->isTalkPage(); |
— | — | @@ -1541,13 +1558,20 @@ |
1542 | 1559 | $selflink = array($this->mTitle->getPrefixedText()); |
1543 | 1560 | } |
1544 | 1561 | $useSubpages = $this->areSubpagesAllowed(); |
1545 | | - wfProfileOut( $fname.'-setup' ); |
| 1562 | + wfProfileOut( __METHOD__.'-setup' ); |
1546 | 1563 | |
1547 | 1564 | # 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 | + |
1550 | 1574 | if ( $useLinkPrefixExtension ) { |
1551 | | - wfProfileIn( $fname.'-prefixhandling' ); |
| 1575 | + wfProfileIn( __METHOD__.'-prefixhandling' ); |
1552 | 1576 | if ( preg_match( $e2, $s, $m ) ) { |
1553 | 1577 | $prefix = $m[2]; |
1554 | 1578 | $s = $m[1]; |
— | — | @@ -1559,12 +1583,12 @@ |
1560 | 1584 | $prefix = $first_prefix; |
1561 | 1585 | $first_prefix = false; |
1562 | 1586 | } |
1563 | | - wfProfileOut( $fname.'-prefixhandling' ); |
| 1587 | + wfProfileOut( __METHOD__.'-prefixhandling' ); |
1564 | 1588 | } |
1565 | 1589 | |
1566 | 1590 | $might_be_img = false; |
1567 | 1591 | |
1568 | | - wfProfileIn( "$fname-e1" ); |
| 1592 | + wfProfileIn( __METHOD__."-e1" ); |
1569 | 1593 | if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt |
1570 | 1594 | $text = $m[2]; |
1571 | 1595 | # If we get a ] at the beginning of $m[3] that means we have a link that's something like: |
— | — | @@ -1598,18 +1622,18 @@ |
1599 | 1623 | $trail = ""; |
1600 | 1624 | } else { # Invalid form; output directly |
1601 | 1625 | $s .= $prefix . '[[' . $line ; |
1602 | | - wfProfileOut( "$fname-e1" ); |
| 1626 | + wfProfileOut( __METHOD__."-e1" ); |
1603 | 1627 | continue; |
1604 | 1628 | } |
1605 | | - wfProfileOut( "$fname-e1" ); |
1606 | | - wfProfileIn( "$fname-misc" ); |
| 1629 | + wfProfileOut( __METHOD__."-e1" ); |
| 1630 | + wfProfileIn( __METHOD__."-misc" ); |
1607 | 1631 | |
1608 | 1632 | # Don't allow internal links to pages containing |
1609 | 1633 | # PROTO: where PROTO is a valid URL protocol; these |
1610 | 1634 | # should be external links. |
1611 | 1635 | if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) { |
1612 | 1636 | $s .= $prefix . '[[' . $line ; |
1613 | | - wfProfileOut( "$fname-misc" ); |
| 1637 | + wfProfileOut( __METHOD__."-misc" ); |
1614 | 1638 | continue; |
1615 | 1639 | } |
1616 | 1640 | |
— | — | @@ -1620,33 +1644,36 @@ |
1621 | 1645 | $link = $m[1]; |
1622 | 1646 | } |
1623 | 1647 | |
1624 | | - $noforce = (substr($m[1], 0, 1) != ':'); |
| 1648 | + $noforce = (substr($m[1], 0, 1) !== ':'); |
1625 | 1649 | if (!$noforce) { |
1626 | 1650 | # Strip off leading ':' |
1627 | 1651 | $link = substr($link, 1); |
1628 | 1652 | } |
1629 | 1653 | |
1630 | | - wfProfileOut( "$fname-misc" ); |
1631 | | - wfProfileIn( "$fname-title" ); |
| 1654 | + wfProfileOut( __METHOD__."-misc" ); |
| 1655 | + wfProfileIn( __METHOD__."-title" ); |
1632 | 1656 | $nt = Title::newFromText( $this->mStripState->unstripNoWiki($link) ); |
1633 | 1657 | if( !$nt ) { |
1634 | 1658 | $s .= $prefix . '[[' . $line; |
1635 | | - wfProfileOut( "$fname-title" ); |
| 1659 | + wfProfileOut( __METHOD__."-title" ); |
1636 | 1660 | continue; |
1637 | 1661 | } |
1638 | 1662 | |
1639 | 1663 | $ns = $nt->getNamespace(); |
1640 | 1664 | $iw = $nt->getInterWiki(); |
1641 | | - wfProfileOut( "$fname-title" ); |
| 1665 | + wfProfileOut( __METHOD__."-title" ); |
1642 | 1666 | |
1643 | 1667 | if ($might_be_img) { # if this is actually an invalid link |
1644 | | - wfProfileIn( "$fname-might_be_img" ); |
| 1668 | + wfProfileIn( __METHOD__."-might_be_img" ); |
1645 | 1669 | if ($ns == NS_IMAGE && $noforce) { #but might be an image |
1646 | 1670 | $found = false; |
1647 | | - while (isset ($a[$k+1]) ) { |
| 1671 | + while ( true ) { |
1648 | 1672 | #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 | + } |
1651 | 1678 | $m = explode( ']]', $next_line, 3 ); |
1652 | 1679 | if ( count( $m ) == 3 ) { |
1653 | 1680 | # the first ]] closes the inner link, the second the image |
— | — | @@ -1666,19 +1693,19 @@ |
1667 | 1694 | if ( !$found ) { |
1668 | 1695 | # we couldn't find the end of this imageLink, so output it raw |
1669 | 1696 | #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 ) ); |
1671 | 1698 | $s .= "{$prefix}[[$link|$text"; |
1672 | 1699 | # note: no $trail, because without an end, there *is* no trail |
1673 | | - wfProfileOut( "$fname-might_be_img" ); |
| 1700 | + wfProfileOut( __METHOD__."-might_be_img" ); |
1674 | 1701 | continue; |
1675 | 1702 | } |
1676 | 1703 | } else { #it's not an image, so output it raw |
1677 | 1704 | $s .= "{$prefix}[[$link|$text"; |
1678 | 1705 | # note: no $trail, because without an end, there *is* no trail |
1679 | | - wfProfileOut( "$fname-might_be_img" ); |
| 1706 | + wfProfileOut( __METHOD__."-might_be_img" ); |
1680 | 1707 | continue; |
1681 | 1708 | } |
1682 | | - wfProfileOut( "$fname-might_be_img" ); |
| 1709 | + wfProfileOut( __METHOD__."-might_be_img" ); |
1683 | 1710 | } |
1684 | 1711 | |
1685 | 1712 | $wasblank = ( '' == $text ); |
— | — | @@ -1688,41 +1715,36 @@ |
1689 | 1716 | if( $noforce ) { |
1690 | 1717 | |
1691 | 1718 | # Interwikis |
1692 | | - wfProfileIn( "$fname-interwiki" ); |
| 1719 | + wfProfileIn( __METHOD__."-interwiki" ); |
1693 | 1720 | if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) { |
1694 | 1721 | $this->mOutput->addLanguageLink( $nt->getFullText() ); |
1695 | 1722 | $s = rtrim($s . $prefix); |
1696 | 1723 | $s .= trim($trail, "\n") == '' ? '': $prefix . $trail; |
1697 | | - wfProfileOut( "$fname-interwiki" ); |
| 1724 | + wfProfileOut( __METHOD__."-interwiki" ); |
1698 | 1725 | continue; |
1699 | 1726 | } |
1700 | | - wfProfileOut( "$fname-interwiki" ); |
| 1727 | + wfProfileOut( __METHOD__."-interwiki" ); |
1701 | 1728 | |
1702 | 1729 | if ( $ns == NS_IMAGE ) { |
1703 | | - wfProfileIn( "$fname-image" ); |
| 1730 | + wfProfileIn( __METHOD__."-image" ); |
1704 | 1731 | if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) { |
1705 | 1732 | # recursively parse links inside the image caption |
1706 | 1733 | # actually, this will parse them in any other parameters, too, |
1707 | 1734 | # but it might be hard to fix that, and it doesn't matter ATM |
1708 | 1735 | $text = $this->replaceExternalLinks($text); |
1709 | | - $text = $this->replaceInternalLinks($text); |
| 1736 | + $holders->merge( $this->replaceInternalLinks2( $text ) ); |
1710 | 1737 | |
1711 | 1738 | # 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; |
1720 | 1740 | } |
1721 | | - wfProfileOut( "$fname-image" ); |
| 1741 | + $this->mOutput->addImage( $nt->getDBkey() ); |
| 1742 | + wfProfileOut( __METHOD__."-image" ); |
| 1743 | + continue; |
1722 | 1744 | |
1723 | 1745 | } |
1724 | 1746 | |
1725 | 1747 | if ( $ns == NS_CATEGORY ) { |
1726 | | - wfProfileIn( "$fname-category" ); |
| 1748 | + wfProfileIn( __METHOD__."-category" ); |
1727 | 1749 | $s = rtrim($s . "\n"); # bug 87 |
1728 | 1750 | |
1729 | 1751 | if ( $wasblank ) { |
— | — | @@ -1741,7 +1763,7 @@ |
1742 | 1764 | */ |
1743 | 1765 | $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail; |
1744 | 1766 | |
1745 | | - wfProfileOut( "$fname-category" ); |
| 1767 | + wfProfileOut( __METHOD__."-category" ); |
1746 | 1768 | continue; |
1747 | 1769 | } |
1748 | 1770 | } |
— | — | @@ -1772,7 +1794,7 @@ |
1773 | 1795 | if( SpecialPage::exists( $nt->getDBkey() ) ) { |
1774 | 1796 | $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix ); |
1775 | 1797 | } else { |
1776 | | - $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix ); |
| 1798 | + $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix ); |
1777 | 1799 | } |
1778 | 1800 | continue; |
1779 | 1801 | } elseif( $ns == NS_IMAGE ) { |
— | — | @@ -1786,10 +1808,10 @@ |
1787 | 1809 | continue; |
1788 | 1810 | } |
1789 | 1811 | } |
1790 | | - $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix ); |
| 1812 | + $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix ); |
1791 | 1813 | } |
1792 | | - wfProfileOut( $fname ); |
1793 | | - return $s; |
| 1814 | + wfProfileOut( __METHOD__ ); |
| 1815 | + return $holders; |
1794 | 1816 | } |
1795 | 1817 | |
1796 | 1818 | /** |
— | — | @@ -1798,32 +1820,10 @@ |
1799 | 1821 | * parsing of interwiki links, and secondly to allow all existence checks and |
1800 | 1822 | * article length checks (for stub links) to be bundled into a single query. |
1801 | 1823 | * |
| 1824 | + * @deprecated |
1802 | 1825 | */ |
1803 | 1826 | 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 ); |
1828 | 1828 | } |
1829 | 1829 | |
1830 | 1830 | /** |
— | — | @@ -1889,8 +1889,7 @@ |
1890 | 1890 | # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage |
1891 | 1891 | # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage |
1892 | 1892 | |
1893 | | - $fname = 'Parser::maybeDoSubpageLink'; |
1894 | | - wfProfileIn( $fname ); |
| 1893 | + wfProfileIn( __METHOD__ ); |
1895 | 1894 | $ret = $target; # default return value is no change |
1896 | 1895 | |
1897 | 1896 | # Some namespaces don't allow subpages, |
— | — | @@ -1906,7 +1905,7 @@ |
1907 | 1906 | # bug 7425 |
1908 | 1907 | $target = trim( $target ); |
1909 | 1908 | # Look at the first character |
1910 | | - if( $target != '' && $target{0} == '/' ) { |
| 1909 | + if( $target != '' && $target{0} === '/' ) { |
1911 | 1910 | # / at end means we don't want the slash to be shown |
1912 | 1911 | $m = array(); |
1913 | 1912 | $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m ); |
— | — | @@ -1933,7 +1932,7 @@ |
1934 | 1933 | if( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page |
1935 | 1934 | $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) ); |
1936 | 1935 | # / at the end means don't show full path |
1937 | | - if( substr( $nodotdot, -1, 1 ) == '/' ) { |
| 1936 | + if( substr( $nodotdot, -1, 1 ) === '/' ) { |
1938 | 1937 | $nodotdot = substr( $nodotdot, 0, -1 ); |
1939 | 1938 | if( '' === $text ) { |
1940 | 1939 | $text = $nodotdot . $suffix; |
— | — | @@ -1949,7 +1948,7 @@ |
1950 | 1949 | } |
1951 | 1950 | } |
1952 | 1951 | |
1953 | | - wfProfileOut( $fname ); |
| 1952 | + wfProfileOut( __METHOD__ ); |
1954 | 1953 | return $ret; |
1955 | 1954 | } |
1956 | 1955 | |
— | — | @@ -1985,10 +1984,10 @@ |
1986 | 1985 | /* private */ function openList( $char ) { |
1987 | 1986 | $result = $this->closeParagraph(); |
1988 | 1987 | |
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 ) { |
1993 | 1992 | $result .= '<dl><dt>'; |
1994 | 1993 | $this->mDTopen = true; |
1995 | 1994 | } |
— | — | @@ -1998,11 +1997,11 @@ |
1999 | 1998 | } |
2000 | 1999 | |
2001 | 2000 | /* 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 ) { |
2004 | 2003 | $close = '</dd>'; |
2005 | 2004 | if ( $this->mDTopen ) { $close = '</dt>'; } |
2006 | | - if ( ';' == $char ) { |
| 2005 | + if ( ';' === $char ) { |
2007 | 2006 | $this->mDTopen = true; |
2008 | 2007 | return $close . '<dt>'; |
2009 | 2008 | } else { |
— | — | @@ -2014,9 +2013,9 @@ |
2015 | 2014 | } |
2016 | 2015 | |
2017 | 2016 | /* 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 ) { |
2021 | 2020 | if ( $this->mDTopen ) { |
2022 | 2021 | $this->mDTopen = false; |
2023 | 2022 | $text = '</dt></dl>'; |
— | — | @@ -2030,56 +2029,59 @@ |
2031 | 2030 | /**#@-*/ |
2032 | 2031 | |
2033 | 2032 | /** |
2034 | | - * Make lists from lines starting with ':', '*', '#', etc. |
| 2033 | + * Make lists from lines starting with ':', '*', '#', etc. (DBL) |
2035 | 2034 | * |
2036 | 2035 | * @private |
2037 | 2036 | * @return string the lists rendered as HTML |
2038 | 2037 | */ |
2039 | 2038 | function doBlockLevels( $text, $linestart ) { |
2040 | | - $fname = 'Parser::doBlockLevels'; |
2041 | | - wfProfileIn( $fname ); |
| 2039 | + wfProfileIn( __METHOD__ ); |
2042 | 2040 | |
2043 | 2041 | # Parsing through the text line by line. The main thing |
2044 | 2042 | # happening here is handling of block-level elements p, pre, |
2045 | 2043 | # and making lists from lines starting with * # : etc. |
2046 | 2044 | # |
2047 | | - $textLines = explode( "\n", $text ); |
| 2045 | + $textLines = StringUtils::explode( "\n", $text ); |
2048 | 2046 | |
2049 | 2047 | $lastPrefix = $output = ''; |
2050 | 2048 | $this->mDTopen = $inBlockElem = false; |
2051 | 2049 | $prefixLength = 0; |
2052 | 2050 | $paragraphStack = false; |
2053 | 2051 | |
2054 | | - if ( !$linestart ) { |
2055 | | - $output .= array_shift( $textLines ); |
2056 | | - } |
2057 | 2052 | foreach ( $textLines as $oLine ) { |
| 2053 | + # Fix up $linestart |
| 2054 | + if ( !$linestart ) { |
| 2055 | + $output .= $oLine; |
| 2056 | + $linestart = true; |
| 2057 | + continue; |
| 2058 | + } |
| 2059 | + |
2058 | 2060 | $lastPrefixLength = strlen( $lastPrefix ); |
2059 | 2061 | $preCloseMatch = preg_match('/<\\/pre/i', $oLine ); |
2060 | 2062 | $preOpenMatch = preg_match('/<pre/i', $oLine ); |
2061 | 2063 | if ( !$this->mInPre ) { |
2062 | 2064 | # Multiple prefixes may abut each other for nested lists. |
2063 | 2065 | $prefixLength = strspn( $oLine, '*#:;' ); |
2064 | | - $pref = substr( $oLine, 0, $prefixLength ); |
| 2066 | + $prefix = substr( $oLine, 0, $prefixLength ); |
2065 | 2067 | |
2066 | 2068 | # eh? |
2067 | | - $pref2 = str_replace( ';', ':', $pref ); |
| 2069 | + $prefix2 = str_replace( ';', ':', $prefix ); |
2068 | 2070 | $t = substr( $oLine, $prefixLength ); |
2069 | | - $this->mInPre = !empty($preOpenMatch); |
| 2071 | + $this->mInPre = (bool)$preOpenMatch; |
2070 | 2072 | } else { |
2071 | 2073 | # Don't interpret any other prefixes in preformatted text |
2072 | 2074 | $prefixLength = 0; |
2073 | | - $pref = $pref2 = ''; |
| 2075 | + $prefix = $prefix2 = ''; |
2074 | 2076 | $t = $oLine; |
2075 | 2077 | } |
2076 | 2078 | |
2077 | 2079 | # List generation |
2078 | | - if( $prefixLength && 0 == strcmp( $lastPrefix, $pref2 ) ) { |
| 2080 | + if( $prefixLength && $lastPrefix === $prefix2 ) { |
2079 | 2081 | # 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 ) ); |
2081 | 2083 | $paragraphStack = false; |
2082 | 2084 | |
2083 | | - if ( substr( $pref, -1 ) == ';') { |
| 2085 | + if ( substr( $prefix, -1 ) === ';') { |
2084 | 2086 | # The one nasty exception: definition lists work like this: |
2085 | 2087 | # ; title : definition text |
2086 | 2088 | # So we check for : in the remainder text to split up the |
— | — | @@ -2092,21 +2094,21 @@ |
2093 | 2095 | } |
2094 | 2096 | } elseif( $prefixLength || $lastPrefixLength ) { |
2095 | 2097 | # Either open or close a level... |
2096 | | - $commonPrefixLength = $this->getCommon( $pref, $lastPrefix ); |
| 2098 | + $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix ); |
2097 | 2099 | $paragraphStack = false; |
2098 | 2100 | |
2099 | 2101 | while( $commonPrefixLength < $lastPrefixLength ) { |
2100 | | - $output .= $this->closeList( $lastPrefix{$lastPrefixLength-1} ); |
| 2102 | + $output .= $this->closeList( $lastPrefix[$lastPrefixLength-1] ); |
2101 | 2103 | --$lastPrefixLength; |
2102 | 2104 | } |
2103 | 2105 | if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) { |
2104 | | - $output .= $this->nextItem( $pref{$commonPrefixLength-1} ); |
| 2106 | + $output .= $this->nextItem( $prefix[$commonPrefixLength-1] ); |
2105 | 2107 | } |
2106 | 2108 | while ( $prefixLength > $commonPrefixLength ) { |
2107 | | - $char = substr( $pref, $commonPrefixLength, 1 ); |
| 2109 | + $char = substr( $prefix, $commonPrefixLength, 1 ); |
2108 | 2110 | $output .= $this->openList( $char ); |
2109 | 2111 | |
2110 | | - if ( ';' == $char ) { |
| 2112 | + if ( ';' === $char ) { |
2111 | 2113 | # FIXME: This is dupe of code above |
2112 | 2114 | if ($this->findColonNoLinks($t, $term, $t2) !== false) { |
2113 | 2115 | $t = $t2; |
— | — | @@ -2115,10 +2117,10 @@ |
2116 | 2118 | } |
2117 | 2119 | ++$commonPrefixLength; |
2118 | 2120 | } |
2119 | | - $lastPrefix = $pref2; |
| 2121 | + $lastPrefix = $prefix2; |
2120 | 2122 | } |
2121 | 2123 | if( 0 == $prefixLength ) { |
2122 | | - wfProfileIn( "$fname-paragraph" ); |
| 2124 | + wfProfileIn( __METHOD__."-paragraph" ); |
2123 | 2125 | # No prefix (not in list)--go to paragraph mode |
2124 | 2126 | // XXX: use a stack for nestable elements like span, table and div |
2125 | 2127 | $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 @@ |
2139 | 2141 | $inBlockElem = true; |
2140 | 2142 | } |
2141 | 2143 | } 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) != '' ) ) { |
2143 | 2145 | // pre |
2144 | | - if ($this->mLastSection != 'pre') { |
| 2146 | + if ($this->mLastSection !== 'pre') { |
2145 | 2147 | $paragraphStack = false; |
2146 | 2148 | $output .= $this->closeParagraph().'<pre>'; |
2147 | 2149 | $this->mLastSection = 'pre'; |
— | — | @@ -2154,7 +2156,7 @@ |
2155 | 2157 | $paragraphStack = false; |
2156 | 2158 | $this->mLastSection = 'p'; |
2157 | 2159 | } else { |
2158 | | - if ($this->mLastSection != 'p' ) { |
| 2160 | + if ($this->mLastSection !== 'p' ) { |
2159 | 2161 | $output .= $this->closeParagraph(); |
2160 | 2162 | $this->mLastSection = ''; |
2161 | 2163 | $paragraphStack = '<p>'; |
— | — | @@ -2167,14 +2169,14 @@ |
2168 | 2170 | $output .= $paragraphStack; |
2169 | 2171 | $paragraphStack = false; |
2170 | 2172 | $this->mLastSection = 'p'; |
2171 | | - } else if ($this->mLastSection != 'p') { |
| 2173 | + } else if ($this->mLastSection !== 'p') { |
2172 | 2174 | $output .= $this->closeParagraph().'<p>'; |
2173 | 2175 | $this->mLastSection = 'p'; |
2174 | 2176 | } |
2175 | 2177 | } |
2176 | 2178 | } |
2177 | 2179 | } |
2178 | | - wfProfileOut( "$fname-paragraph" ); |
| 2180 | + wfProfileOut( __METHOD__."-paragraph" ); |
2179 | 2181 | } |
2180 | 2182 | // somewhere above we forget to get out of pre block (bug 785) |
2181 | 2183 | if($preCloseMatch && $this->mInPre) { |
— | — | @@ -2185,7 +2187,7 @@ |
2186 | 2188 | } |
2187 | 2189 | } |
2188 | 2190 | while ( $prefixLength ) { |
2189 | | - $output .= $this->closeList( $pref2{$prefixLength-1} ); |
| 2191 | + $output .= $this->closeList( $prefix2[$prefixLength-1] ); |
2190 | 2192 | --$prefixLength; |
2191 | 2193 | } |
2192 | 2194 | if ( '' != $this->mLastSection ) { |
— | — | @@ -2193,7 +2195,7 @@ |
2194 | 2196 | $this->mLastSection = ''; |
2195 | 2197 | } |
2196 | 2198 | |
2197 | | - wfProfileOut( $fname ); |
| 2199 | + wfProfileOut( __METHOD__ ); |
2198 | 2200 | return $output; |
2199 | 2201 | } |
2200 | 2202 | |
— | — | @@ -2206,13 +2208,12 @@ |
2207 | 2209 | * return string the position of the ':', or false if none found |
2208 | 2210 | */ |
2209 | 2211 | function findColonNoLinks($str, &$before, &$after) { |
2210 | | - $fname = 'Parser::findColonNoLinks'; |
2211 | | - wfProfileIn( $fname ); |
| 2212 | + wfProfileIn( __METHOD__ ); |
2212 | 2213 | |
2213 | 2214 | $pos = strpos( $str, ':' ); |
2214 | 2215 | if( $pos === false ) { |
2215 | 2216 | // Nothing to find! |
2216 | | - wfProfileOut( $fname ); |
| 2217 | + wfProfileOut( __METHOD__ ); |
2217 | 2218 | return false; |
2218 | 2219 | } |
2219 | 2220 | |
— | — | @@ -2221,7 +2222,7 @@ |
2222 | 2223 | // Easy; no tag nesting to worry about |
2223 | 2224 | $before = substr( $str, 0, $pos ); |
2224 | 2225 | $after = substr( $str, $pos+1 ); |
2225 | | - wfProfileOut( $fname ); |
| 2226 | + wfProfileOut( __METHOD__ ); |
2226 | 2227 | return $pos; |
2227 | 2228 | } |
2228 | 2229 | |
— | — | @@ -2245,7 +2246,7 @@ |
2246 | 2247 | // We found it! |
2247 | 2248 | $before = substr( $str, 0, $i ); |
2248 | 2249 | $after = substr( $str, $i + 1 ); |
2249 | | - wfProfileOut( $fname ); |
| 2250 | + wfProfileOut( __METHOD__ ); |
2250 | 2251 | return $i; |
2251 | 2252 | } |
2252 | 2253 | // Embedded in a tag; don't break it. |
— | — | @@ -2255,7 +2256,7 @@ |
2256 | 2257 | $colon = strpos( $str, ':', $i ); |
2257 | 2258 | if( $colon === false ) { |
2258 | 2259 | // Nothing else interesting |
2259 | | - wfProfileOut( $fname ); |
| 2260 | + wfProfileOut( __METHOD__ ); |
2260 | 2261 | return false; |
2261 | 2262 | } |
2262 | 2263 | $lt = strpos( $str, '<', $i ); |
— | — | @@ -2264,7 +2265,7 @@ |
2265 | 2266 | // We found it! |
2266 | 2267 | $before = substr( $str, 0, $colon ); |
2267 | 2268 | $after = substr( $str, $colon + 1 ); |
2268 | | - wfProfileOut( $fname ); |
| 2269 | + wfProfileOut( __METHOD__ ); |
2269 | 2270 | return $i; |
2270 | 2271 | } |
2271 | 2272 | } |
— | — | @@ -2311,18 +2312,18 @@ |
2312 | 2313 | break; |
2313 | 2314 | case 3: // self::COLON_STATE_CLOSETAG: |
2314 | 2315 | // In a </tag> |
2315 | | - if( $c == ">" ) { |
| 2316 | + if( $c === ">" ) { |
2316 | 2317 | $stack--; |
2317 | 2318 | 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__ ); |
2320 | 2321 | return false; |
2321 | 2322 | } |
2322 | 2323 | $state = self::COLON_STATE_TEXT; |
2323 | 2324 | } |
2324 | 2325 | break; |
2325 | 2326 | case self::COLON_STATE_TAGSLASH: |
2326 | | - if( $c == ">" ) { |
| 2327 | + if( $c === ">" ) { |
2327 | 2328 | // Yes, a self-closed tag <blah/> |
2328 | 2329 | $state = self::COLON_STATE_TEXT; |
2329 | 2330 | } else { |
— | — | @@ -2331,33 +2332,33 @@ |
2332 | 2333 | } |
2333 | 2334 | break; |
2334 | 2335 | case 5: // self::COLON_STATE_COMMENT: |
2335 | | - if( $c == "-" ) { |
| 2336 | + if( $c === "-" ) { |
2336 | 2337 | $state = self::COLON_STATE_COMMENTDASH; |
2337 | 2338 | } |
2338 | 2339 | break; |
2339 | 2340 | case self::COLON_STATE_COMMENTDASH: |
2340 | | - if( $c == "-" ) { |
| 2341 | + if( $c === "-" ) { |
2341 | 2342 | $state = self::COLON_STATE_COMMENTDASHDASH; |
2342 | 2343 | } else { |
2343 | 2344 | $state = self::COLON_STATE_COMMENT; |
2344 | 2345 | } |
2345 | 2346 | break; |
2346 | 2347 | case self::COLON_STATE_COMMENTDASHDASH: |
2347 | | - if( $c == ">" ) { |
| 2348 | + if( $c === ">" ) { |
2348 | 2349 | $state = self::COLON_STATE_TEXT; |
2349 | 2350 | } else { |
2350 | 2351 | $state = self::COLON_STATE_COMMENT; |
2351 | 2352 | } |
2352 | 2353 | break; |
2353 | 2354 | default: |
2354 | | - throw new MWException( "State machine error in $fname" ); |
| 2355 | + throw new MWException( "State machine error in " . __METHOD__ ); |
2355 | 2356 | } |
2356 | 2357 | } |
2357 | 2358 | 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" ); |
2359 | 2360 | return false; |
2360 | 2361 | } |
2361 | | - wfProfileOut( $fname ); |
| 2362 | + wfProfileOut( __METHOD__ ); |
2362 | 2363 | return false; |
2363 | 2364 | } |
2364 | 2365 | |
— | — | @@ -2587,12 +2588,11 @@ |
2588 | 2589 | * @private |
2589 | 2590 | */ |
2590 | 2591 | function initialiseVariables() { |
2591 | | - $fname = 'Parser::initialiseVariables'; |
2592 | | - wfProfileIn( $fname ); |
| 2592 | + wfProfileIn( __METHOD__ ); |
2593 | 2593 | $variableIDs = MagicWord::getVariableIDs(); |
2594 | 2594 | |
2595 | 2595 | $this->mVariables = new MagicWordArray( $variableIDs ); |
2596 | | - wfProfileOut( $fname ); |
| 2596 | + wfProfileOut( __METHOD__ ); |
2597 | 2597 | } |
2598 | 2598 | |
2599 | 2599 | /** |
— | — | @@ -2661,8 +2661,7 @@ |
2662 | 2662 | return $text; |
2663 | 2663 | } |
2664 | 2664 | |
2665 | | - $fname = __METHOD__; |
2666 | | - wfProfileIn( $fname ); |
| 2665 | + wfProfileIn( __METHOD__ ); |
2667 | 2666 | |
2668 | 2667 | if ( $frame === false ) { |
2669 | 2668 | $frame = $this->getPreprocessor()->newFrame(); |
— | — | @@ -2675,7 +2674,7 @@ |
2676 | 2675 | $flags = $argsOnly ? PPFrame::NO_TEMPLATES : 0; |
2677 | 2676 | $text = $frame->expand( $dom, $flags ); |
2678 | 2677 | |
2679 | | - wfProfileOut( $fname ); |
| 2678 | + wfProfileOut( __METHOD__ ); |
2680 | 2679 | return $text; |
2681 | 2680 | } |
2682 | 2681 | |
— | — | @@ -2738,8 +2737,7 @@ |
2739 | 2738 | */ |
2740 | 2739 | function braceSubstitution( $piece, $frame ) { |
2741 | 2740 | global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces; |
2742 | | - $fname = __METHOD__; |
2743 | | - wfProfileIn( $fname ); |
| 2741 | + wfProfileIn( __METHOD__ ); |
2744 | 2742 | wfProfileIn( __METHOD__.'-setup' ); |
2745 | 2743 | |
2746 | 2744 | # Flags |
— | — | @@ -2926,7 +2924,7 @@ |
2927 | 2925 | } |
2928 | 2926 | } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) { |
2929 | 2927 | $found = false; //access denied |
2930 | | - wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() ); |
| 2928 | + wfDebug( __METHOD__.": template inclusion denied for " . $title->getPrefixedDBkey() ); |
2931 | 2929 | } else { |
2932 | 2930 | list( $text, $title ) = $this->getTemplateDom( $title ); |
2933 | 2931 | if ( $text !== false ) { |
— | — | @@ -2960,7 +2958,7 @@ |
2961 | 2959 | # Recover the source wikitext and return it |
2962 | 2960 | if ( !$found ) { |
2963 | 2961 | $text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args ); |
2964 | | - wfProfileOut( $fname ); |
| 2962 | + wfProfileOut( __METHOD__ ); |
2965 | 2963 | return array( 'object' => $text ); |
2966 | 2964 | } |
2967 | 2965 | |
— | — | @@ -3019,7 +3017,7 @@ |
3020 | 3018 | $ret = array( 'text' => $text ); |
3021 | 3019 | } |
3022 | 3020 | |
3023 | | - wfProfileOut( $fname ); |
| 3021 | + wfProfileOut( __METHOD__ ); |
3024 | 3022 | return $ret; |
3025 | 3023 | } |
3026 | 3024 | |
— | — | @@ -3306,7 +3304,7 @@ |
3307 | 3305 | } |
3308 | 3306 | } |
3309 | 3307 | |
3310 | | - if ( $name == 'html' || $name == 'nowiki' ) { |
| 3308 | + if ( $name === 'html' || $name === 'nowiki' ) { |
3311 | 3309 | $this->mStripState->nowiki->setPair( $marker, $output ); |
3312 | 3310 | } else { |
3313 | 3311 | $this->mStripState->general->setPair( $marker, $output ); |
— | — | @@ -3562,12 +3560,7 @@ |
3563 | 3561 | # <!--LINK number--> |
3564 | 3562 | # turns into |
3565 | 3563 | # 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 ); |
3572 | 3565 | |
3573 | 3566 | # Strip out HTML (other than plain <sup> and <sub>: bug 8393) |
3574 | 3567 | $tocline = preg_replace( |
— | — | @@ -3643,7 +3636,7 @@ |
3644 | 3637 | $i = 0; |
3645 | 3638 | |
3646 | 3639 | foreach( $blocks as $block ) { |
3647 | | - if( $showEditLink && $headlineCount > 0 && $i == 0 && $block != "\n" ) { |
| 3640 | + if( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) { |
3648 | 3641 | # This is the [edit] link that appears for the top block of text when |
3649 | 3642 | # section editing is enabled |
3650 | 3643 | |
— | — | @@ -3795,7 +3788,7 @@ |
3796 | 3789 | } else { |
3797 | 3790 | # Failed to validate; fall back to the default |
3798 | 3791 | $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" ); |
3800 | 3793 | } |
3801 | 3794 | } |
3802 | 3795 | |
— | — | @@ -3901,19 +3894,17 @@ |
3902 | 3895 | global $wgTitle; |
3903 | 3896 | static $executing = false; |
3904 | 3897 | |
3905 | | - $fname = "Parser::transformMsg"; |
3906 | | - |
3907 | 3898 | # Guard against infinite recursion |
3908 | 3899 | if ( $executing ) { |
3909 | 3900 | return $text; |
3910 | 3901 | } |
3911 | 3902 | $executing = true; |
3912 | 3903 | |
3913 | | - wfProfileIn($fname); |
| 3904 | + wfProfileIn(__METHOD__); |
3914 | 3905 | $text = $this->preprocess( $text, $wgTitle, $options ); |
3915 | 3906 | |
3916 | 3907 | $executing = false; |
3917 | | - wfProfileOut($fname); |
| 3908 | + wfProfileOut(__METHOD__); |
3918 | 3909 | return $text; |
3919 | 3910 | } |
3920 | 3911 | |
— | — | @@ -4010,7 +4001,7 @@ |
4011 | 4002 | # Add to function cache |
4012 | 4003 | $mw = MagicWord::get( $id ); |
4013 | 4004 | if( !$mw ) |
4014 | | - throw new MWException( 'Parser::setFunctionHook() expecting a magic word identifier.' ); |
| 4005 | + throw new MWException( __METHOD__.'() expecting a magic word identifier.' ); |
4015 | 4006 | |
4016 | 4007 | $synonyms = $mw->getSynonyms(); |
4017 | 4008 | $sensitive = intval( $mw->isCaseSensitive() ); |
— | — | @@ -4025,7 +4016,7 @@ |
4026 | 4017 | $syn = '#' . $syn; |
4027 | 4018 | } |
4028 | 4019 | # Remove trailing colon |
4029 | | - if ( substr( $syn, -1, 1 ) == ':' ) { |
| 4020 | + if ( substr( $syn, -1, 1 ) === ':' ) { |
4030 | 4021 | $syn = substr( $syn, 0, -1 ); |
4031 | 4022 | } |
4032 | 4023 | $this->mFunctionSynonyms[$sensitive][$syn] = $id; |
— | — | @@ -4046,266 +4037,9 @@ |
4047 | 4038 | * Replace <!--LINK--> link placeholders with actual links, in the buffer |
4048 | 4039 | * Placeholders created in Skin::makeLinkObj() |
4049 | 4040 | * Returns an array of link CSS classes, indexed by PDBK. |
4050 | | - * $options is a bit field, RLH_FOR_UPDATE to select for update |
4051 | 4041 | */ |
4052 | 4042 | 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 ); |
4310 | 4044 | } |
4311 | 4045 | |
4312 | 4046 | /** |
— | — | @@ -4315,39 +4049,10 @@ |
4316 | 4050 | * @return string |
4317 | 4051 | */ |
4318 | 4052 | 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 ); |
4329 | 4054 | } |
4330 | 4055 | |
4331 | 4056 | /** |
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 | | - /** |
4352 | 4057 | * Tag hook handler for 'pre'. |
4353 | 4058 | */ |
4354 | 4059 | function renderPreTag( $text, $attribs ) { |
— | — | @@ -4398,7 +4103,7 @@ |
4399 | 4104 | |
4400 | 4105 | wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) ); |
4401 | 4106 | |
4402 | | - $lines = explode( "\n", $text ); |
| 4107 | + $lines = StringUtils::explode( "\n", $text ); |
4403 | 4108 | foreach ( $lines as $line ) { |
4404 | 4109 | # match lines like these: |
4405 | 4110 | # Image:someimage.jpg|This is some image |
— | — | @@ -4411,7 +4116,7 @@ |
4412 | 4117 | |
4413 | 4118 | if ( strpos( $matches[0], '%' ) !== false ) |
4414 | 4119 | $matches[1] = urldecode( $matches[1] ); |
4415 | | - $tp = Title::newFromText( $matches[1] ); |
| 4120 | + $tp = Title::newFromText( $matches[1], NS_IMAGE ); |
4416 | 4121 | $nt =& $tp; |
4417 | 4122 | if( is_null( $nt ) ) { |
4418 | 4123 | # Bogus title. Ignore these so we don't bomb out later. |
— | — | @@ -4477,8 +4182,11 @@ |
4478 | 4183 | |
4479 | 4184 | /** |
4480 | 4185 | * Parse image options text and use it to make an image |
| 4186 | + * @param Title $title |
| 4187 | + * @param string $options |
| 4188 | + * @param LinkHolderArray $holders |
4481 | 4189 | */ |
4482 | | - function makeImage( $title, $options ) { |
| 4190 | + function makeImage( $title, $options, $holders = false ) { |
4483 | 4191 | # Check if the options text is of the form "options|alt text" |
4484 | 4192 | # Options are: |
4485 | 4193 | # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang |
— | — | @@ -4501,7 +4209,7 @@ |
4502 | 4210 | # * bottom |
4503 | 4211 | # * text-bottom |
4504 | 4212 | |
4505 | | - $parts = array_map( 'trim', explode( '|', $options) ); |
| 4213 | + $parts = StringUtils::explode( "|", $options ); |
4506 | 4214 | $sk = $this->mOptions->getSkin(); |
4507 | 4215 | |
4508 | 4216 | # Give extensions a chance to select the file revision for us |
— | — | @@ -4523,13 +4231,14 @@ |
4524 | 4232 | $params = array( 'frame' => array(), 'handler' => array(), |
4525 | 4233 | 'horizAlign' => array(), 'vertAlign' => array() ); |
4526 | 4234 | foreach( $parts as $part ) { |
| 4235 | + $part = trim( $part ); |
4527 | 4236 | list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part ); |
4528 | 4237 | $validated = false; |
4529 | 4238 | if( isset( $paramMap[$magicName] ) ) { |
4530 | 4239 | list( $type, $paramName ) = $paramMap[$magicName]; |
4531 | 4240 | |
4532 | 4241 | // Special case; width and height come in one variable together |
4533 | | - if( $type == 'handler' && $paramName == 'width' ) { |
| 4242 | + if( $type === 'handler' && $paramName === 'width' ) { |
4534 | 4243 | $m = array(); |
4535 | 4244 | # (bug 13500) In both cases (width/height and width only), |
4536 | 4245 | # permit trailing "px" for backward compatibility. |
— | — | @@ -4552,7 +4261,7 @@ |
4553 | 4262 | } |
4554 | 4263 | } // else no validation -- bug 13436 |
4555 | 4264 | } else { |
4556 | | - if ( $type == 'handler' ) { |
| 4265 | + if ( $type === 'handler' ) { |
4557 | 4266 | # Validate handler parameter |
4558 | 4267 | $validated = $handler->validateParam( $paramName, $value ); |
4559 | 4268 | } else { |
— | — | @@ -4588,7 +4297,13 @@ |
4589 | 4298 | } |
4590 | 4299 | |
4591 | 4300 | # 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 | + } |
4593 | 4308 | |
4594 | 4309 | # make sure there are no placeholders in thumbnail attributes |
4595 | 4310 | # that are later expanded to html- so expand them now and |
— | — | @@ -4691,7 +4406,7 @@ |
4692 | 4407 | $sectionParts = explode( '-', $section ); |
4693 | 4408 | $sectionIndex = array_pop( $sectionParts ); |
4694 | 4409 | foreach ( $sectionParts as $part ) { |
4695 | | - if ( $part == 'T' ) { |
| 4410 | + if ( $part === 'T' ) { |
4696 | 4411 | $flags |= self::PTD_FOR_INCLUSION; |
4697 | 4412 | } |
4698 | 4413 | } |
— | — | @@ -4708,14 +4423,14 @@ |
4709 | 4424 | $targetLevel = 1000; |
4710 | 4425 | } else { |
4711 | 4426 | while ( $node ) { |
4712 | | - if ( $node->getName() == 'h' ) { |
| 4427 | + if ( $node->getName() === 'h' ) { |
4713 | 4428 | $bits = $node->splitHeading(); |
4714 | 4429 | if ( $bits['i'] == $sectionIndex ) { |
4715 | 4430 | $targetLevel = $bits['level']; |
4716 | 4431 | break; |
4717 | 4432 | } |
4718 | 4433 | } |
4719 | | - if ( $mode == 'replace' ) { |
| 4434 | + if ( $mode === 'replace' ) { |
4720 | 4435 | $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG ); |
4721 | 4436 | } |
4722 | 4437 | $node = $node->getNextSibling(); |
— | — | @@ -4724,7 +4439,7 @@ |
4725 | 4440 | |
4726 | 4441 | if ( !$node ) { |
4727 | 4442 | // Not found |
4728 | | - if ( $mode == 'get' ) { |
| 4443 | + if ( $mode === 'get' ) { |
4729 | 4444 | return $newText; |
4730 | 4445 | } else { |
4731 | 4446 | return $text; |
— | — | @@ -4733,21 +4448,21 @@ |
4734 | 4449 | |
4735 | 4450 | // Find the end of the section, including nested sections |
4736 | 4451 | do { |
4737 | | - if ( $node->getName() == 'h' ) { |
| 4452 | + if ( $node->getName() === 'h' ) { |
4738 | 4453 | $bits = $node->splitHeading(); |
4739 | 4454 | $curLevel = $bits['level']; |
4740 | 4455 | if ( $bits['i'] != $sectionIndex && $curLevel <= $targetLevel ) { |
4741 | 4456 | break; |
4742 | 4457 | } |
4743 | 4458 | } |
4744 | | - if ( $mode == 'get' ) { |
| 4459 | + if ( $mode === 'get' ) { |
4745 | 4460 | $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG ); |
4746 | 4461 | } |
4747 | 4462 | $node = $node->getNextSibling(); |
4748 | 4463 | } while ( $node ); |
4749 | 4464 | |
4750 | 4465 | // Write out the remainder (in replace mode only) |
4751 | | - if ( $mode == 'replace' ) { |
| 4466 | + if ( $mode === 'replace' ) { |
4752 | 4467 | // Output the replacement text |
4753 | 4468 | // Add two newlines on -- trailing whitespace in $newText is conventionally |
4754 | 4469 | // stripped by the editor, so we need both newlines to restore the paragraph gap |
— | — | @@ -4977,7 +4692,7 @@ |
4978 | 4693 | do { |
4979 | 4694 | $oldText = $text; |
4980 | 4695 | $text = $this->general->replace( $text ); |
4981 | | - } while ( $text != $oldText ); |
| 4696 | + } while ( $text !== $oldText ); |
4982 | 4697 | wfProfileOut( __METHOD__ ); |
4983 | 4698 | return $text; |
4984 | 4699 | } |
— | — | @@ -4987,7 +4702,7 @@ |
4988 | 4703 | do { |
4989 | 4704 | $oldText = $text; |
4990 | 4705 | $text = $this->nowiki->replace( $text ); |
4991 | | - } while ( $text != $oldText ); |
| 4706 | + } while ( $text !== $oldText ); |
4992 | 4707 | wfProfileOut( __METHOD__ ); |
4993 | 4708 | return $text; |
4994 | 4709 | } |
— | — | @@ -4998,7 +4713,7 @@ |
4999 | 4714 | $oldText = $text; |
5000 | 4715 | $text = $this->general->replace( $text ); |
5001 | 4716 | $text = $this->nowiki->replace( $text ); |
5002 | | - } while ( $text != $oldText ); |
| 4717 | + } while ( $text !== $oldText ); |
5003 | 4718 | wfProfileOut( __METHOD__ ); |
5004 | 4719 | return $text; |
5005 | 4720 | } |
— | — | @@ -5012,7 +4727,7 @@ |
5013 | 4728 | var $output = ''; |
5014 | 4729 | |
5015 | 4730 | function replace( $matches ) { |
5016 | | - if ( substr( $matches[1], -1 ) == "\n" ) { |
| 4731 | + if ( substr( $matches[1], -1 ) === "\n" ) { |
5017 | 4732 | $this->output .= substr( $matches[1], 0, -1 ); |
5018 | 4733 | } else { |
5019 | 4734 | $this->output .= $matches[1]; |
Index: trunk/phase3/includes/parser/Parser_DiffTest.php |
— | — | @@ -69,9 +69,17 @@ |
70 | 70 | $lastResult = $currentResult; |
71 | 71 | } |
72 | 72 | 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 | + } |
73 | 79 | throw new MWException( "Parser_DiffTest: results mismatch on call to $name\n" . |
74 | 80 | 'Arguments: ' . $this->formatArray( $args ) . "\n" . |
75 | | - 'Results: ' . $this->formatArray( $results ) . "\n" ); |
| 81 | + 'Results: ' . $this->formatArray( $results ) . "\n" . |
| 82 | + "Diff: $diff\n" |
| 83 | + ); |
76 | 84 | } |
77 | 85 | return $lastResult; |
78 | 86 | } |
Index: trunk/phase3/includes/Title.php |
— | — | @@ -410,6 +410,12 @@ |
411 | 411 | global $wgInterwikiCache, $wgContLang; |
412 | 412 | $fname = 'Title::getInterwikiLink'; |
413 | 413 | |
| 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 | + |
414 | 420 | $key = $wgContLang->lc( $key ); |
415 | 421 | |
416 | 422 | $k = wfMemcKey( 'interwiki', $key ); |
Index: trunk/phase3/languages/LanguageConverter.php |
— | — | @@ -435,8 +435,9 @@ |
436 | 436 | if ($isTitle) return $this->convertTitle($text); |
437 | 437 | |
438 | 438 | $plang = $this->getPreferredVariant(); |
439 | | - $tarray = explode($this->mMarkup['end'], $text); |
| 439 | + $tarray = StringUtils::explode($this->mMarkup['end'], $text); |
440 | 440 | $text = ''; |
| 441 | + $lastDelim = false; |
441 | 442 | foreach($tarray as $txt) { |
442 | 443 | $marked = explode($this->mMarkup['begin'], $txt, 2); |
443 | 444 | |
— | — | @@ -452,8 +453,17 @@ |
453 | 454 | |
454 | 455 | $text .= $crule->getDisplay(); |
455 | 456 | $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; |
456 | 462 | } |
457 | 463 | } |
| 464 | + if ( $lastDelim ) { |
| 465 | + // Remove the last delimiter (wasn't real) |
| 466 | + $text = substr( $text, 0, -strlen( $this->mMarkup['end'] ) ); |
| 467 | + } |
458 | 468 | |
459 | 469 | return $text; |
460 | 470 | } |
Index: trunk/phase3/languages/Language.php |
— | — | @@ -177,6 +177,15 @@ |
178 | 178 | } |
179 | 179 | |
180 | 180 | /** |
| 181 | + * Reduce memory usage |
| 182 | + */ |
| 183 | + function __destruct() { |
| 184 | + foreach ( $this as $name => $value ) { |
| 185 | + unset( $this->$name ); |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + /** |
181 | 190 | * Hook which will be called if this is the content language. |
182 | 191 | * Descendants can use this to register hook functions or modify globals |
183 | 192 | */ |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -94,6 +94,7 @@ |
95 | 95 | * HTML entities like now work (are not escaped) in edit summaries. |
96 | 96 | * (bug 13815) In the comment for page moves, use the colon-separator message |
97 | 97 | instead of a hardcoded colon. |
| 98 | +* Allow <gallery> to accept image names without an Image: prefix |
98 | 99 | |
99 | 100 | === Bug fixes in 1.14 === |
100 | 101 | |