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,10 @@ |
63 | 63 | */ |
64 | 64 | private $oldTablePrefix; |
65 | 65 | |
| 66 | + private $maxFuzzTestLength = 300; |
| 67 | + private $fuzzSeed = 0; |
| 68 | + private $memoryLimit = 50; |
| 69 | + |
66 | 70 | /** |
67 | 71 | * Sets terminal colorization and diff/quick modes depending on OS and |
68 | 72 | * command-line options (--color and --quick). |
— | — | @@ -117,6 +121,10 @@ |
118 | 122 | } |
119 | 123 | $this->keepUploads = isset( $options['keep-uploads'] ); |
120 | 124 | |
| 125 | + if ( isset( $options['seed'] ) ) { |
| 126 | + $this->fuzzSeed = intval( $options['seed'] ) - 1; |
| 127 | + } |
| 128 | + |
121 | 129 | $this->hooks = array(); |
122 | 130 | $this->functionHooks = array(); |
123 | 131 | } |
— | — | @@ -134,6 +142,119 @@ |
135 | 143 | } |
136 | 144 | |
137 | 145 | /** |
| 146 | + * Run a fuzz test series |
| 147 | + * Draw input from a set of test files |
| 148 | + */ |
| 149 | + function fuzzTest( $filenames ) { |
| 150 | + $dict = $this->getFuzzInput( $filenames ); |
| 151 | + $dictSize = strlen( $dict ); |
| 152 | + $logMaxLength = log( $this->maxFuzzTestLength ); |
| 153 | + $this->setupDatabase(); |
| 154 | + ini_set( 'memory_limit', $this->memoryLimit * 1048576 ); |
| 155 | + |
| 156 | + $numTotal = 0; |
| 157 | + $numSuccess = 0; |
| 158 | + $user = new User; |
| 159 | + $opts = ParserOptions::newFromUser( $user ); |
| 160 | + $title = Title::makeTitle( NS_MAIN, 'Parser_test' ); |
| 161 | + |
| 162 | + while ( true ) { |
| 163 | + // Generate test input |
| 164 | + mt_srand( ++$this->fuzzSeed ); |
| 165 | + $totalLength = mt_rand( 1, $this->maxFuzzTestLength ); |
| 166 | + $input = ''; |
| 167 | + while ( strlen( $input ) < $totalLength ) { |
| 168 | + $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength; |
| 169 | + $hairLength = min( intval( exp( $logHairLength ) ), $dictSize ); |
| 170 | + $offset = mt_rand( 0, $dictSize - $hairLength ); |
| 171 | + $input .= substr( $dict, $offset, $hairLength ); |
| 172 | + } |
| 173 | + |
| 174 | + $this->setupGlobals(); |
| 175 | + $parser = $this->getParser(); |
| 176 | + // Run the test |
| 177 | + try { |
| 178 | + $parser->parse( $input, $title, $opts ); |
| 179 | + $fail = false; |
| 180 | + } catch ( Exception $exception ) { |
| 181 | + $fail = true; |
| 182 | + } |
| 183 | + |
| 184 | + if ( $fail ) { |
| 185 | + echo "Test failed with seed {$this->fuzzSeed}\n"; |
| 186 | + echo "Input:\n"; |
| 187 | + var_dump( $input ); |
| 188 | + echo "\n\n"; |
| 189 | + echo "$exception\n"; |
| 190 | + } else { |
| 191 | + $numSuccess++; |
| 192 | + } |
| 193 | + $numTotal++; |
| 194 | + $this->teardownGlobals(); |
| 195 | + $parser->__destruct(); |
| 196 | + |
| 197 | + if ( $numTotal % 100 == 0 ) { |
| 198 | + $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 ); |
| 199 | + echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n"; |
| 200 | + if ( $usage > 90 ) { |
| 201 | + echo "Out of memory:\n"; |
| 202 | + $memStats = $this->getMemoryBreakdown(); |
| 203 | + foreach ( $memStats as $name => $usage ) { |
| 204 | + echo "$name: $usage\n"; |
| 205 | + } |
| 206 | + $this->abort(); |
| 207 | + } |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + |
| 212 | + /** |
| 213 | + * Get an input dictionary from a set of parser test files |
| 214 | + */ |
| 215 | + function getFuzzInput( $filenames ) { |
| 216 | + $dict = ''; |
| 217 | + foreach( $filenames as $filename ) { |
| 218 | + $contents = file_get_contents( $filename ); |
| 219 | + preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches ); |
| 220 | + foreach ( $matches[1] as $match ) { |
| 221 | + $dict .= $match . "\n"; |
| 222 | + } |
| 223 | + } |
| 224 | + return $dict; |
| 225 | + } |
| 226 | + |
| 227 | + /** |
| 228 | + * Get a memory usage breakdown |
| 229 | + */ |
| 230 | + function getMemoryBreakdown() { |
| 231 | + $memStats = array(); |
| 232 | + foreach ( $GLOBALS as $name => $value ) { |
| 233 | + $memStats['$'.$name] = strlen( serialize( $value ) ); |
| 234 | + } |
| 235 | + $classes = get_declared_classes(); |
| 236 | + foreach ( $classes as $class ) { |
| 237 | + $rc = new ReflectionClass( $class ); |
| 238 | + $props = $rc->getStaticProperties(); |
| 239 | + $memStats[$class] = strlen( serialize( $props ) ); |
| 240 | + $methods = $rc->getMethods(); |
| 241 | + foreach ( $methods as $method ) { |
| 242 | + $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) ); |
| 243 | + } |
| 244 | + } |
| 245 | + $functions = get_defined_functions(); |
| 246 | + foreach ( $functions['user'] as $function ) { |
| 247 | + $rf = new ReflectionFunction( $function ); |
| 248 | + $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) ); |
| 249 | + } |
| 250 | + asort( $memStats ); |
| 251 | + return $memStats; |
| 252 | + } |
| 253 | + |
| 254 | + function abort() { |
| 255 | + $this->abort(); |
| 256 | + } |
| 257 | + |
| 258 | + /** |
138 | 259 | * Run a series of tests listed in the given text files. |
139 | 260 | * Each test consists of a brief description, wikitext input, |
140 | 261 | * and the expected HTML output. |
— | — | @@ -267,6 +388,24 @@ |
268 | 389 | } |
269 | 390 | |
270 | 391 | /** |
| 392 | + * Get a Parser object |
| 393 | + */ |
| 394 | + function getParser() { |
| 395 | + global $wgParserConf; |
| 396 | + $class = $wgParserConf['class']; |
| 397 | + $parser = new $class( $wgParserConf ); |
| 398 | + foreach( $this->hooks as $tag => $callback ) { |
| 399 | + $parser->setHook( $tag, $callback ); |
| 400 | + } |
| 401 | + foreach( $this->functionHooks as $tag => $bits ) { |
| 402 | + list( $callback, $flags ) = $bits; |
| 403 | + $parser->setFunctionHook( $tag, $callback, $flags ); |
| 404 | + } |
| 405 | + wfRunHooks( 'ParserTestParser', array( &$parser ) ); |
| 406 | + return $parser; |
| 407 | + } |
| 408 | + |
| 409 | + /** |
271 | 410 | * Run a given wikitext input through a freshly-constructed wiki parser, |
272 | 411 | * and compare the output against the expected results. |
273 | 412 | * Prints status and explanatory messages to stdout. |
— | — | @@ -276,7 +415,6 @@ |
277 | 416 | * @return bool |
278 | 417 | */ |
279 | 418 | private function runTest( $desc, $input, $result, $opts ) { |
280 | | - global $wgParserConf; |
281 | 419 | if( $this->showProgress ) { |
282 | 420 | $this->showTesting( $desc ); |
283 | 421 | } |
— | — | @@ -300,18 +438,7 @@ |
301 | 439 | } |
302 | 440 | |
303 | 441 | $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 | | - |
| 442 | + $parser = $this->getParser(); |
316 | 443 | $title =& Title::makeTitle( NS_MAIN, $titleText ); |
317 | 444 | |
318 | 445 | $matches = array(); |
— | — | @@ -387,6 +514,8 @@ |
388 | 515 | self::getOptionValue( '/variant=([a-z]+(?:-[a-z]+)?)/', $opts, false ); |
389 | 516 | $maxtoclevel = |
390 | 517 | self::getOptionValue( '/wgMaxTocLevel=(\d+)/', $opts, 999 ); |
| 518 | + $linkHolderBatchSize = |
| 519 | + self::getOptionValue( '/wgLinkHolderBatchSize=(\d+)/', $opts, 1000 ); |
391 | 520 | |
392 | 521 | $settings = array( |
393 | 522 | 'wgServer' => 'http://localhost', |
— | — | @@ -432,6 +561,7 @@ |
433 | 562 | ) ), |
434 | 563 | 'wgDefaultExternalStore' => array(), |
435 | 564 | 'wgForeignFileRepos' => array(), |
| 565 | + 'wgLinkHolderBatchSize' => $linkHolderBatchSize, |
436 | 566 | ); |
437 | 567 | $this->savedGlobals = array(); |
438 | 568 | foreach( $settings as $var => $val ) { |
— | — | @@ -441,6 +571,7 @@ |
442 | 572 | $langObj = Language::factory( $lang ); |
443 | 573 | $GLOBALS['wgLang'] = $langObj; |
444 | 574 | $GLOBALS['wgContLang'] = $langObj; |
| 575 | + $GLOBALS['wgMemc'] = new FakeMemCachedClient; |
445 | 576 | |
446 | 577 | //$GLOBALS['wgMessageCache'] = new MessageCache( new BagOStuff(), false, 0, $GLOBALS['wgDBname'] ); |
447 | 578 | |
— | — | @@ -551,10 +682,10 @@ |
552 | 683 | # Hack: insert a few Wikipedia in-project interwiki prefixes, |
553 | 684 | # for testing inter-language links |
554 | 685 | $db->insert( 'interwiki', array( |
555 | | - array( 'iw_prefix' => 'Wikipedia', |
| 686 | + array( 'iw_prefix' => 'wikipedia', |
556 | 687 | 'iw_url' => 'http://en.wikipedia.org/wiki/$1', |
557 | 688 | 'iw_local' => 0 ), |
558 | | - array( 'iw_prefix' => 'MeatBall', |
| 689 | + array( 'iw_prefix' => 'meatball', |
559 | 690 | 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1', |
560 | 691 | 'iw_local' => 0 ), |
561 | 692 | array( 'iw_prefix' => 'zh', |
— | — | @@ -621,11 +752,12 @@ |
622 | 753 | return; |
623 | 754 | } |
624 | 755 | |
| 756 | + /* |
625 | 757 | $tables = $this->listTables(); |
626 | 758 | $db = wfGetDB( DB_MASTER ); |
627 | 759 | foreach ( $tables as $table ) { |
628 | 760 | $db->query( "DROP TABLE `parsertest_$table`" ); |
629 | | - } |
| 761 | + }*/ |
630 | 762 | } |
631 | 763 | |
632 | 764 | /** |
— | — | @@ -645,6 +777,10 @@ |
646 | 778 | } |
647 | 779 | |
648 | 780 | wfDebug( "Creating upload directory $dir\n" ); |
| 781 | + if ( file_exists( $dir ) ) { |
| 782 | + wfDebug( "Already exists!\n" ); |
| 783 | + return $dir; |
| 784 | + } |
649 | 785 | mkdir( $dir ); |
650 | 786 | mkdir( $dir . '/3' ); |
651 | 787 | mkdir( $dir . '/3/3a' ); |
— | — | @@ -658,6 +794,8 @@ |
659 | 795 | */ |
660 | 796 | private function teardownGlobals() { |
661 | 797 | RepoGroup::destroySingleton(); |
| 798 | + LinkCache::singleton()->clear(); |
| 799 | + $GLOBALS['wgLang']->__destruct(); |
662 | 800 | foreach( $this->savedGlobals as $var => $val ) { |
663 | 801 | $GLOBALS[$var] = $val; |
664 | 802 | } |
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/parserTests.txt |
— | — | @@ -7066,7 +7066,30 @@ |
7067 | 7067 | |
7068 | 7068 | !! end |
7069 | 7069 | |
| 7070 | +!! test |
| 7071 | +Interwiki links trounced by replaceExternalLinks after early LinkHolderArray expansion |
| 7072 | +!! options |
| 7073 | +wgLinkHolderBatchSize=0 |
| 7074 | +!! input |
| 7075 | +[[meatball:1]] |
| 7076 | +[[meatball:2]] |
| 7077 | +[[meatball:3]] |
| 7078 | +!! result |
| 7079 | +<p><a href="http://www.usemod.com/cgi-bin/mb.pl?1" class="extiw" title="meatball:1">meatball:1</a> |
| 7080 | +<a href="http://www.usemod.com/cgi-bin/mb.pl?2" class="extiw" title="meatball:2">meatball:2</a> |
| 7081 | +<a href="http://www.usemod.com/cgi-bin/mb.pl?3" class="extiw" title="meatball:3">meatball:3</a> |
| 7082 | +</p> |
| 7083 | +!! end |
7070 | 7084 | |
| 7085 | +!! test |
| 7086 | +Free external link invading image caption |
| 7087 | +!! input |
| 7088 | +[[Image:Foobar.jpg|thumb|http://x|hello]] |
| 7089 | +!! result |
| 7090 | +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="https://www.mediawiki.org/wiki/Image:Foobar.jpg" class="image" title="hello"><img alt="hello" src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" width="180" height="20" border="0" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="https://www.mediawiki.org/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>hello</div></div></div> |
| 7091 | + |
| 7092 | +!! end |
| 7093 | + |
7071 | 7094 | # |
7072 | 7095 | # |
7073 | 7096 | # |
Index: trunk/phase3/maintenance/parserTestsStaticParserHook.php |
— | — | @@ -21,24 +21,27 @@ |
22 | 22 | return true; |
23 | 23 | } |
24 | 24 | |
25 | | -function wfParserTestStaticParserHookHook( $in, $argv ) { |
26 | | - static $buf = null; |
27 | | - |
| 25 | +function wfParserTestStaticParserHookHook( $in, $argv, $parser ) { |
28 | 26 | if ( ! count( $argv ) ) { |
29 | | - $buf = $in; |
| 27 | + $parser->static_tag_buf = $in; |
30 | 28 | return ''; |
31 | | - } else if ( count( $argv ) === 1 && $argv['action'] === 'flush' && $in === null ) { |
| 29 | + } else if ( count( $argv ) === 1 && isset( $argv['action'] ) |
| 30 | + && $argv['action'] === 'flush' && $in === null ) |
| 31 | + { |
32 | 32 | // Clear the buffer, we probably don't need to |
33 | | - $tmp = $buf; |
34 | | - $buf = null; |
| 33 | + if ( isset( $parser->static_tag_buf ) ) { |
| 34 | + $tmp = $parser->static_tag_buf; |
| 35 | + } else { |
| 36 | + $tmp = ''; |
| 37 | + } |
| 38 | + $parser->static_tag_buf = null; |
35 | 39 | return $tmp; |
36 | 40 | } else |
37 | 41 | // wtf? |
38 | | - die( |
| 42 | + return |
39 | 43 | "\nCall this extension as <statictag>string</statictag> or as" . |
40 | 44 | " <statictag action=flush/>, not in any other way.\n" . |
41 | 45 | "text: " . var_export( $in, true ) . "\n" . |
42 | | - "argv: " . var_export( $argv, true ) . "\n" |
43 | | - ); |
| 46 | + "argv: " . var_export( $argv, true ) . "\n"; |
44 | 47 | } |
45 | 48 | |
Index: trunk/phase3/includes/parser/LinkHolderArray.php |
— | — | @@ -1,8 +1,6 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | class LinkHolderArray { |
5 | | - var $batchSize = 1000; |
6 | | - |
7 | 5 | var $internals = array(), $interwikis = array(); |
8 | 6 | var $size = 0; |
9 | 7 | var $parent; |
— | — | @@ -12,6 +10,15 @@ |
13 | 11 | } |
14 | 12 | |
15 | 13 | /** |
| 14 | + * Reduce memory usage to reduce the impact of circular references |
| 15 | + */ |
| 16 | + function __destruct() { |
| 17 | + foreach ( $this as $name => $value ) { |
| 18 | + unset( $this->$name ); |
| 19 | + } |
| 20 | + } |
| 21 | + |
| 22 | + /** |
16 | 23 | * Merge another LinkHolderArray into this one |
17 | 24 | */ |
18 | 25 | function merge( $other ) { |
— | — | @@ -30,7 +37,8 @@ |
31 | 38 | * Returns true if the memory requirements of this object are getting large |
32 | 39 | */ |
33 | 40 | function isBig() { |
34 | | - return $this->size > $this->batchSize; |
| 41 | + global $wgLinkHolderBatchSize; |
| 42 | + return $this->size > $wgLinkHolderBatchSize; |
35 | 43 | } |
36 | 44 | |
37 | 45 | /** |
— | — | @@ -145,7 +153,7 @@ |
146 | 154 | if ( $title->isAlwaysKnown() ) { |
147 | 155 | $colours[$pdbk] = ''; |
148 | 156 | } elseif ( ( $id = $linkCache->getGoodLinkID( $pdbk ) ) != 0 ) { |
149 | | - $colours[$pdbk] = ''; |
| 157 | + $colours[$pdbk] = $sk->getLinkColour( $title, $threshold ); |
150 | 158 | $output->addLink( $title, $id ); |
151 | 159 | } elseif ( $linkCache->isBadLink( $pdbk ) ) { |
152 | 160 | $colours[$pdbk] = 'new'; |
— | — | @@ -180,6 +188,9 @@ |
181 | 189 | $pdbk = $title->getPrefixedDBkey(); |
182 | 190 | $linkCache->addGoodLinkObj( $s->page_id, $title, $s->page_len, $s->page_is_redirect ); |
183 | 191 | $output->addLink( $title, $s->page_id ); |
| 192 | + # FIXME: convoluted data flow |
| 193 | + # The redirect status and length is passed to getLinkColour via the LinkCache |
| 194 | + # Use formal parameters instead |
184 | 195 | $colours[$pdbk] = $sk->getLinkColour( $title, $threshold ); |
185 | 196 | //add id to the extension todolist |
186 | 197 | $linkcolour_ids[$s->page_id] = $pdbk; |
— | — | @@ -274,6 +285,9 @@ |
275 | 286 | $entry['pdbk'] = $varPdbk; |
276 | 287 | |
277 | 288 | // set pdbk and colour |
| 289 | + # FIXME: convoluted data flow |
| 290 | + # The redirect status and length is passed to getLinkColour via the LinkCache |
| 291 | + # Use formal parameters instead |
278 | 292 | $colours[$varPdbk] = $sk->getLinkColour( $variantTitle, $threshold ); |
279 | 293 | $linkcolour_ids[$s->page_id] = $pdbk; |
280 | 294 | } |
Index: trunk/phase3/includes/parser/Parser.php |
— | — | @@ -92,13 +92,13 @@ |
93 | 93 | # Persistent: |
94 | 94 | var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables, |
95 | 95 | $mImageParams, $mImageParamsMagicArray, $mStripList, $mMarkerIndex, $mPreprocessor, |
96 | | - $mExtLinkBracketedRegex, $mDefaultStripList, $mVarCache, $mConf; |
| 96 | + $mExtLinkBracketedRegex, $mUrlProtocols, $mDefaultStripList, $mVarCache, $mConf; |
97 | 97 | |
98 | 98 | |
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; |
— | — | @@ -128,6 +128,7 @@ |
129 | 129 | $this->mFunctionHooks = array(); |
130 | 130 | $this->mFunctionSynonyms = array( 0 => array(), 1 => array() ); |
131 | 131 | $this->mDefaultStripList = $this->mStripList = array( 'nowiki', 'gallery' ); |
| 132 | + $this->mUrlProtocols = wfUrlProtocols(); |
132 | 133 | $this->mExtLinkBracketedRegex = '/\[(\b(' . wfUrlProtocols() . ')'. |
133 | 134 | '[^][<>"\\x00-\\x20\\x7F]+) *([^\]\\x0a\\x0d]*?)\]/S'; |
134 | 135 | $this->mVarCache = array(); |
— | — | @@ -146,6 +147,9 @@ |
147 | 148 | * Reduce memory usage to reduce the impact of circular references |
148 | 149 | */ |
149 | 150 | function __destruct() { |
| 151 | + if ( isset( $this->mLinkHolders ) ) { |
| 152 | + $this->mLinkHolders->__destruct(); |
| 153 | + } |
150 | 154 | foreach ( $this as $name => $value ) { |
151 | 155 | unset( $this->$name ); |
152 | 156 | } |
— | — | @@ -188,17 +192,8 @@ |
189 | 193 | $this->mStripState = new StripState; |
190 | 194 | $this->mArgStack = false; |
191 | 195 | $this->mInPre = false; |
192 | | - $this->mInterwikiLinkHolders = array( |
193 | | - 'texts' => array(), |
194 | | - 'titles' => array() |
195 | | - ); |
196 | | - $this->mLinkHolders = array( |
197 | | - 'namespaces' => array(), |
198 | | - 'dbkeys' => array(), |
199 | | - 'queries' => array(), |
200 | | - 'texts' => array(), |
201 | | - 'titles' => array() |
202 | | - ); |
| 196 | + $this->mLinkHolders = new LinkHolderArray( $this ); |
| 197 | + $this->mLinkID = 0; |
203 | 198 | $this->mRevisionTimestamp = $this->mRevisionId = null; |
204 | 199 | |
205 | 200 | /** |
— | — | @@ -213,7 +208,7 @@ |
214 | 209 | */ |
215 | 210 | #$this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString(); |
216 | 211 | # Changed to \x7f to allow XML double-parsing -- TS |
217 | | - $this->mUniqPrefix = "\x7fUNIQ" . Parser::getRandomString(); |
| 212 | + $this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString(); |
218 | 213 | |
219 | 214 | |
220 | 215 | # Clear these on every parse, bug 4549 |
— | — | @@ -303,7 +298,7 @@ |
304 | 299 | */ |
305 | 300 | |
306 | 301 | global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang; |
307 | | - $fname = 'Parser::parse-' . wfGetCaller(); |
| 302 | + $fname = __METHOD__.'-' . wfGetCaller(); |
308 | 303 | wfProfileIn( __METHOD__ ); |
309 | 304 | wfProfileIn( $fname ); |
310 | 305 | |
— | — | @@ -337,7 +332,6 @@ |
338 | 333 | ); |
339 | 334 | $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text ); |
340 | 335 | |
341 | | - # only once and last |
342 | 336 | $text = $this->doBlockLevels( $text, $linestart ); |
343 | 337 | |
344 | 338 | $this->replaceLinkHolders( $text ); |
— | — | @@ -357,7 +351,7 @@ |
358 | 352 | $uniq_prefix = $this->mUniqPrefix; |
359 | 353 | $matches = array(); |
360 | 354 | $elements = array_keys( $this->mTransparentTagHooks ); |
361 | | - $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); |
| 355 | + $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); |
362 | 356 | |
363 | 357 | foreach( $matches as $marker => $data ) { |
364 | 358 | list( $element, $content, $params, $tag ) = $data; |
— | — | @@ -375,7 +369,7 @@ |
376 | 370 | $text = Sanitizer::normalizeCharReferences( $text ); |
377 | 371 | |
378 | 372 | if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) { |
379 | | - $text = Parser::tidy($text); |
| 373 | + $text = self::tidy($text); |
380 | 374 | } else { |
381 | 375 | # attempt to sanitize at least some nesting problems |
382 | 376 | # (bug #2702 and quite a few others) |
— | — | @@ -480,6 +474,8 @@ |
481 | 475 | function &getTitle() { return $this->mTitle; } |
482 | 476 | function getOptions() { return $this->mOptions; } |
483 | 477 | function getRevisionId() { return $this->mRevisionId; } |
| 478 | + function getOutput() { return $this->mOutput; } |
| 479 | + function nextLinkID() { return $this->mLinkID++; } |
484 | 480 | |
485 | 481 | function getFunctionLang() { |
486 | 482 | global $wgLang, $wgContLang; |
— | — | @@ -558,7 +554,7 @@ |
559 | 555 | $text = $inside; |
560 | 556 | $tail = null; |
561 | 557 | } else { |
562 | | - if( $element == '!--' ) { |
| 558 | + if( $element === '!--' ) { |
563 | 559 | $end = '/(-->)/'; |
564 | 560 | } else { |
565 | 561 | $end = "/(<\\/$element\\s*>)/i"; |
— | — | @@ -667,9 +663,9 @@ |
668 | 664 | ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>'. |
669 | 665 | '<head><title>test</title></head><body>'.$text.'</body></html>'; |
670 | 666 | if( $wgTidyInternal ) { |
671 | | - $correctedtext = Parser::internalTidy( $wrappedtext ); |
| 667 | + $correctedtext = self::internalTidy( $wrappedtext ); |
672 | 668 | } else { |
673 | | - $correctedtext = Parser::externalTidy( $wrappedtext ); |
| 669 | + $correctedtext = self::externalTidy( $wrappedtext ); |
674 | 670 | } |
675 | 671 | if( is_null( $correctedtext ) ) { |
676 | 672 | wfDebug( "Tidy error detected!\n" ); |
— | — | @@ -686,8 +682,7 @@ |
687 | 683 | */ |
688 | 684 | function externalTidy( $text ) { |
689 | 685 | global $wgTidyConf, $wgTidyBin, $wgTidyOpts; |
690 | | - $fname = 'Parser::externalTidy'; |
691 | | - wfProfileIn( $fname ); |
| 686 | + wfProfileIn( __METHOD__ ); |
692 | 687 | |
693 | 688 | $cleansource = ''; |
694 | 689 | $opts = ' -utf8'; |
— | — | @@ -716,7 +711,7 @@ |
717 | 712 | } |
718 | 713 | } |
719 | 714 | |
720 | | - wfProfileOut( $fname ); |
| 715 | + wfProfileOut( __METHOD__ ); |
721 | 716 | |
722 | 717 | if( $cleansource == '' && $text != '') { |
723 | 718 | // Some kind of error happened, so we couldn't get the corrected text. |
— | — | @@ -738,8 +733,7 @@ |
739 | 734 | */ |
740 | 735 | function internalTidy( $text ) { |
741 | 736 | global $wgTidyConf, $IP, $wgDebugTidy; |
742 | | - $fname = 'Parser::internalTidy'; |
743 | | - wfProfileIn( $fname ); |
| 737 | + wfProfileIn( __METHOD__ ); |
744 | 738 | |
745 | 739 | $tidy = new tidy; |
746 | 740 | $tidy->parseString( $text, $wgTidyConf, 'utf8' ); |
— | — | @@ -757,7 +751,7 @@ |
758 | 752 | "\n-->"; |
759 | 753 | } |
760 | 754 | |
761 | | - wfProfileOut( $fname ); |
| 755 | + wfProfileOut( __METHOD__ ); |
762 | 756 | return $cleansource; |
763 | 757 | } |
764 | 758 | |
— | — | @@ -767,34 +761,35 @@ |
768 | 762 | * @private |
769 | 763 | */ |
770 | 764 | function doTableStuff ( $text ) { |
771 | | - $fname = 'Parser::doTableStuff'; |
772 | | - wfProfileIn( $fname ); |
| 765 | + wfProfileIn( __METHOD__ ); |
773 | 766 | |
774 | | - $lines = explode ( "\n" , $text ); |
| 767 | + $lines = StringUtils::explode( "\n", $text ); |
| 768 | + $out = ''; |
775 | 769 | $td_history = array (); // Is currently a td tag open? |
776 | 770 | $last_tag_history = array (); // Save history of last lag activated (td, th or caption) |
777 | 771 | $tr_history = array (); // Is currently a tr tag open? |
778 | 772 | $tr_attributes = array (); // history of tr attributes |
779 | 773 | $has_opened_tr = array(); // Did this table open a <tr> element? |
780 | 774 | $indent_level = 0; // indent level of the table |
781 | | - foreach ( $lines as $key => $line ) |
782 | | - { |
783 | | - $line = trim ( $line ); |
784 | 775 | |
| 776 | + foreach ( $lines as $outLine ) { |
| 777 | + $line = trim( $outLine ); |
| 778 | + |
785 | 779 | if( $line == '' ) { // empty line, go to next line |
| 780 | + $out .= $outLine."\n"; |
786 | 781 | continue; |
787 | 782 | } |
788 | | - $first_character = $line{0}; |
| 783 | + $first_character = $line[0]; |
789 | 784 | $matches = array(); |
790 | 785 | |
791 | | - if ( preg_match( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) { |
| 786 | + if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) { |
792 | 787 | // First check if we are starting a new table |
793 | 788 | $indent_level = strlen( $matches[1] ); |
794 | 789 | |
795 | 790 | $attributes = $this->mStripState->unstripBoth( $matches[2] ); |
796 | 791 | $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' ); |
797 | 792 | |
798 | | - $lines[$key] = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; |
| 793 | + $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>"; |
799 | 794 | array_push ( $td_history , false ); |
800 | 795 | array_push ( $last_tag_history , '' ); |
801 | 796 | array_push ( $tr_history , false ); |
— | — | @@ -802,8 +797,9 @@ |
803 | 798 | array_push ( $has_opened_tr , false ); |
804 | 799 | } else if ( count ( $td_history ) == 0 ) { |
805 | 800 | // Don't do any of the following |
| 801 | + $out .= $outLine."\n"; |
806 | 802 | continue; |
807 | | - } else if ( substr ( $line , 0 , 2 ) == '|}' ) { |
| 803 | + } else if ( substr ( $line , 0 , 2 ) === '|}' ) { |
808 | 804 | // We are ending a table |
809 | 805 | $line = '</table>' . substr ( $line , 2 ); |
810 | 806 | $last_tag = array_pop ( $last_tag_history ); |
— | — | @@ -820,8 +816,8 @@ |
821 | 817 | $line = "</{$last_tag}>{$line}"; |
822 | 818 | } |
823 | 819 | array_pop ( $tr_attributes ); |
824 | | - $lines[$key] = $line . str_repeat( '</dd></dl>' , $indent_level ); |
825 | | - } else if ( substr ( $line , 0 , 2 ) == '|-' ) { |
| 820 | + $outLine = $line . str_repeat( '</dd></dl>' , $indent_level ); |
| 821 | + } else if ( substr ( $line , 0 , 2 ) === '|-' ) { |
826 | 822 | // Now we have a table row |
827 | 823 | $line = preg_replace( '#^\|-+#', '', $line ); |
828 | 824 | |
— | — | @@ -844,21 +840,21 @@ |
845 | 841 | $line = "</{$last_tag}>{$line}"; |
846 | 842 | } |
847 | 843 | |
848 | | - $lines[$key] = $line; |
| 844 | + $outLine = $line; |
849 | 845 | array_push ( $tr_history , false ); |
850 | 846 | array_push ( $td_history , false ); |
851 | 847 | array_push ( $last_tag_history , '' ); |
852 | 848 | } |
853 | | - else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 ) == '|+' ) { |
| 849 | + else if ( $first_character === '|' || $first_character === '!' || substr ( $line , 0 , 2 ) === '|+' ) { |
854 | 850 | // This might be cell elements, td, th or captions |
855 | | - if ( substr ( $line , 0 , 2 ) == '|+' ) { |
| 851 | + if ( substr ( $line , 0 , 2 ) === '|+' ) { |
856 | 852 | $first_character = '+'; |
857 | 853 | $line = substr ( $line , 1 ); |
858 | 854 | } |
859 | 855 | |
860 | 856 | $line = substr ( $line , 1 ); |
861 | 857 | |
862 | | - if ( $first_character == '!' ) { |
| 858 | + if ( $first_character === '!' ) { |
863 | 859 | $line = str_replace ( '!!' , '||' , $line ); |
864 | 860 | } |
865 | 861 | |
— | — | @@ -868,13 +864,13 @@ |
869 | 865 | // attribute values containing literal "||". |
870 | 866 | $cells = StringUtils::explodeMarkup( '||' , $line ); |
871 | 867 | |
872 | | - $lines[$key] = ''; |
| 868 | + $outLine = ''; |
873 | 869 | |
874 | 870 | // Loop through each table cell |
875 | 871 | foreach ( $cells as $cell ) |
876 | 872 | { |
877 | 873 | $previous = ''; |
878 | | - if ( $first_character != '+' ) |
| 874 | + if ( $first_character !== '+' ) |
879 | 875 | { |
880 | 876 | $tr_after = array_pop ( $tr_attributes ); |
881 | 877 | if ( !array_pop ( $tr_history ) ) { |
— | — | @@ -892,11 +888,11 @@ |
893 | 889 | $previous = "</{$last_tag}>{$previous}"; |
894 | 890 | } |
895 | 891 | |
896 | | - if ( $first_character == '|' ) { |
| 892 | + if ( $first_character === '|' ) { |
897 | 893 | $last_tag = 'td'; |
898 | | - } else if ( $first_character == '!' ) { |
| 894 | + } else if ( $first_character === '!' ) { |
899 | 895 | $last_tag = 'th'; |
900 | | - } else if ( $first_character == '+' ) { |
| 896 | + } else if ( $first_character === '+' ) { |
901 | 897 | $last_tag = 'caption'; |
902 | 898 | } else { |
903 | 899 | $last_tag = ''; |
— | — | @@ -919,38 +915,42 @@ |
920 | 916 | $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}"; |
921 | 917 | } |
922 | 918 | |
923 | | - $lines[$key] .= $cell; |
| 919 | + $outLine .= $cell; |
924 | 920 | array_push ( $td_history , true ); |
925 | 921 | } |
926 | 922 | } |
| 923 | + $out .= $outLine . "\n"; |
927 | 924 | } |
928 | 925 | |
929 | 926 | // Closing open td, tr && table |
930 | 927 | while ( count ( $td_history ) > 0 ) |
931 | 928 | { |
932 | 929 | if ( array_pop ( $td_history ) ) { |
933 | | - $lines[] = '</td>' ; |
| 930 | + $out .= "</td>\n"; |
934 | 931 | } |
935 | 932 | if ( array_pop ( $tr_history ) ) { |
936 | | - $lines[] = '</tr>' ; |
| 933 | + $out .= "</tr>\n"; |
937 | 934 | } |
938 | 935 | if ( !array_pop ( $has_opened_tr ) ) { |
939 | | - $lines[] = "<tr><td></td></tr>" ; |
| 936 | + $out .= "<tr><td></td></tr>\n" ; |
940 | 937 | } |
941 | 938 | |
942 | | - $lines[] = '</table>' ; |
| 939 | + $out .= "</table>\n"; |
943 | 940 | } |
944 | 941 | |
945 | | - $output = implode ( "\n" , $lines ) ; |
| 942 | + // Remove trailing line-ending (b/c) |
| 943 | + if ( substr( $out, -1 ) === "\n" ) { |
| 944 | + $out = substr( $out, 0, -1 ); |
| 945 | + } |
946 | 946 | |
947 | 947 | // special case: don't return empty table |
948 | | - if( $output == "<table>\n<tr><td></td></tr>\n</table>" ) { |
949 | | - $output = ''; |
| 948 | + if( $out === "<table>\n<tr><td></td></tr>\n</table>" ) { |
| 949 | + $out = ''; |
950 | 950 | } |
951 | 951 | |
952 | | - wfProfileOut( $fname ); |
| 952 | + wfProfileOut( __METHOD__ ); |
953 | 953 | |
954 | | - return $output; |
| 954 | + return $out; |
955 | 955 | } |
956 | 956 | |
957 | 957 | /** |
— | — | @@ -961,12 +961,11 @@ |
962 | 962 | */ |
963 | 963 | function internalParse( $text ) { |
964 | 964 | $isMain = true; |
965 | | - $fname = 'Parser::internalParse'; |
966 | | - wfProfileIn( $fname ); |
| 965 | + wfProfileIn( __METHOD__ ); |
967 | 966 | |
968 | 967 | # Hook to suspend the parser in this state |
969 | 968 | if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$this->mStripState ) ) ) { |
970 | | - wfProfileOut( $fname ); |
| 969 | + wfProfileOut( __METHOD__ ); |
971 | 970 | return $text ; |
972 | 971 | } |
973 | 972 | |
— | — | @@ -999,84 +998,146 @@ |
1000 | 999 | $text = $this->doMagicLinks( $text ); |
1001 | 1000 | $text = $this->formatHeadings( $text, $isMain ); |
1002 | 1001 | |
1003 | | - wfProfileOut( $fname ); |
| 1002 | + wfProfileOut( __METHOD__ ); |
1004 | 1003 | return $text; |
1005 | 1004 | } |
1006 | 1005 | |
1007 | 1006 | /** |
1008 | 1007 | * Replace special strings like "ISBN xxx" and "RFC xxx" with |
1009 | 1008 | * magic external links. |
1010 | | - * |
| 1009 | + * |
| 1010 | + * DML |
1011 | 1011 | * @private |
1012 | 1012 | */ |
1013 | 1013 | function doMagicLinks( $text ) { |
1014 | 1014 | wfProfileIn( __METHOD__ ); |
| 1015 | + $prots = $this->mUrlProtocols; |
| 1016 | + $urlChar = self::EXT_LINK_URL_CLASS; |
1015 | 1017 | $text = preg_replace_callback( |
1016 | 1018 | '!(?: # Start cases |
1017 | | - <a.*?</a> | # Skip link text |
1018 | | - <.*?> | # Skip stuff inside HTML elements |
1019 | | - (?:RFC|PMID)\s+([0-9]+) | # RFC or PMID, capture number as m[1] |
1020 | | - ISBN\s+(\b # ISBN, capture number as m[2] |
1021 | | - (?: 97[89] [\ \-]? )? # optional 13-digit ISBN prefix |
1022 | | - (?: [0-9] [\ \-]? ){9} # 9 digits with opt. delimiters |
1023 | | - [0-9Xx] # check digit |
1024 | | - \b) |
| 1019 | + (<a.*?</a>) | # m[1]: Skip link text |
| 1020 | + (<.*?>) | # m[2]: Skip stuff inside HTML elements' . " |
| 1021 | + (\\b(?:$prots)$urlChar+) | # m[3]: Free external links" . ' |
| 1022 | + (?:RFC|PMID)\s+([0-9]+) | # m[4]: RFC or PMID, capture number |
| 1023 | + ISBN\s+(\b # m[5]: ISBN, capture number |
| 1024 | + (?: 97[89] [\ \-]? )? # optional 13-digit ISBN prefix |
| 1025 | + (?: [0-9] [\ \-]? ){9} # 9 digits with opt. delimiters |
| 1026 | + [0-9Xx] # check digit |
| 1027 | + \b) |
1025 | 1028 | )!x', array( &$this, 'magicLinkCallback' ), $text ); |
1026 | 1029 | wfProfileOut( __METHOD__ ); |
1027 | 1030 | return $text; |
1028 | 1031 | } |
1029 | 1032 | |
1030 | 1033 | function magicLinkCallback( $m ) { |
1031 | | - if ( substr( $m[0], 0, 1 ) == '<' ) { |
| 1034 | + if ( isset( $m[1] ) && strval( $m[1] ) !== '' ) { |
| 1035 | + # Skip anchor |
| 1036 | + return $m[0]; |
| 1037 | + } elseif ( isset( $m[2] ) && strval( $m[2] ) !== '' ) { |
1032 | 1038 | # Skip HTML element |
1033 | 1039 | return $m[0]; |
1034 | | - } elseif ( substr( $m[0], 0, 4 ) == 'ISBN' ) { |
1035 | | - $isbn = $m[2]; |
1036 | | - $num = strtr( $isbn, array( |
1037 | | - '-' => '', |
1038 | | - ' ' => '', |
1039 | | - 'x' => 'X', |
1040 | | - )); |
1041 | | - $titleObj = SpecialPage::getTitleFor( 'Booksources', $num ); |
1042 | | - $text = '<a href="' . |
1043 | | - $titleObj->escapeLocalUrl() . |
1044 | | - "\" class=\"internal\">ISBN $isbn</a>"; |
1045 | | - } else { |
1046 | | - if ( substr( $m[0], 0, 3 ) == 'RFC' ) { |
| 1040 | + } elseif ( isset( $m[3] ) && strval( $m[3] ) !== '' ) { |
| 1041 | + # Free external link |
| 1042 | + return $this->makeFreeExternalLink( $m[0] ); |
| 1043 | + } elseif ( isset( $m[4] ) && strval( $m[4] ) !== '' ) { |
| 1044 | + # RFC or PMID |
| 1045 | + if ( substr( $m[0], 0, 3 ) === 'RFC' ) { |
1047 | 1046 | $keyword = 'RFC'; |
1048 | 1047 | $urlmsg = 'rfcurl'; |
1049 | | - $id = $m[1]; |
1050 | | - } elseif ( substr( $m[0], 0, 4 ) == 'PMID' ) { |
| 1048 | + $id = $m[4]; |
| 1049 | + } elseif ( substr( $m[0], 0, 4 ) === 'PMID' ) { |
1051 | 1050 | $keyword = 'PMID'; |
1052 | 1051 | $urlmsg = 'pubmedurl'; |
1053 | | - $id = $m[1]; |
| 1052 | + $id = $m[4]; |
1054 | 1053 | } else { |
1055 | 1054 | throw new MWException( __METHOD__.': unrecognised match type "' . |
1056 | 1055 | substr($m[0], 0, 20 ) . '"' ); |
1057 | 1056 | } |
1058 | | - |
1059 | 1057 | $url = wfMsg( $urlmsg, $id); |
1060 | 1058 | $sk = $this->mOptions->getSkin(); |
1061 | 1059 | $la = $sk->getExternalLinkAttributes( $url, $keyword.$id ); |
1062 | | - $text = "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>"; |
| 1060 | + return "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>"; |
| 1061 | + } elseif ( isset( $m[5] ) && strval( $m[5] ) !== '' ) { |
| 1062 | + # ISBN |
| 1063 | + $isbn = $m[5]; |
| 1064 | + $num = strtr( $isbn, array( |
| 1065 | + '-' => '', |
| 1066 | + ' ' => '', |
| 1067 | + 'x' => 'X', |
| 1068 | + )); |
| 1069 | + $titleObj = SpecialPage::getTitleFor( 'Booksources', $num ); |
| 1070 | + return'<a href="' . |
| 1071 | + $titleObj->escapeLocalUrl() . |
| 1072 | + "\" class=\"internal\">ISBN $isbn</a>"; |
| 1073 | + } else { |
| 1074 | + return $m[0]; |
1063 | 1075 | } |
1064 | | - return $text; |
1065 | 1076 | } |
1066 | 1077 | |
1067 | 1078 | /** |
| 1079 | + * Make a free external link, given a user-supplied URL |
| 1080 | + * @return HTML |
| 1081 | + * @private |
| 1082 | + */ |
| 1083 | + function makeFreeExternalLink( $url ) { |
| 1084 | + global $wgContLang; |
| 1085 | + wfProfileIn( __METHOD__ ); |
| 1086 | + |
| 1087 | + $sk = $this->mOptions->getSkin(); |
| 1088 | + $trail = ''; |
| 1089 | + |
| 1090 | + # The characters '<' and '>' (which were escaped by |
| 1091 | + # removeHTMLtags()) should not be included in |
| 1092 | + # URLs, per RFC 2396. |
| 1093 | + $m2 = array(); |
| 1094 | + if (preg_match('/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE)) { |
| 1095 | + $trail = substr($url, $m2[0][1]) . $trail; |
| 1096 | + $url = substr($url, 0, $m2[0][1]); |
| 1097 | + } |
| 1098 | + |
| 1099 | + # Move trailing punctuation to $trail |
| 1100 | + $sep = ',;\.:!?'; |
| 1101 | + # If there is no left bracket, then consider right brackets fair game too |
| 1102 | + if ( strpos( $url, '(' ) === false ) { |
| 1103 | + $sep .= ')'; |
| 1104 | + } |
| 1105 | + |
| 1106 | + $numSepChars = strspn( strrev( $url ), $sep ); |
| 1107 | + if ( $numSepChars ) { |
| 1108 | + $trail = substr( $url, -$numSepChars ) . $trail; |
| 1109 | + $url = substr( $url, 0, -$numSepChars ); |
| 1110 | + } |
| 1111 | + |
| 1112 | + $url = Sanitizer::cleanUrl( $url ); |
| 1113 | + |
| 1114 | + # Is this an external image? |
| 1115 | + $text = $this->maybeMakeExternalImage( $url ); |
| 1116 | + if ( $text === false ) { |
| 1117 | + # Not an image, make a link |
| 1118 | + $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() ); |
| 1119 | + # Register it in the output object... |
| 1120 | + # Replace unnecessary URL escape codes with their equivalent characters |
| 1121 | + $pasteurized = self::replaceUnusualEscapes( $url ); |
| 1122 | + $this->mOutput->addExternalLink( $pasteurized ); |
| 1123 | + } |
| 1124 | + wfProfileOut( __METHOD__ ); |
| 1125 | + return $text . $trail; |
| 1126 | + } |
| 1127 | + |
| 1128 | + |
| 1129 | + /** |
1068 | 1130 | * Parse headers and return html |
1069 | 1131 | * |
1070 | 1132 | * @private |
1071 | 1133 | */ |
1072 | 1134 | function doHeadings( $text ) { |
1073 | | - $fname = 'Parser::doHeadings'; |
1074 | | - wfProfileIn( $fname ); |
| 1135 | + wfProfileIn( __METHOD__ ); |
1075 | 1136 | for ( $i = 6; $i >= 1; --$i ) { |
1076 | 1137 | $h = str_repeat( '=', $i ); |
1077 | 1138 | $text = preg_replace( "/^$h(.+)$h\\s*$/m", |
1078 | 1139 | "<h$i>\\1</h$i>", $text ); |
1079 | 1140 | } |
1080 | | - wfProfileOut( $fname ); |
| 1141 | + wfProfileOut( __METHOD__ ); |
1081 | 1142 | return $text; |
1082 | 1143 | } |
1083 | 1144 | |
— | — | @@ -1086,15 +1147,14 @@ |
1087 | 1148 | * @return string the altered text |
1088 | 1149 | */ |
1089 | 1150 | function doAllQuotes( $text ) { |
1090 | | - $fname = 'Parser::doAllQuotes'; |
1091 | | - wfProfileIn( $fname ); |
| 1151 | + wfProfileIn( __METHOD__ ); |
1092 | 1152 | $outtext = ''; |
1093 | | - $lines = explode( "\n", $text ); |
| 1153 | + $lines = StringUtils::explode( "\n", $text ); |
1094 | 1154 | foreach ( $lines as $line ) { |
1095 | | - $outtext .= $this->doQuotes ( $line ) . "\n"; |
| 1155 | + $outtext .= $this->doQuotes( $line ) . "\n"; |
1096 | 1156 | } |
1097 | 1157 | $outtext = substr($outtext, 0,-1); |
1098 | | - wfProfileOut( $fname ); |
| 1158 | + wfProfileOut( __METHOD__ ); |
1099 | 1159 | return $outtext; |
1100 | 1160 | } |
1101 | 1161 | |
— | — | @@ -1156,9 +1216,9 @@ |
1157 | 1217 | { |
1158 | 1218 | $x1 = substr ($arr[$i-1], -1); |
1159 | 1219 | $x2 = substr ($arr[$i-1], -2, 1); |
1160 | | - if ($x1 == ' ') { |
| 1220 | + if ($x1 === ' ') { |
1161 | 1221 | if ($firstspace == -1) $firstspace = $i; |
1162 | | - } else if ($x2 == ' ') { |
| 1222 | + } else if ($x2 === ' ') { |
1163 | 1223 | if ($firstsingleletterword == -1) $firstsingleletterword = $i; |
1164 | 1224 | } else { |
1165 | 1225 | if ($firstmultiletterword == -1) $firstmultiletterword = $i; |
— | — | @@ -1198,7 +1258,7 @@ |
1199 | 1259 | { |
1200 | 1260 | if (($i % 2) == 0) |
1201 | 1261 | { |
1202 | | - if ($state == 'both') |
| 1262 | + if ($state === 'both') |
1203 | 1263 | $buffer .= $r; |
1204 | 1264 | else |
1205 | 1265 | $output .= $r; |
— | — | @@ -1207,41 +1267,41 @@ |
1208 | 1268 | { |
1209 | 1269 | if (strlen ($r) == 2) |
1210 | 1270 | { |
1211 | | - if ($state == 'i') |
| 1271 | + if ($state === 'i') |
1212 | 1272 | { $output .= '</i>'; $state = ''; } |
1213 | | - else if ($state == 'bi') |
| 1273 | + else if ($state === 'bi') |
1214 | 1274 | { $output .= '</i>'; $state = 'b'; } |
1215 | | - else if ($state == 'ib') |
| 1275 | + else if ($state === 'ib') |
1216 | 1276 | { $output .= '</b></i><b>'; $state = 'b'; } |
1217 | | - else if ($state == 'both') |
| 1277 | + else if ($state === 'both') |
1218 | 1278 | { $output .= '<b><i>'.$buffer.'</i>'; $state = 'b'; } |
1219 | 1279 | else # $state can be 'b' or '' |
1220 | 1280 | { $output .= '<i>'; $state .= 'i'; } |
1221 | 1281 | } |
1222 | 1282 | else if (strlen ($r) == 3) |
1223 | 1283 | { |
1224 | | - if ($state == 'b') |
| 1284 | + if ($state === 'b') |
1225 | 1285 | { $output .= '</b>'; $state = ''; } |
1226 | | - else if ($state == 'bi') |
| 1286 | + else if ($state === 'bi') |
1227 | 1287 | { $output .= '</i></b><i>'; $state = 'i'; } |
1228 | | - else if ($state == 'ib') |
| 1288 | + else if ($state === 'ib') |
1229 | 1289 | { $output .= '</b>'; $state = 'i'; } |
1230 | | - else if ($state == 'both') |
| 1290 | + else if ($state === 'both') |
1231 | 1291 | { $output .= '<i><b>'.$buffer.'</b>'; $state = 'i'; } |
1232 | 1292 | else # $state can be 'i' or '' |
1233 | 1293 | { $output .= '<b>'; $state .= 'b'; } |
1234 | 1294 | } |
1235 | 1295 | else if (strlen ($r) == 5) |
1236 | 1296 | { |
1237 | | - if ($state == 'b') |
| 1297 | + if ($state === 'b') |
1238 | 1298 | { $output .= '</b><i>'; $state = 'i'; } |
1239 | | - else if ($state == 'i') |
| 1299 | + else if ($state === 'i') |
1240 | 1300 | { $output .= '</i><b>'; $state = 'b'; } |
1241 | | - else if ($state == 'bi') |
| 1301 | + else if ($state === 'bi') |
1242 | 1302 | { $output .= '</i></b>'; $state = ''; } |
1243 | | - else if ($state == 'ib') |
| 1303 | + else if ($state === 'ib') |
1244 | 1304 | { $output .= '</b></i>'; $state = ''; } |
1245 | | - else if ($state == 'both') |
| 1305 | + else if ($state === 'both') |
1246 | 1306 | { $output .= '<i><b>'.$buffer.'</b></i>'; $state = ''; } |
1247 | 1307 | else # ($state == '') |
1248 | 1308 | { $buffer = ''; $state = 'both'; } |
— | — | @@ -1250,21 +1310,21 @@ |
1251 | 1311 | $i++; |
1252 | 1312 | } |
1253 | 1313 | # Now close all remaining tags. Notice that the order is important. |
1254 | | - if ($state == 'b' || $state == 'ib') |
| 1314 | + if ($state === 'b' || $state === 'ib') |
1255 | 1315 | $output .= '</b>'; |
1256 | | - if ($state == 'i' || $state == 'bi' || $state == 'ib') |
| 1316 | + if ($state === 'i' || $state === 'bi' || $state === 'ib') |
1257 | 1317 | $output .= '</i>'; |
1258 | | - if ($state == 'bi') |
| 1318 | + if ($state === 'bi') |
1259 | 1319 | $output .= '</b>'; |
1260 | 1320 | # There might be lonely ''''', so make sure we have a buffer |
1261 | | - if ($state == 'both' && $buffer) |
| 1321 | + if ($state === 'both' && $buffer) |
1262 | 1322 | $output .= '<b><i>'.$buffer.'</i></b>'; |
1263 | 1323 | return $output; |
1264 | 1324 | } |
1265 | 1325 | } |
1266 | 1326 | |
1267 | 1327 | /** |
1268 | | - * Replace external links |
| 1328 | + * Replace external links (REL) |
1269 | 1329 | * |
1270 | 1330 | * Note: this is all very hackish and the order of execution matters a lot. |
1271 | 1331 | * Make sure to run maintenance/parserTests.php if you change this code. |
— | — | @@ -1273,15 +1333,13 @@ |
1274 | 1334 | */ |
1275 | 1335 | function replaceExternalLinks( $text ) { |
1276 | 1336 | global $wgContLang; |
1277 | | - $fname = 'Parser::replaceExternalLinks'; |
1278 | | - wfProfileIn( $fname ); |
| 1337 | + wfProfileIn( __METHOD__ ); |
1279 | 1338 | |
1280 | 1339 | $sk = $this->mOptions->getSkin(); |
1281 | 1340 | |
1282 | 1341 | $bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE ); |
| 1342 | + $s = array_shift( $bits ); |
1283 | 1343 | |
1284 | | - $s = $this->replaceFreeExternalLinks( array_shift( $bits ) ); |
1285 | | - |
1286 | 1344 | $i = 0; |
1287 | 1345 | while ( $i<count( $bits ) ) { |
1288 | 1346 | $url = $bits[$i++]; |
— | — | @@ -1308,7 +1366,7 @@ |
1309 | 1367 | $dtrail = ''; |
1310 | 1368 | |
1311 | 1369 | # Set linktype for CSS - if URL==text, link is essentially free |
1312 | | - $linktype = ($text == $url) ? 'free' : 'text'; |
| 1370 | + $linktype = ($text === $url) ? 'free' : 'text'; |
1313 | 1371 | |
1314 | 1372 | # No link text, e.g. [http://domain.tld/some.link] |
1315 | 1373 | if ( $text == '' ) { |
— | — | @@ -1331,10 +1389,6 @@ |
1332 | 1390 | |
1333 | 1391 | $url = Sanitizer::cleanUrl( $url ); |
1334 | 1392 | |
1335 | | - # Process the trail (i.e. everything after this link up until start of the next link), |
1336 | | - # replacing any non-bracketed links |
1337 | | - $trail = $this->replaceFreeExternalLinks( $trail ); |
1338 | | - |
1339 | 1393 | # Use the encoded URL |
1340 | 1394 | # This means that users can paste URLs directly into the text |
1341 | 1395 | # Funny characters like ö aren't valid in URLs anyway |
— | — | @@ -1344,96 +1398,15 @@ |
1345 | 1399 | # Register link in the output object. |
1346 | 1400 | # Replace unnecessary URL escape codes with the referenced character |
1347 | 1401 | # This prevents spammers from hiding links from the filters |
1348 | | - $pasteurized = Parser::replaceUnusualEscapes( $url ); |
| 1402 | + $pasteurized = self::replaceUnusualEscapes( $url ); |
1349 | 1403 | $this->mOutput->addExternalLink( $pasteurized ); |
1350 | 1404 | } |
1351 | 1405 | |
1352 | | - wfProfileOut( $fname ); |
| 1406 | + wfProfileOut( __METHOD__ ); |
1353 | 1407 | return $s; |
1354 | 1408 | } |
1355 | 1409 | |
1356 | 1410 | /** |
1357 | | - * Replace anything that looks like a URL with a link |
1358 | | - * @private |
1359 | | - */ |
1360 | | - function replaceFreeExternalLinks( $text ) { |
1361 | | - global $wgContLang; |
1362 | | - $fname = 'Parser::replaceFreeExternalLinks'; |
1363 | | - wfProfileIn( $fname ); |
1364 | | - |
1365 | | - $bits = preg_split( '/(\b(?:' . wfUrlProtocols() . '))/S', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); |
1366 | | - $s = array_shift( $bits ); |
1367 | | - $i = 0; |
1368 | | - |
1369 | | - $sk = $this->mOptions->getSkin(); |
1370 | | - |
1371 | | - while ( $i < count( $bits ) ){ |
1372 | | - $protocol = $bits[$i++]; |
1373 | | - $remainder = $bits[$i++]; |
1374 | | - |
1375 | | - $m = array(); |
1376 | | - if ( preg_match( '/^('.self::EXT_LINK_URL_CLASS.'+)(.*)$/s', $remainder, $m ) ) { |
1377 | | - # Found some characters after the protocol that look promising |
1378 | | - $url = $protocol . $m[1]; |
1379 | | - $trail = $m[2]; |
1380 | | - |
1381 | | - # special case: handle urls as url args: |
1382 | | - # http://www.example.com/foo?=http://www.example.com/bar |
1383 | | - if(strlen($trail) == 0 && |
1384 | | - isset($bits[$i]) && |
1385 | | - preg_match('/^'. wfUrlProtocols() . '$/S', $bits[$i]) && |
1386 | | - preg_match( '/^('.self::EXT_LINK_URL_CLASS.'+)(.*)$/s', $bits[$i + 1], $m )) |
1387 | | - { |
1388 | | - # add protocol, arg |
1389 | | - $url .= $bits[$i] . $m[1]; # protocol, url as arg to previous link |
1390 | | - $i += 2; |
1391 | | - $trail = $m[2]; |
1392 | | - } |
1393 | | - |
1394 | | - # The characters '<' and '>' (which were escaped by |
1395 | | - # removeHTMLtags()) should not be included in |
1396 | | - # URLs, per RFC 2396. |
1397 | | - $m2 = array(); |
1398 | | - if (preg_match('/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE)) { |
1399 | | - $trail = substr($url, $m2[0][1]) . $trail; |
1400 | | - $url = substr($url, 0, $m2[0][1]); |
1401 | | - } |
1402 | | - |
1403 | | - # Move trailing punctuation to $trail |
1404 | | - $sep = ',;\.:!?'; |
1405 | | - # If there is no left bracket, then consider right brackets fair game too |
1406 | | - if ( strpos( $url, '(' ) === false ) { |
1407 | | - $sep .= ')'; |
1408 | | - } |
1409 | | - |
1410 | | - $numSepChars = strspn( strrev( $url ), $sep ); |
1411 | | - if ( $numSepChars ) { |
1412 | | - $trail = substr( $url, -$numSepChars ) . $trail; |
1413 | | - $url = substr( $url, 0, -$numSepChars ); |
1414 | | - } |
1415 | | - |
1416 | | - $url = Sanitizer::cleanUrl( $url ); |
1417 | | - |
1418 | | - # Is this an external image? |
1419 | | - $text = $this->maybeMakeExternalImage( $url ); |
1420 | | - if ( $text === false ) { |
1421 | | - # Not an image, make a link |
1422 | | - $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() ); |
1423 | | - # Register it in the output object... |
1424 | | - # Replace unnecessary URL escape codes with their equivalent characters |
1425 | | - $pasteurized = Parser::replaceUnusualEscapes( $url ); |
1426 | | - $this->mOutput->addExternalLink( $pasteurized ); |
1427 | | - } |
1428 | | - $s .= $text . $trail; |
1429 | | - } else { |
1430 | | - $s .= $protocol . $remainder; |
1431 | | - } |
1432 | | - } |
1433 | | - wfProfileOut( $fname ); |
1434 | | - return $s; |
1435 | | - } |
1436 | | - |
1437 | | - /** |
1438 | 1411 | * Replace unusual URL escape codes with their equivalent characters |
1439 | 1412 | * @param string |
1440 | 1413 | * @return string |
— | — | @@ -1445,7 +1418,7 @@ |
1446 | 1419 | */ |
1447 | 1420 | static function replaceUnusualEscapes( $url ) { |
1448 | 1421 | return preg_replace_callback( '/%[0-9A-Fa-f]{2}/', |
1449 | | - array( 'Parser', 'replaceUnusualEscapesCallback' ), $url ); |
| 1422 | + array( __CLASS__, 'replaceUnusualEscapesCallback' ), $url ); |
1450 | 1423 | } |
1451 | 1424 | |
1452 | 1425 | /** |
— | — | @@ -1489,35 +1462,48 @@ |
1490 | 1463 | |
1491 | 1464 | /** |
1492 | 1465 | * Process [[ ]] wikilinks |
| 1466 | + * @return processed text |
1493 | 1467 | * |
1494 | 1468 | * @private |
1495 | 1469 | */ |
1496 | 1470 | function replaceInternalLinks( $s ) { |
| 1471 | + $this->mLinkHolders->merge( $this->replaceInternalLinks2( $s ) ); |
| 1472 | + return $s; |
| 1473 | + } |
| 1474 | + |
| 1475 | + /** |
| 1476 | + * Process [[ ]] wikilinks (RIL) |
| 1477 | + * @return LinkHolderArray |
| 1478 | + * |
| 1479 | + * @private |
| 1480 | + */ |
| 1481 | + function replaceInternalLinks2( &$s ) { |
1497 | 1482 | global $wgContLang; |
1498 | | - static $fname = 'Parser::replaceInternalLinks' ; |
1499 | 1483 | |
1500 | | - wfProfileIn( $fname ); |
| 1484 | + wfProfileIn( __METHOD__ ); |
1501 | 1485 | |
1502 | | - wfProfileIn( $fname.'-setup' ); |
1503 | | - static $tc = FALSE; |
| 1486 | + wfProfileIn( __METHOD__.'-setup' ); |
| 1487 | + static $tc = FALSE, $e1, $e1_img; |
1504 | 1488 | # the % is needed to support urlencoded titles as well |
1505 | | - if ( !$tc ) { $tc = Title::legalChars() . '#%'; } |
| 1489 | + if ( !$tc ) { |
| 1490 | + $tc = Title::legalChars() . '#%'; |
| 1491 | + # Match a link having the form [[namespace:link|alternate]]trail |
| 1492 | + $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD"; |
| 1493 | + # Match cases where there is no "]]", which might still be images |
| 1494 | + $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; |
| 1495 | + } |
1506 | 1496 | |
1507 | 1497 | $sk = $this->mOptions->getSkin(); |
| 1498 | + $holders = new LinkHolderArray( $this ); |
1508 | 1499 | |
1509 | 1500 | #split the entire text string on occurences of [[ |
1510 | | - $a = explode( '[[', ' ' . $s ); |
| 1501 | + $a = StringUtils::explode( '[[', ' ' . $s ); |
1511 | 1502 | #get the first element (all text up to first [[), and remove the space we added |
1512 | | - $s = array_shift( $a ); |
| 1503 | + $s = $a->current(); |
| 1504 | + $a->next(); |
| 1505 | + $line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void" |
1513 | 1506 | $s = substr( $s, 1 ); |
1514 | 1507 | |
1515 | | - # Match a link having the form [[namespace:link|alternate]]trail |
1516 | | - static $e1 = FALSE; |
1517 | | - if ( !$e1 ) { $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD"; } |
1518 | | - # Match cases where there is no "]]", which might still be images |
1519 | | - static $e1_img = FALSE; |
1520 | | - if ( !$e1_img ) { $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; } |
1521 | | - |
1522 | 1508 | $useLinkPrefixExtension = $wgContLang->linkPrefixExtension(); |
1523 | 1509 | $e2 = null; |
1524 | 1510 | if ( $useLinkPrefixExtension ) { |
— | — | @@ -1527,8 +1513,8 @@ |
1528 | 1514 | } |
1529 | 1515 | |
1530 | 1516 | if( is_null( $this->mTitle ) ) { |
1531 | | - wfProfileOut( $fname ); |
1532 | | - wfProfileOut( $fname.'-setup' ); |
| 1517 | + wfProfileOut( __METHOD__.'-setup' ); |
| 1518 | + wfProfileOut( __METHOD__ ); |
1533 | 1519 | throw new MWException( __METHOD__.": \$this->mTitle is null\n" ); |
1534 | 1520 | } |
1535 | 1521 | $nottalk = !$this->mTitle->isTalkPage(); |
— | — | @@ -1550,13 +1536,20 @@ |
1551 | 1537 | $selflink = array($this->mTitle->getPrefixedText()); |
1552 | 1538 | } |
1553 | 1539 | $useSubpages = $this->areSubpagesAllowed(); |
1554 | | - wfProfileOut( $fname.'-setup' ); |
| 1540 | + wfProfileOut( __METHOD__.'-setup' ); |
1555 | 1541 | |
1556 | 1542 | # Loop for each link |
1557 | | - for ($k = 0; isset( $a[$k] ); $k++) { |
1558 | | - $line = $a[$k]; |
| 1543 | + for ( ; $line !== false && $line !== null ; $a->next(), $line = $a->current() ) { |
| 1544 | + # Check for excessive memory usage |
| 1545 | + if ( $holders->isBig() ) { |
| 1546 | + # Too big |
| 1547 | + # Do the existence check, replace the link holders and clear the array |
| 1548 | + $holders->replace( $s ); |
| 1549 | + $holders->clear(); |
| 1550 | + } |
| 1551 | + |
1559 | 1552 | if ( $useLinkPrefixExtension ) { |
1560 | | - wfProfileIn( $fname.'-prefixhandling' ); |
| 1553 | + wfProfileIn( __METHOD__.'-prefixhandling' ); |
1561 | 1554 | if ( preg_match( $e2, $s, $m ) ) { |
1562 | 1555 | $prefix = $m[2]; |
1563 | 1556 | $s = $m[1]; |
— | — | @@ -1568,12 +1561,12 @@ |
1569 | 1562 | $prefix = $first_prefix; |
1570 | 1563 | $first_prefix = false; |
1571 | 1564 | } |
1572 | | - wfProfileOut( $fname.'-prefixhandling' ); |
| 1565 | + wfProfileOut( __METHOD__.'-prefixhandling' ); |
1573 | 1566 | } |
1574 | 1567 | |
1575 | 1568 | $might_be_img = false; |
1576 | 1569 | |
1577 | | - wfProfileIn( "$fname-e1" ); |
| 1570 | + wfProfileIn( __METHOD__."-e1" ); |
1578 | 1571 | if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt |
1579 | 1572 | $text = $m[2]; |
1580 | 1573 | # If we get a ] at the beginning of $m[3] that means we have a link that's something like: |
— | — | @@ -1607,18 +1600,18 @@ |
1608 | 1601 | $trail = ""; |
1609 | 1602 | } else { # Invalid form; output directly |
1610 | 1603 | $s .= $prefix . '[[' . $line ; |
1611 | | - wfProfileOut( "$fname-e1" ); |
| 1604 | + wfProfileOut( __METHOD__."-e1" ); |
1612 | 1605 | continue; |
1613 | 1606 | } |
1614 | | - wfProfileOut( "$fname-e1" ); |
1615 | | - wfProfileIn( "$fname-misc" ); |
| 1607 | + wfProfileOut( __METHOD__."-e1" ); |
| 1608 | + wfProfileIn( __METHOD__."-misc" ); |
1616 | 1609 | |
1617 | 1610 | # Don't allow internal links to pages containing |
1618 | 1611 | # PROTO: where PROTO is a valid URL protocol; these |
1619 | 1612 | # should be external links. |
1620 | 1613 | if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) { |
1621 | 1614 | $s .= $prefix . '[[' . $line ; |
1622 | | - wfProfileOut( "$fname-misc" ); |
| 1615 | + wfProfileOut( __METHOD__."-misc" ); |
1623 | 1616 | continue; |
1624 | 1617 | } |
1625 | 1618 | |
— | — | @@ -1629,33 +1622,36 @@ |
1630 | 1623 | $link = $m[1]; |
1631 | 1624 | } |
1632 | 1625 | |
1633 | | - $noforce = (substr($m[1], 0, 1) != ':'); |
| 1626 | + $noforce = (substr($m[1], 0, 1) !== ':'); |
1634 | 1627 | if (!$noforce) { |
1635 | 1628 | # Strip off leading ':' |
1636 | 1629 | $link = substr($link, 1); |
1637 | 1630 | } |
1638 | 1631 | |
1639 | | - wfProfileOut( "$fname-misc" ); |
1640 | | - wfProfileIn( "$fname-title" ); |
| 1632 | + wfProfileOut( __METHOD__."-misc" ); |
| 1633 | + wfProfileIn( __METHOD__."-title" ); |
1641 | 1634 | $nt = Title::newFromText( $this->mStripState->unstripNoWiki($link) ); |
1642 | 1635 | if( !$nt ) { |
1643 | 1636 | $s .= $prefix . '[[' . $line; |
1644 | | - wfProfileOut( "$fname-title" ); |
| 1637 | + wfProfileOut( __METHOD__."-title" ); |
1645 | 1638 | continue; |
1646 | 1639 | } |
1647 | 1640 | |
1648 | 1641 | $ns = $nt->getNamespace(); |
1649 | 1642 | $iw = $nt->getInterWiki(); |
1650 | | - wfProfileOut( "$fname-title" ); |
| 1643 | + wfProfileOut( __METHOD__."-title" ); |
1651 | 1644 | |
1652 | 1645 | if ($might_be_img) { # if this is actually an invalid link |
1653 | | - wfProfileIn( "$fname-might_be_img" ); |
| 1646 | + wfProfileIn( __METHOD__."-might_be_img" ); |
1654 | 1647 | if ($ns == NS_IMAGE && $noforce) { #but might be an image |
1655 | 1648 | $found = false; |
1656 | | - while (isset ($a[$k+1]) ) { |
| 1649 | + while ( true ) { |
1657 | 1650 | #look at the next 'line' to see if we can close it there |
1658 | | - $spliced = array_splice( $a, $k + 1, 1 ); |
1659 | | - $next_line = array_shift( $spliced ); |
| 1651 | + $a->next(); |
| 1652 | + $next_line = $a->current(); |
| 1653 | + if ( $next_line === false || $next_line === null ) { |
| 1654 | + break; |
| 1655 | + } |
1660 | 1656 | $m = explode( ']]', $next_line, 3 ); |
1661 | 1657 | if ( count( $m ) == 3 ) { |
1662 | 1658 | # the first ]] closes the inner link, the second the image |
— | — | @@ -1675,19 +1671,19 @@ |
1676 | 1672 | if ( !$found ) { |
1677 | 1673 | # we couldn't find the end of this imageLink, so output it raw |
1678 | 1674 | #but don't ignore what might be perfectly normal links in the text we've examined |
1679 | | - $text = $this->replaceInternalLinks($text); |
| 1675 | + $holders->merge( $this->replaceInternalLinks2( $text ) ); |
1680 | 1676 | $s .= "{$prefix}[[$link|$text"; |
1681 | 1677 | # note: no $trail, because without an end, there *is* no trail |
1682 | | - wfProfileOut( "$fname-might_be_img" ); |
| 1678 | + wfProfileOut( __METHOD__."-might_be_img" ); |
1683 | 1679 | continue; |
1684 | 1680 | } |
1685 | 1681 | } else { #it's not an image, so output it raw |
1686 | 1682 | $s .= "{$prefix}[[$link|$text"; |
1687 | 1683 | # note: no $trail, because without an end, there *is* no trail |
1688 | | - wfProfileOut( "$fname-might_be_img" ); |
| 1684 | + wfProfileOut( __METHOD__."-might_be_img" ); |
1689 | 1685 | continue; |
1690 | 1686 | } |
1691 | | - wfProfileOut( "$fname-might_be_img" ); |
| 1687 | + wfProfileOut( __METHOD__."-might_be_img" ); |
1692 | 1688 | } |
1693 | 1689 | |
1694 | 1690 | $wasblank = ( '' == $text ); |
— | — | @@ -1697,41 +1693,36 @@ |
1698 | 1694 | if( $noforce ) { |
1699 | 1695 | |
1700 | 1696 | # Interwikis |
1701 | | - wfProfileIn( "$fname-interwiki" ); |
| 1697 | + wfProfileIn( __METHOD__."-interwiki" ); |
1702 | 1698 | if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) { |
1703 | 1699 | $this->mOutput->addLanguageLink( $nt->getFullText() ); |
1704 | 1700 | $s = rtrim($s . $prefix); |
1705 | 1701 | $s .= trim($trail, "\n") == '' ? '': $prefix . $trail; |
1706 | | - wfProfileOut( "$fname-interwiki" ); |
| 1702 | + wfProfileOut( __METHOD__."-interwiki" ); |
1707 | 1703 | continue; |
1708 | 1704 | } |
1709 | | - wfProfileOut( "$fname-interwiki" ); |
| 1705 | + wfProfileOut( __METHOD__."-interwiki" ); |
1710 | 1706 | |
1711 | 1707 | if ( $ns == NS_IMAGE ) { |
1712 | | - wfProfileIn( "$fname-image" ); |
| 1708 | + wfProfileIn( __METHOD__."-image" ); |
1713 | 1709 | if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) { |
1714 | 1710 | # recursively parse links inside the image caption |
1715 | 1711 | # actually, this will parse them in any other parameters, too, |
1716 | 1712 | # but it might be hard to fix that, and it doesn't matter ATM |
1717 | 1713 | $text = $this->replaceExternalLinks($text); |
1718 | | - $text = $this->replaceInternalLinks($text); |
| 1714 | + $holders->merge( $this->replaceInternalLinks2( $text ) ); |
1719 | 1715 | |
1720 | 1716 | # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them |
1721 | | - $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text ) ) . $trail; |
1722 | | - $this->mOutput->addImage( $nt->getDBkey() ); |
1723 | | - |
1724 | | - wfProfileOut( "$fname-image" ); |
1725 | | - continue; |
1726 | | - } else { |
1727 | | - # We still need to record the image's presence on the page |
1728 | | - $this->mOutput->addImage( $nt->getDBkey() ); |
| 1717 | + $s .= $prefix . $this->armorLinks( $this->makeImage( $nt, $text, $holders ) ) . $trail; |
1729 | 1718 | } |
1730 | | - wfProfileOut( "$fname-image" ); |
| 1719 | + $this->mOutput->addImage( $nt->getDBkey() ); |
| 1720 | + wfProfileOut( __METHOD__."-image" ); |
| 1721 | + continue; |
1731 | 1722 | |
1732 | 1723 | } |
1733 | 1724 | |
1734 | 1725 | if ( $ns == NS_CATEGORY ) { |
1735 | | - wfProfileIn( "$fname-category" ); |
| 1726 | + wfProfileIn( __METHOD__."-category" ); |
1736 | 1727 | $s = rtrim($s . "\n"); # bug 87 |
1737 | 1728 | |
1738 | 1729 | if ( $wasblank ) { |
— | — | @@ -1750,7 +1741,7 @@ |
1751 | 1742 | */ |
1752 | 1743 | $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail; |
1753 | 1744 | |
1754 | | - wfProfileOut( "$fname-category" ); |
| 1745 | + wfProfileOut( __METHOD__."-category" ); |
1755 | 1746 | continue; |
1756 | 1747 | } |
1757 | 1748 | } |
— | — | @@ -1781,7 +1772,7 @@ |
1782 | 1773 | if( SpecialPage::exists( $nt->getDBkey() ) ) { |
1783 | 1774 | $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix ); |
1784 | 1775 | } else { |
1785 | | - $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix ); |
| 1776 | + $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix ); |
1786 | 1777 | } |
1787 | 1778 | continue; |
1788 | 1779 | } elseif( $ns == NS_IMAGE ) { |
— | — | @@ -1795,10 +1786,10 @@ |
1796 | 1787 | continue; |
1797 | 1788 | } |
1798 | 1789 | } |
1799 | | - $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix ); |
| 1790 | + $s .= $holders->makeHolder( $nt, $text, '', $trail, $prefix ); |
1800 | 1791 | } |
1801 | | - wfProfileOut( $fname ); |
1802 | | - return $s; |
| 1792 | + wfProfileOut( __METHOD__ ); |
| 1793 | + return $holders; |
1803 | 1794 | } |
1804 | 1795 | |
1805 | 1796 | /** |
— | — | @@ -1807,32 +1798,10 @@ |
1808 | 1799 | * parsing of interwiki links, and secondly to allow all existence checks and |
1809 | 1800 | * article length checks (for stub links) to be bundled into a single query. |
1810 | 1801 | * |
| 1802 | + * @deprecated |
1811 | 1803 | */ |
1812 | 1804 | function makeLinkHolder( &$nt, $text = '', $query = '', $trail = '', $prefix = '' ) { |
1813 | | - wfProfileIn( __METHOD__ ); |
1814 | | - if ( ! is_object($nt) ) { |
1815 | | - # Fail gracefully |
1816 | | - $retVal = "<!-- ERROR -->{$prefix}{$text}{$trail}"; |
1817 | | - } else { |
1818 | | - # Separate the link trail from the rest of the link |
1819 | | - list( $inside, $trail ) = Linker::splitTrail( $trail ); |
1820 | | - |
1821 | | - if ( $nt->isExternal() ) { |
1822 | | - $nr = array_push( $this->mInterwikiLinkHolders['texts'], $prefix.$text.$inside ); |
1823 | | - $this->mInterwikiLinkHolders['titles'][] = $nt; |
1824 | | - $retVal = '<!--IWLINK '. ($nr-1) ."-->{$trail}"; |
1825 | | - } else { |
1826 | | - $nr = array_push( $this->mLinkHolders['namespaces'], $nt->getNamespace() ); |
1827 | | - $this->mLinkHolders['dbkeys'][] = $nt->getDBkey(); |
1828 | | - $this->mLinkHolders['queries'][] = $query; |
1829 | | - $this->mLinkHolders['texts'][] = $prefix.$text.$inside; |
1830 | | - $this->mLinkHolders['titles'][] = $nt; |
1831 | | - |
1832 | | - $retVal = '<!--LINK '. ($nr-1) ."-->{$trail}"; |
1833 | | - } |
1834 | | - } |
1835 | | - wfProfileOut( __METHOD__ ); |
1836 | | - return $retVal; |
| 1805 | + return $this->mLinkHolders->makeHolder( $nt, $text, $query, $trail, $prefix ); |
1837 | 1806 | } |
1838 | 1807 | |
1839 | 1808 | /** |
— | — | @@ -1860,11 +1829,9 @@ |
1861 | 1830 | * Insert a NOPARSE hacky thing into any inline links in a chunk that's |
1862 | 1831 | * going to go through further parsing steps before inline URL expansion. |
1863 | 1832 | * |
1864 | | - * In particular this is important when using action=render, which causes |
1865 | | - * full URLs to be included. |
| 1833 | + * Not needed quite as much as it used to be since free links are a bit |
| 1834 | + * more sensible these days. But bracketed links are still an issue. |
1866 | 1835 | * |
1867 | | - * Oh man I hate our multi-layer parser! |
1868 | | - * |
1869 | 1836 | * @param string more-or-less HTML |
1870 | 1837 | * @return string less-or-more HTML with NOPARSE bits |
1871 | 1838 | */ |
— | — | @@ -1898,8 +1865,7 @@ |
1899 | 1866 | # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage |
1900 | 1867 | # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage |
1901 | 1868 | |
1902 | | - $fname = 'Parser::maybeDoSubpageLink'; |
1903 | | - wfProfileIn( $fname ); |
| 1869 | + wfProfileIn( __METHOD__ ); |
1904 | 1870 | $ret = $target; # default return value is no change |
1905 | 1871 | |
1906 | 1872 | # Some namespaces don't allow subpages, |
— | — | @@ -1915,7 +1881,7 @@ |
1916 | 1882 | # bug 7425 |
1917 | 1883 | $target = trim( $target ); |
1918 | 1884 | # Look at the first character |
1919 | | - if( $target != '' && $target{0} == '/' ) { |
| 1885 | + if( $target != '' && $target{0} === '/' ) { |
1920 | 1886 | # / at end means we don't want the slash to be shown |
1921 | 1887 | $m = array(); |
1922 | 1888 | $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m ); |
— | — | @@ -1942,7 +1908,7 @@ |
1943 | 1909 | if( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page |
1944 | 1910 | $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) ); |
1945 | 1911 | # / at the end means don't show full path |
1946 | | - if( substr( $nodotdot, -1, 1 ) == '/' ) { |
| 1912 | + if( substr( $nodotdot, -1, 1 ) === '/' ) { |
1947 | 1913 | $nodotdot = substr( $nodotdot, 0, -1 ); |
1948 | 1914 | if( '' === $text ) { |
1949 | 1915 | $text = $nodotdot . $suffix; |
— | — | @@ -1958,7 +1924,7 @@ |
1959 | 1925 | } |
1960 | 1926 | } |
1961 | 1927 | |
1962 | | - wfProfileOut( $fname ); |
| 1928 | + wfProfileOut( __METHOD__ ); |
1963 | 1929 | return $ret; |
1964 | 1930 | } |
1965 | 1931 | |
— | — | @@ -1994,10 +1960,10 @@ |
1995 | 1961 | /* private */ function openList( $char ) { |
1996 | 1962 | $result = $this->closeParagraph(); |
1997 | 1963 | |
1998 | | - if ( '*' == $char ) { $result .= '<ul><li>'; } |
1999 | | - else if ( '#' == $char ) { $result .= '<ol><li>'; } |
2000 | | - else if ( ':' == $char ) { $result .= '<dl><dd>'; } |
2001 | | - else if ( ';' == $char ) { |
| 1964 | + if ( '*' === $char ) { $result .= '<ul><li>'; } |
| 1965 | + else if ( '#' === $char ) { $result .= '<ol><li>'; } |
| 1966 | + else if ( ':' === $char ) { $result .= '<dl><dd>'; } |
| 1967 | + else if ( ';' === $char ) { |
2002 | 1968 | $result .= '<dl><dt>'; |
2003 | 1969 | $this->mDTopen = true; |
2004 | 1970 | } |
— | — | @@ -2007,11 +1973,11 @@ |
2008 | 1974 | } |
2009 | 1975 | |
2010 | 1976 | /* private */ function nextItem( $char ) { |
2011 | | - if ( '*' == $char || '#' == $char ) { return '</li><li>'; } |
2012 | | - else if ( ':' == $char || ';' == $char ) { |
| 1977 | + if ( '*' === $char || '#' === $char ) { return '</li><li>'; } |
| 1978 | + else if ( ':' === $char || ';' === $char ) { |
2013 | 1979 | $close = '</dd>'; |
2014 | 1980 | if ( $this->mDTopen ) { $close = '</dt>'; } |
2015 | | - if ( ';' == $char ) { |
| 1981 | + if ( ';' === $char ) { |
2016 | 1982 | $this->mDTopen = true; |
2017 | 1983 | return $close . '<dt>'; |
2018 | 1984 | } else { |
— | — | @@ -2023,9 +1989,9 @@ |
2024 | 1990 | } |
2025 | 1991 | |
2026 | 1992 | /* private */ function closeList( $char ) { |
2027 | | - if ( '*' == $char ) { $text = '</li></ul>'; } |
2028 | | - else if ( '#' == $char ) { $text = '</li></ol>'; } |
2029 | | - else if ( ':' == $char ) { |
| 1993 | + if ( '*' === $char ) { $text = '</li></ul>'; } |
| 1994 | + else if ( '#' === $char ) { $text = '</li></ol>'; } |
| 1995 | + else if ( ':' === $char ) { |
2030 | 1996 | if ( $this->mDTopen ) { |
2031 | 1997 | $this->mDTopen = false; |
2032 | 1998 | $text = '</dt></dl>'; |
— | — | @@ -2039,56 +2005,59 @@ |
2040 | 2006 | /**#@-*/ |
2041 | 2007 | |
2042 | 2008 | /** |
2043 | | - * Make lists from lines starting with ':', '*', '#', etc. |
| 2009 | + * Make lists from lines starting with ':', '*', '#', etc. (DBL) |
2044 | 2010 | * |
2045 | 2011 | * @private |
2046 | 2012 | * @return string the lists rendered as HTML |
2047 | 2013 | */ |
2048 | 2014 | function doBlockLevels( $text, $linestart ) { |
2049 | | - $fname = 'Parser::doBlockLevels'; |
2050 | | - wfProfileIn( $fname ); |
| 2015 | + wfProfileIn( __METHOD__ ); |
2051 | 2016 | |
2052 | 2017 | # Parsing through the text line by line. The main thing |
2053 | 2018 | # happening here is handling of block-level elements p, pre, |
2054 | 2019 | # and making lists from lines starting with * # : etc. |
2055 | 2020 | # |
2056 | | - $textLines = explode( "\n", $text ); |
| 2021 | + $textLines = StringUtils::explode( "\n", $text ); |
2057 | 2022 | |
2058 | 2023 | $lastPrefix = $output = ''; |
2059 | 2024 | $this->mDTopen = $inBlockElem = false; |
2060 | 2025 | $prefixLength = 0; |
2061 | 2026 | $paragraphStack = false; |
2062 | 2027 | |
2063 | | - if ( !$linestart ) { |
2064 | | - $output .= array_shift( $textLines ); |
2065 | | - } |
2066 | 2028 | foreach ( $textLines as $oLine ) { |
| 2029 | + # Fix up $linestart |
| 2030 | + if ( !$linestart ) { |
| 2031 | + $output .= $oLine; |
| 2032 | + $linestart = true; |
| 2033 | + continue; |
| 2034 | + } |
| 2035 | + |
2067 | 2036 | $lastPrefixLength = strlen( $lastPrefix ); |
2068 | 2037 | $preCloseMatch = preg_match('/<\\/pre/i', $oLine ); |
2069 | 2038 | $preOpenMatch = preg_match('/<pre/i', $oLine ); |
2070 | 2039 | if ( !$this->mInPre ) { |
2071 | 2040 | # Multiple prefixes may abut each other for nested lists. |
2072 | 2041 | $prefixLength = strspn( $oLine, '*#:;' ); |
2073 | | - $pref = substr( $oLine, 0, $prefixLength ); |
| 2042 | + $prefix = substr( $oLine, 0, $prefixLength ); |
2074 | 2043 | |
2075 | 2044 | # eh? |
2076 | | - $pref2 = str_replace( ';', ':', $pref ); |
| 2045 | + $prefix2 = str_replace( ';', ':', $prefix ); |
2077 | 2046 | $t = substr( $oLine, $prefixLength ); |
2078 | | - $this->mInPre = !empty($preOpenMatch); |
| 2047 | + $this->mInPre = (bool)$preOpenMatch; |
2079 | 2048 | } else { |
2080 | 2049 | # Don't interpret any other prefixes in preformatted text |
2081 | 2050 | $prefixLength = 0; |
2082 | | - $pref = $pref2 = ''; |
| 2051 | + $prefix = $prefix2 = ''; |
2083 | 2052 | $t = $oLine; |
2084 | 2053 | } |
2085 | 2054 | |
2086 | 2055 | # List generation |
2087 | | - if( $prefixLength && 0 == strcmp( $lastPrefix, $pref2 ) ) { |
| 2056 | + if( $prefixLength && $lastPrefix === $prefix2 ) { |
2088 | 2057 | # Same as the last item, so no need to deal with nesting or opening stuff |
2089 | | - $output .= $this->nextItem( substr( $pref, -1 ) ); |
| 2058 | + $output .= $this->nextItem( substr( $prefix, -1 ) ); |
2090 | 2059 | $paragraphStack = false; |
2091 | 2060 | |
2092 | | - if ( substr( $pref, -1 ) == ';') { |
| 2061 | + if ( substr( $prefix, -1 ) === ';') { |
2093 | 2062 | # The one nasty exception: definition lists work like this: |
2094 | 2063 | # ; title : definition text |
2095 | 2064 | # So we check for : in the remainder text to split up the |
— | — | @@ -2101,21 +2070,21 @@ |
2102 | 2071 | } |
2103 | 2072 | } elseif( $prefixLength || $lastPrefixLength ) { |
2104 | 2073 | # Either open or close a level... |
2105 | | - $commonPrefixLength = $this->getCommon( $pref, $lastPrefix ); |
| 2074 | + $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix ); |
2106 | 2075 | $paragraphStack = false; |
2107 | 2076 | |
2108 | 2077 | while( $commonPrefixLength < $lastPrefixLength ) { |
2109 | | - $output .= $this->closeList( $lastPrefix{$lastPrefixLength-1} ); |
| 2078 | + $output .= $this->closeList( $lastPrefix[$lastPrefixLength-1] ); |
2110 | 2079 | --$lastPrefixLength; |
2111 | 2080 | } |
2112 | 2081 | if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) { |
2113 | | - $output .= $this->nextItem( $pref{$commonPrefixLength-1} ); |
| 2082 | + $output .= $this->nextItem( $prefix[$commonPrefixLength-1] ); |
2114 | 2083 | } |
2115 | 2084 | while ( $prefixLength > $commonPrefixLength ) { |
2116 | | - $char = substr( $pref, $commonPrefixLength, 1 ); |
| 2085 | + $char = substr( $prefix, $commonPrefixLength, 1 ); |
2117 | 2086 | $output .= $this->openList( $char ); |
2118 | 2087 | |
2119 | | - if ( ';' == $char ) { |
| 2088 | + if ( ';' === $char ) { |
2120 | 2089 | # FIXME: This is dupe of code above |
2121 | 2090 | if ($this->findColonNoLinks($t, $term, $t2) !== false) { |
2122 | 2091 | $t = $t2; |
— | — | @@ -2124,10 +2093,10 @@ |
2125 | 2094 | } |
2126 | 2095 | ++$commonPrefixLength; |
2127 | 2096 | } |
2128 | | - $lastPrefix = $pref2; |
| 2097 | + $lastPrefix = $prefix2; |
2129 | 2098 | } |
2130 | 2099 | if( 0 == $prefixLength ) { |
2131 | | - wfProfileIn( "$fname-paragraph" ); |
| 2100 | + wfProfileIn( __METHOD__."-paragraph" ); |
2132 | 2101 | # No prefix (not in list)--go to paragraph mode |
2133 | 2102 | // XXX: use a stack for nestable elements like span, table and div |
2134 | 2103 | $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t ); |
— | — | @@ -2147,9 +2116,9 @@ |
2148 | 2117 | $inBlockElem = true; |
2149 | 2118 | } |
2150 | 2119 | } else if ( !$inBlockElem && !$this->mInPre ) { |
2151 | | - if ( ' ' == $t{0} and ( $this->mLastSection == 'pre' or trim($t) != '' ) ) { |
| 2120 | + if ( ' ' == $t{0} and ( $this->mLastSection === 'pre' or trim($t) != '' ) ) { |
2152 | 2121 | // pre |
2153 | | - if ($this->mLastSection != 'pre') { |
| 2122 | + if ($this->mLastSection !== 'pre') { |
2154 | 2123 | $paragraphStack = false; |
2155 | 2124 | $output .= $this->closeParagraph().'<pre>'; |
2156 | 2125 | $this->mLastSection = 'pre'; |
— | — | @@ -2163,7 +2132,7 @@ |
2164 | 2133 | $paragraphStack = false; |
2165 | 2134 | $this->mLastSection = 'p'; |
2166 | 2135 | } else { |
2167 | | - if ($this->mLastSection != 'p' ) { |
| 2136 | + if ($this->mLastSection !== 'p' ) { |
2168 | 2137 | $output .= $this->closeParagraph(); |
2169 | 2138 | $this->mLastSection = ''; |
2170 | 2139 | $paragraphStack = '<p>'; |
— | — | @@ -2176,14 +2145,14 @@ |
2177 | 2146 | $output .= $paragraphStack; |
2178 | 2147 | $paragraphStack = false; |
2179 | 2148 | $this->mLastSection = 'p'; |
2180 | | - } else if ($this->mLastSection != 'p') { |
| 2149 | + } else if ($this->mLastSection !== 'p') { |
2181 | 2150 | $output .= $this->closeParagraph().'<p>'; |
2182 | 2151 | $this->mLastSection = 'p'; |
2183 | 2152 | } |
2184 | 2153 | } |
2185 | 2154 | } |
2186 | 2155 | } |
2187 | | - wfProfileOut( "$fname-paragraph" ); |
| 2156 | + wfProfileOut( __METHOD__."-paragraph" ); |
2188 | 2157 | } |
2189 | 2158 | // somewhere above we forget to get out of pre block (bug 785) |
2190 | 2159 | if($preCloseMatch && $this->mInPre) { |
— | — | @@ -2194,7 +2163,7 @@ |
2195 | 2164 | } |
2196 | 2165 | } |
2197 | 2166 | while ( $prefixLength ) { |
2198 | | - $output .= $this->closeList( $pref2{$prefixLength-1} ); |
| 2167 | + $output .= $this->closeList( $prefix2[$prefixLength-1] ); |
2199 | 2168 | --$prefixLength; |
2200 | 2169 | } |
2201 | 2170 | if ( '' != $this->mLastSection ) { |
— | — | @@ -2202,7 +2171,7 @@ |
2203 | 2172 | $this->mLastSection = ''; |
2204 | 2173 | } |
2205 | 2174 | |
2206 | | - wfProfileOut( $fname ); |
| 2175 | + wfProfileOut( __METHOD__ ); |
2207 | 2176 | return $output; |
2208 | 2177 | } |
2209 | 2178 | |
— | — | @@ -2215,13 +2184,12 @@ |
2216 | 2185 | * return string the position of the ':', or false if none found |
2217 | 2186 | */ |
2218 | 2187 | function findColonNoLinks($str, &$before, &$after) { |
2219 | | - $fname = 'Parser::findColonNoLinks'; |
2220 | | - wfProfileIn( $fname ); |
| 2188 | + wfProfileIn( __METHOD__ ); |
2221 | 2189 | |
2222 | 2190 | $pos = strpos( $str, ':' ); |
2223 | 2191 | if( $pos === false ) { |
2224 | 2192 | // Nothing to find! |
2225 | | - wfProfileOut( $fname ); |
| 2193 | + wfProfileOut( __METHOD__ ); |
2226 | 2194 | return false; |
2227 | 2195 | } |
2228 | 2196 | |
— | — | @@ -2230,7 +2198,7 @@ |
2231 | 2199 | // Easy; no tag nesting to worry about |
2232 | 2200 | $before = substr( $str, 0, $pos ); |
2233 | 2201 | $after = substr( $str, $pos+1 ); |
2234 | | - wfProfileOut( $fname ); |
| 2202 | + wfProfileOut( __METHOD__ ); |
2235 | 2203 | return $pos; |
2236 | 2204 | } |
2237 | 2205 | |
— | — | @@ -2254,7 +2222,7 @@ |
2255 | 2223 | // We found it! |
2256 | 2224 | $before = substr( $str, 0, $i ); |
2257 | 2225 | $after = substr( $str, $i + 1 ); |
2258 | | - wfProfileOut( $fname ); |
| 2226 | + wfProfileOut( __METHOD__ ); |
2259 | 2227 | return $i; |
2260 | 2228 | } |
2261 | 2229 | // Embedded in a tag; don't break it. |
— | — | @@ -2264,7 +2232,7 @@ |
2265 | 2233 | $colon = strpos( $str, ':', $i ); |
2266 | 2234 | if( $colon === false ) { |
2267 | 2235 | // Nothing else interesting |
2268 | | - wfProfileOut( $fname ); |
| 2236 | + wfProfileOut( __METHOD__ ); |
2269 | 2237 | return false; |
2270 | 2238 | } |
2271 | 2239 | $lt = strpos( $str, '<', $i ); |
— | — | @@ -2273,7 +2241,7 @@ |
2274 | 2242 | // We found it! |
2275 | 2243 | $before = substr( $str, 0, $colon ); |
2276 | 2244 | $after = substr( $str, $colon + 1 ); |
2277 | | - wfProfileOut( $fname ); |
| 2245 | + wfProfileOut( __METHOD__ ); |
2278 | 2246 | return $i; |
2279 | 2247 | } |
2280 | 2248 | } |
— | — | @@ -2320,18 +2288,18 @@ |
2321 | 2289 | break; |
2322 | 2290 | case 3: // self::COLON_STATE_CLOSETAG: |
2323 | 2291 | // In a </tag> |
2324 | | - if( $c == ">" ) { |
| 2292 | + if( $c === ">" ) { |
2325 | 2293 | $stack--; |
2326 | 2294 | if( $stack < 0 ) { |
2327 | | - wfDebug( "Invalid input in $fname; too many close tags\n" ); |
2328 | | - wfProfileOut( $fname ); |
| 2295 | + wfDebug( __METHOD__.": Invalid input; too many close tags\n" ); |
| 2296 | + wfProfileOut( __METHOD__ ); |
2329 | 2297 | return false; |
2330 | 2298 | } |
2331 | 2299 | $state = self::COLON_STATE_TEXT; |
2332 | 2300 | } |
2333 | 2301 | break; |
2334 | 2302 | case self::COLON_STATE_TAGSLASH: |
2335 | | - if( $c == ">" ) { |
| 2303 | + if( $c === ">" ) { |
2336 | 2304 | // Yes, a self-closed tag <blah/> |
2337 | 2305 | $state = self::COLON_STATE_TEXT; |
2338 | 2306 | } else { |
— | — | @@ -2340,33 +2308,33 @@ |
2341 | 2309 | } |
2342 | 2310 | break; |
2343 | 2311 | case 5: // self::COLON_STATE_COMMENT: |
2344 | | - if( $c == "-" ) { |
| 2312 | + if( $c === "-" ) { |
2345 | 2313 | $state = self::COLON_STATE_COMMENTDASH; |
2346 | 2314 | } |
2347 | 2315 | break; |
2348 | 2316 | case self::COLON_STATE_COMMENTDASH: |
2349 | | - if( $c == "-" ) { |
| 2317 | + if( $c === "-" ) { |
2350 | 2318 | $state = self::COLON_STATE_COMMENTDASHDASH; |
2351 | 2319 | } else { |
2352 | 2320 | $state = self::COLON_STATE_COMMENT; |
2353 | 2321 | } |
2354 | 2322 | break; |
2355 | 2323 | case self::COLON_STATE_COMMENTDASHDASH: |
2356 | | - if( $c == ">" ) { |
| 2324 | + if( $c === ">" ) { |
2357 | 2325 | $state = self::COLON_STATE_TEXT; |
2358 | 2326 | } else { |
2359 | 2327 | $state = self::COLON_STATE_COMMENT; |
2360 | 2328 | } |
2361 | 2329 | break; |
2362 | 2330 | default: |
2363 | | - throw new MWException( "State machine error in $fname" ); |
| 2331 | + throw new MWException( "State machine error in " . __METHOD__ ); |
2364 | 2332 | } |
2365 | 2333 | } |
2366 | 2334 | if( $stack > 0 ) { |
2367 | | - wfDebug( "Invalid input in $fname; not enough close tags (stack $stack, state $state)\n" ); |
| 2335 | + wfDebug( __METHOD__.": Invalid input; not enough close tags (stack $stack, state $state)\n" ); |
2368 | 2336 | return false; |
2369 | 2337 | } |
2370 | | - wfProfileOut( $fname ); |
| 2338 | + wfProfileOut( __METHOD__ ); |
2371 | 2339 | return false; |
2372 | 2340 | } |
2373 | 2341 | |
— | — | @@ -2596,12 +2564,11 @@ |
2597 | 2565 | * @private |
2598 | 2566 | */ |
2599 | 2567 | function initialiseVariables() { |
2600 | | - $fname = 'Parser::initialiseVariables'; |
2601 | | - wfProfileIn( $fname ); |
| 2568 | + wfProfileIn( __METHOD__ ); |
2602 | 2569 | $variableIDs = MagicWord::getVariableIDs(); |
2603 | 2570 | |
2604 | 2571 | $this->mVariables = new MagicWordArray( $variableIDs ); |
2605 | | - wfProfileOut( $fname ); |
| 2572 | + wfProfileOut( __METHOD__ ); |
2606 | 2573 | } |
2607 | 2574 | |
2608 | 2575 | /** |
— | — | @@ -2670,8 +2637,7 @@ |
2671 | 2638 | return $text; |
2672 | 2639 | } |
2673 | 2640 | |
2674 | | - $fname = __METHOD__; |
2675 | | - wfProfileIn( $fname ); |
| 2641 | + wfProfileIn( __METHOD__ ); |
2676 | 2642 | |
2677 | 2643 | if ( $frame === false ) { |
2678 | 2644 | $frame = $this->getPreprocessor()->newFrame(); |
— | — | @@ -2684,7 +2650,7 @@ |
2685 | 2651 | $flags = $argsOnly ? PPFrame::NO_TEMPLATES : 0; |
2686 | 2652 | $text = $frame->expand( $dom, $flags ); |
2687 | 2653 | |
2688 | | - wfProfileOut( $fname ); |
| 2654 | + wfProfileOut( __METHOD__ ); |
2689 | 2655 | return $text; |
2690 | 2656 | } |
2691 | 2657 | |
— | — | @@ -2747,8 +2713,7 @@ |
2748 | 2714 | */ |
2749 | 2715 | function braceSubstitution( $piece, $frame ) { |
2750 | 2716 | global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces; |
2751 | | - $fname = __METHOD__; |
2752 | | - wfProfileIn( $fname ); |
| 2717 | + wfProfileIn( __METHOD__ ); |
2753 | 2718 | wfProfileIn( __METHOD__.'-setup' ); |
2754 | 2719 | |
2755 | 2720 | # Flags |
— | — | @@ -2935,7 +2900,7 @@ |
2936 | 2901 | } |
2937 | 2902 | } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) { |
2938 | 2903 | $found = false; //access denied |
2939 | | - wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() ); |
| 2904 | + wfDebug( __METHOD__.": template inclusion denied for " . $title->getPrefixedDBkey() ); |
2940 | 2905 | } else { |
2941 | 2906 | list( $text, $title ) = $this->getTemplateDom( $title ); |
2942 | 2907 | if ( $text !== false ) { |
— | — | @@ -2969,7 +2934,7 @@ |
2970 | 2935 | # Recover the source wikitext and return it |
2971 | 2936 | if ( !$found ) { |
2972 | 2937 | $text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args ); |
2973 | | - wfProfileOut( $fname ); |
| 2938 | + wfProfileOut( __METHOD__ ); |
2974 | 2939 | return array( 'object' => $text ); |
2975 | 2940 | } |
2976 | 2941 | |
— | — | @@ -3028,7 +2993,7 @@ |
3029 | 2994 | $ret = array( 'text' => $text ); |
3030 | 2995 | } |
3031 | 2996 | |
3032 | | - wfProfileOut( $fname ); |
| 2997 | + wfProfileOut( __METHOD__ ); |
3033 | 2998 | return $ret; |
3034 | 2999 | } |
3035 | 3000 | |
— | — | @@ -3315,7 +3280,7 @@ |
3316 | 3281 | } |
3317 | 3282 | } |
3318 | 3283 | |
3319 | | - if ( $name == 'html' || $name == 'nowiki' ) { |
| 3284 | + if ( $name === 'html' || $name === 'nowiki' ) { |
3320 | 3285 | $this->mStripState->nowiki->setPair( $marker, $output ); |
3321 | 3286 | } else { |
3322 | 3287 | $this->mStripState->general->setPair( $marker, $output ); |
— | — | @@ -3571,12 +3536,7 @@ |
3572 | 3537 | # <!--LINK number--> |
3573 | 3538 | # turns into |
3574 | 3539 | # link text with suffix |
3575 | | - $safeHeadline = preg_replace( '/<!--LINK ([0-9]*)-->/e', |
3576 | | - "\$this->mLinkHolders['texts'][\$1]", |
3577 | | - $safeHeadline ); |
3578 | | - $safeHeadline = preg_replace( '/<!--IWLINK ([0-9]*)-->/e', |
3579 | | - "\$this->mInterwikiLinkHolders['texts'][\$1]", |
3580 | | - $safeHeadline ); |
| 3540 | + $safeHeadline = $this->replaceLinkHoldersText( $safeHeadline ); |
3581 | 3541 | |
3582 | 3542 | # Strip out HTML (other than plain <sup> and <sub>: bug 8393) |
3583 | 3543 | $tocline = preg_replace( |
— | — | @@ -3652,7 +3612,7 @@ |
3653 | 3613 | $i = 0; |
3654 | 3614 | |
3655 | 3615 | foreach( $blocks as $block ) { |
3656 | | - if( $showEditLink && $headlineCount > 0 && $i == 0 && $block != "\n" ) { |
| 3616 | + if( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) { |
3657 | 3617 | # This is the [edit] link that appears for the top block of text when |
3658 | 3618 | # section editing is enabled |
3659 | 3619 | |
— | — | @@ -3804,7 +3764,7 @@ |
3805 | 3765 | } else { |
3806 | 3766 | # Failed to validate; fall back to the default |
3807 | 3767 | $nickname = $username; |
3808 | | - wfDebug( "Parser::getUserSig: $username has bad XML tags in signature.\n" ); |
| 3768 | + wfDebug( __METHOD__.": $username has bad XML tags in signature.\n" ); |
3809 | 3769 | } |
3810 | 3770 | } |
3811 | 3771 | |
— | — | @@ -3910,19 +3870,17 @@ |
3911 | 3871 | global $wgTitle; |
3912 | 3872 | static $executing = false; |
3913 | 3873 | |
3914 | | - $fname = "Parser::transformMsg"; |
3915 | | - |
3916 | 3874 | # Guard against infinite recursion |
3917 | 3875 | if ( $executing ) { |
3918 | 3876 | return $text; |
3919 | 3877 | } |
3920 | 3878 | $executing = true; |
3921 | 3879 | |
3922 | | - wfProfileIn($fname); |
| 3880 | + wfProfileIn(__METHOD__); |
3923 | 3881 | $text = $this->preprocess( $text, $wgTitle, $options ); |
3924 | 3882 | |
3925 | 3883 | $executing = false; |
3926 | | - wfProfileOut($fname); |
| 3884 | + wfProfileOut(__METHOD__); |
3927 | 3885 | return $text; |
3928 | 3886 | } |
3929 | 3887 | |
— | — | @@ -4019,7 +3977,7 @@ |
4020 | 3978 | # Add to function cache |
4021 | 3979 | $mw = MagicWord::get( $id ); |
4022 | 3980 | if( !$mw ) |
4023 | | - throw new MWException( 'Parser::setFunctionHook() expecting a magic word identifier.' ); |
| 3981 | + throw new MWException( __METHOD__.'() expecting a magic word identifier.' ); |
4024 | 3982 | |
4025 | 3983 | $synonyms = $mw->getSynonyms(); |
4026 | 3984 | $sensitive = intval( $mw->isCaseSensitive() ); |
— | — | @@ -4034,7 +3992,7 @@ |
4035 | 3993 | $syn = '#' . $syn; |
4036 | 3994 | } |
4037 | 3995 | # Remove trailing colon |
4038 | | - if ( substr( $syn, -1, 1 ) == ':' ) { |
| 3996 | + if ( substr( $syn, -1, 1 ) === ':' ) { |
4039 | 3997 | $syn = substr( $syn, 0, -1 ); |
4040 | 3998 | } |
4041 | 3999 | $this->mFunctionSynonyms[$sensitive][$syn] = $id; |
— | — | @@ -4055,266 +4013,9 @@ |
4056 | 4014 | * Replace <!--LINK--> link placeholders with actual links, in the buffer |
4057 | 4015 | * Placeholders created in Skin::makeLinkObj() |
4058 | 4016 | * Returns an array of link CSS classes, indexed by PDBK. |
4059 | | - * $options is a bit field, RLH_FOR_UPDATE to select for update |
4060 | 4017 | */ |
4061 | 4018 | function replaceLinkHolders( &$text, $options = 0 ) { |
4062 | | - global $wgUser; |
4063 | | - global $wgContLang; |
4064 | | - |
4065 | | - $fname = 'Parser::replaceLinkHolders'; |
4066 | | - wfProfileIn( $fname ); |
4067 | | - |
4068 | | - $pdbks = array(); |
4069 | | - $colours = array(); |
4070 | | - $linkcolour_ids = array(); |
4071 | | - $sk = $this->mOptions->getSkin(); |
4072 | | - $linkCache = LinkCache::singleton(); |
4073 | | - |
4074 | | - if ( !empty( $this->mLinkHolders['namespaces'] ) ) { |
4075 | | - wfProfileIn( $fname.'-check' ); |
4076 | | - $dbr = wfGetDB( DB_SLAVE ); |
4077 | | - $page = $dbr->tableName( 'page' ); |
4078 | | - $threshold = $wgUser->getOption('stubthreshold'); |
4079 | | - |
4080 | | - # Sort by namespace |
4081 | | - asort( $this->mLinkHolders['namespaces'] ); |
4082 | | - |
4083 | | - # Generate query |
4084 | | - $query = false; |
4085 | | - $current = null; |
4086 | | - foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) { |
4087 | | - # Make title object |
4088 | | - $title = $this->mLinkHolders['titles'][$key]; |
4089 | | - |
4090 | | - # Skip invalid entries. |
4091 | | - # Result will be ugly, but prevents crash. |
4092 | | - if ( is_null( $title ) ) { |
4093 | | - continue; |
4094 | | - } |
4095 | | - $pdbk = $pdbks[$key] = $title->getPrefixedDBkey(); |
4096 | | - |
4097 | | - # Check if it's a static known link, e.g. interwiki |
4098 | | - if ( $title->isAlwaysKnown() ) { |
4099 | | - $colours[$pdbk] = ''; |
4100 | | - } elseif ( ( $id = $linkCache->getGoodLinkID( $pdbk ) ) != 0 ) { |
4101 | | - $colours[$pdbk] = ''; |
4102 | | - $this->mOutput->addLink( $title, $id ); |
4103 | | - } elseif ( $linkCache->isBadLink( $pdbk ) ) { |
4104 | | - $colours[$pdbk] = 'new'; |
4105 | | - } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) { |
4106 | | - $colours[$pdbk] = 'new'; |
4107 | | - } else { |
4108 | | - # Not in the link cache, add it to the query |
4109 | | - if ( !isset( $current ) ) { |
4110 | | - $current = $ns; |
4111 | | - $query = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len"; |
4112 | | - $query .= " FROM $page WHERE (page_namespace=$ns AND page_title IN("; |
4113 | | - } elseif ( $current != $ns ) { |
4114 | | - $current = $ns; |
4115 | | - $query .= ")) OR (page_namespace=$ns AND page_title IN("; |
4116 | | - } else { |
4117 | | - $query .= ', '; |
4118 | | - } |
4119 | | - |
4120 | | - $query .= $dbr->addQuotes( $this->mLinkHolders['dbkeys'][$key] ); |
4121 | | - } |
4122 | | - } |
4123 | | - if ( $query ) { |
4124 | | - $query .= '))'; |
4125 | | - if ( $options & RLH_FOR_UPDATE ) { |
4126 | | - $query .= ' FOR UPDATE'; |
4127 | | - } |
4128 | | - |
4129 | | - $res = $dbr->query( $query, $fname ); |
4130 | | - |
4131 | | - # Fetch data and form into an associative array |
4132 | | - # non-existent = broken |
4133 | | - while ( $s = $dbr->fetchObject($res) ) { |
4134 | | - $title = Title::makeTitle( $s->page_namespace, $s->page_title ); |
4135 | | - $pdbk = $title->getPrefixedDBkey(); |
4136 | | - $linkCache->addGoodLinkObj( $s->page_id, $title, $s->page_len, $s->page_is_redirect ); |
4137 | | - $this->mOutput->addLink( $title, $s->page_id ); |
4138 | | - $colours[$pdbk] = $sk->getLinkColour( $title, $threshold ); |
4139 | | - //add id to the extension todolist |
4140 | | - $linkcolour_ids[$s->page_id] = $pdbk; |
4141 | | - } |
4142 | | - //pass an array of page_ids to an extension |
4143 | | - wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) ); |
4144 | | - } |
4145 | | - wfProfileOut( $fname.'-check' ); |
4146 | | - |
4147 | | - # Do a second query for different language variants of links and categories |
4148 | | - if($wgContLang->hasVariants()){ |
4149 | | - $linkBatch = new LinkBatch(); |
4150 | | - $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders) |
4151 | | - $categoryMap = array(); // maps $category_variant => $category (dbkeys) |
4152 | | - $varCategories = array(); // category replacements oldDBkey => newDBkey |
4153 | | - |
4154 | | - $categories = $this->mOutput->getCategoryLinks(); |
4155 | | - |
4156 | | - // Add variants of links to link batch |
4157 | | - foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) { |
4158 | | - $title = $this->mLinkHolders['titles'][$key]; |
4159 | | - if ( is_null( $title ) ) |
4160 | | - continue; |
4161 | | - |
4162 | | - $pdbk = $title->getPrefixedDBkey(); |
4163 | | - $titleText = $title->getText(); |
4164 | | - |
4165 | | - // generate all variants of the link title text |
4166 | | - $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText); |
4167 | | - |
4168 | | - // if link was not found (in first query), add all variants to query |
4169 | | - if ( !isset($colours[$pdbk]) ){ |
4170 | | - foreach($allTextVariants as $textVariant){ |
4171 | | - if($textVariant != $titleText){ |
4172 | | - $variantTitle = Title::makeTitle( $ns, $textVariant ); |
4173 | | - if(is_null($variantTitle)) continue; |
4174 | | - $linkBatch->addObj( $variantTitle ); |
4175 | | - $variantMap[$variantTitle->getPrefixedDBkey()][] = $key; |
4176 | | - } |
4177 | | - } |
4178 | | - } |
4179 | | - } |
4180 | | - |
4181 | | - // process categories, check if a category exists in some variant |
4182 | | - foreach( $categories as $category ){ |
4183 | | - $variants = $wgContLang->convertLinkToAllVariants($category); |
4184 | | - foreach($variants as $variant){ |
4185 | | - if($variant != $category){ |
4186 | | - $variantTitle = Title::newFromDBkey( Title::makeName(NS_CATEGORY,$variant) ); |
4187 | | - if(is_null($variantTitle)) continue; |
4188 | | - $linkBatch->addObj( $variantTitle ); |
4189 | | - $categoryMap[$variant] = $category; |
4190 | | - } |
4191 | | - } |
4192 | | - } |
4193 | | - |
4194 | | - |
4195 | | - if(!$linkBatch->isEmpty()){ |
4196 | | - // construct query |
4197 | | - $titleClause = $linkBatch->constructSet('page', $dbr); |
4198 | | - |
4199 | | - $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len"; |
4200 | | - |
4201 | | - $variantQuery .= " FROM $page WHERE $titleClause"; |
4202 | | - if ( $options & RLH_FOR_UPDATE ) { |
4203 | | - $variantQuery .= ' FOR UPDATE'; |
4204 | | - } |
4205 | | - |
4206 | | - $varRes = $dbr->query( $variantQuery, $fname ); |
4207 | | - |
4208 | | - // for each found variants, figure out link holders and replace |
4209 | | - while ( $s = $dbr->fetchObject($varRes) ) { |
4210 | | - |
4211 | | - $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title ); |
4212 | | - $varPdbk = $variantTitle->getPrefixedDBkey(); |
4213 | | - $vardbk = $variantTitle->getDBkey(); |
4214 | | - |
4215 | | - $holderKeys = array(); |
4216 | | - if(isset($variantMap[$varPdbk])){ |
4217 | | - $holderKeys = $variantMap[$varPdbk]; |
4218 | | - $linkCache->addGoodLinkObj( $s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect ); |
4219 | | - $this->mOutput->addLink( $variantTitle, $s->page_id ); |
4220 | | - } |
4221 | | - |
4222 | | - // loop over link holders |
4223 | | - foreach($holderKeys as $key){ |
4224 | | - $title = $this->mLinkHolders['titles'][$key]; |
4225 | | - if ( is_null( $title ) ) continue; |
4226 | | - |
4227 | | - $pdbk = $title->getPrefixedDBkey(); |
4228 | | - |
4229 | | - if(!isset($colours[$pdbk])){ |
4230 | | - // found link in some of the variants, replace the link holder data |
4231 | | - $this->mLinkHolders['titles'][$key] = $variantTitle; |
4232 | | - $this->mLinkHolders['dbkeys'][$key] = $variantTitle->getDBkey(); |
4233 | | - |
4234 | | - // set pdbk and colour |
4235 | | - $pdbks[$key] = $varPdbk; |
4236 | | - $colours[$varPdbk] = $sk->getLinkColour( $variantTitle, $threshold ); |
4237 | | - $linkcolour_ids[$s->page_id] = $pdbk; |
4238 | | - } |
4239 | | - wfRunHooks( 'GetLinkColours', array( $linkcolour_ids, &$colours ) ); |
4240 | | - } |
4241 | | - |
4242 | | - // check if the object is a variant of a category |
4243 | | - if(isset($categoryMap[$vardbk])){ |
4244 | | - $oldkey = $categoryMap[$vardbk]; |
4245 | | - if($oldkey != $vardbk) |
4246 | | - $varCategories[$oldkey]=$vardbk; |
4247 | | - } |
4248 | | - } |
4249 | | - |
4250 | | - // rebuild the categories in original order (if there are replacements) |
4251 | | - if(count($varCategories)>0){ |
4252 | | - $newCats = array(); |
4253 | | - $originalCats = $this->mOutput->getCategories(); |
4254 | | - foreach($originalCats as $cat => $sortkey){ |
4255 | | - // make the replacement |
4256 | | - if( array_key_exists($cat,$varCategories) ) |
4257 | | - $newCats[$varCategories[$cat]] = $sortkey; |
4258 | | - else $newCats[$cat] = $sortkey; |
4259 | | - } |
4260 | | - $this->mOutput->setCategoryLinks($newCats); |
4261 | | - } |
4262 | | - } |
4263 | | - } |
4264 | | - |
4265 | | - # Construct search and replace arrays |
4266 | | - wfProfileIn( $fname.'-construct' ); |
4267 | | - $replacePairs = array(); |
4268 | | - foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) { |
4269 | | - $pdbk = $pdbks[$key]; |
4270 | | - $searchkey = "<!--LINK $key-->"; |
4271 | | - $title = $this->mLinkHolders['titles'][$key]; |
4272 | | - if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] == 'new' ) { |
4273 | | - $linkCache->addBadLinkObj( $title ); |
4274 | | - $colours[$pdbk] = 'new'; |
4275 | | - $this->mOutput->addLink( $title, 0 ); |
4276 | | - $replacePairs[$searchkey] = $sk->makeBrokenLinkObj( $title, |
4277 | | - $this->mLinkHolders['texts'][$key], |
4278 | | - $this->mLinkHolders['queries'][$key] ); |
4279 | | - } else { |
4280 | | - $replacePairs[$searchkey] = $sk->makeColouredLinkObj( $title, $colours[$pdbk], |
4281 | | - $this->mLinkHolders['texts'][$key], |
4282 | | - $this->mLinkHolders['queries'][$key] ); |
4283 | | - } |
4284 | | - } |
4285 | | - $replacer = new HashtableReplacer( $replacePairs, 1 ); |
4286 | | - wfProfileOut( $fname.'-construct' ); |
4287 | | - |
4288 | | - # Do the thing |
4289 | | - wfProfileIn( $fname.'-replace' ); |
4290 | | - $text = preg_replace_callback( |
4291 | | - '/(<!--LINK .*?-->)/', |
4292 | | - $replacer->cb(), |
4293 | | - $text); |
4294 | | - |
4295 | | - wfProfileOut( $fname.'-replace' ); |
4296 | | - } |
4297 | | - |
4298 | | - # Now process interwiki link holders |
4299 | | - # This is quite a bit simpler than internal links |
4300 | | - if ( !empty( $this->mInterwikiLinkHolders['texts'] ) ) { |
4301 | | - wfProfileIn( $fname.'-interwiki' ); |
4302 | | - # Make interwiki link HTML |
4303 | | - $replacePairs = array(); |
4304 | | - foreach( $this->mInterwikiLinkHolders['texts'] as $key => $link ) { |
4305 | | - $title = $this->mInterwikiLinkHolders['titles'][$key]; |
4306 | | - $replacePairs[$key] = $sk->link( $title, $link ); |
4307 | | - } |
4308 | | - $replacer = new HashtableReplacer( $replacePairs, 1 ); |
4309 | | - |
4310 | | - $text = preg_replace_callback( |
4311 | | - '/<!--IWLINK (.*?)-->/', |
4312 | | - $replacer->cb(), |
4313 | | - $text ); |
4314 | | - wfProfileOut( $fname.'-interwiki' ); |
4315 | | - } |
4316 | | - |
4317 | | - wfProfileOut( $fname ); |
4318 | | - return $colours; |
| 4019 | + return $this->mLinkHolders->replace( $text ); |
4319 | 4020 | } |
4320 | 4021 | |
4321 | 4022 | /** |
— | — | @@ -4324,39 +4025,10 @@ |
4325 | 4026 | * @return string |
4326 | 4027 | */ |
4327 | 4028 | function replaceLinkHoldersText( $text ) { |
4328 | | - $fname = 'Parser::replaceLinkHoldersText'; |
4329 | | - wfProfileIn( $fname ); |
4330 | | - |
4331 | | - $text = preg_replace_callback( |
4332 | | - '/<!--(LINK|IWLINK) (.*?)-->/', |
4333 | | - array( &$this, 'replaceLinkHoldersTextCallback' ), |
4334 | | - $text ); |
4335 | | - |
4336 | | - wfProfileOut( $fname ); |
4337 | | - return $text; |
| 4029 | + return $this->mLinkHolders->replaceText( $text ); |
4338 | 4030 | } |
4339 | 4031 | |
4340 | 4032 | /** |
4341 | | - * @param array $matches |
4342 | | - * @return string |
4343 | | - * @private |
4344 | | - */ |
4345 | | - function replaceLinkHoldersTextCallback( $matches ) { |
4346 | | - $type = $matches[1]; |
4347 | | - $key = $matches[2]; |
4348 | | - if( $type == 'LINK' ) { |
4349 | | - if( isset( $this->mLinkHolders['texts'][$key] ) ) { |
4350 | | - return $this->mLinkHolders['texts'][$key]; |
4351 | | - } |
4352 | | - } elseif( $type == 'IWLINK' ) { |
4353 | | - if( isset( $this->mInterwikiLinkHolders['texts'][$key] ) ) { |
4354 | | - return $this->mInterwikiLinkHolders['texts'][$key]; |
4355 | | - } |
4356 | | - } |
4357 | | - return $matches[0]; |
4358 | | - } |
4359 | | - |
4360 | | - /** |
4361 | 4033 | * Tag hook handler for 'pre'. |
4362 | 4034 | */ |
4363 | 4035 | function renderPreTag( $text, $attribs ) { |
— | — | @@ -4407,7 +4079,7 @@ |
4408 | 4080 | |
4409 | 4081 | wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) ); |
4410 | 4082 | |
4411 | | - $lines = explode( "\n", $text ); |
| 4083 | + $lines = StringUtils::explode( "\n", $text ); |
4412 | 4084 | foreach ( $lines as $line ) { |
4413 | 4085 | # match lines like these: |
4414 | 4086 | # Image:someimage.jpg|This is some image |
— | — | @@ -4420,7 +4092,7 @@ |
4421 | 4093 | |
4422 | 4094 | if ( strpos( $matches[0], '%' ) !== false ) |
4423 | 4095 | $matches[1] = urldecode( $matches[1] ); |
4424 | | - $tp = Title::newFromText( $matches[1] ); |
| 4096 | + $tp = Title::newFromText( $matches[1]/*, NS_IMAGE*/ ); |
4425 | 4097 | $nt =& $tp; |
4426 | 4098 | if( is_null( $nt ) ) { |
4427 | 4099 | # Bogus title. Ignore these so we don't bomb out later. |
— | — | @@ -4486,8 +4158,11 @@ |
4487 | 4159 | |
4488 | 4160 | /** |
4489 | 4161 | * Parse image options text and use it to make an image |
| 4162 | + * @param Title $title |
| 4163 | + * @param string $options |
| 4164 | + * @param LinkHolderArray $holders |
4490 | 4165 | */ |
4491 | | - function makeImage( $title, $options ) { |
| 4166 | + function makeImage( $title, $options, $holders = false ) { |
4492 | 4167 | # Check if the options text is of the form "options|alt text" |
4493 | 4168 | # Options are: |
4494 | 4169 | # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang |
— | — | @@ -4510,7 +4185,7 @@ |
4511 | 4186 | # * bottom |
4512 | 4187 | # * text-bottom |
4513 | 4188 | |
4514 | | - $parts = array_map( 'trim', explode( '|', $options) ); |
| 4189 | + $parts = StringUtils::explode( "|", $options ); |
4515 | 4190 | $sk = $this->mOptions->getSkin(); |
4516 | 4191 | |
4517 | 4192 | # Give extensions a chance to select the file revision for us |
— | — | @@ -4532,13 +4207,14 @@ |
4533 | 4208 | $params = array( 'frame' => array(), 'handler' => array(), |
4534 | 4209 | 'horizAlign' => array(), 'vertAlign' => array() ); |
4535 | 4210 | foreach( $parts as $part ) { |
| 4211 | + $part = trim( $part ); |
4536 | 4212 | list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part ); |
4537 | 4213 | $validated = false; |
4538 | 4214 | if( isset( $paramMap[$magicName] ) ) { |
4539 | 4215 | list( $type, $paramName ) = $paramMap[$magicName]; |
4540 | 4216 | |
4541 | 4217 | // Special case; width and height come in one variable together |
4542 | | - if( $type == 'handler' && $paramName == 'width' ) { |
| 4218 | + if( $type === 'handler' && $paramName === 'width' ) { |
4543 | 4219 | $m = array(); |
4544 | 4220 | # (bug 13500) In both cases (width/height and width only), |
4545 | 4221 | # permit trailing "px" for backward compatibility. |
— | — | @@ -4561,7 +4237,7 @@ |
4562 | 4238 | } |
4563 | 4239 | } // else no validation -- bug 13436 |
4564 | 4240 | } else { |
4565 | | - if ( $type == 'handler' ) { |
| 4241 | + if ( $type === 'handler' ) { |
4566 | 4242 | # Validate handler parameter |
4567 | 4243 | $validated = $handler->validateParam( $paramName, $value ); |
4568 | 4244 | } else { |
— | — | @@ -4597,7 +4273,13 @@ |
4598 | 4274 | } |
4599 | 4275 | |
4600 | 4276 | # Strip bad stuff out of the alt text |
4601 | | - $alt = $this->replaceLinkHoldersText( $caption ); |
| 4277 | + # We can't just use replaceLinkHoldersText() here, because if this function |
| 4278 | + # is called from replaceInternalLinks2(), mLinkHolders won't be up to date. |
| 4279 | + if ( $holders ) { |
| 4280 | + $alt = $holders->replaceText( $caption ); |
| 4281 | + } else { |
| 4282 | + $alt = $this->replaceLinkHoldersText( $caption ); |
| 4283 | + } |
4602 | 4284 | |
4603 | 4285 | # make sure there are no placeholders in thumbnail attributes |
4604 | 4286 | # that are later expanded to html- so expand them now and |
— | — | @@ -4700,7 +4382,7 @@ |
4701 | 4383 | $sectionParts = explode( '-', $section ); |
4702 | 4384 | $sectionIndex = array_pop( $sectionParts ); |
4703 | 4385 | foreach ( $sectionParts as $part ) { |
4704 | | - if ( $part == 'T' ) { |
| 4386 | + if ( $part === 'T' ) { |
4705 | 4387 | $flags |= self::PTD_FOR_INCLUSION; |
4706 | 4388 | } |
4707 | 4389 | } |
— | — | @@ -4717,14 +4399,14 @@ |
4718 | 4400 | $targetLevel = 1000; |
4719 | 4401 | } else { |
4720 | 4402 | while ( $node ) { |
4721 | | - if ( $node->getName() == 'h' ) { |
| 4403 | + if ( $node->getName() === 'h' ) { |
4722 | 4404 | $bits = $node->splitHeading(); |
4723 | 4405 | if ( $bits['i'] == $sectionIndex ) { |
4724 | 4406 | $targetLevel = $bits['level']; |
4725 | 4407 | break; |
4726 | 4408 | } |
4727 | 4409 | } |
4728 | | - if ( $mode == 'replace' ) { |
| 4410 | + if ( $mode === 'replace' ) { |
4729 | 4411 | $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG ); |
4730 | 4412 | } |
4731 | 4413 | $node = $node->getNextSibling(); |
— | — | @@ -4733,7 +4415,7 @@ |
4734 | 4416 | |
4735 | 4417 | if ( !$node ) { |
4736 | 4418 | // Not found |
4737 | | - if ( $mode == 'get' ) { |
| 4419 | + if ( $mode === 'get' ) { |
4738 | 4420 | return $newText; |
4739 | 4421 | } else { |
4740 | 4422 | return $text; |
— | — | @@ -4742,21 +4424,21 @@ |
4743 | 4425 | |
4744 | 4426 | // Find the end of the section, including nested sections |
4745 | 4427 | do { |
4746 | | - if ( $node->getName() == 'h' ) { |
| 4428 | + if ( $node->getName() === 'h' ) { |
4747 | 4429 | $bits = $node->splitHeading(); |
4748 | 4430 | $curLevel = $bits['level']; |
4749 | 4431 | if ( $bits['i'] != $sectionIndex && $curLevel <= $targetLevel ) { |
4750 | 4432 | break; |
4751 | 4433 | } |
4752 | 4434 | } |
4753 | | - if ( $mode == 'get' ) { |
| 4435 | + if ( $mode === 'get' ) { |
4754 | 4436 | $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG ); |
4755 | 4437 | } |
4756 | 4438 | $node = $node->getNextSibling(); |
4757 | 4439 | } while ( $node ); |
4758 | 4440 | |
4759 | 4441 | // Write out the remainder (in replace mode only) |
4760 | | - if ( $mode == 'replace' ) { |
| 4442 | + if ( $mode === 'replace' ) { |
4761 | 4443 | // Output the replacement text |
4762 | 4444 | // Add two newlines on -- trailing whitespace in $newText is conventionally |
4763 | 4445 | // stripped by the editor, so we need both newlines to restore the paragraph gap |
— | — | @@ -4986,7 +4668,7 @@ |
4987 | 4669 | do { |
4988 | 4670 | $oldText = $text; |
4989 | 4671 | $text = $this->general->replace( $text ); |
4990 | | - } while ( $text != $oldText ); |
| 4672 | + } while ( $text !== $oldText ); |
4991 | 4673 | wfProfileOut( __METHOD__ ); |
4992 | 4674 | return $text; |
4993 | 4675 | } |
— | — | @@ -4996,7 +4678,7 @@ |
4997 | 4679 | do { |
4998 | 4680 | $oldText = $text; |
4999 | 4681 | $text = $this->nowiki->replace( $text ); |
5000 | | - } while ( $text != $oldText ); |
| 4682 | + } while ( $text !== $oldText ); |
5001 | 4683 | wfProfileOut( __METHOD__ ); |
5002 | 4684 | return $text; |
5003 | 4685 | } |
— | — | @@ -5007,7 +4689,7 @@ |
5008 | 4690 | $oldText = $text; |
5009 | 4691 | $text = $this->general->replace( $text ); |
5010 | 4692 | $text = $this->nowiki->replace( $text ); |
5011 | | - } while ( $text != $oldText ); |
| 4693 | + } while ( $text !== $oldText ); |
5012 | 4694 | wfProfileOut( __METHOD__ ); |
5013 | 4695 | return $text; |
5014 | 4696 | } |
— | — | @@ -5021,7 +4703,7 @@ |
5022 | 4704 | var $output = ''; |
5023 | 4705 | |
5024 | 4706 | function replace( $matches ) { |
5025 | | - if ( substr( $matches[1], -1 ) == "\n" ) { |
| 4707 | + if ( substr( $matches[1], -1 ) === "\n" ) { |
5026 | 4708 | $this->output .= substr( $matches[1], 0, -1 ); |
5027 | 4709 | } else { |
5028 | 4710 | $this->output .= $matches[1]; |
Index: trunk/phase3/includes/parser/Parser_DiffTest.php |
— | — | @@ -69,9 +69,22 @@ |
70 | 70 | $lastResult = $currentResult; |
71 | 71 | } |
72 | 72 | if ( $mismatch ) { |
73 | | - throw new MWException( "Parser_DiffTest: results mismatch on call to $name\n" . |
74 | | - 'Arguments: ' . $this->formatArray( $args ) . "\n" . |
75 | | - 'Results: ' . $this->formatArray( $results ) . "\n" ); |
| 73 | + if ( count( $results ) == 2 ) { |
| 74 | + $resultsList = array(); |
| 75 | + foreach ( $this->parsers as $i => $parser ) { |
| 76 | + $resultsList[] = var_export( $results[$i], true ); |
| 77 | + } |
| 78 | + $diff = wfDiff( $resultsList[0], $resultsList[1] ); |
| 79 | + } else { |
| 80 | + $diff = '[too many parsers]'; |
| 81 | + } |
| 82 | + $msg = "Parser_DiffTest: results mismatch on call to $name\n"; |
| 83 | + if ( !$this->shortOutput ) { |
| 84 | + $msg .= 'Arguments: ' . $this->formatArray( $args ) . "\n"; |
| 85 | + } |
| 86 | + $msg .= 'Results: ' . $this->formatArray( $results ) . "\n" . |
| 87 | + "Diff: $diff\n"; |
| 88 | + throw new MWException( $msg ); |
76 | 89 | } |
77 | 90 | return $lastResult; |
78 | 91 | } |
Index: trunk/phase3/includes/MessageCache.php |
— | — | @@ -649,12 +649,18 @@ |
650 | 650 | return $message; |
651 | 651 | } |
652 | 652 | |
653 | | - global $wgParser; |
| 653 | + global $wgParser, $wgParserConf; |
654 | 654 | if ( !$this->mParser && isset( $wgParser ) ) { |
655 | 655 | # Do some initialisation so that we don't have to do it twice |
656 | 656 | $wgParser->firstCallInit(); |
657 | 657 | # Clone it and store it |
658 | | - $this->mParser = clone $wgParser; |
| 658 | + $class = $wgParserConf['class']; |
| 659 | + if ( $class == 'Parser_DiffTest' ) { |
| 660 | + # Uncloneable |
| 661 | + $this->mParser = new $class( $wgParserConf ); |
| 662 | + } else { |
| 663 | + $this->mParser = clone $wgParser; |
| 664 | + } |
659 | 665 | #wfDebug( __METHOD__ . ": following contents triggered transform: $message\n" ); |
660 | 666 | } |
661 | 667 | if ( $this->mParser ) { |
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/includes/DefaultSettings.php |
— | — | @@ -3336,6 +3336,12 @@ |
3337 | 3337 | ); |
3338 | 3338 | |
3339 | 3339 | /** |
| 3340 | + * LinkHolderArray batch size |
| 3341 | + * For debugging |
| 3342 | + */ |
| 3343 | +$wgLinkHolderBatchSize = 1000; |
| 3344 | + |
| 3345 | +/** |
3340 | 3346 | * Hooks that are used for outputting exceptions. Format is: |
3341 | 3347 | * $wgExceptionHooks[] = $funcname |
3342 | 3348 | * or: |
Index: trunk/phase3/includes/Exception.php |
— | — | @@ -83,7 +83,7 @@ |
84 | 84 | function getHTML() { |
85 | 85 | global $wgShowExceptionDetails; |
86 | 86 | if( $wgShowExceptionDetails ) { |
87 | | - return '<p>' . htmlspecialchars( $this->getMessage() ) . |
| 87 | + return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) . |
88 | 88 | '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . |
89 | 89 | "</p>\n"; |
90 | 90 | } else { |
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 | |