Tweak for r79018: fatal errors due to something calling parser clearState when mOptio...
[lhc/web/wiklou.git] / includes / parser / Parser.php
index aa7204f..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;
@@ -814,22 +816,7 @@ class Parser {
                $has_opened_tr = array(); # Did this table open a <tr> element?
                $indent_level = 0; # indent level of the table
 
-               # Keep pulling lines off the front of the array until they're all gone.
-               # we want to be able to push lines back on to the front of the stream,
-               # but StringUtils::explode() returns funky optimised Iterators which don't
-               # support insertion.  So maintain a separate buffer and draw on that first if
-               # there's anything in it
-               $extraLines = array();
-               $lines->rewind();
-               do {
-                       if( $extraLines ){
-                               $outLine = array_shift( $extraLines );
-                       } elseif( $lines->valid() ) {
-                               $outLine = $lines->current();
-                               $lines->next();
-                       } else {
-                               break;
-                       }
+               foreach ( $lines as $outLine ) {
                        $line = trim( $outLine );
 
                        if ( $line === '' ) { # empty line, go to next line
@@ -905,10 +892,11 @@ class Parser {
                        } elseif ( $first_character === '|' || $first_character === '!' || substr( $line , 0 , 2 )  === '|+' ) {
                                # This might be cell elements, td, th or captions
                                if ( substr( $line , 0 , 2 ) === '|+' ) {
-                                       $first_character = '|+';
+                                       $first_character = '+';
+                                       $line = substr( $line , 1 );
                                }
 
-                               $line = substr( $line , strlen( $first_character ) );
+                               $line = substr( $line , 1 );
 
                                if ( $first_character === '!' ) {
                                        $line = str_replace( '!!' , '||' , $line );
@@ -919,84 +907,62 @@ class Parser {
                                # by earlier parser steps, but should avoid splitting up eg
                                # attribute values containing literal "||".
                                $cells = StringUtils::explodeMarkup( '||' , $line );
-                               $cell = array_shift( $cells );
-
-                               # Inject cells back into the stream to be dealt with later
-                               # TODO: really we should do the whole thing as a stream...
-                               # but that would be too much like a sensible implementation :P
-                               if( count( $cells ) ){
-                                       foreach( array_reverse( $cells ) as $extraCell ){
-                                               array_unshift( $extraLines, $first_character . $extraCell );
-                                       }
-                               }
 
                                $outLine = '';
 
-                               $previous = '';
-                               if ( $first_character !== '|+' ) {
-                                       $tr_after = array_pop( $tr_attributes );
-                                       if ( !array_pop( $tr_history ) ) {
-                                               $previous = "<tr{$tr_after}>\n";
+                               # Loop through each table cell
+                               foreach ( $cells as $cell ) {
+                                       $previous = '';
+                                       if ( $first_character !== '+' ) {
+                                               $tr_after = array_pop( $tr_attributes );
+                                               if ( !array_pop( $tr_history ) ) {
+                                                       $previous = "<tr{$tr_after}>\n";
+                                               }
+                                               array_push( $tr_history , true );
+                                               array_push( $tr_attributes , '' );
+                                               array_pop( $has_opened_tr );
+                                               array_push( $has_opened_tr , true );
                                        }
-                                       array_push( $tr_history , true );
-                                       array_push( $tr_attributes , '' );
-                                       array_pop( $has_opened_tr );
-                                       array_push( $has_opened_tr , true );
-                               }
 
-                               $last_tag = array_pop( $last_tag_history );
+                                       $last_tag = array_pop( $last_tag_history );
 
-                               if ( array_pop( $td_history ) ) {
-                                       $previous = "</{$last_tag}>\n{$previous}";
-                               }
+                                       if ( array_pop( $td_history ) ) {
+                                               $previous = "</{$last_tag}>\n{$previous}";
+                                       }
 
-                               if ( $first_character === '|' ) {
-                                       $last_tag = 'td';
-                               } elseif ( $first_character === '!' ) {
-                                       $last_tag = 'th';
-                               } elseif ( $first_character === '|+' ) {
-                                       $last_tag = 'caption';
-                               } else {
-                                       $last_tag = '';
-                               }
+                                       if ( $first_character === '|' ) {
+                                               $last_tag = 'td';
+                                       } elseif ( $first_character === '!' ) {
+                                               $last_tag = 'th';
+                                       } elseif ( $first_character === '+' ) {
+                                               $last_tag = 'caption';
+                                       } else {
+                                               $last_tag = '';
+                                       }
 
-                               array_push( $last_tag_history , $last_tag );
-
-                               # A cell could contain both parameters and data... but the pipe could
-                               # also be the start of a nested table, or a raw pipe inside an invalid
-                               # link (bug 553).  
-                               $cell_data = preg_split( '/(?<!\{)\|/', $cell, 2 );
-
-                               # Bug 553: a '|' inside an invalid link should not
-                               # be mistaken as delimiting cell parameters
-                               if ( strpos( $cell_data[0], '[[' ) !== false ) {
-                                       $data = $cell;
-                                       $cell = "{$previous}<{$last_tag}>";
-                               } elseif ( count( $cell_data ) == 1 ) {
-                                       $cell = "{$previous}<{$last_tag}>";
-                                       $data = $cell_data[0];
-                               } else {
-                                       $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
-                                       $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
-                                       $cell = "{$previous}<{$last_tag}{$attributes}>";
-                                       $data = $cell_data[1];
-                               }
+                                       array_push( $last_tag_history , $last_tag );
 
-                               # Bug 529: the start of a table cell should be a linestart context for
-                               # processing other block markup, including nested tables.  The original
-                               # implementation of this was to add a newline before every brace construct,
-                               # which broke all manner of other things.  Instead, push the contents
-                               # of the cell back into the stream and come back to it later.  But don't
-                               # do that if the first line is empty, or you may get extra whitespace
-                               if( $data ){
-                                       array_unshift( $extraLines, trim( $data ) );
-                               }
+                                       # A cell could contain both parameters and data
+                                       $cell_data = explode( '|' , $cell , 2 );
+
+                                       # Bug 553: Note that a '|' inside an invalid link should not
+                                       # be mistaken as delimiting cell parameters
+                                       if ( strpos( $cell_data[0], '[[' ) !== false ) {
+                                               $cell = "{$previous}<{$last_tag}>{$cell}";
+                                       } elseif ( count( $cell_data ) == 1 ) {
+                                               $cell = "{$previous}<{$last_tag}>{$cell_data[0]}";
+                                       } else {
+                                               $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
+                                               $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
+                                               $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
+                                       }
 
-                               $outLine .= $cell;
-                               array_push( $td_history , true );
+                                       $outLine .= $cell;
+                                       array_push( $td_history , true );
+                               }
                        }
                        $out .= $outLine . "\n";
-               } while( $lines->valid() || count( $extraLines ) );
+               }
 
                # Closing open td, tr && table
                while ( count( $td_history ) > 0 ) {
@@ -1147,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];
@@ -2260,7 +2225,6 @@ class Parser {
                                        '/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'.
                                        '<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t );
                                if ( $openmatch or $closematch ) {
-
                                        $paragraphStack = false;
                                        # TODO bug 5718: paragraph closed
                                        $output .= $this->closeParagraph();
@@ -3251,11 +3215,11 @@ class Parser {
                        $text = wfEscapeWikiText( $text );
                } elseif ( is_string( $text )
                        && !$piece['lineStart']
-                       && preg_match( '/^{\\|/', $text ) )
+                       && preg_match( '/^(?:{\\||:|;|#|\*)/', $text ) )
                {
-                       # Bug 529: if the template begins with a table, it should be treated as
-                       # beginning a new line.  This previously handled other block-level elements
-                       # such as #, :, etc, but these have many false-positives (bug 12974).
+                       # Bug 529: if the template begins with a table or block-level
+                       # element, it should be treated as beginning a new line.
+                       # This behaviour is somewhat controversial.
                        $text = "\n" . $text;
                }
 
@@ -3314,7 +3278,7 @@ class Parser {
 
                if ( !$title->equals( $cacheTitle ) ) {
                        $this->mTplRedirCache[$cacheTitle->getPrefixedDBkey()] =
-                               array( $title->getNamespace(), $title->getDBkey() );
+                               array( $title->getNamespace(), $cdb = $title->getDBkey() );
                }
 
                return array( $dom, $title );
@@ -3986,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 .= '/>';
                                }
@@ -4125,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(
@@ -4170,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
@@ -4288,7 +4257,7 @@ 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 $title, ParserOptions $options, $outputType, $clearState = true ) {
+       public function startExternalParse( Title $title = null, ParserOptions $options, $outputType, $clearState = true ) {
                $this->setTitle( $title );
                $this->mOptions = $options;
                $this->setOutputType( $outputType );
@@ -4315,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__ );
@@ -5098,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
         */
@@ -5106,7 +5085,7 @@ class Parser {
                if ( $this->mDefaultSort !== false ) {
                        return $this->mDefaultSort;
                } else {
-                       return $this->mTitle->getCategorySortkey();
+                       return '';
                }
        }