Fix.
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
index bab5d35..3079fba 100644 (file)
@@ -33,9 +33,10 @@ require_once( 'XmlFunctions.php' );
 
 /**
  * Compatibility functions
- * PHP <4.3.x is not actively supported; 4.1.x and 4.2.x might or might not work.
- * <4.1.x will not work, as we use a number of features introduced in 4.1.0
- * such as the new autoglobals.
+ *
+ * We more or less support PHP 5.0.x and up.
+ * Re-implementations of newer functions or functions in non-standard
+ * PHP extensions may be included here.
  */
 if( !function_exists('iconv') ) {
        # iconv support is not in the default configuration and so may not be present.
@@ -49,22 +50,6 @@ if( !function_exists('iconv') ) {
        }
 }
 
-if( !function_exists('file_get_contents') ) {
-       # Exists in PHP 4.3.0+
-       function file_get_contents( $filename ) {
-               return implode( '', file( $filename ) );
-       }
-}
-
-if( !function_exists('is_a') ) {
-       # Exists in PHP 4.2.0+
-       function is_a( $object, $class_name ) {
-               return
-                       (strcasecmp( get_class( $object ), $class_name ) == 0) ||
-                        is_subclass_of( $object, $class_name );
-       }
-}
-
 # UTF-8 substr function based on a PHP manual comment
 if ( !function_exists( 'mb_substr' ) ) {
        function mb_substr( $str, $start ) {
@@ -79,17 +64,6 @@ if ( !function_exists( 'mb_substr' ) ) {
        }
 }
 
-if( !function_exists( 'floatval' ) ) {
-       /**
-        * First defined in PHP 4.2.0
-        * @param mixed $var;
-        * @return float
-        */
-       function floatval( $var ) {
-               return (float)$var;
-       }
-}
-
 if ( !function_exists( 'array_diff_key' ) ) {
        /**
         * Exists in PHP 5.1.0+
@@ -109,39 +83,25 @@ if ( !function_exists( 'array_diff_key' ) ) {
 
 
 /**
- * Wrapper for clone() for PHP 4, for the moment.
+ * Wrapper for clone(), for compatibility with PHP4-friendly extensions.
  * PHP 5 won't let you declare a 'clone' function, even conditionally,
  * so it has to be a wrapper with a different name.
  */
 function wfClone( $object ) {
-       // WARNING: clone() is not a function in PHP 5, so function_exists fails.
-       if( version_compare( PHP_VERSION, '5.0' ) < 0 ) {
-               return $object;
-       } else {
-               return clone( $object );
-       }
+       return clone( $object );
 }
 
 /**
  * Where as we got a random seed
- * @var bool $wgTotalViews
  */
 $wgRandomSeeded = false;
 
 /**
  * Seed Mersenne Twister
- * Only necessary in PHP < 4.2.0
- *
- * @return bool
+ * No-op for compatibility; only necessary in PHP < 4.2.0
  */
 function wfSeedRandom() {
-       global $wgRandomSeeded;
-
-       if ( ! $wgRandomSeeded && version_compare( phpversion(), '4.2.0' ) < 0 ) {
-               $seed = hexdec(substr(md5(microtime()),-8)) & 0x7fffffff;
-               mt_srand( $seed );
-               $wgRandomSeeded = true;
-       }
+       /* No-op */
 }
 
 /**
@@ -190,13 +150,25 @@ function wfUrlencode ( $s ) {
  */
 function wfDebug( $text, $logonly = false ) {
        global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
+       static $recursion = 0;
 
        # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
        if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) {
                return;
        }
 
-       if ( isset( $wgOut ) && $wgDebugComments && !$logonly ) {
+       if ( $wgDebugComments && !$logonly ) {
+               if ( !isset( $wgOut ) ) {
+                       return;
+               }
+               if ( !StubObject::isRealObject( $wgOut ) ) {
+                       if ( $recursion ) {
+                               return;
+                       }
+                       $recursion++;
+                       $wgOut->_unstub();
+                       $recursion--;
+               }
                $wgOut->debug( $text );
        }
        if ( '' != $wgDebugLogFile && !$wgProfileOnly ) {
@@ -243,13 +215,12 @@ function wfLogDBError( $text ) {
 /**
  * @todo document
  */
-function logProfilingData() {
+function wfLogProfilingData() {
        global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
        global $wgProfiling, $wgUser;
-       $now = wfTime();
-
-       $elapsed = $now - $wgRequestTime;
        if ( $wgProfiling ) {
+               $now = wfTime();
+               $elapsed = $now - $wgRequestTime;
                $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
                $forward = '';
                if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
@@ -260,7 +231,8 @@ function logProfilingData() {
                        $forward .= ' from ' . $_SERVER['HTTP_FROM'];
                if( $forward )
                        $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
-               if( is_object($wgUser) && $wgUser->isAnon() )
+               // 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,
@@ -417,13 +389,13 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform =
  */
 function wfMsgWeirdKey ( $key ) {
        $subsource = str_replace ( ' ' , '_' , $key ) ;
-       $source = wfMsg ( $subsource ) ;
-       if ( $source == "&lt;{$subsource}&gt;" ) {
+       $source = wfMsgForContentNoTrans( $subsource ) ;
+       if ( wfEmptyMsg( $subsource, $source) ) {
                # Try again with first char lower case
                $subsource = strtolower ( substr ( $subsource , 0 , 1 ) ) . substr ( $subsource , 1 ) ;
-               $source = wfMsg ( $subsource ) ;
+               $source = wfMsgForContentNoTrans( $subsource ) ;
        }
-       if ( $source == "&lt;{$subsource}&gt;" ) {
+       if ( wfEmptyMsg( $subsource, $source ) ) {
                # Didn't work either, return blank text
                $source = "" ;
        }
@@ -439,7 +411,7 @@ function wfMsgWeirdKey ( $key ) {
  * @private
  */
 function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
-       global $wgParser, $wgMsgParserOptions, $wgContLang, $wgMessageCache, $wgLang;
+       global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
 
        if ( is_object( $wgMessageCache ) )
                $transstat = $wgMessageCache->getTransform();
@@ -466,7 +438,7 @@ function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
                if($message === false)
                        $message = Language::getMessage($key);
                if ( $transform && strstr( $message, '{{' ) !== false ) {
-                       $message = $wgParser->transformMsg($message, $wgMsgParserOptions);
+                       $message = $wgParser->transformMsg($message, $wgMessageCache->getParserOptions() );
                }
        }
 
@@ -621,8 +593,7 @@ function wfAbruptExit( $error = false ){
                wfDebug('WARNING: Abrupt exit\n');
        }
 
-       wfProfileClose();
-       logProfilingData();
+       wfLogProfilingData();
 
        if ( !$error ) {
                $wgLoadBalancer->closeAll();
@@ -867,8 +838,8 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
  */
 function wfEscapeWikiText( $text ) {
        $text = str_replace(
-               array( '[',             '|',      '\'',    'ISBN '        , '://'         , "\n=", '{{' ),
-               array( '&#91;', '&#124;', '&#39;', 'ISBN&#32;', '&#58;//' , "\n&#61;", '&#123;&#123;' ),
+               array( '[',     '|',      '\'',    'ISBN ',     'RFC ',     '://',     "\n=",     '{{' ),
+               array( '&#91;', '&#124;', '&#39;', 'ISBN&#32;', 'RFC&#32;', '&#58;//', "\n&#61;", '&#123;&#123;' ),
                htmlspecialchars($text) );
        return $text;
 }
@@ -1295,6 +1266,11 @@ define('TS_EXIF', 5);
  */
 define('TS_ORACLE', 6);
 
+/**
+ * Postgres format time.
+ */
+define('TS_POSTGRES', 7);
+
 /**
  * @param mixed $outputtype A timestamp in one of the supported formats, the
  *                          function will autodetect which format is supplied
@@ -1306,21 +1282,21 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
        $da = array();
        if ($ts==0) {
                $uts=time();
-       } elseif (preg_match("/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/",$ts,$da)) {
+       } elseif (preg_match("/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D",$ts,$da)) {
                # TS_DB
                $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
                            (int)$da[2],(int)$da[3],(int)$da[1]);
-       } elseif (preg_match("/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/",$ts,$da)) {
+       } elseif (preg_match("/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D",$ts,$da)) {
                # TS_EXIF
                $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
                        (int)$da[2],(int)$da[3],(int)$da[1]);
-       } elseif (preg_match("/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/",$ts,$da)) {
+       } elseif (preg_match("/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D",$ts,$da)) {
                # TS_MW
                $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
                            (int)$da[2],(int)$da[3],(int)$da[1]);
-       } elseif (preg_match("/^(\d{1,13})$/",$ts,$datearray)) {
+       } elseif (preg_match("/^(\d{1,13})$/D",$ts,$datearray)) {
                # TS_UNIX
-               $uts=$ts;
+               $uts = $ts;
        } elseif (preg_match('/^(\d{1,2})-(...)-(\d\d(\d\d)?) (\d\d)\.(\d\d)\.(\d\d)/', $ts, $da)) {
                # TS_ORACLE
                $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
@@ -1329,6 +1305,10 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
                # TS_ISO_8601
                $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
                        (int)$da[2],(int)$da[3],(int)$da[1]);
+       } elseif (preg_match("/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)[\+\- ](\d\d)$/",$ts,$da)) {
+               # TS_POSTGRES
+               $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
+               (int)$da[2],(int)$da[3],(int)$da[1]);
        } else {
                # Bogus value; fall back to the epoch...
                wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n");
@@ -1352,6 +1332,8 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
                        return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT';
                case TS_ORACLE:
                        return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00';
+               case TS_POSTGRES:
+                       return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT';
                default:
                        throw new MWException( 'wfTimestamp() called with illegal output type.');
        }
@@ -1401,7 +1383,7 @@ function wfGetCachedNotice( $name ) {
        
        $needParse = false;
        $notice = wfMsgForContent( $name );
-       if( $notice == '&lt;'. $name . ';&gt' || $notice == '-' ) {
+       if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
                wfProfileOut( $fname );
                return( false );
        }
@@ -1535,18 +1517,46 @@ function wfTempDir() {
 /**
  * Make directory, and make all parent directories if they don't exist
  */
-function wfMkdirParents( $fullDir, $mode ) {
-       $parts = explode( '/', $fullDir );
-       $path = '';
-
-       foreach ( $parts as $dir ) {
-               $path .= $dir . '/';
-               if ( !is_dir( $path ) ) {
-                       if ( !mkdir( $path, $mode ) ) {
-                               return false;
-                       }
+function wfMkdirParents( $fullDir, $mode = 0777 ) {
+       if ( strval( $fullDir ) === '' ) {
+               return true;
+       }
+       
+       # Go back through the paths to find the first directory that exists
+       $currentDir = $fullDir;
+       $createList = array();
+       while ( strval( $currentDir ) !== '' && !file_exists( $currentDir ) ) { 
+               # Strip trailing slashes
+               $currentDir = rtrim( $currentDir, '/\\' );
+
+               # Add to create list
+               $createList[] = $currentDir;
+
+               # Find next delimiter searching from the end
+               $p = max( strrpos( $currentDir, '/' ), strrpos( $currentDir, '\\' ) );
+               if ( $p === false ) {
+                       $currentDir = false;
+               } else {
+                       $currentDir = substr( $currentDir, 0, $p );
                }
        }
+       
+       if ( count( $createList ) == 0 ) {
+               # Directory specified already exists
+               return true;
+       } elseif ( $currentDir === false ) {
+               # Went all the way back to root and it apparently doesn't exist
+               return false;
+       }
+       
+       # Now go forward creating directories
+       $createList = array_reverse( $createList );
+       foreach ( $createList as $dir ) {
+               # use chmod to override the umask, as suggested by the PHP manual
+               if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
+                       return false;
+               } 
+       }
        return true;
 }
 
@@ -1661,7 +1671,7 @@ function wfUrlProtocols() {
  * @return collected stdout as a string (trailing newlines stripped)
  */
 function wfShellExec( $cmd, &$retval=null ) {
-       global $IP;
+       global $IP, $wgMaxShellMemory;
        
        if( ini_get( 'safe_mode' ) ) {
                wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
@@ -1671,15 +1681,12 @@ function wfShellExec( $cmd, &$retval=null ) {
 
        if ( php_uname( 's' ) == 'Linux' ) {
                $time = ini_get( 'max_execution_time' );
-               $mem = ini_get( 'memory_limit' );
-               if( preg_match( '/^([0-9]+)[Mm]$/', trim( $mem ), $m ) ) {
-                       $mem = intval( $m[1] * (1024*1024) );
-               }
+               $mem = intval( $wgMaxShellMemory );
+
                if ( $time > 0 && $mem > 0 ) {
                        $script = "$IP/bin/ulimit.sh";
                        if ( is_executable( $script ) ) {
-                               $memKB = intval( $mem / 1024 );
-                               $cmd = escapeshellarg( $script ) . " $time $memKB $cmd";
+                               $cmd = escapeshellarg( $script ) . " $time $mem $cmd";
                        }
                }
        } elseif ( php_uname( 's' ) == 'Windows NT' ) {
@@ -1853,4 +1860,188 @@ class ReplacerCallback {
        }
 }
 
+
+/**
+ * Convert an arbitrarily-long digit string from one numeric base
+ * to another, optionally zero-padding to a minimum column width.
+ *
+ * Supports base 2 through 36; digit values 10-36 are represented
+ * as lowercase letters a-z. Input is case-insensitive.
+ *
+ * @param $input string of digits
+ * @param $sourceBase int 2-36
+ * @param $destBase int 2-36
+ * @param $pad int 1 or greater
+ * @return string or false on invalid input
+ */
+function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
+       if( $sourceBase < 2 ||
+               $sourceBase > 36 ||
+               $destBase < 2 ||
+               $destBase > 36 ||
+               $pad < 1 ||
+               $sourceBase != intval( $sourceBase ) ||
+               $destBase != intval( $destBase ) ||
+               $pad != intval( $pad ) ||
+               !is_string( $input ) ||
+               $input == '' ) {
+               return false;
+       }
+       
+       $digitChars = '0123456789abcdefghijklmnopqrstuvwxyz';
+       $inDigits = array();
+       $outChars = '';
+       
+       // Decode and validate input string
+       $input = strtolower( $input );
+       for( $i = 0; $i < strlen( $input ); $i++ ) {
+               $n = strpos( $digitChars, $input{$i} );
+               if( $n === false || $n > $sourceBase ) {
+                       return false;
+               }
+               $inDigits[] = $n;
+       }
+       
+       // Iterate over the input, modulo-ing out an output digit
+       // at a time until input is gone.
+       while( count( $inDigits ) ) {
+               $work = 0;
+               $workDigits = array();
+               
+               // Long division...
+               foreach( $inDigits as $digit ) {
+                       $work *= $sourceBase;
+                       $work += $digit;
+                       
+                       if( $work < $destBase ) {
+                               // Gonna need to pull another digit.
+                               if( count( $workDigits ) ) {
+                                       // Avoid zero-padding; this lets us find
+                                       // the end of the input very easily when
+                                       // length drops to zero.
+                                       $workDigits[] = 0;
+                               }
+                       } else {
+                               // Finally! Actual division!
+                               $workDigits[] = intval( $work / $destBase );
+                               
+                               // Isn't it annoying that most programming languages
+                               // don't have a single divide-and-remainder operator,
+                               // even though the CPU implements it that way?
+                               $work = $work % $destBase;
+                       }
+               }
+               
+               // All that division leaves us with a remainder,
+               // which is conveniently our next output digit.
+               $outChars .= $digitChars[$work];
+               
+               // And we continue!
+               $inDigits = $workDigits;
+       }
+       
+       while( strlen( $outChars ) < $pad ) {
+               $outChars .= '0';
+       }
+       
+       return strrev( $outChars );
+}
+
+/**
+ * Create an object with a given name and an array of construct parameters
+ * @param string $name
+ * @param array $p parameters
+ */
+function wfCreateObject( $name, $p ){
+       $p = array_values( $p );
+       switch ( count( $p ) ) {
+               case 0:
+                       return new $name;
+               case 1:
+                       return new $name( $p[0] );
+               case 2:
+                       return new $name( $p[0], $p[1] );
+               case 3:
+                       return new $name( $p[0], $p[1], $p[2] );
+               case 4:
+                       return new $name( $p[0], $p[1], $p[2], $p[3] );
+               case 5:
+                       return new $name( $p[0], $p[1], $p[2], $p[3], $p[4] );
+               case 6:
+                       return new $name( $p[0], $p[1], $p[2], $p[3], $p[4], $p[5] );
+               default:
+                       throw new MWException( "Too many arguments to construtor in wfCreateObject" );
+       }
+}
+
+/**
+ * Aliases for modularized functions
+ */
+function wfGetHTTP( $url, $timeout = 'default' ) { 
+       return Http::get( $url, $timeout ); 
+}
+function wfIsLocalURL( $url ) { 
+       return Http::isLocalURL( $url ); 
+}
+
+/**
+ * Initialise php session
+ */
+function wfSetupSession() {
+       global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
+       if( $wgSessionsInMemcached ) {
+               require_once( 'MemcachedSessions.php' );
+       } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
+               # If it's left on 'user' or another setting from another
+               # application, it will end up failing. Try to recover.
+               ini_set ( 'session.save_handler', 'files' );
+       }
+       session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
+       session_cache_limiter( 'private, must-revalidate' );
+       @session_start();
+}
+
+/**
+ * Get an object from the precompiled serialized directory
+ *
+ * @return mixed The variable on success, false on failure
+ */
+function wfGetPrecompiledData( $name ) {
+       global $IP;
+
+       $file = "$IP/serialized/$name";
+       if ( file_exists( $file ) ) {
+               $blob = file_get_contents( $file );
+               if ( $blob ) {
+                       return unserialize( $blob );
+               }
+       }
+       return false;
+}
+
+function wfGetCaller( $level = 2 ) {
+       $backtrace = debug_backtrace();
+       if ( isset( $backtrace[$level] ) ) {
+               if ( isset( $backtrace[$level]['class'] ) ) {
+                       $caller = $backtrace[$level]['class'] . '::' . $backtrace[$level]['function'];
+               } else {
+                       $caller = $backtrace[$level]['function'];
+               }
+       } else {
+               $caller = 'unknown';
+       }
+       return $caller;
+}
+
+/** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */
+function wfGetAllCallers() {
+       return implode('/', array_map(
+               create_function('$frame',' 
+                       return isset( $frame["class"] )?
+                               $frame["class"]."::".$frame["function"]:
+                               $frame["function"]; 
+                       '),
+               array_reverse(debug_backtrace())));
+}
+
 ?>