* Add form to RCLinked and add to sp:specialpages
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
index 1c6fd35..51d0749 100644 (file)
@@ -87,6 +87,29 @@ if ( !function_exists( 'array_diff_key' ) ) {
        }
 }
 
+/**
+ * Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
+ */
+function wfArrayDiff2( $a, $b ) {
+       return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
+}
+function wfArrayDiff2_cmp( $a, $b ) {
+       if ( !is_array( $a ) ) {
+               return strcmp( $a, $b );
+       } elseif ( count( $a ) !== count( $b ) ) {
+               return count( $a ) < count( $b ) ? -1 : 1;
+       } else {
+               reset( $a );
+               reset( $b );
+               while( ( list( $keyA, $valueA ) = each( $a ) ) && ( list( $keyB, $valueB ) = each( $b ) ) ) {
+                       $cmp = strcmp( $valueA, $valueB );
+                       if ( $cmp !== 0 ) {
+                               return $cmp;
+                       }
+               }
+               return 0;
+       }
+}
 
 /**
  * Wrapper for clone(), for compatibility with PHP4-friendly extensions.
@@ -232,29 +255,30 @@ function wfErrorLog( $text, $file ) {
  */
 function wfLogProfilingData() {
        global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
-       global $wgProfiling, $wgUser;
-       if ( $wgProfiling ) {
-               $now = wfTime();
-               $elapsed = $now - $wgRequestTime;
-               $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
-               $forward = '';
-               if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
-                       $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
-               if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) )
-                       $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
-               if( !empty( $_SERVER['HTTP_FROM'] ) )
-                       $forward .= ' from ' . $_SERVER['HTTP_FROM'];
-               if( $forward )
-                       $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
-               // Don't unstub $wgUser at this late stage just for statistics purposes
-               if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() )
-                       $forward .= ' anon';
-               $log = sprintf( "%s\t%04.3f\t%s\n",
-                 gmdate( 'YmdHis' ), $elapsed,
-                 urldecode( $wgRequest->getRequestURL() . $forward ) );
-               if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
-                       wfErrorLog( $log . $prof, $wgDebugLogFile );
-               }
+       global $wgProfiler, $wgUser;
+       if ( !isset( $wgProfiler ) )
+               return;
+
+       $now = wfTime();
+       $elapsed = $now - $wgRequestTime;
+       $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
+       $forward = '';
+       if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
+               $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
+       if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) )
+               $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
+       if( !empty( $_SERVER['HTTP_FROM'] ) )
+               $forward .= ' from ' . $_SERVER['HTTP_FROM'];
+       if( $forward )
+               $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
+       // Don't unstub $wgUser at this late stage just for statistics purposes
+       if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() )
+               $forward .= ' anon';
+       $log = sprintf( "%s\t%04.3f\t%s\n",
+         gmdate( 'YmdHis' ), $elapsed,
+         urldecode( $wgRequest->getRequestURL() . $forward ) );
+       if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
+               wfErrorLog( $log . $prof, $wgDebugLogFile );
        }
 }
 
@@ -282,6 +306,11 @@ function wfReadOnly() {
        return (bool)$wgReadOnly;
 }
 
+function wfReadOnlyReason() {
+       global $wgReadOnly;
+       wfReadOnly();
+       return $wgReadOnly;
+}
 
 /**
  * Get a message from anywhere, for the current user language.
@@ -289,11 +318,6 @@ function wfReadOnly() {
  * Use wfMsgForContent() instead if the message should NOT
  * change depending on the user preferences.
  *
- * Note that the message may contain HTML, and is therefore
- * not safe for insertion anywhere. Some functions such as
- * addWikiText will do the escaping for you. Use wfMsgHtml()
- * if you need an escaped message.
- *
  * @param $key String: lookup key for the message, usually
  *    defined in languages/Language.php
  * 
@@ -470,15 +494,13 @@ function wfMsgReplaceArgs( $message, $args ) {
        // Replace arguments
        if ( count( $args ) ) {
                if ( is_array( $args[0] ) ) {
-                       foreach ( $args[0] as $key => $val ) {
-                               $message = str_replace( '$' . $key, $val, $message );
-                       }
-               } else {
-                       foreach( $args as $n => $param ) {
-                               $replacementKeys['$' . ($n + 1)] = $param;
-                       }
-                       $message = strtr( $message, $replacementKeys );
+                       $args = array_values( $args[0] );
                }
+               $replacementKeys = array();
+               foreach( $args as $n => $param ) {
+                       $replacementKeys['$' . ($n + 1)] = $param;
+               }
+               $message = strtr( $message, $replacementKeys );
        }
 
        return $message;
@@ -592,7 +614,6 @@ function wfMsgExt( $key, $options ) {
  * @deprecated Please return control to the caller or throw an exception
  */
 function wfAbruptExit( $error = false ){
-       global $wgLoadBalancer;
        static $called = false;
        if ( $called ){
                exit( -1 );
@@ -613,7 +634,7 @@ function wfAbruptExit( $error = false ){
        wfLogProfilingData();
 
        if ( !$error ) {
-               $wgLoadBalancer->closeAll();
+               wfGetLB()->closeAll();
        }
        exit( -1 );
 }
@@ -980,6 +1001,21 @@ function wfAppendQuery( $url, $query ) {
        return $url;
 }
 
+/**
+ * Expand a potentially local URL to a fully-qualified URL.
+ * Assumes $wgServer is correct. :)
+ * @param string $url, either fully-qualified or a local path + query
+ * @return string Fully-qualified URL
+ */
+function wfExpandUrl( $url ) {
+       if( substr( $url, 0, 1 ) == '/' ) {
+               global $wgServer;
+               return $wgServer . $url;
+       } else {
+               return $url;
+       }
+}
+
 /**
  * This is obsolete, use SquidUpdate::purge()
  * @deprecated
@@ -1826,10 +1862,12 @@ function wfShellExec( $cmd, &$retval=null ) {
        }
        wfDebug( "wfShellExec: $cmd\n" );
        
-       $output = array();
        $retval = 1; // error by default?
-       exec( $cmd, $output, $retval ); // returns the last line of output.
-       return implode( "\n", $output );
+       ob_start();
+       passthru( $cmd, $retval );
+       $output = ob_get_contents();
+       ob_end_clean();
+       return $output;
        
 }
 
@@ -1920,8 +1958,18 @@ function wfRelativePath( $path, $from ) {
        $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
        $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
        
+       // Trim trailing slashes -- fix for drive root
+       $path = rtrim( $path, DIRECTORY_SEPARATOR );
+       $from = rtrim( $from, DIRECTORY_SEPARATOR );
+       
        $pieces  = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
        $against = explode( DIRECTORY_SEPARATOR, $from );
+       
+       if( $pieces[0] !== $against[0] ) {
+               // Non-matching Windows drive letters?
+               // Return a full path.
+               return $path;
+       }
 
        // Trim off common prefix
        while( count( $pieces ) && count( $against )
@@ -1941,6 +1989,28 @@ function wfRelativePath( $path, $from ) {
        return implode( DIRECTORY_SEPARATOR, $pieces );
 }
 
+/**
+ * array_merge() does awful things with "numeric" indexes, including
+ * string indexes when happen to look like integers. When we want
+ * to merge arrays with arbitrary string indexes, we don't want our
+ * arrays to be randomly corrupted just because some of them consist
+ * of numbers.
+ *
+ * Fuck you, PHP. Fuck you in the ear!
+ *
+ * @param array $array1, [$array2, [...]]
+ * @return array
+ */
+function wfArrayMerge( $array1/* ... */ ) {
+       $out = $array1;
+       for( $i = 1; $i < func_num_args(); $i++ ) {
+               foreach( func_get_arg( $i ) as $key => $value ) {
+                       $out[$key] = $value;
+               }
+       }
+       return $out;
+}
+
 /**
  * Make a URL index, appropriate for the el_index field of externallinks.
  */
@@ -2164,7 +2234,9 @@ function wfSetupSession() {
        }
        session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
        session_cache_limiter( 'private, must-revalidate' );
-       @session_start();
+       wfSuppressWarnings();
+       session_start();
+       wfRestoreWarnings();
 }
 
 /**
@@ -2247,6 +2319,17 @@ function wfWikiID() {
        }
 }
 
+/**
+ * Split a wiki ID into DB name and table prefix 
+ */
+function wfSplitWikiID( $wiki ) {
+       $bits = explode( '-', $wiki, 2 );
+       if ( count( $bits ) < 2 ) {
+               $bits[] = '';
+       }
+       return $bits;
+}
+
 /*
  * Get a Database object
  * @param integer $db Index of the connection to get. May be DB_MASTER for the 
@@ -2256,11 +2339,29 @@ function wfWikiID() {
  * @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.
+ *
+ * @param string $wiki The wiki ID, or false for the current wiki
  */
-function &wfGetDB( $db = DB_LAST, $groups = array() ) {
-       global $wgLoadBalancer;
-       $ret = $wgLoadBalancer->getConnection( $db, true, $groups );
-       return $ret;
+function &wfGetDB( $db = DB_LAST, $groups = array(), $wiki = false ) {
+       return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
+}
+
+/**
+ * Get a load balancer object.
+ *
+ * @param array $groups List of query groups
+ * @param string $wiki Wiki ID, or false for the current wiki
+ * @return LoadBalancer
+ */
+function wfGetLB( $wiki = false ) {
+       return wfGetLBFactory()->getMainLB( $wiki );
+}
+
+/**
+ * Get the load balancer factory object
+ */
+function &wfGetLBFactory() {
+       return LBFactory::singleton();
 }
 
 /**
@@ -2269,7 +2370,7 @@ function &wfGetDB( $db = DB_LAST, $groups = array() ) {
  * @param mixed $title Title object or string. May be interwiki.
  * @param mixed $time Requested time for an archived image, or false for the 
  *                    current version. An image object will be returned which 
- *                    existed at the specified time.
+ *                    was created at the specified time.
  * @return File, or false if the file does not exist
  */
 function wfFindFile( $title, $time = false ) {
@@ -2363,3 +2464,50 @@ function wfMaxlagError( $host, $lag, $maxLag ) {
                echo "Waiting for a database server: $lag seconds lagged\n";
        }
 }
+
+/**
+ * Throws an E_USER_NOTICE saying that $function is deprecated
+ * @param string $function
+ * @return null
+ */
+function wfDeprecated( $function ) {
+       trigger_error( "Use of $function is deprecated", E_USER_NOTICE );
+}
+
+/**
+ * Sleep until the worst slave's replication lag is less than or equal to
+ * $maxLag, in seconds.  Use this when updating very large numbers of rows, as
+ * in maintenance scripts, to avoid causing too much lag.  Of course, this is
+ * a no-op if there are no slaves.
+ *
+ * Every time the function has to wait for a slave, it will print a message to
+ * that effect (and then sleep for a little while), so it's probably not best
+ * to use this outside maintenance scripts in its present form.
+ *
+ * @param int $maxLag
+ * @return null
+ */
+function wfWaitForSlaves( $maxLag ) {
+       if( $maxLag ) {
+               $lb = wfGetLB();
+               list( $host, $lag ) = $lb->getMaxLag();
+               while( $lag > $maxLag ) {
+                       $name = @gethostbyaddr( $host );
+                       if( $name !== false ) {
+                               $host = $name;
+                       }
+                       print "Waiting for $host (lagged $lag seconds)...\n";
+                       sleep($maxLag);
+                       list( $host, $lag ) = $lb->getMaxLag();
+               }
+       }
+}
+
+/** Generate a random 32-character hexadecimal token.
+ * @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing.
+ */
+ function wfGenerateToken( $salt = '' ) {
+       $salt = serialize($salt);
+       
+       return md5( mt_rand( 0, 0x7fffffff ) . $salt );
+ }
\ No newline at end of file