* Enable category names to be written in variants (use single linkbatch for both...
[lhc/web/wiklou.git] / includes / Parser.php
index cfe1d70..ad39cbd 100644 (file)
@@ -97,7 +97,7 @@ class Parser
        var $mTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables;
 
        # Cleared with clearState():
-       var $mOutput, $mAutonumber, $mDTopen, $mStripState = array();
+       var $mOutput, $mAutonumber, $mDTopen, $mStripState;
        var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
        var $mInterwikiLinkHolders, $mLinkHolders, $mUniqPrefix;
        var $mIncludeSizes;
@@ -112,7 +112,9 @@ class Parser
                $mTitle,        // Title context, used for self-link rendering and similar things
                $mOutputType,   // Output type, one of the OT_xxx constants
                $ot,            // Shortcut alias, see setOutputType()
-               $mRevisionId;   // ID to display in {{REVISIONID}} tags
+               $mRevisionId,   // ID to display in {{REVISIONID}} tags
+               $mRevisionTimestamp, // The timestamp of the specified revision ID
+               $mRevIdForTs;   // The revision ID which was used to fetch the timestamp  
 
        /**#@-*/
 
@@ -164,6 +166,7 @@ class Parser
                $this->setFunctionHook( 'padleft', array( 'CoreParserFunctions', 'padleft' ), SFH_NO_HASH );
                $this->setFunctionHook( 'padright', array( 'CoreParserFunctions', 'padright' ), SFH_NO_HASH );
                $this->setFunctionHook( 'anchorencode', array( 'CoreParserFunctions', 'anchorencode' ), SFH_NO_HASH );
+               $this->setFunctionHook( 'special', array( 'CoreParserFunctions', 'special' ) );
 
                if ( $wgAllowDisplayTitle ) {
                        $this->setFunctionHook( 'displaytitle', array( 'CoreParserFunctions', 'displaytitle' ), SFH_NO_HASH );
@@ -173,7 +176,6 @@ class Parser
                }
 
                $this->initialiseVariables();
-
                $this->mFirstCall = false;
                wfProfileOut( __METHOD__ );
        }
@@ -193,7 +195,7 @@ class Parser
                $this->mLastSection = '';
                $this->mDTopen = false;
                $this->mIncludeCount = array();
-               $this->mStripState = array();
+               $this->mStripState = new StripState;
                $this->mArgStack = array();
                $this->mInPre = false;
                $this->mInterwikiLinkHolders = array(
@@ -207,8 +209,8 @@ class Parser
                        'texts' => array(),
                        'titles' => array()
                );
-               $this->mRevisionId = null;
-
+               $this->mRevisionTimestamp = $this->mRevisionId = null;
+               
                /**
                 * Prefix for temporary replacement strings for the multipass parser.
                 * \x07 should never appear in input as it's disallowed in XML.
@@ -275,6 +277,7 @@ class Parser
 
                global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang;
                $fname = 'Parser::parse-' . wfGetCaller();
+               wfProfileIn( __METHOD__ );
                wfProfileIn( $fname );
 
                if ( $clearState ) {
@@ -284,22 +287,17 @@ class Parser
                $this->mOptions = $options;
                $this->mTitle =& $title;
                $oldRevisionId = $this->mRevisionId;
+               $oldRevisionTimestamp = $this->mRevisionTimestamp;
                if( $revid !== null ) {
                        $this->mRevisionId = $revid;
+                       $this->mRevisionTimestamp = null;
                }
                $this->setOutputType( OT_HTML );
-
-               //$text = $this->strip( $text, $this->mStripState );
-               // VOODOO MAGIC FIX! Sometimes the above segfaults in PHP5.
-               $x =& $this->mStripState;
-
-               wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$x ) );
-               $text = $this->strip( $text, $x );
-               wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$x ) );
-
+               wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
+               $text = $this->strip( $text, $this->mStripState );
+               wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
                $text = $this->internalParse( $text );
-
-               $text = $this->unstrip( $text, $this->mStripState );
+               $text = $this->mStripState->unstripGeneral( $text );
 
                # Clean up special characters, only run once, next-to-last before doBlockLevels
                $fixtags = array(
@@ -322,7 +320,7 @@ class Parser
                # Side-effects: this calls $this->mOutput->setTitleText()
                $text = $wgContLang->parserConvert( $text, $this );
 
-               $text = $this->unstripNoWiki( $text, $this->mStripState );
+               $text = $this->mStripState->unstripNoWiki( $text );
 
                wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
 
@@ -372,7 +370,9 @@ class Parser
                }
                $this->mOutput->setText( $text );
                $this->mRevisionId = $oldRevisionId;
+               $this->mRevisionTimestamp = $oldRevisionTimestamp;
                wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
 
                return $this->mOutput;
        }
@@ -383,10 +383,9 @@ class Parser
         */
        function recursiveTagParse( $text ) {
                wfProfileIn( __METHOD__ );
-               $x =& $this->mStripState;
-               wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$x ) );
-               $text = $this->strip( $text, $x );
-               wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$x ) );
+               wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
+               $text = $this->strip( $text, $this->mStripState );
+               wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
                $text = $this->internalParse( $text );
                wfProfileOut( __METHOD__ );
                return $text;
@@ -402,16 +401,14 @@ class Parser
                $this->setOutputType( OT_PREPROCESS );
                $this->mOptions = $options;
                $this->mTitle = $title;
-               $x =& $this->mStripState;
-               wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$x ) );
-               $text = $this->strip( $text, $x );
-               wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$x ) );
+               wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
+               $text = $this->strip( $text, $this->mStripState );
+               wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
                if ( $this->mOptions->getRemoveComments() ) {
                        $text = Sanitizer::removeHTMLcomments( $text );
                }
                $text = $this->replaceVariables( $text );
-               $text = $this->unstrip( $text, $x );
-               $text = $this->unstripNowiki( $text, $x );
+               $text = $this->mStripState->unstripBoth( $text );
                wfProfileOut( __METHOD__ );
                return $text;
        }
@@ -518,7 +515,8 @@ class Parser
         * Strips and renders nowiki, pre, math, hiero
         * If $render is set, performs necessary rendering operations on plugins
         * Returns the text, and fills an array with data needed in unstrip()
-        * If the $state is already a valid strip state, it adds to the state
+        *
+        * @param StripState $state
         *
         * @param bool $stripcomments when set, HTML comments <!-- like this -->
         *  will be stripped in addition to other tags. This is important
@@ -530,12 +528,13 @@ class Parser
         *
         * @private
         */
-       function strip( $text, &$state, $stripcomments = false , $dontstrip = array () ) {
+       function strip( $text, $state, $stripcomments = false , $dontstrip = array () ) {
+               global $wgContLang;
                wfProfileIn( __METHOD__ );
                $render = ($this->mOutputType == OT_HTML);
 
                $uniq_prefix = $this->mUniqPrefix;
-               $commentState = array();
+               $commentState = new ReplacementArray;
 
                $elements = array_merge(
                        array( 'nowiki', 'gallery' ),
@@ -580,10 +579,10 @@ class Parser
                                        }
                                        // Shouldn't happen otherwise. :)
                                case 'nowiki':
-                                       $output = wfEscapeHTMLTagsOnly( $content );
+                                       $output = Xml::escapeTagsOnly( $content );
                                        break;
                                case 'math':
-                                       $output = MathRenderer::renderMath( $content );
+                                       $output = $wgContLang->armourMath( MathRenderer::renderMath( $content ) );
                                        break;
                                case 'gallery':
                                        $output = $this->renderImageGallery( $content, $params );
@@ -604,14 +603,14 @@ class Parser
 
                        // Unstrip the output, because unstrip() is no longer recursive so
                        // it won't do it itself
-                       $output = $this->unstrip( $output, $state );
+                       $output = $state->unstripBoth( $output );
 
                        if( !$stripcomments && $element == '!--' ) {
-                               $commentState[$marker] = $output;
+                               $commentState->setPair( $marker, $output );
                        } elseif ( $element == 'html' || $element == 'nowiki' ) {
-                               $state['nowiki'][$marker] = $output;
+                               $state->nowiki->setPair( $marker, $output );
                        } else {
-                               $state['general'][$marker] = $output;
+                               $state->general->setPair( $marker, $output );
                        }
                }
 
@@ -621,7 +620,7 @@ class Parser
                # a comment.)
                if ( !$stripcomments ) {
                        // Put them all back and forget them
-                       $text = strtr( $text, $commentState );
+                       $text = $commentState->replace( $text );
                }
 
                wfProfileOut( __METHOD__ );
@@ -633,35 +632,27 @@ class Parser
         *
         * always call unstripNoWiki() after this one
         * @private
+        * @deprecated use $this->mStripState->unstrip()
         */
        function unstrip( $text, $state ) {
-               if ( !isset( $state['general'] ) ) {
-                       return $text;
-               }
-
-               wfProfileIn( __METHOD__ );
-               # TODO: good candidate for FSS
-               $text = strtr( $text, $state['general'] );
-               wfProfileOut( __METHOD__ );
-               return $text;
+               return $state->unstripGeneral( $text );
        }
 
        /**
         * Always call this after unstrip() to preserve the order
         *
         * @private
+        * @deprecated use $this->mStripState->unstrip()
         */
        function unstripNoWiki( $text, $state ) {
-               if ( !isset( $state['nowiki'] ) ) {
-                       return $text;
-               }
-
-               wfProfileIn( __METHOD__ );
-               # TODO: good candidate for FSS
-               $text = strtr( $text, $state['nowiki'] );
-               wfProfileOut( __METHOD__ );
+               return $state->unstripNoWiki( $text );
+       }
 
-               return $text;
+       /**
+        * @deprecated use $this->mStripState->unstripBoth()
+        */
+       function unstripForHTML( $text ) {
+               return $this->mStripState->unstripBoth( $text );
        }
 
        /**
@@ -673,10 +664,7 @@ class Parser
         */
        function insertStripItem( $text, &$state ) {
                $rnd = $this->mUniqPrefix . '-item' . Parser::getRandomString();
-               if ( !$state ) {
-                       $state = array();
-               }
-               $state['general'][$rnd] = $text;
+               $state->general->setPair( $rnd, $text );
                return $rnd;
        }
 
@@ -793,141 +781,191 @@ class Parser
         *
         * @private
         */
-       function doTableStuff ( $t ) {
+       function doTableStuff ( $text ) {
                $fname = 'Parser::doTableStuff';
                wfProfileIn( $fname );
 
-               $t = explode ( "\n" , $t ) ;
-               $td = array () ; # Is currently a td tag open?
-               $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 )
+               $lines = explode ( "\n" , $text );
+               $td_history = array (); // Is currently a td tag open?
+               $last_tag_history = array (); // Save history of last lag activated (td, th or caption)
+               $tr_history = array (); // Is currently a tr tag open?
+               $tr_attributes = array (); // history of tr attributes
+               $has_opened_tr = array(); // Did this table open a <tr> element?
+               $indent_level = 0; // indent level of the table
+               foreach ( $lines as $key => $line )
                {
-                       $x = trim ( $x ) ;
-                       $fc = substr ( $x , 0 , 1 ) ;
+                       $line = trim ( $line );
+
+                       if( $line == '' ) { // empty line, go to next line
+                               continue;
+                       }
+                       $first_character = $line{0};
                        $matches = array();
-                       if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) {
+
+                       if ( preg_match( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) {
+                               // First check if we are starting a new table
                                $indent_level = strlen( $matches[1] );
 
-                               $attributes = $this->unstripForHTML( $matches[2] );
+                               $attributes = $this->mStripState->unstripBoth( $matches[2] );
+                               $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' );
+
+                               $lines[$key] = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
+                               array_push ( $td_history , false );
+                               array_push ( $last_tag_history , '' );
+                               array_push ( $tr_history , false );
+                               array_push ( $tr_attributes , '' );
+                               array_push ( $has_opened_tr , false );
+                       } else if ( count ( $td_history ) == 0 ) {
+                               // Don't do any of the following
+                               continue;
+                       } else if ( substr ( $line , 0 , 2 ) == '|}' ) { 
+                               // We are ending a table
+                               $line = '</table>' . substr ( $line , 2 );
+                               $last_tag = array_pop ( $last_tag_history );
 
-                               $t[$k] = str_repeat( '<dl><dd>', $indent_level ) .
-                                       '<table' . Sanitizer::fixTagAttributes ( $attributes, 'table' ) . '>' ;
-                               array_push ( $td , false ) ;
-                               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 ) ;
-                               $t[$k] = $z . str_repeat( '</dd></dl>', $indent_level );
-                       }
-                       else if ( '|-' == substr ( $x , 0 , 2 ) ) { # Allows for |---------------
-                               $x = substr ( $x , 1 ) ;
-                               while ( $x != '' && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ;
-                               $z = '' ;
-                               $l = array_pop ( $ltd ) ;
+                               if ( !array_pop ( $has_opened_tr ) ) {
+                                       $line = "<tr><td></td></tr>{$line}";
+                               }
+
+                               if ( array_pop ( $tr_history ) ) {
+                                       $line = "</tr>{$line}";
+                               }
+
+                               if ( array_pop ( $td_history ) ) {
+                                       $line = "</{$last_tag}>{$line}";
+                               }
+                               array_pop ( $tr_attributes );
+                               $lines[$key] = $line . str_repeat( '</dd></dl>' , $indent_level );
+                       } else if ( substr ( $line , 0 , 2 ) == '|-' ) {
+                               // Now we have a table row
+                               $line = preg_replace( '#^\|-+#', '', $line );
+
+                               // Whats after the tag is now only attributes
+                               $attributes = $this->mStripState->unstripBoth( $line );
+                               $attributes = Sanitizer::fixTagAttributes ( $attributes , 'tr' );
+                               array_pop ( $tr_attributes );
+                               array_push ( $tr_attributes , $attributes );
+
+                               $line = '';
+                               $last_tag = array_pop ( $last_tag_history );
                                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 ) ;
-                               $t[$k] = $z ;
-                               array_push ( $tr , false ) ;
-                               array_push ( $td , false ) ;
-                               array_push ( $ltd , '' ) ;
-                               $attributes = $this->unstripForHTML( $x );
-                               array_push ( $ltr , Sanitizer::fixTagAttributes ( $attributes, 'tr' ) ) ;
+                               array_push ( $has_opened_tr , true );
+
+                               if ( array_pop ( $tr_history ) ) {
+                                       $line = '</tr>';
+                               }
+
+                               if ( array_pop ( $td_history ) ) {
+                                       $line = "</{$last_tag}>{$line}";
+                               }
+
+                               $lines[$key] = $line;
+                               array_push ( $tr_history , false );
+                               array_push ( $td_history , false );
+                               array_push ( $last_tag_history , '' );
                        }
-                       else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption
-                               # $x is a table row
-                               if ( '|+' == substr ( $x , 0 , 2 ) ) {
-                                       $fc = '+' ;
-                                       $x = substr ( $x , 1 ) ;
+                       else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 )  == '|+' ) { 
+                               // This might be cell elements, td, th or captions
+                               if ( substr ( $line , 0 , 2 ) == '|+' ) {
+                                       $first_character = '+';
+                                       $line = substr ( $line , 1 );
+                               }
+
+                               $line = substr ( $line , 1 );
+
+                               if ( $first_character == '!' ) {
+                                       $line = str_replace ( '!!' , '||' , $line );
                                }
-                               $after = substr ( $x , 1 ) ;
-                               if ( $fc == '!' ) $after = str_replace ( '!!' , '||' , $after ) ;
 
                                // Split up multiple cells on the same line.
-                               // FIXME: This can result in improper nesting of tags processed
+                               // FIXME : This can result in improper nesting of tags processed
                                // by earlier parser steps, but should avoid splitting up eg
                                // attribute values containing literal "||".
-                               $after = wfExplodeMarkup( '||', $after );
+                               $cells = StringUtils::explodeMarkup( '||' , $line );
 
-                               $t[$k] = '' ;
+                               $lines[$key] = '';
 
-                               # Loop through each table cell
-                               foreach ( $after AS $theline )
+                               // Loop through each table cell
+                               foreach ( $cells as $cell )
                                {
-                                       $z = '' ;
-                                       if ( $fc != '+' )
+                                       $previous = '';
+                                       if ( $first_character != '+' )
                                        {
-                                               $tra = array_pop ( $ltr ) ;
-                                               if ( !array_pop ( $tr ) ) $z = '<tr'.$tra.">\n" ;
-                                               array_push ( $tr , true ) ;
-                                               array_push ( $ltr , '' ) ;
+                                               $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 ( $has_opened_tr , true );
                                        }
 
-                                       $l = array_pop ( $ltd ) ;
-                                       if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
-                                       if ( $fc == '|' ) {
-                                           $l = 'td' ;
-                                       } else if ( $fc == '!' ) {
-                                           $l = 'th' ;
-                                       } else if ( $fc == '+' ) {
-                                           $l = 'caption' ;
-                                       } else {
-                                           $l = '' ;
+                                       $last_tag = array_pop ( $last_tag_history );
+
+                                       if ( array_pop ( $td_history ) ) {
+                                               $previous = "</{$last_tag}>{$previous}";
                                        }
-                                       array_push ( $ltd , $l ) ;
-
-                                       # Cell parameters
-                                       $y = explode ( '|' , $theline , 2 ) ;
-                                       # Note that a '|' inside an invalid link should not
-                                       # be mistaken as delimiting cell parameters
-                                       if ( strpos( $y[0], '[[' ) !== false ) {
-                                               $y = array ($theline);
+
+                                       if ( $first_character == '|' ) {
+                                               $last_tag = 'td';
+                                       } else if ( $first_character == '!' ) {
+                                               $last_tag = 'th';
+                                       } else if ( $first_character == '+' ) {
+                                               $last_tag = 'caption';
+                                       } else {
+                                               $last_tag = '';
                                        }
-                                       if ( count ( $y ) == 1 )
-                                               $y = "{$z}<{$l}>{$y[0]}" ;
+
+                                       array_push ( $last_tag_history , $last_tag );
+
+                                       // 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}";
+                                       } else if ( count ( $cell_data ) == 1 )
+                                               $cell = "{$previous}<{$last_tag}>{$cell_data[0]}";
                                        else {
-                                               $attributes = $this->unstripForHTML( $y[0] );
-                                               $y = "{$z}<{$l}".Sanitizer::fixTagAttributes($attributes, $l).">{$y[1]}" ;
+                                               $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
+                                               $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
+                                               $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
                                        }
-                                       $t[$k] .= $y ;
-                                       array_push ( $td , true ) ;
+
+                                       $lines[$key] .= $cell;
+                                       array_push ( $td_history , true );
                                }
                        }
                }
 
-               # Closing open td, tr && table
-               while ( count ( $td ) > 0 )
+               // Closing open td, tr && table
+               while ( count ( $td_history ) > 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>' ;
+                       if ( array_pop ( $td_history ) ) {
+                               $lines[] = '</td>' ;
+                       }
+                       if ( array_pop ( $tr_history ) ) {
+                               $lines[] = '</tr>' ;
+                       }
+                       if ( !array_pop ( $has_opened_tr ) ) {
+                               $lines[] = "<tr><td></td></tr>" ;
+                       }
+
+                       $lines[] = '</table>' ;
+               }
+
+               $output = implode ( "\n" , $lines ) ;
+
+               // special case: don't return empty table
+               if( $output == "<table>\n<tr><td></td></tr>\n</table>" ) {
+                       $output = '';
                }
 
-               $t = implode ( "\n" , $t ) ;
-               # special case: don't return empty table
-               if($t == "<table>\n<tr><td></td></tr>\n</table>")
-                       $t = '';
                wfProfileOut( $fname );
-               return $t ;
+
+               return $output;
        }
 
        /**
@@ -943,8 +981,7 @@ class Parser
                wfProfileIn( $fname );
 
                # Hook to suspend the parser in this state
-               $x =& $this->mStripState; // FIXME: Please check that this initialization is correct.
-               if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$x ) ) ) {
+               if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$this->mStripState ) ) ) {
                        wfProfileOut( $fname );
                        return $text ;
                }
@@ -952,7 +989,7 @@ class Parser
                # Remove <noinclude> tags and <includeonly> sections
                $text = strtr( $text, array( '<onlyinclude>' => '' , '</onlyinclude>' => '' ) );
                $text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') );
-               $text = preg_replace( '/<includeonly>.*?<\/includeonly>/s', '', $text );
+               $text = StringUtils::delimiterReplace( '<includeonly>', '</includeonly>', '', $text );
 
                $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
 
@@ -1001,7 +1038,7 @@ class Parser
                            <a.*?</a> |                 # Skip link text
                            <.*?> |                     # Skip stuff inside HTML elements
                            (?:RFC|PMID)\s+([0-9]+) |   # RFC or PMID, capture number as m[1]
-                           ISBN\s+([0-9Xx-]+)          # ISBN, capture number as m[2]
+                           ISBN\s+(\b[0-9Xx\ \-]+)     # ISBN, capture number as m[2]
                        )!x', array( &$this, 'magicLinkCallback' ), $text );
                wfProfileOut( __METHOD__ );
                return $text;
@@ -1608,7 +1645,7 @@ class Parser
 
                        wfProfileOut( "$fname-misc" );
                        wfProfileIn( "$fname-title" );
-                       $nt = Title::newFromText( $this->unstripNoWiki($link, $this->mStripState) );
+                       $nt = Title::newFromText( $this->mStripState->unstripNoWiki($link) );
                        if( !$nt ) {
                                $s .= $prefix . '[[' . $line;
                                wfProfileOut( "$fname-title" );
@@ -1832,7 +1869,7 @@ class Parser
         * @return string less-or-more HTML with NOPARSE bits
         */
        function armorLinks( $text ) {
-               return preg_replace( "/\b(" . wfUrlProtocols() . ')/',
+               return preg_replace( '/\b(' . wfUrlProtocols() . ')/',
                        "{$this->mUniqPrefix}NOPARSE$1", $text );
        }
 
@@ -2431,15 +2468,15 @@ class Parser
                        case 'revisionid':
                                return $this->mRevisionId;
                        case 'revisionday':
-                               return intval( substr( wfRevisionTimestamp( $this->mRevisionId ), 6, 2 ) );
+                               return intval( substr( $this->getRevisionTimestamp(), 6, 2 ) );
                        case 'revisionday2':
-                               return substr( wfRevisionTimestamp( $this->mRevisionId ), 6, 2 );
+                               return substr( $this->getRevisionTimestamp(), 6, 2 );
                        case 'revisionmonth':
-                               return intval( substr( wfRevisionTimestamp( $this->mRevisionId ), 4, 2 ) );
+                               return intval( substr( $this->getRevisionTimestamp(), 4, 2 ) );
                        case 'revisionyear':
-                               return substr( wfRevisionTimestamp( $this->mRevisionId ), 0, 4 );
+                               return substr( $this->getRevisionTimestamp(), 0, 4 );
                        case 'revisiontimestamp':
-                               return wfRevisionTimestamp( $this->mRevisionId );
+                               return $this->getRevisionTimestamp();
                        case 'namespace':
                                return str_replace('_',' ',$wgContLang->getNsText( $this->mTitle->getNamespace() ) );
                        case 'namespacee':
@@ -2481,15 +2518,15 @@ class Parser
                        case 'localdow':
                                return $varCache[$index] = $wgContLang->formatNum( $localDayOfWeek );
                        case 'numberofarticles':
-                               return $varCache[$index] = $wgContLang->formatNum( wfNumberOfArticles() );
+                               return $varCache[$index] = $wgContLang->formatNum( SiteStats::articles() );
                        case 'numberoffiles':
-                               return $varCache[$index] = $wgContLang->formatNum( wfNumberOfFiles() );
+                               return $varCache[$index] = $wgContLang->formatNum( SiteStats::images() );
                        case 'numberofusers':
-                               return $varCache[$index] = $wgContLang->formatNum( wfNumberOfUsers() );
+                               return $varCache[$index] = $wgContLang->formatNum( SiteStats::users() );
                        case 'numberofpages':
-                               return $varCache[$index] = $wgContLang->formatNum( wfNumberOfPages() );
+                               return $varCache[$index] = $wgContLang->formatNum( SiteStats::pages() );
                        case 'numberofadmins':
-                               return $varCache[$index]  = $wgContLang->formatNum( wfNumberOfAdmins() );
+                               return $varCache[$index]  = $wgContLang->formatNum( SiteStats::admins() );
                        case 'currenttimestamp':
                                return $varCache[$index] = wfTimestampNow();
                        case 'localtimestamp':
@@ -2827,6 +2864,7 @@ class Parser
                $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
+                $headingOffset = 0;         # Skip headings when number, to account for those that weren't transcluded.
                $isHTML = false;            # $text is HTML, armour it against wikitext transformation
                $forceRawInterwiki = false; # Force interwiki transclusion to be done in raw mode not rendered
 
@@ -2976,9 +3014,8 @@ class Parser
 
                        if ( !is_null( $title ) ) {
                                $titleText = $title->getPrefixedText();
-                               $checkVariantLink = sizeof($wgContLang->getVariants())>1;
                                # Check for language variants if the template is not found
-                               if($checkVariantLink && $title->getArticleID() == 0){
+                               if($wgContLang->hasVariants() && $title->getArticleID() == 0){
                                        $wgContLang->findVariantLink($part1, $title);
                                }
 
@@ -3076,14 +3113,13 @@ class Parser
                        if ( !$noparse ) {
                                # If there are any <onlyinclude> tags, only include them
                                if ( in_string( '<onlyinclude>', $text ) && in_string( '</onlyinclude>', $text ) ) {
-                                       $m = array();
-                                       preg_match_all( '/<onlyinclude>(.*?)\n?<\/onlyinclude>/s', $text, $m );
-                                       $text = '';
-                                       foreach ($m[1] as $piece)
-                                               $text .= $piece;
+                                       $replacer = new OnlyIncludeReplacer;
+                                       StringUtils::delimiterReplaceCallback( '<onlyinclude>', '</onlyinclude>', 
+                                               array( &$replacer, 'replace' ), $text );
+                                       $text = $replacer->output;
                                }
                                # Remove <noinclude> sections and <includeonly> tags
-                               $text = preg_replace( '/<noinclude>.*?<\/noinclude>/s', '', $text );
+                               $text = StringUtils::delimiterReplace( '<noinclude>', '</noinclude>', '', $text );
                                $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) );
 
                                if( $this->ot['html'] || $this->ot['pre'] ) {
@@ -3141,7 +3177,7 @@ class Parser
                                        $m = preg_split('/(^={1,6}.*?={1,6}\s*?$)/m', $text, -1,
                                                PREG_SPLIT_DELIM_CAPTURE);
                                        $text = '';
-                                       $nsec = 0;
+                                       $nsec = $headingOffset;
                                        for( $i = 0; $i < count($m); $i += 2 ) {
                                                $text .= $m[$i];
                                                if (!isset($m[$i + 1]) || $m[$i + 1] == "") continue;
@@ -3478,8 +3514,7 @@ class Parser
 
                        # The canonized header is a version of the header text safe to use for links
                        # Avoid insertion of weird stuff like <math> by expanding the relevant sections
-                       $canonized_headline = $this->unstrip( $headline, $this->mStripState );
-                       $canonized_headline = $this->unstripNoWiki( $canonized_headline, $this->mStripState );
+                       $canonized_headline = $this->mStripState->unstripBoth( $headline );
 
                        # Remove link placeholders by the link text.
                        #     <!--LINK number-->
@@ -3501,7 +3536,7 @@ class Parser
                        $refers[$headlineCount] = $canonized_headline;
 
                        # count how many in assoc. array so we can track dupes in anchors
-                       @$refers[$canonized_headline]++;
+                       isset( $refers[$canonized_headline] ) ? $refers[$canonized_headline]++ : $refers[$canonized_headline] = 1;
                        $refcount[$headlineCount]=$refers[$canonized_headline];
 
                        # Don't number the heading if it is the only one (looks silly)
@@ -3519,7 +3554,7 @@ class Parser
                                $toc .= $sk->tocLine($anchor, $tocline, $numbering, $toclevel);
                        }
                        # give headline the correct <h#> tag
-                       @$head[$headlineCount] .= "<a name=\"$anchor\"></a><h".$level.$matches[2][$headlineCount];
+                       $head[$headlineCount] = "<a name=\"$anchor\"></a><h".$level.$matches[2][$headlineCount];
 
                        if( $showEditLink && ( !$istemplate || $templatetitle !== "" ) ) {
                                if ( empty( $head[$headlineCount] ) ) {
@@ -3601,15 +3636,14 @@ class Parser
                        $this->clearState();
                }
 
-               $stripState = false;
+               $stripState = new StripState;
                $pairs = array(
                        "\r\n" => "\n",
                );
                $text = str_replace( array_keys( $pairs ), array_values( $pairs ), $text );
                $text = $this->strip( $text, $stripState, true, array( 'gallery' ) );
                $text = $this->pstPass2( $text, $stripState, $user );
-               $text = $this->unstrip( $text, $stripState );
-               $text = $this->unstripNoWiki( $text, $stripState );
+               $text = $stripState->unstripBoth( $text );
                return $text;
        }
 
@@ -3832,7 +3866,7 @@ class Parser
         */
        function setHook( $tag, $callback ) {
                $tag = strtolower( $tag );
-               $oldVal = @$this->mTagHooks[$tag];
+               $oldVal = isset( $this->mTagHooks[$tag] ) ? $this->mTagHooks[$tag] : null;
                $this->mTagHooks[$tag] = $callback;
 
                return $oldVal;
@@ -3863,7 +3897,7 @@ class Parser
         * @return The old callback function for this name, if any
         */
        function setFunctionHook( $id, $callback, $flags = 0 ) {
-               $oldVal = @$this->mFunctionHooks[$id];
+               $oldVal = isset( $this->mFunctionHooks[$id] ) ? $this->mFunctionHooks[$id] : null;
                $this->mFunctionHooks[$id] = $callback;
 
                # Add to function cache
@@ -3912,7 +3946,6 @@ class Parser
         */
        function replaceLinkHolders( &$text, $options = 0 ) {
                global $wgUser;
-               global $wgOutputReplace;
                global $wgContLang;
 
                $fname = 'Parser::replaceLinkHolders';
@@ -4005,10 +4038,14 @@ class Parser
                        }
                        wfProfileOut( $fname.'-check' );
 
-                       # Do a second query for different language variants of links (if needed)
+                       # Do a second query for different language variants of links and categories
                        if($wgContLang->hasVariants()){
                                $linkBatch = new LinkBatch();
-                               $variantMap = array(); // maps $pdbkey_Variant => $pdbkey_original
+                               $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders)
+                               $categoryMap = array(); // maps $category_variant => $category (dbkeys)
+                               $varCategories = array(); // category replacements oldDBkey => newDBkey
+
+                               $categories = $this->mOutput->getCategoryLinks();
 
                                // Add variants of links to link batch
                                foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) {
@@ -4017,21 +4054,35 @@ class Parser
                                                continue;
 
                                        $pdbk = $title->getPrefixedDBkey();
+                                       $titleText = $title->getText();
 
                                        // generate all variants of the link title text
-                                       $allTextVariants = $wgContLang->convertLinkToAllVariants($title->getText());
+                                       $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText);
 
                                        // if link was not found (in first query), add all variants to query
                                        if ( !isset($colours[$pdbk]) ){
                                                foreach($allTextVariants as $textVariant){
-                                                       $variantTitle = Title::makeTitle( $ns, $textVariant );
-                                                       if(is_null($variantTitle)) continue;
-                                                       $linkBatch->addObj( $variantTitle );
-                                                       $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
+                                                       if($textVariant != $titleText){
+                                                               $variantTitle = Title::makeTitle( $ns, $textVariant );
+                                                               if(is_null($variantTitle)) continue;
+                                                               $linkBatch->addObj( $variantTitle );
+                                                               $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
+                                                       }
                                                }
                                        }
                                }
 
+                               // process categories, check if a category exists in some variant
+                               foreach( $categories as $category){
+                                       $variants = $wgContLang->convertLinkToAllVariants($category);
+                                       foreach($variants as $variant){
+                                               $variantTitle = Title::newFromDBkey( Title::makeName(NS_CATEGORY,$variant) );
+                                               if(is_null($variantTitle)) continue;
+                                               $linkBatch->addObj( $variantTitle );
+                                               $categoryMap[$variant] = $category;
+                                       }
+                               }
+
 
                                if(!$linkBatch->isEmpty()){
                                        // construct query
@@ -4054,10 +4105,13 @@ class Parser
 
                                                $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title );
                                                $varPdbk = $variantTitle->getPrefixedDBkey();
+                                               $vardbk = $variantTitle->getDBkey();
                                                $linkCache->addGoodLinkObj( $s->page_id, $variantTitle );
                                                $this->mOutput->addLink( $variantTitle, $s->page_id );
 
-                                               $holderKeys = $variantMap[$varPdbk];
+                                               $holderKeys = array();
+                                               if(isset($variantMap[$varPdbk]))
+                                                       $holderKeys = $variantMap[$varPdbk];
 
                                                // loop over link holders
                                                foreach($holderKeys as $key){
@@ -4086,13 +4140,33 @@ class Parser
                                                                }
                                                        }
                                                }
+
+                                               // check if the object is a variant of a category
+                                               if(isset($categoryMap[$vardbk])){
+                                                       $oldkey = $categoryMap[$vardbk];
+                                                       if($oldkey != $vardbk)
+                                                               $varCategories[$oldkey]=$vardbk;                                                        
+                                               }                                               
+                                       }
+
+                                       // rebuild the categories in original order (if there are replacements)
+                                       if(count($varCategories)>0){
+                                               $newCats = array();
+                                               $originalCats = $this->mOutput->getCategories();
+                                               foreach($originalCats as $cat => $sortkey){
+                                                       // make the replacement
+                                                       if( array_key_exists($cat,$varCategories) )
+                                                               $newCats[$varCategories[$cat]] = $sortkey;
+                                                       else $newCats[$cat] = $sortkey;
+                                               }
+                                               $this->mOutput->setCategoryLinks($newCats);
                                        }
                                }
                        }
 
                        # Construct search and replace arrays
                        wfProfileIn( $fname.'-construct' );
-                       $wgOutputReplace = array();
+                       $replacePairs = array();
                        foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) {
                                $pdbk = $pdbks[$key];
                                $searchkey = "<!--LINK $key-->";
@@ -4101,27 +4175,27 @@ class Parser
                                        $linkCache->addBadLinkObj( $title );
                                        $colours[$pdbk] = 0;
                                        $this->mOutput->addLink( $title, 0 );
-                                       $wgOutputReplace[$searchkey] = $sk->makeBrokenLinkObj( $title,
+                                       $replacePairs[$searchkey] = $sk->makeBrokenLinkObj( $title,
                                                                        $this->mLinkHolders['texts'][$key],
                                                                        $this->mLinkHolders['queries'][$key] );
                                } elseif ( $colours[$pdbk] == 1 ) {
-                                       $wgOutputReplace[$searchkey] = $sk->makeKnownLinkObj( $title,
+                                       $replacePairs[$searchkey] = $sk->makeKnownLinkObj( $title,
                                                                        $this->mLinkHolders['texts'][$key],
                                                                        $this->mLinkHolders['queries'][$key] );
                                } elseif ( $colours[$pdbk] == 2 ) {
-                                       $wgOutputReplace[$searchkey] = $sk->makeStubLinkObj( $title,
+                                       $replacePairs[$searchkey] = $sk->makeStubLinkObj( $title,
                                                                        $this->mLinkHolders['texts'][$key],
                                                                        $this->mLinkHolders['queries'][$key] );
                                }
                        }
+                       $replacer = new HashtableReplacer( $replacePairs, 1 );
                        wfProfileOut( $fname.'-construct' );
 
                        # Do the thing
                        wfProfileIn( $fname.'-replace' );
-
                        $text = preg_replace_callback(
                                '/(<!--LINK .*?-->)/',
-                               "wfOutputReplaceMatches",
+                               $replacer->cb(),
                                $text);
 
                        wfProfileOut( $fname.'-replace' );
@@ -4132,15 +4206,16 @@ class Parser
                if ( !empty( $this->mInterwikiLinkHolders['texts'] ) ) {
                        wfProfileIn( $fname.'-interwiki' );
                        # Make interwiki link HTML
-                       $wgOutputReplace = array();
+                       $replacePairs = array();
                        foreach( $this->mInterwikiLinkHolders['texts'] as $key => $link ) {
                                $title = $this->mInterwikiLinkHolders['titles'][$key];
-                               $wgOutputReplace[$key] = $sk->makeLinkObj( $title, $link );
+                               $replacePairs[$key] = $sk->makeLinkObj( $title, $link );
                        }
+                       $replacer = new HashtableReplacer( $replacePairs, 1 );
 
                        $text = preg_replace_callback(
                                '/<!--IWLINK (.*?)-->/',
-                               "wfOutputReplaceMatches",
+                               $replacer->cb(),
                                $text );
                        wfProfileOut( $fname.'-interwiki' );
                }
@@ -4193,11 +4268,11 @@ class Parser
         */
        function renderPreTag( $text, $attribs ) {
                // Backwards-compatibility hack
-               $content = preg_replace( '!<nowiki>(.*?)</nowiki>!is', '\\1', $text );
+               $content = StringUtils::delimiterReplace( '<nowiki>', '</nowiki>', '$1', $text, 'i' );
 
                $attribs = Sanitizer::validateTagAttributes( $attribs, 'pre' );
                return wfOpenElement( 'pre', $attribs ) .
-                       wfEscapeHTMLTagsOnly( $content ) .
+                       Xml::escapeTagsOnly( $content ) .
                        '</pre>';
        }
 
@@ -4340,7 +4415,7 @@ class Parser
                # make sure there are no placeholders in thumbnail attributes
                # that are later expanded to html- so expand them now and
                # remove the tags
-               $alt = $this->unstrip($alt, $this->mStripState);
+               $alt = $this->mStripState->unstripBoth( $alt );
                $alt = Sanitizer::stripAllTags( $alt );
 
                # Linker does the rest
@@ -4367,15 +4442,10 @@ class Parser
         */
        function attributeStripCallback( &$text, $args ) {
                $text = $this->replaceVariables( $text, $args );
-               $text = $this->unstripForHTML( $text );
+               $text = $this->mStripState->unstripBoth( $text );
                return $text;
        }
 
-       function unstripForHTML( $text ) {
-               $text = $this->unstrip( $text, $this->mStripState );
-               $text = $this->unstripNoWiki( $text, $this->mStripState );
-               return $text;
-       }
        /**#@-*/
 
        /**#@+
@@ -4411,14 +4481,14 @@ class Parser
        private function extractSections( $text, $section, $mode, $newtext='' ) {
                # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
                # comments to be stripped as well)
-               $striparray = array();
+               $stripState = new StripState;
 
                $oldOutputType = $this->mOutputType;
                $oldOptions = $this->mOptions;
                $this->mOptions = new ParserOptions();
                $this->setOutputType( OT_WIKI );
 
-               $striptext = $this->strip( $text, $striparray, true );
+               $striptext = $this->strip( $text, $stripState, true );
 
                $this->setOutputType( $oldOutputType );
                $this->mOptions = $oldOptions;
@@ -4525,9 +4595,7 @@ class Parser
                        }
                }
                # reinsert stripped tags
-               $rv = $this->unstrip( $rv, $striparray );
-               $rv = $this->unstripNoWiki( $rv, $striparray );
-               $rv = trim( $rv );
+               $rv = trim( $stripState->unstripBoth( $rv ) );
                return $rv;
        }
 
@@ -4550,6 +4618,36 @@ class Parser
                return $this->extractSections( $oldtext, $section, "replace", $text );
        }
 
+       /**
+        * Get the timestamp associated with the current revision, adjusted for 
+        * the default server-local timestamp
+        */
+       function getRevisionTimestamp() {
+               if ( is_null( $this->mRevisionTimestamp ) ) {
+                       wfProfileIn( __METHOD__ );
+                       global $wgContLang;
+                       $dbr =& wfGetDB( DB_SLAVE );
+                       $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
+                                       array( 'rev_id' => $this->mRevisionId ), __METHOD__ );
+                       
+                       // Normalize timestamp to internal MW format for timezone processing.
+                       // This has the added side-effect of replacing a null value with
+                       // the current time, which gives us more sensible behavior for
+                       // previews.
+                       $timestamp = wfTimestamp( TS_MW, $timestamp );
+                       
+                       // The cryptic '' timezone parameter tells to use the site-default
+                       // timezone offset instead of the user settings.
+                       //
+                       // Since this value will be saved into the parser cache, served
+                       // to other users, and potentially even used inside links and such,
+                       // it needs to be consistent for all visitors.
+                       $this->mRevisionTimestamp = $wgContLang->userAdjust( $timestamp, '' );
+                       
+                       wfProfileOut( __METHOD__ );
+               }
+               return $this->mRevisionTimestamp;
+       }
 }
 
 /**
@@ -4784,152 +4882,47 @@ class ParserOptions
        }
 }
 
-/**
- * Callback function used by Parser::replaceLinkHolders()
- * to substitute link placeholders.
- */
-function &wfOutputReplaceMatches( $matches ) {
-       global $wgOutputReplace;
-       return $wgOutputReplace[$matches[1]];
-}
+class OnlyIncludeReplacer {
+       var $output = '';
 
-/**
- * Return the total number of articles
- */
-function wfNumberOfArticles() {
-       global $wgNumberOfArticles;
-
-       wfLoadSiteStats();
-       return $wgNumberOfArticles;
-}
-
-/**
- * Return the number of files
- */
-function wfNumberOfFiles() {
-       $fname = 'wfNumberOfFiles';
-
-       wfProfileIn( $fname );
-       $dbr =& wfGetDB( DB_SLAVE );
-       $numImages = $dbr->selectField('site_stats', 'ss_images', array(), $fname );
-       wfProfileOut( $fname );
-
-       return $numImages;
-}
-
-/**
- * Return the number of user accounts
- * @return integer
- */
-function wfNumberOfUsers() {
-       wfProfileIn( 'wfNumberOfUsers' );
-       $dbr =& wfGetDB( DB_SLAVE );
-       $count = $dbr->selectField( 'site_stats', 'ss_users', array(), 'wfNumberOfUsers' );
-       wfProfileOut( 'wfNumberOfUsers' );
-       return (int)$count;
+       function replace( $matches ) { 
+               if ( substr( $matches[1], -1 ) == "\n" ) {
+                       $this->output .= substr( $matches[1], 0, -1 );
+               } else {
+                       $this->output .= $matches[1];
+               }
+       }
 }
 
-/**
- * Return the total number of pages
- * @return integer
- */
-function wfNumberOfPages() {
-       wfProfileIn( 'wfNumberOfPages' );
-       $dbr =& wfGetDB( DB_SLAVE );
-       $count = $dbr->selectField( 'site_stats', 'ss_total_pages', array(), 'wfNumberOfPages' );
-       wfProfileOut( 'wfNumberOfPages' );
-       return (int)$count;
-}
+class StripState {
+       var $general, $nowiki;
 
-/**
- * Return the total number of admins
- *
- * @return integer
- */
-function wfNumberOfAdmins() {
-       static $admins = -1;
-       wfProfileIn( 'wfNumberOfAdmins' );
-       if( $admins == -1 ) {
-               $dbr =& wfGetDB( DB_SLAVE );
-               $admins = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), 'wfNumberOfAdmins' );
+       function __construct() {
+               $this->general = new ReplacementArray;
+               $this->nowiki = new ReplacementArray;
        }
-       wfProfileOut( 'wfNumberOfAdmins' );
-       return (int)$admins;
-}
 
-/**
- * Count the number of pages in a particular namespace
- *
- * @param $ns Namespace
- * @return integer
- */
-function wfPagesInNs( $ns ) {
-       static $pageCount = array();
-       wfProfileIn( 'wfPagesInNs' );
-       if( !isset( $pageCount[$ns] ) ) {
-               $dbr =& wfGetDB( DB_SLAVE );
-               $pageCount[$ns] = $dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $ns ), 'wfPagesInNs' );
+       function unstripGeneral( $text ) {
+               wfProfileIn( __METHOD__ );
+               $text = $this->general->replace( $text );
+               wfProfileOut( __METHOD__ );
+               return $text;
        }
-       wfProfileOut( 'wfPagesInNs' );
-       return (int)$pageCount[$ns];
-}
 
-/**
- * Get various statistics from the database
- * @private
- */
-function wfLoadSiteStats() {
-       global $wgNumberOfArticles, $wgTotalViews, $wgTotalEdits;
-       $fname = 'wfLoadSiteStats';
-
-       if ( -1 != $wgNumberOfArticles ) return;
-       $dbr =& wfGetDB( DB_SLAVE );
-       $s = $dbr->selectRow( 'site_stats',
-               array( 'ss_total_views', 'ss_total_edits', 'ss_good_articles' ),
-               array( 'ss_row_id' => 1 ), $fname
-       );
-
-       if ( $s === false ) {
-               return;
-       } else {
-               $wgTotalViews = $s->ss_total_views;
-               $wgTotalEdits = $s->ss_total_edits;
-               $wgNumberOfArticles = $s->ss_good_articles;
+       function unstripNoWiki( $text ) {
+               wfProfileIn( __METHOD__ );
+               $text = $this->nowiki->replace( $text );
+               wfProfileOut( __METHOD__ );
+               return $text;
        }
-}
 
-/**
- * Get revision timestamp from the database considering timecorrection
- *
- * @param $id Int: page revision id
- * @return integer
- */
-function wfRevisionTimestamp( $id ) {
-       global $wgContLang;
-       $fname = 'wfRevisionTimestamp';
-
-       wfProfileIn( $fname );
-       $dbr =& wfGetDB( DB_SLAVE );
-       $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
-                       array( 'rev_id' => $id ), __METHOD__ );
-       $timestamp = $wgContLang->userAdjust( $timestamp );
-       wfProfileOut( $fname );
-
-       return $timestamp;
-}
-
-/**
- * Escape html tags
- * Basically replacing " > and < with HTML entities ( &quot;, &gt;, &lt;)
- *
- * @param $in String: text that might contain HTML tags.
- * @return string Escaped string
- */
-function wfEscapeHTMLTagsOnly( $in ) {
-       return str_replace(
-               array( '"', '>', '<' ),
-               array( '&quot;', '&gt;', '&lt;' ),
-               $in );
+       function unstripBoth( $text ) {
+               wfProfileIn( __METHOD__ );
+               $text = $this->general->replace( $text );
+               $text = $this->nowiki->replace( $text );
+               wfProfileOut( __METHOD__ );
+               return $text;
+       }
 }
 
 ?>