replace TYPE= with ENGINE=, (supported since 4.0, TYPE deprecated since 4.1)
[lhc/web/wiklou.git] / includes / Parser.php
index 580d63b..3b5d119 100644 (file)
@@ -73,7 +73,7 @@ define( 'EXT_IMAGE_REGEX',
  *   performs brace substitution on MediaWiki messages
  *
  * Globals used:
- *    objects:   $wgLang
+ *    objects:   $wgLang, $wgContLang
  *
  * NOT $wgArticle, $wgUser or $wgTitle. Keep them away!
  *
@@ -99,16 +99,16 @@ class Parser
        var $mOutput, $mAutonumber, $mDTopen, $mStripState = array();
        var $mVariables, $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
        var $mInterwikiLinkHolders, $mLinkHolders, $mUniqPrefix;
+       var $mTemplates,        // cache of already loaded templates, avoids
+                               // multiple SQL queries for the same string
+           $mTemplatePath;     // stores an unsorted hash of all the templates already loaded
+                               // in this path. Used for loop detection.
 
-       # Temporary:
+       # Temporary
+       # These are variables reset at least once per parse regardless of $clearState
        var $mOptions,      // ParserOptions object
                $mTitle,        // Title context, used for self-link rendering and similar things
                $mOutputType,   // Output type, one of the OT_xxx constants
-           $mTemplates,        // cache of already loaded templates, avoids
-                               // multiple SQL queries for the same string
-           $mTemplatePath,     // stores an unsorted hash of all the templates already loaded
-                               // in this path. Used for loop detection.
-               $mIWTransData = array(),
                $mRevisionId;   // ID to display in {{REVISIONID}} tags
 
        /**#@-*/
@@ -119,8 +119,6 @@ class Parser
         * @access public
         */
        function Parser() {
-               $this->mTemplates = array();
-               $this->mTemplatePath = array();
                $this->mTagHooks = array();
                $this->clearState();
        }
@@ -154,6 +152,10 @@ class Parser
                $this->mRevisionId = null;
                $this->mUniqPrefix = 'UNIQ' . Parser::getRandomString();
 
+               # Clear these on every parse, bug 4549
+               $this->mTemplates = array();
+               $this->mTemplatePath = array();
+
                wfRunHooks( 'ParserClearState', array( &$this ) );
        }
 
@@ -234,11 +236,11 @@ class Parser
 
                $this->replaceLinkHolders( $text );
 
-               # the position of the convert() call should not be changed. it
-               # assumes that the links are all replaces and the only thing left
+               # the position of the parserConvert() call should not be changed. it
+               # assumes that the links are all replaced and the only thing left
                # is the <nowiki> mark.
-               $text = $wgContLang->convert($text);
-               $this->mOutput->setTitleText($wgContLang->getParsedTitle());
+               # Side-effects: this calls $this->mOutput->setTitleText()
+               $text = $wgContLang->parserConvert( $text, $this );
 
                $text = $this->unstripNoWiki( $text, $this->mStripState );
 
@@ -268,6 +270,9 @@ class Parser
                return dechex(mt_rand(0, 0x7fffffff)) . dechex(mt_rand(0, 0x7fffffff));
        }
 
+       function &getTitle() { return $this->mTitle; }
+       function getOptions() { return $this->mOptions; }
+
        /**
         * Replaces all occurrences of <$tag>content</$tag> in the text
         * with a random marker and returns the new text. the output parameter
@@ -476,8 +481,8 @@ class Parser
                        $state['nowiki'] = $state['nowiki'] + $nowiki_content;
                        $state['math'] = $state['math'] + $math_content;
                        $state['pre'] = $state['pre'] + $pre_content;
-                       $state['comment'] = $state['comment'] + $comment_content;
                        $state['gallery'] = $state['gallery'] + $gallery_content;
+                       $state['comment'] = $state['comment'] + $comment_content;
 
                        foreach( $ext_content as $tag => $array ) {
                                if ( array_key_exists( $tag, $state ) ) {
@@ -490,8 +495,8 @@ class Parser
                          'nowiki' => $nowiki_content,
                          'math' => $math_content,
                          'pre' => $pre_content,
-                         'comment' => $comment_content,
                          'gallery' => $gallery_content,
+                         'comment' => $comment_content,
                        ) + $ext_content;
                }
                return $text;
@@ -621,6 +626,11 @@ class Parser
                $pipes = array();
                $process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
                if (is_resource($process)) {
+                       // Theoretically, this style of communication could cause a deadlock
+                       // here. If the stdout buffer fills up, then writes to stdin could
+                       // block. This doesn't appear to happen with tidy, because tidy only
+                       // writes to stdout after it's finished reading from stdin. Search
+                       // for tidyParseStdin and tidySaveStdout in console/tidy.c
                        fwrite($pipes[0], $text);
                        fclose($pipes[0]);
                        while (!feof($pipes[1])) {
@@ -685,6 +695,7 @@ class Parser
                $ltd = array () ; # Was it TD or TH?
                $tr = array () ; # Is currently a tr tag open?
                $ltr = array () ; # tr attributes
+               $has_opened_tr = array(); # Did this table open a <tr> element?
                $indent_level = 0; # indent level of the table
                foreach ( $t AS $k => $x )
                {
@@ -701,11 +712,13 @@ class Parser
                                array_push ( $ltd , '' ) ;
                                array_push ( $tr , false ) ;
                                array_push ( $ltr , '' ) ;
+                               array_push ( $has_opened_tr, false );
                        }
                        else if ( count ( $td ) == 0 ) { } # Don't do any of the following
                        else if ( '|}' == substr ( $x , 0 , 2 ) ) {
                                $z = "</table>" . substr ( $x , 2);
                                $l = array_pop ( $ltd ) ;
+                               if ( !array_pop ( $has_opened_tr ) ) $z = "<tr><td></td></tr>" . $z ;
                                if ( array_pop ( $tr ) ) $z = '</tr>' . $z ;
                                if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
                                array_pop ( $ltr ) ;
@@ -716,6 +729,8 @@ class Parser
                                while ( $x != '' && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ;
                                $z = '' ;
                                $l = array_pop ( $ltd ) ;
+                               array_pop ( $has_opened_tr );
+                               array_push ( $has_opened_tr , true ) ;
                                if ( array_pop ( $tr ) ) $z = '</tr>' . $z ;
                                if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
                                array_pop ( $ltr ) ;
@@ -747,6 +762,8 @@ class Parser
                                                if ( !array_pop ( $tr ) ) $z = '<tr'.$tra.">\n" ;
                                                array_push ( $tr , true ) ;
                                                array_push ( $ltr , '' ) ;
+                                               array_pop ( $has_opened_tr );
+                                               array_push ( $has_opened_tr , true ) ;
                                        }
 
                                        $l = array_pop ( $ltd ) ;
@@ -779,8 +796,10 @@ class Parser
                # Closing open td, tr && table
                while ( count ( $td ) > 0 )
                {
+                       $l = array_pop ( $ltd ) ;
                        if ( array_pop ( $td ) ) $t[] = '</td>' ;
                        if ( array_pop ( $tr ) ) $t[] = '</tr>' ;
+                       if ( !array_pop ( $has_opened_tr ) ) $t[] = "<tr><td></td></tr>" ;
                        $t[] = '</table>' ;
                }
 
@@ -796,7 +815,6 @@ class Parser
         * @access private
         */
        function internalParse( $text ) {
-               global $wgContLang;
                $args = array();
                $isMain = true;
                $fname = 'Parser::internalParse';
@@ -829,18 +847,10 @@ class Parser
                $text = $this->doTableStuff( $text );
                $text = $this->formatHeadings( $text, $isMain );
 
-               $regex = '/<!--IW_TRANSCLUDE (\d+)-->/';
-               $text = preg_replace_callback($regex, array(&$this, 'scarySubstitution'), $text);
-
                wfProfileOut( $fname );
                return $text;
        }
 
-       function scarySubstitution($matches) {
-#              return "[[".$matches[0]."]]";
-               return $this->mIWTransData[(int)$matches[0]];
-       }
-
        /**
         * Replace special strings like "ISBN xxx" and "RFC xxx" with
         * magic external links.
@@ -1215,12 +1225,12 @@ class Parser
 
        /**
         * Replace unusual URL escape codes with their equivalent characters
-        * @param string 
+        * @param string
         * @return string
         * @static
         */
        function replaceUnusualEscapes( $url ) {
-               return preg_replace_callback( '/%[0-9A-Fa-f]{2}/', 
+               return preg_replace_callback( '/%[0-9A-Fa-f]{2}/',
                        array( 'Parser', 'replaceUnusualEscapesCallback' ), $url );
        }
 
@@ -2011,6 +2021,8 @@ class Parser
                                return $this->mTitle->getPrefixedText();
                        case MAG_FULLPAGENAMEE:
                                return $this->mTitle->getPrefixedURL();
+                       case MAG_SUBPAGENAME:
+                               return $this->mTitle->getSubpageText();
                        case MAG_REVISIONID:
                                return $this->mRevisionId;
                        case MAG_NAMESPACE:
@@ -2253,9 +2265,10 @@ class Parser
         *
         * @param string $tex The text to transform
         * @param array $args Key-value pairs representing template parameters to substitute
+        * @param bool $argsOnly Only do argument (triple-brace) expansion, not double-brace expansion
         * @access private
         */
-       function replaceVariables( $text, $args = array() ) {
+       function replaceVariables( $text, $args = array(), $argsOnly = false ) {
                # Prevent too big inclusions
                if( strlen( $text ) > MAX_INCLUDE_SIZE ) {
                        return $text;
@@ -2268,7 +2281,9 @@ class Parser
                array_push( $this->mArgStack, $args );
 
                $braceCallbacks = array();
-               $braceCallbacks[2] = array( &$this, 'braceSubstitution' );
+               if ( !$argsOnly ) {
+                       $braceCallbacks[2] = array( &$this, 'braceSubstitution' );
+               }
                if ( $this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI ) {
                        $braceCallbacks[3] = array( &$this, 'argSubstitution' );
                }
@@ -2356,10 +2371,16 @@ class Parser
                $fname = 'Parser::braceSubstitution';
                wfProfileIn( $fname );
 
-               $found = false;
-               $nowiki = false;
-               $noparse = false;
+               # Flags
+               $found = false;             # $text has been filled
+               $nowiki = false;            # wiki markup in $text should be escaped
+               $noparse = false;           # Unsafe HTML tags should not be stripped, etc.
+               $noargs = false;            # Don't replace triple-brace arguments in $text
+               $replaceHeadings = false;   # Make the edit section links go to the template not the article
+               $isHTML = false;            # $text is HTML, armour it against wikitext transformation
+               $forceRawInterwiki = false; # Force interwiki transclusion to be done in raw mode not rendered
 
+               # Title object, where $text came from
                $title = NULL;
 
                $linestart = '';
@@ -2376,6 +2397,7 @@ class Parser
                                $text = $replaceWith;
                                $found = true;
                                $noparse = true;
+                               $noargs = true;
                        }
                }
 
@@ -2393,10 +2415,11 @@ class Parser
                                $text = $piece['text'];
                                $found = true;
                                $noparse = true;
+                               $noargs = true;
                        }
                }
 
-               # MSG, MSGNW and INT
+               # MSG, MSGNW, INT and RAW
                if ( !$found ) {
                        # Check for MSGNW:
                        $mwMsgnw =& MagicWord::get( MAG_MSGNW );
@@ -2407,7 +2430,13 @@ class Parser
                                $mwMsg =& MagicWord::get( MAG_MSG );
                                $mwMsg->matchStartAndRemove( $part1 );
                        }
-
+                       
+                       # Check for RAW:
+                       $mwRaw =& MagicWord::get( MAG_RAW );
+                       if ( $mwRaw->matchStartAndRemove( $part1 ) ) {
+                               $forceRawInterwiki = true;
+                       }
+                       
                        # Check if it is an internal message
                        $mwInt =& MagicWord::get( MAG_INT );
                        if ( $mwInt->matchStartAndRemove( $part1 ) ) {
@@ -2513,12 +2542,13 @@ class Parser
 
                # Did we encounter this template already? If yes, it is in the cache
                # and we need to check for loops.
-               if ( !$found && isset( $this->mTemplates[$part1] ) ) {
+               if ( !$found && isset( $this->mTemplates[$piece['title']] ) ) {
                        $found = true;
 
                        # Infinite loop test
                        if ( isset( $this->mTemplatePath[$part1] ) ) {
                                $noparse = true;
+                               $noargs = true;
                                $found = true;
                                $text = $linestart .
                                        '{{' . $part1 . '}}' .
@@ -2526,13 +2556,11 @@ class Parser
                                wfDebug( "$fname: template loop broken at '$part1'\n" );
                        } else {
                                # set $text to cached message.
-                               $text = $linestart . $this->mTemplates[$part1];
+                               $text = $linestart . $this->mTemplates[$piece['title']];
                        }
                }
 
                # Load from database
-               $replaceHeadings = false;
-               $isHTML = false;
                $lastPathLevel = $this->mTemplatePath;
                if ( !$found ) {
                        $ns = NS_TEMPLATE;
@@ -2542,45 +2570,54 @@ class Parser
                        }
                        $title = Title::newFromText( $part1, $ns );
 
-                       if ($title) {
-                               $interwiki = Title::getInterwikiLink($title->getInterwiki());
-                               if ($interwiki != '' && $title->isTrans()) {
-                                       return $this->scarytransclude($title, $interwiki);
-                               }
-                       }
-
-                       if ( !is_null( $title ) && !$title->isExternal() ) {
-                               # Check for excessive inclusion
-                               $dbk = $title->getPrefixedDBkey();
-                               if ( $this->incrementIncludeCount( $dbk ) ) {
-                                       if ( $title->getNamespace() == NS_SPECIAL && $this->mOptions->getAllowSpecialInclusion() ) {
-                                               # Capture special page output
-                                               $text = SpecialPage::capturePath( $title );
-                                               if ( is_string( $text ) ) {
-                                                       $found = true;
-                                                       $noparse = true;
-                                                       $isHTML = true;
-                                                       $this->disableCache();
-                                               }
-                                       } else {
-                                               $articleContent = $this->fetchTemplate( $title );
-                                               if ( $articleContent !== false ) {
-                                                       $found = true;
-                                                       $text = $articleContent;
-                                                       $replaceHeadings = true;
+                       if ( !is_null( $title ) ) {
+                               if ( !$title->isExternal() ) {
+                                       # Check for excessive inclusion
+                                       $dbk = $title->getPrefixedDBkey();
+                                       if ( $this->incrementIncludeCount( $dbk ) ) {
+                                               if ( $title->getNamespace() == NS_SPECIAL && $this->mOptions->getAllowSpecialInclusion() ) {
+                                                       # Capture special page output
+                                                       $text = SpecialPage::capturePath( $title );
+                                                       if ( is_string( $text ) ) {
+                                                               $found = true;
+                                                               $noparse = true;
+                                                               $noargs = true;
+                                                               $isHTML = true;
+                                                               $this->disableCache();
+                                                       }
+                                               } else {
+                                                       $articleContent = $this->fetchTemplate( $title );
+                                                       if ( $articleContent !== false ) {
+                                                               $found = true;
+                                                               $text = $articleContent;
+                                                               $replaceHeadings = true;
+                                                       }
                                                }
                                        }
-                               }
 
-                               # If the title is valid but undisplayable, make a link to it
-                               if ( $this->mOutputType == OT_HTML && !$found ) {
-                                       $text = '[['.$title->getPrefixedText().']]';
+                                       # If the title is valid but undisplayable, make a link to it
+                                       if ( $this->mOutputType == OT_HTML && !$found ) {
+                                               $text = '[['.$title->getPrefixedText().']]';
+                                               $found = true;
+                                       }
+                               } elseif ( $title->isTrans() ) {
+                                       // Interwiki transclusion
+                                       if ( $this->mOutputType == OT_HTML && !$forceRawInterwiki ) {
+                                               $text = $this->interwikiTransclude( $title, 'render' );
+                                               $isHTML = true;
+                                               $noparse = true;
+                                       } else {
+                                               $text = $this->interwikiTransclude( $title, 'raw' );
+                                               $replaceHeadings = true;
+                                       }
                                        $found = true;
                                }
-
+                               
                                # Template cache array insertion
+                               # Use the original $piece['title'] not the mangled $part1, so that
+                               # modifiers such as RAW: produce separate cache entries
                                if( $found ) {
-                                       $this->mTemplates[$part1] = $text;
+                                       $this->mTemplates[$piece['title']] = $text;
                                        $text = $linestart . $text;
                                }
                        }
@@ -2590,51 +2627,60 @@ class Parser
                # Only for HTML output
                if ( $nowiki && $found && $this->mOutputType == OT_HTML ) {
                        $text = wfEscapeWikiText( $text );
-               } elseif ( ($this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI) && $found && !$noparse) {
-                       # Clean up argument array
-                       $assocArgs = array();
-                       $index = 1;
-                       foreach( $args as $arg ) {
-                               $eqpos = strpos( $arg, '=' );
-                               if ( $eqpos === false ) {
-                                       $assocArgs[$index++] = $arg;
-                               } else {
-                                       $name = trim( substr( $arg, 0, $eqpos ) );
-                                       $value = trim( substr( $arg, $eqpos+1 ) );
-                                       if ( $value === false ) {
-                                               $value = '';
-                                       }
-                                       if ( $name !== false ) {
-                                               $assocArgs[$name] = $value;
+               } elseif ( ($this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI) && $found ) {
+                       if ( !$noargs ) {
+                               # Clean up argument array
+                               $assocArgs = array();
+                               $index = 1;
+                               foreach( $args as $arg ) {
+                                       $eqpos = strpos( $arg, '=' );
+                                       if ( $eqpos === false ) {
+                                               $assocArgs[$index++] = $arg;
+                                       } else {
+                                               $name = trim( substr( $arg, 0, $eqpos ) );
+                                               $value = trim( substr( $arg, $eqpos+1 ) );
+                                               if ( $value === false ) {
+                                                       $value = '';
+                                               }
+                                               if ( $name !== false ) {
+                                                       $assocArgs[$name] = $value;
+                                               }
                                        }
                                }
-                       }
-
-                       # Add a new element to the templace recursion path
-                       $this->mTemplatePath[$part1] = 1;
 
-                       # If there are any <onlyinclude> tags, only include them
-                       if ( in_string( '<onlyinclude>', $text ) && in_string( '</onlyinclude>', $text ) ) {
-                               preg_match_all( '/<onlyinclude>(.*?)\n?<\/onlyinclude>/s', $text, $m );
-                               $text = '';
-                               foreach ($m[1] as $piece)
-                                       $text .= $piece;
+                               # Add a new element to the templace recursion path
+                               $this->mTemplatePath[$part1] = 1;
                        }
-                       # Remove <noinclude> sections and <includeonly> tags
-                       $text = preg_replace( '/<noinclude>.*?<\/noinclude>/s', '', $text );
-                       $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) );
 
-                       if( $this->mOutputType == OT_HTML ) {
-                               # Strip <nowiki>, <pre>, etc.
-                               $text = $this->strip( $text, $this->mStripState );
-                               $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ), $assocArgs );
-                       }
-                       $text = $this->replaceVariables( $text, $assocArgs );
+                       if ( !$noparse ) {
+                               # If there are any <onlyinclude> tags, only include them
+                               if ( in_string( '<onlyinclude>', $text ) && in_string( '</onlyinclude>', $text ) ) {
+                                       preg_match_all( '/<onlyinclude>(.*?)\n?<\/onlyinclude>/s', $text, $m );
+                                       $text = '';
+                                       foreach ($m[1] as $piece)
+                                               $text .= $piece;
+                               }
+                               # Remove <noinclude> sections and <includeonly> tags
+                               $text = preg_replace( '/<noinclude>.*?<\/noinclude>/s', '', $text );
+                               $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) );
+
+                               if( $this->mOutputType == OT_HTML ) {
+                                       # Strip <nowiki>, <pre>, etc.
+                                       $text = $this->strip( $text, $this->mStripState );
+                                       $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ), $assocArgs );
+                               }
+                               $text = $this->replaceVariables( $text, $assocArgs );
 
-                       # If the template begins with a table or block-level
-                       # element, it should be treated as beginning a new line.
-                       if (!$piece['lineStart'] && preg_match('/^({\\||:|;|#|\*)/', $text)) {
-                               $text = "\n" . $text;
+                               # If the template begins with a table or block-level
+                               # element, it should be treated as beginning a new line.
+                               if (!$piece['lineStart'] && preg_match('/^({\\||:|;|#|\*)/', $text)) {
+                                       $text = "\n" . $text;
+                               }
+                       } elseif ( !$noargs ) {
+                               # $noparse and !$noargs
+                               # Just replace the arguments, not any double-brace items
+                               # This is used for rendered interwiki transclusion
+                               $text = $this->replaceVariables( $text, $assocArgs, true );
                        }
                }
                # Prune lower levels off the recursion check path
@@ -2692,7 +2738,7 @@ class Parser
        }
 
        /**
-        * Fetch the unparsed text of a template and register a reference to it. 
+        * Fetch the unparsed text of a template and register a reference to it.
         */
        function fetchTemplate( $title ) {
                $text = false;
@@ -2714,41 +2760,49 @@ class Parser
        }
 
        /**
-        * Translude an interwiki link.
+        * Transclude an interwiki link.
         */
-       function scarytransclude($title, $interwiki) {
-               global $wgEnableScaryTranscluding;
+       function interwikiTransclude( $title, $action ) {
+               global $wgEnableScaryTranscluding, $wgCanonicalNamespaceNames;
 
                if (!$wgEnableScaryTranscluding)
                        return wfMsg('scarytranscludedisabled');
 
-               $articlename = "Template:" . $title->getDBkey();
-               $url = str_replace('$1', urlencode($articlename), $interwiki);
+               // The namespace will actually only be 0 or 10, depending on whether there was a leading :
+               // But we'll handle it generally anyway
+               if ( $title->getNamespace() ) {
+                       // Use the canonical namespace, which should work anywhere
+                       $articleName = $wgCanonicalNamespaceNames[$title->getNamespace()] . ':' . $title->getDBkey();
+               } else {
+                       $articleName = $title->getDBkey();
+               }
+
+               $url = str_replace('$1', urlencode($articleName), Title::getInterwikiLink($title->getInterwiki()));
+               $url .= "?action=$action";
                if (strlen($url) > 255)
                        return wfMsg('scarytranscludetoolong');
-               $text = $this->fetchScaryTemplateMaybeFromCache($url);
-               $this->mIWTransData[] = $text;
-               return "<!--IW_TRANSCLUDE ".(count($this->mIWTransData) - 1)."-->";
+               return $this->fetchScaryTemplateMaybeFromCache($url);
        }
 
        function fetchScaryTemplateMaybeFromCache($url) {
+               global $wgTranscludeCacheExpiry;
                $dbr =& wfGetDB(DB_SLAVE);
                $obj = $dbr->selectRow('transcache', array('tc_time', 'tc_contents'),
                                array('tc_url' => $url));
                if ($obj) {
                        $time = $obj->tc_time;
                        $text = $obj->tc_contents;
-                       if ($time && $time < (time() + (60*60))) {
+                       if ($time && time() < $time + $wgTranscludeCacheExpiry ) {
                                return $text;
                        }
                }
 
-               $text = wfGetHTTP($url . '?action=render');
+               $text = wfGetHTTP($url);
                if (!$text)
                        return wfMsg('scarytranscludefailed', $url);
 
                $dbw =& wfGetDB(DB_MASTER);
-               $dbw->replace('transcache', array(), array(
+               $dbw->replace('transcache', array('tc_url'), array(
                        'tc_url' => $url,
                        'tc_time' => time(),
                        'tc_contents' => $text));
@@ -2967,6 +3021,8 @@ class Parser
                        # strip out HTML
                        $canonized_headline = preg_replace( '/<.*?' . '>/','',$canonized_headline );
                        $tocline = trim( $canonized_headline );
+                       # Save headline for section edit hint before it's escaped
+                       $headline_hint = trim( $canonized_headline );
                        $canonized_headline = Sanitizer::escapeId( $tocline );
                        $refers[$headlineCount] = $canonized_headline;
 
@@ -2995,7 +3051,7 @@ class Parser
                                if( $istemplate )
                                        $head[$headlineCount] .= $sk->editSectionLinkForOther($templatetitle, $templatesection);
                                else
-                                       $head[$headlineCount] .= $sk->editSectionLink($this->mTitle, $sectionCount+1);
+                                       $head[$headlineCount] .= $sk->editSectionLink($this->mTitle, $sectionCount+1, $headline_hint);
                        }
 
                        # give headline the correct <h#> tag
@@ -3222,7 +3278,7 @@ class Parser
                
                # Signatures
                $sigText = $this->getUserSig( $user );
-               $text = strtr( $text, array( 
+               $text = strtr( $text, array(
                        '~~~~~' => $d,
                        '~~~~' => "$sigText $d",
                        '~~~' => $sigText
@@ -3275,8 +3331,6 @@ class Parser
         * @access private
         */
        function getUserSig( &$user ) {
-               global $wgContLang;
-
                $username = $user->getName();
                $nickname = $user->getOption( 'nickname' );
                $nickname = $nickname === '' ? $username : $nickname;
@@ -3378,8 +3432,11 @@ class Parser
 
        /**
         * Create an HTML-style tag, e.g. <yourtag>special text</yourtag>
-        * Callback will be called with the text within
-        * Transform and return the text within
+        * The callback should have the following form:
+        *    function myParserHook( $text, $params, &$parser ) { ... }
+        *
+        * Transform and return $text. Use $parser for any required context, e.g. use
+        * $parser->getTitle() and $parser->getOptions() not $wgTitle or $wgOut->mParserOptions
         *
         * @access public
         *
@@ -3563,9 +3620,6 @@ class Parser
         * @return string
         */
        function replaceLinkHoldersText( $text ) {
-               global $wgUser;
-               global $wgOutputReplace;
-
                $fname = 'Parser::replaceLinkHoldersText';
                wfProfileIn( $fname );
 
@@ -3649,7 +3703,7 @@ class Parser
         * Parse image options text and use it to make an image
         */
        function makeImage( &$nt, $options ) {
-               global $wgContLang, $wgUseImageResize, $wgUser;
+               global $wgUseImageResize;
 
                $align = '';
 
@@ -3729,7 +3783,7 @@ class Parser
                $this->mOutput->mCacheTime = -1;
        }
 
-       /**#@+ 
+       /**#@+
         * Callback from the Sanitizer for expanding items found in HTML attribute
         * values, so they can be safely tested and escaped.
         * @param string $text
@@ -3931,8 +3985,8 @@ class ParserOptions
 
        /** Get user options */
        function initialiseFromUser( &$userInput ) {
-               global $wgUseTeX, $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages,
-                      $wgAllowExternalImagesFrom, $wgAllowSpecialInclusion;
+               global $wgUseTeX, $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
+               global $wgAllowExternalImagesFrom, $wgAllowSpecialInclusion;
                $fname = 'ParserOptions::initialiseFromUser';
                wfProfileIn( $fname );
                if ( !$userInput ) {