* Introduced StringUtils.php, populated it with some generic string functions, both...
authorTim Starling <tstarling@users.mediawiki.org>
Tue, 21 Nov 2006 09:53:45 +0000 (09:53 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Tue, 21 Nov 2006 09:53:45 +0000 (09:53 +0000)
* Removed some backtracking regexes with an O(N^2) worst case, replaced with StringUtils::delimiterReplace(). There is a beneficial functional difference: /*/ is no longer considered to be a complete CSS comment.
* Changed the parser strip state from an array to an object. This should hopefully avoid the PHP bugs with array references. StripState uses the new ReplacementArray to do the replacements, thereby supporting FSS.
* Removed DatabaseFunctions.php from the default startup sequence. Moved wfGetDB() to GlobalFunctions.php.
* Introduced the SiteStats class, with a collection of cached site stats accessor functions.
* Removed all global functions from Parser.php, they don't belong there.
* Made LanguageConverter use the new ReplacementArray class instead of managing its own FSS objects.

22 files changed:
includes/AutoLoader.php
includes/BagOStuff.php
includes/Block.php
includes/CoreParserFunctions.php
includes/Database.php
includes/DatabaseFunctions.php
includes/GlobalFunctions.php
includes/Image.php
includes/Linker.php
includes/MagicWord.php
includes/Parser.php
includes/Sanitizer.php
includes/SiteStats.php [new file with mode: 0644]
includes/SiteStatsUpdate.php [deleted file]
includes/SpecialStatistics.php
includes/SpecialWatchlist.php
includes/Xml.php
languages/LanguageConverter.php
languages/classes/LanguageKk.php
languages/classes/LanguageSr.php
languages/classes/LanguageZh.php
maintenance/namespace2sql.php

index 15fe3e7..f826ec1 100644 (file)
@@ -86,7 +86,6 @@ function __autoload($className) {
                'FileStore' => 'includes/FileStore.php',
                'FSException' => 'includes/FileStore.php',
                'FSTransaction' => 'includes/FileStore.php',
-               'ReplacerCallback' => 'includes/GlobalFunctions.php',
                'HTMLForm' => 'includes/HTMLForm.php',
                'HistoryBlob' => 'includes/HistoryBlob.php',
                'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
@@ -150,7 +149,8 @@ function __autoload($className) {
                'SearchUpdate' => 'includes/SearchUpdate.php',
                'SearchUpdateMyISAM' => 'includes/SearchUpdate.php',
                'SiteConfiguration' => 'includes/SiteConfiguration.php',
-               'SiteStatsUpdate' => 'includes/SiteStatsUpdate.php',
+               'SiteStats' => 'includes/SiteStats.php',
+               'SiteStatsUpdate' => 'includes/SiteStats.php',
                'Skin' => 'includes/Skin.php',
                'MediaWiki_I18N' => 'includes/SkinTemplate.php',
                'SkinTemplate' => 'includes/SkinTemplate.php',
@@ -215,6 +215,12 @@ function __autoload($className) {
                'WantedPagesPage' => 'includes/SpecialWantedpages.php',
                'WhatLinksHerePage' => 'includes/SpecialWhatlinkshere.php',
                'SquidUpdate' => 'includes/SquidUpdate.php',
+               'ReplacementArray' => 'includes/StringUtils.php',
+               'Replacer' => 'includes/StringUtils.php',
+               'RegexlikeReplacer' => 'includes/StringUtils.php',
+               'DoubleReplacer' => 'includes/StringUtils.php',
+               'HashtableReplacer' => 'includes/StringUtils.php',
+               'StringUtils' => 'includes/StringUtils.php',
                'Title' => 'includes/Title.php',
                'User' => 'includes/User.php',
                'MailAddress' => 'includes/UserMailer.php',
index 1dc93a2..7595126 100644 (file)
@@ -492,11 +492,14 @@ class TurckBagOStuff extends BagOStuff {
 class APCBagOStuff extends BagOStuff {
        function get($key) {
                $val = apc_fetch($key);
+               if ( is_string( $val ) ) {
+                       $val = unserialize( $val );
+               }
                return $val;
        }
        
        function set($key, $value, $exptime=0) {
-               apc_store($key, $value, $exptime);
+               apc_store($key, serialize($value), $exptime);
                return true;
        }
        
index 39ebe04..547bf53 100644 (file)
@@ -338,7 +338,7 @@ class Block
                                call_user_func( $callback, $block, $tag );
                        }
                }
-               wfFreeResult( $res );
+               $db->freeResult( $res );
                return $num_rows;
        }
 
index b2ee789..c0cdba9 100644 (file)
@@ -126,21 +126,21 @@ class CoreParserFunctions {
 
        function statisticsFunction( $func, $raw = null ) {
                if ( self::isRaw( $raw ) ) {
-                       return call_user_func( $func );
+                       return call_user_func( array( 'SiteStats', $func ) );
                } else {
                        global $wgContLang;
-                       return $wgContLang->formatNum( call_user_func( $func ) );
+                       return $wgContLang->formatNum( call_user_func( array( 'SiteStats', $func ) ) );
                }
        }
 
-       function numberofpages( $parser, $raw = null ) { return self::statisticsFunction( 'wfNumberOfPages', $raw ); }
-       function numberofusers( $parser, $raw = null ) { return self::statisticsFunction( 'wfNumberOfUsers', $raw ); }
-       function numberofarticles( $parser, $raw = null ) { return self::statisticsFunction( 'wfNumberOfArticles', $raw ); }
-       function numberoffiles( $parser, $raw = null ) { return self::statisticsFunction( 'wfNumberOfFiles', $raw ); }
-       function numberofadmins( $parser, $raw = null ) { return self::statisticsFunction( 'wfNumberOfAdmins', $raw ); }
+       function numberofpages( $parser, $raw = null ) { return self::statisticsFunction( 'pages', $raw ); }
+       function numberofusers( $parser, $raw = null ) { return self::statisticsFunction( 'users', $raw ); }
+       function numberofarticles( $parser, $raw = null ) { return self::statisticsFunction( 'articles', $raw ); }
+       function numberoffiles( $parser, $raw = null ) { return self::statisticsFunction( 'images', $raw ); }
+       function numberofadmins( $parser, $raw = null ) { return self::statisticsFunction( 'admins', $raw ); }
 
        function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
-               $count = wfPagesInNs( intval( $namespace ) );
+               $count = SiteStats::pagesInNs( intval( $namespace ) );
                if ( self::isRaw( $raw ) ) {
                        global $wgContLang;
                        return $wgContLang->formatNum( $count );
index 7b6df37..d8f7757 100644 (file)
@@ -295,7 +295,7 @@ class Database {
         * Turns on (false) or off (true) the automatic generation and sending
         * of a "we're sorry, but there has been a database error" page on
         * database errors. Default is on (false). When turned off, the
-        * code should use wfLastErrno() and wfLastError() to handle the
+        * code should use lastErrno() and lastError() to handle the
         * situation as appropriate.
         */
        function ignoreErrors( $ignoreErrors = NULL ) {
@@ -1296,7 +1296,7 @@ class Database {
        }
 
        /**
-        * Makes a wfStrencoded list from an array
+        * Makes an encoded list of strings from an array
         * $mode:
         *        LIST_COMMA         - comma separated, no field names
         *        LIST_AND           - ANDed WHERE clause (without the WHERE)
index 74b35a3..c9dc9e7 100644 (file)
@@ -1,8 +1,7 @@
 <?php
 /**
- * Backwards compatibility wrapper for Database.php
- *
- * Note: $wgDatabase has ceased to exist. Destroy all references.
+ * Legacy database functions, for compatibility with pre-1.3 code
+ * NOTE: this file is no longer loaded by default.
  *
  * @package MediaWiki
  */
@@ -44,15 +43,6 @@ function wfSingleQuery( $sql, $dbi, $fname = '' ) {
        return $ret;
 }
 
-/*
- * @todo document function
- */
-function &wfGetDB( $db = DB_LAST, $groups = array() ) {
-       global $wgLoadBalancer;
-       $ret =& $wgLoadBalancer->getConnection( $db, true, $groups );
-       return $ret;
-}
-
 /**
  * Turns on (false) or off (true) the automatic generation and sending
  * of a "we're sorry, but there has been a database error" page on
index d6ee0d7..6632735 100644 (file)
@@ -26,7 +26,6 @@ $wgTotalViews = -1;
 $wgTotalEdits = -1;
 
 
-require_once( 'DatabaseFunctions.php' );
 require_once( 'LogPage.php' );
 require_once( 'normal/UtfNormalUtil.php' );
 require_once( 'XmlFunctions.php' );
@@ -1736,16 +1735,10 @@ function wfUseMW( $req_ver ) {
 }
 
 /**
- * Escape a string to make it suitable for inclusion in a preg_replace()
- * replacement parameter.
- *
- * @param string $string
- * @return string
+ * @deprecated use StringUtils::escapeRegexReplacement
  */
 function wfRegexReplacement( $string ) {
-       $string = str_replace( '\\', '\\\\', $string );
-       $string = str_replace( '$', '\\$', $string );
-       return $string;
+       return StringUtils::escapeRegexReplacement( $string );
 }
 
 /**
@@ -1815,42 +1808,12 @@ function wfDoUpdates()
 }
 
 /**
- * More or less "markup-safe" explode()
- * Ignores any instances of the separator inside <...>
- * @param string $separator
- * @param string $text
- * @return array
+ * @deprecated use StringUtils::explodeMarkup
  */
 function wfExplodeMarkup( $separator, $text ) {
-       $placeholder = "\x00";
-       
-       // Just in case...
-       $text = str_replace( $placeholder, '', $text );
-       
-       // Trim stuff
-       $replacer = new ReplacerCallback( $separator, $placeholder );
-       $cleaned = preg_replace_callback( '/(<.*?>)/', array( $replacer, 'go' ), $text );
-       
-       $items = explode( $separator, $cleaned );
-       foreach( $items as $i => $str ) {
-               $items[$i] = str_replace( $placeholder, $separator, $str );
-       }
-       
-       return $items;
+       return StringUtils::explodeMarkup( $separator, $text );
 }
 
-class ReplacerCallback {
-       function ReplacerCallback( $from, $to ) {
-               $this->from = $from;
-               $this->to = $to;
-       }
-       
-       function go( $matches ) {
-               return str_replace( $this->from, $this->to, $matches[1] );
-       }
-}
-
-
 /**
  * Convert an arbitrarily-long digit string from one numeric base
  * to another, optionally zero-padding to a minimum column width.
@@ -2074,4 +2037,20 @@ function wfWikiID() {
        }
 }
 
+/*
+ * Get a Database object
+ * @param integer $db Index of the connection to get. May be DB_MASTER for the 
+ *                master (for write queries), DB_SLAVE for potentially lagged 
+ *                read queries, or an integer >= 0 for a particular server.
+ *
+ * @param array $groups Query groups. A list of group names that this query 
+ *              belongs to.
+ */
+function &wfGetDB( $db = DB_LAST, $groups = array() ) {
+       global $wgLoadBalancer;
+       $ret =& $wgLoadBalancer->getConnection( $db, true, $groups );
+       return $ret;
+}
+
+
 ?>
index b2a6396..c03466b 100644 (file)
@@ -1462,7 +1462,7 @@ class Image
                                array( 'img_name' => $this->title->getDBkey() ),
                                __METHOD__
                        );
-                       if ( 0 == wfNumRows( $this->historyRes ) ) {
+                       if ( 0 == $dbr->numRows( $this->historyRes ) ) {
                                return FALSE;
                        }
                } else if ( $this->historyLine == 1 ) {
index 2a50547..f4e52ea 100644 (file)
@@ -959,7 +959,7 @@ class Linker {
                                        $match[1] = substr($match[1], 1);
                                $thelink = $this->makeLink( $match[1], $text, "", $trail );
                        }
-                       $comment = preg_replace( $linkRegexp, wfRegexReplacement( $thelink ), $comment, 1 );
+                       $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 );
                }
                wfProfileOut( __METHOD__ );
                return $comment;
index 68cbe34..350fb8e 100644 (file)
@@ -298,7 +298,7 @@ class MagicWord {
         * Replaces the word with something else
         */
        function replace( $replacement, $subject, $limit=-1 ) {
-               $res = preg_replace( $this->getRegex(), wfRegexReplacement( $replacement ), $subject, $limit );
+               $res = preg_replace( $this->getRegex(), StringUtils::escapeRegexReplacement( $replacement ), $subject, $limit );
                $this->mModified = !($res === $subject);
                return $res;
        }
index e5550dc..73ac43c 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  
 
        /**#@-*/
 
@@ -174,7 +176,6 @@ class Parser
                }
 
                $this->initialiseVariables();
-
                $this->mFirstCall = false;
                wfProfileOut( __METHOD__ );
        }
@@ -194,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(
@@ -208,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.
@@ -286,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(
@@ -324,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 ) );
 
@@ -374,6 +370,7 @@ class Parser
                }
                $this->mOutput->setText( $text );
                $this->mRevisionId = $oldRevisionId;
+               $this->mRevisionTimestamp = $oldRevisionTimestamp;
                wfProfileOut( $fname );
                wfProfileOut( __METHOD__ );
 
@@ -405,16 +402,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;
        }
@@ -521,7 +516,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
@@ -533,12 +529,12 @@ class Parser
         *
         * @private
         */
-       function strip( $text, &$state, $stripcomments = false , $dontstrip = array () ) {
+       function strip( $text, $state, $stripcomments = false , $dontstrip = array () ) {
                wfProfileIn( __METHOD__ );
                $render = ($this->mOutputType == OT_HTML);
 
                $uniq_prefix = $this->mUniqPrefix;
-               $commentState = array();
+               $commentState = new ReplacementArray;
 
                $elements = array_merge(
                        array( 'nowiki', 'gallery' ),
@@ -583,7 +579,7 @@ class Parser
                                        }
                                        // Shouldn't happen otherwise. :)
                                case 'nowiki':
-                                       $output = wfEscapeHTMLTagsOnly( $content );
+                                       $output = Xml::escapeTagsOnly( $content );
                                        break;
                                case 'math':
                                        $output = MathRenderer::renderMath( $content );
@@ -607,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 );
                        }
                }
 
@@ -624,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__ );
@@ -636,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 );
        }
 
        /**
@@ -676,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;
        }
 
@@ -815,7 +800,7 @@ class Parser
                        if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) {
                                $indent_level = strlen( $matches[1] );
 
-                               $attributes = $this->unstripForHTML( $matches[2] );
+                               $attributes = $this->mStripState->unstripBoth( $matches[2] );
 
                                $t[$k] = str_repeat( '<dl><dd>', $indent_level ) .
                                        '<table' . Sanitizer::fixTagAttributes ( $attributes, 'table' ) . '>' ;
@@ -849,7 +834,7 @@ class Parser
                                array_push ( $tr , false ) ;
                                array_push ( $td , false ) ;
                                array_push ( $ltd , '' ) ;
-                               $attributes = $this->unstripForHTML( $x );
+                               $attributes = $this->mStripState->unstripBoth( $x );
                                array_push ( $ltr , Sanitizer::fixTagAttributes ( $attributes, 'tr' ) ) ;
                        }
                        else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption
@@ -865,7 +850,7 @@ class Parser
                                // 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 );
+                               $after = StringUtils::explodeMarkup( '||', $after );
 
                                $t[$k] = '' ;
 
@@ -906,7 +891,7 @@ class Parser
                                        if ( count ( $y ) == 1 )
                                                $y = "{$z}<{$l}>{$y[0]}" ;
                                        else {
-                                               $attributes = $this->unstripForHTML( $y[0] );
+                                               $attributes = $this->mStripState->unstripBoth( $y[0] );
                                                $y = "{$z}<{$l}".Sanitizer::fixTagAttributes($attributes, $l).">{$y[1]}" ;
                                        }
                                        $t[$k] .= $y ;
@@ -955,7 +940,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' ) );
 
@@ -1611,7 +1596,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" );
@@ -2434,15 +2419,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':
@@ -2484,15 +2469,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':
@@ -3079,14 +3064,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'] ) {
@@ -3481,8 +3465,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-->
@@ -3604,15 +3587,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;
        }
 
@@ -3915,7 +3897,6 @@ class Parser
         */
        function replaceLinkHolders( &$text, $options = 0 ) {
                global $wgUser;
-               global $wgOutputReplace;
                global $wgContLang;
 
                $fname = 'Parser::replaceLinkHolders';
@@ -4095,7 +4076,7 @@ class Parser
 
                        # 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-->";
@@ -4104,27 +4085,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' );
@@ -4135,15 +4116,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' );
                }
@@ -4196,11 +4178,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>';
        }
 
@@ -4343,7 +4325,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
@@ -4370,15 +4352,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;
-       }
        /**#@-*/
 
        /**#@+
@@ -4414,14 +4391,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;
@@ -4528,9 +4505,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;
        }
 
@@ -4553,6 +4528,23 @@ class Parser
                return $this->extractSections( $oldtext, $section, "replace", $text );
        }
 
+       /**
+        * Get the timestamp associated with the current revision, adjusted for 
+        * the user's current 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' => $id ), __METHOD__ );
+                       $this->mRevisionTimestamp = $wgContLang->userAdjust( $timestamp );
+               
+                       wfProfileOut( __METHOD__ );
+               }
+               return $this->mRevisionTimestamp;
+       }
 }
 
 /**
@@ -4787,152 +4779,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;
+       }
 }
 
 ?>
index 7d9ccc0..4ed2692 100644 (file)
@@ -603,7 +603,8 @@ class Sanitizer {
                $stripped = Sanitizer::decodeCharReferences( $value );
 
                // Remove any comments; IE gets token splitting wrong
-               $stripped = preg_replace( '!/\\*.*?\\*/!S', ' ', $stripped );
+               $stripped = StringUtils::delimiterReplace( '/\*', '\*/', ' ', $stripped );
+               
                $value = $stripped;
 
                // ... and continue checks
@@ -1178,7 +1179,7 @@ class Sanitizer {
         */
        static function stripAllTags( $text ) {
                # Actual <tags>
-               $text = preg_replace( '/ < .*? > /x', '', $text );
+               $text = StringUtils::delimiterReplace( '<', '>', '', $text );
 
                # Normalize &entities and whitespace
                $text = Sanitizer::normalizeAttributeValue( $text );
diff --git a/includes/SiteStats.php b/includes/SiteStats.php
new file mode 100644 (file)
index 0000000..4480ab5
--- /dev/null
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Static accessor class for site_stats and related things
+ * @package MediaWiki
+ */
+class SiteStats {
+       static $row, $loaded = false;
+       static $admins;
+       static $pageCount = array();
+
+       static function recache() {
+               self::load( true );
+       }
+
+       static function load( $recache = false ) {
+               if ( self::$loaded && !$recache ) {
+                       return;
+               }
+
+               $dbr =& wfGetDB( DB_SLAVE );
+               self::$row = $dbr->selectRow( 'site_stats', '*', false, __METHOD__ );
+
+               # This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS
+               if ( !isset( self::$row->ss_total_pages ) && self::$row->ss_total_pages == -1 ) {
+                       # Update schema
+                       $u = new SiteStatsUpdate( 0, 0, 0 );
+                       $u->doUpdate();
+                       self::$row = $dbr->selectRow( 'site_stats', '*', false, $fname );
+               }
+       }
+
+       static function views() {
+               self::load();
+               return self::$row->ss_total_views;
+       }
+
+       static function edits() {
+               self::load();
+               return self::$row->ss_total_edits;
+       }
+
+       static function articles() {
+               self::load();
+               return self::$row->ss_good_articles;
+       }
+
+       static function pages() {
+               self::load();
+               return self::$row->ss_total_pages;
+       }
+
+       static function users() {
+               self::load();
+               return self::$row->ss_users;
+       }
+       
+       static function images() {
+               self::load();
+               return self::$row->ss_images;
+       }
+
+       static function admins() {
+               if ( !isset( self::$admins ) ) {
+                       $dbr =& wfGetDB( DB_SLAVE );
+                       self::$admins = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
+               }
+               return self::$admins;
+       }
+
+       static function pagesInNs( $ns ) {
+               wfProfileIn( __METHOD__ );
+               if( !isset( self::$pageCount[$ns] ) ) {
+                       $dbr =& wfGetDB( DB_SLAVE );
+                       $pageCount[$ns] = (int)$dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $ns ), __METHOD__ );
+               }
+               wfProfileOut( __METHOD__ );
+               return $pageCount[$ns];
+       }
+
+}
+
+
+/**
+ *
+ * @package MediaWiki
+ */
+class SiteStatsUpdate {
+
+       var $mViews, $mEdits, $mGood, $mPages, $mUsers;
+
+       function SiteStatsUpdate( $views, $edits, $good, $pages = 0, $users = 0 ) {
+               $this->mViews = $views;
+               $this->mEdits = $edits;
+               $this->mGood = $good;
+               $this->mPages = $pages;
+               $this->mUsers = $users;
+       }
+
+       function appendUpdate( &$sql, $field, $delta ) {
+               if ( $delta ) {
+                       if ( $sql ) {
+                               $sql .= ',';
+                       }
+                       if ( $delta < 0 ) {
+                               $sql .= "$field=$field-1";
+                       } else {
+                               $sql .= "$field=$field+1";
+                       }
+               }
+       }
+
+       function doUpdate() {
+               $fname = 'SiteStatsUpdate::doUpdate';
+               $dbw =& wfGetDB( DB_MASTER );
+
+               # First retrieve the row just to find out which schema we're in
+               $row = $dbw->selectRow( 'site_stats', '*', false, $fname );
+
+               $updates = '';
+
+               $this->appendUpdate( $updates, 'ss_total_views', $this->mViews );
+               $this->appendUpdate( $updates, 'ss_total_edits', $this->mEdits );
+               $this->appendUpdate( $updates, 'ss_good_articles', $this->mGood );
+
+               if ( isset( $row->ss_total_pages ) ) {
+                       # Update schema if required
+                       if ( $row->ss_total_pages == -1 && !$this->mViews ) {
+                               $dbr =& wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow') );
+                               extract( $dbr->tableNames( 'page', 'user' ) );
+
+                               $sql = "SELECT COUNT(page_namespace) AS total FROM $page";
+                               $res = $dbr->query( $sql, $fname );
+                               $pageRow = $dbr->fetchObject( $res );
+                               $pages = $pageRow->total + $this->mPages;
+
+                               $sql = "SELECT COUNT(user_id) AS total FROM $user";
+                               $res = $dbr->query( $sql, $fname );
+                               $userRow = $dbr->fetchObject( $res );
+                               $users = $userRow->total + $this->mUsers;
+
+                               if ( $updates ) {
+                                       $updates .= ',';
+                               }
+                               $updates .= "ss_total_pages=$pages, ss_users=$users";
+                       } else {
+                               $this->appendUpdate( $updates, 'ss_total_pages', $this->mPages );
+                               $this->appendUpdate( $updates, 'ss_users', $this->mUsers );
+                       }
+               }
+               if ( $updates ) {
+                       $site_stats = $dbw->tableName( 'site_stats' );
+                       $sql = $dbw->limitResultForUpdate("UPDATE $site_stats SET $updates", 1);
+                       $dbw->begin();
+                       $dbw->query( $sql, $fname );
+                       $dbw->commit();
+               }
+
+               /*
+               global $wgDBname, $wgTitle;
+               if ( $this->mGood && $wgDBname == 'enwiki' ) {
+                       $good = $dbw->selectField( 'site_stats', 'ss_good_articles', '', $fname );
+                       error_log( $good . ' ' . $wgTitle->getPrefixedDBkey() . "\n", 3, '/home/wikipedia/logs/million.log' );
+               }
+               */
+       }
+}
+?>
diff --git a/includes/SiteStatsUpdate.php b/includes/SiteStatsUpdate.php
deleted file mode 100644 (file)
index b91dcfe..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-/**
- * See deferred.txt
- *
- * @package MediaWiki
- */
-
-/**
- *
- * @package MediaWiki
- */
-class SiteStatsUpdate {
-
-       var $mViews, $mEdits, $mGood, $mPages, $mUsers;
-
-       function SiteStatsUpdate( $views, $edits, $good, $pages = 0, $users = 0 ) {
-               $this->mViews = $views;
-               $this->mEdits = $edits;
-               $this->mGood = $good;
-               $this->mPages = $pages;
-               $this->mUsers = $users;
-       }
-
-       function appendUpdate( &$sql, $field, $delta ) {
-               if ( $delta ) {
-                       if ( $sql ) {
-                               $sql .= ',';
-                       }
-                       if ( $delta < 0 ) {
-                               $sql .= "$field=$field-1";
-                       } else {
-                               $sql .= "$field=$field+1";
-                       }
-               }
-       }
-
-       function doUpdate() {
-               $fname = 'SiteStatsUpdate::doUpdate';
-               $dbw =& wfGetDB( DB_MASTER );
-
-               # First retrieve the row just to find out which schema we're in
-               $row = $dbw->selectRow( 'site_stats', '*', false, $fname );
-
-               $updates = '';
-
-               $this->appendUpdate( $updates, 'ss_total_views', $this->mViews );
-               $this->appendUpdate( $updates, 'ss_total_edits', $this->mEdits );
-               $this->appendUpdate( $updates, 'ss_good_articles', $this->mGood );
-
-               if ( isset( $row->ss_total_pages ) ) {
-                       # Update schema if required
-                       if ( $row->ss_total_pages == -1 && !$this->mViews ) {
-                               $dbr =& wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow') );
-                               extract( $dbr->tableNames( 'page', 'user' ) );
-
-                               $sql = "SELECT COUNT(page_namespace) AS total FROM $page";
-                               $res = $dbr->query( $sql, $fname );
-                               $pageRow = $dbr->fetchObject( $res );
-                               $pages = $pageRow->total + $this->mPages;
-
-                               $sql = "SELECT COUNT(user_id) AS total FROM $user";
-                               $res = $dbr->query( $sql, $fname );
-                               $userRow = $dbr->fetchObject( $res );
-                               $users = $userRow->total + $this->mUsers;
-
-                               if ( $updates ) {
-                                       $updates .= ',';
-                               }
-                               $updates .= "ss_total_pages=$pages, ss_users=$users";
-                       } else {
-                               $this->appendUpdate( $updates, 'ss_total_pages', $this->mPages );
-                               $this->appendUpdate( $updates, 'ss_users', $this->mUsers );
-                       }
-               }
-               if ( $updates ) {
-                       $site_stats = $dbw->tableName( 'site_stats' );
-                       $sql = $dbw->limitResultForUpdate("UPDATE $site_stats SET $updates", 1);
-                       $dbw->begin();
-                       $dbw->query( $sql, $fname );
-                       $dbw->commit();
-               }
-
-               /*
-               global $wgDBname, $wgTitle;
-               if ( $this->mGood && $wgDBname == 'enwiki' ) {
-                       $good = $dbw->selectField( 'site_stats', 'ss_good_articles', '', $fname );
-                       error_log( $good . ' ' . $wgTitle->getPrefixedDBkey() . "\n", 3, '/home/wikipedia/logs/million.log' );
-               }
-               */
-       }
-}
-?>
index 4a51efd..66b1424 100644 (file)
@@ -15,39 +15,14 @@ function wfSpecialStatistics() {
        $action = $wgRequest->getVal( 'action' );
 
        $dbr =& wfGetDB( DB_SLAVE );
-       extract( $dbr->tableNames( 'page', 'site_stats', 'user', 'user_groups' ) );
+       extract( $dbr->tableNames( 'site_stats', 'user', 'user_groups' ) );
 
-       $row = $dbr->selectRow( 'site_stats', '*', false, $fname );
-       $views = $row->ss_total_views;
-       $edits = $row->ss_total_edits;
-       $good = $row->ss_good_articles;
-       $images = $row->ss_images;
-
-       # This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS
-       if ( isset( $row->ss_total_pages ) && $row->ss_total_pages == -1 ) {
-               # Update schema
-               $u = new SiteStatsUpdate( 0, 0, 0 );
-               $u->doUpdate();
-               $row = $dbr->selectRow( 'site_stats', '*', false, $fname );
-       }
-
-       if ( isset( $row->ss_total_pages ) ) {
-               $total = $row->ss_total_pages;
-       } else {
-               $sql = "SELECT COUNT(page_namespace) AS total FROM $page";
-               $res = $dbr->query( $sql, $fname );
-               $pageRow = $dbr->fetchObject( $res );
-               $total = $pageRow->total;
-       }
-
-       if ( isset( $row->ss_users ) ) {
-               $users = $row->ss_users;
-       } else {
-               $sql = "SELECT MAX(user_id) AS total FROM $user";
-               $res = $dbr->query( $sql, $fname );
-               $userRow = $dbr->fetchObject( $res );
-               $users = $userRow->total;
-       }
+       $views = SiteStats::views();
+       $edits = SiteStats::edits();
+       $good = SiteStats::articles();
+       $images = SiteStats::images();
+       $total = SiteStats::pages();
+       $users = SiteStats::users();
 
        $admins = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), $fname );
        $numJobs = $dbr->selectField( 'job', 'COUNT(*)', '', $fname );
index 0ce03b8..bc170b3 100644 (file)
@@ -381,8 +381,8 @@ function wfSpecialWatchlist( $par ) {
                }
 
                if ($wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' )) {
-                       $sql3 = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_title='" .wfStrencode($obj->page_title). "' AND wl_namespace='{$obj->page_namespace}'" ;
-                       $res3 = $dbr->query( $sql3, DB_READ, $fname );
+                       $sql3 = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_title='" .$dbr->strencode($obj->page_title). "' AND wl_namespace='{$obj->page_namespace}'" ;
+                       $res3 = $dbr->query( $sql3, $fname );
                        $x = $dbr->fetchObject( $res3 );
                        $rc->numberofWatchingusers = $x->n;
                } else {
index 3457445..6e66fc4 100644 (file)
@@ -297,5 +297,19 @@ class Xml {
                        '</html>';
                return Xml::isWellFormed( $html );
        }
+
+       /**
+        * 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 escapeTagsOnly( $in ) {
+               return str_replace(
+                       array( '"', '>', '<' ),
+                       array( '&quot;', '&gt;', '&lt;' ),
+                       $in );
+       }
 }
 ?>
index a0f7ee1..86e025c 100644 (file)
@@ -12,9 +12,7 @@ class LanguageConverter {
        var $mMainLanguageCode;
        var $mVariants, $mVariantFallbacks;
        var $mTablesLoaded = false;
-       var $mUseFss = false;
        var $mTables;
-       var $mFssObjects;
        var $mTitleDisplay='';
        var $mDoTitleConvert=true, $mDoContentConvert=true;
        var $mCacheKey;
@@ -49,9 +47,6 @@ class LanguageConverter {
                $this->mMarkup = array_merge($m, $markup);
                $f = array('A'=>'A', 'T'=>'T');
                $this->mFlags = array_merge($f, $flags);
-               if ( function_exists( 'fss_prep_replace' ) ) {
-                       $this->mUseFss = true;
-               }
        }
 
        /**
@@ -194,19 +189,12 @@ class LanguageConverter {
         * @return string Translated text
         */
        function translate( $text, $variant ) {
+               wfProfileIn( __METHOD__ );
                if( !$this->mTablesLoaded )
                        $this->loadTables();
-               if ( $this->mUseFss ) {
-                       wfProfileIn( __METHOD__.'-fss' );
-                       $text = fss_exec_replace( $this->mFssObjects[$variant], $text );
-                       wfProfileOut( __METHOD__.'-fss' );
-                       return $text;
-               } else {
-                       wfProfileIn( __METHOD__.'-strtr' );
-                       $text = strtr( $text, $this->mTables[$variant] );
-                       wfProfileOut( __METHOD__.'-strtr' );
-                       return $text;
-               }
+               $text = $this->mTables[$variant]->replace( $text );
+               wfProfileOut( __METHOD__ );
+               return $text;
        }
 
        /**
@@ -407,13 +395,9 @@ class LanguageConverter {
                                                                continue;
                                                        if(!array_key_exists($vto, $carray))
                                                                continue;
-                                                       $this->mTables[$vto][$carray[$vfrom]] = $carray[$vto];
-
+                                                       $this->mTables[$vto]->setPair($carray[$vfrom], $carray[$vto]);
                                                }
                                        }
-                                       if ( $this->mUseFss ) {
-                                               $this->generateFssObjects();
-                                       }
                                }
                        }
                        else {
@@ -557,8 +541,8 @@ class LanguageConverter {
                        $this->mTables = $wgMemc->get( $this->mCacheKey );
                        wfProfileOut( __METHOD__.'-cache' );
                }
-               if ( !$this->mTables ) {
-                       wfProfileOut( __METHOD__.'-recache' );
+               if ( !$this->mTables || !isset( $this->mTables['VERSION 2'] ) ) {
+                       wfProfileIn( __METHOD__.'-recache' );
                        // not in cache, or we need a fresh reload.
                        // we will first load the default tables
                        // then update them using things in MediaWiki:Zhconversiontable/*
@@ -566,10 +550,11 @@ class LanguageConverter {
                        $this->loadDefaultTables();
                        foreach($this->mVariants as $var) {
                                $cached = $this->parseCachedTable($var);
-                               $this->mTables[$var] = array_merge($this->mTables[$var], $cached);
+                               $this->mTables[$var]->mergeArray($cached);
                        }
 
                        $this->postLoadTables();
+                       $this->mTables['VERSION 2'] = true;
 
                        if($this->lockCache()) {
                                $wgMemc->set($this->mCacheKey, $this->mTables, 43200);
@@ -577,23 +562,9 @@ class LanguageConverter {
                        }
                        wfProfileOut( __METHOD__.'-recache' );
                }
-               if ( $this->mUseFss ) {
-                       wfProfileIn( __METHOD__.'-fss' );
-                       $this->generateFssObjects();
-                       wfProfileOut( __METHOD__.'-fss' );
-               }
                wfProfileOut( __METHOD__ );
        }
 
-       /**
-        * Generate FSS objects. The FSS extension must be available.
-        */
-       function generateFssObjects() {
-               foreach ( $this->mTables as $variant => $table ) {
-                       $this->mFssObjects[$variant] = fss_prep_replace( $table );
-               }
-       }
-
     /**
      * Hook for post processig after conversion tables are loaded
      *
index bfbdc54..71ca081 100644 (file)
@@ -89,11 +89,12 @@ class KkConverter extends LanguageConverter {
     );
 
     function loadDefaultTables() {
-        $this->mTables = array();
-        $this->mTables['kk-kz'] = $this->mLatinToCyrillic;
-        $this->mTables['kk-tr'] = $this->mCyrillicToLatin;
-        $this->mTables['kk-cn'] = $this->mCyrillicToArabic;
-        $this->mTables['kk'] = array();
+               $this->mTables = array(
+                       'kk-kz' => new ReplacementArray( $this->mLatinToCyrillic ),
+                       'kk-tr' => new ReplacementArray( $this->mCyrillicToLatin ),
+                       'kk-cn' => new ReplacementArray( $this->mCyrillicToArabic ),
+                       'kk' => new ReplacementArray()
+               );
     }
 
     /*
index 412463f..fadfb35 100644 (file)
@@ -53,12 +53,13 @@ class SrConverter extends LanguageConverter {
        );
 
        function loadDefaultTables() {
-               $this->mTables = array();
-               $this->mTables['sr-ec'] = $this->mToCyrillics;
-               $this->mTables['sr-jc'] = $this->mToCyrillics;
-               $this->mTables['sr-el'] = $this->mToLatin;
-               $this->mTables['sr-jl'] = $this->mToLatin;
-               $this->mTables['sr'] = array();
+               $this->mTables = array(
+                       'sr-ec' => new ReplacementArray( $this->mToCyrillics ),
+                       'sr-jc' => new ReplacementArray( $this->mToCyrillics),
+                       'sr-el' => new ReplacementArray( $this->mToLatin),
+                       'sr-jl' => new ReplacementArray( $this->mToLatin),
+                       'sr'    => new ReplacementArray()
+               );
        }
 
        /* rules should be defined as -{ekavian | iyekavian-} -or-
@@ -139,7 +140,7 @@ class SrConverter extends LanguageConverter {
                $matches = preg_split($reg, $text, -1, PREG_SPLIT_OFFSET_CAPTURE);
                
                $m = array_shift($matches);
-               $ret = strtr($m[0], $this->mTables[$toVariant]);
+               $ret = $this->mTables[$toVariant]->replace( $m[0] );
                $mstart = $m[1]+strlen($m[0]);
                foreach($matches as $m) {
                        $ret .= substr($text, $mstart, $m[1]-$mstart);
@@ -149,8 +150,6 @@ class SrConverter extends LanguageConverter {
 
                return $ret;
        }
-
-
 }
 
 class LanguageSr extends LanguageSr_ec {
index c34315a..4f8a285 100644 (file)
@@ -9,17 +9,18 @@ require_once( dirname(__FILE__).'/LanguageZh_cn.php' );
 class ZhConverter extends LanguageConverter {
        function loadDefaultTables() {
                require( "includes/ZhConversion.php" );
-               $this->mTables = array();
-               $this->mTables['zh-cn'] = $zh2CN;
-               $this->mTables['zh-tw'] = $zh2TW;
-               $this->mTables['zh-sg'] = array_merge($zh2CN, $zh2SG);
-               $this->mTables['zh-hk'] = array_merge($zh2TW, $zh2HK);
-               $this->mTables['zh'] = array();
+               $this->mTables = array(
+                       'zh-cn' => new ReplacementArray( $zh2CN ),
+                       'zh-tw' => new ReplacementArray( $zh2TW ),
+                       'zh-sg' => new ReplacementArray( array_merge($zh2CN, $zh2SG) ),
+                       'zh-hk' => new ReplacementArray( array_merge($zh2TW, $zh2HK) ),
+                       'zh' => new ReplacementArray
+               );
        }
 
        function postLoadTables() {
-               $this->mTables['zh-sg'] = array_merge($this->mTables['zh-cn'], $this->mTables['zh-sg']);
-               $this->mTables['zh-hk'] = array_merge($this->mTables['zh-tw'], $this->mTables['zh-hk']);
+               $this->mTables['zh-sg']->merge( $this->mTables['zh-cn'] );
+               $this->mTables['zh-hk']->merge( $this->mTables['zh-tw'] );
     }
 
        /* there shouldn't be any latin text in Chinese conversion, so no need
index 8084bfe..081f609 100644 (file)
@@ -6,8 +6,8 @@
 require_once( "commandLine.inc" );
 
 for ($i = -2; $i < 16; ++$i) {
-       $nsname = wfStrencode( $wgLang->getNsText( $i ) );
-       $dbname = wfStrencode( $wgDBname );
+       $nsname = mysql_escape_string( $wgLang->getNsText( $i ) );
+       $dbname = mysql_escape_string( $wgDBname );
        print "INSERT INTO ns_name(ns_db, ns_num, ns_name) VALUES('$dbname', $i, '$nsname');\n";
 }