Tweak for r79018: fatal errors due to something calling parser clearState when mOptio...
[lhc/web/wiklou.git] / includes / parser / Parser.php
index 9346ddf..9d9ccad 100644 (file)
@@ -194,7 +194,9 @@ class Parser {
                        $this->firstCallInit();
                }
                $this->mOutput = new ParserOutput;
-               $this->mOptions->registerWatcher( array( $this->mOutput, 'recordOption' ) );
+               if( $this->mOptions ) {
+                       $this->mOptions->registerWatcher( array( $this->mOutput, 'recordOption' ) );
+               }
                $this->mAutonumber = 0;
                $this->mLastSection = '';
                $this->mDTopen = false;
@@ -271,12 +273,7 @@ class Parser {
                wfProfileIn( __METHOD__ );
                wfProfileIn( $fname );
 
-               $this->mOptions = $options;
-               if ( $clearState ) {
-                       $this->clearState();
-               }
-
-               $this->setTitle( $title ); # Page title has to be set for the pre-processor
+               $this->startExternalParse( $title, $options, self::OT_HTML, $clearState );
 
                $oldRevisionId = $this->mRevisionId;
                $oldRevisionObject = $this->mRevisionObject;
@@ -288,7 +285,7 @@ class Parser {
                        $this->mRevisionTimestamp = null;
                        $this->mRevisionUser = null;
                }
-               $this->setOutputType( self::OT_HTML );
+
                wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
                # No more strip!
                wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
@@ -458,12 +455,9 @@ class Parser {
         * Expand templates and variables in the text, producing valid, static wikitext.
         * Also removes comments.
         */
-       function preprocess( $text, $title, $options, $revid = null ) {
+       function preprocess( $text, Title $title, ParserOptions $options, $revid = null ) {
                wfProfileIn( __METHOD__ );
-               $this->mOptions = $options;
-               $this->clearState();
-               $this->setOutputType( self::OT_PREPROCESS );
-               $this->setTitle( $title );
+               $this->startExternalParse( $title, $options, self::OT_PREPROCESS, true );
                if ( $revid !== null ) {
                        $this->mRevisionId = $revid;
                }
@@ -481,12 +475,9 @@ class Parser {
         * <noinclude>, <includeonly> etc. are parsed as for template transclusion,
         * comments, templates, arguments, tags hooks and parser functions are untouched.
         */
-       public function getPreloadText( $text, $title, $options ) {
+       public function getPreloadText( $text, Title $title, ParserOptions $options ) {
                # Parser (re)initialisation
-               $this->mOptions = $options;
-               $this->clearState();
-               $this->setOutputType( self::OT_PLAIN );
-               $this->setTitle( $title );
+               $this->startExternalParse( $title, $options, self::OT_PLAIN, true );
 
                $flags = PPFrame::NO_ARGS | PPFrame::NO_TEMPLATES;
                $dom = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION );
@@ -1122,10 +1113,9 @@ class Parser {
                                throw new MWException( __METHOD__.': unrecognised match type "' .
                                        substr( $m[0], 0, 20 ) . '"' );
                        }
-                       $url = wfMsgForContent( $urlmsg, $id);
+                       $url = wfMsgForContent( $urlmsg, $id );
                        $sk = $this->mOptions->getSkin( $this->mTitle );
-                       $la = $sk->getExternalLinkAttributes( "external $CssClass" );
-                       return "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
+                       return $sk->makeExternalLink( $url, "{$keyword} {$id}", true, $CssClass );
                } elseif ( isset( $m[5] ) && $m[5] !== '' ) {
                        # ISBN
                        $isbn = $m[5];
@@ -2556,25 +2546,25 @@ class Parser {
                                $value = wfEscapeWikiText( $this->mTitle->getText() );
                                break;
                        case 'pagenamee':
-                               $value = $this->mTitle->getPartialURL();
+                               $value = wfEscapeWikiText( $this->mTitle->getPartialURL() );
                                break;
                        case 'fullpagename':
                                $value = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
                                break;
                        case 'fullpagenamee':
-                               $value = $this->mTitle->getPrefixedURL();
+                               $value = wfEscapeWikiText( $this->mTitle->getPrefixedURL() );
                                break;
                        case 'subpagename':
                                $value = wfEscapeWikiText( $this->mTitle->getSubpageText() );
                                break;
                        case 'subpagenamee':
-                               $value = $this->mTitle->getSubpageUrlForm();
+                               $value = wfEscapeWikiText( $this->mTitle->getSubpageUrlForm() );
                                break;
                        case 'basepagename':
                                $value = wfEscapeWikiText( $this->mTitle->getBaseText() );
                                break;
                        case 'basepagenamee':
-                               $value = wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) );
+                               $value = wfEscapeWikiText( wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) ) );
                                break;
                        case 'talkpagename':
                                if ( $this->mTitle->canTalk() ) {
@@ -2587,7 +2577,7 @@ class Parser {
                        case 'talkpagenamee':
                                if ( $this->mTitle->canTalk() ) {
                                        $talkPage = $this->mTitle->getTalkPage();
-                                       $value = $talkPage->getPrefixedUrl();
+                                       $value = wfEscapeWikiText( $talkPage->getPrefixedUrl() );
                                } else {
                                        $value = '';
                                }
@@ -2598,7 +2588,7 @@ class Parser {
                                break;
                        case 'subjectpagenamee':
                                $subjPage = $this->mTitle->getSubjectPage();
-                               $value = $subjPage->getPrefixedUrl();
+                               $value = wfEscapeWikiText( $subjPage->getPrefixedUrl() );
                                break;
                        case 'revisionid':
                                # Let the edit saving system know we should parse the page
@@ -3355,12 +3345,12 @@ class Parser {
                                $text = $rev->getText();
                        } elseif ( $title->getNamespace() == NS_MEDIAWIKI ) {
                                global $wgContLang;
-                               $message = $wgContLang->lcfirst( $title->getText() );
-                               $text = wfMsgForContentNoTrans( $message );
-                               if ( wfEmptyMsg( $message, $text ) ) {
+                               $message = wfMessage( $wgContLang->lcfirst( $title->getText() ) )->inContentLanguage();
+                               if ( !$message->exists() ) {
                                        $text = false;
                                        break;
                                }
+                               $text = $message->plain();
                        } else {
                                break;
                        }
@@ -3960,10 +3950,10 @@ class Parser {
                                // We use a page and section attribute to stop the language converter from converting these important bits
                                // of data, but put the headline hint inside a content block because the language converter is supposed to
                                // be able to convert that piece of data.
-                               $editlink = '<editsection page="' . htmlspecialchars($editlinkArgs[0]);
+                               $editlink = '<mw:editsection page="' . htmlspecialchars($editlinkArgs[0]);
                                $editlink .= '" section="' . htmlspecialchars($editlinkArgs[1]) .'"';
                                if ( isset($editlinkArgs[2]) ) {
-                                       $editlink .= '>' . $editlinkArgs[2] . '</editsection>';
+                                       $editlink .= '>' . $editlinkArgs[2] . '</mw:editsection>';
                                } else {
                                        $editlink .= '/>';
                                }
@@ -4040,20 +4030,16 @@ class Parser {
         * @return String: the altered wiki markup
         */
        public function preSaveTransform( $text, Title $title, User $user, ParserOptions $options, $clearState = true ) {
-               $this->mOptions = $options;
-               $this->setTitle( $title );
+               $this->startExternalParse( $title, $options, self::OT_WIKI, $clearState );
                $this->setUser( $user );
-               $this->setOutputType( self::OT_WIKI );
-
-               if ( $clearState ) {
-                       $this->clearState();
-               }
 
                $pairs = array(
                        "\r\n" => "\n",
                );
                $text = str_replace( array_keys( $pairs ), array_values( $pairs ), $text );
-               $text = $this->pstPass2( $text, $user );
+               if( $options->getPreSaveTransform() ) {
+                       $text = $this->pstPass2( $text, $user );
+               }
                $text = $this->mStripState->unstripBoth( $text );
 
                $this->setUser( null ); #Reset
@@ -4103,6 +4089,9 @@ class Parser {
                # Because mOutputType is OT_WIKI, this will only process {{subst:xxx}} type tags
                $text = $this->replaceVariables( $text );
 
+               # This works almost by chance, as the replaceVariables are done before the getUserSig(), 
+               # which may corrupt this parser instance via its wfMsgExt( parsemag ) call-
+
                # Signatures
                $sigText = $this->getUserSig( $user );
                $text = strtr( $text, array(
@@ -4148,6 +4137,8 @@ class Parser {
         * validated, ready-to-insert wikitext.
         * If you have pre-fetched the nickname or the fancySig option, you can
         * specify them here to save a database query.
+        * Do not reuse this parser instance after calling getUserSig(),
+        * as it may have changed if it's the $wgParser.
         *
         * @param $user User
         * @param $nickname String: nickname to use or false to use user's default nickname
@@ -4266,9 +4257,8 @@ class Parser {
         * Set up some variables which are usually set up in parse()
         * so that an external function can call some class members with confidence
         */
-       public function startExternalParse( &$title, $options, $outputType, $clearState = true ) {
+       public function startExternalParse( Title $title = null, ParserOptions $options, $outputType, $clearState = true ) {
                $this->setTitle( $title );
-               $options->resetUsage();
                $this->mOptions = $options;
                $this->setOutputType( $outputType );
                if ( $clearState ) {
@@ -4294,7 +4284,13 @@ class Parser {
                $executing = true;
 
                wfProfileIn( __METHOD__ );
-               $text = $this->preprocess( $text, $wgTitle, $options );
+               $title = $wgTitle;
+               if ( !$title ) {
+                       # It's not uncommon having a null $wgTitle in scripts. See r80898
+                       # Create a ghost title in such case
+                       $title = Title::newFromText( 'Dwimmerlaik' );
+               }
+               $text = $this->preprocess( $text, $title, $options );
 
                $executing = false;
                wfProfileOut( __METHOD__ );
@@ -4315,6 +4311,7 @@ class Parser {
         */
        public function setHook( $tag, $callback ) {
                $tag = strtolower( $tag );
+               if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) throw new MWException( "Invalid character {$m[0]} in setHook('$tag', ...) call" );
                $oldVal = isset( $this->mTagHooks[$tag] ) ? $this->mTagHooks[$tag] : null;
                $this->mTagHooks[$tag] = $callback;
                if ( !in_array( $tag, $this->mStripList ) ) {
@@ -4326,6 +4323,7 @@ class Parser {
 
        function setTransparentTagHook( $tag, $callback ) {
                $tag = strtolower( $tag );
+               if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) throw new MWException( "Invalid character {$m[0]} in setTransparentHook('$tag', ...) call" );
                $oldVal = isset( $this->mTransparentTagHooks[$tag] ) ? $this->mTransparentTagHooks[$tag] : null;
                $this->mTransparentTagHooks[$tag] = $callback;
 
@@ -4430,6 +4428,7 @@ class Parser {
         */
        function setFunctionTagHook( $tag, $callback, $flags ) {
                $tag = strtolower( $tag );
+               if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) throw new MWException( "Invalid character {$m[0]} in setFunctionTagHook('$tag', ...) call" );
                $old = isset( $this->mFunctionTagHooks[$tag] ) ?
                        $this->mFunctionTagHooks[$tag] : null;
                $this->mFunctionTagHooks[$tag] = array( $callback, $flags );
@@ -4870,11 +4869,8 @@ class Parser {
         *                 for "replace", the whole page with the section replaced.
         */
        private function extractSections( $text, $section, $mode, $newText='' ) {
-               global $wgTitle;
-               $this->mOptions = new ParserOptions;
-               $this->clearState();
-               $this->setTitle( $wgTitle ); # not generally used but removes an ugly failure mode
-               $this->setOutputType( self::OT_PLAIN );
+               global $wgTitle; # not generally used but removes an ugly failure mode
+               $this->startExternalParse( $wgTitle, new ParserOptions, self::OT_PLAIN, true );
                $outText = '';
                $frame = $this->getPreprocessor()->newFrame();
 
@@ -5077,7 +5073,11 @@ class Parser {
 
        /**
         * Accessor for $mDefaultSort
-        * Will use the title/prefixed title if none is set
+        * Will use the empty string if none is set.
+        *
+        * This value is treated as a prefix, so the
+        * empty string is equivalent to sorting by
+        * page name.
         *
         * @return string
         */
@@ -5085,7 +5085,7 @@ class Parser {
                if ( $this->mDefaultSort !== false ) {
                        return $this->mDefaultSort;
                } else {
-                       return $this->mTitle->getCategorySortkey();
+                       return '';
                }
        }
 
@@ -5161,15 +5161,12 @@ class Parser {
        /**
         * strip/replaceVariables/unstrip for preprocessor regression testing
         */
-       function testSrvus( $text, $title, $options, $outputType = self::OT_HTML ) {
-               $this->mOptions = $options;
-               $this->clearState();
+       function testSrvus( $text, $title, ParserOptions $options, $outputType = self::OT_HTML ) {
                if ( !$title instanceof Title ) {
                        $title = Title::newFromText( $title );
                }
-               $this->mTitle = $title;
-               $options->resetUsage();
-               $this->setOutputType( $outputType );
+               $this->startExternalParse( $title, $options, $outputType, true );
+
                $text = $this->replaceVariables( $text );
                $text = $this->mStripState->unstripBoth( $text );
                $text = Sanitizer::removeHTMLtags( $text );