Use eval "$4" instead of "$@", so that shell metacharacters such as "(" are understood
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
index 238c77e..1ffde74 100644 (file)
@@ -1,34 +1,30 @@
 <?php
 
+if ( !defined( 'MEDIAWIKI' ) ) {
+       die( "This file is part of MediaWiki, it is not a valid entry point" );
+}
+
 /**
  * Global functions used everywhere
- * @package MediaWiki
  */
 
 /**
  * Some globals and requires needed
  */
 
-/**
- * Total number of articles
- * @global integer $wgNumberOfArticles
- */
+/** Total number of articles */
 $wgNumberOfArticles = -1; # Unset
-/**
- * Total number of views
- * @global integer $wgTotalViews
- */
+
+/** Total number of views */
 $wgTotalViews = -1;
-/**
- * Total number of edits
- * @global integer $wgTotalEdits
- */
+
+/** Total number of edits */
 $wgTotalEdits = -1;
 
 
-require_once( 'LogPage.php' );
-require_once( 'normal/UtfNormalUtil.php' );
-require_once( 'XmlFunctions.php' );
+require_once dirname(__FILE__) . '/LogPage.php';
+require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php';
+require_once dirname(__FILE__) . '/XmlFunctions.php';
 
 /**
  * Compatibility functions
@@ -64,6 +60,30 @@ if ( !function_exists( 'mb_substr' ) ) {
        }
 }
 
+if ( !function_exists( 'mb_strlen' ) ) {
+       /**
+        * Fallback implementation of mb_strlen, hardcoded to UTF-8.
+        * @param string $str
+        * @param string $enc optional encoding; ignored
+        * @return int
+        */
+       function mb_strlen( $str, $enc="" ) {
+               $counts = count_chars( $str );
+               $total = 0;
+
+               // Count ASCII bytes
+               for( $i = 0; $i < 0x80; $i++ ) {
+                       $total += $counts[$i];
+               }
+
+               // Count multibyte sequence heads
+               for( $i = 0xc0; $i < 0xff; $i++ ) {
+                       $total += $counts[$i];
+               }
+               return $total;
+       }
+}
+
 if ( !function_exists( 'array_diff_key' ) ) {
        /**
         * Exists in PHP 5.1.0+
@@ -114,7 +134,7 @@ function wfSeedRandom() {
 function wfRandom() {
        # The maximum random value is "only" 2^31-1, so get two random
        # values to reduce the chance of dupes
-       $max = mt_getrandmax();
+       $max = mt_getrandmax() + 1;
        $rand = number_format( (mt_rand() * $max + mt_rand())
                / $max / $max, 12, '.', '' );
        return $rand;
@@ -175,7 +195,7 @@ function wfDebug( $text, $logonly = false ) {
                # Strip unprintables; they can switch terminal modes when binary data
                # gets dumped, which is pretty annoying.
                $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
-               @error_log( $text, 3, $wgDebugLogFile );
+               wfErrorLog( $text, $wgDebugLogFile );
        }
 }
 
@@ -194,7 +214,7 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
        if( isset( $wgDebugLogGroups[$logGroup] ) ) {
                $time = wfTimestamp( TS_DB );
                $wiki = wfWikiID();
-               @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] );
+               wfErrorLog( "$time $wiki: $text", $wgDebugLogGroups[$logGroup] );
        } else if ( $public === true ) {
                wfDebug( $text, true );
        }
@@ -205,14 +225,27 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
  * @param $text String: database error message.
  */
 function wfLogDBError( $text ) {
-       global $wgDBerrorLog;
+       global $wgDBerrorLog, $wgDBname;
        if ( $wgDBerrorLog ) {
                $host = trim(`hostname`);
-               $text = date('D M j G:i:s T Y') . "\t$host\t".$text;
-               error_log( $text, 3, $wgDBerrorLog );
+               $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text";
+               wfErrorLog( $text, $wgDBerrorLog );
        }
 }
 
+/**
+ * Log to a file without getting "file size exceeded" signals
+ */
+function wfErrorLog( $text, $file ) {
+       wfSuppressWarnings();
+       $exists = file_exists( $file );
+       $size = $exists ? filesize( $file ) : false;
+       if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
+               error_log( $text, 3, $file );
+       }
+       wfRestoreWarnings();
+}
+
 /**
  * @todo document
  */
@@ -237,9 +270,9 @@ function wfLogProfilingData() {
                        $forward .= ' anon';
                $log = sprintf( "%s\t%04.3f\t%s\n",
                  gmdate( 'YmdHis' ), $elapsed,
-                 urldecode( $_SERVER['REQUEST_URI'] . $forward ) );
+                 urldecode( $wgRequest->getRequestURL() . $forward ) );
                if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
-                       error_log( $log . $prof, 3, $wgDebugLogFile );
+                       wfErrorLog( $log . $prof, $wgDebugLogFile );
                }
        }
 }
@@ -299,7 +332,7 @@ function wfMsg( $key ) {
 function wfMsgNoTrans( $key ) {
        $args = func_get_args();
        array_shift( $args );
-       return wfMsgReal( $key, $args, true, false );
+       return wfMsgReal( $key, $args, true, false, false );
 }
 
 /**
@@ -383,8 +416,11 @@ function wfMsgNoDBForContent( $key ) {
  * @return String: the requested message.
  */
 function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) {
+       $fname = 'wfMsgReal';
+       wfProfileIn( $fname );
        $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
        $message = wfMsgReplaceArgs( $message, $args );
+       wfProfileOut( $fname );
        return $message;
 }
 
@@ -522,11 +558,11 @@ function wfMsgWikiHtml( $key ) {
  * Returns message in the requested format
  * @param string $key Key of the message
  * @param array $options Processing rules:
- *  <i>parse<i>: parses wikitext to html
- *  <i>parseinline<i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
- *  <i>escape<i>: filters message trough htmlspecialchars
- *  <i>replaceafter<i>: parameters are substituted after parsing or escaping
- *  <i>parsemag<i>: ??
+ *  <i>parse</i>: parses wikitext to html
+ *  <i>parseinline</i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
+ *  <i>escape</i>: filters message trough htmlspecialchars
+ *  <i>replaceafter</i>: parameters are substituted after parsing or escaping
+ *  <i>parsemag</i>: transform the message using magic phrases
  */
 function wfMsgExt( $key, $options ) {
        global $wgOut, $wgParser;
@@ -554,12 +590,10 @@ function wfMsgExt( $key, $options ) {
                        $string = $m[1];
                }
        } elseif ( in_array('parsemag', $options) ) {
-               global $wgTitle;
-               $parser = new Parser();
-               $parserOptions = new ParserOptions();
-               $parserOptions->setInterfaceMessage( true );
-               $parser->startExternalParse( $wgTitle, $parserOptions, OT_MSG );
-               $string = $parser->transformMsg( $string, $parserOptions );
+               global $wgMessageCache;
+               if ( isset( $wgMessageCache ) ) {
+                       $string = $wgMessageCache->transform( $string );
+               }
        }
 
        if ( in_array('escape', $options) ) {
@@ -578,7 +612,7 @@ function wfMsgExt( $key, $options ) {
  * Just like exit() but makes a note of it.
  * Commits open transactions except if the error parameter is set
  *
- * @obsolete Please return control to the caller or throw an exception
+ * @deprecated Please return control to the caller or throw an exception
  */
 function wfAbruptExit( $error = false ){
        global $wgLoadBalancer;
@@ -588,8 +622,8 @@ function wfAbruptExit( $error = false ){
        }
        $called = true;
 
-       if( function_exists( 'debug_backtrace' ) ){ // PHP >= 4.3
-               $bt = debug_backtrace();
+       $bt = wfDebugBacktrace();
+       if( $bt ) {
                for($i = 0; $i < count($bt) ; $i++){
                        $file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown";
                        $line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown";
@@ -608,7 +642,7 @@ function wfAbruptExit( $error = false ){
 }
 
 /**
- * @obsolete Please return control the caller or throw an exception
+ * @deprecated Please return control the caller or throw an exception
  */
 function wfErrorExit() {
        wfAbruptExit( true );
@@ -671,18 +705,36 @@ function wfHostname() {
                return $com;
        }
 
+/**
+ * Safety wrapper for debug_backtrace().
+ *
+ * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat
+ * murky circumstances, which may be triggered in part by stub objects
+ * or other fancy talkin'.
+ *
+ * Will return an empty array if Zend Optimizer is detected, otherwise
+ * the output from debug_backtrace() (trimmed).
+ *
+ * @return array of backtrace information
+ */
+function wfDebugBacktrace() {
+       if( extension_loaded( 'Zend Optimizer' ) ) {
+               wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
+               return array();
+       } else {
+               return array_slice( debug_backtrace(), 1 );
+       }
+}
+
 function wfBacktrace() {
        global $wgCommandLineMode;
-       if ( !function_exists( 'debug_backtrace' ) ) {
-               return false;
-       }
 
        if ( $wgCommandLineMode ) {
                $msg = '';
        } else {
                $msg = "<ul>\n";
        }
-       $backtrace = debug_backtrace();
+       $backtrace = wfDebugBacktrace();
        foreach( $backtrace as $call ) {
                if( isset( $call['file'] ) ) {
                        $f = explode( DIRECTORY_SEPARATOR, $call['file'] );
@@ -727,7 +779,7 @@ function wfBacktrace() {
  */
 function wfShowingResults( $offset, $limit ) {
        global $wgLang;
-       return wfMsg( 'showingresults', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
+       return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
 }
 
 /**
@@ -735,7 +787,7 @@ function wfShowingResults( $offset, $limit ) {
  */
 function wfShowingResultsNum( $offset, $limit, $num ) {
        global $wgLang;
-       return wfMsg( 'showingresultsnum', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
+       return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
 }
 
 /**
@@ -931,6 +983,26 @@ function wfArrayToCGI( $array1, $array2 = NULL )
        return $cgi;
 }
 
+/**
+ * Append a query string to an existing URL, which may or may not already
+ * have query string parameters already. If so, they will be combined.
+ *
+ * @param string $url
+ * @param string $query
+ * @return string
+ */
+function wfAppendQuery( $url, $query ) {
+       if( $query != '' ) {
+               if( false === strpos( $url, '?' ) ) {
+                       $url .= '?';
+               } else {
+                       $url .= '&';
+               }
+               $url .= $query;
+       }
+       return $url;
+}
+
 /**
  * This is obsolete, use SquidUpdate::purge()
  * @deprecated
@@ -1069,16 +1141,81 @@ function wfHttpError( $code, $label, $desc ) {
        header( "Status: $code $label" );
        $wgOut->sendCacheControl();
 
-       header( 'Content-type: text/html' );
-       print "<html><head><title>" .
+       header( 'Content-type: text/html; charset=utf-8' );
+       print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">".
+               "<html><head><title>" .
                htmlspecialchars( $label ) .
                "</title></head><body><h1>" .
                htmlspecialchars( $label ) .
                "</h1><p>" .
-               htmlspecialchars( $desc ) .
+               nl2br( htmlspecialchars( $desc ) ) .
                "</p></body></html>\n";
 }
 
+/**
+ * Clear away any user-level output buffers, discarding contents.
+ *
+ * Suitable for 'starting afresh', for instance when streaming
+ * relatively large amounts of data without buffering, or wanting to
+ * output image files without ob_gzhandler's compression.
+ *
+ * The optional $resetGzipEncoding parameter controls suppression of
+ * the Content-Encoding header sent by ob_gzhandler; by default it
+ * is left. See comments for wfClearOutputBuffers() for why it would
+ * be used.
+ *
+ * Note that some PHP configuration options may add output buffer
+ * layers which cannot be removed; these are left in place.
+ *
+ * @param bool $resetGzipEncoding
+ */
+function wfResetOutputBuffers( $resetGzipEncoding=true ) {
+       if( $resetGzipEncoding ) {
+               // Suppress Content-Encoding and Content-Length
+               // headers from 1.10+s wfOutputHandler
+               global $wgDisableOutputCompression;
+               $wgDisableOutputCompression = true;
+       }
+       while( $status = ob_get_status() ) {
+               if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
+                       // Probably from zlib.output_compression or other
+                       // PHP-internal setting which can't be removed.
+                       //
+                       // Give up, and hope the result doesn't break
+                       // output behavior.
+                       break;
+               }
+               if( !ob_end_clean() ) {
+                       // Could not remove output buffer handler; abort now
+                       // to avoid getting in some kind of infinite loop.
+                       break;
+               }
+               if( $resetGzipEncoding ) {
+                       if( $status['name'] == 'ob_gzhandler' ) {
+                               // Reset the 'Content-Encoding' field set by this handler
+                               // so we can start fresh.
+                               header( 'Content-Encoding:' );
+                       }
+               }
+       }
+}
+
+/**
+ * More legible than passing a 'false' parameter to wfResetOutputBuffers():
+ *
+ * Clear away output buffers, but keep the Content-Encoding header
+ * produced by ob_gzhandler, if any.
+ *
+ * This should be used for HTTP 304 responses, where you need to
+ * preserve the Content-Encoding header of the real result, but
+ * also need to suppress the output of ob_gzhandler to keep to spec
+ * and avoid breaking Firefox in rare cases where the headers and
+ * body are broken over two packets.
+ */
+function wfClearOutputBuffers() {
+       wfResetOutputBuffers( false );
+}
+
 /**
  * Converts an Accept-* header into an array mapping string values to quality
  * factors
@@ -1264,7 +1401,7 @@ define('TS_ISO_8601', 4);
 /**
  * An Exif timestamp (YYYY:MM:DD HH:MM:SS)
  *
- * @url http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
  *       DateTime tag and page 36 for the DateTimeOriginal and
  *       DateTimeDigitized tags.
  */
@@ -1395,10 +1532,21 @@ function wfGetCachedNotice( $name ) {
        wfProfileIn( $fname );
        
        $needParse = false;
-       $notice = wfMsgForContent( $name );
-       if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
-               wfProfileOut( $fname );
-               return( false );
+       
+       if( $name === 'default' ) {
+               // special case
+               global $wgSiteNotice;
+               $notice = $wgSiteNotice;
+               if( empty( $notice ) ) {
+                       wfProfileOut( $fname );
+                       return false;
+               }
+       } else {
+               $notice = wfMsgForContentNoTrans( $name );
+               if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
+                       wfProfileOut( $fname );
+                       return( false );
+               }
        }
        
        $cachedNotice = $parserMemc->get( wfMemcKey( $name ) );
@@ -1458,16 +1606,17 @@ function wfGetSiteNotice() {
        if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) {
                if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) {
                        $siteNotice = wfGetCachedNotice( 'sitenotice' );
-                       $siteNotice = !$siteNotice ? $wgSiteNotice : $siteNotice;
                } else {
                        $anonNotice = wfGetCachedNotice( 'anonnotice' );
                        if( !$anonNotice ) {
                                $siteNotice = wfGetCachedNotice( 'sitenotice' );
-                               $siteNotice = !$siteNotice ? $wgSiteNotice : $siteNotice;
                        } else {
                                $siteNotice = $anonNotice;
                        }
                }
+               if( !$siteNotice ) {
+                       $siteNotice = wfGetCachedNotice( 'default' );
+               }
        }
 
        wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) );
@@ -1544,6 +1693,7 @@ function wfMkdirParents( $fullDir, $mode = 0777 ) {
        foreach ( $createList as $dir ) {
                # use chmod to override the umask, as suggested by the PHP manual
                if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
+                       wfDebugLog( 'mkdir', "Unable to create directory $dir\n" );
                        return false;
                } 
        }
@@ -1670,14 +1820,14 @@ function wfShellExec( $cmd, &$retval=null ) {
        }
 
        if ( php_uname( 's' ) == 'Linux' ) {
-               $time = ini_get( 'max_execution_time' );
+               $time = intval( ini_get( 'max_execution_time' ) );
                $mem = intval( $wgMaxShellMemory );
                $filesize = intval( $wgMaxShellFileSize );
 
                if ( $time > 0 && $mem > 0 ) {
-                       $script = "$IP/bin/ulimit-tvf.sh";
+                       $script = "$IP/bin/ulimit4.sh";
                        if ( is_executable( $script ) ) {
-                               $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd";
+                               $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd );
                        }
                }
        } elseif ( php_uname( 's' ) == 'Windows NT' ) {
@@ -1763,24 +1913,84 @@ function wfBaseName( $path ) {
        }
 }
 
+/**
+ * Generate a relative path name to the given file.
+ * May explode on non-matching case-insensitive paths,
+ * funky symlinks, etc.
+ *
+ * @param string $path Absolute destination path including target filename
+ * @param string $from Absolute source path, directory only
+ * @return string
+ */
+function wfRelativePath( $path, $from ) {
+       // Normalize mixed input on Windows...
+       $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
+       $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
+       
+       $pieces  = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
+       $against = explode( DIRECTORY_SEPARATOR, $from );
+
+       // Trim off common prefix
+       while( count( $pieces ) && count( $against )
+               && $pieces[0] == $against[0] ) {
+               array_shift( $pieces );
+               array_shift( $against );
+       }
+
+       // relative dots to bump us to the parent
+       while( count( $against ) ) {
+               array_unshift( $pieces, '..' );
+               array_shift( $against );
+       }
+
+       array_push( $pieces, wfBaseName( $path ) );
+
+       return implode( DIRECTORY_SEPARATOR, $pieces );
+}
+
 /**
  * Make a URL index, appropriate for the el_index field of externallinks.
  */
 function wfMakeUrlIndex( $url ) {
-       wfSuppressWarnings();
+       global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
        $bits = parse_url( $url );
+       wfSuppressWarnings();
        wfRestoreWarnings();
-       if ( !$bits || $bits['scheme'] !== 'http' ) {
+       if ( !$bits ) {
+               return false;
+       }
+       // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
+       $delimiter = '';
+       if ( in_array( $bits['scheme'] . '://' , $wgUrlProtocols ) ) {
+               $delimiter = '://';
+       } elseif ( in_array( $bits['scheme'] .':' , $wgUrlProtocols ) ) {
+               $delimiter = ':';
+               // parse_url detects for news: and mailto: the host part of an url as path
+               // We have to correct this wrong detection
+               if ( isset ( $bits['path'] ) ) { 
+                       $bits['host'] = $bits['path'];
+                       $bits['path'] = '';
+               }
+       } else {
                return false;
        }
+
        // Reverse the labels in the hostname, convert to lower case
-       $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+       // For emails reverse domainpart only
+       if ( $bits['scheme'] == 'mailto' ) {
+               $mailparts = explode( '@', $bits['host'] );
+               $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
+               $reversedHost = $domainpart . '@' . $mailparts[0];
+       } else {
+               $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+       }
        // Add an extra dot to the end
        if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
                $reversedHost .= '.';
        }
        // Reconstruct the pseudo-URL
-       $index = "http://$reversedHost";
+       $prot = $bits['scheme'];
+       $index = "$prot$delimiter$reversedHost";
        // Leave out user and password. Add the port, path, query and fragment
        if ( isset( $bits['port'] ) )      $index .= ':' . $bits['port'];
        if ( isset( $bits['path'] ) ) {
@@ -1828,9 +2038,11 @@ function wfExplodeMarkup( $separator, $text ) {
  * @param $sourceBase int 2-36
  * @param $destBase int 2-36
  * @param $pad int 1 or greater
+ * @param $lowercase bool
  * @return string or false on invalid input
  */
-function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
+function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) {
+       $input = strval( $input );
        if( $sourceBase < 2 ||
                $sourceBase > 36 ||
                $destBase < 2 ||
@@ -1843,8 +2055,7 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
                $input == '' ) {
                return false;
        }
-       
-       $digitChars = '0123456789abcdefghijklmnopqrstuvwxyz';
+       $digitChars = ( $lowercase ) ?  '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $inDigits = array();
        $outChars = '';
        
@@ -1944,7 +2155,7 @@ function wfIsLocalURL( $url ) {
  * Initialise php session
  */
 function wfSetupSession() {
-       global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
+       global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure;
        if( $wgSessionsInMemcached ) {
                require_once( 'MemcachedSessions.php' );
        } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
@@ -1952,7 +2163,7 @@ function wfSetupSession() {
                # application, it will end up failing. Try to recover.
                ini_set ( 'session.save_handler', 'files' );
        }
-       session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
+       session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
        session_cache_limiter( 'private, must-revalidate' );
        @session_start();
 }
@@ -1976,7 +2187,7 @@ function wfGetPrecompiledData( $name ) {
 }
 
 function wfGetCaller( $level = 2 ) {
-       $backtrace = debug_backtrace();
+       $backtrace = wfDebugBacktrace();
        if ( isset( $backtrace[$level] ) ) {
                if ( isset( $backtrace[$level]['class'] ) ) {
                        $caller = $backtrace[$level]['class'] . '::' . $backtrace[$level]['function'];
@@ -1997,7 +2208,7 @@ function wfGetAllCallers() {
                                $frame["class"]."::".$frame["function"]:
                                $frame["function"]; 
                        '),
-               array_reverse(debug_backtrace())));
+               array_reverse(wfDebugBacktrace())));
 }
 
 /**
@@ -2046,12 +2257,13 @@ function wfWikiID() {
  *                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.
+ * @param mixed $groups Query groups. An array of group names that this query 
+ *              belongs to. May contain a single string if the query is only 
+ *              in one group.
  */
 function &wfGetDB( $db = DB_LAST, $groups = array() ) {
        global $wgLoadBalancer;
-       $ret =& $wgLoadBalancer->getConnection( $db, true, $groups );
+       $ret = $wgLoadBalancer->getConnection( $db, true, $groups );
        return $ret;
 }
 ?>